video: Expose HDR metadata per-window

Moves the HDR properties from the display to be per-window, and adds the frog_color protocol to enable HDR under Wayland.
This commit is contained in:
Frank Praznik 2024-06-17 19:23:10 -04:00
parent 0383333b54
commit 2f276a2eea
18 changed files with 526 additions and 104 deletions

View File

@ -94,14 +94,13 @@ typedef enum SDL_EventType
/* Display events */
/* 0x150 was SDL_DISPLAYEVENT, reserve the number for sdl2-compat */
SDL_EVENT_DISPLAY_ORIENTATION = 0x151, /**< Display orientation has changed to data1 */
SDL_EVENT_DISPLAY_ADDED, /**< Display has been added to the system */
SDL_EVENT_DISPLAY_REMOVED, /**< Display has been removed from the system */
SDL_EVENT_DISPLAY_MOVED, /**< Display has changed position */
SDL_EVENT_DISPLAY_ORIENTATION = 0x151, /**< Display orientation has changed to data1 */
SDL_EVENT_DISPLAY_ADDED, /**< Display has been added to the system */
SDL_EVENT_DISPLAY_REMOVED, /**< Display has been removed from the system */
SDL_EVENT_DISPLAY_MOVED, /**< Display has changed position */
SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED, /**< Display has changed content scale */
SDL_EVENT_DISPLAY_HDR_STATE_CHANGED, /**< Display HDR properties have changed */
SDL_EVENT_DISPLAY_FIRST = SDL_EVENT_DISPLAY_ORIENTATION,
SDL_EVENT_DISPLAY_LAST = SDL_EVENT_DISPLAY_HDR_STATE_CHANGED,
SDL_EVENT_DISPLAY_LAST = SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED,
/* Window events */
/* 0x200 was SDL_WINDOWEVENT, reserve the number for sdl2-compat */
@ -134,6 +133,7 @@ typedef enum SDL_EventType
associated with it are invalid */
SDL_EVENT_WINDOW_PEN_ENTER, /**< Window has gained focus of the pressure-sensitive pen with ID "data1" */
SDL_EVENT_WINDOW_PEN_LEAVE, /**< Window has lost focus of the pressure-sensitive pen with ID "data1" */
SDL_EVENT_WINDOW_HDR_STATE_CHANGED, /**< Window HDR properties have changed */
SDL_EVENT_WINDOW_FIRST = SDL_EVENT_WINDOW_SHOWN,
SDL_EVENT_WINDOW_LAST = SDL_EVENT_WINDOW_PEN_LEAVE,

View File

@ -411,17 +411,9 @@ extern SDL_DECLSPEC SDL_DisplayID SDLCALL SDL_GetPrimaryDisplay(void);
* The following read-only properties are provided by SDL:
*
* - `SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN`: true if the display has HDR
* headroom above the SDR white point. This property can change dynamically
* when SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent.
* - `SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT`: the value of SDR white in the
* SDL_COLORSPACE_SRGB_LINEAR colorspace. On Windows this corresponds to the
* SDR white level in scRGB colorspace, and on Apple platforms this is
* always 1.0 for EDR content. This property can change dynamically when
* SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent.
* - `SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT`: the additional high dynamic range
* that can be displayed, in terms of the SDR white point. When HDR is not
* enabled, this will be 1.0. This property can change dynamically when
* SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent.
* headroom above the SDR white point. This is for informational and diagnostic
* purposes only, as not all platforms provide this information at the display
* level.
*
* On KMS/DRM:
*
@ -443,8 +435,6 @@ extern SDL_DECLSPEC SDL_DisplayID SDLCALL SDL_GetPrimaryDisplay(void);
extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetDisplayProperties(SDL_DisplayID displayID);
#define SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN "SDL.display.HDR_enabled"
#define SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT "SDL.display.SDR_white_point"
#define SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT "SDL.display.HDR_headroom"
#define SDL_PROP_DISPLAY_KMSDRM_PANEL_ORIENTATION_NUMBER "SDL.display.KMSDRM.panel_orientation"
/**
@ -1113,6 +1103,18 @@ extern SDL_DECLSPEC SDL_Window *SDLCALL SDL_GetWindowParent(SDL_Window *window);
*
* - `SDL_PROP_WINDOW_SHAPE_POINTER`: the surface associated with a shaped
* window
* - `SDL_PROP_WINDOW_HDR_ENABLED_BOOLEAN`: true if the window has HDR
* headroom above the SDR white point. This property can change dynamically
* when SDL_EVENT_WINDOW_HDR_STATE_CHANGED is sent.
* - `SDL_PROP_WINDOW_SDR_WHITE_LEVEL_FLOAT`: the value of SDR white in the
* SDL_COLORSPACE_SRGB_LINEAR colorspace. On Windows this corresponds to the
* SDR white level in scRGB colorspace, and on Apple platforms this is
* always 1.0 for EDR content. This property can change dynamically when
* SDL_EVENT_WINDOW_HDR_STATE_CHANGED is sent.
* - `SDL_PROP_WINDOW_HDR_HEADROOM_FLOAT`: the additional high dynamic range
* that can be displayed, in terms of the SDR white point. When HDR is not
* enabled, this will be 1.0. This property can change dynamically when
* SDL_EVENT_WINDOW_HDR_STATE_CHANGED is sent.
*
* On Android:
*
@ -1216,6 +1218,9 @@ extern SDL_DECLSPEC SDL_Window *SDLCALL SDL_GetWindowParent(SDL_Window *window);
extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetWindowProperties(SDL_Window *window);
#define SDL_PROP_WINDOW_SHAPE_POINTER "SDL.window.shape"
#define SDL_PROP_WINDOW_HDR_ENABLED_BOOLEAN "SDL.window.HDR_enabled"
#define SDL_PROP_WINDOW_SDR_WHITE_LEVEL_FLOAT "SDL.window.SDR_white_level"
#define SDL_PROP_WINDOW_HDR_HEADROOM_FLOAT "SDL.window.HDR_headroom"
#define SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER "SDL.window.android.window"
#define SDL_PROP_WINDOW_ANDROID_SURFACE_POINTER "SDL.window.android.surface"
#define SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER "SDL.window.uikit.window"

View File

@ -285,7 +285,6 @@ static void SDL_LogEvent(const SDL_Event *event)
SDL_DISPLAYEVENT_CASE(SDL_EVENT_DISPLAY_REMOVED);
SDL_DISPLAYEVENT_CASE(SDL_EVENT_DISPLAY_MOVED);
SDL_DISPLAYEVENT_CASE(SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED);
SDL_DISPLAYEVENT_CASE(SDL_EVENT_DISPLAY_HDR_STATE_CHANGED);
#undef SDL_DISPLAYEVENT_CASE
#define SDL_WINDOWEVENT_CASE(x) \
@ -319,6 +318,7 @@ static void SDL_LogEvent(const SDL_Event *event)
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_ENTER_FULLSCREEN);
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_LEAVE_FULLSCREEN);
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_DESTROYED);
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_HDR_STATE_CHANGED);
#undef SDL_WINDOWEVENT_CASE
#define PRINT_KEYDEV_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%u)", (uint)event->kdevice.timestamp, (uint)event->kdevice.which)

View File

@ -737,16 +737,11 @@ static void UpdateMainViewDimensions(SDL_Renderer *renderer)
static void UpdateHDRProperties(SDL_Renderer *renderer)
{
SDL_DisplayID displayID = SDL_GetDisplayForWindow(renderer->window);
SDL_PropertiesID display_props;
SDL_PropertiesID window_props;
SDL_PropertiesID renderer_props;
if (!displayID) {
return;
}
display_props = SDL_GetDisplayProperties(displayID);
if (!display_props) {
window_props = SDL_GetWindowProperties(renderer->window);
if (!window_props) {
return;
}
@ -758,8 +753,8 @@ static void UpdateHDRProperties(SDL_Renderer *renderer)
renderer->color_scale /= renderer->SDR_white_point;
if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
renderer->SDR_white_point = SDL_GetFloatProperty(display_props, SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT, 1.0f);
renderer->HDR_headroom = SDL_GetFloatProperty(display_props, SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT, 1.0f);
renderer->SDR_white_point = SDL_GetFloatProperty(window_props, SDL_PROP_WINDOW_SDR_WHITE_LEVEL_FLOAT, 1.0f);
renderer->HDR_headroom = SDL_GetFloatProperty(window_props, SDL_PROP_WINDOW_HDR_HEADROOM_FLOAT, 1.0f);
} else {
renderer->SDR_white_point = 1.0f;
renderer->HDR_headroom = 1.0f;
@ -836,7 +831,7 @@ static int SDLCALL SDL_RendererEventWatch(void *userdata, SDL_Event *event)
UpdateHDRProperties(renderer);
}
}
} else if (event->type == SDL_EVENT_DISPLAY_HDR_STATE_CHANGED) {
} else if (event->type == SDL_EVENT_WINDOW_HDR_STATE_CHANGED) {
UpdateHDRProperties(renderer);
}

View File

@ -1576,12 +1576,6 @@ static void SDLTest_PrintEvent(const SDL_Event *event)
event->display.displayID, (int)(scale * 100.0f));
}
break;
case SDL_EVENT_DISPLAY_HDR_STATE_CHANGED:
{
SDL_Log("SDL EVENT: Display %" SDL_PRIu32 " HDR %s",
event->display.displayID, event->display.data1 ? "enabled" : "disabled");
}
break;
case SDL_EVENT_DISPLAY_MOVED:
SDL_Log("SDL EVENT: Display %" SDL_PRIu32 " changed position",
event->display.displayID);
@ -1668,6 +1662,9 @@ static void SDLTest_PrintEvent(const SDL_Event *event)
case SDL_EVENT_WINDOW_DESTROYED:
SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " destroyed", event->window.windowID);
break;
case SDL_EVENT_WINDOW_HDR_STATE_CHANGED:
SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " HDR %s", event->window.windowID, event->window.data1 ? "enabled" : "disabled");
break;
case SDL_EVENT_KEYBOARD_ADDED:
SDL_Log("SDL EVENT: Keyboard %" SDL_PRIu32 " attached",
event->kdevice.which);

View File

@ -34,6 +34,12 @@ typedef struct SDL_DisplayData SDL_DisplayData;
typedef struct SDL_DisplayModeData SDL_DisplayModeData;
typedef struct SDL_WindowData SDL_WindowData;
typedef struct
{
float SDR_white_level;
float HDR_headroom;
} SDL_HDROutputProperties;
/* Define the SDL window structure, corresponding to toplevel windows */
struct SDL_Window
{
@ -82,6 +88,7 @@ struct SDL_Window
SDL_DisplayMode requested_fullscreen_mode;
SDL_DisplayMode current_fullscreen_mode;
SDL_HDROutputProperties HDR;
float opacity;
@ -119,12 +126,6 @@ struct SDL_Window
#define SDL_WINDOW_IS_POPUP(W) \
(((W)->flags & (SDL_WINDOW_TOOLTIP | SDL_WINDOW_POPUP_MENU)) != 0)
typedef struct
{
float SDR_white_point;
float HDR_headroom;
} SDL_HDRDisplayProperties;
/*
* Define the SDL display structure.
* This corresponds to physical monitors attached to the system.
@ -141,7 +142,7 @@ struct SDL_VideoDisplay
SDL_DisplayOrientation natural_orientation;
SDL_DisplayOrientation current_orientation;
float content_scale;
SDL_HDRDisplayProperties HDR;
SDL_HDROutputProperties HDR;
SDL_Window *fullscreen_window;
@ -160,7 +161,8 @@ typedef enum
VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS = 0x04,
VIDEO_DEVICE_CAPS_FULLSCREEN_ONLY = 0x08,
VIDEO_DEVICE_CAPS_SENDS_DISPLAY_CHANGES = 0x10,
VIDEO_DEVICE_CAPS_DISABLE_MOUSE_WARP_ON_FULLSCREEN_TRANSITIONS = 0x20
VIDEO_DEVICE_CAPS_DISABLE_MOUSE_WARP_ON_FULLSCREEN_TRANSITIONS = 0x20,
VIDEO_DEVICE_CAPS_SENDS_HDR_CHANGES = 0x40
} DeviceCaps;
/* Fullscreen operations */
@ -511,7 +513,7 @@ extern void SDL_ResetFullscreenDisplayModes(SDL_VideoDisplay *display);
extern void SDL_SetDesktopDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode);
extern void SDL_SetCurrentDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode);
extern void SDL_SetDisplayContentScale(SDL_VideoDisplay *display, float scale);
extern void SDL_SetDisplayHDRProperties(SDL_VideoDisplay *display, const SDL_HDRDisplayProperties *HDR);
extern void SDL_SetDisplayHDRProperties(SDL_VideoDisplay *display, const SDL_HDROutputProperties *HDR);
extern int SDL_SetDisplayModeForDisplay(SDL_VideoDisplay *display, SDL_DisplayMode *mode);
extern SDL_VideoDisplay *SDL_GetVideoDisplay(SDL_DisplayID display);
extern SDL_DisplayID SDL_GetDisplayForWindowPosition(SDL_Window *window);
@ -521,6 +523,7 @@ extern int SDL_GetDisplayIndex(SDL_DisplayID displayID);
extern SDL_DisplayData *SDL_GetDisplayDriverData(SDL_DisplayID display);
extern SDL_DisplayData *SDL_GetDisplayDriverDataForWindow(SDL_Window *window);
extern int SDL_GetMessageBoxCount(void);
extern void SDL_SetWindowHDRProperties(SDL_Window *window, const SDL_HDROutputProperties *HDR, SDL_bool send_event);
extern void SDL_GL_DeduceMaxSupportedESProfile(int *major, int *minor);

View File

@ -195,6 +195,11 @@ static SDL_bool SDL_DisableMouseWarpOnFullscreenTransitions(SDL_VideoDevice *_th
return !!(_this->device_caps & VIDEO_DEVICE_CAPS_DISABLE_MOUSE_WARP_ON_FULLSCREEN_TRANSITIONS);
}
static SDL_bool SDL_DriverSendsHDRChanges(SDL_VideoDevice *_this)
{
return !!(_this->device_caps & VIDEO_DEVICE_CAPS_SENDS_HDR_CHANGES);
}
/* Hint to treat all window ops as synchronous */
static SDL_bool syncHint;
@ -763,23 +768,11 @@ SDL_DisplayID SDL_AddVideoDisplay(const SDL_VideoDisplay *display, SDL_bool send
new_display->fullscreen_modes[i].displayID = id;
}
props = SDL_GetDisplayProperties(id);
new_display->HDR.HDR_headroom = SDL_max(display->HDR.HDR_headroom, 1.0f);
new_display->HDR.SDR_white_level = SDL_max(display->HDR.SDR_white_level, 1.0f);
if (display->HDR.HDR_headroom > 1.0f) {
SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_TRUE);
} else {
SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE);
}
if (display->HDR.SDR_white_point <= 1.0f) {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT, 1.0f);
} else {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT, display->HDR.SDR_white_point);
}
if (display->HDR.HDR_headroom <= 1.0f) {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT, 1.0f);
} else {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT, display->HDR.HDR_headroom);
}
props = SDL_GetDisplayProperties(id);
SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, new_display->HDR.HDR_headroom > 1.0f);
SDL_UpdateDesktopBounds();
@ -1053,37 +1046,42 @@ float SDL_GetDisplayContentScale(SDL_DisplayID displayID)
return display->content_scale;
}
void SDL_SetDisplayHDRProperties(SDL_VideoDisplay *display, const SDL_HDRDisplayProperties *HDR)
void SDL_SetWindowHDRProperties(SDL_Window *window, const SDL_HDROutputProperties *HDR, SDL_bool send_event)
{
if (window->HDR.HDR_headroom != HDR->HDR_headroom || window->HDR.SDR_white_level != window->HDR.SDR_white_level) {
SDL_PropertiesID window_props = SDL_GetWindowProperties(window);
SDL_SetFloatProperty(window_props, SDL_PROP_WINDOW_HDR_HEADROOM_FLOAT, SDL_max(HDR->HDR_headroom, 1.0f));
SDL_SetFloatProperty(window_props, SDL_PROP_WINDOW_SDR_WHITE_LEVEL_FLOAT, SDL_max(HDR->SDR_white_level, 1.0f));
SDL_SetBooleanProperty(window_props, SDL_PROP_WINDOW_HDR_ENABLED_BOOLEAN, HDR->HDR_headroom > 1.0f);
SDL_copyp(&window->HDR, HDR);
if (send_event) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_HDR_STATE_CHANGED, HDR->HDR_headroom > 1.0f, 0);
}
}
}
void SDL_SetDisplayHDRProperties(SDL_VideoDisplay *display, const SDL_HDROutputProperties *HDR)
{
SDL_PropertiesID props = SDL_GetDisplayProperties(display->id);
SDL_bool changed = SDL_FALSE;
if (HDR->SDR_white_point != display->HDR.SDR_white_point) {
if (HDR->SDR_white_point <= 1.0f) {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT, 1.0f);
} else {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT, HDR->SDR_white_point);
}
if (HDR->SDR_white_level != display->HDR.SDR_white_level) {
display->HDR.SDR_white_level = SDL_max(HDR->SDR_white_level, 1.0f);
changed = SDL_TRUE;
}
if (HDR->HDR_headroom != display->HDR.HDR_headroom) {
if (HDR->HDR_headroom > 1.0f) {
SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_TRUE);
} else {
SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE);
}
if (HDR->HDR_headroom <= 1.0f) {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT, 1.0f);
} else {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT, HDR->HDR_headroom);
}
display->HDR.HDR_headroom = SDL_max(HDR->HDR_headroom, 1.0f);
changed = SDL_TRUE;
}
SDL_copyp(&display->HDR, HDR);
if (changed) {
SDL_bool enabled = SDL_GetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE);
SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_HDR_STATE_CHANGED, enabled);
if (changed && !SDL_DriverSendsHDRChanges(_this)) {
for (SDL_Window *w = display->device->windows; w; w = w->next) {
if (SDL_GetDisplayForWindow(w) == display->id) {
SDL_SetWindowHDRProperties(w, &display->HDR, SDL_TRUE);
}
}
}
}
@ -2247,8 +2245,10 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props)
window->undefined_x = undefined_x;
window->undefined_y = undefined_y;
SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window);
SDL_SetWindowHDRProperties(window, &display->HDR, SDL_FALSE);
if (flags & SDL_WINDOW_FULLSCREEN || IsFullscreenOnly(_this)) {
SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window);
SDL_Rect bounds;
SDL_GetDisplayBounds(display->id, &bounds);

View File

@ -290,9 +290,9 @@ static char *Cocoa_GetDisplayName(CGDirectDisplayID displayID)
return displayName;
}
static void Cocoa_GetHDRProperties(CGDirectDisplayID displayID, SDL_HDRDisplayProperties *HDR)
static void Cocoa_GetHDRProperties(CGDirectDisplayID displayID, SDL_HDROutputProperties *HDR)
{
HDR->SDR_white_point = 1.0f;
HDR->SDR_white_level = 1.0f;
HDR->HDR_headroom = 1.0f;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 /* Added in the 10.15 SDK */
@ -397,7 +397,7 @@ void Cocoa_InitModes(SDL_VideoDevice *_this)
void Cocoa_UpdateDisplays(SDL_VideoDevice *_this)
{
SDL_HDRDisplayProperties HDR;
SDL_HDROutputProperties HDR;
int i;
for (i = 0; i < _this->num_displays; ++i) {

View File

@ -242,7 +242,7 @@ int UIKit_AddDisplay(UIScreen *uiscreen, SDL_bool send_event)
}
display.desktop_mode = mode;
display.HDR.SDR_white_point = 1.0f;
display.HDR.SDR_white_level = 1.0f;
display.HDR.HDR_headroom = 1.0f;
#ifndef SDL_PLATFORM_TVOS

View File

@ -46,6 +46,7 @@
#include "alpha-modifier-v1-client-protocol.h"
#include "cursor-shape-v1-client-protocol.h"
#include "fractional-scale-v1-client-protocol.h"
#include "frog-color-management-v1-client-protocol.h"
#include "idle-inhibit-unstable-v1-client-protocol.h"
#include "input-timestamps-unstable-v1-client-protocol.h"
#include "kde-output-order-v1-client-protocol.h"
@ -526,7 +527,8 @@ static SDL_VideoDevice *Wayland_CreateDevice(void)
VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT |
VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS |
VIDEO_DEVICE_CAPS_SENDS_DISPLAY_CHANGES |
VIDEO_DEVICE_CAPS_DISABLE_MOUSE_WARP_ON_FULLSCREEN_TRANSITIONS;
VIDEO_DEVICE_CAPS_DISABLE_MOUSE_WARP_ON_FULLSCREEN_TRANSITIONS |
VIDEO_DEVICE_CAPS_SENDS_HDR_CHANGES;
return device;
}
@ -1114,6 +1116,8 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint
} else if (SDL_strcmp(interface, "kde_output_order_v1") == 0) {
d->kde_output_order = wl_registry_bind(d->registry, id, &kde_output_order_v1_interface, 1);
kde_output_order_v1_add_listener(d->kde_output_order, &kde_output_order_listener, d);
} else if (SDL_strcmp(interface, "frog_color_management_factory_v1") == 0) {
d->frog_color_management_factory_v1 = wl_registry_bind(d->registry, id, &frog_color_management_factory_v1_interface, 1);
}
}
@ -1384,6 +1388,11 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this)
data->kde_output_order = NULL;
}
if (data->frog_color_management_factory_v1) {
frog_color_management_factory_v1_destroy(data->frog_color_management_factory_v1);
data->frog_color_management_factory_v1 = NULL;
}
if (data->compositor) {
wl_compositor_destroy(data->compositor);
data->compositor = NULL;

View File

@ -83,6 +83,7 @@ struct SDL_VideoData
struct xdg_wm_dialog_v1 *xdg_wm_dialog_v1;
struct wp_alpha_modifier_v1 *wp_alpha_modifier_v1;
struct kde_output_order_v1 *kde_output_order;
struct frog_color_management_factory_v1 *frog_color_management_factory_v1;
struct xkb_context *xkb_context;
struct SDL_WaylandInput *input;

View File

@ -41,6 +41,7 @@
#include "fractional-scale-v1-client-protocol.h"
#include "xdg-foreign-unstable-v2-client-protocol.h"
#include "xdg-dialog-v1-client-protocol.h"
#include "frog-color-management-v1-client-protocol.h"
#ifdef HAVE_LIBDECOR_H
#include <libdecor.h>
@ -1486,6 +1487,47 @@ static const struct wp_fractional_scale_v1_listener fractional_scale_listener =
handle_preferred_fractional_scale
};
static void frog_preferred_metadata_handler(void *data, struct frog_color_managed_surface *frog_color_managed_surface, uint32_t transfer_function,
uint32_t output_display_primary_red_x, uint32_t output_display_primary_red_y,
uint32_t output_display_primary_green_x, uint32_t output_display_primary_green_y,
uint32_t output_display_primary_blue_x, uint32_t output_display_primary_blue_y,
uint32_t output_white_point_x, uint32_t output_white_point_y,
uint32_t max_luminance, uint32_t min_luminance,
uint32_t max_full_frame_luminance)
{
SDL_WindowData *wind = (SDL_WindowData *)data;
SDL_HDROutputProperties HDR;
SDL_zero(HDR);
switch (transfer_function) {
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ:
/* ITU-R BT.2408-7 (Sept 2023) has the reference PQ white level at 203 nits,
* while older Dolby documentation claims a reference level of 100 nits.
*
* Use 203 nits for now.
*/
HDR.HDR_headroom = max_luminance / 203.0f;
break;
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SCRGB_LINEAR:
HDR.HDR_headroom = max_luminance / 80.0f;
break;
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_UNDEFINED:
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SRGB:
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_GAMMA_22:
default:
HDR.HDR_headroom = 1.0f;
break;
}
HDR.SDR_white_level = 1.0f;
SDL_SetWindowHDRProperties(wind->sdlwindow, &HDR, SDL_TRUE);
}
static const struct frog_color_managed_surface_listener frog_surface_listener = {
frog_preferred_metadata_handler
};
static void SetKeyboardFocus(SDL_Window *window)
{
SDL_Window *kb_focus = SDL_GetKeyboardFocus();
@ -2366,9 +2408,16 @@ int Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Propert
}
}
if (!custom_surface_role && c->wp_alpha_modifier_v1) {
data->wp_alpha_modifier_surface_v1 = wp_alpha_modifier_v1_get_surface(c->wp_alpha_modifier_v1, data->surface);
wp_alpha_modifier_surface_v1_set_multiplier(data->wp_alpha_modifier_surface_v1, SDL_MAX_UINT32);
if (!custom_surface_role) {
if (c->frog_color_management_factory_v1) {
data->frog_color_managed_surface = frog_color_management_factory_v1_get_color_managed_surface(c->frog_color_management_factory_v1, data->surface);
frog_color_managed_surface_add_listener(data->frog_color_managed_surface, &frog_surface_listener, data);
}
if (c->wp_alpha_modifier_v1) {
data->wp_alpha_modifier_surface_v1 = wp_alpha_modifier_v1_get_surface(c->wp_alpha_modifier_v1, data->surface);
wp_alpha_modifier_surface_v1_set_multiplier(data->wp_alpha_modifier_surface_v1, SDL_MAX_UINT32);
}
}
/* Must be called before EGL configuration to set the drawable backbuffer size. */
@ -2701,6 +2750,10 @@ void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
wp_alpha_modifier_surface_v1_destroy(wind->wp_alpha_modifier_surface_v1);
}
if (wind->frog_color_managed_surface) {
frog_color_managed_surface_destroy(wind->frog_color_managed_surface);
}
SDL_free(wind->outputs);
SDL_free(wind->app_id);

View File

@ -98,6 +98,7 @@ struct SDL_WindowData
struct zxdg_exported_v2 *exported;
struct xdg_dialog_v1 *xdg_dialog_v1;
struct wp_alpha_modifier_surface_v1 *wp_alpha_modifier_surface_v1;
struct frog_color_managed_surface *frog_color_managed_surface;
SDL_AtomicInt swap_interval_ready;

View File

@ -443,7 +443,7 @@ static float WIN_GetSDRWhitePoint(SDL_VideoDevice *_this, HMONITOR hMonitor)
{
DISPLAYCONFIG_PATH_INFO path_info;
SDL_VideoData *videodata = _this->driverdata;
float SDR_white_point = 1.0f;
float SDR_white_level = 1.0f;
if (WIN_GetMonitorPathInfo(videodata, hMonitor, &path_info)) {
DISPLAYCONFIG_SDR_WHITE_LEVEL white_level;
@ -456,13 +456,13 @@ static float WIN_GetSDRWhitePoint(SDL_VideoDevice *_this, HMONITOR hMonitor)
/* WIN_GetMonitorPathInfo() succeeded: DisplayConfigGetDeviceInfo is not NULL */
if (videodata->DisplayConfigGetDeviceInfo(&white_level.header) == ERROR_SUCCESS &&
white_level.SDRWhiteLevel > 0) {
SDR_white_point = (white_level.SDRWhiteLevel / 1000.0f);
SDR_white_level = (white_level.SDRWhiteLevel / 1000.0f);
}
}
return SDR_white_point;
return SDR_white_level;
}
static void WIN_GetHDRProperties(SDL_VideoDevice *_this, HMONITOR hMonitor, SDL_HDRDisplayProperties *HDR)
static void WIN_GetHDRProperties(SDL_VideoDevice *_this, HMONITOR hMonitor, SDL_HDROutputProperties *HDR)
{
DXGI_OUTPUT_DESC1 desc;
@ -470,8 +470,8 @@ static void WIN_GetHDRProperties(SDL_VideoDevice *_this, HMONITOR hMonitor, SDL_
if (WIN_GetMonitorDESC1(hMonitor, &desc)) {
if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
HDR->SDR_white_point = WIN_GetSDRWhitePoint(_this, hMonitor);
HDR->HDR_headroom = (desc.MaxLuminance / 80.0f) / HDR->SDR_white_point;
HDR->SDR_white_level = WIN_GetSDRWhitePoint(_this, hMonitor);
HDR->HDR_headroom = (desc.MaxLuminance / 80.0f) / HDR->SDR_white_level;
}
}
}
@ -529,7 +529,6 @@ static void WIN_AddDisplay(SDL_VideoDevice *_this, HMONITOR hMonitor, const MONI
if (!_this->setting_display_mode) {
SDL_VideoDisplay *existing_display = _this->displays[i];
SDL_Rect bounds;
SDL_HDRDisplayProperties HDR;
SDL_ResetFullscreenDisplayModes(existing_display);
SDL_SetDesktopDisplayMode(existing_display, &mode);
@ -544,6 +543,7 @@ static void WIN_AddDisplay(SDL_VideoDevice *_this, HMONITOR hMonitor, const MONI
SDL_SendDisplayEvent(existing_display, SDL_EVENT_DISPLAY_ORIENTATION, current_orientation);
SDL_SetDisplayContentScale(existing_display, content_scale);
#ifdef HAVE_DXGI1_6_H
SDL_HDROutputProperties HDR;
WIN_GetHDRProperties(_this, hMonitor, &HDR);
SDL_SetDisplayHDRProperties(existing_display, &HDR);
#endif

View File

@ -59,8 +59,8 @@ static void UpdateHDRState(void)
SDL_PropertiesID props;
SDL_bool HDR_enabled;
props = SDL_GetDisplayProperties(SDL_GetDisplayForWindow(window));
HDR_enabled = SDL_GetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE);
props = SDL_GetWindowProperties(window);
HDR_enabled = SDL_GetBooleanProperty(props, SDL_PROP_WINDOW_HDR_ENABLED_BOOLEAN, SDL_FALSE);
SDL_Log("HDR %s\n", HDR_enabled ? "enabled" : "disabled");
@ -523,7 +523,7 @@ static void loop(void)
default:
break;
}
} else if (event.type == SDL_EVENT_DISPLAY_HDR_STATE_CHANGED) {
} else if (event.type == SDL_EVENT_WINDOW_HDR_STATE_CHANGED) {
UpdateHDRState();
} else if (event.type == SDL_EVENT_QUIT) {
done = 1;

View File

@ -66,12 +66,14 @@ int main(int argc, char *argv[])
for (i = 0; i < num_displays; i++) {
SDL_DisplayID dpy = displays[i];
SDL_PropertiesID props = SDL_GetDisplayProperties(dpy);
SDL_Rect rect = { 0, 0, 0, 0 };
int m, num_modes = 0;
const SDL_bool has_HDR = SDL_GetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE);
SDL_GetDisplayBounds(dpy, &rect);
modes = SDL_GetFullscreenDisplayModes(dpy, &num_modes);
SDL_Log("%" SDL_PRIu32 ": \"%s\" (%dx%d at %d,%d), content scale %.2f, %d fullscreen modes.\n", dpy, SDL_GetDisplayName(dpy), rect.w, rect.h, rect.x, rect.y, SDL_GetDisplayContentScale(dpy), num_modes);
SDL_Log("%" SDL_PRIu32 ": \"%s\" (%dx%d at %d,%d), content scale %.2f, %d fullscreen modes, HDR capable: %s.\n", dpy, SDL_GetDisplayName(dpy), rect.w, rect.h, rect.x, rect.y, SDL_GetDisplayContentScale(dpy), num_modes, has_HDR ? "yes" : "no");
mode = SDL_GetCurrentDisplayMode(dpy);
if (mode) {

View File

@ -155,7 +155,7 @@ static SDL_bool CreateWindowAndRenderer(SDL_WindowFlags window_flags, const char
if (useVulkan) {
SetupVulkanRenderProperties(vulkan_context, props);
}
if (SDL_GetBooleanProperty(SDL_GetDisplayProperties(SDL_GetDisplayForWindow(window)), SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE)) {
if (SDL_GetBooleanProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_HDR_ENABLED_BOOLEAN, SDL_FALSE)) {
/* Try to create an HDR capable renderer */
SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB_LINEAR);
renderer = SDL_CreateRendererWithProperties(props);

View File

@ -0,0 +1,356 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="frog_color_management_v1">
<copyright>
Copyright © 2023 Joshua Ashton for Valve Software
Copyright © 2023 Xaver Hugl
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.
</copyright>
<description summary="experimental color management protocol">
The aim of this color management extension is to get HDR games working quickly,
and have an easy way to test implementations in the wild before the upstream
protocol is ready to be merged.
For that purpose it's intentionally limited and cut down and does not serve
all uses cases.
</description>
<interface name="frog_color_management_factory_v1" version="1">
<description summary="color management factory">
The color management factory singleton creates color managed surface objects.
</description>
<request name="destroy" type="destructor"></request>
<request name="get_color_managed_surface">
<description summary="create color management interface for surface">
</description>
<arg name="surface" type="object" interface="wl_surface"
summary="target surface"/>
<arg name="callback" type="new_id" interface="frog_color_managed_surface"
summary="new color managed surface object"/>
</request>
</interface>
<interface name="frog_color_managed_surface" version="1">
<description summary="color managed surface">
Interface for changing surface color management and HDR state.
An implementation must: support every part of the version
of the frog_color_managed_surface interface it exposes.
Including all known enums associated with a given version.
</description>
<request name="destroy" type="destructor">
<description summary="destroy color managed surface">
Destroying the color managed surface resets all known color
state for the surface back to 'undefined' implementation-specific
values.
</description>
</request>
<enum name="transfer_function">
<description summary="known transfer functions">
Extended information on the transfer functions described
here can be found in the Khronos Data Format specification:
https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.html
</description>
<entry name="undefined" value="0" summary="specifies undefined, implementation-specific handling of the surface's transfer function."/>
<entry name="srgb" value="1" summary="specifies the sRGB non-linear EOTF. An implementation may: display this as Gamma 2.2 for the purposes of being consistent with content rendering across displays, rendering_intent and user expectations."/>
<entry name="gamma_22" value="2" summary="specifies gamma 2.2 power curve as the EOTF"/>
<entry name="st2084_pq" value="3" summary="specifies the SMPTE ST2084 Perceptual Quantizer (PQ) EOTF"/>
<entry name="scrgb_linear" value="4" summary="specifies the scRGB (extended sRGB) linear EOTF. Note: Primaries outside the gamut triangle specified can be expressed with negative values for this transfer function."/>
</enum>
<request name="set_known_transfer_function">
<description summary="sets a known transfer function for a surface"/>
<arg name="transfer_function" type="uint" enum="transfer_function" summary="transfer function for the surface"/>
</request>
<enum name="primaries">
<description summary="known primaries"/>
<entry name="undefined" value="0" summary="specifies undefined, implementation-specific handling"/>
<entry name="rec709" value="1" summary="specifies Rec.709/sRGB primaries with D65 white point"/>
<entry name="rec2020" value="2" summary="specifies Rec.2020/HDR10 primaries with D65 white point"/>
</enum>
<request name="set_known_container_color_volume">
<description summary="sets the container color volume (primaries) for a surface"/>
<arg name="primaries" type="uint" enum="primaries" summary="primaries for the surface"/>
</request>
<enum name="render_intent">
<description summary="known render intents">
Extended information on render intents described
here can be found in ICC.1:2022:
https://www.color.org/specification/ICC.1-2022-05.pdf
</description>
<entry name="perceptual" value="0" summary="perceptual"/>
</enum>
<request name="set_render_intent">
<description summary="sets the render intent for a surface">
NOTE: On a surface with "perceptual" (default) render intent, handling of the container's color volume
is implementation-specific, and may differ between different transfer functions it is paired with:
ie. sRGB + 709 rendering may have it's primaries widened to more of the available display's gamut
to be be more pleasing for the viewer.
Compared to scRGB Linear + 709 being treated faithfully as 709
(including utilizing negatives out of the 709 gamut triangle)
</description>
<arg name="render_intent" type="uint" enum="render_intent" summary="render intent for the surface"/>
</request>
<request name="set_hdr_metadata">
<description summary="set HDR metadata for a surface">
Forwards HDR metadata from the client to the compositor.
HDR Metadata Infoframe as per CTA 861.G spec.
Usage of this HDR metadata is implementation specific and
outside of the scope of this protocol.
</description>
<arg name="mastering_display_primary_red_x" type="uint">
<description summary="red primary x coordinate">
Mastering Red Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_red_y" type="uint">
<description summary="red primary y coordinate">
Mastering Red Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_green_x" type="uint">
<description summary="green primary x coordinate">
Mastering Green Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_green_y" type="uint">
<description summary="green primary y coordinate">
Mastering Green Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_blue_x" type="uint">
<description summary="blue primary x coordinate">
Mastering Blue Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_blue_y" type="uint">
<description summary="blue primary y coordinate">
Mastering Blue Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_white_point_x" type="uint">
<description summary="white point x coordinate">
Mastering White Point X Coordinate of the Data.
These are coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_white_point_y" type="uint">
<description summary="white point y coordinate">
Mastering White Point Y Coordinate of the Data.
These are coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="max_display_mastering_luminance" type="uint">
<description summary="max display mastering luminance">
Max Mastering Display Luminance.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
<arg name="min_display_mastering_luminance" type="uint">
<description summary="min display mastering luminance">
Min Mastering Display Luminance.
This value is coded as an unsigned 16-bit value in units of
0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF
represents 6.5535 cd/m2.
</description>
</arg>
<arg name="max_cll" type="uint">
<description summary="max content light level">
Max Content Light Level.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
<arg name="max_fall" type="uint">
<description summary="max frame average light level">
Max Frame Average Light Level.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
</request>
<event name="preferred_metadata">
<description summary="preferred metadata for a surface">
Current preferred metadata for a surface.
The application should use this information to tone-map its buffers
to this target before committing.
This metadata does not necessarily correspond to any physical output, but
rather what the compositor thinks would be best for a given surface.
</description>
<arg name="transfer_function" type="uint" enum="transfer_function">
<description summary="output's current transfer function">
Specifies a known transfer function that corresponds to the
output the surface is targeting.
</description>
</arg>
<arg name="output_display_primary_red_x" type="uint">
<description summary="red primary x coordinate">
Output Red Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_red_y" type="uint">
<description summary="red primary y coordinate">
Output Red Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_green_x" type="uint">
<description summary="green primary x coordinate">
Output Green Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_green_y" type="uint">
<description summary="green primary y coordinate">
Output Green Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_blue_x" type="uint">
<description summary="blue primary x coordinate">
Output Blue Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_blue_y" type="uint">
<description summary="blue primary y coordinate">
Output Blue Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_white_point_x" type="uint">
<description summary="white point x coordinate">
Output White Point X Coordinate of the Data.
These are coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_white_point_y" type="uint">
<description summary="white point y coordinate">
Output White Point Y Coordinate of the Data.
These are coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="max_luminance" type="uint">
<description summary="maximum luminance">
Max Output Luminance
The max luminance in nits that the output is capable of rendering in small areas.
Content should: not exceed this value to avoid clipping.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
<arg name="min_luminance" type="uint">
<description summary="minimum luminance">
Min Output Luminance
The min luminance that the output is capable of rendering.
Content should: not exceed this value to avoid clipping.
This value is coded as an unsigned 16-bit value in units of
0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF
represents 6.5535 cd/m2.
</description>
</arg>
<arg name="max_full_frame_luminance" type="uint">
<description summary="maximum full frame luminance">
Max Full Frame Luminance
The max luminance in nits that the output is capable of rendering for the
full frame sustained.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
</event>
</interface>
</protocol>