mirror of
https://github.com/par274/sharpemu.git
synced 2026-08-01 23:49:44 +08:00
[shader decoder] correct RDNA2 operands, fixing synchronization problems
This commit is contained in:
@@ -77,8 +77,11 @@ public static class AgcExports
|
||||
private const uint CbColor0Attrib3 = 0x3B8;
|
||||
private const int ColorTargetCount = 8;
|
||||
private const uint PsTextureUserDataRegister = 0xC;
|
||||
private const uint VsUserDataRegister = 0x4C;
|
||||
private const uint GsUserDataRegister = 0x8C;
|
||||
private const uint EsUserDataRegister = 0xCC;
|
||||
private const uint ComputeUserDataRegister = 0x240;
|
||||
private const uint NggUserDataScalarRegisterBase = 8;
|
||||
private const uint Gen5TextureFormatR8G8B8A8Unorm = 56;
|
||||
private const uint Gen5TextureFormatR16G16B16A16Float = 71;
|
||||
private const uint Gen5TextureType2D = 9;
|
||||
@@ -2873,9 +2876,10 @@ public static class AgcExports
|
||||
exportShaderAddress,
|
||||
exportShaderHeader,
|
||||
state.ShRegisters,
|
||||
EsUserDataRegister,
|
||||
SelectExportUserDataRegister(state.ShRegisters),
|
||||
out var exportState,
|
||||
out error) ||
|
||||
out error,
|
||||
userDataScalarRegisterBase: NggUserDataScalarRegisterBase) ||
|
||||
!Gen5ShaderScalarEvaluator.TryEvaluate(
|
||||
ctx,
|
||||
exportState,
|
||||
@@ -2904,11 +2908,13 @@ public static class AgcExports
|
||||
HasPixelColorExport(pixelState, target.Slot))
|
||||
.ToArray();
|
||||
var outputKind = GetPixelOutputKind(renderTargets.FirstOrDefault().NumberType);
|
||||
var exportStateFingerprint = ComputeShaderStateFingerprint(exportEvaluation);
|
||||
var pixelStateFingerprint = ComputeShaderStateFingerprint(pixelEvaluation);
|
||||
var shaderKey = (
|
||||
exportShaderAddress,
|
||||
ComputeShaderStateFingerprint(exportEvaluation),
|
||||
exportStateFingerprint,
|
||||
pixelShaderAddress,
|
||||
ComputeShaderStateFingerprint(pixelEvaluation),
|
||||
pixelStateFingerprint,
|
||||
outputKind);
|
||||
(byte[] Vertex, byte[] Pixel) compiled;
|
||||
lock (_submitTraceGate)
|
||||
@@ -2943,6 +2949,18 @@ public static class AgcExports
|
||||
}
|
||||
|
||||
compiled = (vertexShader.Spirv, pixelShader.Spirv);
|
||||
DumpSpirv(
|
||||
"vs",
|
||||
exportShaderAddress,
|
||||
exportStateFingerprint,
|
||||
compiled.Vertex,
|
||||
exportState.Program);
|
||||
DumpSpirv(
|
||||
"ps",
|
||||
pixelShaderAddress,
|
||||
pixelStateFingerprint,
|
||||
compiled.Pixel,
|
||||
pixelState.Program);
|
||||
lock (_submitTraceGate)
|
||||
{
|
||||
_graphicsSpirvCache.TryAdd(shaderKey, compiled);
|
||||
@@ -3004,7 +3022,9 @@ public static class AgcExports
|
||||
var byteOffset = checked((ulong)state.DrawIndexOffset * (uint)bytesPerIndex);
|
||||
var byteCount = checked((int)(indexCount * (uint)bytesPerIndex));
|
||||
var data = new byte[byteCount];
|
||||
return ctx.Memory.TryRead(state.IndexBufferAddress + byteOffset, data)
|
||||
var address = state.IndexBufferAddress + byteOffset;
|
||||
return (ctx.Memory.TryRead(address, data) ||
|
||||
KernelMemoryCompatExports.TryReadTrackedLibcHeap(address, data))
|
||||
? new VulkanGuestIndexBuffer(data, is32Bit)
|
||||
: null;
|
||||
}
|
||||
@@ -3144,12 +3164,22 @@ public static class AgcExports
|
||||
$"fmt{texture.Format}/num{texture.NumberType}/tile{texture.TileMode}" +
|
||||
$"/storage={binding.IsStorage}{target}/{probe}{writer}";
|
||||
}));
|
||||
var buffers = string.Join(
|
||||
',',
|
||||
draw.GlobalMemoryBindings.Select((binding, index) =>
|
||||
$"{index}:0x{binding.BaseAddress:X16}:{binding.Data.Length}:" +
|
||||
Convert.ToHexString(binding.Data.AsSpan(0, Math.Min(binding.Data.Length, 32)))));
|
||||
var indices = draw.IndexBuffer is { } indexBuffer
|
||||
? $"{(indexBuffer.Is32Bit ? 32 : 16)}:" +
|
||||
Convert.ToHexString(indexBuffer.Data.AsSpan(0, Math.Min(indexBuffer.Data.Length, 32)))
|
||||
: "none";
|
||||
TraceAgcShader(
|
||||
$"agc.shader_draw es=0x{draw.ExportShaderAddress:X16} " +
|
||||
$"ps=0x{draw.PixelShaderAddress:X16} spirv={draw.PixelSpirv.Length} " +
|
||||
$"primitive=0x{draw.PrimitiveType:X} " +
|
||||
$"ps_ena=0x{psInputEna:X8} ps_addr=0x{psInputAddr:X8} " +
|
||||
$"targets=[{targets}] textures=[{textures}]");
|
||||
$"targets=[{targets}] textures=[{textures}] " +
|
||||
$"buffers=[{buffers}] indices=[{indices}]");
|
||||
}
|
||||
|
||||
private static IReadOnlyList<VulkanGuestDrawTexture> CreateVulkanGuestDrawTextures(
|
||||
@@ -3511,6 +3541,12 @@ public static class AgcExports
|
||||
out computeError))
|
||||
{
|
||||
computeSpirv = compiledCompute.Spirv;
|
||||
DumpSpirv(
|
||||
"cs",
|
||||
shaderAddress,
|
||||
shaderKey.Item2,
|
||||
computeSpirv,
|
||||
shaderState.Program);
|
||||
}
|
||||
|
||||
if (computeSpirv is not null)
|
||||
@@ -3527,6 +3563,7 @@ public static class AgcExports
|
||||
var globalMemoryBuffers =
|
||||
CreateVulkanGuestMemoryBuffers(evaluation.GlobalMemoryBindings);
|
||||
VulkanVideoPresenter.SubmitComputeDispatch(
|
||||
shaderAddress,
|
||||
computeSpirv,
|
||||
textures,
|
||||
globalMemoryBuffers,
|
||||
@@ -3601,6 +3638,62 @@ public static class AgcExports
|
||||
private static string DescribeRegister(uint? register) =>
|
||||
register.HasValue ? $"s{register.Value}" : "-";
|
||||
|
||||
private static uint SelectExportUserDataRegister(
|
||||
IReadOnlyDictionary<uint, uint> registers)
|
||||
{
|
||||
if (HasUserDataRange(registers, GsUserDataRegister))
|
||||
{
|
||||
return GsUserDataRegister;
|
||||
}
|
||||
|
||||
if (HasUserDataRange(registers, EsUserDataRegister))
|
||||
{
|
||||
return EsUserDataRegister;
|
||||
}
|
||||
|
||||
if (HasUserDataRange(registers, VsUserDataRegister))
|
||||
{
|
||||
return VsUserDataRegister;
|
||||
}
|
||||
|
||||
var esValues = CountUserDataValues(registers, EsUserDataRegister);
|
||||
var vsValues = CountUserDataValues(registers, VsUserDataRegister);
|
||||
return esValues == 0 && vsValues != 0
|
||||
? VsUserDataRegister
|
||||
: EsUserDataRegister;
|
||||
}
|
||||
|
||||
private static bool HasUserDataRange(
|
||||
IReadOnlyDictionary<uint, uint> registers,
|
||||
uint startRegister)
|
||||
{
|
||||
for (var index = 0u; index < 16; index++)
|
||||
{
|
||||
if (registers.ContainsKey(startRegister + index))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int CountUserDataValues(
|
||||
IReadOnlyDictionary<uint, uint> registers,
|
||||
uint startRegister)
|
||||
{
|
||||
var count = 0;
|
||||
for (var index = 0u; index < 16; index++)
|
||||
{
|
||||
count += registers.TryGetValue(startRegister + index, out var value) &&
|
||||
value != 0
|
||||
? 1
|
||||
: 0;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private static uint GetComputeLocalSize(
|
||||
IReadOnlyDictionary<uint, uint> registers,
|
||||
uint register)
|
||||
@@ -3798,7 +3891,31 @@ public static class AgcExports
|
||||
}
|
||||
}
|
||||
|
||||
return $"probe={reads}/{sampleCount}:{nonzero}:0x{hash:X16}";
|
||||
var bytesPerTexel = GetTextureBytesPerTexel(texture.Format);
|
||||
var texels = bytesPerTexel is > 0 and <= 16
|
||||
? string.Join(
|
||||
'/',
|
||||
ProbeTextureTexel(ctx, texture.Address, (int)bytesPerTexel),
|
||||
ProbeTextureTexel(
|
||||
ctx,
|
||||
texture.Address +
|
||||
(((ulong)(texture.Height / 2) * texture.Width) + (texture.Width / 2)) *
|
||||
bytesPerTexel,
|
||||
(int)bytesPerTexel),
|
||||
ProbeTextureTexel(
|
||||
ctx,
|
||||
texture.Address + totalBytes - bytesPerTexel,
|
||||
(int)bytesPerTexel))
|
||||
: "unsupported";
|
||||
return $"probe={reads}/{sampleCount}:{nonzero}:0x{hash:X16}:texels={texels}";
|
||||
}
|
||||
|
||||
private static string ProbeTextureTexel(CpuContext ctx, ulong address, int size)
|
||||
{
|
||||
var texel = new byte[size];
|
||||
return ctx.Memory.TryRead(address, texel)
|
||||
? Convert.ToHexString(texel)
|
||||
: "unreadable";
|
||||
}
|
||||
|
||||
private static ulong GetTextureBytesPerTexel(uint format) =>
|
||||
@@ -3912,9 +4029,10 @@ public static class AgcExports
|
||||
exportShaderAddress,
|
||||
exportShaderHeader,
|
||||
state.ShRegisters,
|
||||
EsUserDataRegister,
|
||||
SelectExportUserDataRegister(state.ShRegisters),
|
||||
out var exportState,
|
||||
out _) &&
|
||||
out _,
|
||||
userDataScalarRegisterBase: NggUserDataScalarRegisterBase) &&
|
||||
Gen5ShaderTranslator.TryCreateState(
|
||||
ctx,
|
||||
pixelShaderAddress,
|
||||
@@ -4882,6 +5000,46 @@ public static class AgcExports
|
||||
? "none"
|
||||
: string.Join(',', values.Select(static value => $"{value:X8}"));
|
||||
|
||||
private static void DumpSpirv(
|
||||
string stage,
|
||||
ulong shaderAddress,
|
||||
ulong stateFingerprint,
|
||||
byte[] spirv,
|
||||
Gen5ShaderProgram program)
|
||||
{
|
||||
if (spirv.Length == 0 ||
|
||||
!string.Equals(
|
||||
Environment.GetEnvironmentVariable("SHARPEMU_DUMP_SPIRV"),
|
||||
"1",
|
||||
StringComparison.Ordinal))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var directory = Path.Combine(AppContext.BaseDirectory, "shader-dumps");
|
||||
Directory.CreateDirectory(directory);
|
||||
var name = $"{shaderAddress:X16}-{stateFingerprint:X16}.{stage}";
|
||||
File.WriteAllBytes(Path.Combine(directory, $"{name}.spv"), spirv);
|
||||
|
||||
var lines = new List<string>(program.Instructions.Count + 2)
|
||||
{
|
||||
$"address=0x{program.Address:X16}",
|
||||
"pc words opcode destinations <- sources control",
|
||||
};
|
||||
foreach (var instruction in program.Instructions)
|
||||
{
|
||||
lines.Add(
|
||||
$"0x{instruction.Pc:X4} " +
|
||||
$"{string.Join('_', instruction.Words.Select(static word => $"{word:X8}"))} " +
|
||||
$"{instruction.Opcode} " +
|
||||
$"{string.Join(',', instruction.Destinations)} <- " +
|
||||
$"{string.Join(',', instruction.Sources)} " +
|
||||
$"{instruction.Control}");
|
||||
}
|
||||
|
||||
File.WriteAllLines(Path.Combine(directory, $"{name}.ir.txt"), lines);
|
||||
}
|
||||
|
||||
private static void TraceCreateShader(ulong destinationAddress, ulong headerAddress, ulong codeAddress, string detail)
|
||||
{
|
||||
var isOk = string.Equals(detail, "ok", StringComparison.Ordinal);
|
||||
|
||||
@@ -131,7 +131,8 @@ internal sealed record Gen5ShaderState(
|
||||
Gen5ShaderProgram Program,
|
||||
IReadOnlyList<uint> UserData,
|
||||
Gen5ShaderMetadata? Metadata,
|
||||
Gen5ComputeSystemRegisters? ComputeSystemRegisters = null);
|
||||
Gen5ComputeSystemRegisters? ComputeSystemRegisters = null,
|
||||
uint UserDataScalarRegisterBase = 0);
|
||||
|
||||
internal readonly record struct Gen5Operand(Gen5OperandKind Kind, uint Value)
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using SharpEmu.HLE;
|
||||
using SharpEmu.Libs.Kernel;
|
||||
using System.Buffers.Binary;
|
||||
using System.Numerics;
|
||||
|
||||
@@ -45,9 +46,13 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
evaluation = default!;
|
||||
error = string.Empty;
|
||||
var scalarRegisters = new uint[ScalarRegisterCount];
|
||||
for (var index = 0; index < state.UserData.Count && index < scalarRegisters.Length; index++)
|
||||
for (var index = 0;
|
||||
index < state.UserData.Count &&
|
||||
state.UserDataScalarRegisterBase + (uint)index < scalarRegisters.Length;
|
||||
index++)
|
||||
{
|
||||
scalarRegisters[index] = state.UserData[index];
|
||||
scalarRegisters[state.UserDataScalarRegisterBase + (uint)index] =
|
||||
state.UserData[index];
|
||||
}
|
||||
|
||||
if (state.ComputeSystemRegisters is { } computeSystemRegisters)
|
||||
@@ -259,10 +264,16 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
bufferDescriptor.SizeBytes,
|
||||
out var data))
|
||||
{
|
||||
var descriptorWords = string.Join(
|
||||
':',
|
||||
Enumerable.Range(0, 4).Select(index =>
|
||||
$"{scalarRegisters[bufferMemory.ScalarResource + (uint)index]:X8}"));
|
||||
error =
|
||||
$"buffer-memory-read-failed pc=0x{instruction.Pc:X} " +
|
||||
$"address=0x{bufferDescriptor.BaseAddress:X16} " +
|
||||
$"bytes={bufferDescriptor.SizeBytes}";
|
||||
$"bytes={bufferDescriptor.SizeBytes} " +
|
||||
$"stride={bufferDescriptor.Stride} records={bufferDescriptor.NumRecords} " +
|
||||
$"s{bufferMemory.ScalarResource}=[{descriptorWords}]";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -471,10 +482,22 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
return false;
|
||||
}
|
||||
|
||||
data = GC.AllocateUninitializedArray<byte>((int)cappedSize);
|
||||
if (ctx.Memory.TryRead(baseAddress, data))
|
||||
var candidateSize = (int)cappedSize;
|
||||
while (candidateSize >= sizeof(uint))
|
||||
{
|
||||
return true;
|
||||
data = GC.AllocateUninitializedArray<byte>(candidateSize);
|
||||
if (ctx.Memory.TryRead(baseAddress, data) ||
|
||||
KernelMemoryCompatExports.TryReadTrackedLibcHeap(baseAddress, data))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (candidateSize == sizeof(uint))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
candidateSize = Math.Max(candidateSize / 2, sizeof(uint));
|
||||
}
|
||||
|
||||
data = [];
|
||||
@@ -582,6 +605,49 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
return true;
|
||||
}
|
||||
|
||||
if (instruction.Opcode is "SBfeU64" or "SBfeI64")
|
||||
{
|
||||
if (instruction.Sources.Count < 2 ||
|
||||
destination.Value >= ScalarRegisterCount - 1 ||
|
||||
!TryEvaluateScalarOperand64(
|
||||
instruction.Sources[0],
|
||||
registers,
|
||||
execMask,
|
||||
out var source) ||
|
||||
!TryEvaluateScalarOperand(
|
||||
instruction.Sources[1],
|
||||
registers,
|
||||
out var control))
|
||||
{
|
||||
error = $"scalar-source64 pc=0x{instruction.Pc:X} op={instruction.Opcode}";
|
||||
return false;
|
||||
}
|
||||
|
||||
var offset = (int)control & 63;
|
||||
var width = Math.Min(((int)control >> 16) & 0x7F, 64 - offset);
|
||||
ulong value;
|
||||
if (width == 0)
|
||||
{
|
||||
value = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = source >> offset;
|
||||
if (width < 64)
|
||||
{
|
||||
value &= ulong.MaxValue >> (64 - width);
|
||||
if (instruction.Opcode == "SBfeI64")
|
||||
{
|
||||
value = unchecked((ulong)((long)(value << (64 - width)) >> (64 - width)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WriteScalarPair(registers, destination.Value, value, ref execMask);
|
||||
scalarConditionCode = value != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (instruction.Opcode is
|
||||
"SCselectB64" or
|
||||
"SAndB64" or
|
||||
|
||||
@@ -113,7 +113,8 @@ internal static class Gen5ShaderTranslator
|
||||
uint userDataBaseRegister,
|
||||
out Gen5ShaderState state,
|
||||
out string error,
|
||||
Gen5ComputeSystemRegisters? computeSystemRegisters = null)
|
||||
Gen5ComputeSystemRegisters? computeSystemRegisters = null,
|
||||
uint userDataScalarRegisterBase = 0)
|
||||
{
|
||||
state = default!;
|
||||
if (!TryDecodeProgram(ctx, shaderAddress, out var program, out error))
|
||||
@@ -134,7 +135,12 @@ internal static class Gen5ShaderTranslator
|
||||
shaderRegisters.TryGetValue(userDataBaseRegister + index, out userData[index]);
|
||||
}
|
||||
|
||||
state = new Gen5ShaderState(program, userData, metadata, computeSystemRegisters);
|
||||
state = new Gen5ShaderState(
|
||||
program,
|
||||
userData,
|
||||
metadata,
|
||||
computeSystemRegisters,
|
||||
userDataScalarRegisterBase);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -179,7 +185,9 @@ internal static class Gen5ShaderTranslator
|
||||
: string.Empty;
|
||||
if (state.Metadata is not { } metadata)
|
||||
{
|
||||
return $"ud[{userData}]{systemRegisters} metadata=missing";
|
||||
return
|
||||
$"ud_base=s{state.UserDataScalarRegisterBase} ud[{userData}]" +
|
||||
$"{systemRegisters} metadata=missing";
|
||||
}
|
||||
|
||||
var direct = string.Join(
|
||||
@@ -191,7 +199,8 @@ internal static class Gen5ShaderTranslator
|
||||
$"{resource.Kind}[{resource.Slot}]@{resource.OffsetDwords}" +
|
||||
(resource.SizeFlag ? "+" : string.Empty)));
|
||||
return
|
||||
$"ud[{userData}]{systemRegisters} metadata[eud={metadata.ExtendedUserDataSizeDwords}," +
|
||||
$"ud_base=s{state.UserDataScalarRegisterBase} ud[{userData}]" +
|
||||
$"{systemRegisters} metadata[eud={metadata.ExtendedUserDataSizeDwords}," +
|
||||
$"srt={metadata.ShaderResourceTableSizeDwords},direct={direct},resources={resources}]";
|
||||
}
|
||||
|
||||
@@ -668,9 +677,8 @@ internal static class Gen5ShaderTranslator
|
||||
error = string.Empty;
|
||||
name = opcode switch
|
||||
{
|
||||
0x00 => "VCndmaskB32",
|
||||
0x01 => "VReadlaneB32",
|
||||
0x02 => "VWritelaneB32",
|
||||
0x01 => "VCndmaskB32",
|
||||
0x02 => "VDot2cF32F16",
|
||||
0x03 => "VAddF32",
|
||||
0x04 => "VSubF32",
|
||||
0x05 => "VSubrevF32",
|
||||
@@ -810,7 +818,7 @@ internal static class Gen5ShaderTranslator
|
||||
}
|
||||
: opcode switch
|
||||
{
|
||||
0x101 => "VReadlaneB32",
|
||||
0x101 => "VCndmaskB32",
|
||||
0x103 => "VAddF32",
|
||||
0x104 => "VSubF32",
|
||||
0x108 => "VMulF32",
|
||||
@@ -1246,17 +1254,16 @@ internal static class Gen5ShaderTranslator
|
||||
var scalarOffset = (extra >> 25) & 0x7F;
|
||||
var offset = SignExtend(extra & 0x1FFFFF, 21);
|
||||
var count = ScalarLoadDwordCount(opcode);
|
||||
var dynamicOffsetRegister = scalarOffset <= 105 || scalarOffset == 124
|
||||
? scalarOffset
|
||||
: (uint?)null;
|
||||
sources = dynamicOffsetRegister.HasValue
|
||||
? [Gen5Operand.Scalar(scalarBase), Gen5Operand.Scalar(dynamicOffsetRegister.Value)]
|
||||
: [Gen5Operand.Scalar(scalarBase)];
|
||||
sources =
|
||||
[
|
||||
Gen5Operand.Scalar(scalarBase),
|
||||
Gen5Operand.Scalar(scalarOffset),
|
||||
];
|
||||
destinations = Enumerable
|
||||
.Range((int)scalarDestination, checked((int)count))
|
||||
.Select(index => Gen5Operand.Scalar((uint)index))
|
||||
.ToArray();
|
||||
control = new Gen5ScalarMemoryControl(count, offset, dynamicOffsetRegister);
|
||||
control = new Gen5ScalarMemoryControl(count, offset, scalarOffset);
|
||||
break;
|
||||
}
|
||||
case Gen5ShaderEncoding.Vop1:
|
||||
|
||||
@@ -38,7 +38,9 @@ internal static partial class Gen5SpirvTranslator
|
||||
break;
|
||||
case "VCndmaskB32":
|
||||
{
|
||||
var condition = Load(_boolType, _vcc);
|
||||
var condition = instruction.Sources.Count > 2
|
||||
? IsCurrentLaneSet(GetRawSource64(instruction, 2))
|
||||
: Load(_boolType, _vcc);
|
||||
result = _module.AddInstruction(
|
||||
SpirvOp.Select,
|
||||
_uintType,
|
||||
@@ -857,7 +859,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
condition = _module.AddInstruction(operation, _boolType, left, right);
|
||||
}
|
||||
|
||||
Store(_vcc, condition);
|
||||
StoreWaveMask(106, condition);
|
||||
if (opcode.StartsWith("VCmpx", StringComparison.Ordinal))
|
||||
{
|
||||
var active = _module.AddInstruction(
|
||||
@@ -865,7 +867,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
_boolType,
|
||||
Load(_boolType, _exec),
|
||||
condition);
|
||||
Store(_exec, active);
|
||||
StoreWaveMask(126, active);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -930,7 +932,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
}
|
||||
|
||||
if (instruction.Opcode.EndsWith("B64", StringComparison.Ordinal) ||
|
||||
instruction.Opcode == "SWqmB64")
|
||||
instruction.Opcode is "SWqmB64" or "SBfeU64" or "SBfeI64")
|
||||
{
|
||||
return TryEmitScalar64(instruction, destination, out error);
|
||||
}
|
||||
@@ -1312,7 +1314,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
var left = GetRawSource64(instruction, 0);
|
||||
if (instruction.Opcode.EndsWith("SaveexecB64", StringComparison.Ordinal))
|
||||
{
|
||||
var oldExec = LoadS64(126);
|
||||
var oldExec = BooleanToLaneMask(Load(_boolType, _exec));
|
||||
var notLeft = _module.AddInstruction(SpirvOp.Not, _ulongType, left);
|
||||
var newExec = instruction.Opcode switch
|
||||
{
|
||||
@@ -1345,7 +1347,6 @@ internal static partial class Gen5SpirvTranslator
|
||||
|
||||
StoreS64(destination, oldExec);
|
||||
StoreS64(126, newExec);
|
||||
Store(_exec, IsNotZero64(newExec));
|
||||
Store(_scc, IsNotZero64(newExec));
|
||||
return true;
|
||||
}
|
||||
@@ -1374,6 +1375,49 @@ internal static partial class Gen5SpirvTranslator
|
||||
return true;
|
||||
}
|
||||
|
||||
if (instruction.Opcode is "SBfeU64" or "SBfeI64")
|
||||
{
|
||||
if (instruction.Sources.Count < 2)
|
||||
{
|
||||
error = "missing scalar 64-bit bitfield source";
|
||||
return false;
|
||||
}
|
||||
|
||||
var control = GetRawSource(instruction, 1);
|
||||
var offset = BitwiseAnd(control, UInt(63));
|
||||
var requestedWidth = BitwiseAnd(
|
||||
ShiftRightLogical(control, UInt(16)),
|
||||
UInt(0x7F));
|
||||
var remaining = _module.AddInstruction(
|
||||
SpirvOp.ISub,
|
||||
_uintType,
|
||||
UInt(64),
|
||||
offset);
|
||||
var width = Ext(
|
||||
38,
|
||||
_uintType,
|
||||
requestedWidth,
|
||||
remaining);
|
||||
var extracted = instruction.Opcode == "SBfeI64"
|
||||
? Bitcast(
|
||||
_ulongType,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.BitFieldSExtract,
|
||||
_longType,
|
||||
Bitcast(_longType, left),
|
||||
offset,
|
||||
width))
|
||||
: _module.AddInstruction(
|
||||
SpirvOp.BitFieldUExtract,
|
||||
_ulongType,
|
||||
left,
|
||||
offset,
|
||||
width);
|
||||
StoreS64(destination, extracted);
|
||||
Store(_scc, IsNotZero64(extracted));
|
||||
return true;
|
||||
}
|
||||
|
||||
uint value;
|
||||
if (instruction.Opcode is "SMovB64" or "SWqmB64")
|
||||
{
|
||||
@@ -1451,11 +1495,6 @@ internal static partial class Gen5SpirvTranslator
|
||||
}
|
||||
|
||||
StoreS64(destination, value);
|
||||
if (destination == 126)
|
||||
{
|
||||
Store(_exec, IsNotZero64(value));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1472,14 +1511,6 @@ internal static partial class Gen5SpirvTranslator
|
||||
uint value = operand.Kind switch
|
||||
{
|
||||
Gen5OperandKind.VectorRegister => LoadV(operand.Value),
|
||||
Gen5OperandKind.ScalarRegister when operand.Value == 106 =>
|
||||
_module.AddInstruction(
|
||||
SpirvOp.Select,
|
||||
_uintType,
|
||||
Load(_boolType, _vcc),
|
||||
UInt(1),
|
||||
UInt(0)),
|
||||
Gen5OperandKind.ScalarRegister when operand.Value == 107 => UInt(0),
|
||||
Gen5OperandKind.ScalarRegister => LoadS(operand.Value),
|
||||
Gen5OperandKind.LiteralConstant => UInt(operand.Value),
|
||||
Gen5OperandKind.EncodedConstant when TryDecodeInlineConstant(
|
||||
@@ -1881,7 +1912,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
_boolType,
|
||||
_module.AddInstruction(SpirvOp.ULessThan, _boolType, partial, left),
|
||||
_module.AddInstruction(SpirvOp.ULessThan, _boolType, result, partial));
|
||||
Store(_vcc, carry);
|
||||
StoreWaveMask(106, carry);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1912,7 +1943,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
_boolType,
|
||||
partial,
|
||||
borrowIn));
|
||||
Store(_vcc, borrow);
|
||||
StoreWaveMask(106, borrow);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1932,13 +1963,13 @@ internal static partial class Gen5SpirvTranslator
|
||||
UInt(0)));
|
||||
if (register == 106)
|
||||
{
|
||||
Store(_vcc, carry);
|
||||
StoreWaveMask(106, carry);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Store(_vcc, carry);
|
||||
StoreWaveMask(106, carry);
|
||||
}
|
||||
|
||||
private uint EmitPermlane16(
|
||||
|
||||
@@ -102,6 +102,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
private uint _boolType;
|
||||
private uint _uintType;
|
||||
private uint _intType;
|
||||
private uint _longType;
|
||||
private uint _ulongType;
|
||||
private uint _floatType;
|
||||
private uint _vec2Type;
|
||||
@@ -297,10 +298,18 @@ internal static partial class Gen5SpirvTranslator
|
||||
_module.AddCapability(SpirvCapability.Shader);
|
||||
_module.AddCapability(SpirvCapability.Int64);
|
||||
_module.AddCapability(SpirvCapability.ImageQuery);
|
||||
if (UsesSubgroupShuffle())
|
||||
if (UsesSubgroupOperations())
|
||||
{
|
||||
_module.AddCapability(SpirvCapability.GroupNonUniform);
|
||||
_module.AddCapability(SpirvCapability.GroupNonUniformShuffle);
|
||||
if (UsesSubgroupShuffle())
|
||||
{
|
||||
_module.AddCapability(SpirvCapability.GroupNonUniformShuffle);
|
||||
}
|
||||
|
||||
if (UsesWaveControl())
|
||||
{
|
||||
_module.AddCapability(SpirvCapability.GroupNonUniformVote);
|
||||
}
|
||||
}
|
||||
|
||||
_glsl = _module.ImportExtInst("GLSL.std.450");
|
||||
@@ -308,6 +317,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
_boolType = _module.TypeBool();
|
||||
_uintType = _module.TypeInt(32, signed: false);
|
||||
_intType = _module.TypeInt(32, signed: true);
|
||||
_longType = _module.TypeInt(64, signed: true);
|
||||
_ulongType = _module.TypeInt(64, signed: false);
|
||||
_floatType = _module.TypeFloat(32);
|
||||
_vec2Type = _module.TypeVector(_floatType, 2);
|
||||
@@ -557,7 +567,7 @@ internal static partial class Gen5SpirvTranslator
|
||||
|
||||
private void DeclareStageInterface()
|
||||
{
|
||||
if (UsesSubgroupShuffle())
|
||||
if (UsesSubgroupOperations())
|
||||
{
|
||||
var subgroupPointer =
|
||||
_module.TypePointer(SpirvStorageClass.Input, _uintType);
|
||||
@@ -706,8 +716,16 @@ internal static partial class Gen5SpirvTranslator
|
||||
}
|
||||
|
||||
Store(_scc, _module.ConstantBool(false));
|
||||
Store(_vcc, _module.ConstantBool(false));
|
||||
Store(_exec, _module.ConstantBool(true));
|
||||
if (_subgroupInvocationIdInput != 0)
|
||||
{
|
||||
StoreWaveMask(106, _module.ConstantBool(false));
|
||||
StoreWaveMask(126, _module.ConstantBool(true));
|
||||
}
|
||||
else
|
||||
{
|
||||
Store(_vcc, _module.ConstantBool(false));
|
||||
Store(_exec, _module.ConstantBool(true));
|
||||
}
|
||||
Store(_programCounter, UInt(0));
|
||||
Store(_programActive, _module.ConstantBool(true));
|
||||
|
||||
@@ -923,10 +941,10 @@ internal static partial class Gen5SpirvTranslator
|
||||
{
|
||||
"SCbranchScc0" => LogicalNot(Load(_boolType, _scc)),
|
||||
"SCbranchScc1" => Load(_boolType, _scc),
|
||||
"SCbranchVccz" => LogicalNot(Load(_boolType, _vcc)),
|
||||
"SCbranchVccnz" => Load(_boolType, _vcc),
|
||||
"SCbranchExecz" => LogicalNot(Load(_boolType, _exec)),
|
||||
"SCbranchExecnz" => Load(_boolType, _exec),
|
||||
"SCbranchVccz" => LogicalNot(SubgroupAny(Load(_boolType, _vcc))),
|
||||
"SCbranchVccnz" => SubgroupAny(Load(_boolType, _vcc)),
|
||||
"SCbranchExecz" => LogicalNot(SubgroupAny(Load(_boolType, _exec))),
|
||||
"SCbranchExecnz" => SubgroupAny(Load(_boolType, _exec)),
|
||||
_ => 0,
|
||||
};
|
||||
return condition != 0;
|
||||
@@ -1297,30 +1315,39 @@ internal static partial class Gen5SpirvTranslator
|
||||
|
||||
if (instruction.Opcode == "BufferAtomicAdd")
|
||||
{
|
||||
var original = _module.AddInstruction(
|
||||
SpirvOp.AtomicIAdd,
|
||||
_uintType,
|
||||
BufferWordPointer(bindingIndex, dwordAddress),
|
||||
UInt(1),
|
||||
UInt(0x48),
|
||||
LoadV(control.VectorData));
|
||||
if (control.Glc)
|
||||
EmitExecConditional(() =>
|
||||
{
|
||||
StoreV(control.VectorData, original);
|
||||
}
|
||||
var original = _module.AddInstruction(
|
||||
SpirvOp.AtomicIAdd,
|
||||
_uintType,
|
||||
BufferWordPointer(bindingIndex, dwordAddress),
|
||||
UInt(1),
|
||||
UInt(0x48),
|
||||
LoadV(control.VectorData));
|
||||
if (control.Glc)
|
||||
{
|
||||
StoreV(control.VectorData, original);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (instruction.Opcode.StartsWith("BufferStoreDword", StringComparison.Ordinal))
|
||||
{
|
||||
for (uint index = 0; index < control.DwordCount; index++)
|
||||
EmitExecConditional(() =>
|
||||
{
|
||||
var address = index == 0
|
||||
? dwordAddress
|
||||
: IAdd(dwordAddress, UInt(index));
|
||||
StoreBufferWord(bindingIndex, address, LoadV(control.VectorData + index));
|
||||
}
|
||||
for (uint index = 0; index < control.DwordCount; index++)
|
||||
{
|
||||
var address = index == 0
|
||||
? dwordAddress
|
||||
: IAdd(dwordAddress, UInt(index));
|
||||
StoreBufferWord(
|
||||
bindingIndex,
|
||||
address,
|
||||
LoadV(control.VectorData + index));
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1449,11 +1476,12 @@ internal static partial class Gen5SpirvTranslator
|
||||
}
|
||||
else
|
||||
{
|
||||
_module.AddStatement(
|
||||
SpirvOp.ImageWrite,
|
||||
imageObject,
|
||||
coordinates,
|
||||
texel);
|
||||
EmitExecConditional(
|
||||
() => _module.AddStatement(
|
||||
SpirvOp.ImageWrite,
|
||||
imageObject,
|
||||
coordinates,
|
||||
texel));
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -1774,6 +1802,11 @@ internal static partial class Gen5SpirvTranslator
|
||||
_boolType,
|
||||
lowerInRange,
|
||||
upperInRange);
|
||||
inRange = _module.AddInstruction(
|
||||
SpirvOp.LogicalAnd,
|
||||
_boolType,
|
||||
Load(_boolType, _exec),
|
||||
inRange);
|
||||
var writeLabel = _module.AllocateId();
|
||||
var mergeLabel = _module.AllocateId();
|
||||
_module.AddStatement(SpirvOp.SelectionMerge, mergeLabel, 0);
|
||||
@@ -1900,6 +1933,12 @@ internal static partial class Gen5SpirvTranslator
|
||||
SpirvOp.CompositeConstruct,
|
||||
outputType,
|
||||
values);
|
||||
vector = _module.AddInstruction(
|
||||
SpirvOp.Select,
|
||||
outputType,
|
||||
Load(_boolType, _exec),
|
||||
vector,
|
||||
Load(outputType, _pixelOutput));
|
||||
Store(_pixelOutput, vector);
|
||||
return true;
|
||||
}
|
||||
@@ -1945,6 +1984,12 @@ internal static partial class Gen5SpirvTranslator
|
||||
SpirvOp.CompositeConstruct,
|
||||
_vec4Type,
|
||||
components);
|
||||
outputValue = _module.AddInstruction(
|
||||
SpirvOp.Select,
|
||||
_vec4Type,
|
||||
Load(_boolType, _exec),
|
||||
outputValue,
|
||||
Load(_vec4Type, outputVariable));
|
||||
Store(outputVariable, outputValue);
|
||||
return true;
|
||||
}
|
||||
@@ -2009,7 +2054,23 @@ internal static partial class Gen5SpirvTranslator
|
||||
|
||||
private uint LoadV(uint register) => Load(_uintType, VectorPointer(register));
|
||||
|
||||
private void StoreS(uint register, uint value) => Store(ScalarPointer(register), value);
|
||||
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)));
|
||||
}
|
||||
else if (register is 126 or 127)
|
||||
{
|
||||
Store(_exec, IsCurrentLaneSet(LoadS64(126)));
|
||||
}
|
||||
}
|
||||
|
||||
private void StoreV(uint register, uint value, bool guardWithExec = true)
|
||||
{
|
||||
@@ -2059,6 +2120,61 @@ internal static partial class Gen5SpirvTranslator
|
||||
private uint LogicalNot(uint value) =>
|
||||
_module.AddInstruction(SpirvOp.LogicalNot, _boolType, value);
|
||||
|
||||
private uint SubgroupAny(uint 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,
|
||||
_module.Constant64(_ulongType, 1),
|
||||
lane);
|
||||
}
|
||||
|
||||
private uint BooleanToLaneMask(uint condition) =>
|
||||
_module.AddInstruction(
|
||||
SpirvOp.Select,
|
||||
_ulongType,
|
||||
condition,
|
||||
CurrentLaneBit(),
|
||||
_module.Constant64(_ulongType, 0));
|
||||
|
||||
private uint IsCurrentLaneSet(uint mask) =>
|
||||
IsNotZero64(
|
||||
_module.AddInstruction(
|
||||
SpirvOp.BitwiseAnd,
|
||||
_ulongType,
|
||||
mask,
|
||||
CurrentLaneBit()));
|
||||
|
||||
private void StoreWaveMask(uint register, uint condition) =>
|
||||
StoreS64(register, BooleanToLaneMask(condition));
|
||||
|
||||
private void EmitExecConditional(Action emit)
|
||||
{
|
||||
var activeLabel = _module.AllocateId();
|
||||
var mergeLabel = _module.AllocateId();
|
||||
_module.AddStatement(SpirvOp.SelectionMerge, mergeLabel, 0);
|
||||
_module.AddStatement(
|
||||
SpirvOp.BranchConditional,
|
||||
Load(_boolType, _exec),
|
||||
activeLabel,
|
||||
mergeLabel);
|
||||
_module.AddLabel(activeLabel);
|
||||
emit();
|
||||
_module.AddStatement(SpirvOp.Branch, mergeLabel);
|
||||
_module.AddLabel(mergeLabel);
|
||||
}
|
||||
|
||||
private bool UsesLds() =>
|
||||
_state.Program.Instructions.Any(instruction =>
|
||||
instruction.Control is Gen5DataShareControl);
|
||||
@@ -2067,6 +2183,22 @@ internal static partial class Gen5SpirvTranslator
|
||||
_state.Program.Instructions.Any(instruction =>
|
||||
instruction.Opcode is "VPermlane16B32" or "VPermlanex16B32");
|
||||
|
||||
private bool UsesWaveControl() =>
|
||||
_state.Program.Instructions.Any(instruction =>
|
||||
instruction.Opcode.Contains("Saveexec", StringComparison.Ordinal) ||
|
||||
instruction.Opcode.StartsWith("SCbranchExec", StringComparison.Ordinal) ||
|
||||
instruction.Opcode.StartsWith("SCbranchVcc", StringComparison.Ordinal) ||
|
||||
instruction.Opcode.StartsWith("VCmpx", StringComparison.Ordinal) ||
|
||||
instruction.Sources.Any(IsWaveMaskOperand) ||
|
||||
instruction.Destinations.Any(IsWaveMaskOperand));
|
||||
|
||||
private bool UsesSubgroupOperations() =>
|
||||
UsesSubgroupShuffle() || UsesWaveControl();
|
||||
|
||||
private static bool IsWaveMaskOperand(Gen5Operand operand) =>
|
||||
operand.Kind == Gen5OperandKind.ScalarRegister &&
|
||||
operand.Value is 106 or 107 or 126 or 127;
|
||||
|
||||
private static bool TryGetVectorDestination(
|
||||
Gen5ShaderInstruction instruction,
|
||||
out uint destination)
|
||||
|
||||
@@ -177,6 +177,7 @@ internal enum SpirvCapability : uint
|
||||
StorageImageReadWithoutFormat = 55,
|
||||
StorageImageWriteWithoutFormat = 56,
|
||||
GroupNonUniform = 61,
|
||||
GroupNonUniformVote = 62,
|
||||
GroupNonUniformBallot = 64,
|
||||
GroupNonUniformShuffle = 65,
|
||||
RuntimeDescriptorArray = 5302,
|
||||
|
||||
@@ -5310,6 +5310,37 @@ public static class KernelMemoryCompatExports
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool TryReadTrackedLibcHeap(
|
||||
ulong address,
|
||||
Span<byte> destination)
|
||||
{
|
||||
if (destination.IsEmpty)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var length = (ulong)destination.Length;
|
||||
lock (_libcAllocGate)
|
||||
{
|
||||
foreach (var (allocationAddress, allocation) in _libcAllocations)
|
||||
{
|
||||
var allocationSize = (ulong)allocation.Size;
|
||||
var offset = address >= allocationAddress
|
||||
? address - allocationAddress
|
||||
: ulong.MaxValue;
|
||||
if (offset > allocationSize ||
|
||||
length > allocationSize - offset)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return TryReadHostMemory(address, destination);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryAllocateLibcHeap(ulong requestedSize, nuint alignment, bool zeroFill, out ulong address)
|
||||
{
|
||||
address = 0;
|
||||
|
||||
@@ -66,6 +66,7 @@ internal sealed record VulkanOffscreenGuestDraw(
|
||||
bool PublishTarget);
|
||||
|
||||
internal sealed record VulkanComputeGuestDispatch(
|
||||
ulong ShaderAddress,
|
||||
byte[] ComputeSpirv,
|
||||
IReadOnlyList<VulkanGuestDrawTexture> Textures,
|
||||
IReadOnlyList<VulkanGuestMemoryBuffer> GlobalMemoryBuffers,
|
||||
@@ -434,6 +435,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
}
|
||||
|
||||
public static void SubmitComputeDispatch(
|
||||
ulong shaderAddress,
|
||||
byte[] computeSpirv,
|
||||
IReadOnlyList<VulkanGuestDrawTexture> textures,
|
||||
IReadOnlyList<VulkanGuestMemoryBuffer> globalMemoryBuffers,
|
||||
@@ -459,6 +461,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
|
||||
EnqueueGuestWorkLocked(
|
||||
new VulkanComputeGuestDispatch(
|
||||
shaderAddress,
|
||||
computeSpirv,
|
||||
textures.ToArray(),
|
||||
globalMemoryBuffers.ToArray(),
|
||||
@@ -845,7 +848,8 @@ internal static unsafe class VulkanVideoPresenter
|
||||
Fence Fence,
|
||||
CommandBuffer CommandBuffer,
|
||||
TranslatedDrawResources Resources,
|
||||
IReadOnlyList<GuestImageResource> TraceImages);
|
||||
IReadOnlyList<GuestImageResource> TraceImages,
|
||||
string DebugName);
|
||||
|
||||
public Presenter(uint width, uint height)
|
||||
{
|
||||
@@ -1056,8 +1060,10 @@ internal static unsafe class VulkanVideoPresenter
|
||||
{
|
||||
var storage = dispatch.Textures.FirstOrDefault(texture => texture.IsStorage && texture.Address != 0);
|
||||
return storage is null
|
||||
? $"SharpEmu compute {dispatch.GroupCountX}x{dispatch.GroupCountY}x{dispatch.GroupCountZ}"
|
||||
: $"SharpEmu compute storage=0x{storage.Address:X16} " +
|
||||
? $"SharpEmu compute cs=0x{dispatch.ShaderAddress:X16} " +
|
||||
$"{dispatch.GroupCountX}x{dispatch.GroupCountY}x{dispatch.GroupCountZ}"
|
||||
: $"SharpEmu compute cs=0x{dispatch.ShaderAddress:X16} " +
|
||||
$"storage=0x{storage.Address:X16} " +
|
||||
$"{storage.Width}x{storage.Height} fmt{storage.Format} " +
|
||||
$"{dispatch.GroupCountX}x{dispatch.GroupCountY}x{dispatch.GroupCountZ}";
|
||||
}
|
||||
@@ -1365,7 +1371,8 @@ internal static unsafe class VulkanVideoPresenter
|
||||
fence,
|
||||
commandBuffer,
|
||||
resources,
|
||||
traceImages));
|
||||
traceImages,
|
||||
resources.DebugName));
|
||||
}
|
||||
|
||||
private void EnsureGuestSubmissionCapacity()
|
||||
@@ -1382,14 +1389,13 @@ internal static unsafe class VulkanVideoPresenter
|
||||
if (waitForOldest && _pendingGuestSubmissions.TryPeek(out var oldest))
|
||||
{
|
||||
var fence = oldest.Fence;
|
||||
Check(
|
||||
_vk.WaitForFences(
|
||||
_device,
|
||||
1,
|
||||
&fence,
|
||||
true,
|
||||
ulong.MaxValue),
|
||||
"vkWaitForFences(guest)");
|
||||
var result = _vk.WaitForFences(
|
||||
_device,
|
||||
1,
|
||||
&fence,
|
||||
true,
|
||||
ulong.MaxValue);
|
||||
Check(result, $"vkWaitForFences(guest: {oldest.DebugName})");
|
||||
}
|
||||
|
||||
while (_pendingGuestSubmissions.TryPeek(out var submission))
|
||||
@@ -1400,7 +1406,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
break;
|
||||
}
|
||||
|
||||
Check(status, "vkGetFenceStatus(guest)");
|
||||
Check(status, $"vkGetFenceStatus(guest: {submission.DebugName})");
|
||||
_pendingGuestSubmissions.Dequeue();
|
||||
|
||||
foreach (var image in submission.TraceImages)
|
||||
@@ -2156,6 +2162,13 @@ internal static unsafe class VulkanVideoPresenter
|
||||
AttachmentCount = 1,
|
||||
PAttachments = &colorBlendAttachment,
|
||||
};
|
||||
var dynamicStateValue = DynamicState.Scissor;
|
||||
var dynamicState = new PipelineDynamicStateCreateInfo
|
||||
{
|
||||
SType = StructureType.PipelineDynamicStateCreateInfo,
|
||||
DynamicStateCount = 1,
|
||||
PDynamicStates = &dynamicStateValue,
|
||||
};
|
||||
var pipelineInfo = new GraphicsPipelineCreateInfo
|
||||
{
|
||||
SType = StructureType.GraphicsPipelineCreateInfo,
|
||||
@@ -2167,6 +2180,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
PRasterizationState = &rasterization,
|
||||
PMultisampleState = &multisample,
|
||||
PColorBlendState = &colorBlend,
|
||||
PDynamicState = &dynamicState,
|
||||
Layout = resources.PipelineLayout,
|
||||
RenderPass = renderPass,
|
||||
Subpass = 0,
|
||||
@@ -2893,11 +2907,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
null);
|
||||
}
|
||||
|
||||
_vk.CmdDispatch(
|
||||
_commandBuffer,
|
||||
work.GroupCountX,
|
||||
work.GroupCountY,
|
||||
work.GroupCountZ);
|
||||
RecordChunkedComputeDispatch(_commandBuffer, work);
|
||||
RecordStorageImagesForRead(resources, PipelineStageFlags.ComputeShaderBit);
|
||||
EndDebugLabel(_commandBuffer);
|
||||
|
||||
@@ -2911,7 +2921,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
TraceVulkanShader(
|
||||
$"vk.compute_dispatch groups={work.GroupCountX}x" +
|
||||
$"{work.GroupCountY}x{work.GroupCountZ} " +
|
||||
$"textures={work.Textures.Count}");
|
||||
$"textures={work.Textures.Count} cs=0x{work.ShaderAddress:X16}");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -2937,6 +2947,44 @@ internal static unsafe class VulkanVideoPresenter
|
||||
}
|
||||
}
|
||||
|
||||
private void RecordChunkedComputeDispatch(
|
||||
CommandBuffer commandBuffer,
|
||||
VulkanComputeGuestDispatch work)
|
||||
{
|
||||
const uint maxWorkgroupsPerCommand = 4096;
|
||||
var yChunk = Math.Max(
|
||||
1u,
|
||||
Math.Min(
|
||||
work.GroupCountY,
|
||||
maxWorkgroupsPerCommand / Math.Max(work.GroupCountX, 1u)));
|
||||
var commandCount = 0u;
|
||||
|
||||
for (var z = 0u; z < work.GroupCountZ; z++)
|
||||
{
|
||||
for (var y = 0u; y < work.GroupCountY; y += yChunk)
|
||||
{
|
||||
var countY = Math.Min(yChunk, work.GroupCountY - y);
|
||||
_vk.CmdDispatchBase(
|
||||
commandBuffer,
|
||||
0,
|
||||
y,
|
||||
z,
|
||||
work.GroupCountX,
|
||||
countY,
|
||||
1);
|
||||
commandCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (commandCount > 1)
|
||||
{
|
||||
TraceVulkanShader(
|
||||
$"vk.compute_chunked cs=0x{work.ShaderAddress:X16} " +
|
||||
$"groups={work.GroupCountX}x{work.GroupCountY}x{work.GroupCountZ} " +
|
||||
$"commands={commandCount} y_chunk={yChunk}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteOffscreenDraw(VulkanOffscreenGuestDraw work)
|
||||
{
|
||||
var format = GetRenderTargetFormat(work.Target.Format, work.Target.NumberType);
|
||||
@@ -4294,29 +4342,51 @@ internal static unsafe class VulkanVideoPresenter
|
||||
null);
|
||||
}
|
||||
|
||||
if (resources.IndexBuffer.Handle != 0)
|
||||
const uint maxPixelsPerDraw = 512 * 512;
|
||||
var rowsPerDraw = Math.Max(
|
||||
1u,
|
||||
Math.Min(extent.Height, maxPixelsPerDraw / Math.Max(extent.Width, 1u)));
|
||||
var drawCount = 0u;
|
||||
for (var y = 0u; y < extent.Height; y += rowsPerDraw)
|
||||
{
|
||||
_vk.CmdBindIndexBuffer(
|
||||
_commandBuffer,
|
||||
resources.IndexBuffer,
|
||||
0,
|
||||
resources.Index32Bit ? IndexType.Uint32 : IndexType.Uint16);
|
||||
_vk.CmdDrawIndexed(
|
||||
_commandBuffer,
|
||||
resources.VertexCount,
|
||||
resources.InstanceCount,
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
var scissor = new Rect2D(
|
||||
new Offset2D(0, checked((int)y)),
|
||||
new Extent2D(extent.Width, Math.Min(rowsPerDraw, extent.Height - y)));
|
||||
_vk.CmdSetScissor(_commandBuffer, 0, 1, &scissor);
|
||||
|
||||
if (resources.IndexBuffer.Handle != 0)
|
||||
{
|
||||
_vk.CmdBindIndexBuffer(
|
||||
_commandBuffer,
|
||||
resources.IndexBuffer,
|
||||
0,
|
||||
resources.Index32Bit ? IndexType.Uint32 : IndexType.Uint16);
|
||||
_vk.CmdDrawIndexed(
|
||||
_commandBuffer,
|
||||
resources.VertexCount,
|
||||
resources.InstanceCount,
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_vk.CmdDraw(
|
||||
_commandBuffer,
|
||||
resources.VertexCount,
|
||||
resources.InstanceCount,
|
||||
0,
|
||||
0);
|
||||
}
|
||||
|
||||
drawCount++;
|
||||
}
|
||||
else
|
||||
|
||||
if (drawCount > 1)
|
||||
{
|
||||
_vk.CmdDraw(
|
||||
_commandBuffer,
|
||||
resources.VertexCount,
|
||||
resources.InstanceCount,
|
||||
0,
|
||||
0);
|
||||
TraceVulkanShader(
|
||||
$"vk.graphics_chunked target={extent.Width}x{extent.Height} " +
|
||||
$"draws={drawCount} rows={rowsPerDraw} name={resources.DebugName}");
|
||||
}
|
||||
_vk.CmdEndRenderPass(_commandBuffer);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user