GPU: Add Lanczos scaling option

This commit is contained in:
Stenzek 2025-02-05 19:30:42 +10:00
parent 99e81f1559
commit ecbb79c4bf
No known key found for this signature in database
5 changed files with 76 additions and 4 deletions

View File

@ -132,6 +132,10 @@ bool GPUPresenter::CompileDisplayPipelines(bool display, bool deinterlace, bool
fs = shadergen.GenerateDisplayFragmentShader(true, false);
break;
case DisplayScalingMode::Lanczos:
fs = shadergen.GenerateDisplayLanczosFragmentShader();
break;
case DisplayScalingMode::Nearest:
case DisplayScalingMode::NearestInteger:
default:
@ -654,6 +658,8 @@ void GPUPresenter::DrawDisplay(const GSVector2i target_size, const GSVector2i fi
} uniforms;
std::memset(uniforms.params, 0, sizeof(uniforms.params));
const GSVector2 display_texture_size = GSVector2(m_display_texture->GetSizeVec());
switch (g_gpu_settings.display_scaling)
{
case DisplayScalingMode::Nearest:
@ -665,6 +671,14 @@ void GPUPresenter::DrawDisplay(const GSVector2i target_size, const GSVector2i fi
texture_filter_linear = true;
break;
case DisplayScalingMode::Lanczos:
{
const GSVector2 fdisplay_rect_size = GSVector2(display_rect.rsize());
GSVector2::store<true>(&uniforms.params[0], fdisplay_rect_size);
GSVector2::store<true>(&uniforms.params[2], GSVector2::cxpr(1.0f) / (fdisplay_rect_size / display_texture_size));
}
break;
case DisplayScalingMode::BilinearSharp:
{
texture_filter_linear = true;
@ -689,7 +703,6 @@ void GPUPresenter::DrawDisplay(const GSVector2i target_size, const GSVector2i fi
// For bilinear, clamp to 0.5/SIZE-0.5 to avoid bleeding from the adjacent texels in VRAM. This is because
// 1.0 in UV space is not the bottom-right texel, but a mix of the bottom-right and wrapped/next texel.
const GSVector2 display_texture_size = GSVector2(m_display_texture->GetSizeVec());
const GSVector4 display_texture_size4 = GSVector4::xyxy(display_texture_size);
const GSVector4 uv_rect = GSVector4(GSVector4i(m_display_texture_view_x, m_display_texture_view_y,
m_display_texture_view_x + m_display_texture_view_width,

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "gpu_shadergen.h"
@ -95,6 +95,62 @@ std::string GPUShaderGen::GenerateDisplaySharpBilinearFragmentShader() const
return ss.str();
}
std::string GPUShaderGen::GenerateDisplayLanczosFragmentShader() const
{
std::stringstream ss;
WriteHeader(ss);
WriteDisplayUniformBuffer(ss);
DeclareTexture(ss, "samp0", 0, false);
ss << R"(
CONSTANT int KERNEL_SIZE = 3;
CONSTANT float PI = 3.14159265359;
float lanczos(float x)
{
x = abs(x);
if (x < 0.0001)
return 1.0;
if (x > float(KERNEL_SIZE))
return 0.0;
float px = PI * x;
return (float(KERNEL_SIZE) * sin(px) * sin(px / float(KERNEL_SIZE))) / (px * px);
}
)";
DeclareFragmentEntryPoint(ss, 0, 1);
ss << R"(
{
float2 pixel = v_tex0 * u_params.xy;
float2 src_pixel = pixel * u_params.zw;
float2 src = floor(src_pixel - 0.5) + 0.5;
float3 color = float3(0.0, 0.0, 0.0);
float total_weight = 0.0;
for (int i = -KERNEL_SIZE; i <= KERNEL_SIZE; i++)
{
for (int j = -KERNEL_SIZE; j <= KERNEL_SIZE; j++)
{
float2 offset = float2(int2(i, j));
float2 sample_pos = (src + offset) * u_src_size.zw;
float2 dxdy = src_pixel - (src + offset);
float weight = lanczos(dxdy.x) * lanczos(dxdy.y);
color += SAMPLE_TEXTURE_LEVEL(samp0, ClampUV(sample_pos), 0.0).rgb * weight;
total_weight += weight;
}
}
o_col0 = float4(color / total_weight, 1.0);
})";
return std::move(ss).str();
}
std::string GPUShaderGen::GenerateDeinterlaceWeaveFragmentShader() const
{
std::stringstream ss;

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once
@ -14,6 +14,7 @@ public:
std::string GenerateDisplayVertexShader() const;
std::string GenerateDisplayFragmentShader(bool clamp_uv, bool nearest) const;
std::string GenerateDisplaySharpBilinearFragmentShader() const;
std::string GenerateDisplayLanczosFragmentShader() const;
std::string GenerateDeinterlaceWeaveFragmentShader() const;
std::string GenerateDeinterlaceBlendFragmentShader() const;

View File

@ -1900,7 +1900,7 @@ const char* Settings::GetForceVideoTimingDisplayName(ForceVideoTimingMode mode)
}
static constexpr const std::array s_display_scaling_names = {
"Nearest", "NearestInteger", "BilinearSmooth", "BilinearSharp", "BilinearInteger",
"Nearest", "NearestInteger", "BilinearSmooth", "BilinearSharp", "BilinearInteger", "Lanczos",
};
static constexpr const std::array s_display_scaling_display_names = {
TRANSLATE_DISAMBIG_NOOP("Settings", "Nearest-Neighbor", "DisplayScalingMode"),
@ -1908,6 +1908,7 @@ static constexpr const std::array s_display_scaling_display_names = {
TRANSLATE_DISAMBIG_NOOP("Settings", "Bilinear (Smooth)", "DisplayScalingMode"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Bilinear (Sharp)", "DisplayScalingMode"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Bilinear (Integer)", "DisplayScalingMode"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Lanczos (Sharp)", "DisplayScalingMode"),
};
std::optional<DisplayScalingMode> Settings::ParseDisplayScaling(const char* str)

View File

@ -184,6 +184,7 @@ enum class DisplayScalingMode : u8
BilinearSmooth,
BilinearSharp,
BilinearInteger,
Lanczos,
Count
};