From 558973f201093da443e1f616794391df543ef653 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Tue, 28 Apr 2026 19:56:16 +1000 Subject: [PATCH] GPU/HW: Fix rare VRAM corruption after state load Happened in e.g. Max Power Racing when loading state desynchronized the UBO mask bit flag with the batch flag. --- src/core/gpu_hw.cpp | 39 +++++++++++++++++++++++++-------------- src/core/gpu_hw.h | 2 ++ 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index d2044c424..21a79df9e 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -370,12 +370,33 @@ void GPU_HW::ClearVRAM() ClearFramebuffer(); } -void GPU_HW::LoadState(const GPUBackendLoadStateCommand* cmd) +void GPU_HW::ClearBatch() { DebugAssert((m_batch_vertex_ptr != nullptr) == (m_batch_index_ptr != nullptr)); if (m_batch_vertex_ptr) UnmapGPUBuffer(0, 0); + // Don't desync mask bit etc. + m_batch = {}; + m_batch_ubo_data.u_set_mask_while_drawing = 0; + m_batch_ubo_dirty = true; + + // UpdateVRAMOnGPU() will write depth, no need to do it again. + m_current_depth = 1; +} + +void GPU_HW::PostLoadState() +{ + ClearVRAMDirtyRectangle(); + SetFullVRAMDirtyRectangle(); + UpdateVRAMReadTexture(true, false); + ClearVRAMDirtyRectangle(); +} + +void GPU_HW::LoadState(const GPUBackendLoadStateCommand* cmd) +{ + ClearBatch(); + std::memcpy(g_vram, cmd->vram_data, sizeof(g_vram)); std::memcpy(g_gpu_clut, cmd->clut_data, sizeof(g_gpu_clut)); UpdateVRAMOnGPU(0, 0, VRAM_WIDTH, VRAM_HEIGHT, g_vram, VRAM_WIDTH * sizeof(u16), false, false, VRAM_SIZE_RECT); @@ -388,13 +409,7 @@ void GPU_HW::LoadState(const GPUBackendLoadStateCommand* cmd) Panic("Failed to process texture cache state."); } - m_batch = {}; - m_current_depth = 1; - ClearVRAMDirtyRectangle(); - SetFullVRAMDirtyRectangle(); - UpdateVRAMReadTexture(true, false); - ClearVRAMDirtyRectangle(); - ResetBatchVertexDepth(); + PostLoadState(); } bool GPU_HW::AllocateMemorySaveState(System::MemorySaveState& mss, Error* error) @@ -441,8 +456,7 @@ void GPU_HW::DoMemoryState(StateWrapper& sw, System::MemorySaveState& mss) if (m_batch_vertex_ptr) UnmapGPUBuffer(0, 0); - m_batch = {}; - ResetBatchVertexDepth(); + ClearBatch(); } else { @@ -478,10 +492,7 @@ void GPU_HW::DoMemoryState(StateWrapper& sw, System::MemorySaveState& mss) UpdateVRAMOnGPU(0, 0, VRAM_WIDTH, VRAM_HEIGHT, g_vram, VRAM_WIDTH * sizeof(u16), false, false, VRAM_SIZE_RECT); } - ClearVRAMDirtyRectangle(); - SetFullVRAMDirtyRectangle(); - UpdateVRAMReadTexture(true, false); - ClearVRAMDirtyRectangle(); + PostLoadState(); } else { diff --git a/src/core/gpu_hw.h b/src/core/gpu_hw.h index 5bc5ab13c..4feed4da9 100644 --- a/src/core/gpu_hw.h +++ b/src/core/gpu_hw.h @@ -184,6 +184,8 @@ private: void PrintSettingsToLog(); void CheckSettings(); + void ClearBatch(); + void PostLoadState(); void UpdateVRAMReadTexture(bool drawn, bool written); void UpdateDepthBufferFromMaskBit(); void CopyAndClearDepthBuffer(bool only_drawing_area);