Added HDR display properties and related event

Also added an HDR calibration stage to testcolorspace
This commit is contained in:
Sam Lantinga
2024-02-06 01:53:03 -08:00
parent d4caef5b89
commit 30e176d6ba
11 changed files with 510 additions and 57 deletions

View File

@@ -282,6 +282,7 @@ 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) \

View File

@@ -854,6 +854,7 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props)
const int n = SDL_GetNumRenderDrivers();
const char *hint;
int i, attempted = 0;
SDL_PropertiesID new_props;
if (!window && surface) {
return SDL_CreateSoftwareRenderer(surface);
@@ -964,6 +965,16 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props)
renderer->hidden = SDL_FALSE;
}
new_props = SDL_GetRendererProperties(renderer);
SDL_SetStringProperty(new_props, SDL_PROP_RENDERER_NAME_STRING, renderer->info.name);
if (window) {
SDL_SetProperty(new_props, SDL_PROP_RENDERER_WINDOW_POINTER, window);
}
if (surface) {
SDL_SetProperty(new_props, SDL_PROP_RENDERER_SURFACE_POINTER, surface);
}
SDL_SetNumberProperty(new_props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, renderer->output_colorspace);
SDL_SetProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_RENDERER, renderer);
SDL_SetRenderViewport(renderer, NULL);

View File

@@ -1666,6 +1666,12 @@ 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);

View File

@@ -117,6 +117,12 @@ struct SDL_Window
#define SDL_WINDOW_IS_POPUP(W) \
(((W)->flags & (SDL_WINDOW_TOOLTIP | SDL_WINDOW_POPUP_MENU)) != 0)
typedef struct
{
SDL_bool enabled;
float SDR_whitelevel;
} SDL_HDRDisplayProperties;
/*
* Define the SDL display structure.
* This corresponds to physical monitors attached to the system.
@@ -133,6 +139,7 @@ struct SDL_VideoDisplay
SDL_DisplayOrientation natural_orientation;
SDL_DisplayOrientation current_orientation;
float content_scale;
SDL_HDRDisplayProperties HDR;
SDL_Window *fullscreen_window;
@@ -489,6 +496,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 int SDL_SetDisplayModeForDisplay(SDL_VideoDisplay *display, SDL_DisplayMode *mode);
extern SDL_VideoDisplay *SDL_GetVideoDisplay(SDL_DisplayID display);
extern SDL_VideoDisplay *SDL_GetVideoDisplayForWindow(SDL_Window *window);

View File

@@ -671,6 +671,7 @@ SDL_DisplayID SDL_AddVideoDisplay(const SDL_VideoDisplay *display, SDL_bool send
{
SDL_VideoDisplay **displays, *new_display;
SDL_DisplayID id;
SDL_PropertiesID props;
int i;
new_display = (SDL_VideoDisplay *)SDL_malloc(sizeof(*new_display));
@@ -710,9 +711,15 @@ SDL_DisplayID SDL_AddVideoDisplay(const SDL_VideoDisplay *display, SDL_bool send
new_display->fullscreen_modes[i].displayID = id;
}
if (send_event) {
SDL_SendDisplayEvent(new_display, SDL_EVENT_DISPLAY_ADDED, 0);
props = SDL_GetDisplayProperties(id);
if (display->HDR.enabled) {
SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_TRUE);
}
if (display->HDR.SDR_whitelevel != 0.0f) {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT, display->HDR.SDR_whitelevel);
}
return id;
}
@@ -976,6 +983,26 @@ float SDL_GetDisplayContentScale(SDL_DisplayID displayID)
return display->content_scale;
}
void SDL_SetDisplayHDRProperties(SDL_VideoDisplay *display, const SDL_HDRDisplayProperties *HDR)
{
SDL_PropertiesID props = SDL_GetDisplayProperties(display->id);
SDL_bool changed = SDL_FALSE;
if (HDR->enabled != display->HDR.enabled) {
SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, HDR->enabled);
changed = SDL_TRUE;
}
if (HDR->SDR_whitelevel != display->HDR.SDR_whitelevel) {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT, HDR->SDR_whitelevel);
changed = SDL_TRUE;
}
SDL_copyp(&display->HDR, HDR);
if (changed) {
SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_HDR_STATE_CHANGED, HDR->enabled);
}
}
static const SDL_DisplayMode *SDL_GetFullscreenModeMatch(const SDL_DisplayMode *mode)
{
const SDL_DisplayMode **modes;

View File

@@ -25,6 +25,9 @@
#include "SDL_windowsvideo.h"
#include "../../events/SDL_displayevents_c.h"
#define COBJMACROS
#include <dxgi1_6.h>
/* Windows CE compatibility */
#ifndef CDS_FULLSCREEN
#define CDS_FULLSCREEN 0
@@ -334,6 +337,162 @@ WIN_GetDisplayNameVista_failed:
return NULL;
}
static SDL_bool WIN_GetMonitorDESC1(HMONITOR hMonitor, DXGI_OUTPUT_DESC1 *desc)
{
typedef HRESULT(WINAPI * PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory);
PFN_CREATE_DXGI_FACTORY CreateDXGIFactoryFunc = NULL;
void *hDXGIMod = NULL;
SDL_bool found = SDL_FALSE;
#ifdef SDL_PLATFORM_WINRT
CreateDXGIFactoryFunc = CreateDXGIFactory1;
#else
hDXGIMod = SDL_LoadObject("dxgi.dll");
if (hDXGIMod) {
CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY)SDL_LoadFunction(hDXGIMod, "CreateDXGIFactory");
}
#endif
if (CreateDXGIFactoryFunc) {
static const GUID SDL_IID_IDXGIFactory2 = { 0x50c83a1c, 0xe072, 0x4c48, { 0x87, 0xb0, 0x36, 0x30, 0xfa, 0x36, 0xa6, 0xd0 } };
static const GUID SDL_IID_IDXGIOutput6 = { 0x068346e8, 0xaaec, 0x4b84, { 0xad, 0xd7, 0x13, 0x7f, 0x51, 0x3f, 0x77, 0xa1 } };
IDXGIFactory2 *dxgiFactory;
if (SUCCEEDED(CreateDXGIFactoryFunc(&SDL_IID_IDXGIFactory2, (void **)&dxgiFactory))) {
IDXGIAdapter1 *dxgiAdapter;
UINT adapter = 0;
while (!found && SUCCEEDED(IDXGIFactory2_EnumAdapters1(dxgiFactory, adapter, &dxgiAdapter))) {
IDXGIOutput *dxgiOutput;
UINT output = 0;
while (!found && SUCCEEDED(IDXGIAdapter1_EnumOutputs(dxgiAdapter, output, &dxgiOutput))) {
IDXGIOutput6 *dxgiOutput6;
if (SUCCEEDED(IDXGIOutput_QueryInterface(dxgiOutput, &SDL_IID_IDXGIOutput6, (void **)&dxgiOutput6))) {
if (SUCCEEDED(IDXGIOutput6_GetDesc1(dxgiOutput6, desc))) {
if (desc->Monitor == hMonitor) {
found = SDL_TRUE;
}
}
IDXGIOutput6_Release(dxgiOutput6);
}
IDXGIOutput_Release(dxgiOutput);
++output;
}
IDXGIAdapter1_Release(dxgiAdapter);
++adapter;
}
IDXGIFactory2_Release(dxgiFactory);
}
}
if (hDXGIMod) {
SDL_UnloadObject(hDXGIMod);
}
return found;
}
static SDL_bool WIN_GetMonitorPathInfo(HMONITOR hMonitor, DISPLAYCONFIG_PATH_INFO *path_info)
{
LONG result;
MONITORINFOEXW view_info;
UINT32 i;
UINT32 num_path_array_elements = 0;
UINT32 num_mode_info_array_elements = 0;
DISPLAYCONFIG_PATH_INFO *path_infos = NULL, *new_path_infos;
DISPLAYCONFIG_MODE_INFO *mode_infos = NULL, *new_mode_infos;
SDL_bool found = SDL_FALSE;
SDL_zero(view_info);
view_info.cbSize = sizeof(view_info);
if (!GetMonitorInfoW(hMonitor, (MONITORINFO *)&view_info)) {
goto done;
}
do {
if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &num_path_array_elements, &num_mode_info_array_elements) != ERROR_SUCCESS) {
return -1;
}
new_path_infos = (DISPLAYCONFIG_PATH_INFO *)SDL_realloc(path_infos, num_path_array_elements * sizeof(*path_infos));
if (!new_path_infos) {
goto done;
}
path_infos = new_path_infos;
new_mode_infos = (DISPLAYCONFIG_MODE_INFO *)SDL_realloc(mode_infos, num_mode_info_array_elements * sizeof(*mode_infos));
if (!new_mode_infos) {
goto done;
}
mode_infos = new_mode_infos;
result = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &num_path_array_elements, path_infos, &num_mode_info_array_elements, mode_infos, NULL);
} while (result == ERROR_INSUFFICIENT_BUFFER);
if (result == ERROR_SUCCESS) {
for (i = 0; i < num_path_array_elements; ++i) {
DISPLAYCONFIG_SOURCE_DEVICE_NAME device_name;
SDL_zero(device_name);
device_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
device_name.header.size = sizeof(device_name);
device_name.header.adapterId = path_infos[i].sourceInfo.adapterId;
device_name.header.id = path_infos[i].sourceInfo.id;
if (DisplayConfigGetDeviceInfo(&device_name.header) == ERROR_SUCCESS) {
if (SDL_wcscmp(view_info.szDevice, device_name.viewGdiDeviceName) == 0) {
SDL_copyp(path_info, &path_infos[i]);
found = SDL_TRUE;
break;
}
}
}
}
done:
SDL_free(path_infos);
SDL_free(mode_infos);
return found;
}
static float WIN_GetSDRWhiteLevel(HMONITOR hMonitor)
{
DISPLAYCONFIG_PATH_INFO path_info;
float SDR_whitelevel = 200.0f;
if (WIN_GetMonitorPathInfo(hMonitor, &path_info)) {
DISPLAYCONFIG_SDR_WHITE_LEVEL white_level;
SDL_zero(white_level);
white_level.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL;
white_level.header.size = sizeof(white_level);
white_level.header.adapterId = path_info.targetInfo.adapterId;
white_level.header.id = path_info.targetInfo.id;
if (DisplayConfigGetDeviceInfo(&white_level.header) == ERROR_SUCCESS) {
SDR_whitelevel = (white_level.SDRWhiteLevel / 1000.0f) * 80.0f;
}
}
return SDR_whitelevel;
}
static void WIN_GetHDRProperties(SDL_VideoDevice *_this, HMONITOR hMonitor, SDL_HDRDisplayProperties *HDR)
{
DXGI_OUTPUT_DESC1 desc;
SDL_zerop(HDR);
if (WIN_GetMonitorDESC1(hMonitor, &desc)) {
if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
HDR->enabled = SDL_TRUE;
HDR->SDR_whitelevel = WIN_GetSDRWhiteLevel(hMonitor);
/* In theory you can get the maximum luminence from desc.MaxLuminance, but this value is 80
* on my system regardless of whether HDR is enabled. Because the value isn't reliable games
* will typically have a calibration step where they show you a white image at high luminence
* and slowly lower the brightness until you can see it as distinct from the background and
* then use that as the calibrated maximum luminence. The value 400 is a reasonable default.
*/
}
}
}
static void WIN_AddDisplay(SDL_VideoDevice *_this, HMONITOR hMonitor, const MONITORINFOEXW *info, int *display_index)
{
int i, index = *display_index;
@@ -386,6 +545,7 @@ 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);
@@ -399,6 +559,8 @@ 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);
WIN_GetHDRProperties(_this, hMonitor, &HDR);
SDL_SetDisplayHDRProperties(existing_display, &HDR);
}
goto done;
}
@@ -430,6 +592,7 @@ static void WIN_AddDisplay(SDL_VideoDevice *_this, HMONITOR hMonitor, const MONI
display.device = _this;
display.driverdata = displaydata;
WIN_GetDisplayBounds(_this, &display, &displaydata->bounds);
WIN_GetHDRProperties(_this, hMonitor, &display.HDR);
SDL_AddVideoDisplay(&display, SDL_FALSE);
SDL_free(display.name);