diff --git a/src/graphics/host_gpu/renderer/cache/bufferCache.cpp b/src/graphics/host_gpu/renderer/cache/bufferCache.cpp index 9df8b3615..b8a8b74a4 100644 --- a/src/graphics/host_gpu/renderer/cache/bufferCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/bufferCache.cpp @@ -110,6 +110,7 @@ void BufferCache::DeleteBuffer(BufferId id) { } } +template bool BufferCache::DownloadBufferMemory(Buffer& buffer, uint64_t vaddr, uint64_t size) { std::vector copies; uint64_t total_size = 0; @@ -170,14 +171,22 @@ bool BufferCache::DownloadBufferMemory(Buffer& buffer, uint64_t vaddr, uint64_t vk::PipelineStageFlagBits::eAllCommands | vk::PipelineStageFlagBits::eHost, {}, 0, nullptr, 1, &after, 0, nullptr); - m_scheduler.DeferPriorityOperation([this, mapped, offset, total_size, buffer_address, - copies = std::move(copies), owner = std::move(temporary)] { + auto publish = [this, mapped, offset, total_size, buffer_address, + copies = std::move(copies), owner = std::move(temporary)] { (owner ? *owner : m_download_buffer).Invalidate(offset, total_size); for (const auto& copy: copies) { Libs::LibKernel::Memory::WriteBacking(buffer_address + copy.srcOffset, mapped + (copy.dstOffset - offset), copy.size); } - }); + }; + if constexpr (async) { + m_scheduler.DeferPriorityOperation(std::move(publish)); + } else { + const auto tick = m_scheduler.CurrentTick(); + m_scheduler.Wait(tick); + m_scheduler.WaitPriorityOperations(tick); + publish(); + } return true; } @@ -256,10 +265,7 @@ void BufferCache::ReadMemory(uint64_t vaddr, uint64_t size, bool is_write) { const auto window_begin = std::max(Common::AlignDown(vaddr, WindowSize), buffer_begin); const auto window_end = std::min(std::max(window_begin + WindowSize, vaddr + size), buffer_end); - if (DownloadBufferMemory(buffer, window_begin, window_end - window_begin)) { - const auto tick = m_scheduler.CurrentTick(); - m_scheduler.Wait(tick); - m_scheduler.WaitPriorityOperations(tick); + if (DownloadBufferMemory(buffer, window_begin, window_end - window_begin)) { m_memory_tracker.UnmarkRegionAsGpuModified(window_begin, window_end - window_begin); } if (is_write) { @@ -614,7 +620,7 @@ void BufferCache::RunGarbageCollector() { return false; } if (dirty) { - EXIT_IF(!DownloadBufferMemory(buffer, buffer.CpuAddress(), buffer.Size())); + EXIT_NOT_IMPLEMENTED(!DownloadBufferMemory(buffer, buffer.CpuAddress(), buffer.Size())); dirty_buffers.push_back(id); } else { m_memory_tracker.UntrackMemory(buffer.CpuAddress(), buffer.Size()); diff --git a/src/graphics/host_gpu/renderer/cache/bufferCache.h b/src/graphics/host_gpu/renderer/cache/bufferCache.h index aa969a9dd..1212bd535 100644 --- a/src/graphics/host_gpu/renderer/cache/bufferCache.h +++ b/src/graphics/host_gpu/renderer/cache/bufferCache.h @@ -117,7 +117,8 @@ private: [[nodiscard]] vk::Buffer UploadCopies(Buffer& buffer, std::span copies, uint64_t total_size); [[nodiscard]] bool SynchronizeBufferFromImage(Buffer& buffer, uint64_t vaddr, uint64_t size); - // Queues backing publication; callers wait before clearing dirty pages or reusing their data. + // Synchronous downloads publish before returning; asynchronous callers wait before reuse. + template [[nodiscard]] bool DownloadBufferMemory(Buffer& buffer, uint64_t vaddr, uint64_t size); GraphicContext& m_graphics; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 1be1349f1..7644326f8 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -4208,7 +4208,30 @@ public: cache.FillBuffer(base + offset, sizeof(window_value), window_value, false); } + const auto window_publication_tick = scheduler.CurrentTick(); + std::binary_semaphore window_publication_entered{0}; + std::binary_semaphore release_window_publication{0}; + std::atomic window_readback_returned{false}; + scheduler.DeferPriorityOperation([&] { + window_publication_entered.release(); + release_window_publication.acquire(); + Libs::LibKernel::Memory::WriteBacking( + base + window_inside_offset, &window_stale, sizeof(window_stale)); + }); + std::jthread release_window_callback([&] { + window_publication_entered.acquire(); + const auto deadline = std::chrono::steady_clock::now() + + std::chrono::milliseconds(100); + while (!window_readback_returned.load() && + std::chrono::steady_clock::now() < deadline) { + std::this_thread::yield(); + } + release_window_publication.release(); + }); cache.ReadMemory(base + window_fault_offset, sizeof(window_value)); + window_readback_returned = true; + release_window_callback.join(); + scheduler.WaitPriorityOperations(window_publication_tick); uint32_t window_inside_backing = 0; uint32_t window_outside_backing = 0; Libs::LibKernel::Memory::TryReadBacking(base + window_inside_offset, @@ -4217,7 +4240,7 @@ public: Libs::LibKernel::Memory::TryReadBacking(base + window_outside_offset, &window_outside_backing, sizeof(window_outside_backing)); - Require(name, "widened-window boundary", + Require(name, "widened-window publication order and boundary", window_inside_backing == window_value && window_outside_backing == window_stale && !cache.HasGpuDirtyBytes(base + window_inside_offset, @@ -4228,7 +4251,8 @@ public: sizeof(window_value)) && cache.IsRegionGpuModified(base + window_outside_offset, sizeof(window_value)), - "readback did not honor the clamped half-open 512 KiB window"); + "readback did not publish after the older overlapping callback " + "or honor the clamped half-open 512 KiB window"); cache.ReadMemory(base + window_outside_offset, sizeof(window_value)); Libs::LibKernel::Memory::WriteBacking(base + first_offset, &first_stale,