From 8c97cb7858e5d6c35d1a055930904346fb4248db Mon Sep 17 00:00:00 2001 From: UjinT34 <41110182+UjinT34@users.noreply.github.com> Date: Fri, 14 Mar 2025 02:15:18 +0300 Subject: renderer: add simple color management (#9506) Adds proper color management and transformations for CM surfaces. --- src/render/OpenGL.cpp | 98 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 3 deletions(-) (limited to 'src/render/OpenGL.cpp') 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 #include using namespace Hyprutils::OS; +using namespace NColorManagement; const std::vector 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 tex, const CB CBox newBox = box; m_RenderData.renderModif.applyToBox(newBox); - static auto PDT = CConfigValue("debug:damage_tracking"); + static auto PDT = CConfigValue("debug:damage_tracking"); + static auto PPASS = CConfigValue("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 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 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 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); -- cgit v1.2.3