From 5b59a35f9821437f398fae54e169e9b508122b3b Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 25 Sep 2025 07:44:47 +0000 Subject: [PATCH 01/19] [vk, ogl] VK_QCOM ZTC, Bspline, Mitchell filter weights Signed-off-by: lizzie --- .../app/src/main/res/values/arrays.xml | 10 +++ .../app/src/main/res/values/strings.xml | 3 + src/common/settings_enums.h | 2 +- src/qt_common/shared_translation.cpp | 3 + src/qt_common/shared_translation.h | 3 + src/video_core/host_shaders/CMakeLists.txt | 3 + .../host_shaders/present_bicubic.frag | 69 +++++++------------ .../host_shaders/present_bspline.frag | 35 ++++++++++ .../host_shaders/present_mitchell.frag | 35 ++++++++++ .../host_shaders/present_zero_tangent.frag | 35 ++++++++++ .../renderer_opengl/gl_blit_screen.cpp | 10 +++ .../renderer_opengl/present/filters.cpp | 18 +++++ .../renderer_opengl/present/filters.h | 3 + .../renderer_vulkan/present/filters.cpp | 30 ++++++-- .../renderer_vulkan/present/filters.h | 2 +- .../renderer_vulkan/present/util.cpp | 13 +++- src/video_core/renderer_vulkan/present/util.h | 2 +- .../renderer_vulkan/vk_blit_screen.cpp | 12 +++- 18 files changed, 230 insertions(+), 58 deletions(-) create mode 100644 src/video_core/host_shaders/present_bspline.frag create mode 100644 src/video_core/host_shaders/present_mitchell.frag create mode 100644 src/video_core/host_shaders/present_zero_tangent.frag diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 08ca53ad81..fa9a02aead 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -251,7 +251,12 @@ @string/scaling_filter_nearest_neighbor @string/scaling_filter_bilinear @string/scaling_filter_bicubic + @string/scaling_filter_zero_tangent + @string/scaling_filter_bspline + @string/scaling_filter_mitchell + @string/scaling_filter_spline1 @string/scaling_filter_gaussian + @string/scaling_filter_lanczos @string/scaling_filter_scale_force @string/scaling_filter_fsr @string/scaling_filter_area @@ -265,6 +270,11 @@ 4 5 6 + 7 + 8 + 9 + 10 + 11 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 48ea3ed99b..6881d581ca 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -996,6 +996,9 @@ ScaleForce AMD FidelityFX™ Super Resolution Area + Zero-Tangent-Cardinal + B-Spline + Mitchell-Netravali None diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index ebfa4ceb9e..49fdaf686a 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, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, MaxEnum); +ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, ZeroTangent, BSpline, Mitchell, 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 98a55e1fcf..fea986cf0a 100644 --- a/src/qt_common/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -570,6 +570,9 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")), PAIR(ScalingFilter, Bilinear, tr("Bilinear")), PAIR(ScalingFilter, Bicubic, tr("Bicubic")), + PAIR(ScalingFilter, ZeroTangent, tr("Zero-Tangent-Cardinal")), + PAIR(ScalingFilter, BSpline, tr("B-Spline")), + PAIR(ScalingFilter, Mitchell, tr("Mitchell-Netravali")), PAIR(ScalingFilter, Spline1, tr("Spline-1")), PAIR(ScalingFilter, Gaussian, tr("Gaussian")), PAIR(ScalingFilter, Lanczos, tr("Lanczos")), diff --git a/src/qt_common/shared_translation.h b/src/qt_common/shared_translation.h index c9216c2daa..801d27c416 100644 --- a/src/qt_common/shared_translation.h +++ b/src/qt_common/shared_translation.h @@ -38,6 +38,9 @@ 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::ZeroTangent, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Zero-Tangent-Cardinal"))}, + {Settings::ScalingFilter::BSpline, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "B-Spline"))}, + {Settings::ScalingFilter::Mitchell, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Mitchell-Netravali"))}, {Settings::ScalingFilter::Spline1, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Spline-1"))}, {Settings::ScalingFilter::Gaussian, diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index c14b44a45a..4bbeb1e33f 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -44,6 +44,9 @@ set(SHADER_FILES pitch_unswizzle.comp present_area.frag present_bicubic.frag + present_zero_tangent.frag + present_bspline.frag + present_mitchell.frag present_gaussian.frag present_lanczos.frag present_spline1.frag diff --git a/src/video_core/host_shaders/present_bicubic.frag b/src/video_core/host_shaders/present_bicubic.frag index a9d9d40a38..a03b330165 100644 --- a/src/video_core/host_shaders/present_bicubic.frag +++ b/src/video_core/host_shaders/present_bicubic.frag @@ -1,56 +1,35 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later - #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 cubic(float v) { - vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; - vec4 s = n * n * n; - float x = s.x; - float y = s.y - 4.0 * s.x; - float z = s.z - 4.0 * s.y + 6.0 * s.x; - float w = 6.0 - x - y - z; - return vec4(x, y, z, w) * (1.0 / 6.0); +vec4 cubic(float x) { + float x2 = x * x; + float x3 = x2 * x; + return vec4(1.0, x, x2, x3) * transpose(mat4x4( + 0.0, 2.0, 0.0, 0.0, + -1.0, 0.0, 1.0, 0.0, + 2.0, -5.0, 4.0, -1.0, + -1.0, 3.0, -3.0, 1.0 + ) * (1.0 / 2.0)); } - -vec4 textureBicubic( sampler2D textureSampler, vec2 texCoords ) { - - vec2 texSize = textureSize(textureSampler, 0); - vec2 invTexSize = 1.0 / texSize; - - texCoords = texCoords * texSize - 0.5; - - vec2 fxy = fract(texCoords); - texCoords -= fxy; - - vec4 xcubic = cubic(fxy.x); - vec4 ycubic = cubic(fxy.y); - - vec4 c = texCoords.xxyy + vec2(-0.5, +1.5).xyxy; - - vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); - vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; - - offset *= invTexSize.xxyy; - - vec4 sample0 = texture(textureSampler, offset.xz); - vec4 sample1 = texture(textureSampler, offset.yz); - vec4 sample2 = texture(textureSampler, offset.xw); - vec4 sample3 = texture(textureSampler, offset.yw); - - float sx = s.x / (s.x + s.y); - float sy = s.z / (s.z + s.w); - - return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); +vec4 textureBicubic(sampler2D samp, vec2 uv) { + vec2 tex_size = vec2(textureSize(samp, 0)); + vec2 cc_tex = uv * tex_size - 0.5f; + vec2 fex = cc_tex - floor(cc_tex); + vec4 xcubic = cubic(fex.x); + vec4 ycubic = cubic(fex.y); + vec4 c = floor(cc_tex).xxyy + vec2(-0.5f, 1.5f).xyxy; + vec4 z = vec4(xcubic.yw, ycubic.yw); + vec4 s = vec4(xcubic.xz, ycubic.xz) + z; + vec4 offset = (c + z / s) * (1.0f / tex_size).xxyy; + vec2 n = vec2(s.x / (s.x + s.y), s.z / (s.z + s.w)); + return mix( + mix(texture(samp, offset.yw), texture(samp, offset.xw), n.x), + mix(texture(samp, offset.yz), texture(samp, offset.xz), n.x), + n.y); } - void main() { color = textureBicubic(color_texture, frag_tex_coord); } diff --git a/src/video_core/host_shaders/present_bspline.frag b/src/video_core/host_shaders/present_bspline.frag new file mode 100644 index 0000000000..92fd6f646b --- /dev/null +++ b/src/video_core/host_shaders/present_bspline.frag @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#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 cubic(float x) { + float x2 = x * x; + float x3 = x2 * x; + return vec4(1.0, x, x2, x3) * transpose(mat4x4( + 1.0, 4.0, 1.0, 0.0, + -3.0, 0.0, 3.0, 0.0, + 3.0, -6.0, 3.0, 0.0, + -1.0, 3.0, -3.0, 1.0 + ) * (1.0 / 6.0)); +} +vec4 textureBicubic(sampler2D samp, vec2 uv) { + vec2 tex_size = vec2(textureSize(samp, 0)); + vec2 cc_tex = uv * tex_size - 0.5f; + vec2 fex = cc_tex - floor(cc_tex); + vec4 xcubic = cubic(fex.x); + vec4 ycubic = cubic(fex.y); + vec4 c = floor(cc_tex).xxyy + vec2(-0.5f, 1.5f).xyxy; + vec4 z = vec4(xcubic.yw, ycubic.yw); + vec4 s = vec4(xcubic.xz, ycubic.xz) + z; + vec4 offset = (c + z / s) * (1.0f / tex_size).xxyy; + vec2 n = vec2(s.x / (s.x + s.y), s.z / (s.z + s.w)); + return mix( + mix(texture(samp, offset.yw), texture(samp, offset.xw), n.x), + mix(texture(samp, offset.yz), texture(samp, offset.xz), n.x), + n.y); +} +void main() { + color = textureBicubic(color_texture, frag_tex_coord); +} diff --git a/src/video_core/host_shaders/present_mitchell.frag b/src/video_core/host_shaders/present_mitchell.frag new file mode 100644 index 0000000000..7a3efa79a1 --- /dev/null +++ b/src/video_core/host_shaders/present_mitchell.frag @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#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 cubic(float x) { + float x2 = x * x; + float x3 = x2 * x; + return vec4(1.0, x, x2, x3) * transpose(mat4x4( + 1.0, 16.0, 1.0, 0.0, + -9.0, 0.0, 9.0, 0.0, + 15.0, -36.0, 27.0, -6.0, + -7.0, 21.0, -21.0, 7.0 + ) * (1.0 / 18.0)); +} +vec4 textureBicubic(sampler2D samp, vec2 uv) { + vec2 tex_size = vec2(textureSize(samp, 0)); + vec2 cc_tex = uv * tex_size - 0.5f; + vec2 fex = cc_tex - floor(cc_tex); + vec4 xcubic = cubic(fex.x); + vec4 ycubic = cubic(fex.y); + vec4 c = floor(cc_tex).xxyy + vec2(-0.5f, 1.5f).xyxy; + vec4 z = vec4(xcubic.yw, ycubic.yw); + vec4 s = vec4(xcubic.xz, ycubic.xz) + z; + vec4 offset = (c + z / s) * (1.0f / tex_size).xxyy; + vec2 n = vec2(s.x / (s.x + s.y), s.z / (s.z + s.w)); + return mix( + mix(texture(samp, offset.yw), texture(samp, offset.xw), n.x), + mix(texture(samp, offset.yz), texture(samp, offset.xz), n.x), + n.y); +} +void main() { + color = textureBicubic(color_texture, frag_tex_coord); +} diff --git a/src/video_core/host_shaders/present_zero_tangent.frag b/src/video_core/host_shaders/present_zero_tangent.frag new file mode 100644 index 0000000000..ccaa4d5f5d --- /dev/null +++ b/src/video_core/host_shaders/present_zero_tangent.frag @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#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 cubic(float x) { + float x2 = x * x; + float x3 = x2 * x; + return vec4(1.0, x, x2, x3) * transpose(mat4x4( + 0.0, 2.0, 0.0, 0.0, + -2.0, 0.0, 2.0, 0.0, + 4.0, -4.0, 2.0, -2.0, + -2.0, 2.0, -2.0, 1.0 + ) * (1.0 / 2.0)); +} +vec4 textureBicubic(sampler2D samp, vec2 uv) { + vec2 tex_size = vec2(textureSize(samp, 0)); + vec2 cc_tex = uv * tex_size - 0.5f; + vec2 fex = cc_tex - floor(cc_tex); + vec4 xcubic = cubic(fex.x); + vec4 ycubic = cubic(fex.y); + vec4 c = floor(cc_tex).xxyy + vec2(-0.5f, 1.5f).xyxy; + vec4 z = vec4(xcubic.yw, ycubic.yw); + vec4 s = vec4(xcubic.xz, ycubic.xz) + z; + vec4 offset = (c + z / s) * (1.0f / tex_size).xxyy; + vec2 n = vec2(s.x / (s.x + s.y), s.z / (s.z + s.w)); + return mix( + mix(texture(samp, offset.yw), texture(samp, offset.xw), n.x), + mix(texture(samp, offset.yz), texture(samp, offset.xz), n.x), + n.y); +} +void main() { + color = textureBicubic(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 65670fcad8..1d3eb16e63 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.cpp +++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp @@ -8,6 +8,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/settings.h" +#include "common/settings_enums.h" #include "video_core/present.h" #include "video_core/renderer_opengl/gl_blit_screen.h" #include "video_core/renderer_opengl/gl_state_tracker.h" @@ -86,6 +87,15 @@ void BlitScreen::CreateWindowAdapt() { case Settings::ScalingFilter::Bicubic: window_adapt = MakeBicubic(device); break; + case Settings::ScalingFilter::ZeroTangent: + window_adapt = MakeZeroTangent(device); + break; + case Settings::ScalingFilter::BSpline: + window_adapt = MakeBSpline(device); + break; + case Settings::ScalingFilter::Mitchell: + window_adapt = MakeMitchell(device); + break; case Settings::ScalingFilter::Gaussian: window_adapt = MakeGaussian(device); break; diff --git a/src/video_core/renderer_opengl/present/filters.cpp b/src/video_core/renderer_opengl/present/filters.cpp index 5e3d1538c6..da8e11a864 100644 --- a/src/video_core/renderer_opengl/present/filters.cpp +++ b/src/video_core/renderer_opengl/present/filters.cpp @@ -14,6 +14,9 @@ #include "video_core/host_shaders/present_gaussian_frag.h" #include "video_core/host_shaders/present_lanczos_frag.h" #include "video_core/host_shaders/present_spline1_frag.h" +#include "video_core/host_shaders/present_mitchell_frag_spv.h" +#include "video_core/host_shaders/present_bspline_frag_spv.h" +#include "video_core/host_shaders/present_zero_tangent_frag_spv.h" #include "video_core/renderer_opengl/present/filters.h" #include "video_core/renderer_opengl/present/util.h" @@ -39,6 +42,21 @@ std::unique_ptr MakeBicubic(const Device& device) { HostShaders::PRESENT_BICUBIC_FRAG); } +std::unique_ptr MakeMitchell(const Device& device) { + return std::make_unique(device, CreateBilinearSampler(), + HostShaders::PRESENT_MITCHELL_FRAG); +} + +std::unique_ptr MakeZeroTangent(const Device& device) { + return std::make_unique(device, CreateBilinearSampler(), + HostShaders::PRESENT_ZERO_TANGENT_FRAG); +} + +std::unique_ptr MakeBSpline(const Device& device) { + return std::make_unique(device, CreateBilinearSampler(), + HostShaders::PRESENT_BSPLINE_FRAG); +} + std::unique_ptr MakeGaussian(const Device& device) { return std::make_unique(device, CreateBilinearSampler(), HostShaders::PRESENT_GAUSSIAN_FRAG); diff --git a/src/video_core/renderer_opengl/present/filters.h b/src/video_core/renderer_opengl/present/filters.h index 7b38ac56bc..0fba4408dc 100644 --- a/src/video_core/renderer_opengl/present/filters.h +++ b/src/video_core/renderer_opengl/present/filters.h @@ -17,6 +17,9 @@ namespace OpenGL { std::unique_ptr MakeNearestNeighbor(const Device& device); std::unique_ptr MakeBilinear(const Device& device); std::unique_ptr MakeBicubic(const Device& device); +std::unique_ptr MakeZeroTangent(const Device& device); +std::unique_ptr MakeMitchell(const Device& device); +std::unique_ptr MakeBSpline(const Device& device); std::unique_ptr MakeGaussian(const Device& device); std::unique_ptr MakeSpline1(const Device& device); std::unique_ptr MakeLanczos(const Device& device); diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp index e0f2b26f84..632f1bf16e 100644 --- a/src/video_core/renderer_vulkan/present/filters.cpp +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -7,6 +7,7 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/assert.h" #include "common/common_types.h" #include "video_core/host_shaders/present_area_frag_spv.h" @@ -14,6 +15,9 @@ #include "video_core/host_shaders/present_gaussian_frag_spv.h" #include "video_core/host_shaders/present_lanczos_frag_spv.h" #include "video_core/host_shaders/present_spline1_frag_spv.h" +#include "video_core/host_shaders/present_mitchell_frag_spv.h" +#include "video_core/host_shaders/present_bspline_frag_spv.h" +#include "video_core/host_shaders/present_zero_tangent_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" @@ -52,13 +56,27 @@ std::unique_ptr MakeSpline1(const Device& device, VkFormat fram BuildShader(device, PRESENT_SPLINE1_FRAG_SPV)); } -std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format) { +std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format, VkCubicFilterWeightsQCOM qcom_weights) { // No need for handrolled shader -- if the VK impl can do it for us ;) - if (device.IsExtFilterCubicSupported()) - return std::make_unique(device, frame_format, CreateCubicSampler(device), - BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); - return std::make_unique(device, frame_format, CreateBilinearSampler(device), - BuildShader(device, PRESENT_BICUBIC_FRAG_SPV)); + if (device.IsExtFilterCubicSupported()) { + return std::make_unique(device, frame_format, CreateCubicSampler(device, + qcom_weights), BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); + } else { + return std::make_unique(device, frame_format, CreateBilinearSampler(device), [&](){ + switch (qcom_weights) { + case VK_CUBIC_FILTER_WEIGHTS_CATMULL_ROM_QCOM: + return BuildShader(device, PRESENT_BICUBIC_FRAG_SPV); + case VK_CUBIC_FILTER_WEIGHTS_ZERO_TANGENT_CARDINAL_QCOM: + return BuildShader(device, PRESENT_ZERO_TANGENT_FRAG_SPV); + case VK_CUBIC_FILTER_WEIGHTS_B_SPLINE_QCOM: + return BuildShader(device, PRESENT_BSPLINE_FRAG_SPV); + case VK_CUBIC_FILTER_WEIGHTS_MITCHELL_NETRAVALI_QCOM: + return BuildShader(device, PRESENT_MITCHELL_FRAG_SPV); + default: + UNREACHABLE(); + } + }()); + } } std::unique_ptr MakeGaussian(const Device& device, VkFormat frame_format) { diff --git a/src/video_core/renderer_vulkan/present/filters.h b/src/video_core/renderer_vulkan/present/filters.h index 015bffc8a5..2f02003235 100644 --- a/src/video_core/renderer_vulkan/present/filters.h +++ b/src/video_core/renderer_vulkan/present/filters.h @@ -17,7 +17,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 MakeBicubic(const Device& device, VkFormat frame_format, VkCubicFilterWeightsQCOM qcom_weights); 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); diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp index 0b1a89eec0..29a1c34976 100644 --- a/src/video_core/renderer_vulkan/present/util.cpp +++ b/src/video_core/renderer_vulkan/present/util.cpp @@ -624,8 +624,8 @@ vk::Sampler CreateNearestNeighborSampler(const Device& device) { return device.GetLogical().CreateSampler(ci_nn); } -vk::Sampler CreateCubicSampler(const Device& device) { - const VkSamplerCreateInfo ci_nn{ +vk::Sampler CreateCubicSampler(const Device& device, VkCubicFilterWeightsQCOM qcom_weights) { + VkSamplerCreateInfo ci_nn{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .pNext = nullptr, .flags = 0, @@ -645,7 +645,14 @@ vk::Sampler CreateCubicSampler(const Device& device) { .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, .unnormalizedCoordinates = VK_FALSE, }; - + const VkSamplerCubicWeightsCreateInfoQCOM ci_qcom_nn{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_CUBIC_WEIGHTS_CREATE_INFO_QCOM, + .pNext = nullptr, + .cubicWeights = qcom_weights + }; + // If not specified, assume Catmull-Rom + if (qcom_weights != VK_CUBIC_FILTER_WEIGHTS_CATMULL_ROM_QCOM) + ci_nn.pNext = &ci_qcom_nn; return device.GetLogical().CreateSampler(ci_nn); } diff --git a/src/video_core/renderer_vulkan/present/util.h b/src/video_core/renderer_vulkan/present/util.h index 11810352df..0325858d13 100644 --- a/src/video_core/renderer_vulkan/present/util.h +++ b/src/video_core/renderer_vulkan/present/util.h @@ -57,7 +57,7 @@ VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector VkDescriptorSet set, u32 binding); vk::Sampler CreateBilinearSampler(const Device& device); vk::Sampler CreateNearestNeighborSampler(const Device& device); -vk::Sampler CreateCubicSampler(const Device& device); +vk::Sampler CreateCubicSampler(const Device& device, std::optional qcom_weights); void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer, VkExtent2D extent); diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index b720bcded3..14a914c0b3 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -7,6 +7,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include "video_core/framebuffer_config.h" #include "video_core/present.h" #include "video_core/renderer_vulkan/present/filters.h" @@ -41,7 +42,16 @@ void BlitScreen::SetWindowAdaptPass() { window_adapt = MakeNearestNeighbor(device, swapchain_view_format); break; case Settings::ScalingFilter::Bicubic: - window_adapt = MakeBicubic(device, swapchain_view_format); + window_adapt = MakeBicubic(device, swapchain_view_format, VK_CUBIC_FILTER_WEIGHTS_CATMULL_ROM_QCOM); + break; + case Settings::ScalingFilter::ZeroTangent: + window_adapt = MakeBicubic(device, swapchain_view_format, VK_CUBIC_FILTER_WEIGHTS_ZERO_TANGENT_CARDINAL_QCOM); + break; + case Settings::ScalingFilter::BSpline: + window_adapt = MakeBicubic(device, swapchain_view_format, VK_CUBIC_FILTER_WEIGHTS_B_SPLINE_QCOM); + break; + case Settings::ScalingFilter::Mitchell: + window_adapt = MakeBicubic(device, swapchain_view_format, VK_CUBIC_FILTER_WEIGHTS_MITCHELL_NETRAVALI_QCOM); break; case Settings::ScalingFilter::Spline1: window_adapt = MakeSpline1(device, swapchain_view_format); From 4709a15d92ffaeb46797a4d47b53a90eeeabf26a Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 25 Sep 2025 07:48:50 +0000 Subject: [PATCH 02/19] fix ogl Signed-off-by: lizzie --- src/video_core/renderer_opengl/present/filters.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_opengl/present/filters.cpp b/src/video_core/renderer_opengl/present/filters.cpp index da8e11a864..4aaec3fa0b 100644 --- a/src/video_core/renderer_opengl/present/filters.cpp +++ b/src/video_core/renderer_opengl/present/filters.cpp @@ -14,9 +14,9 @@ #include "video_core/host_shaders/present_gaussian_frag.h" #include "video_core/host_shaders/present_lanczos_frag.h" #include "video_core/host_shaders/present_spline1_frag.h" -#include "video_core/host_shaders/present_mitchell_frag_spv.h" -#include "video_core/host_shaders/present_bspline_frag_spv.h" -#include "video_core/host_shaders/present_zero_tangent_frag_spv.h" +#include "video_core/host_shaders/present_mitchell_frag.h" +#include "video_core/host_shaders/present_bspline_frag.h" +#include "video_core/host_shaders/present_zero_tangent_frag.h" #include "video_core/renderer_opengl/present/filters.h" #include "video_core/renderer_opengl/present/util.h" From ce50838d36a400364ee839218dfd394680cf8f49 Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 25 Sep 2025 07:56:23 +0000 Subject: [PATCH 03/19] fix vk Signed-off-by: lizzie --- src/video_core/renderer_vulkan/present/util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/present/util.h b/src/video_core/renderer_vulkan/present/util.h index 0325858d13..38cc6203c5 100644 --- a/src/video_core/renderer_vulkan/present/util.h +++ b/src/video_core/renderer_vulkan/present/util.h @@ -57,7 +57,7 @@ VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector VkDescriptorSet set, u32 binding); vk::Sampler CreateBilinearSampler(const Device& device); vk::Sampler CreateNearestNeighborSampler(const Device& device); -vk::Sampler CreateCubicSampler(const Device& device, std::optional qcom_weights); +vk::Sampler CreateCubicSampler(const Device& device, VkCubicFilterWeightsQCOM qcom_weights); void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer, VkExtent2D extent); From 38c75597ec427c5a7c72cd06108f63f26e773404 Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 25 Sep 2025 08:02:54 +0000 Subject: [PATCH 04/19] better logic Signed-off-by: lizzie --- src/video_core/renderer_vulkan/present/filters.cpp | 4 +++- src/video_core/vulkan_common/vulkan_device.h | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp index 632f1bf16e..e3c457b44c 100644 --- a/src/video_core/renderer_vulkan/present/filters.cpp +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -7,6 +7,7 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include "common/assert.h" #include "common/common_types.h" @@ -58,7 +59,8 @@ std::unique_ptr MakeSpline1(const Device& device, VkFormat fram std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format, VkCubicFilterWeightsQCOM qcom_weights) { // No need for handrolled shader -- if the VK impl can do it for us ;) - if (device.IsExtFilterCubicSupported()) { + // Catmull-Rom is default bicubic for all implementations... + if (device.IsExtFilterCubicSupported() && (device.IsQcomFilterCubicWeightsSupported() || qcom_weights == VK_CUBIC_FILTER_WEIGHTS_CATMULL_ROM_QCOM)) { return std::make_unique(device, frame_format, CreateCubicSampler(device, qcom_weights), BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); } else { diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index bd54144480..cb13f28523 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -89,7 +89,8 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \ EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle) \ EXTENSION(EXT, DESCRIPTOR_INDEXING, descriptor_indexing) \ - EXTENSION(EXT, FILTER_CUBIC, filter_cubic) + EXTENSION(EXT, FILTER_CUBIC, filter_cubic) \ + EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights) // Define extensions which must be supported. #define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \ @@ -558,6 +559,11 @@ public: return extensions.filter_cubic; } + /// Returns true if the device supports VK_QCOM_filter_cubic_weights + bool IsQcomFilterCubicWeightsSupported() const { + return extensions.filter_cubic_weights; + } + /// Returns true if the device supports VK_EXT_line_rasterization. bool IsExtLineRasterizationSupported() const { return extensions.line_rasterization; From ffb0df4ccc407c3e6c0ad7c5fd607cb0913c9a6e Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 25 Sep 2025 17:25:04 +0000 Subject: [PATCH 05/19] add MMPX filter Signed-off-by: lizzie --- docs/User.md | 18 +++ .../app/src/main/res/values/arrays.xml | 2 + .../app/src/main/res/values/strings.xml | 5 +- src/common/settings_enums.h | 2 +- src/qt_common/shared_translation.cpp | 5 +- src/qt_common/shared_translation.h | 5 +- src/video_core/host_shaders/CMakeLists.txt | 1 + .../host_shaders/present_bicubic.frag | 2 + .../host_shaders/present_bspline.frag | 4 +- .../host_shaders/present_mitchell.frag | 4 +- src/video_core/host_shaders/present_mmpx.frag | 131 ++++++++++++++++++ .../host_shaders/present_zero_tangent.frag | 4 +- .../renderer_opengl/gl_blit_screen.cpp | 3 + .../renderer_opengl/present/filters.cpp | 6 + .../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 + 18 files changed, 190 insertions(+), 13 deletions(-) create mode 100644 src/video_core/host_shaders/present_mmpx.frag diff --git a/docs/User.md b/docs/User.md index cfc81063f8..b8da9bc8d5 100644 --- a/docs/User.md +++ b/docs/User.md @@ -9,3 +9,21 @@ Eden will store configuration in the following directories: - **Linux, macOS, FreeBSD, Solaris, OpenBSD**: `$XDG_DATA_HOME`, `$XDG_CACHE_HOME`, `$XDG_CONFIG_HOME`. If a `user` directory is present in the current working directory, that will override all global configuration directories and the emulator will use that instead. + +# Enhancements + +## Filters + +Various graphical filters exist - each of them aimed at a specific target/image quality preset. + +- **Nearest**: Provides no filtering - useful for debugging. +- **Bilinear**: Provides the hardware default filtering of the Tegra X1. +- **Bicubic**: Provides a bicubic interpolation using a Catmull-Rom (or hardware-accelerated) implementation. +- **Zero-Tangent, B-Spline, Mitchell**: Provides bicubic interpolation using the respective matrix weights. They're normally not hardware accelerated unless the device supports the `VK_QCOM_filter_cubic_weights` extension. The matrix weights are those matching [the specification itself](https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#VkSamplerCubicWeightsCreateInfoQCOM). +- **Spline-1**: Bicubic interpolation (similar to Mitchell) but with a faster texel fetch method. Generally less blurry than bicubic. +- **Gaussian**: Whole-area blur, an applied gaussian blur is done to the entire frame. +- **Lanczos**: An implementation using `a = 3` (49 texel fetches). Provides sharper edges but blurrier artifacts. +- **ScaleForce**: Experimental texture upscale method, see [ScaleFish](https://github.com/BreadFish64/ScaleFish). +- **FSR**: Uses AMD FidelityFX Super Resolution to enhance image quality. +- **Area**: Area interpolation (high kernel count). +- **MMPX**: Nearest-neighbour filter aimed at providing higher pixel-art quality. diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index fa9a02aead..86fae9ba3c 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -260,6 +260,7 @@ @string/scaling_filter_scale_force @string/scaling_filter_fsr @string/scaling_filter_area + @string/scaling_filter_mmpx @@ -275,6 +276,7 @@ 9 10 11 + 12 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 6881d581ca..b8f59de880 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -996,9 +996,10 @@ ScaleForce AMD FidelityFX™ Super Resolution Area - Zero-Tangent-Cardinal + Zero-Tangent B-Spline - Mitchell-Netravali + Mitchell + MMPX None diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 49fdaf686a..f7dc5dc6c8 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, ZeroTangent, BSpline, Mitchell, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, MaxEnum); +ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, ZeroTangent, BSpline, Mitchell, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, Mmpx, MaxEnum); ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); diff --git a/src/qt_common/shared_translation.cpp b/src/qt_common/shared_translation.cpp index fea986cf0a..dc20fbf246 100644 --- a/src/qt_common/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -570,15 +570,16 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")), PAIR(ScalingFilter, Bilinear, tr("Bilinear")), PAIR(ScalingFilter, Bicubic, tr("Bicubic")), - PAIR(ScalingFilter, ZeroTangent, tr("Zero-Tangent-Cardinal")), + PAIR(ScalingFilter, ZeroTangent, tr("Zero-Tangent")), PAIR(ScalingFilter, BSpline, tr("B-Spline")), - PAIR(ScalingFilter, Mitchell, tr("Mitchell-Netravali")), + PAIR(ScalingFilter, Mitchell, tr("Mitchell")), PAIR(ScalingFilter, Spline1, tr("Spline-1")), PAIR(ScalingFilter, Gaussian, tr("Gaussian")), PAIR(ScalingFilter, Lanczos, tr("Lanczos")), PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")), PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™️ Super Resolution")), PAIR(ScalingFilter, Area, tr("Area")), + PAIR(ScalingFilter, Mmpx, tr("MMPX")), }}); translations->insert({Settings::EnumMetadata::Index(), { diff --git a/src/qt_common/shared_translation.h b/src/qt_common/shared_translation.h index 801d27c416..a25887bb87 100644 --- a/src/qt_common/shared_translation.h +++ b/src/qt_common/shared_translation.h @@ -38,9 +38,9 @@ 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::ZeroTangent, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Zero-Tangent-Cardinal"))}, + {Settings::ScalingFilter::ZeroTangent, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Zero-Tangent"))}, {Settings::ScalingFilter::BSpline, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "B-Spline"))}, - {Settings::ScalingFilter::Mitchell, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Mitchell-Netravali"))}, + {Settings::ScalingFilter::Mitchell, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Mitchell"))}, {Settings::ScalingFilter::Spline1, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Spline-1"))}, {Settings::ScalingFilter::Gaussian, @@ -51,6 +51,7 @@ static const std::map scaling_filter_texts_map QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))}, {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, {Settings::ScalingFilter::Area, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Area"))}, + {Settings::ScalingFilter::Mmpx, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "MMPX"))}, }; static const std::map use_docked_mode_texts_map = { diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 4bbeb1e33f..9f7b9edd5a 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -50,6 +50,7 @@ set(SHADER_FILES present_gaussian.frag present_lanczos.frag present_spline1.frag + present_mmpx.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_bicubic.frag b/src/video_core/host_shaders/present_bicubic.frag index a03b330165..5347fd2ef7 100644 --- a/src/video_core/host_shaders/present_bicubic.frag +++ b/src/video_core/host_shaders/present_bicubic.frag @@ -1,3 +1,5 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #version 460 core diff --git a/src/video_core/host_shaders/present_bspline.frag b/src/video_core/host_shaders/present_bspline.frag index 92fd6f646b..f229de6030 100644 --- a/src/video_core/host_shaders/present_bspline.frag +++ b/src/video_core/host_shaders/present_bspline.frag @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2021 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 #version 460 core layout (location = 0) in vec2 frag_tex_coord; layout (location = 0) out vec4 color; diff --git a/src/video_core/host_shaders/present_mitchell.frag b/src/video_core/host_shaders/present_mitchell.frag index 7a3efa79a1..4ca65cd6f0 100644 --- a/src/video_core/host_shaders/present_mitchell.frag +++ b/src/video_core/host_shaders/present_mitchell.frag @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2021 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 #version 460 core layout (location = 0) in vec2 frag_tex_coord; layout (location = 0) out vec4 color; diff --git a/src/video_core/host_shaders/present_mmpx.frag b/src/video_core/host_shaders/present_mmpx.frag new file mode 100644 index 0000000000..6c2c05a63a --- /dev/null +++ b/src/video_core/host_shaders/present_mmpx.frag @@ -0,0 +1,131 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#version 460 core +layout(location = 0) in vec2 tex_coord; +layout(location = 0) out vec4 frag_color; +layout(binding = 0) uniform sampler2D tex; + +#define src(x, y) texture(tex, coord + vec2(x, y) * 1.0 / source_size) + +float luma(vec4 col) { + return dot(col.rgb, vec3(0.2126, 0.7152, 0.0722)) * (1.0 - col.a); +} + +bool same(vec4 B, vec4 A0) { + return all(equal(B, A0)); +} + +bool notsame(vec4 B, vec4 A0) { + return any(notEqual(B, A0)); +} + +bool all_eq2(vec4 B, vec4 A0, vec4 A1) { + return (same(B,A0) && same(B,A1)); +} + +bool all_eq3(vec4 B, vec4 A0, vec4 A1, vec4 A2) { + return (same(B,A0) && same(B,A1) && same(B,A2)); +} + +bool all_eq4(vec4 B, vec4 A0, vec4 A1, vec4 A2, vec4 A3) { + return (same(B,A0) && same(B,A1) && same(B,A2) && same(B,A3)); +} + +bool any_eq3(vec4 B, vec4 A0, vec4 A1, vec4 A2) { + return (same(B,A0) || same(B,A1) || same(B,A2)); +} + +bool none_eq2(vec4 B, vec4 A0, vec4 A1) { + return (notsame(B,A0) && notsame(B,A1)); +} + +bool none_eq4(vec4 B, vec4 A0, vec4 A1, vec4 A2, vec4 A3) { + return (notsame(B,A0) && notsame(B,A1) && notsame(B,A2) && notsame(B,A3)); +} + +void main() +{ + vec2 source_size = vec2(textureSize(tex, 0)); + vec2 pos = fract(tex_coord * source_size) - vec2(0.5, 0.5); + vec2 coord = tex_coord - pos / source_size; + + vec4 E = src(0.0,0.0); + + vec4 A = src(-1.0,-1.0); + vec4 B = src(0.0,-1.0); + vec4 C = src(1.0,-1.0); + + vec4 D = src(-1.0,0.0); + vec4 F = src(1.0,0.0); + + vec4 G = src(-1.0,1.0); + vec4 H = src(0.0,1.0); + vec4 I = src(1.0,1.0); + + vec4 J = E; + vec4 K = E; + vec4 L = E; + vec4 M = E; + + frag_color = E; + + if(same(E,A) && same(E,B) && same(E,C) && same(E,D) && same(E,F) && same(E,G) && same(E,H) && same(E,I)) return; + + vec4 P = src(0.0,2.0); + vec4 Q = src(-2.0,0.0); + vec4 R = src(2.0,0.0); + vec4 S = src(0.0,2.0); + + float Bl = luma(B); + float Dl = luma(D); + float El = luma(E); + float Fl = luma(F); + float Hl = luma(H); + + if (((same(D,B) && notsame(D,H) && notsame(D,F))) && ((El>=Dl) || same(E,A)) && any_eq3(E,A,C,G) && ((El=Bl) || same(E,C)) && any_eq3(E,A,C,I) && ((El=Hl) || same(E,G)) && any_eq3(E,A,G,I) && ((El=Fl) || same(E,I)) && any_eq3(E,C,G,I) && ((El MakeArea(const Device& device) { HostShaders::PRESENT_AREA_FRAG); } +std::unique_ptr MakeMmpx(const Device& device) { + return std::make_unique(device, CreateNearestNeighborSampler(), + HostShaders::PRESENT_MMPX_FRAG); +} + } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/filters.h b/src/video_core/renderer_opengl/present/filters.h index 0fba4408dc..187d0f1298 100644 --- a/src/video_core/renderer_opengl/present/filters.h +++ b/src/video_core/renderer_opengl/present/filters.h @@ -25,5 +25,6 @@ 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); +std::unique_ptr MakeMmpx(const Device& device); } // namespace OpenGL diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp index e3c457b44c..0a28ea6349 100644 --- a/src/video_core/renderer_vulkan/present/filters.cpp +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -19,6 +19,7 @@ #include "video_core/host_shaders/present_mitchell_frag_spv.h" #include "video_core/host_shaders/present_bspline_frag_spv.h" #include "video_core/host_shaders/present_zero_tangent_frag_spv.h" +#include "video_core/host_shaders/present_mmpx_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" @@ -101,4 +102,9 @@ std::unique_ptr MakeArea(const Device& device, VkFormat frame_f BuildShader(device, PRESENT_AREA_FRAG_SPV)); } +std::unique_ptr MakeMmpx(const Device& device, VkFormat frame_format) { + return std::make_unique(device, frame_format, CreateNearestNeighborSampler(device), + BuildShader(device, PRESENT_MMPX_FRAG_SPV)); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/filters.h b/src/video_core/renderer_vulkan/present/filters.h index 2f02003235..afc3ba29a0 100644 --- a/src/video_core/renderer_vulkan/present/filters.h +++ b/src/video_core/renderer_vulkan/present/filters.h @@ -23,5 +23,6 @@ std::unique_ptr MakeGaussian(const Device& device, VkFormat fra 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); +std::unique_ptr MakeMmpx(const Device& device, VkFormat frame_format); } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 14a914c0b3..0f54dd5ade 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -68,6 +68,9 @@ void BlitScreen::SetWindowAdaptPass() { case Settings::ScalingFilter::Area: window_adapt = MakeArea(device, swapchain_view_format); break; + case Settings::ScalingFilter::Mmpx: + window_adapt = MakeMmpx(device, swapchain_view_format); + break; case Settings::ScalingFilter::Fsr: case Settings::ScalingFilter::Bilinear: default: From f484944d2da53b59c2e48d71057a64996b3c8d3c Mon Sep 17 00:00:00 2001 From: lizzie Date: Fri, 26 Sep 2025 23:41:45 +0000 Subject: [PATCH 06/19] fix Signed-off-by: lizzie --- src/android/app/src/main/res/values/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index b8f59de880..c529eaf63f 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -992,7 +992,9 @@ Nearest Neighbor Bilinear Bicubic + Spline-1 Gaussian + Lanczos ScaleForce AMD FidelityFX™ Super Resolution Area From d19a7c3782d8402a5c3d8c730fb2ef080da7a8b5 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 28 Sep 2025 18:43:01 +0200 Subject: [PATCH 07/19] [service] unstub process winding (#2590) It's used on FW19+ and FW20+ but since all 20+ applets stuck on HID, you still can't boot into applets. Should fix: Bioshock Infinite on FW19 Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2590 Reviewed-by: Shinmegumi Reviewed-by: MaranBr Co-authored-by: unknown Co-committed-by: unknown --- src/core/hle/service/am/applet.h | 5 ++ .../am/service/process_winding_controller.cpp | 54 ++++++++++++++++--- .../am/service/process_winding_controller.h | 10 ++++ .../service/am/service/self_controller.cpp | 12 +++++ .../hle/service/am/service/self_controller.h | 4 ++ 5 files changed, 79 insertions(+), 6 deletions(-) diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h index 6cc8cdf741..ad84f39dc7 100644 --- a/src/core/hle/service/am/applet.h +++ b/src/core/hle/service/am/applet.h @@ -8,6 +8,7 @@ #include #include +#include #include "common/math_util.h" #include "core/hle/service/apm/apm_controller.h" @@ -23,6 +24,7 @@ #include "core/hle/service/am/hid_registration.h" #include "core/hle/service/am/lifecycle_manager.h" #include "core/hle/service/am/process_holder.h" +#include "core/hle/service/am/service/storage.h" namespace Service::AM { @@ -97,6 +99,9 @@ struct Applet { std::deque> preselected_user_launch_parameter{}; std::deque> friend_invitation_storage_channel{}; + // Context Stack + std::stack> context_stack{}; + // Caller applet std::weak_ptr caller_applet{}; std::shared_ptr caller_applet_broker{}; diff --git a/src/core/hle/service/am/service/process_winding_controller.cpp b/src/core/hle/service/am/service/process_winding_controller.cpp index 10df830d70..30529de550 100644 --- a/src/core/hle/service/am/service/process_winding_controller.cpp +++ b/src/core/hle/service/am/service/process_winding_controller.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,12 +18,12 @@ IProcessWindingController::IProcessWindingController(Core::System& system_, static const FunctionInfo functions[] = { {0, D<&IProcessWindingController::GetLaunchReason>, "GetLaunchReason"}, {11, D<&IProcessWindingController::OpenCallingLibraryApplet>, "OpenCallingLibraryApplet"}, - {21, nullptr, "PushContext"}, - {22, nullptr, "PopContext"}, - {23, nullptr, "CancelWindingReservation"}, - {30, nullptr, "WindAndDoReserved"}, - {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"}, - {41, nullptr, "ReserveToStartAndWait"}, + {21, D<&IProcessWindingController::PushContext>, "PushContext"}, + {22, D<&IProcessWindingController::PopContext>, "PopContext"}, + {23, D<&IProcessWindingController::CancelWindingReservation>, "CancelWindingReservation"}, + {30, D<&IProcessWindingController::WindAndDoReserved>, "WindAndDoReserved"}, + {40, D<&IProcessWindingController::ReserveToStartAndWaitAndUnwindThis>, "ReserveToStartAndWaitAndUnwindThis"}, + {41, D<&IProcessWindingController::ReserveToStartAndWait>, "ReserveToStartAndWait"}, }; // clang-format on @@ -51,4 +54,43 @@ Result IProcessWindingController::OpenCallingLibraryApplet( R_SUCCEED(); } +Result IProcessWindingController::PushContext(SharedPointer context) { + LOG_INFO(Service_AM, "called"); + m_applet->context_stack.push(context); + R_SUCCEED(); +} + +Result IProcessWindingController::PopContext(Out> out_context) { + LOG_INFO(Service_AM, "called"); + + if (m_applet->context_stack.empty()) { + LOG_ERROR(Service_AM, "Context stack is empty"); + R_THROW(ResultUnknown); + } + + *out_context = m_applet->context_stack.top(); + m_applet->context_stack.pop(); + R_SUCCEED(); +} + +Result IProcessWindingController::CancelWindingReservation() { + LOG_WARNING(Service_AM, "STUBBED"); + R_SUCCEED(); +} + +Result IProcessWindingController::WindAndDoReserved() { + LOG_WARNING(Service_AM, "STUBBED"); + R_SUCCEED(); +} + +Result IProcessWindingController::ReserveToStartAndWaitAndUnwindThis() { + LOG_WARNING(Service_AM, "STUBBED"); + R_SUCCEED(); +} + +Result IProcessWindingController::ReserveToStartAndWait() { + LOG_WARNING(Service_AM, "STUBBED"); + R_SUCCEED(); +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/service/process_winding_controller.h b/src/core/hle/service/am/service/process_winding_controller.h index 4408af1f1d..0d53223033 100644 --- a/src/core/hle/service/am/service/process_winding_controller.h +++ b/src/core/hle/service/am/service/process_winding_controller.h @@ -1,8 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "core/hle/service/am/service/storage.h" #include "core/hle/service/am/am_types.h" #include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" @@ -21,6 +25,12 @@ private: Result GetLaunchReason(Out out_launch_reason); Result OpenCallingLibraryApplet( Out> out_calling_library_applet); + Result PushContext(SharedPointer in_context); + Result PopContext(Out> out_context); + Result CancelWindingReservation(); + Result WindAndDoReserved(); + Result ReserveToStartAndWaitAndUnwindThis(); + Result ReserveToStartAndWait(); const std::shared_ptr m_applet; }; diff --git a/src/core/hle/service/am/service/self_controller.cpp b/src/core/hle/service/am/service/self_controller.cpp index 1db02b88fd..1b58cbea27 100644 --- a/src/core/hle/service/am/service/self_controller.cpp +++ b/src/core/hle/service/am/service/self_controller.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -67,6 +70,7 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr {110, nullptr, "SetApplicationAlbumUserData"}, {120, D<&ISelfController::SaveCurrentScreenshot>, "SaveCurrentScreenshot"}, {130, D<&ISelfController::SetRecordVolumeMuted>, "SetRecordVolumeMuted"}, + {230, D<&ISelfController::Unknown230>, "Unknown230"}, {1000, nullptr, "GetDebugStorageChannel"}, }; // clang-format on @@ -404,4 +408,12 @@ Result ISelfController::SetRecordVolumeMuted(bool muted) { R_SUCCEED(); } +Result ISelfController::Unknown230(u32 in_val, Out out_val) { + LOG_WARNING(Service_AM, "(STUBBED) called, in_val={}", in_val); + + *out_val = 0; + + R_SUCCEED(); +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/service/self_controller.h b/src/core/hle/service/am/service/self_controller.h index eca083cfe5..86cd9f1118 100644 --- a/src/core/hle/service/am/service/self_controller.h +++ b/src/core/hle/service/am/service/self_controller.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -63,6 +66,7 @@ private: Result SetAlbumImageTakenNotificationEnabled(bool enabled); Result SaveCurrentScreenshot(Capture::AlbumReportOption album_report_option); Result SetRecordVolumeMuted(bool muted); + Result Unknown230(u32 in_val, Out out_val); Kernel::KProcess* const m_process; const std::shared_ptr m_applet; From bf302d7917f2bd4b2638ac61a751fedd5727c7a6 Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 29 Sep 2025 18:40:29 +0200 Subject: [PATCH 08/19] [common] No need to specify min/max for settings; fix crash when OOB value is given for some settings (#2609) This fixes issues when migrating settings that refer to invalid filters/scales. For example if we had 5 filters, but we set filter=6, the program would crash. This also makes so specifying min/max manually isn't needed (but can still be set for cases like NCE). Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2609 Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- src/common/settings.h | 41 ++----- src/common/settings_enums.h | 120 ++++++++------------- src/common/settings_setting.h | 67 ++++++------ src/yuzu/configuration/configure_audio.cpp | 6 +- 4 files changed, 84 insertions(+), 150 deletions(-) diff --git a/src/common/settings.h b/src/common/settings.h index 8605445837..891bde608c 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -178,7 +178,7 @@ struct Values { SwitchableSetting audio_input_device_id{ linkage, "auto", "input_device", Category::Audio, Specialization::RuntimeList}; SwitchableSetting sound_index{ - linkage, AudioMode::Stereo, AudioMode::Mono, AudioMode::Surround, + linkage, AudioMode::Stereo, "sound_index", Category::SystemAudio, Specialization::Default, true, true}; SwitchableSetting volume{linkage, @@ -199,8 +199,6 @@ struct Values { SwitchableSetting use_multi_core{linkage, true, "use_multi_core", Category::Core}; SwitchableSetting memory_layout_mode{linkage, MemoryLayout::Memory_4Gb, - MemoryLayout::Memory_4Gb, - MemoryLayout::Memory_12Gb, "memory_layout_mode", Category::Core, Specialization::Default, @@ -240,9 +238,8 @@ struct Values { #endif "cpu_backend", Category::Cpu}; - SwitchableSetting cpu_accuracy{linkage, CpuAccuracy::Auto, - CpuAccuracy::Auto, CpuAccuracy::Paranoid, - "cpu_accuracy", Category::Cpu}; + SwitchableSetting cpu_accuracy{linkage, CpuAccuracy::Auto, + "cpu_accuracy", Category::Cpu}; SwitchableSetting use_fast_cpu_time{linkage, false, @@ -324,10 +321,10 @@ struct Values { // Renderer SwitchableSetting renderer_backend{ - linkage, RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, + linkage, RendererBackend::Vulkan, "backend", Category::Renderer}; SwitchableSetting shader_backend{ - linkage, ShaderBackend::SpirV, ShaderBackend::Glsl, ShaderBackend::SpirV, + linkage, ShaderBackend::SpirV, "shader_backend", Category::Renderer, Specialization::RuntimeList}; SwitchableSetting vulkan_device{linkage, 0, "vulkan_device", Category::Renderer, Specialization::RuntimeList}; @@ -342,8 +339,6 @@ struct Values { Category::Renderer}; SwitchableSetting optimize_spirv_output{linkage, SpirvOptimizeMode::Never, - SpirvOptimizeMode::Never, - SpirvOptimizeMode::Always, "optimize_spirv_output", Category::Renderer}; SwitchableSetting use_asynchronous_gpu_emulation{ @@ -354,12 +349,10 @@ struct Values { #else AstcDecodeMode::Gpu, #endif - AstcDecodeMode::Cpu, - AstcDecodeMode::CpuAsynchronous, "accelerate_astc", Category::Renderer}; SwitchableSetting vsync_mode{ - linkage, VSyncMode::Fifo, VSyncMode::Immediate, VSyncMode::FifoRelaxed, + linkage, VSyncMode::Fifo, "use_vsync", Category::Renderer, Specialization::RuntimeList, true, true}; SwitchableSetting nvdec_emulation{linkage, NvdecEmulation::Gpu, @@ -372,8 +365,6 @@ struct Values { #else FullscreenMode::Exclusive, #endif - FullscreenMode::Borderless, - FullscreenMode::Exclusive, "fullscreen_mode", Category::Renderer, Specialization::Default, @@ -381,8 +372,6 @@ struct Values { true}; SwitchableSetting aspect_ratio{linkage, AspectRatio::R16_9, - AspectRatio::R16_9, - AspectRatio::Stretch, "aspect_ratio", Category::Renderer, Specialization::Default, @@ -430,8 +419,6 @@ struct Values { #else GpuAccuracy::High, #endif - GpuAccuracy::Normal, - GpuAccuracy::Extreme, "gpu_accuracy", Category::RendererAdvanced, Specialization::Default, @@ -442,8 +429,6 @@ struct Values { SwitchableSetting dma_accuracy{linkage, DmaAccuracy::Default, - DmaAccuracy::Default, - DmaAccuracy::Safe, "dma_accuracy", Category::RendererAdvanced, Specialization::Default, @@ -456,20 +441,14 @@ struct Values { #else AnisotropyMode::Automatic, #endif - AnisotropyMode::Automatic, - AnisotropyMode::X16, "max_anisotropy", Category::RendererAdvanced}; SwitchableSetting astc_recompression{linkage, AstcRecompression::Uncompressed, - AstcRecompression::Uncompressed, - AstcRecompression::Bc3, "astc_recompression", Category::RendererAdvanced}; SwitchableSetting vram_usage_mode{linkage, VramUsageMode::Conservative, - VramUsageMode::Conservative, - VramUsageMode::Aggressive, "vram_usage_mode", Category::RendererAdvanced}; SwitchableSetting skip_cpu_inner_invalidation{linkage, @@ -595,14 +574,10 @@ struct Values { // System SwitchableSetting language_index{linkage, Language::EnglishAmerican, - Language::Japanese, - Language::Serbian, "language_index", Category::System}; - SwitchableSetting region_index{linkage, Region::Usa, Region::Japan, - Region::Taiwan, "region_index", Category::System}; - SwitchableSetting time_zone_index{linkage, TimeZone::Auto, - TimeZone::Auto, TimeZone::Zulu, + SwitchableSetting region_index{linkage, Region::Usa, "region_index", Category::System}; + SwitchableSetting time_zone_index{linkage, TimeZone::Auto, "time_zone_index", Category::System}; // Measured in seconds since epoch SwitchableSetting custom_rtc_enabled{ diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index ebfa4ceb9e..0e5a08d845 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -10,6 +10,7 @@ #pragma once #include +#include #include #include #include "common/common_types.h" @@ -18,8 +19,10 @@ namespace Settings { template struct EnumMetadata { - static std::vector> Canonicalizations(); + static std::vector> Canonicalizations(); static u32 Index(); + static constexpr T GetFirst(); + static constexpr T GetLast(); }; #define PAIR_45(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_46(N, __VA_ARGS__)) @@ -69,138 +72,101 @@ struct EnumMetadata { #define PAIR_1(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_2(N, __VA_ARGS__)) #define PAIR(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_1(N, __VA_ARGS__)) -#define ENUM(NAME, ...) \ - enum class NAME : u32 { __VA_ARGS__ }; \ - template <> \ - inline std::vector> EnumMetadata::Canonicalizations() { \ - return {PAIR(NAME, __VA_ARGS__)}; \ - } \ - template <> \ - inline u32 EnumMetadata::Index() { \ - return __COUNTER__; \ +#define PP_HEAD(A, ...) A + +#define ENUM(NAME, ...) \ + enum class NAME : u32 { __VA_ARGS__ }; \ + template<> inline std::vector> EnumMetadata::Canonicalizations() { \ + return {PAIR(NAME, __VA_ARGS__)}; \ + } \ + template<> inline u32 EnumMetadata::Index() { \ + return __COUNTER__; \ + } \ + template<> inline constexpr NAME EnumMetadata::GetFirst() { \ + return NAME::PP_HEAD(__VA_ARGS__); \ + } \ + template<> inline constexpr NAME EnumMetadata::GetLast() { \ + return (std::vector>{PAIR(NAME, __VA_ARGS__)}).back().second; \ } // AudioEngine must be specified discretely due to having existing but slightly different // canonicalizations // TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id -enum class AudioEngine : u32 { - Auto, - Cubeb, - Sdl2, - Null, - Oboe, -}; - -template <> -inline std::vector> -EnumMetadata::Canonicalizations() { +enum class AudioEngine : u32 { Auto, Cubeb, Sdl2, Null, Oboe, }; +template<> +inline std::vector> EnumMetadata::Canonicalizations() { return { {"auto", AudioEngine::Auto}, {"cubeb", AudioEngine::Cubeb}, {"sdl2", AudioEngine::Sdl2}, {"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe}, }; } - -template <> +/// @brief This is just a sufficiently large number that is more than the number of other enums declared here +template<> inline u32 EnumMetadata::Index() { - // This is just a sufficiently large number that is more than the number of other enums declared - // here return 100; } +template<> +inline constexpr AudioEngine EnumMetadata::GetFirst() { + return AudioEngine::Auto; +} +template<> +inline constexpr AudioEngine EnumMetadata::GetLast() { + return AudioEngine::Oboe; +} ENUM(AudioMode, Mono, Stereo, Surround); +static_assert(EnumMetadata::GetFirst() == AudioMode::Mono); +static_assert(EnumMetadata::GetLast() == AudioMode::Surround); ENUM(Language, Japanese, EnglishAmerican, French, German, Italian, Spanish, Chinese, Korean, Dutch, Portuguese, Russian, Taiwanese, EnglishBritish, FrenchCanadian, SpanishLatin, ChineseSimplified, ChineseTraditional, PortugueseBrazilian, Serbian); - ENUM(Region, Japan, Usa, Europe, Australia, China, Korea, Taiwan); - ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt, Gb, GbEire, Gmt, - GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica, - Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt, - Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu); - + GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica, + Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt, + Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu); ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16); - ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous); - ENUM(AstcRecompression, Uncompressed, Bc1, Bc3); - ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed); - ENUM(VramUsageMode, Conservative, Aggressive); - ENUM(RendererBackend, OpenGL, Vulkan, Null); - ENUM(ShaderBackend, Glsl, Glasm, SpirV); - ENUM(GpuAccuracy, Normal, High, Extreme); - ENUM(DmaAccuracy, Default, Unsafe, Safe); - ENUM(CpuBackend, Dynarmic, Nce); - ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid); - ENUM(CpuClock, Boost, Fast) - ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb, Memory_10Gb, Memory_12Gb); - ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never); - ENUM(FullscreenMode, Borderless, Exclusive); - ENUM(NvdecEmulation, Off, Cpu, Gpu); - -ENUM(ResolutionSetup, - Res1_4X, - Res1_2X, - Res3_4X, - Res1X, - Res3_2X, - Res2X, - Res3X, - Res4X, - Res5X, - Res6X, - Res7X, - Res8X); - +ENUM(ResolutionSetup, Res1_4X, Res1_2X, Res3_4X, Res1X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X, Res8X); ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, MaxEnum); - ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); - ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); - ENUM(ConsoleMode, Handheld, Docked); - ENUM(AppletMode, HLE, LLE); - ENUM(SpirvOptimizeMode, Never, OnLoad, Always); - ENUM(GpuOverclock, Low, Medium, High) - ENUM(TemperatureUnits, Celsius, Fahrenheit) template -inline std::string CanonicalizeEnum(Type id) { +inline std::string_view CanonicalizeEnum(Type id) { const auto group = EnumMetadata::Canonicalizations(); - for (auto& [name, value] : group) { - if (value == id) { + for (auto& [name, value] : group) + if (value == id) return name; - } - } return "unknown"; } template inline Type ToEnum(const std::string& canonicalization) { const auto group = EnumMetadata::Canonicalizations(); - for (auto& [name, value] : group) { - if (name == canonicalization) { + for (auto& [name, value] : group) + if (name == canonicalization) return value; - } - } return {}; } } // namespace Settings diff --git a/src/common/settings_setting.h b/src/common/settings_setting.h index 0aba2e11c9..a7e6bb6168 100644 --- a/src/common/settings_setting.h +++ b/src/common/settings_setting.h @@ -72,10 +72,17 @@ public: u32 specialization_ = Specialization::Default, bool save_ = true, bool runtime_modifiable_ = false, BasicSetting* other_setting_ = nullptr) requires(ranged) - : BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_, - other_setting_), + : BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_, other_setting_), value{default_val}, default_value{default_val}, maximum{max_val}, minimum{min_val} {} + explicit Setting(Linkage& linkage, const Type& default_val, + const std::string& name, Category category_, + u32 specialization_ = Specialization::Default, bool save_ = true, + bool runtime_modifiable_ = false, BasicSetting* other_setting_ = nullptr) + requires(ranged && std::is_enum_v) + : BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_, other_setting_), + value{default_val}, default_value{default_val}, maximum{EnumMetadata::GetLast()}, minimum{EnumMetadata::GetFirst()} {} + /** * Returns a reference to the setting's value. * @@ -119,9 +126,6 @@ protected: return value_.has_value() ? std::to_string(*value_) : "none"; } else if constexpr (std::is_same_v) { return value_ ? "true" : "false"; - } else if constexpr (std::is_same_v) { - // Compatibility with old AudioEngine setting being a string - return CanonicalizeEnum(value_); } else if constexpr (std::is_floating_point_v) { return fmt::format("{:f}", value_); } else if constexpr (std::is_enum_v) { @@ -207,7 +211,7 @@ public: [[nodiscard]] std::string Canonicalize() const override final { if constexpr (std::is_enum_v) { - return CanonicalizeEnum(this->GetValue()); + return std::string{CanonicalizeEnum(this->GetValue())}; } else { return ToString(this->GetValue()); } @@ -288,41 +292,32 @@ public: * @param other_setting_ A second Setting to associate to this one in metadata */ template - explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name, - Category category_, u32 specialization_ = Specialization::Default, - bool save_ = true, bool runtime_modifiable_ = false, - typename std::enable_if::type other_setting_ = nullptr) - : Setting{ - linkage, default_val, name, category_, specialization_, - save_, runtime_modifiable_, other_setting_} { + explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name, Category category_, u32 specialization_ = Specialization::Default, bool save_ = true, bool runtime_modifiable_ = false, T* other_setting_ = nullptr) requires(!ranged) + : Setting{ linkage, default_val, name, category_, specialization_, save_, runtime_modifiable_, other_setting_} { linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); }); } virtual ~SwitchableSetting() = default; - /** - * Sets a default value, minimum value, maximum value, and label. - * - * @param linkage Setting registry - * @param default_val Initial value of the setting, and default value of the setting - * @param min_val Sets the minimum allowed value of the setting - * @param max_val Sets the maximum allowed value of the setting - * @param name Label for the setting - * @param category_ Category of the setting AKA INI group - * @param specialization_ Suggestion for how frontend implementations represent this in a config - * @param save_ Suggests that this should or should not be saved to a frontend config file - * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded - * @param other_setting_ A second Setting to associate to this one in metadata - */ + /// @brief Sets a default value, minimum value, maximum value, and label. + /// @param linkage Setting registry + /// @param default_val Initial value of the setting, and default value of the setting + /// @param min_val Sets the minimum allowed value of the setting + /// @param max_val Sets the maximum allowed value of the setting + /// @param name Label for the setting + /// @param category_ Category of the setting AKA INI group + /// @param specialization_ Suggestion for how frontend implementations represent this in a config + /// @param save_ Suggests that this should or should not be saved to a frontend config file + /// @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded + /// @param other_setting_ A second Setting to associate to this one in metadata template - explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val, - const Type& max_val, const std::string& name, Category category_, - u32 specialization_ = Specialization::Default, bool save_ = true, - bool runtime_modifiable_ = false, - typename std::enable_if::type other_setting_ = nullptr) - : Setting{linkage, default_val, min_val, - max_val, name, category_, - specialization_, save_, runtime_modifiable_, - other_setting_} { + explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val, const Type& max_val, const std::string& name, Category category_, u32 specialization_ = Specialization::Default, bool save_ = true, bool runtime_modifiable_ = false, T* other_setting_ = nullptr) requires(ranged) + : Setting{linkage, default_val, min_val, max_val, name, category_, specialization_, save_, runtime_modifiable_, other_setting_} { + linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); }); + } + + template + explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name, Category category_, u32 specialization_ = Specialization::Default, bool save_ = true, bool runtime_modifiable_ = false, T* other_setting_ = nullptr) requires(ranged) + : Setting{linkage, default_val, EnumMetadata::GetFirst(), EnumMetadata::GetLast(), name, category_, specialization_, save_, runtime_modifiable_, other_setting_} { linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); }); } diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index a7ebae91f8..af81ef552e 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp @@ -270,10 +270,8 @@ void ConfigureAudio::UpdateAudioDevices(int sink_index) { void ConfigureAudio::InitializeAudioSinkComboBox() { sink_combo_box->clear(); sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); - - for (const auto& id : AudioCore::Sink::GetSinkIDs()) { - sink_combo_box->addItem(QString::fromStdString(Settings::CanonicalizeEnum(id))); - } + for (const auto& id : AudioCore::Sink::GetSinkIDs()) + sink_combo_box->addItem(QString::fromStdString(std::string{Settings::CanonicalizeEnum(id)})); } void ConfigureAudio::RetranslateUI() { From ecb811ad04e75b221e0eee31f6bbd582620bd7c7 Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 29 Sep 2025 18:41:28 +0200 Subject: [PATCH 09/19] [qt] move addons row to rightmost side (#2610) This is because the rightmost row is "extended" to the rest of the table, and add-ons have long names, play time doesn't need that much space. Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2610 Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- src/yuzu/game_list.h | 4 ++-- src/yuzu/game_list_worker.cpp | 36 ++++++++++++----------------------- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 94e7b2dc42..cd71fb2139 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -58,11 +58,11 @@ class GameList : public QWidget { public: enum { COLUMN_NAME, - COLUMN_COMPATIBILITY, - COLUMN_ADD_ONS, COLUMN_FILE_TYPE, COLUMN_SIZE, COLUMN_PLAY_TIME, + COLUMN_ADD_ONS, + COLUMN_COMPATIBILITY, COLUMN_COUNT, // Number of columns }; diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 538c7ab822..2914c275a8 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -204,36 +204,24 @@ QList MakeGameListEntry(const std::string& path, const PlayTime::PlayTimeManager& play_time_manager, const FileSys::PatchManager& patch) { - const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); + auto const it = FindMatchingCompatibilityEntry(compatibility_list, program_id); + // The game list uses 99 as compatibility number for untested games + QString compatibility = it != compatibility_list.end() ? it->second.first : QStringLiteral("99"); - // The game list uses this as compatibility number for untested games - QString compatibility{QStringLiteral("99")}; - if (it != compatibility_list.end()) { - compatibility = it->second.first; - } + auto const file_type = loader.GetFileType(); + auto const file_type_string = QString::fromStdString(Loader::GetFileTypeString(file_type)); - const auto file_type = loader.GetFileType(); - const auto file_type_string = QString::fromStdString(Loader::GetFileTypeString(file_type)); - - QList list{ - new GameListItemPath(FormatGameName(path), icon, QString::fromStdString(name), - file_type_string, program_id), - new GameListItemCompat(compatibility), + QString patch_versions = GetGameListCachedObject(fmt::format("{:016X}", patch.GetTitleID()), "pv.txt", [&patch, &loader] { + return FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable()); + }); + return QList{ + new GameListItemPath(FormatGameName(path), icon, QString::fromStdString(name), file_type_string, program_id), new GameListItem(file_type_string), new GameListItemSize(size), new GameListItemPlayTime(play_time_manager.GetPlayTime(program_id)), + new GameListItem(patch_versions), + new GameListItemCompat(compatibility), }; - - QString patch_versions; - - patch_versions = GetGameListCachedObject( - fmt::format("{:016X}", patch.GetTitleID()), "pv.txt", [&patch, &loader] { - return FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable()); - }); - - list.insert(2, new GameListItem(patch_versions)); - - return list; } } // Anonymous namespace From 50ceb9a43a6dfe417a7760042e4710ce59553d94 Mon Sep 17 00:00:00 2001 From: Caio Oliveira Date: Mon, 29 Sep 2025 18:42:04 +0200 Subject: [PATCH 10/19] [.ci] install-msvc: fix installation on MSVC (#2611) * changed from Build Tools to Community (congrats Microsoft very cool) * add spining to show it didnt stopped installing Signed-off-by: Caio Oliveira Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2611 Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: Caio Oliveira Co-committed-by: Caio Oliveira --- .ci/windows/install-msvc.ps1 | 40 ++++++++++++++++++++++++++---------- docs/Deps.md | 6 +++--- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/.ci/windows/install-msvc.ps1 b/.ci/windows/install-msvc.ps1 index b88f727ed8..788b2848ad 100755 --- a/.ci/windows/install-msvc.ps1 +++ b/.ci/windows/install-msvc.ps1 @@ -10,7 +10,7 @@ if (-not ([bool](net session 2>$null))) { } $VSVer = "17" -$ExeFile = "vs_BuildTools.exe" +$ExeFile = "vs_community.exe" $Uri = "https://aka.ms/vs/$VSVer/release/$ExeFile" $Destination = "./$ExeFile" @@ -19,21 +19,39 @@ $WebClient = New-Object System.Net.WebClient $WebClient.DownloadFile($Uri, $Destination) Write-Host "Finished downloading $ExeFile" -$VSROOT = "C:/VSBuildTools/$VSVer" $Arguments = @( - "--installPath `"$VSROOT`"", # set custom installation path - "--quiet", # suppress UI - "--wait", # wait for installation to complete - "--norestart", # prevent automatic restart - "--add Microsoft.VisualStudio.Workload.VCTools", # add C++ build tools workload - "--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64", # add core x86/x64 C++ tools - "--add Microsoft.VisualStudio.Component.Windows10SDK.19041" # add specific Windows SDK + "--quiet", # Suppress installer UI + "--wait", # Wait for installation to complete + "--norestart", # Prevent automatic restart + "--force", # Force installation even if components are already installed + "--add Microsoft.VisualStudio.Workload.NativeDesktop", # Desktop development with C++ + "--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64", # Core C++ compiler/tools for x86/x64 + "--add Microsoft.VisualStudio.Component.Windows11SDK.26100",# Windows 11 SDK (26100) + "--add Microsoft.VisualStudio.Component.Windows10SDK.19041",# Windows 10 SDK (19041) + "--add Microsoft.VisualStudio.Component.VC.Llvm.Clang", # LLVM Clang compiler + "--add Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset", # LLVM Clang integration toolset + "--add Microsoft.VisualStudio.Component.Windows11SDK.22621",# Windows 11 SDK (22621) + "--add Microsoft.VisualStudio.Component.VC.CMake.Project", # CMake project support + "--add Microsoft.VisualStudio.ComponentGroup.VC.Tools.142.x86.x64", # VC++ 14.2 toolset + "--add Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang" # LLVM Clang for native desktop ) Write-Host "Installing Visual Studio Build Tools" -$InstallProcess = Start-Process -FilePath $Destination -NoNewWindow -PassThru -Wait -ArgumentList $Arguments -$ExitCode = $InstallProcess.ExitCode +$InstallProcess = Start-Process -FilePath $Destination -NoNewWindow -PassThru -ArgumentList $Arguments +# Spinner while installing +$Spinner = "|/-\" +$i = 0 +while (-not $InstallProcess.HasExited) { + Write-Host -NoNewline ("`rInstalling... " + $Spinner[$i % $Spinner.Length]) + Start-Sleep -Milliseconds 250 + $i++ +} + +# Clear spinner line +Write-Host "`rSetup completed! " + +$ExitCode = $InstallProcess.ExitCode if ($ExitCode -ne 0) { Write-Host "Error installing Visual Studio Build Tools (Error: $ExitCode)" Exit $ExitCode diff --git a/docs/Deps.md b/docs/Deps.md index cfc6f0365b..0e7b7cff62 100644 --- a/docs/Deps.md +++ b/docs/Deps.md @@ -4,8 +4,8 @@ To build Eden, you MUST have a C++ compiler. * On Linux, this is usually [GCC](https://gcc.gnu.org/) 11+ or [Clang](https://clang.llvm.org/) v14+ - GCC 12 also requires Clang 14+ * On Windows, this is either: - - **[MSVC](https://visualstudio.microsoft.com/downloads/)**, - * *A convenience script to install the **minimal** version (Visual Build Tools) is provided in `.ci/windows/install-msvc.ps1`* + - **[MSVC](https://visualstudio.microsoft.com/downloads/)** (you should select *Community* option), + * *A convenience script to install the Visual Community Studio 2022 with necessary tools is provided in `.ci/windows/install-msvc.ps1`* - clang-cl - can be downloaded from the MSVC installer, - or **[MSYS2](https://www.msys2.org)** * On macOS, this is Apple Clang @@ -211,4 +211,4 @@ Then install the libraries: `sudo pkg install qt6 boost glslang libzip library/l ## All Done -You may now return to the **[root build guide](Build.md)**. \ No newline at end of file +You may now return to the **[root build guide](Build.md)**. From 9f423a24b82e1b3223d8d4204455e637f86271fa Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 29 Sep 2025 18:42:28 +0200 Subject: [PATCH 11/19] [linux] fix aarch64 builds (again) + fix with slightly outdated qt (#2612) Fixes issues building on aarch64 linux with a slightly outdated system qt; also fixes linker selection process Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2612 Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- CMakeLists.txt | 5 ++--- src/dynarmic/tests/CMakeLists.txt | 4 +++- src/yuzu/configuration/shared_widget.h | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ef3c0bef6e..f5d7126f92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -895,13 +895,13 @@ if (MSVC AND CXX_CLANG) endif() if (YUZU_USE_FASTER_LD) + # fallback if everything fails (bfd) + set(LINKER bfd) # clang should always use lld find_program(LLD lld) - if (LLD) set(LINKER lld) endif() - # GNU appears to work better with mold # TODO: mold has been slow lately, see if better options exist (search for gold?) if (CXX_GCC) @@ -910,7 +910,6 @@ if (YUZU_USE_FASTER_LD) set(LINKER mold) endif() endif() - message(NOTICE "Selecting ${LINKER} as linker") add_link_options("-fuse-ld=${LINKER}") endif() diff --git a/src/dynarmic/tests/CMakeLists.txt b/src/dynarmic/tests/CMakeLists.txt index 4ace6c2afd..df90168a52 100644 --- a/src/dynarmic/tests/CMakeLists.txt +++ b/src/dynarmic/tests/CMakeLists.txt @@ -135,6 +135,8 @@ target_include_directories(dynarmic_tests PRIVATE . ../src) target_compile_options(dynarmic_tests PRIVATE ${DYNARMIC_CXX_FLAGS}) target_compile_definitions(dynarmic_tests PRIVATE FMT_USE_USER_DEFINED_LITERALS=1) -target_compile_options(dynarmic_tests PRIVATE -mavx2) +if ("x86_64" IN_LIST ARCHITECTURE) + target_compile_options(dynarmic_tests PRIVATE -mavx2) +endif() add_test(dynarmic_tests dynarmic_tests --durations yes) diff --git a/src/yuzu/configuration/shared_widget.h b/src/yuzu/configuration/shared_widget.h index 9e718098a3..dd5d5b7257 100644 --- a/src/yuzu/configuration/shared_widget.h +++ b/src/yuzu/configuration/shared_widget.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "qt_common/shared_translation.h" From 33f93ad003a47419d545a6bae018d3bb1c5ee3fb Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 29 Sep 2025 18:42:51 +0200 Subject: [PATCH 12/19] [macos, qt] workaround upstream rendering bug (#2616) See https://bugreports.qt.io/browse/QTBUG-138942 Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2616 Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- src/yuzu/Info.plist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/yuzu/Info.plist b/src/yuzu/Info.plist index 96096c84d1..0c43c834d4 100644 --- a/src/yuzu/Info.plist +++ b/src/yuzu/Info.plist @@ -49,5 +49,7 @@ SPDX-License-Identifier: GPL-2.0-or-later NSApplication NSHighResolutionCapable True + UIDesignRequiresCompatibility + From 324ace3cd635bac4230a04c4a769bed236ed1e8d Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 29 Sep 2025 18:43:13 +0200 Subject: [PATCH 13/19] [macos] associate .XCI/NSP file extensions (#2617) Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2617 Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- src/yuzu/Info.plist | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/yuzu/Info.plist b/src/yuzu/Info.plist index 0c43c834d4..773c4ee302 100644 --- a/src/yuzu/Info.plist +++ b/src/yuzu/Info.plist @@ -45,6 +45,26 @@ SPDX-License-Identifier: GPL-2.0-or-later NSHumanReadableCopyright + + LSApplicationCategoryType + public.app-category.games + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + nsp + xci + nro + + CFBundleTypeName + Switch File + CFBundleTypeRole + Viewer + LSHandlerRank + Default + + NSPrincipalClass NSApplication NSHighResolutionCapable From 85b5e650cc01a97956b3e4cc268f691a8fbc4cba Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 29 Sep 2025 19:41:01 +0200 Subject: [PATCH 14/19] [dist, docs] Clearer wording for settings, guidelines for new settings (#2570) Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2570 Reviewed-by: MaranBr Reviewed-by: crueter Co-authored-by: lizzie Co-committed-by: lizzie --- docs/Development.md | 22 +++ .../app/src/main/res/values/arrays.xml | 4 + .../app/src/main/res/values/strings.xml | 14 +- src/qt_common/shared_translation.cpp | 144 ++++++++---------- .../configure_profile_manager.cpp | 4 +- 5 files changed, 100 insertions(+), 88 deletions(-) diff --git a/docs/Development.md b/docs/Development.md index 1ed75a7c23..c223409243 100644 --- a/docs/Development.md +++ b/docs/Development.md @@ -36,6 +36,28 @@ Pull requests are only to be merged by core developers when properly tested and - Maintainers are permitted to change namespaces at will. - Commits within PRs are not required to be namespaced, but it is highly recommended. +## Adding new settings + +When adding new settings, use `tr("Setting:")` if the setting is meant to be a field, otherwise use `tr("Setting")` if the setting is meant to be a Yes/No or checkmark type of setting, see [this short style guide](https://learn.microsoft.com/en-us/style-guide/punctuation/colons#in-ui). + +- The majority of software must work with the default option selected for such setting. Unless the setting significantly degrades performance. +- Debug settings must never be turned on by default. +- Provide reasonable bounds (for example, a setting controlling the amount of VRAM should never be 0). +- The description of the setting must be short and concise, if the setting "does a lot of things" consider splitting the setting into multiple if possible. +- Try to avoid excessive/redundant explainations "recommended for most users and games" can just be "(recommended)". +- Try to not write "slow/fast" options unless it clearly degrades/increases performance for a given case, as most options may modify behaviour that result in different metrics accross different systems. If for example the option is an "accuracy" option, writing "High" is sufficient to imply "Slow". No need to write "High (Slow)". + +Some examples: +- "[...] negatively affecting image quality", "[...] degrading image quality": Same wording but with less filler. +- "[...] this may cause some glitches or crashes in some games", "[...] this may cause soft-crashes": Crashes implies there may be glitches (as crashes are technically a form of a fatal glitch). The entire sentence is structured as "may cause [...] on some games", which is redundant, because "may cause [...] in games" has the same semantic meaning ("may" is a chance that it will occur on "some" given set). +- "FIFO Relaxed is similar to FIFO [...]", "FIFO Relaxed [...]": The name already implies similarity. +- "[...] but may also reduce performance in some cases", "[...] but may degrade performance": Again, "some cases" and "may" implies there is a probability. +- "[...] it can [...] in some cases", "[...] it can [...]": Implied probability. + +Before adding a new setting, consider: +- Does the piece of code that the setting pertains to, make a significant difference if it's on/off? +- Can it be auto-detected? + # IDE setup ## VSCode diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 08ca53ad81..1b66c191d3 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -251,7 +251,9 @@ @string/scaling_filter_nearest_neighbor @string/scaling_filter_bilinear @string/scaling_filter_bicubic + @string/scaling_filter_spline1 @string/scaling_filter_gaussian + @string/scaling_filter_lanczos @string/scaling_filter_scale_force @string/scaling_filter_fsr @string/scaling_filter_area @@ -265,6 +267,8 @@ 4 5 6 + 7 + 8 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 48ea3ed99b..c05420a17f 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -865,12 +865,12 @@ Abort Continue System Archive Not Found - %s is missing. Please dump your system archives.\nContinuing emulation may result in crashes and bugs. + %s is missing. Please dump your system archives.\nContinuing emulation may result in crashes. A system archive Save/Load Error Fatal Error - A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs. - Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled. + A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes. + Turning off this setting will significantly degrade performance. It's recommended that you leave this setting enabled. Device RAM: %1$s\nRecommended: %2$s %1$s %2$s No bootable game present! @@ -941,12 +941,12 @@ Normal High - Extreme (Slow) + Extreme Default - Unsafe (fast) - Safe (stable) + Unsafe + Safe ASTC Decoding Method @@ -992,7 +992,9 @@ Nearest Neighbor Bilinear Bicubic + Spline-1 Gaussian + Lanczos ScaleForce AMD FidelityFX™ Super Resolution Area diff --git a/src/qt_common/shared_translation.cpp b/src/qt_common/shared_translation.cpp index 98a55e1fcf..8f5d929b74 100644 --- a/src/qt_common/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -62,22 +62,20 @@ std::unique_ptr InitializeTranslations(QObject* parent) Settings, use_multi_core, tr("Multicore CPU Emulation"), - tr("This option increases CPU emulation thread use from 1 to the Switch’s maximum of 4.\n" - "This is mainly a debug option and shouldn’t be disabled.")); + tr("This option increases CPU emulation thread use from 1 to the maximum of 4.\n" + "This is mainly a debug option and shouldn't be disabled.")); INSERT( Settings, memory_layout_mode, tr("Memory Layout"), - tr("Increases the amount of emulated RAM from the stock 4GB of the retail Switch to the " - "developer kit's 8/6GB.\nIt’s doesn’t improve stability or performance and is intended " - "to let big texture mods fit in emulated RAM.\nEnabling it will increase memory " - "use. It is not recommended to enable unless a specific game with a texture mod needs " - "it.")); + tr("Increases the amount of emulated RAM from 4GB of the board to the " + "devkit 8/6GB.\nDoesn't affect performance/stability but may allow HD texture " + "mods to load.")); INSERT(Settings, use_speed_limit, QString(), QString()); INSERT(Settings, speed_limit, tr("Limit Speed Percent"), - tr("Controls the game's maximum rendering speed, but it’s up to each game if it runs " + tr("Controls the game's maximum rendering speed, but it's up to each game if it runs " "faster or not.\n200% for a 30 FPS game is 60 FPS, and for a " "60 FPS game it will be 120 FPS.\nDisabling it means unlocking the framerate to the " "maximum your PC can reach.")); @@ -86,15 +84,13 @@ std::unique_ptr InitializeTranslations(QObject* parent) tr("Synchronize Core Speed"), tr("Synchronizes CPU core speed with the game's maximum rendering speed to boost FPS " "without affecting game speed (animations, physics, etc.).\n" - "Compatibility varies by game; many (especially older ones) may not respond well.\n" "Can help reduce stuttering at lower framerates.")); // Cpu INSERT(Settings, cpu_accuracy, tr("Accuracy:"), - tr("This setting controls the accuracy of the emulated CPU.\nDon't change this unless " - "you know what you are doing.")); + tr("Change the accuracy of the emulated CPU (for debugging only).")); INSERT(Settings, cpu_backend, tr("Backend:"), QString()); INSERT(Settings, use_fast_cpu_time, QString(), QString()); @@ -110,7 +106,7 @@ std::unique_ptr InitializeTranslations(QObject* parent) cpu_ticks, tr("Custom CPU Ticks"), tr("Set a custom value of CPU ticks. Higher values can increase performance, but may " - "also cause the game to freeze. A range of 77–21000 is recommended.")); + "cause deadlocks. A range of 77-21000 is recommended.")); INSERT(Settings, cpu_backend, tr("Backend:"), QString()); // Cpu Debug @@ -144,8 +140,7 @@ std::unique_ptr InitializeTranslations(QObject* parent) cpuopt_unsafe_fastmem_check, tr("Disable address space checks"), tr("This option improves speed by eliminating a safety check before every memory " - "read/write in guest.\nDisabling it may allow a game to read/write the emulator's " - "memory.")); + "operation.\nDisabling it may allow arbitrary code execution.")); INSERT( Settings, cpuopt_unsafe_ignore_global_monitor, @@ -159,36 +154,31 @@ std::unique_ptr InitializeTranslations(QObject* parent) Settings, renderer_backend, tr("API:"), - tr("Switches between the available graphics APIs.\nVulkan is recommended in most cases.")); + tr("Changes the output graphics API.\nVulkan is recommended.")); INSERT(Settings, vulkan_device, tr("Device:"), - tr("This setting selects the GPU to use with the Vulkan backend.")); + tr("This setting selects the GPU to use (Vulkan only).")); INSERT(Settings, shader_backend, tr("Shader Backend:"), - tr("The shader backend to use for the OpenGL renderer.\nGLSL is the fastest in " - "performance and the best in rendering accuracy.\n" - "GLASM is a deprecated NVIDIA-only backend that offers much better shader building " - "performance at the cost of FPS and rendering accuracy.\n" - "SPIR-V compiles the fastest, but yields poor results on most GPU drivers.")); + tr("The shader backend to use with OpenGL.\nGLSL is recommended.")); INSERT(Settings, resolution_setup, tr("Resolution:"), - tr("Forces the game to render at a different resolution.\nHigher resolutions require " - "much more VRAM and bandwidth.\n" - "Options lower than 1X can cause rendering issues.")); + tr("Forces to render at a different resolution.\n" + "Higher resolutions require more VRAM and bandwidth.\n" + "Options lower than 1X can cause artifacts.")); INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QString()); INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"), - tr("Determines how sharpened the image will look while using FSR’s dynamic contrast.")); + tr("Determines how sharpened the image will look using FSR's dynamic contrast.")); INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"), - tr("The anti-aliasing method to use.\nSMAA offers the best quality.\nFXAA has a " - "lower performance impact and can produce a better and more stable picture under " - "very low resolutions.")); + tr("The anti-aliasing method to use.\nSMAA offers the best quality.\nFXAA " + "can produce a more stable picture in lower resolutions.")); INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"), @@ -199,18 +189,17 @@ std::unique_ptr InitializeTranslations(QObject* parent) INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"), - tr("Stretches the game to fit the specified aspect ratio.\nSwitch games only support " - "16:9, so custom game mods are required to get other ratios.\nAlso controls the " + tr("Stretches the renderer to fit the specified aspect ratio.\nMost games only support " + "16:9, so modifications are required to get other ratios.\nAlso controls the " "aspect ratio of captured screenshots.")); INSERT(Settings, use_disk_shader_cache, - tr("Use disk pipeline cache"), + tr("Use persistent pipeline cache"), tr("Allows saving shaders to storage for faster loading on following game " - "boots.\nDisabling " - "it is only intended for debugging.")); + "boots.\nDisabling it is only intended for debugging.")); INSERT(Settings, optimize_spirv_output, - tr("Optimize SPIRV output shader"), + tr("Optimize SPIRV output"), tr("Runs an additional optimization pass over generated SPIRV shaders.\n" "Will increase time required for shader compilation.\nMay slightly improve " "performance.\nThis feature is experimental.")); @@ -229,37 +218,35 @@ std::unique_ptr InitializeTranslations(QObject* parent) accelerate_astc, tr("ASTC Decoding Method:"), tr("This option controls how ASTC textures should be decoded.\n" - "CPU: Use the CPU for decoding, slowest but safest method.\n" - "GPU: Use the GPU's compute shaders to decode ASTC textures, recommended for most " - "games and users.\n" - "CPU Asynchronously: Use the CPU to decode ASTC textures as they arrive. Completely " - "eliminates ASTC decoding\nstuttering at the cost of rendering issues while the " - "texture is being decoded.")); + "CPU: Use the CPU for decoding.\n" + "GPU: Use the GPU's compute shaders to decode ASTC textures (recommended).\n" + "CPU Asynchronously: Use the CPU to decode ASTC textures on demand. Eliminates" + "ASTC decoding\nstuttering but may present artifacts.")); INSERT( Settings, astc_recompression, tr("ASTC Recompression Method:"), - tr("Almost all desktop and laptop dedicated GPUs lack support for ASTC textures, forcing " - "the emulator to decompress to an intermediate format any card supports, RGBA8.\n" - "This option recompresses RGBA8 to either the BC1 or BC3 format, saving VRAM but " - "negatively affecting image quality.")); + tr("Most GPUs lack support for ASTC textures and must decompress to an" + "intermediate format: RGBA8.\n" + "BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format,\n" + " saving VRAM but degrading image quality.")); INSERT(Settings, vram_usage_mode, tr("VRAM Usage Mode:"), - tr("Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance.\nAggressive mode may severely impact the performance of other applications such as recording software.")); + tr("Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance.\nAggressive mode may impact performance of other applications such as recording software.")); INSERT(Settings, skip_cpu_inner_invalidation, tr("Skip CPU Inner Invalidation"), - tr("Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and " - "improving it's performance. This may cause glitches or crashes on some games.")); + tr("Skips certain cache invalidations during memory updates, reducing CPU usage and " + "improving latency. This may cause soft-crashes.")); INSERT( Settings, vsync_mode, tr("VSync Mode:"), tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen " - "refresh rate.\nFIFO Relaxed is similar to FIFO but allows tearing as it recovers from " - "a slow down.\nMailbox can have lower latency than FIFO and does not tear but may drop " - "frames.\nImmediate (no synchronization) just presents whatever is available and can " + "refresh rate.\nFIFO Relaxed allows tearing as it recovers from a slow down.\n" + "Mailbox can have lower latency than FIFO and does not tear but may drop " + "frames.\nImmediate (no synchronization) presents whatever is available and can " "exhibit tearing.")); INSERT(Settings, bg_red, QString(), QString()); INSERT(Settings, bg_green, QString(), QString()); @@ -267,7 +254,7 @@ std::unique_ptr InitializeTranslations(QObject* parent) // Renderer (Advanced Graphics) INSERT(Settings, sync_memory_operations, tr("Sync Memory Operations"), - tr("Ensures data consistency between compute and memory operations.\nThis option should fix issues in some games, but may also reduce performance in some cases.\nUnreal Engine 4 games often see the most significant changes thereof.")); + tr("Ensures data consistency between compute and memory operations.\nThis option fixes issues in games, but may degrade performance.\nUnreal Engine 4 games often see the most significant changes thereof.")); INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"), @@ -281,8 +268,7 @@ std::unique_ptr InitializeTranslations(QObject* parent) INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"), - tr("Controls the quality of texture rendering at oblique angles.\nIt’s a light setting " - "and safe to set at 16x on most GPUs.")); + tr("Controls the quality of texture rendering at oblique angles.\nSafe to set at 16x on most GPUs.")); INSERT(Settings, gpu_accuracy, tr("GPU Accuracy:"), @@ -292,11 +278,11 @@ std::unique_ptr InitializeTranslations(QObject* parent) INSERT(Settings, dma_accuracy, tr("DMA Accuracy:"), - tr("Controls the DMA precision accuracy. Safe precision can fix issues in some games, but it can also impact performance in some cases.\nIf unsure, leave this on Default.")); + tr("Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance.")); INSERT(Settings, use_asynchronous_shaders, - tr("Use asynchronous shader building (Hack)"), - tr("Enables asynchronous shader compilation, which may reduce shader stutter.")); + tr("Enable asynchronous shader compilation (Hack)"), + tr("May reduce shader stutter.")); INSERT(Settings, use_fast_gpu_time, QString(), QString()); INSERT(Settings, fast_gpu_time, @@ -314,8 +300,8 @@ std::unique_ptr InitializeTranslations(QObject* parent) Settings, enable_compute_pipelines, tr("Enable Compute Pipelines (Intel Vulkan Only)"), - tr("Enable compute pipelines, required by some games.\nThis setting only exists for Intel " - "proprietary drivers, and may crash if enabled.\nCompute pipelines are always enabled " + tr("Required by some games.\nThis setting only exists for Intel " + "proprietary drivers and may crash if enabled.\nCompute pipelines are always enabled " "on all other drivers.")); INSERT( Settings, @@ -343,12 +329,12 @@ std::unique_ptr InitializeTranslations(QObject* parent) INSERT(Settings, dyna_state, tr("Extended Dynamic State"), - tr("Controls the number of features that can be used in Extended Dynamic State.\nHigher numbers allow for more features and can increase performance, but may cause issues with some drivers and vendors.\nThe default value may vary depending on your system and hardware capabilities.\nThis value can be changed until stability and a better visual quality are achieved.")); + tr("Controls the number of features that can be used in Extended Dynamic State.\nHigher numbers allow for more features and can increase performance, but may cause issues.\nThe default value is per-system.")); INSERT(Settings, provoking_vertex, tr("Provoking Vertex"), - tr("Improves lighting and vertex handling in certain games.\n" + tr("Improves lighting and vertex handling in some games.\n" "Only Vulkan 1.0+ devices support this extension.")); INSERT(Settings, @@ -363,8 +349,8 @@ std::unique_ptr InitializeTranslations(QObject* parent) sample_shading_fraction, tr("Sample Shading"), tr("Allows the fragment shader to execute per sample in a multi-sampled fragment " - "instead once per fragment. Improves graphics quality at the cost of some performance.\n" - "Higher values improve quality more but also reduce performance to a greater extent.")); + "instead of once per fragment. Improves graphics quality at the cost of performance.\n" + "Higher values improve quality but degrade performance.")); // Renderer (Debug) @@ -372,31 +358,30 @@ std::unique_ptr InitializeTranslations(QObject* parent) INSERT(Settings, rng_seed, tr("RNG Seed"), - tr("Controls the seed of the random number generator.\nMainly used for speedrunning " - "purposes.")); + tr("Controls the seed of the random number generator.\nMainly used for speedrunning.")); INSERT(Settings, rng_seed_enabled, QString(), QString()); - INSERT(Settings, device_name, tr("Device Name"), tr("The name of the emulated Switch.")); + INSERT(Settings, device_name, tr("Device Name"), tr("The name of the console.")); INSERT(Settings, custom_rtc, tr("Custom RTC Date:"), - tr("This option allows to change the emulated clock of the Switch.\n" + tr("This option allows to change the clock of the console.\n" "Can be used to manipulate time in games.")); INSERT(Settings, custom_rtc_enabled, QString(), QString()); INSERT(Settings, custom_rtc_offset, QStringLiteral(" "), - QStringLiteral("The number of seconds from the current unix time")); + tr("The number of seconds from the current unix time")); INSERT(Settings, language_index, tr("Language:"), - tr("Note: this can be overridden when region setting is auto-select")); - INSERT(Settings, region_index, tr("Region:"), tr("The region of the emulated Switch.")); - INSERT(Settings, time_zone_index, tr("Time Zone:"), tr("The time zone of the emulated Switch.")); + tr("This option can be overridden when region setting is auto-select")); + INSERT(Settings, region_index, tr("Region:"), tr("The region of the console.")); + INSERT(Settings, time_zone_index, tr("Time Zone:"), tr("The time zone of the console.")); INSERT(Settings, sound_index, tr("Sound Output Mode:"), QString()); INSERT(Settings, use_docked_mode, tr("Console Mode:"), - tr("Selects if the console is emulated in Docked or Handheld mode.\nGames will change " + tr("Selects if the console is in Docked or Handheld mode.\nGames will change " "their resolution, details and supported controllers and depending on this setting.\n" "Setting to Handheld can help improve performance for low end systems.")); INSERT(Settings, current_user, QString(), QString()); @@ -418,27 +403,26 @@ std::unique_ptr InitializeTranslations(QObject* parent) // Ui General INSERT(UISettings, select_user_on_boot, - tr("Prompt for user on game boot"), - tr("Ask to select a user profile on each boot, useful if multiple people use Eden on " - "the same PC.")); + tr("Prompt for user profile on boot"), + tr("Useful if multiple people use the same PC.")); INSERT(UISettings, pause_when_in_background, - tr("Pause emulation when in background"), - tr("This setting pauses Eden when focusing other windows.")); + tr("Pause when not in focus"), + tr("Pauses emulation when focusing on other windows.")); INSERT(UISettings, confirm_before_stopping, tr("Confirm before stopping emulation"), - tr("This setting overrides game prompts asking to confirm stopping the game.\nEnabling " + tr("Overrides prompts asking to confirm stopping the emulation.\nEnabling " "it bypasses such prompts and directly exits the emulation.")); INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"), - tr("This setting hides the mouse after 2.5s of inactivity.")); + tr("Hides the mouse after 2.5s of inactivity.")); INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"), - tr("Forcibly disables the use of the controller applet by guests.\nWhen a guest " - "attempts to open the controller applet, it is immediately closed.")); + tr("Forcibly disables the use of the controller applet in emulated programs.\n" + "When a program attempts to open the controller applet, it is immediately closed.")); INSERT(UISettings, check_for_updates, tr("Check for updates"), diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index f5de08a676..df74738df4 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp @@ -373,13 +373,13 @@ bool ConfigureProfileManager::LoadAvatarData() { const auto romfs = nca->GetRomFS(); if (!romfs) { QMessageBox::warning(this, tr("Error loading archive"), - tr("Archive does not contain romfs. It is probably corrupt.")); + tr("Could not locate RomFS. Your file or decryption keys may be corrupted.")); return false; } const auto extracted = FileSys::ExtractRomFS(romfs); if (!extracted) { QMessageBox::warning(this, tr("Error extracting archive"), - tr("Archive could not be extracted. It is probably corrupt.")); + tr("Could not extract RomFS. Your file or decryption keys may be corrupted.")); return false; } const auto chara_dir = extracted->GetSubdirectory("chara"); From 4a1e0e27ab0ec138e11e1567885b0d0ffd125705 Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 25 Sep 2025 07:44:47 +0000 Subject: [PATCH 15/19] [vk, ogl] VK_QCOM ZTC, Bspline, Mitchell filter weights Signed-off-by: lizzie --- .../app/src/main/res/values/arrays.xml | 6 ++ .../app/src/main/res/values/strings.xml | 3 + src/common/settings_enums.h | 2 +- src/qt_common/shared_translation.cpp | 3 + src/qt_common/shared_translation.h | 3 + src/video_core/host_shaders/CMakeLists.txt | 3 + .../host_shaders/present_bicubic.frag | 69 +++++++------------ .../host_shaders/present_bspline.frag | 35 ++++++++++ .../host_shaders/present_mitchell.frag | 35 ++++++++++ .../host_shaders/present_zero_tangent.frag | 35 ++++++++++ .../renderer_opengl/gl_blit_screen.cpp | 10 +++ .../renderer_opengl/present/filters.cpp | 18 +++++ .../renderer_opengl/present/filters.h | 3 + .../renderer_vulkan/present/filters.cpp | 30 ++++++-- .../renderer_vulkan/present/filters.h | 2 +- .../renderer_vulkan/present/util.cpp | 13 +++- src/video_core/renderer_vulkan/present/util.h | 2 +- .../renderer_vulkan/vk_blit_screen.cpp | 12 +++- 18 files changed, 226 insertions(+), 58 deletions(-) create mode 100644 src/video_core/host_shaders/present_bspline.frag create mode 100644 src/video_core/host_shaders/present_mitchell.frag create mode 100644 src/video_core/host_shaders/present_zero_tangent.frag diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 1b66c191d3..fa9a02aead 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -251,6 +251,9 @@ @string/scaling_filter_nearest_neighbor @string/scaling_filter_bilinear @string/scaling_filter_bicubic + @string/scaling_filter_zero_tangent + @string/scaling_filter_bspline + @string/scaling_filter_mitchell @string/scaling_filter_spline1 @string/scaling_filter_gaussian @string/scaling_filter_lanczos @@ -269,6 +272,9 @@ 6 7 8 + 9 + 10 + 11 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index c05420a17f..bbd89d4b2a 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -998,6 +998,9 @@ ScaleForce AMD FidelityFX™ Super Resolution Area + Zero-Tangent-Cardinal + B-Spline + Mitchell-Netravali None diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 0e5a08d845..2a3116bcdf 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -143,7 +143,7 @@ ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never); ENUM(FullscreenMode, Borderless, Exclusive); ENUM(NvdecEmulation, Off, Cpu, Gpu); ENUM(ResolutionSetup, Res1_4X, Res1_2X, Res3_4X, Res1X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X, Res8X); -ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, MaxEnum); +ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, ZeroTangent, BSpline, Mitchell, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, MaxEnum); ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); ENUM(ConsoleMode, Handheld, Docked); diff --git a/src/qt_common/shared_translation.cpp b/src/qt_common/shared_translation.cpp index 8f5d929b74..ac65e94303 100644 --- a/src/qt_common/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -554,6 +554,9 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")), PAIR(ScalingFilter, Bilinear, tr("Bilinear")), PAIR(ScalingFilter, Bicubic, tr("Bicubic")), + PAIR(ScalingFilter, ZeroTangent, tr("Zero-Tangent-Cardinal")), + PAIR(ScalingFilter, BSpline, tr("B-Spline")), + PAIR(ScalingFilter, Mitchell, tr("Mitchell-Netravali")), PAIR(ScalingFilter, Spline1, tr("Spline-1")), PAIR(ScalingFilter, Gaussian, tr("Gaussian")), PAIR(ScalingFilter, Lanczos, tr("Lanczos")), diff --git a/src/qt_common/shared_translation.h b/src/qt_common/shared_translation.h index c9216c2daa..801d27c416 100644 --- a/src/qt_common/shared_translation.h +++ b/src/qt_common/shared_translation.h @@ -38,6 +38,9 @@ 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::ZeroTangent, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Zero-Tangent-Cardinal"))}, + {Settings::ScalingFilter::BSpline, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "B-Spline"))}, + {Settings::ScalingFilter::Mitchell, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Mitchell-Netravali"))}, {Settings::ScalingFilter::Spline1, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Spline-1"))}, {Settings::ScalingFilter::Gaussian, diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index c14b44a45a..4bbeb1e33f 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -44,6 +44,9 @@ set(SHADER_FILES pitch_unswizzle.comp present_area.frag present_bicubic.frag + present_zero_tangent.frag + present_bspline.frag + present_mitchell.frag present_gaussian.frag present_lanczos.frag present_spline1.frag diff --git a/src/video_core/host_shaders/present_bicubic.frag b/src/video_core/host_shaders/present_bicubic.frag index a9d9d40a38..a03b330165 100644 --- a/src/video_core/host_shaders/present_bicubic.frag +++ b/src/video_core/host_shaders/present_bicubic.frag @@ -1,56 +1,35 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later - #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 cubic(float v) { - vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; - vec4 s = n * n * n; - float x = s.x; - float y = s.y - 4.0 * s.x; - float z = s.z - 4.0 * s.y + 6.0 * s.x; - float w = 6.0 - x - y - z; - return vec4(x, y, z, w) * (1.0 / 6.0); +vec4 cubic(float x) { + float x2 = x * x; + float x3 = x2 * x; + return vec4(1.0, x, x2, x3) * transpose(mat4x4( + 0.0, 2.0, 0.0, 0.0, + -1.0, 0.0, 1.0, 0.0, + 2.0, -5.0, 4.0, -1.0, + -1.0, 3.0, -3.0, 1.0 + ) * (1.0 / 2.0)); } - -vec4 textureBicubic( sampler2D textureSampler, vec2 texCoords ) { - - vec2 texSize = textureSize(textureSampler, 0); - vec2 invTexSize = 1.0 / texSize; - - texCoords = texCoords * texSize - 0.5; - - vec2 fxy = fract(texCoords); - texCoords -= fxy; - - vec4 xcubic = cubic(fxy.x); - vec4 ycubic = cubic(fxy.y); - - vec4 c = texCoords.xxyy + vec2(-0.5, +1.5).xyxy; - - vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); - vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; - - offset *= invTexSize.xxyy; - - vec4 sample0 = texture(textureSampler, offset.xz); - vec4 sample1 = texture(textureSampler, offset.yz); - vec4 sample2 = texture(textureSampler, offset.xw); - vec4 sample3 = texture(textureSampler, offset.yw); - - float sx = s.x / (s.x + s.y); - float sy = s.z / (s.z + s.w); - - return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); +vec4 textureBicubic(sampler2D samp, vec2 uv) { + vec2 tex_size = vec2(textureSize(samp, 0)); + vec2 cc_tex = uv * tex_size - 0.5f; + vec2 fex = cc_tex - floor(cc_tex); + vec4 xcubic = cubic(fex.x); + vec4 ycubic = cubic(fex.y); + vec4 c = floor(cc_tex).xxyy + vec2(-0.5f, 1.5f).xyxy; + vec4 z = vec4(xcubic.yw, ycubic.yw); + vec4 s = vec4(xcubic.xz, ycubic.xz) + z; + vec4 offset = (c + z / s) * (1.0f / tex_size).xxyy; + vec2 n = vec2(s.x / (s.x + s.y), s.z / (s.z + s.w)); + return mix( + mix(texture(samp, offset.yw), texture(samp, offset.xw), n.x), + mix(texture(samp, offset.yz), texture(samp, offset.xz), n.x), + n.y); } - void main() { color = textureBicubic(color_texture, frag_tex_coord); } diff --git a/src/video_core/host_shaders/present_bspline.frag b/src/video_core/host_shaders/present_bspline.frag new file mode 100644 index 0000000000..92fd6f646b --- /dev/null +++ b/src/video_core/host_shaders/present_bspline.frag @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#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 cubic(float x) { + float x2 = x * x; + float x3 = x2 * x; + return vec4(1.0, x, x2, x3) * transpose(mat4x4( + 1.0, 4.0, 1.0, 0.0, + -3.0, 0.0, 3.0, 0.0, + 3.0, -6.0, 3.0, 0.0, + -1.0, 3.0, -3.0, 1.0 + ) * (1.0 / 6.0)); +} +vec4 textureBicubic(sampler2D samp, vec2 uv) { + vec2 tex_size = vec2(textureSize(samp, 0)); + vec2 cc_tex = uv * tex_size - 0.5f; + vec2 fex = cc_tex - floor(cc_tex); + vec4 xcubic = cubic(fex.x); + vec4 ycubic = cubic(fex.y); + vec4 c = floor(cc_tex).xxyy + vec2(-0.5f, 1.5f).xyxy; + vec4 z = vec4(xcubic.yw, ycubic.yw); + vec4 s = vec4(xcubic.xz, ycubic.xz) + z; + vec4 offset = (c + z / s) * (1.0f / tex_size).xxyy; + vec2 n = vec2(s.x / (s.x + s.y), s.z / (s.z + s.w)); + return mix( + mix(texture(samp, offset.yw), texture(samp, offset.xw), n.x), + mix(texture(samp, offset.yz), texture(samp, offset.xz), n.x), + n.y); +} +void main() { + color = textureBicubic(color_texture, frag_tex_coord); +} diff --git a/src/video_core/host_shaders/present_mitchell.frag b/src/video_core/host_shaders/present_mitchell.frag new file mode 100644 index 0000000000..7a3efa79a1 --- /dev/null +++ b/src/video_core/host_shaders/present_mitchell.frag @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#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 cubic(float x) { + float x2 = x * x; + float x3 = x2 * x; + return vec4(1.0, x, x2, x3) * transpose(mat4x4( + 1.0, 16.0, 1.0, 0.0, + -9.0, 0.0, 9.0, 0.0, + 15.0, -36.0, 27.0, -6.0, + -7.0, 21.0, -21.0, 7.0 + ) * (1.0 / 18.0)); +} +vec4 textureBicubic(sampler2D samp, vec2 uv) { + vec2 tex_size = vec2(textureSize(samp, 0)); + vec2 cc_tex = uv * tex_size - 0.5f; + vec2 fex = cc_tex - floor(cc_tex); + vec4 xcubic = cubic(fex.x); + vec4 ycubic = cubic(fex.y); + vec4 c = floor(cc_tex).xxyy + vec2(-0.5f, 1.5f).xyxy; + vec4 z = vec4(xcubic.yw, ycubic.yw); + vec4 s = vec4(xcubic.xz, ycubic.xz) + z; + vec4 offset = (c + z / s) * (1.0f / tex_size).xxyy; + vec2 n = vec2(s.x / (s.x + s.y), s.z / (s.z + s.w)); + return mix( + mix(texture(samp, offset.yw), texture(samp, offset.xw), n.x), + mix(texture(samp, offset.yz), texture(samp, offset.xz), n.x), + n.y); +} +void main() { + color = textureBicubic(color_texture, frag_tex_coord); +} diff --git a/src/video_core/host_shaders/present_zero_tangent.frag b/src/video_core/host_shaders/present_zero_tangent.frag new file mode 100644 index 0000000000..ccaa4d5f5d --- /dev/null +++ b/src/video_core/host_shaders/present_zero_tangent.frag @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#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 cubic(float x) { + float x2 = x * x; + float x3 = x2 * x; + return vec4(1.0, x, x2, x3) * transpose(mat4x4( + 0.0, 2.0, 0.0, 0.0, + -2.0, 0.0, 2.0, 0.0, + 4.0, -4.0, 2.0, -2.0, + -2.0, 2.0, -2.0, 1.0 + ) * (1.0 / 2.0)); +} +vec4 textureBicubic(sampler2D samp, vec2 uv) { + vec2 tex_size = vec2(textureSize(samp, 0)); + vec2 cc_tex = uv * tex_size - 0.5f; + vec2 fex = cc_tex - floor(cc_tex); + vec4 xcubic = cubic(fex.x); + vec4 ycubic = cubic(fex.y); + vec4 c = floor(cc_tex).xxyy + vec2(-0.5f, 1.5f).xyxy; + vec4 z = vec4(xcubic.yw, ycubic.yw); + vec4 s = vec4(xcubic.xz, ycubic.xz) + z; + vec4 offset = (c + z / s) * (1.0f / tex_size).xxyy; + vec2 n = vec2(s.x / (s.x + s.y), s.z / (s.z + s.w)); + return mix( + mix(texture(samp, offset.yw), texture(samp, offset.xw), n.x), + mix(texture(samp, offset.yz), texture(samp, offset.xz), n.x), + n.y); +} +void main() { + color = textureBicubic(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 65670fcad8..1d3eb16e63 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.cpp +++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp @@ -8,6 +8,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/settings.h" +#include "common/settings_enums.h" #include "video_core/present.h" #include "video_core/renderer_opengl/gl_blit_screen.h" #include "video_core/renderer_opengl/gl_state_tracker.h" @@ -86,6 +87,15 @@ void BlitScreen::CreateWindowAdapt() { case Settings::ScalingFilter::Bicubic: window_adapt = MakeBicubic(device); break; + case Settings::ScalingFilter::ZeroTangent: + window_adapt = MakeZeroTangent(device); + break; + case Settings::ScalingFilter::BSpline: + window_adapt = MakeBSpline(device); + break; + case Settings::ScalingFilter::Mitchell: + window_adapt = MakeMitchell(device); + break; case Settings::ScalingFilter::Gaussian: window_adapt = MakeGaussian(device); break; diff --git a/src/video_core/renderer_opengl/present/filters.cpp b/src/video_core/renderer_opengl/present/filters.cpp index 5e3d1538c6..da8e11a864 100644 --- a/src/video_core/renderer_opengl/present/filters.cpp +++ b/src/video_core/renderer_opengl/present/filters.cpp @@ -14,6 +14,9 @@ #include "video_core/host_shaders/present_gaussian_frag.h" #include "video_core/host_shaders/present_lanczos_frag.h" #include "video_core/host_shaders/present_spline1_frag.h" +#include "video_core/host_shaders/present_mitchell_frag_spv.h" +#include "video_core/host_shaders/present_bspline_frag_spv.h" +#include "video_core/host_shaders/present_zero_tangent_frag_spv.h" #include "video_core/renderer_opengl/present/filters.h" #include "video_core/renderer_opengl/present/util.h" @@ -39,6 +42,21 @@ std::unique_ptr MakeBicubic(const Device& device) { HostShaders::PRESENT_BICUBIC_FRAG); } +std::unique_ptr MakeMitchell(const Device& device) { + return std::make_unique(device, CreateBilinearSampler(), + HostShaders::PRESENT_MITCHELL_FRAG); +} + +std::unique_ptr MakeZeroTangent(const Device& device) { + return std::make_unique(device, CreateBilinearSampler(), + HostShaders::PRESENT_ZERO_TANGENT_FRAG); +} + +std::unique_ptr MakeBSpline(const Device& device) { + return std::make_unique(device, CreateBilinearSampler(), + HostShaders::PRESENT_BSPLINE_FRAG); +} + std::unique_ptr MakeGaussian(const Device& device) { return std::make_unique(device, CreateBilinearSampler(), HostShaders::PRESENT_GAUSSIAN_FRAG); diff --git a/src/video_core/renderer_opengl/present/filters.h b/src/video_core/renderer_opengl/present/filters.h index 7b38ac56bc..0fba4408dc 100644 --- a/src/video_core/renderer_opengl/present/filters.h +++ b/src/video_core/renderer_opengl/present/filters.h @@ -17,6 +17,9 @@ namespace OpenGL { std::unique_ptr MakeNearestNeighbor(const Device& device); std::unique_ptr MakeBilinear(const Device& device); std::unique_ptr MakeBicubic(const Device& device); +std::unique_ptr MakeZeroTangent(const Device& device); +std::unique_ptr MakeMitchell(const Device& device); +std::unique_ptr MakeBSpline(const Device& device); std::unique_ptr MakeGaussian(const Device& device); std::unique_ptr MakeSpline1(const Device& device); std::unique_ptr MakeLanczos(const Device& device); diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp index e0f2b26f84..632f1bf16e 100644 --- a/src/video_core/renderer_vulkan/present/filters.cpp +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -7,6 +7,7 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/assert.h" #include "common/common_types.h" #include "video_core/host_shaders/present_area_frag_spv.h" @@ -14,6 +15,9 @@ #include "video_core/host_shaders/present_gaussian_frag_spv.h" #include "video_core/host_shaders/present_lanczos_frag_spv.h" #include "video_core/host_shaders/present_spline1_frag_spv.h" +#include "video_core/host_shaders/present_mitchell_frag_spv.h" +#include "video_core/host_shaders/present_bspline_frag_spv.h" +#include "video_core/host_shaders/present_zero_tangent_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" @@ -52,13 +56,27 @@ std::unique_ptr MakeSpline1(const Device& device, VkFormat fram BuildShader(device, PRESENT_SPLINE1_FRAG_SPV)); } -std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format) { +std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format, VkCubicFilterWeightsQCOM qcom_weights) { // No need for handrolled shader -- if the VK impl can do it for us ;) - if (device.IsExtFilterCubicSupported()) - return std::make_unique(device, frame_format, CreateCubicSampler(device), - BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); - return std::make_unique(device, frame_format, CreateBilinearSampler(device), - BuildShader(device, PRESENT_BICUBIC_FRAG_SPV)); + if (device.IsExtFilterCubicSupported()) { + return std::make_unique(device, frame_format, CreateCubicSampler(device, + qcom_weights), BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); + } else { + return std::make_unique(device, frame_format, CreateBilinearSampler(device), [&](){ + switch (qcom_weights) { + case VK_CUBIC_FILTER_WEIGHTS_CATMULL_ROM_QCOM: + return BuildShader(device, PRESENT_BICUBIC_FRAG_SPV); + case VK_CUBIC_FILTER_WEIGHTS_ZERO_TANGENT_CARDINAL_QCOM: + return BuildShader(device, PRESENT_ZERO_TANGENT_FRAG_SPV); + case VK_CUBIC_FILTER_WEIGHTS_B_SPLINE_QCOM: + return BuildShader(device, PRESENT_BSPLINE_FRAG_SPV); + case VK_CUBIC_FILTER_WEIGHTS_MITCHELL_NETRAVALI_QCOM: + return BuildShader(device, PRESENT_MITCHELL_FRAG_SPV); + default: + UNREACHABLE(); + } + }()); + } } std::unique_ptr MakeGaussian(const Device& device, VkFormat frame_format) { diff --git a/src/video_core/renderer_vulkan/present/filters.h b/src/video_core/renderer_vulkan/present/filters.h index 015bffc8a5..2f02003235 100644 --- a/src/video_core/renderer_vulkan/present/filters.h +++ b/src/video_core/renderer_vulkan/present/filters.h @@ -17,7 +17,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 MakeBicubic(const Device& device, VkFormat frame_format, VkCubicFilterWeightsQCOM qcom_weights); 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); diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp index 0b1a89eec0..29a1c34976 100644 --- a/src/video_core/renderer_vulkan/present/util.cpp +++ b/src/video_core/renderer_vulkan/present/util.cpp @@ -624,8 +624,8 @@ vk::Sampler CreateNearestNeighborSampler(const Device& device) { return device.GetLogical().CreateSampler(ci_nn); } -vk::Sampler CreateCubicSampler(const Device& device) { - const VkSamplerCreateInfo ci_nn{ +vk::Sampler CreateCubicSampler(const Device& device, VkCubicFilterWeightsQCOM qcom_weights) { + VkSamplerCreateInfo ci_nn{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .pNext = nullptr, .flags = 0, @@ -645,7 +645,14 @@ vk::Sampler CreateCubicSampler(const Device& device) { .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, .unnormalizedCoordinates = VK_FALSE, }; - + const VkSamplerCubicWeightsCreateInfoQCOM ci_qcom_nn{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_CUBIC_WEIGHTS_CREATE_INFO_QCOM, + .pNext = nullptr, + .cubicWeights = qcom_weights + }; + // If not specified, assume Catmull-Rom + if (qcom_weights != VK_CUBIC_FILTER_WEIGHTS_CATMULL_ROM_QCOM) + ci_nn.pNext = &ci_qcom_nn; return device.GetLogical().CreateSampler(ci_nn); } diff --git a/src/video_core/renderer_vulkan/present/util.h b/src/video_core/renderer_vulkan/present/util.h index 11810352df..0325858d13 100644 --- a/src/video_core/renderer_vulkan/present/util.h +++ b/src/video_core/renderer_vulkan/present/util.h @@ -57,7 +57,7 @@ VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector VkDescriptorSet set, u32 binding); vk::Sampler CreateBilinearSampler(const Device& device); vk::Sampler CreateNearestNeighborSampler(const Device& device); -vk::Sampler CreateCubicSampler(const Device& device); +vk::Sampler CreateCubicSampler(const Device& device, std::optional qcom_weights); void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer, VkExtent2D extent); diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index b720bcded3..14a914c0b3 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -7,6 +7,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include "video_core/framebuffer_config.h" #include "video_core/present.h" #include "video_core/renderer_vulkan/present/filters.h" @@ -41,7 +42,16 @@ void BlitScreen::SetWindowAdaptPass() { window_adapt = MakeNearestNeighbor(device, swapchain_view_format); break; case Settings::ScalingFilter::Bicubic: - window_adapt = MakeBicubic(device, swapchain_view_format); + window_adapt = MakeBicubic(device, swapchain_view_format, VK_CUBIC_FILTER_WEIGHTS_CATMULL_ROM_QCOM); + break; + case Settings::ScalingFilter::ZeroTangent: + window_adapt = MakeBicubic(device, swapchain_view_format, VK_CUBIC_FILTER_WEIGHTS_ZERO_TANGENT_CARDINAL_QCOM); + break; + case Settings::ScalingFilter::BSpline: + window_adapt = MakeBicubic(device, swapchain_view_format, VK_CUBIC_FILTER_WEIGHTS_B_SPLINE_QCOM); + break; + case Settings::ScalingFilter::Mitchell: + window_adapt = MakeBicubic(device, swapchain_view_format, VK_CUBIC_FILTER_WEIGHTS_MITCHELL_NETRAVALI_QCOM); break; case Settings::ScalingFilter::Spline1: window_adapt = MakeSpline1(device, swapchain_view_format); From 1290f7dca3e436bdc64015d494044d5faaaa9d8c Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 25 Sep 2025 07:48:50 +0000 Subject: [PATCH 16/19] fix ogl Signed-off-by: lizzie --- src/video_core/renderer_opengl/present/filters.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_opengl/present/filters.cpp b/src/video_core/renderer_opengl/present/filters.cpp index da8e11a864..4aaec3fa0b 100644 --- a/src/video_core/renderer_opengl/present/filters.cpp +++ b/src/video_core/renderer_opengl/present/filters.cpp @@ -14,9 +14,9 @@ #include "video_core/host_shaders/present_gaussian_frag.h" #include "video_core/host_shaders/present_lanczos_frag.h" #include "video_core/host_shaders/present_spline1_frag.h" -#include "video_core/host_shaders/present_mitchell_frag_spv.h" -#include "video_core/host_shaders/present_bspline_frag_spv.h" -#include "video_core/host_shaders/present_zero_tangent_frag_spv.h" +#include "video_core/host_shaders/present_mitchell_frag.h" +#include "video_core/host_shaders/present_bspline_frag.h" +#include "video_core/host_shaders/present_zero_tangent_frag.h" #include "video_core/renderer_opengl/present/filters.h" #include "video_core/renderer_opengl/present/util.h" From d8d1ccbe629009a9297e11c1c3f9c5050ba31d45 Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 25 Sep 2025 07:56:23 +0000 Subject: [PATCH 17/19] fix vk Signed-off-by: lizzie --- src/video_core/renderer_vulkan/present/util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/present/util.h b/src/video_core/renderer_vulkan/present/util.h index 0325858d13..38cc6203c5 100644 --- a/src/video_core/renderer_vulkan/present/util.h +++ b/src/video_core/renderer_vulkan/present/util.h @@ -57,7 +57,7 @@ VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector VkDescriptorSet set, u32 binding); vk::Sampler CreateBilinearSampler(const Device& device); vk::Sampler CreateNearestNeighborSampler(const Device& device); -vk::Sampler CreateCubicSampler(const Device& device, std::optional qcom_weights); +vk::Sampler CreateCubicSampler(const Device& device, VkCubicFilterWeightsQCOM qcom_weights); void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer, VkExtent2D extent); From e8d45d294a733975633a79969b176b0d945d7445 Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 25 Sep 2025 08:02:54 +0000 Subject: [PATCH 18/19] better logic Signed-off-by: lizzie --- src/video_core/renderer_vulkan/present/filters.cpp | 4 +++- src/video_core/vulkan_common/vulkan_device.h | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp index 632f1bf16e..e3c457b44c 100644 --- a/src/video_core/renderer_vulkan/present/filters.cpp +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -7,6 +7,7 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include "common/assert.h" #include "common/common_types.h" @@ -58,7 +59,8 @@ std::unique_ptr MakeSpline1(const Device& device, VkFormat fram std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format, VkCubicFilterWeightsQCOM qcom_weights) { // No need for handrolled shader -- if the VK impl can do it for us ;) - if (device.IsExtFilterCubicSupported()) { + // Catmull-Rom is default bicubic for all implementations... + if (device.IsExtFilterCubicSupported() && (device.IsQcomFilterCubicWeightsSupported() || qcom_weights == VK_CUBIC_FILTER_WEIGHTS_CATMULL_ROM_QCOM)) { return std::make_unique(device, frame_format, CreateCubicSampler(device, qcom_weights), BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); } else { diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index bd54144480..cb13f28523 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -89,7 +89,8 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \ EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle) \ EXTENSION(EXT, DESCRIPTOR_INDEXING, descriptor_indexing) \ - EXTENSION(EXT, FILTER_CUBIC, filter_cubic) + EXTENSION(EXT, FILTER_CUBIC, filter_cubic) \ + EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights) // Define extensions which must be supported. #define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \ @@ -558,6 +559,11 @@ public: return extensions.filter_cubic; } + /// Returns true if the device supports VK_QCOM_filter_cubic_weights + bool IsQcomFilterCubicWeightsSupported() const { + return extensions.filter_cubic_weights; + } + /// Returns true if the device supports VK_EXT_line_rasterization. bool IsExtLineRasterizationSupported() const { return extensions.line_rasterization; From 36fae754374198a8019a27fec16d4fef4d799edd Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 25 Sep 2025 17:25:04 +0000 Subject: [PATCH 19/19] add MMPX filter Signed-off-by: lizzie --- docs/User.md | 18 +++ .../app/src/main/res/values/arrays.xml | 2 + .../app/src/main/res/values/strings.xml | 5 +- src/common/settings_enums.h | 2 +- src/qt_common/shared_translation.cpp | 5 +- src/qt_common/shared_translation.h | 5 +- src/video_core/host_shaders/CMakeLists.txt | 1 + .../host_shaders/present_bicubic.frag | 2 + .../host_shaders/present_bspline.frag | 4 +- .../host_shaders/present_mitchell.frag | 4 +- src/video_core/host_shaders/present_mmpx.frag | 131 ++++++++++++++++++ .../host_shaders/present_zero_tangent.frag | 4 +- .../renderer_opengl/gl_blit_screen.cpp | 3 + .../renderer_opengl/present/filters.cpp | 6 + .../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 + 18 files changed, 190 insertions(+), 13 deletions(-) create mode 100644 src/video_core/host_shaders/present_mmpx.frag diff --git a/docs/User.md b/docs/User.md index cfc81063f8..b8da9bc8d5 100644 --- a/docs/User.md +++ b/docs/User.md @@ -9,3 +9,21 @@ Eden will store configuration in the following directories: - **Linux, macOS, FreeBSD, Solaris, OpenBSD**: `$XDG_DATA_HOME`, `$XDG_CACHE_HOME`, `$XDG_CONFIG_HOME`. If a `user` directory is present in the current working directory, that will override all global configuration directories and the emulator will use that instead. + +# Enhancements + +## Filters + +Various graphical filters exist - each of them aimed at a specific target/image quality preset. + +- **Nearest**: Provides no filtering - useful for debugging. +- **Bilinear**: Provides the hardware default filtering of the Tegra X1. +- **Bicubic**: Provides a bicubic interpolation using a Catmull-Rom (or hardware-accelerated) implementation. +- **Zero-Tangent, B-Spline, Mitchell**: Provides bicubic interpolation using the respective matrix weights. They're normally not hardware accelerated unless the device supports the `VK_QCOM_filter_cubic_weights` extension. The matrix weights are those matching [the specification itself](https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#VkSamplerCubicWeightsCreateInfoQCOM). +- **Spline-1**: Bicubic interpolation (similar to Mitchell) but with a faster texel fetch method. Generally less blurry than bicubic. +- **Gaussian**: Whole-area blur, an applied gaussian blur is done to the entire frame. +- **Lanczos**: An implementation using `a = 3` (49 texel fetches). Provides sharper edges but blurrier artifacts. +- **ScaleForce**: Experimental texture upscale method, see [ScaleFish](https://github.com/BreadFish64/ScaleFish). +- **FSR**: Uses AMD FidelityFX Super Resolution to enhance image quality. +- **Area**: Area interpolation (high kernel count). +- **MMPX**: Nearest-neighbour filter aimed at providing higher pixel-art quality. diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index fa9a02aead..86fae9ba3c 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -260,6 +260,7 @@ @string/scaling_filter_scale_force @string/scaling_filter_fsr @string/scaling_filter_area + @string/scaling_filter_mmpx @@ -275,6 +276,7 @@ 9 10 11 + 12 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index bbd89d4b2a..ab8375b325 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -998,9 +998,10 @@ ScaleForce AMD FidelityFX™ Super Resolution Area - Zero-Tangent-Cardinal + Zero-Tangent B-Spline - Mitchell-Netravali + Mitchell + MMPX None diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 2a3116bcdf..8022d8216e 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -143,7 +143,7 @@ ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never); ENUM(FullscreenMode, Borderless, Exclusive); ENUM(NvdecEmulation, Off, Cpu, Gpu); ENUM(ResolutionSetup, Res1_4X, Res1_2X, Res3_4X, Res1X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X, Res8X); -ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, ZeroTangent, BSpline, Mitchell, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, MaxEnum); +ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, ZeroTangent, BSpline, Mitchell, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, Mmpx, MaxEnum); ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); ENUM(ConsoleMode, Handheld, Docked); diff --git a/src/qt_common/shared_translation.cpp b/src/qt_common/shared_translation.cpp index ac65e94303..2cac28a937 100644 --- a/src/qt_common/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -554,15 +554,16 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")), PAIR(ScalingFilter, Bilinear, tr("Bilinear")), PAIR(ScalingFilter, Bicubic, tr("Bicubic")), - PAIR(ScalingFilter, ZeroTangent, tr("Zero-Tangent-Cardinal")), + PAIR(ScalingFilter, ZeroTangent, tr("Zero-Tangent")), PAIR(ScalingFilter, BSpline, tr("B-Spline")), - PAIR(ScalingFilter, Mitchell, tr("Mitchell-Netravali")), + PAIR(ScalingFilter, Mitchell, tr("Mitchell")), PAIR(ScalingFilter, Spline1, tr("Spline-1")), PAIR(ScalingFilter, Gaussian, tr("Gaussian")), PAIR(ScalingFilter, Lanczos, tr("Lanczos")), PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")), PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™️ Super Resolution")), PAIR(ScalingFilter, Area, tr("Area")), + PAIR(ScalingFilter, Mmpx, tr("MMPX")), }}); translations->insert({Settings::EnumMetadata::Index(), { diff --git a/src/qt_common/shared_translation.h b/src/qt_common/shared_translation.h index 801d27c416..a25887bb87 100644 --- a/src/qt_common/shared_translation.h +++ b/src/qt_common/shared_translation.h @@ -38,9 +38,9 @@ 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::ZeroTangent, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Zero-Tangent-Cardinal"))}, + {Settings::ScalingFilter::ZeroTangent, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Zero-Tangent"))}, {Settings::ScalingFilter::BSpline, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "B-Spline"))}, - {Settings::ScalingFilter::Mitchell, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Mitchell-Netravali"))}, + {Settings::ScalingFilter::Mitchell, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Mitchell"))}, {Settings::ScalingFilter::Spline1, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Spline-1"))}, {Settings::ScalingFilter::Gaussian, @@ -51,6 +51,7 @@ static const std::map scaling_filter_texts_map QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))}, {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, {Settings::ScalingFilter::Area, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Area"))}, + {Settings::ScalingFilter::Mmpx, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "MMPX"))}, }; static const std::map use_docked_mode_texts_map = { diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 4bbeb1e33f..9f7b9edd5a 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -50,6 +50,7 @@ set(SHADER_FILES present_gaussian.frag present_lanczos.frag present_spline1.frag + present_mmpx.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_bicubic.frag b/src/video_core/host_shaders/present_bicubic.frag index a03b330165..5347fd2ef7 100644 --- a/src/video_core/host_shaders/present_bicubic.frag +++ b/src/video_core/host_shaders/present_bicubic.frag @@ -1,3 +1,5 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #version 460 core diff --git a/src/video_core/host_shaders/present_bspline.frag b/src/video_core/host_shaders/present_bspline.frag index 92fd6f646b..f229de6030 100644 --- a/src/video_core/host_shaders/present_bspline.frag +++ b/src/video_core/host_shaders/present_bspline.frag @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2021 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 #version 460 core layout (location = 0) in vec2 frag_tex_coord; layout (location = 0) out vec4 color; diff --git a/src/video_core/host_shaders/present_mitchell.frag b/src/video_core/host_shaders/present_mitchell.frag index 7a3efa79a1..4ca65cd6f0 100644 --- a/src/video_core/host_shaders/present_mitchell.frag +++ b/src/video_core/host_shaders/present_mitchell.frag @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2021 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 #version 460 core layout (location = 0) in vec2 frag_tex_coord; layout (location = 0) out vec4 color; diff --git a/src/video_core/host_shaders/present_mmpx.frag b/src/video_core/host_shaders/present_mmpx.frag new file mode 100644 index 0000000000..6c2c05a63a --- /dev/null +++ b/src/video_core/host_shaders/present_mmpx.frag @@ -0,0 +1,131 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#version 460 core +layout(location = 0) in vec2 tex_coord; +layout(location = 0) out vec4 frag_color; +layout(binding = 0) uniform sampler2D tex; + +#define src(x, y) texture(tex, coord + vec2(x, y) * 1.0 / source_size) + +float luma(vec4 col) { + return dot(col.rgb, vec3(0.2126, 0.7152, 0.0722)) * (1.0 - col.a); +} + +bool same(vec4 B, vec4 A0) { + return all(equal(B, A0)); +} + +bool notsame(vec4 B, vec4 A0) { + return any(notEqual(B, A0)); +} + +bool all_eq2(vec4 B, vec4 A0, vec4 A1) { + return (same(B,A0) && same(B,A1)); +} + +bool all_eq3(vec4 B, vec4 A0, vec4 A1, vec4 A2) { + return (same(B,A0) && same(B,A1) && same(B,A2)); +} + +bool all_eq4(vec4 B, vec4 A0, vec4 A1, vec4 A2, vec4 A3) { + return (same(B,A0) && same(B,A1) && same(B,A2) && same(B,A3)); +} + +bool any_eq3(vec4 B, vec4 A0, vec4 A1, vec4 A2) { + return (same(B,A0) || same(B,A1) || same(B,A2)); +} + +bool none_eq2(vec4 B, vec4 A0, vec4 A1) { + return (notsame(B,A0) && notsame(B,A1)); +} + +bool none_eq4(vec4 B, vec4 A0, vec4 A1, vec4 A2, vec4 A3) { + return (notsame(B,A0) && notsame(B,A1) && notsame(B,A2) && notsame(B,A3)); +} + +void main() +{ + vec2 source_size = vec2(textureSize(tex, 0)); + vec2 pos = fract(tex_coord * source_size) - vec2(0.5, 0.5); + vec2 coord = tex_coord - pos / source_size; + + vec4 E = src(0.0,0.0); + + vec4 A = src(-1.0,-1.0); + vec4 B = src(0.0,-1.0); + vec4 C = src(1.0,-1.0); + + vec4 D = src(-1.0,0.0); + vec4 F = src(1.0,0.0); + + vec4 G = src(-1.0,1.0); + vec4 H = src(0.0,1.0); + vec4 I = src(1.0,1.0); + + vec4 J = E; + vec4 K = E; + vec4 L = E; + vec4 M = E; + + frag_color = E; + + if(same(E,A) && same(E,B) && same(E,C) && same(E,D) && same(E,F) && same(E,G) && same(E,H) && same(E,I)) return; + + vec4 P = src(0.0,2.0); + vec4 Q = src(-2.0,0.0); + vec4 R = src(2.0,0.0); + vec4 S = src(0.0,2.0); + + float Bl = luma(B); + float Dl = luma(D); + float El = luma(E); + float Fl = luma(F); + float Hl = luma(H); + + if (((same(D,B) && notsame(D,H) && notsame(D,F))) && ((El>=Dl) || same(E,A)) && any_eq3(E,A,C,G) && ((El=Bl) || same(E,C)) && any_eq3(E,A,C,I) && ((El=Hl) || same(E,G)) && any_eq3(E,A,G,I) && ((El=Fl) || same(E,I)) && any_eq3(E,C,G,I) && ((El MakeArea(const Device& device) { HostShaders::PRESENT_AREA_FRAG); } +std::unique_ptr MakeMmpx(const Device& device) { + return std::make_unique(device, CreateNearestNeighborSampler(), + HostShaders::PRESENT_MMPX_FRAG); +} + } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/filters.h b/src/video_core/renderer_opengl/present/filters.h index 0fba4408dc..187d0f1298 100644 --- a/src/video_core/renderer_opengl/present/filters.h +++ b/src/video_core/renderer_opengl/present/filters.h @@ -25,5 +25,6 @@ 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); +std::unique_ptr MakeMmpx(const Device& device); } // namespace OpenGL diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp index e3c457b44c..0a28ea6349 100644 --- a/src/video_core/renderer_vulkan/present/filters.cpp +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -19,6 +19,7 @@ #include "video_core/host_shaders/present_mitchell_frag_spv.h" #include "video_core/host_shaders/present_bspline_frag_spv.h" #include "video_core/host_shaders/present_zero_tangent_frag_spv.h" +#include "video_core/host_shaders/present_mmpx_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" @@ -101,4 +102,9 @@ std::unique_ptr MakeArea(const Device& device, VkFormat frame_f BuildShader(device, PRESENT_AREA_FRAG_SPV)); } +std::unique_ptr MakeMmpx(const Device& device, VkFormat frame_format) { + return std::make_unique(device, frame_format, CreateNearestNeighborSampler(device), + BuildShader(device, PRESENT_MMPX_FRAG_SPV)); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/filters.h b/src/video_core/renderer_vulkan/present/filters.h index 2f02003235..afc3ba29a0 100644 --- a/src/video_core/renderer_vulkan/present/filters.h +++ b/src/video_core/renderer_vulkan/present/filters.h @@ -23,5 +23,6 @@ std::unique_ptr MakeGaussian(const Device& device, VkFormat fra 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); +std::unique_ptr MakeMmpx(const Device& device, VkFormat frame_format); } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 14a914c0b3..0f54dd5ade 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -68,6 +68,9 @@ void BlitScreen::SetWindowAdaptPass() { case Settings::ScalingFilter::Area: window_adapt = MakeArea(device, swapchain_view_format); break; + case Settings::ScalingFilter::Mmpx: + window_adapt = MakeMmpx(device, swapchain_view_format); + break; case Settings::ScalingFilter::Fsr: case Settings::ScalingFilter::Bilinear: default: