sharpemu/tests/SharpEmu.Libs.Tests/Gpu/Vulkan/SdwaFloatModifierDeviceTest...

71 lines
3.5 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.Libs.Gpu.Buffers;
using SharpEmu.Libs.Tests.Gpu.Images;
using SharpEmu.ShaderCompiler;
using SharpEmu.ShaderCompiler.Resources;
using SharpEmu.ShaderCompiler.Tests;
using SharpEmu.ShaderCompiler.Vulkan;
using Xunit;
using static SharpEmu.ShaderCompiler.Tests.Resources.ResourceTestProgram;
namespace SharpEmu.Libs.Tests.Gpu.Vulkan;
// Float VOP2 SDWA with DWORD selects, used only for its source negates, output modifier and
// clamp. The instruction words are taken from Ghost of Yotei's TAA resolve (0xD42889155B0A6B9F).
public sealed class SdwaFloatModifierDeviceTests(HeadlessVulkanFixture fixture) : IClassFixture<HeadlessVulkanFixture>
{
[Fact]
public void NegateOutputModifierAndClampApply()
{
var vulkan = fixture.Vulkan;
if (!GatePrerequisites.Ready(vulkan, shaderInt64: true)) return;
const float x = 1.5f, y = 0.25f, z = 3f, w = -2f;
var program = Program(
MoveVectorFromScalar(0, 42, 8),
MoveVectorFromScalar(4, 41, 9),
MoveVectorFromScalar(8, 12, 10),
MoveVectorFromScalar(12, 2, 11),
MoveVectorFromScalar(16, 15, 8),
MoveVectorFromScalar(20, 11, 10),
// v_add_f32_sdwa v4, -v42, -v41
Sdwa(24, 0x060852F9u, 0x1616062Au),
// v_mul_f32_sdwa v13, v12, v2 div:2
Sdwa(32, 0x101A04F9u, 0x0606C60Cu),
// v_mul_f32_sdwa v1, v15, v11 clamp
Sdwa(40, 0x100216F9u, 0x0606260Fu),
Vop2(48, "VOrB32", 20, Gen5Operand.Vector(4), Gen5Operand.Vector(4)),
Vop2(52, "VOrB32", 21, Gen5Operand.Vector(13), Gen5Operand.Vector(13)),
Vop2(56, "VOrB32", 22, Gen5Operand.Vector(1), Gen5Operand.Vector(1)),
Vop2(60, "VAndB32", 1, Operand(0), Gen5Operand.Vector(1)),
BufferAccess(64, "BufferStoreDwordx3", 4, dwords: 3, vectorData: 20, offsetEnabled: true, vectorAddress: 1),
EndProgram(72));
var (plan, resources, layout) = Prepare(program);
var request = new ShaderCompileRequest(plan, resources, layout) { LocalSizeX = 64 };
Assert.True(Gen5SpirvTranslator.TryCompileProgram(request, out var shader, out var error), error);
using var harness = new ImageTestHarness(vulkan);
using var runner = new LayoutComputeRunner(harness, request, shader.Spirv);
var output = runner.CreateBuffer(16);
var bindings = new Dictionary<DescriptorBindingKind, GpuBuffer[]>
{
[DescriptorBindingKind.Buffers] = Enumerable.Repeat(output, Math.Max(1, plan.Info.Buffers.Count)).ToArray(),
};
var registers = new uint[256];
registers[6] = 16;
registers[8] = BitConverter.SingleToUInt32Bits(x);
registers[9] = BitConverter.SingleToUInt32Bits(y);
registers[10] = BitConverter.SingleToUInt32Bits(z);
registers[11] = BitConverter.SingleToUInt32Bits(w);
harness.Run(() => runner.Dispatch(registers, bindings, 1));
harness.AssertNoValidationMessages();
var bytes = runner.ReadBack(output, 0, 12);
Assert.Equal(-(x + y), BitConverter.ToSingle(bytes, 0));
Assert.Equal(z * w * 0.5f, BitConverter.ToSingle(bytes, 4));
Assert.Equal(1f, BitConverter.ToSingle(bytes, 8));
}
private static Gen5ShaderInstruction Sdwa(uint pc, uint word0, uint word1) =>
Gen5Float16ArithmeticTests.Decode([word0, word1, 0xBF81_0000u]).Instructions[0] with { Pc = pc };
}