From ad472ad28878b49ba84fea91cb4b8d5ddfcf0115 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 20 Sep 2025 03:17:42 +0000 Subject: [PATCH 01/13] [vk, opengl] add lanczo filtering Signed-off-by: lizzie --- src/common/settings_enums.h | 2 +- src/qt_common/shared_translation.cpp | 1 + src/qt_common/shared_translation.h | 2 + src/video_core/host_shaders/CMakeLists.txt | 5 ++- .../host_shaders/present_lanczo.frag | 43 +++++++++++++++++++ .../renderer_opengl/gl_blit_screen.cpp | 3 ++ .../renderer_opengl/present/filters.cpp | 5 +++ .../renderer_opengl/present/filters.h | 1 + .../renderer_vulkan/present/filters.cpp | 6 +++ .../renderer_vulkan/present/filters.h | 1 + .../renderer_vulkan/vk_blit_screen.cpp | 3 ++ 11 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 src/video_core/host_shaders/present_lanczo.frag diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 41133a7819..8d93c61ec1 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -166,7 +166,7 @@ ENUM(ResolutionSetup, Res7X, Res8X); -ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, ScaleForce, Fsr, Area, MaxEnum); +ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczo, ScaleForce, Fsr, Area, MaxEnum); ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); diff --git a/src/qt_common/shared_translation.cpp b/src/qt_common/shared_translation.cpp index eb413f28e9..c549b6b67f 100644 --- a/src/qt_common/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -573,6 +573,7 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) PAIR(ScalingFilter, Bilinear, tr("Bilinear")), PAIR(ScalingFilter, Bicubic, tr("Bicubic")), PAIR(ScalingFilter, Gaussian, tr("Gaussian")), + PAIR(ScalingFilter, Lanczo, tr("Lanczo")), PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")), PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™️ Super Resolution")), PAIR(ScalingFilter, Area, tr("Area")), diff --git a/src/qt_common/shared_translation.h b/src/qt_common/shared_translation.h index 48a2cb5205..a894da290a 100644 --- a/src/qt_common/shared_translation.h +++ b/src/qt_common/shared_translation.h @@ -40,6 +40,8 @@ static const std::map scaling_filter_texts_map {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))}, {Settings::ScalingFilter::Gaussian, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))}, + {Settings::ScalingFilter::Lanczo, + QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Lanczo"))}, {Settings::ScalingFilter::ScaleForce, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))}, {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 688e10d2e4..e7dac21f98 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -1,5 +1,5 @@ -# SPDX-FileCopyrightText: 2018 yuzu Emulator Project -# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later set(FIDELITYFX_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/externals/FidelityFX-FSR/ffx-fsr) @@ -45,6 +45,7 @@ set(SHADER_FILES present_area.frag present_bicubic.frag present_gaussian.frag + present_lanczo.frag queries_prefix_scan_sum.comp queries_prefix_scan_sum_nosubgroups.comp resolve_conditional_render.comp diff --git a/src/video_core/host_shaders/present_lanczo.frag b/src/video_core/host_shaders/present_lanczo.frag new file mode 100644 index 0000000000..5afc985bc3 --- /dev/null +++ b/src/video_core/host_shaders/present_lanczo.frag @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// https://en.wikipedia.org/wiki/Lanczos_resampling + +#version 460 core + +layout (location = 0) in vec2 frag_tex_coord; +layout (location = 0) out vec4 color; +layout (binding = 0) uniform sampler2D color_texture; + +#define PI 3.1415926535897932384626433 + +float sinc(float x) { + return x == 0.0f ? 1.0f : sin(PI * x) / (PI * x); +} + +float lanczos(vec2 v, float a) { + float d = sqrt(v.x * v.x + v.y * v.y); + return sinc(d) / sinc(d / a); +} + +vec4 textureLanczos(sampler2D textureSampler, vec2 p) { + const int r = 1; //radius (1 = 3 steps) + vec3 c_sum = vec3(0.0f); + float w_sum = 0.0f; + vec2 res = vec2(textureSize(textureSampler, 0)); + vec2 cc = floor(p * res) / res; + // kernel size = (r * 2 + 1) * (r * 2 + 1) + for (int x = -r; x <= r; x++) + for (int y = -r; y <= r; y++) { + vec2 kp = 0.5f * (vec2(x, y) / res); // 0.5 = half-pixel level resampling + vec2 uv = cc + kp; + float w = lanczos(kp, float(r)); + c_sum += w * texture(textureSampler, p + kp).rgb; + w_sum += w; + } + return vec4(c_sum / w_sum, 1.0f); +} + +void main() { + color = textureLanczos(color_texture, frag_tex_coord); +} diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp index 2071fe8d15..9fff39143e 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.cpp +++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp @@ -89,6 +89,9 @@ void BlitScreen::CreateWindowAdapt() { case Settings::ScalingFilter::Gaussian: window_adapt = MakeGaussian(device); break; + case Settings::ScalingFilter::Lanczo: + window_adapt = MakeLanczo(device); + break; case Settings::ScalingFilter::ScaleForce: window_adapt = MakeScaleForce(device); break; diff --git a/src/video_core/renderer_opengl/present/filters.cpp b/src/video_core/renderer_opengl/present/filters.cpp index c5ac8e7823..a9b3cdd0d9 100644 --- a/src/video_core/renderer_opengl/present/filters.cpp +++ b/src/video_core/renderer_opengl/present/filters.cpp @@ -37,6 +37,11 @@ std::unique_ptr MakeGaussian(const Device& device) { HostShaders::PRESENT_GAUSSIAN_FRAG); } +std::unique_ptr MakeLanczo(const Device& device) { + return std::make_unique(device, CreateBilinearSampler(), + HostShaders::PRESENT_LANCZO_FRAG); +} + std::unique_ptr MakeScaleForce(const Device& device) { return std::make_unique( device, CreateBilinearSampler(), diff --git a/src/video_core/renderer_opengl/present/filters.h b/src/video_core/renderer_opengl/present/filters.h index be2ce24842..c098d0da2e 100644 --- a/src/video_core/renderer_opengl/present/filters.h +++ b/src/video_core/renderer_opengl/present/filters.h @@ -18,6 +18,7 @@ std::unique_ptr MakeNearestNeighbor(const Device& device); std::unique_ptr MakeBilinear(const Device& device); std::unique_ptr MakeBicubic(const Device& device); std::unique_ptr MakeGaussian(const Device& device); +std::unique_ptr MakeLanczo(const Device& device); std::unique_ptr MakeScaleForce(const Device& device); std::unique_ptr MakeArea(const Device& device); diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp index 7843f38d2c..a3a6bfc2f6 100644 --- a/src/video_core/renderer_vulkan/present/filters.cpp +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -12,6 +12,7 @@ #include "video_core/host_shaders/present_area_frag_spv.h" #include "video_core/host_shaders/present_bicubic_frag_spv.h" #include "video_core/host_shaders/present_gaussian_frag_spv.h" +#include "video_core/host_shaders/present_lanczso_frag_spv.h" #include "video_core/host_shaders/vulkan_present_frag_spv.h" #include "video_core/host_shaders/vulkan_present_scaleforce_fp16_frag_spv.h" #include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h" @@ -59,6 +60,11 @@ std::unique_ptr MakeGaussian(const Device& device, VkFormat fra BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV)); } +std::unique_ptr MakeLanczo(const Device& device, VkFormat frame_format) { + return std::make_unique(device, frame_format, CreateBilinearSampler(device), + BuildShader(device, PRESENT_LANCZO_FRAG_SPV)); +} + std::unique_ptr MakeScaleForce(const Device& device, VkFormat frame_format) { return std::make_unique(device, frame_format, CreateBilinearSampler(device), SelectScaleForceShader(device)); diff --git a/src/video_core/renderer_vulkan/present/filters.h b/src/video_core/renderer_vulkan/present/filters.h index c8259487f8..c51938db24 100644 --- a/src/video_core/renderer_vulkan/present/filters.h +++ b/src/video_core/renderer_vulkan/present/filters.h @@ -19,6 +19,7 @@ std::unique_ptr MakeNearestNeighbor(const Device& device, VkFor std::unique_ptr MakeBilinear(const Device& device, VkFormat frame_format); std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format); std::unique_ptr MakeGaussian(const Device& device, VkFormat frame_format); +std::unique_ptr MakeLanczo(const Device& device, VkFormat frame_format); std::unique_ptr MakeScaleForce(const Device& device, VkFormat frame_format); std::unique_ptr MakeArea(const Device& device, VkFormat frame_format); diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 39f07b966d..b398062dae 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -46,6 +46,9 @@ void BlitScreen::SetWindowAdaptPass() { case Settings::ScalingFilter::Gaussian: window_adapt = MakeGaussian(device, swapchain_view_format); break; + case Settings::ScalingFilter::Lanczo: + window_adapt = MakeLanczo(device, swapchain_view_format); + break; case Settings::ScalingFilter::ScaleForce: window_adapt = MakeScaleForce(device, swapchain_view_format); break; From 2a472487555a9865e9ca2c429281680e4fc1bc5a Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 20 Sep 2025 03:26:07 +0000 Subject: [PATCH 02/13] fix mispell Signed-off-by: lizzie --- src/common/settings_enums.h | 2 +- src/qt_common/shared_translation.cpp | 2 +- src/qt_common/shared_translation.h | 4 ++-- src/video_core/host_shaders/CMakeLists.txt | 2 +- .../{present_lanczo.frag => present_lanczos.frag} | 0 src/video_core/renderer_opengl/gl_blit_screen.cpp | 4 ++-- src/video_core/renderer_opengl/present/filters.cpp | 3 ++- src/video_core/renderer_opengl/present/filters.h | 2 +- src/video_core/renderer_vulkan/present/filters.cpp | 4 ++-- src/video_core/renderer_vulkan/present/filters.h | 2 +- src/video_core/renderer_vulkan/vk_blit_screen.cpp | 4 ++-- 11 files changed, 15 insertions(+), 14 deletions(-) rename src/video_core/host_shaders/{present_lanczo.frag => present_lanczos.frag} (100%) diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 8d93c61ec1..c768c23cda 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -166,7 +166,7 @@ ENUM(ResolutionSetup, Res7X, Res8X); -ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczo, ScaleForce, Fsr, Area, MaxEnum); +ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, ScaleForce, Fsr, Area, MaxEnum); ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); diff --git a/src/qt_common/shared_translation.cpp b/src/qt_common/shared_translation.cpp index c549b6b67f..91c16f3102 100644 --- a/src/qt_common/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -573,7 +573,7 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) PAIR(ScalingFilter, Bilinear, tr("Bilinear")), PAIR(ScalingFilter, Bicubic, tr("Bicubic")), PAIR(ScalingFilter, Gaussian, tr("Gaussian")), - PAIR(ScalingFilter, Lanczo, tr("Lanczo")), + PAIR(ScalingFilter, Lanczos, tr("Lanczos")), PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")), PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™️ Super Resolution")), PAIR(ScalingFilter, Area, tr("Area")), diff --git a/src/qt_common/shared_translation.h b/src/qt_common/shared_translation.h index a894da290a..ea8e7fe1bd 100644 --- a/src/qt_common/shared_translation.h +++ b/src/qt_common/shared_translation.h @@ -40,8 +40,8 @@ static const std::map scaling_filter_texts_map {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))}, {Settings::ScalingFilter::Gaussian, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))}, - {Settings::ScalingFilter::Lanczo, - QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Lanczo"))}, + {Settings::ScalingFilter::Lanczos, + QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Lanczos"))}, {Settings::ScalingFilter::ScaleForce, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))}, {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index e7dac21f98..d8ea826498 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -45,7 +45,7 @@ set(SHADER_FILES present_area.frag present_bicubic.frag present_gaussian.frag - present_lanczo.frag + present_lanczos.frag queries_prefix_scan_sum.comp queries_prefix_scan_sum_nosubgroups.comp resolve_conditional_render.comp diff --git a/src/video_core/host_shaders/present_lanczo.frag b/src/video_core/host_shaders/present_lanczos.frag similarity index 100% rename from src/video_core/host_shaders/present_lanczo.frag rename to src/video_core/host_shaders/present_lanczos.frag diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp index 9fff39143e..5d2246ada1 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.cpp +++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp @@ -89,8 +89,8 @@ void BlitScreen::CreateWindowAdapt() { case Settings::ScalingFilter::Gaussian: window_adapt = MakeGaussian(device); break; - case Settings::ScalingFilter::Lanczo: - window_adapt = MakeLanczo(device); + case Settings::ScalingFilter::Lanczos: + window_adapt = MakeLanczos(device); break; case Settings::ScalingFilter::ScaleForce: window_adapt = MakeScaleForce(device); diff --git a/src/video_core/renderer_opengl/present/filters.cpp b/src/video_core/renderer_opengl/present/filters.cpp index a9b3cdd0d9..8464123be0 100644 --- a/src/video_core/renderer_opengl/present/filters.cpp +++ b/src/video_core/renderer_opengl/present/filters.cpp @@ -12,6 +12,7 @@ #include "video_core/host_shaders/present_area_frag.h" #include "video_core/host_shaders/present_bicubic_frag.h" #include "video_core/host_shaders/present_gaussian_frag.h" +#include "video_core/host_shaders/present_lanczos_frag.h" #include "video_core/renderer_opengl/present/filters.h" #include "video_core/renderer_opengl/present/util.h" @@ -37,7 +38,7 @@ std::unique_ptr MakeGaussian(const Device& device) { HostShaders::PRESENT_GAUSSIAN_FRAG); } -std::unique_ptr MakeLanczo(const Device& device) { +std::unique_ptr MakeLanczos(const Device& device) { return std::make_unique(device, CreateBilinearSampler(), HostShaders::PRESENT_LANCZO_FRAG); } diff --git a/src/video_core/renderer_opengl/present/filters.h b/src/video_core/renderer_opengl/present/filters.h index c098d0da2e..f71b5f93d3 100644 --- a/src/video_core/renderer_opengl/present/filters.h +++ b/src/video_core/renderer_opengl/present/filters.h @@ -18,7 +18,7 @@ std::unique_ptr MakeNearestNeighbor(const Device& device); std::unique_ptr MakeBilinear(const Device& device); std::unique_ptr MakeBicubic(const Device& device); std::unique_ptr MakeGaussian(const Device& device); -std::unique_ptr MakeLanczo(const Device& device); +std::unique_ptr MakeLanczos(const Device& device); std::unique_ptr MakeScaleForce(const Device& device); std::unique_ptr MakeArea(const Device& device); diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp index a3a6bfc2f6..5ab3ac3114 100644 --- a/src/video_core/renderer_vulkan/present/filters.cpp +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -12,7 +12,7 @@ #include "video_core/host_shaders/present_area_frag_spv.h" #include "video_core/host_shaders/present_bicubic_frag_spv.h" #include "video_core/host_shaders/present_gaussian_frag_spv.h" -#include "video_core/host_shaders/present_lanczso_frag_spv.h" +#include "video_core/host_shaders/present_lanczos_frag_spv.h" #include "video_core/host_shaders/vulkan_present_frag_spv.h" #include "video_core/host_shaders/vulkan_present_scaleforce_fp16_frag_spv.h" #include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h" @@ -60,7 +60,7 @@ std::unique_ptr MakeGaussian(const Device& device, VkFormat fra BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV)); } -std::unique_ptr MakeLanczo(const Device& device, VkFormat frame_format) { +std::unique_ptr MakeLanczos(const Device& device, VkFormat frame_format) { return std::make_unique(device, frame_format, CreateBilinearSampler(device), BuildShader(device, PRESENT_LANCZO_FRAG_SPV)); } diff --git a/src/video_core/renderer_vulkan/present/filters.h b/src/video_core/renderer_vulkan/present/filters.h index c51938db24..8b0630e748 100644 --- a/src/video_core/renderer_vulkan/present/filters.h +++ b/src/video_core/renderer_vulkan/present/filters.h @@ -19,7 +19,7 @@ std::unique_ptr MakeNearestNeighbor(const Device& device, VkFor std::unique_ptr MakeBilinear(const Device& device, VkFormat frame_format); std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format); std::unique_ptr MakeGaussian(const Device& device, VkFormat frame_format); -std::unique_ptr MakeLanczo(const Device& device, VkFormat frame_format); +std::unique_ptr MakeLanczos(const Device& device, VkFormat frame_format); std::unique_ptr MakeScaleForce(const Device& device, VkFormat frame_format); std::unique_ptr MakeArea(const Device& device, VkFormat frame_format); diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index b398062dae..3a003a871e 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -46,8 +46,8 @@ void BlitScreen::SetWindowAdaptPass() { case Settings::ScalingFilter::Gaussian: window_adapt = MakeGaussian(device, swapchain_view_format); break; - case Settings::ScalingFilter::Lanczo: - window_adapt = MakeLanczo(device, swapchain_view_format); + case Settings::ScalingFilter::Lanczos: + window_adapt = MakeLanczos(device, swapchain_view_format); break; case Settings::ScalingFilter::ScaleForce: window_adapt = MakeScaleForce(device, swapchain_view_format); From 5c00af4a02079d2deb01356841a272b7fedc0c09 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 20 Sep 2025 03:39:37 +0000 Subject: [PATCH 03/13] fix Signed-off-by: lizzie --- src/video_core/renderer_opengl/present/filters.cpp | 2 +- src/video_core/renderer_vulkan/present/filters.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_opengl/present/filters.cpp b/src/video_core/renderer_opengl/present/filters.cpp index 8464123be0..3424a52d80 100644 --- a/src/video_core/renderer_opengl/present/filters.cpp +++ b/src/video_core/renderer_opengl/present/filters.cpp @@ -40,7 +40,7 @@ std::unique_ptr MakeGaussian(const Device& device) { std::unique_ptr MakeLanczos(const Device& device) { return std::make_unique(device, CreateBilinearSampler(), - HostShaders::PRESENT_LANCZO_FRAG); + HostShaders::PRESENT_LANCZOS_FRAG); } std::unique_ptr MakeScaleForce(const Device& device) { diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp index 5ab3ac3114..8fed222504 100644 --- a/src/video_core/renderer_vulkan/present/filters.cpp +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -62,7 +62,7 @@ std::unique_ptr MakeGaussian(const Device& device, VkFormat fra std::unique_ptr MakeLanczos(const Device& device, VkFormat frame_format) { return std::make_unique(device, frame_format, CreateBilinearSampler(device), - BuildShader(device, PRESENT_LANCZO_FRAG_SPV)); + BuildShader(device, PRESENT_LANCZOS_FRAG_SPV)); } std::unique_ptr MakeScaleForce(const Device& device, VkFormat frame_format) { From 21b0964df6f7e987fda92c0db4592fc68a4586a3 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 20 Sep 2025 13:00:46 +0000 Subject: [PATCH 04/13] optimize with precomputed kernel Signed-off-by: lizzie --- .../host_shaders/present_lanczos.frag | 35 +++++--------- tools/lanczos_gen.c | 48 +++++++++++++++++++ 2 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 tools/lanczos_gen.c diff --git a/src/video_core/host_shaders/present_lanczos.frag b/src/video_core/host_shaders/present_lanczos.frag index 5afc985bc3..9501b7ca33 100644 --- a/src/video_core/host_shaders/present_lanczos.frag +++ b/src/video_core/host_shaders/present_lanczos.frag @@ -9,32 +9,23 @@ layout (location = 0) in vec2 frag_tex_coord; layout (location = 0) out vec4 color; layout (binding = 0) uniform sampler2D color_texture; -#define PI 3.1415926535897932384626433 - -float sinc(float x) { - return x == 0.0f ? 1.0f : sin(PI * x) / (PI * x); -} - -float lanczos(vec2 v, float a) { - float d = sqrt(v.x * v.x + v.y * v.y); - return sinc(d) / sinc(d / a); -} - +// precomputed kernel +const float w_kernel[49] = float[] ( + -0.238811f, 0.531959f, 0.961865f, 1.000000f, 0.961865f, 0.531959f, -0.238811f, 0.531959f, 0.957419f, 0.313883f, -0.000000f, 0.313883f, 0.957419f, 0.531959f, 0.961865f, 0.313883f, -0.322602f, 0.000000f, -0.322602f, 0.313883f, 0.961865f, 1.000000f, -0.000000f, 0.000000f, 1.000000f, 0.000000f, -0.000000f, 1.000000f, 0.961865f, 0.313883f, -0.322602f, 0.000000f, -0.322602f, 0.313883f, 0.961865f, 0.531959f, 0.957419f, 0.313883f, -0.000000f, 0.313883f, 0.957419f, 0.531959f, -0.238811f, 0.531959f, 0.961865f, 1.000000f, 0.961865f, 0.531959f, -0.238811f +); +const vec2 w_pos[49] = vec2[] ( + vec2(-0.750000f, -0.750000f), vec2(-0.750000f, -0.500000f), vec2(-0.750000f, -0.250000f), vec2(-0.750000f, 0.000000f), vec2(-0.750000f, 0.250000f), vec2(-0.750000f, 0.500000f), vec2(-0.750000f, 0.750000f), vec2(-0.500000f, -0.750000f), vec2(-0.500000f, -0.500000f), vec2(-0.500000f, -0.250000f), vec2(-0.500000f, 0.000000f), vec2(-0.500000f, 0.250000f), vec2(-0.500000f, 0.500000f), vec2(-0.500000f, 0.750000f), vec2(-0.250000f, -0.750000f), vec2(-0.250000f, -0.500000f), vec2(-0.250000f, -0.250000f), vec2(-0.250000f, 0.000000f), vec2(-0.250000f, 0.250000f), vec2(-0.250000f, 0.500000f), vec2(-0.250000f, 0.750000f), vec2(0.000000f, -0.750000f), vec2(0.000000f, -0.500000f), vec2(0.000000f, -0.250000f), vec2(0.000000f, 0.000000f), vec2(0.000000f, 0.250000f), vec2(0.000000f, 0.500000f), vec2(0.000000f, 0.750000f), vec2(0.250000f, -0.750000f), vec2(0.250000f, -0.500000f), vec2(0.250000f, -0.250000f), vec2(0.250000f, 0.000000f), vec2(0.250000f, 0.250000f), vec2(0.250000f, 0.500000f), vec2(0.250000f, 0.750000f), vec2(0.500000f, -0.750000f), vec2(0.500000f, -0.500000f), vec2(0.500000f, -0.250000f), vec2(0.500000f, 0.000000f), vec2(0.500000f, 0.250000f), vec2(0.500000f, 0.500000f), vec2(0.500000f, 0.750000f), vec2(0.750000f, -0.750000f), vec2(0.750000f, -0.500000f), vec2(0.750000f, -0.250000f), vec2(0.750000f, 0.000000f), vec2(0.750000f, 0.250000f), vec2(0.750000f, 0.500000f), vec2(0.750000f, 0.750000f) +); +const float w_sum = 21.045683f; vec4 textureLanczos(sampler2D textureSampler, vec2 p) { - const int r = 1; //radius (1 = 3 steps) vec3 c_sum = vec3(0.0f); - float w_sum = 0.0f; vec2 res = vec2(textureSize(textureSampler, 0)); vec2 cc = floor(p * res) / res; - // kernel size = (r * 2 + 1) * (r * 2 + 1) - for (int x = -r; x <= r; x++) - for (int y = -r; y <= r; y++) { - vec2 kp = 0.5f * (vec2(x, y) / res); // 0.5 = half-pixel level resampling - vec2 uv = cc + kp; - float w = lanczos(kp, float(r)); - c_sum += w * texture(textureSampler, p + kp).rgb; - w_sum += w; - } + for (int i = 0; i < 49; i++) { // kernel size = (r * 2 + 1) ^ 2 + vec2 kp = w_pos[i] / res; + vec2 uv = cc + kp; + c_sum += w_kernel[i] * texture(textureSampler, p + kp).rgb; + } return vec4(c_sum / w_sum, 1.0f); } diff --git a/tools/lanczos_gen.c b/tools/lanczos_gen.c new file mode 100644 index 0000000000..6d7be3cb0e --- /dev/null +++ b/tools/lanczos_gen.c @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// clang -lm tools/lanczos_gen.c -o tools/lanczos_gen && ./tools/lanczos_gen +#include +#include + +double sinc(double x) { + return x == 0.0f ? 1.0f : sin(M_PI * x) / (M_PI * x); +} + +typedef struct vec2 { + double x; + double y; +} vec2; + +double lanczos(vec2 v, float a) { + double d = sqrt(v.x * v.x + v.y * v.y); + return sinc(d) / sinc(d / a); +} + +int main(int argc, char* argv[]) { + const int r = 3; //radius (1 = 3 steps) + const int k_size = (r * 2 + 1) * (r * 2 + 1); + double w_sum = 0.0f; + // kernel size = (r * 2 + 1) ^ 2 + printf("const float w_kernel[%i] = float[] (\n ", k_size); + double factor = 1.0f / ((double)r + 1.0f); + for (int x = -r; x <= r; x++) + for (int y = -r; y <= r; y++) { + double w = lanczos((vec2){ .x = x, .y = y }, (double)r); + printf("%lff, ", w); + w_sum += w; + } + printf("\n);\n"); + printf("const vec2 w_pos[%i] = vec2[] (\n ", k_size); + for (int x = -r; x <= r; x++) + for (int y = -r; y <= r; y++) { + vec2 kp = (vec2){ + .x = x * factor, + .y = y * factor + }; + printf("vec2(%lff, %lff), ", kp.x, kp.y); + } + printf("\n);\n"); + printf("const float w_sum = %lff;\n", w_sum); + return 0; +} From de8dc44ab1a359ba5b7f16c9b864d4ad4946ea2a Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 20 Sep 2025 14:26:53 +0000 Subject: [PATCH 05/13] actually memory is bad Signed-off-by: lizzie --- .../host_shaders/present_lanczos.frag | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/video_core/host_shaders/present_lanczos.frag b/src/video_core/host_shaders/present_lanczos.frag index 9501b7ca33..b69b329c1b 100644 --- a/src/video_core/host_shaders/present_lanczos.frag +++ b/src/video_core/host_shaders/present_lanczos.frag @@ -9,23 +9,29 @@ layout (location = 0) in vec2 frag_tex_coord; layout (location = 0) out vec4 color; layout (binding = 0) uniform sampler2D color_texture; -// precomputed kernel -const float w_kernel[49] = float[] ( - -0.238811f, 0.531959f, 0.961865f, 1.000000f, 0.961865f, 0.531959f, -0.238811f, 0.531959f, 0.957419f, 0.313883f, -0.000000f, 0.313883f, 0.957419f, 0.531959f, 0.961865f, 0.313883f, -0.322602f, 0.000000f, -0.322602f, 0.313883f, 0.961865f, 1.000000f, -0.000000f, 0.000000f, 1.000000f, 0.000000f, -0.000000f, 1.000000f, 0.961865f, 0.313883f, -0.322602f, 0.000000f, -0.322602f, 0.313883f, 0.961865f, 0.531959f, 0.957419f, 0.313883f, -0.000000f, 0.313883f, 0.957419f, 0.531959f, -0.238811f, 0.531959f, 0.961865f, 1.000000f, 0.961865f, 0.531959f, -0.238811f -); -const vec2 w_pos[49] = vec2[] ( - vec2(-0.750000f, -0.750000f), vec2(-0.750000f, -0.500000f), vec2(-0.750000f, -0.250000f), vec2(-0.750000f, 0.000000f), vec2(-0.750000f, 0.250000f), vec2(-0.750000f, 0.500000f), vec2(-0.750000f, 0.750000f), vec2(-0.500000f, -0.750000f), vec2(-0.500000f, -0.500000f), vec2(-0.500000f, -0.250000f), vec2(-0.500000f, 0.000000f), vec2(-0.500000f, 0.250000f), vec2(-0.500000f, 0.500000f), vec2(-0.500000f, 0.750000f), vec2(-0.250000f, -0.750000f), vec2(-0.250000f, -0.500000f), vec2(-0.250000f, -0.250000f), vec2(-0.250000f, 0.000000f), vec2(-0.250000f, 0.250000f), vec2(-0.250000f, 0.500000f), vec2(-0.250000f, 0.750000f), vec2(0.000000f, -0.750000f), vec2(0.000000f, -0.500000f), vec2(0.000000f, -0.250000f), vec2(0.000000f, 0.000000f), vec2(0.000000f, 0.250000f), vec2(0.000000f, 0.500000f), vec2(0.000000f, 0.750000f), vec2(0.250000f, -0.750000f), vec2(0.250000f, -0.500000f), vec2(0.250000f, -0.250000f), vec2(0.250000f, 0.000000f), vec2(0.250000f, 0.250000f), vec2(0.250000f, 0.500000f), vec2(0.250000f, 0.750000f), vec2(0.500000f, -0.750000f), vec2(0.500000f, -0.500000f), vec2(0.500000f, -0.250000f), vec2(0.500000f, 0.000000f), vec2(0.500000f, 0.250000f), vec2(0.500000f, 0.500000f), vec2(0.500000f, 0.750000f), vec2(0.750000f, -0.750000f), vec2(0.750000f, -0.500000f), vec2(0.750000f, -0.250000f), vec2(0.750000f, 0.000000f), vec2(0.750000f, 0.250000f), vec2(0.750000f, 0.500000f), vec2(0.750000f, 0.750000f) -); -const float w_sum = 21.045683f; +#define PI 3.1415926535897932384626433 +float sinc(float x) { + return x == 0.0f ? 1.0f : sin(PI * x) / (PI * x); +} +float lanczos(vec2 v, float a) { + float d = length(v); + return sinc(d) / sinc(d / a); +} vec4 textureLanczos(sampler2D textureSampler, vec2 p) { vec3 c_sum = vec3(0.0f); + float w_sum = 0.0f; vec2 res = vec2(textureSize(textureSampler, 0)); vec2 cc = floor(p * res) / res; - for (int i = 0; i < 49; i++) { // kernel size = (r * 2 + 1) ^ 2 - vec2 kp = w_pos[i] / res; - vec2 uv = cc + kp; - c_sum += w_kernel[i] * texture(textureSampler, p + kp).rgb; - } + // kernel size = (2r + 1)^2 + const int r = 3; //radius (1 = 3 steps) + for (int x = -r; x <= r; x++) + for (int y = -r; y <= r; y++) { + vec2 kp = 0.5f * (vec2(x, y) / res); // 0.5 = half-pixel level resampling + vec2 uv = cc + kp; + float w = lanczos(kp, float(r)); + c_sum += w * texture(textureSampler, p + kp).rgb; + w_sum += w; + } return vec4(c_sum / w_sum, 1.0f); } From 97933fe7e8bea72c06be9136fd10a4864b8c7ae9 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 20 Sep 2025 20:12:34 +0000 Subject: [PATCH 06/13] add spline-1 filter Signed-off-by: lizzie --- src/common/settings_enums.h | 2 +- src/qt_common/shared_translation.cpp | 1 + src/qt_common/shared_translation.h | 2 ++ src/video_core/host_shaders/CMakeLists.txt | 1 + .../host_shaders/present_spline1.frag | 24 +++++++++++++++++++ .../renderer_opengl/gl_blit_screen.cpp | 3 +++ .../renderer_opengl/present/filters.cpp | 5 ++++ .../renderer_opengl/present/filters.h | 1 + .../renderer_vulkan/present/filters.cpp | 5 ++++ .../renderer_vulkan/present/filters.h | 1 + .../renderer_vulkan/vk_blit_screen.cpp | 3 +++ 11 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/video_core/host_shaders/present_spline1.frag diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index c768c23cda..ebfa4ceb9e 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -166,7 +166,7 @@ ENUM(ResolutionSetup, Res7X, Res8X); -ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, ScaleForce, Fsr, Area, MaxEnum); +ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, MaxEnum); ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); diff --git a/src/qt_common/shared_translation.cpp b/src/qt_common/shared_translation.cpp index 91c16f3102..4254253c2f 100644 --- a/src/qt_common/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -572,6 +572,7 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")), PAIR(ScalingFilter, Bilinear, tr("Bilinear")), PAIR(ScalingFilter, Bicubic, tr("Bicubic")), + PAIR(ScalingFilter, Spline1, tr("Spline-1")), PAIR(ScalingFilter, Gaussian, tr("Gaussian")), PAIR(ScalingFilter, Lanczos, tr("Lanczos")), PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")), diff --git a/src/qt_common/shared_translation.h b/src/qt_common/shared_translation.h index ea8e7fe1bd..c9216c2daa 100644 --- a/src/qt_common/shared_translation.h +++ b/src/qt_common/shared_translation.h @@ -38,6 +38,8 @@ static const std::map scaling_filter_texts_map {Settings::ScalingFilter::Bilinear, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))}, {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))}, + {Settings::ScalingFilter::Spline1, + QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Spline-1"))}, {Settings::ScalingFilter::Gaussian, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))}, {Settings::ScalingFilter::Lanczos, diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index d8ea826498..c14b44a45a 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -46,6 +46,7 @@ set(SHADER_FILES present_bicubic.frag present_gaussian.frag present_lanczos.frag + present_spline1.frag queries_prefix_scan_sum.comp queries_prefix_scan_sum_nosubgroups.comp resolve_conditional_render.comp diff --git a/src/video_core/host_shaders/present_spline1.frag b/src/video_core/host_shaders/present_spline1.frag new file mode 100644 index 0000000000..871b47586b --- /dev/null +++ b/src/video_core/host_shaders/present_spline1.frag @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// Spline (smooth linear inerpolation) with 1 texel fetch (needs bilinear to work) +// Emulates bicubic without actually doing bicubic +// See https://iquilezles.org/articles/texture, unfortunely there are issues with the original +// where smoothstep "expansion" actually results in worse codegen (SPIRV-Opt does a direct conv to smoothstep) +// TODO: Numerical analysis - fract is sawtooth func and floor, reuse params? Perhaps - no need for precision + +#version 460 core + +layout (location = 0) in vec2 frag_tex_coord; +layout (location = 0) out vec4 color; +layout (binding = 0) uniform sampler2D color_texture; + +vec4 textureSpline1(sampler2D sam, vec2 uv) { + float r = float(textureSize(sam, 0).x); + vec2 x = fract(uv * r + 0.5); + return texture(sam, (floor(uv * r + 0.5) + smoothstep(0.0, 1.0, x) - 0.5) / r); +} + +void main() { + color = textureSpline1(color_texture, frag_tex_coord); +} diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp index 5d2246ada1..65670fcad8 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.cpp +++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp @@ -89,6 +89,9 @@ void BlitScreen::CreateWindowAdapt() { case Settings::ScalingFilter::Gaussian: window_adapt = MakeGaussian(device); break; + case Settings::ScalingFilter::Spline1: + window_adapt = MakeSpline1(device); + break; case Settings::ScalingFilter::Lanczos: window_adapt = MakeLanczos(device); break; diff --git a/src/video_core/renderer_opengl/present/filters.cpp b/src/video_core/renderer_opengl/present/filters.cpp index 3424a52d80..a840de304e 100644 --- a/src/video_core/renderer_opengl/present/filters.cpp +++ b/src/video_core/renderer_opengl/present/filters.cpp @@ -28,6 +28,11 @@ std::unique_ptr MakeBilinear(const Device& device) { HostShaders::OPENGL_PRESENT_FRAG); } +std::unique_ptr MakeSpline1(const Device& device) { + return std::make_unique(device, CreateBilinearSampler(), + HostShaders::PRESENT_SPLINE1_FRAG); +} + std::unique_ptr MakeBicubic(const Device& device) { return std::make_unique(device, CreateBilinearSampler(), HostShaders::PRESENT_BICUBIC_FRAG); diff --git a/src/video_core/renderer_opengl/present/filters.h b/src/video_core/renderer_opengl/present/filters.h index f71b5f93d3..7b38ac56bc 100644 --- a/src/video_core/renderer_opengl/present/filters.h +++ b/src/video_core/renderer_opengl/present/filters.h @@ -18,6 +18,7 @@ std::unique_ptr MakeNearestNeighbor(const Device& device); std::unique_ptr MakeBilinear(const Device& device); std::unique_ptr MakeBicubic(const Device& device); std::unique_ptr MakeGaussian(const Device& device); +std::unique_ptr MakeSpline1(const Device& device); std::unique_ptr MakeLanczos(const Device& device); std::unique_ptr MakeScaleForce(const Device& device); std::unique_ptr MakeArea(const Device& device); diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp index 8fed222504..6622b8daea 100644 --- a/src/video_core/renderer_vulkan/present/filters.cpp +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -46,6 +46,11 @@ std::unique_ptr MakeBilinear(const Device& device, VkFormat fra BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); } +std::unique_ptr MakeSpline1(const Device& device, VkFormat frame_format) { + return std::make_unique(device, frame_format, CreateBilinearSampler(device), + BuildShader(device, PRESENT_SPLINE1_FRAG_SPV)); +} + std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format) { // No need for handrolled shader -- if the VK impl can do it for us ;) if (device.IsExtFilterCubicSupported()) diff --git a/src/video_core/renderer_vulkan/present/filters.h b/src/video_core/renderer_vulkan/present/filters.h index 8b0630e748..015bffc8a5 100644 --- a/src/video_core/renderer_vulkan/present/filters.h +++ b/src/video_core/renderer_vulkan/present/filters.h @@ -18,6 +18,7 @@ class MemoryAllocator; std::unique_ptr MakeNearestNeighbor(const Device& device, VkFormat frame_format); std::unique_ptr MakeBilinear(const Device& device, VkFormat frame_format); std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format); +std::unique_ptr MakeSpline1(const Device& device, VkFormat frame_format); std::unique_ptr MakeGaussian(const Device& device, VkFormat frame_format); std::unique_ptr MakeLanczos(const Device& device, VkFormat frame_format); std::unique_ptr MakeScaleForce(const Device& device, VkFormat frame_format); diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 3a003a871e..b720bcded3 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -43,6 +43,9 @@ void BlitScreen::SetWindowAdaptPass() { case Settings::ScalingFilter::Bicubic: window_adapt = MakeBicubic(device, swapchain_view_format); break; + case Settings::ScalingFilter::Spline1: + window_adapt = MakeSpline1(device, swapchain_view_format); + break; case Settings::ScalingFilter::Gaussian: window_adapt = MakeGaussian(device, swapchain_view_format); break; From 06dabbadcbdb7291d9e238da46b81eb0b7fc1e83 Mon Sep 17 00:00:00 2001 From: crueter Date: Sat, 20 Sep 2025 22:58:33 +0200 Subject: [PATCH 07/13] [cmake] fix OpenGL, git rev identifier (#2530) Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2530 Reviewed-by: MaranBr --- CMakeLists.txt | 11 ++++--- CMakeModules/CPMUtil.cmake | 5 ---- CMakeModules/GenerateSCMRev.cmake | 35 ++++++++++------------ CMakeModules/GetSCMRev.cmake | 49 +++++++++++++++++++++++++++++++ src/common/scm_rev.cpp.in | 6 ++-- src/common/scm_rev.h | 4 +-- src/dynarmic/CMakeLists.txt | 2 +- src/qt_common/CMakeLists.txt | 5 +++- src/yuzu/CMakeLists.txt | 4 --- src/yuzu/about_dialog.cpp | 16 +++++----- src/yuzu/main.cpp | 13 +++----- 11 files changed, 93 insertions(+), 57 deletions(-) create mode 100644 CMakeModules/GetSCMRev.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 673aab9e6e..6d8739fb00 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,9 +187,7 @@ option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android" option(FORCE_DOWNLOAD_WIN_BUNDLES "Forcefully download bundled Windows dependencies (useful for CI)" OFF) # TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system -if (YUZU_USE_CPM AND ENABLE_SDL2) - option(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}") -endif() +CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}" "ENABLE_SDL2" OFF) CMAKE_DEPENDENT_OPTION(YUZU_ROOM "Enable dedicated room functionality" ON "NOT ANDROID" OFF) @@ -209,6 +207,8 @@ CMAKE_DEPENDENT_OPTION(USE_SYSTEM_MOLTENVK "Use the system MoltenVK lib (instead set(YUZU_TZDB_PATH "" CACHE STRING "Path to a pre-downloaded timezone database") +option(YUZU_DISABLE_LLVM "Disable LLVM (useful for CI)" OFF) + set(DEFAULT_ENABLE_OPENSSL ON) if (ANDROID OR WIN32 OR APPLE OR PLATFORM_SUN) # - Windows defaults to the Schannel backend. @@ -483,7 +483,10 @@ if (YUZU_USE_CPM) else() # Enforce the search mode of non-required packages for better and shorter failure messages find_package(fmt 8 REQUIRED) - find_package(LLVM MODULE COMPONENTS Demangle) + if (NOT YUZU_DISABLE_LLVM) + find_package(LLVM MODULE COMPONENTS Demangle) + endif() + find_package(nlohmann_json 3.8 REQUIRED) find_package(lz4 REQUIRED) find_package(RenderDoc MODULE) diff --git a/CMakeModules/CPMUtil.cmake b/CMakeModules/CPMUtil.cmake index f76a16c103..6a1384e730 100644 --- a/CMakeModules/CPMUtil.cmake +++ b/CMakeModules/CPMUtil.cmake @@ -598,11 +598,6 @@ function(AddCIPackage) if (DEFINED ARTIFACT_DIR) include(${ARTIFACT_DIR}/${ARTIFACT_CMAKE}.cmake) - - set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_NAMES ${ARTIFACT_NAME}) - set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_URLS "https://github.com/${ARTIFACT_REPO}") # TODO(crueter) other hosts? - set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS ${ARTIFACT_VERSION}) - set(${ARTIFACT_PACKAGE}_ADDED TRUE PARENT_SCOPE) else() find_package(${ARTIFACT_PACKAGE} ${ARTIFACT_MIN_VERSION} REQUIRED) diff --git a/CMakeModules/GenerateSCMRev.cmake b/CMakeModules/GenerateSCMRev.cmake index bcb5dc466a..2d7081b7db 100644 --- a/CMakeModules/GenerateSCMRev.cmake +++ b/CMakeModules/GenerateSCMRev.cmake @@ -1,38 +1,35 @@ +# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + # SPDX-FileCopyrightText: 2019 yuzu Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later -# Gets a UTC timestamp and sets the provided variable to it +# generate git/build information +include(GetSCMRev) + function(get_timestamp _var) string(TIMESTAMP timestamp UTC) set(${_var} "${timestamp}" PARENT_SCOPE) endfunction() -# generate git/build information -include(GetGitRevisionDescription) -if(NOT GIT_REF_SPEC) - get_git_head_revision(GIT_REF_SPEC GIT_REV) -endif() -if(NOT GIT_DESC) - git_describe(GIT_DESC --always --long --dirty) -endif() -if (NOT GIT_BRANCH) - git_branch_name(GIT_BRANCH) -endif() get_timestamp(BUILD_DATE) -git_get_exact_tag(GIT_TAG --tags) -if (GIT_TAG MATCHES "NOTFOUND") - set(BUILD_VERSION "${GIT_DESC}") - set(IS_DEV_BUILD true) -else() - set(BUILD_VERSION ${GIT_TAG}) +if (DEFINED GIT_RELEASE) + set(BUILD_VERSION "${GIT_TAG}") + set(GIT_REFSPEC "${GIT_RELEASE}") set(IS_DEV_BUILD false) +else() + string(SUBSTRING ${GIT_COMMIT} 0 10 BUILD_VERSION) + set(BUILD_VERSION "${BUILD_VERSION}-${GIT_REFSPEC}") + set(IS_DEV_BUILD true) endif() +set(GIT_DESC ${BUILD_VERSION}) + # Generate cpp with Git revision from template # Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well set(REPO_NAME "Eden") -set(BUILD_ID ${GIT_BRANCH}) +set(BUILD_ID ${GIT_REFSPEC}) set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ") set(CXX_COMPILER "${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}") diff --git a/CMakeModules/GetSCMRev.cmake b/CMakeModules/GetSCMRev.cmake new file mode 100644 index 0000000000..ee5ce6a91c --- /dev/null +++ b/CMakeModules/GetSCMRev.cmake @@ -0,0 +1,49 @@ +# SPDX-FileCopyrightText: 2025 crueter +# SPDX-License-Identifier: GPL-3.0-or-later + +include(GetGitRevisionDescription) + +function(trim var) + string(REGEX REPLACE "\n" "" new "${${var}}") + set(${var} ${new} PARENT_SCOPE) +endfunction() + +set(TAG_FILE ${CMAKE_SOURCE_DIR}/GIT-TAG) +set(REF_FILE ${CMAKE_SOURCE_DIR}/GIT-REFSPEC) +set(COMMIT_FILE ${CMAKE_SOURCE_DIR}/GIT-COMMIT) +set(RELEASE_FILE ${CMAKE_SOURCE_DIR}/GIT-RELEASE) + +if (EXISTS ${REF_FILE} AND EXISTS ${COMMIT_FILE}) + file(READ ${REF_FILE} GIT_REFSPEC) + file(READ ${COMMIT_FILE} GIT_COMMIT) +else() + get_git_head_revision(GIT_REFSPEC GIT_COMMIT) + git_branch_name(GIT_REFSPEC) + if (GIT_REFSPEC MATCHES "NOTFOUND") + set(GIT_REFSPEC 1.0.0) + set(GIT_COMMIT stable) + endif() +endif() + +if (EXISTS ${TAG_FILE}) + file(READ ${TAG_FILE} GIT_TAG) +else() + git_describe(GIT_TAG --tags --abbrev=0) + if (GIT_TAG MATCHES "NOTFOUND") + set(GIT_TAG "${GIT_REFSPEC}") + endif() +endif() + +if (EXISTS ${RELEASE_FILE}) + file(READ ${RELEASE_FILE} GIT_RELEASE) + trim(GIT_RELEASE) + message(STATUS "Git release: ${GIT_RELEASE}") +endif() + +trim(GIT_REFSPEC) +trim(GIT_COMMIT) +trim(GIT_TAG) + +message(STATUS "Git commit: ${GIT_COMMIT}") +message(STATUS "Git tag: ${GIT_TAG}") +message(STATUS "Git refspec: ${GIT_REFSPEC}") diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in index a157d03878..1630ceae83 100644 --- a/src/common/scm_rev.cpp.in +++ b/src/common/scm_rev.cpp.in @@ -6,8 +6,8 @@ #include "common/scm_rev.h" -#define GIT_REV "@GIT_REV@" -#define GIT_BRANCH "@GIT_BRANCH@" +#define GIT_REV "@GIT_COMMIT@" +#define GIT_BRANCH "@GIT_REFSPEC@" #define GIT_DESC "@GIT_DESC@" #define BUILD_NAME "@REPO_NAME@" #define BUILD_DATE "@BUILD_DATE@" @@ -31,7 +31,7 @@ constexpr const char g_build_version[] = BUILD_VERSION; constexpr const char g_build_id[] = BUILD_ID; constexpr const char g_title_bar_format_idle[] = TITLE_BAR_FORMAT_IDLE; constexpr const char g_title_bar_format_running[] = TITLE_BAR_FORMAT_RUNNING; -constexpr const bool g_is_dev_build = IS_DEV_BUILD; constexpr const char g_compiler_id[] = COMPILER_ID; +constexpr const bool g_is_dev_build = IS_DEV_BUILD; } // namespace Common diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h index 84356ad64a..8f48241557 100644 --- a/src/common/scm_rev.h +++ b/src/common/scm_rev.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2014 Citra Emulator Project @@ -19,7 +19,7 @@ extern const char g_build_id[]; extern const char g_title_bar_format_idle[]; extern const char g_title_bar_format_running[]; extern const char g_shader_cache_version[]; -extern const bool g_is_dev_build; extern const char g_compiler_id[]; +extern const bool g_is_dev_build; } // namespace Common diff --git a/src/dynarmic/CMakeLists.txt b/src/dynarmic/CMakeLists.txt index 38457deb50..a9808391ae 100644 --- a/src/dynarmic/CMakeLists.txt +++ b/src/dynarmic/CMakeLists.txt @@ -27,7 +27,7 @@ endif() option(DYNARMIC_FATAL_ERRORS "Errors are fatal" OFF) option(DYNARMIC_IGNORE_ASSERTS "Ignore asserts" OFF) option(DYNARMIC_TESTS_USE_UNICORN "Enable fuzzing tests against unicorn" OFF) -option(DYNARMIC_USE_LLVM "Support disassembly of jitted x86_64 code using LLVM" OFF) +CMAKE_DEPENDENT_OPTION(DYNARMIC_USE_LLVM "Support disassembly of jitted x86_64 code using LLVM" OFF "NOT YUZU_DISABLE_LLVM" OFF) if (PLATFORM_OPENBSD) option(DYNARMIC_USE_PRECOMPILED_HEADERS "Use precompiled headers" OFF) diff --git a/src/qt_common/CMakeLists.txt b/src/qt_common/CMakeLists.txt index 9d292da401..eb36de4cf2 100644 --- a/src/qt_common/CMakeLists.txt +++ b/src/qt_common/CMakeLists.txt @@ -40,7 +40,10 @@ endif() add_subdirectory(externals) target_link_libraries(qt_common PRIVATE core Qt6::Core SimpleIni::SimpleIni QuaZip::QuaZip frozen::frozen) -target_link_libraries(qt_common PRIVATE Qt6::Core) + +if (NOT APPLE AND ENABLE_OPENGL) + target_compile_definitions(qt_common PUBLIC HAS_OPENGL) +endif() if (NOT WIN32) target_include_directories(qt_common PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 8a1d760c50..b16c1d99ce 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -480,10 +480,6 @@ if (MSVC) copy_yuzu_FFmpeg_deps(yuzu) endif() -if (NOT APPLE AND ENABLE_OPENGL) - target_compile_definitions(yuzu PRIVATE HAS_OPENGL) -endif() - if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) target_link_libraries(yuzu PRIVATE dynarmic::dynarmic) endif() diff --git a/src/yuzu/about_dialog.cpp b/src/yuzu/about_dialog.cpp index c8edb90268..b7c0cd58d5 100644 --- a/src/yuzu/about_dialog.cpp +++ b/src/yuzu/about_dialog.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -11,16 +14,11 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent) , ui{std::make_unique()} { - static const std::string description = std::string(Common::g_build_version); - static const std::string build_id = std::string(Common::g_build_id); - static const std::string compiler = std::string(Common::g_compiler_id); + static const std::string description = std::string{Common::g_build_version}; + static const std::string build_id = std::string{Common::g_build_id}; + static const std::string compiler = std::string{Common::g_compiler_id}; - std::string yuzu_build; - if (Common::g_is_dev_build) { - yuzu_build = fmt::format("Eden Nightly | {}-{} | {}", description, build_id, compiler); - } else { - yuzu_build = fmt::format("Eden | {} | {}", description, compiler); - } + static const std::string yuzu_build = fmt::format("Eden | {} | {}", description, compiler); const auto override_build = fmt::format(fmt::runtime( std::string(Common::g_title_bar_format_idle)), diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d7d4e94ab7..19162a4ab7 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -4205,16 +4205,11 @@ void GMainWindow::OnEmulatorUpdateAvailable() { void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_view title_version, std::string_view gpu_vendor) { - static const std::string description = std::string(Common::g_build_version); - static const std::string build_id = std::string(Common::g_build_id); - static const std::string compiler = std::string(Common::g_compiler_id); + static const std::string description = std::string{Common::g_build_version}; + static const std::string build_id = std::string{Common::g_build_id}; + static const std::string compiler = std::string{Common::g_compiler_id}; - std::string yuzu_title; - if (Common::g_is_dev_build) { - yuzu_title = fmt::format("Eden Nightly | {}-{} | {}", description, build_id, compiler); - } else { - yuzu_title = fmt::format("Eden | {} | {}", description, compiler); - } + static const std::string yuzu_title = fmt::format("Eden | {} | {}", description, compiler); const auto override_title = fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id); From f3e8eaabf76ad4d1f07861b0c3f3ae2066e845ab Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 20 Sep 2025 03:17:42 +0000 Subject: [PATCH 08/13] [vk, opengl] add lanczo filtering Signed-off-by: lizzie --- src/common/settings_enums.h | 2 +- src/qt_common/shared_translation.cpp | 1 + src/qt_common/shared_translation.h | 2 + src/video_core/host_shaders/CMakeLists.txt | 5 ++- .../host_shaders/present_lanczo.frag | 43 +++++++++++++++++++ .../renderer_opengl/gl_blit_screen.cpp | 3 ++ .../renderer_opengl/present/filters.cpp | 5 +++ .../renderer_opengl/present/filters.h | 1 + .../renderer_vulkan/present/filters.cpp | 6 +++ .../renderer_vulkan/present/filters.h | 1 + .../renderer_vulkan/vk_blit_screen.cpp | 3 ++ 11 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 src/video_core/host_shaders/present_lanczo.frag diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 41133a7819..8d93c61ec1 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -166,7 +166,7 @@ ENUM(ResolutionSetup, Res7X, Res8X); -ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, ScaleForce, Fsr, Area, MaxEnum); +ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczo, ScaleForce, Fsr, Area, MaxEnum); ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); diff --git a/src/qt_common/shared_translation.cpp b/src/qt_common/shared_translation.cpp index eb413f28e9..c549b6b67f 100644 --- a/src/qt_common/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -573,6 +573,7 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) PAIR(ScalingFilter, Bilinear, tr("Bilinear")), PAIR(ScalingFilter, Bicubic, tr("Bicubic")), PAIR(ScalingFilter, Gaussian, tr("Gaussian")), + PAIR(ScalingFilter, Lanczo, tr("Lanczo")), PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")), PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™️ Super Resolution")), PAIR(ScalingFilter, Area, tr("Area")), diff --git a/src/qt_common/shared_translation.h b/src/qt_common/shared_translation.h index 48a2cb5205..a894da290a 100644 --- a/src/qt_common/shared_translation.h +++ b/src/qt_common/shared_translation.h @@ -40,6 +40,8 @@ static const std::map scaling_filter_texts_map {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))}, {Settings::ScalingFilter::Gaussian, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))}, + {Settings::ScalingFilter::Lanczo, + QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Lanczo"))}, {Settings::ScalingFilter::ScaleForce, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))}, {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 688e10d2e4..e7dac21f98 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -1,5 +1,5 @@ -# SPDX-FileCopyrightText: 2018 yuzu Emulator Project -# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later set(FIDELITYFX_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/externals/FidelityFX-FSR/ffx-fsr) @@ -45,6 +45,7 @@ set(SHADER_FILES present_area.frag present_bicubic.frag present_gaussian.frag + present_lanczo.frag queries_prefix_scan_sum.comp queries_prefix_scan_sum_nosubgroups.comp resolve_conditional_render.comp diff --git a/src/video_core/host_shaders/present_lanczo.frag b/src/video_core/host_shaders/present_lanczo.frag new file mode 100644 index 0000000000..5afc985bc3 --- /dev/null +++ b/src/video_core/host_shaders/present_lanczo.frag @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// https://en.wikipedia.org/wiki/Lanczos_resampling + +#version 460 core + +layout (location = 0) in vec2 frag_tex_coord; +layout (location = 0) out vec4 color; +layout (binding = 0) uniform sampler2D color_texture; + +#define PI 3.1415926535897932384626433 + +float sinc(float x) { + return x == 0.0f ? 1.0f : sin(PI * x) / (PI * x); +} + +float lanczos(vec2 v, float a) { + float d = sqrt(v.x * v.x + v.y * v.y); + return sinc(d) / sinc(d / a); +} + +vec4 textureLanczos(sampler2D textureSampler, vec2 p) { + const int r = 1; //radius (1 = 3 steps) + vec3 c_sum = vec3(0.0f); + float w_sum = 0.0f; + vec2 res = vec2(textureSize(textureSampler, 0)); + vec2 cc = floor(p * res) / res; + // kernel size = (r * 2 + 1) * (r * 2 + 1) + for (int x = -r; x <= r; x++) + for (int y = -r; y <= r; y++) { + vec2 kp = 0.5f * (vec2(x, y) / res); // 0.5 = half-pixel level resampling + vec2 uv = cc + kp; + float w = lanczos(kp, float(r)); + c_sum += w * texture(textureSampler, p + kp).rgb; + w_sum += w; + } + return vec4(c_sum / w_sum, 1.0f); +} + +void main() { + color = textureLanczos(color_texture, frag_tex_coord); +} diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp index 2071fe8d15..9fff39143e 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.cpp +++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp @@ -89,6 +89,9 @@ void BlitScreen::CreateWindowAdapt() { case Settings::ScalingFilter::Gaussian: window_adapt = MakeGaussian(device); break; + case Settings::ScalingFilter::Lanczo: + window_adapt = MakeLanczo(device); + break; case Settings::ScalingFilter::ScaleForce: window_adapt = MakeScaleForce(device); break; diff --git a/src/video_core/renderer_opengl/present/filters.cpp b/src/video_core/renderer_opengl/present/filters.cpp index c5ac8e7823..a9b3cdd0d9 100644 --- a/src/video_core/renderer_opengl/present/filters.cpp +++ b/src/video_core/renderer_opengl/present/filters.cpp @@ -37,6 +37,11 @@ std::unique_ptr MakeGaussian(const Device& device) { HostShaders::PRESENT_GAUSSIAN_FRAG); } +std::unique_ptr MakeLanczo(const Device& device) { + return std::make_unique(device, CreateBilinearSampler(), + HostShaders::PRESENT_LANCZO_FRAG); +} + std::unique_ptr MakeScaleForce(const Device& device) { return std::make_unique( device, CreateBilinearSampler(), diff --git a/src/video_core/renderer_opengl/present/filters.h b/src/video_core/renderer_opengl/present/filters.h index be2ce24842..c098d0da2e 100644 --- a/src/video_core/renderer_opengl/present/filters.h +++ b/src/video_core/renderer_opengl/present/filters.h @@ -18,6 +18,7 @@ std::unique_ptr MakeNearestNeighbor(const Device& device); std::unique_ptr MakeBilinear(const Device& device); std::unique_ptr MakeBicubic(const Device& device); std::unique_ptr MakeGaussian(const Device& device); +std::unique_ptr MakeLanczo(const Device& device); std::unique_ptr MakeScaleForce(const Device& device); std::unique_ptr MakeArea(const Device& device); diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp index 7843f38d2c..a3a6bfc2f6 100644 --- a/src/video_core/renderer_vulkan/present/filters.cpp +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -12,6 +12,7 @@ #include "video_core/host_shaders/present_area_frag_spv.h" #include "video_core/host_shaders/present_bicubic_frag_spv.h" #include "video_core/host_shaders/present_gaussian_frag_spv.h" +#include "video_core/host_shaders/present_lanczso_frag_spv.h" #include "video_core/host_shaders/vulkan_present_frag_spv.h" #include "video_core/host_shaders/vulkan_present_scaleforce_fp16_frag_spv.h" #include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h" @@ -59,6 +60,11 @@ std::unique_ptr MakeGaussian(const Device& device, VkFormat fra BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV)); } +std::unique_ptr MakeLanczo(const Device& device, VkFormat frame_format) { + return std::make_unique(device, frame_format, CreateBilinearSampler(device), + BuildShader(device, PRESENT_LANCZO_FRAG_SPV)); +} + std::unique_ptr MakeScaleForce(const Device& device, VkFormat frame_format) { return std::make_unique(device, frame_format, CreateBilinearSampler(device), SelectScaleForceShader(device)); diff --git a/src/video_core/renderer_vulkan/present/filters.h b/src/video_core/renderer_vulkan/present/filters.h index c8259487f8..c51938db24 100644 --- a/src/video_core/renderer_vulkan/present/filters.h +++ b/src/video_core/renderer_vulkan/present/filters.h @@ -19,6 +19,7 @@ std::unique_ptr MakeNearestNeighbor(const Device& device, VkFor std::unique_ptr MakeBilinear(const Device& device, VkFormat frame_format); std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format); std::unique_ptr MakeGaussian(const Device& device, VkFormat frame_format); +std::unique_ptr MakeLanczo(const Device& device, VkFormat frame_format); std::unique_ptr MakeScaleForce(const Device& device, VkFormat frame_format); std::unique_ptr MakeArea(const Device& device, VkFormat frame_format); diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 39f07b966d..b398062dae 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -46,6 +46,9 @@ void BlitScreen::SetWindowAdaptPass() { case Settings::ScalingFilter::Gaussian: window_adapt = MakeGaussian(device, swapchain_view_format); break; + case Settings::ScalingFilter::Lanczo: + window_adapt = MakeLanczo(device, swapchain_view_format); + break; case Settings::ScalingFilter::ScaleForce: window_adapt = MakeScaleForce(device, swapchain_view_format); break; From b0b6ecc6cb3dc99a4d5185aa46dc677e4efb3e63 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 20 Sep 2025 03:26:07 +0000 Subject: [PATCH 09/13] fix mispell Signed-off-by: lizzie --- src/common/settings_enums.h | 2 +- src/qt_common/shared_translation.cpp | 2 +- src/qt_common/shared_translation.h | 4 ++-- src/video_core/host_shaders/CMakeLists.txt | 2 +- .../{present_lanczo.frag => present_lanczos.frag} | 0 src/video_core/renderer_opengl/gl_blit_screen.cpp | 4 ++-- src/video_core/renderer_opengl/present/filters.cpp | 3 ++- src/video_core/renderer_opengl/present/filters.h | 2 +- src/video_core/renderer_vulkan/present/filters.cpp | 4 ++-- src/video_core/renderer_vulkan/present/filters.h | 2 +- src/video_core/renderer_vulkan/vk_blit_screen.cpp | 4 ++-- 11 files changed, 15 insertions(+), 14 deletions(-) rename src/video_core/host_shaders/{present_lanczo.frag => present_lanczos.frag} (100%) diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 8d93c61ec1..c768c23cda 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -166,7 +166,7 @@ ENUM(ResolutionSetup, Res7X, Res8X); -ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczo, ScaleForce, Fsr, Area, MaxEnum); +ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, ScaleForce, Fsr, Area, MaxEnum); ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); diff --git a/src/qt_common/shared_translation.cpp b/src/qt_common/shared_translation.cpp index c549b6b67f..91c16f3102 100644 --- a/src/qt_common/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -573,7 +573,7 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) PAIR(ScalingFilter, Bilinear, tr("Bilinear")), PAIR(ScalingFilter, Bicubic, tr("Bicubic")), PAIR(ScalingFilter, Gaussian, tr("Gaussian")), - PAIR(ScalingFilter, Lanczo, tr("Lanczo")), + PAIR(ScalingFilter, Lanczos, tr("Lanczos")), PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")), PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™️ Super Resolution")), PAIR(ScalingFilter, Area, tr("Area")), diff --git a/src/qt_common/shared_translation.h b/src/qt_common/shared_translation.h index a894da290a..ea8e7fe1bd 100644 --- a/src/qt_common/shared_translation.h +++ b/src/qt_common/shared_translation.h @@ -40,8 +40,8 @@ static const std::map scaling_filter_texts_map {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))}, {Settings::ScalingFilter::Gaussian, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))}, - {Settings::ScalingFilter::Lanczo, - QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Lanczo"))}, + {Settings::ScalingFilter::Lanczos, + QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Lanczos"))}, {Settings::ScalingFilter::ScaleForce, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))}, {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index e7dac21f98..d8ea826498 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -45,7 +45,7 @@ set(SHADER_FILES present_area.frag present_bicubic.frag present_gaussian.frag - present_lanczo.frag + present_lanczos.frag queries_prefix_scan_sum.comp queries_prefix_scan_sum_nosubgroups.comp resolve_conditional_render.comp diff --git a/src/video_core/host_shaders/present_lanczo.frag b/src/video_core/host_shaders/present_lanczos.frag similarity index 100% rename from src/video_core/host_shaders/present_lanczo.frag rename to src/video_core/host_shaders/present_lanczos.frag diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp index 9fff39143e..5d2246ada1 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.cpp +++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp @@ -89,8 +89,8 @@ void BlitScreen::CreateWindowAdapt() { case Settings::ScalingFilter::Gaussian: window_adapt = MakeGaussian(device); break; - case Settings::ScalingFilter::Lanczo: - window_adapt = MakeLanczo(device); + case Settings::ScalingFilter::Lanczos: + window_adapt = MakeLanczos(device); break; case Settings::ScalingFilter::ScaleForce: window_adapt = MakeScaleForce(device); diff --git a/src/video_core/renderer_opengl/present/filters.cpp b/src/video_core/renderer_opengl/present/filters.cpp index a9b3cdd0d9..8464123be0 100644 --- a/src/video_core/renderer_opengl/present/filters.cpp +++ b/src/video_core/renderer_opengl/present/filters.cpp @@ -12,6 +12,7 @@ #include "video_core/host_shaders/present_area_frag.h" #include "video_core/host_shaders/present_bicubic_frag.h" #include "video_core/host_shaders/present_gaussian_frag.h" +#include "video_core/host_shaders/present_lanczos_frag.h" #include "video_core/renderer_opengl/present/filters.h" #include "video_core/renderer_opengl/present/util.h" @@ -37,7 +38,7 @@ std::unique_ptr MakeGaussian(const Device& device) { HostShaders::PRESENT_GAUSSIAN_FRAG); } -std::unique_ptr MakeLanczo(const Device& device) { +std::unique_ptr MakeLanczos(const Device& device) { return std::make_unique(device, CreateBilinearSampler(), HostShaders::PRESENT_LANCZO_FRAG); } diff --git a/src/video_core/renderer_opengl/present/filters.h b/src/video_core/renderer_opengl/present/filters.h index c098d0da2e..f71b5f93d3 100644 --- a/src/video_core/renderer_opengl/present/filters.h +++ b/src/video_core/renderer_opengl/present/filters.h @@ -18,7 +18,7 @@ std::unique_ptr MakeNearestNeighbor(const Device& device); std::unique_ptr MakeBilinear(const Device& device); std::unique_ptr MakeBicubic(const Device& device); std::unique_ptr MakeGaussian(const Device& device); -std::unique_ptr MakeLanczo(const Device& device); +std::unique_ptr MakeLanczos(const Device& device); std::unique_ptr MakeScaleForce(const Device& device); std::unique_ptr MakeArea(const Device& device); diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp index a3a6bfc2f6..5ab3ac3114 100644 --- a/src/video_core/renderer_vulkan/present/filters.cpp +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -12,7 +12,7 @@ #include "video_core/host_shaders/present_area_frag_spv.h" #include "video_core/host_shaders/present_bicubic_frag_spv.h" #include "video_core/host_shaders/present_gaussian_frag_spv.h" -#include "video_core/host_shaders/present_lanczso_frag_spv.h" +#include "video_core/host_shaders/present_lanczos_frag_spv.h" #include "video_core/host_shaders/vulkan_present_frag_spv.h" #include "video_core/host_shaders/vulkan_present_scaleforce_fp16_frag_spv.h" #include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h" @@ -60,7 +60,7 @@ std::unique_ptr MakeGaussian(const Device& device, VkFormat fra BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV)); } -std::unique_ptr MakeLanczo(const Device& device, VkFormat frame_format) { +std::unique_ptr MakeLanczos(const Device& device, VkFormat frame_format) { return std::make_unique(device, frame_format, CreateBilinearSampler(device), BuildShader(device, PRESENT_LANCZO_FRAG_SPV)); } diff --git a/src/video_core/renderer_vulkan/present/filters.h b/src/video_core/renderer_vulkan/present/filters.h index c51938db24..8b0630e748 100644 --- a/src/video_core/renderer_vulkan/present/filters.h +++ b/src/video_core/renderer_vulkan/present/filters.h @@ -19,7 +19,7 @@ std::unique_ptr MakeNearestNeighbor(const Device& device, VkFor std::unique_ptr MakeBilinear(const Device& device, VkFormat frame_format); std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format); std::unique_ptr MakeGaussian(const Device& device, VkFormat frame_format); -std::unique_ptr MakeLanczo(const Device& device, VkFormat frame_format); +std::unique_ptr MakeLanczos(const Device& device, VkFormat frame_format); std::unique_ptr MakeScaleForce(const Device& device, VkFormat frame_format); std::unique_ptr MakeArea(const Device& device, VkFormat frame_format); diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index b398062dae..3a003a871e 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -46,8 +46,8 @@ void BlitScreen::SetWindowAdaptPass() { case Settings::ScalingFilter::Gaussian: window_adapt = MakeGaussian(device, swapchain_view_format); break; - case Settings::ScalingFilter::Lanczo: - window_adapt = MakeLanczo(device, swapchain_view_format); + case Settings::ScalingFilter::Lanczos: + window_adapt = MakeLanczos(device, swapchain_view_format); break; case Settings::ScalingFilter::ScaleForce: window_adapt = MakeScaleForce(device, swapchain_view_format); From eecc47b96822634724db8d8174b2681683cb5f1e Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 20 Sep 2025 03:39:37 +0000 Subject: [PATCH 10/13] fix Signed-off-by: lizzie --- src/video_core/renderer_opengl/present/filters.cpp | 2 +- src/video_core/renderer_vulkan/present/filters.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_opengl/present/filters.cpp b/src/video_core/renderer_opengl/present/filters.cpp index 8464123be0..3424a52d80 100644 --- a/src/video_core/renderer_opengl/present/filters.cpp +++ b/src/video_core/renderer_opengl/present/filters.cpp @@ -40,7 +40,7 @@ std::unique_ptr MakeGaussian(const Device& device) { std::unique_ptr MakeLanczos(const Device& device) { return std::make_unique(device, CreateBilinearSampler(), - HostShaders::PRESENT_LANCZO_FRAG); + HostShaders::PRESENT_LANCZOS_FRAG); } std::unique_ptr MakeScaleForce(const Device& device) { diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp index 5ab3ac3114..8fed222504 100644 --- a/src/video_core/renderer_vulkan/present/filters.cpp +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -62,7 +62,7 @@ std::unique_ptr MakeGaussian(const Device& device, VkFormat fra std::unique_ptr MakeLanczos(const Device& device, VkFormat frame_format) { return std::make_unique(device, frame_format, CreateBilinearSampler(device), - BuildShader(device, PRESENT_LANCZO_FRAG_SPV)); + BuildShader(device, PRESENT_LANCZOS_FRAG_SPV)); } std::unique_ptr MakeScaleForce(const Device& device, VkFormat frame_format) { From 82b61bdd394aa24c0e04e250561f4479d2ac8cb7 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 20 Sep 2025 13:00:46 +0000 Subject: [PATCH 11/13] optimize with precomputed kernel Signed-off-by: lizzie --- .../host_shaders/present_lanczos.frag | 35 +++++--------- tools/lanczos_gen.c | 48 +++++++++++++++++++ 2 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 tools/lanczos_gen.c diff --git a/src/video_core/host_shaders/present_lanczos.frag b/src/video_core/host_shaders/present_lanczos.frag index 5afc985bc3..9501b7ca33 100644 --- a/src/video_core/host_shaders/present_lanczos.frag +++ b/src/video_core/host_shaders/present_lanczos.frag @@ -9,32 +9,23 @@ layout (location = 0) in vec2 frag_tex_coord; layout (location = 0) out vec4 color; layout (binding = 0) uniform sampler2D color_texture; -#define PI 3.1415926535897932384626433 - -float sinc(float x) { - return x == 0.0f ? 1.0f : sin(PI * x) / (PI * x); -} - -float lanczos(vec2 v, float a) { - float d = sqrt(v.x * v.x + v.y * v.y); - return sinc(d) / sinc(d / a); -} - +// precomputed kernel +const float w_kernel[49] = float[] ( + -0.238811f, 0.531959f, 0.961865f, 1.000000f, 0.961865f, 0.531959f, -0.238811f, 0.531959f, 0.957419f, 0.313883f, -0.000000f, 0.313883f, 0.957419f, 0.531959f, 0.961865f, 0.313883f, -0.322602f, 0.000000f, -0.322602f, 0.313883f, 0.961865f, 1.000000f, -0.000000f, 0.000000f, 1.000000f, 0.000000f, -0.000000f, 1.000000f, 0.961865f, 0.313883f, -0.322602f, 0.000000f, -0.322602f, 0.313883f, 0.961865f, 0.531959f, 0.957419f, 0.313883f, -0.000000f, 0.313883f, 0.957419f, 0.531959f, -0.238811f, 0.531959f, 0.961865f, 1.000000f, 0.961865f, 0.531959f, -0.238811f +); +const vec2 w_pos[49] = vec2[] ( + vec2(-0.750000f, -0.750000f), vec2(-0.750000f, -0.500000f), vec2(-0.750000f, -0.250000f), vec2(-0.750000f, 0.000000f), vec2(-0.750000f, 0.250000f), vec2(-0.750000f, 0.500000f), vec2(-0.750000f, 0.750000f), vec2(-0.500000f, -0.750000f), vec2(-0.500000f, -0.500000f), vec2(-0.500000f, -0.250000f), vec2(-0.500000f, 0.000000f), vec2(-0.500000f, 0.250000f), vec2(-0.500000f, 0.500000f), vec2(-0.500000f, 0.750000f), vec2(-0.250000f, -0.750000f), vec2(-0.250000f, -0.500000f), vec2(-0.250000f, -0.250000f), vec2(-0.250000f, 0.000000f), vec2(-0.250000f, 0.250000f), vec2(-0.250000f, 0.500000f), vec2(-0.250000f, 0.750000f), vec2(0.000000f, -0.750000f), vec2(0.000000f, -0.500000f), vec2(0.000000f, -0.250000f), vec2(0.000000f, 0.000000f), vec2(0.000000f, 0.250000f), vec2(0.000000f, 0.500000f), vec2(0.000000f, 0.750000f), vec2(0.250000f, -0.750000f), vec2(0.250000f, -0.500000f), vec2(0.250000f, -0.250000f), vec2(0.250000f, 0.000000f), vec2(0.250000f, 0.250000f), vec2(0.250000f, 0.500000f), vec2(0.250000f, 0.750000f), vec2(0.500000f, -0.750000f), vec2(0.500000f, -0.500000f), vec2(0.500000f, -0.250000f), vec2(0.500000f, 0.000000f), vec2(0.500000f, 0.250000f), vec2(0.500000f, 0.500000f), vec2(0.500000f, 0.750000f), vec2(0.750000f, -0.750000f), vec2(0.750000f, -0.500000f), vec2(0.750000f, -0.250000f), vec2(0.750000f, 0.000000f), vec2(0.750000f, 0.250000f), vec2(0.750000f, 0.500000f), vec2(0.750000f, 0.750000f) +); +const float w_sum = 21.045683f; vec4 textureLanczos(sampler2D textureSampler, vec2 p) { - const int r = 1; //radius (1 = 3 steps) vec3 c_sum = vec3(0.0f); - float w_sum = 0.0f; vec2 res = vec2(textureSize(textureSampler, 0)); vec2 cc = floor(p * res) / res; - // kernel size = (r * 2 + 1) * (r * 2 + 1) - for (int x = -r; x <= r; x++) - for (int y = -r; y <= r; y++) { - vec2 kp = 0.5f * (vec2(x, y) / res); // 0.5 = half-pixel level resampling - vec2 uv = cc + kp; - float w = lanczos(kp, float(r)); - c_sum += w * texture(textureSampler, p + kp).rgb; - w_sum += w; - } + for (int i = 0; i < 49; i++) { // kernel size = (r * 2 + 1) ^ 2 + vec2 kp = w_pos[i] / res; + vec2 uv = cc + kp; + c_sum += w_kernel[i] * texture(textureSampler, p + kp).rgb; + } return vec4(c_sum / w_sum, 1.0f); } diff --git a/tools/lanczos_gen.c b/tools/lanczos_gen.c new file mode 100644 index 0000000000..6d7be3cb0e --- /dev/null +++ b/tools/lanczos_gen.c @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// clang -lm tools/lanczos_gen.c -o tools/lanczos_gen && ./tools/lanczos_gen +#include +#include + +double sinc(double x) { + return x == 0.0f ? 1.0f : sin(M_PI * x) / (M_PI * x); +} + +typedef struct vec2 { + double x; + double y; +} vec2; + +double lanczos(vec2 v, float a) { + double d = sqrt(v.x * v.x + v.y * v.y); + return sinc(d) / sinc(d / a); +} + +int main(int argc, char* argv[]) { + const int r = 3; //radius (1 = 3 steps) + const int k_size = (r * 2 + 1) * (r * 2 + 1); + double w_sum = 0.0f; + // kernel size = (r * 2 + 1) ^ 2 + printf("const float w_kernel[%i] = float[] (\n ", k_size); + double factor = 1.0f / ((double)r + 1.0f); + for (int x = -r; x <= r; x++) + for (int y = -r; y <= r; y++) { + double w = lanczos((vec2){ .x = x, .y = y }, (double)r); + printf("%lff, ", w); + w_sum += w; + } + printf("\n);\n"); + printf("const vec2 w_pos[%i] = vec2[] (\n ", k_size); + for (int x = -r; x <= r; x++) + for (int y = -r; y <= r; y++) { + vec2 kp = (vec2){ + .x = x * factor, + .y = y * factor + }; + printf("vec2(%lff, %lff), ", kp.x, kp.y); + } + printf("\n);\n"); + printf("const float w_sum = %lff;\n", w_sum); + return 0; +} From e1193bf89d75bdbde144722362fdbd5fdabf1fa4 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 20 Sep 2025 14:26:53 +0000 Subject: [PATCH 12/13] actually memory is bad Signed-off-by: lizzie --- .../host_shaders/present_lanczos.frag | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/video_core/host_shaders/present_lanczos.frag b/src/video_core/host_shaders/present_lanczos.frag index 9501b7ca33..b69b329c1b 100644 --- a/src/video_core/host_shaders/present_lanczos.frag +++ b/src/video_core/host_shaders/present_lanczos.frag @@ -9,23 +9,29 @@ layout (location = 0) in vec2 frag_tex_coord; layout (location = 0) out vec4 color; layout (binding = 0) uniform sampler2D color_texture; -// precomputed kernel -const float w_kernel[49] = float[] ( - -0.238811f, 0.531959f, 0.961865f, 1.000000f, 0.961865f, 0.531959f, -0.238811f, 0.531959f, 0.957419f, 0.313883f, -0.000000f, 0.313883f, 0.957419f, 0.531959f, 0.961865f, 0.313883f, -0.322602f, 0.000000f, -0.322602f, 0.313883f, 0.961865f, 1.000000f, -0.000000f, 0.000000f, 1.000000f, 0.000000f, -0.000000f, 1.000000f, 0.961865f, 0.313883f, -0.322602f, 0.000000f, -0.322602f, 0.313883f, 0.961865f, 0.531959f, 0.957419f, 0.313883f, -0.000000f, 0.313883f, 0.957419f, 0.531959f, -0.238811f, 0.531959f, 0.961865f, 1.000000f, 0.961865f, 0.531959f, -0.238811f -); -const vec2 w_pos[49] = vec2[] ( - vec2(-0.750000f, -0.750000f), vec2(-0.750000f, -0.500000f), vec2(-0.750000f, -0.250000f), vec2(-0.750000f, 0.000000f), vec2(-0.750000f, 0.250000f), vec2(-0.750000f, 0.500000f), vec2(-0.750000f, 0.750000f), vec2(-0.500000f, -0.750000f), vec2(-0.500000f, -0.500000f), vec2(-0.500000f, -0.250000f), vec2(-0.500000f, 0.000000f), vec2(-0.500000f, 0.250000f), vec2(-0.500000f, 0.500000f), vec2(-0.500000f, 0.750000f), vec2(-0.250000f, -0.750000f), vec2(-0.250000f, -0.500000f), vec2(-0.250000f, -0.250000f), vec2(-0.250000f, 0.000000f), vec2(-0.250000f, 0.250000f), vec2(-0.250000f, 0.500000f), vec2(-0.250000f, 0.750000f), vec2(0.000000f, -0.750000f), vec2(0.000000f, -0.500000f), vec2(0.000000f, -0.250000f), vec2(0.000000f, 0.000000f), vec2(0.000000f, 0.250000f), vec2(0.000000f, 0.500000f), vec2(0.000000f, 0.750000f), vec2(0.250000f, -0.750000f), vec2(0.250000f, -0.500000f), vec2(0.250000f, -0.250000f), vec2(0.250000f, 0.000000f), vec2(0.250000f, 0.250000f), vec2(0.250000f, 0.500000f), vec2(0.250000f, 0.750000f), vec2(0.500000f, -0.750000f), vec2(0.500000f, -0.500000f), vec2(0.500000f, -0.250000f), vec2(0.500000f, 0.000000f), vec2(0.500000f, 0.250000f), vec2(0.500000f, 0.500000f), vec2(0.500000f, 0.750000f), vec2(0.750000f, -0.750000f), vec2(0.750000f, -0.500000f), vec2(0.750000f, -0.250000f), vec2(0.750000f, 0.000000f), vec2(0.750000f, 0.250000f), vec2(0.750000f, 0.500000f), vec2(0.750000f, 0.750000f) -); -const float w_sum = 21.045683f; +#define PI 3.1415926535897932384626433 +float sinc(float x) { + return x == 0.0f ? 1.0f : sin(PI * x) / (PI * x); +} +float lanczos(vec2 v, float a) { + float d = length(v); + return sinc(d) / sinc(d / a); +} vec4 textureLanczos(sampler2D textureSampler, vec2 p) { vec3 c_sum = vec3(0.0f); + float w_sum = 0.0f; vec2 res = vec2(textureSize(textureSampler, 0)); vec2 cc = floor(p * res) / res; - for (int i = 0; i < 49; i++) { // kernel size = (r * 2 + 1) ^ 2 - vec2 kp = w_pos[i] / res; - vec2 uv = cc + kp; - c_sum += w_kernel[i] * texture(textureSampler, p + kp).rgb; - } + // kernel size = (2r + 1)^2 + const int r = 3; //radius (1 = 3 steps) + for (int x = -r; x <= r; x++) + for (int y = -r; y <= r; y++) { + vec2 kp = 0.5f * (vec2(x, y) / res); // 0.5 = half-pixel level resampling + vec2 uv = cc + kp; + float w = lanczos(kp, float(r)); + c_sum += w * texture(textureSampler, p + kp).rgb; + w_sum += w; + } return vec4(c_sum / w_sum, 1.0f); } From 354f0ea2f0e5274b65905b21424a56368e245f3f Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 20 Sep 2025 20:12:34 +0000 Subject: [PATCH 13/13] add spline-1 filter Signed-off-by: lizzie --- src/common/settings_enums.h | 2 +- src/qt_common/shared_translation.cpp | 1 + src/qt_common/shared_translation.h | 2 ++ src/video_core/host_shaders/CMakeLists.txt | 1 + .../host_shaders/present_spline1.frag | 24 +++++++++++++++++++ .../renderer_opengl/gl_blit_screen.cpp | 3 +++ .../renderer_opengl/present/filters.cpp | 5 ++++ .../renderer_opengl/present/filters.h | 1 + .../renderer_vulkan/present/filters.cpp | 5 ++++ .../renderer_vulkan/present/filters.h | 1 + .../renderer_vulkan/vk_blit_screen.cpp | 3 +++ 11 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/video_core/host_shaders/present_spline1.frag diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index c768c23cda..ebfa4ceb9e 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -166,7 +166,7 @@ ENUM(ResolutionSetup, Res7X, Res8X); -ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, ScaleForce, Fsr, Area, MaxEnum); +ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, MaxEnum); ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); diff --git a/src/qt_common/shared_translation.cpp b/src/qt_common/shared_translation.cpp index 91c16f3102..4254253c2f 100644 --- a/src/qt_common/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -572,6 +572,7 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")), PAIR(ScalingFilter, Bilinear, tr("Bilinear")), PAIR(ScalingFilter, Bicubic, tr("Bicubic")), + PAIR(ScalingFilter, Spline1, tr("Spline-1")), PAIR(ScalingFilter, Gaussian, tr("Gaussian")), PAIR(ScalingFilter, Lanczos, tr("Lanczos")), PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")), diff --git a/src/qt_common/shared_translation.h b/src/qt_common/shared_translation.h index ea8e7fe1bd..c9216c2daa 100644 --- a/src/qt_common/shared_translation.h +++ b/src/qt_common/shared_translation.h @@ -38,6 +38,8 @@ static const std::map scaling_filter_texts_map {Settings::ScalingFilter::Bilinear, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))}, {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))}, + {Settings::ScalingFilter::Spline1, + QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Spline-1"))}, {Settings::ScalingFilter::Gaussian, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))}, {Settings::ScalingFilter::Lanczos, diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index d8ea826498..c14b44a45a 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -46,6 +46,7 @@ set(SHADER_FILES present_bicubic.frag present_gaussian.frag present_lanczos.frag + present_spline1.frag queries_prefix_scan_sum.comp queries_prefix_scan_sum_nosubgroups.comp resolve_conditional_render.comp diff --git a/src/video_core/host_shaders/present_spline1.frag b/src/video_core/host_shaders/present_spline1.frag new file mode 100644 index 0000000000..871b47586b --- /dev/null +++ b/src/video_core/host_shaders/present_spline1.frag @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// Spline (smooth linear inerpolation) with 1 texel fetch (needs bilinear to work) +// Emulates bicubic without actually doing bicubic +// See https://iquilezles.org/articles/texture, unfortunely there are issues with the original +// where smoothstep "expansion" actually results in worse codegen (SPIRV-Opt does a direct conv to smoothstep) +// TODO: Numerical analysis - fract is sawtooth func and floor, reuse params? Perhaps - no need for precision + +#version 460 core + +layout (location = 0) in vec2 frag_tex_coord; +layout (location = 0) out vec4 color; +layout (binding = 0) uniform sampler2D color_texture; + +vec4 textureSpline1(sampler2D sam, vec2 uv) { + float r = float(textureSize(sam, 0).x); + vec2 x = fract(uv * r + 0.5); + return texture(sam, (floor(uv * r + 0.5) + smoothstep(0.0, 1.0, x) - 0.5) / r); +} + +void main() { + color = textureSpline1(color_texture, frag_tex_coord); +} diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp index 5d2246ada1..65670fcad8 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.cpp +++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp @@ -89,6 +89,9 @@ void BlitScreen::CreateWindowAdapt() { case Settings::ScalingFilter::Gaussian: window_adapt = MakeGaussian(device); break; + case Settings::ScalingFilter::Spline1: + window_adapt = MakeSpline1(device); + break; case Settings::ScalingFilter::Lanczos: window_adapt = MakeLanczos(device); break; diff --git a/src/video_core/renderer_opengl/present/filters.cpp b/src/video_core/renderer_opengl/present/filters.cpp index 3424a52d80..a840de304e 100644 --- a/src/video_core/renderer_opengl/present/filters.cpp +++ b/src/video_core/renderer_opengl/present/filters.cpp @@ -28,6 +28,11 @@ std::unique_ptr MakeBilinear(const Device& device) { HostShaders::OPENGL_PRESENT_FRAG); } +std::unique_ptr MakeSpline1(const Device& device) { + return std::make_unique(device, CreateBilinearSampler(), + HostShaders::PRESENT_SPLINE1_FRAG); +} + std::unique_ptr MakeBicubic(const Device& device) { return std::make_unique(device, CreateBilinearSampler(), HostShaders::PRESENT_BICUBIC_FRAG); diff --git a/src/video_core/renderer_opengl/present/filters.h b/src/video_core/renderer_opengl/present/filters.h index f71b5f93d3..7b38ac56bc 100644 --- a/src/video_core/renderer_opengl/present/filters.h +++ b/src/video_core/renderer_opengl/present/filters.h @@ -18,6 +18,7 @@ std::unique_ptr MakeNearestNeighbor(const Device& device); std::unique_ptr MakeBilinear(const Device& device); std::unique_ptr MakeBicubic(const Device& device); std::unique_ptr MakeGaussian(const Device& device); +std::unique_ptr MakeSpline1(const Device& device); std::unique_ptr MakeLanczos(const Device& device); std::unique_ptr MakeScaleForce(const Device& device); std::unique_ptr MakeArea(const Device& device); diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp index 8fed222504..6622b8daea 100644 --- a/src/video_core/renderer_vulkan/present/filters.cpp +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -46,6 +46,11 @@ std::unique_ptr MakeBilinear(const Device& device, VkFormat fra BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); } +std::unique_ptr MakeSpline1(const Device& device, VkFormat frame_format) { + return std::make_unique(device, frame_format, CreateBilinearSampler(device), + BuildShader(device, PRESENT_SPLINE1_FRAG_SPV)); +} + std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format) { // No need for handrolled shader -- if the VK impl can do it for us ;) if (device.IsExtFilterCubicSupported()) diff --git a/src/video_core/renderer_vulkan/present/filters.h b/src/video_core/renderer_vulkan/present/filters.h index 8b0630e748..015bffc8a5 100644 --- a/src/video_core/renderer_vulkan/present/filters.h +++ b/src/video_core/renderer_vulkan/present/filters.h @@ -18,6 +18,7 @@ class MemoryAllocator; std::unique_ptr MakeNearestNeighbor(const Device& device, VkFormat frame_format); std::unique_ptr MakeBilinear(const Device& device, VkFormat frame_format); std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format); +std::unique_ptr MakeSpline1(const Device& device, VkFormat frame_format); std::unique_ptr MakeGaussian(const Device& device, VkFormat frame_format); std::unique_ptr MakeLanczos(const Device& device, VkFormat frame_format); std::unique_ptr MakeScaleForce(const Device& device, VkFormat frame_format); diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 3a003a871e..b720bcded3 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -43,6 +43,9 @@ void BlitScreen::SetWindowAdaptPass() { case Settings::ScalingFilter::Bicubic: window_adapt = MakeBicubic(device, swapchain_view_format); break; + case Settings::ScalingFilter::Spline1: + window_adapt = MakeSpline1(device, swapchain_view_format); + break; case Settings::ScalingFilter::Gaussian: window_adapt = MakeGaussian(device, swapchain_view_format); break;