Skip to content

Commit

Permalink
video: Expose HDR metadata via window properties
Browse files Browse the repository at this point in the history
- Makes the HDR and colorspace properties 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.
  • Loading branch information
Kontrabant committed May 28, 2024
1 parent 2b374e6 commit 508e551
Show file tree
Hide file tree
Showing 16 changed files with 637 additions and 104 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
27 changes: 26 additions & 1 deletion include/SDL3/SDL_video.h
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,18 @@ 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_COLOR_PRIMARY_RED_X "SDL.display.color.primary_red_x"
#define SDL_PROP_DISPLAY_COLOR_PRIMARY_RED_Y "SDL.display.color.primary_red_y"
#define SDL_PROP_DISPLAY_COLOR_PRIMARY_GREEN_X "SDL.display.color.primary_green_x"
#define SDL_PROP_DISPLAY_COLOR_PRIMARY_GREEN_Y "SDL.display.color.primary_green_y"
#define SDL_PROP_DISPLAY_COLOR_PRIMARY_BLUE_X "SDL.display.color.primary_blue_x"
#define SDL_PROP_DISPLAY_COLOR_PRIMARY_BLUE_Y "SDL.display.color.primary_blue_y"
#define SDL_PROP_DISPLAY_COLOR_WHITE_POINT_X "SDL.display.color.white_point_x"
#define SDL_PROP_DISPLAY_COLOR_WHITE_POINT_Y "SDL.display.color.white_point_y"
#define SDL_PROP_DISPLAY_COLOR_MAX_LUMINANCE "SDL.display.color.max_luminance"
#define SDL_PROP_DISPLAY_COLOR_MIN_LUMINANCE "SDL.display.color.min_luminance"
#define SDL_PROP_DISPLAY_COLOR_MAX_FULL_FRAME_LUMINANCE "SDL.display.color.max_full_frame_luminance"
#define SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT "SDL.display.SDR_white_level"
#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 @@ -1194,6 +1205,20 @@ 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_HDR_ENABLED_BOOLEAN "SDL.window.HDR_enabled"
#define SDL_PROP_WINDOW_COLOR_PRIMARY_RED_X "SDL.window.color.primary_red_x"
#define SDL_PROP_WINDOW_COLOR_PRIMARY_RED_Y "SDL.window.color.primary_red_y"
#define SDL_PROP_WINDOW_COLOR_PRIMARY_GREEN_X "SDL.window.color.primary_green_x"
#define SDL_PROP_WINDOW_COLOR_PRIMARY_GREEN_Y "SDL.window.color.primary_green_y"
#define SDL_PROP_WINDOW_COLOR_PRIMARY_BLUE_X "SDL.window.color.primary_blue_x"
#define SDL_PROP_WINDOW_COLOR_PRIMARY_BLUE_Y "SDL.window.color.primary_blue_y"
#define SDL_PROP_WINDOW_COLOR_WHITE_POINT_X "SDL.window.color.white_point_x"
#define SDL_PROP_WINDOW_COLOR_WHITE_POINT_Y "SDL.window.color.white_point_y"
#define SDL_PROP_WINDOW_COLOR_MAX_LUMINANCE "SDL.window.color.max_luminance"
#define SDL_PROP_WINDOW_COLOR_MIN_LUMINANCE "SDL.window.color.min_luminance"
#define SDL_PROP_WINDOW_COLOR_MAX_FULL_FRAME_LUMINANCE "SDL.window.color.max_full_frame_luminance"
#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_SHAPE_POINTER "SDL.window.shape"
#define SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER "SDL.window.android.window"
#define SDL_PROP_WINDOW_ANDROID_SURFACE_POINTER "SDL.window.android.surface"
Expand Down
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
34 changes: 13 additions & 21 deletions src/render/SDL_render.c
Original file line number Diff line number Diff line change
Expand Up @@ -732,16 +732,10 @@ 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 win_props = SDL_GetWindowProperties(renderer->window);
SDL_PropertiesID renderer_props;

if (!displayID) {
return;
}

display_props = SDL_GetDisplayProperties(displayID);
if (!display_props) {
if (!win_props) {
return;
}

Expand All @@ -750,13 +744,13 @@ static void UpdateHDRProperties(SDL_Renderer *renderer)
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 = SDL_GetFloatProperty(win_props, SDL_PROP_WINDOW_SDR_WHITE_LEVEL_FLOAT, 1.0f);
renderer->HDR_headroom = SDL_GetFloatProperty(win_props, SDL_PROP_WINDOW_HDR_HEADROOM_FLOAT, 1.0f);
} else {
renderer->SDR_white_point = 1.0f;
renderer->SDR_white_level = 1.0f;
renderer->HDR_headroom = 1.0f;
}

Expand All @@ -765,10 +759,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 +820,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 +1049,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 +2989,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 +2998,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 +4361,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
35 changes: 26 additions & 9 deletions src/video/SDL_sysvideo.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,23 @@ typedef struct SDL_DisplayData SDL_DisplayData;
typedef struct SDL_DisplayModeData SDL_DisplayModeData;
typedef struct SDL_WindowData SDL_WindowData;

typedef struct
{
float red_primary_x;
float red_primary_y;
float green_primary_x;
float green_primary_y;
float blue_primary_x;
float blue_primary_y;
float white_point_x;
float white_point_y;
float min_luminance;
float max_luminance;
float max_full_frame_luminance;
float SDR_white_level;
float HDR_headroom;
} SDL_HDRProperties;

/* Define the SDL window structure, corresponding to toplevel windows */
struct SDL_Window
{
Expand All @@ -55,6 +72,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 +138,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 +154,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 +173,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 +526,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 +536,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 +547,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 +570,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 508e551

Please sign in to comment.