From 9607e3b5a88f22017af64ab1ba360a39169a4bf7 Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Thu, 31 Jul 2025 18:07:59 +0200 Subject: [PATCH] screencopy: un-hdr screencopy buffers for cm-unaware clients (#11294) --- src/protocols/ColorManagement.cpp | 8 ++++++++ src/protocols/ColorManagement.hpp | 5 ++++- src/protocols/Screencopy.cpp | 19 +++++++++++++++---- src/protocols/Screencopy.hpp | 1 + src/render/OpenGL.cpp | 14 +++++++++----- src/render/OpenGL.hpp | 2 ++ 6 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/protocols/ColorManagement.cpp b/src/protocols/ColorManagement.cpp index 215ab5ac..ce00a96b 100644 --- a/src/protocols/ColorManagement.cpp +++ b/src/protocols/ColorManagement.cpp @@ -204,6 +204,10 @@ bool CColorManager::good() { return m_resource->resource(); } +wl_client* CColorManager::client() { + return m_resource->client(); +} + CColorManagementOutput::CColorManagementOutput(SP resource, WP monitor) : m_resource(resource), m_monitor(monitor) { if UNLIKELY (!good()) return; @@ -818,6 +822,10 @@ void CColorManagementProtocol::onMonitorImageDescriptionChanged(WP mon feedback->onPreferredChanged(); } +bool CColorManagementProtocol::isClientCMAware(wl_client* client) { + return std::ranges::any_of(m_managers, [client](const auto& m) { return m->client() == client; }); +} + void CColorManagementProtocol::destroyResource(CColorManager* resource) { std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; }); } diff --git a/src/protocols/ColorManagement.hpp b/src/protocols/ColorManagement.hpp index 1d2795ed..06349927 100644 --- a/src/protocols/ColorManagement.hpp +++ b/src/protocols/ColorManagement.hpp @@ -18,7 +18,8 @@ class CColorManager { public: CColorManager(SP resource); - bool good(); + bool good(); + wl_client* client(); private: SP m_resource; @@ -187,6 +188,8 @@ class CColorManagementProtocol : public IWaylandProtocol { void onImagePreferredChanged(uint32_t preferredId); void onMonitorImageDescriptionChanged(WP monitor); + bool isClientCMAware(wl_client* client); + private: void destroyResource(CColorManager* resource); void destroyResource(CColorManagementOutput* resource); diff --git a/src/protocols/Screencopy.cpp b/src/protocols/Screencopy.cpp index ddbb371a..a9e0e5b3 100644 --- a/src/protocols/Screencopy.cpp +++ b/src/protocols/Screencopy.cpp @@ -11,6 +11,7 @@ #include "core/Output.hpp" #include "types/WLBuffer.hpp" #include "types/Buffer.hpp" +#include "ColorManagement.hpp" #include "../helpers/Format.hpp" #include "../helpers/time/Time.hpp" #include "XDGShell.hpp" @@ -191,16 +192,22 @@ void CScreencopyFrame::share() { } void CScreencopyFrame::renderMon() { - auto TEXTURE = makeShared(m_monitor->m_output->state->state().buffer); + auto TEXTURE = makeShared(m_monitor->m_output->state->state().buffer); - CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX}; + CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX}; - CBox monbox = CBox{0, 0, m_monitor->m_pixelSize.x, m_monitor->m_pixelSize.y} + const bool IS_CM_AWARE = PROTO::colorManagement->isClientCMAware(m_client->client()); + + CBox monbox = CBox{0, 0, m_monitor->m_pixelSize.x, m_monitor->m_pixelSize.y} .translate({-m_box.x, -m_box.y}) // vvvv kinda ass-backwards but that's how I designed the renderer... sigh. .transform(wlTransformToHyprutils(invertTransform(m_monitor->m_transform)), m_monitor->m_pixelSize.x, m_monitor->m_pixelSize.y); g_pHyprOpenGL->pushMonitorTransformEnabled(true); g_pHyprOpenGL->setRenderModifEnabled(false); - g_pHyprOpenGL->renderTexture(TEXTURE, monbox, {}); + g_pHyprOpenGL->renderTexture(TEXTURE, monbox, + { + .cmBackToSRGB = !IS_CM_AWARE, + .cmBackToSRGBSource = !IS_CM_AWARE ? m_monitor.lock() : nullptr, + }); g_pHyprOpenGL->setRenderModifEnabled(true); g_pHyprOpenGL->popMonitorTransformEnabled(); @@ -453,6 +460,10 @@ bool CScreencopyClient::good() { return m_resource->resource(); } +wl_client* CScreencopyClient::client() { + return m_resource ? m_resource->client() : nullptr; +} + CScreencopyProtocol::CScreencopyProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { ; } diff --git a/src/protocols/Screencopy.hpp b/src/protocols/Screencopy.hpp index 665fede1..a44628fd 100644 --- a/src/protocols/Screencopy.hpp +++ b/src/protocols/Screencopy.hpp @@ -28,6 +28,7 @@ class CScreencopyClient { ~CScreencopyClient(); bool good(); + wl_client* client(); WP m_self; eClientOwners m_clientOwner = CLIENT_SCREENCOPY; diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 5470951e..420e997e 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -1593,11 +1593,12 @@ void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, c tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); } - const auto imageDescription = - m_renderData.surface.valid() && m_renderData.surface->m_colorManagement.valid() ? m_renderData.surface->m_colorManagement->imageDescription() : SImageDescription{}; + const auto imageDescription = m_renderData.surface.valid() && m_renderData.surface->m_colorManagement.valid() ? + m_renderData.surface->m_colorManagement->imageDescription() : + (data.cmBackToSRGB ? data.cmBackToSRGBSource->m_imageDescription : SImageDescription{}); - const bool skipCM = !*PENABLECM || !m_cmSupported /* CM unsupported or disabled */ - || (imageDescription == m_renderData.pMonitor->m_imageDescription) /* Source and target have the same image description */ + const bool skipCM = !*PENABLECM || !m_cmSupported /* CM unsupported or disabled */ + || (imageDescription == m_renderData.pMonitor->m_imageDescription && !data.cmBackToSRGB) /* Source and target have the same image description */ || ((*PPASS == 1 || (*PPASS == 2 && imageDescription.transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ)) && m_renderData.pMonitor->m_activeWorkspace && m_renderData.pMonitor->m_activeWorkspace->m_hasFullscreenWindow && m_renderData.pMonitor->m_activeWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN) /* Fullscreen window with pass cm enabled */; @@ -1609,7 +1610,10 @@ void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, c if (shader == &m_shaders->m_shCM) { shader->setUniformInt(SHADER_TEX_TYPE, texType); - passCMUniforms(*shader, imageDescription); + if (data.cmBackToSRGB) + passCMUniforms(*shader, imageDescription, NColorManagement::SImageDescription{}, true, -1, -1); + else + passCMUniforms(*shader, imageDescription); } shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 40031ff1..5b3189e9 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -200,6 +200,8 @@ class CHyprOpenGLImpl { bool noAA = false; bool blockBlurOptimization = false; GLenum wrapX = GL_CLAMP_TO_EDGE, wrapY = GL_CLAMP_TO_EDGE; + bool cmBackToSRGB = false; + SP cmBackToSRGBSource; }; struct SBorderRenderData {