From 7353e8766fb98daecd962630d1213e3808dbdbb7 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Sun, 19 Jul 2026 20:42:45 +1000 Subject: [PATCH] [client] wayland: separate mastering metadata validation Validate mastering primaries and luminance independently so one unsupported component does not discard the other. Trust content chromaticities when extended target volumes are supported, and report the exact values and reason when metadata must be omitted. --- client/displayservers/Wayland/gl.c | 93 ++++++++++++++++-------------- 1 file changed, 51 insertions(+), 42 deletions(-) diff --git a/client/displayservers/Wayland/gl.c b/client/displayservers/Wayland/gl.c index 32c0f8ae..228a2b9d 100644 --- a/client/displayservers/Wayland/gl.c +++ b/client/displayservers/Wayland/gl.c @@ -314,6 +314,8 @@ EGLNativeWindowType waylandGetEGLNativeWindow(void) // HDR color management support enum { + HDR_CHROMATICITY_SCALE = 20, + HDR_MIN_LUMINANCE_SCALE = 10000, HDR_PQ_MIN_LUMINANCE = 50, HDR_PQ_MAX_LUMINANCE = 10000, HDR_PQ_DEFAULT_WHITE_LEVEL = 203, @@ -346,35 +348,20 @@ static bool hdrPointInBT2020(int32_t x, int32_t y) (edge0 <= 0 && edge1 <= 0 && edge2 <= 0); } -static bool hdrChromaticitiesValid(const uint16_t primary[3][2], +static bool hdrTargetPrimariesContained(const uint16_t primary[3][2], const uint16_t whitePoint[2]) -{ - for (unsigned i = 0; i < 3; ++i) - if (primary[i][1] == 0 || primary[i][0] > 50000 || - primary[i][1] > 50000 || - (uint32_t)primary[i][0] + primary[i][1] > 50000) - return false; - - if (whitePoint[1] == 0 || whitePoint[0] > 50000 || - whitePoint[1] > 50000 || - (uint32_t)whitePoint[0] + whitePoint[1] > 50000) - return false; - - return hdrTriangleEdge( - primary[0][0], primary[0][1], primary[1][0], primary[1][1], - primary[2][0], primary[2][1]) != 0; -} - -static bool hdrTargetVolumeContained(const uint16_t primary[3][2], - const uint16_t whitePoint[2], uint32_t minLuminance, - uint32_t maxLuminance) { for (unsigned i = 0; i < 3; ++i) if (!hdrPointInBT2020(primary[i][0], primary[i][1])) return false; - return hdrPointInBT2020(whitePoint[0], whitePoint[1]) && - minLuminance >= HDR_PQ_MIN_LUMINANCE && + return hdrPointInBT2020(whitePoint[0], whitePoint[1]); +} + +static bool hdrTargetLuminanceContained(uint32_t minLuminance, + uint32_t maxLuminance) +{ + return minLuminance >= HDR_PQ_MIN_LUMINANCE && maxLuminance <= HDR_PQ_MAX_LUMINANCE; } @@ -511,33 +498,55 @@ void waylandSetHDRImageDescription(const uint16_t displayPrimary[3][2], // KVMFR uses the ST 2086/DXGI scale of 50,000 units per coordinate while // color-management-v1 uses 1,000,000 units per coordinate. - const bool validMasteringLuminance = - (uint64_t)maxDisplayLuminance * 10000 > minDisplayLuminance; - const bool validMasteringChromaticities = - hdrChromaticitiesValid(displayPrimary, whitePoint); - const bool targetVolumeContained = hdrTargetVolumeContained( - displayPrimary, whitePoint, minDisplayLuminance, - maxDisplayLuminance); - const bool canSetMasteringMetadata = hdrPQ && hdrMetadata && - wlWm.cmHasMasteringPrimaries && validMasteringLuminance && - validMasteringChromaticities && - (targetVolumeContained || wlWm.cmHasExtendedTargetVolume); - if (canSetMasteringMetadata) + const bool canSetMastering = hdrPQ && hdrMetadata && + wlWm.cmHasMasteringPrimaries; + const bool masteringPrimariesContained = + hdrTargetPrimariesContained(displayPrimary, whitePoint); + const bool masteringLuminanceContained = + hdrTargetLuminanceContained(minDisplayLuminance, + maxDisplayLuminance); + const bool validMasteringLuminance = + (uint64_t)maxDisplayLuminance * HDR_MIN_LUMINANCE_SCALE > + minDisplayLuminance; + if (canSetMastering && + (masteringPrimariesContained || wlWm.cmHasExtendedTargetVolume)) { wp_image_description_creator_params_v1_set_mastering_display_primaries( wlWm.hdrImageCreator, - displayPrimary[0][0] * 20, displayPrimary[0][1] * 20, - displayPrimary[1][0] * 20, displayPrimary[1][1] * 20, - displayPrimary[2][0] * 20, displayPrimary[2][1] * 20, - whitePoint[0] * 20, whitePoint[1] * 20); + displayPrimary[0][0] * HDR_CHROMATICITY_SCALE, + displayPrimary[0][1] * HDR_CHROMATICITY_SCALE, + displayPrimary[1][0] * HDR_CHROMATICITY_SCALE, + displayPrimary[1][1] * HDR_CHROMATICITY_SCALE, + displayPrimary[2][0] * HDR_CHROMATICITY_SCALE, + displayPrimary[2][1] * HDR_CHROMATICITY_SCALE, + whitePoint[0] * HDR_CHROMATICITY_SCALE, + whitePoint[1] * HDR_CHROMATICITY_SCALE); + } + else if (canSetMastering) + DEBUG_WARN("HDR mastering primaries exceed the BT.2020 primary volume " + "without extended target-volume support; omitting them " + "(R:%u,%u G:%u,%u B:%u,%u W:%u,%u)", + displayPrimary[0][0], displayPrimary[0][1], + displayPrimary[1][0], displayPrimary[1][1], + displayPrimary[2][0], displayPrimary[2][1], + whitePoint[0], whitePoint[1]); + if (canSetMastering && validMasteringLuminance && + (masteringLuminanceContained || wlWm.cmHasExtendedTargetVolume)) + { wp_image_description_creator_params_v1_set_mastering_luminance( wlWm.hdrImageCreator, minDisplayLuminance, maxDisplayLuminance); } - else if (hdrPQ && hdrMetadata && wlWm.cmHasMasteringPrimaries) - DEBUG_WARN("HDR mastering target volume is invalid or unsupported; " - "omitting mastering display metadata"); + else if (canSetMastering && !validMasteringLuminance) + DEBUG_WARN("Invalid HDR mastering luminance range; omitting it " + "(max:%u cd/m^2 min:%u (0.0001 cd/m^2))", + maxDisplayLuminance, minDisplayLuminance); + else if (canSetMastering) + DEBUG_WARN("HDR mastering luminance exceeds the PQ primary volume " + "without extended target-volume support; omitting it " + "(max:%u cd/m^2 min:%u (0.0001 cd/m^2))", + maxDisplayLuminance, minDisplayLuminance); if (hdrPQ && hdrMetadata) {