[buffer_cache] Fix buffer upload overwriting GPU-modified regions (#4000)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run

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 <lizzie@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
This commit is contained in:
MaranBr 2026-05-29 14:01:06 +02:00 committed by crueter
parent def03f6589
commit c84d605426
No known key found for this signature in database
GPG key ID: 425ACD2D4830EBC6

View file

@ -1619,24 +1619,38 @@ void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept {
template <class P>
bool BufferCache<P>::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<BufferCopy> 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;
}