From 5994c3d05505123282b88ed0f46c1c2c4f678166 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Tue, 24 Mar 2026 02:48:16 -0700 Subject: [PATCH 1/2] color_management_v1: implement set_luminances v2: Fix coding errors v3: Fall back to default luminances if not set v4: Return luminances, with override max for PQ --- include/wlr/types/wlr_color_management_v1.h | 6 +++ include/wlr/types/wlr_scene.h | 6 +++ types/scene/surface.c | 5 ++ types/scene/wlr_scene.c | 22 ++++++++- types/wlr_color_management_v1.c | 55 ++++++++++++++++++--- 5 files changed, 86 insertions(+), 8 deletions(-) diff --git a/include/wlr/types/wlr_color_management_v1.h b/include/wlr/types/wlr_color_management_v1.h index 2fc46d5ea..9edad1058 100644 --- a/include/wlr/types/wlr_color_management_v1.h +++ b/include/wlr/types/wlr_color_management_v1.h @@ -29,6 +29,9 @@ struct wlr_image_description_v1_data { float min, max; // cd/m² } mastering_luminance; + bool has_luminances; + struct wlr_color_luminances luminances; // cd/m² + uint32_t max_cll, max_fall; // cd/m², zero if unset }; @@ -94,6 +97,9 @@ void wlr_color_manager_v1_set_surface_preferred_image_description( struct wlr_color_manager_v1 *manager, struct wlr_surface *surface, const struct wlr_image_description_v1_data *data); +void wlr_color_manager_v1_get_luminances(const struct wlr_image_description_v1_data *img_desc, + struct wlr_color_luminances *lum); + /** * Convert a protocol transfer function to enum wlr_color_transfer_function. * Aborts if there is no matching wlroots entry. diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index f6f97cfea..4df4b0a09 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -196,6 +196,9 @@ struct wlr_scene_buffer { enum wlr_color_encoding color_encoding; enum wlr_color_range color_range; + bool has_luminances; + struct wlr_color_luminances luminances; + struct { uint64_t active_outputs; struct wlr_texture *texture; @@ -570,6 +573,9 @@ void wlr_scene_buffer_set_color_encoding(struct wlr_scene_buffer *scene_buffer, void wlr_scene_buffer_set_color_range(struct wlr_scene_buffer *scene_buffer, enum wlr_color_range range); +void wlr_scene_buffer_set_luminances(struct wlr_scene_buffer *scene_buffer, + const struct wlr_color_luminances *lum); + /** * Calls the buffer's frame_done signal. */ diff --git a/types/scene/surface.c b/types/scene/surface.c index e6ea1333a..662c9c640 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -12,6 +12,7 @@ #include #include #include "types/wlr_scene.h" +#include "render/color.h" static double get_surface_preferred_buffer_scale(struct wlr_surface *surface) { double scale = 1; @@ -278,11 +279,14 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { enum wlr_color_transfer_function tf = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22; enum wlr_color_named_primaries primaries = WLR_COLOR_NAMED_PRIMARIES_SRGB; + struct wlr_color_luminances luminances; + wlr_color_transfer_function_get_default_luminance(tf, &luminances); const struct wlr_image_description_v1_data *img_desc = wlr_surface_get_image_description_v1_data(surface); if (img_desc != NULL) { tf = wlr_color_manager_v1_transfer_function_to_wlr(img_desc->tf_named); primaries = wlr_color_manager_v1_primaries_to_wlr(img_desc->primaries_named); + wlr_color_manager_v1_get_luminances(img_desc, &luminances); } enum wlr_color_encoding color_encoding = WLR_COLOR_ENCODING_NONE; @@ -307,6 +311,7 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { wlr_scene_buffer_set_primaries(scene_buffer, primaries); wlr_scene_buffer_set_color_encoding(scene_buffer, color_encoding); wlr_scene_buffer_set_color_range(scene_buffer, color_range); + wlr_scene_buffer_set_luminances(scene_buffer, &luminances); scene_buffer_unmark_client_buffer(scene_buffer); diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 7231422e8..fb3e180c5 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1148,6 +1148,20 @@ void wlr_scene_buffer_set_color_range(struct wlr_scene_buffer *scene_buffer, scene_node_update(&scene_buffer->node, NULL); } +void wlr_scene_buffer_set_luminances(struct wlr_scene_buffer *scene_buffer, + const struct wlr_color_luminances *lum) { + if (scene_buffer->has_luminances && + scene_buffer->luminances.min == lum->min && + scene_buffer->luminances.max == lum->max && + scene_buffer->luminances.reference == lum->reference) { + return; + } + + scene_buffer->has_luminances = true; + scene_buffer->luminances = *lum; + scene_node_update(&scene_buffer->node, NULL); +} + static struct wlr_texture *scene_buffer_get_texture( struct wlr_scene_buffer *scene_buffer, struct wlr_renderer *renderer) { if (scene_buffer->buffer == NULL || scene_buffer->texture != NULL) { @@ -1495,8 +1509,12 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren } struct wlr_color_luminances src_lum, srgb_lum; - wlr_color_transfer_function_get_default_luminance( - scene_buffer->transfer_function, &src_lum); + if (scene_buffer->has_luminances) { + src_lum = scene_buffer->luminances; + } else { + wlr_color_transfer_function_get_default_luminance( + scene_buffer->transfer_function, &src_lum); + } wlr_color_transfer_function_get_default_luminance( WLR_COLOR_TRANSFER_FUNCTION_SRGB, &srgb_lum); float luminance_multiplier = get_luminance_multiplier(&src_lum, &srgb_lum); diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index aa924e36d..d1dac7a1b 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -93,6 +93,7 @@ static bool img_desc_data_equal(const struct wlr_image_description_v1_data *a, a->primaries_named != b->primaries_named || a->has_mastering_display_primaries != b->has_mastering_display_primaries || a->has_mastering_luminance != b->has_mastering_luminance || + a->has_luminances != b->has_luminances || a->max_cll != b->max_cll || a->max_fall != b->max_fall) { return false; @@ -106,6 +107,12 @@ static bool img_desc_data_equal(const struct wlr_image_description_v1_data *a, a->mastering_luminance.max != b->mastering_luminance.max)) { return false; } + if (a->has_luminances && + (a->luminances.min != b->luminances.min || + a->luminances.max != b->luminances.max || + a->luminances.reference != b->luminances.reference)) { + return false; + } return true; } @@ -146,8 +153,7 @@ static void image_desc_handle_get_information(struct wl_client *client, wlr_color_manager_v1_primaries_to_wlr(image_desc->data.primaries_named)); struct wlr_color_luminances luminances; - wlr_color_transfer_function_get_default_luminance( - wlr_color_manager_v1_transfer_function_to_wlr(image_desc->data.tf_named), &luminances); + wlr_color_manager_v1_get_luminances(&image_desc->data, &luminances); wp_image_description_info_v1_send_primaries_named(resource, image_desc->data.primaries_named); wp_image_description_info_v1_send_primaries(resource, @@ -608,9 +614,33 @@ static void image_desc_creator_params_handle_set_primaries(struct wl_client *cli static void image_desc_creator_params_handle_set_luminances(struct wl_client *client, struct wl_resource *params_resource, uint32_t min_lum, uint32_t max_lum, uint32_t reference_lum) { - wl_resource_post_error(params_resource, - WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_UNSUPPORTED_FEATURE, - "set_luminances is not supported"); + struct wlr_image_description_creator_params_v1 *params = + image_desc_creator_params_from_resource(params_resource); + if (!params->manager->features.set_luminances) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_UNSUPPORTED_FEATURE, + "set_luminances is not supported"); + return; + } + + if (params->data.has_luminances) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, + "luminances already set"); + return; + } + + params->data.has_luminances = true; + params->data.luminances.min = (float)min_lum / 10000; + params->data.luminances.max = max_lum; + params->data.luminances.reference = reference_lum; + + if (params->data.luminances.max <= params->data.luminances.min) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_LUMINANCE, + "max luminance must be greater than min luminance"); + return; + } } static void image_desc_creator_params_handle_set_mastering_display_primaries( @@ -972,7 +1002,6 @@ struct wlr_color_manager_v1 *wlr_color_manager_v1_create(struct wl_display *disp assert(!options->features.icc_v2_v4); assert(!options->features.set_primaries); assert(!options->features.set_tf_power); - assert(!options->features.set_luminances); assert(!options->features.extended_target_volume); assert(!options->features.windows_scrgb); @@ -1159,3 +1188,17 @@ wlr_color_manager_v1_primaries_list_from_renderer(struct wlr_renderer *renderer, *len = sizeof(list) / sizeof(list[0]); return out; } + +void wlr_color_manager_v1_get_luminances(const struct wlr_image_description_v1_data *img_desc, + struct wlr_color_luminances *lum) { + if (img_desc->has_luminances) { + *lum = img_desc->luminances; + if (img_desc->tf_named == WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ) { + lum->max = lum->min + 10000; + } + return; + } + + wlr_color_transfer_function_get_default_luminance( + wlr_color_manager_v1_transfer_function_to_wlr(img_desc->tf_named), lum); +} From 5a13fa848ae47148fa35861844d152b51ac709b7 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 20 Aug 2025 18:04:14 -0700 Subject: [PATCH 2/2] color_management_v1: add create windows scrgb v2: Remove assert blocking feature, gate feature behind compositor enabling it, and set get_info_allowed to false for the descriptor returned v3: Whoops, made a minor error without testing it first. Thanks, CI. v4: Add default luminance level response for linear color. Thanks, Dominick DiMaggio @njdom24 v5: Revert v4, and instead use new implementation of set_luminances to apply a color luminance range v6: Move luminances declaration to the correct place --- types/wlr_color_management_v1.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index d1dac7a1b..5b1ffd114 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -908,9 +908,25 @@ static void manager_handle_create_parametric_creator(struct wl_client *client, static void manager_handle_create_windows_scrgb(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id) { - wl_resource_post_error(manager_resource, - WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, - "get_windows_scrgb is not supported"); + struct wlr_color_manager_v1 *manager = manager_from_resource(manager_resource); + if (!manager->features.windows_scrgb) { + wl_resource_post_error(manager_resource, + WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, + "get_windows_scrgb is not supported"); + return; + } + + struct wlr_image_description_v1_data data = { + .tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR, + .primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, + .has_luminances = true, + .luminances = (struct wlr_color_luminances){ + .min = 0.02, + .max = 80, + .reference = 203, + }, + }; + image_desc_create_ready(manager, manager_resource, id, &data, false); } static const struct wp_color_manager_v1_interface manager_impl = { @@ -1003,7 +1019,6 @@ struct wlr_color_manager_v1 *wlr_color_manager_v1_create(struct wl_display *disp assert(!options->features.set_primaries); assert(!options->features.set_tf_power); assert(!options->features.extended_target_volume); - assert(!options->features.windows_scrgb); struct wlr_color_manager_v1 *manager = calloc(1, sizeof(*manager)); if (manager == NULL) {