Generalize SDR white level handling into a color scale

This gives applications better control over how and when light levels are adjusted when working with HDR content and display.
This commit is contained in:
Sam Lantinga 2024-02-05 23:20:43 -08:00
parent 82d89ff4fb
commit d4caef5b89
34 changed files with 3279 additions and 3234 deletions

View file

@ -77,12 +77,20 @@ typedef struct
typedef struct
{
float scRGB_output;
float SDR_whitelevel;
float HDR_whitelevel;
float maxCLL;
float color_scale;
float unused1;
float unused2;
float YCbCr_matrix[16];
} PixelShaderConstants;
typedef struct
{
ID3D11Buffer *constants;
SDL_bool scRGB_output;
float color_scale;
const float *shader_params;
} PixelShaderState;
/* Per-vertex data */
typedef struct
{
@ -147,7 +155,6 @@ typedef struct
size_t vertexBufferSizes[8];
ID3D11VertexShader *vertexShader;
ID3D11PixelShader *pixelShaders[NUM_SHADERS];
ID3D11Buffer *pixelShaderConstants[NUM_SHADERS];
int blendModesCount;
D3D11_BlendMode *blendModes;
ID3D11SamplerState *nearestPixelSampler;
@ -155,11 +162,6 @@ typedef struct
D3D_FEATURE_LEVEL featureLevel;
SDL_bool pixelSizeChanged;
/* HDR state */
SDL_bool HDR_enabled;
int SDR_whitelevel;
int HDR_whitelevel;
/* Rasterizers */
ID3D11RasterizerState *mainRasterizer;
ID3D11RasterizerState *clippedRasterizer;
@ -174,7 +176,7 @@ typedef struct
ID3D11RasterizerState *currentRasterizerState;
ID3D11BlendState *currentBlendState;
D3D11_Shader currentShader;
const float *currentShaderParams[NUM_SHADERS];
PixelShaderState currentShaderState[NUM_SHADERS];
ID3D11ShaderResourceView *currentShaderResource;
ID3D11SamplerState *currentSampler;
SDL_bool cliprectDirty;
@ -325,8 +327,8 @@ static void D3D11_ReleaseAll(SDL_Renderer *renderer)
for (i = 0; i < SDL_arraysize(data->pixelShaders); ++i) {
SAFE_RELEASE(data->pixelShaders[i]);
}
for (i = 0; i < SDL_arraysize(data->pixelShaderConstants); ++i) {
SAFE_RELEASE(data->pixelShaderConstants[i]);
for (i = 0; i < SDL_arraysize(data->currentShaderState); ++i) {
SAFE_RELEASE(data->currentShaderState[i].constants);
}
SAFE_RELEASE(data->vertexShader);
for (i = 0; i < SDL_arraysize(data->vertexBuffers); ++i) {
@ -352,7 +354,7 @@ static void D3D11_ReleaseAll(SDL_Renderer *renderer)
data->currentRasterizerState = NULL;
data->currentBlendState = NULL;
data->currentShader = SHADER_NONE;
SDL_zero(data->currentShaderParams);
SDL_zero(data->currentShaderState);
data->currentShaderResource = NULL;
data->currentSampler = NULL;
@ -380,21 +382,6 @@ static void D3D11_DestroyRenderer(SDL_Renderer *renderer)
SDL_free(renderer);
}
static void D3D11_UpdateHDRState(SDL_Renderer *renderer)
{
D3D11_RenderData *data = (D3D11_RenderData *)renderer->driverdata;
/* Using some placeholder values here... */
data->HDR_enabled = SDL_TRUE;
if (renderer->output_colorspace == SDL_COLORSPACE_SCRGB && data->HDR_enabled) {
data->SDR_whitelevel = 200.0f;
data->HDR_whitelevel = 400.0f;
} else {
data->SDR_whitelevel = 80.0f;
data->HDR_whitelevel = 80.0f;
}
}
static D3D11_BLEND GetBlendFunc(SDL_BlendFactor factor)
{
switch (factor) {
@ -736,8 +723,6 @@ static HRESULT D3D11_CreateDeviceResources(SDL_Renderer *renderer)
SDL_SetProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_D3D11_DEVICE_POINTER, data->d3dDevice);
D3D11_UpdateHDRState(renderer);
done:
SAFE_RELEASE(d3dDevice);
SAFE_RELEASE(d3dContext);
@ -2139,6 +2124,9 @@ static int D3D11_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *c
const SDL_BlendMode blendMode = cmd->data.draw.blend;
ID3D11BlendState *blendState = NULL;
SDL_bool updateSubresource = SDL_FALSE;
SDL_bool scRGB_output = SDL_RenderingLinearSpace(renderer);
float color_scale = cmd->data.draw.color_scale;
PixelShaderState *shader_state = &rendererData->currentShaderState[shader];
if (numShaderResources > 0) {
shaderResource = shaderResources[0];
@ -2212,20 +2200,15 @@ static int D3D11_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *c
rendererData->currentBlendState = blendState;
}
if (!rendererData->pixelShaderConstants[shader] ||
shader_params != rendererData->currentShaderParams[shader]) {
SAFE_RELEASE(rendererData->pixelShaderConstants[shader]);
if (!shader_state->constants ||
scRGB_output != shader_state->scRGB_output ||
color_scale != shader_state->color_scale ||
shader_params != shader_state->shader_params) {
SAFE_RELEASE(shader_state->constants);
PixelShaderConstants constants;
if (renderer->output_colorspace == SDL_COLORSPACE_SCRGB) {
constants.scRGB_output = 1.0f;
} else {
constants.scRGB_output = 0.0f;
}
constants.SDR_whitelevel = (float)rendererData->SDR_whitelevel;
constants.HDR_whitelevel = (float)rendererData->HDR_whitelevel;
constants.maxCLL = 400.0f;
constants.scRGB_output = (float)scRGB_output;
constants.color_scale = color_scale;
if (shader_params) {
SDL_memcpy(constants.YCbCr_matrix, shader_params, sizeof(constants.YCbCr_matrix));
}
@ -2240,12 +2223,16 @@ static int D3D11_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *c
SDL_zero(data);
data.pSysMem = &constants;
HRESULT result = ID3D11Device_CreateBuffer(rendererData->d3dDevice, &desc, &data, &rendererData->pixelShaderConstants[shader]);
HRESULT result = ID3D11Device_CreateBuffer(rendererData->d3dDevice, &desc, &data, &shader_state->constants);
if (FAILED(result)) {
WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device::CreateBuffer [create shader constants]"), result);
return -1;
}
rendererData->currentShaderParams[shader] = shader_params;
shader_state->scRGB_output = scRGB_output;
shader_state->color_scale = color_scale;
shader_state->shader_params = shader_params;
/* Force the shader parameters to be re-set */
rendererData->currentShader = SHADER_NONE;
}
if (shader != rendererData->currentShader) {
@ -2255,8 +2242,8 @@ static int D3D11_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *c
}
}
ID3D11DeviceContext_PSSetShader(rendererData->d3dContext, rendererData->pixelShaders[shader], NULL, 0);
if (rendererData->pixelShaderConstants[shader]) {
ID3D11DeviceContext_PSSetConstantBuffers(rendererData->d3dContext, 0, 1, &rendererData->pixelShaderConstants[shader]);
if (rendererData->currentShaderState[shader].constants) {
ID3D11DeviceContext_PSSetConstantBuffers(rendererData->d3dContext, 0, 1, &rendererData->currentShaderState[shader].constants);
}
rendererData->currentShader = shader;
}
@ -2338,8 +2325,7 @@ static void D3D11_InvalidateCachedState(SDL_Renderer *renderer)
data->currentRenderTargetView = NULL;
data->currentRasterizerState = NULL;
data->currentBlendState = NULL;
data->currentShader = NUM_SHADERS;
SDL_zero(data->currentShaderParams);
data->currentShader = SHADER_NONE;
data->currentShaderResource = NULL;
data->currentSampler = NULL;
data->cliprectDirty = SDL_TRUE;
@ -2401,14 +2387,11 @@ static int D3D11_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd,
SDL_bool convert_color = SDL_RenderingLinearSpace(renderer);
SDL_FColor color = cmd->data.color.color;
if (convert_color) {
float light_scale = (float)rendererData->SDR_whitelevel / 80.0f;
SDL_ConvertToLinear(&color);
color.r *= light_scale;
color.g *= light_scale;
color.b *= light_scale;
}
color.r *= cmd->data.color.color_scale;
color.g *= cmd->data.color.color_scale;
color.b *= cmd->data.color.color_scale;
ID3D11DeviceContext_ClearRenderTargetView(rendererData->d3dContext, D3D11_GetCurrentRenderTargetView(renderer), &color.r);
break;
}