Merge remote-tracking branch 'origin/main' into perf_computed_shaders
# Conflicts: # tests/SharpEmu.Libs.Tests/Agc/Gen5WaveMaskSpirvTests.cs
This commit is contained in:
commit
90b1cafe6d
|
|
@ -8023,11 +8023,17 @@ public static partial class Gen5SpirvTranslator
|
|||
_uvec4Type,
|
||||
UInt(3),
|
||||
condition);
|
||||
return _module.AddInstruction(
|
||||
SpirvOp.VectorExtractDynamic,
|
||||
_uintType,
|
||||
ballot,
|
||||
ShiftRightLogical(Load(_uintType, _subgroupInvocationIdInput), UInt(5)));
|
||||
var component = ShiftRightLogical(Load(_uintType, _subgroupInvocationIdInput), UInt(5));
|
||||
// Avoid dynamic extraction of subgroup ballot words: on the NVIDIA
|
||||
// device tests it produced zero masks. Select the same word explicitly.
|
||||
var result = _module.AddInstruction(SpirvOp.CompositeExtract, _uintType, ballot, 0);
|
||||
for (uint index = 1; index < 4; index++)
|
||||
{
|
||||
var word = _module.AddInstruction(SpirvOp.CompositeExtract, _uintType, ballot, index);
|
||||
result = _module.AddInstruction(SpirvOp.Select, _uintType,
|
||||
_module.AddInstruction(SpirvOp.IEqual, _boolType, component, UInt(index)), word, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private uint BooleanToHalfWaveMask(uint condition)
|
||||
|
|
@ -8259,7 +8265,7 @@ public static partial class Gen5SpirvTranslator
|
|||
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.Opcode.StartsWith("VCmp", StringComparison.Ordinal) ||
|
||||
instruction.Sources.Any(IsWaveMaskOperand) ||
|
||||
instruction.Destinations.Any(IsWaveMaskOperand));
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System.Buffers.Binary;
|
||||
using SharpEmu.HLE;
|
||||
using SharpEmu.ShaderCompiler;
|
||||
using SharpEmu.ShaderCompiler.Resources;
|
||||
using SharpEmu.ShaderCompiler.Tests.Resources;
|
||||
using SharpEmu.ShaderCompiler.Vulkan;
|
||||
using Xunit;
|
||||
|
|
@ -15,15 +16,17 @@ public sealed class Gen5WaveMaskSpirvTests
|
|||
{
|
||||
private const ulong ShaderAddress = 0x1_0000_0000;
|
||||
|
||||
[Fact]
|
||||
public void WaveMaskPredicate_IsTestedAtCurrentLaneBit()
|
||||
[Theory]
|
||||
[InlineData(ShaderStage.Compute)]
|
||||
[InlineData(ShaderStage.Pixel)]
|
||||
public void WaveMaskPredicate_IsTestedAtCurrentLaneBit(ShaderStage stage)
|
||||
{
|
||||
// V_CMP_EQ_F32 vcc, v0, v1 writes VCC at run time, which re-materialises
|
||||
// the per-lane _vcc predicate from the wave mask via IsWaveMaskActive.
|
||||
var spirv = Compile([0x7C04_0300u]);
|
||||
var spirv = Compile([0x7C04_0300u], stage);
|
||||
|
||||
// The lane's bit in single-lane emulation is the constant 1, so the
|
||||
// predicate is `(mask & 1) != 0`. The whole-word bug emitted `mask != 0`
|
||||
// Graphics single-lane emulation uses 1; compute shifts it by the lane ID. The
|
||||
// predicate is `(mask & lane_bit) != 0`. The whole-word bug emitted `mask != 0`
|
||||
// with no such mask. Require the lane-bit AND to be present.
|
||||
Assert.True(
|
||||
ContainsLaneBitMaskedWaveTest(spirv),
|
||||
|
|
@ -47,10 +50,7 @@ public sealed class Gen5WaveMaskSpirvTests
|
|||
Assert.NotEmpty(spirv);
|
||||
}
|
||||
|
||||
// True when the module contains an OpBitwiseAnd whose operand is an integer
|
||||
// constant of value 1 — the current-lane bit that the lane test masks the
|
||||
// wave mask with before the non-zero test. A wave64 mask is a 64-bit value
|
||||
// and a wave32 mask is a single dword, so either width counts.
|
||||
// True when OpBitwiseAnd consumes a constant or shifted current-lane bit.
|
||||
private static bool ContainsLaneBitMaskedWaveTest(byte[] spirv)
|
||||
{
|
||||
var laneBitConstIds = new HashSet<uint>();
|
||||
|
|
@ -74,7 +74,23 @@ public sealed class Gen5WaveMaskSpirvTests
|
|||
}
|
||||
}
|
||||
|
||||
// Pass 2: look for an OpBitwiseAnd that consumes one of those constants.
|
||||
// Compute forms the lane bit with OpShiftLeftLogical, then may select
|
||||
// zero for lanes outside the guest wave. Follow those result IDs too.
|
||||
foreach (var (op, wordCount, offset) in EnumerateInstructions(spirv))
|
||||
{
|
||||
if (op == 196 && wordCount == 5 &&
|
||||
laneBitConstIds.Contains(ReadWord(spirv, offset + 12)))
|
||||
{
|
||||
laneBitConstIds.Add(ReadWord(spirv, offset + 8));
|
||||
}
|
||||
else if (op == 169 && wordCount == 6 &&
|
||||
laneBitConstIds.Contains(ReadWord(spirv, offset + 16)))
|
||||
{
|
||||
laneBitConstIds.Add(ReadWord(spirv, offset + 8));
|
||||
}
|
||||
}
|
||||
|
||||
// Look for an OpBitwiseAnd that consumes a current-lane bit.
|
||||
foreach (var (op, wordCount, offset) in EnumerateInstructions(spirv))
|
||||
{
|
||||
// OpBitwiseAnd = 199 (opcode, resultType, resultId, operand0, operand1).
|
||||
|
|
@ -115,13 +131,13 @@ public sealed class Gen5WaveMaskSpirvTests
|
|||
private static uint ReadWord(byte[] spirv, int offset) =>
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(spirv.AsSpan(offset, sizeof(uint)));
|
||||
|
||||
private static byte[] Compile(uint[] programWords)
|
||||
private static byte[] Compile(uint[] programWords, ShaderStage stage = ShaderStage.Compute)
|
||||
{
|
||||
var memory = new FakeCpuMemory(ShaderAddress, 0x2000);
|
||||
var ctx = new CpuContext(memory, Generation.Gen5);
|
||||
Gen5ShaderAtomicDecodeTests.WriteProgram(memory, ShaderAddress, programWords);
|
||||
Assert.True(Gen5ShaderTranslator.TryDecodeProgram(ctx, ShaderAddress, out var program, out var error), error);
|
||||
var request = ResourceTestProgram.Request(program, userDataCount: 16);
|
||||
var request = ResourceTestProgram.Request(program, stage: stage, userDataCount: 16);
|
||||
Assert.True(Gen5SpirvTranslator.TryCompileProgram(request, out var shader, out error), error);
|
||||
return shader.Spirv;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,6 +77,25 @@ public sealed class Wave64HalfMaskDeviceTests(HeadlessVulkanFixture fixture) : I
|
|||
return lanes;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExplicitCompareMask_IsCollectedAcrossTheWave()
|
||||
{
|
||||
if (!Ready()) return;
|
||||
Lane();
|
||||
CompareTo("VCmpGeU32", 10, Gen5Operand.Vector(2), Operand(3));
|
||||
Add(MoveVectorFromScalar(0, 4, 10));
|
||||
Assert.All(Run(), x => Assert.Equal(0xFFFF_FFF8u, x));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InitialExecContainsEveryLane()
|
||||
{
|
||||
if (!Ready()) return;
|
||||
Lane();
|
||||
Add(MoveVectorFromScalar(0, 4, 126));
|
||||
Assert.All(Run(), x => Assert.Equal(uint.MaxValue, x));
|
||||
}
|
||||
|
||||
private bool Ready() => GatePrerequisites.Ready(fixture.Vulkan, shaderInt64: true);
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Reference in New Issue