Skip to content

Commit

Permalink
video: Expose additional HDR metadata on displays and windows
Browse files Browse the repository at this point in the history
- Adds a public SDL_HDRProperties struct with color and luminance info, and makes the HDR and colorspace info available per-window, in addition to per-display.
- Passes through color primary points, white points, and min/max luminance levels, when available.
- Adds the frog_color Wayland protocol to pass HDR metadata through to client applications.
- Extend testdisplayinfo to print the color and luminance data
  • Loading branch information
Kontrabant committed Jun 4, 2024
1 parent fd2b9c0 commit bd40c84
Show file tree
Hide file tree
Showing 22 changed files with 678 additions and 121 deletions.
1 change: 1 addition & 0 deletions include/SDL3/SDL_events.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,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,

Expand Down
69 changes: 53 additions & 16 deletions include/SDL3/SDL_video.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,27 @@ typedef struct SDL_DisplayMode
void *driverdata; /**< driver-specific data, initialize to 0 */
} SDL_DisplayMode;

/**
* The structure containing the display/window HDR color and luminance properties.
*
* \since This struct is available since SDL 3.0.0
*
* \sa SDL_GetDisplayHDRProperties
* \sa SDL_GetWindowHDRProperties
*/
typedef struct SDL_HDRProperties
{
SDL_FPoint red_primary; /**< the red primary x/y coordinates */
SDL_FPoint green_primary; /**< the green primary x/y coordinates */
SDL_FPoint blue_primary; /**< the blue primary x/y coordinates */
SDL_FPoint white_point; /**< the white point x/y coordinates */
float min_luminance; /**< the min luminance level */
float max_luminance; /**< the max luminance level */
float max_full_frame_luminance; /**< the max full-frame luminance */
float SDR_white_level; /**< the SDR white level */
float HDR_headroom; /**< the additional dynamic range that can be displayed in terms of the SDR white level (1.0 if HDR is not enabled) */
} SDL_HDRProperties;

/**
* Display orientation values; the way a display is rotated.
*
Expand Down Expand Up @@ -398,19 +419,6 @@ 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.
*
* On KMS/DRM:
*
* - `SDL_PROP_DISPLAY_KMSDRM_ORIENTATION_NUMBER`: the "panel orientation"
Expand All @@ -430,9 +438,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"

/**
Expand Down Expand Up @@ -533,6 +538,25 @@ extern SDL_DECLSPEC SDL_DisplayOrientation SDLCALL SDL_GetCurrentDisplayOrientat
*/
extern SDL_DECLSPEC float SDLCALL SDL_GetDisplayContentScale(SDL_DisplayID displayID);

/**
* Get the HDR color and luminance properties of a display.
*
* Client applications should use SDL_GetWindowHDRProperties to retrieve this information on
* a per-window basis as some platforms may not expose this information per-display, display
* and per-window color/luminance info can be different, or the desktop may require specific
* behavior when the window is moved between displays. This should only be used for informational
* purposes (e.g. logging system specifications for troubleshooting purposes).
*
* \param displayID the instance ID of the display to query
* \param props a pointer to the SDL_HDRProperties struct to filled in, may be NULL
* \returns 0 on success or -1 on failure; call SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetWindowHDRProperties
*/
extern SDL_DECLSPEC int SDLCALL SDL_GetDisplayHDRProperties(SDL_DisplayID displayID, SDL_HDRProperties *props);

/**
* Get a list of fullscreen display modes available on a display.
*
Expand Down Expand Up @@ -779,6 +803,19 @@ extern SDL_DECLSPEC void *SDLCALL SDL_GetWindowICCProfile(SDL_Window *window, si
*/
extern SDL_DECLSPEC Uint32 SDLCALL SDL_GetWindowPixelFormat(SDL_Window *window);

/**
* Get the HDR color and luminance properties associated with a window.
*
* \param window the window to query
* \param props a pointer to the SDL_HDRProperties struct to filled in, may be NULL
* \returns 0 on success or -1 on failure; call SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetDisplayHDRProperties
*/
extern SDL_DECLSPEC int SDLCALL SDL_GetWindowHDRProperties(SDL_Window *window, SDL_HDRProperties *props);

/**
* Create a window with the specified dimensions and flags.
*
Expand Down
2 changes: 2 additions & 0 deletions src/dynapi/SDL_dynapi.sym
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,8 @@ SDL3_0.0.0 {
SDL_wcsnstr;
SDL_wcsstr;
SDL_wcstol;
SDL_GetWindowHDRProperties;
SDL_GetDisplayHDRProperties;
# extra symbols go here (don't modify this line)
local: *;
};
2 changes: 2 additions & 0 deletions src/dynapi/SDL_dynapi_overrides.h
Original file line number Diff line number Diff line change
Expand Up @@ -1043,3 +1043,5 @@
#define SDL_wcsnstr SDL_wcsnstr_REAL
#define SDL_wcsstr SDL_wcsstr_REAL
#define SDL_wcstol SDL_wcstol_REAL
#define SDL_GetWindowHDRProperties SDL_GetWindowHDRProperties_REAL
#define SDL_GetDisplayHDRProperties SDL_GetDisplayHDRProperties_REAL
2 changes: 2 additions & 0 deletions src/dynapi/SDL_dynapi_procs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1049,3 +1049,5 @@ SDL_DYNAPI_PROC(size_t,SDL_wcsnlen,(const wchar_t *a, size_t b),(a,b),return)
SDL_DYNAPI_PROC(wchar_t*,SDL_wcsnstr,(const wchar_t *a, const wchar_t *b, size_t c),(a,b,c),return)
SDL_DYNAPI_PROC(wchar_t*,SDL_wcsstr,(const wchar_t *a, const wchar_t *b),(a,b),return)
SDL_DYNAPI_PROC(long,SDL_wcstol,(const wchar_t *a, wchar_t **b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_GetWindowHDRProperties,(SDL_Window *a, SDL_HDRProperties *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_GetDisplayHDRProperties,(SDL_DisplayID a, SDL_HDRProperties *b),(a,b),return)
3 changes: 3 additions & 0 deletions src/events/SDL_displayevents.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ int SDL_SendDisplayEvent(SDL_VideoDisplay *display, SDL_EventType displayevent,
case SDL_EVENT_DISPLAY_MOVED:
SDL_OnDisplayMoved(display);
break;
case SDL_EVENT_DISPLAY_HDR_STATE_CHANGED:
SDL_OnDisplayHDRStateChanged(display);
break;
default:
break;
}
Expand Down
1 change: 1 addition & 0 deletions src/events/SDL_events.c
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,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)
Expand Down
36 changes: 13 additions & 23 deletions src/render/SDL_render.c
Original file line number Diff line number Diff line change
Expand Up @@ -732,31 +732,23 @@ static void UpdateMainViewDimensions(SDL_Renderer *renderer)

static void UpdateHDRProperties(SDL_Renderer *renderer)
{
SDL_DisplayID displayID = SDL_GetDisplayForWindow(renderer->window);
SDL_PropertiesID display_props;
SDL_HDRProperties HDR_props;
SDL_PropertiesID renderer_props;

if (!displayID) {
return;
}

display_props = SDL_GetDisplayProperties(displayID);
if (!display_props) {
return;
}
SDL_GetWindowHDRProperties(renderer->window, &HDR_props);

renderer_props = SDL_GetRendererProperties(renderer);
if (!renderer_props) {
return;
}

renderer->color_scale /= renderer->SDR_white_point;
renderer->color_scale /= renderer->SDR_white_level;

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_level = HDR_props.SDR_white_level;
renderer->HDR_headroom = HDR_props.HDR_headroom;
} else {
renderer->SDR_white_point = 1.0f;
renderer->SDR_white_level = 1.0f;
renderer->HDR_headroom = 1.0f;
}

Expand All @@ -765,10 +757,10 @@ static void UpdateHDRProperties(SDL_Renderer *renderer)
} else {
SDL_SetBooleanProperty(renderer_props, SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN, SDL_FALSE);
}
SDL_SetFloatProperty(renderer_props, SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT, renderer->SDR_white_point);
SDL_SetFloatProperty(renderer_props, SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT, renderer->SDR_white_level);
SDL_SetFloatProperty(renderer_props, SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT, renderer->HDR_headroom);

renderer->color_scale *= renderer->SDR_white_point;
renderer->color_scale *= renderer->SDR_white_level;
}

static int UpdateLogicalPresentation(SDL_Renderer *renderer);
Expand Down Expand Up @@ -826,12 +818,10 @@ static int SDLCALL SDL_RendererEventWatch(void *userdata, SDL_Event *event)
if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_HIDDEN)) {
renderer->hidden = SDL_FALSE;
}
} else if (event->type == SDL_EVENT_WINDOW_DISPLAY_CHANGED) {
} else if (event->type == SDL_EVENT_WINDOW_HDR_STATE_CHANGED) {
UpdateHDRProperties(renderer);
}
}
} else if (event->type == SDL_EVENT_DISPLAY_HDR_STATE_CHANGED) {
UpdateHDRProperties(renderer);
}

return 0;
Expand Down Expand Up @@ -1057,7 +1047,7 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props)
renderer->line_method = SDL_GetRenderLineMethod();
}

renderer->SDR_white_point = 1.0f;
renderer->SDR_white_level = 1.0f;
renderer->HDR_headroom = 1.0f;
renderer->color_scale = 1.0f;

Expand Down Expand Up @@ -2997,7 +2987,7 @@ int SDL_SetRenderColorScale(SDL_Renderer *renderer, float scale)
{
CHECK_RENDERER_MAGIC(renderer, -1);

renderer->color_scale = scale * renderer->SDR_white_point;
renderer->color_scale = scale * renderer->SDR_white_level;
return 0;
}

Expand All @@ -3006,7 +2996,7 @@ int SDL_GetRenderColorScale(SDL_Renderer *renderer, float *scale)
CHECK_RENDERER_MAGIC(renderer, -1);

if (scale) {
*scale = renderer->color_scale / renderer->SDR_white_point;
*scale = renderer->color_scale / renderer->SDR_white_level;
}
return 0;
}
Expand Down Expand Up @@ -4369,7 +4359,7 @@ SDL_Surface *SDL_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect)
SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, renderer->target->SDR_white_point);
SDL_SetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, renderer->target->HDR_headroom);
} else {
SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, renderer->SDR_white_point);
SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, renderer->SDR_white_level);
SDL_SetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, renderer->HDR_headroom);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/render/SDL_sysrender.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ struct SDL_Renderer
SDL_Mutex *target_mutex;

SDL_Colorspace output_colorspace;
float SDR_white_point;
float SDR_white_level;
float HDR_headroom;

float color_scale;
Expand Down
18 changes: 9 additions & 9 deletions src/video/SDL_sysvideo.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ struct SDL_Window
SDL_bool fullscreen_exclusive; /* The window is currently fullscreen exclusive */
SDL_DisplayID last_fullscreen_exclusive_display; /* The last fullscreen_exclusive display */
SDL_DisplayID last_displayID;
SDL_HDRProperties HDR;

/* Stored position and size for the window in the non-fullscreen state,
* including when the window is maximized or tiled.
Expand Down Expand Up @@ -120,12 +121,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.
Expand All @@ -142,7 +137,7 @@ struct SDL_VideoDisplay
SDL_DisplayOrientation natural_orientation;
SDL_DisplayOrientation current_orientation;
float content_scale;
SDL_HDRDisplayProperties HDR;
SDL_HDRProperties HDR;

SDL_Window *fullscreen_window;

Expand All @@ -161,7 +156,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 */
Expand Down Expand Up @@ -513,7 +509,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_HDRProperties *HDR, SDL_bool send_event);
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);
Expand All @@ -523,6 +519,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_HDRProperties *HDR, SDL_bool send_event);

extern void SDL_GL_DeduceMaxSupportedESProfile(int *major, int *minor);

Expand All @@ -533,6 +530,7 @@ extern void SDL_GlobalToRelativeForWindow(SDL_Window *window, int abs_x, int abs

extern void SDL_OnDisplayAdded(SDL_VideoDisplay *display);
extern void SDL_OnDisplayMoved(SDL_VideoDisplay *display);
extern void SDL_OnDisplayHDRStateChanged(SDL_VideoDisplay *display);
extern void SDL_OnWindowShown(SDL_Window *window);
extern void SDL_OnWindowHidden(SDL_Window *window);
extern void SDL_OnWindowMoved(SDL_Window *window);
Expand All @@ -555,4 +553,6 @@ extern SDL_bool SDL_ShouldAllowTopmost(void);

extern void SDL_ToggleDragAndDropSupport(void);

extern void SDL_GetHDRDefaults(SDL_HDRProperties *HDR);

#endif /* SDL_sysvideo_h_ */
Loading

0 comments on commit bd40c84

Please sign in to comment.