From ad43969c1aa2a879c3f40f7f62c90a9135371e9a Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Wed, 26 Apr 2023 16:26:33 +1000 Subject: [PATCH] [client] egl: add support to map HDR to SDR --- client/include/interface/renderer.h | 1 + client/renderers/EGL/desktop.c | 9 ++ client/renderers/EGL/shader/desktop_rgb.frag | 5 + client/renderers/EGL/shader/hdr.h | 108 +++++++++++++++++++ client/src/main.c | 6 +- 5 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 client/renderers/EGL/shader/hdr.h diff --git a/client/include/interface/renderer.h b/client/include/interface/renderer.h index b074e496..0c7b1430 100644 --- a/client/include/interface/renderer.h +++ b/client/include/interface/renderer.h @@ -72,6 +72,7 @@ LG_RendererRotate; typedef struct LG_RendererFormat { FrameType type; // frame type + bool hdr; // if the frame is HDR or not unsigned int screenWidth; // actual width of the host unsigned int screenHeight; // actual height of the host unsigned int frameWidth; // width of frame transmitted diff --git a/client/renderers/EGL/desktop.c b/client/renderers/EGL/desktop.c index b2c607c4..0d79e6ab 100644 --- a/client/renderers/EGL/desktop.c +++ b/client/renderers/EGL/desktop.c @@ -50,6 +50,7 @@ struct DesktopShader GLint uScaleAlgo; GLint uNVGain; GLint uCBMode; + GLint uIsHDR; }; struct EGL_Desktop @@ -64,6 +65,7 @@ struct EGL_Desktop // internals int width, height; + bool hdr; LG_RendererRotate rotate; bool useSpice; @@ -113,6 +115,7 @@ static bool egl_initDesktopShader( shader->uScaleAlgo = egl_shaderGetUniform(shader->shader, "scaleAlgo" ); shader->uNVGain = egl_shaderGetUniform(shader->shader, "nvGain" ); shader->uCBMode = egl_shaderGetUniform(shader->shader, "cbMode" ); + shader->uIsHDR = egl_shaderGetUniform(shader->shader, "isHDR" ); return true; } @@ -303,6 +306,7 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format) desktop->width = format.frameWidth; desktop->height = format.frameHeight; + desktop->hdr = format.hdr; if (!egl_textureSetup( desktop->texture, @@ -481,6 +485,11 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth, .type = EGL_UNIFORM_TYPE_1I, .location = shader->uCBMode, .f = { desktop->cbMode } + }, + { + .type = EGL_UNIFORM_TYPE_1I, + .location = shader->uIsHDR, + .i = { desktop->hdr } } }; diff --git a/client/renderers/EGL/shader/desktop_rgb.frag b/client/renderers/EGL/shader/desktop_rgb.frag index 2b54747f..f0b0755b 100644 --- a/client/renderers/EGL/shader/desktop_rgb.frag +++ b/client/renderers/EGL/shader/desktop_rgb.frag @@ -9,6 +9,7 @@ precision highp float; #define EGL_SCALE_MAX 3 #include "color_blind.h" +#include "hdr.h" in vec2 uv; out vec4 color; @@ -19,6 +20,7 @@ uniform int scaleAlgo; uniform float nvGain; uniform int cbMode; +uniform bool isHDR; void main() { @@ -38,6 +40,9 @@ void main() } } + if (isHDR) + color.rgb = mapToSDR(color.rgb); + if (cbMode > 0) color = cbTransform(color, cbMode); diff --git a/client/renderers/EGL/shader/hdr.h b/client/renderers/EGL/shader/hdr.h new file mode 100644 index 00000000..11ea9860 --- /dev/null +++ b/client/renderers/EGL/shader/hdr.h @@ -0,0 +1,108 @@ +/* + _______ _____ __ __ ____ __ + / ____(_)___ ___ ____ ___ ____ _ / ___// /_ ____ _____/ /__ _____ / __ \____ ______/ /__ + / / / / __ \/ _ \/ __ `__ \/ __ `/ \__ \/ __ \/ __ `/ __ / _ \/ ___/ / /_/ / __ `/ ___/ //_/ +/ /___/ / / / / __/ / / / / / /_/ / ___/ / / / / /_/ / /_/ / __/ / / ____/ /_/ / /__/ ,< +\____/_/_/ /_/\___/_/ /_/ /_/\__,_/ /____/_/ /_/\__,_/\__,_/\___/_/ /_/ \__,_/\___/_/|_| + http://en.sbence.hu/ Shader: Try to get the SDR part of HDR content +*/ + +/** + * Translated to GLSL, original source is: + * https://github.com/VoidXH/Cinema-Shader-Pack + */ + +// Configuration --------------------------------------------------------------- +const float peakLuminance = 250.0; // Peak playback screen luminance in nits +const float knee = 0.75; // Compressor knee position +const float ratio = 4.0; // Compressor ratio: 1 = disabled, <1 = expander +const float maxCLL = 10000.0; // Maximum content light level in nits +// ----------------------------------------------------------------------------- + +// Precalculated values +const float gain = maxCLL / peakLuminance; +const float compressor = 1.0 / ratio; + +// PQ constants +const float m1inv = 16384.0 / 2610.0; +const float m2inv = 32.0 / 2523.0; +const float c1 = 3424.0 / 4096.0; +const float c2 = 2413.0 / 128.0; +const float c3 = 2392.0 / 128.0; + +float minGain(vec3 pixel) { return min(pixel.r, min(pixel.g, pixel.b)); } +float maxGain(vec3 pixel) { return max(pixel.r, max(pixel.g, pixel.b)); } +float midGain(vec3 pixel) +{ + return pixel.r < pixel.g ? + (pixel.r < pixel.b ? + min(pixel.g, pixel.b) : // min = r + min(pixel.r, pixel.g)) : // min = b + (pixel.g < pixel.b ? + min(pixel.r, pixel.b) : // min = g + min(pixel.r, pixel.g)); // min = b +} + +vec3 compress(vec3 pixel) +{ + float gain = maxGain(pixel); + return pixel * (gain < knee ? gain : knee + max(gain - knee, 0.0) * compressor) / gain; +} + +vec3 fixClip(vec3 pixel) +{ + // keep the (mid - min) / (max - min) ratio + float preMin = minGain(pixel); + float preMid = midGain(pixel); + float preMax = maxGain(pixel); + vec3 clip = clamp(pixel, 0.0, 1.0); + float postMin = minGain(clip); + float postMid = midGain(clip); + float postMax = maxGain(clip); + float ratio = (preMid - preMin) / (preMax - preMin); + float newMid = ratio * (postMax - postMin) + postMin; + return vec3(clip.r != postMid ? clip.r : newMid, + clip.g != postMid ? clip.g : newMid, + clip.b != postMid ? clip.b : newMid); +} + +// Returns luminance in nits +vec3 pq2lin(vec3 pq) +{ + vec3 p = pow(pq, vec3(m2inv)); + vec3 d = max(p - c1, vec3(0.0)) / (c2 - c3 * p); + return pow(d, vec3(m1inv)) * gain; +} + +vec3 srgb2lin(vec3 c) +{ + vec3 v = c / 12.92; + vec3 v2 = pow((c + vec3(0.055)) / 1.055, vec3(2.4)); + vec3 threshold = vec3(0.04045); + vec3 result = mix(v, v2, greaterThanEqual(c, threshold)); + return result; +} + +vec3 lin2srgb(vec3 c) +{ + vec3 v = c * 12.92; + vec3 v2 = pow(c, vec3(1.0/2.4)) * 1.055 - 0.055; + vec3 threshold = vec3(0.0031308); + vec3 result = mix(v, v2, greaterThanEqual(c, threshold)); + return result; +} + +// in linear space +vec3 bt2020to709(vec3 bt2020) +{ + return vec3( + bt2020.r * 1.6605 + bt2020.g * -0.5876 + bt2020.b * -0.0728, + bt2020.r * -0.1246 + bt2020.g * 1.1329 + bt2020.b * -0.0083, + bt2020.r * -0.0182 + bt2020.g * -0.1006 + bt2020.b * 1.1187); +} + +vec3 mapToSDR(vec3 color) +{ + vec3 lin = bt2020to709(pq2lin(color.rgb)); + return lin2srgb(compress(lin)); +} diff --git a/client/src/main.c b/client/src/main.c index 8cc3673a..5855db41 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -631,6 +631,7 @@ int main_frameThread(void * unused) lgrFormat.frameHeight = frame->frameHeight; lgrFormat.stride = frame->stride; lgrFormat.pitch = frame->pitch; + lgrFormat.hdr = frame->flags & FRAME_FLAG_HDR; if (frame->flags & FRAME_FLAG_TRUNCATED) { @@ -695,11 +696,12 @@ int main_frameThread(void * unused) g_state.formatValid = true; formatVer = frame->formatVer; - DEBUG_INFO("Format: %s %ux%u stride:%u pitch:%u rotation:%d", + DEBUG_INFO("Format: %s %ux%u stride:%u pitch:%u rotation:%d hdr:%d", FrameTypeStr[frame->type], frame->frameWidth, frame->frameHeight, frame->stride, frame->pitch, - frame->rotation); + frame->rotation, + frame->flags & FRAME_FLAG_HDR ? 1 : 0); LG_LOCK(g_state.lgrLock); if (!RENDERER(onFrameFormat, lgrFormat))