scene: send color_management_v1 surface feedback

Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3999
This commit is contained in:
Simon Ser 2025-07-29 19:29:45 +02:00 committed by Simon Zeni
parent 26c1476827
commit 7cb3393e75
3 changed files with 84 additions and 0 deletions

View file

@ -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.
*/

View file

@ -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(

View file

@ -5,6 +5,7 @@
#include <wlr/render/swapchain.h>
#include <wlr/render/drm_syncobj.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_color_management_v1.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_damage_ring.h>
#include <wlr/types/wlr_gamma_control_v1.h>
@ -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);