Skip to content

Commit

Permalink
GLES: Implement fast HQ scalers (xbmc#24611)
Browse files Browse the repository at this point in the history
  • Loading branch information
sarbes authored Sep 7, 2024
1 parent e8d15b2 commit 2defdf6
Show file tree
Hide file tree
Showing 7 changed files with 297 additions and 10 deletions.
28 changes: 28 additions & 0 deletions system/shaders/GLES/3.1/gles310_yuv2rgb.vert
Original file line number Diff line number Diff line change
@@ -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;
}
129 changes: 129 additions & 0 deletions system/shaders/GLES/3.1/gles310_yuv2rgb_filter.frag
Original file line number Diff line number Diff line change
@@ -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;
}
34 changes: 29 additions & 5 deletions xbmc/cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -616,15 +618,15 @@ 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);
m_renderQuality = RQ_SINGLEPASS;
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:
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -10,6 +10,7 @@
#include "YUV2RGBShaderGLES.h"

#include "../RenderFlags.h"
#include "ConvolutionKernels.h"
#include "ToneMappers.h"
#include "settings/AdvancedSettings.h"
#include "utils/GLUtils.h"
Expand Down Expand Up @@ -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();
}
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
19 changes: 18 additions & 1 deletion xbmc/rendering/gles/RenderSystemGLES.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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"
Expand Down Expand Up @@ -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;
}
Loading

0 comments on commit 2defdf6

Please sign in to comment.