aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsarbes <sarbes@kodi.tv>2020-11-08 20:44:14 +0100
committersarbes <sarbes@kodi.tv>2020-11-08 20:44:14 +0100
commita6f509ced00026192828d3cc79e887bed8f590fc (patch)
treed669e97a970038983645397ea3af047202097537
parent306145723ecad9b60e81837ad3d681762bdb8384 (diff)
[OpenGL] Added ACES and Hable mapping
-rw-r--r--system/shaders/GL/1.5/gl_tonemap.glsl50
-rw-r--r--system/shaders/GL/1.5/gl_yuv2rgb_basic.glsl19
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGL.cpp74
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGL.h2
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.cpp100
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGL.h8
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;