hdr: scRGB, HLG and SDR -> HDR fixes (#11499)

This commit is contained in:
UjinT34 2025-08-22 13:13:55 +03:00 committed by GitHub
parent fdf1612f0f
commit 4e8875b5e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 67 additions and 38 deletions

View file

@ -1470,7 +1470,13 @@ void CHyprOpenGLImpl::renderTexture(SP<CTexture> tex, const CBox& box, STextureR
static std::map<std::pair<uint32_t, uint32_t>, std::array<GLfloat, 9>> primariesConversionCache;
//
static bool isSDR2HDR(const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription) {
// might be too strict
return imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB &&
(targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ||
targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_HLG);
}
void CHyprOpenGLImpl::passCMUniforms(SShader& shader, const NColorManagement::SImageDescription& imageDescription,
const NColorManagement::SImageDescription& targetImageDescription, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance) {
shader.setUniformInt(SHADER_SOURCE_TF, imageDescription.transferFunction);
@ -1486,22 +1492,19 @@ void CHyprOpenGLImpl::passCMUniforms(SShader& shader, const NColorManagement::SI
};
shader.setUniformMatrix4x2fv(SHADER_TARGET_PRIMARIES, 1, false, glTargetPrimaries);
shader.setUniformFloat2(SHADER_SRC_TF_RANGE, imageDescription.getTFMinLuminance(sdrMinLuminance), imageDescription.getTFMaxLuminance(sdrMaxLuminance));
shader.setUniformFloat2(SHADER_DST_TF_RANGE, targetImageDescription.getTFMinLuminance(sdrMinLuminance), targetImageDescription.getTFMaxLuminance(sdrMaxLuminance));
const bool needsSDRmod = modifySDR && isSDR2HDR(imageDescription, targetImageDescription);
shader.setUniformFloat2(SHADER_SRC_TF_RANGE, imageDescription.getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1),
imageDescription.getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1));
shader.setUniformFloat2(SHADER_DST_TF_RANGE, targetImageDescription.getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1),
targetImageDescription.getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1));
const float maxLuminance = imageDescription.luminances.max > 0 ? imageDescription.luminances.max : imageDescription.luminances.reference;
shader.setUniformFloat(SHADER_MAX_LUMINANCE, maxLuminance * targetImageDescription.luminances.reference / imageDescription.luminances.reference);
shader.setUniformFloat(SHADER_DST_MAX_LUMINANCE, targetImageDescription.luminances.max > 0 ? targetImageDescription.luminances.max : 10000);
shader.setUniformFloat(SHADER_DST_REF_LUMINANCE, targetImageDescription.luminances.reference);
shader.setUniformFloat(SHADER_SDR_SATURATION,
modifySDR && m_renderData.pMonitor->m_sdrSaturation > 0 && targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ?
m_renderData.pMonitor->m_sdrSaturation :
1.0f);
shader.setUniformFloat(SHADER_SDR_BRIGHTNESS,
modifySDR && m_renderData.pMonitor->m_sdrBrightness > 0 && targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ?
m_renderData.pMonitor->m_sdrBrightness :
1.0f);
shader.setUniformFloat(SHADER_SDR_SATURATION, needsSDRmod && m_renderData.pMonitor->m_sdrSaturation > 0 ? m_renderData.pMonitor->m_sdrSaturation : 1.0f);
shader.setUniformFloat(SHADER_SDR_BRIGHTNESS, needsSDRmod && m_renderData.pMonitor->m_sdrBrightness > 0 ? m_renderData.pMonitor->m_sdrBrightness : 1.0f);
const auto cacheKey = std::make_pair(imageDescription.getId(), targetImageDescription.getId());
if (!primariesConversionCache.contains(cacheKey)) {
const auto mat = imageDescription.getPrimaries().convertMatrix(targetImageDescription.getPrimaries()).mat();
@ -1594,14 +1597,16 @@ void CHyprOpenGLImpl::renderTextureInternal(SP<CTexture> tex, const CBox& box, c
tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
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 canPassHDRSurface = m_renderData.surface.valid() && m_renderData.surface->m_colorManagement.valid() ?
m_renderData.surface->m_colorManagement->isHDR() && !m_renderData.surface->m_colorManagement->isWindowsScRGB() :
false; // windows scRGB requires CM shader
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 && !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 &&
|| ((*PPASS == 1 || (*PPASS == 2 && canPassHDRSurface)) && 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 */;
if (!skipCM && !usingFinalShader && (texType == TEXTURE_RGBA || texType == TEXTURE_RGBX))

View file

@ -1460,6 +1460,9 @@ static hdr_output_metadata createHDRMetadata(SImageDescription settings, S
switch (settings.transferFunction) {
case CM_TRANSFER_FUNCTION_SRGB: eotf = 0; break; // used to send primaries and luminances to AQ. ignored for now
case CM_TRANSFER_FUNCTION_ST2084_PQ: eotf = 2; break;
case CM_TRANSFER_FUNCTION_EXT_LINEAR:
eotf = 2;
break; // should be Windows scRGB
// case CM_TRANSFER_FUNCTION_HLG: eotf = 3; break; TODO check display capabilities first
default: return NO_HDR_METADATA; // empty metadata for SDR
}
@ -1501,7 +1504,6 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
static auto PAUTOHDR = CConfigValue<Hyprlang::INT>("render:cm_auto_hdr");
const bool configuredHDR = (pMonitor->m_cmType == CM_HDR_EDID || pMonitor->m_cmType == CM_HDR);
const bool hdsIsActive = pMonitor->m_output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2;
bool wantHDR = configuredHDR;
if (pMonitor->supportsHDR()) {
@ -1524,7 +1526,7 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
// we have a surface with image description
if (SURF && SURF->m_colorManagement.valid() && SURF->m_colorManagement->hasImageDescription()) {
const bool surfaceIsHDR = SURF->m_colorManagement->imageDescription().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ;
const bool surfaceIsHDR = SURF->m_colorManagement->isHDR();
if (*PPASS == 1 || (*PPASS == 2 && surfaceIsHDR)) {
// passthrough
bool needsHdrMetadataUpdate = SURF->m_colorManagement->needsHdrMetadataUpdate() || pMonitor->m_previousFSWindow != WINDOW;
@ -1541,8 +1543,8 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
}
if (!hdrIsHandled) {
if (hdsIsActive != wantHDR) {
if (*PAUTOHDR && !(hdsIsActive && configuredHDR)) {
if (pMonitor->inHDR() != wantHDR) {
if (*PAUTOHDR && !(pMonitor->inHDR() && configuredHDR)) {
// modify or restore monitor image description for auto-hdr
// FIXME ok for now, will need some other logic if monitor image description can be modified some other way
pMonitor->applyCMType(wantHDR ? (*PAUTOHDR == 2 ? CM_HDR_EDID : CM_HDR) : pMonitor->m_cmType);
@ -1553,7 +1555,7 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
}
}
const bool needsWCG = pMonitor->m_output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2 || pMonitor->m_imageDescription.primariesNamed == CM_PRIMARIES_BT2020;
const bool needsWCG = pMonitor->wantsWideColor();
if (pMonitor->m_output->state->state().wideColorGamut != needsWCG) {
Debug::log(TRACE, "Setting wide color gamut {}", needsWCG ? "on" : "off");
pMonitor->m_output->state->setWideColorGamut(needsWCG);

View file

@ -77,8 +77,6 @@ void CSurfacePassElement::draw(const CRegion& damage) {
DELTALESSTHAN(windowBox.height, m_data.surface->m_current.bufferSize.y, 3) /* off by one-or-two */ &&
(!m_data.pWindow || (!m_data.pWindow->m_realSize->isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */;
if (m_data.surface->m_colorManagement.valid())
Debug::log(TRACE, "FIXME: rendering surface with color management enabled, should apply necessary transformations");
g_pHyprRenderer->calculateUVForSurface(m_data.pWindow, m_data.surface, m_data.pMonitor->m_self.lock(), m_data.mainSurface, windowBox.size(), PROJSIZEUNSCALED, MISALIGNEDFSV1);
auto cancelRender = false;