mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-09-05 19:08:18 +00:00
hdr: scRGB, HLG and SDR -> HDR fixes (#11499)
This commit is contained in:
@@ -42,6 +42,7 @@ using namespace Hyprutils::String;
|
||||
using namespace Hyprutils::Utils;
|
||||
using namespace Hyprutils::OS;
|
||||
using enum NContentType::eContentType;
|
||||
using namespace NColorManagement;
|
||||
|
||||
CMonitor::CMonitor(SP<Aquamarine::IOutput> output_) : m_state(this), m_output(output_) {
|
||||
g_pAnimationManager->createAnimation(0.f, m_specialFade, g_pConfigManager->getAnimationPropertyConfig("specialWorkspaceIn"), AVARDAMAGE_NONE);
|
||||
@@ -1686,6 +1687,18 @@ int CMonitor::maxAvgLuminance(int defaultValue) {
|
||||
(m_output->parsedEDID.hdrMetadata.has_value() ? m_output->parsedEDID.hdrMetadata->desiredMaxFrameAverageLuminance : defaultValue);
|
||||
}
|
||||
|
||||
bool CMonitor::wantsWideColor() {
|
||||
return supportsWideColor() && (wantsHDR() || m_imageDescription.primariesNamed == CM_PRIMARIES_BT2020);
|
||||
}
|
||||
|
||||
bool CMonitor::wantsHDR() {
|
||||
return supportsHDR() && inHDR();
|
||||
}
|
||||
|
||||
bool CMonitor::inHDR() {
|
||||
return m_output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2;
|
||||
}
|
||||
|
||||
CMonitorState::CMonitorState(CMonitor* owner) : m_owner(owner) {
|
||||
;
|
||||
}
|
||||
|
@@ -249,6 +249,11 @@ class CMonitor {
|
||||
int maxLuminance(int defaultValue = 80);
|
||||
int maxAvgLuminance(int defaultValue = 80);
|
||||
|
||||
bool wantsWideColor();
|
||||
bool wantsHDR();
|
||||
|
||||
bool inHDR();
|
||||
|
||||
bool m_enabled = false;
|
||||
bool m_renderingInitPassed = false;
|
||||
WP<CWindow> m_previousFSWindow;
|
||||
|
@@ -15,13 +15,13 @@ CColorManager::CColorManager(SP<CWpColorManagerV1> resource) : m_resource(resour
|
||||
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_PARAMETRIC);
|
||||
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_PRIMARIES);
|
||||
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_LUMINANCES);
|
||||
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_WINDOWS_SCRGB);
|
||||
|
||||
if (PROTO::colorManagement->m_debug) {
|
||||
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_ICC_V2_V4);
|
||||
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_TF_POWER);
|
||||
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES);
|
||||
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_EXTENDED_TARGET_VOLUME);
|
||||
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_WINDOWS_SCRGB);
|
||||
}
|
||||
|
||||
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_SRGB);
|
||||
@@ -170,11 +170,7 @@ CColorManager::CColorManager(SP<CWpColorManagerV1> resource) : m_resource(resour
|
||||
RESOURCE->m_self = RESOURCE;
|
||||
});
|
||||
m_resource->setCreateWindowsScrgb([](CWpColorManagerV1* r, uint32_t id) {
|
||||
LOGM(WARN, "New Windows scRGB description id={} (unsupported)", id);
|
||||
if (!PROTO::colorManagement->m_debug) {
|
||||
r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Windows scRGB profiles are not supported");
|
||||
return;
|
||||
}
|
||||
LOGM(WARN, "New Windows scRGB description id={}", id);
|
||||
|
||||
const auto RESOURCE = PROTO::colorManagement->m_imageDescriptions.emplace_back(
|
||||
makeShared<CColorManagementImageDescription>(makeShared<CWpImageDescriptionV1>(r->client(), r->version(), id), false));
|
||||
@@ -261,11 +257,11 @@ CColorManagementSurface::CColorManagementSurface(SP<CWpColorManagementSurfaceV1>
|
||||
m_client = m_resource->client();
|
||||
|
||||
m_resource->setDestroy([this](CWpColorManagementSurfaceV1* r) {
|
||||
LOGM(TRACE, "Destroy xx cm surface {}", (uintptr_t)m_surface);
|
||||
LOGM(TRACE, "Destroy wp cm surface {}", (uintptr_t)m_surface);
|
||||
PROTO::colorManagement->destroyResource(this);
|
||||
});
|
||||
m_resource->setOnDestroy([this](CWpColorManagementSurfaceV1* r) {
|
||||
LOGM(TRACE, "Destroy xx cm surface {}", (uintptr_t)m_surface);
|
||||
LOGM(TRACE, "Destroy wp cm surface {}", (uintptr_t)m_surface);
|
||||
PROTO::colorManagement->destroyResource(this);
|
||||
});
|
||||
|
||||
@@ -340,6 +336,16 @@ bool CColorManagementSurface::needsHdrMetadataUpdate() {
|
||||
return m_needsNewMetadata;
|
||||
}
|
||||
|
||||
bool CColorManagementSurface::isHDR() {
|
||||
return m_imageDescription.transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ || m_imageDescription.transferFunction == CM_TRANSFER_FUNCTION_HLG || isWindowsScRGB();
|
||||
}
|
||||
|
||||
bool CColorManagementSurface::isWindowsScRGB() {
|
||||
return m_imageDescription.windowsScRGB ||
|
||||
// autodetect scRGB, might be incorrect
|
||||
(m_imageDescription.primariesNamed == CM_PRIMARIES_SRGB && m_imageDescription.transferFunction == CM_TRANSFER_FUNCTION_EXT_LINEAR);
|
||||
}
|
||||
|
||||
CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SP<CWpColorManagementSurfaceFeedbackV1> resource, SP<CWLSurfaceResource> surface_) :
|
||||
m_surface(surface_), m_resource(resource) {
|
||||
if UNLIKELY (!good())
|
||||
@@ -348,13 +354,13 @@ CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SP<CWpColorMana
|
||||
m_client = m_resource->client();
|
||||
|
||||
m_resource->setDestroy([this](CWpColorManagementSurfaceFeedbackV1* r) {
|
||||
LOGM(TRACE, "Destroy xx cm feedback surface {}", (uintptr_t)m_surface);
|
||||
LOGM(TRACE, "Destroy wp cm feedback surface {}", (uintptr_t)m_surface);
|
||||
if (m_currentPreferred.valid())
|
||||
PROTO::colorManagement->destroyResource(m_currentPreferred.get());
|
||||
PROTO::colorManagement->destroyResource(this);
|
||||
});
|
||||
m_resource->setOnDestroy([this](CWpColorManagementSurfaceFeedbackV1* r) {
|
||||
LOGM(TRACE, "Destroy xx cm feedback surface {}", (uintptr_t)m_surface);
|
||||
LOGM(TRACE, "Destroy wp cm feedback surface {}", (uintptr_t)m_surface);
|
||||
if (m_currentPreferred.valid())
|
||||
PROTO::colorManagement->destroyResource(m_currentPreferred.get());
|
||||
PROTO::colorManagement->destroyResource(this);
|
||||
@@ -616,10 +622,6 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SP<CWpImage
|
||||
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Primaries already set");
|
||||
return;
|
||||
}
|
||||
if (!PROTO::colorManagement->m_debug) {
|
||||
r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Custom primaries aren't supported");
|
||||
return;
|
||||
}
|
||||
m_settings.primariesNameSet = false;
|
||||
m_settings.primaries = SPCPRimaries{.red = {.x = r_x / 1000000.0f, .y = r_y / 1000000.0f},
|
||||
.green = {.x = g_x / 1000000.0f, .y = g_y / 1000000.0f},
|
||||
|
@@ -61,6 +61,8 @@ class CColorManagementSurface {
|
||||
const hdr_output_metadata& hdrMetadata();
|
||||
void setHDRMetadata(const hdr_output_metadata& metadata);
|
||||
bool needsHdrMetadataUpdate();
|
||||
bool isHDR();
|
||||
bool isWindowsScRGB();
|
||||
|
||||
private:
|
||||
SP<CWpColorManagementSurfaceV1> m_resource;
|
||||
|
@@ -203,6 +203,8 @@ namespace NColorManagement {
|
||||
|
||||
float getTFMaxLuminance(int sdrMaxLuminance = -1) const {
|
||||
switch (transferFunction) {
|
||||
case CM_TRANSFER_FUNCTION_EXT_LINEAR:
|
||||
return SDR_MAX_LUMINANCE; // assume Windows scRGB. white color range 1.0 - 125.0 maps to SDR_MAX_LUMINANCE (80) - HDR_MAX_LUMINANCE (10000)
|
||||
case CM_TRANSFER_FUNCTION_ST2084_PQ: return HDR_MAX_LUMINANCE;
|
||||
case CM_TRANSFER_FUNCTION_HLG: return HLG_MAX_LUMINANCE;
|
||||
case CM_TRANSFER_FUNCTION_GAMMA22:
|
||||
|
@@ -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))
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user