From 3ea26cc9108256270ae1d1953919b12c6809866e Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 21 Feb 2025 23:31:53 +1000 Subject: [PATCH] GPU: Rewrite automatic resolution scaling Make it play nice with rewind/runahead. --- src/core/gpu.cpp | 36 +++++++++++++++++++++++++++++-- src/core/gpu.h | 3 +++ src/core/gpu_backend.cpp | 20 ------------------ src/core/gpu_backend.h | 5 ----- src/core/gpu_hw.cpp | 43 +------------------------------------- src/core/gpu_hw.h | 2 -- src/core/gpu_presenter.cpp | 3 +-- src/core/gpu_sw.cpp | 5 ----- src/core/gpu_sw.h | 2 -- src/core/gpu_thread.cpp | 7 ------- src/core/settings.cpp | 2 ++ src/core/settings.h | 6 ++++++ src/core/system.cpp | 37 ++++++++++++++++++++++++++++---- src/core/system_private.h | 3 +++ 14 files changed, 83 insertions(+), 91 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 91adb9904..097cabae3 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -1018,9 +1018,9 @@ void GPU::UpdateCRTCDisplayParameters() } if ((cs.display_vram_width != old_vram_width || cs.display_vram_height != old_vram_height) && - g_settings.gpu_resolution_scale == 0) + g_settings.gpu_automatic_resolution_scale) { - GPUBackend::QueueUpdateResolutionScale(); + System::UpdateAutomaticResolutionScale(); } } @@ -1980,6 +1980,38 @@ void GPU::QueuePresentCurrentFrame() } } +u8 GPU::CalculateAutomaticResolutionScale() const +{ + // Auto scaling. + // When the system is starting and all borders crop is enabled, the registers are zero, and + // display_height therefore is also zero. Keep the existing resolution until it updates. + u32 scale = 1; + if (const WindowInfo& main_window_info = GPUThread::GetRenderWindowInfo(); + !main_window_info.IsSurfaceless() && m_crtc_state.display_width > 0 && m_crtc_state.display_height > 0 && + m_crtc_state.display_vram_width > 0 && m_crtc_state.display_vram_height > 0) + { + GSVector4i display_rect, draw_rect; + CalculateDrawRect(main_window_info.surface_width, main_window_info.surface_height, m_crtc_state.display_width, + m_crtc_state.display_height, m_crtc_state.display_origin_left, m_crtc_state.display_origin_top, + m_crtc_state.display_vram_width, m_crtc_state.display_vram_height, g_settings.display_rotation, + g_settings.display_alignment, g_settings.gpu_show_vram ? 1.0f : ComputePixelAspectRatio(), + g_settings.IsUsingIntegerDisplayScaling(), &display_rect, &draw_rect); + + // We use the draw rect to determine scaling. This way we match the resolution as best we can, regardless of the + // anamorphic aspect ratio. + const s32 draw_width = draw_rect.width(); + const s32 draw_height = draw_rect.height(); + scale = static_cast( + std::ceil(std::max(static_cast(draw_width) / static_cast(m_crtc_state.display_vram_width), + static_cast(draw_height) / static_cast(m_crtc_state.display_vram_height)))); + scale = std::min(scale, std::numeric_limits::max()); + VERBOSE_LOG("Draw Size = {}x{}, VRAM Size = {}x{}, Preferred Scale = {}", draw_width, draw_height, + m_crtc_state.display_vram_width, m_crtc_state.display_vram_height, scale); + } + + return Truncate8(scale); +} + bool GPU::DumpVRAMToFile(const char* filename) { ReadVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT); diff --git a/src/core/gpu.h b/src/core/gpu.h index 111c44cb0..14975a91c 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -230,6 +230,9 @@ public: // Queues the current frame for presentation. Should only be used with runahead. void QueuePresentCurrentFrame(); + /// Computes the effective resolution scale when it is set to automatic. + u8 CalculateAutomaticResolutionScale() const; + /// Helper function for computing the draw rectangle in a larger window. static void CalculateDrawRect(u32 window_width, u32 window_height, u32 crtc_display_width, u32 crtc_display_height, s32 display_origin_left, s32 display_origin_top, u32 display_vram_width, diff --git a/src/core/gpu_backend.cpp b/src/core/gpu_backend.cpp index 3519aaaab..f791099b0 100644 --- a/src/core/gpu_backend.cpp +++ b/src/core/gpu_backend.cpp @@ -398,20 +398,6 @@ bool GPUBackend::AllocateMemorySaveStates(std::span sta return result; } -void GPUBackend::QueueUpdateResolutionScale() -{ - DebugAssert(!GPUThread::IsOnThread()); - - GPUThread::RunOnBackend( - [](GPUBackend* backend) { - Error error; - if (!backend->UpdateResolutionScale(&error)) [[unlikely]] - GPUThread::ReportFatalErrorAndShutdown( - fmt::format("Failed to update resolution scale: {}", error.GetDescription())); - }, - false, true); -} - void GPUBackend::HandleCommand(const GPUThreadCommand* cmd) { switch (cmd->type) @@ -837,7 +823,6 @@ public: bool UpdateSettings(const GPUSettings& old_settings, Error* error) override; u32 GetResolutionScale() const override; - bool UpdateResolutionScale(Error* error) override; void RestoreDeviceContext() override; void FlushRender() override; @@ -891,11 +876,6 @@ u32 GPUNullBackend::GetResolutionScale() const return 1; } -bool GPUNullBackend::UpdateResolutionScale(Error* error) -{ - return true; -} - void GPUNullBackend::RestoreDeviceContext() { } diff --git a/src/core/gpu_backend.h b/src/core/gpu_backend.h index 07c69cf73..2771ad58c 100644 --- a/src/core/gpu_backend.h +++ b/src/core/gpu_backend.h @@ -69,8 +69,6 @@ public: static bool AllocateMemorySaveStates(std::span states, Error* error); - static void QueueUpdateResolutionScale(); - public: GPUBackend(GPUPresenter& presenter); virtual ~GPUBackend(); @@ -86,9 +84,6 @@ public: /// Returns the current resolution scale. virtual u32 GetResolutionScale() const = 0; - /// Updates the resolution scale when it's set to automatic. - virtual bool UpdateResolutionScale(Error* error) = 0; - // Graphics API state reset/restore - call when drawing the UI etc. // TODO: replace with "invalidate cached state" virtual void RestoreDeviceContext() = 0; diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index efb7beb7c..9b655d59a 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -726,40 +726,7 @@ void GPU_HW::CheckSettings() u32 GPU_HW::CalculateResolutionScale() const { - u32 scale; - if (g_gpu_settings.gpu_resolution_scale != 0) - { - scale = g_gpu_settings.gpu_resolution_scale; - } - else - { - // Auto scaling. - if (m_presenter.GetDisplayWidth() == 0 || m_presenter.GetDisplayHeight() == 0 || - m_presenter.GetDisplayVRAMWidth() == 0 || m_presenter.GetDisplayVRAMHeight() == 0 || - !m_presenter.HasDisplayTexture() || !g_gpu_device->HasMainSwapChain()) - { - // When the system is starting and all borders crop is enabled, the registers are zero, and - // display_height therefore is also zero. Keep the existing resolution until it updates. - scale = m_resolution_scale; - } - else - { - GSVector4i display_rect, draw_rect; - m_presenter.CalculateDrawRect(g_gpu_device->GetMainSwapChain()->GetWidth(), - g_gpu_device->GetMainSwapChain()->GetHeight(), true, true, &display_rect, - &draw_rect); - - // We use the draw rect to determine scaling. This way we match the resolution as best we can, regardless of the - // anamorphic aspect ratio. - const s32 draw_width = draw_rect.width(); - const s32 draw_height = draw_rect.height(); - scale = static_cast( - std::ceil(std::max(static_cast(draw_width) / static_cast(m_presenter.GetDisplayVRAMWidth()), - static_cast(draw_height) / static_cast(m_presenter.GetDisplayVRAMHeight())))); - VERBOSE_LOG("Draw Size = {}x{}, VRAM Size = {}x{}, Preferred Scale = {}", draw_width, draw_height, - m_presenter.GetDisplayVRAMWidth(), m_presenter.GetDisplayVRAMHeight(), scale); - } - } + u32 scale = g_gpu_settings.gpu_resolution_scale; if (g_gpu_settings.gpu_downsample_mode == GPUDownsampleMode::Adaptive && scale > 1 && !Common::IsPow2(scale)) { @@ -782,14 +749,6 @@ u32 GPU_HW::CalculateResolutionScale() const return std::clamp(scale, 1, GetMaxResolutionScale()); } -bool GPU_HW::UpdateResolutionScale(Error* error) -{ - if (CalculateResolutionScale() == m_resolution_scale) - return true; - - return UpdateSettings(g_gpu_settings, error); -} - GPUDownsampleMode GPU_HW::GetDownsampleMode(u32 resolution_scale) const { return (resolution_scale == 1) ? GPUDownsampleMode::Disabled : g_gpu_settings.gpu_downsample_mode; diff --git a/src/core/gpu_hw.h b/src/core/gpu_hw.h index 8d94bf6d1..b6585355d 100644 --- a/src/core/gpu_hw.h +++ b/src/core/gpu_hw.h @@ -74,8 +74,6 @@ public: bool UpdateSettings(const GPUSettings& old_settings, Error* error) override; void UpdatePostProcessingSettings(bool force_reload) override; - bool UpdateResolutionScale(Error* error) override; - void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color, bool interlaced_rendering, u8 active_line_lsb) override; void ReadVRAM(u32 x, u32 y, u32 width, u32 height) override; void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data, bool set_mask, bool check_mask) override; diff --git a/src/core/gpu_presenter.cpp b/src/core/gpu_presenter.cpp index 74df95eb6..2cadfd0de 100644 --- a/src/core/gpu_presenter.cpp +++ b/src/core/gpu_presenter.cpp @@ -1004,8 +1004,7 @@ bool GPUPresenter::ApplyChromaSmoothing() void GPUPresenter::CalculateDrawRect(s32 window_width, s32 window_height, bool apply_aspect_ratio, bool apply_alignment, GSVector4i* display_rect, GSVector4i* draw_rect) const { - const bool integer_scale = (g_gpu_settings.display_scaling == DisplayScalingMode::NearestInteger || - g_gpu_settings.display_scaling == DisplayScalingMode::BilinearInteger); + const bool integer_scale = g_gpu_settings.IsUsingIntegerDisplayScaling(); const bool show_vram = g_gpu_settings.gpu_show_vram; const u32 display_width = show_vram ? VRAM_WIDTH : m_display_width; const u32 display_height = show_vram ? VRAM_HEIGHT : m_display_height; diff --git a/src/core/gpu_sw.cpp b/src/core/gpu_sw.cpp index 02a96093f..fa13d40de 100644 --- a/src/core/gpu_sw.cpp +++ b/src/core/gpu_sw.cpp @@ -65,11 +65,6 @@ void GPU_SW::ClearVRAM() std::memset(g_gpu_clut, 0, sizeof(g_gpu_clut)); } -bool GPU_SW::UpdateResolutionScale(Error* error) -{ - return true; -} - void GPU_SW::LoadState(const GPUBackendLoadStateCommand* cmd) { std::memcpy(g_vram, cmd->vram_data, sizeof(g_vram)); diff --git a/src/core/gpu_sw.h b/src/core/gpu_sw.h index a0e31cc56..dd68e55bc 100644 --- a/src/core/gpu_sw.h +++ b/src/core/gpu_sw.h @@ -46,8 +46,6 @@ public: void ClearVRAM() override; - bool UpdateResolutionScale(Error* error) override; - void LoadState(const GPUBackendLoadStateCommand* cmd) override; bool AllocateMemorySaveState(System::MemorySaveState& mss, Error* error) override; diff --git a/src/core/gpu_thread.cpp b/src/core/gpu_thread.cpp index 93c849796..60767546b 100644 --- a/src/core/gpu_thread.cpp +++ b/src/core/gpu_thread.cpp @@ -1287,13 +1287,6 @@ void GPUThread::DisplayWindowResizedOnThread() Internal::PresentFrameAndRestoreContext(); Internal::PresentFrameAndRestoreContext(); } - - if (g_gpu_settings.gpu_resolution_scale == 0) - { - Error error; - if (!s_state.gpu_backend->UpdateResolutionScale(&error)) [[unlikely]] - ReportFatalErrorAndShutdown(fmt::format("Failed to update resolution scale: {}", error.GetDescription())); - } } } diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 3f981c438..c01edaad1 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -199,6 +199,7 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro .value_or(DEFAULT_GPU_RENDERER); gpu_adapter = si.GetStringValue("GPU", "Adapter", ""); gpu_resolution_scale = static_cast(si.GetUIntValue("GPU", "ResolutionScale", 1u)); + gpu_automatic_resolution_scale = (gpu_resolution_scale == 0); gpu_multisamples = static_cast(si.GetUIntValue("GPU", "Multisamples", 1u)); gpu_use_debug_device = si.GetBoolValue("GPU", "UseDebugDevice", false); gpu_disable_shader_cache = si.GetBoolValue("GPU", "DisableShaderCache", false); @@ -972,6 +973,7 @@ void Settings::FixIncompatibleSettings(const SettingsInterface& si, bool display g_settings.enable_8mb_ram = false; g_settings.gpu_resolution_scale = 1; g_settings.gpu_multisamples = 1; + g_settings.gpu_automatic_resolution_scale = false; g_settings.gpu_per_sample_shading = false; g_settings.gpu_true_color = false; g_settings.gpu_scaled_dithering = false; diff --git a/src/core/settings.h b/src/core/settings.h index 42d8d6b8e..bbcf734b7 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -102,6 +102,7 @@ struct GPUSettings bool gpu_disable_raster_order_views : 1 = false; bool gpu_disable_compute_shaders : 1 = false; bool gpu_disable_compressed_textures : 1 = false; + bool gpu_automatic_resolution_scale : 1 = false; bool gpu_per_sample_shading : 1 = false; bool gpu_true_color : 1 = true; bool gpu_scaled_dithering : 1 = true; @@ -210,6 +211,11 @@ struct GPUSettings ALWAYS_INLINE bool IsUsingSoftwareRenderer() const { return (gpu_renderer == GPURenderer::Software); } ALWAYS_INLINE bool IsUsingAccurateBlending() const { return (gpu_accurate_blending && !gpu_true_color); } + ALWAYS_INLINE bool IsUsingIntegerDisplayScaling() const + { + return (display_scaling == DisplayScalingMode::NearestInteger || + display_scaling == DisplayScalingMode::BilinearInteger); + } ALWAYS_INLINE bool UsingPGXPCPUMode() const { return gpu_pgxp_enable && gpu_pgxp_cpu; } ALWAYS_INLINE bool UsingPGXPDepthBuffer() const { return gpu_pgxp_enable && gpu_pgxp_depth_buffer; } diff --git a/src/core/system.cpp b/src/core/system.cpp index 3919b99e6..97e2ea325 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -1219,6 +1219,10 @@ void System::LoadSettings(bool display_osd_messages) (g_settings.disable_all_enhancements || Host::Internal::GetBaseSettingsLayer()->GetBoolValue("Main", "DisableAllEnhancements", false)); + // Fix up automatic resolution scale, yuck. + if (g_settings.gpu_automatic_resolution_scale && IsValid()) + g_settings.gpu_resolution_scale = g_gpu.CalculateAutomaticResolutionScale(); + Settings::UpdateLogConfig(si); Host::LoadSettings(si, lock); InputManager::ReloadSources(controller_si, lock); @@ -1934,6 +1938,7 @@ bool System::Initialize(std::unique_ptr disc, DiscRegion disc_region, b PCDrv::Initialize(); UpdateGTEAspectRatio(); + UpdateAutomaticResolutionScale(); UpdateThrottlePeriod(); UpdateMemorySaveStateSettings(); @@ -2592,7 +2597,7 @@ void System::ClearMemorySaveStates(bool reallocate_resources, bool recycle_textu } // immediately save a rewind state next frame - s_state.rewind_save_counter = (s_state.rewind_save_frequency > 0) ? 0 : -1; + s_state.rewind_save_counter = (s_state.rewind_save_frequency >= 0) ? 0 : -1; } void System::FreeMemoryStateStorage(bool release_memory, bool release_textures, bool recycle_textures) @@ -4220,8 +4225,8 @@ void System::UpdateRunningGame(const std::string& path, CDImage* image, bool boo UpdateRichPresence(booting); - FullscreenUI::OnRunningGameChanged(s_state.running_game_path, s_state.running_game_serial, - s_state.running_game_title, s_state.running_game_hash); + FullscreenUI::OnRunningGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title, + s_state.running_game_hash); Host::OnGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title, s_state.running_game_hash); @@ -4508,7 +4513,8 @@ void System::CheckForSettingsChanges(const Settings& old_settings) // NOTE: Must come after the GPU thread settings update, otherwise it allocs the wrong size textures. const bool use_existing_textures = (g_settings.gpu_resolution_scale == old_settings.gpu_resolution_scale); - ClearMemorySaveStates(true, use_existing_textures); + FreeMemoryStateStorage(false, true, use_existing_textures); + ClearMemorySaveStates(true, true); if (IsPaused()) { @@ -5823,6 +5829,7 @@ void System::DisplayWindowResized() return; UpdateGTEAspectRatio(); + UpdateAutomaticResolutionScale(); } void System::UpdateGTEAspectRatio() @@ -5866,6 +5873,28 @@ void System::UpdateGTEAspectRatio() GTE::SetAspectRatio(gte_ar, custom_num, custom_denom); } +void System::UpdateAutomaticResolutionScale() +{ + if (!IsValidOrInitializing() || !g_settings.gpu_automatic_resolution_scale) + return; + + const u32 new_scale = g_gpu.CalculateAutomaticResolutionScale(); + if (g_settings.gpu_resolution_scale == new_scale) + return; + + g_settings.gpu_resolution_scale = Truncate8(new_scale); + GPUThread::UpdateSettings(true, false, false); + FreeMemoryStateStorage(false, true, false); + ClearMemorySaveStates(true, false); + + if (IsPaused()) + { + // resolution change needs display updated + g_gpu.UpdateDisplay(false); + GPUThread::PresentCurrentFrame(); + } +} + bool System::ChangeGPUDump(std::string new_path) { Error error; diff --git a/src/core/system_private.h b/src/core/system_private.h index ee920a92a..367fc227f 100644 --- a/src/core/system_private.h +++ b/src/core/system_private.h @@ -50,6 +50,9 @@ void DisplayWindowResized(); /// Updates the internal GTE aspect ratio. Use with "match display" aspect ratio setting. void UpdateGTEAspectRatio(); +/// Updates the resolution scale when it is set to automatic. +void UpdateAutomaticResolutionScale(); + /// Called on card read/write, handles fast forwarding. void OnMemoryCardAccessed();