From 7cb3393e75667ca349287ad70aef3f7e2d008ec6 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 29 Jul 2025 19:29:45 +0200 Subject: [PATCH] scene: send color_management_v1 surface feedback Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3999 --- include/wlr/types/wlr_scene.h | 10 ++++++ types/scene/surface.c | 58 +++++++++++++++++++++++++++++++++++ types/scene/wlr_scene.c | 16 ++++++++++ 3 files changed, 84 insertions(+) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 0f4aa4c27..58794e8db 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -43,6 +43,7 @@ struct wlr_scene_output_layout; struct wlr_presentation; struct wlr_linux_dmabuf_v1; struct wlr_gamma_control_manager_v1; +struct wlr_color_manager_v1; struct wlr_output_state; typedef bool (*wlr_scene_buffer_point_accepts_input_func_t)( @@ -102,11 +103,13 @@ struct wlr_scene { // May be NULL struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1; + struct wlr_color_manager_v1 *color_manager_v1; struct { struct wl_listener linux_dmabuf_v1_destroy; struct wl_listener gamma_control_manager_v1_destroy; struct wl_listener gamma_control_manager_v1_set_gamma; + struct wl_listener color_manager_v1_destroy; enum wlr_scene_debug_damage_option debug_damage_option; bool direct_scanout; @@ -366,6 +369,13 @@ void wlr_scene_set_linux_dmabuf_v1(struct wlr_scene *scene, void wlr_scene_set_gamma_control_manager_v1(struct wlr_scene *scene, struct wlr_gamma_control_manager_v1 *gamma_control); +/** + * Handles color_management_v1 feedback for all surfaces in the scene. + * + * Asserts that a struct wlr_color_manager_v1 hasn't already been set for the scene. + */ +void wlr_scene_set_color_manager_v1(struct wlr_scene *scene, struct wlr_color_manager_v1 *manager); + /** * Add a node displaying nothing but its children. */ diff --git a/types/scene/surface.c b/types/scene/surface.c index 135ded6fd..c798abfb3 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -35,16 +35,74 @@ static struct wlr_output *get_surface_frame_pacing_output(struct wlr_surface *su return frame_pacing_output; } +static bool get_tf_preference(enum wlr_color_transfer_function tf) { + switch (tf) { + case WLR_COLOR_TRANSFER_FUNCTION_SRGB: + return 0; + case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: + return 1; + case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: + return -1; + } + abort(); // unreachable +} + +static bool get_primaries_preference(enum wlr_color_named_primaries primaries) { + switch (primaries) { + case WLR_COLOR_NAMED_PRIMARIES_SRGB: + return 0; + case WLR_COLOR_NAMED_PRIMARIES_BT2020: + return 1; + } + abort(); // unreachable +} + +static void get_surface_preferred_image_description(struct wlr_surface *surface, + struct wlr_image_description_v1_data *out) { + struct wlr_output_image_description preferred = { + .transfer_function = WLR_COLOR_TRANSFER_FUNCTION_SRGB, + .primaries = WLR_COLOR_NAMED_PRIMARIES_SRGB, + }; + + struct wlr_surface_output *surface_output; + wl_list_for_each(surface_output, &surface->current_outputs, link) { + const struct wlr_output_image_description *img_desc = + surface_output->output->image_description; + if (img_desc == NULL) { + continue; + } + if (get_tf_preference(preferred.transfer_function) < get_tf_preference(img_desc->transfer_function)) { + preferred.transfer_function = img_desc->transfer_function; + } + if (get_primaries_preference(preferred.primaries) < get_primaries_preference(img_desc->primaries)) { + preferred.primaries = img_desc->primaries; + } + } + + *out = (struct wlr_image_description_v1_data){ + .tf_named = wlr_color_manager_v1_transfer_function_from_wlr(preferred.transfer_function), + .primaries_named = wlr_color_manager_v1_primaries_from_wlr(preferred.primaries), + }; +} + static void handle_scene_buffer_outputs_update( struct wl_listener *listener, void *data) { struct wlr_scene_surface *surface = wl_container_of(listener, surface, outputs_update); + struct wlr_scene *scene = scene_node_get_root(&surface->buffer->node); surface->frame_pacing_output = get_surface_frame_pacing_output(surface->surface); double scale = get_surface_preferred_buffer_scale(surface->surface); wlr_fractional_scale_v1_notify_scale(surface->surface, scale); wlr_surface_set_preferred_buffer_scale(surface->surface, ceil(scale)); + + if (scene->color_manager_v1 != NULL) { + struct wlr_image_description_v1_data img_desc = {0}; + get_surface_preferred_image_description(surface->surface, &img_desc); + wlr_color_manager_v1_set_surface_preferred_image_description(scene->color_manager_v1, + surface->surface, &img_desc); + } } static void handle_scene_buffer_output_enter( diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index d2327eda2..ac3ea3ecf 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -1562,6 +1563,21 @@ void wlr_scene_set_gamma_control_manager_v1(struct wlr_scene *scene, wl_signal_add(&gamma_control->events.set_gamma, &scene->gamma_control_manager_v1_set_gamma); } +static void scene_handle_color_manager_v1_destroy(struct wl_listener *listener, void *data) { + struct wlr_scene *scene = wl_container_of(listener, scene, color_manager_v1_destroy); + wl_list_remove(&scene->color_manager_v1_destroy.link); + wl_list_init(&scene->color_manager_v1_destroy.link); + scene->color_manager_v1 = NULL; +} + +void wlr_scene_set_color_manager_v1(struct wlr_scene *scene, struct wlr_color_manager_v1 *manager) { + assert(scene->color_manager_v1 == NULL); + scene->color_manager_v1 = manager; + + scene->color_manager_v1_destroy.notify = scene_handle_color_manager_v1_destroy; + wl_signal_add(&manager->events.destroy, &scene->color_manager_v1_destroy); +} + static void scene_output_handle_destroy(struct wlr_addon *addon) { struct wlr_scene_output *scene_output = wl_container_of(addon, scene_output, addon);