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

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,
* 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"

View File

@ -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;

View File

@ -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 {

View File

@ -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);