mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-04-28 02:04:20 +00:00
Added HDR display properties and related event
Also added an HDR calibration stage to testcolorspace
This commit is contained in:
@@ -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) \
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user