mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-08-13 11:05:46 -07:00
renderer: add simple color management (#9506)
Adds proper color management and transformations for CM surfaces.
This commit is contained in:
@@ -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
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
|
||||
|
Reference in New Issue
Block a user