diff options
| author | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-11-01 15:14:37 -0500 |
|---|---|---|
| committer | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-11-01 15:14:37 -0500 |
| commit | 91b7400a27a89f1ff9bef7f148913aa76f0a39ad (patch) | |
| tree | 1d2ff177cb93ed6af25ad011bf3b06c0bb9a89c2 /src/video_core/dma_pusher.cpp | |
| parent | 76812d0bd86c3dfbef473ad6236a68045ad3d4b5 (diff) | |
| parent | bef001c1515a13a9b8e361a5a7d372fc2e01094c (diff) | |
Merge remote-tracking branch 'origin/fix/nx2-gpu-written-command-lists' into test/linux-all-prs-plus
# Conflicts:
# src/core/hle/service/nvdrv/nvdrv.cpp
Diffstat (limited to 'src/video_core/dma_pusher.cpp')
| -rw-r--r-- | src/video_core/dma_pusher.cpp | 27 |
1 files changed, 27 insertions, 0 deletions
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 88d571ae5f..8c142b36db 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp @@ -162,6 +162,7 @@ bool DmaPusher::Step() { command_resume_offset += ProcessCommands(commands.subspan(command_resume_offset), command_resume_offset); } else { + FlushGpuWrittenCommands(dma_state.dma_get, size_t{header.size} * sizeof(u32)); Tegra::Memory::GpuGuestMemory<Tegra::CommandHeader, Tegra::Memory::GuestMemoryFlags::UnsafeRead>headers(memory_manager, dma_state.dma_get, header.size, &command_headers); const std::span<const CommandHeader> commands{headers.data(), headers.size()}; RecordParsedEntry(header, commands); @@ -445,6 +446,32 @@ void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) { } } +void DmaPusher::FlushGpuWrittenCommands(GPUVAddr address, size_t size) { + // Title 1 runs small command lists its culling dispatches fill in each frame. Accurate GPU + // mode reads every command list through the caches (safe reads); here only the ones on pages + // the GPU has written are, or their draws run with an older frame's words (trees vanish). + if (!Tegra::g_nx2_ampere.load(std::memory_order_relaxed) || size == 0) { + return; + } + bool written = false; + for (GPUVAddr page = address & ~GPUVAddr{0xfff}; page < address + size; page += 0x1000) { + if (const auto device = memory_manager.GpuToCpuAddress(page); + device && Tegra::Nx2IsGpuWritten(*device)) { + written = true; + break; + } + } + if (!written) { + return; + } + // The caches belong to the split's back: it first runs what the front queued (the dispatch + // that writes these words among it). + VideoCommon::GpuSplit::Get().Drain(); + if (memory_manager.IsMemoryDirty(address, size, VideoCommon::CacheType::BufferCache)) { + memory_manager.FlushRegion(address, size, VideoCommon::CacheType::BufferCache); + } +} + void DmaPusher::ReportUnboundSubchannel() { // Methods sent to a subchannel with no engine (no SetObject yet) are dropped. static std::atomic<u32> reported{}; |
