mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-22 19:06:15 +08:00
perf(shader): cache pixel export masks (#288)
This commit is contained in:
@@ -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));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user