diff --git a/src/egl/wayland/wayland-drm/meson.build b/src/egl/wayland/wayland-drm/meson.build index 59ce3442499..d3b6c15ddd2 100644 --- a/src/egl/wayland/wayland-drm/meson.build +++ b/src/egl/wayland/wayland-drm/meson.build @@ -48,6 +48,7 @@ wp_protos = { 'presentation-time': 'stable/presentation-time/presentation-time.xml', 'tearing-control-v1': 'staging/tearing-control/tearing-control-v1.xml', 'linux-drm-syncobj-v1': 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml', + 'color-management-v1': 'staging/color-management/color-management-v1.xml', } wp_files = {} foreach name, xml : wp_protos diff --git a/src/vulkan/wsi/meson.build b/src/vulkan/wsi/meson.build index 6933eb780c4..ba609ab3151 100644 --- a/src/vulkan/wsi/meson.build +++ b/src/vulkan/wsi/meson.build @@ -22,6 +22,7 @@ if with_platform_wayland files_vulkan_wsi += wp_files['tearing-control-v1'] links_vulkan_wsi += libloader_wayland_helper files_vulkan_wsi += wp_files['linux-drm-syncobj-v1'] + files_vulkan_wsi += wp_files['color-management-v1'] endif if with_platform_windows diff --git a/src/vulkan/wsi/wsi_common_wayland.c b/src/vulkan/wsi/wsi_common_wayland.c index 1f6176756b8..1036f4886f8 100644 --- a/src/vulkan/wsi/wsi_common_wayland.c +++ b/src/vulkan/wsi/wsi_common_wayland.c @@ -47,6 +47,7 @@ #include "presentation-time-client-protocol.h" #include "linux-drm-syncobj-v1-client-protocol.h" #include "tearing-control-v1-client-protocol.h" +#include "color-management-v1-client-protocol.h" #include #include @@ -110,8 +111,13 @@ struct wsi_wl_display { struct wp_tearing_control_manager_v1 *tearing_control_manager; struct wp_linux_drm_syncobj_manager_v1 *wl_syncobj; + struct wp_color_manager_v1 *color_manager; + struct dmabuf_feedback_format_table format_table; + struct u_vector color_primaries; + struct u_vector color_transfer_funcs; + /* users want per-chain wsi_wl_swapchain->present_ids.wp_presentation */ struct wp_presentation *wp_presentation_notwrapped; uint32_t wp_presentation_version; @@ -125,6 +131,9 @@ struct wsi_wl_display { /* Formats populated by zwp_linux_dmabuf_v1 or wl_shm interfaces */ struct u_vector formats; + /* Additional colorspaces returned by wp_color_management_v1. */ + struct u_vector colorspaces; + bool sw; dev_t main_device; @@ -180,6 +189,11 @@ struct wsi_wl_surface { struct dmabuf_feedback dmabuf_feedback, pending_dmabuf_feedback; struct wp_linux_drm_syncobj_surface_v1 *wl_syncobj_surface; + + struct vk_instance *instance; + struct wp_color_management_surface_v1 *color_surface; + int color_surface_refcount; + VkColorSpaceKHR colorspace; }; struct wsi_wl_swapchain { @@ -230,6 +244,8 @@ struct wsi_wl_swapchain { unsigned int refresh_nsec; } present_ids; + VkColorSpaceKHR colorspace; + struct wsi_wl_image images[0]; }; VK_DEFINE_NONDISP_HANDLE_CASTS(wsi_wl_swapchain, base.base, VkSwapchainKHR, @@ -885,6 +901,316 @@ static const struct wl_shm_listener shm_listener = { .format = shm_handle_format }; +static bool +vector_contains(struct u_vector *vec, unsigned int val) +{ + unsigned int *ptr; + + u_vector_foreach(ptr, vec) + if (*ptr == val) + return true; + + return false; +} + +struct Colorspace { + VkColorSpaceKHR colorspace; + enum wp_color_manager_v1_primaries primaries; + enum wp_color_manager_v1_transfer_function tf; +}; +struct Colorspace colorspace_mapping[] = { + { + .colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + .primaries = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, + .tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB, + }, + { + .colorspace = VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT, + .primaries = WP_COLOR_MANAGER_V1_PRIMARIES_DISPLAY_P3, + .tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB, + }, + { + .colorspace = VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT, + .primaries = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, + .tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR, + }, + { + .colorspace = VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT, + .primaries = WP_COLOR_MANAGER_V1_PRIMARIES_DISPLAY_P3, + .tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR, + }, + { + .colorspace = VK_COLOR_SPACE_BT709_LINEAR_EXT, + .primaries = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, + .tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR, + }, + { + .colorspace = VK_COLOR_SPACE_BT709_NONLINEAR_EXT, + .primaries = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, + .tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886, + }, + { + .colorspace = VK_COLOR_SPACE_BT2020_LINEAR_EXT, + .primaries = WP_COLOR_MANAGER_V1_PRIMARIES_BT2020, + .tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR, + }, + { + .colorspace = VK_COLOR_SPACE_HDR10_ST2084_EXT, + .primaries = WP_COLOR_MANAGER_V1_PRIMARIES_BT2020, + .tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ, + }, + /* VK_COLOR_SPACE_DOLBYVISION_EXT is left out because it's deprecated */ + { + .colorspace = VK_COLOR_SPACE_HDR10_HLG_EXT, + .primaries = WP_COLOR_MANAGER_V1_PRIMARIES_BT2020, + .tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_HLG, + }, + { + .colorspace = VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT, + .primaries = WP_COLOR_MANAGER_V1_PRIMARIES_ADOBE_RGB, + .tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR, + }, + /* VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT is left out because there's no + * exactly matching transfer function in the Wayland protocol */ + /* VK_COLOR_SPACE_PASS_THROUGH_EXT is handled elsewhere */ + { + .colorspace = VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT, + .primaries = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, + .tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_SRGB, + }, + /* VK_COLOR_SPACE_DISPLAY_NATIVE_AMD isn't supported */ + /* VK_COLORSPACE_SRGB_NONLINEAR_KHR is just an alias */ + /* VK_COLOR_SPACE_DCI_P3_LINEAR_EXT is just an alias */ +}; + +static int +wsi_wl_display_determine_colorspaces(struct wsi_wl_display *display) +{ + u_vector_finish(&display->colorspaces); + if (!u_vector_init(&display->colorspaces, 8, sizeof(VkColorSpaceKHR))) + return -1; + + /* SRGB_NONLINEAR is always supported. */ + VkColorSpaceKHR *new_cs = u_vector_add(&display->colorspaces); + if (!new_cs) + return -1; + *new_cs = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + + /* as is PASS_THROUGH */ + new_cs = u_vector_add(&display->colorspaces); + if (!new_cs) + return -1; + *new_cs = VK_COLOR_SPACE_PASS_THROUGH_EXT; + + if (!display->color_manager) + return 0; + + struct u_vector *tfs = &display->color_transfer_funcs; + struct u_vector *primaries = &display->color_primaries; + for (int i = 0; i < ARRAY_SIZE(colorspace_mapping); i++) { + if (!vector_contains(primaries, colorspace_mapping[i].primaries)) + continue; + if (!vector_contains(tfs, colorspace_mapping[i].tf)) + continue; + VkColorSpaceKHR *new_cs = u_vector_add(&display->colorspaces); + if (!new_cs) + return -1; + *new_cs = colorspace_mapping[i].colorspace; + } + return 0; +} + +static void +color_management_handle_supported_intent(void *data, + struct wp_color_manager_v1 *color_manager, + unsigned int intent) +{ + /* We only use the perceptual rendering intent, which is always supported. */ +} + +static void +color_management_handle_supported_features(void *data, + struct wp_color_manager_v1 *color_manager, + unsigned int feature) +{ + /* We don't use any non-default features yet. */ +} + +static void +color_management_handle_supported_tf_named(void *data, + struct wp_color_manager_v1 *color_manager, + unsigned int tf) +{ + struct wsi_wl_display *display = data; + unsigned int *new_tf = u_vector_add(&display->color_transfer_funcs); + if (new_tf) + *new_tf = tf; +} + +static void +color_management_handle_supported_primaries_named(void *data, + struct wp_color_manager_v1 *color_manager, + unsigned int primaries) +{ + struct wsi_wl_display *display = data; + unsigned int *new_primaries = u_vector_add(&display->color_primaries); + if (new_primaries) + *new_primaries = primaries; +} + +static void +color_management_handle_done(void *data, struct wp_color_manager_v1 *color_manager) +{ + /* Intentionally left blank */ +} + +static const struct wp_color_manager_v1_listener color_manager_listener = { + .supported_intent = color_management_handle_supported_intent, + .supported_feature = color_management_handle_supported_features, + .supported_tf_named = color_management_handle_supported_tf_named, + .supported_primaries_named = color_management_handle_supported_primaries_named, + .done = color_management_handle_done, +}; + +enum image_description_status { + undefined, + ready, + failed, +}; + +static void +color_management_handle_image_desc_failed(void *data, + struct wp_image_description_v1 *desc, + unsigned int cause, + const char *msg) +{ + enum image_description_status *status = data; + *status = failed; +} + +static void +color_management_handle_image_desc_ready(void *data, + struct wp_image_description_v1 *desc, + unsigned int id) +{ + enum image_description_status *status = data; + *status = ready; +} + +static const struct wp_image_description_v1_listener image_description_listener = { + .failed = color_management_handle_image_desc_failed, + .ready = color_management_handle_image_desc_ready, +}; + +static bool +needs_color_surface(VkColorSpaceKHR colorspace) +{ + return colorspace != VK_COLOR_SPACE_PASS_THROUGH_EXT; +} + +static void +wsi_wl_surface_add_color_refcount(struct wsi_wl_surface *wsi_surface) +{ + wsi_surface->color_surface_refcount++; + if (wsi_surface->color_surface_refcount == 1) { + wsi_surface->color_surface = + wp_color_manager_v1_get_surface(wsi_surface->display->color_manager, wsi_surface->surface); + } +} + +static void +wsi_wl_surface_remove_color_refcount(struct wsi_wl_surface *wsi_surface) +{ + wsi_surface->color_surface_refcount--; + if (wsi_surface->color_surface_refcount == 0) { + wp_color_management_surface_v1_destroy(wsi_surface->color_surface); + wsi_surface->color_surface = NULL; + } +} + +static VkResult +wsi_wl_swapchain_update_colorspace(struct wsi_wl_swapchain *chain) +{ + struct wsi_wl_surface *surface = chain->wsi_wl_surface; + struct wsi_wl_display *display = surface->display; + + /* we need the color management extension for + * everything except sRGB and PASS_THROUGH */ + if (!display->color_manager) { + if (chain->colorspace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR || + chain->colorspace == VK_COLOR_SPACE_PASS_THROUGH_EXT) { + return VK_SUCCESS; + } else { + return VK_ERROR_SURFACE_LOST_KHR; + } + } + + bool new_color_surface = !surface->color_surface; + bool needs_color_surface_new = needs_color_surface(chain->colorspace); + bool needs_color_surface_old = needs_color_surface(surface->colorspace); + if ((new_color_surface || !needs_color_surface_old) && needs_color_surface_new) { + wsi_wl_surface_add_color_refcount(surface); + } else if (needs_color_surface_old && !needs_color_surface_new) { + wsi_wl_surface_remove_color_refcount(surface); + } + + if (!new_color_surface && surface->colorspace == chain->colorspace) + return VK_SUCCESS; + + /* failure is fatal, so this potentially being wrong + in that case doesn't matter */ + surface->colorspace = chain->colorspace; + if (!needs_color_surface_new) + return VK_SUCCESS; + + struct wp_image_description_creator_params_v1 *creator = + wp_color_manager_v1_create_parametric_creator(display->color_manager); + + if (!creator) + return VK_ERROR_SURFACE_LOST_KHR; + + unsigned int primaries = 0; + unsigned int tf = 0; + for (int i = 0; i < ARRAY_SIZE(colorspace_mapping); i++) { + if (colorspace_mapping[i].colorspace == chain->colorspace) { + primaries = colorspace_mapping[i].primaries; + tf = colorspace_mapping[i].tf; + } + } + + if (!primaries) + return VK_ERROR_SURFACE_LOST_KHR; + + wp_image_description_creator_params_v1_set_primaries_named(creator, primaries); + wp_image_description_creator_params_v1_set_tf_named(creator, tf); + + wl_proxy_set_queue((struct wl_proxy *) creator, display->queue); + + struct wp_image_description_v1 *image_desc = + wp_image_description_creator_params_v1_create(creator); + if (!image_desc) + return VK_ERROR_SURFACE_LOST_KHR; + + enum image_description_status status = undefined; + wp_image_description_v1_add_listener(image_desc, &image_description_listener, &status); + + while (status == undefined) { + int ret = wl_display_dispatch_queue(display->wl_display, display->queue); + if (ret < 0) + return VK_ERROR_OUT_OF_DATE_KHR; + } + if (status == failed) + return VK_ERROR_SURFACE_LOST_KHR; + + wp_color_management_surface_v1_set_image_description(chain->wsi_wl_surface->color_surface, + image_desc, + WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL); + wp_image_description_v1_destroy(image_desc); + + return VK_SUCCESS; +} + + static void presentation_handle_clock_id(void* data, struct wp_presentation *wp_presentation, uint32_t clk_id) { @@ -942,6 +1268,17 @@ registry_handle_global(void *data, struct wl_registry *registry, display->commit_timing_manager = wl_registry_bind(registry, name, &wp_commit_timing_manager_v1_interface, 1); } + + if (strcmp(interface, wp_color_manager_v1_interface.name) == 0) { + display->color_manager = + wl_registry_bind(registry, name, &wp_color_manager_v1_interface, 1); + + u_vector_init(&display->color_primaries, 8, sizeof(uint32_t)); + u_vector_init(&display->color_transfer_funcs, 8, sizeof(uint32_t)); + + wp_color_manager_v1_add_listener(display->color_manager, + &color_manager_listener, display); + } } static void @@ -961,6 +1298,10 @@ wsi_wl_display_finish(struct wsi_wl_display *display) u_vector_foreach(f, &display->formats) u_vector_finish(&f->modifiers); u_vector_finish(&display->formats); + u_vector_finish(&display->colorspaces); + u_vector_finish(&display->color_primaries); + u_vector_finish(&display->color_transfer_funcs); + if (display->wl_shm) wl_shm_destroy(display->wl_shm); if (display->wl_syncobj) @@ -975,6 +1316,8 @@ wsi_wl_display_finish(struct wsi_wl_display *display) wp_commit_timing_manager_v1_destroy(display->commit_timing_manager); if (display->tearing_control_manager) wp_tearing_control_manager_v1_destroy(display->tearing_control_manager); + if (display->color_manager) + wp_color_manager_v1_destroy(display->color_manager); if (display->wl_display_wrapper) wl_proxy_wrapper_destroy(display->wl_display_wrapper); if (display->queue) @@ -1066,9 +1409,14 @@ wsi_wl_display_init(struct wsi_wayland *wsi_wl, } } - /* Round-trip again to get formats and modifiers */ + /* Round-trip again to get formats, modifiers and color properties */ wl_display_roundtrip_queue(display->wl_display, display->queue); + if (wsi_wl_display_determine_colorspaces(display) < 0) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail; + } + if (wsi_wl->wsi->force_bgra8_unorm_first) { /* Find BGRA8_UNORM in the list and swap it to the first position if we * can find it. Some apps get confused if SRGB is first in the list. @@ -1359,6 +1707,8 @@ wsi_wl_surface_get_formats(VkIcdSurfaceBase *icd_surface, VkSurfaceFormatKHR* pSurfaceFormats) { VkIcdSurfaceWayland *surface = (VkIcdSurfaceWayland *)icd_surface; + struct wsi_wl_surface *wsi_wl_surface = + wl_container_of((VkIcdSurfaceWayland *)icd_surface, wsi_wl_surface, base); struct wsi_wayland *wsi = (struct wsi_wayland *)wsi_device->wsi[VK_ICD_WSI_PLATFORM_WAYLAND]; @@ -1370,18 +1720,22 @@ wsi_wl_surface_get_formats(VkIcdSurfaceBase *icd_surface, VK_OUTARRAY_MAKE_TYPED(VkSurfaceFormatKHR, out, pSurfaceFormats, pSurfaceFormatCount); - struct wsi_wl_format *disp_fmt; - u_vector_foreach(disp_fmt, &display.formats) { - /* Skip formats for which we can't support both alpha & opaque - * formats. - */ - if (!(disp_fmt->flags & WSI_WL_FMT_ALPHA) || - !(disp_fmt->flags & WSI_WL_FMT_OPAQUE)) - continue; + VkColorSpaceKHR *cs; + u_vector_foreach(cs, &display.colorspaces) { + struct wsi_wl_format *disp_fmt; + u_vector_foreach(disp_fmt, &display.formats) { + /* Skip formats for which we can't support both alpha & opaque + * formats. + */ + if (!(disp_fmt->flags & WSI_WL_FMT_ALPHA) || + !(disp_fmt->flags & WSI_WL_FMT_OPAQUE)) { + continue; + } - vk_outarray_append_typed(VkSurfaceFormatKHR, &out, out_fmt) { - out_fmt->format = disp_fmt->vk_format; - out_fmt->colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + vk_outarray_append_typed(VkSurfaceFormatKHR, &out, out_fmt) { + out_fmt->format = disp_fmt->vk_format; + out_fmt->colorSpace = *cs; + } } } @@ -1398,6 +1752,8 @@ wsi_wl_surface_get_formats2(VkIcdSurfaceBase *icd_surface, VkSurfaceFormat2KHR* pSurfaceFormats) { VkIcdSurfaceWayland *surface = (VkIcdSurfaceWayland *)icd_surface; + struct wsi_wl_surface *wsi_wl_surface = + wl_container_of((VkIcdSurfaceWayland *)icd_surface, wsi_wl_surface, base); struct wsi_wayland *wsi = (struct wsi_wayland *)wsi_device->wsi[VK_ICD_WSI_PLATFORM_WAYLAND]; @@ -1409,18 +1765,22 @@ wsi_wl_surface_get_formats2(VkIcdSurfaceBase *icd_surface, VK_OUTARRAY_MAKE_TYPED(VkSurfaceFormat2KHR, out, pSurfaceFormats, pSurfaceFormatCount); - struct wsi_wl_format *disp_fmt; - u_vector_foreach(disp_fmt, &display.formats) { - /* Skip formats for which we can't support both alpha & opaque - * formats. - */ - if (!(disp_fmt->flags & WSI_WL_FMT_ALPHA) || - !(disp_fmt->flags & WSI_WL_FMT_OPAQUE)) - continue; + VkColorSpaceKHR *cs; + u_vector_foreach(cs, &display.colorspaces) { + struct wsi_wl_format *disp_fmt; + u_vector_foreach(disp_fmt, &display.formats) { + /* Skip formats for which we can't support both alpha & opaque + * formats. + */ + if (!(disp_fmt->flags & WSI_WL_FMT_ALPHA) || + !(disp_fmt->flags & WSI_WL_FMT_OPAQUE)) { + continue; + } - vk_outarray_append_typed(VkSurfaceFormat2KHR, &out, out_fmt) { - out_fmt->surfaceFormat.format = disp_fmt->vk_format; - out_fmt->surfaceFormat.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + vk_outarray_append_typed(VkSurfaceFormat2KHR, &out, out_fmt) { + out_fmt->surfaceFormat.format = disp_fmt->vk_format; + out_fmt->surfaceFormat.colorSpace = *cs; + } } } @@ -1516,6 +1876,9 @@ wsi_wl_surface_destroy(VkIcdSurfaceBase *icd_surface, VkInstance _instance, dmabuf_feedback_fini(&wsi_wl_surface->pending_dmabuf_feedback); } + if (wsi_wl_surface->color_surface) + wp_color_management_surface_v1_destroy(wsi_wl_surface->color_surface); + if (wsi_wl_surface->surface) wl_proxy_wrapper_destroy(wsi_wl_surface->surface); @@ -1838,6 +2201,9 @@ wsi_CreateWaylandSurfaceKHR(VkInstance _instance, surface->display = pCreateInfo->display; surface->surface = pCreateInfo->surface; + wsi_wl_surface->instance = instance; + wsi_wl_surface->colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + *pSurface = VkIcdSurfaceBase_to_handle(&surface->base); return VK_SUCCESS; @@ -2460,6 +2826,10 @@ wsi_wl_swapchain_queue_present(struct wsi_swapchain *wsi_chain, image->base.row_pitches[0] * chain->extent.height); } + VkResult ret = wsi_wl_swapchain_update_colorspace(chain); + if (ret != VK_SUCCESS) + return ret; + /* For EXT_swapchain_maintenance1. We might have transitioned from FIFO to MAILBOX. * In this case we need to let the FIFO request complete, before presenting MAILBOX. */ while (!chain->legacy_fifo_ready) { @@ -2816,6 +3186,9 @@ wsi_wl_swapchain_chain_free(struct wsi_wl_swapchain *chain, wl_callback_destroy(chain->frame); if (chain->tearing_control) wp_tearing_control_v1_destroy(chain->tearing_control); + if (needs_color_surface(chain->colorspace) && wsi_wl_surface->color_surface) { + wsi_wl_surface_remove_color_refcount(wsi_wl_surface); + } /* Only unregister if we are the non-retired swapchain, or * we are a retired swapchain and memory allocation failed, @@ -2980,6 +3353,8 @@ wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface, WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC); } + chain->colorspace = pCreateInfo->imageColorSpace; + enum wsi_wl_buffer_type buffer_type; struct wsi_base_image_params *image_params = NULL; struct wsi_cpu_image_params cpu_image_params;