From 889a0fd2de5b19623b986845f773970b76ca61d8 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 22 Aug 2026 16:10:23 +1000 Subject: [PATCH] GPU/TextureCache: Fix last_vram_write and num_splits not being serialized --- src/core/gpu_hw_texture_cache.cpp | 36 +++++++++++++++++++++++-------- src/core/save_state_version.h | 2 +- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/core/gpu_hw_texture_cache.cpp b/src/core/gpu_hw_texture_cache.cpp index 37b496f98..c13962fea 100644 --- a/src/core/gpu_hw_texture_cache.cpp +++ b/src/core/gpu_hw_texture_cache.cpp @@ -41,6 +41,7 @@ #include #include +#include #include #include @@ -53,10 +54,12 @@ LOG_CHANNEL(GPU_HW); namespace GPUTextureCache { static constexpr u32 MAX_CLUT_SIZE = 256; static constexpr u32 NUM_PAGE_DRAW_RECTS = 4; +static constexpr u32 INVALID_VRAM_WRITE_INDEX = std::numeric_limits::max(); static constexpr const GSVector4i& INVALID_RECT = GPU_HW::INVALID_RECT; static constexpr const GPUTextureFormat REPLACEMENT_TEXTURE_FORMAT = GPUTextureFormat::RGBA8; static constexpr const char LOCAL_CONFIG_FILENAME[] = "config.yaml"; +static constexpr u32 STATE_VRAM_WRITE_SIZE = sizeof(GSVector4i) * 2 + sizeof(HashType) + sizeof(u32); static constexpr u32 STATE_PALETTE_RECORD_SIZE = sizeof(GSVector4i) + sizeof(SourceKey) + sizeof(PaletteRecordFlags) + sizeof(HashType) + sizeof(u16) * MAX_CLUT_SIZE; @@ -666,12 +669,13 @@ bool GPUTextureCache::GetStateSize(StateWrapper& sw, u32* size) if (!sw.DoMarker("GPUTextureCache")) [[unlikely]] return false; - u32 num_vram_writes = 0; + u32 num_vram_writes = 0, last_vram_write_index = 0; sw.Do(&num_vram_writes); + sw.DoEx(&last_vram_write_index, 86, INVALID_VRAM_WRITE_INDEX); for (u32 i = 0; i < num_vram_writes; i++) { - sw.SkipBytes(sizeof(GSVector4i) * 2 + sizeof(HashType)); + sw.SkipBytes((sw.GetVersion() < 86) ? (STATE_VRAM_WRITE_SIZE - 4) : STATE_VRAM_WRITE_SIZE); u32 num_palette_records = 0; sw.Do(&num_palette_records); @@ -704,30 +708,33 @@ bool GPUTextureCache::DoState(StateWrapper& sw, bool skip) if (!skip) Invalidate(); - u32 num_vram_writes = 0; + u32 num_vram_writes = 0, last_vram_write_index = 0; sw.Do(&num_vram_writes); + sw.DoEx(&last_vram_write_index, 86, INVALID_VRAM_WRITE_INDEX); const bool skip_writes = (skip || !s_state.track_vram_writes); for (u32 i = 0; i < num_vram_writes; i++) { - static constexpr u32 PALETTE_RECORD_SIZE = sizeof(GSVector4i) + sizeof(SourceKey) + sizeof(PaletteRecordFlags) + - sizeof(HashType) + sizeof(u16) * MAX_CLUT_SIZE; - if (skip_writes) { - sw.SkipBytes(sizeof(GSVector4i) * 2 + sizeof(HashType)); + // pre-v86 states are missing num_splits. + sw.SkipBytes((sw.GetVersion() < 86) ? (STATE_VRAM_WRITE_SIZE - 4) : STATE_VRAM_WRITE_SIZE); u32 num_palette_records = 0; sw.Do(&num_palette_records); - sw.SkipBytes(num_palette_records * PALETTE_RECORD_SIZE); + sw.SkipBytes(num_palette_records * STATE_PALETTE_RECORD_SIZE); } else { VRAMWrite* vrw = new VRAMWrite(); + if (i == last_vram_write_index) + s_state.last_vram_write = vrw; + DoStateVector(sw, &vrw->active_rect); DoStateVector(sw, &vrw->write_rect); sw.Do(&vrw->hash); + sw.DoEx(&vrw->num_splits, 86, 0u); u32 num_palette_records = 0; sw.Do(&num_palette_records); @@ -748,7 +755,7 @@ bool GPUTextureCache::DoState(StateWrapper& sw, bool skip) } else { - sw.SkipBytes(num_palette_records * PALETTE_RECORD_SIZE); + sw.SkipBytes(num_palette_records * STATE_PALETTE_RECORD_SIZE); } if (sw.HasError()) @@ -793,11 +800,22 @@ bool GPUTextureCache::DoState(StateWrapper& sw, bool skip) u32 num_vram_writes = static_cast(s_state.temp_vram_write_list.size()); sw.Do(&num_vram_writes); + + u32 last_vram_write_index = INVALID_VRAM_WRITE_INDEX; + if (s_state.last_vram_write) + { + const auto iter = std::ranges::find(s_state.temp_vram_write_list, s_state.last_vram_write); + if (iter != s_state.temp_vram_write_list.end()) + last_vram_write_index = static_cast(std::distance(s_state.temp_vram_write_list.begin(), iter)); + } + sw.Do(&last_vram_write_index); + for (VRAMWrite* vrw : s_state.temp_vram_write_list) { DoStateVector(sw, &vrw->active_rect); DoStateVector(sw, &vrw->write_rect); sw.Do(&vrw->hash); + sw.Do(&vrw->num_splits); u32 num_palette_records = static_cast(vrw->palette_records.size()); sw.Do(&num_palette_records); diff --git a/src/core/save_state_version.h b/src/core/save_state_version.h index 4e8448755..b274bc0fd 100644 --- a/src/core/save_state_version.h +++ b/src/core/save_state_version.h @@ -6,7 +6,7 @@ #include "common/types.h" inline constexpr u32 SAVE_STATE_MAGIC = 0x43435544; -inline constexpr u32 SAVE_STATE_VERSION = 85; +inline constexpr u32 SAVE_STATE_VERSION = 86; inline constexpr u32 SAVE_STATE_MINIMUM_VERSION = 42; static_assert(SAVE_STATE_VERSION >= SAVE_STATE_MINIMUM_VERSION);