aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/engines/puller.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/engines/puller.cpp')
-rw-r--r--src/video_core/engines/puller.cpp133
1 files changed, 126 insertions, 7 deletions
diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp
index 9e78d586d1..97a58903fe 100644
--- a/src/video_core/engines/puller.cpp
+++ b/src/video_core/engines/puller.cpp
@@ -4,6 +4,7 @@
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
+#include <atomic>
#include <cstdlib>
#include "common/assert.h"
@@ -30,8 +31,58 @@ namespace {
constexpr u32 Gf100BindClassMask = 0xffff;
constexpr u32 Gf100BindValidMask = 0x1f0000 | Gf100BindClassMask;
+// Reads a semaphore word, flushing a GPU-written copy out of the buffer cache first.
+u32 ReadSemaphoreWord(DmaPusher& dma_pusher, GPUVAddr address) {
+ if (const auto device = dma_pusher.memory_manager.GpuToCpuAddress(address);
+ device && dma_pusher.rasterizer->MustFlushRegion(*device, sizeof(u32),
+ VideoCommon::CacheType::BufferCache)) {
+ dma_pusher.rasterizer->FlushRegion(*device, sizeof(u32),
+ VideoCommon::CacheType::BufferCache);
+ }
+ return dma_pusher.memory_manager.Read<u32>(address);
+}
+
} // Anonymous namespace
+bool Puller::PendingAcquire::Matches(u32 current) const {
+ switch (static_cast<GpuSemaphoreOperation>(operation)) {
+ case GpuSemaphoreOperation::AcquireEqual:
+ return current == value;
+ case GpuSemaphoreOperation::AcquireGequal:
+ return current >= value;
+ case GpuSemaphoreOperation::AcquireMask:
+ return (current & value) != 0;
+ default:
+ return true;
+ }
+}
+
+bool Puller::CanResumeAcquire(DmaPusher& dma_pusher) {
+ if (!pending_acquire) {
+ return true;
+ }
+ const u32 current = ReadSemaphoreWord(dma_pusher, pending_acquire->address);
+ if (pending_acquire->Matches(current)) {
+ return true;
+ }
+ if (!pending_acquire->reported &&
+ std::chrono::steady_clock::now() - pending_acquire->since > std::chrono::seconds{2}) {
+ pending_acquire->reported = true;
+ LOG_WARNING(HW_GPU, "Channel {} waits on semaphore {:#x} (op {}, {:#x}, now {:#x}) for 2 s",
+ dma_pusher.channel_state.bind_id, pending_acquire->address,
+ pending_acquire->operation, pending_acquire->value, current);
+ }
+ return false;
+}
+
+bool Puller::ResumeAcquire(DmaPusher& dma_pusher) {
+ if (!CanResumeAcquire(dma_pusher)) {
+ return false;
+ }
+ pending_acquire.reset();
+ return true;
+}
+
void Puller::ProcessBindMethod(DmaPusher& dma_pusher, const MethodCall& method_call) {
LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {:#x}", method_call.subchannel, method_call.argument);
u32 engine = method_call.argument;
@@ -109,12 +160,39 @@ void Puller::ProcessSemaphoreTriggerMethod(DmaPusher& dma_pusher) {
if (op == GpuSemaphoreOperation::WriteLong) {
const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()};
const u32 payload = regs.semaphore_sequence;
- dma_pusher.rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload, VideoCommon::QueryPropertiesFlags::HasTimeout, payload, 0);
- const u64 source_page = g_nx2_semaphore_source_page;
- if (source_page != 0 && (sequence_address & ~GPUVAddr{0xFFF}) == source_page) {
- const GPUVAddr mirror = g_nx2_semaphore_mirror_page + (sequence_address & 0xFFF);
+ // RELEASE_SIZE (bit 24) set means a 4-byte release without the timestamp; writing 16
+ // bytes there clobbers the words next to it (Switch 2 packs semaphores tightly).
+ const auto properties = g_nx2_ampere && (regs.semaphore_trigger & (1U << 24)) != 0
+ ? VideoCommon::QueryPropertiesFlags::IsAFence
+ : VideoCommon::QueryPropertiesFlags::HasTimeout;
+ dma_pusher.rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload, properties, payload, 0);
+ if (const GPUVAddr mirror = g_nx2_ampere ? Nx2SemaphoreMirror(sequence_address) : 0;
+ mirror != 0) {
LOG_DEBUG(HW_GPU, "Switch 2 semaphore {:#x}={:#x} mirrored to {:#x}", sequence_address, payload, mirror);
- dma_pusher.rasterizer->Query(mirror, VideoCommon::QueryType::Payload, VideoCommon::QueryPropertiesFlags::HasTimeout, payload, 0);
+ dma_pusher.rasterizer->Query(mirror, VideoCommon::QueryType::Payload, properties, payload, 0);
+ }
+ } else if (g_nx2_ampere) {
+ // Switch 2 waits on semaphores the CPU (or another channel) releases, e.g. once it has
+ // written the commands this channel runs next: suspend the channel until the value is
+ // met instead of reading ahead (approach from changes.patch).
+ PendingAcquire acquire{
+ .address = regs.semaphore_address.SemaphoreAddress(),
+ .value = regs.semaphore_sequence,
+ .operation = static_cast<u32>(op),
+ .since = std::chrono::steady_clock::now(),
+ .reported = false,
+ };
+ if (op != GpuSemaphoreOperation::AcquireEqual && op != GpuSemaphoreOperation::AcquireGequal &&
+ op != GpuSemaphoreOperation::AcquireMask) {
+ LOG_ERROR(HW_GPU, "Invalid semaphore operation {}", static_cast<u32>(op));
+ return;
+ }
+ if (acquire.Matches(ReadSemaphoreWord(dma_pusher, acquire.address))) {
+ return;
+ }
+ dma_pusher.rasterizer->ReleaseFences();
+ if (!acquire.Matches(ReadSemaphoreWord(dma_pusher, acquire.address))) {
+ pending_acquire = acquire;
}
} else {
do {
@@ -167,6 +245,36 @@ void Puller::ProcessSemaphoreAcquire(DmaPusher& dma_pusher) {
}
}
+// Ampere SEM_EXECUTE (method 0x1B): operation in bits 2:0 on the semaphore at
+// SEM_ADDR_HI:SEM_ADDR_LO (methods 0x18/0x17) with payload SEM_PAYLOAD_LO (0x19).
+void Puller::ProcessSemaphoreExecute(DmaPusher& dma_pusher, u32 argument) {
+ const GPUVAddr address = (GPUVAddr{regs.reg_array[0x18] & 0x1FFFFFF} << 32) |
+ (regs.reg_array[0x17] & ~3U);
+ const u32 payload = regs.reg_array[0x19];
+ const u32 operation = argument & 7;
+ if (operation == 1) { // RELEASE
+ dma_pusher.rasterizer->Query(address, VideoCommon::QueryType::Payload,
+ VideoCommon::QueryPropertiesFlags::IsAFence, payload, 0);
+ return;
+ }
+ // Acquires: check once after flushing pending fences; never block the GPU thread on them.
+ dma_pusher.rasterizer->ReleaseFences();
+ const u32 word = dma_pusher.memory_manager.Read<u32>(address);
+ const bool ready = operation == 0 ? word == payload
+ : operation == 2 ? word >= payload
+ : operation == 3 ? static_cast<s32>(word - payload) >= 0
+ : operation == 4 ? (word & payload) != 0
+ : operation == 5 ? (word & payload) == 0
+ : true;
+ if (!ready) {
+ static std::atomic<u32> reported{};
+ if (reported.fetch_add(1) < 8) {
+ LOG_WARNING(HW_GPU, "SEM_EXECUTE op {} at {:#x}: {:#x} vs {:#x} not met, continuing",
+ operation, address, word, payload);
+ }
+ }
+}
+
/// Calls a GPU puller method.
void Puller::CallPullerMethod(DmaPusher& dma_pusher, const MethodCall& method_call) {
static const bool nx2_trace = std::getenv("EDEN_NX2_PULLER_TRACE") != nullptr;
@@ -221,11 +329,18 @@ void Puller::CallPullerMethod(DmaPusher& dma_pusher, const MethodCall& method_ca
break;
}
case BufferMethods::SemaphoreAcquire: {
- ProcessSemaphoreAcquire(dma_pusher);
+ // On Ampere host classes 0x17..0x1A are SEM_ADDR_LO/HI and SEM_PAYLOAD_LO/HI.
+ if (!g_nx2_ampere) {
+ ProcessSemaphoreAcquire(dma_pusher);
+ }
break;
}
case BufferMethods::SemaphoreRelease: {
- ProcessSemaphoreRelease(dma_pusher);
+ if (g_nx2_ampere) {
+ ProcessSemaphoreExecute(dma_pusher, method_call.argument);
+ } else {
+ ProcessSemaphoreRelease(dma_pusher);
+ }
break;
}
case BufferMethods::Yield: {
@@ -235,6 +350,10 @@ void Puller::CallPullerMethod(DmaPusher& dma_pusher, const MethodCall& method_ca
}
default:
LOG_ERROR(HW_GPU, "Special puller engine method {:X} not implemented", method);
+ if (static bool dumped = false; g_nx2_ampere && !dumped) {
+ dumped = true;
+ dma_pusher.DumpCurrentEntry("unknown host method");
+ }
break;
}
}