From 19175706eb9d55b9b363341312c3cf8a3096458b Mon Sep 17 00:00:00 2001 From: Vaxry Date: Fri, 24 Apr 2026 19:48:46 +0100 Subject: [PATCH] use a better work space, not linear, only for sdr --- src/helpers/Monitor.cpp | 22 +++++++++++++++- src/helpers/Monitor.hpp | 9 ++++--- src/helpers/MonitorResources.cpp | 18 ++++++------- src/managers/screenshare/ScreenshareFrame.cpp | 9 +++---- src/render/OpenGL.cpp | 16 +++--------- src/render/OpenGL.hpp | 1 - src/render/Renderer.cpp | 9 +------ src/render/ShaderLoader.cpp | 13 +++------- src/render/ShaderLoader.hpp | 21 ++++++++-------- src/render/gl/GLElementRenderer.cpp | 25 +++++++++---------- src/render/pass/TexPassElement.hpp | 1 - src/render/shaders/glsl/cm_helpers.glsl | 10 -------- src/render/shaders/glsl/defines.h | 21 ++++++++-------- 13 files changed, 77 insertions(+), 98 deletions(-) diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index a5195a94f..528cb84e2 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -2549,9 +2549,29 @@ bool CMonitor::useFP16() { return shouldUse; } +PImageDescription CMonitor::workBufferImageDescription() { + if (!useFP16()) + return m_imageDescription; + + const auto& value = m_imageDescription->value(); + + const bool isHDRLikeTF = + value.transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ || value.transferFunction == CM_TRANSFER_FUNCTION_HLG || value.transferFunction == CM_TRANSFER_FUNCTION_EXT_LINEAR; + + if (isHDRLikeTF || value.windowsScRGB) + return LINEAR_IMAGE_DESCRIPTION; + + return CImageDescription::from(SImageDescription{ + .transferFunction = chooseTF(m_sdrEotf), + .primariesNameSet = true, + .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, + .primaries = NColorPrimaries::BT2020, + }); +} + WP CMonitor::resources() { const auto DRM_FORMAT = useFP16() ? DRM_FORMAT_ABGR16161616F : m_output->state->state().drmFormat; - const auto DESC = useFP16() ? LINEAR_IMAGE_DESCRIPTION : m_imageDescription; + const auto DESC = workBufferImageDescription(); if (!m_resources || m_resources->m_drmFormat != DRM_FORMAT || m_resources->m_size != m_pixelSize) m_resources = makeUnique(m_self, DRM_FORMAT, m_pixelSize, DESC); diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 59a64237e..b4882fe66 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -393,10 +393,11 @@ class CMonitor { return m_position == rhs.m_position && m_size == rhs.m_size && m_name == rhs.m_name; } - bool needsACopyFB(); - bool needsUnmodifiedCopy(); - bool useFP16(); - WP resources(); + bool needsACopyFB(); + bool needsUnmodifiedCopy(); + bool useFP16(); + NColorManagement::PImageDescription workBufferImageDescription(); + WP resources(); private: void updateMatrix(); diff --git a/src/helpers/MonitorResources.cpp b/src/helpers/MonitorResources.cpp index 8e7723c23..f54aa9dc4 100644 --- a/src/helpers/MonitorResources.cpp +++ b/src/helpers/MonitorResources.cpp @@ -32,7 +32,7 @@ void CMonitorResources::setImageDescription(NColorManagement::PImageDescription for (const auto& res : m_workBuffers) res.buffer->setImageDescription(imageDescription); if (m_monitorMirrorFB) - m_monitorMirrorFB->setImageDescription(NColorManagement::getDefaultImageDescription()); + m_monitorMirrorFB->setImageDescription(getMirrorTexImageDescription()); if (m_mirrorTex) m_mirrorTex->m_imageDescription = getMirrorTexImageDescription(); } @@ -78,8 +78,8 @@ SP CMonitorResources::mirrorFB() { m_monitorMirrorFB = g_pHyprRenderer->createFB(std::format("Monitor {} mirror FB", m_monitor->m_name)); if (!m_monitorMirrorFB->isAllocated()) { - m_monitorMirrorFB->alloc(m_size.x, m_size.y, DRM_FORMAT_XRGB8888); - m_monitorMirrorFB->setImageDescription(NColorManagement::getDefaultImageDescription()); + m_monitorMirrorFB->alloc(m_size.x, m_size.y, m_monitor->m_activeMonitorRule.m_enable10bit ? DRM_FORMAT_XRGB2101010 : DRM_FORMAT_XRGB8888); + m_monitorMirrorFB->setImageDescription(getMirrorTexImageDescription()); } return m_monitorMirrorFB; @@ -90,20 +90,16 @@ SP CMonitorResources::getMirrorTexture() { } NColorManagement::PImageDescription CMonitorResources::getMirrorTexImageDescription() { - return CImageDescription::from(SImageDescription{ - .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_SRGB, - .primariesNameSet = m_imageDescription->value().primariesNameSet, - .primariesNamed = m_imageDescription->value().primariesNamed, - .primaries = m_imageDescription->value().primaries, - .luminances = {.min = SDR_MIN_LUMINANCE, .max = 80, .reference = 80}, - }); + if (!m_monitor) + return DEFAULT_SRGB_IMAGE_DESCRIPTION; + return m_monitor->workBufferImageDescription(); } void CMonitorResources::enableMirror() { if (m_mirrorTex) return; m_mirrorTex = g_pHyprRenderer->createTexture(); - m_mirrorTex->allocate({m_size.x, m_size.y}, DRM_FORMAT_XRGB8888); + m_mirrorTex->allocate({m_size.x, m_size.y}, m_monitor->m_activeMonitorRule.m_enable10bit ? DRM_FORMAT_XRGB2101010 : DRM_FORMAT_XRGB8888); m_mirrorTex->m_imageDescription = getMirrorTexImageDescription(); m_monitor->m_blurFBDirty = true; } diff --git a/src/managers/screenshare/ScreenshareFrame.cpp b/src/managers/screenshare/ScreenshareFrame.cpp index 7a596d0c6..4cc51335e 100644 --- a/src/managers/screenshare/ScreenshareFrame.cpp +++ b/src/managers/screenshare/ScreenshareFrame.cpp @@ -196,11 +196,10 @@ void CScreenshareFrame::renderMonitor() { g_pHyprRenderer->startRenderPass(); g_pHyprRenderer->draw( CTexPassElement::SRenderData{ - .tex = TEXTURE, - .box = monbox, - .flipEndFrame = true, - .cmBackToSRGB = !IS_CM_AWARE, - .cmBackToSRGBSource = !IS_CM_AWARE ? PMONITOR : nullptr, + .tex = TEXTURE, + .box = monbox, + .flipEndFrame = true, + .cmBackToSRGB = !IS_CM_AWARE, }, {0, 0, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y}); g_pHyprRenderer->m_renderData.renderModif.enabled = OLD; diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 64aa4eb03..76f38ce04 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -1298,7 +1298,7 @@ WP CHyprOpenGLImpl::renderToFBInternal(SP tex, const STexture shaderFeatures &= ~SH_FEAT_RGBA; const auto surface = g_pHyprRenderer->m_renderData.surface; - const auto WORK_BUFFER_IMAGE_DESCRIPTION = g_pHyprRenderer->workBufferImageDescription(); + const auto WORK_BUFFER_IMAGE_DESCRIPTION = g_pHyprRenderer->m_renderData.pMonitor->workBufferImageDescription(); // chosenSdrEotf contains the valid eotf for this display @@ -1311,7 +1311,7 @@ WP CHyprOpenGLImpl::renderToFBInternal(SP tex, const STexture return CImageDescription::from(surface->m_colorManagement->imageDescription()); if (data.cmBackToSRGB) - return g_pHyprRenderer->m_renderData.pMonitor->m_imageDescription; + return tex->m_imageDescription ? tex->m_imageDescription : WORK_BUFFER_IMAGE_DESCRIPTION; // otherwise, if we are CM'ing back into source, use chosen, because that's what our work buffer is in // the same applies to the final monitor CM @@ -1349,14 +1349,6 @@ WP CHyprOpenGLImpl::renderToFBInternal(SP tex, const STexture || !SOURCE_IMAGE_DESCRIPTION->needsCM(TARGET_IMAGE_DESCRIPTION) /* Source and target have matching image descriptions */ ; - const auto sourceTF = SOURCE_IMAGE_DESCRIPTION->value().transferFunction; - const bool sourceIsUnmanagedSDRSurface = surface.valid() && !surface->m_colorManagement.valid() && - (sourceTF == CM_TRANSFER_FUNCTION_SRGB || sourceTF == CM_TRANSFER_FUNCTION_GAMMA22 || sourceTF == CM_TRANSFER_FUNCTION_EXT_SRGB || sourceTF == CM_TRANSFER_FUNCTION_BT1886); - const bool targetIsLinearWorkBuffer = TARGET_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_EXT_LINEAR; - - if (!skipCM && !data.finalMonitorCM && !data.cmBackToSRGB && sourceIsUnmanagedSDRSurface && targetIsLinearWorkBuffer) - shaderFeatures |= SH_FEAT_SDR_PREMUL_COMPAT; - if (g_pHyprRenderer->m_renderData.pMonitor->needsACopyFB()) Log::logger->log(Log::TRACE, "CM: render to FB skip={} {} -> {}", skipCM, SOURCE_IMAGE_DESCRIPTION->value(), TARGET_IMAGE_DESCRIPTION->value()); @@ -2395,7 +2387,8 @@ void CHyprOpenGLImpl::saveBufferForMirror(const CBox& box) { Log::logger->log(Log::ERR, "Invalid source texture for mirror"); return; } - auto guard = g_pHyprRenderer->bindTempFB(g_pHyprRenderer->m_renderData.pMonitor->resources()->mirrorFB()); + auto fb = g_pHyprRenderer->m_renderData.pMonitor->resources()->mirrorFB(); + auto guard = g_pHyprRenderer->bindTempFB(fb); Log::logger->log(Log::TRACE, "CM: saveBufferForMirror {} -> {}", TEX->m_imageDescription->value(), g_pHyprRenderer->m_renderData.currentFB->imageDescription()->value()); @@ -2408,7 +2401,6 @@ void CHyprOpenGLImpl::saveBufferForMirror(const CBox& box) { .round = 0, .discardActive = false, .allowCustomUV = false, - .cmBackToSRGB = true, }); blend(true); diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 82b8f0548..2434c0edc 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -174,7 +174,6 @@ namespace Render::GL { GLenum wrapX = GL_CLAMP_TO_EDGE, wrapY = GL_CLAMP_TO_EDGE; bool cmBackToSRGB = false; bool finalMonitorCM = false; - SP cmBackToSRGBSource; uint32_t discardMode = DISCARD_OPAQUE; float discardOpacity = 0.f; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 4bd4ed6db..20282315b 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -3270,17 +3270,10 @@ void IHyprRenderer::renderSnapshot(WP popup) { } NColorManagement::PImageDescription IHyprRenderer::workBufferImageDescription() { - // TODO - // const bool IS_MONITOR_ICC = m_renderData.pMonitor->m_imageDescription.valid() && m_renderData.pMonitor->m_imageDescription->value().icc.present; - // const auto sdrEOTF = NTransferFunction::fromConfig(IS_MONITOR_ICC); - // const auto CHOSEN_SDR_EOTF = sdrEOTF != NTransferFunction::TF_SRGB ? NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 : NColorManagement::CM_TRANSFER_FUNCTION_SRGB; - if (!m_renderData.pMonitor) return LINEAR_IMAGE_DESCRIPTION; - return m_renderData.pMonitor->useFP16() ? - LINEAR_IMAGE_DESCRIPTION : - m_renderData.pMonitor->m_imageDescription; //CImageDescription::from(NColorManagement::SImageDescription{.transferFunction = CHOSEN_SDR_EOTF}); + return m_renderData.pMonitor->workBufferImageDescription(); } bool IHyprRenderer::shouldBlur(PHLLS ls) { diff --git a/src/render/ShaderLoader.cpp b/src/render/ShaderLoader.cpp index 8f711abde..50ab61d04 100644 --- a/src/render/ShaderLoader.cpp +++ b/src/render/ShaderLoader.cpp @@ -67,17 +67,10 @@ void CShaderLoader::include(const std::string& filename) { std::string CShaderLoader::getDefines(ShaderFeatureFlags features) { std::string res = ""; std::map defines = { - {"USE_RGBA", features & SH_FEAT_RGBA ? "1" : "0"}, - {"USE_DISCARD", features & SH_FEAT_DISCARD ? "1" : "0"}, - {"USE_TINT", features & SH_FEAT_TINT ? "1" : "0"}, - {"USE_ROUNDING", features & SH_FEAT_ROUNDING ? "1" : "0"}, - {"USE_CM", features & SH_FEAT_CM ? "1" : "0"}, - {"USE_TONEMAP", features & SH_FEAT_TONEMAP ? "1" : "0"}, - {"USE_SDR_MOD", features & SH_FEAT_SDR_MOD ? "1" : "0"}, - {"USE_BLUR", features & SH_FEAT_BLUR ? "1" : "0"}, - {"USE_ICC", features & SH_FEAT_ICC ? "1" : "0"}, + {"USE_RGBA", features & SH_FEAT_RGBA ? "1" : "0"}, {"USE_DISCARD", features & SH_FEAT_DISCARD ? "1" : "0"}, {"USE_TINT", features & SH_FEAT_TINT ? "1" : "0"}, + {"USE_ROUNDING", features & SH_FEAT_ROUNDING ? "1" : "0"}, {"USE_CM", features & SH_FEAT_CM ? "1" : "0"}, {"USE_TONEMAP", features & SH_FEAT_TONEMAP ? "1" : "0"}, + {"USE_SDR_MOD", features & SH_FEAT_SDR_MOD ? "1" : "0"}, {"USE_BLUR", features & SH_FEAT_BLUR ? "1" : "0"}, {"USE_ICC", features & SH_FEAT_ICC ? "1" : "0"}, {"USE_MIRROR", features & SH_FEAT_MIRROR ? "1" : "0"}, - {"USE_SDR_PREMUL_COMPAT", features & SH_FEAT_SDR_PREMUL_COMPAT ? "1" : "0"}, }; for (const auto& [name, value] : defines) { res += std::format("#define {} {}\n", name, value); diff --git a/src/render/ShaderLoader.hpp b/src/render/ShaderLoader.hpp index faf053ba2..58184659c 100644 --- a/src/render/ShaderLoader.hpp +++ b/src/render/ShaderLoader.hpp @@ -11,17 +11,16 @@ namespace Render { enum ePreparedFragmentShaderFeature : uint16_t { SH_FEAT_UNKNOWN = 0, // all features just in case - SH_FEAT_RGBA = (1 << 0), // RGBA/RGBX texture sampling - SH_FEAT_DISCARD = (1 << 1), // RGBA/RGBX texture sampling - SH_FEAT_TINT = (1 << 2), // uniforms: tint; condition: applyTint - SH_FEAT_ROUNDING = (1 << 3), // uniforms: radius, roundingPower, topLeft, fullSize; condition: radius > 0 - SH_FEAT_CM = (1 << 4), // uniforms: srcTFRange, dstTFRange, srcRefLuminance, convertMatrix; condition: !skipCM - SH_FEAT_TONEMAP = (1 << 5), // uniforms: maxLuminance, dstMaxLuminance, dstRefLuminance; condition: maxLuminance < dstMaxLuminance * 1.01 - SH_FEAT_SDR_MOD = (1 << 6), // uniforms: sdrSaturation, sdrBrightnessMultiplier; condition: SDR <-> HDR && (sdrSaturation != 1 || sdrBrightnessMultiplier != 1) - SH_FEAT_BLUR = (1 << 7), // condition: render:use_shader_blur_blend - SH_FEAT_ICC = (1 << 8), // - SH_FEAT_MIRROR = (1 << 9), // condition: mirror or screenshare - SH_FEAT_SDR_PREMUL_COMPAT = (1 << 10), // condition: unmanaged SDR surfaces drawn into linear workbuffer + SH_FEAT_RGBA = (1 << 0), // RGBA/RGBX texture sampling + SH_FEAT_DISCARD = (1 << 1), // RGBA/RGBX texture sampling + SH_FEAT_TINT = (1 << 2), // uniforms: tint; condition: applyTint + SH_FEAT_ROUNDING = (1 << 3), // uniforms: radius, roundingPower, topLeft, fullSize; condition: radius > 0 + SH_FEAT_CM = (1 << 4), // uniforms: srcTFRange, dstTFRange, srcRefLuminance, convertMatrix; condition: !skipCM + SH_FEAT_TONEMAP = (1 << 5), // uniforms: maxLuminance, dstMaxLuminance, dstRefLuminance; condition: maxLuminance < dstMaxLuminance * 1.01 + SH_FEAT_SDR_MOD = (1 << 6), // uniforms: sdrSaturation, sdrBrightnessMultiplier; condition: SDR <-> HDR && (sdrSaturation != 1 || sdrBrightnessMultiplier != 1) + SH_FEAT_BLUR = (1 << 7), // condition: render:use_shader_blur_blend + SH_FEAT_ICC = (1 << 8), // + SH_FEAT_MIRROR = (1 << 9), // condition: mirror or screenshare // uniforms: targetPrimariesXYZ; condition: SH_FEAT_TONEMAP || SH_FEAT_SDR_MOD }; diff --git a/src/render/gl/GLElementRenderer.cpp b/src/render/gl/GLElementRenderer.cpp index dbf62cb8f..c20fffc08 100644 --- a/src/render/gl/GLElementRenderer.cpp +++ b/src/render/gl/GLElementRenderer.cpp @@ -114,19 +114,18 @@ void CGLElementRenderer::draw(WP element, const CRegion& damage .blurredBG = m_data.blurredBG, // common settings - .damage = m_data.damage.empty() ? &damage : &m_data.damage, - .surface = m_data.surface, - .a = m_data.a, - .round = m_data.round, - .roundingPower = m_data.roundingPower, - .discardActive = m_data.discardActive, - .allowCustomUV = m_data.allowCustomUV, - .cmBackToSRGB = m_data.cmBackToSRGB, - .cmBackToSRGBSource = m_data.cmBackToSRGBSource, - .discardMode = m_data.ignoreAlpha.has_value() ? sc(DISCARD_ALPHA) : m_data.discardMode, - .discardOpacity = m_data.ignoreAlpha.has_value() ? *m_data.ignoreAlpha : m_data.discardOpacity, - .clipRegion = m_data.clipRegion, - .currentLS = m_data.currentLS, + .damage = m_data.damage.empty() ? &damage : &m_data.damage, + .surface = m_data.surface, + .a = m_data.a, + .round = m_data.round, + .roundingPower = m_data.roundingPower, + .discardActive = m_data.discardActive, + .allowCustomUV = m_data.allowCustomUV, + .cmBackToSRGB = m_data.cmBackToSRGB, + .discardMode = m_data.ignoreAlpha.has_value() ? sc(DISCARD_ALPHA) : m_data.discardMode, + .discardOpacity = m_data.ignoreAlpha.has_value() ? *m_data.ignoreAlpha : m_data.discardOpacity, + .clipRegion = m_data.clipRegion, + .currentLS = m_data.currentLS, .primarySurfaceUVTopLeft = g_pHyprRenderer->m_renderData.primarySurfaceUVTopLeft, .primarySurfaceUVBottomRight = g_pHyprRenderer->m_renderData.primarySurfaceUVBottomRight, diff --git a/src/render/pass/TexPassElement.hpp b/src/render/pass/TexPassElement.hpp index 6c0cfab11..413f98753 100644 --- a/src/render/pass/TexPassElement.hpp +++ b/src/render/pass/TexPassElement.hpp @@ -31,7 +31,6 @@ class CTexPassElement : public IPassElement { std::optional ignoreAlpha; std::optional blockBlurOptimization; bool cmBackToSRGB = false; - SP cmBackToSRGBSource; bool discardActive = false; bool allowCustomUV = false; diff --git a/src/render/shaders/glsl/cm_helpers.glsl b/src/render/shaders/glsl/cm_helpers.glsl index b00767dc8..0c7d3d510 100644 --- a/src/render/shaders/glsl/cm_helpers.glsl +++ b/src/render/shaders/glsl/cm_helpers.glsl @@ -224,25 +224,15 @@ vec4 #endif #endif ) { -#if USE_SDR_PREMUL_COMPAT - // Compatibility path for unmanaged SDR translucent surfaces rendered into a linear workbuffer. - // Keep premultiplication during source TF decoding to avoid low-alpha hue/opacity drift. - pixColor.rgb = toLinearRGB(pixColor.rgb, srcTF); -#else pixColor.rgb /= max(pixColor.a, 0.001); pixColor.rgb = toLinearRGB(pixColor.rgb, srcTF); -#endif #if USE_ICC pixColor.rgb = applyIcc3DLut(pixColor.rgb, iccLut3D, iccLutSize); pixColor.rgb *= pixColor.a; #else pixColor.rgb = convertMatrix * pixColor.rgb; -#if USE_SDR_PREMUL_COMPAT - pixColor.rgb = pixColor.rgb * (srcTFRange[1] - srcTFRange[0]) + srcTFRange[0] * pixColor.a; -#else pixColor = toNit(pixColor, srcTFRange); pixColor.rgb *= pixColor.a; -#endif #if USE_TONEMAP pixColor = tonemap(pixColor, dstxyz, maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance); #endif diff --git a/src/render/shaders/glsl/defines.h b/src/render/shaders/glsl/defines.h index d38eeffd9..a04e0250b 100644 --- a/src/render/shaders/glsl/defines.h +++ b/src/render/shaders/glsl/defines.h @@ -2,14 +2,13 @@ // Values here are only for highlighting and static checking // 1 assumes that a shader either supports this feature or doesn't use any code supporting it // 0 assumes that shader uses some code supporting the feature but will never have this feature enabled -#define USE_RGBA 1 -#define USE_DISCARD 1 -#define USE_TINT 1 -#define USE_ROUNDING 1 -#define USE_CM 1 -#define USE_TONEMAP 1 -#define USE_SDR_MOD 1 -#define USE_BLUR 1 -#define USE_ICC 0 -#define USE_MIRROR 0 -#define USE_SDR_PREMUL_COMPAT 1 +#define USE_RGBA 1 +#define USE_DISCARD 1 +#define USE_TINT 1 +#define USE_ROUNDING 1 +#define USE_CM 1 +#define USE_TONEMAP 1 +#define USE_SDR_MOD 1 +#define USE_BLUR 1 +#define USE_ICC 0 +#define USE_MIRROR 0