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 0000000000000..7f4dcdf64923d --- /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 0000000000000..cabd56e3a2c45 --- /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 97663f9c09746..67275f920b8e6 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(m_sourceWidth) - m_destRect.Width()) / m_sourceWidth) * 100; float scaleY = fabs((static_cast(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 63e4d304a4a9a..5f4e63090a317 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 917f0f35f49a2..75bf05f3254b4 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 471938a7142b0..ef2261ff186b1 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 9c19bf6c28be9..1ea0ea60a5c02 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 @@ class CRenderSystemGLES : public CRenderSystemBase 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();