mirror of
https://github.com/par274/sharpemu.git
synced 2026-08-02 16:09:47 +08:00
AGC: correct GS program registers, stop dropping rect-list draws, add a missing size export (#734)
Three defects found while bringing up a PS5 title. None are title-specific. SPI_SHADER_PGM_LO_GS / HI_GS were 0x8A/0x8B, which are actually SPI_SHADER_PGM_RSRC1/RSRC2_GS. Reading them as an address produced a 58-bit value (observed live: 0x30004622C008300). The correct offsets are 0x88/0x89, consistent with SPI_SHADER_PGM_CHKSUM_GS = 0x80 and SPI_SHADER_PGM_LO_ES = 0xC8 already in the table. Draw translation dropped any RECT_LIST draw whose vertex program exported no parameters while the pixel shader had interpolated inputs. A disposition census over a real run measured this deleting ~620 of every 5000 draws (12%). Rect lists are what AMD drivers emit for clears, blits and resolves, so the guard was deleting clears and leaving previous frame contents on screen as trails. Rendering a draw whose interpolants are undefined is strictly better than deleting it. sceAgcDcbDrawIndexIndirectMultiGetSize was unimplemented while sceAgcDcbDrawIndexIndirectMulti emits an eight-dword packet. A title that sizes its command buffer from the missing export under-reserves by three dwords. NID derived with Ps5Nid.Compute, which reproduces the committed NIDs of the neighbouring exports. Tests: AgcContextRegisterTests and AgcShaderStageRegisterTests drive real PM4 packets through sceAgcDriverSubmitDcb and assert what the parser retained for the context and SH register dictionaries, via two new internal accessors; neither dictionary had any test surface, which is why register questions previously cost five-minute game runs.
This commit is contained in:
@@ -95,8 +95,10 @@ public static partial class AgcExports
|
||||
private const uint SpiShaderPgmRsrc1Hs = 0x10A;
|
||||
private const uint SpiShaderPgmLoLs = 0x148;
|
||||
private const uint SpiShaderPgmHiLs = 0x149;
|
||||
private const uint SpiShaderPgmLoGs = 0x8A;
|
||||
private const uint SpiShaderPgmHiGs = 0x8B;
|
||||
// Not 0x8A/0x8B - those are SPI_SHADER_PGM_RSRC1/RSRC2_GS, and reading them
|
||||
// as an address yields a 58-bit value (observed live: 0x30004622C008300).
|
||||
private const uint SpiShaderPgmLoGs = 0x88;
|
||||
private const uint SpiShaderPgmHiGs = 0x89;
|
||||
private const uint SpiShaderPgmChksumGs = 0x80;
|
||||
private const uint SpiPsInputEna = 0x1B3;
|
||||
private const uint SpiPsInputAddr = 0x1B4;
|
||||
@@ -2174,6 +2176,18 @@ public static partial class AgcExports
|
||||
return (int)ctx[CpuRegister.Rax];
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "r98I08t+LOg",
|
||||
ExportName = "sceAgcDcbDrawIndexIndirectMultiGetSize",
|
||||
Target = Generation.Gen5,
|
||||
LibraryName = "libSceAgc")]
|
||||
public static int DcbDrawIndexIndirectMultiGetSize(CpuContext ctx)
|
||||
{
|
||||
// Eight, matching the packet DcbDrawIndexIndirectMulti emits.
|
||||
ctx[CpuRegister.Rax] = 8u * sizeof(uint);
|
||||
return (int)ctx[CpuRegister.Rax];
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "rUuVjyR+Rd4",
|
||||
ExportName = "sceAgcDcbGetLodStatsGetSize",
|
||||
@@ -6426,6 +6440,48 @@ public static partial class AgcExports
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test-only view of a parsed graphics context register. False when the
|
||||
/// register was never written.
|
||||
/// </summary>
|
||||
internal static bool TryGetGraphicsContextRegisterForTests(
|
||||
CpuContext ctx,
|
||||
uint registerOffset,
|
||||
out uint value)
|
||||
{
|
||||
value = 0;
|
||||
if (!_submittedGpuStates.TryGetValue(ctx.Memory, out var gpuState))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (gpuState.Gate)
|
||||
{
|
||||
return gpuState.Graphics.CxRegisters.TryGetValue(registerOffset, out value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SH-register counterpart of <see cref="TryGetGraphicsContextRegisterForTests"/>;
|
||||
/// the shader stage addresses live here.
|
||||
/// </summary>
|
||||
internal static bool TryGetGraphicsShRegisterForTests(
|
||||
CpuContext ctx,
|
||||
uint registerOffset,
|
||||
out uint value)
|
||||
{
|
||||
value = 0;
|
||||
if (!_submittedGpuStates.TryGetValue(ctx.Memory, out var gpuState))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (gpuState.Gate)
|
||||
{
|
||||
return gpuState.Graphics.ShRegisters.TryGetValue(registerOffset, out value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GraphicsDcbSetIndexSize writes VGT_INDEX_TYPE via SET_UCONFIG_REG.
|
||||
/// Mirror that into <see cref="SubmittedDcbState.IndexSize"/>.
|
||||
@@ -7444,22 +7500,6 @@ public static partial class AgcExports
|
||||
}
|
||||
}
|
||||
|
||||
state.UcRegisters.TryGetValue(VgtPrimitiveType, out var earlyPrimitiveType);
|
||||
if (IsRectListPrimitive(earlyPrimitiveType) &&
|
||||
(exportEvaluation.VertexInputs is null || exportEvaluation.VertexInputs.Count == 0) &&
|
||||
!VertexProgramExportsParameters(exportState.Program) &&
|
||||
GetInterpolatedAttributeCount(pixelState) != 0)
|
||||
{
|
||||
ReturnPooledEvaluationArrays(exportEvaluation);
|
||||
ReturnPooledEvaluationArrays(pixelEvaluation);
|
||||
error =
|
||||
$"rect-list-no-param-exports ps_inputs={GetInterpolatedAttributeCount(pixelState)}";
|
||||
TraceAgcShader(
|
||||
$"agc.rect_list_skip es=0x{exportShaderAddress:X16} " +
|
||||
$"ps=0x{pixelShaderAddress:X16} {error}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Every bound color target the shader exports to. Deferred renderers
|
||||
// draw a multi-render-target G-buffer (up to eight slots) in one pass.
|
||||
// Fall back to slot 0 if we cannot match any export to a bound target.
|
||||
@@ -8236,20 +8276,6 @@ public static partial class AgcExports
|
||||
? (packedMasks >> (int)(target * 4)) & 0xFu
|
||||
: 0;
|
||||
|
||||
private static bool VertexProgramExportsParameters(Gen5ShaderProgram program)
|
||||
{
|
||||
foreach (var instruction in program.Instructions)
|
||||
{
|
||||
if (instruction.Control is Gen5ExportControl export &&
|
||||
export.Target is >= 32 and < 64)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static uint GetInterpolatedAttributeCount(Gen5ShaderState state)
|
||||
{
|
||||
var maxAttribute = -1;
|
||||
@@ -12673,9 +12699,6 @@ public static partial class AgcExports
|
||||
private static bool IsEsGeometryShaderType(byte shaderType) =>
|
||||
shaderType is GsShaderType or GsBackShaderType;
|
||||
|
||||
private static bool IsRectListPrimitive(uint primitiveType) =>
|
||||
AgcPrimitiveHelpers.IsRectListPrimitive(primitiveType);
|
||||
|
||||
private static int SetIndirectPatchAddress(CpuContext ctx, string registerSpace)
|
||||
{
|
||||
var commandAddress = ctx[CpuRegister.Rdi];
|
||||
|
||||
Reference in New Issue
Block a user