summaryrefslogtreecommitdiff
path: root/src/render
diff options
context:
space:
mode:
authorUjinT34 <41110182+UjinT34@users.noreply.github.com>2025-03-14 02:15:18 +0300
committerGitHub <noreply@github.com>2025-03-14 00:15:18 +0100
commit8c97cb7858e5d6c35d1a055930904346fb4248db (patch)
tree87d7e30393f9177d035183f273be54f03b9451d5 /src/render
parente86d3a14e46d19d8a47f8ceb6410546715d45f10 (diff)
renderer: add simple color management (#9506)
Adds proper color management and transformations for CM surfaces.
Diffstat (limited to 'src/render')
-rw-r--r--src/render/OpenGL.cpp98
-rw-r--r--src/render/OpenGL.hpp66
-rw-r--r--src/render/Renderer.cpp95
-rw-r--r--src/render/Shader.hpp11
-rw-r--r--src/render/pass/SurfacePassElement.cpp2
-rw-r--r--src/render/shaders/CM.frag439
-rw-r--r--src/render/shaders/Textures.hpp4
7 files changed, 614 insertions, 101 deletions
diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp
index 17224523..a8a38bac 100644
--- a/src/render/OpenGL.cpp
+++ b/src/render/OpenGL.cpp
@@ -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);
diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp
index cf1d2549..11079591 100644
--- a/src/render/OpenGL.hpp
+++ b/src/render/OpenGL.hpp
@@ -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;
-
- 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
-
- 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);
-
- CBox clipBox = {}; // scaled coordinates
- CRegion clipRegion;
-
- uint32_t discardMode = DISCARD_OPAQUE;
- float discardOpacity = 0.f;
-
- PHLLSREF currentLS;
- PHLWINDOWREF currentWindow;
+ 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)
+
+ 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;
+
+ Vector2D primarySurfaceUVTopLeft = Vector2D(-1, -1);
+ Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1);
+
+ CBox clipBox = {}; // scaled coordinates
+ CRegion clipRegion;
+
+ uint32_t discardMode = DISCARD_OPAQUE;
+ float discardOpacity = 0.f;
+
+ PHLLSREF currentLS;
+ PHLWINDOWREF currentWindow;
+ WP<CWLSurfaceResource> surface;
};
class CEGLSync {
diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp
index 3175b915..88de1ada 100644
--- a/src/render/Renderer.cpp
+++ b/src/render/Renderer.cpp
@@ -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);
diff --git a/src/render/Shader.hpp b/src/render/Shader.hpp
index 666ee9d4..bcc25268 100644
--- a/src/render/Shader.hpp
+++ b/src/render/Shader.hpp
@@ -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;
diff --git a/src/render/pass/SurfacePassElement.cpp b/src/render/pass/SurfacePassElement.cpp
index a5d3d7c0..46c6d221 100644
--- a/src/render/pass/SurfacePassElement.cpp
+++ b/src/render/pass/SurfacePassElement.cpp
@@ -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();
}};
diff --git a/src/render/shaders/CM.frag b/src/render/shaders/CM.frag
new file mode 100644
index 00000000..dd695d44
--- /dev/null
+++ b/src/render/shaders/CM.frag
@@ -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;
+}
+)#"
diff --git a/src/render/shaders/Textures.hpp b/src/render/shaders/Textures.hpp
index 78845322..002c3459 100644
--- a/src/render/shaders/Textures.hpp
+++ b/src/render/shaders/Textures.hpp
@@ -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