diff --git a/bin/resources/shaders/dx11/convert.fx b/bin/resources/shaders/dx11/convert.fx index e504ef2fde..2d169effb0 100644 --- a/bin/resources/shaders/dx11/convert.fx +++ b/bin/resources/shaders/dx11/convert.fx @@ -80,6 +80,7 @@ PS_OUTPUT ps_downsample_copy(PS_INPUT input) int DownsampleFactor = DOFFSET; int2 ClampMin = int2(EMODA, EMODC); float Weight = BGColor.x; + float step_multiplier = BGColor.y; int2 coord = max(int2(input.p.xy) * DownsampleFactor, ClampMin); @@ -88,7 +89,7 @@ PS_OUTPUT ps_downsample_copy(PS_INPUT input) for (int yoff = 0; yoff < DownsampleFactor; yoff++) { for (int xoff = 0; xoff < DownsampleFactor; xoff++) - output.c += Texture.Load(int3(coord + int2(xoff, yoff), 0)); + output.c += Texture.Load(int3(coord + int2(xoff * step_multiplier, yoff * step_multiplier), 0)); } output.c /= Weight; return output; diff --git a/bin/resources/shaders/opengl/convert.glsl b/bin/resources/shaders/opengl/convert.glsl index 92336d86b2..4989c71c98 100644 --- a/bin/resources/shaders/opengl/convert.glsl +++ b/bin/resources/shaders/opengl/convert.glsl @@ -70,6 +70,7 @@ void ps_depth_copy() uniform ivec2 ClampMin; uniform int DownsampleFactor; uniform float Weight; +uniform float StepMultiplier; void ps_downsample_copy() { @@ -78,7 +79,7 @@ void ps_downsample_copy() for (int yoff = 0; yoff < DownsampleFactor; yoff++) { for (int xoff = 0; xoff < DownsampleFactor; xoff++) - result += texelFetch(TextureSampler, coord + ivec2(xoff, yoff), 0); + result += texelFetch(TextureSampler, coord + ivec2(xoff * StepMultiplier, yoff * StepMultiplier), 0); } SV_Target0 = result / Weight; } diff --git a/bin/resources/shaders/vulkan/convert.glsl b/bin/resources/shaders/vulkan/convert.glsl index 0844108f59..0aed8dba32 100644 --- a/bin/resources/shaders/vulkan/convert.glsl +++ b/bin/resources/shaders/vulkan/convert.glsl @@ -66,7 +66,8 @@ layout(push_constant) uniform cb10 int DownsampleFactor; int pad0; float Weight; - vec3 pad1; + float step_multiplier; + vec2 pad1; }; void ps_downsample_copy() { @@ -75,7 +76,9 @@ void ps_downsample_copy() for (int yoff = 0; yoff < DownsampleFactor; yoff++) { for (int xoff = 0; xoff < DownsampleFactor; xoff++) - result += texelFetch(samp0, coord + ivec2(xoff, yoff), 0); + { + result += texelFetch(samp0, coord + ivec2(xoff * step_multiplier, yoff * step_multiplier), 0); + } } o_col0 = result / Weight; } diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp index 57409e48d1..337180adca 100644 --- a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp @@ -970,7 +970,6 @@ void GraphicsSettingsWidget::onTextureReplacementChanged() m_texture.precacheTextureReplacements->setEnabled(enabled); } - void GraphicsSettingsWidget::onCaptureContainerChanged() { const std::string container( diff --git a/pcsx2-qt/Settings/GraphicsUpscalingFixesSettingsTab.ui b/pcsx2-qt/Settings/GraphicsUpscalingFixesSettingsTab.ui index 30276e8c88..d6f6fcc5d4 100644 --- a/pcsx2-qt/Settings/GraphicsUpscalingFixesSettingsTab.ui +++ b/pcsx2-qt/Settings/GraphicsUpscalingFixesSettingsTab.ui @@ -136,6 +136,16 @@ Aggressive + + + Normal (Maintain Upscale) + + + + + Aggressive (Maintain Upscale) + + diff --git a/pcsx2/Config.h b/pcsx2/Config.h index 86b1c4d225..14776ae8d5 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -456,6 +456,8 @@ enum class GSNativeScaling : u8 Off, Normal, Aggressive, + NormalUpscaled, + AggressiveUpscaled, MaxCount }; diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp index 7bbab09748..8bb8761d27 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp @@ -1465,14 +1465,15 @@ void GSDevice11::FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u32 struct Uniforms { float weight; - float pad0[3]; + float step_multiplier; + float pad0[2]; GSVector2i clamp_min; int downsample_factor; int pad1; }; const Uniforms cb = { - static_cast(downsample_factor * downsample_factor), {}, clamp_min, static_cast(downsample_factor), 0}; + static_cast(downsample_factor * downsample_factor), (GSConfig.UserHacks_NativeScaling > GSNativeScaling::Aggressive) ? 2.0f : 1.0f, {}, clamp_min, static_cast(downsample_factor), 0}; m_ctx->UpdateSubresource(m_merge.cb.get(), 0, nullptr, &cb, 0, 0); const ShaderConvert shader = ShaderConvert::DOWNSAMPLE_COPY; diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp index fe5fe2e1d9..62f989a5a5 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp @@ -1512,14 +1512,15 @@ void GSDevice12::FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u32 struct Uniforms { float weight; - float pad0[3]; + float step_multiplier; + float pad0[2]; GSVector2i clamp_min; int downsample_factor; int pad1; }; const Uniforms cb = { - static_cast(downsample_factor * downsample_factor), {}, clamp_min, static_cast(downsample_factor), 0}; + static_cast(downsample_factor * downsample_factor), (GSConfig.UserHacks_NativeScaling > GSNativeScaling::Aggressive) ? 2.0f : 1.0f, {}, clamp_min, static_cast(downsample_factor), 0}; SetUtilityRootSignature(); SetUtilityPushConstants(&cb, sizeof(cb)); diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index 9c0ef6eeff..0a2264acb7 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -3192,11 +3192,12 @@ void GSRendererHW::Draw() // 2 == Upscale, so likely putting it over the top of the render target. if (scale_draw == 1) { - target_scale = 1.0f; + if (!PRIM->ABE || GSConfig.UserHacks_NativeScaling < GSNativeScaling::NormalUpscaled) + target_scale = 1.0f; m_downscale_source = src->m_from_target ? src->m_from_target->GetScale() > 1.0f : false; } else - m_downscale_source = (GSConfig.UserHacks_NativeScaling != GSNativeScaling::Aggressive || !src->m_from_target) ? false : src->m_from_target->GetScale() > 1.0f; // Bad for GTA + Full Spectrum Warrior, good for Sacred Blaze + Parappa. + m_downscale_source = ((GSConfig.UserHacks_NativeScaling != GSNativeScaling::Aggressive && GSConfig.UserHacks_NativeScaling != GSNativeScaling::AggressiveUpscaled) || !src->m_from_target) ? false : src->m_from_target->GetScale() > 1.0f; // Bad for GTA + Full Spectrum Warrior, good for Sacred Blaze + Parappa. } else { @@ -3516,7 +3517,7 @@ void GSRendererHW::Draw() // Of course if this size is different (in width) or this is a shuffle happening, this will be bypassed. const bool preserve_downscale_draw = (GSConfig.UserHacks_NativeScaling != GSNativeScaling::Off && (std::abs(scale_draw) == 1 || (scale_draw == 0 && src && src->m_from_target && src->m_from_target->m_downscaled))) || is_possible_mem_clear == ClearType::ClearWithDraw; - rt = g_texture_cache->LookupTarget(FRAME_TEX0, t_size, ((src && src->m_scale != 1) && GSConfig.UserHacks_NativeScaling == GSNativeScaling::Normal && !possible_shuffle) ? GetTextureScaleFactor() : target_scale, GSTextureCache::RenderTarget, true, + rt = g_texture_cache->LookupTarget(FRAME_TEX0, t_size, ((src && src->m_scale != 1) && (GSConfig.UserHacks_NativeScaling == GSNativeScaling::Normal || GSConfig.UserHacks_NativeScaling == GSNativeScaling::NormalUpscaled) && !possible_shuffle) ? GetTextureScaleFactor() : target_scale, GSTextureCache::RenderTarget, true, fm, false, force_preload, preserve_rt_rgb, preserve_rt_alpha, lookup_rect, possible_shuffle, is_possible_mem_clear && FRAME_TEX0.TBP0 != m_cached_ctx.ZBUF.Block(), GSConfig.UserHacks_NativeScaling != GSNativeScaling::Off && preserve_downscale_draw && is_possible_mem_clear != ClearType::NormalClear, src, ds, (no_ds || !ds) ? -1 : (m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0)); diff --git a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm index 376a3b4380..4dcf53c2bb 100644 --- a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm +++ b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm @@ -1754,7 +1754,7 @@ void GSDeviceMTL::FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u3 [NSException raise:@"StretchRect Missing Pipeline" format:@"No pipeline for %d", static_cast(shader)]; GSMTLDownsamplePSUniform uniform = { {static_cast(clamp_min.x), static_cast(clamp_min.x)}, downsample_factor, - static_cast(downsample_factor * downsample_factor) }; + static_cast(downsample_factor * downsample_factor), (GSConfig.UserHacks_NativeScaling > GSNativeScaling::Aggressive) ? 2.0f : 1.0f }; DoStretchRect(sTex, GSVector4::zero(), dTex, dRect, pipeline, false, LoadAction::DontCareIfFull, &uniform, sizeof(uniform)); }} diff --git a/pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h b/pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h index 1f87d89ab6..f7fb7f72e1 100644 --- a/pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h +++ b/pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h @@ -72,6 +72,7 @@ struct GSMTLDownsamplePSUniform vector_uint2 clamp_min; uint downsample_factor; float weight; + float step_multiplier; }; struct GSMTLMainVertex diff --git a/pcsx2/GS/Renderers/Metal/convert.metal b/pcsx2/GS/Renderers/Metal/convert.metal index eaed4d1970..5c5c249bcb 100644 --- a/pcsx2/GS/Renderers/Metal/convert.metal +++ b/pcsx2/GS/Renderers/Metal/convert.metal @@ -192,7 +192,7 @@ fragment float4 ps_downsample_copy(ConvertShaderData data [[stage_in]], for (uint yoff = 0; yoff < uniform.downsample_factor; yoff++) { for (uint xoff = 0; xoff < uniform.downsample_factor; xoff++) - result += texture.read(coord + uint2(xoff, yoff), 0); + result += texture.read(coord + uint2(xoff * uniform.step_multiplier, yoff * uniform.step_multiplier), 0); } result /= uniform.weight; return result; diff --git a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp index f3023a4d4d..67e7452bba 100644 --- a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp +++ b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp @@ -365,6 +365,7 @@ bool GSDeviceOGL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle) m_convert.ps[i].RegisterUniform("ClampMin"); m_convert.ps[i].RegisterUniform("DownsampleFactor"); m_convert.ps[i].RegisterUniform("Weight"); + m_convert.ps[i].RegisterUniform("StepMultiplier"); } } @@ -1632,6 +1633,7 @@ void GSDeviceOGL::FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u3 prog.Uniform2iv(0, clamp_min.v); prog.Uniform1i(1, downsample_factor); prog.Uniform1f(2, static_cast(downsample_factor * downsample_factor)); + prog.Uniform1f(3, (GSConfig.UserHacks_NativeScaling > GSNativeScaling::Aggressive) ? 2.0f : 1.0f); OMSetDepthStencilState(m_convert.dss); OMSetBlendState(false); diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp index 3332f9fb26..a39cb8b84d 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp @@ -3154,11 +3154,12 @@ void GSDeviceVK::FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u32 int downsample_factor; int pad0; float weight; - float pad1[3]; + float step_multiplier; + float pad1[2]; }; const Uniforms uniforms = { - clamp_min, static_cast(downsample_factor), 0, static_cast(downsample_factor * downsample_factor)}; + clamp_min, static_cast(downsample_factor), 0, static_cast(downsample_factor * downsample_factor), (GSConfig.UserHacks_NativeScaling > GSNativeScaling::Aggressive) ? 2.0f : 1.0f}; SetUtilityPushConstants(&uniforms, sizeof(uniforms)); const ShaderConvert shader = ShaderConvert::DOWNSAMPLE_COPY; diff --git a/pcsx2/ImGui/FullscreenUI.cpp b/pcsx2/ImGui/FullscreenUI.cpp index 62742acdde..0bca7e40b0 100644 --- a/pcsx2/ImGui/FullscreenUI.cpp +++ b/pcsx2/ImGui/FullscreenUI.cpp @@ -4573,9 +4573,11 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad FSUI_NSTR("Align to Native - with Texture Offset"), }; static constexpr const char* s_native_scaling_options[] = { - FSUI_NSTR("Normal (Default)"), + FSUI_NSTR("Off (Default)"), + FSUI_NSTR("Normal"), FSUI_NSTR("Aggressive"), - FSUI_NSTR("Off"), + FSUI_NSTR("Normal (Maintain Upscale)"), + FSUI_NSTR("Aggressive (Maintain Upscale)"), }; static constexpr const char* s_round_sprite_options[] = { FSUI_NSTR("Off (Default)"), diff --git a/pcsx2/ShaderCacheVersion.h b/pcsx2/ShaderCacheVersion.h index 7c6de74bb9..a52eb2645c 100644 --- a/pcsx2/ShaderCacheVersion.h +++ b/pcsx2/ShaderCacheVersion.h @@ -3,4 +3,4 @@ /// Version number for GS and other shaders. Increment whenever any of the contents of the /// shaders change, to invalidate the cache. -static constexpr u32 SHADER_CACHE_VERSION = 76; +static constexpr u32 SHADER_CACHE_VERSION = 77;