diff --git a/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp b/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp index 9fc83d09e..519f1859a 100644 --- a/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp @@ -321,12 +321,6 @@ std::vector EmitProgram(const IR::Program& program, workgroup != nullptr && program.wave_size == 64u && workgroup->host_subgroup_size == 32u ? 2u : 1u; - state.inputs.reserve(program.info.inputs.size()); - state.outputs.reserve(program.info.outputs.size()); - state.interface_variables.reserve(program.info.inputs.size() + program.info.outputs.size()); - CopyProgramInputsAndOutputs(state, program); - AllocateInputVariables(state); - AllocateOutputVariables(state); DefineModule(state); EmitProgram(state); state.builder.AddEntryPoint(ExecutionModelForStage(state.program.stage), state.main_func, diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAnalysis.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAnalysis.cpp index 0e8475ca9..3280f7972 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAnalysis.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAnalysis.cpp @@ -30,16 +30,6 @@ bool PixelParameterIsCustom(const EmitterState& state, uint32_t attr) { ShaderPixelParameterIsCustom(*state.input_info.pixel, attr); } -void CopyProgramInputsAndOutputs(EmitterState& state, const IR::Program& program) { - for (const auto& input: program.info.inputs) { - state.inputs.push_back({input.kind, input.location, input.component_count, 0, - input.debug_name, input.per_vertex}); - } - for (const auto& output: program.info.outputs) { - state.outputs.push_back({output.kind, output.index, output.location, 0, output.debug_name}); - } -} - uint32_t OutputVariableForExport(const EmitterState& state, const IR::ExportInfo& exp) { if (exp.kind == IR::ExportTargetKind::Position && exp.index == 0) { return state.per_vertex_variable; diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h index ff887eada..5c296c1fd 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h @@ -25,22 +25,13 @@ namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter { -struct InputBinding { - IR::StageInputKind kind = IR::StageInputKind::VertexIndex; - uint32_t location = 0; - uint32_t component_count = 1; - uint32_t variable_id = 0; - std::string debug_name; - bool per_vertex = false; +struct InputBinding : IR::StageInput { + uint32_t variable_id = 0; }; -struct OutputBinding { - IR::StageOutputKind kind = IR::StageOutputKind::Parameter; - uint32_t index = 0; - uint32_t location = 0; - uint32_t variable_id = 0; - std::string debug_name; - uint32_t mesh_data_variable = 0; +struct OutputBinding : IR::StageOutput { + uint32_t variable_id = 0; + uint32_t mesh_data_variable = 0; }; using ImageDimension = Decoder::ImageDimension; @@ -124,8 +115,6 @@ struct EmitterState { uint32_t cull_distance_variable = 0; uint32_t layer_variable = 0; uint32_t viewport_index_variable = 0; - uint32_t clip_distance_count = 0; - uint32_t cull_distance_count = 0; uint32_t depth_variable = 0; uint32_t sample_mask_variable = 0; std::vector inputs; @@ -267,11 +256,6 @@ uint32_t VertexParameterComponentCount(const InputBinding& input); uint32_t VertexParameterScalarType(EmitterState& state, VertexInputScalarKind kind); -uint32_t VertexParameterInputPointerType(EmitterState& state, VertexInputScalarKind kind, - uint32_t components); - - -void CopyProgramInputsAndOutputs(EmitterState& state, const IR::Program& program); uint32_t OutputVariableForExport(const EmitterState& state, const IR::ExportInfo& exp); @@ -329,21 +313,6 @@ uint32_t ConstantU64(EmitterState& state, uint64_t value); uint32_t ConstantU32CompositeZero(EmitterState& state, uint32_t components); -void AllocateInputVariables(EmitterState& state); - -void AllocateOutputVariables(EmitterState& state); - -uint32_t BuiltInForInput(IR::StageInputKind kind); - -void AddInputAnnotationsAndNames(EmitterState& state); - -void AddOutputAnnotationsAndNames(EmitterState& state); - -void DecorateDescriptor(EmitterState& state, uint32_t variable, const char* name, - IR::DescriptorBindingKind kind); - -void AddDescriptorAnnotationsAndNames(EmitterState& state); - void DefineModule(EmitterState& state); void DefineMeshOutputs(EmitterState& state); void EmitMeshEntryPoint(EmitterState& state); diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp index e91774ed4..a0fd6eeb1 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp @@ -4,10 +4,6 @@ namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter { -const IR::DescriptorBinding* DescriptorBinding(const EmitterState& state, - IR::DescriptorBindingKind kind); -uint32_t DescriptorCount(const EmitterState& state, IR::DescriptorBindingKind kind); - uint32_t TypeVoid(EmitterState& state) { return state.builder.Type(spv::OpTypeVoid); } @@ -160,105 +156,91 @@ uint32_t SampleMaskArrayType(EmitterState& state) { return state.builder.Type(spv::OpTypeArray, {TypeI32(state), ConstantU32(state, 1)}); } -uint32_t BdaPagetableType(EmitterState& state) { - const auto array = state.builder.DecoratedType( - spv::OpTypeRuntimeArray, {TypeScalarU64(state)}, - {{spv::OpDecorate, {spv::DecorationArrayStride, sizeof(uint64_t)}}}); - return state.builder.DecoratedType(spv::OpTypeStruct, {array}, - {{spv::OpMemberDecorate, {0, spv::DecorationOffset, 0}}, - {spv::OpDecorate, {spv::DecorationBlock}}}); -} - uint32_t F32ArrayType(EmitterState& state, uint32_t count) { return state.builder.Type(spv::OpTypeArray, {TypeF32(state), ConstantU32(state, count)}); } -void DefineDescriptorVariables(EmitterState& state) { - if (DescriptorBinding(state, IR::DescriptorBindingKind::Buffers) != nullptr) { - const auto count = - ConstantU32(state, DescriptorCount(state, IR::DescriptorBindingKind::Buffers)); - const auto array_type = - state.builder.Type(spv::OpTypeArray, {StorageBufferType(state), count}); - const auto pointer_type = TypePointer(state, spv::StorageClassStorageBuffer, array_type); - state.storage_buffer_variable = - state.builder.DefineGlobalVariable(pointer_type, spv::StorageClassStorageBuffer); - if (state.requirements.buffer_int64_atomics) { - const auto u64_array_type = - state.builder.Type(spv::OpTypeArray, {StorageBufferU64Type(state), count}); - state.storage_buffer_u64_variable = state.builder.DefineGlobalVariable( - TypePointer(state, spv::StorageClassStorageBuffer, u64_array_type), - spv::StorageClassStorageBuffer); - } - } - if (DescriptorBinding(state, IR::DescriptorBindingKind::BdaPagetable) != nullptr) { - state.bda_pagetable_variable = state.builder.DefineGlobalVariable( - TypePointer(state, spv::StorageClassStorageBuffer, BdaPagetableType(state)), - spv::StorageClassStorageBuffer); - } - if (DescriptorBinding(state, IR::DescriptorBindingKind::FaultBuffer) != nullptr) { - state.fault_buffer_variable = state.builder.DefineGlobalVariable( - TypeStorageBufferPointer(state), spv::StorageClassStorageBuffer); - } +void DefineDescriptors(EmitterState& state) { if (state.program.bindings.UsesPushData() || state.program.stage == ShaderType::Mesh) { - const auto pointer_type = - TypePointer(state, spv::StorageClassPushConstant, PushConstantBlockType(state)); - state.push_constant_variable = - state.builder.DefineGlobalVariable(pointer_type, spv::StorageClassPushConstant); - } - if (DescriptorBinding(state, IR::DescriptorBindingKind::ShaderData) != nullptr) { - state.shader_data_storage_variable = state.builder.DefineGlobalVariable( - TypeStorageBufferPointer(state), spv::StorageClassStorageBuffer); - } - if (DescriptorBinding(state, IR::DescriptorBindingKind::FlattenedSrt) != nullptr) { - state.flattened_srt_variable = state.builder.DefineGlobalVariable( - TypeStorageBufferPointer(state), spv::StorageClassStorageBuffer); + const auto type = PushConstantBlockType(state); + state.push_constant_variable = state.builder.DefineGlobalVariable( + TypePointer(state, spv::StorageClassPushConstant, type), spv::StorageClassPushConstant); + state.builder.AddName(type, "BufferResource"); + state.builder.AddName(state.push_constant_variable, "vsharp"); } for (const auto& binding: state.program.bindings.descriptors) { - if (IR::ImageBindingResourceClass(binding.kind) == IR::ImageResourceClass::None) { - continue; + const auto Define = [&](uint32_t type, const char* name, + spv::StorageClass storage = spv::StorageClassStorageBuffer) { + const auto variable = + state.builder.DefineGlobalVariable(TypePointer(state, storage, type), storage); + state.builder.AddName(variable, name); + state.builder.AddAnnotation( + {spv::OpDecorate, variable, spv::DecorationDescriptorSet, 0}); + state.builder.AddAnnotation({spv::OpDecorate, variable, spv::DecorationBinding, + IR::NativeBinding(state.program.stage, binding.kind)}); + return variable; + }; + const auto ArrayType = [&](uint32_t type) { + return state.builder.Type( + spv::OpTypeArray, + {type, ConstantU32(state, static_cast(binding.resources.size()))}); + }; + switch (binding.kind) { + case IR::DescriptorBindingKind::Buffers: + state.storage_buffer_variable = + Define(ArrayType(StorageBufferType(state)), "buffers"); + if (state.requirements.buffer_int64_atomics) { + state.storage_buffer_u64_variable = + Define(ArrayType(StorageBufferU64Type(state)), "buffers_u64"); + state.builder.AddAnnotation( + {spv::OpDecorate, state.storage_buffer_variable, spv::DecorationAliased}); + state.builder.AddAnnotation({spv::OpDecorate, state.storage_buffer_u64_variable, + spv::DecorationAliased}); + } + break; + case IR::DescriptorBindingKind::BdaPagetable: + state.bda_pagetable_variable = Define(StorageBufferU64Type(state), "bda_pagetable"); + break; + case IR::DescriptorBindingKind::FaultBuffer: + state.fault_buffer_variable = Define(StorageBufferType(state), "fault_buffer"); + break; + case IR::DescriptorBindingKind::ShaderData: + state.shader_data_storage_variable = + Define(StorageBufferType(state), "shader_data"); + break; + case IR::DescriptorBindingKind::FlattenedSrt: + state.flattened_srt_variable = Define(StorageBufferType(state), "flattened_srt"); + break; + case IR::DescriptorBindingKind::Samplers: + state.sampler_variable = Define(ArrayType(state.builder.Type(spv::OpTypeSampler)), + "samplers", spv::StorageClassUniformConstant); + break; + case IR::DescriptorBindingKind::Gds: + state.gds_variable = Define(StorageBufferType(state), "gds"); + break; + default: { + EXIT_IF(IR::ImageBindingResourceClass(binding.kind) == + IR::ImageResourceClass::None); + const auto& image = state.program.info.images.at(binding.resources.front()); + const auto name = "image_" + std::to_string(static_cast(binding.kind)); + state.image_variables[IR::ImageBindingIndex(binding.kind)] = + Define(ArrayType(ImageType(state, image)), name.c_str(), + spv::StorageClassUniformConstant); + if (image.dimension == ImageDimension::Dim1D || + image.dimension == ImageDimension::Dim1DArray) { + state.builder.RequireCapability(image.resource_class == + IR::ImageResourceClass::Sampled + ? spv::CapabilitySampled1D + : spv::CapabilityImage1D); + } + break; + } } - const auto& image = state.program.info.images.at(binding.resources.front()); - const auto count = ConstantU32(state, DescriptorCount(state, binding.kind)); - const auto image_type = ImageType(state, image); - const auto array_type = state.builder.Type(spv::OpTypeArray, {image_type, count}); - const auto pointer_type = TypePointer(state, spv::StorageClassUniformConstant, array_type); - state.image_variables[IR::ImageBindingIndex(binding.kind)] = - state.builder.DefineGlobalVariable(pointer_type, spv::StorageClassUniformConstant); - if (image.dimension == ImageDimension::Dim1D || - image.dimension == ImageDimension::Dim1DArray) { - const auto capability = image.resource_class == IR::ImageResourceClass::Sampled - ? spv::CapabilitySampled1D - : spv::CapabilityImage1D; - state.builder.RequireCapability(capability); - } - } - if (DescriptorBinding(state, IR::DescriptorBindingKind::Samplers) != nullptr) { - const auto sampler_type = state.builder.Type(spv::OpTypeSampler); - const auto count = - ConstantU32(state, DescriptorCount(state, IR::DescriptorBindingKind::Samplers)); - const auto array_type = state.builder.Type(spv::OpTypeArray, {sampler_type, count}); - const auto pointer_type = TypePointer(state, spv::StorageClassUniformConstant, array_type); - state.sampler_variable = - state.builder.DefineGlobalVariable(pointer_type, spv::StorageClassUniformConstant); - } - if (DescriptorBinding(state, IR::DescriptorBindingKind::Gds) != nullptr) { - state.gds_variable = state.builder.DefineGlobalVariable(TypeStorageBufferPointer(state), - spv::StorageClassStorageBuffer); } } } // namespace -const IR::DescriptorBinding* DescriptorBinding(const EmitterState& state, - IR::DescriptorBindingKind kind) { - return IR::FindBinding(state.program.bindings, kind); -} - -uint32_t DescriptorCount(const EmitterState& state, IR::DescriptorBindingKind kind) { - const auto* binding = DescriptorBinding(state, kind); - return binding != nullptr ? static_cast(binding->resources.size()) : 0; -} - uint32_t ConstantU32(EmitterState& state, uint32_t value) { return state.builder.Constant(spv::OpConstant, TypeU32(state), {value}); } @@ -348,127 +330,17 @@ uint32_t VertexParameterScalarType(EmitterState& state, VertexInputScalarKind ki } } -uint32_t VertexParameterInputPointerType(EmitterState& state, VertexInputScalarKind kind, - uint32_t components) { - switch (kind) { - case VertexInputScalarKind::Sint: - switch (components) { - case 1: return TypePointer(state, spv::StorageClassInput, TypeI32(state)); - case 2: return TypePointer(state, spv::StorageClassInput, TypeI32Vector(state, 2)); - case 3: return TypePointer(state, spv::StorageClassInput, TypeI32Vector(state, 3)); - default: return TypePointer(state, spv::StorageClassInput, TypeI32Vector(state, 4)); - } - case VertexInputScalarKind::Uint: - switch (components) { - case 1: return TypePointer(state, spv::StorageClassInput, TypeU32(state)); - case 2: return TypePointer(state, spv::StorageClassInput, TypeU32Vector(state, 2)); - case 3: return TypePointer(state, spv::StorageClassInput, TypeU32Vector(state, 3)); - default: return TypePointer(state, spv::StorageClassInput, TypeU32Vector(state, 4)); - } - case VertexInputScalarKind::Float: - default: - switch (components) { - case 1: return TypePointer(state, spv::StorageClassInput, TypeF32(state)); - case 2: return TypePointer(state, spv::StorageClassInput, TypeF32Vector(state, 2)); - case 3: return TypePointer(state, spv::StorageClassInput, TypeF32Vector(state, 3)); - default: return TypePointer(state, spv::StorageClassInput, TypeF32Vector(state, 4)); - } - } -} +namespace { -static bool MrtUsesUintOutput(const EmitterState& state, uint32_t index) { - return state.program.stage == ShaderType::Pixel && - index < std::size(state.input_info.pixel->target_output_mode) && - state.input_info.pixel->target_output_mode[index] == 7u; -} - -void AllocateInputVariables(EmitterState& state) { - if (state.lane_count == 2) { - const auto add_builtin = [&](IR::StageInputKind kind, uint32_t components, - const char* name) { - if (std::ranges::none_of(state.inputs, [kind](const InputBinding& input) { - return input.kind == kind; - })) { - state.inputs.push_back({kind, 0, components, 0, name}); - } - }; - add_builtin(IR::StageInputKind::LocalInvocationIndex, 1, "gl_LocalInvocationIndex"); - if (std::ranges::any_of(state.inputs, [](const InputBinding& input) { - return input.kind == IR::StageInputKind::GlobalInvocationId; - })) { - add_builtin(IR::StageInputKind::WorkgroupId, 3, "gl_WorkGroupID"); - } - } - for (auto& binding: state.inputs) { - binding.variable_id = state.builder.AllocateId(); - state.interface_variables.push_back(binding.variable_id); - } - if (state.requirements.subgroup_local_invocation_id) { - state.subgroup_local_invocation_id_variable = state.builder.AllocateId(); - state.interface_variables.push_back(state.subgroup_local_invocation_id_variable); - } -} - -static uint32_t AllocateInterfaceVariable(EmitterState& state) { - const auto variable = state.builder.AllocateId(); +uint32_t DefineInterfaceVariable(EmitterState& state, uint32_t type, spv::StorageClass storage, + const char* name) { + const auto variable = + state.builder.DefineGlobalVariable(TypePointer(state, storage, type), storage); state.interface_variables.push_back(variable); + state.builder.AddName(variable, name); return variable; } -static uint32_t AllocateSharedOutputVariable(EmitterState& state, uint32_t& variable) { - if (variable == 0) { - variable = AllocateInterfaceVariable(state); - } - return variable; -} - -void AllocateOutputVariables(EmitterState& state) { - if (state.program.stage == ShaderType::Mesh) { - DefineMeshOutputs(state); - return; - } - for (auto& binding: state.outputs) { - switch (binding.kind) { - case IR::StageOutputKind::Position: - binding.variable_id = - AllocateSharedOutputVariable(state, state.per_vertex_variable); - break; - case IR::StageOutputKind::PointSize: - binding.variable_id = - AllocateSharedOutputVariable(state, state.point_size_variable); - break; - case IR::StageOutputKind::ClipDistance: - binding.variable_id = - AllocateSharedOutputVariable(state, state.clip_distance_variable); - state.clip_distance_count = std::max(state.clip_distance_count, binding.index + 1); - break; - case IR::StageOutputKind::CullDistance: - binding.variable_id = - AllocateSharedOutputVariable(state, state.cull_distance_variable); - state.cull_distance_count = std::max(state.cull_distance_count, binding.index + 1); - break; - case IR::StageOutputKind::Layer: - binding.variable_id = AllocateSharedOutputVariable(state, state.layer_variable); - break; - case IR::StageOutputKind::ViewportIndex: - binding.variable_id = - AllocateSharedOutputVariable(state, state.viewport_index_variable); - break; - case IR::StageOutputKind::Depth: - binding.variable_id = AllocateSharedOutputVariable(state, state.depth_variable); - break; - case IR::StageOutputKind::SampleMask: - binding.variable_id = - AllocateSharedOutputVariable(state, state.sample_mask_variable); - break; - case IR::StageOutputKind::Parameter: - case IR::StageOutputKind::Mrt: - binding.variable_id = AllocateInterfaceVariable(state); - break; - } - } -} - uint32_t BuiltInForInput(IR::StageInputKind kind) { switch (kind) { case IR::StageInputKind::VertexIndex: return spv::BuiltInVertexIndex; @@ -487,21 +359,60 @@ uint32_t BuiltInForInput(IR::StageInputKind kind) { } } -void AddInputAnnotationsAndNames(EmitterState& state) { - if (state.subgroup_local_invocation_id_variable != 0) { - state.builder.AddName(state.subgroup_local_invocation_id_variable, - "gl_SubgroupInvocationID"); - state.builder.AddAnnotation({spv::OpDecorate, state.subgroup_local_invocation_id_variable, - spv::DecorationBuiltIn, - spv::BuiltInSubgroupLocalInvocationId}); - if (state.program.stage == ShaderType::Pixel) { - state.builder.AddAnnotation({spv::OpDecorate, - state.subgroup_local_invocation_id_variable, - spv::DecorationFlat}); +void DefineInputs(EmitterState& state) { + state.inputs.reserve(state.program.info.inputs.size()); + for (const auto& input: state.program.info.inputs) { + state.inputs.push_back({input}); + } + if (state.lane_count == 2) { + const auto add_builtin = [&](IR::StageInputKind kind, uint32_t components, + const char* name) { + if (std::ranges::none_of(state.inputs, [kind](const InputBinding& input) { + return input.kind == kind; + })) { + state.inputs.push_back({{kind, 0, components, name}}); + } + }; + add_builtin(IR::StageInputKind::LocalInvocationIndex, 1, "gl_LocalInvocationIndex"); + if (std::ranges::any_of(state.inputs, [](const InputBinding& input) { + return input.kind == IR::StageInputKind::GlobalInvocationId; + })) { + add_builtin(IR::StageInputKind::WorkgroupId, 3, "gl_WorkGroupID"); } } - for (const auto& input: state.inputs) { - state.builder.AddName(input.variable_id, input.debug_name.c_str()); + for (auto& input: state.inputs) { + uint32_t type = TypeU32(state); + switch (input.kind) { + case IR::StageInputKind::VertexIndex: + case IR::StageInputKind::InstanceIndex: + case IR::StageInputKind::Layer: + case IR::StageInputKind::SampleId: type = TypeI32(state); break; + case IR::StageInputKind::WorkgroupId: + case IR::StageInputKind::LocalInvocationId: + case IR::StageInputKind::GlobalInvocationId: type = TypeU32Vector(state, 3); break; + case IR::StageInputKind::FragCoord: type = TypeF32Vector(state, 4); break; + case IR::StageInputKind::BaryCoordSmooth: + case IR::StageInputKind::BaryCoordNoPerspective: type = TypeF32Vector(state, 3); break; + case IR::StageInputKind::FrontFacing: type = TypeBool(state); break; + case IR::StageInputKind::Parameter: + if (state.program.stage == ShaderType::Vertex) { + type = VertexParameterScalarType( + state, VertexParameterScalarKind(state, input.location)); + const auto components = VertexParameterComponentCount(input); + if (components > 1u) { + type = state.builder.Type(spv::OpTypeVector, {type, components}); + } + } else if (input.per_vertex) { + type = state.builder.Type(spv::OpTypeArray, + {TypeF32Vector(state, 4), ConstantU32(state, 3)}); + } else { + type = TypeF32Vector(state, 4); + } + break; + default: break; + } + input.variable_id = + DefineInterfaceVariable(state, type, spv::StorageClassInput, input.debug_name.c_str()); if (input.kind == IR::StageInputKind::Layer || input.kind == IR::StageInputKind::SampleId) { state.builder.AddAnnotation({spv::OpDecorate, input.variable_id, spv::DecorationFlat}); } @@ -519,126 +430,119 @@ void AddInputAnnotationsAndNames(EmitterState& state) { state.builder.AddAnnotation( {spv::OpDecorate, input.variable_id, spv::DecorationNoPerspective}); } - const auto location = PixelParameterLocation(state, input.location); - state.builder.AddAnnotation( - {spv::OpDecorate, input.variable_id, spv::DecorationLocation, location}); - continue; - } - const auto builtin = BuiltInForInput(input.kind); - if (builtin != UINT32_MAX) { + state.builder.AddAnnotation({spv::OpDecorate, input.variable_id, + spv::DecorationLocation, + PixelParameterLocation(state, input.location)}); + } else if (const auto builtin = BuiltInForInput(input.kind); builtin != UINT32_MAX) { state.builder.AddAnnotation( {spv::OpDecorate, input.variable_id, spv::DecorationBuiltIn, builtin}); } } + if (state.requirements.subgroup_local_invocation_id) { + const auto variable = DefineInterfaceVariable(state, TypeU32(state), spv::StorageClassInput, + "gl_SubgroupInvocationID"); + state.subgroup_local_invocation_id_variable = variable; + state.builder.AddAnnotation({spv::OpDecorate, variable, spv::DecorationBuiltIn, + spv::BuiltInSubgroupLocalInvocationId}); + if (state.program.stage == ShaderType::Pixel) { + state.builder.AddAnnotation({spv::OpDecorate, variable, spv::DecorationFlat}); + } + } } -void AddOutputAnnotationsAndNames(EmitterState& state) { +void DefineOutputs(EmitterState& state) { + state.outputs.reserve(state.program.info.outputs.size()); + uint32_t clip_distance_count = 0; + uint32_t cull_distance_count = 0; + for (const auto& output: state.program.info.outputs) { + state.outputs.push_back({output}); + if (output.kind == IR::StageOutputKind::ClipDistance) { + clip_distance_count = std::max(clip_distance_count, output.index + 1); + } else if (output.kind == IR::StageOutputKind::CullDistance) { + cull_distance_count = std::max(cull_distance_count, output.index + 1); + } + } if (state.program.stage == ShaderType::Mesh) { + DefineMeshOutputs(state); return; } - if (state.per_vertex_variable != 0) { - state.builder.AddName(PerVertexType(state), "gl_PerVertex"); - state.builder.AddName(state.per_vertex_variable, "outPerVertex"); - } - auto AddBuiltIn = [&](uint32_t variable, const char* name, uint32_t builtin) { - if (variable != 0) { - state.builder.AddName(variable, name); + const auto BuiltIn = [&](uint32_t& variable, uint32_t type, const char* name, + spv::BuiltIn builtin) { + if (variable == 0) { + variable = DefineInterfaceVariable(state, type, spv::StorageClassOutput, name); state.builder.AddAnnotation( {spv::OpDecorate, variable, spv::DecorationBuiltIn, builtin}); } + return variable; }; - AddBuiltIn(state.point_size_variable, "gl_PointSize", spv::BuiltInPointSize); - AddBuiltIn(state.clip_distance_variable, "gl_ClipDistance", spv::BuiltInClipDistance); - AddBuiltIn(state.cull_distance_variable, "gl_CullDistance", spv::BuiltInCullDistance); - AddBuiltIn(state.layer_variable, "gl_Layer", spv::BuiltInLayer); - AddBuiltIn(state.viewport_index_variable, "gl_ViewportIndex", spv::BuiltInViewportIndex); - if (state.depth_variable != 0) { - state.builder.AddName(state.depth_variable, "gl_FragDepth"); - state.builder.AddAnnotation( - {spv::OpDecorate, state.depth_variable, spv::DecorationBuiltIn, spv::BuiltInFragDepth}); - } - if (state.sample_mask_variable != 0) { - state.builder.AddName(state.sample_mask_variable, "gl_SampleMask"); - state.builder.AddAnnotation({spv::OpDecorate, state.sample_mask_variable, - spv::DecorationBuiltIn, spv::BuiltInSampleMask}); - } - for (const auto& binding: state.outputs) { - if (binding.kind == IR::StageOutputKind::Parameter || - binding.kind == IR::StageOutputKind::Mrt) { - state.builder.AddName(binding.variable_id, binding.debug_name.c_str()); - state.builder.AddAnnotation( - {spv::OpDecorate, binding.variable_id, spv::DecorationLocation, binding.location}); + for (auto& binding: state.outputs) { + switch (binding.kind) { + case IR::StageOutputKind::Position: + if (state.per_vertex_variable == 0) { + const auto type = PerVertexType(state); + state.builder.AddName(type, "gl_PerVertex"); + state.per_vertex_variable = DefineInterfaceVariable( + state, type, spv::StorageClassOutput, "outPerVertex"); + } + binding.variable_id = state.per_vertex_variable; + break; + case IR::StageOutputKind::PointSize: + binding.variable_id = BuiltIn(state.point_size_variable, TypeF32(state), + "gl_PointSize", spv::BuiltInPointSize); + break; + case IR::StageOutputKind::ClipDistance: + binding.variable_id = + BuiltIn(state.clip_distance_variable, F32ArrayType(state, clip_distance_count), + "gl_ClipDistance", spv::BuiltInClipDistance); + break; + case IR::StageOutputKind::CullDistance: + binding.variable_id = + BuiltIn(state.cull_distance_variable, F32ArrayType(state, cull_distance_count), + "gl_CullDistance", spv::BuiltInCullDistance); + break; + case IR::StageOutputKind::Layer: + binding.variable_id = + BuiltIn(state.layer_variable, TypeU32(state), "gl_Layer", spv::BuiltInLayer); + break; + case IR::StageOutputKind::ViewportIndex: + binding.variable_id = BuiltIn(state.viewport_index_variable, TypeU32(state), + "gl_ViewportIndex", spv::BuiltInViewportIndex); + break; + case IR::StageOutputKind::Depth: + binding.variable_id = BuiltIn(state.depth_variable, TypeF32(state), "gl_FragDepth", + spv::BuiltInFragDepth); + break; + case IR::StageOutputKind::SampleMask: + binding.variable_id = + BuiltIn(state.sample_mask_variable, SampleMaskArrayType(state), "gl_SampleMask", + spv::BuiltInSampleMask); + break; + case IR::StageOutputKind::Parameter: + case IR::StageOutputKind::Mrt: { + const bool uint_output = + binding.kind == IR::StageOutputKind::Mrt && + state.program.stage == ShaderType::Pixel && + binding.index < std::size(state.input_info.pixel->target_output_mode) && + state.input_info.pixel->target_output_mode[binding.index] == 7u; + const auto type = uint_output ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4); + binding.variable_id = DefineInterfaceVariable(state, type, spv::StorageClassOutput, + binding.debug_name.c_str()); + state.builder.AddAnnotation({spv::OpDecorate, binding.variable_id, + spv::DecorationLocation, binding.location}); + break; + } } } } -void DecorateDescriptor(EmitterState& state, uint32_t variable, const char* name, - IR::DescriptorBindingKind kind) { - if (variable == 0) { - return; - } - state.builder.AddName(variable, name); - state.builder.AddAnnotation({spv::OpDecorate, variable, spv::DecorationDescriptorSet, 0}); - state.builder.AddAnnotation({spv::OpDecorate, variable, spv::DecorationBinding, - IR::NativeBinding(state.program.stage, kind)}); -} - -void AddDescriptorAnnotationsAndNames(EmitterState& state) { - auto Decorate = [&](uint32_t variable, const char* name, IR::DescriptorBindingKind kind) { - DecorateDescriptor(state, variable, name, kind); - }; - if (state.storage_buffer_variable != 0) { - Decorate(state.storage_buffer_variable, "buffers", IR::DescriptorBindingKind::Buffers); - } - if (state.storage_buffer_u64_variable != 0) { - Decorate(state.storage_buffer_u64_variable, "buffers_u64", - IR::DescriptorBindingKind::Buffers); - state.builder.AddAnnotation( - {spv::OpDecorate, state.storage_buffer_variable, spv::DecorationAliased}); - state.builder.AddAnnotation( - {spv::OpDecorate, state.storage_buffer_u64_variable, spv::DecorationAliased}); - } - if (state.bda_pagetable_variable != 0) { - Decorate(state.bda_pagetable_variable, "bda_pagetable", - IR::DescriptorBindingKind::BdaPagetable); - } - if (state.fault_buffer_variable != 0) { - Decorate(state.fault_buffer_variable, "fault_buffer", - IR::DescriptorBindingKind::FaultBuffer); - } - for (const auto& binding: state.program.bindings.descriptors) { - if (IR::ImageBindingResourceClass(binding.kind) == IR::ImageResourceClass::None) { - continue; - } - const auto name = "image_" + std::to_string(static_cast(binding.kind)); - Decorate(state.image_variables[IR::ImageBindingIndex(binding.kind)], name.c_str(), - binding.kind); - } - if (state.sampler_variable != 0) { - Decorate(state.sampler_variable, "samplers", IR::DescriptorBindingKind::Samplers); - } - if (state.gds_variable != 0) { - Decorate(state.gds_variable, "gds", IR::DescriptorBindingKind::Gds); - } - if (state.flattened_srt_variable != 0) { - Decorate(state.flattened_srt_variable, "flattened_srt", - IR::DescriptorBindingKind::FlattenedSrt); - } -} - -void AddVsharpAnnotationsAndNames(EmitterState& state) { - if (state.push_constant_variable != 0) { - state.builder.AddName(PushConstantBlockType(state), "BufferResource"); - state.builder.AddName(state.push_constant_variable, "vsharp"); - } - if (state.shader_data_storage_variable != 0) { - DecorateDescriptor(state, state.shader_data_storage_variable, "shader_data", - IR::DescriptorBindingKind::ShaderData); - } -} +} // namespace void DefineModule(EmitterState& state) { - DefineDescriptorVariables(state); + state.interface_variables.reserve(state.program.info.inputs.size() + + state.program.info.outputs.size()); + DefineInputs(state); + DefineOutputs(state); + DefineDescriptors(state); if (state.requirements.function_lds) { state.lds_variable = state.builder.AllocateId(); } @@ -647,7 +551,7 @@ void DefineModule(EmitterState& state) { state.scratch_variable[half] = state.builder.AllocateId(); } } - state.main_func = state.builder.AllocateId(); + state.main_func = state.builder.AllocateId(); if (state.program.stage == ShaderType::Mesh) { state.mesh_guest_func = state.builder.AllocateId(); state.builder.RequireCapability(spv::CapabilityMeshShadingEXT); // MeshShadingEXT @@ -767,117 +671,6 @@ void DefineModule(EmitterState& state) { state.builder.AddName(state.scratch_variable[half], "scratch_dwords"); } } - AddInputAnnotationsAndNames(state); - AddOutputAnnotationsAndNames(state); - AddDescriptorAnnotationsAndNames(state); - AddVsharpAnnotationsAndNames(state); - - if (state.subgroup_local_invocation_id_variable != 0) { - state.builder.DefineGlobalVariable( - state.subgroup_local_invocation_id_variable, - TypePointer(state, spv::StorageClassInput, TypeU32(state)), spv::StorageClassInput); - } - for (const auto& input: state.inputs) { - uint32_t ptr_type = TypePointer(state, spv::StorageClassInput, TypeU32(state)); - switch (input.kind) { - case IR::StageInputKind::VertexIndex: - case IR::StageInputKind::InstanceIndex: - case IR::StageInputKind::Layer: - case IR::StageInputKind::SampleId: - ptr_type = TypePointer(state, spv::StorageClassInput, TypeI32(state)); - break; - case IR::StageInputKind::WorkgroupId: - case IR::StageInputKind::LocalInvocationId: - case IR::StageInputKind::GlobalInvocationId: - ptr_type = TypePointer(state, spv::StorageClassInput, TypeU32Vector(state, 3)); - break; - case IR::StageInputKind::FragCoord: - ptr_type = TypePointer(state, spv::StorageClassInput, TypeF32Vector(state, 4)); - break; - case IR::StageInputKind::BaryCoordSmooth: - case IR::StageInputKind::BaryCoordNoPerspective: - ptr_type = TypePointer(state, spv::StorageClassInput, TypeF32Vector(state, 3)); - break; - case IR::StageInputKind::FrontFacing: - ptr_type = TypePointer(state, spv::StorageClassInput, TypeBool(state)); - break; - case IR::StageInputKind::Parameter: - if (state.program.stage == ShaderType::Vertex) { - const auto kind = VertexParameterScalarKind(state, input.location); - const auto components = VertexParameterComponentCount(input); - ptr_type = VertexParameterInputPointerType(state, kind, components); - } else if (input.per_vertex) { - const auto array_type = state.builder.Type( - spv::OpTypeArray, {TypeF32Vector(state, 4), ConstantU32(state, 3)}); - ptr_type = TypePointer(state, spv::StorageClassInput, array_type); - } else { - ptr_type = TypePointer(state, spv::StorageClassInput, TypeF32Vector(state, 4)); - } - break; - default: break; - } - state.builder.DefineGlobalVariable(input.variable_id, ptr_type, spv::StorageClassInput); - } - if (state.program.stage == ShaderType::Mesh) { - return; - } - if (state.per_vertex_variable != 0) { - state.builder.DefineGlobalVariable( - state.per_vertex_variable, - TypePointer(state, spv::StorageClassOutput, PerVertexType(state)), - spv::StorageClassOutput); - } - if (state.point_size_variable != 0) { - state.builder.DefineGlobalVariable( - state.point_size_variable, TypePointer(state, spv::StorageClassOutput, TypeF32(state)), - spv::StorageClassOutput); - } - if (state.clip_distance_variable != 0) { - state.builder.DefineGlobalVariable( - state.clip_distance_variable, - TypePointer(state, spv::StorageClassOutput, - F32ArrayType(state, state.clip_distance_count)), - spv::StorageClassOutput); - } - if (state.cull_distance_variable != 0) { - state.builder.DefineGlobalVariable( - state.cull_distance_variable, - TypePointer(state, spv::StorageClassOutput, - F32ArrayType(state, state.cull_distance_count)), - spv::StorageClassOutput); - } - if (state.layer_variable != 0) { - state.builder.DefineGlobalVariable( - state.layer_variable, TypePointer(state, spv::StorageClassOutput, TypeU32(state)), - spv::StorageClassOutput); - } - if (state.viewport_index_variable != 0) { - state.builder.DefineGlobalVariable( - state.viewport_index_variable, - TypePointer(state, spv::StorageClassOutput, TypeU32(state)), spv::StorageClassOutput); - } - for (const auto& binding: state.outputs) { - if (binding.kind == IR::StageOutputKind::Parameter || - binding.kind == IR::StageOutputKind::Mrt) { - const auto pointer_type = - binding.kind == IR::StageOutputKind::Mrt && MrtUsesUintOutput(state, binding.index) - ? TypePointer(state, spv::StorageClassOutput, TypeU32Vector(state, 4)) - : TypePointer(state, spv::StorageClassOutput, TypeF32Vector(state, 4)); - state.builder.DefineGlobalVariable(binding.variable_id, pointer_type, - spv::StorageClassOutput); - } - } - if (state.depth_variable != 0) { - state.builder.DefineGlobalVariable( - state.depth_variable, TypePointer(state, spv::StorageClassOutput, TypeF32(state)), - spv::StorageClassOutput); - } - if (state.sample_mask_variable != 0) { - state.builder.DefineGlobalVariable( - state.sample_mask_variable, - TypePointer(state, spv::StorageClassOutput, SampleMaskArrayType(state)), - spv::StorageClassOutput); - } } } // namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter