From cb3864949055aac24a464ebe561904328a088424 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 16 Feb 2024 17:36:11 -0800 Subject: [PATCH] Added SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT --- include/SDL3/SDL_video.h | 2 + src/video/SDL_sysvideo.h | 1 + src/video/SDL_video.c | 7 ++++ src/video/cocoa/SDL_cocoamodes.m | 1 + src/video/uikit/SDL_uikitmodes.m | 1 + src/video/windows/SDL_windowsmodes.c | 20 ++++----- test/testcolorspace.c | 63 ++++++++++++++++++++-------- 7 files changed, 64 insertions(+), 31 deletions(-) diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h index e3b995583..730f10fc4 100644 --- a/include/SDL3/SDL_video.h +++ b/include/SDL3/SDL_video.h @@ -360,6 +360,7 @@ extern DECLSPEC SDL_DisplayID SDLCALL SDL_GetPrimaryDisplay(void); * - `SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT`: the luminance, in nits, that * SDR white is rendered on this display. If this value is not set or is * zero, the value 200 is a reasonable default when HDR is enabled. + * - `SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT`: the maximum luminance, in nits, of HDR content on this display. If this value is not set or is zero, the value 400 is a reasonable default when HDR is enabled. * * \param displayID the instance ID of the display to query * \returns a valid property ID on success or 0 on failure; call @@ -374,6 +375,7 @@ extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetDisplayProperties(SDL_DisplayID #define SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN "SDL.display.HDR_enabled" #define SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT "SDL.display.SDR_white_level" +#define SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT "SDL.display.HDR_white_level" /** * Get the name of a display in UTF-8 encoding. diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 4eb861c3f..ac112cbd3 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -122,6 +122,7 @@ typedef struct { SDL_bool enabled; float SDR_whitelevel; + float HDR_whitelevel; } SDL_HDRDisplayProperties; /* diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 8fe9c9a0b..3cfa955db 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -715,6 +715,9 @@ SDL_DisplayID SDL_AddVideoDisplay(const SDL_VideoDisplay *display, SDL_bool send if (display->HDR.SDR_whitelevel != 0.0f) { SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT, display->HDR.SDR_whitelevel); } + if (display->HDR.HDR_whitelevel != 0.0f) { + SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT, display->HDR.HDR_whitelevel); + } return id; } @@ -992,6 +995,10 @@ void SDL_SetDisplayHDRProperties(SDL_VideoDisplay *display, const SDL_HDRDisplay SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT, HDR->SDR_whitelevel); changed = SDL_TRUE; } + if (HDR->HDR_whitelevel != display->HDR.HDR_whitelevel) { + SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT, HDR->HDR_whitelevel); + changed = SDL_TRUE; + } SDL_copyp(&display->HDR, HDR); if (changed) { diff --git a/src/video/cocoa/SDL_cocoamodes.m b/src/video/cocoa/SDL_cocoamodes.m index fa6215e6a..66bb3a72e 100644 --- a/src/video/cocoa/SDL_cocoamodes.m +++ b/src/video/cocoa/SDL_cocoamodes.m @@ -302,6 +302,7 @@ static void Cocoa_GetHDRProperties(CGDirectDisplayID displayID, SDL_HDRDisplayPr if (screen && screen.maximumPotentialExtendedDynamicRangeColorComponentValue > 1.0f) { HDR->enabled = SDL_TRUE; HDR->SDR_whitelevel = 80.0f; /* SDR content is always at scRGB 1.0 */ + HDR->HDR_whitelevel = HDR->SDR_whitelevel * screen.maximumExtendedDynamicRangeColorComponentValue; } } #endif diff --git a/src/video/uikit/SDL_uikitmodes.m b/src/video/uikit/SDL_uikitmodes.m index d9ca17f2c..e655c7c39 100644 --- a/src/video/uikit/SDL_uikitmodes.m +++ b/src/video/uikit/SDL_uikitmodes.m @@ -247,6 +247,7 @@ int UIKit_AddDisplay(UIScreen *uiscreen, SDL_bool send_event) if (uiscreen.potentialEDRHeadroom > 1.0f) { display.HDR.enabled = SDL_TRUE; display.HDR.SDR_whitelevel = 80.0f; /* SDR content is always at scRGB 1.0 */ + display.HDR.HDR_whitelevel = display.HDR.SDR_whitelevel * uiscreen.currentEDRHeadroom; } } #endif /* !SDL_PLATFORM_TVOS */ diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index 2ae567766..c417420e9 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -339,7 +339,7 @@ WIN_GetDisplayNameVista_failed: static SDL_bool WIN_GetMonitorDESC1(HMONITOR hMonitor, DXGI_OUTPUT_DESC1 *desc) { - typedef HRESULT(WINAPI * PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory); + 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; @@ -349,18 +349,18 @@ static SDL_bool WIN_GetMonitorDESC1(HMONITOR hMonitor, DXGI_OUTPUT_DESC1 *desc) #else hDXGIMod = SDL_LoadObject("dxgi.dll"); if (hDXGIMod) { - CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY)SDL_LoadFunction(hDXGIMod, "CreateDXGIFactory"); + CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY)SDL_LoadFunction(hDXGIMod, "CreateDXGIFactory1"); } #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_IDXGIFactory1 = { 0x770aae78, 0xf26f, 0x4dba, { 0xa8, 0x29, 0x25, 0x3c, 0x83, 0xd1, 0xb3, 0x87 } }; static const GUID SDL_IID_IDXGIOutput6 = { 0x068346e8, 0xaaec, 0x4b84, { 0xad, 0xd7, 0x13, 0x7f, 0x51, 0x3f, 0x77, 0xa1 } }; - IDXGIFactory2 *dxgiFactory; + IDXGIFactory1 *dxgiFactory; - if (SUCCEEDED(CreateDXGIFactoryFunc(&SDL_IID_IDXGIFactory2, (void **)&dxgiFactory))) { + if (SUCCEEDED(CreateDXGIFactoryFunc(&SDL_IID_IDXGIFactory1, (void **)&dxgiFactory))) { IDXGIAdapter1 *dxgiAdapter; UINT adapter = 0; - while (!found && SUCCEEDED(IDXGIFactory2_EnumAdapters1(dxgiFactory, adapter, &dxgiAdapter))) { + while (!found && SUCCEEDED(IDXGIFactory1_EnumAdapters1(dxgiFactory, adapter, &dxgiAdapter))) { IDXGIOutput *dxgiOutput; UINT output = 0; while (!found && SUCCEEDED(IDXGIAdapter1_EnumOutputs(dxgiAdapter, output, &dxgiOutput))) { @@ -482,13 +482,7 @@ static void WIN_GetHDRProperties(SDL_VideoDevice *_this, HMONITOR hMonitor, SDL_ 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. - */ + HDR->HDR_whitelevel = desc.MaxLuminance; } } } diff --git a/test/testcolorspace.c b/test/testcolorspace.c index f3b6b3a9b..3b479ae09 100644 --- a/test/testcolorspace.c +++ b/test/testcolorspace.c @@ -50,8 +50,8 @@ static SDL_bool HDR_enabled = SDL_FALSE; static float SDR_white_level = SDR_DISPLAY_WHITE_LEVEL; static float SDR_color_scale = 1.0f; static SDL_FRect SDR_calibration_rect; -static float HDR_white_level = DEFAULT_HDR_WHITE_LEVEL; -static float HDR_color_scale = DEFAULT_HDR_WHITE_LEVEL / SDR_DISPLAY_WHITE_LEVEL; +static float HDR_white_level = SDR_DISPLAY_WHITE_LEVEL; +static float HDR_color_scale = 1.0f; static SDL_FRect HDR_calibration_rect; enum @@ -77,7 +77,10 @@ static float GetDisplaySDRWhiteLevel(void) { SDL_PropertiesID props; - HDR_enabled = SDL_FALSE; + if (!HDR_enabled) { + /* HDR is not enabled, use the SDR white level */ + return SDR_DISPLAY_WHITE_LEVEL; + } props = SDL_GetRendererProperties(renderer); if (SDL_GetNumberProperty(props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB) != SDL_COLORSPACE_SCRGB) { @@ -86,13 +89,6 @@ static float GetDisplaySDRWhiteLevel(void) } props = SDL_GetDisplayProperties(SDL_GetDisplayForWindow(window)); - if (!SDL_GetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE)) { - /* HDR is not enabled on the display */ - return SDR_DISPLAY_WHITE_LEVEL; - } - - HDR_enabled = SDL_TRUE; - return SDL_GetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT, DEFAULT_SDR_WHITE_LEVEL); } @@ -108,9 +104,23 @@ static void SetSDRWhiteLevel(float value) SDL_SetRenderColorScale(renderer, SDR_color_scale); } -static void UpdateSDRWhiteLevel(void) +static float GetDisplayHDRWhiteLevel(void) { - SetSDRWhiteLevel(GetDisplaySDRWhiteLevel()); + SDL_PropertiesID props; + + if (!HDR_enabled) { + /* HDR is not enabled, use the SDR white level */ + return SDR_DISPLAY_WHITE_LEVEL; + } + + props = SDL_GetRendererProperties(renderer); + if (SDL_GetNumberProperty(props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB) != SDL_COLORSPACE_SCRGB) { + /* We're not displaying in HDR, use the SDR white level */ + return SDR_DISPLAY_WHITE_LEVEL; + } + + props = SDL_GetDisplayProperties(SDL_GetDisplayForWindow(window)); + return SDL_GetFloatProperty(props, SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT, DEFAULT_HDR_WHITE_LEVEL); } static void SetHDRWhiteLevel(float value) @@ -124,6 +134,26 @@ static void SetHDRWhiteLevel(float value) HDR_color_scale = HDR_white_level / SDR_DISPLAY_WHITE_LEVEL; } +static void UpdateHDRState(void) +{ + SDL_PropertiesID props; + + props = SDL_GetDisplayProperties(SDL_GetDisplayForWindow(window)); + HDR_enabled = SDL_GetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE); + + SetHDRWhiteLevel(GetDisplayHDRWhiteLevel()); + SetSDRWhiteLevel(GetDisplaySDRWhiteLevel()); + + SDL_Log("HDR %s\n", HDR_enabled ? "enabled" : "disabled"); + + if (HDR_enabled) { + props = SDL_GetRendererProperties(renderer); + if (SDL_GetNumberProperty(props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB) != SDL_COLORSPACE_SCRGB) { + SDL_Log("Run with --colorspace scRGB to display HDR colors\n"); + } + } +} + static void CreateRenderer(void) { SDL_PropertiesID props; @@ -144,9 +174,7 @@ static void CreateRenderer(void) SDL_Log("Created renderer %s\n", info.name); renderer_name = info.name; - UpdateSDRWhiteLevel(); - - SDL_Log("HDR is %s\n", HDR_enabled ? "enabled" : "disabled"); + UpdateHDRState(); } static void NextRenderer( void ) @@ -661,7 +689,7 @@ static void RenderHDRCalibration(void) SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); SDL_SetRenderColorScale(renderer, MAXIMUM_HDR_WHITE_LEVEL / SDR_DISPLAY_WHITE_LEVEL); SDL_RenderFillRect(renderer, &HDR_calibration_rect); - SDL_SetRenderColorScale(renderer, HDR_color_scale); + SDL_SetRenderColorScale(renderer, HDR_color_scale * 0.90f); rect = HDR_calibration_rect; rect.h -= 4.0f; rect.w = 60.0f; @@ -747,8 +775,7 @@ static void loop(void) OnMouseHeld(event.button.x, event.button.y); } } else if (event.type == SDL_EVENT_DISPLAY_HDR_STATE_CHANGED) { - SDL_Log("HDR %s\n", event.display.data1 ? "enabled" : "disabled"); - UpdateSDRWhiteLevel(); + UpdateHDRState(); } else if (event.type == SDL_EVENT_QUIT) { done = 1; }