diff options
author | sarbes <sarbes@kodi.tv> | 2020-11-08 20:44:14 +0100 |
---|---|---|
committer | sarbes <sarbes@kodi.tv> | 2020-11-08 20:44:14 +0100 |
commit | a6f509ced00026192828d3cc79e887bed8f590fc (patch) | |
tree | d669e97a970038983645397ea3af047202097537 | |
parent | 306145723ecad9b60e81837ad3d681762bdb8384 (diff) |
[OpenGL] Added ACES and Hable mapping
6 files changed, 193 insertions, 60 deletions
diff --git a/system/shaders/GL/1.5/gl_tonemap.glsl b/system/shaders/GL/1.5/gl_tonemap.glsl index bcec4e6550..a70dca2f97 100644 --- a/system/shaders/GL/1.5/gl_tonemap.glsl +++ b/system/shaders/GL/1.5/gl_tonemap.glsl @@ -1,4 +1,50 @@ -float tonemap(float val) +#if (defined(KODI_TONE_MAPPING_REINHARD) || defined(KODI_TONE_MAPPING_ACES) || defined(KODI_TONE_MAPPING_HABLE)) +const float ST2084_m1 = 2610.0 / (4096.0 * 4.0); +const float ST2084_m2 = (2523.0 / 4096.0) * 128.0; +const float ST2084_c1 = 3424.0 / 4096.0; +const float ST2084_c2 = (2413.0 / 4096.0) * 32.0; +const float ST2084_c3 = (2392.0 / 4096.0) * 32.0; +#endif + +#if defined(KODI_TONE_MAPPING_REINHARD) +float reinhard(float x) { - return val * (1 + val/(m_toneP1*m_toneP1))/(1 + val); + return x * (1.0 + x / (m_toneP1 * m_toneP1)) / (1.0 + x); } +#endif + +#if defined(KODI_TONE_MAPPING_ACES) +vec3 aces(vec3 x) +{ + float A = 2.51; + float B = 0.03; + float C = 2.43; + float D = 0.59; + float E = 0.14; + return (x * (A * x + B)) / (x * (C * x + D) + E); +} +#endif + +#if defined(KODI_TONE_MAPPING_HABLE) +vec3 hable(vec3 x) +{ + float A = 0.15; + float B = 0.5; + float C = 0.1; + float D = 0.2; + float E = 0.02; + float F = 0.3; + return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F; +} +#endif + +#if (defined(KODI_TONE_MAPPING_ACES) || defined(KODI_TONE_MAPPING_HABLE)) +vec3 inversePQ(vec3 x) +{ + x = pow(max(x, 0.0), vec3(1.0 / ST2084_m2)); + x = max(x - ST2084_c1, 0.0) / (ST2084_c2 - ST2084_c3 * x); + x = pow(x, vec3(1.0 / ST2084_m1)); + return x; +} +#endif + diff --git a/system/shaders/GL/1.5/gl_yuv2rgb_basic.glsl b/system/shaders/GL/1.5/gl_yuv2rgb_basic.glsl index 5bd2565ac2..7d5605c9cd 100644 --- a/system/shaders/GL/1.5/gl_yuv2rgb_basic.glsl +++ b/system/shaders/GL/1.5/gl_yuv2rgb_basic.glsl @@ -16,6 +16,7 @@ uniform mat3 m_primMat; uniform float m_gammaDstInv; uniform float m_gammaSrc; uniform float m_toneP1; +uniform float m_luminance; uniform vec3 m_coefsDst; in vec2 m_cordY; in vec2 m_cordU; @@ -97,9 +98,23 @@ vec4 process() rgb.rgb = max(vec3(0), m_primMat * rgb.rgb); rgb.rgb = pow(rgb.rgb, vec3(m_gammaDstInv)); -#if defined(XBMC_TONE_MAPPING) +#if defined(KODI_TONE_MAPPING_REINHARD) float luma = dot(rgb.rgb, m_coefsDst); - rgb.rgb *= tonemap(luma) / luma; + rgb.rgb *= reinhard(luma) / luma; + +#elif defined(KODI_TONE_MAPPING_ACES) + rgb.rgb = inversePQ(rgb.rgb); + rgb.rgb *= (10000.0 / m_luminance) * (2.0 / m_toneP1); + rgb.rgb = aces(rgb.rgb); + rgb.rgb *= (1.24 / m_toneP1); + rgb.rgb = pow(rgb.rgb, vec3(0.27)); + +#elif defined(KODI_TONE_MAPPING_HABLE) + rgb.rgb = inversePQ(rgb.rgb); + rgb.rgb *= m_toneP1; + float wp = m_luminance / 100.0; + rgb.rgb = hable(rgb.rgb * wp) / hable(vec3(wp)); + rgb.rgb = pow(rgb.rgb, vec3(1.0 / 2.2)); #endif #endif diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGL.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGL.cpp index 5141635350..777909f22b 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGL.cpp +++ b/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGL.cpp @@ -886,6 +886,7 @@ void CLinuxRendererGL::LoadShaders(int field) // if single pass, create GLSLOutput helper and pass it to YUV2RGB shader EShaderFormat shaderFormat = GetShaderFormat(); std::shared_ptr<GLSLOutput> out; + m_toneMapMethod = m_videoSettings.m_ToneMapMethod; if (m_renderQuality == RQ_SINGLEPASS) { out = std::make_shared<GLSLOutput>(GLSLOutput(4, m_useDithering, m_ditherDepth, @@ -899,6 +900,7 @@ void CLinuxRendererGL::LoadShaders(int field) shaderFormat, m_nonLinStretch, AVColorPrimaries::AVCOL_PRI_BT709, m_srcPrimaries, m_toneMap, + m_toneMapMethod, m_scalingMethod, out); if (!m_cmsOn) m_pYUVShader->SetConvertFullColorRange(m_fullRange); @@ -923,7 +925,7 @@ void CLinuxRendererGL::LoadShaders(int field) { m_pYUVShader = new YUV2RGBProgressiveShader(m_textureTarget == GL_TEXTURE_RECTANGLE, shaderFormat, m_nonLinStretch && m_renderQuality == RQ_SINGLEPASS, - AVColorPrimaries::AVCOL_PRI_BT709, m_srcPrimaries, m_toneMap, out); + AVColorPrimaries::AVCOL_PRI_BT709, m_srcPrimaries, m_toneMap, m_toneMapMethod, out); if (!m_cmsOn) m_pYUVShader->SetConvertFullColorRange(m_fullRange); @@ -1025,26 +1027,11 @@ void CLinuxRendererGL::RenderSinglePass(int index, int field) CPictureBuffer &buf = m_buffers[index]; CYuvPlane (&planes)[YuvImage::MAX_PLANES] = m_buffers[index].fields[field]; - AVColorPrimaries srcPrim = GetSrcPrimaries(buf.m_srcPrimaries, buf.image.width, buf.image.height); - if (srcPrim != m_srcPrimaries) - { - m_srcPrimaries = srcPrim; - m_reloadShaders = true; - } - - bool toneMap = false; - if (m_videoSettings.m_ToneMapMethod != VS_TONEMAPMETHOD_OFF) - { - if (buf.hasLightMetadata || (buf.hasDisplayMetadata && buf.displayMetadata.has_luminance)) - toneMap = true; - } - - if (toneMap != m_toneMap) - m_reloadShaders = true; - m_toneMap = toneMap; + CheckVideoParameters(index); if (m_reloadShaders) { + m_reloadShaders = 0; LoadShaders(field); } @@ -1072,7 +1059,7 @@ void CLinuxRendererGL::RenderSinglePass(int index, int field) m_pYUVShader->SetColParams(buf.m_srcColSpace, buf.m_srcBits, !buf.m_srcFullRange, buf.m_srcTextureBits); m_pYUVShader->SetDisplayMetadata(buf.hasDisplayMetadata, buf.displayMetadata, buf.hasLightMetadata, buf.lightMetadata); - m_pYUVShader->SetToneMapParam(m_videoSettings.m_ToneMapParam); + m_pYUVShader->SetToneMapParam(m_toneMapMethod, m_videoSettings.m_ToneMapParam); //disable non-linear stretch when a dvd menu is shown, parts of the menu are rendered through the overlay renderer //having non-linear stretch on breaks the alignment @@ -1195,23 +1182,7 @@ void CLinuxRendererGL::RenderToFBO(int index, int field, bool weave /*= false*/) CPictureBuffer &buf = m_buffers[index]; CYuvPlane (&planes)[YuvImage::MAX_PLANES] = m_buffers[index].fields[field]; - AVColorPrimaries srcPrim = GetSrcPrimaries(buf.m_srcPrimaries, buf.image.width, buf.image.height); - if (srcPrim != m_srcPrimaries) - { - m_srcPrimaries = srcPrim; - m_reloadShaders = true; - } - - bool toneMap = false; - if (m_videoSettings.m_ToneMapMethod != VS_TONEMAPMETHOD_OFF) - { - if (buf.hasLightMetadata || (buf.hasDisplayMetadata && buf.displayMetadata.has_luminance)) - toneMap = true; - } - - if (toneMap != m_toneMap) - m_reloadShaders = true; - m_toneMap = toneMap; + CheckVideoParameters(index); if (m_reloadShaders) { @@ -1272,7 +1243,7 @@ void CLinuxRendererGL::RenderToFBO(int index, int field, bool weave /*= false*/) m_pYUVShader->SetColParams(buf.m_srcColSpace, buf.m_srcBits, !buf.m_srcFullRange, buf.m_srcTextureBits); m_pYUVShader->SetDisplayMetadata(buf.hasDisplayMetadata, buf.displayMetadata, buf.hasLightMetadata, buf.lightMetadata); - m_pYUVShader->SetToneMapParam(m_videoSettings.m_ToneMapParam); + m_pYUVShader->SetToneMapParam(m_toneMapMethod, m_videoSettings.m_ToneMapParam); if (field == FIELD_TOP) m_pYUVShader->SetField(1); @@ -2732,6 +2703,35 @@ void CLinuxRendererGL::DeleteCLUT() } } +void CLinuxRendererGL::CheckVideoParameters(int index) +{ + CPictureBuffer &buf = m_buffers[index]; + int method = m_videoSettings.m_ToneMapMethod; + + AVColorPrimaries srcPrim = GetSrcPrimaries(buf.m_srcPrimaries, buf.image.width, buf.image.height); + if (srcPrim != m_srcPrimaries) + { + m_srcPrimaries = srcPrim; + m_reloadShaders = true; + } + + bool toneMap = false; + if (method != VS_TONEMAPMETHOD_OFF) + { + if (buf.hasLightMetadata || (buf.hasDisplayMetadata && buf.displayMetadata.has_luminance)) + { + toneMap = true; + } + } + + if (toneMap != m_toneMap || (m_toneMapMethod != method)) + { + m_reloadShaders = true; + } + m_toneMap = toneMap; + m_toneMapMethod = method; +} + AVColorPrimaries CLinuxRendererGL::GetSrcPrimaries(AVColorPrimaries srcPrimaries, unsigned int width, unsigned int height) { AVColorPrimaries ret = srcPrimaries; diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGL.h b/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGL.h index 43b08052ea..91a7d7344a 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGL.h +++ b/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGL.h @@ -103,6 +103,7 @@ protected: virtual void LoadShaders(int field=FIELD_FULL); void SetTextureFilter(GLenum method); void UpdateVideoFilter(); + void CheckVideoParameters(int index); AVColorPrimaries GetSrcPrimaries(AVColorPrimaries srcPrimaries, unsigned int width, unsigned int height); // textures @@ -219,6 +220,7 @@ protected: bool m_fullRange; AVColorPrimaries m_srcPrimaries; bool m_toneMap = false; + int m_toneMapMethod = 0; float m_clearColour = 0.0f; bool m_pboSupported = true; bool m_pboUsed = false; diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.cpp index cff71f037f..7ef9e8ef10 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.cpp +++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.cpp @@ -31,6 +31,7 @@ using namespace Shaders; BaseYUV2RGBGLSLShader::BaseYUV2RGBGLSLShader(bool rect, EShaderFormat format, bool stretch, AVColorPrimaries dstPrimaries, AVColorPrimaries srcPrimaries, bool toneMap, + int toneMapMethod, std::shared_ptr<GLSLOutput> output) { m_width = 1; @@ -90,7 +91,14 @@ BaseYUV2RGBGLSLShader::BaseYUV2RGBGLSLShader(bool rect, EShaderFormat format, bo if (toneMap) { m_toneMapping = true; + m_toneMappingMethod = toneMapMethod; m_defines += "#define XBMC_TONE_MAPPING\n"; + if (toneMapMethod == VS_TONEMAPMETHOD_REINHARD) + m_defines += "#define KODI_TONE_MAPPING_REINHARD\n"; + else if (toneMapMethod == VS_TONEMAPMETHOD_ACES) + m_defines += "#define KODI_TONE_MAPPING_ACES\n"; + else if (toneMapMethod == VS_TONEMAPMETHOD_HABLE) + m_defines += "#define KODI_TONE_MAPPING_HABLE\n"; } VertexShader()->LoadSource("gl_yuv2rgb_vertex.glsl", m_defines); @@ -127,6 +135,7 @@ void BaseYUV2RGBGLSLShader::OnCompiledAndLinked() m_hGammaDstInv = glGetUniformLocation(ProgramHandle(), "m_gammaDstInv"); m_hCoefsDst = glGetUniformLocation(ProgramHandle(), "m_coefsDst"); m_hToneP1 = glGetUniformLocation(ProgramHandle(), "m_toneP1"); + m_hLuminance = glGetUniformLocation(ProgramHandle(), "m_luminance"); VerifyGLState(); if (m_glslOutput) @@ -161,22 +170,37 @@ bool BaseYUV2RGBGLSLShader::OnEnabled() if (m_toneMapping) { - float param = 0.7; - if (m_hasLightMetadata) - param = log10(100) / log10(m_lightMetadata.MaxCLL); - else if (m_hasDisplayMetadata && m_displayMetadata.has_luminance) - param = log10(100) / log10(m_displayMetadata.max_luminance.num/m_displayMetadata.max_luminance.den); - - // Sanity check - if (param < 0.1f || param > 5.0f) - param = 0.7f; - - param *= m_toneMappingParam; - - float coefs[3]; - CConvertMatrix::GetRGBYuvCoefs(AVColorSpace::AVCOL_SPC_BT709, coefs); - glUniform3f(m_hCoefsDst, coefs[0], coefs[1], coefs[2]); - glUniform1f(m_hToneP1, param); + if (m_toneMappingMethod == VS_TONEMAPMETHOD_REINHARD) + { + float param = 0.7; + if (m_hasLightMetadata) + param = log10(100) / log10(m_lightMetadata.MaxCLL); + else if (m_hasDisplayMetadata && m_displayMetadata.has_luminance) + param = log10(100) / log10(m_displayMetadata.max_luminance.num/m_displayMetadata.max_luminance.den); + + // Sanity check + if (param < 0.1f || param > 5.0f) + param = 0.7f; + + param *= m_toneMappingParam; + + float coefs[3]; + CConvertMatrix::GetRGBYuvCoefs(AVColorSpace::AVCOL_SPC_BT709, coefs); + glUniform3f(m_hCoefsDst, coefs[0], coefs[1], coefs[2]); + glUniform1f(m_hToneP1, param); + } + else if (m_toneMapping && m_toneMappingMethod == VS_TONEMAPMETHOD_ACES) + { + glUniform1f(m_hLuminance, GetLuminanceValue()); + glUniform1f(m_hToneP1, m_toneMappingParam); + } + else if (m_toneMapping && m_toneMappingMethod == VS_TONEMAPMETHOD_HABLE) + { + float lumin = GetLuminanceValue(); + float param = (10000.0f / lumin) * (2.0f / m_toneMappingParam); + glUniform1f(m_hLuminance, lumin); + glUniform1f(m_hToneP1, param); + } } VerifyGLState(); @@ -219,6 +243,44 @@ void BaseYUV2RGBGLSLShader::SetDisplayMetadata(bool hasDisplayMetadata, AVMaster m_lightMetadata = lightMetadata; } + +void BaseYUV2RGBGLSLShader::SetToneMapParam(int method, float param) +{ + m_toneMappingMethod = method; + m_toneMappingParam = param; +} + +float BaseYUV2RGBGLSLShader::GetLuminanceValue() const //Maybe move this to linuxrenderer?! same as in baserenderer +{ + float lum1 = 400.0f; // default for bad quality HDR-PQ sources (with no metadata) + float lum2 = lum1; + float lum3 = lum1; + + if (m_hasLightMetadata) + { + uint16_t lum = m_displayMetadata.max_luminance.num / m_displayMetadata.max_luminance.den; + if (m_lightMetadata.MaxCLL >= lum) + { + lum1 = static_cast<float>(lum); + lum2 = static_cast<float>(m_lightMetadata.MaxCLL); + } + else + { + lum1 = static_cast<float>(m_lightMetadata.MaxCLL); + lum2 = static_cast<float>(lum); + } + lum3 = static_cast<float>(m_lightMetadata.MaxFALL); + lum1 = (lum1 * 0.5f) + (lum2 * 0.2f) + (lum3 * 0.3f); + } + else if (m_hasDisplayMetadata && m_displayMetadata.has_luminance) + { + uint16_t lum = m_displayMetadata.max_luminance.num / m_displayMetadata.max_luminance.den; + lum1 = static_cast<float>(lum); + } + + return lum1; +} + ////////////////////////////////////////////////////////////////////// // YUV2RGBProgressiveShader - YUV2RGB with no deinterlacing // Use for weave deinterlacing / progressive @@ -230,9 +292,10 @@ YUV2RGBProgressiveShader::YUV2RGBProgressiveShader(bool rect, AVColorPrimaries dstPrimaries, AVColorPrimaries srcPrimaries, bool toneMap, + int toneMapMethod, std::shared_ptr<GLSLOutput> output) : BaseYUV2RGBGLSLShader( - rect, format, stretch, dstPrimaries, srcPrimaries, toneMap, std::move(output)) + rect, format, stretch, dstPrimaries, srcPrimaries, toneMap, toneMapMethod, std::move(output)) { PixelShader()->LoadSource("gl_yuv2rgb_basic.glsl", m_defines); PixelShader()->AppendSource("gl_output.glsl"); @@ -250,10 +313,11 @@ YUV2RGBFilterShader4::YUV2RGBFilterShader4(bool rect, AVColorPrimaries dstPrimaries, AVColorPrimaries srcPrimaries, bool toneMap, + int toneMapMethod, ESCALINGMETHOD method, std::shared_ptr<GLSLOutput> output) : BaseYUV2RGBGLSLShader( - rect, format, stretch, dstPrimaries, srcPrimaries, toneMap, std::move(output)) + rect, format, stretch, dstPrimaries, srcPrimaries, toneMap, toneMapMethod, std::move(output)) { m_scaling = method; PixelShader()->LoadSource("gl_yuv2rgb_filter4.glsl", m_defines); diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.h b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.h index c2af6c4e4f..e898278203 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.h +++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.h @@ -31,6 +31,7 @@ public: BaseYUV2RGBGLSLShader(bool rect, EShaderFormat format, bool stretch, AVColorPrimaries dst, AVColorPrimaries src, bool toneMap, + int toneMapMethod, std::shared_ptr<GLSLOutput> output); ~BaseYUV2RGBGLSLShader() override; @@ -44,7 +45,8 @@ public: void SetNonLinStretch(float stretch) { m_stretch = stretch; } void SetDisplayMetadata(bool hasDisplayMetadata, AVMasteringDisplayMetadata displayMetadata, bool hasLightMetadata, AVContentLightMetadata lightMetadata); - void SetToneMapParam(float param) { m_toneMappingParam = param; } + void SetToneMapParam(int method, float param); + float GetLuminanceValue() const; void SetConvertFullColorRange(bool convertFullRange) { m_convertFullRange = convertFullRange; } @@ -73,6 +75,7 @@ protected: bool m_hasLightMetadata = false; AVContentLightMetadata m_lightMetadata; bool m_toneMapping = false; + int m_toneMappingMethod = VS_TONEMAPMETHOD_REINHARD; float m_toneMappingParam = 1.0; float m_black; @@ -100,6 +103,7 @@ protected: GLint m_hPrimMat = -1; GLint m_hToneP1 = -1; GLint m_hCoefsDst = -1; + GLint m_hLuminance = -1; // vertex shader attribute handles GLint m_hVertex = -1; @@ -119,6 +123,7 @@ public: bool stretch, AVColorPrimaries dstPrimaries, AVColorPrimaries srcPrimaries, bool toneMap, + int toneMapMethod, std::shared_ptr<GLSLOutput> output); }; @@ -130,6 +135,7 @@ public: bool stretch, AVColorPrimaries dstPrimaries, AVColorPrimaries srcPrimaries, bool toneMap, + int toneMapMethod, ESCALINGMETHOD method, std::shared_ptr<GLSLOutput> output); ~YUV2RGBFilterShader4() override; |