aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-31 22:53:16 -0400
committerkraibse <33869366+kraibse@users.noreply.github.com>2026-11-01 17:29:56 +0100
commit139b7e5efb7c4181f14e987706eef26ca1f327c0 (patch)
tree0d6ce4243e0eb9566c997c780f1917040c8ac2f3
parent4d6a13e3d9eaa7bf17381fce0e50d5275ac8957e (diff)
nx2: count a mapped sparse tile as written
With remap support Mario Kart World fills nine world-sized masks (R8 16384x16384, RG16 and R16 8192x8192, one sparse region each) in its own memory and then maps them in through 216. Nothing writes them through the sparse region, so its blocks stayed unwritten and the texture cache kept the images over them without memory, reading zero: the static world (roads, buildings, rocks, trees) rendered unlit while karts and cars were lit. Without remap support the game never maps them and lights the world without them. A tile map now counts its blocks as written and invalidates the caches over the tile, so those images get memory and load what the game mapped. That costs the masks' full size in device memory (about 2.9 GB in Mario Kart World), which is what they need until the texture cache can back single images tile by tile. DeviceMemoryManager::Unmap no longer needs its flag to keep the caches.
-rw-r--r--src/core/device_memory_manager.h4
-rw-r--r--src/core/device_memory_manager.inc6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp7
-rw-r--r--src/core/hle/service/nx2_gsv.h12
4 files changed, 18 insertions, 11 deletions
diff --git a/src/core/device_memory_manager.h b/src/core/device_memory_manager.h
index cdf3996aa6..27ff342213 100644
--- a/src/core/device_memory_manager.h
+++ b/src/core/device_memory_manager.h
@@ -59,9 +59,7 @@ public:
void Map(DAddr address, VAddr virtual_address, size_t size, Asid asid, bool track = false);
- /// invalidate = false leaves the caches over the range as they are (a Switch 2 sparse tile the
- /// game remaps and then writes itself).
- void Unmap(DAddr address, size_t size, bool invalidate = true);
+ void Unmap(DAddr address, size_t size);
void TrackContinuityImpl(DAddr address, VAddr virtual_address, size_t size, Asid asid);
void TrackContinuity(DAddr address, VAddr virtual_address, size_t size, Asid asid) {
diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc
index abb65f6922..83faa1b3f3 100644
--- a/src/core/device_memory_manager.inc
+++ b/src/core/device_memory_manager.inc
@@ -245,12 +245,10 @@ void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size
}
template <typename Traits>
-void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size, bool invalidate) {
+void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) {
size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
- if (invalidate) {
- device_inter->InvalidateRegion(address, size);
- }
+ device_inter->InvalidateRegion(address, 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
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index cb23eea59f..e66c72cd26 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -260,13 +260,12 @@ void nvhost_as_gpu::Nx2SparseTile(u64 gpu_address, u64 cpu_address, u64 size) {
return;
}
// In GPU-thread order: command lists submitted before the request still see the old tile.
- // Unmapping first keeps the SMMU's reverse map right. The caches over the tile stay: the game
- // writes a tile after mapping it, and an invalidation here would make the texture cache give
- // memory to (and re-upload) every array layer the game only mapped.
+ // Unmapping first keeps the SMMU's reverse map right and invalidates the caches over the tile,
+ // whose contents are now what the game's memory holds there.
const DAddr device = device_address + (gpu_address - start);
auto& smmu = system.Host1x().MemoryManager();
system.GPU().RunInOrder([&smmu, device, cpu_address, size, asid = session->asid] {
- smmu.Unmap(device, size, false);
+ smmu.Unmap(device, size);
if (cpu_address != 0) {
smmu.Map(device, cpu_address, size, asid, true);
}
diff --git a/src/core/hle/service/nx2_gsv.h b/src/core/hle/service/nx2_gsv.h
index 6d6634b16b..44d2b16240 100644
--- a/src/core/hle/service/nx2_gsv.h
+++ b/src/core/hle/service/nx2_gsv.h
@@ -986,6 +986,18 @@ private:
if (!region->tiles) {
StartTiles(start, *region);
}
+ // The game's memory already holds what the tile shows (title 1 fills nine world-sized
+ // masks, R8/RG16/R16 at 8192-16384 texels, in its own memory before mapping them): the
+ // blocks count as written, so the texture cache gives the images over them memory and
+ // loads them. Left unwritten, those images read zero and the static world was unlit.
+ const u64 first_block = (gpu_address - start) / SparseBlockSize;
+ const u64 last_block = (gpu_address + size - 1 - start) / SparseBlockSize;
+ for (u64 block = first_block; block <= last_block; ++block) {
+ if (region->blocks[block] == BlockEmpty) {
+ region->blocks[block] = BlockWritten;
+ }
+ Tegra::Nx2SparseBlockWritten(start, block);
+ }
registry.MapSparseTile(gpu_address, cpu_address, size);
MarkTiles(*region, gpu_address - start, size, true);
}