sharpemu/tests/SharpEmu.ShaderCompiler.Tests/DeviceAddressCodeSizeTests.cs

93 lines
4.1 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.ShaderCompiler;
using SharpEmu.ShaderCompiler.Vulkan;
using System.Buffers.Binary;
using Xunit;
using static SharpEmu.ShaderCompiler.Tests.Resources.ResourceTestProgram;
namespace SharpEmu.ShaderCompiler.Tests;
public sealed class DeviceAddressCodeSizeTests
{
[Theory]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
public void PartialDeviceFormatLoad_ConvertsOnlyItsRequestedComponents(uint count)
{
var request = Request(Program(ReadFirstLane(0, 12, 0), MoveScalar(4, 13, 0),
MoveScalar(8, 14, 16), MoveScalar(12, 15, 0),
ScalarBufferLoad(16, 12, destination: 20, count: 4),
BufferLoad(24, 20, dwords: count, formatted: true), EndProgram(32)));
Assert.Contains(request.Memory.Entries, memory => memory.Pc == 24 && memory.DeviceDescriptor);
Assert.True(Gen5SpirvTranslator.TryCompileProgram(request, out var shader, out var error), error);
Gen5LargeDispatcherValidationTests.ValidateWithSpirvToolsWhenAvailable(shader.Spirv);
var divisions = 0;
for (var offset = 20; offset < shader.Spirv.Length;)
{
var header = BinaryPrimitives.ReadUInt32LittleEndian(shader.Spirv.AsSpan(offset));
if ((header & 0xFFFF) == (uint)SpirvOp.FDiv) divisions++;
offset += checked((int)(header >> 16) * 4);
}
Assert.Equal((int)count * 2, divisions);
}
[Theory]
[InlineData(1)]
[InlineData(2)]
[InlineData(4)]
public void RepeatedDeviceFormatLoads_ShareFormatDecoding(uint components)
{
byte[] Compile(int count)
{
var instructions = new List<Gen5ShaderInstruction>
{
ReadFirstLane(0, 12, 0), MoveScalar(4, 13, 0),
MoveScalar(8, 14, 16), MoveScalar(12, 15, 0),
ScalarBufferLoad(16, 12, destination: 20, count: 4),
};
for (uint index = 0; index < count; index++)
instructions.Add(BufferLoad(24 + index * 8, 20,
offset: (int)index * 16, dwords: components, formatted: true));
instructions.Add(EndProgram(24 + (uint)count * 8));
var request = Request(Program(instructions.ToArray()));
Assert.True(Gen5SpirvTranslator.TryCompileProgram(request, out var shader, out var error), error);
Gen5LargeDispatcherValidationTests.ValidateWithSpirvToolsWhenAvailable(shader.Spirv);
return shader.Spirv;
}
var single = Compile(1);
var repeated = Compile(16);
// Addresses and destination writes vary at every site. The runtime format
// catalogue, conversions, swizzles and page walks must not be duplicated.
Assert.True(repeated.Length - single.Length < 15 * 1500,
$"Additional device formatted loads grew SPIR-V by {repeated.Length - single.Length} bytes.");
}
[Fact]
public void RepeatedRuntimeFormats_DoNotDuplicateTheLayoutCatalogue()
{
byte[] Compile(int count)
{
var instructions = new List<Gen5ShaderInstruction>();
for (uint index = 0; index < count; index++)
instructions.Add(BufferAccess(index * 8, "BufferLoadFormatXyzw", 8,
(int)index * 16, 4, vectorData: 4));
instructions.Add(EndProgram((uint)count * 8));
var (plan, resources, layout) = Prepare(Program(instructions.ToArray()));
var request = new ShaderCompileRequest(plan, resources, layout)
{
LocalSizeX = 1, ThreadCountX = 1, ForceGenericBufferFormats = true,
};
Assert.True(Gen5SpirvTranslator.TryCompileProgram(request, out var shader, out var error), error);
Gen5LargeDispatcherValidationTests.ValidateWithSpirvToolsWhenAvailable(shader.Spirv);
return shader.Spirv;
}
var single = Compile(1);
var repeated = Compile(16);
Assert.True(repeated.Length - single.Length < 15 * 18000,
$"Additional formatted loads grew SPIR-V by {repeated.Length - single.Length} bytes.");
}
}