diff --git a/src/core/fullscreenui_settings.cpp b/src/core/fullscreenui_settings.cpp index 2f31ab557..6e2cedb17 100644 --- a/src/core/fullscreenui_settings.cpp +++ b/src/core/fullscreenui_settings.cpp @@ -4806,6 +4806,12 @@ void FullscreenUI::DrawGraphicsSettingsPage() "less noticeable. Usually safe to enable."), "GPU", "ScaledInterlacing", true, resolution_scale > 1); + DrawToggleSetting( + bsi, FSUI_ICONVSTR(ICON_FA_IMAGE, "Disable Upscaled Direct Textures"), + FSUI_VSTR("Samples 16-bit direct-color textures at native resolution when upscaling. This can fix filtering of " + "FMVs/backgrounds in some games, but may reduce the quality of render-to-texture effects."), + "GPU", "DisableUpscaledDirectTextures", false, resolution_scale > 1); + DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_SWATCHBOOK, "Texture Modulation Cropping (\"Old/v0\" GPU)"), FSUI_VSTR("Crops vertex colours to 5:5:5 before modulating with the texture colour, which " "typically results in more visible banding."), diff --git a/src/core/fullscreenui_strings.h b/src/core/fullscreenui_strings.h index 0c001801b..6ee457830 100644 --- a/src/core/fullscreenui_strings.h +++ b/src/core/fullscreenui_strings.h @@ -273,6 +273,7 @@ TRANSLATE_NOOP("FullscreenUI", "Disable Mailbox Presentation"); TRANSLATE_NOOP("FullscreenUI", "Disable Speedup on MDEC"); TRANSLATE_NOOP("FullscreenUI", "Disable Subdirectory Scanning"); TRANSLATE_NOOP("FullscreenUI", "Disable Textures"); +TRANSLATE_NOOP("FullscreenUI", "Disable Upscaled Direct Textures"); TRANSLATE_NOOP("FullscreenUI", "Disable Vertex Lighting"); TRANSLATE_NOOP("FullscreenUI", "Disable Window Resizing"); TRANSLATE_NOOP("FullscreenUI", "Disable on 2D Polygons"); @@ -678,6 +679,7 @@ TRANSLATE_NOOP("FullscreenUI", "Runs the software renderer in parallel for VRAM TRANSLATE_NOOP("FullscreenUI", "SDL DualSense Player LED"); TRANSLATE_NOOP("FullscreenUI", "SDL DualShock 4 / DualSense Enhanced Mode"); TRANSLATE_NOOP("FullscreenUI", "Safe Mode"); +TRANSLATE_NOOP("FullscreenUI", "Samples 16-bit direct-color textures at native resolution when upscaling. This can fix filtering of FMVs/backgrounds in some games, but may reduce the quality of render-to-texture effects."); TRANSLATE_NOOP("FullscreenUI", "Save"); TRANSLATE_NOOP("FullscreenUI", "Save Controller Preset"); TRANSLATE_NOOP("FullscreenUI", "Save Locations"); diff --git a/src/core/game_database.cpp b/src/core/game_database.cpp index 5c5d7db8c..63d601378 100644 --- a/src/core/game_database.cpp +++ b/src/core/game_database.cpp @@ -151,6 +151,7 @@ static constexpr const std::array s_trait_names = { "ForceRecompilerICache", "ForceCDROMSubQSkew", "IsLibCryptProtected", + "DisableUpscaledDirectTextures", }; static_assert(s_trait_names.size() == static_cast(Trait::MaxCount)); @@ -189,6 +190,7 @@ static constexpr const std::array s_trait_display_names = { TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Force Recompiler ICache", "GameDatabase::Trait"), TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Force CD-ROM SubQ Skew", "GameDatabase::Trait"), TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Is LibCrypt Protected", "GameDatabase::Trait"), + TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Disable Upscaled Direct Textures", "GameDatabase::Trait"), }; static_assert(s_trait_display_names.size() == static_cast(Trait::MaxCount)); @@ -681,6 +683,11 @@ void GameDatabase::Entry::ApplySettings(Settings& settings, bool display_osd_mes settings.gpu_force_round_texcoords = true; } + if (HasTrait(Trait::DisableUpscaledDirectTextures)) + { + settings.gpu_disable_upscaled_direct_textures = true; + } + if (HasTrait(Trait::ForceDeinterlacing)) { const DisplayDeinterlacingMode new_mode = display_deinterlacing_mode.value_or( diff --git a/src/core/game_database.h b/src/core/game_database.h index 333ec47e5..e280e7e81 100644 --- a/src/core/game_database.h +++ b/src/core/game_database.h @@ -68,6 +68,7 @@ enum class Trait : u32 ForceRecompilerICache, ForceCDROMSubQSkew, IsLibCryptProtected, + DisableUpscaledDirectTextures, MaxCount }; diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index 60c66bf06..bcfa7a87f 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -527,6 +527,8 @@ bool GPU_HW::UpdateSettings(const GPUSettings& old_settings, Error* error) g_gpu_settings.gpu_scaled_interlacing != old_settings.gpu_scaled_interlacing)) || (resolution_scale > 1 && g_gpu_settings.gpu_texture_filter == GPUTextureFilter::Nearest && g_gpu_settings.gpu_force_round_texcoords != old_settings.gpu_force_round_texcoords) || + (resolution_scale > 1 && + g_gpu_settings.gpu_disable_upscaled_direct_textures != old_settings.gpu_disable_upscaled_direct_textures) || g_gpu_settings.gpu_modulation_crop != old_settings.gpu_modulation_crop || g_gpu_settings.IsUsingShaderBlending() != old_settings.IsUsingShaderBlending() || m_texture_filtering != g_gpu_settings.gpu_texture_filter || @@ -915,6 +917,8 @@ void GPU_HW::PrintSettingsToLog() (m_resolution_scale > 1 && g_gpu_settings.gpu_scaled_interlacing) ? " (scaled)" : ""); INFO_LOG("Force round texture coordinates: {}", (m_resolution_scale > 1 && g_gpu_settings.gpu_force_round_texcoords) ? "Enabled" : "Disabled"); + INFO_LOG("Disable upscaled direct textures: {}", + (m_resolution_scale > 1 && g_gpu_settings.gpu_disable_upscaled_direct_textures) ? "Enabled" : "Disabled"); INFO_LOG("Texture Filtering: {}/{}", Settings::GetTextureFilterDisplayName(m_texture_filtering), Settings::GetTextureFilterDisplayName(m_sprite_texture_filtering)); INFO_LOG("Dual-source blending: {}", m_supports_dual_source_blend ? "Supported" : "Not supported"); @@ -1277,6 +1281,7 @@ bool GPU_HW::CompilePipelines(Error* error) const bool per_sample_shading = (msaa && g_gpu_settings.gpu_per_sample_shading && features.per_sample_shading); const bool force_round_texcoords = (upscaled && m_texture_filtering == GPUTextureFilter::Nearest && g_gpu_settings.gpu_force_round_texcoords); + const bool disable_upscaled_direct_textures = (upscaled && g_gpu_settings.gpu_disable_upscaled_direct_textures); const bool modulation_crop = g_gpu_settings.gpu_modulation_crop; const bool true_color = g_gpu_settings.IsUsingTrueColor(); const bool scaled_dithering = (!m_true_color && upscaled && g_gpu_settings.IsUsingScaledDithering()); @@ -1380,6 +1385,7 @@ bool GPU_HW::CompilePipelines(Error* error) vssel.per_sample_shading = per_sample_shading; vssel.pgxp_depth = m_pgxp_depth_buffer; vssel.disable_color_perspective = disable_color_perspective; + vssel.disable_upscaled_direct_textures = disable_upscaled_direct_textures; for (u8 textured = 0; textured < 2; textured++) { vssel.textured = (textured != 0); @@ -1430,6 +1436,7 @@ bool GPU_HW::CompilePipelines(Error* error) fssel.true_color = true_color; fssel.disable_color_perspective = disable_color_perspective; fssel.write_mask_as_depth = m_write_mask_as_depth; + fssel.disable_upscaled_direct_textures = disable_upscaled_direct_textures; for (u8 depth_test = 0; depth_test < 2; depth_test++) { diff --git a/src/core/gpu_hw_shadergen.cpp b/src/core/gpu_hw_shadergen.cpp index e15f9859a..38433ad11 100644 --- a/src/core/gpu_hw_shadergen.cpp +++ b/src/core/gpu_hw_shadergen.cpp @@ -81,6 +81,7 @@ std::string GPU_HW_ShaderGen::GenerateBatchVertexShader(const BatchVertexShaderS DefineMacro(ss, "PAGE_TEXTURE", sel.page_texture); DefineMacro(ss, "UV_LIMITS", sel.uv_limits); DefineMacro(ss, "FORCE_ROUND_TEXCOORDS", sel.force_round_texcoords); + DefineMacro(ss, "DISABLE_UPSCALED_DIRECT_TEXTURES", sel.disable_upscaled_direct_textures); DefineMacro(ss, "PGXP_DEPTH", sel.pgxp_depth); DefineMacro(ss, "UPSCALED", sel.upscaled); @@ -159,7 +160,7 @@ std::string GPU_HW_ShaderGen::GenerateBatchVertexShader(const BatchVertexShaderS v_col0 = a_col0; #if TEXTURED v_tex0 = float2(uint2(a_texcoord & 0xFFFFu, a_texcoord >> 16)); - #if !PALETTE && !PAGE_TEXTURE + #if !PALETTE && !PAGE_TEXTURE && !DISABLE_UPSCALED_DIRECT_TEXTURES v_tex0 *= u_resolution_scale; #endif @@ -180,7 +181,7 @@ std::string GPU_HW_ShaderGen::GenerateBatchVertexShader(const BatchVertexShaderS // Add 0.5 to the upper bounds when upscaling, to work around interpolation differences. // Limited to force-round-texcoord hack, to avoid breaking other games. v_uv_limits.zw += 0.5; - #elif !PAGE_TEXTURE && !PALETTE + #elif !PAGE_TEXTURE && !PALETTE && !DISABLE_UPSCALED_DIRECT_TEXTURES // Treat coordinates as being in upscaled space, and extend the UV range to all "upscaled" // pixels. This means 1-pixel-high polygon-based framebuffer effects won't be downsampled. // (e.g. Mega Man Legends 2 haze effect) @@ -2200,6 +2201,7 @@ std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader(const BatchFragmentSha DefineMacro(ss, "USE_DUAL_SOURCE", use_dual_source); DefineMacro(ss, "WRITE_MASK_AS_DEPTH", sel.write_mask_as_depth); DefineMacro(ss, "FORCE_ROUND_TEXCOORDS", sel.force_round_texcoords); + DefineMacro(ss, "DISABLE_UPSCALED_DIRECT_TEXTURES", sel.disable_upscaled_direct_textures); DefineMacro(ss, "UPSCALED", sel.upscaled); // Used for converting to normalized coordinates for sampling. @@ -2356,6 +2358,12 @@ float4 SampleFromVRAM(TEXPAGE_VALUE texpage, DECLARE_UV_LIMITS(float2 coords, fl uint2 icoord = ApplyTextureWindow(FloatToIntegerCoords(DECLARE_UV_LIMITS(coords, uv_limits))); uint2 vicoord = (texpage.xy + icoord) & uint2(1023, 511); return LOAD_TEXTURE(samp0, int2(vicoord), 0); + #elif DISABLE_UPSCALED_DIRECT_TEXTURES + // Treat direct textures as native resolution, so filtering offsets move to adjacent native texels instead of + // duplicated upscaled texels. + uint2 icoord = ApplyTextureWindow(FloatToIntegerCoords(DECLARE_UV_LIMITS(coords, uv_limits))); + uint2 vicoord = (texpage.xy + icoord) & uint2(1023, 511); + return SAMPLE_TEXTURE_LEVEL(samp0, float2(vicoord) * RCP_VRAM_SIZE, 0.0); #else // Coordinates are already upscaled, we need to downscale them to apply the texture // window, then re-upscale/offset. We can't round here, because it could result in diff --git a/src/core/gpu_hw_shadergen.h b/src/core/gpu_hw_shadergen.h index 02919adb5..41b640a3c 100644 --- a/src/core/gpu_hw_shadergen.h +++ b/src/core/gpu_hw_shadergen.h @@ -22,6 +22,7 @@ public: bool force_round_texcoords : 1; bool pgxp_depth : 1; bool disable_color_perspective : 1; + bool disable_upscaled_direct_textures : 1; }; struct BatchFragmentShaderSelector @@ -49,6 +50,7 @@ public: bool use_rov_depth : 1; bool rov_depth_test : 1; bool rov_depth_write : 1; + bool disable_upscaled_direct_textures : 1; }; public: diff --git a/src/core/settings.cpp b/src/core/settings.cpp index cdf857da0..551735107 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -325,6 +325,7 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro gpu_use_software_renderer_for_memory_states = si.GetBoolValue("GPU", "UseSoftwareRendererForMemoryStates", false); gpu_scaled_interlacing = si.GetBoolValue("GPU", "ScaledInterlacing", true); gpu_force_round_texcoords = si.GetBoolValue("GPU", "ForceRoundTextureCoordinates", false); + gpu_disable_upscaled_direct_textures = si.GetBoolValue("GPU", "DisableUpscaledDirectTextures", false); gpu_texture_filter = ParseTextureFilterName(si.GetStringViewValue("GPU", "TextureFilter", GetTextureFilterName(DEFAULT_GPU_TEXTURE_FILTER))) .value_or(DEFAULT_GPU_TEXTURE_FILTER); @@ -734,6 +735,7 @@ void Settings::Save(SettingsInterface& si, bool for_copy) const si.SetBoolValue("GPU", "UseSoftwareRendererForMemoryStates", gpu_use_software_renderer_for_memory_states); si.SetBoolValue("GPU", "ScaledInterlacing", gpu_scaled_interlacing); si.SetBoolValue("GPU", "ForceRoundTextureCoordinates", gpu_force_round_texcoords); + si.SetBoolValue("GPU", "DisableUpscaledDirectTextures", gpu_disable_upscaled_direct_textures); si.SetStringValue("GPU", "TextureFilter", GetTextureFilterName(gpu_texture_filter)); si.SetStringValue("GPU", "SpriteTextureFilter", GetTextureFilterName(gpu_sprite_texture_filter)); si.SetStringValue("GPU", "DitheringMode", GetGPUDitheringModeName(gpu_dithering_mode)); @@ -1148,6 +1150,7 @@ void Settings::ApplySettingRestrictions() gpu_per_sample_shading = false; gpu_scaled_interlacing = false; gpu_force_round_texcoords = false; + gpu_disable_upscaled_direct_textures = false; gpu_texture_filter = GPUTextureFilter::Nearest; gpu_sprite_texture_filter = GPUTextureFilter::Nearest; gpu_dithering_mode = GPUDitheringMode::Unscaled; diff --git a/src/core/settings.h b/src/core/settings.h index fd908ee9e..ce8e693f3 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -78,6 +78,7 @@ struct GPUSettings bool gpu_per_sample_shading : 1 = false; bool gpu_scaled_interlacing : 1 = true; bool gpu_force_round_texcoords : 1 = false; + bool gpu_disable_upscaled_direct_textures : 1 = false; bool gpu_widescreen_rendering : 1 = false; bool gpu_widescreen_hack : 1 = false; bool gpu_modulation_crop : 1 = false; diff --git a/src/core/shader_cache_version.h b/src/core/shader_cache_version.h index 8ce470fde..3566d8412 100644 --- a/src/core/shader_cache_version.h +++ b/src/core/shader_cache_version.h @@ -5,7 +5,7 @@ #include "common/types.h" -inline constexpr u32 SHADER_CACHE_VERSION = 39; +inline constexpr u32 SHADER_CACHE_VERSION = 40; // Used to tag opaque keys. enum class ShaderCacheKeyType : u16 diff --git a/src/core/system.cpp b/src/core/system.cpp index c365c6255..68d69b54c 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -4658,6 +4658,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings) old_settings.gpu_use_software_renderer_for_memory_states || g_settings.gpu_scaled_interlacing != old_settings.gpu_scaled_interlacing || g_settings.gpu_force_round_texcoords != old_settings.gpu_force_round_texcoords || + g_settings.gpu_disable_upscaled_direct_textures != old_settings.gpu_disable_upscaled_direct_textures || g_settings.gpu_texture_filter != old_settings.gpu_texture_filter || g_settings.gpu_sprite_texture_filter != old_settings.gpu_sprite_texture_filter || g_settings.gpu_dithering_mode != old_settings.gpu_dithering_mode || diff --git a/src/duckstation-qt/graphicssettingswidget.cpp b/src/duckstation-qt/graphicssettingswidget.cpp index 19f621502..f478eab12 100644 --- a/src/duckstation-qt/graphicssettingswidget.cpp +++ b/src/duckstation-qt/graphicssettingswidget.cpp @@ -178,6 +178,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* Settings::DEFAULT_GPU_MAX_QUEUED_FRAMES); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.modulationCrop, "GPU", "EnableModulationCrop", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.scaledInterlacing, "GPU", "ScaledInterlacing", true); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableUpscaledDirectTextures, "GPU", + "DisableUpscaledDirectTextures", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useSoftwareRendererForReadbacks, "GPU", "UseSoftwareRendererForReadbacks", false); @@ -192,6 +194,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* m_dialog->hasGameTrait(GameDatabase::Trait::ForceSoftwareRendererForReadbacks)); SettingWidgetBinder::SetForceEnabled( m_ui.forceRoundedTexcoords, m_dialog->hasGameTrait(GameDatabase::Trait::ForceRoundUpscaledTextureCoordinates)); + SettingWidgetBinder::SetForceEnabled(m_ui.disableUpscaledDirectTextures, + m_dialog->hasGameTrait(GameDatabase::Trait::DisableUpscaledDirectTextures)); // PGXP Tab @@ -461,6 +465,10 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* dialog->registerWidgetHelp(m_ui.scaledInterlacing, tr("Scaled Interlacing"), tr("Checked"), tr("Scales line skipping in interlaced rendering to the internal resolution. This makes " "the combing less obvious at higher resolutions. Usually safe to enable.")); + dialog->registerWidgetHelp( + m_ui.disableUpscaledDirectTextures, tr("Disable Upscaled Direct Textures"), tr("Unchecked"), + tr("Samples 16-bit direct-color textures at native resolution when upscaling. This can fix filtering of " + "FMVs/backgrounds in some games, but may reduce the quality of render-to-texture effects.")); dialog->registerWidgetHelp( m_ui.useSoftwareRendererForReadbacks, tr("Software Renderer Readbacks"), tr("Unchecked"), tr("Runs the software renderer in parallel for VRAM readbacks. On some systems, this may result in greater " @@ -1022,6 +1030,8 @@ void GraphicsSettingsWidget::updateResolutionDependentOptions() m_ui.forceRoundedTexcoords->setEnabled( is_hardware && scale != 1 && texture_filtering == GPUTextureFilter::Nearest && !m_dialog->hasGameTrait(GameDatabase::Trait::ForceRoundUpscaledTextureCoordinates)); + m_ui.disableUpscaledDirectTextures->setEnabled( + is_hardware && scale != 1 && !m_dialog->hasGameTrait(GameDatabase::Trait::DisableUpscaledDirectTextures)); m_ui.resolutionScaleWarningIcon->setVisible(scale != 1 && !pgxp_enabled); } diff --git a/src/duckstation-qt/graphicssettingswidget.ui b/src/duckstation-qt/graphicssettingswidget.ui index 009cefcb6..f2650abd8 100644 --- a/src/duckstation-qt/graphicssettingswidget.ui +++ b/src/duckstation-qt/graphicssettingswidget.ui @@ -607,6 +607,13 @@ + + + + Disable Upscaled Direct Textures + + +