Don't assume HDR headroom for HDR10 surfaces

Applications that support HDR will set the correct values for their content.
This commit is contained in:
Sam Lantinga
2024-03-02 15:02:17 -08:00
parent c74f273848
commit 7f9ff6277c
4 changed files with 16 additions and 35 deletions
+2 -18
View File
@@ -210,27 +210,13 @@ extern DECLSPEC void SDLCALL SDL_DestroySurface(SDL_Surface *surface);
* floating point formats, SDL_COLORSPACE_HDR10 for 10-bit formats, * floating point formats, SDL_COLORSPACE_HDR10 for 10-bit formats,
* SDL_COLORSPACE_SRGB for other RGB surfaces and SDL_COLORSPACE_BT709_FULL * SDL_COLORSPACE_SRGB for other RGB surfaces and SDL_COLORSPACE_BT709_FULL
* for YUV surfaces. * 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 * - `SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT`: for HDR10 and floating point
* surfaces, this defines the value of 100% diffuse white, with higher * surfaces, this defines the value of 100% diffuse white, with higher
* values being displayed in the High Dynamic Range headroom. This defaults * 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 * - `SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT`: for HDR10 and floating point
* surfaces, this defines the maximum dynamic range used by the content, in * surfaces, this defines the maximum dynamic range used by the content, in
* terms of the SDR white point. This defaults to * terms of the SDR white point. This defaults to 0.0, which disables tone mapping.
* SDL_PROP_SURFACE_MAXCLL_NUMBER / SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT,
* or 4.0, for HDR10 surfaces.
* - `SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING`: the tone mapping operator * - `SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING`: the tone mapping operator
* used when compressing from a surface with high dynamic range to another * used when compressing from a surface with high dynamic range to another
* with lower dynamic range. Currently this supports "chrome", which uses * 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); extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetSurfaceProperties(SDL_Surface *surface);
#define SDL_PROP_SURFACE_COLORSPACE_NUMBER "SDL.surface.colorspace" #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_SDR_WHITE_POINT_FLOAT "SDL.surface.SDR_white_point"
#define SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT "SDL.surface.HDR_headroom" #define SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT "SDL.surface.HDR_headroom"
#define SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING "SDL.surface.tonemap" #define SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING "SDL.surface.tonemap"
-5
View File
@@ -355,11 +355,6 @@ float SDL_GetSurfaceHDRHeadroom(SDL_Surface *surface, SDL_Colorspace colorspace)
} else { } else {
props = 0; 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 SDL_GetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, default_value);
} }
return 1.0f; return 1.0f;
-3
View File
@@ -157,9 +157,6 @@ static SDL_bool ReadPixel(int x, int y, SDL_Color *c)
surface = SDL_RenderReadPixels(renderer, &r); surface = SDL_RenderReadPixels(renderer, &r);
if (surface) { 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) { if (SDL_ReadSurfacePixel(surface, 0, 0, &c->r, &c->g, &c->b, &c->a) == 0) {
result = SDL_TRUE; result = SDL_TRUE;
} else { } else {
+14 -9
View File
@@ -436,20 +436,25 @@ static SDL_PropertiesID CreateVideoTextureProperties(AVFrame *frame, Uint32 form
{ {
AVFrameSideData *pSideData; AVFrameSideData *pSideData;
SDL_PropertiesID props; 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(); 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); pSideData = av_frame_get_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
if (pSideData) { 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; AVMasteringDisplayMetadata *pMasteringDisplayMetadata = (AVMasteringDisplayMetadata *)pSideData->data;
float flMaxLuminance = (float)pMasteringDisplayMetadata->max_luminance.num / pMasteringDisplayMetadata->max_luminance.den; flMaxLuminance = (float)pMasteringDisplayMetadata->max_luminance.num / pMasteringDisplayMetadata->max_luminance.den;
if (flMaxLuminance > k_flSDRWhitePoint) { } else if (SDL_COLORSPACETRANSFER(colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) {
SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, k_flSDRWhitePoint); /* The official definition is 10000, but PQ game content is often mastered for 400 or 1000 nits */
SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, flMaxLuminance / k_flSDRWhitePoint); 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_FORMAT_NUMBER, format);
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, access); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, access);