perf(shader): cache pixel export masks (#288)

This commit is contained in:
999sian
2026-07-17 02:17:39 +01:00
committed by GitHub
parent 7494792249
commit 6b1abc1a38
3 changed files with 74 additions and 25 deletions
+11 -25
View File
@@ -5937,13 +5937,14 @@ public static partial class AgcExports
// Every bound color target the shader exports to. Deferred renderers // Every bound color target the shader exports to. Deferred renderers
// draw a multi-render-target G-buffer (up to eight slots) in one pass. // 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. // Fall back to slot 0 if we cannot match any export to a bound target.
var pixelColorExportMasks = pixelState.Program.PixelColorExportMasks;
var allBoundTargets = GetRenderTargets(state.CxRegisters); var allBoundTargets = GetRenderTargets(state.CxRegisters);
// At most 8 slots; a manual filter avoids the per-draw LINQ iterator/ // At most 8 slots; a manual filter avoids the per-draw LINQ iterator/
// closure allocations. Slots are distinct, so sorting by slot is stable. // closure allocations. Slots are distinct, so sorting by slot is stable.
var selectedTargets = new List<RenderTargetDescriptor>(allBoundTargets.Count); var selectedTargets = new List<RenderTargetDescriptor>(allBoundTargets.Count);
foreach (var target in allBoundTargets) foreach (var target in allBoundTargets)
{ {
if (HasPixelColorExport(pixelState, target.Slot)) if (GetPixelColorExportMask(pixelColorExportMasks, target.Slot) != 0)
{ {
selectedTargets.Add(target); selectedTargets.Add(target);
} }
@@ -5966,7 +5967,7 @@ public static partial class AgcExports
{ {
TraceAgcShader( TraceAgcShader(
$"agc.mrt_filter ps=0x{pixelShaderAddress:X16} " + $"agc.mrt_filter ps=0x{pixelShaderAddress:X16} " +
$"bound=[{string.Join(",", allBoundTargets.Select(t => $"s{t.Slot}:0x{t.Address:X}:exp{(HasPixelColorExport(pixelState, t.Slot) ? 1 : 0)}"))}] " + $"bound=[{string.Join(",", allBoundTargets.Select(t => $"s{t.Slot}:0x{t.Address:X}:exp{(GetPixelColorExportMask(pixelColorExportMasks, t.Slot) != 0 ? 1 : 0)}"))}] " +
$"kept={renderTargets.Length}"); $"kept={renderTargets.Length}");
} }
@@ -6166,7 +6167,7 @@ public static partial class AgcExports
DecodeDepthTarget(state.CxRegisters), DecodeDepthTarget(state.CxRegisters),
guestTargets, guestTargets,
ApplyTransparentPremultipliedFillClear( ApplyTransparentPremultipliedFillClear(
CreateRenderState(state.CxRegisters, renderTargets, pixelState), CreateRenderState(state.CxRegisters, renderTargets, pixelColorExportMasks),
textures, textures,
vertexInputs, vertexInputs,
pixelEvaluation.InitialScalarRegisters), pixelEvaluation.InitialScalarRegisters),
@@ -6410,26 +6411,10 @@ public static partial class AgcExports
return true; return true;
} }
private static bool HasPixelColorExport(Gen5ShaderState state, uint target) => private static uint GetPixelColorExportMask(uint packedMasks, uint target) =>
GetPixelColorExportMask(state, target) != 0; target < ColorTargetCount
? (packedMasks >> (int)(target * 4)) & 0xFu
private static uint GetPixelColorExportMask(Gen5ShaderState state, uint target) : 0;
{
// Called per render target (twice per draw via CreateRenderState +
// HasPixelColorExport); a manual scan avoids the per-call LINQ iterator
// and closure allocations. Same result as the previous
// Select/OfType/Where/Aggregate chain.
var mask = 0u;
foreach (var instruction in state.Program.Instructions)
{
if (instruction.Control is Gen5ExportControl export && export.Target == target)
{
mask |= export.EnableMask & 0xFu;
}
}
return mask;
}
private static uint GetInterpolatedAttributeCount(Gen5ShaderState state) private static uint GetInterpolatedAttributeCount(Gen5ShaderState state)
{ {
@@ -6627,7 +6612,7 @@ public static partial class AgcExports
private static GuestRenderState CreateRenderState( private static GuestRenderState CreateRenderState(
IReadOnlyDictionary<uint, uint> registers, IReadOnlyDictionary<uint, uint> registers,
IReadOnlyList<RenderTargetDescriptor> targets, IReadOnlyList<RenderTargetDescriptor> targets,
Gen5ShaderState pixelState) uint pixelColorExportMasks)
{ {
if (targets.Count == 0) if (targets.Count == 0)
{ {
@@ -6642,7 +6627,8 @@ public static partial class AgcExports
var blend = DecodeBlendState(registers, target.Slot); var blend = DecodeBlendState(registers, target.Slot);
return blend with return blend with
{ {
WriteMask = blend.WriteMask & GetPixelColorExportMask(pixelState, target.Slot), WriteMask = blend.WriteMask &
GetPixelColorExportMask(pixelColorExportMasks, target.Slot),
}; };
}).ToArray(), }).ToArray(),
scissor, scissor,
@@ -325,9 +325,31 @@ public sealed record Gen5ShaderProgram(
ulong Address, ulong Address,
IReadOnlyList<Gen5ShaderInstruction> Instructions) IReadOnlyList<Gen5ShaderInstruction> Instructions)
{ {
private const uint PixelColorTargetCount = 8;
private const int PixelColorMaskBits = 4;
private readonly uint _pixelColorExportMasks = ComputePixelColorExportMasks(Instructions);
private const int ScalarRegisterCount = 256; private const int ScalarRegisterCount = 256;
private IReadOnlySet<uint>? _runtimeScalarRegisters; private IReadOnlySet<uint>? _runtimeScalarRegisters;
public uint PixelColorExportMasks => _pixelColorExportMasks;
private static uint ComputePixelColorExportMasks(
IReadOnlyList<Gen5ShaderInstruction> instructions)
{
var masks = 0u;
foreach (var instruction in instructions)
{
if (instruction.Control is Gen5ExportControl export &&
export.Target < PixelColorTargetCount)
{
masks |= (export.EnableMask & 0xFu) <<
(int)(export.Target * PixelColorMaskBits);
}
}
return masks;
}
public IEnumerable<Gen5ImageControl> ImageResources => public IEnumerable<Gen5ImageControl> ImageResources =>
Instructions Instructions
.Select(instruction => instruction.Control) .Select(instruction => instruction.Control)
@@ -0,0 +1,41 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.ShaderCompiler;
using Xunit;
namespace SharpEmu.Libs.Tests.Agc;
public sealed class Gen5ShaderProgramTests
{
[Fact]
public void PixelColorExportMasksPackAllColorTargets()
{
var program = new Gen5ShaderProgram(
0,
[
Export(0, 0x1),
Export(0, 0x4),
Export(1, 0x2),
Export(2, 0x3),
Export(3, 0x4),
Export(4, 0x5),
Export(5, 0x6),
Export(6, 0x7),
Export(7, 0x8),
Export(8, 0xF),
]);
Assert.Equal(0x8765_4325u, program.PixelColorExportMasks);
Assert.Equal(0x8765_4325u, program.PixelColorExportMasks);
}
private static Gen5ShaderInstruction Export(uint target, uint enableMask) => new(
0,
Gen5ShaderEncoding.Exp,
"exp",
[],
[],
[],
new Gen5ExportControl(target, enableMask, false, false, false));
}