System: Improve texture recycling when changing rewind/runahead settings

Fix suprious failures when changing rewind settings when low on VRAM.
This commit is contained in:
Stenzek 2025-01-01 19:24:15 +10:00
parent 8605722cdf
commit a08acdb93a
No known key found for this signature in database
5 changed files with 51 additions and 66 deletions

View File

@ -332,21 +332,32 @@ bool GPUBackend::AllocateMemorySaveStates(std::span<System::MemorySaveState> sta
for (size_t i = 0; i < states.size(); i++) for (size_t i = 0; i < states.size(); i++)
g_gpu_device->RecycleTexture(std::move(states[i].vram_texture)); 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++) for (size_t i = 0; i < states.size(); i++)
{ {
if (!backend->AllocateMemorySaveState(states[i], error)) if (!backend->AllocateMemorySaveState(states[i], error))
{ {
// Free anything that was allocated. // Try flushing the pool.
for (size_t j = 0; j <= i; i++) 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(); // Free anything that was allocated.
states[j].vram_texture.reset(); for (size_t j = 0; j <= i; i++)
result = false; {
return; states[j].state_data.deallocate();
states[j].vram_texture.reset();
result = false;
return;
}
} }
} }
} }
backend->RestoreDeviceContext();
result = true; result = true;
}, },
true, false); true, false);

View File

@ -59,7 +59,7 @@ static void HotkeyModifyResolutionScale(s32 increment)
if (System::IsValid()) if (System::IsValid())
{ {
System::ClearMemorySaveStates(true); System::ClearMemorySaveStates(true, false);
GPUThread::UpdateSettings(true, false); GPUThread::UpdateSettings(true, false);
} }
} }
@ -373,7 +373,7 @@ DEFINE_HOTKEY("TogglePGXP", TRANSLATE_NOOP("Hotkeys", "Graphics"), TRANSLATE_NOO
[](s32 pressed) { [](s32 pressed) {
if (!pressed && System::IsValid()) if (!pressed && System::IsValid())
{ {
System::ClearMemorySaveStates(true); System::ClearMemorySaveStates(true, true);
g_settings.gpu_pgxp_enable = !g_settings.gpu_pgxp_enable; g_settings.gpu_pgxp_enable = !g_settings.gpu_pgxp_enable;
GPUThread::UpdateSettings(true, false); GPUThread::UpdateSettings(true, false);
@ -451,7 +451,7 @@ DEFINE_HOTKEY("TogglePGXPDepth", TRANSLATE_NOOP("Hotkeys", "Graphics"),
if (!g_settings.gpu_pgxp_enable) if (!g_settings.gpu_pgxp_enable)
return; return;
System::ClearMemorySaveStates(true); System::ClearMemorySaveStates(true, true);
g_settings.gpu_pgxp_depth_buffer = !g_settings.gpu_pgxp_depth_buffer; g_settings.gpu_pgxp_depth_buffer = !g_settings.gpu_pgxp_depth_buffer;
GPUThread::UpdateSettings(true, false); GPUThread::UpdateSettings(true, false);
@ -471,7 +471,7 @@ DEFINE_HOTKEY("TogglePGXPCPU", TRANSLATE_NOOP("Hotkeys", "Graphics"), TRANSLATE_
if (!g_settings.gpu_pgxp_enable) if (!g_settings.gpu_pgxp_enable)
return; return;
System::ClearMemorySaveStates(true); System::ClearMemorySaveStates(true, true);
// GPU thread is unchanged // GPU thread is unchanged
g_settings.gpu_pgxp_cpu = !g_settings.gpu_pgxp_cpu; g_settings.gpu_pgxp_cpu = !g_settings.gpu_pgxp_cpu;

View File

@ -1167,7 +1167,7 @@ DiscRegion System::GetRegionForPsf(const char* path)
void System::RecreateGPU(GPURenderer renderer) void System::RecreateGPU(GPURenderer renderer)
{ {
FreeMemoryStateTextures(); FreeMemoryStateStorage(false);
StopMediaCapture(); StopMediaCapture();
Error error; Error error;
@ -1177,7 +1177,7 @@ void System::RecreateGPU(GPURenderer renderer)
Panic("Failed to switch renderer."); Panic("Failed to switch renderer.");
} }
ClearMemorySaveStates(true); ClearMemorySaveStates(true, false);
} }
void System::LoadSettings(bool display_osd_messages) void System::LoadSettings(bool display_osd_messages)
@ -1954,7 +1954,7 @@ void System::DestroySystem()
if (g_settings.inhibit_screensaver) if (g_settings.inhibit_screensaver)
PlatformMisc::ResumeScreensaver(); PlatformMisc::ResumeScreensaver();
FreeMemoryStateStorage(); FreeMemoryStateStorage(false);
Cheats::UnloadAll(); Cheats::UnloadAll();
PCDrv::Shutdown(); PCDrv::Shutdown();
@ -2495,13 +2495,13 @@ System::MemorySaveState& System::PopMemoryState()
return s_state.memory_save_states[s_state.memory_save_state_front]; 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); DEV_LOG("Allocating {} memory save state slots", state_count);
if (state_count != s_state.memory_save_states.size()) if (state_count != s_state.memory_save_states.size())
{ {
FreeMemoryStateStorage(); FreeMemoryStateStorage(recycle_old_textures);
s_state.memory_save_states.resize(state_count); 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_LOG("Failed to allocate {} memory save states: {}", s_state.memory_save_states.size(),
error.GetDescription()); error.GetDescription());
ERROR_LOG("Disabling runahead/rewind."); ERROR_LOG("Disabling runahead/rewind.");
FreeMemoryStateStorage(); FreeMemoryStateStorage(false);
s_state.runahead_frames = 0; s_state.runahead_frames = 0;
s_state.memory_save_state_front = 0; s_state.memory_save_state_front = 0;
s_state.memory_save_state_count = 0; s_state.memory_save_state_count = 0;
@ -2536,48 +2536,16 @@ bool System::AllocateMemoryStates(size_t state_count)
return true; 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_front = 0;
s_state.memory_save_state_count = 0; s_state.memory_save_state_count = 0;
if (reallocate_resources && !s_state.memory_save_states.empty()) 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() void System::FreeMemoryStateStorage(bool recycle_textures)
{
// TODO: use non-copyable function, that way we don't need to store raw pointers
std::vector<GPUTexture*> 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<GPUTexture>(texture));
});
}
}
void System::FreeMemoryStateStorage()
{ {
// TODO: use non-copyable function, that way we don't need to store raw pointers // TODO: use non-copyable function, that way we don't need to store raw pointers
std::vector<GPUTexture*> textures; std::vector<GPUTexture*> textures;
@ -2607,9 +2575,14 @@ void System::FreeMemoryStateStorage()
if (!textures.empty()) if (!textures.empty())
{ {
GPUThread::RunOnThread([textures = std::move(textures)]() mutable { GPUThread::RunOnThread([textures = std::move(textures), recycle_textures]() mutable {
for (GPUTexture* texture : textures) for (GPUTexture* texture : textures)
g_gpu_device->RecycleTexture(std::unique_ptr<GPUTexture>(texture)); {
if (recycle_textures)
g_gpu_device->RecycleTexture(std::unique_ptr<GPUTexture>(texture));
else
delete texture;
}
}); });
} }
@ -2919,7 +2892,7 @@ bool System::LoadStateFromBuffer(const SaveStateBuffer& buffer, Error* error, bo
if (g_settings.HasAnyPerGameMemoryCards()) if (g_settings.HasAnyPerGameMemoryCards())
UpdatePerGameMemoryCards(); UpdatePerGameMemoryCards();
ClearMemorySaveStates(false); ClearMemorySaveStates(false, false);
// Updating game/loading settings can turn on hardcore mode. Catch this. // Updating game/loading settings can turn on hardcore mode. Catch this.
Achievements::DisableHardcoreMode(); Achievements::DisableHardcoreMode();
@ -4005,7 +3978,7 @@ bool System::InsertMedia(const char* path)
if (IsGPUDumpPath(path)) [[unlikely]] if (IsGPUDumpPath(path)) [[unlikely]]
return ChangeGPUDump(path); return ChangeGPUDump(path);
ClearMemorySaveStates(true); ClearMemorySaveStates(true, true);
Error error; Error error;
std::unique_ptr<CDImage> image = CDImage::Open(path, g_settings.cdrom_load_image_patches, &error); std::unique_ptr<CDImage> image = CDImage::Open(path, g_settings.cdrom_load_image_patches, &error);
@ -4044,7 +4017,7 @@ bool System::InsertMedia(const char* path)
void System::RemoveMedia() void System::RemoveMedia()
{ {
ClearMemorySaveStates(true); ClearMemorySaveStates(true, true);
CDROM::RemoveMedia(false); CDROM::RemoveMedia(false);
} }
@ -4254,7 +4227,7 @@ bool System::SwitchMediaSubImage(u32 index)
if (!CDROM::HasMedia()) if (!CDROM::HasMedia())
return false; return false;
ClearMemorySaveStates(true); ClearMemorySaveStates(true, true);
std::unique_ptr<CDImage> image = CDROM::RemoveMedia(true); std::unique_ptr<CDImage> image = CDROM::RemoveMedia(true);
Assert(image); Assert(image);
@ -4308,7 +4281,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
{ {
if (IsValid()) if (IsValid())
{ {
ClearMemorySaveStates(false); ClearMemorySaveStates(false, false);
if (g_settings.cpu_overclock_active != old_settings.cpu_overclock_active || if (g_settings.cpu_overclock_active != old_settings.cpu_overclock_active ||
(g_settings.cpu_overclock_active && (g_settings.cpu_overclock_active &&
@ -4448,7 +4421,8 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
GPUThread::UpdateSettings(true, false); GPUThread::UpdateSettings(true, false);
// NOTE: Must come after the GPU thread settings update, otherwise it allocs the wrong size textures. // 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()) if (IsPaused())
GPUThread::PresentCurrentFrame(); GPUThread::PresentCurrentFrame();
@ -4884,7 +4858,8 @@ void System::CalculateRewindMemoryUsage(u32 num_saves, u32 resolution_scale, u64
void System::UpdateMemorySaveStateSettings() 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]] if (IsReplayingGPUDump()) [[unlikely]]
{ {
@ -4926,7 +4901,7 @@ void System::UpdateMemorySaveStateSettings()
// allocate storage for memory save states // allocate storage for memory save states
if (num_slots > 0) 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 // reenter execution loop, don't want to try to save a state now if runahead was turned off
InterruptExecution(); InterruptExecution();
@ -5024,7 +4999,7 @@ bool System::DoRunahead()
s_state.runahead_replay_frames = s_state.memory_save_state_count; s_state.runahead_replay_frames = s_state.memory_save_state_count;
// and throw away all the states, forcing us to catch up below // and throw away all the states, forcing us to catch up below
ClearMemorySaveStates(false); ClearMemorySaveStates(false, false);
// run the frames with no audio // run the frames with no audio
SPU::SetAudioOutputMuted(true); SPU::SetAudioOutputMuted(true);

View File

@ -418,7 +418,7 @@ void RequestDisplaySize(float scale = 0.0f);
// Memory Save States (Rewind and Runahead) // Memory Save States (Rewind and Runahead)
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void CalculateRewindMemoryUsage(u32 num_saves, u32 resolution_scale, u64* ram_usage, u64* vram_usage); 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(); void SetRunaheadReplayFlag();
/// Shared socket multiplexer, used by PINE/GDB/etc. /// Shared socket multiplexer, used by PINE/GDB/etc.

View File

@ -26,9 +26,8 @@ struct MemorySaveState
MemorySaveState& AllocateMemoryState(); MemorySaveState& AllocateMemoryState();
MemorySaveState& GetFirstMemoryState(); MemorySaveState& GetFirstMemoryState();
MemorySaveState& PopMemoryState(); MemorySaveState& PopMemoryState();
bool AllocateMemoryStates(size_t state_count); bool AllocateMemoryStates(size_t state_count, bool recycle_old_textures);
void FreeMemoryStateTextures(); void FreeMemoryStateStorage(bool recycle_texture);
void FreeMemoryStateStorage();
void LoadMemoryState(MemorySaveState& mss, bool update_display); void LoadMemoryState(MemorySaveState& mss, bool update_display);
void SaveMemoryState(MemorySaveState& mss); void SaveMemoryState(MemorySaveState& mss);