From a08acdb93a083f56f91ee357281b48edf5e2a042 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 1 Jan 2025 19:24:15 +1000 Subject: [PATCH] System: Improve texture recycling when changing rewind/runahead settings Fix suprious failures when changing rewind settings when low on VRAM. --- src/core/gpu_backend.cpp | 23 +++++++++--- src/core/hotkeys.cpp | 8 ++-- src/core/system.cpp | 79 +++++++++++++-------------------------- src/core/system.h | 2 +- src/core/system_private.h | 5 +-- 5 files changed, 51 insertions(+), 66 deletions(-) diff --git a/src/core/gpu_backend.cpp b/src/core/gpu_backend.cpp index d2c2e9b34..e93f05736 100644 --- a/src/core/gpu_backend.cpp +++ b/src/core/gpu_backend.cpp @@ -332,21 +332,32 @@ bool GPUBackend::AllocateMemorySaveStates(std::span sta for (size_t i = 0; i < states.size(); i++) g_gpu_device->RecycleTexture(std::move(states[i].vram_texture)); + // Maximize potential for texture reuse by flushing the current command buffer. + g_gpu_device->WaitForGPUIdle(); + for (size_t i = 0; i < states.size(); i++) { if (!backend->AllocateMemorySaveState(states[i], error)) { - // Free anything that was allocated. - for (size_t j = 0; j <= i; i++) + // Try flushing the pool. + WARNING_LOG("Failed to allocate memory save state texture, trying flushing pool."); + g_gpu_device->PurgeTexturePool(); + g_gpu_device->WaitForGPUIdle(); + if (!backend->AllocateMemorySaveState(states[i], error)) { - states[j].state_data.deallocate(); - states[j].vram_texture.reset(); - result = false; - return; + // Free anything that was allocated. + for (size_t j = 0; j <= i; i++) + { + states[j].state_data.deallocate(); + states[j].vram_texture.reset(); + result = false; + return; + } } } } + backend->RestoreDeviceContext(); result = true; }, true, false); diff --git a/src/core/hotkeys.cpp b/src/core/hotkeys.cpp index 865aa2ce8..3f4377e59 100644 --- a/src/core/hotkeys.cpp +++ b/src/core/hotkeys.cpp @@ -59,7 +59,7 @@ static void HotkeyModifyResolutionScale(s32 increment) if (System::IsValid()) { - System::ClearMemorySaveStates(true); + System::ClearMemorySaveStates(true, false); GPUThread::UpdateSettings(true, false); } } @@ -373,7 +373,7 @@ DEFINE_HOTKEY("TogglePGXP", TRANSLATE_NOOP("Hotkeys", "Graphics"), TRANSLATE_NOO [](s32 pressed) { if (!pressed && System::IsValid()) { - System::ClearMemorySaveStates(true); + System::ClearMemorySaveStates(true, true); g_settings.gpu_pgxp_enable = !g_settings.gpu_pgxp_enable; GPUThread::UpdateSettings(true, false); @@ -451,7 +451,7 @@ DEFINE_HOTKEY("TogglePGXPDepth", TRANSLATE_NOOP("Hotkeys", "Graphics"), if (!g_settings.gpu_pgxp_enable) return; - System::ClearMemorySaveStates(true); + System::ClearMemorySaveStates(true, true); g_settings.gpu_pgxp_depth_buffer = !g_settings.gpu_pgxp_depth_buffer; GPUThread::UpdateSettings(true, false); @@ -471,7 +471,7 @@ DEFINE_HOTKEY("TogglePGXPCPU", TRANSLATE_NOOP("Hotkeys", "Graphics"), TRANSLATE_ if (!g_settings.gpu_pgxp_enable) return; - System::ClearMemorySaveStates(true); + System::ClearMemorySaveStates(true, true); // GPU thread is unchanged g_settings.gpu_pgxp_cpu = !g_settings.gpu_pgxp_cpu; diff --git a/src/core/system.cpp b/src/core/system.cpp index 5a4dc733d..270f6e89d 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -1167,7 +1167,7 @@ DiscRegion System::GetRegionForPsf(const char* path) void System::RecreateGPU(GPURenderer renderer) { - FreeMemoryStateTextures(); + FreeMemoryStateStorage(false); StopMediaCapture(); Error error; @@ -1177,7 +1177,7 @@ void System::RecreateGPU(GPURenderer renderer) Panic("Failed to switch renderer."); } - ClearMemorySaveStates(true); + ClearMemorySaveStates(true, false); } void System::LoadSettings(bool display_osd_messages) @@ -1954,7 +1954,7 @@ void System::DestroySystem() if (g_settings.inhibit_screensaver) PlatformMisc::ResumeScreensaver(); - FreeMemoryStateStorage(); + FreeMemoryStateStorage(false); Cheats::UnloadAll(); PCDrv::Shutdown(); @@ -2495,13 +2495,13 @@ System::MemorySaveState& System::PopMemoryState() return s_state.memory_save_states[s_state.memory_save_state_front]; } -bool System::AllocateMemoryStates(size_t state_count) +bool System::AllocateMemoryStates(size_t state_count, bool recycle_old_textures) { DEV_LOG("Allocating {} memory save state slots", state_count); if (state_count != s_state.memory_save_states.size()) { - FreeMemoryStateStorage(); + FreeMemoryStateStorage(recycle_old_textures); s_state.memory_save_states.resize(state_count); } @@ -2522,7 +2522,7 @@ bool System::AllocateMemoryStates(size_t state_count) ERROR_LOG("Failed to allocate {} memory save states: {}", s_state.memory_save_states.size(), error.GetDescription()); ERROR_LOG("Disabling runahead/rewind."); - FreeMemoryStateStorage(); + FreeMemoryStateStorage(false); s_state.runahead_frames = 0; s_state.memory_save_state_front = 0; s_state.memory_save_state_count = 0; @@ -2536,48 +2536,16 @@ bool System::AllocateMemoryStates(size_t state_count) return true; } -void System::ClearMemorySaveStates(bool reallocate_resources) +void System::ClearMemorySaveStates(bool reallocate_resources, bool recycle_textures) { s_state.memory_save_state_front = 0; s_state.memory_save_state_count = 0; if (reallocate_resources && !s_state.memory_save_states.empty()) - AllocateMemoryStates(s_state.memory_save_states.size()); + AllocateMemoryStates(s_state.memory_save_states.size(), recycle_textures); } -void System::FreeMemoryStateTextures() -{ - // TODO: use non-copyable function, that way we don't need to store raw pointers - std::vector textures; - bool gpu_thread_synced = false; - - for (MemorySaveState& mss : s_state.memory_save_states) - { - if ((mss.vram_texture || !mss.gpu_state_data.empty()) && !gpu_thread_synced) - { - gpu_thread_synced = true; - GPUThread::SyncGPUThread(true); - } - - if (mss.vram_texture) - { - if (textures.empty()) - textures.reserve(s_state.memory_save_states.size()); - - textures.push_back(mss.vram_texture.release()); - } - } - - if (!textures.empty()) - { - GPUThread::RunOnThread([textures = std::move(textures)]() mutable { - for (GPUTexture* texture : textures) - g_gpu_device->RecycleTexture(std::unique_ptr(texture)); - }); - } -} - -void System::FreeMemoryStateStorage() +void System::FreeMemoryStateStorage(bool recycle_textures) { // TODO: use non-copyable function, that way we don't need to store raw pointers std::vector textures; @@ -2607,9 +2575,14 @@ void System::FreeMemoryStateStorage() if (!textures.empty()) { - GPUThread::RunOnThread([textures = std::move(textures)]() mutable { + GPUThread::RunOnThread([textures = std::move(textures), recycle_textures]() mutable { for (GPUTexture* texture : textures) - g_gpu_device->RecycleTexture(std::unique_ptr(texture)); + { + if (recycle_textures) + g_gpu_device->RecycleTexture(std::unique_ptr(texture)); + else + delete texture; + } }); } @@ -2919,7 +2892,7 @@ bool System::LoadStateFromBuffer(const SaveStateBuffer& buffer, Error* error, bo if (g_settings.HasAnyPerGameMemoryCards()) UpdatePerGameMemoryCards(); - ClearMemorySaveStates(false); + ClearMemorySaveStates(false, false); // Updating game/loading settings can turn on hardcore mode. Catch this. Achievements::DisableHardcoreMode(); @@ -4005,7 +3978,7 @@ bool System::InsertMedia(const char* path) if (IsGPUDumpPath(path)) [[unlikely]] return ChangeGPUDump(path); - ClearMemorySaveStates(true); + ClearMemorySaveStates(true, true); Error error; std::unique_ptr image = CDImage::Open(path, g_settings.cdrom_load_image_patches, &error); @@ -4044,7 +4017,7 @@ bool System::InsertMedia(const char* path) void System::RemoveMedia() { - ClearMemorySaveStates(true); + ClearMemorySaveStates(true, true); CDROM::RemoveMedia(false); } @@ -4254,7 +4227,7 @@ bool System::SwitchMediaSubImage(u32 index) if (!CDROM::HasMedia()) return false; - ClearMemorySaveStates(true); + ClearMemorySaveStates(true, true); std::unique_ptr image = CDROM::RemoveMedia(true); Assert(image); @@ -4308,7 +4281,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings) { if (IsValid()) { - ClearMemorySaveStates(false); + ClearMemorySaveStates(false, false); if (g_settings.cpu_overclock_active != old_settings.cpu_overclock_active || (g_settings.cpu_overclock_active && @@ -4448,7 +4421,8 @@ void System::CheckForSettingsChanges(const Settings& old_settings) GPUThread::UpdateSettings(true, false); // NOTE: Must come after the GPU thread settings update, otherwise it allocs the wrong size textures. - ClearMemorySaveStates(true); + const bool use_existing_textures = (g_settings.gpu_resolution_scale == old_settings.gpu_resolution_scale); + ClearMemorySaveStates(true, use_existing_textures); if (IsPaused()) GPUThread::PresentCurrentFrame(); @@ -4884,7 +4858,8 @@ void System::CalculateRewindMemoryUsage(u32 num_saves, u32 resolution_scale, u64 void System::UpdateMemorySaveStateSettings() { - FreeMemoryStateStorage(); + const bool any_memory_states_active = (g_settings.IsRunaheadEnabled() || g_settings.rewind_enable); + FreeMemoryStateStorage(any_memory_states_active); if (IsReplayingGPUDump()) [[unlikely]] { @@ -4926,7 +4901,7 @@ void System::UpdateMemorySaveStateSettings() // allocate storage for memory save states if (num_slots > 0) - AllocateMemoryStates(num_slots); + AllocateMemoryStates(num_slots, true); // reenter execution loop, don't want to try to save a state now if runahead was turned off InterruptExecution(); @@ -5024,7 +4999,7 @@ bool System::DoRunahead() s_state.runahead_replay_frames = s_state.memory_save_state_count; // and throw away all the states, forcing us to catch up below - ClearMemorySaveStates(false); + ClearMemorySaveStates(false, false); // run the frames with no audio SPU::SetAudioOutputMuted(true); diff --git a/src/core/system.h b/src/core/system.h index 33d7b02a5..80e4d116e 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -418,7 +418,7 @@ void RequestDisplaySize(float scale = 0.0f); // Memory Save States (Rewind and Runahead) ////////////////////////////////////////////////////////////////////////// void CalculateRewindMemoryUsage(u32 num_saves, u32 resolution_scale, u64* ram_usage, u64* vram_usage); -void ClearMemorySaveStates(bool reallocate_resources); +void ClearMemorySaveStates(bool reallocate_resources, bool recycle_textures); void SetRunaheadReplayFlag(); /// Shared socket multiplexer, used by PINE/GDB/etc. diff --git a/src/core/system_private.h b/src/core/system_private.h index 046bec245..74b874a22 100644 --- a/src/core/system_private.h +++ b/src/core/system_private.h @@ -26,9 +26,8 @@ struct MemorySaveState MemorySaveState& AllocateMemoryState(); MemorySaveState& GetFirstMemoryState(); MemorySaveState& PopMemoryState(); -bool AllocateMemoryStates(size_t state_count); -void FreeMemoryStateTextures(); -void FreeMemoryStateStorage(); +bool AllocateMemoryStates(size_t state_count, bool recycle_old_textures); +void FreeMemoryStateStorage(bool recycle_texture); void LoadMemoryState(MemorySaveState& mss, bool update_display); void SaveMemoryState(MemorySaveState& mss);