From 5c2fdf4e603b7c28baa759f75b6f90e00ff63bd9 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Thu, 21 Jan 2021 19:08:15 -0300 Subject: [PATCH 1/3] video_core/memory_manager: Add GPU address based flush method Allow flushing rasterizer contents based on a GPU address. --- src/video_core/memory_manager.cpp | 15 +++++++++++++++ src/video_core/memory_manager.h | 2 ++ 2 files changed, 17 insertions(+) diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 65feff588d..1926bb025c 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -314,6 +314,21 @@ void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buf } } +void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const { + size_t remaining_size{size}; + size_t page_index{gpu_addr >> page_bits}; + size_t page_offset{gpu_addr & page_mask}; + while (remaining_size > 0) { + const size_t num_bytes{std::min(page_size - page_offset, remaining_size)}; + if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) { + rasterizer->FlushRegion(*page_addr + page_offset, num_bytes); + } + ++page_index; + page_offset = 0; + remaining_size -= num_bytes; + } +} + void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) { std::vector tmp_buffer(size); ReadBlock(gpu_src_addr, tmp_buffer.data(), size); diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index c35e576894..e3c0c53a90 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -131,6 +131,8 @@ private: void TryLockPage(PageEntry page_entry, std::size_t size); void TryUnlockPage(PageEntry page_entry, std::size_t size); + void FlushRegion(GPUVAddr gpu_addr, size_t size) const; + [[nodiscard]] static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) { return (gpu_addr >> page_bits) & page_table_mask; } From 85390839b08fb283a1867b4e42a3304f0865dc69 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Thu, 21 Jan 2021 19:08:43 -0300 Subject: [PATCH 2/3] video_core/memory_manager: Flush destination buffer on CopyBlock When we copy into a buffer, it might contain data modified from the GPU on the same pages. Because of this, we have to flush the contents before writing new data. An alternative approach would be to write the data in place, but games can also write data in other ways, invalidating our contents. Fixes geometry in Zombie Panic in Wonderland DX. --- src/video_core/memory_manager.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 1926bb025c..e5d357773e 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -332,6 +332,10 @@ void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const { void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) { std::vector tmp_buffer(size); ReadBlock(gpu_src_addr, tmp_buffer.data(), size); + + // The output block must be flushed in case it has data modified from the GPU. + // Fixes NPC geometry in Zombie Panic in Wonderland DX + FlushRegion(gpu_dest_addr, size); WriteBlock(gpu_dest_addr, tmp_buffer.data(), size); } From 220ba5933cdccacd961bc305036a3cf6cc221aa9 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Thu, 21 Jan 2021 19:14:49 -0300 Subject: [PATCH 3/3] video_core/memory_manager: Remove unused CopyBlockUnsafe This function was not being used. --- src/video_core/memory_manager.cpp | 7 ------- src/video_core/memory_manager.h | 1 - 2 files changed, 8 deletions(-) diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index e5d357773e..f5cdf548e5 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -339,13 +339,6 @@ void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std WriteBlock(gpu_dest_addr, tmp_buffer.data(), size); } -void MemoryManager::CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, - std::size_t size) { - std::vector tmp_buffer(size); - ReadBlockUnsafe(gpu_src_addr, tmp_buffer.data(), size); - WriteBlockUnsafe(gpu_dest_addr, tmp_buffer.data(), size); -} - bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const { const auto cpu_addr{GpuToCpuAddress(gpu_addr)}; if (!cpu_addr) { diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index e3c0c53a90..a52fbbd8c4 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -107,7 +107,6 @@ public: */ void ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; void WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); - void CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size); /** * IsGranularRange checks if a gpu region can be simply read with a pointer.