diff --git a/src/citra_qt/configuration/configure_enhancements.ui b/src/citra_qt/configuration/configure_enhancements.ui
index 83b85ba9a..ef77b2bc6 100644
--- a/src/citra_qt/configuration/configure_enhancements.ui
+++ b/src/citra_qt/configuration/configure_enhancements.ui
@@ -184,11 +184,6 @@
Bicubic
- -
-
- Nearest Neighbor
-
-
-
ScaleForce
@@ -199,11 +194,11 @@
xBRZ
- -
-
- MMPX
-
-
+ -
+
+ MMPX
+
+
diff --git a/src/citra_qt/configuration/configure_graphics.cpp b/src/citra_qt/configuration/configure_graphics.cpp
index 6424e3af1..463b9c848 100644
--- a/src/citra_qt/configuration/configure_graphics.cpp
+++ b/src/citra_qt/configuration/configure_graphics.cpp
@@ -71,11 +71,17 @@ void ConfigureGraphics::SetConfiguration() {
!Settings::values.physical_device.UsingGlobal());
ConfigurationShared::SetPerGameSetting(ui->physical_device_combo,
&Settings::values.physical_device);
+ ConfigurationShared::SetPerGameSetting(ui->texture_sampling_combobox,
+ &Settings::values.texture_sampling);
+ ConfigurationShared::SetHighlight(ui->widget_texture_sampling,
+ !Settings::values.texture_sampling.UsingGlobal());
} else {
ui->graphics_api_combo->setCurrentIndex(
static_cast(Settings::values.graphics_api.GetValue()));
ui->physical_device_combo->setCurrentIndex(
static_cast(Settings::values.physical_device.GetValue()));
+ ui->texture_sampling_combobox->setCurrentIndex(
+ static_cast(Settings::values.texture_sampling.GetValue()));
}
ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader.GetValue());
@@ -106,6 +112,8 @@ void ConfigureGraphics::ApplyConfiguration() {
use_hw_shader);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.shaders_accurate_mul,
ui->toggle_accurate_mul, shaders_accurate_mul);
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.texture_sampling,
+ ui->texture_sampling_combobox);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache,
ui->toggle_disk_shader_cache, use_disk_shader_cache);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync_new, ui->toggle_vsync_new,
@@ -132,6 +140,7 @@ void ConfigureGraphics::SetupPerGameUI() {
Settings::values.use_vsync_new.UsingGlobal());
ui->toggle_async_shaders->setEnabled(
Settings::values.async_shader_compilation.UsingGlobal());
+ ui->widget_texture_sampling->setEnabled(Settings::values.texture_sampling.UsingGlobal());
ui->toggle_async_present->setEnabled(Settings::values.async_presentation.UsingGlobal());
ui->graphics_api_combo->setEnabled(Settings::values.graphics_api.UsingGlobal());
ui->physical_device_combo->setEnabled(Settings::values.physical_device.UsingGlobal());
@@ -148,6 +157,10 @@ void ConfigureGraphics::SetupPerGameUI() {
ui->physical_device_combo, ui->physical_device_group,
static_cast(Settings::values.physical_device.GetValue(true)));
+ ConfigurationShared::SetColoredComboBox(
+ ui->texture_sampling_combobox, ui->widget_texture_sampling,
+ static_cast(Settings::values.texture_sampling.GetValue(true)));
+
ConfigurationShared::SetColoredTristate(ui->toggle_hw_shader, Settings::values.use_hw_shader,
use_hw_shader);
ConfigurationShared::SetColoredTristate(
diff --git a/src/citra_qt/configuration/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui
index bd77e6d6b..be0764f4a 100644
--- a/src/citra_qt/configuration/configure_graphics.ui
+++ b/src/citra_qt/configuration/configure_graphics.ui
@@ -212,6 +212,53 @@
Advanced
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ <html><head/><body><p>Overrides the sampling filter used by games. This can be useful in certain cases with poorly behaved games when upscaling. If unsure set this to Game Controlled</p></body></html>
+
+
+ Texture Sampling
+
+
+
+ -
+
+
-
+
+ Game Controlled
+
+
+ -
+
+ Nearest Neighbor
+
+
+ -
+
+ Linear
+
+
+
+
+
+
+
-
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index d0b9af658..486340a3f 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -46,8 +46,6 @@ std::string_view GetTextureFilterName(TextureFilter filter) {
return "Anime4K";
case TextureFilter::Bicubic:
return "Bicubic";
- case TextureFilter::NearestNeighbor:
- return "NearestNeighbor";
case TextureFilter::ScaleForce:
return "ScaleForce";
case TextureFilter::xBRZ:
@@ -59,6 +57,19 @@ std::string_view GetTextureFilterName(TextureFilter filter) {
}
}
+std::string_view GetTextureSamplingName(TextureSampling sampling) {
+ switch (sampling) {
+ case TextureSampling::GameControlled:
+ return "GameControlled";
+ case TextureSampling::NearestNeighbor:
+ return "NearestNeighbor";
+ case TextureSampling::Linear:
+ return "Linear";
+ default:
+ return "Invalid";
+ }
+}
+
} // Anonymous namespace
Values values = {};
@@ -87,6 +98,8 @@ void LogSettings() {
log_setting("Renderer_PostProcessingShader", values.pp_shader_name.GetValue());
log_setting("Renderer_FilterMode", values.filter_mode.GetValue());
log_setting("Renderer_TextureFilter", GetTextureFilterName(values.texture_filter.GetValue()));
+ log_setting("Renderer_TextureSampling",
+ GetTextureSamplingName(values.texture_sampling.GetValue()));
log_setting("Stereoscopy_Render3d", values.render_3d.GetValue());
log_setting("Stereoscopy_Factor3d", values.factor_3d.GetValue());
log_setting("Stereoscopy_MonoRenderOption", values.mono_render_option.GetValue());
@@ -175,6 +188,7 @@ void RestoreGlobalState(bool is_powered_on) {
values.resolution_factor.SetGlobal(true);
values.frame_limit.SetGlobal(true);
values.texture_filter.SetGlobal(true);
+ values.texture_sampling.SetGlobal(true);
values.layout_option.SetGlobal(true);
values.swap_screen.SetGlobal(true);
values.upright_screen.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index aa4b4908e..e37e4eef3 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -72,10 +72,15 @@ enum class TextureFilter : u32 {
None = 0,
Anime4K = 1,
Bicubic = 2,
- NearestNeighbor = 3,
- ScaleForce = 4,
- xBRZ = 5,
- MMPX = 6
+ ScaleForce = 3,
+ xBRZ = 4,
+ MMPX = 5,
+};
+
+enum class TextureSampling : u32 {
+ GameControlled = 0,
+ NearestNeighbor = 1,
+ Linear = 2,
};
namespace NativeButton {
@@ -451,6 +456,8 @@ struct Values {
SwitchableSetting resolution_factor{1, 0, 10, "resolution_factor"};
SwitchableSetting frame_limit{100, 0, 1000, "frame_limit"};
SwitchableSetting texture_filter{TextureFilter::None, "texture_filter"};
+ SwitchableSetting texture_sampling{TextureSampling::GameControlled,
+ "texture_sampling"};
SwitchableSetting layout_option{LayoutOption::Default, "layout_option"};
SwitchableSetting swap_screen{false, "swap_screen"};
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index 8acc71c03..27b466876 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -7,7 +7,6 @@ set(SHADER_FILES
format_reinterpreter/rgba4_to_rgb5a1.frag
format_reinterpreter/vulkan_d24s8_to_rgba8.comp
texture_filtering/bicubic.frag
- texture_filtering/nearest_neighbor.frag
texture_filtering/refine.frag
texture_filtering/scale_force.frag
texture_filtering/xbrz_freescale.frag
diff --git a/src/video_core/host_shaders/texture_filtering/nearest_neighbor.frag b/src/video_core/host_shaders/texture_filtering/nearest_neighbor.frag
deleted file mode 100644
index e2db96f91..000000000
--- a/src/video_core/host_shaders/texture_filtering/nearest_neighbor.frag
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2023 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-//? #version 430 core
-precision mediump float;
-
-layout(location = 0) in vec2 tex_coord;
-layout(location = 0) out vec4 frag_color;
-
-layout(binding = 2) uniform sampler2D input_texture;
-
-void main() {
- frag_color = texture(input_texture, tex_coord);
-}
diff --git a/src/video_core/rasterizer_cache/rasterizer_cache.h b/src/video_core/rasterizer_cache/rasterizer_cache.h
index efb3eeb58..92f1613a1 100644
--- a/src/video_core/rasterizer_cache/rasterizer_cache.h
+++ b/src/video_core/rasterizer_cache/rasterizer_cache.h
@@ -361,10 +361,25 @@ typename T::Sampler& RasterizerCache::GetSampler(SamplerId sampler_id) {
template
typename T::Sampler& RasterizerCache::GetSampler(
const Pica::TexturingRegs::TextureConfig& config) {
+ using TextureFilter = Pica::TexturingRegs::TextureConfig::TextureFilter;
+
+ const auto get_filter = [](TextureFilter filter) {
+ switch (Settings::values.texture_sampling.GetValue()) {
+ case Settings::TextureSampling::GameControlled:
+ return filter;
+ case Settings::TextureSampling::NearestNeighbor:
+ return TextureFilter::Nearest;
+ case Settings::TextureSampling::Linear:
+ return TextureFilter::Linear;
+ default:
+ return filter;
+ }
+ };
+
const SamplerParams params = {
- .mag_filter = config.mag_filter,
- .min_filter = config.min_filter,
- .mip_filter = config.mip_filter,
+ .mag_filter = get_filter(config.mag_filter),
+ .min_filter = get_filter(config.min_filter),
+ .mip_filter = get_filter(config.mip_filter),
.wrap_s = config.wrap_s,
.wrap_t = config.wrap_t,
.border_color = config.border_color.raw,
diff --git a/src/video_core/renderer_opengl/gl_blit_helper.cpp b/src/video_core/renderer_opengl/gl_blit_helper.cpp
index 830d28e32..73484894b 100644
--- a/src/video_core/renderer_opengl/gl_blit_helper.cpp
+++ b/src/video_core/renderer_opengl/gl_blit_helper.cpp
@@ -15,7 +15,6 @@
#include "video_core/host_shaders/full_screen_triangle_vert.h"
#include "video_core/host_shaders/texture_filtering/bicubic_frag.h"
#include "video_core/host_shaders/texture_filtering/mmpx_frag.h"
-#include "video_core/host_shaders/texture_filtering/nearest_neighbor_frag.h"
#include "video_core/host_shaders/texture_filtering/refine_frag.h"
#include "video_core/host_shaders/texture_filtering/scale_force_frag.h"
#include "video_core/host_shaders/texture_filtering/x_gradient_frag.h"
@@ -58,7 +57,6 @@ BlitHelper::BlitHelper(const Driver& driver_)
: driver{driver_}, linear_sampler{CreateSampler(GL_LINEAR)},
nearest_sampler{CreateSampler(GL_NEAREST)}, bicubic_program{CreateProgram(
HostShaders::BICUBIC_FRAG)},
- nearest_program{CreateProgram(HostShaders::NEAREST_NEIGHBOR_FRAG)},
scale_force_program{CreateProgram(HostShaders::SCALE_FORCE_FRAG)},
xbrz_program{CreateProgram(HostShaders::XBRZ_FREESCALE_FRAG)},
mmpx_program{CreateProgram(HostShaders::MMPX_FRAG)}, gradient_x_program{CreateProgram(
@@ -175,9 +173,6 @@ bool BlitHelper::Filter(Surface& surface, const VideoCore::TextureBlit& blit) {
case TextureFilter::Bicubic:
FilterBicubic(surface, blit);
break;
- case TextureFilter::NearestNeighbor:
- FilterNearest(surface, blit);
- break;
case TextureFilter::ScaleForce:
FilterScaleForce(surface, blit);
break;
@@ -257,14 +252,6 @@ void BlitHelper::FilterBicubic(Surface& surface, const VideoCore::TextureBlit& b
Draw(bicubic_program, surface.Handle(), draw_fbo.handle, blit.dst_level, blit.dst_rect);
}
-void BlitHelper::FilterNearest(Surface& surface, const VideoCore::TextureBlit& blit) {
- const OpenGLState prev_state = OpenGLState::GetCurState();
- SCOPE_EXIT({ prev_state.Apply(); });
- state.texture_units[2].texture_2d = surface.Handle(0);
- SetParams(nearest_program, surface.RealExtent(false), blit.src_rect);
- Draw(nearest_program, surface.Handle(), draw_fbo.handle, blit.dst_level, blit.dst_rect);
-}
-
void BlitHelper::FilterScaleForce(Surface& surface, const VideoCore::TextureBlit& blit) {
const OpenGLState prev_state = OpenGLState::GetCurState();
SCOPE_EXIT({ prev_state.Apply(); });
diff --git a/src/video_core/renderer_opengl/gl_blit_helper.h b/src/video_core/renderer_opengl/gl_blit_helper.h
index e6ba9cce5..054fdef84 100644
--- a/src/video_core/renderer_opengl/gl_blit_helper.h
+++ b/src/video_core/renderer_opengl/gl_blit_helper.h
@@ -33,20 +33,13 @@ public:
private:
void FilterAnime4K(Surface& surface, const VideoCore::TextureBlit& blit);
-
void FilterBicubic(Surface& surface, const VideoCore::TextureBlit& blit);
-
- void FilterNearest(Surface& surface, const VideoCore::TextureBlit& blit);
-
void FilterScaleForce(Surface& surface, const VideoCore::TextureBlit& blit);
-
void FilterXbrz(Surface& surface, const VideoCore::TextureBlit& blit);
-
void FilterMMPX(Surface& surface, const VideoCore::TextureBlit& blit);
void SetParams(OGLProgram& program, const VideoCore::Extent& src_extent,
Common::Rectangle src_rect);
-
void Draw(OGLProgram& program, GLuint dst_tex, GLuint dst_fbo, u32 dst_level,
Common::Rectangle dst_rect);
@@ -59,7 +52,6 @@ private:
OGLSampler nearest_sampler;
OGLProgram bicubic_program;
- OGLProgram nearest_program;
OGLProgram scale_force_program;
OGLProgram xbrz_program;
OGLProgram mmpx_program;
diff --git a/src/video_core/renderer_vulkan/vk_blit_helper.h b/src/video_core/renderer_vulkan/vk_blit_helper.h
index b7735fcc9..8060a225b 100644
--- a/src/video_core/renderer_vulkan/vk_blit_helper.h
+++ b/src/video_core/renderer_vulkan/vk_blit_helper.h
@@ -35,10 +35,7 @@ public:
const VideoCore::BufferTextureCopy& copy);
private:
- /// Creates compute pipelines used for blit
vk::Pipeline MakeComputePipeline(vk::ShaderModule shader, vk::PipelineLayout layout);
-
- /// Creates graphics pipelines used for blit
vk::Pipeline MakeDepthStencilBlitPipeline();
private: