From 62d7a733817146a75c120634eb7d1f7168821297 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 22 Mar 2025 00:21:03 +1000 Subject: [PATCH] GPU/HW: Add 'Scaled Interlacing' option Disabling this option causes rendering to skip N lines instead of 1 line, where N is the internal resolution multiplier. --- src/core/fullscreen_ui.cpp | 17 ++++++++++---- src/core/game_database.cpp | 17 ++++++++++++-- src/core/game_database.h | 1 + src/core/gpu_hw.cpp | 11 ++++++--- src/core/gpu_hw_shadergen.cpp | 14 +++++++---- src/core/gpu_hw_shadergen.h | 5 ++-- src/core/settings.cpp | 3 +++ src/core/settings.h | 1 + src/core/shader_cache_version.h | 2 +- src/core/system.cpp | 1 + src/duckstation-qt/graphicssettingswidget.cpp | 9 +++++++- src/duckstation-qt/graphicssettingswidget.ui | 23 ++++++++++++------- 12 files changed, 78 insertions(+), 26 deletions(-) diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index b27450b5c..dd064b218 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -486,10 +486,9 @@ static constexpr const std::array s_ps_button_mapping{ }; static constexpr std::array s_theme_names = { - FSUI_NSTR("Automatic"), FSUI_NSTR("Dark"), FSUI_NSTR("Light"), - FSUI_NSTR("AMOLED"), FSUI_NSTR("Cobalt Sky"), FSUI_NSTR("Grey Matter"), - FSUI_NSTR("Green Giant"), FSUI_NSTR("Pinky Pals"), FSUI_NSTR("Dark Ruby"), - FSUI_NSTR("Purple Rain")}; + FSUI_NSTR("Automatic"), FSUI_NSTR("Dark"), FSUI_NSTR("Light"), FSUI_NSTR("AMOLED"), + FSUI_NSTR("Cobalt Sky"), FSUI_NSTR("Grey Matter"), FSUI_NSTR("Green Giant"), FSUI_NSTR("Pinky Pals"), + FSUI_NSTR("Dark Ruby"), FSUI_NSTR("Purple Rain")}; static constexpr std::array s_theme_values = {"", "Dark", "Light", "AMOLED", "CobaltSky", "GreyMatter", "GreenGiant", "PinkyPals", "DarkRuby", "PurpleRain"}; @@ -5231,7 +5230,12 @@ void FullscreenUI::DrawGraphicsSettingsPage() bsi, FSUI_ICONSTR(ICON_FA_TINT_SLASH, "Scaled Dithering"), FSUI_CSTR("Scales the dithering pattern with the internal rendering resolution, making it less noticeable. " "Usually safe to enable."), - "GPU", "ScaledDithering", true, !true_color_enabled); + "GPU", "ScaledDithering", true, !true_color_enabled && resolution_scale > 1); + + DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_TINT_SLASH, "Scaled Interlacing"), + FSUI_CSTR("Scales line skipping in interlaced rendering to the internal resolution, making it " + "less noticeable. Usually safe to enable."), + "GPU", "ScaledInterlacing", true, resolution_scale > 1); DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_FILL, "Accurate Blending"), FSUI_CSTR("Forces blending to be done in the shader at 16-bit precision, when not using true " @@ -9106,6 +9110,7 @@ TRANSLATE_NOOP("FullscreenUI", "GitHub Repository"); TRANSLATE_NOOP("FullscreenUI", "Global Slot {0} - {1}##global_slot_{0}"); TRANSLATE_NOOP("FullscreenUI", "Global Slot {0}##global_slot_{0}"); TRANSLATE_NOOP("FullscreenUI", "Graphics Settings"); +TRANSLATE_NOOP("FullscreenUI", "Green Giant"); TRANSLATE_NOOP("FullscreenUI", "Grey Matter"); TRANSLATE_NOOP("FullscreenUI", "Hardcore Mode"); TRANSLATE_NOOP("FullscreenUI", "Hardcore mode will be enabled on next game restart."); @@ -9318,7 +9323,9 @@ TRANSLATE_NOOP("FullscreenUI", "Save State On Shutdown"); TRANSLATE_NOOP("FullscreenUI", "Saved {:%c}"); TRANSLATE_NOOP("FullscreenUI", "Saves state periodically so you can rewind any mistakes while playing."); TRANSLATE_NOOP("FullscreenUI", "Scaled Dithering"); +TRANSLATE_NOOP("FullscreenUI", "Scaled Interlacing"); TRANSLATE_NOOP("FullscreenUI", "Scales internal VRAM resolution by the specified multiplier. Some games require 1x VRAM resolution."); +TRANSLATE_NOOP("FullscreenUI", "Scales line skipping in interlaced rendering to the internal resolution, making it less noticeable. Usually safe to enable."); TRANSLATE_NOOP("FullscreenUI", "Scales the dithering pattern with the internal rendering resolution, making it less noticeable. Usually safe to enable."); TRANSLATE_NOOP("FullscreenUI", "Scaling"); TRANSLATE_NOOP("FullscreenUI", "Scan For New Games"); diff --git a/src/core/game_database.cpp b/src/core/game_database.cpp index c8b4c3557..88cd91835 100644 --- a/src/core/game_database.cpp +++ b/src/core/game_database.cpp @@ -40,7 +40,7 @@ namespace GameDatabase { enum : u32 { GAME_DATABASE_CACHE_SIGNATURE = 0x45434C48, - GAME_DATABASE_CACHE_VERSION = 22, + GAME_DATABASE_CACHE_VERSION = 23, }; static const Entry* GetEntryForId(std::string_view code); @@ -89,6 +89,7 @@ static constexpr const std::array(Trait::MaxCou "DisableTextureFiltering", "DisableSpriteTextureFiltering", "DisableScaledDithering", + "DisableScaledInterlacing", "DisableWidescreen", "DisablePGXP", "DisablePGXPCulling", @@ -118,6 +119,7 @@ static constexpr const std::array(Trait::MaxCou TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Disable Texture Filtering", "GameDatabase::Trait"), TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Disable Sprite Texture Filtering", "GameDatabase::Trait"), TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Disable Scaled Dithering", "GameDatabase::Trait"), + TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Disable Scaled Interlacing", "GameDatabase::Trait"), TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Disable Widescreen", "GameDatabase::Trait"), TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Disable PGXP", "GameDatabase::Trait"), TRANSLATE_DISAMBIG_NOOP("GameDatabase", "Disable PGXP Culling", "GameDatabase::Trait"), @@ -566,11 +568,22 @@ void GameDatabase::Entry::ApplySettings(Settings& settings, bool display_osd_mes if (HasTrait(Trait::DisableScaledDithering)) { if (display_osd_messages && settings.gpu_scaled_dithering) - APPEND_MESSAGE(TRANSLATE_SV("GameDatabase", "Scaled dithering.")); + APPEND_MESSAGE(TRANSLATE_SV("GameDatabase", "Scaled dithering disabled.")); settings.gpu_scaled_dithering = false; } + if (HasTrait(Trait::DisableScaledInterlacing)) + { + if (display_osd_messages && settings.gpu_scaled_interlacing && + settings.display_deinterlacing_mode != DisplayDeinterlacingMode::Progressive) + { + APPEND_MESSAGE(TRANSLATE_SV("GameDatabase", "Scaled interlacing disabled.")); + } + + settings.gpu_scaled_interlacing = false; + } + if (HasTrait(Trait::DisableWidescreen)) { if (display_osd_messages && settings.gpu_widescreen_hack) diff --git a/src/core/game_database.h b/src/core/game_database.h index 57b2e92e9..456b3605e 100644 --- a/src/core/game_database.h +++ b/src/core/game_database.h @@ -47,6 +47,7 @@ enum class Trait : u32 DisableTextureFiltering, DisableSpriteTextureFiltering, DisableScaledDithering, + DisableScaledInterlacing, DisableWidescreen, DisablePGXP, DisablePGXPCulling, diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index 43758b433..8c667a1ed 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -457,7 +457,8 @@ bool GPU_HW::UpdateSettings(const GPUSettings& old_settings, Error* error) (old_settings.display_deinterlacing_mode == DisplayDeinterlacingMode::Progressive) != (g_gpu_settings.display_deinterlacing_mode == DisplayDeinterlacingMode::Progressive) || (multisamples > 1 && g_gpu_settings.gpu_per_sample_shading != old_settings.gpu_per_sample_shading) || - (resolution_scale > 1 && g_gpu_settings.gpu_scaled_dithering != old_settings.gpu_scaled_dithering) || + (resolution_scale > 1 && (g_gpu_settings.gpu_scaled_dithering != old_settings.gpu_scaled_dithering || + 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) || g_gpu_settings.IsUsingAccurateBlending() != old_settings.IsUsingAccurateBlending() || @@ -847,6 +848,9 @@ void GPU_HW::PrintSettingsToLog() ""); INFO_LOG("Dithering: {}", m_true_color ? "Disabled" : "Enabled", (!m_true_color && g_gpu_settings.gpu_scaled_dithering)); + INFO_LOG("Deinterlacing: {}{}", + Settings::GetDisplayDeinterlacingModeDisplayName(g_gpu_settings.display_deinterlacing_mode), + (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("Texture Filtering: {}/{}", Settings::GetTextureFilterDisplayName(m_texture_filtering), @@ -1052,6 +1056,7 @@ bool GPU_HW::CompilePipelines(Error* error) (upscaled && m_texture_filtering == GPUTextureFilter::Nearest && g_gpu_settings.gpu_force_round_texcoords); const bool true_color = g_gpu_settings.gpu_true_color; const bool scaled_dithering = (!m_true_color && upscaled && g_gpu_settings.gpu_scaled_dithering); + const bool scaled_interlacing = (upscaled && g_gpu_settings.gpu_scaled_interlacing); const bool disable_color_perspective = (features.noperspective_interpolation && ShouldDisableColorPerspective()); const bool needs_page_texture = m_use_texture_cache; const bool force_progressive_scan = @@ -1258,8 +1263,8 @@ bool GPU_HW::CompilePipelines(Error* error) shader_texmode, sprite ? m_sprite_texture_filtering : m_texture_filtering, upscaled, msaa, per_sample_shading, uv_limits, !sprite && force_round_texcoords, true_color, ConvertToBoolUnchecked(dithering), scaled_dithering, disable_color_perspective, - ConvertToBoolUnchecked(interlacing), ConvertToBoolUnchecked(check_mask), m_write_mask_as_depth, - use_rov, needs_rov_depth, rov_depth_test, rov_depth_write); + ConvertToBoolUnchecked(interlacing), scaled_interlacing, ConvertToBoolUnchecked(check_mask), + m_write_mask_as_depth, use_rov, needs_rov_depth, rov_depth_test, rov_depth_write); if (!(batch_fragment_shaders[depth_test][render_mode][transparency_mode][texture_mode][check_mask] [dithering][interlacing] = g_gpu_device->CreateShader( diff --git a/src/core/gpu_hw_shadergen.cpp b/src/core/gpu_hw_shadergen.cpp index fc9678555..a0b543cd3 100644 --- a/src/core/gpu_hw_shadergen.cpp +++ b/src/core/gpu_hw_shadergen.cpp @@ -732,8 +732,8 @@ std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader( GPU_HW::BatchRenderMode render_mode, GPUTransparencyMode transparency, GPU_HW::BatchTextureMode texture_mode, GPUTextureFilter texture_filtering, bool upscaled, bool msaa, bool per_sample_shading, bool uv_limits, bool force_round_texcoords, bool true_color, bool dithering, bool scaled_dithering, bool disable_color_perspective, - bool interlacing, bool check_mask, bool write_mask_as_depth, bool use_rov, bool use_rov_depth, bool rov_depth_test, - bool rov_depth_write) const + bool interlacing, bool scaled_interlacing, bool check_mask, bool write_mask_as_depth, bool use_rov, + bool use_rov_depth, bool rov_depth_test, bool rov_depth_write) const { DebugAssert(!true_color || !dithering); // Should not be doing dithering+true color. @@ -766,6 +766,7 @@ std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader( DefineMacro(ss, "DITHERING", dithering); DefineMacro(ss, "DITHERING_SCALED", dithering && scaled_dithering); DefineMacro(ss, "INTERLACING", interlacing); + DefineMacro(ss, "INTERLACING_SCALED", interlacing && scaled_interlacing); DefineMacro(ss, "TRUE_COLOR", true_color); DefineMacro(ss, "TEXTURE_FILTERING", texture_filtering != GPUTextureFilter::Nearest); DefineMacro(ss, "UV_LIMITS", uv_limits); @@ -992,8 +993,13 @@ float4 SampleFromVRAM(TEXPAGE_VALUE texpage, float2 coords) float oalpha; #if INTERLACING - if ((fragpos.y & 1u) == u_interlaced_displayed_field) - discard; + #if INTERLACING_SCALED || !UPSCALED + if ((fragpos.y & 1u) == u_interlaced_displayed_field) + discard; + #else + if ((uint(v_pos.y * u_rcp_resolution_scale) & 1u) == u_interlaced_displayed_field) + discard; + #endif #endif #if TEXTURED diff --git a/src/core/gpu_hw_shadergen.h b/src/core/gpu_hw_shadergen.h index 339ead5aa..090d1bfee 100644 --- a/src/core/gpu_hw_shadergen.h +++ b/src/core/gpu_hw_shadergen.h @@ -23,8 +23,9 @@ public: bool upscaled, bool msaa, bool per_sample_shading, bool uv_limits, bool force_round_texcoords, bool true_color, bool dithering, bool scaled_dithering, bool disable_color_perspective, bool interlacing, - bool check_mask, bool write_mask_as_depth, bool use_rov, bool use_rov_depth, - bool rov_depth_test, bool rov_depth_write) const; + bool scaled_interlacing, bool check_mask, bool write_mask_as_depth, + bool use_rov, bool use_rov_depth, bool rov_depth_test, + bool rov_depth_write) const; std::string GenerateWireframeGeometryShader() const; std::string GenerateWireframeFragmentShader() const; std::string GenerateVRAMReadFragmentShader(u32 resolution_scale, u32 multisamples) const; diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 6bcffb66c..baa8077cb 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -231,6 +231,7 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro gpu_use_software_renderer_for_readbacks = si.GetBoolValue("GPU", "UseSoftwareRendererForReadbacks", false); gpu_true_color = si.GetBoolValue("GPU", "TrueColor", true); gpu_scaled_dithering = si.GetBoolValue("GPU", "ScaledDithering", true); + gpu_scaled_interlacing = si.GetBoolValue("GPU", "ScaledInterlacing", true); gpu_force_round_texcoords = si.GetBoolValue("GPU", "ForceRoundTextureCoordinates", false); gpu_accurate_blending = si.GetBoolValue("GPU", "AccurateBlending", false); gpu_texture_filter = @@ -588,6 +589,7 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const si.SetBoolValue("GPU", "UseSoftwareRendererForReadbacks", gpu_use_software_renderer_for_readbacks); si.SetBoolValue("GPU", "TrueColor", gpu_true_color); si.SetBoolValue("GPU", "ScaledDithering", gpu_scaled_dithering); + si.SetBoolValue("GPU", "ScaledInterlacing", gpu_scaled_interlacing); si.SetBoolValue("GPU", "ForceRoundTextureCoordinates", gpu_force_round_texcoords); si.SetBoolValue("GPU", "AccurateBlending", gpu_accurate_blending); si.SetStringValue("GPU", "TextureFilter", GetTextureFilterName(gpu_texture_filter)); @@ -991,6 +993,7 @@ void Settings::FixIncompatibleSettings(const SettingsInterface& si, bool display g_settings.gpu_per_sample_shading = false; g_settings.gpu_true_color = false; g_settings.gpu_scaled_dithering = false; + g_settings.gpu_scaled_interlacing = false; g_settings.gpu_force_round_texcoords = false; g_settings.gpu_texture_filter = GPUTextureFilter::Nearest; g_settings.gpu_sprite_texture_filter = GPUTextureFilter::Nearest; diff --git a/src/core/settings.h b/src/core/settings.h index c17258d34..e47ab761f 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -106,6 +106,7 @@ struct GPUSettings bool gpu_per_sample_shading : 1 = false; bool gpu_true_color : 1 = true; bool gpu_scaled_dithering : 1 = true; + bool gpu_scaled_interlacing : 1 = true; bool gpu_force_round_texcoords : 1 = false; bool gpu_accurate_blending : 1 = false; bool gpu_widescreen_hack : 1 = false; diff --git a/src/core/shader_cache_version.h b/src/core/shader_cache_version.h index 08be5965d..fed074bcc 100644 --- a/src/core/shader_cache_version.h +++ b/src/core/shader_cache_version.h @@ -5,4 +5,4 @@ #include "common/types.h" -static constexpr u32 SHADER_CACHE_VERSION = 28; +static constexpr u32 SHADER_CACHE_VERSION = 29; diff --git a/src/core/system.cpp b/src/core/system.cpp index ca4e6b022..6dccf60b5 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -4473,6 +4473,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings) old_settings.gpu_use_software_renderer_for_readbacks || g_settings.gpu_true_color != old_settings.gpu_true_color || g_settings.gpu_scaled_dithering != old_settings.gpu_scaled_dithering || + 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_accurate_blending != old_settings.gpu_accurate_blending || g_settings.gpu_texture_filter != old_settings.gpu_texture_filter || diff --git a/src/duckstation-qt/graphicssettingswidget.cpp b/src/duckstation-qt/graphicssettingswidget.cpp index 39f378a45..0a0d7594e 100644 --- a/src/duckstation-qt/graphicssettingswidget.cpp +++ b/src/duckstation-qt/graphicssettingswidget.cpp @@ -147,7 +147,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.maxQueuedFrames, "GPU", "MaxQueuedFrames", Settings::DEFAULT_GPU_MAX_QUEUED_FRAMES); connect(m_ui.gpuThread, &QCheckBox::checkStateChanged, this, &GraphicsSettingsWidget::onGPUThreadChanged); - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.scaledDithering, "GPU", "ScaledDithering", false); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.scaledDithering, "GPU", "ScaledDithering", true); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.scaledInterlacing, "GPU", "ScaledInterlacing", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useSoftwareRendererForReadbacks, "GPU", "UseSoftwareRendererForReadbacks", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.forceRoundedTexcoords, "GPU", "ForceRoundTextureCoordinates", @@ -156,6 +157,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* SettingWidgetBinder::SetAvailability(m_ui.scaledDithering, !m_dialog->hasGameTrait(GameDatabase::Trait::DisableScaledDithering)); + SettingWidgetBinder::SetAvailability(m_ui.scaledInterlacing, + !m_dialog->hasGameTrait(GameDatabase::Trait::DisableScaledInterlacing)); // PGXP Tab @@ -468,6 +471,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* m_ui.scaledDithering, tr("Scaled Dithering"), tr("Checked"), tr("Scales the dither pattern to the resolution scale of the emulated GPU. This makes the dither pattern much less " "obvious at higher resolutions. Usually safe to enable.")); + 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.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 " @@ -853,6 +859,7 @@ void GraphicsSettingsWidget::updateRendererDependentOptions() m_ui.gpuWireframeMode->setEnabled(is_hardware); m_ui.gpuWireframeModeLabel->setEnabled(is_hardware); m_ui.scaledDithering->setEnabled(is_hardware && !m_dialog->hasGameTrait(GameDatabase::Trait::DisableScaledDithering)); + m_ui.scaledInterlacing->setEnabled(is_hardware && !m_dialog->hasGameTrait(GameDatabase::Trait::DisableScaledInterlacing)); m_ui.useSoftwareRendererForReadbacks->setEnabled(is_hardware); m_ui.forceRoundedTexcoords->setEnabled(is_hardware); m_ui.accurateBlending->setEnabled(is_hardware); diff --git a/src/duckstation-qt/graphicssettingswidget.ui b/src/duckstation-qt/graphicssettingswidget.ui index 3acc7548a..b8e1c862a 100644 --- a/src/duckstation-qt/graphicssettingswidget.ui +++ b/src/duckstation-qt/graphicssettingswidget.ui @@ -354,13 +354,20 @@ - + Use Blit Swap Chain + + + + Software Renderer Readbacks + + + @@ -421,13 +428,6 @@ - - - - Software Renderer Readbacks - - - @@ -467,6 +467,13 @@ + + + + Scaled Interlacing + + +