aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/engines
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-28 05:41:45 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-28 05:41:45 -0400
commitcff4add442d78e088d13005c4c6fdd436b47eecd (patch)
treec4bcb200d58b5c1f70ebcd9449597a987ad2e6e8 /src/video_core/engines
parent5c4e642c286279f3e44d476550736ed6bff400ab (diff)
nx2: semaphore releases latch the acquires they satisfy
Switch 2 channels that wait for an exact semaphore value poll on the console and see every release. Eden resumes a waiting channel only between command lists and writes releases when fences signal, often several back to back, so an acquire-equal could miss its value and wait forever. Title 4's 3D queue ticks a counter that two other queues wait on value by value; under the loading after the post-customization save one of them missed a tick and the game froze (channel 4 waiting for 0x5293 with the counter at 0x5310). Pending acquires now register with Nx2SemaphoreWaiters before fences run, and every query-cache payload write and QMD add/max release latches the waiters it meets at the moment it is written.
Diffstat (limited to 'src/video_core/engines')
-rw-r--r--src/video_core/engines/kepler_compute.cpp7
-rw-r--r--src/video_core/engines/puller.cpp20
2 files changed, 26 insertions, 1 deletions
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index 85b1f971f7..dc3f9b25b6 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -14,6 +14,7 @@
#include "common/logging.h"
#include "shader_recompiler/exception.h"
#include "core/core.h"
+#include "video_core/nx2.h"
#include "video_core/engines/fermi_2d.h"
#include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h"
@@ -240,6 +241,9 @@ bool KeplerCompute::ProcessNativeMethod(Core::System& system, u32 method, u32 ar
rasterizer->SignalFence([this, address, payload] {
const u32 value = memory_manager.Read<u32>(address) + payload;
memory_manager.Write<u32>(address, value);
+ if (const auto device = memory_manager.GpuToCpuAddress(address)) {
+ Tegra::Nx2SemaphoreWaiters::Get().OnWrite(*device, value);
+ }
static const u64 watch = [] {
const char* text = std::getenv("EDEN_NX2_SEM_WATCH");
return text ? u64{std::strtoull(text, nullptr, 16)} : u64{};
@@ -257,6 +261,9 @@ bool KeplerCompute::ProcessNativeMethod(Core::System& system, u32 method, u32 ar
rasterizer->SignalFence([this, address, payload] {
const u32 value = std::max(memory_manager.Read<u32>(address), payload);
memory_manager.Write<u32>(address, value);
+ if (const auto device = memory_manager.GpuToCpuAddress(address)) {
+ Tegra::Nx2SemaphoreWaiters::Get().OnWrite(*device, value);
+ }
});
} else {
static const u64 watch = [] {
diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp
index 54abf790d2..b607f20161 100644
--- a/src/video_core/engines/puller.cpp
+++ b/src/video_core/engines/puller.cpp
@@ -62,6 +62,15 @@ bool Puller::CanResumeAcquire(DmaPusher& dma_pusher) {
return true;
}
const u32 current = ReadSemaphoreWord(dma_pusher, pending_acquire->address);
+ if (Nx2SemaphoreWaiters::Get().Latched(this)) {
+ if (static std::atomic<u32> rescued{};
+ !pending_acquire->Matches(current) && rescued.fetch_add(1) < 64) {
+ LOG_INFO(HW_GPU, "Channel {} met semaphore {:#x} value {:#x} at its release (now {:#x})",
+ dma_pusher.channel_state.bind_id, pending_acquire->address,
+ pending_acquire->value, current);
+ }
+ return true;
+ }
if (pending_acquire->Matches(current)) {
return true;
}
@@ -87,6 +96,7 @@ bool Puller::ResumeAcquire(DmaPusher& dma_pusher) {
return false;
}
pending_acquire.reset();
+ Nx2SemaphoreWaiters::Get().Remove(this);
return true;
}
@@ -210,9 +220,17 @@ void Puller::ProcessSemaphoreTriggerMethod(DmaPusher& dma_pusher) {
if (acquire.Matches(ReadSemaphoreWord(dma_pusher, acquire.address))) {
return;
}
+ // Registered before the fences run, so a release among them that meets the value counts
+ // even if a later one overwrites it.
+ auto& waiters = Nx2SemaphoreWaiters::Get();
+ if (const auto device = dma_pusher.memory_manager.GpuToCpuAddress(acquire.address)) {
+ waiters.Add(this, *device, acquire.value, acquire.operation);
+ }
dma_pusher.rasterizer->ReleaseFences();
- if (!acquire.Matches(ReadSemaphoreWord(dma_pusher, acquire.address))) {
+ if (!waiters.Latched(this) && !acquire.Matches(ReadSemaphoreWord(dma_pusher, acquire.address))) {
pending_acquire = acquire;
+ } else {
+ waiters.Remove(this);
}
} else {
do {