From 98e7eeb72a5b19e0a367458143cc230eeb644e2c Mon Sep 17 00:00:00 2001 From: nmzik Date: Sun, 6 Sep 2026 05:36:41 +0400 Subject: [PATCH] textures: keep dirty pages during image-to-buffer sync --- .../host_gpu/renderer/cache/bufferCache.cpp | 64 +++---------------- tests/ShaderRecompilerComputeTests.cpp | 20 ++++-- 2 files changed, 25 insertions(+), 59 deletions(-) diff --git a/src/graphics/host_gpu/renderer/cache/bufferCache.cpp b/src/graphics/host_gpu/renderer/cache/bufferCache.cpp index aa4071ff5..e61fdfea4 100644 --- a/src/graphics/host_gpu/renderer/cache/bufferCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/bufferCache.cpp @@ -501,37 +501,18 @@ std::pair BufferCache::ObtainBufferForImage(uint64_t vaddr, u if (!GuestRange {vaddr, size}.Valid()) { EXIT("BufferCache: invalid image source\n"); } - auto find_owner = [&]() -> Buffer* { - const auto* owner = m_page_table.Find(vaddr >> PageTable::kPageBits); - if (owner == nullptr || !*owner) { - return nullptr; - } + const auto* owner = m_page_table.Find(vaddr >> PageTable::kPageBits); + if (owner != nullptr && *owner) { auto& buffer = m_slot_buffers[*owner]; - return buffer.IsInBounds(vaddr, size) ? &buffer : nullptr; - }; - - { - const bool cpu_modified = m_memory_tracker.IsRegionCpuModified(vaddr, size); - const bool gpu_modified = m_memory_tracker.IsRegionGpuModified(vaddr, size); - const bool has_dirty_buffer_source = m_gpu_modified_ranges.Intersects(vaddr, size); - m_memory_tracker.ValidateGpuDirtyOwnership(m_gpu_modified_ranges, vaddr, size, - "image source"); - - auto* owner = find_owner(); - if (has_dirty_buffer_source && owner == nullptr) { - if (!IsRegionRegistered(vaddr, size)) { - EXIT("BufferCache: GPU-dirty image source has no native buffer\n"); - } - owner = &m_slot_buffers[FindBuffer(vaddr, size)]; - } - if (owner != nullptr && !cpu_modified && (!gpu_modified || has_dirty_buffer_source)) { - TouchBuffer(*owner); - return {owner, owner->Offset(vaddr)}; - } - if (has_dirty_buffer_source && owner == nullptr) { - EXIT("BufferCache: GPU-dirty image source could not resolve its native owner\n"); + if (buffer.IsInBounds(vaddr, size)) { + TouchBuffer(buffer); + (void)SynchronizeBuffer(buffer, vaddr, size, false, false); + return {&buffer, buffer.Offset(vaddr)}; } } + if (IsRegionGpuModified(vaddr, size)) { + return ObtainBuffer(vaddr, size, false, false); + } auto [staging, stage_offset] = m_staging_buffer.Map(size, 16); if (staging == nullptr || (!Libs::LibKernel::Memory::TryReadBacking(vaddr, staging, size) && @@ -539,32 +520,7 @@ std::pair BufferCache::ObtainBufferForImage(uint64_t vaddr, u EXIT("BufferCache: failed to read mapped guest image backing\n"); } m_staging_buffer.Commit(); - - const bool has_dirty_buffer_source = m_gpu_modified_ranges.Intersects(vaddr, size); - auto* owner = find_owner(); - if (has_dirty_buffer_source && owner == nullptr) { - EXIT("BufferCache: GPU-dirty image source lost its native owner\n"); - } - if (owner == nullptr || - (m_memory_tracker.IsRegionGpuModified(vaddr, size) && !has_dirty_buffer_source)) { - return {&m_staging_buffer, stage_offset}; - } - - TouchBuffer(*owner); - std::vector> uploads; - m_memory_tracker.ForEachUploadRange( - vaddr, size, false, - [&](uint64_t address, uint64_t upload_size) noexcept { - uploads.emplace_back(address, upload_size); - }, - [&]() noexcept { - for (const auto& [address, upload_size]: uploads) { - owner->CopyFrom(m_scheduler.Current(), m_staging_buffer, - stage_offset + address - vaddr, owner->Offset(address), upload_size, - vk::AccessFlagBits::eHostWrite); - } - }); - return {owner, owner->Offset(vaddr)}; + return {&m_staging_buffer, stage_offset}; } void BufferCache::WriteHostMemory(uint64_t vaddr, std::span data) { diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 8443763ef..0e1224557 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -5585,6 +5585,15 @@ public: "same-page CPU write did not invalidate cached ownership"); std::memcpy(memory + partial_clean_offset, &partial_cpu_refresh_value, sizeof(partial_cpu_refresh_value)); + constexpr std::array partial_neighbors{0x2468ace0u, + 0xfedcba98u}; + std::memcpy(memory + partial_clean_offset - sizeof(uint32_t), + &partial_neighbors[0], sizeof(uint32_t)); + std::memcpy(memory + partial_clean_offset + sizeof(uint32_t), + &partial_neighbors[1], sizeof(uint32_t)); + std::vector partial_cpu_refresh_expected(TRACKER_PAGE_SIZE / sizeof(u32)); + std::memcpy(partial_cpu_refresh_expected.data(), memory + partial_image_offset, + TRACKER_PAGE_SIZE); const auto partial_cpu_refresh_source = resources.GetBufferCache().ObtainBufferForImage( base + partial_clean_offset, sizeof(partial_cpu_refresh_value)); @@ -5593,11 +5602,11 @@ public: "CPU-dirty same-page bytes did not resolve through the cached " "buffer"); auto partial_cpu_refresh_readback = - CreateHostBuffer(name, sizeof(partial_cpu_refresh_value), + CreateHostBuffer(name, TRACKER_PAGE_SIZE, vk::BufferUsageFlagBits::eTransferDst, {0}); const vk::BufferCopy partial_cpu_refresh_copy{ - partial_cpu_refresh_source.second, 0, - sizeof(partial_cpu_refresh_value)}; + partial_cpu_refresh_source.first->Offset(base + partial_image_offset), 0, + TRACKER_PAGE_SIZE}; command.Handle().copyBuffer(partial_cpu_refresh_source.first->Handle(), partial_cpu_refresh_readback.buffer, 1, &partial_cpu_refresh_copy); @@ -6443,8 +6452,9 @@ public: "exact-format recreation initialized from " "stale guest bytes"); Require(name, "partial-page CPU refresh content", - ReadBuffer(name, partial_cpu_refresh_readback, 1) == - std::vector{partial_cpu_refresh_value}, + ReadBuffer(name, partial_cpu_refresh_readback, + partial_cpu_refresh_expected.size()) == + partial_cpu_refresh_expected, "cached buffer uploaded bytes outside the exact " "staged guest range"); const auto mixed_source_words = ReadBuffer(