aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/dma_pusher.cpp
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-11-01 15:14:37 -0500
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-11-01 15:14:37 -0500
commit91b7400a27a89f1ff9bef7f148913aa76f0a39ad (patch)
tree1d2ff177cb93ed6af25ad011bf3b06c0bb9a89c2 /src/video_core/dma_pusher.cpp
parent76812d0bd86c3dfbef473ad6236a68045ad3d4b5 (diff)
parentbef001c1515a13a9b8e361a5a7d372fc2e01094c (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.cpp27
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{};