From c84d6054261ca7535981c10859b6932c31389c63 Mon Sep 17 00:00:00 2001 From: MaranBr Date: Fri, 29 May 2026 14:01:06 +0200 Subject: [PATCH] [buffer_cache] Fix buffer upload overwriting GPU-modified regions (#4000) This fixes a bug in Super Mario Odyssey, in Bowser's Kingdom, where particles rapidly freeze and unfreeze in midair. It also fixes vertex explosions in Super Mario Party Jamboree. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4000 Reviewed-by: Lizzie Reviewed-by: Maufeat --- src/video_core/buffer_cache/buffer_cache.h | 40 +++++++++++++++------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 0d8d37ec0c..b36dd46176 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1619,24 +1619,38 @@ void BufferCache

::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept { template bool BufferCache

::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 size) { upload_copies.clear(); - u64 total_size_bytes = 0; + u64 staging_offset = 0; u64 largest_copy = 0; - const DAddr buffer_start = buffer.cpu_addr_cached; - memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) { - upload_copies.push_back(BufferCopy{ - .src_offset = total_size_bytes, - .dst_offset = device_addr_out - buffer_start, - .size = range_size, + DAddr buffer_start = buffer.CpuAddr(); + auto push = [&](u64 start, u64 end) { + if (start >= end) { + return; + } + u64 sz = end - start; + upload_copies.push_back({ + .src_offset = staging_offset, + .dst_offset = start - buffer_start, + .size = sz }); - total_size_bytes += range_size; - largest_copy = (std::max)(largest_copy, range_size); + staging_offset += sz; + largest_copy = (std::max)(largest_copy, sz); + }; + memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 addr, u64 range_size) { + u64 start = addr; + u64 end = addr + range_size; + gpu_modified_ranges.ForEachInRange(start, range_size, [&](u64 gstart, u64 gsize) { + u64 gend = gstart + gsize; + push(start, gstart); + start = (std::max)(start, gend); + }); + push(start, end); + ClearDownload(addr, range_size); + gpu_modified_ranges.Subtract(addr, range_size); }); - if (total_size_bytes == 0) { + if (upload_copies.empty()) { return true; } - const std::span copies_span(upload_copies.data(), upload_copies.size()); - UploadMemory(buffer, total_size_bytes, largest_copy, copies_span); - any_buffer_uploaded = true; + UploadMemory(buffer, staging_offset, largest_copy, std::span(upload_copies)); return false; }