wsi/display: Implement VK_EXT_hdr_metadata on KHR_display swapchain

Signed-off-by: Joshua Ashton <joshua@froggi.es>
Reviewed-by: Emma Anholt <emma@anholt.net>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/35461>
This commit is contained in:
Joshua Ashton 2022-11-14 11:27:38 +00:00 committed by Marge Bot
parent b465d144ca
commit b4176393a0

View file

@ -20,6 +20,35 @@
* OF THIS SOFTWARE.
*/
/*
* Colorimetry helper functions (color_xy_to_u16, nits_to_u16, nits_to_u16_dark),
* kindly taken from Weston:
* https://gitlab.freedesktop.org/wayland/weston/-/blob/main/libweston/backend-drm/kms-color.c
*
* Copyright 2021-2022 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "util/u_atomic.h"
#include "util/macros.h"
#include <stdlib.h>
@ -86,6 +115,8 @@ typedef struct wsi_display_mode {
enum connector_property {
CONN_CRTC_ID,
DPMS,
HDR_OUTPUT_METADATA,
Colorspace,
CONNECTOR_PROPERTY_MAX,
};
@ -112,6 +143,12 @@ enum plane_property {
PLANE_PROPERTY_MAX,
};
enum colorspace_enum {
COLORSPACE_Default,
COLORSPACE_BT2020_RGB,
COLORSPACE_ENUM_MAX,
};
typedef struct wsi_display_connector {
struct list_head list;
struct wsi_display *wsi;
@ -128,6 +165,8 @@ typedef struct wsi_display_connector {
uint32_t property[CONNECTOR_PROPERTY_MAX];
uint32_t crtc_property[CRTC_PROPERTY_MAX];
uint32_t plane_property[PLANE_PROPERTY_MAX];
uint32_t colorspace_enum[COLORSPACE_ENUM_MAX];
uint64_t color_outcome_serial;
#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
xcb_randr_output_t output;
#endif
@ -151,6 +190,12 @@ struct wsi_display {
pthread_t hotplug_thread;
struct list_head connectors; /* list of all discovered connectors */
/* A unique monotonically increasing value to associate with an individual
* colorimetry outcome on the output. This is used to avoid propagating
* dirty tracking flags across large numbers of objects.
*/
uint64_t color_outcome_serial_counter;
};
/**
@ -214,6 +259,8 @@ find_properties(struct wsi_display_connector *connector, int fd, uint32_t type)
STATIC_ASSERT(CRTC_ID == (enum plane_property) CONN_CRTC_ID);
PROPERTY(CRTC_ID);
PROPERTY(DPMS);
PROPERTY(HDR_OUTPUT_METADATA);
PROPERTY(Colorspace);
break;
case DRM_MODE_OBJECT_CRTC:
PROPERTY(MODE_ID);
@ -236,6 +283,18 @@ find_properties(struct wsi_display_connector *connector, int fd, uint32_t type)
break;
}
#undef PROPERTY
if (!strcmp(prop->name, "Colorspace")) {
assert(prop->flags & DRM_MODE_PROP_ENUM);
#define COLORSPACE_ENUM(x) if (!strcmp(en->name, #x)) connector->colorspace_enum[COLORSPACE_##x] = en->value
for (int e = 0; e < prop->count_enums; e++) {
struct drm_mode_property_enum *en = &prop->enums[e];
COLORSPACE_ENUM(Default);
COLORSPACE_ENUM(BT2020_RGB);
}
#undef COLORSPACE_ENUM
}
drmModeFreeProperty(prop);
}
@ -289,6 +348,10 @@ struct wsi_display_swapchain {
uint64_t present_id;
VkResult present_id_error;
/* A unique ID for the color outcome of the swapchain. A serial of 0 means unset/default. */
uint64_t color_outcome_serial;
VkHdrMetadataEXT hdr_metadata;
struct wsi_display_image images[0];
};
@ -2244,6 +2307,105 @@ wsi_register_vblank_event(struct wsi_display_fence *fence,
}
}
static inline uint16_t
color_xy_to_u16(float v)
{
assert(v >= 0.0f);
assert(v <= 1.0f);
/*
* CTA-861-G
* 6.9.1 Static Metadata Type 1
* chromaticity coordinate encoding
*/
return (uint16_t)round(v * 50000.0);
}
static inline uint16_t
nits_to_u16(float nits)
{
assert(nits >= 1.0f);
assert(nits <= 65535.0f);
/*
* CTA-861-G
* 6.9.1 Static Metadata Type 1
* max display mastering luminance, max content light level,
* max frame-average light level
*/
return (uint16_t)round(nits);
}
static inline uint16_t
nits_to_u16_dark(float nits)
{
assert(nits >= 0.0001f);
assert(nits <= 6.5535f);
/*
* CTA-861-G
* 6.9.1 Static Metadata Type 1
* min display mastering luminance
*/
return (uint16_t)round(nits * 10000.0);
}
/* from CTA-861-G */
#define HDMI_EOTF_SDR 0
#define HDMI_EOTF_TRADITIONAL_HDR 1
#define HDMI_EOTF_ST2084 2
#define HDMI_EOTF_HLG 3
static int
_wsi_hdmi_metadata_eotf_from_vk_colorspace(VkColorSpaceKHR color_space)
{
switch (color_space) {
default:
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
return HDMI_EOTF_SDR;
case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:
return HDMI_EOTF_TRADITIONAL_HDR;
case VK_COLOR_SPACE_HDR10_ST2084_EXT:
return HDMI_EOTF_ST2084;
case VK_COLOR_SPACE_HDR10_HLG_EXT:
return HDMI_EOTF_HLG;
}
}
static enum colorspace_enum
vk_colorspace_to_drm_colorspace(VkColorSpaceKHR color_space)
{
switch (color_space) {
default:
case VK_COLORSPACE_SRGB_NONLINEAR_KHR:
return COLORSPACE_Default;
case VK_COLOR_SPACE_HDR10_ST2084_EXT:
return COLORSPACE_BT2020_RGB;
}
}
static void
_wsi_display_convert_hdr_metadata(VkHdrMetadataEXT *pMetadata, uint8_t hdmi_eotf, struct hdr_output_metadata *drm_metadata)
{
memset(drm_metadata, 0, sizeof(struct hdr_output_metadata));
drm_metadata->metadata_type = 0;
drm_metadata->hdmi_metadata_type1.eotf = hdmi_eotf;
drm_metadata->hdmi_metadata_type1.metadata_type = drm_metadata->metadata_type; /* duplicated */
if (drm_metadata->hdmi_metadata_type1.eotf == HDMI_EOTF_ST2084) {
drm_metadata->hdmi_metadata_type1.display_primaries[0].x = color_xy_to_u16(pMetadata->displayPrimaryRed.x);
drm_metadata->hdmi_metadata_type1.display_primaries[0].y = color_xy_to_u16(pMetadata->displayPrimaryRed.y);
drm_metadata->hdmi_metadata_type1.display_primaries[1].x = color_xy_to_u16(pMetadata->displayPrimaryGreen.x);
drm_metadata->hdmi_metadata_type1.display_primaries[1].y = color_xy_to_u16(pMetadata->displayPrimaryGreen.y);
drm_metadata->hdmi_metadata_type1.display_primaries[2].x = color_xy_to_u16(pMetadata->displayPrimaryBlue.x);
drm_metadata->hdmi_metadata_type1.display_primaries[2].y = color_xy_to_u16(pMetadata->displayPrimaryBlue.y);
drm_metadata->hdmi_metadata_type1.white_point.x = color_xy_to_u16(pMetadata->whitePoint.x);
drm_metadata->hdmi_metadata_type1.white_point.y = color_xy_to_u16(pMetadata->whitePoint.y);
drm_metadata->hdmi_metadata_type1.max_display_mastering_luminance = nits_to_u16(pMetadata->maxLuminance);
drm_metadata->hdmi_metadata_type1.min_display_mastering_luminance = nits_to_u16_dark(pMetadata->minLuminance);
drm_metadata->hdmi_metadata_type1.max_cll = nits_to_u16(pMetadata->maxContentLightLevel);
drm_metadata->hdmi_metadata_type1.max_fall = nits_to_u16(pMetadata->maxFrameAverageLightLevel);
}
}
static int
drm_atomic_commit(wsi_display_connector *connector, struct wsi_display_image *image)
{
@ -2285,6 +2447,36 @@ drm_atomic_commit(wsi_display_connector *connector, struct wsi_display_image *im
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
}
if (connector->color_outcome_serial != image->chain->color_outcome_serial) {
if (connector->property[HDR_OUTPUT_METADATA] != -1) {
const uint8_t hdmi_eotf =
_wsi_hdmi_metadata_eotf_from_vk_colorspace(image->chain->base.image_info.color_space);
blob_id = 0;
/* Only bother making a blob if we are HDR, otherwise set it to 0 (empty). */
if (hdmi_eotf != HDMI_EOTF_SDR) {
struct hdr_output_metadata drm_metadata;
_wsi_display_convert_hdr_metadata(&image->chain->hdr_metadata, hdmi_eotf, &drm_metadata);
if (drmModeCreatePropertyBlob(image->chain->wsi->fd, &drm_metadata,
sizeof(drm_metadata), &blob_id) != 0)
return -1;
}
drmModeAtomicAddProperty(req, connector->id, connector->property[HDR_OUTPUT_METADATA], blob_id);
}
if (connector->property[Colorspace] != -1) {
const enum colorspace_enum drm_colorspace =
vk_colorspace_to_drm_colorspace(image->chain->base.image_info.color_space);
drmModeAtomicAddProperty(req, connector->id, connector->property[Colorspace],
connector->colorspace_enum[drm_colorspace]);
}
connector->color_outcome_serial = image->chain->color_outcome_serial;
}
const uint32_t *prop = connector->plane_property;
drmModeAtomicAddProperty(req, plane_id, prop[FB_ID], image->fb_id);
drmModeAtomicAddProperty(req, plane_id, prop[CRTC_ID], crtc_id);
@ -2463,6 +2655,16 @@ wsi_display_wait_for_present(struct wsi_swapchain *wsi_chain,
return result;
}
static void
wsi_display_set_hdr_metadata(struct wsi_swapchain *wsi_chain,
const VkHdrMetadataEXT* pMetadata)
{
struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *)wsi_chain;
chain->color_outcome_serial = p_atomic_inc_return(&chain->wsi->color_outcome_serial_counter);
chain->hdr_metadata = *pMetadata;
}
static VkResult
wsi_display_surface_create_swapchain(
VkIcdSurfaceBase *icd_surface,
@ -2557,8 +2759,10 @@ wsi_display_surface_create_swapchain(
chain->base.queue_present = wsi_display_queue_present;
chain->base.wait_for_present = wsi_display_wait_for_present;
chain->base.wait_for_present2 = wsi_display_wait_for_present;
chain->base.set_hdr_metadata = wsi_display_set_hdr_metadata;
chain->base.present_mode = wsi_swapchain_get_present_mode(wsi_device, create_info);
chain->base.image_count = num_images;
chain->color_outcome_serial = 0;
chain->wsi = wsi;
chain->status = VK_SUCCESS;