aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsarbes <sarbes@kodi.tv>2024-09-07 18:55:39 +0200
committerGitHub <noreply@github.com>2024-09-07 18:55:39 +0200
commit2defdf63bcd9508eb9627c5d9c2dc876557f9a80 (patch)
treeaa8df667306cfacee5e9739737f8cc5e25a25bfb
parente8d15b23ca3061b25966fe97e15bcde27699a6e8 (diff)
GLES: Implement fast HQ scalers (#24611)
-rw-r--r--system/shaders/GLES/3.1/gles310_yuv2rgb.vert28
-rw-r--r--system/shaders/GLES/3.1/gles310_yuv2rgb_filter.frag129
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.cpp34
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.cpp71
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.h22
-rw-r--r--xbmc/rendering/gles/RenderSystemGLES.cpp19
-rw-r--r--xbmc/rendering/gles/RenderSystemGLES.h4
7 files changed, 297 insertions, 10 deletions
diff --git a/system/shaders/GLES/3.1/gles310_yuv2rgb.vert b/system/shaders/GLES/3.1/gles310_yuv2rgb.vert
new file mode 100644
index 0000000000..7f4dcdf649
--- /dev/null
+++ b/system/shaders/GLES/3.1/gles310_yuv2rgb.vert
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#version 310 es
+
+in vec4 m_attrpos;
+in vec2 m_attrcordY;
+in vec2 m_attrcordU;
+in vec2 m_attrcordV;
+out vec2 m_cordY;
+out vec2 m_cordU;
+out vec2 m_cordV;
+uniform mat4 m_proj;
+uniform mat4 m_model;
+
+void main()
+{
+ mat4 mvp = m_proj * m_model;
+ gl_Position = mvp * m_attrpos;
+ m_cordY = m_attrcordY;
+ m_cordU = m_attrcordU;
+ m_cordV = m_attrcordV;
+}
diff --git a/system/shaders/GLES/3.1/gles310_yuv2rgb_filter.frag b/system/shaders/GLES/3.1/gles310_yuv2rgb_filter.frag
new file mode 100644
index 0000000000..cabd56e3a2
--- /dev/null
+++ b/system/shaders/GLES/3.1/gles310_yuv2rgb_filter.frag
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#version 310 es
+
+precision highp float;
+
+uniform sampler2D m_sampY;
+uniform sampler2D m_sampU;
+uniform sampler2D m_sampV;
+uniform vec2 m_step;
+uniform mat4 m_yuvmat;
+uniform float m_stretch;
+uniform float m_alpha;
+uniform sampler2D m_kernelTex;
+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;
+in vec2 m_cordV;
+out vec4 fragColor;
+
+vec4[4] load4x4_0(sampler2D sampler, vec2 pos)
+{
+ vec4[4] tex4x4;
+ vec4 tex2x2 = textureGather(sampler, pos, 0);
+ tex4x4[0].xy = tex2x2.wz;
+ tex4x4[1].xy = tex2x2.xy;
+ tex2x2 = textureGatherOffset(sampler, pos, ivec2(2,0), 0);
+ tex4x4[0].zw = tex2x2.wz;
+ tex4x4[1].zw = tex2x2.xy;
+ tex2x2 = textureGatherOffset(sampler, pos, ivec2(0,2), 0);
+ tex4x4[2].xy = tex2x2.wz;
+ tex4x4[3].xy = tex2x2.xy;
+ tex2x2 = textureGatherOffset(sampler, pos, ivec2(2,2), 0);
+ tex4x4[2].zw = tex2x2.wz;
+ tex4x4[3].zw = tex2x2.xy;
+ return tex4x4;
+}
+
+float filter_0(sampler2D sampler, vec2 coord)
+{
+ vec2 pos = coord + m_step * 0.5;
+ vec2 f = fract(pos / m_step);
+
+ vec4 linetaps = texture(m_kernelTex, vec2(1.0 - f.x, 0.));
+ vec4 coltaps = texture(m_kernelTex, vec2(1.0 - f.y, 0.));
+ linetaps /= linetaps.r + linetaps.g + linetaps.b + linetaps.a;
+ coltaps /= coltaps.r + coltaps.g + coltaps.b + coltaps.a;
+ mat4 conv;
+ conv[0] = linetaps * coltaps.x;
+ conv[1] = linetaps * coltaps.y;
+ conv[2] = linetaps * coltaps.z;
+ conv[3] = linetaps * coltaps.w;
+
+ vec2 startPos = (-1.0 - f) * m_step + pos;
+ vec4[4] tex4x4 = load4x4_0(sampler, startPos);
+ vec4 imageLine0 = tex4x4[0];
+ vec4 imageLine1 = tex4x4[1];
+ vec4 imageLine2 = tex4x4[2];
+ vec4 imageLine3 = tex4x4[3];
+
+ return dot(imageLine0, conv[0]) +
+ dot(imageLine1, conv[1]) +
+ dot(imageLine2, conv[2]) +
+ dot(imageLine3, conv[3]);
+}
+
+void main()
+{
+ vec4 rgb;
+ vec4 yuv;
+
+#if defined(XBMC_YV12) || defined(XBMC_NV12)
+
+ yuv = vec4(filter_0(m_sampY, m_cordY),
+ texture2D(m_sampU, m_cordU).g,
+ texture2D(m_sampV, m_cordV).a,
+ 1.0);
+
+#elif defined(XBMC_NV12_RRG)
+
+ yuv = vec4(filter_0(m_sampY, m_cordY),
+ texture2D(m_sampU, m_cordU).r,
+ texture2D(m_sampV, m_cordV).g,
+ 1.0);
+
+#endif
+
+ rgb = m_yuvmat * yuv;
+ rgb.a = m_alpha;
+
+#if defined(XBMC_COL_CONVERSION)
+ rgb.rgb = pow(max(vec3(0), rgb.rgb), vec3(m_gammaSrc));
+ rgb.rgb = max(vec3(0), m_primMat * rgb.rgb);
+ rgb.rgb = pow(rgb.rgb, vec3(m_gammaDstInv));
+
+#if defined(KODI_TONE_MAPPING_REINHARD)
+ float luma = dot(rgb.rgb, m_coefsDst);
+ 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
+
+ fragColor = rgb;
+}
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.cpp
index 97663f9c09..67275f920b 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.cpp
@@ -584,6 +584,8 @@ void CLinuxRendererGLES::UpdateVideoFilter()
}
m_scalingMethodGui = m_videoSettings.m_ScalingMethod;
+ if (m_scalingMethod != m_scalingMethodGui)
+ m_reloadShaders = true;
m_scalingMethod = m_scalingMethodGui;
m_viewRect = viewRect;
@@ -616,6 +618,8 @@ void CLinuxRendererGLES::UpdateVideoFilter()
return;
}
case VS_SCALINGMETHOD_LINEAR:
+ case VS_SCALINGMETHOD_LANCZOS3_FAST:
+ case VS_SCALINGMETHOD_SPLINE36_FAST:
{
CLog::Log(LOGINFO, "GLES: Selecting single pass rendering");
SetTextureFilter(GL_LINEAR);
@@ -623,8 +627,6 @@ void CLinuxRendererGLES::UpdateVideoFilter()
return;
}
case VS_SCALINGMETHOD_LANCZOS2:
- case VS_SCALINGMETHOD_SPLINE36_FAST:
- case VS_SCALINGMETHOD_LANCZOS3_FAST:
case VS_SCALINGMETHOD_SPLINE36:
case VS_SCALINGMETHOD_LANCZOS3:
case VS_SCALINGMETHOD_CUBIC_B_SPLINE:
@@ -709,9 +711,19 @@ void CLinuxRendererGLES::LoadShaders(int field)
EShaderFormat shaderFormat = GetShaderFormat();
m_toneMapMethod = m_videoSettings.m_ToneMapMethod;
- m_pYUVProgShader = new YUV2RGBProgressiveShader(
- shaderFormat, m_passthroughHDR ? m_srcPrimaries : AVColorPrimaries::AVCOL_PRI_BT709,
- m_srcPrimaries, m_toneMap, m_toneMapMethod);
+ if (m_scalingMethod == VS_SCALINGMETHOD_LANCZOS3_FAST ||
+ m_scalingMethod == VS_SCALINGMETHOD_SPLINE36_FAST)
+ {
+ m_pYUVProgShader = new YUV2RGBFilterShader(
+ shaderFormat, m_passthroughHDR ? m_srcPrimaries : AVColorPrimaries::AVCOL_PRI_BT709,
+ m_srcPrimaries, m_toneMap, m_toneMapMethod, m_scalingMethod);
+ }
+ else
+ {
+ m_pYUVProgShader = new YUV2RGBProgressiveShader(
+ shaderFormat, m_passthroughHDR ? m_srcPrimaries : AVColorPrimaries::AVCOL_PRI_BT709,
+ m_srcPrimaries, m_toneMap, m_toneMapMethod);
+ }
m_pYUVProgShader->SetConvertFullColorRange(m_fullRange);
m_pYUVBobShader = new YUV2RGBBobShader(
shaderFormat, m_passthroughHDR ? m_srcPrimaries : AVColorPrimaries::AVCOL_PRI_BT709,
@@ -1790,6 +1802,18 @@ bool CLinuxRendererGLES::Supports(ESCALINGMETHOD method) const
method == VS_SCALINGMETHOD_SPLINE36 ||
method == VS_SCALINGMETHOD_LANCZOS3)
{
+ if (method == VS_SCALINGMETHOD_SPLINE36_FAST || method == VS_SCALINGMETHOD_LANCZOS3_FAST)
+ {
+#if defined(GL_ES_VERSION_3_0)
+ // we need GLES 3.0 headers for GL_RGBA16f, but GLES 3.1 for the shader
+ uint32_t major, minor;
+ m_renderSystem->GetRenderVersion(major, minor);
+ if (major < 3 || minor == 0)
+ return false;
+#else
+ return false;
+#endif
+ }
// if scaling is below level, avoid hq scaling
float scaleX = fabs((static_cast<float>(m_sourceWidth) - m_destRect.Width()) / m_sourceWidth) * 100;
float scaleY = fabs((static_cast<float>(m_sourceHeight) - m_destRect.Height()) / m_sourceHeight) * 100;
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.cpp
index 63e4d304a4..5f4e63090a 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2007 d4rk
- * Copyright (C) 2007-2018 Team Kodi
+ * Copyright (C) 2007-2024 Team Kodi
* This file is part of Kodi - https://kodi.tv
*
* SPDX-License-Identifier: GPL-2.0-or-later
@@ -10,6 +10,7 @@
#include "YUV2RGBShaderGLES.h"
#include "../RenderFlags.h"
+#include "ConvolutionKernels.h"
#include "ToneMappers.h"
#include "settings/AdvancedSettings.h"
#include "utils/GLUtils.h"
@@ -267,3 +268,71 @@ bool YUV2RGBBobShader::OnEnabled()
VerifyGLState();
return true;
}
+
+//------------------------------------------------------------------------------
+// YUV2RGBFilterShader
+//------------------------------------------------------------------------------
+
+YUV2RGBFilterShader::YUV2RGBFilterShader(EShaderFormat format,
+ AVColorPrimaries dstPrimaries,
+ AVColorPrimaries srcPrimaries,
+ bool toneMap,
+ ETONEMAPMETHOD toneMapMethod,
+ ESCALINGMETHOD method)
+ : BaseYUV2RGBGLSLShader(format, dstPrimaries, srcPrimaries, toneMap, toneMapMethod)
+{
+ m_scaling = method;
+ PixelShader()->LoadSource("gles310_yuv2rgb_filter.frag", m_defines);
+ VertexShader()->LoadSource("gles310_yuv2rgb.vert");
+ PixelShader()->AppendSource("gl_output.glsl");
+
+ PixelShader()->InsertSource("gl_tonemap.glsl", "void main()");
+}
+
+YUV2RGBFilterShader::~YUV2RGBFilterShader()
+{
+ if (m_kernelTex)
+ glDeleteTextures(1, &m_kernelTex);
+ m_kernelTex = 0;
+}
+
+void YUV2RGBFilterShader::OnCompiledAndLinked()
+{
+ BaseYUV2RGBGLSLShader::OnCompiledAndLinked();
+ m_hKernTex = glGetUniformLocation(ProgramHandle(), "m_kernelTex");
+
+ if (m_scaling != VS_SCALINGMETHOD_LANCZOS3_FAST && m_scaling != VS_SCALINGMETHOD_SPLINE36_FAST)
+ m_scaling = VS_SCALINGMETHOD_LANCZOS3_FAST;
+
+ CConvolutionKernel kernel(m_scaling, 256);
+
+ if (m_kernelTex)
+ {
+ glDeleteTextures(1, &m_kernelTex);
+ m_kernelTex = 0;
+ }
+ glGenTextures(1, &m_kernelTex);
+
+ glActiveTexture(GL_TEXTURE3);
+ glBindTexture(GL_TEXTURE_2D, m_kernelTex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+
+ GLvoid* data = (GLvoid*)kernel.GetFloatPixels();
+#if defined(GL_ES_VERSION_3_0)
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, kernel.GetSize(), 1, 0, GL_RGBA, GL_FLOAT, data);
+#endif
+ glActiveTexture(GL_TEXTURE0);
+ VerifyGLState();
+}
+
+bool YUV2RGBFilterShader::OnEnabled()
+{
+ glActiveTexture(GL_TEXTURE3);
+ glBindTexture(GL_TEXTURE_2D, m_kernelTex);
+ glUniform1i(m_hKernTex, 3);
+ glActiveTexture(GL_TEXTURE0);
+
+ return BaseYUV2RGBGLSLShader::OnEnabled();
+}
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.h b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.h
index 917f0f35f4..75bf05f325 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.h
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/YUV2RGBShaderGLES.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2018 Team Kodi
+ * Copyright (C) 2007-2024 Team Kodi
* This file is part of Kodi - https://kodi.tv
*
* SPDX-License-Identifier: GPL-2.0-or-later
@@ -135,5 +135,25 @@ class BaseYUV2RGBGLSLShader : public CGLSLShaderProgram
GLint m_hField = -1;
};
+ class YUV2RGBFilterShader : public BaseYUV2RGBGLSLShader
+ {
+ public:
+ YUV2RGBFilterShader(EShaderFormat format,
+ AVColorPrimaries dstPrimaries,
+ AVColorPrimaries srcPrimaries,
+ bool toneMap,
+ ETONEMAPMETHOD toneMapMethod,
+ ESCALINGMETHOD method);
+ ~YUV2RGBFilterShader() override;
+
+ protected:
+ void OnCompiledAndLinked() override;
+ bool OnEnabled() override;
+
+ GLuint m_kernelTex = 0;
+ GLint m_hKernTex = -1;
+ ESCALINGMETHOD m_scaling = VS_SCALINGMETHOD_LANCZOS3_FAST;
+ };
+
} // namespace GLES
} // end namespace
diff --git a/xbmc/rendering/gles/RenderSystemGLES.cpp b/xbmc/rendering/gles/RenderSystemGLES.cpp
index 471938a714..ef2261ff18 100644
--- a/xbmc/rendering/gles/RenderSystemGLES.cpp
+++ b/xbmc/rendering/gles/RenderSystemGLES.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2018 Team Kodi
+ * Copyright (C) 2005-2024 Team Kodi
* This file is part of Kodi - https://kodi.tv
*
* SPDX-License-Identifier: GPL-2.0-or-later
@@ -8,11 +8,13 @@
#include "RenderSystemGLES.h"
+#include "URL.h"
#include "guilib/DirtyRegion.h"
#include "guilib/GUITextureGLES.h"
#include "rendering/MatrixGL.h"
#include "settings/AdvancedSettings.h"
#include "settings/SettingsComponent.h"
+#include "utils/FileUtils.h"
#include "utils/GLUtils.h"
#include "utils/MathUtils.h"
#include "utils/SystemInfo.h"
@@ -774,3 +776,18 @@ GLint CRenderSystemGLES::GUIShaderGetCoordStep()
return -1;
}
+
+std::string CRenderSystemGLES::GetShaderPath(const std::string& filename)
+{
+ std::string path = "GLES/2.0/";
+
+ if (m_RenderVersionMajor >= 3 && m_RenderVersionMinor >= 1)
+ {
+ std::string file = "special://xbmc/system/shaders/GLES/3.1/" + filename;
+ const CURL pathToUrl(file);
+ if (CFileUtils::Exists(pathToUrl.Get()))
+ return "GLES/3.1/";
+ }
+
+ return path;
+}
diff --git a/xbmc/rendering/gles/RenderSystemGLES.h b/xbmc/rendering/gles/RenderSystemGLES.h
index 9c19bf6c28..1ea0ea60a5 100644
--- a/xbmc/rendering/gles/RenderSystemGLES.h
+++ b/xbmc/rendering/gles/RenderSystemGLES.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2018 Team Kodi
+ * Copyright (C) 2005-2024 Team Kodi
* This file is part of Kodi - https://kodi.tv
*
* SPDX-License-Identifier: GPL-2.0-or-later
@@ -111,7 +111,7 @@ public:
void Project(float &x, float &y, float &z) override;
- std::string GetShaderPath(const std::string &filename) override { return "GLES/2.0/"; }
+ std::string GetShaderPath(const std::string& filename) override;
void InitialiseShaders();
void ReleaseShaders();