mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-25 20:28:48 +08:00
52e17b5056
* [shader-decoder-part1] Implemented a shader decoder for Gen5 shaders, including IR generation, metadata reading, scalar evaluation, and SPIR-V translation. Updated related exports and video output components to support the new shader decoding functionality. * [shader decoder] correct RDNA2 operands, fixing synchronization problems * [shader-decoder] RDNA2 decoder improvements * [shader-decoder] fix RDNA2 shift masking and sprite draws * [shader-decoder] improve RDNA2 shader decoder to support more instructions and fix some issues with the previous implementation.
314 lines
7.9 KiB
C#
314 lines
7.9 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
namespace SharpEmu.Libs.Agc;
|
|
|
|
internal enum Gen5ShaderEncoding
|
|
{
|
|
Sop1,
|
|
Sop2,
|
|
Sopc,
|
|
Sopp,
|
|
Sopk,
|
|
Smrd,
|
|
Smem,
|
|
Mubuf,
|
|
Mtbuf,
|
|
Vop1,
|
|
Vop2,
|
|
Vopc,
|
|
Vop3,
|
|
Vintrp,
|
|
Ds,
|
|
Flat,
|
|
Vop3p,
|
|
Mimg,
|
|
Exp,
|
|
}
|
|
|
|
internal enum Gen5OperandKind
|
|
{
|
|
ScalarRegister,
|
|
VectorRegister,
|
|
EncodedConstant,
|
|
LiteralConstant,
|
|
}
|
|
|
|
internal enum Gen5ShaderResourceKind
|
|
{
|
|
ReadOnlyTexture,
|
|
ReadWriteTexture,
|
|
Sampler,
|
|
ConstantBuffer,
|
|
}
|
|
|
|
internal enum Gen5PixelOutputKind
|
|
{
|
|
Float,
|
|
Uint,
|
|
Sint,
|
|
}
|
|
|
|
internal enum Gen5SpirvStage
|
|
{
|
|
Vertex,
|
|
Pixel,
|
|
Compute,
|
|
}
|
|
|
|
internal sealed record Gen5SpirvShader(
|
|
byte[] Spirv,
|
|
IReadOnlyList<Gen5GlobalMemoryBinding> GlobalMemoryBindings,
|
|
IReadOnlyList<Gen5ImageBinding> ImageBindings,
|
|
uint AttributeCount,
|
|
IReadOnlyList<Gen5VertexInputBinding> VertexInputs);
|
|
|
|
internal readonly record struct Gen5ShaderResourceMapping(
|
|
Gen5ShaderResourceKind Kind,
|
|
uint Slot,
|
|
uint OffsetDwords,
|
|
bool SizeFlag);
|
|
|
|
internal sealed record Gen5ShaderMetadata(
|
|
uint ExtendedUserDataSizeDwords,
|
|
uint ShaderResourceTableSizeDwords,
|
|
IReadOnlyDictionary<uint, uint> DirectResources,
|
|
IReadOnlyList<Gen5ShaderResourceMapping> Resources);
|
|
|
|
internal readonly record struct Gen5ComputeSystemRegisters(
|
|
uint? WorkGroupXRegister,
|
|
uint? WorkGroupYRegister,
|
|
uint? WorkGroupZRegister,
|
|
uint? ThreadGroupSizeRegister)
|
|
{
|
|
public bool TryGetExpression(uint scalarRegister, out string expression)
|
|
{
|
|
if (WorkGroupXRegister == scalarRegister)
|
|
{
|
|
expression = "gl_WorkGroupID.x";
|
|
return true;
|
|
}
|
|
|
|
if (WorkGroupYRegister == scalarRegister)
|
|
{
|
|
expression = "gl_WorkGroupID.y";
|
|
return true;
|
|
}
|
|
|
|
if (WorkGroupZRegister == scalarRegister)
|
|
{
|
|
expression = "gl_WorkGroupID.z";
|
|
return true;
|
|
}
|
|
|
|
if (ThreadGroupSizeRegister == scalarRegister)
|
|
{
|
|
expression = "(gl_WorkGroupSize.x * gl_WorkGroupSize.y * gl_WorkGroupSize.z)";
|
|
return true;
|
|
}
|
|
|
|
expression = string.Empty;
|
|
return false;
|
|
}
|
|
|
|
public void ClearStaticValues(Span<uint> scalarRegisters)
|
|
{
|
|
ClearStaticValue(scalarRegisters, WorkGroupXRegister);
|
|
ClearStaticValue(scalarRegisters, WorkGroupYRegister);
|
|
ClearStaticValue(scalarRegisters, WorkGroupZRegister);
|
|
ClearStaticValue(scalarRegisters, ThreadGroupSizeRegister);
|
|
}
|
|
|
|
private static void ClearStaticValue(Span<uint> scalarRegisters, uint? scalarRegister)
|
|
{
|
|
if (scalarRegister is { } register && register < scalarRegisters.Length)
|
|
{
|
|
scalarRegisters[(int)register] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal sealed record Gen5ShaderState(
|
|
Gen5ShaderProgram Program,
|
|
IReadOnlyList<uint> UserData,
|
|
Gen5ShaderMetadata? Metadata,
|
|
Gen5ComputeSystemRegisters? ComputeSystemRegisters = null,
|
|
uint UserDataScalarRegisterBase = 0);
|
|
|
|
internal readonly record struct Gen5Operand(Gen5OperandKind Kind, uint Value)
|
|
{
|
|
public static Gen5Operand Scalar(uint index) =>
|
|
new(Gen5OperandKind.ScalarRegister, index);
|
|
|
|
public static Gen5Operand Vector(uint index) =>
|
|
new(Gen5OperandKind.VectorRegister, index);
|
|
|
|
public static Gen5Operand Source(uint encoded, uint? literal = null)
|
|
{
|
|
if (encoded >= 256)
|
|
{
|
|
return Vector(encoded - 256);
|
|
}
|
|
|
|
if (encoded is 249 or 255 && literal.HasValue)
|
|
{
|
|
return new(Gen5OperandKind.LiteralConstant, literal.Value);
|
|
}
|
|
|
|
if (encoded <= 105 || encoded is 106 or 107 or 124 or 126 or 127)
|
|
{
|
|
return Scalar(encoded);
|
|
}
|
|
|
|
return new(Gen5OperandKind.EncodedConstant, encoded);
|
|
}
|
|
|
|
public override string ToString() => Kind switch
|
|
{
|
|
Gen5OperandKind.ScalarRegister => $"s{Value}",
|
|
Gen5OperandKind.VectorRegister => $"v{Value}",
|
|
Gen5OperandKind.LiteralConstant => $"0x{Value:X8}",
|
|
_ => $"src[{Value}]",
|
|
};
|
|
}
|
|
|
|
internal abstract record Gen5InstructionControl;
|
|
|
|
internal sealed record Gen5ImageControl(
|
|
uint Dmask,
|
|
uint VectorAddress,
|
|
IReadOnlyList<uint> AddressRegisters,
|
|
uint VectorData,
|
|
uint ScalarResource,
|
|
uint ScalarSampler,
|
|
uint Dimension,
|
|
bool IsArray,
|
|
bool Glc,
|
|
bool Slc) : Gen5InstructionControl
|
|
{
|
|
public uint GetAddressRegister(int component) =>
|
|
component < AddressRegisters.Count
|
|
? AddressRegisters[component]
|
|
: VectorAddress + (uint)component;
|
|
}
|
|
|
|
internal sealed record Gen5GlobalMemoryControl(
|
|
uint DwordCount,
|
|
uint VectorAddress,
|
|
uint VectorData,
|
|
uint ScalarAddress,
|
|
int OffsetBytes,
|
|
bool Glc,
|
|
bool Slc) : Gen5InstructionControl;
|
|
|
|
internal sealed record Gen5BufferMemoryControl(
|
|
uint DwordCount,
|
|
uint VectorAddress,
|
|
uint VectorData,
|
|
uint ScalarResource,
|
|
int OffsetBytes,
|
|
bool IndexEnabled,
|
|
bool OffsetEnabled,
|
|
bool Glc,
|
|
bool Slc) : Gen5InstructionControl;
|
|
|
|
internal sealed record Gen5ExportControl(
|
|
uint Target,
|
|
uint EnableMask,
|
|
bool Compressed,
|
|
bool Done,
|
|
bool ValidMask) : Gen5InstructionControl;
|
|
|
|
internal sealed record Gen5InterpolationControl(
|
|
uint Attribute,
|
|
uint Channel) : Gen5InstructionControl;
|
|
|
|
internal sealed record Gen5Vop3Control(
|
|
uint AbsoluteMask,
|
|
uint NegateMask,
|
|
uint OutputModifier,
|
|
bool Clamp,
|
|
uint? ScalarDestination) : Gen5InstructionControl;
|
|
|
|
internal sealed record Gen5SdwaControl(
|
|
uint DestinationSelect,
|
|
uint Source0Select,
|
|
uint Source1Select,
|
|
uint AbsoluteMask,
|
|
uint NegateMask,
|
|
uint OutputModifier,
|
|
bool Clamp) : Gen5InstructionControl;
|
|
|
|
internal sealed record Gen5DppControl(
|
|
uint Control,
|
|
bool FetchInactive,
|
|
bool BoundControl,
|
|
uint AbsoluteMask,
|
|
uint NegateMask,
|
|
uint BankMask,
|
|
uint RowMask) : Gen5InstructionControl;
|
|
|
|
internal sealed record Gen5ScalarMemoryControl(
|
|
uint DestinationCount,
|
|
int ImmediateOffsetBytes,
|
|
uint? DynamicOffsetRegister) : Gen5InstructionControl;
|
|
|
|
internal sealed record Gen5DataShareControl(
|
|
uint Offset0,
|
|
uint Offset1,
|
|
bool Gds) : Gen5InstructionControl;
|
|
|
|
internal sealed record Gen5ImageBinding(
|
|
uint Pc,
|
|
string Opcode,
|
|
Gen5ImageControl Control,
|
|
IReadOnlyList<uint> ResourceDescriptor,
|
|
IReadOnlyList<uint> SamplerDescriptor,
|
|
uint? MipLevel);
|
|
|
|
internal sealed record Gen5GlobalMemoryBinding(
|
|
uint ScalarAddress,
|
|
ulong BaseAddress,
|
|
IReadOnlyList<uint> InstructionPcs,
|
|
byte[] Data);
|
|
|
|
internal sealed record Gen5VertexInputBinding(
|
|
uint Pc,
|
|
uint Location,
|
|
uint ComponentCount,
|
|
uint DataFormat,
|
|
uint NumberFormat,
|
|
ulong BaseAddress,
|
|
uint Stride,
|
|
uint OffsetBytes,
|
|
byte[] Data);
|
|
|
|
internal sealed record Gen5ShaderEvaluation(
|
|
IReadOnlyList<uint> InitialScalarRegisters,
|
|
IReadOnlyList<uint> ScalarRegisters,
|
|
IReadOnlyDictionary<uint, IReadOnlyList<uint>> ScalarRegistersByPc,
|
|
IReadOnlyList<Gen5ImageBinding> ImageBindings,
|
|
IReadOnlyList<Gen5GlobalMemoryBinding> GlobalMemoryBindings,
|
|
Gen5ComputeSystemRegisters? ComputeSystemRegisters = null,
|
|
IReadOnlySet<uint>? RuntimeScalarRegisters = null,
|
|
IReadOnlyList<Gen5VertexInputBinding>? VertexInputs = null);
|
|
|
|
internal sealed record Gen5ShaderInstruction(
|
|
uint Pc,
|
|
Gen5ShaderEncoding Encoding,
|
|
string Opcode,
|
|
IReadOnlyList<uint> Words,
|
|
IReadOnlyList<Gen5Operand> Sources,
|
|
IReadOnlyList<Gen5Operand> Destinations,
|
|
Gen5InstructionControl? Control);
|
|
|
|
internal sealed record Gen5ShaderProgram(
|
|
ulong Address,
|
|
IReadOnlyList<Gen5ShaderInstruction> Instructions)
|
|
{
|
|
public IEnumerable<Gen5ImageControl> ImageResources =>
|
|
Instructions
|
|
.Select(instruction => instruction.Control)
|
|
.OfType<Gen5ImageControl>();
|
|
}
|