Fix Windows Clang SPIR-V serialization with typed operands

This commit is contained in:
nmzik
2026-09-12 13:08:23 +02:00
parent 3a27f8a76a
commit 1208d22547
17 changed files with 1204 additions and 1084 deletions
@@ -4,15 +4,15 @@ namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
namespace {
uint32_t MeshArray(EmitterState& state, spv::StorageClass storage, uint32_t type, uint32_t count) {
const auto array = state.builder.Type(spv::OpTypeArray, {type, ConstantU32(state, count)});
const auto array = state.builder.Type(spv::OpTypeArray, type, ConstantU32(state, count));
return state.builder.DefineGlobalVariable(TypePointer(state, storage, array), storage);
}
uint32_t MeshElement(EmitterState& state, uint32_t variable, spv::StorageClass storage,
uint32_t type, uint32_t index) {
const auto pointer = state.builder.AllocateId();
state.builder.AddFunction(
{spv::OpAccessChain, TypePointer(state, storage, type), pointer, variable, index});
state.builder.AddFunction(spv::OpAccessChain, TypePointer(state, storage, type), pointer,
variable, index);
return pointer;
}
@@ -20,7 +20,7 @@ uint32_t MeshLoad(EmitterState& state, uint32_t variable, spv::StorageClass stor
uint32_t index) {
const auto pointer = MeshElement(state, variable, storage, type, index);
const auto value = state.builder.AllocateId();
state.builder.AddFunction({spv::OpLoad, type, value, pointer});
state.builder.AddFunction(spv::OpLoad, type, value, pointer);
return value;
}
@@ -50,17 +50,17 @@ void DefineMeshOutputs(EmitterState& state) {
state.interface_variables.push_back(output.variable_id);
state.builder.AddName(output.variable_id, output.debug_name.c_str());
if (output.kind == IR::StageOutputKind::Parameter) {
state.builder.AddAnnotation(
{spv::OpDecorate, output.variable_id, spv::DecorationLocation, output.location});
state.builder.AddAnnotation(spv::OpDecorate, output.variable_id,
spv::DecorationLocation, output.location);
} else {
state.builder.AddAnnotation(
{spv::OpDecorate, output.variable_id, spv::DecorationBuiltIn,
output.kind == IR::StageOutputKind::Layer ? spv::BuiltInLayer
: spv::BuiltInPosition});
state.builder.AddAnnotation(spv::OpDecorate, output.variable_id, spv::DecorationBuiltIn,
output.kind == IR::StageOutputKind::Layer
? spv::BuiltInLayer
: spv::BuiltInPosition);
}
if (output.kind == IR::StageOutputKind::Layer) {
state.builder.AddAnnotation({spv::OpDecorate, output.variable_id,
spv::DecorationPerPrimitiveEXT}); // PerPrimitiveEXT
state.builder.AddAnnotation(spv::OpDecorate, output.variable_id,
spv::DecorationPerPrimitiveEXT); // PerPrimitiveEXT
}
}
state.mesh_allocation = MeshArray(state, spv::StorageClassWorkgroup, TypeU32(state), 2);
@@ -73,11 +73,11 @@ void DefineMeshOutputs(EmitterState& state) {
state.interface_variables.push_back(state.mesh_primitives);
state.interface_variables.push_back(state.mesh_cull);
state.builder.AddAnnotation(
{spv::OpDecorate, state.mesh_primitives, spv::DecorationBuiltIn,
spv::BuiltInPrimitiveTriangleIndicesEXT}); // PrimitiveTriangleIndicesEXT
state.builder.AddAnnotation({spv::OpDecorate, state.mesh_cull, spv::DecorationBuiltIn,
spv::BuiltInCullPrimitiveEXT}); // CullPrimitiveEXT
state.builder.AddAnnotation({spv::OpDecorate, state.mesh_cull, spv::DecorationPerPrimitiveEXT});
spv::OpDecorate, state.mesh_primitives, spv::DecorationBuiltIn,
spv::BuiltInPrimitiveTriangleIndicesEXT); // PrimitiveTriangleIndicesEXT
state.builder.AddAnnotation(spv::OpDecorate, state.mesh_cull, spv::DecorationBuiltIn,
spv::BuiltInCullPrimitiveEXT); // CullPrimitiveEXT
state.builder.AddAnnotation(spv::OpDecorate, state.mesh_cull, spv::DecorationPerPrimitiveEXT);
}
uint32_t MeshOutputPointer(EmitterState& state, IR::StageOutputKind kind, uint32_t index) {
@@ -103,45 +103,45 @@ uint32_t MeshPrimitivePointer(EmitterState& state) {
void EmitMeshAllocate(ValueEmitContext& ctx, const IR::Inst& inst) {
auto& state = ctx.state;
const auto first = state.builder.AllocateId();
state.builder.AddFunction({spv::OpIEqual, TypeBool(state), first,
EmitLocalInvocationIndex(state), ConstantU32(state, 0)});
state.builder.AddFunction(spv::OpIEqual, TypeBool(state), first,
EmitLocalInvocationIndex(state), ConstantU32(state, 0));
EmitIfCondition(state, first, [&] {
const auto allocation = ctx.Arg(inst, 0);
for (uint32_t field = 0; field < 2; field++) {
const auto value = state.builder.AllocateId();
state.builder.AddFunction({spv::OpBitFieldUExtract, TypeU32(state), value, allocation,
ConstantU32(state, field * 12u),
ConstantU32(state, field == 0 ? 10u : 11u)});
state.builder.AddFunction(spv::OpBitFieldUExtract, TypeU32(state), value, allocation,
ConstantU32(state, field * 12u),
ConstantU32(state, field == 0 ? 10u : 11u));
const auto pointer =
MeshElement(state, state.mesh_allocation, spv::StorageClassWorkgroup,
TypeU32(state), ConstantU32(state, field));
state.builder.AddFunction({spv::OpStore, pointer, value});
state.builder.AddFunction(spv::OpStore, pointer, value);
}
});
}
void EmitMeshEntryPoint(EmitterState& state) {
state.builder.AddFunction({spv::OpFunction, TypeVoid(state), state.main_func,
spv::FunctionControlMaskNone, TypeFunction(state)});
state.builder.AddFunction(spv::OpFunction, TypeVoid(state), state.main_func,
spv::FunctionControlMaskNone, TypeFunction(state));
EmitLabel(state, state.builder.AllocateId());
state.builder.AddFunction(
{spv::OpFunctionCall, TypeVoid(state), state.builder.AllocateId(), state.mesh_guest_func});
state.builder.AddFunction(spv::OpFunctionCall, TypeVoid(state), state.builder.AllocateId(),
state.mesh_guest_func);
// All guest waves finish before the uniform Vulkan allocation and output stores.
state.builder.AddFunction({spv::OpControlBarrier, ConstantU32(state, spv::ScopeWorkgroup),
ConstantU32(state, spv::ScopeWorkgroup),
ConstantU32(state, spv::MemorySemanticsAcquireReleaseMask |
spv::MemorySemanticsWorkgroupMemoryMask)});
state.builder.AddFunction(spv::OpControlBarrier, ConstantU32(state, spv::ScopeWorkgroup),
ConstantU32(state, spv::ScopeWorkgroup),
ConstantU32(state, spv::MemorySemanticsAcquireReleaseMask |
spv::MemorySemanticsWorkgroupMemoryMask));
const auto vertices = MeshLoad(state, state.mesh_allocation, spv::StorageClassWorkgroup,
TypeU32(state), ConstantU32(state, 0));
const auto primitives = MeshLoad(state, state.mesh_allocation, spv::StorageClassWorkgroup,
TypeU32(state), ConstantU32(state, 1));
state.builder.AddFunction(
{spv::OpSetMeshOutputsEXT, vertices, primitives}); // OpSetMeshOutputsEXT
state.builder.AddFunction(spv::OpSetMeshOutputsEXT, vertices,
primitives); // OpSetMeshOutputsEXT
for (uint32_t half = 0; half < state.lane_count; half++) {
state.lane_half = half;
const auto index = EmitLocalInvocationIndex(state);
const auto is_vertex = state.builder.AllocateId();
state.builder.AddFunction({spv::OpULessThan, TypeBool(state), is_vertex, index, vertices});
state.builder.AddFunction(spv::OpULessThan, TypeBool(state), is_vertex, index, vertices);
EmitIfCondition(state, is_vertex, [&] {
for (const auto& output: state.outputs) {
if (output.kind == IR::StageOutputKind::Layer) {
@@ -153,12 +153,12 @@ void EmitMeshEntryPoint(EmitterState& state) {
ConstantU32(state, half));
const auto pointer =
MeshElement(state, output.variable_id, spv::StorageClassOutput, type, index);
state.builder.AddFunction({spv::OpStore, pointer, value});
state.builder.AddFunction(spv::OpStore, pointer, value);
}
});
const auto is_primitive = state.builder.AllocateId();
state.builder.AddFunction(
{spv::OpULessThan, TypeBool(state), is_primitive, index, primitives});
state.builder.AddFunction(spv::OpULessThan, TypeBool(state), is_primitive, index,
primitives);
EmitIfCondition(state, is_primitive, [&] {
const auto packed = MeshLoad(state, state.mesh_primitive_data, spv::StorageClassPrivate,
TypeU32(state), ConstantU32(state, half));
@@ -166,25 +166,25 @@ void EmitMeshEntryPoint(EmitterState& state) {
for (uint32_t component = 0; component < 3; component++) {
vertex[component] = state.builder.AllocateId();
state.builder.AddFunction(
{spv::OpBitFieldUExtract, TypeU32(state), vertex[component], packed,
ConstantU32(state, component * 10u), ConstantU32(state, 10)});
spv::OpBitFieldUExtract, TypeU32(state), vertex[component], packed,
ConstantU32(state, component * 10u), ConstantU32(state, 10));
}
const auto triangle = state.builder.AllocateId();
state.builder.AddFunction({spv::OpCompositeConstruct, TypeU32Vector(state, 3), triangle,
vertex[0], vertex[1], vertex[2]});
state.builder.AddFunction(spv::OpCompositeConstruct, TypeU32Vector(state, 3), triangle,
vertex[0], vertex[1], vertex[2]);
const auto triangle_pointer =
MeshElement(state, state.mesh_primitives, spv::StorageClassOutput,
TypeU32Vector(state, 3), index);
state.builder.AddFunction({spv::OpStore, triangle_pointer, triangle});
state.builder.AddFunction(spv::OpStore, triangle_pointer, triangle);
const auto null_bit =
EmitBinaryU32(state, spv::OpBitwiseAnd, packed, ConstantU32(state, 0x80000000u));
const auto culled = state.builder.AllocateId();
state.builder.AddFunction(
{spv::OpINotEqual, TypeBool(state), culled, null_bit, ConstantU32(state, 0)});
state.builder.AddFunction({spv::OpStore,
MeshElement(state, state.mesh_cull, spv::StorageClassOutput,
TypeBool(state), index),
culled});
state.builder.AddFunction(spv::OpINotEqual, TypeBool(state), culled, null_bit,
ConstantU32(state, 0));
state.builder.AddFunction(spv::OpStore,
MeshElement(state, state.mesh_cull, spv::StorageClassOutput,
TypeBool(state), index),
culled);
for (const auto& output: state.outputs) {
if (output.kind != IR::StageOutputKind::Layer) {
continue;
@@ -194,13 +194,13 @@ void EmitMeshEntryPoint(EmitterState& state) {
vertex[state.input_info.vertex->mesh.provoking_vertex]);
const auto pointer = MeshElement(state, output.variable_id, spv::StorageClassOutput,
TypeU32(state), index);
state.builder.AddFunction({spv::OpStore, pointer, layer});
state.builder.AddFunction(spv::OpStore, pointer, layer);
}
});
}
state.lane_half = 0;
state.builder.AddFunction({spv::OpReturn});
state.builder.AddFunction({spv::OpFunctionEnd});
state.builder.AddFunction(spv::OpReturn);
state.builder.AddFunction(spv::OpFunctionEnd);
}
} // namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter