[gpu] reduce queue stalls

This commit is contained in:
ParantezTech
2026-07-18 23:02:25 +03:00
parent da9f132226
commit d81d643227
+40 -59
View File
@@ -568,6 +568,8 @@ public static partial class AgcExports
public ulong WorkSequence { get; set; } public ulong WorkSequence { get; set; }
public ulong SubmissionSequence { get; set; } public ulong SubmissionSequence { get; set; }
public bool WaitMonitorRunning { get; set; } public bool WaitMonitorRunning { get; set; }
public object WaitMonitorSignalGate { get; } = new();
public long WaitMonitorSignalVersion { get; set; }
} }
private readonly record struct RegisteredAgcResource( private readonly record struct RegisteredAgcResource(
@@ -3313,24 +3315,11 @@ public static partial class AgcExports
length, length,
op, op,
out var dispatch, out var dispatch,
out var indirectDimsRetryAddress)) out _))
{ {
state.FrameDispatchCount++; state.FrameDispatchCount++;
ObserveComputeDispatch(ctx, gpuState, state, dispatch); ObserveComputeDispatch(ctx, gpuState, state, dispatch);
} }
else if (indirectDimsRetryAddress != 0 &&
HandleSubmittedIndirectDimsWait(
ctx,
state,
commandAddress,
currentAddress,
offset,
dwordCount,
indirectDimsRetryAddress,
tracePackets))
{
return true; // suspend until the producer computes the dims
}
} }
if (op == ItNop && if (op == ItNop &&
@@ -3668,27 +3657,11 @@ public static partial class AgcExports
void CompleteAndWake() void CompleteAndWake()
{ {
CompleteLabelProducer(producer); CompleteLabelProducer(producer);
if (GpuWaitRegistry.Count == 0) lock (gpuState.WaitMonitorSignalGate)
{ {
return; gpuState.WaitMonitorSignalVersion++;
Monitor.Pulse(gpuState.WaitMonitorSignalGate);
} }
// Resuming a DCB can enqueue another compute dispatch and wait for
// it. Never do that reentrantly on the Vulkan render thread.
ThreadPool.UnsafeQueueUserWorkItem(
static state =>
{
var (resumeContext, resumeGpuState) = state;
lock (resumeGpuState.Gate)
{
DrainResumableDcbs(
resumeContext,
resumeGpuState,
tracePackets: _traceAgc);
}
},
(ctx, gpuState),
preferLocal: false);
} }
void ApplyAndQueueCompletion() void ApplyAndQueueCompletion()
@@ -4867,38 +4840,45 @@ public static partial class AgcExports
SubmittedGpuState gpuState) SubmittedGpuState gpuState)
{ {
var delayMilliseconds = 1; var delayMilliseconds = 1;
long observedSignal;
lock (gpuState.WaitMonitorSignalGate)
{
observedSignal = gpuState.WaitMonitorSignalVersion;
}
while (true) while (true)
{ {
var madeProgress = false; int resumed;
int remaining;
lock (gpuState.Gate) lock (gpuState.Gate)
{ {
var before = GpuWaitRegistry.CountForMemory(ctx.Memory); resumed = DrainResumableDcbs(ctx, gpuState, tracePackets: _traceAgc);
if (before == 0) remaining = GpuWaitRegistry.CountForMemory(ctx.Memory);
{ if (_traceAgc && resumed != 0)
gpuState.WaitMonitorRunning = false;
return;
}
DrainResumableDcbs(ctx, gpuState, tracePackets: _traceAgc);
var after = GpuWaitRegistry.CountForMemory(ctx.Memory);
madeProgress = after < before;
if (madeProgress)
{ {
Console.Error.WriteLine( Console.Error.WriteLine(
$"[LOADER][TRACE] agc.wait_monitor_resumed count={before - after} " + $"[LOADER][TRACE] agc.wait_monitor_resumed count={resumed} " +
$"remaining={after}"); $"remaining={remaining}");
} }
if (after == 0) if (remaining == 0)
{ {
gpuState.WaitMonitorRunning = false; gpuState.WaitMonitorRunning = false;
return; return;
} }
} }
delayMilliseconds = madeProgress delayMilliseconds = resumed != 0
? 1 ? 1
: Math.Min(delayMilliseconds * 2, 16); : Math.Min(delayMilliseconds * 2, 16);
Thread.Sleep(delayMilliseconds); lock (gpuState.WaitMonitorSignalGate)
{
if (gpuState.WaitMonitorSignalVersion == observedSignal)
{
Monitor.Wait(gpuState.WaitMonitorSignalGate, delayMilliseconds);
}
observedSignal = gpuState.WaitMonitorSignalVersion;
}
} }
} }
@@ -4952,16 +4932,17 @@ public static partial class AgcExports
// guest memory (labels are advanced by ReleaseMem/WriteData/DmaData packets // guest memory (labels are advanced by ReleaseMem/WriteData/DmaData packets
// or direct CPU writes) and resumes the ones now satisfied. A resumed DCB // or direct CPU writes) and resumes the ones now satisfied. A resumed DCB
// can itself write labels that unblock others, so loop to a fixed point. // can itself write labels that unblock others, so loop to a fixed point.
private static void DrainResumableDcbs( private static int DrainResumableDcbs(
CpuContext ctx, CpuContext ctx,
SubmittedGpuState gpuState, SubmittedGpuState gpuState,
bool tracePackets) bool tracePackets)
{ {
if (!_gpuWaitSuspendEnabled) if (!_gpuWaitSuspendEnabled)
{ {
return; return 0;
} }
var resumedCount = 0;
for (var pass = 0; pass < 256; pass++) for (var pass = 0; pass < 256; pass++)
{ {
var woken = GpuWaitRegistry.CollectSatisfied(ctx.Memory, (address, is64Bit) => var woken = GpuWaitRegistry.CollectSatisfied(ctx.Memory, (address, is64Bit) =>
@@ -5039,7 +5020,7 @@ public static partial class AgcExports
} }
} }
return; return resumedCount;
} }
if (woken is not null) if (woken is not null)
@@ -5047,9 +5028,12 @@ public static partial class AgcExports
foreach (var waiter in woken) foreach (var waiter in woken)
{ {
ResumeSuspendedDcb(ctx, gpuState, waiter, tracePackets); ResumeSuspendedDcb(ctx, gpuState, waiter, tracePackets);
resumedCount++;
} }
} }
} }
return resumedCount;
} }
private static void ResumeSuspendedDcb( private static void ResumeSuspendedDcb(
@@ -8894,7 +8878,7 @@ public static partial class AgcExports
out _); out _);
var globalMemoryBuffers = var globalMemoryBuffers =
CreateTranslatedComputeGlobalBuffers(evaluation); CreateTranslatedComputeGlobalBuffers(evaluation);
var workSequence = GuestGpu.Current.SubmitComputeDispatch( GuestGpu.Current.SubmitComputeDispatch(
shaderAddress, shaderAddress,
computeShader, computeShader,
textures, textures,
@@ -8913,12 +8897,9 @@ public static partial class AgcExports
dispatch.ThreadCountX, dispatch.ThreadCountX,
dispatch.ThreadCountY, dispatch.ThreadCountY,
dispatch.ThreadCountZ); dispatch.ThreadCountZ);
// Vulkan queue order keeps dependent dispatches coherent. CPU visibility is
// published by explicit PM4 release/write actions instead of per dispatch.
gpuDispatch = true; gpuDispatch = true;
if (writesGlobalMemory &&
!GuestGpu.Current.WaitForGuestWork(workSequence))
{
computeError = $"global-write-sync-timeout sequence={workSequence}";
}
} }
} }