1
0
mirror of https://github.com/hyprwm/Hyprland.git synced 2025-08-13 11:05:46 -07:00

renderer: add simple color management ()

Adds proper color management and transformations for CM surfaces.
This commit is contained in:
UjinT34
2025-03-14 02:15:18 +03:00
committed by GitHub
parent e86d3a14e4
commit 8c97cb7858
14 changed files with 823 additions and 179 deletions

@@ -1378,6 +1378,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "render:cm_fs_passthrough",
.description = "Passthrough color settings for fullscreen apps when possible",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
/*
* cursor:
@@ -1764,18 +1770,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},
SConfigOptionDescription{
.value = "experimental:wide_color_gamut",
.description = "force wide color gamut for all supported outputs",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "experimental:hdr",
.description = "force static hdr for all supported outputs",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "experimental:xx_color_management_v4",
.description = "enable color management protocol",

@@ -692,12 +692,11 @@ CConfigManager::CConfigManager() {
registerConfigVar("render:xp_mode", Hyprlang::INT{0});
registerConfigVar("render:ctm_animation", Hyprlang::INT{2});
registerConfigVar("render:allow_early_buffer_release", Hyprlang::INT{1});
registerConfigVar("render:cm_fs_passthrough", Hyprlang::INT{1});
registerConfigVar("ecosystem:no_update_news", Hyprlang::INT{0});
registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0});
registerConfigVar("experimental:wide_color_gamut", Hyprlang::INT{0});
registerConfigVar("experimental:hdr", Hyprlang::INT{0});
registerConfigVar("experimental:xx_color_management_v4", Hyprlang::INT{0});
// devices
@@ -2042,6 +2041,32 @@ std::optional<std::string> CConfigManager::handleMonitor(const std::string& comm
} else if (ARGS[argno] == "bitdepth") {
newrule.enable10bit = ARGS[argno + 1] == "10";
argno++;
} else if (ARGS[argno] == "cm") {
if (ARGS[argno + 1] == "auto")
newrule.cmType = CM_AUTO;
else if (ARGS[argno + 1] == "srgb")
newrule.cmType = CM_SRGB;
else if (ARGS[argno + 1] == "wide")
newrule.cmType = CM_WIDE;
else if (ARGS[argno + 1] == "edid")
newrule.cmType = CM_EDID;
else if (ARGS[argno + 1] == "hdr")
newrule.cmType = CM_HDR;
else if (ARGS[argno + 1] == "hdredid")
newrule.cmType = CM_HDR_EDID;
else
error = "invalid cm ";
argno++;
} else if (ARGS[argno] == "sdrsaturation") {
try {
newrule.sdrSaturation = stof(ARGS[argno + 1]);
} catch (...) { error = "invalid sdrsaturation "; }
argno++;
} else if (ARGS[argno] == "sdrbrightness") {
try {
newrule.sdrBrightness = stof(ARGS[argno + 1]);
} catch (...) { error = "invalid sdrbrightness "; }
argno++;
} else if (ARGS[argno] == "transform") {
if (!isNumber(ARGS[argno + 1])) {
error = "invalid transform ";

@@ -2,6 +2,7 @@
#include "MiscFunctions.hpp"
#include "../macros.hpp"
#include "math/Math.hpp"
#include "../protocols/ColorManagement.hpp"
#include "sync/SyncReleaser.hpp"
#include "../Compositor.hpp"
#include "../config/ConfigValue.hpp"
@@ -412,7 +413,8 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) {
if (!force && DELTALESSTHAN(vecPixelSize.x, RULE->resolution.x, 1) && DELTALESSTHAN(vecPixelSize.y, RULE->resolution.y, 1) &&
DELTALESSTHAN(refreshRate, RULE->refreshRate, 1) && setScale == RULE->scale &&
((DELTALESSTHAN(vecPosition.x, RULE->offset.x, 1) && DELTALESSTHAN(vecPosition.y, RULE->offset.y, 1)) || RULE->offset == Vector2D(-INT32_MAX, -INT32_MAX)) &&
transform == RULE->transform && RULE->enable10bit == enabled10bit && !std::memcmp(&customDrmMode, &RULE->drmMode, sizeof(customDrmMode))) {
transform == RULE->transform && RULE->enable10bit == enabled10bit && RULE->cmType == cmType && RULE->sdrSaturation == sdrSaturation &&
RULE->sdrBrightness == sdrBrightness && !std::memcmp(&customDrmMode, &RULE->drmMode, sizeof(customDrmMode))) {
Debug::log(LOG, "Not applying a new rule to {} because it's already applied!", szName);
@@ -670,6 +672,66 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) {
enabled10bit = set10bit;
auto oldImageDescription = imageDescription;
cmType = RULE->cmType;
switch (cmType) {
case CM_AUTO: cmType = enabled10bit && output->parsedEDID.supportsBT2020 ? CM_WIDE : CM_SRGB; break;
case CM_EDID: cmType = output->parsedEDID.chromaticityCoords.has_value() ? CM_EDID : CM_SRGB; break;
case CM_HDR:
case CM_HDR_EDID:
cmType = output->parsedEDID.supportsBT2020 && output->parsedEDID.hdrMetadata.has_value() && output->parsedEDID.hdrMetadata->supportsPQ ? cmType : CM_SRGB;
break;
default: break;
}
switch (cmType) {
case CM_SRGB: imageDescription = {}; break; // assumes SImageDescirption defaults to sRGB
case CM_WIDE:
imageDescription = {.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020)};
break;
case CM_EDID:
imageDescription = {.primariesNameSet = false,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = {
.red = {.x = output->parsedEDID.chromaticityCoords->red.x, .y = output->parsedEDID.chromaticityCoords->red.y},
.green = {.x = output->parsedEDID.chromaticityCoords->green.x, .y = output->parsedEDID.chromaticityCoords->green.y},
.blue = {.x = output->parsedEDID.chromaticityCoords->blue.x, .y = output->parsedEDID.chromaticityCoords->blue.y},
.white = {.x = output->parsedEDID.chromaticityCoords->white.x, .y = output->parsedEDID.chromaticityCoords->white.y},
}};
break;
case CM_HDR:
imageDescription = {.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
.luminances = {.min = 0, .max = 10000, .reference = 203}};
break;
case CM_HDR_EDID:
imageDescription = {.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
.primariesNameSet = false,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = output->parsedEDID.chromaticityCoords.has_value() ?
NColorManagement::SPCPRimaries{
.red = {.x = output->parsedEDID.chromaticityCoords->red.x, .y = output->parsedEDID.chromaticityCoords->red.y},
.green = {.x = output->parsedEDID.chromaticityCoords->green.x, .y = output->parsedEDID.chromaticityCoords->green.y},
.blue = {.x = output->parsedEDID.chromaticityCoords->blue.x, .y = output->parsedEDID.chromaticityCoords->blue.y},
.white = {.x = output->parsedEDID.chromaticityCoords->white.x, .y = output->parsedEDID.chromaticityCoords->white.y},
} :
NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
.luminances = {.min = output->parsedEDID.hdrMetadata->desiredContentMinLuminance,
.max = output->parsedEDID.hdrMetadata->desiredContentMaxLuminance,
.reference = output->parsedEDID.hdrMetadata->desiredMaxFrameAverageLuminance}};
break;
default: UNREACHABLE();
}
if (oldImageDescription != imageDescription)
PROTO::colorManagement->onMonitorImageDescriptionChanged(self);
sdrSaturation = RULE->sdrSaturation;
sdrBrightness = RULE->sdrBrightness;
Vector2D logicalSize = vecPixelSize / scale;
if (!*PDISABLESCALECHECKS && (logicalSize.x != std::round(logicalSize.x) || logicalSize.y != std::round(logicalSize.y))) {
// invalid scale, will produce fractional pixels.

@@ -28,18 +28,30 @@ enum eAutoDirs : uint8_t {
DIR_AUTO_RIGHT
};
enum eCMType : uint8_t {
CM_AUTO = 0, // subject to change. srgb for 8bpc, wide for 10bpc if supported
CM_SRGB, // default, sRGB primaries
CM_WIDE, // wide color gamut, BT2020 primaries
CM_EDID, // primaries from edid (known to be inaccurate)
CM_HDR, // wide color gamut and HDR PQ transfer function
CM_HDR_EDID, // same as CM_HDR with edid primaries
};
struct SMonitorRule {
eAutoDirs autoDir = DIR_AUTO_NONE;
std::string name = "";
Vector2D resolution = Vector2D(1280, 720);
Vector2D offset = Vector2D(0, 0);
float scale = 1;
float refreshRate = 60; // Hz
bool disabled = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
std::string mirrorOf = "";
bool enable10bit = false;
drmModeModeInfo drmMode = {};
eAutoDirs autoDir = DIR_AUTO_NONE;
std::string name = "";
Vector2D resolution = Vector2D(1280, 720);
Vector2D offset = Vector2D(0, 0);
float scale = 1;
float refreshRate = 60; // Hz
bool disabled = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
std::string mirrorOf = "";
bool enable10bit = false;
eCMType cmType = CM_SRGB;
float sdrSaturation = 1.0f; // SDR -> HDR
float sdrBrightness = 1.0f; // SDR -> HDR
drmModeModeInfo drmMode = {};
std::optional<int> vrr;
};
@@ -108,6 +120,9 @@ class CMonitor {
bool dpmsStatus = true;
bool vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it.
bool enabled10bit = false; // as above, this can be TRUE even if 10 bit failed.
eCMType cmType = CM_SRGB;
float sdrSaturation = 1.0f;
float sdrBrightness = 1.0f;
bool createdByUser = false;
bool isUnsafeFallback = false;

@@ -14,46 +14,47 @@ CColorManager::CColorManager(SP<CWpColorManagerV1> resource) : m_resource(resour
if UNLIKELY (!good())
return;
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_PARAMETRIC);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_PRIMARIES);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_LUMINANCES);
if (PROTO::colorManagement->m_debug) {
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_ICC_V2_V4);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_PARAMETRIC);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_PRIMARIES);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_TF_POWER);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_LUMINANCES);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_EXTENDED_TARGET_VOLUME);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_WINDOWS_SCRGB);
}
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_SRGB);
if (PROTO::colorManagement->m_debug) {
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_BT2020); // HDR for fullscreen only
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_BT2020);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_PAL_M);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_PAL);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_NTSC);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_GENERIC_FILM);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_DCI_P3);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_DISPLAY_P3);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_ADOBE_RGB);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_PAL_M);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_PAL);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_NTSC);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_GENERIC_FILM);
if (PROTO::colorManagement->m_debug) {
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_CIE1931_XYZ);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_DCI_P3);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_DISPLAY_P3);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_ADOBE_RGB);
}
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB);
if (PROTO::colorManagement->m_debug) {
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ); // HDR for fullscreen only
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA28);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_HLG);
if (PROTO::colorManagement->m_debug) {
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA28);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST240);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_100);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_316);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_XVYCC);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_SRGB);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST428);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_HLG);
}
m_resource->sendSupportedIntent(WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL);
@@ -162,11 +163,6 @@ CColorManager::CColorManager(SP<CWpColorManagerV1> resource) : m_resource(resour
m_resource->setCreateParametricCreator([](CWpColorManagerV1* r, uint32_t id) {
LOGM(TRACE, "New parametric creator for id={}", id);
if (!PROTO::colorManagement->m_debug) {
r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Parametric creator is not supported");
return;
}
const auto RESOURCE = PROTO::colorManagement->m_vParametricCreators.emplace_back(
makeShared<CColorManagementParametricCreator>(makeShared<CWpImageDescriptionCreatorParamsV1>(r->client(), r->version(), id)));
@@ -536,6 +532,10 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SP<CWpImage
switch (tf) {
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB: break;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ: break;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR: break;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22: break;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA28: break;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_HLG: break;
default: r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_TF, "Unsupported transfer function"); return;
}
@@ -566,11 +566,6 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SP<CWpImage
return;
}
if (!PROTO::colorManagement->m_debug && primaries != WP_COLOR_MANAGER_V1_PRIMARIES_SRGB) {
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_PRIMARIES_NAMED, "Unsupported primaries");
return;
}
switch (primaries) {
case WP_COLOR_MANAGER_V1_PRIMARIES_SRGB:
case WP_COLOR_MANAGER_V1_PRIMARIES_BT2020:
@@ -580,14 +575,18 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SP<CWpImage
case WP_COLOR_MANAGER_V1_PRIMARIES_GENERIC_FILM:
case WP_COLOR_MANAGER_V1_PRIMARIES_DCI_P3:
case WP_COLOR_MANAGER_V1_PRIMARIES_DISPLAY_P3:
case WP_COLOR_MANAGER_V1_PRIMARIES_ADOBE_RGB:
settings.primariesNameSet = true;
settings.primariesNamed = convertPrimaries((wpColorManagerV1Primaries)primaries);
settings.primaries = getPrimaries(settings.primariesNamed);
valuesSet |= PC_PRIMARIES;
break;
default: r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_PRIMARIES_NAMED, "Unsupported primaries");
case WP_COLOR_MANAGER_V1_PRIMARIES_ADOBE_RGB: break;
default:
if (!PROTO::colorManagement->m_debug) {
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_PRIMARIES_NAMED, "Unsupported primaries");
return;
}
}
settings.primariesNameSet = true;
settings.primariesNamed = convertPrimaries((wpColorManagerV1Primaries)primaries);
settings.primaries = getPrimaries(settings.primariesNamed);
valuesSet |= PC_PRIMARIES;
});
m_resource->setSetPrimaries(
[this](CWpImageDescriptionCreatorParamsV1* r, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) {
@@ -618,10 +617,6 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SP<CWpImage
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_LUMINANCE, "Invalid luminances");
return;
}
if (!PROTO::colorManagement->m_debug) {
r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Luminances aren't supported");
return;
}
settings.luminances = SImageDescription::SPCLuminances{.min = min, .max = max_lum, .reference = reference_lum};
valuesSet |= PC_LUMINANCES;
});

@@ -3,6 +3,7 @@
#include "ColorManagement.hpp"
#include "color-management-v1.hpp"
#include "types/ColorManagement.hpp"
#include "xx-color-management-v4.hpp"
using namespace NColorManagement;
@@ -40,20 +41,22 @@ CXXColorManager::CXXColorManager(SP<CXxColorManagerV4> resource_) : resource(res
resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_SET_LUMINANCES);
resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_SRGB);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_PAL_M);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_PAL);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_NTSC);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_GENERIC_FILM);
resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_PAL_M);
resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_PAL);
resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_NTSC);
resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_GENERIC_FILM);
resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_BT2020);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_CIE1931_XYZ);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_DCI_P3);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_DISPLAY_P3);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_ADOBE_RGB);
resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_DCI_P3);
resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_DISPLAY_P3);
resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_ADOBE_RGB);
// resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22);
resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22);
resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA28);
resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_HLG);
resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB);
resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ);
// resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR);
resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR);
resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_PERCEPTUAL);
// resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_RELATIVE);
@@ -396,8 +399,12 @@ CXXColorManagementParametricCreator::CXXColorManagementParametricCreator(SP<CXxI
}
switch (tf) {
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB: break;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ: break;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA28:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_HLG:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR: break;
default: r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_TF, "Unsupported transfer function"); return;
}
@@ -422,15 +429,17 @@ CXXColorManagementParametricCreator::CXXColorManagementParametricCreator(SP<CXxI
switch (primaries) {
case XX_COLOR_MANAGER_V4_PRIMARIES_SRGB:
settings.primariesNameSet = true;
settings.primariesNamed = convertPrimaries(getWPPrimaries(XX_COLOR_MANAGER_V4_PRIMARIES_SRGB));
settings.primaries = NColorPrimaries::BT709;
valuesSet |= PC_PRIMARIES;
break;
case XX_COLOR_MANAGER_V4_PRIMARIES_PAL_M:
case XX_COLOR_MANAGER_V4_PRIMARIES_PAL:
case XX_COLOR_MANAGER_V4_PRIMARIES_NTSC:
case XX_COLOR_MANAGER_V4_PRIMARIES_GENERIC_FILM:
case XX_COLOR_MANAGER_V4_PRIMARIES_BT2020:
case XX_COLOR_MANAGER_V4_PRIMARIES_DCI_P3:
case XX_COLOR_MANAGER_V4_PRIMARIES_DISPLAY_P3:
case XX_COLOR_MANAGER_V4_PRIMARIES_ADOBE_RGB:
settings.primariesNameSet = true;
settings.primariesNamed = convertPrimaries(getWPPrimaries(XX_COLOR_MANAGER_V4_PRIMARIES_BT2020));
settings.primaries = NColorPrimaries::BT2020;
settings.primariesNamed = convertPrimaries(getWPPrimaries((xxColorManagerV4Primaries)primaries));
settings.primaries = getPrimaries(settings.primariesNamed);
valuesSet |= PC_PRIMARIES;
break;
default: r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_PRIMARIES, "Unsupported primaries");

@@ -48,10 +48,17 @@ namespace NColorManagement {
}
struct SPCPRimaries {
struct {
struct xy { //NOLINT(readability-identifier-naming)
float x = 0;
float y = 0;
bool operator==(const xy& p2) const {
return x == p2.x && y == p2.y;
}
} red, green, blue, white;
bool operator==(const SPCPRimaries& p2) const {
return red == p2.red && green == p2.green && blue == p2.blue && white == p2.white;
}
};
namespace NColorPrimaries {
@@ -102,6 +109,7 @@ namespace NColorManagement {
.blue = {.x = 0.150, .y = 0.060},
.white = {.x = 0.314, .y = 0.351},
};
static const auto DISPLAY_P3 = SPCPRimaries{
.red = {.x = 0.680, .y = 0.320},
.green = {.x = 0.265, .y = 0.690},
@@ -125,6 +133,9 @@ namespace NColorManagement {
int fd = -1;
uint32_t length = 0;
uint32_t offset = 0;
bool operator==(const SIccFile& i2) const {
return fd == i2.fd;
}
} icc;
bool windowsScRGB = false;
@@ -135,7 +146,8 @@ namespace NColorManagement {
bool primariesNameSet = false;
ePrimaries primariesNamed = CM_PRIMARIES_SRGB;
// primaries are stored as FP values with the same scale as standard defines (0.0 - 1.0)
// wayland protocol expects int32_t values multiplied by 10000
// wayland protocol expects int32_t values multiplied by 1000000
// xx protocol expects int32_t values multiplied by 10000
// drm expects uint16_t values multiplied by 50000
// frog protocol expects drm values
SPCPRimaries primaries, masteringPrimaries;
@@ -146,13 +158,32 @@ namespace NColorManagement {
float min = 0.2; // 0.2 cd/m²
uint32_t max = 80; // 80 cd/m²
uint32_t reference = 80; // 80 cd/m²
bool operator==(const SPCLuminances& l2) const {
return min == l2.min && max == l2.max && reference == l2.reference;
}
} luminances;
struct SPCMasteringLuminances {
float min = 0;
uint32_t max = 0;
bool operator==(const SPCMasteringLuminances& l2) const {
return min == l2.min && max == l2.max;
}
} masteringLuminances;
uint32_t maxCLL = 0;
uint32_t maxFALL = 0;
bool operator==(const SImageDescription& d2) const {
return (id != 0 && id == d2.id) ||
(icc == d2.icc && windowsScRGB == d2.windowsScRGB && transferFunction == d2.transferFunction && transferFunctionPower == d2.transferFunctionPower &&
((primariesNameSet && primariesNamed == d2.primariesNameSet) || (primaries == d2.primaries)) && masteringPrimaries == d2.masteringPrimaries &&
luminances == d2.luminances && masteringLuminances == d2.masteringLuminances && maxCLL == d2.maxCLL && maxFALL == d2.maxFALL);
}
const SPCPRimaries& getPrimaries() const {
if (primariesNameSet || primaries == SPCPRimaries{})
return NColorManagement::getPrimaries(primariesNamed);
return primaries;
}
};
}

@@ -9,6 +9,7 @@
#include "../desktop/LayerSurface.hpp"
#include "../protocols/LayerShell.hpp"
#include "../protocols/core/Compositor.hpp"
#include "../protocols/ColorManagement.hpp"
#include "../managers/HookSystemManager.hpp"
#include "../managers/input/InputManager.hpp"
#include "pass/TexPassElement.hpp"
@@ -20,6 +21,7 @@
#include <gbm.h>
#include <filesystem>
using namespace Hyprutils::OS;
using namespace NColorManagement;
const std::vector<const char*> ASSET_PATHS = {
#ifdef DATAROOTDIR
@@ -866,6 +868,39 @@ void CHyprOpenGLImpl::initShaders() {
m_RenderData.pCurrentMonData->m_shQUAD.radius = glGetUniformLocation(prog, "radius");
m_RenderData.pCurrentMonData->m_shQUAD.roundingPower = glGetUniformLocation(prog, "roundingPower");
#ifndef GLES2
prog = createProgram(TEXVERTSRC320, TEXFRAGSRCCM);
m_RenderData.pCurrentMonData->m_shCM.program = prog;
m_RenderData.pCurrentMonData->m_shCM.proj = glGetUniformLocation(prog, "proj");
m_RenderData.pCurrentMonData->m_shCM.tex = glGetUniformLocation(prog, "tex");
m_RenderData.pCurrentMonData->m_shCM.texType = glGetUniformLocation(prog, "texType");
m_RenderData.pCurrentMonData->m_shCM.skipCM = glGetUniformLocation(prog, "skipCM");
m_RenderData.pCurrentMonData->m_shCM.sourceTF = glGetUniformLocation(prog, "sourceTF");
m_RenderData.pCurrentMonData->m_shCM.targetTF = glGetUniformLocation(prog, "targetTF");
m_RenderData.pCurrentMonData->m_shCM.sourcePrimaries = glGetUniformLocation(prog, "sourcePrimaries");
m_RenderData.pCurrentMonData->m_shCM.targetPrimaries = glGetUniformLocation(prog, "targetPrimaries");
m_RenderData.pCurrentMonData->m_shCM.maxLuminance = glGetUniformLocation(prog, "maxLuminance");
m_RenderData.pCurrentMonData->m_shCM.dstMaxLuminance = glGetUniformLocation(prog, "dstMaxLuminance");
m_RenderData.pCurrentMonData->m_shCM.dstRefLuminance = glGetUniformLocation(prog, "dstRefLuminance");
m_RenderData.pCurrentMonData->m_shCM.sdrSaturation = glGetUniformLocation(prog, "sdrSaturation");
m_RenderData.pCurrentMonData->m_shCM.sdrBrightness = glGetUniformLocation(prog, "sdrBrightnessMultiplier");
m_RenderData.pCurrentMonData->m_shCM.alphaMatte = glGetUniformLocation(prog, "texMatte");
m_RenderData.pCurrentMonData->m_shCM.alpha = glGetUniformLocation(prog, "alpha");
m_RenderData.pCurrentMonData->m_shCM.texAttrib = glGetAttribLocation(prog, "texcoord");
m_RenderData.pCurrentMonData->m_shCM.matteTexAttrib = glGetAttribLocation(prog, "texcoordMatte");
m_RenderData.pCurrentMonData->m_shCM.posAttrib = glGetAttribLocation(prog, "pos");
m_RenderData.pCurrentMonData->m_shCM.discardOpaque = glGetUniformLocation(prog, "discardOpaque");
m_RenderData.pCurrentMonData->m_shCM.discardAlpha = glGetUniformLocation(prog, "discardAlpha");
m_RenderData.pCurrentMonData->m_shCM.discardAlphaValue = glGetUniformLocation(prog, "discardAlphaValue");
m_RenderData.pCurrentMonData->m_shCM.topLeft = glGetUniformLocation(prog, "topLeft");
m_RenderData.pCurrentMonData->m_shCM.fullSize = glGetUniformLocation(prog, "fullSize");
m_RenderData.pCurrentMonData->m_shCM.radius = glGetUniformLocation(prog, "radius");
m_RenderData.pCurrentMonData->m_shCM.roundingPower = glGetUniformLocation(prog, "roundingPower");
m_RenderData.pCurrentMonData->m_shCM.applyTint = glGetUniformLocation(prog, "applyTint");
m_RenderData.pCurrentMonData->m_shCM.tint = glGetUniformLocation(prog, "tint");
m_RenderData.pCurrentMonData->m_shCM.useAlphaMatte = glGetUniformLocation(prog, "useAlphaMatte");
#endif
prog = createProgram(TEXVERTSRC, TEXFRAGSRCRGBA);
m_RenderData.pCurrentMonData->m_shRGBA.program = prog;
m_RenderData.pCurrentMonData->m_shRGBA.proj = glGetUniformLocation(prog, "proj");
@@ -1280,7 +1315,8 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, const CB
CBox newBox = box;
m_RenderData.renderModif.applyToBox(newBox);
static auto PDT = CConfigValue<Hyprlang::INT>("debug:damage_tracking");
static auto PDT = CConfigValue<Hyprlang::INT>("debug:damage_tracking");
static auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough");
// get the needed transform for this texture
const bool TRANSFORMS_MATCH = wlTransformToHyprutils(m_RenderData.pMonitor->transform) == tex->m_eTransform; // FIXME: combine them properly!!!
@@ -1304,6 +1340,8 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, const CB
const bool CRASHING = m_bApplyFinalShader && g_pHyprRenderer->m_bCrashingInProgress;
auto texType = tex->m_iType;
if (CRASHING) {
shader = &m_RenderData.pCurrentMonData->m_shGLITCH;
usingFinalShader = true;
@@ -1316,16 +1354,27 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, const CB
usingFinalShader = true;
} else {
switch (tex->m_iType) {
#ifdef GLES2
case TEXTURE_RGBA: shader = &m_RenderData.pCurrentMonData->m_shRGBA; break;
case TEXTURE_RGBX: shader = &m_RenderData.pCurrentMonData->m_shRGBX; break;
case TEXTURE_EXTERNAL: shader = &m_RenderData.pCurrentMonData->m_shEXT; break;
#else
case TEXTURE_RGBA:
case TEXTURE_RGBX: shader = &m_RenderData.pCurrentMonData->m_shCM; break;
#endif
case TEXTURE_EXTERNAL: shader = &m_RenderData.pCurrentMonData->m_shEXT; break; // might be unused
default: RASSERT(false, "tex->m_iTarget unsupported!");
}
}
}
if (m_RenderData.currentWindow && m_RenderData.currentWindow->m_sWindowData.RGBX.valueOrDefault())
if (m_RenderData.currentWindow && m_RenderData.currentWindow->m_sWindowData.RGBX.valueOrDefault()) {
#ifdef GLES2
shader = &m_RenderData.pCurrentMonData->m_shRGBX;
#else
shader = &m_RenderData.pCurrentMonData->m_shCM;
#endif
texType = TEXTURE_RGBX;
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(tex->m_iTarget, tex->m_iTexID);
@@ -1350,6 +1399,49 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, const CB
glUniformMatrix3fv(shader->proj, 1, GL_FALSE, glMatrix.getMatrix().data());
#endif
glUniform1i(shader->tex, 0);
#ifndef GLES2
if (!usingFinalShader && (texType == TEXTURE_RGBA || texType == TEXTURE_RGBX)) {
const bool skipCM = *PPASS && m_RenderData.pMonitor->activeWorkspace && m_RenderData.pMonitor->activeWorkspace->m_bHasFullscreenWindow &&
m_RenderData.pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN;
glUniform1i(shader->texType, texType);
glUniform1i(shader->skipCM, skipCM);
if (!skipCM) {
const auto imageDescription =
m_RenderData.surface.valid() && m_RenderData.surface->colorManagement.valid() ? m_RenderData.surface->colorManagement->imageDescription() : SImageDescription{};
glUniform1i(shader->sourceTF, imageDescription.transferFunction);
glUniform1i(shader->targetTF, m_RenderData.pMonitor->imageDescription.transferFunction);
const auto sourcePrimaries =
imageDescription.primariesNameSet || imageDescription.primaries == SPCPRimaries{} ? getPrimaries(imageDescription.primariesNamed) : imageDescription.primaries;
const auto targetPrimaries = m_RenderData.pMonitor->imageDescription.primariesNameSet || m_RenderData.pMonitor->imageDescription.primaries == SPCPRimaries{} ?
getPrimaries(m_RenderData.pMonitor->imageDescription.primariesNamed) :
m_RenderData.pMonitor->imageDescription.primaries;
const GLfloat glSourcePrimaries[8] = {
sourcePrimaries.red.x, sourcePrimaries.red.y, sourcePrimaries.green.x, sourcePrimaries.green.y,
sourcePrimaries.blue.x, sourcePrimaries.blue.y, sourcePrimaries.white.x, sourcePrimaries.white.y,
};
const GLfloat glTargetPrimaries[8] = {
targetPrimaries.red.x, targetPrimaries.red.y, targetPrimaries.green.x, targetPrimaries.green.y,
targetPrimaries.blue.x, targetPrimaries.blue.y, targetPrimaries.white.x, targetPrimaries.white.y,
};
glUniformMatrix4x2fv(shader->sourcePrimaries, 1, false, glSourcePrimaries);
glUniformMatrix4x2fv(shader->targetPrimaries, 1, false, glTargetPrimaries);
const float maxLuminance = imageDescription.luminances.max > 0 ? imageDescription.luminances.max : imageDescription.luminances.reference;
glUniform1f(shader->maxLuminance, maxLuminance * m_RenderData.pMonitor->imageDescription.luminances.reference / imageDescription.luminances.reference);
glUniform1f(shader->dstMaxLuminance, m_RenderData.pMonitor->imageDescription.luminances.max > 0 ? m_RenderData.pMonitor->imageDescription.luminances.max : 10000);
glUniform1f(shader->dstRefLuminance, m_RenderData.pMonitor->imageDescription.luminances.reference);
glUniform1f(shader->sdrSaturation,
m_RenderData.pMonitor->sdrSaturation > 0 && m_RenderData.pMonitor->imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ?
m_RenderData.pMonitor->sdrSaturation :
1.0f);
glUniform1f(shader->sdrBrightness,
m_RenderData.pMonitor->sdrBrightness > 0 && m_RenderData.pMonitor->imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ?
m_RenderData.pMonitor->sdrBrightness :
1.0f);
}
}
#endif
if ((usingFinalShader && *PDT == 0) || CRASHING) {
glUniform1f(shader->time, m_tGlobalTimer.getSeconds() - shader->initialTime);

@@ -27,6 +27,7 @@
#include <hyprutils/os/FileDescriptor.hpp>
#include "../debug/TracyDefines.hpp"
#include "../protocols/core/Compositor.hpp"
struct gbm_device;
class CHyprRenderer;
@@ -105,41 +106,42 @@ struct SMonitorRenderData {
CShader m_shSHADOW;
CShader m_shBORDER1;
CShader m_shGLITCH;
//
CShader m_shCM;
};
struct SCurrentRenderData {
PHLMONITORREF pMonitor;
Mat3x3 projection;
Mat3x3 savedProjection;
Mat3x3 monitorProjection;
PHLMONITORREF pMonitor;
Mat3x3 projection;
Mat3x3 savedProjection;
Mat3x3 monitorProjection;
SMonitorRenderData* pCurrentMonData = nullptr;
CFramebuffer* currentFB = nullptr; // current rendering to
CFramebuffer* mainFB = nullptr; // main to render to
CFramebuffer* outFB = nullptr; // out to render to (if offloaded, etc)
SMonitorRenderData* pCurrentMonData = nullptr;
CFramebuffer* currentFB = nullptr; // current rendering to
CFramebuffer* mainFB = nullptr; // main to render to
CFramebuffer* outFB = nullptr; // out to render to (if offloaded, etc)
CRegion damage;
CRegion finalDamage; // damage used for funal off -> main
CRegion damage;
CRegion finalDamage; // damage used for funal off -> main
SRenderModifData renderModif;
float mouseZoomFactor = 1.f;
bool mouseZoomUseMouse = true; // true by default
bool useNearestNeighbor = false;
bool blockScreenShader = false;
bool simplePass = false;
SRenderModifData renderModif;
float mouseZoomFactor = 1.f;
bool mouseZoomUseMouse = true; // true by default
bool useNearestNeighbor = false;
bool blockScreenShader = false;
bool simplePass = false;
Vector2D primarySurfaceUVTopLeft = Vector2D(-1, -1);
Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1);
Vector2D primarySurfaceUVTopLeft = Vector2D(-1, -1);
Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1);
CBox clipBox = {}; // scaled coordinates
CRegion clipRegion;
CBox clipBox = {}; // scaled coordinates
CRegion clipRegion;
uint32_t discardMode = DISCARD_OPAQUE;
float discardOpacity = 0.f;
uint32_t discardMode = DISCARD_OPAQUE;
float discardOpacity = 0.f;
PHLLSREF currentLS;
PHLWINDOWREF currentWindow;
PHLLSREF currentLS;
PHLWINDOWREF currentWindow;
WP<CWLSurfaceResource> surface;
};
class CEGLSync {

@@ -32,9 +32,9 @@
#include "pass/RendererHintsPassElement.hpp"
#include "pass/SurfacePassElement.hpp"
#include "debug/Log.hpp"
#include "protocols/ColorManagement.hpp"
#include "../protocols/ColorManagement.hpp"
#if AQUAMARINE_VERSION_NUMBER > 702 // >0.7.2
#include "protocols/types/ContentType.hpp"
#include "../protocols/types/ContentType.hpp"
#endif
#include <hyprutils/utils/ScopeGuard.hpp>
@@ -1409,78 +1409,40 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) {
}
}
static const auto BT709 = Aquamarine::IOutput::SChromaticityCoords{
.red = Aquamarine::IOutput::xy{.x = 0.64, .y = 0.33},
.green = Aquamarine::IOutput::xy{.x = 0.30, .y = 0.60},
.blue = Aquamarine::IOutput::xy{.x = 0.15, .y = 0.06},
.white = Aquamarine::IOutput::xy{.x = 0.3127, .y = 0.3290},
};
static const hdr_output_metadata NO_HDR_METADATA = {.hdmi_metadata_type1 = hdr_metadata_infoframe{.eotf = 0}};
static hdr_output_metadata createHDRMetadata(uint8_t eotf, Aquamarine::IOutput::SParsedEDID edid) {
if (eotf == 0)
return hdr_output_metadata{.hdmi_metadata_type1 = hdr_metadata_infoframe{.eotf = 0}}; // empty metadata for SDR
const auto toNits = [](float value) { return uint16_t(std::round(value)); };
const auto to16Bit = [](float value) { return uint16_t(std::round(value * 50000)); };
const auto colorimetry = edid.chromaticityCoords.value_or(BT709);
Debug::log(TRACE, "ColorManagement primaries {},{} {},{} {},{} {},{}", colorimetry.red.x, colorimetry.red.y, colorimetry.green.x, colorimetry.green.y, colorimetry.blue.x,
colorimetry.blue.y, colorimetry.white.x, colorimetry.white.y);
Debug::log(TRACE, "ColorManagement max avg {}, min {}, max {}", edid.hdrMetadata->desiredMaxFrameAverageLuminance, edid.hdrMetadata->desiredContentMinLuminance,
edid.hdrMetadata->desiredContentMaxLuminance);
return hdr_output_metadata{
.metadata_type = 0,
.hdmi_metadata_type1 =
hdr_metadata_infoframe{
.eotf = eotf,
.metadata_type = 0,
.display_primaries =
{
{.x = to16Bit(colorimetry.red.x), .y = to16Bit(colorimetry.red.y)},
{.x = to16Bit(colorimetry.green.x), .y = to16Bit(colorimetry.green.y)},
{.x = to16Bit(colorimetry.blue.x), .y = to16Bit(colorimetry.blue.y)},
},
.white_point = {.x = to16Bit(colorimetry.white.x), .y = to16Bit(colorimetry.white.y)},
.max_display_mastering_luminance = toNits(edid.hdrMetadata->desiredMaxFrameAverageLuminance),
.min_display_mastering_luminance = toNits(edid.hdrMetadata->desiredContentMinLuminance * 10000),
.max_cll = toNits(edid.hdrMetadata->desiredMaxFrameAverageLuminance),
.max_fall = toNits(edid.hdrMetadata->desiredMaxFrameAverageLuminance),
},
};
}
static hdr_output_metadata createHDRMetadata(SImageDescription settings, Aquamarine::IOutput::SParsedEDID edid) {
static hdr_output_metadata createHDRMetadata(SImageDescription settings, Aquamarine::IOutput::SParsedEDID edid) {
if (settings.transferFunction != CM_TRANSFER_FUNCTION_ST2084_PQ)
return hdr_output_metadata{.hdmi_metadata_type1 = hdr_metadata_infoframe{.eotf = 0}}; // empty metadata for SDR
return NO_HDR_METADATA; // empty metadata for SDR
const auto toNits = [](uint32_t value) { return uint16_t(std::round(value)); };
const auto to16Bit = [](uint32_t value) { return uint16_t(std::round(value * 50000)); };
auto colorimetry = settings.primaries;
auto colorimetry = settings.primariesNameSet || settings.primaries == SPCPRimaries{} ? getPrimaries(settings.primariesNamed) : settings.primaries;
auto luminances = settings.masteringLuminances.max > 0 ?
settings.masteringLuminances :
SImageDescription::SPCMasteringLuminances{.min = edid.hdrMetadata->desiredContentMinLuminance, .max = edid.hdrMetadata->desiredContentMaxLuminance};
settings.masteringLuminances :
SImageDescription::SPCMasteringLuminances{.min = edid.hdrMetadata->desiredContentMinLuminance, .max = edid.hdrMetadata->desiredContentMaxLuminance};
Debug::log(TRACE, "ColorManagement primaries {},{} {},{} {},{} {},{}", colorimetry.red.x, colorimetry.red.y, colorimetry.green.x, colorimetry.green.y, colorimetry.blue.x,
colorimetry.blue.y, colorimetry.white.x, colorimetry.white.y);
colorimetry.blue.y, colorimetry.white.x, colorimetry.white.y);
Debug::log(TRACE, "ColorManagement min {}, max {}, cll {}, fall {}", luminances.min, luminances.max, settings.maxCLL, settings.maxFALL);
return hdr_output_metadata{
.metadata_type = 0,
.hdmi_metadata_type1 =
.metadata_type = 0,
.hdmi_metadata_type1 =
hdr_metadata_infoframe{
.eotf = 2,
.metadata_type = 0,
.display_primaries =
{
.eotf = 2,
.metadata_type = 0,
.display_primaries =
{
{.x = to16Bit(colorimetry.red.x), .y = to16Bit(colorimetry.red.y)},
{.x = to16Bit(colorimetry.green.x), .y = to16Bit(colorimetry.green.y)},
{.x = to16Bit(colorimetry.blue.x), .y = to16Bit(colorimetry.blue.y)},
},
.white_point = {.x = to16Bit(colorimetry.white.x), .y = to16Bit(colorimetry.white.y)},
.max_display_mastering_luminance = toNits(luminances.max),
.min_display_mastering_luminance = toNits(luminances.min * 10000),
.max_cll = toNits(settings.maxCLL),
.max_fall = toNits(settings.maxFALL),
.white_point = {.x = to16Bit(colorimetry.white.x), .y = to16Bit(colorimetry.white.y)},
.max_display_mastering_luminance = toNits(luminances.max),
.min_display_mastering_luminance = toNits(luminances.min * 10000),
.max_cll = toNits(settings.maxCLL),
.max_fall = toNits(settings.maxFALL),
},
};
}
@@ -1495,12 +1457,14 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
if (inFD.isValid())
pMonitor->output->state->setExplicitInFence(inFD.get());
static auto PHDR = CConfigValue<Hyprlang::INT>("experimental:hdr");
static auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough");
const bool PHDR = pMonitor->imageDescription.transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ;
const bool SUPPORTSPQ = pMonitor->output->parsedEDID.hdrMetadata.has_value() ? pMonitor->output->parsedEDID.hdrMetadata->supportsPQ : false;
Debug::log(TRACE, "ColorManagement supportsBT2020 {}, supportsPQ {}", pMonitor->output->parsedEDID.supportsBT2020, SUPPORTSPQ);
if (pMonitor->output->parsedEDID.supportsBT2020 && SUPPORTSPQ) {
if (pMonitor->activeWorkspace && pMonitor->activeWorkspace->m_bHasFullscreenWindow && pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) {
if (*PPASS && pMonitor->activeWorkspace && pMonitor->activeWorkspace->m_bHasFullscreenWindow && pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) {
const auto WINDOW = pMonitor->activeWorkspace->getFullscreenWindow();
const auto ROOT_SURF = WINDOW->m_pWLSurface->resource();
const auto SURF =
@@ -1512,18 +1476,17 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
SURF->colorManagement->setHDRMetadata(createHDRMetadata(SURF->colorManagement->imageDescription(), pMonitor->output->parsedEDID));
if (needsHdrMetadataUpdate)
pMonitor->output->state->setHDRMetadata(SURF->colorManagement->hdrMetadata());
} else if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != *PHDR)
pMonitor->output->state->setHDRMetadata(*PHDR ? createHDRMetadata(2, pMonitor->output->parsedEDID) : createHDRMetadata(0, pMonitor->output->parsedEDID));
} else if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != PHDR)
pMonitor->output->state->setHDRMetadata(PHDR ? createHDRMetadata(pMonitor->imageDescription, pMonitor->output->parsedEDID) : NO_HDR_METADATA);
pMonitor->m_previousFSWindow = WINDOW;
} else {
if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != *PHDR)
pMonitor->output->state->setHDRMetadata(*PHDR ? createHDRMetadata(2, pMonitor->output->parsedEDID) : createHDRMetadata(0, pMonitor->output->parsedEDID));
if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != PHDR)
pMonitor->output->state->setHDRMetadata(PHDR ? createHDRMetadata(pMonitor->imageDescription, pMonitor->output->parsedEDID) : NO_HDR_METADATA);
pMonitor->m_previousFSWindow.reset();
}
}
static auto PWIDE = CConfigValue<Hyprlang::INT>("experimental:wide_color_gamut");
const bool needsWCG = *PWIDE || pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2;
const bool needsWCG = pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2 || pMonitor->imageDescription.primariesNamed == CM_PRIMARIES_BT2020;
if (pMonitor->output->state->state().wideColorGamut != needsWCG) {
Debug::log(TRACE, "Setting wide color gamut {}", needsWCG ? "on" : "off");
pMonitor->output->state->setWideColorGamut(needsWCG);

@@ -11,6 +11,17 @@ class CShader {
GLint proj = -1;
GLint color = -1;
GLint alphaMatte = -1;
GLint texType = -1;
GLint skipCM = -1;
GLint sourceTF = -1;
GLint targetTF = -1;
GLint sourcePrimaries = -1;
GLint targetPrimaries = -1;
GLint maxLuminance = -1;
GLint dstMaxLuminance = -1;
GLint dstRefLuminance = -1;
GLint sdrSaturation = -1; // sdr -> hdr saturation
GLint sdrBrightness = -1; // sdr -> hdr brightness multiplier
GLint tex = -1;
GLint alpha = -1;
GLint posAttrib = -1;

@@ -18,6 +18,7 @@ CSurfacePassElement::CSurfacePassElement(const CSurfacePassElement::SRenderData&
void CSurfacePassElement::draw(const CRegion& damage) {
g_pHyprOpenGL->m_RenderData.currentWindow = data.pWindow;
g_pHyprOpenGL->m_RenderData.surface = data.surface;
g_pHyprOpenGL->m_RenderData.currentLS = data.pLS;
g_pHyprOpenGL->m_RenderData.clipBox = data.clipBox;
g_pHyprOpenGL->m_RenderData.discardMode = data.discardMode;
@@ -36,6 +37,7 @@ void CSurfacePassElement::draw(const CRegion& damage) {
g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false;
g_pHyprOpenGL->m_bEndFrame = false;
g_pHyprOpenGL->m_RenderData.currentWindow.reset();
g_pHyprOpenGL->m_RenderData.surface.reset();
g_pHyprOpenGL->m_RenderData.currentLS.reset();
}};

439
src/render/shaders/CM.frag Normal file

@@ -0,0 +1,439 @@
R"#(
#version 320 es
//#extension GL_OES_EGL_image_external : require
precision highp float;
in vec2 v_texcoord;
uniform sampler2D tex;
//uniform samplerExternalOES texture0;
uniform int texType; // eTextureType: 0 - rgba, 1 - rgbx, 2 - ext
uniform int skipCM;
uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction
uniform mat4x2 sourcePrimaries;
uniform mat4x2 targetPrimaries;
uniform float maxLuminance;
uniform float dstMaxLuminance;
uniform float dstRefLuminance;
uniform float sdrSaturation;
uniform float sdrBrightnessMultiplier;
uniform float alpha;
uniform vec2 topLeft;
uniform vec2 fullSize;
uniform float radius;
uniform float roundingPower;
uniform int discardOpaque;
uniform int discardAlpha;
uniform float discardAlphaValue;
uniform int applyTint;
uniform vec3 tint;
//enum eTransferFunction
#define CM_TRANSFER_FUNCTION_BT1886 1
#define CM_TRANSFER_FUNCTION_GAMMA22 2
#define CM_TRANSFER_FUNCTION_GAMMA28 3
#define CM_TRANSFER_FUNCTION_ST240 4
#define CM_TRANSFER_FUNCTION_EXT_LINEAR 5
#define CM_TRANSFER_FUNCTION_LOG_100 6
#define CM_TRANSFER_FUNCTION_LOG_316 7
#define CM_TRANSFER_FUNCTION_XVYCC 8
#define CM_TRANSFER_FUNCTION_SRGB 9
#define CM_TRANSFER_FUNCTION_EXT_SRGB 10
#define CM_TRANSFER_FUNCTION_ST2084_PQ 11
#define CM_TRANSFER_FUNCTION_ST428 12
#define CM_TRANSFER_FUNCTION_HLG 13
// sRGB constants
#define SRGB_POW 2.4
#define SRGB_INV_POW (1.0 / SRGB_POW)
#define SRGB_D_CUT 0.04045
#define SRGB_E_CUT 0.0031308
#define SRGB_LO 12.92
#define SRGB_HI 1.055
#define SRGB_HI_ADD 0.055
// PQ constants
#define PQ_M1 0.1593017578125
#define PQ_M2 78.84375
#define PQ_INV_M1 (1.0 / PQ_M1)
#define PQ_INV_M2 (1.0 / PQ_M2)
#define PQ_C1 0.8359375
#define PQ_C2 18.8515625
#define PQ_C3 18.6875
// HLG constants
#define HLG_D_CUT (1.0 / 12.0)
#define HLG_E_CUT (sqrt(3.0) * pow(HLG_D_CUT, 0.5))
#define HLG_A 0.17883277
#define HLG_B 0.28466892
#define HLG_C 0.55991073
#define SDR_MIN_LUMINANCE 0.2
#define SDR_MAX_LUMINANCE 80.0
#define HDR_MIN_LUMINANCE 0.005
#define HDR_MAX_LUMINANCE 10000.0
#define HLG_MAX_LUMINANCE 1000.0
// smoothing constant for the edge: more = blurrier, but smoother
#define M_PI 3.1415926535897932384626433832795
#define M_E 2.718281828459045
#define SMOOTHING_CONSTANT (M_PI / 5.34665792551)
vec4 rounding(vec4 color) {
highp vec2 pixCoord = vec2(gl_FragCoord);
pixCoord -= topLeft + fullSize * 0.5;
pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0;
pixCoord -= fullSize * 0.5 - radius;
pixCoord += vec2(1.0, 1.0) / fullSize; // center the pix dont make it top-left
if (pixCoord.x + pixCoord.y > radius) {
float dist = pow(pow(pixCoord.x, roundingPower) + pow(pixCoord.y, roundingPower), 1.0/roundingPower);
if (dist > radius + SMOOTHING_CONSTANT)
discard;
float normalized = 1.0 - smoothstep(0.0, 1.0, (dist - radius + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0));
color *= normalized;
}
return color;
}
vec3 xy2xyz(vec2 xy) {
if (xy.y == 0.0)
return vec3(0.0, 0.0, 0.0);
return vec3(xy.x / xy.y, 1.0, (1.0 - xy.x - xy.y) / xy.y);
}
vec4 saturate(vec4 color, mat3 primaries, float saturation) {
if (saturation == 1.0)
return color;
vec3 brightness = vec3(primaries[1][0], primaries[1][1], primaries[1][2]);
float Y = dot(color.rgb, brightness);
return vec4(mix(vec3(Y), color.rgb, saturation), color[3]);
}
vec3 toLinearRGB(vec3 color, int tf) {
if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR)
return color;
bvec3 isLow;
vec3 lo;
vec3 hi;
switch (tf) {
case CM_TRANSFER_FUNCTION_ST2084_PQ:
vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_INV_M2));
return pow(
(max(E - PQ_C1, vec3(0.0))) / (PQ_C2 - PQ_C3 * E),
vec3(PQ_INV_M1)
);
case CM_TRANSFER_FUNCTION_GAMMA22:
return pow(max(color.rgb, vec3(0.0)), vec3(2.2));
case CM_TRANSFER_FUNCTION_GAMMA28:
return pow(max(color.rgb, vec3(0.0)), vec3(2.8));
case CM_TRANSFER_FUNCTION_HLG:
isLow = lessThanEqual(color.rgb, vec3(HLG_D_CUT));
lo = sqrt(3.0) * pow(color.rgb, vec3(0.5));
hi = HLG_A * log(12.0 * color.rgb - HLG_B) + HLG_C;
return mix(hi, lo, isLow);
case CM_TRANSFER_FUNCTION_BT1886:
case CM_TRANSFER_FUNCTION_ST240:
case CM_TRANSFER_FUNCTION_LOG_100:
case CM_TRANSFER_FUNCTION_LOG_316:
case CM_TRANSFER_FUNCTION_XVYCC:
case CM_TRANSFER_FUNCTION_EXT_SRGB:
case CM_TRANSFER_FUNCTION_ST428:
case CM_TRANSFER_FUNCTION_SRGB:
default:
isLow = lessThanEqual(color.rgb, vec3(SRGB_D_CUT));
lo = color.rgb / SRGB_LO;
hi = pow((color.rgb + SRGB_HI_ADD) / SRGB_HI, vec3(SRGB_POW));
return mix(hi, lo, isLow);
}
}
vec4 toLinear(vec4 color, int tf) {
if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR)
return color;
color.rgb /= max(color.a, 0.001);
color.rgb = toLinearRGB(color.rgb, tf);
color.rgb *= color.a;
return color;
}
vec4 toNit(vec4 color, int tf) {
if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR)
color.rgb = color.rgb * SDR_MAX_LUMINANCE;
else {
switch (tf) {
case CM_TRANSFER_FUNCTION_ST2084_PQ:
color.rgb = color.rgb * (HDR_MAX_LUMINANCE - HDR_MIN_LUMINANCE) + HDR_MIN_LUMINANCE; break;
case CM_TRANSFER_FUNCTION_HLG:
color.rgb = color.rgb * (HLG_MAX_LUMINANCE - HDR_MIN_LUMINANCE) + HDR_MIN_LUMINANCE; break;
case CM_TRANSFER_FUNCTION_GAMMA22:
case CM_TRANSFER_FUNCTION_GAMMA28:
case CM_TRANSFER_FUNCTION_BT1886:
case CM_TRANSFER_FUNCTION_ST240:
case CM_TRANSFER_FUNCTION_LOG_100:
case CM_TRANSFER_FUNCTION_LOG_316:
case CM_TRANSFER_FUNCTION_XVYCC:
case CM_TRANSFER_FUNCTION_EXT_SRGB:
case CM_TRANSFER_FUNCTION_ST428:
case CM_TRANSFER_FUNCTION_SRGB:
default:
color.rgb = color.rgb * (SDR_MAX_LUMINANCE - SDR_MIN_LUMINANCE) + SDR_MIN_LUMINANCE; break;
}
}
return color;
}
vec3 fromLinearRGB(vec3 color, int tf) {
bvec3 isLow;
vec3 lo;
vec3 hi;
switch (tf) {
case CM_TRANSFER_FUNCTION_EXT_LINEAR:
return color;
case CM_TRANSFER_FUNCTION_ST2084_PQ:
vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_M1));
return pow(
(vec3(PQ_C1) + PQ_C2 * E) / (vec3(1.0) + PQ_C3 * E),
vec3(PQ_M2)
);
break;
case CM_TRANSFER_FUNCTION_GAMMA22:
return pow(max(color.rgb, vec3(0.0)), vec3(1.0 / 2.2));
case CM_TRANSFER_FUNCTION_GAMMA28:
return pow(max(color.rgb, vec3(0.0)), vec3(1.0 / 2.8));
case CM_TRANSFER_FUNCTION_HLG:
isLow = lessThanEqual(color.rgb, vec3(HLG_E_CUT));
lo = pow(color.rgb / sqrt(3.0), vec3(2.0));
hi = (pow(vec3(M_E), (color.rgb - HLG_C) / HLG_A) + HLG_B) / 12.0;
return mix(hi, lo, isLow);
case CM_TRANSFER_FUNCTION_BT1886:
case CM_TRANSFER_FUNCTION_ST240:
case CM_TRANSFER_FUNCTION_LOG_100:
case CM_TRANSFER_FUNCTION_LOG_316:
case CM_TRANSFER_FUNCTION_XVYCC:
case CM_TRANSFER_FUNCTION_EXT_SRGB:
case CM_TRANSFER_FUNCTION_ST428:
case CM_TRANSFER_FUNCTION_SRGB:
default:
isLow = lessThanEqual(color.rgb, vec3(SRGB_E_CUT));
lo = color.rgb * SRGB_LO;
hi = pow(color.rgb, vec3(SRGB_INV_POW)) * SRGB_HI - SRGB_HI_ADD;
return mix(hi, lo, isLow);
}
}
vec4 fromLinear(vec4 color, int tf) {
if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR)
return color;
color.rgb /= max(color.a, 0.001);
color.rgb = fromLinearRGB(color.rgb, tf);
color.rgb *= color.a;
return color;
}
vec4 fromLinearNit(vec4 color, int tf) {
if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR)
color.rgb = color.rgb / SDR_MAX_LUMINANCE;
else {
color.rgb /= max(color.a, 0.001);
switch (tf) {
case CM_TRANSFER_FUNCTION_ST2084_PQ:
color.rgb = (color.rgb - HDR_MIN_LUMINANCE) / (HDR_MAX_LUMINANCE - HDR_MIN_LUMINANCE); break;
case CM_TRANSFER_FUNCTION_HLG:
color.rgb = (color.rgb - HDR_MIN_LUMINANCE) / (HLG_MAX_LUMINANCE - HDR_MIN_LUMINANCE); break;
case CM_TRANSFER_FUNCTION_GAMMA22:
case CM_TRANSFER_FUNCTION_GAMMA28:
case CM_TRANSFER_FUNCTION_BT1886:
case CM_TRANSFER_FUNCTION_ST240:
case CM_TRANSFER_FUNCTION_LOG_100:
case CM_TRANSFER_FUNCTION_LOG_316:
case CM_TRANSFER_FUNCTION_XVYCC:
case CM_TRANSFER_FUNCTION_EXT_SRGB:
case CM_TRANSFER_FUNCTION_ST428:
case CM_TRANSFER_FUNCTION_SRGB:
default:
color.rgb = (color.rgb - SDR_MIN_LUMINANCE) / (SDR_MAX_LUMINANCE - SDR_MIN_LUMINANCE); break;
}
color.rgb = fromLinearRGB(color.rgb, tf);
color.rgb *= color.a;
}
return color;
}
mat3 primaries2xyz(mat4x2 primaries) {
vec3 r = xy2xyz(primaries[0]);
vec3 g = xy2xyz(primaries[1]);
vec3 b = xy2xyz(primaries[2]);
vec3 w = xy2xyz(primaries[3]);
mat3 invMat = inverse(
mat3(
r.x, r.y, r.z,
g.x, g.y, g.z,
b.x, b.y, b.z
)
);
vec3 s = invMat * w;
return mat3(
s.r * r.x, s.r * r.y, s.r * r.z,
s.g * g.x, s.g * g.y, s.g * g.z,
s.b * b.x, s.b * b.y, s.b * b.z
);
}
// const vec2 D65 = vec2(0.3127, 0.3290);
const mat3 Bradford = mat3(
0.8951, 0.2664, -0.1614,
-0.7502, 1.7135, 0.0367,
0.0389, -0.0685, 1.0296
);
const mat3 BradfordInv = inverse(Bradford);
mat3 adaptWhite(vec2 src, vec2 dst) {
if (src == dst)
return mat3(
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0
);
vec3 srcXYZ = xy2xyz(src);
vec3 dstXYZ = xy2xyz(dst);
vec3 factors = (Bradford * dstXYZ) / (Bradford * srcXYZ);
return BradfordInv * mat3(
factors.x, 0.0, 0.0,
0.0, factors.y, 0.0,
0.0, 0.0, factors.z
) * Bradford;
}
vec4 convertPrimaries(vec4 color, mat3 src, vec2 srcWhite, mat3 dst, vec2 dstWhite) {
mat3 convMat = inverse(dst) * adaptWhite(srcWhite, dstWhite) * src;
return vec4(convMat * color.rgb, color[3]);
}
const mat3 BT2020toLMS = mat3(
0.3592, 0.6976, -0.0358,
-0.1922, 1.1004, 0.0755,
0.0070, 0.0749, 0.8434
);
const mat3 LMStoBT2020 = inverse(BT2020toLMS);
const mat3 ICtCpPQ = transpose(mat3(
2048.0, 2048.0, 0.0,
6610.0, -13613.0, 7003.0,
17933.0, -17390.0, -543.0
) / 4096.0);
const mat3 ICtCpPQInv = inverse(ICtCpPQ);
const mat3 ICtCpHLG = transpose(mat3(
2048.0, 2048.0, 0.0,
3625.0, -7465.0, 3840.0,
9500.0, -9212.0, -288.0
) / 4096.0);
const mat3 ICtCpHLGInv = inverse(ICtCpHLG);
vec4 tonemap(vec4 color, mat3 dstXYZ) {
if (maxLuminance < dstMaxLuminance * 1.01)
return vec4(clamp(color.rgb, vec3(0.0), vec3(dstMaxLuminance)), color[3]);
mat3 toLMS = BT2020toLMS * dstXYZ;
mat3 fromLMS = inverse(dstXYZ) * LMStoBT2020;
vec3 lms = fromLinear(vec4((toLMS * color.rgb) / HDR_MAX_LUMINANCE, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb;
vec3 ICtCp = ICtCpPQ * lms;
float E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_INV_M2);
float luminance = pow(
(max(E - PQ_C1, 0.0)) / (PQ_C2 - PQ_C3 * E),
PQ_INV_M1
) * HDR_MAX_LUMINANCE;
float srcScale = maxLuminance / dstRefLuminance;
float dstScale = dstMaxLuminance / dstRefLuminance;
float minScale = min(srcScale, 1.5);
float dimming = 1.0 / clamp(minScale / dstScale, 1.0, minScale);
float refLuminance = dstRefLuminance * dimming;
float low = min(luminance * dimming, refLuminance);
float highlight = clamp((luminance / dstRefLuminance - 1.0) / (srcScale - 1.0), 0.0, 1.0);
float high = log(highlight * (M_E - 1.0) + 1.0) * (dstMaxLuminance - refLuminance);
luminance = low + high;
E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_M1);
ICtCp[0] = pow(
(PQ_C1 + PQ_C2 * E) / (1.0 + PQ_C3 * E),
PQ_M2
) / HDR_MAX_LUMINANCE;
return vec4(fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb * HDR_MAX_LUMINANCE, color[3]);
}
layout(location = 0) out vec4 fragColor;
void main() {
vec4 pixColor;
if (texType == 1)
pixColor = vec4(texture(tex, v_texcoord).rgb, 1.0);
// else if (texType == 2)
// pixColor = texture(texture0, v_texcoord);
else // assume rgba
pixColor = texture(tex, v_texcoord);
if (discardOpaque == 1 && pixColor[3] * alpha == 1.0)
discard;
if (discardAlpha == 1 && pixColor[3] <= discardAlphaValue)
discard;
if (skipCM == 0) {
pixColor.rgb /= max(pixColor.a, 0.001);
pixColor.rgb = toLinearRGB(pixColor.rgb, sourceTF);
mat3 srcxyz = primaries2xyz(sourcePrimaries);
mat3 dstxyz;
if (sourcePrimaries == targetPrimaries)
dstxyz = srcxyz;
else {
dstxyz = primaries2xyz(targetPrimaries);
pixColor = convertPrimaries(pixColor, srcxyz, sourcePrimaries[3], dstxyz, targetPrimaries[3]);
}
pixColor = toNit(pixColor, sourceTF);
pixColor.rgb *= pixColor.a;
pixColor = tonemap(pixColor, dstxyz);
if (sourceTF == CM_TRANSFER_FUNCTION_SRGB && targetTF == CM_TRANSFER_FUNCTION_ST2084_PQ)
pixColor = saturate(pixColor, srcxyz, sdrSaturation);
pixColor *= sdrBrightnessMultiplier;
pixColor = fromLinearNit(pixColor, targetTF);
}
if (applyTint == 1)
pixColor = vec4(pixColor.rgb * tint.rgb, pixColor[3]);
if (radius > 0.0)
pixColor = rounding(pixColor);
fragColor = pixColor * alpha;
}
)#"

@@ -94,6 +94,10 @@ void main() {
v_texcoord = texcoord;
})#";
inline const std::string TEXFRAGSRCCM =
#include "CM.frag"
;
inline const std::string TEXFRAGSRCRGBA = R"#(
precision highp float;
varying vec2 v_texcoord; // is in 0-1