diff --git a/client/displayservers/Wayland/colormgmt.c b/client/displayservers/Wayland/colormgmt.c index 16b35be6..71b7bb78 100644 --- a/client/displayservers/Wayland/colormgmt.c +++ b/client/displayservers/Wayland/colormgmt.c @@ -45,6 +45,9 @@ static void cmFeature(void * data, struct wp_color_manager_v1 * cm, uint32_t fea case WP_COLOR_MANAGER_V1_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES: wlWm.cmHasMasteringPrimaries = true; break; + case WP_COLOR_MANAGER_V1_FEATURE_WINDOWS_SCRGB: + wlWm.cmHasWindowsSCRGB = true; + break; default: break; } @@ -69,18 +72,22 @@ static void cmPrimariesNamed(void * data, struct wp_color_manager_v1 * cm, static void cmDone(void * data, struct wp_color_manager_v1 * cm) { - wlWm.cmFeaturesDone = true; - wlWm.cmCanDoHDR = wlWm.cmHasParametric && - ((wlWm.cmHasTFSt2084PQ && wlWm.cmHasPrimariesBT2020) || - (wlWm.cmHasTFExtLinear && wlWm.cmHasPrimariesSRGB)); + const bool canPQ = wlWm.cmHasPerceptualIntent && wlWm.cmHasParametric && + wlWm.cmHasTFSt2084PQ && wlWm.cmHasPrimariesBT2020; + atomic_store_explicit(&wlWm.cmCanDoHDR, + canPQ || (wlWm.cmHasPerceptualIntent && wlWm.cmHasWindowsSCRGB), + memory_order_relaxed); + atomic_store_explicit(&wlWm.cmFeaturesDone, true, memory_order_release); DEBUG_INFO("Color management features: parametric:%d luminances:%d " "mastering_primaries:%d st2084_pq:%d ext_linear:%d bt2020:%d srgb:%d " + "windows_scrgb:%d perceptual:%d " "can_do_hdr:%d", wlWm.cmHasParametric, wlWm.cmHasLuminances, wlWm.cmHasMasteringPrimaries, wlWm.cmHasTFSt2084PQ, wlWm.cmHasTFExtLinear, wlWm.cmHasPrimariesBT2020, wlWm.cmHasPrimariesSRGB, - wlWm.cmCanDoHDR); + wlWm.cmHasWindowsSCRGB, wlWm.cmHasPerceptualIntent, + atomic_load(&wlWm.cmCanDoHDR)); } static const struct wp_color_manager_v1_listener cmListener = { @@ -97,6 +104,7 @@ bool waylandColorMgmtInit(void) return true; wp_color_manager_v1_add_listener(wlWm.colorManager, &cmListener, NULL); + waylandOutputColorMgmtInitAll(); return true; } @@ -105,6 +113,7 @@ void waylandColorMgmtFree(void) if (!wlWm.colorManager) return; + LG_LOCK(wlWm.hdrLock); if (wlWm.hdrImageCreator) { wp_image_description_creator_params_v1_destroy(wlWm.hdrImageCreator); @@ -121,6 +130,7 @@ void waylandColorMgmtFree(void) wp_color_management_surface_v1_destroy(wlWm.colorSurface); wlWm.colorSurface = NULL; } + LG_UNLOCK(wlWm.hdrLock); wp_color_manager_v1_destroy(wlWm.colorManager); wlWm.colorManager = NULL; diff --git a/client/displayservers/Wayland/gl.c b/client/displayservers/Wayland/gl.c index 44b710fb..e8bc7ef9 100644 --- a/client/displayservers/Wayland/gl.c +++ b/client/displayservers/Wayland/gl.c @@ -98,6 +98,7 @@ static void applyHDRPending(void) void waylandClearHDRImageDescription(void) { + LG_LOCK(wlWm.hdrLock); if (wlWm.hdrImageCreator) { wp_image_description_creator_params_v1_destroy(wlWm.hdrImageCreator); @@ -114,19 +115,25 @@ void waylandClearHDRImageDescription(void) wp_color_management_surface_v1_unset_image_description(wlWm.colorSurface); atomic_store(&wlWm.hdrActive, false); + LG_UNLOCK(wlWm.hdrLock); DEBUG_INFO("HDR image description removed from surface"); } static void hdrImageDescriptionReady(struct wp_image_description_v1 * desc) { + LG_LOCK(wlWm.hdrLock); if (desc != wlWm.hdrImageDesc) + { + LG_UNLOCK(wlWm.hdrLock); return; + } if (!wlWm.colorSurface) { DEBUG_WARN("HDR image description became ready without a color surface"); wp_image_description_v1_destroy(desc); wlWm.hdrImageDesc = NULL; + LG_UNLOCK(wlWm.hdrLock); return; } @@ -134,16 +141,22 @@ static void hdrImageDescriptionReady(struct wp_image_description_v1 * desc) // then switch to native HDR for the next render, and that native frame and // its image description become active in the same following commit. wlWm.hdrImageDescReady = true; + const bool pq = wlWm.hdrImageDescPQ; + LG_UNLOCK(wlWm.hdrLock); DEBUG_INFO("HDR image description is ready (%s)", - wlWm.hdrImageDescPQ ? "PQ" : "scRGB"); + pq ? "PQ" : "scRGB"); app_invalidateWindow(true); waylandStopWaitFrame(); } static void activateReadyHDRImageDescription(void) { + LG_LOCK(wlWm.hdrLock); if (!wlWm.hdrImageDesc || !wlWm.hdrImageDescReady || !wlWm.colorSurface) + { + LG_UNLOCK(wlWm.hdrLock); return; + } struct wp_image_description_v1 * desc = wlWm.hdrImageDesc; wp_color_management_surface_v1_set_image_description( @@ -157,9 +170,11 @@ static void activateReadyHDRImageDescription(void) wlWm.hdrImageDesc = NULL; wlWm.hdrImageDescReady = false; wp_image_description_v1_destroy(desc); + const bool pq = atomic_load(&wlWm.hdrActivePQ); + LG_UNLOCK(wlWm.hdrLock); DEBUG_INFO("HDR image description pending next surface commit (%s)", - atomic_load(&wlWm.hdrActivePQ) ? "PQ" : "scRGB"); + pq ? "PQ" : "scRGB"); app_invalidateWindow(true); waylandStopWaitFrame(); } @@ -168,14 +183,19 @@ static void hdrImageDescriptionFailed(void * data, struct wp_image_description_v1 * desc, uint32_t cause, const char * message) { (void)data; + LG_LOCK(wlWm.hdrLock); if (desc != wlWm.hdrImageDesc) + { + LG_UNLOCK(wlWm.hdrLock); return; + } DEBUG_WARN("Failed to create HDR image description (cause:%u): %s", cause, message); wlWm.hdrImageDesc = NULL; wlWm.hdrImageDescReady = false; wp_image_description_v1_destroy(desc); + LG_UNLOCK(wlWm.hdrLock); app_invalidateWindow(true); waylandStopWaitFrame(); } @@ -295,13 +315,19 @@ void waylandSetHDRImageDescription(const uint16_t displayPrimary[3][2], if (!wlWm.colorManager) return; - if (!wlWm.cmFeaturesDone) + if (!atomic_load_explicit(&wlWm.cmFeaturesDone, memory_order_acquire)) { DEBUG_WARN("Color management features not yet advertised, deferring HDR"); return; } - if (!wlWm.cmHasParametric) + if (!wlWm.cmHasPerceptualIntent) + { + DEBUG_WARN("Compositor does not support the perceptual render intent"); + return; + } + + if (hdrPQ && !wlWm.cmHasParametric) { DEBUG_WARN("Compositor does not support parametric image descriptions"); return; @@ -313,9 +339,9 @@ void waylandSetHDRImageDescription(const uint16_t displayPrimary[3][2], DEBUG_WARN("Compositor does not support ST2084_PQ transfer function"); return; } - if (!hdrPQ && !wlWm.cmHasTFExtLinear) + if (!hdrPQ && !wlWm.cmHasWindowsSCRGB) { - DEBUG_WARN("Compositor does not support EXT_LINEAR transfer function"); + DEBUG_WARN("Compositor does not support Windows-scRGB image descriptions"); return; } @@ -325,11 +351,7 @@ void waylandSetHDRImageDescription(const uint16_t displayPrimary[3][2], DEBUG_WARN("Compositor does not support BT.2020 primaries"); return; } - if (!hdrPQ && !wlWm.cmHasPrimariesSRGB) - { - DEBUG_WARN("Compositor does not support sRGB primaries"); - return; - } + LG_LOCK(wlWm.hdrLock); // Cancel only an in-flight replacement. The active surface description is // retained until this replacement is ready. @@ -360,29 +382,49 @@ void waylandSetHDRImageDescription(const uint16_t displayPrimary[3][2], if (!wlWm.colorSurface) { DEBUG_WARN("Failed to get color management surface"); + LG_UNLOCK(wlWm.hdrLock); return; } } + if (!hdrPQ) + { + wlWm.hdrImageDesc = + wp_color_manager_v1_create_windows_scrgb(wlWm.colorManager); + if (!wlWm.hdrImageDesc) + { + DEBUG_WARN("Failed to create Windows-scRGB image description"); + LG_UNLOCK(wlWm.hdrLock); + return; + } + + wlWm.hdrImageDescPQ = false; + wlWm.hdrImageDescReady = false; + wp_image_description_v1_add_listener( + wlWm.hdrImageDesc, &hdrImageDescListener, NULL); + LG_UNLOCK(wlWm.hdrLock); + DEBUG_INFO("HDR image description requested (scRGB, Windows-scRGB)"); + return; + } + wlWm.hdrImageCreator = wp_color_manager_v1_create_parametric_creator(wlWm.colorManager); if (!wlWm.hdrImageCreator) { DEBUG_WARN("Failed to create parametric image description creator"); + LG_UNLOCK(wlWm.hdrLock); return; } // Set primaries: BT.2020 for PQ HDR10, sRGB for scRGB/FP16 wp_image_description_creator_params_v1_set_primaries_named( wlWm.hdrImageCreator, - hdrPQ ? WP_COLOR_MANAGER_V1_PRIMARIES_BT2020 - : WP_COLOR_MANAGER_V1_PRIMARIES_SRGB); + WP_COLOR_MANAGER_V1_PRIMARIES_BT2020); // Select transfer function: PQ for PQ-encoded content, linear for scRGB/FP16 wp_image_description_creator_params_v1_set_tf_named( wlWm.hdrImageCreator, - hdrPQ ? WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ - : WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR); + WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ); // Set the primary colour volume luminances. // min_lum : 0.0001 cd/m² (source already scaled) -> pass through @@ -394,9 +436,7 @@ void waylandSetHDRImageDescription(const uint16_t displayPrimary[3][2], wlWm.hdrImageCreator, minDisplayLuminance > 0 ? minDisplayLuminance : 50, maxDisplayLuminance > 0 ? maxDisplayLuminance : 1000, - hdrPQ ? - wlWm.hdrWhiteLevels.pq : - wlWm.hdrWhiteLevels.scRGB); + atomic_load(&wlWm.hdrPQWhiteLevel)); // KVMFR uses the ST 2086/DXGI scale of 50,000 units per coordinate while // color-management-v1 uses 1,000,000 units per coordinate. @@ -432,6 +472,7 @@ void waylandSetHDRImageDescription(const uint16_t displayPrimary[3][2], if (!wlWm.hdrImageDesc) { DEBUG_WARN("Failed to create HDR image description"); + LG_UNLOCK(wlWm.hdrLock); return; } @@ -439,6 +480,7 @@ void waylandSetHDRImageDescription(const uint16_t displayPrimary[3][2], wlWm.hdrImageDescReady = false; wp_image_description_v1_add_listener( wlWm.hdrImageDesc, &hdrImageDescListener, NULL); + LG_UNLOCK(wlWm.hdrLock); DEBUG_INFO("HDR image description requested (%s, %s, " "maxLum:%u cd/m² minLum:%u (0.0001 cd/m²) maxCLL:%u maxFALL:%u)", @@ -451,15 +493,14 @@ bool waylandRequestHDR(const uint16_t displayPrimary[3][2], uint32_t minDisplayLuminance, uint32_t maxCLL, uint32_t maxFALL, bool hdrPQ, bool hdrMetadata) { - if (!wlWm.cmFeaturesDone || !wlWm.cmHasParametric) + if (!atomic_load_explicit(&wlWm.cmFeaturesDone, memory_order_acquire)) return false; - if (hdrPQ && !wlWm.cmHasTFSt2084PQ) + if (!wlWm.cmHasPerceptualIntent) return false; - if (!hdrPQ && !wlWm.cmHasTFExtLinear) + if (hdrPQ && (!wlWm.cmHasParametric || !wlWm.cmHasTFSt2084PQ || + !wlWm.cmHasPrimariesBT2020)) return false; - if (hdrPQ && !wlWm.cmHasPrimariesBT2020) - return false; - if (!hdrPQ && !wlWm.cmHasPrimariesSRGB) + if (!hdrPQ && !wlWm.cmHasWindowsSCRGB) return false; atomic_store(&wlWm.hdrRequestedPQ, hdrPQ); diff --git a/client/displayservers/Wayland/output.c b/client/displayservers/Wayland/output.c index 909d4bce..8e994649 100644 --- a/client/displayservers/Wayland/output.c +++ b/client/displayservers/Wayland/output.c @@ -21,12 +21,264 @@ #include "wayland.h" #include +#include #include #include +#include +#include "app.h" #include "common/debug.h" +struct OutputColorDescription +{ + struct WaylandOutput * output; + struct wp_image_description_v1 * description; + struct wp_image_description_info_v1 * info; +}; + +static void outputColorDescriptionFree(struct OutputColorDescription * desc) +{ + if (!desc) + return; + + if (desc->info) + wl_proxy_destroy((struct wl_proxy *)desc->info); + if (desc->description) + wp_image_description_v1_destroy(desc->description); + if (desc->output && desc->output->colorDescription == desc) + desc->output->colorDescription = NULL; + free(desc); +} + +static struct WaylandOutput * outputFind(struct wl_output * output) +{ + struct WaylandOutput * node; + wl_list_for_each(node, &wlWm.outputs, link) + if (node->output == output) + return node; + return NULL; +} + +void waylandOutputUpdateHDRWhiteLevel(void) +{ + uint32_t pqWhite = 203; + uint32_t scRGBWhite = 80; + struct SurfaceOutput * surfaceOutput; + + wl_list_for_each(surfaceOutput, &wlWm.surfaceOutputs, link) + { + struct WaylandOutput * output = outputFind(surfaceOutput->output); + if (!output || !output->referenceWhiteValid) + continue; + + pqWhite = scRGBWhite = output->referenceWhiteLevel; + break; + } + + const uint32_t oldPQ = atomic_exchange(&wlWm.hdrPQWhiteLevel, pqWhite); + const uint32_t oldScRGB = + atomic_exchange(&wlWm.hdrScRGBWhiteLevel, scRGBWhite); + if ((oldPQ != pqWhite || oldScRGB != scRGBWhite) && wlWm.frameEvent) + { + DEBUG_INFO("Wayland output reference white: %u cd/m²", pqWhite); + if (atomic_load(&wlWm.hdrRequested)) + { + LG_LOCK(wlWm.pendingHDRLock); + wlWm.pendingHDRAction = WAYLAND_HDR_PENDING_APPLY; + LG_UNLOCK(wlWm.pendingHDRLock); + } + app_invalidateWindow(true); + waylandStopWaitFrame(); + } +} + +static void outputInfoDone(void * data, + struct wp_image_description_info_v1 * info) +{ + struct OutputColorDescription * desc = data; + desc->info = NULL; // the protocol destroys it after this event + outputColorDescriptionFree(desc); +} + +static void outputInfoICCFile(void * data, + struct wp_image_description_info_v1 * info, int32_t fd, uint32_t size) +{ + close(fd); +} + +static void outputInfoPrimaries(void * data, + struct wp_image_description_info_v1 * info, + int32_t rx, int32_t ry, int32_t gx, int32_t gy, + int32_t bx, int32_t by, int32_t wx, int32_t wy) +{ +} + +static void outputInfoPrimariesNamed(void * data, + struct wp_image_description_info_v1 * info, uint32_t primaries) +{ +} + +static void outputInfoTFPower(void * data, + struct wp_image_description_info_v1 * info, uint32_t exponent) +{ +} + +static void outputInfoTFNamed(void * data, + struct wp_image_description_info_v1 * info, uint32_t tf) +{ +} + +static void outputInfoLuminances(void * data, + struct wp_image_description_info_v1 * info, + uint32_t minLum, uint32_t maxLum, uint32_t referenceLum) +{ + struct OutputColorDescription * desc = data; + if (!referenceLum || desc->output->colorDescription != desc) + return; + + desc->output->referenceWhiteLevel = referenceLum; + desc->output->referenceWhiteValid = true; + waylandOutputUpdateHDRWhiteLevel(); +} + +static void outputInfoTargetPrimaries(void * data, + struct wp_image_description_info_v1 * info, + int32_t rx, int32_t ry, int32_t gx, int32_t gy, + int32_t bx, int32_t by, int32_t wx, int32_t wy) +{ +} + +static void outputInfoTargetLuminance(void * data, + struct wp_image_description_info_v1 * info, + uint32_t minLum, uint32_t maxLum) +{ +} + +static void outputInfoTargetMaxCLL(void * data, + struct wp_image_description_info_v1 * info, uint32_t maxCLL) +{ +} + +static void outputInfoTargetMaxFALL(void * data, + struct wp_image_description_info_v1 * info, uint32_t maxFALL) +{ +} + +static const struct wp_image_description_info_v1_listener outputInfoListener = +{ + .done = outputInfoDone, + .icc_file = outputInfoICCFile, + .primaries = outputInfoPrimaries, + .primaries_named = outputInfoPrimariesNamed, + .tf_power = outputInfoTFPower, + .tf_named = outputInfoTFNamed, + .luminances = outputInfoLuminances, + .target_primaries = outputInfoTargetPrimaries, + .target_luminance = outputInfoTargetLuminance, + .target_max_cll = outputInfoTargetMaxCLL, + .target_max_fall = outputInfoTargetMaxFALL, +}; + +static void outputDescriptionFailed(void * data, + struct wp_image_description_v1 * description, + uint32_t cause, const char * message) +{ + struct OutputColorDescription * desc = data; + DEBUG_WARN("Failed to query Wayland output colour description " + "(cause:%u): %s", cause, message); + outputColorDescriptionFree(desc); +} + +static void outputDescriptionReady(struct OutputColorDescription * desc) +{ + desc->info = wp_image_description_v1_get_information(desc->description); + if (!desc->info) + { + outputColorDescriptionFree(desc); + return; + } + wp_image_description_info_v1_add_listener( + desc->info, &outputInfoListener, desc); +} + +static void outputDescriptionReadyV1(void * data, + struct wp_image_description_v1 * description, uint32_t identity) +{ + outputDescriptionReady(data); +} + +static void outputDescriptionReadyV2(void * data, + struct wp_image_description_v1 * description, + uint32_t identityHi, uint32_t identityLo) +{ + outputDescriptionReady(data); +} + +static const struct wp_image_description_v1_listener outputDescriptionListener = +{ + .failed = outputDescriptionFailed, + .ready = outputDescriptionReadyV1, + .ready2 = outputDescriptionReadyV2, +}; + +static void outputRequestColorDescription(struct WaylandOutput * output) +{ + outputColorDescriptionFree(output->colorDescription); + output->referenceWhiteValid = false; + waylandOutputUpdateHDRWhiteLevel(); + + struct OutputColorDescription * desc = calloc(1, sizeof(*desc)); + if (!desc) + return; + + desc->output = output; + desc->description = + wp_color_management_output_v1_get_image_description(output->colorOutput); + if (!desc->description) + { + free(desc); + return; + } + + output->colorDescription = desc; + wp_image_description_v1_add_listener( + desc->description, &outputDescriptionListener, desc); +} + +static void outputImageDescriptionChanged(void * data, + struct wp_color_management_output_v1 * colorOutput) +{ + outputRequestColorDescription(data); +} + +static const struct wp_color_management_output_v1_listener colorOutputListener = +{ + .image_description_changed = outputImageDescriptionChanged, +}; + +void waylandOutputColorMgmtInit(struct WaylandOutput * output) +{ + if (!wlWm.colorManager || output->colorOutput) + return; + + output->colorOutput = + wp_color_manager_v1_get_output(wlWm.colorManager, output->output); + if (!output->colorOutput) + return; + + wp_color_management_output_v1_add_listener( + output->colorOutput, &colorOutputListener, output); + outputRequestColorDescription(output); +} + +void waylandOutputColorMgmtInitAll(void) +{ + struct WaylandOutput * output; + wl_list_for_each(output, &wlWm.outputs, link) + waylandOutputColorMgmtInit(output); +} + static void outputUpdateScale(struct WaylandOutput * node) { struct WaylandScale original = node->scale; @@ -144,6 +396,9 @@ void waylandOutputFree(void) struct WaylandOutput * temp; wl_list_for_each_safe(node, temp, &wlWm.outputs, link) { + outputColorDescriptionFree(node->colorDescription); + if (node->colorOutput) + wp_color_management_output_v1_destroy(node->colorOutput); if (node->version >= 3) wl_output_release(node->output); if (node->xdgOutput) @@ -188,6 +443,7 @@ void waylandOutputBind(uint32_t name, uint32_t version) wl_output_add_listener(node->output, &outputListener, node); wl_list_insert(&wlWm.outputs, &node->link); + waylandOutputColorMgmtInit(node); } void waylandOutputTryUnbind(uint32_t name) @@ -198,12 +454,27 @@ void waylandOutputTryUnbind(uint32_t name) { if (node->name == name) { + struct SurfaceOutput * surfaceOutput; + struct SurfaceOutput * temp; + wl_list_for_each_safe( + surfaceOutput, temp, &wlWm.surfaceOutputs, link) + if (surfaceOutput->output == node->output) + { + wl_list_remove(&surfaceOutput->link); + free(surfaceOutput); + } + + outputColorDescriptionFree(node->colorDescription); + if (node->colorOutput) + wp_color_management_output_v1_destroy(node->colorOutput); if (node->version >= 3) wl_output_release(node->output); if (node->xdgOutput) zxdg_output_v1_destroy(node->xdgOutput); wl_list_remove(&node->link); free(node); + waylandWindowUpdateScale(); + waylandOutputUpdateHDRWhiteLevel(); break; } } diff --git a/client/displayservers/Wayland/wayland.c b/client/displayservers/Wayland/wayland.c index 7d59a9c1..c205d10a 100644 --- a/client/displayservers/Wayland/wayland.c +++ b/client/displayservers/Wayland/wayland.c @@ -109,12 +109,12 @@ static bool waylandInit(const LG_DSInitParams params) { memset(&wlWm, 0, sizeof(wlWm)); LG_LOCK_INIT(wlWm.pendingHDRLock); + LG_LOCK_INIT(wlWm.hdrLock); wlWm.desktop = WL_Desktops[0]; - wlWm.hdrWhiteLevels = (LG_DSHDRWhiteLevels) - { - .pq = 203, - .scRGB = 80, - }; + atomic_init(&wlWm.cmFeaturesDone, false); + atomic_init(&wlWm.cmCanDoHDR, false); + atomic_init(&wlWm.hdrPQWhiteLevel, 203); + atomic_init(&wlWm.hdrScRGBWhiteLevel, 80); wlWm.display = wl_display_connect(NULL); if (!wlWm.display) @@ -212,6 +212,7 @@ static void waylandFree(void) waylandRegistryFree(); waylandCursorFree(); wl_display_disconnect(wlWm.display); + LG_LOCK_FREE(wlWm.hdrLock); LG_LOCK_FREE(wlWm.pendingHDRLock); } @@ -234,7 +235,11 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret) if (prop == LG_DS_HDR_WHITE_LEVELS) { - *(LG_DSHDRWhiteLevels *)ret = wlWm.hdrWhiteLevels; + *(LG_DSHDRWhiteLevels *)ret = (LG_DSHDRWhiteLevels) + { + .pq = atomic_load(&wlWm.hdrPQWhiteLevel), + .scRGB = atomic_load(&wlWm.hdrScRGBWhiteLevel), + }; return true; } diff --git a/client/displayservers/Wayland/wayland.h b/client/displayservers/Wayland/wayland.h index a79c1682..5d40a48b 100644 --- a/client/displayservers/Wayland/wayland.h +++ b/client/displayservers/Wayland/wayland.h @@ -19,6 +19,8 @@ */ #include +#include +#include #include #include @@ -65,6 +67,8 @@ struct WaylandPoll struct wl_list link; }; +struct OutputColorDescription; + struct WaylandOutput { uint32_t name; @@ -77,6 +81,10 @@ struct WaylandOutput bool modeRotate; struct wl_output * output; struct zxdg_output_v1 * xdgOutput; + struct wp_color_management_output_v1 * colorOutput; + struct OutputColorDescription * colorDescription; + uint32_t referenceWhiteLevel; + bool referenceWhiteValid; uint32_t version; struct wl_list link; }; @@ -216,11 +224,12 @@ struct WaylandDSState _Atomic(bool) hdrRequestedPQ; bool hdrImageDescPQ; bool hdrImageDescReady; + LG_Lock hdrLock; // wp_color_manager_v1 feature advertisement tracking. // Set to true after the done event for the color-manager has been received // and all compositor capabilities have been recorded. - bool cmFeaturesDone; + _Atomic(bool) cmFeaturesDone; bool cmHasParametric; bool cmHasLuminances; bool cmHasMasteringPrimaries; @@ -228,11 +237,13 @@ struct WaylandDSState bool cmHasTFExtLinear; bool cmHasPrimariesBT2020; bool cmHasPrimariesSRGB; + bool cmHasWindowsSCRGB; bool cmHasPerceptualIntent; - bool cmCanDoHDR; // true if compositor supports features needed for HDR + _Atomic(bool) cmCanDoHDR; // compositor supports a native HDR encoding - // Reference white configured on native HDR surface image descriptions. - LG_DSHDRWhiteLevels hdrWhiteLevels; + // Local output reference whites used for native overlay composition. + _Atomic(uint32_t) hdrPQWhiteLevel; + _Atomic(uint32_t) hdrScRGBWhiteLevel; // toplevel icon manager struct xdg_toplevel_icon_manager_v1 * iconManager; @@ -373,6 +384,9 @@ void waylandOutputFree(void); void waylandOutputBind(uint32_t name, uint32_t version); void waylandOutputTryUnbind(uint32_t name); struct WaylandScale waylandOutputGetScale(struct wl_output * output); +void waylandOutputColorMgmtInit(struct WaylandOutput * output); +void waylandOutputColorMgmtInitAll(void); +void waylandOutputUpdateHDRWhiteLevel(void); // poll module bool waylandPollInit(void); diff --git a/client/displayservers/Wayland/window.c b/client/displayservers/Wayland/window.c index 5b1087ac..9eccc126 100644 --- a/client/displayservers/Wayland/window.c +++ b/client/displayservers/Wayland/window.c @@ -76,6 +76,7 @@ static void wlSurfaceEnterHandler(void * data, struct wl_surface * surface, stru node->output = output; wl_list_insert(&wlWm.surfaceOutputs, &node->link); waylandWindowUpdateScale(); + waylandOutputUpdateHDRWhiteLevel(); } static void wlSurfaceLeaveHandler(void * data, struct wl_surface * surface, struct wl_output * output) @@ -85,9 +86,11 @@ static void wlSurfaceLeaveHandler(void * data, struct wl_surface * surface, stru if (node->output == output) { wl_list_remove(&node->link); + free(node); break; } waylandWindowUpdateScale(); + waylandOutputUpdateHDRWhiteLevel(); } static const struct wl_surface_listener wlSurfaceListener = { @@ -169,6 +172,14 @@ bool waylandWindowInit(const char * title, const char * appId, bool fullscreen, void waylandWindowFree(void) { + struct SurfaceOutput * output; + struct SurfaceOutput * temp; + wl_list_for_each_safe(output, temp, &wlWm.surfaceOutputs, link) + { + wl_list_remove(&output->link); + free(output); + } + if (wlWm.fractionalScaleInterface) wp_fractional_scale_v1_destroy(wlWm.fractionalScaleInterface); if (wlWm.contentType)