AGC: reduce inactive trace and draw allocations (#308)

This commit is contained in:
kuba
2026-07-17 03:25:26 +02:00
committed by GitHub
parent dbd74654c4
commit f544146d6d
+127 -67
View File
@@ -3729,13 +3729,16 @@ public static partial class AgcExports
_labelProducers.Add(producer); _labelProducers.Add(producer);
} }
foreach (var waiting in GpuWaitRegistry.SnapshotInRange(memory, address, length)) if (_traceAgc)
{ {
TraceAgc( foreach (var waiting in GpuWaitRegistry.SnapshotInRange(memory, address, length))
$"agc.wait_producer_scheduled label=0x{waiting.Address:X16} " + {
$"waiters={waiting.Count} producer_seq={producer.Sequence} " + TraceAgc(
$"queue={producer.QueueName} submission={producer.SubmissionId} " + $"agc.wait_producer_scheduled label=0x{waiting.Address:X16} " +
$"packet=0x{packetAddress:X16} action='{debugName}'"); $"waiters={waiting.Count} producer_seq={producer.Sequence} " +
$"queue={producer.QueueName} submission={producer.SubmissionId} " +
$"packet=0x{packetAddress:X16} action='{debugName}'");
}
} }
return producer; return producer;
@@ -3753,16 +3756,19 @@ public static partial class AgcExports
producer.Completed = true; producer.Completed = true;
} }
foreach (var waiting in GpuWaitRegistry.SnapshotInRange( if (_traceAgc)
producer.Memory,
producer.Address,
producer.Length))
{ {
TraceAgc( foreach (var waiting in GpuWaitRegistry.SnapshotInRange(
$"agc.wait_producer_completed label=0x{waiting.Address:X16} " + producer.Memory,
$"waiters={waiting.Count} producer_seq={producer.Sequence} " + producer.Address,
$"queue={producer.QueueName} submission={producer.SubmissionId} " + producer.Length))
$"action='{producer.DebugName}'"); {
TraceAgc(
$"agc.wait_producer_completed label=0x{waiting.Address:X16} " +
$"waiters={waiting.Count} producer_seq={producer.Sequence} " +
$"queue={producer.QueueName} submission={producer.SubmissionId} " +
$"action='{producer.DebugName}'");
}
} }
} }
@@ -3807,6 +3813,14 @@ public static partial class AgcExports
} }
} }
// Producer-backed waits are trace-only. Keep the producer lookup above
// because producerless waits are always warned, but do not build the
// detailed condition strings when AGC tracing is disabled.
if (producer is not null && !_traceAgc)
{
return;
}
var prefix = stale ? "agc.wait_stale" : "agc.wait_suspended"; var prefix = stale ? "agc.wait_stale" : "agc.wait_suspended";
var current = currentValue.HasValue var current = currentValue.HasValue
? $"0x{currentValue.Value:X16}" ? $"0x{currentValue.Value:X16}"
@@ -6088,54 +6102,39 @@ public static partial class AgcExports
_graphicsShaderCache.TryAdd(shaderKey, compiled); _graphicsShaderCache.TryAdd(shaderKey, compiled);
} }
var imageBindings = pixelEvaluation.ImageBindings
.Concat(exportEvaluation.ImageBindings);
var textures = new List<TranslatedImageBinding>( var textures = new List<TranslatedImageBinding>(
pixelEvaluation.ImageBindings.Count + pixelEvaluation.ImageBindings.Count +
exportEvaluation.ImageBindings.Count); exportEvaluation.ImageBindings.Count);
foreach (var binding in imageBindings) if (!TryAppendTranslatedImageBindings(
pixelEvaluation.ImageBindings,
textures,
pixelShaderAddress,
exportShaderAddress,
out error) ||
!TryAppendTranslatedImageBindings(
exportEvaluation.ImageBindings,
textures,
pixelShaderAddress,
exportShaderAddress,
out error))
{ {
if (!TryDecodeTextureDescriptor(binding.ResourceDescriptor, out var texture)) ReturnPooledEvaluationArrays(exportEvaluation);
{ ReturnPooledEvaluationArrays(pixelEvaluation);
// A garbage/zeroed texture descriptor (from a per-draw descriptor return false;
// setup race — the same root as scalar-load-failed) would drop
// the whole draw, so Demon's Souls' deferred-lighting/composite
// passes that produce the composite's feeder targets never run
// and the frame stays black. Substitute a 1x1 fallback binding so
// the pass still renders (that one sampled texture reads black)
// rather than dropping it entirely. STRICT reverts.
if (_strictShaderDescriptors)
{
error = $"invalid texture descriptor at pc=0x{binding.Pc:X}";
ReturnPooledEvaluationArrays(exportEvaluation);
ReturnPooledEvaluationArrays(pixelEvaluation);
return false;
}
texture = new TextureDescriptor(
0, 1, 1, Gen5TextureFormatR8G8B8A8Unorm, 0, 0, 0, 0, 0, 1, 0xFAC);
}
if (_traceAgcShader || _tracePixelShaderAddress == pixelShaderAddress)
{
Console.Error.WriteLine(
"[LOADER][TRACE] " +
$"agc.texture_binding ps=0x{pixelShaderAddress:X16} es=0x{exportShaderAddress:X16} " +
$"pc=0x{binding.Pc:X} op={binding.Opcode} storage={(Gen5ShaderTranslator.IsStorageImageOperation(binding.Opcode) ? 1 : 0)} " +
$"decoded={FormatTextureDescriptor(texture)} " +
$"raw={FormatShaderDwords(binding.ResourceDescriptor)} sampler={FormatShaderDwords(binding.SamplerDescriptor)}");
}
textures.Add(
new TranslatedImageBinding(
texture,
Gen5ShaderTranslator.IsStorageImageOperation(binding.Opcode),
binding.MipLevel ?? 0,
binding.SamplerDescriptor));
} }
var globalMemoryBindings = pixelEvaluation.GlobalMemoryBindings var globalMemoryBindings = new Gen5GlobalMemoryBinding[
.Concat(exportEvaluation.GlobalMemoryBindings) pixelEvaluation.GlobalMemoryBindings.Count +
.ToArray(); exportEvaluation.GlobalMemoryBindings.Count];
for (var index = 0; index < pixelEvaluation.GlobalMemoryBindings.Count; index++)
{
globalMemoryBindings[index] = pixelEvaluation.GlobalMemoryBindings[index];
}
for (var index = 0; index < exportEvaluation.GlobalMemoryBindings.Count; index++)
{
globalMemoryBindings[pixelEvaluation.GlobalMemoryBindings.Count + index] =
exportEvaluation.GlobalMemoryBindings[index];
}
IReadOnlyList<Gen5VertexInputBinding> vertexInputs = IReadOnlyList<Gen5VertexInputBinding> vertexInputs =
exportEvaluation.VertexInputs ?? []; exportEvaluation.VertexInputs ?? [];
state.UcRegisters.TryGetValue(VgtPrimitiveType, out var primitiveType); state.UcRegisters.TryGetValue(VgtPrimitiveType, out var primitiveType);
@@ -6150,6 +6149,13 @@ public static partial class AgcExports
renderTargets[index].NumberType); renderTargets[index].NumberType);
} }
var pixelUserDataCount = Math.Min(pixelEvaluation.InitialScalarRegisters.Count, 8);
var pixelUserData = new uint[pixelUserDataCount];
for (var index = 0; index < pixelUserDataCount; index++)
{
pixelUserData[index] = pixelEvaluation.InitialScalarRegisters[index];
}
draw = new TranslatedGuestDraw( draw = new TranslatedGuestDraw(
exportShaderAddress, exportShaderAddress,
pixelShaderAddress, pixelShaderAddress,
@@ -6171,7 +6177,7 @@ public static partial class AgcExports
textures, textures,
vertexInputs, vertexInputs,
pixelEvaluation.InitialScalarRegisters), pixelEvaluation.InitialScalarRegisters),
pixelEvaluation.InitialScalarRegisters.Take(8).ToArray(), pixelUserData,
state.CxRegisters.TryGetValue(CbBlend0Control, out var rawBlend) ? rawBlend : 0, state.CxRegisters.TryGetValue(CbBlend0Control, out var rawBlend) ? rawBlend : 0,
state.CxRegisters.TryGetValue( state.CxRegisters.TryGetValue(
CbColor0Info + renderTargets.FirstOrDefault().Slot * CbColorRegisterStride, CbColor0Info + renderTargets.FirstOrDefault().Slot * CbColorRegisterStride,
@@ -6183,6 +6189,55 @@ public static partial class AgcExports
return true; return true;
} }
private static bool TryAppendTranslatedImageBindings(
IReadOnlyList<Gen5ImageBinding> bindings,
List<TranslatedImageBinding> textures,
ulong pixelShaderAddress,
ulong exportShaderAddress,
out string error)
{
foreach (var binding in bindings)
{
if (!TryDecodeTextureDescriptor(binding.ResourceDescriptor, out var texture))
{
// A garbage/zeroed texture descriptor (from a per-draw descriptor
// setup race — the same root as scalar-load-failed) would drop
// the whole draw, so deferred-lighting/composite passes that
// produce the composite's feeder targets never run. Keep the
// existing 1x1 fallback unless strict diagnostics are requested.
if (_strictShaderDescriptors)
{
error = $"invalid texture descriptor at pc=0x{binding.Pc:X}";
return false;
}
texture = new TextureDescriptor(
0, 1, 1, Gen5TextureFormatR8G8B8A8Unorm, 0, 0, 0, 0, 0, 1, 0xFAC);
}
var isStorage =
Gen5ShaderTranslator.IsStorageImageOperation(binding.Opcode);
if (_traceAgcShader || _tracePixelShaderAddress == pixelShaderAddress)
{
Console.Error.WriteLine(
"[LOADER][TRACE] " +
$"agc.texture_binding ps=0x{pixelShaderAddress:X16} es=0x{exportShaderAddress:X16} " +
$"pc=0x{binding.Pc:X} op={binding.Opcode} storage={(isStorage ? 1 : 0)} " +
$"decoded={FormatTextureDescriptor(texture)} " +
$"raw={FormatShaderDwords(binding.ResourceDescriptor)} sampler={FormatShaderDwords(binding.SamplerDescriptor)}");
}
textures.Add(
new TranslatedImageBinding(
texture,
isStorage,
binding.MipLevel ?? 0,
binding.SamplerDescriptor));
}
error = string.Empty;
return true;
}
private static int _tracedAstroTitlePixelGlobals; private static int _tracedAstroTitlePixelGlobals;
private static int _tracedAstroTitlePixelGlobalProbe; private static int _tracedAstroTitlePixelGlobalProbe;
@@ -6621,16 +6676,21 @@ public static partial class AgcExports
var target = targets[0]; var target = targets[0];
var scissor = DecodeScissor(registers, target.Width, target.Height); var scissor = DecodeScissor(registers, target.Width, target.Height);
return new GuestRenderState( var blends = new GuestBlendState[targets.Count];
targets.Select(target => for (var index = 0; index < targets.Count; index++)
{
var blend = DecodeBlendState(registers, targets[index].Slot);
blends[index] = blend with
{ {
var blend = DecodeBlendState(registers, target.Slot); WriteMask = blend.WriteMask &
return blend with GetPixelColorExportMask(
{ pixelColorExportMasks,
WriteMask = blend.WriteMask & targets[index].Slot),
GetPixelColorExportMask(pixelColorExportMasks, target.Slot), };
}; }
}).ToArray(),
return new GuestRenderState(
blends,
scissor, scissor,
DecodeViewport(registers, target.Width, target.Height, scissor), DecodeViewport(registers, target.Width, target.Height, scissor),
DecodeRasterState(registers), DecodeRasterState(registers),