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++)
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);

View File

@ -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;

View File

@ -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<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()
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;
@ -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<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())
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<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()
{
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<CDImage> 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);

View File

@ -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.

View File

@ -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);