From ecbb79c4bf068f653527cefd13372a4321bdb76a Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 5 Feb 2025 19:30:42 +1000 Subject: [PATCH] GPU: Add Lanczos scaling option --- src/core/gpu_presenter.cpp | 15 +++++++++- src/core/gpu_shadergen.cpp | 58 +++++++++++++++++++++++++++++++++++++- src/core/gpu_shadergen.h | 3 +- src/core/settings.cpp | 3 +- src/core/types.h | 1 + 5 files changed, 76 insertions(+), 4 deletions(-) diff --git a/src/core/gpu_presenter.cpp b/src/core/gpu_presenter.cpp index f97c5eb43..01759f5ab 100644 --- a/src/core/gpu_presenter.cpp +++ b/src/core/gpu_presenter.cpp @@ -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(&uniforms.params[0], fdisplay_rect_size); + GSVector2::store(&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, diff --git a/src/core/gpu_shadergen.cpp b/src/core/gpu_shadergen.cpp index 649bacb1f..2d690e748 100644 --- a/src/core/gpu_shadergen.cpp +++ b/src/core/gpu_shadergen.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin // 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; diff --git a/src/core/gpu_shadergen.h b/src/core/gpu_shadergen.h index cb742f75f..b8a7a447c 100644 --- a/src/core/gpu_shadergen.h +++ b/src/core/gpu_shadergen.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin // 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; diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 89a15ee62..d9b9acf15 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -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 Settings::ParseDisplayScaling(const char* str) diff --git a/src/core/types.h b/src/core/types.h index 3b1ab0773..b168d51b7 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -184,6 +184,7 @@ enum class DisplayScalingMode : u8 BilinearSmooth, BilinearSharp, BilinearInteger, + Lanczos, Count };