From 7f9ff6277c789bdd4d475601f1a0c45d7c1b3b0b Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 2 Mar 2024 15:02:17 -0800 Subject: [PATCH] Don't assume HDR headroom for HDR10 surfaces Applications that support HDR will set the correct values for their content. --- include/SDL3/SDL_surface.h | 20 ++------------------ src/video/SDL_surface.c | 5 ----- test/testcolorspace.c | 3 --- test/testffmpeg.c | 23 ++++++++++++++--------- 4 files changed, 16 insertions(+), 35 deletions(-) diff --git a/include/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h index 5c8184e44f..4064354979 100644 --- a/include/SDL3/SDL_surface.h +++ b/include/SDL3/SDL_surface.h @@ -210,27 +210,13 @@ extern DECLSPEC void SDLCALL SDL_DestroySurface(SDL_Surface *surface); * floating point formats, SDL_COLORSPACE_HDR10 for 10-bit formats, * SDL_COLORSPACE_SRGB for other RGB surfaces and SDL_COLORSPACE_BT709_FULL * for YUV surfaces. - * - `SDL_PROP_SURFACE_MAXCLL_NUMBER`: MaxCLL (Maximum Content Light Level) - * indicates the maximum light level of any single pixel (in cd/m2 or nits) - * of the content. MaxCLL is usually measured off the final delivered - * content after mastering. If one uses the full light level of the HDR - * mastering display and adds a hard clip at its maximum value, MaxCLL would - * be equal to the peak luminance of the mastering monitor. This defaults to - * 400 for HDR10 surfaces. - * - `SDL_PROP_SURFACE_MAXFALL_NUMBER`: MaxFALL (Maximum Frame Average Light - * Level) indicates the maximum value of the frame average light level (in - * cd/m2 or nits) of the content. MaxFALL is calculated by averaging the - * decoded luminance values of all the pixels within a frame. MaxFALL is - * usually much lower than MaxCLL. * - `SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT`: for HDR10 and floating point * surfaces, this defines the value of 100% diffuse white, with higher * values being displayed in the High Dynamic Range headroom. This defaults - * to 100 for HDR10 surfaces and 1.0 for other surfaces. + * to 203 for HDR10 surfaces and 1.0 for floating point surfaces. * - `SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT`: for HDR10 and floating point * surfaces, this defines the maximum dynamic range used by the content, in - * terms of the SDR white point. This defaults to - * SDL_PROP_SURFACE_MAXCLL_NUMBER / SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, - * or 4.0, for HDR10 surfaces. + * terms of the SDR white point. This defaults to 0.0, which disables tone mapping. * - `SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING`: the tone mapping operator * used when compressing from a surface with high dynamic range to another * with lower dynamic range. Currently this supports "chrome", which uses @@ -250,8 +236,6 @@ extern DECLSPEC void SDLCALL SDL_DestroySurface(SDL_Surface *surface); extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetSurfaceProperties(SDL_Surface *surface); #define SDL_PROP_SURFACE_COLORSPACE_NUMBER "SDL.surface.colorspace" -#define SDL_PROP_SURFACE_MAXCLL_NUMBER "SDL.surface.maxCLL" -#define SDL_PROP_SURFACE_MAXFALL_NUMBER "SDL.surface.maxFALL" #define SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT "SDL.surface.SDR_white_point" #define SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT "SDL.surface.HDR_headroom" #define SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING "SDL.surface.tonemap" diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c index 16de375822..e003f31e53 100644 --- a/src/video/SDL_surface.c +++ b/src/video/SDL_surface.c @@ -355,11 +355,6 @@ float SDL_GetSurfaceHDRHeadroom(SDL_Surface *surface, SDL_Colorspace colorspace) } else { props = 0; } - if (transfer == SDL_TRANSFER_CHARACTERISTICS_PQ) { - /* The official definition is 10000, but PQ game content is often mastered for 400 or 1000 nits */ - const int DEFAULT_PQ_MAXCLL = 1000; - default_value = (float)SDL_GetNumberProperty(props, SDL_PROP_SURFACE_MAXCLL_NUMBER, DEFAULT_PQ_MAXCLL) / SDL_GetSurfaceSDRWhitePoint(surface, colorspace); - } return SDL_GetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, default_value); } return 1.0f; diff --git a/test/testcolorspace.c b/test/testcolorspace.c index 7fb262b161..ee1c610b1e 100644 --- a/test/testcolorspace.c +++ b/test/testcolorspace.c @@ -157,9 +157,6 @@ static SDL_bool ReadPixel(int x, int y, SDL_Color *c) surface = SDL_RenderReadPixels(renderer, &r); if (surface) { - /* We don't want to do any HDR -> SDR tone mapping */ - SDL_SetFloatProperty(SDL_GetSurfaceProperties(surface), SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, 0.0f); - if (SDL_ReadSurfacePixel(surface, 0, 0, &c->r, &c->g, &c->b, &c->a) == 0) { result = SDL_TRUE; } else { diff --git a/test/testffmpeg.c b/test/testffmpeg.c index 1770dca450..4c3ef85b9a 100644 --- a/test/testffmpeg.c +++ b/test/testffmpeg.c @@ -436,20 +436,25 @@ static SDL_PropertiesID CreateVideoTextureProperties(AVFrame *frame, Uint32 form { AVFrameSideData *pSideData; SDL_PropertiesID props; + SDL_Colorspace colorspace = GetFrameColorspace(frame); + + /* ITU-R BT.2408-6 recommends using an SDR white point of 203 nits, which is more likely for game content */ + static const float k_flSDRWhitePoint = 203.0f; + float flMaxLuminance = k_flSDRWhitePoint; props = SDL_CreateProperties(); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, GetFrameColorspace(frame)); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, colorspace); pSideData = av_frame_get_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); if (pSideData) { - /* ITU-R BT.2408-6 recommends using an SDR white point of 203 nits, which is more likely for game content */ - static const float k_flSDRWhitePoint = 203.0f; - AVMasteringDisplayMetadata *pMasteringDisplayMetadata = (AVMasteringDisplayMetadata *)pSideData->data; - float flMaxLuminance = (float)pMasteringDisplayMetadata->max_luminance.num / pMasteringDisplayMetadata->max_luminance.den; - if (flMaxLuminance > k_flSDRWhitePoint) { - SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, k_flSDRWhitePoint); - SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, flMaxLuminance / k_flSDRWhitePoint); - } + flMaxLuminance = (float)pMasteringDisplayMetadata->max_luminance.num / pMasteringDisplayMetadata->max_luminance.den; + } else if (SDL_COLORSPACETRANSFER(colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) { + /* The official definition is 10000, but PQ game content is often mastered for 400 or 1000 nits */ + flMaxLuminance = 1000.0f; + } + if (flMaxLuminance > k_flSDRWhitePoint) { + SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, k_flSDRWhitePoint); + SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, flMaxLuminance / k_flSDRWhitePoint); } SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, access);