mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-26 12:48:39 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aa152de934 |
@@ -68,13 +68,35 @@ public static class AgcExports
|
||||
private const uint ComputeNumThreadZ = 0x209;
|
||||
private const uint SpiPsInputCntl0 = 0x191;
|
||||
private const uint VgtPrimitiveType = 0x242;
|
||||
private const uint PaScScreenScissorTl = 0x0C;
|
||||
private const uint PaScScreenScissorBr = 0x0D;
|
||||
private const uint CbTargetMask = 0x8E;
|
||||
private const uint PaScWindowOffset = 0x80;
|
||||
private const uint PaScWindowScissorTl = 0x81;
|
||||
private const uint PaScWindowScissorBr = 0x82;
|
||||
private const uint PaScGenericScissorTl = 0x90;
|
||||
private const uint PaScGenericScissorBr = 0x91;
|
||||
private const uint PaScVportScissor0Tl = 0x94;
|
||||
private const uint PaScVportScissor0Br = 0x95;
|
||||
private const uint PaClVportXScale = 0x10F;
|
||||
private const uint PaClVportXOffset = 0x110;
|
||||
private const uint PaClVportYScale = 0x111;
|
||||
private const uint PaClVportYOffset = 0x112;
|
||||
private const uint PaScVportZMin0 = 0xB4;
|
||||
private const uint PaScVportZMax0 = 0xB5;
|
||||
private const uint CbBlendRed = 0x105;
|
||||
private const uint CbBlendGreen = 0x106;
|
||||
private const uint CbBlendBlue = 0x107;
|
||||
private const uint CbBlendAlpha = 0x108;
|
||||
private const uint CbColorControl = 0x202;
|
||||
private const uint CbColor0Base = 0x318;
|
||||
private const uint CbColorRegisterStride = 15;
|
||||
private const uint CbColor0Info = 0x31C;
|
||||
private const uint CbColor0BaseExt = 0x390;
|
||||
private const uint CbColor0Attrib2 = 0x3B0;
|
||||
private const uint CbColor0Attrib3 = 0x3B8;
|
||||
private const uint CbBlend0Control = 0x1E0;
|
||||
private const uint PaScModeCntl0 = 0x292;
|
||||
private const int ColorTargetCount = 8;
|
||||
private const uint PsTextureUserDataRegister = 0xC;
|
||||
private const uint VsUserDataRegister = 0x4C;
|
||||
@@ -151,9 +173,25 @@ public static class AgcExports
|
||||
|
||||
private static readonly RegisterDefaultGroup[] PrimaryRegisterDefaults =
|
||||
[
|
||||
new(0, 0, 0xE24F806D, [new(CbColorControl, 0x00CC0010)]),
|
||||
new(0, 3, 0x0BC65DA4, [new(0x08F, 0)]),
|
||||
new(0, 4, 0x9E5AD592, [new(0x08E, 0)]),
|
||||
new(0, 12, 0x6DE4C312, [new(0x203, 0)]),
|
||||
new(0, 28, 0x1EB8D73A, [new(PaScModeCntl0, 0x00000002)]),
|
||||
new(0, 31, 0xA20EFC70, [new(PaScWindowOffset, 0)]),
|
||||
new(0, 58, 0x43FBD769,
|
||||
[
|
||||
new(CbBlendRed, 0),
|
||||
new(CbBlendBlue, 0),
|
||||
new(CbBlendGreen, 0),
|
||||
new(CbBlendAlpha, 0),
|
||||
]),
|
||||
new(0, 59, 0xEF550356, [new(CbBlend0Control, 0x20010001)]),
|
||||
new(0, 67, 0x918106BB,
|
||||
[
|
||||
new(PaScGenericScissorTl, 0x80000000),
|
||||
new(PaScGenericScissorBr, 0x40004000),
|
||||
]),
|
||||
new(0, 72, 0x38E92C91,
|
||||
[
|
||||
new(0x318, 0),
|
||||
@@ -188,6 +226,11 @@ public static class AgcExports
|
||||
new(0x0B4, 0),
|
||||
new(0x0B5, 0),
|
||||
]),
|
||||
new(0, 77, 0x078D7060,
|
||||
[
|
||||
new(PaScWindowScissorTl, 0x80000000),
|
||||
new(PaScWindowScissorBr, 0x40004000),
|
||||
]),
|
||||
new(1, 13, 0xC918DF3E, [new(0x20C, 0), new(0x20D, 0)]),
|
||||
new(1, 14, 0xC9751C9C, [new(0x0C8, 0), new(0x0C9, 0)]),
|
||||
new(1, 18, 0xC9E01B31, [new(0x008, 0), new(0x009, 0)]),
|
||||
@@ -231,7 +274,9 @@ public static class AgcExports
|
||||
uint TileMode,
|
||||
uint Type,
|
||||
uint BaseLevel,
|
||||
uint LastLevel)
|
||||
uint LastLevel,
|
||||
uint Pitch,
|
||||
uint DstSelect)
|
||||
{
|
||||
public uint MipLevels
|
||||
{
|
||||
@@ -274,12 +319,15 @@ public static class AgcExports
|
||||
VulkanGuestIndexBuffer? IndexBuffer,
|
||||
IReadOnlyList<TranslatedImageBinding> Textures,
|
||||
IReadOnlyList<Gen5GlobalMemoryBinding> GlobalMemoryBindings,
|
||||
IReadOnlyList<RenderTargetDescriptor> RenderTargets);
|
||||
IReadOnlyList<Gen5VertexInputBinding> VertexInputs,
|
||||
IReadOnlyList<RenderTargetDescriptor> RenderTargets,
|
||||
VulkanGuestRenderState RenderState);
|
||||
|
||||
private sealed record TranslatedImageBinding(
|
||||
TextureDescriptor Descriptor,
|
||||
bool IsStorage,
|
||||
uint MipLevel);
|
||||
uint MipLevel,
|
||||
IReadOnlyList<uint> SamplerDescriptor);
|
||||
|
||||
private readonly record struct RenderTargetWriter(
|
||||
ulong Sequence,
|
||||
@@ -2770,11 +2818,13 @@ public static class AgcExports
|
||||
out _);
|
||||
var globalMemoryBuffers =
|
||||
CreateVulkanGuestMemoryBuffers(translatedDraw.GlobalMemoryBindings);
|
||||
VulkanVideoPresenter.SubmitOffscreenTranslatedDraw(
|
||||
translatedDraw.PixelSpirv,
|
||||
textures,
|
||||
globalMemoryBuffers,
|
||||
translatedDraw.AttributeCount,
|
||||
var vertexBuffers =
|
||||
CreateVulkanGuestVertexBuffers(translatedDraw.VertexInputs);
|
||||
VulkanVideoPresenter.SubmitOffscreenTranslatedDraw(
|
||||
translatedDraw.PixelSpirv,
|
||||
textures,
|
||||
globalMemoryBuffers,
|
||||
translatedDraw.AttributeCount,
|
||||
new VulkanGuestRenderTarget(
|
||||
firstTarget.Address,
|
||||
firstTarget.Width,
|
||||
@@ -2785,7 +2835,9 @@ public static class AgcExports
|
||||
translatedDraw.VertexCount,
|
||||
translatedDraw.InstanceCount,
|
||||
translatedDraw.PrimitiveType,
|
||||
translatedDraw.IndexBuffer);
|
||||
translatedDraw.IndexBuffer,
|
||||
vertexBuffers,
|
||||
translatedDraw.RenderState);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2884,7 +2936,8 @@ public static class AgcExports
|
||||
ctx,
|
||||
exportState,
|
||||
out var exportEvaluation,
|
||||
out error) ||
|
||||
out error,
|
||||
resolveVertexInputs: true) ||
|
||||
!Gen5ShaderTranslator.TryCreateState(
|
||||
ctx,
|
||||
pixelShaderAddress,
|
||||
@@ -2984,12 +3037,15 @@ public static class AgcExports
|
||||
new TranslatedImageBinding(
|
||||
texture,
|
||||
Gen5ShaderTranslator.IsStorageImageOperation(binding.Opcode),
|
||||
binding.MipLevel ?? 0));
|
||||
binding.MipLevel ?? 0,
|
||||
binding.SamplerDescriptor));
|
||||
}
|
||||
|
||||
var globalMemoryBindings = pixelEvaluation.GlobalMemoryBindings
|
||||
.Concat(exportEvaluation.GlobalMemoryBindings)
|
||||
.ToArray();
|
||||
IReadOnlyList<Gen5VertexInputBinding> vertexInputs =
|
||||
exportEvaluation.VertexInputs ?? [];
|
||||
state.UcRegisters.TryGetValue(VgtPrimitiveType, out var primitiveType);
|
||||
draw = new TranslatedGuestDraw(
|
||||
exportShaderAddress,
|
||||
@@ -3003,7 +3059,9 @@ public static class AgcExports
|
||||
indexed ? CreateVulkanIndexBuffer(ctx, state, vertexCount) : null,
|
||||
textures,
|
||||
globalMemoryBindings,
|
||||
renderTargets);
|
||||
vertexInputs,
|
||||
renderTargets,
|
||||
CreateRenderState(state.CxRegisters, renderTargets.FirstOrDefault()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3115,6 +3173,246 @@ public static class AgcExports
|
||||
return targets;
|
||||
}
|
||||
|
||||
private static VulkanGuestRenderState CreateRenderState(
|
||||
IReadOnlyDictionary<uint, uint> registers,
|
||||
RenderTargetDescriptor target)
|
||||
{
|
||||
var scissor = DecodeScissor(registers, target.Width, target.Height);
|
||||
return new VulkanGuestRenderState(
|
||||
DecodeBlendState(registers, target.Slot),
|
||||
scissor,
|
||||
DecodeViewport(registers, target.Width, target.Height, scissor));
|
||||
}
|
||||
|
||||
private static VulkanGuestBlendState DecodeBlendState(
|
||||
IReadOnlyDictionary<uint, uint> registers,
|
||||
uint slot)
|
||||
{
|
||||
var writeMask = 0xFu;
|
||||
if (registers.TryGetValue(CbTargetMask, out var targetMask))
|
||||
{
|
||||
writeMask = (targetMask >> checked((int)(slot * 4))) & 0xFu;
|
||||
}
|
||||
|
||||
registers.TryGetValue(CbBlend0Control + slot, out var control);
|
||||
return new VulkanGuestBlendState(
|
||||
((control >> 30) & 1u) != 0,
|
||||
control & 0x1Fu,
|
||||
(control >> 8) & 0x1Fu,
|
||||
(control >> 5) & 0x7u,
|
||||
(control >> 16) & 0x1Fu,
|
||||
(control >> 24) & 0x1Fu,
|
||||
(control >> 21) & 0x7u,
|
||||
((control >> 29) & 1u) != 0,
|
||||
writeMask == 0 ? 0xFu : writeMask);
|
||||
}
|
||||
|
||||
private static VulkanGuestRect? DecodeScissor(
|
||||
IReadOnlyDictionary<uint, uint> registers,
|
||||
uint targetWidth,
|
||||
uint targetHeight)
|
||||
{
|
||||
if (targetWidth == 0 || targetHeight == 0)
|
||||
{
|
||||
return new VulkanGuestRect(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
var left = 0;
|
||||
var top = 0;
|
||||
var right = checked((int)Math.Min(targetWidth, int.MaxValue));
|
||||
var bottom = checked((int)Math.Min(targetHeight, int.MaxValue));
|
||||
|
||||
var windowOffsetX = 0;
|
||||
var windowOffsetY = 0;
|
||||
var enableWindowOffset = true;
|
||||
if (registers.TryGetValue(PaScWindowScissorTl, out var windowScissorTl))
|
||||
{
|
||||
enableWindowOffset = (windowScissorTl & 0x80000000u) == 0;
|
||||
}
|
||||
|
||||
if (enableWindowOffset &&
|
||||
registers.TryGetValue(PaScWindowOffset, out var windowOffset))
|
||||
{
|
||||
windowOffsetX = (short)(windowOffset & 0xFFFFu);
|
||||
windowOffsetY = (short)(windowOffset >> 16);
|
||||
}
|
||||
|
||||
IntersectScissorPair(registers, PaScScreenScissorTl, PaScScreenScissorBr, ref left, ref top, ref right, ref bottom);
|
||||
IntersectScissorPair(
|
||||
registers,
|
||||
PaScWindowScissorTl,
|
||||
PaScWindowScissorBr,
|
||||
ref left,
|
||||
ref top,
|
||||
ref right,
|
||||
ref bottom,
|
||||
windowOffsetX,
|
||||
windowOffsetY);
|
||||
IntersectScissorPair(
|
||||
registers,
|
||||
PaScGenericScissorTl,
|
||||
PaScGenericScissorBr,
|
||||
ref left,
|
||||
ref top,
|
||||
ref right,
|
||||
ref bottom,
|
||||
windowOffsetX,
|
||||
windowOffsetY);
|
||||
var vportScissorEnabled =
|
||||
!registers.TryGetValue(PaScModeCntl0, out var modeControl) ||
|
||||
((modeControl >> 1) & 1u) != 0;
|
||||
if (vportScissorEnabled)
|
||||
{
|
||||
IntersectScissorPair(registers, PaScVportScissor0Tl, PaScVportScissor0Br, ref left, ref top, ref right, ref bottom);
|
||||
}
|
||||
|
||||
left = Math.Clamp(left, 0, checked((int)targetWidth));
|
||||
top = Math.Clamp(top, 0, checked((int)targetHeight));
|
||||
right = Math.Clamp(right, left, checked((int)targetWidth));
|
||||
bottom = Math.Clamp(bottom, top, checked((int)targetHeight));
|
||||
|
||||
if (left == 0 &&
|
||||
top == 0 &&
|
||||
right == (int)targetWidth &&
|
||||
bottom == (int)targetHeight)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new VulkanGuestRect(
|
||||
left,
|
||||
top,
|
||||
checked((uint)(right - left)),
|
||||
checked((uint)(bottom - top)));
|
||||
}
|
||||
|
||||
private static VulkanGuestViewport? DecodeViewport(
|
||||
IReadOnlyDictionary<uint, uint> registers,
|
||||
uint targetWidth,
|
||||
uint targetHeight,
|
||||
VulkanGuestRect? scissor)
|
||||
{
|
||||
if (targetWidth == 0 || targetHeight == 0)
|
||||
{
|
||||
return new VulkanGuestViewport(0, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
var minDepth = 0f;
|
||||
var maxDepth = 1f;
|
||||
if (registers.TryGetValue(PaScVportZMin0, out var zMinBits) &&
|
||||
registers.TryGetValue(PaScVportZMax0, out var zMaxBits))
|
||||
{
|
||||
var decodedMin = BitConverter.UInt32BitsToSingle(zMinBits);
|
||||
var decodedMax = BitConverter.UInt32BitsToSingle(zMaxBits);
|
||||
if (float.IsFinite(decodedMin) &&
|
||||
float.IsFinite(decodedMax) &&
|
||||
decodedMax > decodedMin)
|
||||
{
|
||||
minDepth = decodedMin;
|
||||
maxDepth = decodedMax;
|
||||
}
|
||||
}
|
||||
|
||||
if (TryDecodeFiniteFloat(registers, PaClVportXScale, out var xScale) &&
|
||||
TryDecodeFiniteFloat(registers, PaClVportXOffset, out var xOffset) &&
|
||||
TryDecodeFiniteFloat(registers, PaClVportYScale, out var yScale) &&
|
||||
TryDecodeFiniteFloat(registers, PaClVportYOffset, out var yOffset) &&
|
||||
xScale > 0f &&
|
||||
yScale != 0f)
|
||||
{
|
||||
return new VulkanGuestViewport(
|
||||
xOffset - xScale,
|
||||
yOffset - yScale,
|
||||
xScale * 2f,
|
||||
yScale * 2f,
|
||||
minDepth,
|
||||
maxDepth);
|
||||
}
|
||||
|
||||
if (scissor is not { } rect)
|
||||
{
|
||||
return minDepth == 0f && maxDepth == 1f
|
||||
? null
|
||||
: new VulkanGuestViewport(0, 0, targetWidth, targetHeight, minDepth, maxDepth);
|
||||
}
|
||||
|
||||
return new VulkanGuestViewport(
|
||||
rect.X,
|
||||
rect.Y,
|
||||
rect.Width,
|
||||
rect.Height,
|
||||
minDepth,
|
||||
maxDepth);
|
||||
}
|
||||
|
||||
private static bool TryDecodeFiniteFloat(
|
||||
IReadOnlyDictionary<uint, uint> registers,
|
||||
uint register,
|
||||
out float value)
|
||||
{
|
||||
value = 0;
|
||||
if (!registers.TryGetValue(register, out var bits))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BitConverter.UInt32BitsToSingle(bits);
|
||||
return float.IsFinite(value);
|
||||
}
|
||||
|
||||
private static void IntersectScissorPair(
|
||||
IReadOnlyDictionary<uint, uint> registers,
|
||||
uint tlRegister,
|
||||
uint brRegister,
|
||||
ref int left,
|
||||
ref int top,
|
||||
ref int right,
|
||||
ref int bottom,
|
||||
int offsetX = 0,
|
||||
int offsetY = 0)
|
||||
{
|
||||
if (!TryDecodeScissorPair(registers, tlRegister, brRegister, out var pairLeft, out var pairTop, out var pairRight, out var pairBottom))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pairLeft += offsetX;
|
||||
pairTop += offsetY;
|
||||
pairRight += offsetX;
|
||||
pairBottom += offsetY;
|
||||
|
||||
left = Math.Max(left, pairLeft);
|
||||
top = Math.Max(top, pairTop);
|
||||
right = Math.Min(right, pairRight);
|
||||
bottom = Math.Min(bottom, pairBottom);
|
||||
}
|
||||
|
||||
private static bool TryDecodeScissorPair(
|
||||
IReadOnlyDictionary<uint, uint> registers,
|
||||
uint tlRegister,
|
||||
uint brRegister,
|
||||
out int left,
|
||||
out int top,
|
||||
out int right,
|
||||
out int bottom)
|
||||
{
|
||||
left = 0;
|
||||
top = 0;
|
||||
right = 0;
|
||||
bottom = 0;
|
||||
if (!registers.TryGetValue(tlRegister, out var tl) ||
|
||||
!registers.TryGetValue(brRegister, out var br))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
left = (int)(tl & 0x7FFFu);
|
||||
top = (int)((tl >> 16) & 0x7FFFu);
|
||||
right = (int)(br & 0x7FFFu);
|
||||
bottom = (int)((br >> 16) & 0x7FFFu);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void TraceTranslatedGuestDraw(
|
||||
CpuContext ctx,
|
||||
SubmittedGpuState gpuState,
|
||||
@@ -3173,13 +3471,31 @@ public static class AgcExports
|
||||
? $"{(indexBuffer.Is32Bit ? 32 : 16)}:" +
|
||||
Convert.ToHexString(indexBuffer.Data.AsSpan(0, Math.Min(indexBuffer.Data.Length, 32)))
|
||||
: "none";
|
||||
var vertexInputs = draw.VertexInputs.Count == 0
|
||||
? "none"
|
||||
: string.Join(
|
||||
',',
|
||||
draw.VertexInputs.Select(input =>
|
||||
$"{input.Location}:pc=0x{input.Pc:X}:0x{input.BaseAddress:X16}" +
|
||||
$":stride{input.Stride}:off{input.OffsetBytes}:c{input.ComponentCount}"));
|
||||
var scissor = draw.RenderState.Scissor is { } drawScissor
|
||||
? $"{drawScissor.X},{drawScissor.Y},{drawScissor.Width}x{drawScissor.Height}"
|
||||
: "full";
|
||||
var viewport = draw.RenderState.Viewport is { } drawViewport
|
||||
? $"{drawViewport.X:0.###},{drawViewport.Y:0.###}," +
|
||||
$"{drawViewport.Width:0.###}x{drawViewport.Height:0.###}:" +
|
||||
$"{drawViewport.MinDepth:0.###}-{drawViewport.MaxDepth:0.###}"
|
||||
: "full";
|
||||
var blend = draw.RenderState.Blend;
|
||||
TraceAgcShader(
|
||||
$"agc.shader_draw es=0x{draw.ExportShaderAddress:X16} " +
|
||||
$"ps=0x{draw.PixelShaderAddress:X16} spirv={draw.PixelSpirv.Length} " +
|
||||
$"primitive=0x{draw.PrimitiveType:X} " +
|
||||
$"blend={(blend.Enable ? 1 : 0)}:{blend.ColorSrcFactor}/{blend.ColorDstFactor}/{blend.ColorFunc} " +
|
||||
$"write_mask=0x{blend.WriteMask:X} scissor={scissor} viewport={viewport} " +
|
||||
$"ps_ena=0x{psInputEna:X8} ps_addr=0x{psInputAddr:X8} " +
|
||||
$"targets=[{targets}] textures=[{textures}] " +
|
||||
$"buffers=[{buffers}] indices=[{indices}]");
|
||||
$"buffers=[{buffers}] vertex=[{vertexInputs}] indices=[{indices}]");
|
||||
}
|
||||
|
||||
private static IReadOnlyList<VulkanGuestDrawTexture> CreateVulkanGuestDrawTextures(
|
||||
@@ -3196,6 +3512,7 @@ public static class AgcExports
|
||||
binding.Descriptor,
|
||||
binding.IsStorage,
|
||||
binding.MipLevel,
|
||||
binding.SamplerDescriptor,
|
||||
out var texture))
|
||||
{
|
||||
textures.Add(texture);
|
||||
@@ -3223,11 +3540,31 @@ public static class AgcExports
|
||||
return buffers;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<VulkanGuestVertexBuffer> CreateVulkanGuestVertexBuffers(
|
||||
IReadOnlyList<Gen5VertexInputBinding> bindings)
|
||||
{
|
||||
var buffers = new VulkanGuestVertexBuffer[bindings.Count];
|
||||
for (var index = 0; index < bindings.Count; index++)
|
||||
{
|
||||
var binding = bindings[index];
|
||||
buffers[index] = new VulkanGuestVertexBuffer(
|
||||
binding.Location,
|
||||
binding.ComponentCount,
|
||||
binding.BaseAddress,
|
||||
binding.Stride,
|
||||
binding.OffsetBytes,
|
||||
binding.Data);
|
||||
}
|
||||
|
||||
return buffers;
|
||||
}
|
||||
|
||||
private static bool TryCreateVulkanGuestDrawTexture(
|
||||
CpuContext ctx,
|
||||
TextureDescriptor descriptor,
|
||||
bool isStorage,
|
||||
uint mipLevel,
|
||||
IReadOnlyList<uint> samplerDescriptor,
|
||||
out VulkanGuestDrawTexture texture)
|
||||
{
|
||||
texture = default!;
|
||||
@@ -3241,9 +3578,12 @@ public static class AgcExports
|
||||
return true;
|
||||
}
|
||||
|
||||
var sourceWidth = descriptor.TileMode == 0
|
||||
? Math.Max(descriptor.Width, descriptor.Pitch)
|
||||
: descriptor.Width;
|
||||
var sourceByteCount = GetTextureByteCount(
|
||||
descriptor.Format,
|
||||
descriptor.Width,
|
||||
sourceWidth,
|
||||
descriptor.Height);
|
||||
if (sourceByteCount == 0 ||
|
||||
sourceByteCount > MaxPresentedTextureBytes ||
|
||||
@@ -3276,7 +3616,11 @@ public static class AgcExports
|
||||
IsFallback: descriptor.Address == 0,
|
||||
IsStorage: true,
|
||||
MipLevels: descriptor.MipLevels,
|
||||
MipLevel: mipLevel);
|
||||
MipLevel: mipLevel,
|
||||
Pitch: descriptor.Pitch,
|
||||
TileMode: descriptor.TileMode,
|
||||
DstSelect: descriptor.DstSelect,
|
||||
Sampler: ToVulkanSampler(samplerDescriptor));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3303,7 +3647,8 @@ public static class AgcExports
|
||||
TraceAgcShader(
|
||||
$"agc.texture_source addr=0x{descriptor.Address:X16} " +
|
||||
$"fmt={descriptor.Format} num={descriptor.NumberType} tile={descriptor.TileMode} " +
|
||||
$"size={descriptor.Width}x{descriptor.Height} " +
|
||||
$"size={descriptor.Width}x{descriptor.Height} pitch={descriptor.Pitch} " +
|
||||
$"dst=0x{descriptor.DstSelect:X3} " +
|
||||
$"bytes={source.Length} nonzero64={nonZero}");
|
||||
|
||||
var rgba = source;
|
||||
@@ -3317,7 +3662,11 @@ public static class AgcExports
|
||||
IsFallback: false,
|
||||
IsStorage: isStorage,
|
||||
MipLevels: descriptor.MipLevels,
|
||||
MipLevel: mipLevel);
|
||||
MipLevel: mipLevel,
|
||||
Pitch: descriptor.Pitch,
|
||||
TileMode: descriptor.TileMode,
|
||||
DstSelect: descriptor.DstSelect,
|
||||
Sampler: ToVulkanSampler(samplerDescriptor));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3334,6 +3683,15 @@ public static class AgcExports
|
||||
MipLevels: 1,
|
||||
MipLevel: 0);
|
||||
|
||||
private static VulkanGuestSampler ToVulkanSampler(IReadOnlyList<uint> descriptor) =>
|
||||
descriptor.Count >= 4
|
||||
? new VulkanGuestSampler(
|
||||
descriptor[0],
|
||||
descriptor[1],
|
||||
descriptor[2],
|
||||
descriptor[3])
|
||||
: default;
|
||||
|
||||
private static byte[] ConvertRgba16FloatToRgba8(ReadOnlySpan<byte> source, uint width, uint height)
|
||||
{
|
||||
var destination = new byte[checked((int)((ulong)width * height * 4))];
|
||||
@@ -3486,7 +3844,8 @@ public static class AgcExports
|
||||
new TranslatedImageBinding(
|
||||
texture,
|
||||
isStorage,
|
||||
binding.MipLevel ?? 0));
|
||||
binding.MipLevel ?? 0,
|
||||
binding.SamplerDescriptor));
|
||||
hasStorageBinding |= isStorage;
|
||||
|
||||
var descriptorState = descriptorValid ? string.Empty : "/invalid-desc";
|
||||
@@ -4248,6 +4607,10 @@ public static class AgcExports
|
||||
var type = (fields[3] >> 28) & 0xFu;
|
||||
var baseLevel = (fields[3] >> 12) & 0xFu;
|
||||
var lastLevel = (fields[3] >> 16) & 0xFu;
|
||||
var pitch = fields.Count >= 5
|
||||
? ((fields[4] >> 13) & 0x3FFFu) + 1
|
||||
: width;
|
||||
var dstSelect = fields[3] & 0xFFFu;
|
||||
if (address == 0 || width == 0 || height == 0)
|
||||
{
|
||||
return false;
|
||||
@@ -4262,7 +4625,9 @@ public static class AgcExports
|
||||
tileMode,
|
||||
type,
|
||||
baseLevel,
|
||||
lastLevel);
|
||||
lastLevel,
|
||||
pitch,
|
||||
dstSelect);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4291,7 +4656,9 @@ public static class AgcExports
|
||||
TileMode: tileMode,
|
||||
Type: Gen5TextureType2D,
|
||||
BaseLevel: 0,
|
||||
LastLevel: 0);
|
||||
LastLevel: 0,
|
||||
Pitch: 1,
|
||||
DstSelect: 0xFAC);
|
||||
}
|
||||
|
||||
private static bool TrySoftwarePresent(
|
||||
|
||||
@@ -60,7 +60,8 @@ internal sealed record Gen5SpirvShader(
|
||||
byte[] Spirv,
|
||||
IReadOnlyList<Gen5GlobalMemoryBinding> GlobalMemoryBindings,
|
||||
IReadOnlyList<Gen5ImageBinding> ImageBindings,
|
||||
uint AttributeCount);
|
||||
uint AttributeCount,
|
||||
IReadOnlyList<Gen5VertexInputBinding> VertexInputs);
|
||||
|
||||
internal readonly record struct Gen5ShaderResourceMapping(
|
||||
Gen5ShaderResourceKind Kind,
|
||||
@@ -271,6 +272,15 @@ internal sealed record Gen5GlobalMemoryBinding(
|
||||
IReadOnlyList<uint> InstructionPcs,
|
||||
byte[] Data);
|
||||
|
||||
internal sealed record Gen5VertexInputBinding(
|
||||
uint Pc,
|
||||
uint Location,
|
||||
uint ComponentCount,
|
||||
ulong BaseAddress,
|
||||
uint Stride,
|
||||
uint OffsetBytes,
|
||||
byte[] Data);
|
||||
|
||||
internal sealed record Gen5ShaderEvaluation(
|
||||
IReadOnlyList<uint> InitialScalarRegisters,
|
||||
IReadOnlyList<uint> ScalarRegisters,
|
||||
@@ -278,7 +288,8 @@ internal sealed record Gen5ShaderEvaluation(
|
||||
IReadOnlyList<Gen5ImageBinding> ImageBindings,
|
||||
IReadOnlyList<Gen5GlobalMemoryBinding> GlobalMemoryBindings,
|
||||
Gen5ComputeSystemRegisters? ComputeSystemRegisters = null,
|
||||
IReadOnlySet<uint>? RuntimeScalarRegisters = null);
|
||||
IReadOnlySet<uint>? RuntimeScalarRegisters = null,
|
||||
IReadOnlyList<Gen5VertexInputBinding>? VertexInputs = null);
|
||||
|
||||
internal sealed record Gen5ShaderInstruction(
|
||||
uint Pc,
|
||||
|
||||
@@ -14,6 +14,7 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
private const int ImageDescriptorDwords = 8;
|
||||
private const int SamplerDescriptorDwords = 4;
|
||||
private const int MaxGlobalMemoryBindingBytes = 16 * 1024 * 1024;
|
||||
private const ulong RdnaWaveMask = 0xFFFF_FFFFUL;
|
||||
|
||||
private readonly record struct BufferDescriptor(
|
||||
ulong BaseAddress,
|
||||
@@ -41,7 +42,8 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
CpuContext ctx,
|
||||
Gen5ShaderState state,
|
||||
out Gen5ShaderEvaluation evaluation,
|
||||
out string error)
|
||||
out string error,
|
||||
bool resolveVertexInputs = false)
|
||||
{
|
||||
evaluation = default!;
|
||||
error = string.Empty;
|
||||
@@ -60,14 +62,15 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
computeSystemRegisters.ClearStaticValues(scalarRegisters);
|
||||
}
|
||||
|
||||
var execMask = ulong.MaxValue;
|
||||
WriteScalarPair(scalarRegisters, 106, ulong.MaxValue, ref execMask);
|
||||
var execMask = RdnaWaveMask;
|
||||
WriteScalarPair(scalarRegisters, 106, 0, ref execMask);
|
||||
WriteScalarPair(scalarRegisters, 126, execMask, ref execMask);
|
||||
var initialScalarRegisters = (uint[])scalarRegisters.Clone();
|
||||
|
||||
var resolved = new List<Gen5ImageBinding>();
|
||||
var globalMemoryBindings = new List<Gen5GlobalMemoryBinding>();
|
||||
var globalMemoryByAddress = new Dictionary<(uint ScalarAddress, ulong BaseAddress), Gen5GlobalMemoryBinding>();
|
||||
var vertexInputBindings = new List<Gen5VertexInputBinding>();
|
||||
var runtimeScalarRegisters = CollectRuntimeScalarRegisters(state.Program);
|
||||
var scalarRegisterSnapshots = new Dictionary<uint, IReadOnlyList<uint>>();
|
||||
var scalarConditionCode = false;
|
||||
@@ -248,6 +251,42 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resolveVertexInputs &&
|
||||
IsVertexFetchCandidate(instruction, bufferMemory, bufferDescriptor))
|
||||
{
|
||||
if (!TryReadGlobalMemory(
|
||||
ctx,
|
||||
bufferDescriptor.BaseAddress,
|
||||
bufferDescriptor.SizeBytes,
|
||||
out var vertexData))
|
||||
{
|
||||
error =
|
||||
$"vertex-buffer-read-failed pc=0x{instruction.Pc:X} " +
|
||||
$"address=0x{bufferDescriptor.BaseAddress:X16} " +
|
||||
$"bytes={bufferDescriptor.SizeBytes} " +
|
||||
$"stride={bufferDescriptor.Stride} records={bufferDescriptor.NumRecords}";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryCreateVertexInputBinding(
|
||||
instruction,
|
||||
bufferMemory,
|
||||
bufferDescriptor,
|
||||
vertexData,
|
||||
(uint)vertexInputBindings.Count,
|
||||
scalarRegisters,
|
||||
out var vertexInputBinding))
|
||||
{
|
||||
error =
|
||||
$"vertex-input-binding-failed pc=0x{instruction.Pc:X} " +
|
||||
$"s{bufferMemory.ScalarResource}";
|
||||
return false;
|
||||
}
|
||||
|
||||
vertexInputBindings.Add(vertexInputBinding);
|
||||
continue;
|
||||
}
|
||||
|
||||
var key = (bufferMemory.ScalarResource, bufferDescriptor.BaseAddress);
|
||||
if (globalMemoryByAddress.TryGetValue(key, out var existingBinding))
|
||||
{
|
||||
@@ -339,10 +378,51 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
resolved,
|
||||
globalMemoryBindings,
|
||||
state.ComputeSystemRegisters,
|
||||
runtimeScalarRegisters);
|
||||
runtimeScalarRegisters,
|
||||
vertexInputBindings);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryCreateVertexInputBinding(
|
||||
Gen5ShaderInstruction instruction,
|
||||
Gen5BufferMemoryControl control,
|
||||
BufferDescriptor descriptor,
|
||||
byte[] data,
|
||||
uint location,
|
||||
uint[] scalarRegisters,
|
||||
out Gen5VertexInputBinding binding)
|
||||
{
|
||||
binding = default!;
|
||||
if (!IsVertexFetchCandidate(instruction, control, descriptor) ||
|
||||
instruction.Sources.Count <= 2 ||
|
||||
!TryEvaluateScalarOperand(instruction.Sources[2], scalarRegisters, out var scalarOffset))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
binding = new Gen5VertexInputBinding(
|
||||
instruction.Pc,
|
||||
location,
|
||||
control.DwordCount,
|
||||
descriptor.BaseAddress,
|
||||
descriptor.Stride,
|
||||
unchecked((uint)control.OffsetBytes + scalarOffset),
|
||||
data);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsVertexFetchCandidate(
|
||||
Gen5ShaderInstruction instruction,
|
||||
Gen5BufferMemoryControl control,
|
||||
BufferDescriptor descriptor) =>
|
||||
control.IndexEnabled &&
|
||||
!control.OffsetEnabled &&
|
||||
control.DwordCount is >= 1 and <= 4 &&
|
||||
descriptor.BaseAddress != 0 &&
|
||||
descriptor.Stride != 0 &&
|
||||
(instruction.Opcode.StartsWith("BufferLoadFormat", StringComparison.Ordinal) ||
|
||||
instruction.Opcode.StartsWith("TBufferLoadFormat", StringComparison.Ordinal));
|
||||
|
||||
private static HashSet<uint> CollectRuntimeScalarRegisters(Gen5ShaderProgram program)
|
||||
{
|
||||
var registers = new HashSet<uint>();
|
||||
@@ -950,9 +1030,9 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
};
|
||||
|
||||
WriteScalarPair(registers, destination.Value, oldExec, ref execMask);
|
||||
execMask = newExec;
|
||||
execMask = MaskWaveValue(newExec);
|
||||
WriteScalarPair(registers, 126, execMask, ref execMask);
|
||||
scalarConditionCode = newExec != 0;
|
||||
scalarConditionCode = execMask != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1000,6 +1080,11 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
return;
|
||||
}
|
||||
|
||||
if (destination == 126)
|
||||
{
|
||||
value = MaskWaveValue(value);
|
||||
}
|
||||
|
||||
registers[destination] = (uint)value;
|
||||
registers[destination + 1] = (uint)(value >> 32);
|
||||
if (destination == 126)
|
||||
@@ -1008,6 +1093,8 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong MaskWaveValue(ulong value) => value & RdnaWaveMask;
|
||||
|
||||
private static bool TryExecuteScalarCompare(
|
||||
Gen5ShaderInstruction instruction,
|
||||
uint[] registers,
|
||||
|
||||
@@ -133,9 +133,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
SpirvOp.ConvertFToU,
|
||||
_uintType,
|
||||
GetFloatSource(instruction, 0));
|
||||
var offset = _module.AddInstruction(
|
||||
SpirvOp.ShiftLeftLogical,
|
||||
_uintType,
|
||||
var offset = ShiftLeftLogical(
|
||||
BitwiseAnd(GetRawSource(instruction, 1), UInt(3)),
|
||||
UInt(3));
|
||||
result = _module.AddInstruction(
|
||||
@@ -352,9 +350,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
result = _module.AddInstruction(
|
||||
SpirvOp.UConvert,
|
||||
_uintType,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.ShiftRightLogical,
|
||||
_ulongType,
|
||||
ShiftRightLogical64(
|
||||
product,
|
||||
_module.Constant64(_ulongType, 32)));
|
||||
break;
|
||||
@@ -417,21 +413,12 @@ internal static partial class Gen5SpirvTranslator
|
||||
var reverse = instruction.Opcode == "VAshrrevI32";
|
||||
var left = GetRawSource(instruction, reverse ? 1 : 0);
|
||||
var right = GetRawSource(instruction, reverse ? 0 : 1);
|
||||
right = BitwiseAnd(right, UInt(31));
|
||||
result = Bitcast(
|
||||
_uintType,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.ShiftRightArithmetic,
|
||||
_intType,
|
||||
Bitcast(_intType, left),
|
||||
right));
|
||||
result = ShiftRightArithmetic(left, right);
|
||||
break;
|
||||
}
|
||||
case "VLshlAddU32":
|
||||
{
|
||||
var shifted = _module.AddInstruction(
|
||||
SpirvOp.ShiftLeftLogical,
|
||||
_uintType,
|
||||
var shifted = ShiftLeftLogical(
|
||||
GetRawSource(instruction, 0),
|
||||
BitwiseAnd(GetRawSource(instruction, 1), UInt(31)));
|
||||
result = IAdd(shifted, GetRawSource(instruction, 2));
|
||||
@@ -439,9 +426,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
}
|
||||
case "VLshlOrU32":
|
||||
{
|
||||
var shifted = _module.AddInstruction(
|
||||
SpirvOp.ShiftLeftLogical,
|
||||
_uintType,
|
||||
var shifted = ShiftLeftLogical(
|
||||
GetRawSource(instruction, 0),
|
||||
BitwiseAnd(GetRawSource(instruction, 1), UInt(31)));
|
||||
result = BitwiseOr(
|
||||
@@ -474,11 +459,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
var added = IAdd(
|
||||
GetRawSource(instruction, 0),
|
||||
GetRawSource(instruction, 1));
|
||||
result = _module.AddInstruction(
|
||||
SpirvOp.ShiftLeftLogical,
|
||||
_uintType,
|
||||
added,
|
||||
BitwiseAnd(GetRawSource(instruction, 2), UInt(31)));
|
||||
result = ShiftLeftLogical(added, GetRawSource(instruction, 2));
|
||||
break;
|
||||
}
|
||||
case "VAdd3U32":
|
||||
@@ -1101,11 +1082,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
_module.AddInstruction(SpirvOp.Not, _uintType, right));
|
||||
break;
|
||||
case "SLshlB32":
|
||||
result = _module.AddInstruction(
|
||||
SpirvOp.ShiftLeftLogical,
|
||||
_uintType,
|
||||
left,
|
||||
BitwiseAnd(right, UInt(31)));
|
||||
result = ShiftLeftLogical(left, right);
|
||||
break;
|
||||
case "SLshrB32":
|
||||
result = ShiftRightLogical(
|
||||
@@ -1113,13 +1090,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
BitwiseAnd(right, UInt(31)));
|
||||
break;
|
||||
case "SAshrI32":
|
||||
result = Bitcast(
|
||||
_uintType,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.ShiftRightArithmetic,
|
||||
_intType,
|
||||
Bitcast(_intType, left),
|
||||
BitwiseAnd(right, UInt(31))));
|
||||
result = ShiftRightArithmetic(left, right);
|
||||
break;
|
||||
case "SBfmB32":
|
||||
result = _module.AddInstruction(
|
||||
@@ -1185,11 +1156,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
{
|
||||
var shift = (uint)(instruction.Opcode[5] - '0');
|
||||
result = IAdd(
|
||||
_module.AddInstruction(
|
||||
SpirvOp.ShiftLeftLogical,
|
||||
_uintType,
|
||||
left,
|
||||
UInt(shift)),
|
||||
ShiftLeftLogical(left, UInt(shift)),
|
||||
right);
|
||||
break;
|
||||
}
|
||||
@@ -1362,14 +1329,10 @@ internal static partial class Gen5SpirvTranslator
|
||||
var shift = _module.AddInstruction(
|
||||
SpirvOp.UConvert,
|
||||
_ulongType,
|
||||
BitwiseAnd(GetRawSource(instruction, 1), UInt(63)));
|
||||
var shiftedValue = _module.AddInstruction(
|
||||
instruction.Opcode == "SLshlB64"
|
||||
? SpirvOp.ShiftLeftLogical
|
||||
: SpirvOp.ShiftRightLogical,
|
||||
_ulongType,
|
||||
left,
|
||||
shift);
|
||||
GetRawSource(instruction, 1));
|
||||
var shiftedValue = instruction.Opcode == "SLshlB64"
|
||||
? ShiftLeftLogical64(left, shift)
|
||||
: ShiftRightLogical64(left, shift);
|
||||
StoreS64(destination, shiftedValue);
|
||||
Store(_scc, IsNotZero64(shiftedValue));
|
||||
return true;
|
||||
@@ -1398,21 +1361,71 @@ internal static partial class Gen5SpirvTranslator
|
||||
_uintType,
|
||||
requestedWidth,
|
||||
remaining);
|
||||
var extracted = instruction.Opcode == "SBfeI64"
|
||||
? Bitcast(
|
||||
var offset64 = _module.AddInstruction(
|
||||
SpirvOp.UConvert,
|
||||
_ulongType,
|
||||
offset);
|
||||
var width64 = _module.AddInstruction(
|
||||
SpirvOp.UConvert,
|
||||
_ulongType,
|
||||
width);
|
||||
var one64 = _module.Constant64(_ulongType, 1);
|
||||
var shifted = ShiftRightLogical64(left, offset64);
|
||||
var partialMask = _module.AddInstruction(
|
||||
SpirvOp.ISub,
|
||||
_ulongType,
|
||||
ShiftLeftLogical64(one64, width64),
|
||||
one64);
|
||||
var fullWidth = _module.AddInstruction(
|
||||
SpirvOp.IEqual,
|
||||
_boolType,
|
||||
width,
|
||||
UInt(64));
|
||||
var mask = _module.AddInstruction(
|
||||
SpirvOp.Select,
|
||||
_ulongType,
|
||||
fullWidth,
|
||||
_module.Constant64(_ulongType, ulong.MaxValue),
|
||||
partialMask);
|
||||
var extracted = _module.AddInstruction(
|
||||
SpirvOp.BitwiseAnd,
|
||||
_ulongType,
|
||||
shifted,
|
||||
mask);
|
||||
if (instruction.Opcode == "SBfeI64")
|
||||
{
|
||||
var signShift = _module.AddInstruction(
|
||||
SpirvOp.ISub,
|
||||
_uintType,
|
||||
width,
|
||||
UInt(1));
|
||||
var signBit = ShiftLeftLogical64(
|
||||
one64,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.UConvert,
|
||||
_ulongType,
|
||||
signShift));
|
||||
var signExtended = _module.AddInstruction(
|
||||
SpirvOp.ISub,
|
||||
_ulongType,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.BitFieldSExtract,
|
||||
_longType,
|
||||
Bitcast(_longType, left),
|
||||
offset,
|
||||
width))
|
||||
: _module.AddInstruction(
|
||||
SpirvOp.BitFieldUExtract,
|
||||
SpirvOp.BitwiseXor,
|
||||
_ulongType,
|
||||
extracted,
|
||||
signBit),
|
||||
signBit);
|
||||
extracted = _module.AddInstruction(
|
||||
SpirvOp.Select,
|
||||
_ulongType,
|
||||
left,
|
||||
offset,
|
||||
width);
|
||||
_module.AddInstruction(
|
||||
SpirvOp.IEqual,
|
||||
_boolType,
|
||||
width,
|
||||
UInt(0)),
|
||||
_module.Constant64(_ulongType, 0),
|
||||
signExtended);
|
||||
}
|
||||
|
||||
StoreS64(destination, extracted);
|
||||
Store(_scc, IsNotZero64(extracted));
|
||||
return true;
|
||||
@@ -1615,11 +1628,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
SpirvOp.UConvert,
|
||||
_ulongType,
|
||||
LoadS(register + 1));
|
||||
high = _module.AddInstruction(
|
||||
SpirvOp.ShiftLeftLogical,
|
||||
_ulongType,
|
||||
high,
|
||||
_module.Constant64(_ulongType, 32));
|
||||
high = ShiftLeftLogical64(high, _module.Constant64(_ulongType, 32));
|
||||
return _module.AddInstruction(SpirvOp.BitwiseOr, _ulongType, low, high);
|
||||
}
|
||||
|
||||
@@ -1628,9 +1637,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
StoreS(
|
||||
register,
|
||||
_module.AddInstruction(SpirvOp.UConvert, _uintType, value));
|
||||
var high = _module.AddInstruction(
|
||||
SpirvOp.ShiftRightLogical,
|
||||
_ulongType,
|
||||
var high = ShiftRightLogical64(
|
||||
value,
|
||||
_module.Constant64(_ulongType, 32));
|
||||
StoreS(
|
||||
@@ -1682,12 +1689,19 @@ internal static partial class Gen5SpirvTranslator
|
||||
{
|
||||
var left = GetRawSource(instruction, reverse ? 1 : 0);
|
||||
var right = GetRawSource(instruction, reverse ? 0 : 1);
|
||||
if (operation is
|
||||
SpirvOp.ShiftLeftLogical or
|
||||
SpirvOp.ShiftRightLogical or
|
||||
SpirvOp.ShiftRightArithmetic)
|
||||
if (operation == SpirvOp.ShiftLeftLogical)
|
||||
{
|
||||
right = BitwiseAnd(right, UInt(31));
|
||||
return ShiftLeftLogical(left, right);
|
||||
}
|
||||
|
||||
if (operation == SpirvOp.ShiftRightLogical)
|
||||
{
|
||||
return ShiftRightLogical(left, right);
|
||||
}
|
||||
|
||||
if (operation == SpirvOp.ShiftRightArithmetic)
|
||||
{
|
||||
return ShiftRightArithmetic(left, right);
|
||||
}
|
||||
|
||||
return _module.AddInstruction(operation, _uintType, left, right);
|
||||
@@ -1986,21 +2000,13 @@ internal static partial class Gen5SpirvTranslator
|
||||
_boolType,
|
||||
localLane,
|
||||
UInt(8));
|
||||
var lowShift = _module.AddInstruction(
|
||||
SpirvOp.ShiftLeftLogical,
|
||||
_uintType,
|
||||
localLane,
|
||||
UInt(2));
|
||||
var lowShift = ShiftLeftLogical(localLane, UInt(2));
|
||||
var highLane = _module.AddInstruction(
|
||||
SpirvOp.ISub,
|
||||
_uintType,
|
||||
localLane,
|
||||
UInt(8));
|
||||
var highShift = _module.AddInstruction(
|
||||
SpirvOp.ShiftLeftLogical,
|
||||
_uintType,
|
||||
highLane,
|
||||
UInt(2));
|
||||
var highShift = ShiftLeftLogical(highLane, UInt(2));
|
||||
var lowSelector = BitwiseAnd(
|
||||
ShiftRightLogical(selectorLow, lowShift),
|
||||
UInt(15));
|
||||
|
||||
@@ -8,6 +8,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
private const uint ScalarRegisterCount = 256;
|
||||
private const uint VectorRegisterCount = 512;
|
||||
private const uint LdsDwordCount = 8192;
|
||||
private const uint RdnaWaveLaneCount = 32;
|
||||
|
||||
public static bool TryCompilePixelShader(
|
||||
Gen5ShaderState state,
|
||||
@@ -95,6 +96,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
private readonly List<uint> _interfaces = [];
|
||||
private readonly Dictionary<uint, uint> _pixelInputs = [];
|
||||
private readonly Dictionary<uint, uint> _vertexOutputs = [];
|
||||
private readonly Dictionary<uint, SpirvVertexInput> _vertexInputsByPc = [];
|
||||
private readonly List<SpirvImageResource> _imageResources = [];
|
||||
private readonly Dictionary<uint, int> _imageBindingByPc = [];
|
||||
private readonly Dictionary<uint, int> _bufferBindingByPc = [];
|
||||
@@ -150,6 +152,11 @@ internal static partial class Gen5SpirvTranslator
|
||||
ImageComponentKind ComponentKind,
|
||||
bool IsStorage);
|
||||
|
||||
private readonly record struct SpirvVertexInput(
|
||||
uint Variable,
|
||||
uint Type,
|
||||
uint ComponentCount);
|
||||
|
||||
public CompilationContext(
|
||||
Gen5SpirvStage stage,
|
||||
Gen5ShaderState state,
|
||||
@@ -283,7 +290,10 @@ internal static partial class Gen5SpirvTranslator
|
||||
_module.Build(),
|
||||
_evaluation.GlobalMemoryBindings,
|
||||
_evaluation.ImageBindings,
|
||||
attributeCount);
|
||||
attributeCount,
|
||||
_stage == Gen5SpirvStage.Vertex
|
||||
? _evaluation.VertexInputs ?? []
|
||||
: []);
|
||||
return true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
@@ -298,6 +308,19 @@ internal static partial class Gen5SpirvTranslator
|
||||
_module.AddCapability(SpirvCapability.Shader);
|
||||
_module.AddCapability(SpirvCapability.Int64);
|
||||
_module.AddCapability(SpirvCapability.ImageQuery);
|
||||
if (_evaluation.ImageBindings.Any(
|
||||
static binding =>
|
||||
(binding.Opcode.StartsWith(
|
||||
"ImageSample",
|
||||
StringComparison.Ordinal) ||
|
||||
binding.Opcode.StartsWith(
|
||||
"ImageGather4",
|
||||
StringComparison.Ordinal)) &&
|
||||
binding.Opcode.EndsWith("O", StringComparison.Ordinal)))
|
||||
{
|
||||
_module.AddCapability(SpirvCapability.ImageGatherExtended);
|
||||
}
|
||||
|
||||
if (UsesSubgroupOperations())
|
||||
{
|
||||
_module.AddCapability(SpirvCapability.GroupNonUniform);
|
||||
@@ -389,10 +412,6 @@ internal static partial class Gen5SpirvTranslator
|
||||
}
|
||||
|
||||
var ldsArrayType = _module.TypeArray(_uintType, LdsDwordCount);
|
||||
_module.AddDecoration(
|
||||
ldsArrayType,
|
||||
SpirvDecoration.ArrayStride,
|
||||
sizeof(uint));
|
||||
var ldsPointer =
|
||||
_module.TypePointer(SpirvStorageClass.Workgroup, ldsArrayType);
|
||||
_workgroupUintPointer =
|
||||
@@ -466,6 +485,11 @@ internal static partial class Gen5SpirvTranslator
|
||||
_module.AddCapability(
|
||||
SpirvCapability.StorageImageWriteWithoutFormat);
|
||||
}
|
||||
else if (isStorage && RequiresExtendedStorageImageFormat(format))
|
||||
{
|
||||
_module.AddCapability(
|
||||
SpirvCapability.StorageImageExtendedFormats);
|
||||
}
|
||||
|
||||
var imageType = _module.TypeImage(
|
||||
componentType,
|
||||
@@ -503,6 +527,13 @@ internal static partial class Gen5SpirvTranslator
|
||||
}
|
||||
}
|
||||
|
||||
private static bool RequiresExtendedStorageImageFormat(
|
||||
SpirvImageFormat format) =>
|
||||
format is not SpirvImageFormat.Unknown and
|
||||
not SpirvImageFormat.Rgba32f and
|
||||
not SpirvImageFormat.Rgba32i and
|
||||
not SpirvImageFormat.Rgba32ui;
|
||||
|
||||
private static (SpirvImageFormat Format, ImageComponentKind Kind)
|
||||
DecodeImageFormat(IReadOnlyList<uint> descriptor)
|
||||
{
|
||||
@@ -583,6 +614,8 @@ internal static partial class Gen5SpirvTranslator
|
||||
|
||||
if (_stage == Gen5SpirvStage.Vertex)
|
||||
{
|
||||
DeclareVertexInputs();
|
||||
|
||||
var inputPointer =
|
||||
_module.TypePointer(SpirvStorageClass.Input, _uintType);
|
||||
_vertexIndexInput = _module.AddGlobalVariable(
|
||||
@@ -628,7 +661,6 @@ internal static partial class Gen5SpirvTranslator
|
||||
outputPointer,
|
||||
SpirvStorageClass.Output);
|
||||
_module.AddDecoration(variable, SpirvDecoration.Location, parameter);
|
||||
_module.AddDecoration(variable, SpirvDecoration.NoPerspective);
|
||||
_vertexOutputs.Add(parameter, variable);
|
||||
_interfaces.Add(variable);
|
||||
}
|
||||
@@ -650,7 +682,6 @@ internal static partial class Gen5SpirvTranslator
|
||||
inputVec4Pointer,
|
||||
SpirvStorageClass.Input);
|
||||
_module.AddDecoration(variable, SpirvDecoration.Location, attribute);
|
||||
_module.AddDecoration(variable, SpirvDecoration.NoPerspective);
|
||||
_pixelInputs.Add(attribute, variable);
|
||||
_interfaces.Add(variable);
|
||||
}
|
||||
@@ -701,6 +732,42 @@ internal static partial class Gen5SpirvTranslator
|
||||
}
|
||||
}
|
||||
|
||||
private void DeclareVertexInputs()
|
||||
{
|
||||
foreach (var input in _evaluation.VertexInputs ?? [])
|
||||
{
|
||||
var type = input.ComponentCount switch
|
||||
{
|
||||
1u => _floatType,
|
||||
2u => _vec2Type,
|
||||
3u => _vec3Type,
|
||||
4u => _vec4Type,
|
||||
_ => 0u,
|
||||
};
|
||||
if (type == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var pointer = _module.TypePointer(SpirvStorageClass.Input, type);
|
||||
var variable = _module.AddGlobalVariable(
|
||||
pointer,
|
||||
SpirvStorageClass.Input);
|
||||
_module.AddName(variable, $"attr{input.Location}");
|
||||
_module.AddDecoration(
|
||||
variable,
|
||||
SpirvDecoration.Location,
|
||||
input.Location);
|
||||
_vertexInputsByPc.TryAdd(
|
||||
input.Pc,
|
||||
new SpirvVertexInput(
|
||||
variable,
|
||||
type,
|
||||
input.ComponentCount));
|
||||
_interfaces.Add(variable);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitInitialState()
|
||||
{
|
||||
for (uint index = 0;
|
||||
@@ -1287,6 +1354,19 @@ internal static partial class Gen5SpirvTranslator
|
||||
out string error)
|
||||
{
|
||||
error = string.Empty;
|
||||
if (_stage == Gen5SpirvStage.Vertex &&
|
||||
_vertexInputsByPc.TryGetValue(instruction.Pc, out var vertexInput))
|
||||
{
|
||||
return TryEmitVertexInputFetch(control, vertexInput, out error);
|
||||
}
|
||||
|
||||
if (_stage == Gen5SpirvStage.Vertex &&
|
||||
IsFormatBufferLoad(instruction.Opcode))
|
||||
{
|
||||
error = $"missing vertex input for {instruction.Opcode} pc=0x{instruction.Pc:X}";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_bufferBindingByPc.TryGetValue(instruction.Pc, out var bindingIndex))
|
||||
{
|
||||
error = "missing buffer-memory binding";
|
||||
@@ -1372,6 +1452,41 @@ internal static partial class Gen5SpirvTranslator
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsFormatBufferLoad(string opcode) =>
|
||||
opcode.StartsWith("BufferLoadFormat", StringComparison.Ordinal) ||
|
||||
opcode.StartsWith("TBufferLoadFormat", StringComparison.Ordinal);
|
||||
|
||||
private bool TryEmitVertexInputFetch(
|
||||
Gen5BufferMemoryControl control,
|
||||
SpirvVertexInput input,
|
||||
out string error)
|
||||
{
|
||||
error = string.Empty;
|
||||
if (control.DwordCount == 0 ||
|
||||
control.DwordCount > input.ComponentCount)
|
||||
{
|
||||
error =
|
||||
$"invalid vertex input fetch components={control.DwordCount} " +
|
||||
$"input={input.ComponentCount}";
|
||||
return false;
|
||||
}
|
||||
|
||||
var loaded = Load(input.Type, input.Variable);
|
||||
for (uint component = 0; component < control.DwordCount; component++)
|
||||
{
|
||||
var value = input.ComponentCount == 1
|
||||
? loaded
|
||||
: _module.AddInstruction(
|
||||
SpirvOp.CompositeExtract,
|
||||
_floatType,
|
||||
loaded,
|
||||
component);
|
||||
StoreV(control.VectorData + component, Bitcast(_uintType, value));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryEmitImage(
|
||||
Gen5ShaderInstruction instruction,
|
||||
Gen5ImageControl image,
|
||||
@@ -1389,11 +1504,20 @@ internal static partial class Gen5SpirvTranslator
|
||||
var imageObject = Load(resource.ObjectType, resource.Variable);
|
||||
if (instruction.Opcode == "ImageGetResinfo")
|
||||
{
|
||||
var queryImage = resource.IsStorage
|
||||
? imageObject
|
||||
: _module.AddInstruction(
|
||||
SpirvOp.Image,
|
||||
resource.ImageType,
|
||||
imageObject);
|
||||
var size = _module.AddInstruction(
|
||||
SpirvOp.ImageQuerySizeLod,
|
||||
resource.IsStorage
|
||||
? SpirvOp.ImageQuerySize
|
||||
: SpirvOp.ImageQuerySizeLod,
|
||||
_module.TypeVector(_intType, 2),
|
||||
imageObject,
|
||||
UInt(0));
|
||||
resource.IsStorage
|
||||
? [queryImage]
|
||||
: [queryImage, UInt(0)]);
|
||||
uint outputIndex = 0;
|
||||
for (uint component = 0; component < 4; component++)
|
||||
{
|
||||
@@ -1504,10 +1628,14 @@ internal static partial class Gen5SpirvTranslator
|
||||
? BuildClampedIntegerCoordinates(image, 0, width, height)
|
||||
: BuildIntegerCoordinates(image, 0);
|
||||
var mipLevel = _evaluation.ImageBindings[bindingIndex].MipLevel ?? 0;
|
||||
var fetchedImage = _module.AddInstruction(
|
||||
SpirvOp.Image,
|
||||
resource.ImageType,
|
||||
imageObject);
|
||||
sampled = _module.AddInstruction(
|
||||
SpirvOp.ImageFetch,
|
||||
resource.VectorType,
|
||||
imageObject,
|
||||
fetchedImage,
|
||||
coordinates,
|
||||
2,
|
||||
UInt(mipLevel));
|
||||
@@ -2057,18 +2185,13 @@ internal static partial class Gen5SpirvTranslator
|
||||
private void StoreS(uint register, uint value)
|
||||
{
|
||||
Store(ScalarPointer(register), value);
|
||||
if (_subgroupInvocationIdInput == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (register is 106 or 107)
|
||||
{
|
||||
Store(_vcc, IsCurrentLaneSet(LoadS64(106)));
|
||||
Store(_vcc, IsWaveMaskActive(LoadS64(106)));
|
||||
}
|
||||
else if (register is 126 or 127)
|
||||
{
|
||||
Store(_exec, IsCurrentLaneSet(LoadS64(126)));
|
||||
Store(_exec, IsWaveMaskActive(LoadS64(126)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2105,12 +2228,49 @@ internal static partial class Gen5SpirvTranslator
|
||||
private uint IAdd(uint left, uint right) =>
|
||||
_module.AddInstruction(SpirvOp.IAdd, _uintType, left, right);
|
||||
|
||||
private uint ShiftLeftLogical(uint left, uint right) =>
|
||||
_module.AddInstruction(
|
||||
SpirvOp.ShiftLeftLogical,
|
||||
_uintType,
|
||||
left,
|
||||
BitwiseAnd(right, UInt(31)));
|
||||
|
||||
private uint ShiftRightLogical(uint left, uint right) =>
|
||||
_module.AddInstruction(SpirvOp.ShiftRightLogical, _uintType, left, right);
|
||||
_module.AddInstruction(
|
||||
SpirvOp.ShiftRightLogical,
|
||||
_uintType,
|
||||
left,
|
||||
BitwiseAnd(right, UInt(31)));
|
||||
|
||||
private uint ShiftRightArithmetic(uint left, uint right) =>
|
||||
Bitcast(
|
||||
_uintType,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.ShiftRightArithmetic,
|
||||
_intType,
|
||||
Bitcast(_intType, left),
|
||||
BitwiseAnd(right, UInt(31))));
|
||||
|
||||
private uint ShiftLeftLogical64(uint left, uint right) =>
|
||||
_module.AddInstruction(
|
||||
SpirvOp.ShiftLeftLogical,
|
||||
_ulongType,
|
||||
left,
|
||||
BitwiseAnd64(right, _module.Constant64(_ulongType, 63)));
|
||||
|
||||
private uint ShiftRightLogical64(uint left, uint right) =>
|
||||
_module.AddInstruction(
|
||||
SpirvOp.ShiftRightLogical,
|
||||
_ulongType,
|
||||
left,
|
||||
BitwiseAnd64(right, _module.Constant64(_ulongType, 63)));
|
||||
|
||||
private uint BitwiseAnd(uint left, uint right) =>
|
||||
_module.AddInstruction(SpirvOp.BitwiseAnd, _uintType, left, right);
|
||||
|
||||
private uint BitwiseAnd64(uint left, uint right) =>
|
||||
_module.AddInstruction(SpirvOp.BitwiseAnd, _ulongType, left, right);
|
||||
|
||||
private uint BitwiseOr(uint left, uint right) =>
|
||||
_module.AddInstruction(SpirvOp.BitwiseOr, _uintType, left, right);
|
||||
|
||||
@@ -2121,25 +2281,44 @@ internal static partial class Gen5SpirvTranslator
|
||||
_module.AddInstruction(SpirvOp.LogicalNot, _boolType, value);
|
||||
|
||||
private uint SubgroupAny(uint condition) =>
|
||||
_module.AddInstruction(
|
||||
SpirvOp.GroupNonUniformAny,
|
||||
_boolType,
|
||||
UInt(3),
|
||||
condition);
|
||||
_subgroupInvocationIdInput == 0
|
||||
? condition
|
||||
: _module.AddInstruction(
|
||||
SpirvOp.GroupNonUniformAny,
|
||||
_boolType,
|
||||
UInt(3),
|
||||
condition);
|
||||
|
||||
private uint CurrentLaneBit()
|
||||
{
|
||||
var lane = _module.AddInstruction(
|
||||
SpirvOp.UConvert,
|
||||
_ulongType,
|
||||
Load(_uintType, _subgroupInvocationIdInput));
|
||||
return _module.AddInstruction(
|
||||
SpirvOp.ShiftLeftLogical,
|
||||
_ulongType,
|
||||
if (_subgroupInvocationIdInput == 0)
|
||||
{
|
||||
return _module.Constant64(_ulongType, 1);
|
||||
}
|
||||
|
||||
var lane = Load(_uintType, _subgroupInvocationIdInput);
|
||||
var maskedLane = BitwiseAnd(lane, UInt(RdnaWaveLaneCount - 1));
|
||||
var shifted = ShiftLeftLogical64(
|
||||
_module.Constant64(_ulongType, 1),
|
||||
lane);
|
||||
_module.AddInstruction(
|
||||
SpirvOp.UConvert,
|
||||
_ulongType,
|
||||
maskedLane));
|
||||
return _module.AddInstruction(
|
||||
SpirvOp.Select,
|
||||
_ulongType,
|
||||
IsCurrentLaneInRdnaWave(),
|
||||
shifted,
|
||||
_module.Constant64(_ulongType, 0));
|
||||
}
|
||||
|
||||
private uint IsCurrentLaneInRdnaWave() =>
|
||||
_module.AddInstruction(
|
||||
SpirvOp.ULessThan,
|
||||
_boolType,
|
||||
Load(_uintType, _subgroupInvocationIdInput),
|
||||
UInt(RdnaWaveLaneCount));
|
||||
|
||||
private uint BooleanToLaneMask(uint condition) =>
|
||||
_module.AddInstruction(
|
||||
SpirvOp.Select,
|
||||
@@ -2148,6 +2327,11 @@ internal static partial class Gen5SpirvTranslator
|
||||
CurrentLaneBit(),
|
||||
_module.Constant64(_ulongType, 0));
|
||||
|
||||
private uint IsWaveMaskActive(uint mask) =>
|
||||
_subgroupInvocationIdInput == 0
|
||||
? IsNotZero64(mask)
|
||||
: IsCurrentLaneSet(mask);
|
||||
|
||||
private uint IsCurrentLaneSet(uint mask) =>
|
||||
IsNotZero64(
|
||||
_module.AddInstruction(
|
||||
@@ -2163,10 +2347,11 @@ internal static partial class Gen5SpirvTranslator
|
||||
{
|
||||
var activeLabel = _module.AllocateId();
|
||||
var mergeLabel = _module.AllocateId();
|
||||
var active = Load(_boolType, _exec);
|
||||
_module.AddStatement(SpirvOp.SelectionMerge, mergeLabel, 0);
|
||||
_module.AddStatement(
|
||||
SpirvOp.BranchConditional,
|
||||
Load(_boolType, _exec),
|
||||
active,
|
||||
activeLabel,
|
||||
mergeLabel);
|
||||
_module.AddLabel(activeLabel);
|
||||
@@ -2193,7 +2378,8 @@ internal static partial class Gen5SpirvTranslator
|
||||
instruction.Destinations.Any(IsWaveMaskOperand));
|
||||
|
||||
private bool UsesSubgroupOperations() =>
|
||||
UsesSubgroupShuffle() || UsesWaveControl();
|
||||
_stage == Gen5SpirvStage.Compute &&
|
||||
(UsesSubgroupShuffle() || UsesWaveControl());
|
||||
|
||||
private static bool IsWaveMaskOperand(Gen5Operand operand) =>
|
||||
operand.Kind == Gen5OperandKind.ScalarRegister &&
|
||||
|
||||
@@ -62,6 +62,7 @@ internal enum SpirvOp : ushort
|
||||
ImageDrefGather = 97,
|
||||
ImageRead = 98,
|
||||
ImageWrite = 99,
|
||||
Image = 100,
|
||||
ImageQuerySizeLod = 103,
|
||||
ImageQuerySize = 104,
|
||||
ImageQueryLod = 105,
|
||||
@@ -173,6 +174,8 @@ internal enum SpirvCapability : uint
|
||||
Float64 = 10,
|
||||
Int64 = 11,
|
||||
Int16 = 22,
|
||||
ImageGatherExtended = 25,
|
||||
StorageImageExtendedFormats = 49,
|
||||
ImageQuery = 50,
|
||||
StorageImageReadWithoutFormat = 55,
|
||||
StorageImageWriteWithoutFormat = 56,
|
||||
@@ -310,6 +313,18 @@ internal sealed class SpirvModuleBuilder
|
||||
private readonly Dictionary<(uint Width, bool Signed), uint> _integerTypes = [];
|
||||
private readonly Dictionary<uint, uint> _floatTypes = [];
|
||||
private readonly Dictionary<(uint Component, uint Count), uint> _vectorTypes = [];
|
||||
private readonly Dictionary<
|
||||
(
|
||||
uint SampledType,
|
||||
SpirvImageDim Dimension,
|
||||
bool Depth,
|
||||
bool Arrayed,
|
||||
bool Multisampled,
|
||||
uint Sampled,
|
||||
SpirvImageFormat Format
|
||||
),
|
||||
uint> _imageTypes = [];
|
||||
private readonly Dictionary<uint, uint> _sampledImageTypes = [];
|
||||
private readonly Dictionary<(SpirvStorageClass Storage, uint Type), uint> _pointerTypes = [];
|
||||
private readonly Dictionary<(uint Element, uint Count), uint> _arrayTypes = [];
|
||||
private readonly Dictionary<uint, uint> _runtimeArrayTypes = [];
|
||||
@@ -478,6 +493,19 @@ internal sealed class SpirvModuleBuilder
|
||||
uint sampled,
|
||||
SpirvImageFormat format)
|
||||
{
|
||||
var key = (
|
||||
sampledType,
|
||||
dimension,
|
||||
depth,
|
||||
arrayed,
|
||||
multisampled,
|
||||
sampled,
|
||||
format);
|
||||
if (_imageTypes.TryGetValue(key, out var existing))
|
||||
{
|
||||
return existing;
|
||||
}
|
||||
|
||||
var id = AllocateId();
|
||||
Emit(
|
||||
_typesConstantsGlobals,
|
||||
@@ -490,13 +518,20 @@ internal sealed class SpirvModuleBuilder
|
||||
multisampled ? 1u : 0u,
|
||||
sampled,
|
||||
(uint)format);
|
||||
_imageTypes.Add(key, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
public uint TypeSampledImage(uint imageType)
|
||||
{
|
||||
if (_sampledImageTypes.TryGetValue(imageType, out var existing))
|
||||
{
|
||||
return existing;
|
||||
}
|
||||
|
||||
var id = AllocateId();
|
||||
Emit(_typesConstantsGlobals, SpirvOp.TypeSampledImage, id, imageType);
|
||||
_sampledImageTypes.Add(imageType, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user