aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-30 13:41:36 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-30 13:41:49 -0400
commitddd367a8152f1c37bfc972475b40afece2da31ac (patch)
tree062c859069aead4a429c2ecb2d836bab4812fb05 /src
parentea33ad1865970ee731e5f74dbcc26ca472798afe (diff)
core: end device page runs where pages are unmapped or mapped again
WalkBlock copies a run of physically contiguous device pages at once, using each page's continuity_tracker. Unmap left the counts as they were, Map without tracking did not touch them, and the pages before a changed range kept counting into it. A read starting on such a page then copied the host memory that followed the old mapping, or, starting on an unmapped page, zero-filled the whole old run, mapped pages after it included. Splatoon Raiders lost its dialogue text this way: the glyph vertex shader reads its corner table from constant buffer 1 (the program's constants, at device 0x5617d700), and the buffer cache uploaded the range from 0x5617c000, an unmapped page still counted as the start of a long run, so the table reached the GPU as zeros and every glyph sampled one empty texel. Unmap and Map now reset the counts of their pages (Map recounts them when tracking) and cut the runs of the pages before them at the range start.
Diffstat (limited to 'src')
-rw-r--r--src/core/device_memory_manager.h2
-rw-r--r--src/core/device_memory_manager.inc21
2 files changed, 23 insertions, 0 deletions
diff --git a/src/core/device_memory_manager.h b/src/core/device_memory_manager.h
index 46cda14e78..9c034f01a1 100644
--- a/src/core/device_memory_manager.h
+++ b/src/core/device_memory_manager.h
@@ -187,6 +187,8 @@ private:
void WalkBlock(const DAddr addr, const std::size_t size, auto on_unmapped, auto on_memory,
auto increment);
+ void CutContinuityAt(size_t page);
+
void InnerGatherDeviceAddresses(Common::ScratchBuffer<u32>& buffer, PAddr address);
std::unique_ptr<DeviceMemoryManagerAllocator<Traits>> impl;
diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc
index d78a229e1f..c45bc71656 100644
--- a/src/core/device_memory_manager.inc
+++ b/src/core/device_memory_manager.inc
@@ -211,9 +211,13 @@ void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size
std::scoped_lock lk(mapping_guard);
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages);
+ CutContinuityAt(start_page_d);
for (size_t i = 0; i < num_pages; i++) {
const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE;
auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress));
+ // One page per run unless tracked below: a run left from an earlier mapping of these
+ // device pages would make WalkBlock copy the host memory that followed that mapping.
+ tracked_entries.GetUnchecked(start_page_d + i).continuity_tracker = 0;
if (ptr == nullptr) [[unlikely]] {
tracked_entries.GetUnchecked(start_page_d + i).compressed_physical_ptr = 0;
continue;
@@ -248,11 +252,13 @@ void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) {
std::scoped_lock lk(mapping_guard);
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages); // should already be committed, but just in case
+ CutContinuityAt(start_page_d);
for (size_t i = 0; i < num_pages; i++) {
auto& entry = tracked_entries.GetUnchecked(start_page_d + i);
auto phys_addr = entry.compressed_physical_ptr;
entry.compressed_physical_ptr = 0;
entry.cpu_backing_address = 0;
+ entry.continuity_tracker = 0;
if (phys_addr != 0) [[likely]] {
u32& base_dev = compressed_device_addr.GetAndFault(phys_addr - 1U);
if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] {
@@ -270,6 +276,21 @@ void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) {
}
translation_generation.fetch_add(1, std::memory_order_release);
}
+
+template <typename Traits>
+void DeviceMemoryManager<Traits>::CutContinuityAt(size_t page) {
+ // Runs counted from the pages before `page` end there: the pages from `page` on are being
+ // mapped again or unmapped. A run is counted down page by page (TrackContinuityImpl), so the
+ // first page whose run already ends before `page` ends the walk.
+ for (size_t index = page; index-- > 0;) {
+ const size_t last_count = page - 1 - index;
+ if (tracked_entries[index].continuity_tracker <= last_count) {
+ break;
+ }
+ tracked_entries.GetUnchecked(index).continuity_tracker = static_cast<u32>(last_count);
+ }
+}
+
template <typename Traits>
void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtual_address,
size_t size, Asid asid) {