From 965b4dce6d0c1e66071b5da97e3f8b575fd738dd Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 9 Jul 2025 10:50:20 +0200 Subject: [PATCH] Wayland: Set surface color management This PR solves running Wayland on compositors that don't support HDR/ color management. It also allows to let Blender window be drawn across monitor boundaries and being transferred and clamped to the monitor it is being displayed on. From our point of view monitor configurations is a compositor/OS responsibility. This PR provides the compositor that the provided swapchain image will be using sRGB whitepoints and transfer function. The compositor should then take care of performing the final transfer to the monitor color volume. The color management protocol doesn't provide guarantees that every compositor does this. It is mentioned as a recommendation and 'should do this'. Pull Request: https://projects.blender.org/blender/blender/pulls/141598 --- intern/ghost/CMakeLists.txt | 4 +++ intern/ghost/intern/GHOST_SystemWayland.cc | 32 ++++++++++++++++++++++ intern/ghost/intern/GHOST_SystemWayland.hh | 1 + intern/ghost/intern/GHOST_WindowWayland.cc | 32 +++++++++++++++++++++- 4 files changed, 68 insertions(+), 1 deletion(-) diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index 66df2914e92..5a343e4c7ce 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -402,6 +402,10 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) generate_protocol_bindings( "${WAYLAND_PROTOCOLS_DIR}/stable/viewporter/viewporter.xml" ) + # Color management (HDR properties) + generate_protocol_bindings( + "${WAYLAND_PROTOCOLS_DIR}/staging/color-management/color-management-v1.xml" + ) # Pointer-constraints. generate_protocol_bindings( "${WAYLAND_PROTOCOLS_DIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" diff --git a/intern/ghost/intern/GHOST_SystemWayland.cc b/intern/ghost/intern/GHOST_SystemWayland.cc index a50d2aa17c2..bee5d71f573 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cc +++ b/intern/ghost/intern/GHOST_SystemWayland.cc @@ -59,6 +59,7 @@ #include /* Generated by `wayland-scanner`. */ +#include #include #include #include @@ -1441,6 +1442,7 @@ struct GWL_Display { zwp_primary_selection_device_manager_v1 *primary_selection_device_manager = nullptr; wp_fractional_scale_manager_v1 *fractional_scale_manager = nullptr; wp_viewporter *viewporter = nullptr; + wp_color_manager_v1 *color_manager = nullptr; zwp_pointer_constraints_v1 *pointer_constraints = nullptr; zwp_pointer_gestures_v1 *pointer_gestures = nullptr; #ifdef WITH_INPUT_IME @@ -6621,6 +6623,26 @@ static void gwl_registry_wl_output_remove(GWL_Display *display, delete output; } +/* #GWL_Display.wp_color_manager */ + +static void gwl_registry_wp_color_manager_add(GWL_Display *display, + const GWL_RegisteryAdd_Params ¶ms) +{ + const uint version = GWL_IFACE_VERSION_CLAMP(params.version, 1u, 1u); + + display->wp.color_manager = static_cast(wl_registry_bind( + display->wl.registry, params.name, &wp_color_manager_v1_interface, version)); + gwl_registry_entry_add(display, params, nullptr); +} +static void gwl_registry_wp_color_manager_remove(GWL_Display *display, + void * /*user_data*/, + const bool /*on_exit*/) +{ + wp_color_manager_v1 **value_p = &display->wp.color_manager; + wp_color_manager_v1_destroy(*value_p); + *value_p = nullptr; +} + /* #GWL_Display.seats */ static void gwl_registry_wl_seat_add(GWL_Display *display, const GWL_RegisteryAdd_Params ¶ms) @@ -7177,6 +7199,12 @@ static const GWL_RegistryHandler gwl_registry_handlers[] = { /*update_fn*/ gwl_registry_wl_output_update, /*remove_fn*/ gwl_registry_wl_output_remove, }, + { + /*interface_p*/ &wp_color_manager_v1_interface.name, + /*add_fn*/ gwl_registry_wp_color_manager_add, + /*update_fn*/ nullptr, + /*remove_fn*/ gwl_registry_wp_color_manager_remove, + }, /* Seats. * Keep the seat near the end to ensure other types are created first. * as the seat creates data based on other interfaces. */ @@ -9026,6 +9054,10 @@ wp_viewporter *GHOST_SystemWayland::wp_viewporter_get() { return display_->wp.viewporter; } +wp_color_manager_v1 *GHOST_SystemWayland::wp_color_manager_get() +{ + return display_->wp.color_manager; +} zwp_pointer_gestures_v1 *GHOST_SystemWayland::wp_pointer_gestures_get() { diff --git a/intern/ghost/intern/GHOST_SystemWayland.hh b/intern/ghost/intern/GHOST_SystemWayland.hh index e1bba3a8c50..d40dbdfa6ed 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.hh +++ b/intern/ghost/intern/GHOST_SystemWayland.hh @@ -264,6 +264,7 @@ class GHOST_SystemWayland : public GHOST_System { struct zwp_pointer_gestures_v1 *wp_pointer_gestures_get(); struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager_get(); struct wp_viewporter *wp_viewporter_get(); + struct wp_color_manager_v1 *wp_color_manager_get(); #ifdef WITH_GHOST_WAYLAND_LIBDECOR libdecor *libdecor_context_get(); diff --git a/intern/ghost/intern/GHOST_WindowWayland.cc b/intern/ghost/intern/GHOST_WindowWayland.cc index 2f7eca327e3..d8dba322baa 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cc +++ b/intern/ghost/intern/GHOST_WindowWayland.cc @@ -41,6 +41,7 @@ #endif /* Generated by `wayland-scanner`. */ +#include #include #include #include @@ -442,6 +443,7 @@ struct GWL_Window { * and ignore updated scale based on #wl_surface_listener::enter & exit events. */ wp_fractional_scale_v1 *fractional_scale_handle = nullptr; + wp_color_management_surface_v1 *color_management_surface = nullptr; } wp; /** XDG native types. */ @@ -1819,6 +1821,29 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, wl_surface_add_listener(window_->wl.surface, &wl_surface_listener, window_); + /* Color management */ + wp_color_manager_v1 *color_manager = system->wp_color_manager_get(); + if (color_manager) { + window_->wp.color_management_surface = wp_color_manager_v1_get_surface(color_manager, + window_->wl.surface); + + wp_image_description_creator_params_v1 *image_creator_params = + wp_color_manager_v1_create_parametric_creator(color_manager); + wp_image_description_creator_params_v1_set_tf_named( + image_creator_params, WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB); + wp_image_description_creator_params_v1_set_primaries_named(image_creator_params, + WP_COLOR_MANAGER_V1_PRIMARIES_SRGB); + + wp_image_description_v1 *image_description = wp_image_description_creator_params_v1_create( + image_creator_params); + + wp_color_management_surface_v1_set_image_description( + window_->wp.color_management_surface, + image_description, + WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL); + wp_image_description_v1_destroy(image_description); + } + wp_fractional_scale_manager_v1 *fractional_scale_manager = system->wp_fractional_scale_manager_get(); if (fractional_scale_manager) { @@ -2026,7 +2051,7 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, window_->backend.vulkan_window_info = new GHOST_ContextVK_WindowInfo; window_->backend.vulkan_window_info->size[0] = window_->frame.size[0]; window_->backend.vulkan_window_info->size[1] = window_->frame.size[1]; - window_->backend.vulkan_window_info->is_color_managed = true; + window_->backend.vulkan_window_info->is_color_managed = color_manager != nullptr; } #endif @@ -2161,6 +2186,11 @@ GHOST_WindowWayland::~GHOST_WindowWayland() window_->wp.viewport = nullptr; } + if (window_->wp.color_management_surface) { + wp_color_management_surface_v1_destroy(window_->wp.color_management_surface); + window_->wp.color_management_surface = nullptr; + } + #ifdef WITH_GHOST_WAYLAND_LIBDECOR if (use_libdecor) { gwl_libdecor_window_destroy(window_->libdecor);