mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-22 10:56:20 +08:00
[ShaderCompiler/Vulkan] Match vertex input numeric types (#351)
Declare UINT and SINT vertex attributes with integer SPIR-V component types so shader interfaces match the Vulkan pipeline formats. Keep normalized, scaled, and floating-point formats on float inputs. Signed-off-by: missatjuhvdk1 <177474143+missatjuhvdk1@users.noreply.github.com> Co-authored-by: missatjuhvdk1 <177474143+missatjuhvdk1@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
0b1dea43e8
commit
743fe5cc26
@@ -299,6 +299,13 @@ public static partial class Gen5SpirvTranslator
|
||||
Uint,
|
||||
}
|
||||
|
||||
private enum VertexInputComponentKind
|
||||
{
|
||||
Float,
|
||||
Sint,
|
||||
Uint,
|
||||
}
|
||||
|
||||
private readonly record struct SpirvImageResource(
|
||||
uint Variable,
|
||||
uint ImageType,
|
||||
@@ -311,7 +318,9 @@ public static partial class Gen5SpirvTranslator
|
||||
private readonly record struct SpirvVertexInput(
|
||||
uint Variable,
|
||||
uint Type,
|
||||
uint ComponentCount);
|
||||
uint ComponentType,
|
||||
uint ComponentCount,
|
||||
VertexInputComponentKind ComponentKind);
|
||||
|
||||
private readonly record struct SpirvPixelOutput(
|
||||
uint Variable,
|
||||
@@ -1289,12 +1298,23 @@ public static partial class Gen5SpirvTranslator
|
||||
{
|
||||
foreach (var input in _evaluation.VertexInputs ?? [])
|
||||
{
|
||||
var componentKind = input.NumberFormat switch
|
||||
{
|
||||
4 => VertexInputComponentKind.Uint,
|
||||
5 => VertexInputComponentKind.Sint,
|
||||
_ => VertexInputComponentKind.Float,
|
||||
};
|
||||
var componentType = componentKind switch
|
||||
{
|
||||
VertexInputComponentKind.Uint => _uintType,
|
||||
VertexInputComponentKind.Sint => _intType,
|
||||
_ => _floatType,
|
||||
};
|
||||
var type = input.ComponentCount switch
|
||||
{
|
||||
1u => _floatType,
|
||||
2u => _vec2Type,
|
||||
3u => _vec3Type,
|
||||
4u => _vec4Type,
|
||||
1u => componentType,
|
||||
>= 2u and <= 4u =>
|
||||
_module.TypeVector(componentType, input.ComponentCount),
|
||||
_ => 0u,
|
||||
};
|
||||
if (type == 0)
|
||||
@@ -1316,7 +1336,9 @@ public static partial class Gen5SpirvTranslator
|
||||
new SpirvVertexInput(
|
||||
variable,
|
||||
type,
|
||||
input.ComponentCount));
|
||||
componentType,
|
||||
input.ComponentCount,
|
||||
componentKind));
|
||||
_interfaces.Add(variable);
|
||||
}
|
||||
}
|
||||
@@ -3198,10 +3220,13 @@ public static partial class Gen5SpirvTranslator
|
||||
? loaded
|
||||
: _module.AddInstruction(
|
||||
SpirvOp.CompositeExtract,
|
||||
_floatType,
|
||||
input.ComponentType,
|
||||
loaded,
|
||||
component);
|
||||
StoreV(control.VectorData + component, Bitcast(_uintType, value));
|
||||
var raw = input.ComponentKind == VertexInputComponentKind.Uint
|
||||
? value
|
||||
: Bitcast(_uintType, value);
|
||||
StoreV(control.VectorData + component, raw);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using SharpEmu.ShaderCompiler;
|
||||
using SharpEmu.ShaderCompiler.Vulkan;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpEmu.Libs.Tests.Agc;
|
||||
|
||||
public sealed class Gen5VertexInputSpirvTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(0u, null)]
|
||||
[InlineData(4u, 0u)]
|
||||
[InlineData(5u, 1u)]
|
||||
public void VertexInputTypeMatchesGuestNumberFormat(
|
||||
uint numberFormat,
|
||||
uint? expectedIntegerSignedness)
|
||||
{
|
||||
var instruction = new Gen5ShaderInstruction(
|
||||
0,
|
||||
Gen5ShaderEncoding.Mubuf,
|
||||
"BufferLoadFormatXyzw",
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
new Gen5BufferMemoryControl(
|
||||
4,
|
||||
5,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
IndexEnabled: true,
|
||||
OffsetEnabled: false,
|
||||
Glc: false,
|
||||
Slc: false));
|
||||
var end = new Gen5ShaderInstruction(
|
||||
4,
|
||||
Gen5ShaderEncoding.Sopp,
|
||||
"SEndpgm",
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
null);
|
||||
var state = new Gen5ShaderState(
|
||||
new Gen5ShaderProgram(0, [instruction, end]),
|
||||
[],
|
||||
null);
|
||||
var registers = new uint[256];
|
||||
var data = new byte[16];
|
||||
var evaluation = new Gen5ShaderEvaluation(
|
||||
registers,
|
||||
registers,
|
||||
[],
|
||||
[],
|
||||
VertexInputs:
|
||||
[
|
||||
new Gen5VertexInputBinding(
|
||||
0,
|
||||
0,
|
||||
4,
|
||||
10,
|
||||
numberFormat,
|
||||
0x1000,
|
||||
4,
|
||||
0,
|
||||
data,
|
||||
data.Length,
|
||||
DataPooled: false),
|
||||
]);
|
||||
|
||||
Assert.True(
|
||||
Gen5SpirvTranslator.TryCompileVertexShader(
|
||||
state,
|
||||
evaluation,
|
||||
out var shader,
|
||||
out var error),
|
||||
error);
|
||||
|
||||
var module = ParseModule(shader.Spirv);
|
||||
var inputVariable = module.Single(candidate =>
|
||||
candidate.Opcode == SpirvOp.Decorate &&
|
||||
candidate.Operands.Length >= 3 &&
|
||||
candidate.Operands[1] == (uint)SpirvDecoration.Location &&
|
||||
candidate.Operands[2] == 0).Operands[0];
|
||||
var pointerType = module.Single(candidate =>
|
||||
candidate.Opcode == SpirvOp.Variable &&
|
||||
candidate.Operands[1] == inputVariable).Operands[0];
|
||||
var vectorType = module.Single(candidate =>
|
||||
candidate.Opcode == SpirvOp.TypePointer &&
|
||||
candidate.Operands[0] == pointerType).Operands[2];
|
||||
var componentType = module.Single(candidate =>
|
||||
candidate.Opcode == SpirvOp.TypeVector &&
|
||||
candidate.Operands[0] == vectorType).Operands[1];
|
||||
|
||||
if (expectedIntegerSignedness is { } signedness)
|
||||
{
|
||||
Assert.Contains(
|
||||
module,
|
||||
candidate =>
|
||||
candidate.Opcode == SpirvOp.TypeInt &&
|
||||
candidate.Operands[0] == componentType &&
|
||||
candidate.Operands[1] == 32 &&
|
||||
candidate.Operands[2] == signedness);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Contains(
|
||||
module,
|
||||
candidate =>
|
||||
candidate.Opcode == SpirvOp.TypeFloat &&
|
||||
candidate.Operands[0] == componentType &&
|
||||
candidate.Operands[1] == 32);
|
||||
}
|
||||
}
|
||||
|
||||
private static IReadOnlyList<ParsedInstruction> ParseModule(byte[] spirv)
|
||||
{
|
||||
var instructions = new List<ParsedInstruction>();
|
||||
for (var offset = 5; offset < spirv.Length / sizeof(uint);)
|
||||
{
|
||||
var header = BitConverter.ToUInt32(spirv, offset * sizeof(uint));
|
||||
var wordCount = (int)(header >> 16);
|
||||
Assert.True(wordCount > 0);
|
||||
var operands = new uint[wordCount - 1];
|
||||
for (var index = 0; index < operands.Length; index++)
|
||||
{
|
||||
operands[index] = BitConverter.ToUInt32(
|
||||
spirv,
|
||||
(offset + index + 1) * sizeof(uint));
|
||||
}
|
||||
|
||||
instructions.Add(
|
||||
new ParsedInstruction((SpirvOp)(ushort)header, operands));
|
||||
offset += wordCount;
|
||||
}
|
||||
|
||||
return instructions;
|
||||
}
|
||||
|
||||
private sealed record ParsedInstruction(SpirvOp Opcode, uint[] Operands);
|
||||
}
|
||||
Reference in New Issue
Block a user