[client] egl: make it possible to configure HDR to SDR mapping

This commit is contained in:
Geoffrey McRae 2023-10-20 15:26:27 +11:00
parent 844a37a276
commit 78df2073ff
4 changed files with 73 additions and 18 deletions

View File

@ -51,6 +51,8 @@ struct DesktopShader
GLint uNVGain; GLint uNVGain;
GLint uCBMode; GLint uCBMode;
GLint uIsHDR; GLint uIsHDR;
GLint uMapHDRtoSDR;
GLint uMapHDRGain;
}; };
struct EGL_Desktop struct EGL_Desktop
@ -85,6 +87,11 @@ struct EGL_Desktop
bool useDMA; bool useDMA;
LG_RendererFormat format; LG_RendererFormat format;
// map HDR content to SDR
bool mapHDRtoSDR;
int peakLuminance;
int maxCLL;
EGL_PostProcess * pp; EGL_PostProcess * pp;
_Atomic(bool) processFrame; _Atomic(bool) processFrame;
}; };
@ -110,12 +117,14 @@ static bool egl_initDesktopShader(
return false; return false;
} }
shader->uTransform = egl_shaderGetUniform(shader->shader, "transform" ); shader->uTransform = egl_shaderGetUniform(shader->shader, "transform" );
shader->uDesktopSize = egl_shaderGetUniform(shader->shader, "desktopSize"); shader->uDesktopSize = egl_shaderGetUniform(shader->shader, "desktopSize" );
shader->uScaleAlgo = egl_shaderGetUniform(shader->shader, "scaleAlgo" ); shader->uScaleAlgo = egl_shaderGetUniform(shader->shader, "scaleAlgo" );
shader->uNVGain = egl_shaderGetUniform(shader->shader, "nvGain" ); shader->uNVGain = egl_shaderGetUniform(shader->shader, "nvGain" );
shader->uCBMode = egl_shaderGetUniform(shader->shader, "cbMode" ); shader->uCBMode = egl_shaderGetUniform(shader->shader, "cbMode" );
shader->uIsHDR = egl_shaderGetUniform(shader->shader, "isHDR" ); shader->uIsHDR = egl_shaderGetUniform(shader->shader, "isHDR" );
shader->uMapHDRtoSDR = egl_shaderGetUniform(shader->shader, "mapHDRtoSDR" );
shader->uMapHDRGain = egl_shaderGetUniform(shader->shader, "mapHDRGain" );
return true; return true;
} }
@ -184,6 +193,10 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
desktop->scaleAlgo = option_get_int("egl", "scale" ); desktop->scaleAlgo = option_get_int("egl", "scale" );
desktop->useDMA = useDMA; desktop->useDMA = useDMA;
desktop->mapHDRtoSDR = option_get_bool("egl", "mapHDRtoSDR" );
desktop->peakLuminance = option_get_int ("egl", "peakLuminance");
desktop->maxCLL = option_get_int ("egl", "maxCLL" );
if (!egl_postProcessInit(&desktop->pp)) if (!egl_postProcessInit(&desktop->pp))
{ {
DEBUG_ERROR("Failed to initialize the post process manager"); DEBUG_ERROR("Failed to initialize the post process manager");
@ -274,6 +287,14 @@ void egl_desktopConfigUI(EGL_Desktop * desktop)
} }
igSliderInt("##nvgain", &desktop->nvGain, 0, desktop->nvMax, format, 0); igSliderInt("##nvgain", &desktop->nvGain, 0, desktop->nvMax, format, 0);
igPopItemWidth(); igPopItemWidth();
igSeparator();
igCheckbox("Map HDR content to SDR", &desktop->mapHDRtoSDR);
igSliderInt("Peak Luminance", &desktop->peakLuminance, 1, 10000,
"%d nits",
ImGuiInputTextFlags_CharsDecimal);
igSliderInt("Max content light level (nits)", &desktop->maxCLL, 1, 10000,
"%d nits", ImGuiInputTextFlags_CharsDecimal);
} }
bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format) bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
@ -458,6 +479,9 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
desktop->useDMA && texture == desktop->texture ? desktop->useDMA && texture == desktop->texture ?
&desktop->dmaShader : &desktop->shader; &desktop->dmaShader : &desktop->shader;
const float mapHDRGain =
desktop->maxCLL / desktop->peakLuminance;
EGL_Uniform uniforms[] = EGL_Uniform uniforms[] =
{ {
{ {
@ -490,6 +514,16 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
.type = EGL_UNIFORM_TYPE_1I, .type = EGL_UNIFORM_TYPE_1I,
.location = shader->uIsHDR, .location = shader->uIsHDR,
.i = { desktop->hdr } .i = { desktop->hdr }
},
{
.type = EGL_UNIFORM_TYPE_1I,
.location = shader->uMapHDRtoSDR,
.i = { desktop->mapHDRtoSDR }
},
{
.type = EGL_UNIFORM_TYPE_1F,
.location = shader->uMapHDRGain,
.f = { mapHDRGain }
} }
}; };

View File

@ -202,6 +202,27 @@ static struct Option egl_options[] =
.type = OPTION_TYPE_BOOL, .type = OPTION_TYPE_BOOL,
.value.x_bool = true .value.x_bool = true
}, },
{
.module = "egl",
.name = "mapHDRtoSDR",
.description = "Map HDR content to the SDR color space",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "egl",
.name = "peakLuminance",
.description = "The peak luminance level in nits for HDR to SDR mapping",
.type = OPTION_TYPE_INT,
.value.x_int = 250,
},
{
.module = "egl",
.name = "maxCLL",
.description = "Maximum content light level in nits for HDR to SDR mapping",
.type = OPTION_TYPE_INT,
.value.x_int = 10000,
},
{0} {0}
}; };

View File

@ -21,6 +21,8 @@ uniform int scaleAlgo;
uniform float nvGain; uniform float nvGain;
uniform int cbMode; uniform int cbMode;
uniform bool isHDR; uniform bool isHDR;
uniform bool mapHDRtoSDR;
uniform float mapHDRGain;
void main() void main()
{ {
@ -40,8 +42,8 @@ void main()
} }
} }
if (isHDR) if (isHDR && mapHDRtoSDR)
color.rgb = mapToSDR(color.rgb); color.rgb = mapToSDR(color.rgb, mapHDRGain);
if (cbMode > 0) if (cbMode > 0)
color = cbTransform(color, cbMode); color = cbTransform(color, cbMode);

View File

@ -13,14 +13,11 @@
*/ */
// Configuration --------------------------------------------------------------- // Configuration ---------------------------------------------------------------
const float peakLuminance = 250.0; // Peak playback screen luminance in nits
const float knee = 0.75; // Compressor knee position const float knee = 0.75; // Compressor knee position
const float ratio = 4.0; // Compressor ratio: 1 = disabled, <1 = expander const float ratio = 4.0; // Compressor ratio: 1 = disabled, <1 = expander
const float maxCLL = 10000.0; // Maximum content light level in nits
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Precalculated values // Precalculated values
const float gain = maxCLL / peakLuminance;
const float compressor = 1.0 / ratio; const float compressor = 1.0 / ratio;
// PQ constants // PQ constants
@ -43,10 +40,11 @@ float midGain(vec3 pixel)
min(pixel.r, pixel.g)); // min = b min(pixel.r, pixel.g)); // min = b
} }
vec3 compress(vec3 pixel) vec3 compress(vec3 pixel, float gain)
{ {
float gain = maxGain(pixel); float maxGain = maxGain(pixel);
return pixel * (gain < knee ? gain : knee + max(gain - knee, 0.0) * compressor) / gain; return pixel * (maxGain < knee ? maxGain :
knee + max(maxGain - knee, 0.0) * compressor) / maxGain;
} }
vec3 fixClip(vec3 pixel) vec3 fixClip(vec3 pixel)
@ -67,7 +65,7 @@ vec3 fixClip(vec3 pixel)
} }
// Returns luminance in nits // Returns luminance in nits
vec3 pq2lin(vec3 pq) vec3 pq2lin(vec3 pq, float gain)
{ {
vec3 p = pow(pq, vec3(m2inv)); vec3 p = pow(pq, vec3(m2inv));
vec3 d = max(p - c1, vec3(0.0)) / (c2 - c3 * p); vec3 d = max(p - c1, vec3(0.0)) / (c2 - c3 * p);
@ -101,8 +99,8 @@ vec3 bt2020to709(vec3 bt2020)
bt2020.r * -0.0182 + bt2020.g * -0.1006 + bt2020.b * 1.1187); bt2020.r * -0.0182 + bt2020.g * -0.1006 + bt2020.b * 1.1187);
} }
vec3 mapToSDR(vec3 color) vec3 mapToSDR(vec3 color, float gain)
{ {
vec3 lin = bt2020to709(pq2lin(color.rgb)); vec3 lin = bt2020to709(pq2lin(color.rgb, gain));
return lin2srgb(compress(lin)); return lin2srgb(compress(lin, gain));
} }