diff --git a/src/graphics/host_gpu/renderer/pipeline/shaderSubgroup.cpp b/src/graphics/host_gpu/renderer/pipeline/shaderSubgroup.cpp index cb8f3177d..534e655ae 100644 --- a/src/graphics/host_gpu/renderer/pipeline/shaderSubgroup.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/shaderSubgroup.cpp @@ -25,7 +25,8 @@ ShaderSubgroupConfiguration ConfigureShaderSubgroup(const ShaderSubgroupCapabili vk::ShaderStageFlagBits stage, const ShaderRecompiler::IR::Program& program) { const auto guest_wave_size = program.wave_size; - if (guest_wave_size != 32u && guest_wave_size != 64u) { + if ((guest_wave_size != 32u && guest_wave_size != 64u) || + !program.spirv_requirements.has_value()) { return {}; } if (stage != vk::ShaderStageFlagBits::eCompute) { @@ -51,7 +52,7 @@ ShaderSubgroupConfiguration ConfigureShaderSubgroup(const ShaderSubgroupCapabili guest_wave_size <= capabilities.max_subgroup_size) { return {ShaderSubgroupMode::Controlled, guest_wave_size}; } - if (!ShaderRecompiler::Spirv::GetProgramRequirements(program).requires_exact_subgroup) { + if (!program.spirv_requirements->requires_exact_subgroup) { return {ShaderSubgroupMode::FlattenedMasks, 0}; } return {}; diff --git a/src/graphics/shader/recompiler/ShaderRecompiler.cpp b/src/graphics/shader/recompiler/ShaderRecompiler.cpp index c005c6bdb..d346f5548 100644 --- a/src/graphics/shader/recompiler/ShaderRecompiler.cpp +++ b/src/graphics/shader/recompiler/ShaderRecompiler.cpp @@ -942,7 +942,7 @@ bool TryRecompile(std::span code, const CompileOptions& options, if (!IR::AllocateBindings(ir, layout_options, error)) { return false; } - ir.spirv_requirements = Spirv::GetProgramRequirements(ir); + Spirv::AnalyzeProgramRequirements(ir); std::string ir_dump; if (options.dump_ir) { ir_dump = MakeIrDump(cfg, ir); diff --git a/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.cpp b/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.cpp index e9b4d48b9..4fbcf93da 100644 --- a/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.cpp @@ -1,12 +1,11 @@ #include "graphics/shader/recompiler/backend/spirv/SpirvBuilder.h" -#include #include namespace Libs::Graphics::ShaderRecompiler::Spirv { static constexpr size_t InitialSpirvSectionReserve = 4096; -static constexpr size_t InitialSpirvFunctionSectionReserve = 450000; +static constexpr size_t InitialSpirvFunctionSectionReserve = 32768; static void AppendInstructionWords(std::vector& section, const uint32_t* words, size_t words_num) { @@ -22,7 +21,7 @@ static void AppendInstructionWords(std::vector& section, const uint32_ Builder::Builder(uint32_t version): m_version(version) { m_debug.reserve(InitialSpirvSectionReserve); m_annotations.reserve(InitialSpirvSectionReserve); - m_types.reserve(InitialSpirvSectionReserve); + m_declarations.reserve(InitialSpirvSectionReserve); m_functions.reserve(InitialSpirvFunctionSectionReserve); } @@ -30,6 +29,110 @@ uint32_t Builder::AllocateId() { return m_next_id++; } +void Builder::RequireCapability(uint32_t capability) { + if (m_required_capabilities.insert(capability).second) { + AddCapability({capability}); + } +} + +void Builder::RequireExtension(const char* name) { + if (m_required_extensions.emplace(name).second) { + AddExtension(name); + } +} + +uint32_t Builder::Import(const char* name) { + if (const auto it = m_import_ids.find(name); it != m_import_ids.end()) { + return it->second; + } + const auto id = AllocateId(); + m_import_ids.emplace(name, id); + AddExtInstImport(id, name); + return id; +} + +uint32_t Builder::Type(uint32_t opcode, std::initializer_list operands) { + return Type(opcode, std::vector(operands)); +} + +uint32_t Builder::Type(uint32_t opcode, const std::vector& operands) { + std::vector key; + key.reserve(operands.size() + 2u); + key.push_back(opcode); + key.push_back(static_cast(operands.size())); + key.insert(key.end(), operands.begin(), operands.end()); + if (const auto it = m_declaration_ids.find(key); it != m_declaration_ids.end()) { + return it->second; + } + const auto id = AllocateId(); + m_declaration_ids.emplace(std::move(key), id); + std::vector words {opcode, id}; + words.insert(words.end(), operands.begin(), operands.end()); + AppendInstructionWords(m_declarations, words.data(), words.size()); + return id; +} + +uint32_t Builder::DecoratedType(uint32_t opcode, std::initializer_list operands, + std::initializer_list annotations) { + if (annotations.size() == 0) { + return Type(opcode, operands); + } + std::vector key {opcode, static_cast(operands.size())}; + key.insert(key.end(), operands.begin(), operands.end()); + key.push_back(static_cast(annotations.size())); + for (const auto& annotation: annotations) { + key.push_back(annotation.opcode); + key.push_back(static_cast(annotation.operands.size())); + key.insert(key.end(), annotation.operands.begin(), annotation.operands.end()); + } + if (const auto it = m_declaration_ids.find(key); it != m_declaration_ids.end()) { + return it->second; + } + const auto id = AllocateId(); + m_declaration_ids.emplace(std::move(key), id); + std::vector words {opcode, id}; + words.insert(words.end(), operands.begin(), operands.end()); + AppendInstructionWords(m_declarations, words.data(), words.size()); + for (const auto& annotation: annotations) { + words = {annotation.opcode, id}; + words.insert(words.end(), annotation.operands.begin(), annotation.operands.end()); + AppendInstructionWords(m_annotations, words.data(), words.size()); + } + return id; +} + +uint32_t Builder::Constant(uint32_t opcode, uint32_t type, + std::initializer_list operands) { + return Constant(opcode, type, std::vector(operands)); +} + +uint32_t Builder::Constant(uint32_t opcode, uint32_t type, const std::vector& operands) { + std::vector key; + key.reserve(operands.size() + 2u); + key.push_back(opcode); + key.push_back(type); + key.insert(key.end(), operands.begin(), operands.end()); + if (const auto it = m_declaration_ids.find(key); it != m_declaration_ids.end()) { + return it->second; + } + const auto id = AllocateId(); + m_declaration_ids.emplace(std::move(key), id); + std::vector words {opcode, type, id}; + words.insert(words.end(), operands.begin(), operands.end()); + AppendInstructionWords(m_declarations, words.data(), words.size()); + return id; +} + +uint32_t Builder::DefineGlobalVariable(uint32_t pointer_type, uint32_t storage_class) { + const auto id = AllocateId(); + DefineGlobalVariable(id, pointer_type, storage_class); + return id; +} + +void Builder::DefineGlobalVariable(uint32_t id, uint32_t pointer_type, uint32_t storage_class) { + AddType({59u, pointer_type, id, storage_class}); +} + void Builder::AppendString(std::vector& words, const char* text) { const auto len = text != nullptr ? std::strlen(text) : 0; const auto word_count = (len + 1u + 3u) / 4u; @@ -103,7 +206,7 @@ void Builder::AddAnnotation(std::initializer_list words) { } void Builder::AddType(std::initializer_list words) { - AppendInstructionWords(m_types, words.begin(), words.size()); + AppendInstructionWords(m_declarations, words.begin(), words.size()); } void Builder::AddFunction(std::initializer_list words) { @@ -115,27 +218,12 @@ void Builder::AddFunction(const std::vector& words) { } std::vector Builder::Build() const { - if (m_debug.size() > InitialSpirvSectionReserve) { - std::printf("SPIR-V builder reserve exceeded: section=debug size=%zu reserve=%zu\n", - m_debug.size(), InitialSpirvSectionReserve); - } - if (m_annotations.size() > InitialSpirvSectionReserve) { - std::printf("SPIR-V builder reserve exceeded: section=annotations size=%zu reserve=%zu\n", - m_annotations.size(), InitialSpirvSectionReserve); - } - if (m_types.size() > InitialSpirvSectionReserve) { - std::printf("SPIR-V builder reserve exceeded: section=types size=%zu reserve=%zu\n", - m_types.size(), InitialSpirvSectionReserve); - } - if (m_functions.size() > InitialSpirvFunctionSectionReserve) { - std::printf("SPIR-V builder reserve exceeded: section=functions size=%zu reserve=%zu\n", - m_functions.size(), InitialSpirvFunctionSectionReserve); - } std::vector module; module.reserve(5u + m_capabilities.size() + m_extensions.size() + m_ext_inst_imports.size() + m_memory_model.size() + m_entry_points.size() + m_execution_modes.size() + - m_debug.size() + m_annotations.size() + m_types.size() + m_functions.size()); + m_debug.size() + m_annotations.size() + m_declarations.size() + + m_functions.size()); module.push_back(0x07230203u); module.push_back(m_version); @@ -151,7 +239,7 @@ std::vector Builder::Build() const { module.insert(module.end(), m_execution_modes.begin(), m_execution_modes.end()); module.insert(module.end(), m_debug.begin(), m_debug.end()); module.insert(module.end(), m_annotations.begin(), m_annotations.end()); - module.insert(module.end(), m_types.begin(), m_types.end()); + module.insert(module.end(), m_declarations.begin(), m_declarations.end()); module.insert(module.end(), m_functions.begin(), m_functions.end()); return module; diff --git a/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.h b/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.h index 787865a45..663ed383b 100644 --- a/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.h +++ b/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.h @@ -4,10 +4,18 @@ #include "common/common.h" #include +#include +#include +#include #include namespace Libs::Graphics::ShaderRecompiler::Spirv { +struct TypeAnnotation { + uint32_t opcode = 0; + std::vector operands; +}; + class Builder { public: explicit Builder(uint32_t version = 0x00010300u); @@ -15,17 +23,25 @@ public: KYTY_CLASS_DEFAULT_COPY(Builder); uint32_t AllocateId(); + void RequireCapability(uint32_t capability); + void RequireExtension(const char* name); + uint32_t Import(const char* name); + uint32_t Type(uint32_t opcode, std::initializer_list operands = {}); + uint32_t Type(uint32_t opcode, const std::vector& operands); + uint32_t DecoratedType(uint32_t opcode, std::initializer_list operands, + std::initializer_list annotations); + uint32_t Constant(uint32_t opcode, uint32_t type, + std::initializer_list operands = {}); + uint32_t Constant(uint32_t opcode, uint32_t type, const std::vector& operands); + uint32_t DefineGlobalVariable(uint32_t pointer_type, uint32_t storage_class); + void DefineGlobalVariable(uint32_t id, uint32_t pointer_type, uint32_t storage_class); - void AddCapability(std::initializer_list operands); - void AddExtension(const char* name); - void AddExtInstImport(uint32_t id, const char* name); void AddMemoryModel(std::initializer_list operands); void AddEntryPoint(uint32_t execution_model, uint32_t entry_point, const char* name, const std::vector& interfaces); void AddExecutionMode(std::initializer_list operands); void AddName(uint32_t target, const char* name); void AddAnnotation(std::initializer_list words); - void AddType(std::initializer_list words); void AddFunction(std::initializer_list words); void AddFunction(const std::vector& words); @@ -37,19 +53,27 @@ private: static void AppendInstruction(std::vector& section, uint32_t opcode, std::initializer_list operands); static void AppendString(std::vector& words, const char* text); + void AddCapability(std::initializer_list operands); + void AddExtension(const char* name); + void AddExtInstImport(uint32_t id, const char* name); + void AddType(std::initializer_list words); - uint32_t m_next_id = 1; - uint32_t m_version = 0; - std::vector m_capabilities; - std::vector m_extensions; - std::vector m_ext_inst_imports; - std::vector m_memory_model; - std::vector m_entry_points; - std::vector m_execution_modes; - std::vector m_debug; - std::vector m_annotations; - std::vector m_types; - std::vector m_functions; + uint32_t m_next_id = 1; + uint32_t m_version = 0; + std::vector m_capabilities; + std::vector m_extensions; + std::vector m_ext_inst_imports; + std::vector m_memory_model; + std::vector m_entry_points; + std::vector m_execution_modes; + std::vector m_debug; + std::vector m_annotations; + std::vector m_declarations; + std::vector m_functions; + std::set m_required_capabilities; + std::set m_required_extensions; + std::map m_import_ids; + std::map, uint32_t> m_declaration_ids; }; } // namespace Libs::Graphics::ShaderRecompiler::Spirv diff --git a/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp b/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp index f56e584fd..939eaea90 100644 --- a/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp @@ -232,13 +232,10 @@ static void ReportReserveExceeded(const IR::Program& program, const char* vector InitialEmitterVectorReserve); } -IR::SpirvRequirements GetProgramRequirements(const IR::Program& program) { - if (program.spirv_requirements.has_value()) { - return *program.spirv_requirements; - } - IR::SpirvRequirements requirements; +void AnalyzeProgramRequirements(IR::Program& program) { + auto& requirements = program.spirv_requirements.emplace(); if (program.values == nullptr) { - return requirements; + return; } const auto MarkExactSubgroup = [&] { requirements.requires_exact_subgroup = true; @@ -277,7 +274,6 @@ IR::SpirvRequirements GetProgramRequirements(const IR::Program& program) { MarkExactSubgroup(); requirements.subgroup_shuffle = true; requirements.subgroup_local_invocation_id = true; - requirements.function_lds = program.stage != ShaderType::Compute; break; } case IR::ValueOpcode::DataAppend: @@ -333,7 +329,6 @@ IR::SpirvRequirements GetProgramRequirements(const IR::Program& program) { } } } - return requirements; } bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resources, @@ -347,7 +342,8 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc return false; } if (!program.srt_plan_complete || !program.resource_tracking_complete || - !program.shader_info_complete || !program.binding_layout_complete) { + !program.shader_info_complete || !program.binding_layout_complete || + !program.spirv_requirements.has_value()) { SetError(error, "SPIR-V emitter requires a fully planned native shader program"); return false; } @@ -369,7 +365,6 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc return false; } EmitterState state(program, resources, input_info); - const auto requirements = GetProgramRequirements(program); state.stage = program.stage; state.wave_size = program.wave_size; state.per_invocation_masks = program.lane_mask_mode == ShaderLaneMaskMode::PerInvocation; @@ -377,17 +372,9 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc state.outputs.reserve(InitialEmitterVectorReserve); state.interface_variables.reserve(InitialEmitterVectorReserve); CopyProgramInputsAndOutputs(state, program); - state.needs_subgroup_ballot = requirements.subgroup_ballot; - state.needs_subgroup_shuffle = requirements.subgroup_shuffle; - state.needs_subgroup_local_invocation_id = requirements.subgroup_local_invocation_id; - state.needs_compute_derivatives = requirements.compute_derivatives; - state.needs_image_gather_extended = requirements.image_gather_extended; - state.needs_function_lds = requirements.function_lds; - state.needs_pixel_valid_mask = requirements.pixel_valid_mask; AllocateInputVariables(state); AllocateOutputVariables(state); - AllocateDescriptorVariables(state); - EmitHeaderAndTypes(state); + DefineModule(state); if (!EmitValueProgram(state, value_program, error)) { return false; } diff --git a/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.h b/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.h index cf5195bd5..d5ce807e8 100644 --- a/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.h +++ b/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.h @@ -9,7 +9,7 @@ namespace Libs::Graphics::ShaderRecompiler::Spirv { -IR::SpirvRequirements GetProgramRequirements(const IR::Program& program); +void AnalyzeProgramRequirements(IR::Program& program); bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resources, ShaderStageInputInfo input_info, std::vector& spirv, std::string* error); diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAlu.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAlu.cpp index e991c2af8..774986b62 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAlu.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAlu.cpp @@ -32,14 +32,14 @@ uint32_t NewSelect(EmitterState& state, uint32_t type, uint32_t condition, uint3 Pair ExtractPair(EmitterState& state, uint32_t value) { Pair result {state.builder.AllocateId(), state.builder.AllocateId()}; - state.builder.AddFunction({OpCompositeExtract, state.uint_type, result.low, value, 0}); - state.builder.AddFunction({OpCompositeExtract, state.uint_type, result.high, value, 1}); + state.builder.AddFunction({OpCompositeExtract, TypeU32(state), result.low, value, 0}); + state.builder.AddFunction({OpCompositeExtract, TypeU32(state), result.high, value, 1}); return result; } uint32_t MakePair(EmitterState& state, uint32_t low, uint32_t high) { const auto result = state.builder.AllocateId(); - state.builder.AddFunction({OpCompositeConstruct, state.uint_pair_type, result, low, high}); + state.builder.AddFunction({OpCompositeConstruct, TypeU32Pair(state), result, low, high}); return result; } @@ -48,76 +48,76 @@ uint32_t Compare64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value, bool not_equal = false) { const auto lhs = ExtractPair(state, lhs_value); const auto rhs = ExtractPair(state, rhs_value); - const auto high_equal = NewBinary(state, OpIEqual, state.bool_type, lhs.high, rhs.high); - const auto low_equal = NewBinary(state, OpIEqual, state.bool_type, lhs.low, rhs.low); + const auto high_equal = NewBinary(state, OpIEqual, TypeBool(state), lhs.high, rhs.high); + const auto low_equal = NewBinary(state, OpIEqual, TypeBool(state), lhs.low, rhs.low); if (equal_compare) { return NewBinary( - state, not_equal ? OpLogicalOr : OpLogicalAnd, state.bool_type, - not_equal ? NewUnary(state, OpLogicalNot, state.bool_type, high_equal) : high_equal, - not_equal ? NewUnary(state, OpLogicalNot, state.bool_type, low_equal) : low_equal); + state, not_equal ? OpLogicalOr : OpLogicalAnd, TypeBool(state), + not_equal ? NewUnary(state, OpLogicalNot, TypeBool(state), high_equal) : high_equal, + not_equal ? NewUnary(state, OpLogicalNot, TypeBool(state), low_equal) : low_equal); } - const auto high_result = NewBinary(state, high_compare, state.bool_type, lhs.high, rhs.high); - const auto low_result = NewBinary(state, low_compare, state.bool_type, lhs.low, rhs.low); - const auto low_path = NewBinary(state, OpLogicalAnd, state.bool_type, high_equal, low_result); - return NewBinary(state, OpLogicalOr, state.bool_type, high_result, low_path); + const auto high_result = NewBinary(state, high_compare, TypeBool(state), lhs.high, rhs.high); + const auto low_result = NewBinary(state, low_compare, TypeBool(state), lhs.low, rhs.low); + const auto low_path = NewBinary(state, OpLogicalAnd, TypeBool(state), high_equal, low_result); + return NewBinary(state, OpLogicalOr, TypeBool(state), high_result, low_path); } uint32_t EmitAdd64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value) { const auto lhs = ExtractPair(state, lhs_value); const auto rhs = ExtractPair(state, rhs_value); const auto carry = EmitAddCarryValues(state, lhs.low, rhs.low, ConstantU32(state, 0)); - const auto high0 = NewBinary(state, OpIAdd, state.uint_type, lhs.high, rhs.high); - const auto high = NewBinary(state, OpIAdd, state.uint_type, high0, carry.carry); + const auto high0 = NewBinary(state, OpIAdd, TypeU32(state), lhs.high, rhs.high); + const auto high = NewBinary(state, OpIAdd, TypeU32(state), high0, carry.carry); return MakePair(state, carry.sum, high); } uint32_t EmitSub64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value) { const auto lhs = ExtractPair(state, lhs_value); const auto rhs = ExtractPair(state, rhs_value); - const auto low = NewBinary(state, OpISub, state.uint_type, lhs.low, rhs.low); - const auto borrow = NewBinary(state, OpULessThan, state.bool_type, lhs.low, rhs.low); + const auto low = NewBinary(state, OpISub, TypeU32(state), lhs.low, rhs.low); + const auto borrow = NewBinary(state, OpULessThan, TypeBool(state), lhs.low, rhs.low); const auto borrow_u32 = - NewSelect(state, state.uint_type, borrow, ConstantU32(state, 1), ConstantU32(state, 0)); - const auto high0 = NewBinary(state, OpISub, state.uint_type, lhs.high, rhs.high); - const auto high = NewBinary(state, OpISub, state.uint_type, high0, borrow_u32); + NewSelect(state, TypeU32(state), borrow, ConstantU32(state, 1), ConstantU32(state, 0)); + const auto high0 = NewBinary(state, OpISub, TypeU32(state), lhs.high, rhs.high); + const auto high = NewBinary(state, OpISub, TypeU32(state), high0, borrow_u32); return MakePair(state, low, high); } uint32_t EmitMulHigh(EmitterState& state, uint32_t lhs, uint32_t rhs, bool signed_value) { - const auto operand_type = signed_value ? state.int_type : state.uint_type; - const auto pair_type = signed_value ? state.int_pair_type : state.uint_pair_type; + const auto operand_type = signed_value ? TypeI32(state) : TypeU32(state); + const auto pair_type = signed_value ? TypeI32Pair(state) : TypeU32Pair(state); uint32_t lhs_operand = lhs; uint32_t rhs_operand = rhs; if (signed_value) { - lhs_operand = NewUnary(state, OpBitcast, state.int_type, lhs); - rhs_operand = NewUnary(state, OpBitcast, state.int_type, rhs); + lhs_operand = NewUnary(state, OpBitcast, TypeI32(state), lhs); + rhs_operand = NewUnary(state, OpBitcast, TypeI32(state), rhs); } const auto extended = state.builder.AllocateId(); state.builder.AddFunction({signed_value ? OpSMulExtended : OpUMulExtended, pair_type, extended, lhs_operand, rhs_operand}); const auto high = state.builder.AllocateId(); state.builder.AddFunction({OpCompositeExtract, operand_type, high, extended, 1}); - return signed_value ? NewUnary(state, OpBitcast, state.uint_type, high) : high; + return signed_value ? NewUnary(state, OpBitcast, TypeU32(state), high) : high; } uint32_t EmitMul64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value) { const auto lhs = ExtractPair(state, lhs_value); const auto rhs = ExtractPair(state, rhs_value); - const auto low = NewBinary(state, OpIMul, state.uint_type, lhs.low, rhs.low); + const auto low = NewBinary(state, OpIMul, TypeU32(state), lhs.low, rhs.low); const auto high0 = EmitMulHigh(state, lhs.low, rhs.low, false); - const auto high1 = NewBinary(state, OpIMul, state.uint_type, lhs.low, rhs.high); - const auto high2 = NewBinary(state, OpIMul, state.uint_type, lhs.high, rhs.low); + const auto high1 = NewBinary(state, OpIMul, TypeU32(state), lhs.low, rhs.high); + const auto high2 = NewBinary(state, OpIMul, TypeU32(state), lhs.high, rhs.low); return MakePair(state, low, - NewBinary(state, OpIAdd, state.uint_type, - NewBinary(state, OpIAdd, state.uint_type, high0, high1), high2)); + NewBinary(state, OpIAdd, TypeU32(state), + NewBinary(state, OpIAdd, TypeU32(state), high0, high1), high2)); } uint32_t EmitBitwise64(EmitterState& state, uint32_t opcode, uint32_t lhs_value, uint32_t rhs_value) { const auto lhs = ExtractPair(state, lhs_value); const auto rhs = ExtractPair(state, rhs_value); - return MakePair(state, NewBinary(state, opcode, state.uint_type, lhs.low, rhs.low), - NewBinary(state, opcode, state.uint_type, lhs.high, rhs.high)); + return MakePair(state, NewBinary(state, opcode, TypeU32(state), lhs.low, rhs.low), + NewBinary(state, opcode, TypeU32(state), lhs.high, rhs.high)); } uint32_t EmitShift64(EmitterState& state, uint32_t opcode, uint32_t value, uint32_t shift) { @@ -132,31 +132,31 @@ uint32_t EmitShift64(EmitterState& state, uint32_t opcode, uint32_t value, uint3 const auto logical = EmitShift64(state, OpShiftRightLogical, value, shift); const auto logical_pair = ExtractPair(state, logical); const auto sign = - NewBinary(state, OpSLessThan, state.bool_type, pair.high, ConstantU32(state, 0)); + NewBinary(state, OpSLessThan, TypeBool(state), pair.high, ConstantU32(state, 0)); const auto inverse = - NewBinary(state, OpISub, state.uint_type, ConstantU32(state, 64), shift); - const auto fill = NewBinary(state, OpShiftLeftLogical, state.uint_type, + NewBinary(state, OpISub, TypeU32(state), ConstantU32(state, 64), shift); + const auto fill = NewBinary(state, OpShiftLeftLogical, TypeU32(state), ConstantU32(state, 0xffffffffu), inverse); const auto high_shift = - NewBinary(state, OpISub, state.uint_type, shift, ConstantU32(state, 32)); + NewBinary(state, OpISub, TypeU32(state), shift, ConstantU32(state, 32)); const auto high_fill = NewSelect( - state, state.uint_type, - NewBinary(state, OpUGreaterThanEqual, state.bool_type, shift, ConstantU32(state, 32)), + state, TypeU32(state), + NewBinary(state, OpUGreaterThanEqual, TypeBool(state), shift, ConstantU32(state, 32)), ConstantU32(state, 0xffffffffu), fill); const auto fill_low = - NewSelect(state, state.uint_type, sign, - NewSelect(state, state.uint_type, - NewBinary(state, OpUGreaterThanEqual, state.bool_type, shift, + NewSelect(state, TypeU32(state), sign, + NewSelect(state, TypeU32(state), + NewBinary(state, OpUGreaterThanEqual, TypeBool(state), shift, ConstantU32(state, 32)), - NewBinary(state, OpShiftLeftLogical, state.uint_type, + NewBinary(state, OpShiftLeftLogical, TypeU32(state), ConstantU32(state, 0xffffffffu), high_shift), ConstantU32(state, 0)), ConstantU32(state, 0)); const auto fill_high = - NewSelect(state, state.uint_type, sign, high_fill, ConstantU32(state, 0)); + NewSelect(state, TypeU32(state), sign, high_fill, ConstantU32(state, 0)); return MakePair( - state, NewBinary(state, OpBitwiseOr, state.uint_type, logical_pair.low, fill_low), - NewBinary(state, OpBitwiseOr, state.uint_type, logical_pair.high, fill_high)); + state, NewBinary(state, OpBitwiseOr, TypeU32(state), logical_pair.low, fill_low), + NewBinary(state, OpBitwiseOr, TypeU32(state), logical_pair.high, fill_high)); } return MakePair(state, low, high); } @@ -166,15 +166,15 @@ uint32_t EmitFindMsb64(EmitterState& state, uint32_t value) { const auto high_i = state.builder.AllocateId(); const auto low_i = state.builder.AllocateId(); state.builder.AddFunction( - {OpExtInst, state.int_type, high_i, state.glsl_std450, GlslFindUMsb, pair.high}); + {OpExtInst, TypeI32(state), high_i, GlslStd450(state), GlslFindUMsb, pair.high}); state.builder.AddFunction( - {OpExtInst, state.int_type, low_i, state.glsl_std450, GlslFindUMsb, pair.low}); - const auto high = NewUnary(state, OpBitcast, state.uint_type, high_i); - const auto low = NewUnary(state, OpBitcast, state.uint_type, low_i); + {OpExtInst, TypeI32(state), low_i, GlslStd450(state), GlslFindUMsb, pair.low}); + const auto high = NewUnary(state, OpBitcast, TypeU32(state), high_i); + const auto low = NewUnary(state, OpBitcast, TypeU32(state), low_i); const auto high_nonzero = - NewBinary(state, OpINotEqual, state.bool_type, pair.high, ConstantU32(state, 0)); - return NewSelect(state, state.uint_type, high_nonzero, - NewBinary(state, OpIAdd, state.uint_type, high, ConstantU32(state, 32)), low); + NewBinary(state, OpINotEqual, TypeBool(state), pair.high, ConstantU32(state, 0)); + return NewSelect(state, TypeU32(state), high_nonzero, + NewBinary(state, OpIAdd, TypeU32(state), high, ConstantU32(state, 32)), low); } uint32_t EmitMinMax3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c, bool signed_value, @@ -188,10 +188,10 @@ uint32_t EmitMinMax3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c, bo uint32_t EmitMed3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c, bool signed_value) { const auto minimum = EmitMinMax3(state, a, b, c, signed_value, false); const auto maximum = EmitMinMax3(state, a, b, c, signed_value, true); - const auto ab = NewBinary(state, OpIAdd, state.uint_type, a, b); - const auto abc = NewBinary(state, OpIAdd, state.uint_type, ab, c); - return NewBinary(state, OpISub, state.uint_type, - NewBinary(state, OpISub, state.uint_type, abc, minimum), maximum); + const auto ab = NewBinary(state, OpIAdd, TypeU32(state), a, b); + const auto abc = NewBinary(state, OpIAdd, TypeU32(state), ab, c); + return NewBinary(state, OpISub, TypeU32(state), + NewBinary(state, OpISub, TypeU32(state), abc, minimum), maximum); } uint32_t EmitFMinMax3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c, bool max_value) { @@ -204,17 +204,17 @@ uint32_t EmitFMed3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c) { const auto max_ab = EmitMinMaxF32Value(state, a, b, true); const auto high_min = EmitMinMaxF32Value(state, max_ab, c, false); const auto median = EmitMinMaxF32Value(state, min_ab, high_min, true); - const auto nan_ab = NewBinary(state, OpLogicalOr, state.bool_type, + const auto nan_ab = NewBinary(state, OpLogicalOr, TypeBool(state), EmitClassifyF32(state, a).nan, EmitClassifyF32(state, b).nan); const auto any_nan = - NewBinary(state, OpLogicalOr, state.bool_type, nan_ab, EmitClassifyF32(state, c).nan); - return NewSelect(state, state.float_type, any_nan, min3, median); + NewBinary(state, OpLogicalOr, TypeBool(state), nan_ab, EmitClassifyF32(state, c).nan); + return NewSelect(state, TypeF32(state), any_nan, min3, median); } uint32_t EmitExt(EmitterState& state, uint32_t type, uint32_t opcode, std::initializer_list args) { const auto result = state.builder.AllocateId(); - std::vector words {OpExtInst, type, result, state.glsl_std450, opcode}; + std::vector words {OpExtInst, type, result, GlslStd450(state), opcode}; words.insert(words.end(), args.begin(), args.end()); state.builder.AddFunction(words); return result; @@ -225,41 +225,41 @@ uint32_t EmitF32ToU32(EmitterState& state, uint32_t src, bool signed_value) { const auto converted_raw = state.builder.AllocateId(); if (signed_value) { const auto converted_i = state.builder.AllocateId(); - state.builder.AddFunction({OpConvertFToS, state.int_type, converted_i, trunc}); - state.builder.AddFunction({OpBitcast, state.uint_type, converted_raw, converted_i}); + state.builder.AddFunction({OpConvertFToS, TypeI32(state), converted_i, trunc}); + state.builder.AddFunction({OpBitcast, TypeU32(state), converted_raw, converted_i}); } else { - state.builder.AddFunction({OpConvertFToU, state.uint_type, converted_raw, trunc}); + state.builder.AddFunction({OpConvertFToU, TypeU32(state), converted_raw, trunc}); } const auto nan = EmitClassifyF32(state, src).nan; if (signed_value) { - const auto below = NewBinary(state, OpFOrdLessThanEqual, state.bool_type, src, + const auto below = NewBinary(state, OpFOrdLessThanEqual, TypeBool(state), src, ConstantF32(state, 0xcf000000u)); - const auto above = NewBinary(state, OpFOrdGreaterThanEqual, state.bool_type, src, + const auto above = NewBinary(state, OpFOrdGreaterThanEqual, TypeBool(state), src, ConstantF32(state, 0x4f000000u)); - const auto high = NewSelect(state, state.uint_type, above, ConstantU32(state, 0x7fffffffu), - converted_raw); + const auto high = + NewSelect(state, TypeU32(state), above, ConstantU32(state, 0x7fffffffu), converted_raw); const auto low = - NewSelect(state, state.uint_type, below, ConstantU32(state, 0x80000000u), high); - return NewSelect(state, state.uint_type, nan, ConstantU32(state, 0), low); + NewSelect(state, TypeU32(state), below, ConstantU32(state, 0x80000000u), high); + return NewSelect(state, TypeU32(state), nan, ConstantU32(state, 0), low); } const auto below = - NewBinary(state, OpFOrdLessThanEqual, state.bool_type, src, ConstantF32(state, 0)); - const auto above = NewBinary(state, OpFOrdGreaterThanEqual, state.bool_type, src, + NewBinary(state, OpFOrdLessThanEqual, TypeBool(state), src, ConstantF32(state, 0)); + const auto above = NewBinary(state, OpFOrdGreaterThanEqual, TypeBool(state), src, ConstantF32(state, 0x4f800000u)); - const auto zero = NewBinary(state, OpLogicalOr, state.bool_type, nan, below); + const auto zero = NewBinary(state, OpLogicalOr, TypeBool(state), nan, below); const auto high = - NewSelect(state, state.uint_type, above, ConstantU32(state, 0xffffffffu), converted_raw); - return NewSelect(state, state.uint_type, zero, ConstantU32(state, 0), high); + NewSelect(state, TypeU32(state), above, ConstantU32(state, 0xffffffffu), converted_raw); + return NewSelect(state, TypeU32(state), zero, ConstantU32(state, 0), high); } uint32_t EmitPackHalf(EmitterState& state, uint32_t src) { - return EmitExt(state, state.uint_type, GlslPackHalf2x16, {src}); + return EmitExt(state, TypeU32(state), GlslPackHalf2x16, {src}); } uint32_t EmitUnpackHalf(EmitterState& state, uint32_t bits) { - const auto pair = EmitExt(state, state.vec2_float_type, GlslUnpackHalf2x16, {bits}); + const auto pair = EmitExt(state, TypeF32Vector(state, 2), GlslUnpackHalf2x16, {bits}); const auto result = state.builder.AllocateId(); - state.builder.AddFunction({OpCompositeExtract, state.float_type, result, pair, 0}); + state.builder.AddFunction({OpCompositeExtract, TypeF32(state), result, pair, 0}); return result; } @@ -277,7 +277,7 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) { return true; }; const auto ext_unary = [&](uint32_t ext) { - ctx.Define(inst, EmitExt(state, state.float_type, ext, {ctx.Arg(inst, 0)})); + ctx.Define(inst, EmitExt(state, TypeF32(state), ext, {ctx.Arg(inst, 0)})); return true; }; switch (op) { @@ -299,7 +299,7 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) { return true; case IR::ValueOpcode::ConvertF16F32: { const auto pair = state.builder.AllocateId(); - state.builder.AddFunction({OpCompositeConstruct, state.vec2_float_type, pair, + state.builder.AddFunction({OpCompositeConstruct, TypeF32Vector(state, 2), pair, ctx.Arg(inst, 0), ConstantF32(state, 0)}); ctx.Define(inst, EmitPackHalf(state, pair)); return true; @@ -314,7 +314,7 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) { ctx.Define(inst, EmitF32ToU32(state, ctx.Arg(inst, 0), false)); return true; case IR::ValueOpcode::ConvertF32S32: { - const auto signed_value = NewUnary(state, OpBitcast, state.int_type, ctx.Arg(inst, 0)); + const auto signed_value = NewUnary(state, OpBitcast, TypeI32(state), ctx.Arg(inst, 0)); ctx.Emit(inst, OpConvertSToF, IR::Type::F32, {signed_value}); return true; } @@ -337,22 +337,20 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) { {ctx.Arg(inst, 0), inst.Arg(1).U32()}); return true; case IR::ValueOpcode::PackHalf2x16: - ctx.Define(inst, EmitExt(state, state.uint_type, GlslPackHalf2x16, {ctx.Arg(inst, 0)})); + ctx.Define(inst, EmitExt(state, TypeU32(state), GlslPackHalf2x16, {ctx.Arg(inst, 0)})); return true; case IR::ValueOpcode::PackSnorm2x16: - ctx.Define(inst, - EmitExt(state, state.uint_type, GlslPackSnorm2x16, {ctx.Arg(inst, 0)})); + ctx.Define(inst, EmitExt(state, TypeU32(state), GlslPackSnorm2x16, {ctx.Arg(inst, 0)})); return true; case IR::ValueOpcode::PackUnorm2x16: - ctx.Define(inst, - EmitExt(state, state.uint_type, GlslPackUnorm2x16, {ctx.Arg(inst, 0)})); + ctx.Define(inst, EmitExt(state, TypeU32(state), GlslPackUnorm2x16, {ctx.Arg(inst, 0)})); return true; case IR::ValueOpcode::PackFloat2x16Rtz: { const auto low = EmitF32ToF16RtzBits(state, ctx.Arg(inst, 0)); const auto high = - NewBinary(state, OpShiftLeftLogical, state.uint_type, + NewBinary(state, OpShiftLeftLogical, TypeU32(state), EmitF32ToF16RtzBits(state, ctx.Arg(inst, 1)), ConstantU32(state, 16)); - ctx.Define(inst, NewBinary(state, OpBitwiseOr, state.uint_type, low, high)); + ctx.Define(inst, NewBinary(state, OpBitwiseOr, TypeU32(state), low, high)); return true; } case IR::ValueOpcode::FPAbs32: @@ -362,7 +360,7 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) { ctx.Define(inst, EmitFNegateValue(state, ctx.Arg(inst, 0))); return true; case IR::ValueOpcode::FPSaturate32: - ctx.Define(inst, EmitExt(state, state.float_type, GlslFClamp, + ctx.Define(inst, EmitExt(state, TypeF32(state), GlslFClamp, {ctx.Arg(inst, 0), ConstantF32(state, 0), ConstantF32(state, 0x3f800000u)})); return true; @@ -414,10 +412,10 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) { return true; case IR::ValueOpcode::IAbs32: { const auto value = ctx.Arg(inst, 0); - const auto neg = NewUnary(state, OpSNegate, state.uint_type, value); + const auto neg = NewUnary(state, OpSNegate, TypeU32(state), value); const auto negative = - NewBinary(state, OpSLessThan, state.bool_type, value, ConstantU32(state, 0)); - ctx.Define(inst, NewSelect(state, state.uint_type, negative, neg, value)); + NewBinary(state, OpSLessThan, TypeBool(state), value, ConstantU32(state, 0)); + ctx.Define(inst, NewSelect(state, TypeU32(state), negative, neg, value)); return true; } case IR::ValueOpcode::ShiftLeftLogical32: return binary(OpShiftLeftLogical, IR::Type::U32); @@ -452,19 +450,19 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) { case IR::ValueOpcode::BitCount32: return unary(OpBitCount, IR::Type::U32); case IR::ValueOpcode::BitCount64: { const auto pair = ExtractPair(state, ctx.Arg(inst, 0)); - ctx.Define(inst, NewBinary(state, OpIAdd, state.uint_type, - NewUnary(state, OpBitCount, state.uint_type, pair.low), - NewUnary(state, OpBitCount, state.uint_type, pair.high))); + ctx.Define(inst, NewBinary(state, OpIAdd, TypeU32(state), + NewUnary(state, OpBitCount, TypeU32(state), pair.low), + NewUnary(state, OpBitCount, TypeU32(state), pair.high))); return true; } case IR::ValueOpcode::FindILsb32: { - const auto value = EmitExt(state, state.int_type, GlslFindILsb, {ctx.Arg(inst, 0)}); - ctx.Define(inst, NewUnary(state, OpBitcast, state.uint_type, value)); + const auto value = EmitExt(state, TypeI32(state), GlslFindILsb, {ctx.Arg(inst, 0)}); + ctx.Define(inst, NewUnary(state, OpBitcast, TypeU32(state), value)); return true; } case IR::ValueOpcode::FindUMsb32: { - const auto value = EmitExt(state, state.int_type, GlslFindUMsb, {ctx.Arg(inst, 0)}); - ctx.Define(inst, NewUnary(state, OpBitcast, state.uint_type, value)); + const auto value = EmitExt(state, TypeI32(state), GlslFindUMsb, {ctx.Arg(inst, 0)}); + ctx.Define(inst, NewUnary(state, OpBitcast, TypeU32(state), value)); return true; } case IR::ValueOpcode::FindUMsb64: @@ -472,9 +470,9 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) { return true; case IR::ValueOpcode::FindSMsb32: { const auto value = - EmitExt(state, state.int_type, 74u, - {NewUnary(state, OpBitcast, state.int_type, ctx.Arg(inst, 0))}); - ctx.Define(inst, NewUnary(state, OpBitcast, state.uint_type, value)); + EmitExt(state, TypeI32(state), 74u, + {NewUnary(state, OpBitcast, TypeI32(state), ctx.Arg(inst, 0))}); + ctx.Define(inst, NewUnary(state, OpBitcast, TypeU32(state), value)); return true; } case IR::ValueOpcode::SMin32: @@ -550,25 +548,25 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) { case IR::ValueOpcode::ULessThanEqual64: { const auto greater = Compare64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), OpUGreaterThan, OpUGreaterThan); - ctx.Define(inst, NewUnary(state, OpLogicalNot, state.bool_type, greater)); + ctx.Define(inst, NewUnary(state, OpLogicalNot, TypeBool(state), greater)); return true; } case IR::ValueOpcode::SLessThanEqual64: { const auto greater = Compare64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), OpSGreaterThan, OpUGreaterThan); - ctx.Define(inst, NewUnary(state, OpLogicalNot, state.bool_type, greater)); + ctx.Define(inst, NewUnary(state, OpLogicalNot, TypeBool(state), greater)); return true; } case IR::ValueOpcode::UGreaterThanEqual64: { const auto less = Compare64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), OpULessThan, OpULessThan); - ctx.Define(inst, NewUnary(state, OpLogicalNot, state.bool_type, less)); + ctx.Define(inst, NewUnary(state, OpLogicalNot, TypeBool(state), less)); return true; } case IR::ValueOpcode::SGreaterThanEqual64: { const auto less = Compare64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), OpSLessThan, OpULessThan); - ctx.Define(inst, NewUnary(state, OpLogicalNot, state.bool_type, less)); + ctx.Define(inst, NewUnary(state, OpLogicalNot, TypeBool(state), less)); return true; } case IR::ValueOpcode::LogicalOr: return binary(OpLogicalOr, IR::Type::U1); @@ -602,7 +600,7 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) { case IR::ValueOpcode::FPSub32: return binary(OpFSub, IR::Type::F32); case IR::ValueOpcode::FPMul32: return binary(OpFMul, IR::Type::F32); case IR::ValueOpcode::FPFma32: - ctx.Define(inst, EmitExt(state, state.float_type, GlslFma, + ctx.Define(inst, EmitExt(state, TypeF32(state), GlslFma, {ctx.Arg(inst, 0), ctx.Arg(inst, 1), ctx.Arg(inst, 2)})); return true; case IR::ValueOpcode::FPMin32: @@ -625,44 +623,44 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) { return true; case IR::ValueOpcode::FPRecip32: { const auto source = EmitFlushF32DenormToSignedZero(state, ctx.Arg(inst, 0)); - ctx.Define(inst, NewBinary(state, OpFDiv, state.float_type, + ctx.Define(inst, NewBinary(state, OpFDiv, TypeF32(state), ConstantF32(state, 0x3f800000u), source)); return true; } case IR::ValueOpcode::FPRecipIFlag32: // Integer-to-float inputs used by IFLAG cannot be denormal. - ctx.Define(inst, NewBinary(state, OpFDiv, state.float_type, + ctx.Define(inst, NewBinary(state, OpFDiv, TypeF32(state), ConstantF32(state, 0x3f800000u), ctx.Arg(inst, 0))); return true; case IR::ValueOpcode::FPRecipSqrt32: - ctx.Define(inst, EmitExt(state, state.float_type, GlslInverseSqrt, + ctx.Define(inst, EmitExt(state, TypeF32(state), GlslInverseSqrt, {EmitFlushF32DenormToSignedZero(state, ctx.Arg(inst, 0))})); return true; case IR::ValueOpcode::FPSqrt: - ctx.Define(inst, EmitExt(state, state.float_type, GlslSqrt, + ctx.Define(inst, EmitExt(state, TypeF32(state), GlslSqrt, {EmitFlushF32DenormToSignedZero(state, ctx.Arg(inst, 0))})); return true; case IR::ValueOpcode::FPSin: case IR::ValueOpcode::FPCos: { auto source = EmitTrigCycleF32(state, ctx.Arg(inst, 0), op == IR::ValueOpcode::FPSin); source = - NewBinary(state, OpFMul, state.float_type, source, ConstantF32(state, 0x40c90fdbu)); - ctx.Define(inst, EmitExt(state, state.float_type, + NewBinary(state, OpFMul, TypeF32(state), source, ConstantF32(state, 0x40c90fdbu)); + ctx.Define(inst, EmitExt(state, TypeF32(state), op == IR::ValueOpcode::FPSin ? GlslSin : GlslCos, {source})); return true; } case IR::ValueOpcode::FPExp2: - ctx.Define(inst, EmitExt(state, state.float_type, GlslExp2, + ctx.Define(inst, EmitExt(state, TypeF32(state), GlslExp2, {EmitFlushF32DenormToSignedZero(state, ctx.Arg(inst, 0))})); return true; case IR::ValueOpcode::FPLog2: - ctx.Define(inst, EmitExt(state, state.float_type, GlslLog2, + ctx.Define(inst, EmitExt(state, TypeF32(state), GlslLog2, {EmitFlushF32DenormToSignedZero(state, ctx.Arg(inst, 0))})); return true; case IR::ValueOpcode::FPLdexp: { - const auto exponent = NewUnary(state, OpBitcast, state.int_type, ctx.Arg(inst, 1)); + const auto exponent = NewUnary(state, OpBitcast, TypeI32(state), ctx.Arg(inst, 1)); ctx.Define(inst, - EmitExt(state, state.float_type, GlslLdexp, {ctx.Arg(inst, 0), exponent})); + EmitExt(state, TypeF32(state), GlslLdexp, {ctx.Arg(inst, 0), exponent})); return true; } case IR::ValueOpcode::FPRoundEven32: return ext_unary(GlslRoundEven); diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAluHelpers.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAluHelpers.cpp index efd67f910..920b42328 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAluHelpers.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAluHelpers.cpp @@ -5,7 +5,7 @@ namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter { uint32_t EmitSelectValueU32(EmitterState& state, uint32_t cond, uint32_t true_value, uint32_t false_value) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({OpSelect, state.uint_type, ret, cond, true_value, false_value}); + state.builder.AddFunction({OpSelect, TypeU32(state), ret, cond, true_value, false_value}); return ret; } @@ -26,20 +26,20 @@ void EmitShiftLeftLogicalU64Values(EmitterState& state, uint32_t low, uint32_t h const auto high_less = state.builder.AllocateId(); const auto high_ge = state.builder.AllocateId(); state.builder.AddFunction( - {OpULessThan, state.bool_type, less32, amount, ConstantU32(state, 32)}); + {OpULessThan, TypeBool(state), less32, amount, ConstantU32(state, 32)}); state.builder.AddFunction( - {OpINotEqual, state.bool_type, non_zero, amount, ConstantU32(state, 0)}); + {OpINotEqual, TypeBool(state), non_zero, amount, ConstantU32(state, 0)}); state.builder.AddFunction( - {OpISub, state.uint_type, carry_count_base, ConstantU32(state, 32), dword_shift}); + {OpISub, TypeU32(state), carry_count_base, ConstantU32(state, 32), dword_shift}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, carry_count, carry_count_base, ConstantU32(state, 31)}); - state.builder.AddFunction({OpShiftLeftLogical, state.uint_type, low_part, low, dword_shift}); - state.builder.AddFunction({OpShiftLeftLogical, state.uint_type, high_part, high, dword_shift}); - state.builder.AddFunction({OpShiftRightLogical, state.uint_type, carry_part, low, carry_count}); + {OpBitwiseAnd, TypeU32(state), carry_count, carry_count_base, ConstantU32(state, 31)}); + state.builder.AddFunction({OpShiftLeftLogical, TypeU32(state), low_part, low, dword_shift}); + state.builder.AddFunction({OpShiftLeftLogical, TypeU32(state), high_part, high, dword_shift}); + state.builder.AddFunction({OpShiftRightLogical, TypeU32(state), carry_part, low, carry_count}); state.builder.AddFunction( - {OpSelect, state.uint_type, carry, non_zero, carry_part, ConstantU32(state, 0)}); - state.builder.AddFunction({OpBitwiseOr, state.uint_type, high_less, high_part, carry}); - state.builder.AddFunction({OpShiftLeftLogical, state.uint_type, high_ge, low, dword_shift}); + {OpSelect, TypeU32(state), carry, non_zero, carry_part, ConstantU32(state, 0)}); + state.builder.AddFunction({OpBitwiseOr, TypeU32(state), high_less, high_part, carry}); + state.builder.AddFunction({OpShiftLeftLogical, TypeU32(state), high_ge, low, dword_shift}); out_low = EmitSelectValueU32(state, less32, low_part, ConstantU32(state, 0)); out_high = EmitSelectValueU32(state, less32, high_less, high_ge); } @@ -59,28 +59,27 @@ void EmitShiftRightLogicalU64Values(EmitterState& state, uint32_t low, uint32_t const auto low_less = state.builder.AllocateId(); const auto low_ge = state.builder.AllocateId(); state.builder.AddFunction( - {OpULessThan, state.bool_type, less32, amount, ConstantU32(state, 32)}); + {OpULessThan, TypeBool(state), less32, amount, ConstantU32(state, 32)}); state.builder.AddFunction( - {OpINotEqual, state.bool_type, non_zero, amount, ConstantU32(state, 0)}); + {OpINotEqual, TypeBool(state), non_zero, amount, ConstantU32(state, 0)}); state.builder.AddFunction( - {OpISub, state.uint_type, carry_count_base, ConstantU32(state, 32), dword_shift}); + {OpISub, TypeU32(state), carry_count_base, ConstantU32(state, 32), dword_shift}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, carry_count, carry_count_base, ConstantU32(state, 31)}); - state.builder.AddFunction({OpShiftRightLogical, state.uint_type, low_part, low, dword_shift}); - state.builder.AddFunction({OpShiftRightLogical, state.uint_type, high_part, high, dword_shift}); - state.builder.AddFunction({OpShiftLeftLogical, state.uint_type, carry_part, high, carry_count}); + {OpBitwiseAnd, TypeU32(state), carry_count, carry_count_base, ConstantU32(state, 31)}); + state.builder.AddFunction({OpShiftRightLogical, TypeU32(state), low_part, low, dword_shift}); + state.builder.AddFunction({OpShiftRightLogical, TypeU32(state), high_part, high, dword_shift}); + state.builder.AddFunction({OpShiftLeftLogical, TypeU32(state), carry_part, high, carry_count}); state.builder.AddFunction( - {OpSelect, state.uint_type, carry, non_zero, carry_part, ConstantU32(state, 0)}); - state.builder.AddFunction({OpBitwiseOr, state.uint_type, low_less, low_part, carry}); - state.builder.AddFunction({OpShiftRightLogical, state.uint_type, low_ge, high, dword_shift}); + {OpSelect, TypeU32(state), carry, non_zero, carry_part, ConstantU32(state, 0)}); + state.builder.AddFunction({OpBitwiseOr, TypeU32(state), low_less, low_part, carry}); + state.builder.AddFunction({OpShiftRightLogical, TypeU32(state), low_ge, high, dword_shift}); out_low = EmitSelectValueU32(state, less32, low_less, low_ge); out_high = EmitSelectValueU32(state, less32, high_part, ConstantU32(state, 0)); } uint32_t EmitAndConstant(EmitterState& state, uint32_t value, uint32_t mask) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, ret, value, ConstantU32(state, mask)}); + state.builder.AddFunction({OpBitwiseAnd, TypeU32(state), ret, value, ConstantU32(state, mask)}); return ret; } @@ -93,45 +92,45 @@ AddCarryResult EmitAddCarryValues(EmitterState& state, uint32_t lhs, uint32_t rh const auto sum1 = state.builder.AllocateId(); const auto carry1 = state.builder.AllocateId(); const auto carry = state.builder.AllocateId(); - state.builder.AddFunction({OpIAddCarry, state.uint_pair_type, pair0, lhs, rhs}); - state.builder.AddFunction({OpCompositeExtract, state.uint_type, sum0, pair0, 0}); - state.builder.AddFunction({OpCompositeExtract, state.uint_type, carry0, pair0, 1}); - state.builder.AddFunction({OpIAddCarry, state.uint_pair_type, pair1, sum0, carry_in}); - state.builder.AddFunction({OpCompositeExtract, state.uint_type, sum1, pair1, 0}); - state.builder.AddFunction({OpCompositeExtract, state.uint_type, carry1, pair1, 1}); - state.builder.AddFunction({OpBitwiseOr, state.uint_type, carry, carry0, carry1}); + state.builder.AddFunction({OpIAddCarry, TypeU32Pair(state), pair0, lhs, rhs}); + state.builder.AddFunction({OpCompositeExtract, TypeU32(state), sum0, pair0, 0}); + state.builder.AddFunction({OpCompositeExtract, TypeU32(state), carry0, pair0, 1}); + state.builder.AddFunction({OpIAddCarry, TypeU32Pair(state), pair1, sum0, carry_in}); + state.builder.AddFunction({OpCompositeExtract, TypeU32(state), sum1, pair1, 0}); + state.builder.AddFunction({OpCompositeExtract, TypeU32(state), carry1, pair1, 1}); + state.builder.AddFunction({OpBitwiseOr, TypeU32(state), carry, carry0, carry1}); return {sum1, carry}; } uint32_t EmitShiftRightConstant(EmitterState& state, uint32_t value, uint32_t shift) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction( - {OpShiftRightLogical, state.uint_type, ret, value, ConstantU32(state, shift)}); + {OpShiftRightLogical, TypeU32(state), ret, value, ConstantU32(state, shift)}); return ret; } uint32_t EmitOrU32(EmitterState& state, uint32_t lhs, uint32_t rhs) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({OpBitwiseOr, state.uint_type, ret, lhs, rhs}); + state.builder.AddFunction({OpBitwiseOr, TypeU32(state), ret, lhs, rhs}); return ret; } uint32_t EmitCompareU32Constant(EmitterState& state, uint32_t opcode, uint32_t value, uint32_t constant) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({opcode, state.bool_type, ret, value, ConstantU32(state, constant)}); + state.builder.AddFunction({opcode, TypeBool(state), ret, value, ConstantU32(state, constant)}); return ret; } uint32_t EmitSubConstantMinusU32(EmitterState& state, uint32_t constant, uint32_t value) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({OpISub, state.uint_type, ret, ConstantU32(state, constant), value}); + state.builder.AddFunction({OpISub, TypeU32(state), ret, ConstantU32(state, constant), value}); return ret; } uint32_t EmitF32ToF16RtzBits(EmitterState& state, uint32_t f32) { const auto bits = state.builder.AllocateId(); - state.builder.AddFunction({OpBitcast, state.uint_type, bits, f32}); + state.builder.AddFunction({OpBitcast, TypeU32(state), bits, f32}); const auto sign = EmitAndConstant(state, EmitShiftRightConstant(state, bits, 16), 0x8000u); const auto exp = EmitAndConstant(state, EmitShiftRightConstant(state, bits, 23), 0xffu); @@ -142,12 +141,12 @@ uint32_t EmitF32ToF16RtzBits(EmitterState& state, uint32_t f32) { const auto normal_mant = EmitShiftRightConstant(state, mant, 13); const auto normal_payload = state.builder.AllocateId(); const auto normal = state.builder.AllocateId(); - state.builder.AddFunction({OpISub, state.uint_type, half_exp, exp, ConstantU32(state, 112)}); + state.builder.AddFunction({OpISub, TypeU32(state), half_exp, exp, ConstantU32(state, 112)}); state.builder.AddFunction( - {OpShiftLeftLogical, state.uint_type, normal_exp, half_exp, ConstantU32(state, 10)}); + {OpShiftLeftLogical, TypeU32(state), normal_exp, half_exp, ConstantU32(state, 10)}); state.builder.AddFunction( - {OpBitwiseOr, state.uint_type, normal_payload, normal_exp, normal_mant}); - state.builder.AddFunction({OpBitwiseOr, state.uint_type, normal, sign, normal_payload}); + {OpBitwiseOr, TypeU32(state), normal_payload, normal_exp, normal_mant}); + state.builder.AddFunction({OpBitwiseOr, TypeU32(state), normal, sign, normal_payload}); const auto mant_with_hidden = EmitOrU32(state, mant, ConstantU32(state, 0x00800000u)); const auto raw_sub_shift = EmitSubConstantMinusU32(state, 126, exp); @@ -160,8 +159,8 @@ uint32_t EmitF32ToF16RtzBits(EmitterState& state, uint32_t f32) { const auto sub_mant = state.builder.AllocateId(); const auto subnormal = state.builder.AllocateId(); state.builder.AddFunction( - {OpShiftRightLogical, state.uint_type, sub_mant, mant_with_hidden, sub_shift}); - state.builder.AddFunction({OpBitwiseOr, state.uint_type, subnormal, sign, sub_mant}); + {OpShiftRightLogical, TypeU32(state), sub_mant, mant_with_hidden, sub_shift}); + state.builder.AddFunction({OpBitwiseOr, TypeU32(state), subnormal, sign, sub_mant}); const auto nan_payload = EmitOrU32(state, EmitShiftRightConstant(state, mant, 13), ConstantU32(state, 0x0200u)); @@ -185,8 +184,8 @@ uint32_t EmitMinMaxU32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, boo const auto cond = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction( - {max_value ? OpUGreaterThan : OpULessThan, state.bool_type, cond, lhs, rhs}); - state.builder.AddFunction({OpSelect, state.uint_type, ret, cond, lhs, rhs}); + {max_value ? OpUGreaterThan : OpULessThan, TypeBool(state), cond, lhs, rhs}); + state.builder.AddFunction({OpSelect, TypeU32(state), ret, cond, lhs, rhs}); return ret; } @@ -194,44 +193,44 @@ uint32_t EmitMinMaxI32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, boo const auto cond = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction( - {max_value ? OpSGreaterThan : OpSLessThan, state.bool_type, cond, lhs, rhs}); - state.builder.AddFunction({OpSelect, state.uint_type, ret, cond, lhs, rhs}); + {max_value ? OpSGreaterThan : OpSLessThan, TypeBool(state), cond, lhs, rhs}); + state.builder.AddFunction({OpSelect, TypeU32(state), ret, cond, lhs, rhs}); return ret; } uint32_t EmitBitcastF32ToU32(EmitterState& state, uint32_t value) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({OpBitcast, state.uint_type, ret, value}); + state.builder.AddFunction({OpBitcast, TypeU32(state), ret, value}); return ret; } uint32_t EmitBitcastU32ToF32(EmitterState& state, uint32_t value) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({OpBitcast, state.float_type, ret, value}); + state.builder.AddFunction({OpBitcast, TypeF32(state), ret, value}); return ret; } uint32_t EmitAndU32(EmitterState& state, uint32_t lhs, uint32_t rhs) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({OpBitwiseAnd, state.uint_type, ret, lhs, rhs}); + state.builder.AddFunction({OpBitwiseAnd, TypeU32(state), ret, lhs, rhs}); return ret; } uint32_t EmitLogicalAndBool(EmitterState& state, uint32_t lhs, uint32_t rhs) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({OpLogicalAnd, state.bool_type, ret, lhs, rhs}); + state.builder.AddFunction({OpLogicalAnd, TypeBool(state), ret, lhs, rhs}); return ret; } uint32_t EmitLogicalOrBool(EmitterState& state, uint32_t lhs, uint32_t rhs) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({OpLogicalOr, state.bool_type, ret, lhs, rhs}); + state.builder.AddFunction({OpLogicalOr, TypeBool(state), ret, lhs, rhs}); return ret; } uint32_t EmitLogicalNotBool(EmitterState& state, uint32_t value) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({OpLogicalNot, state.bool_type, ret, value}); + state.builder.AddFunction({OpLogicalNot, TypeBool(state), ret, value}); return ret; } @@ -316,7 +315,7 @@ uint32_t EmitMinMaxF32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, boo const auto rhs_class = EmitClassifyF32(state, rhs); const auto numeric_cond = state.builder.AllocateId(); - state.builder.AddFunction({max_value ? OpFOrdGreaterThanEqual : OpFOrdLessThan, state.bool_type, + state.builder.AddFunction({max_value ? OpFOrdGreaterThanEqual : OpFOrdLessThan, TypeBool(state), numeric_cond, lhs, rhs}); const auto ordered_bits = EmitSelectValueU32(state, numeric_cond, lhs_class.bits, rhs_class.bits); @@ -340,7 +339,7 @@ uint32_t EmitMinMaxF32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, boo uint32_t EmitTruncF32Value(EmitterState& state, uint32_t value) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction( - {OpExtInst, state.float_type, ret, state.glsl_std450, GlslTrunc, value}); + {OpExtInst, TypeF32(state), ret, GlslStd450(state), GlslTrunc, value}); return ret; } @@ -353,18 +352,18 @@ uint32_t EmitFlushF32DenormToSignedZero(EmitterState& state, uint32_t value) { const auto flush = state.builder.AllocateId(); const auto selected = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({OpBitcast, state.uint_type, bits, value}); + state.builder.AddFunction({OpBitcast, TypeU32(state), bits, value}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, abs_bits, bits, ConstantU32(state, 0x7fffffffu)}); + {OpBitwiseAnd, TypeU32(state), abs_bits, bits, ConstantU32(state, 0x7fffffffu)}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, sign_bits, bits, ConstantU32(state, 0x80000000u)}); + {OpBitwiseAnd, TypeU32(state), sign_bits, bits, ConstantU32(state, 0x80000000u)}); state.builder.AddFunction( - {OpINotEqual, state.bool_type, non_zero, abs_bits, ConstantU32(state, 0)}); + {OpINotEqual, TypeBool(state), non_zero, abs_bits, ConstantU32(state, 0)}); state.builder.AddFunction( - {OpULessThan, state.bool_type, subnormal, abs_bits, ConstantU32(state, 0x00800000u)}); - state.builder.AddFunction({OpLogicalAnd, state.bool_type, flush, non_zero, subnormal}); - state.builder.AddFunction({OpSelect, state.uint_type, selected, flush, sign_bits, bits}); - state.builder.AddFunction({OpBitcast, state.float_type, ret, selected}); + {OpULessThan, TypeBool(state), subnormal, abs_bits, ConstantU32(state, 0x00800000u)}); + state.builder.AddFunction({OpLogicalAnd, TypeBool(state), flush, non_zero, subnormal}); + state.builder.AddFunction({OpSelect, TypeU32(state), selected, flush, sign_bits, bits}); + state.builder.AddFunction({OpBitcast, TypeF32(state), ret, selected}); return ret; } @@ -377,46 +376,45 @@ uint32_t EmitTrigCycleF32(EmitterState& state, uint32_t src, bool preserve_signe const auto large_finite = state.builder.AllocateId(); const auto reduced = state.builder.AllocateId(); state.builder.AddFunction( - {OpExtInst, state.float_type, fract, state.glsl_std450, GlslFract, src}); - state.builder.AddFunction({OpBitcast, state.uint_type, bits, src}); + {OpExtInst, TypeF32(state), fract, GlslStd450(state), GlslFract, src}); + state.builder.AddFunction({OpBitcast, TypeU32(state), bits, src}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, abs_bits, bits, ConstantU32(state, 0x7fffffffu)}); + {OpBitwiseAnd, TypeU32(state), abs_bits, bits, ConstantU32(state, 0x7fffffffu)}); state.builder.AddFunction( - {OpUGreaterThanEqual, state.bool_type, large, abs_bits, ConstantU32(state, 0x4b000000u)}); + {OpUGreaterThanEqual, TypeBool(state), large, abs_bits, ConstantU32(state, 0x4b000000u)}); state.builder.AddFunction( - {OpULessThan, state.bool_type, finite, abs_bits, ConstantU32(state, 0x7f800000u)}); - state.builder.AddFunction({OpLogicalAnd, state.bool_type, large_finite, large, finite}); + {OpULessThan, TypeBool(state), finite, abs_bits, ConstantU32(state, 0x7f800000u)}); + state.builder.AddFunction({OpLogicalAnd, TypeBool(state), large_finite, large, finite}); state.builder.AddFunction( - {OpSelect, state.float_type, reduced, large_finite, ConstantF32(state, 0), fract}); + {OpSelect, TypeF32(state), reduced, large_finite, ConstantF32(state, 0), fract}); if (!preserve_signed_zero) { return reduced; } const auto zero = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({OpIEqual, state.bool_type, zero, abs_bits, ConstantU32(state, 0)}); - state.builder.AddFunction({OpSelect, state.float_type, ret, zero, src, reduced}); + state.builder.AddFunction({OpIEqual, TypeBool(state), zero, abs_bits, ConstantU32(state, 0)}); + state.builder.AddFunction({OpSelect, TypeF32(state), ret, zero, src, reduced}); return ret; } uint32_t EmitFNegateValue(EmitterState& state, uint32_t value) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({OpFNegate, state.float_type, ret, value}); + state.builder.AddFunction({OpFNegate, TypeF32(state), ret, value}); return ret; } uint32_t EmitFAbsValue(EmitterState& state, uint32_t value) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction( - {OpExtInst, state.float_type, ret, state.glsl_std450, GlslFAbs, value}); + state.builder.AddFunction({OpExtInst, TypeF32(state), ret, GlslStd450(state), GlslFAbs, value}); return ret; } uint32_t EmitF16BitsToF32(EmitterState& state, uint32_t bits) { const auto unpacked = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); - state.builder.AddFunction( - {OpExtInst, state.vec2_float_type, unpacked, state.glsl_std450, GlslUnpackHalf2x16, bits}); - state.builder.AddFunction({OpCompositeExtract, state.float_type, ret, unpacked, 0}); + state.builder.AddFunction({OpExtInst, TypeF32Vector(state, 2), unpacked, GlslStd450(state), + GlslUnpackHalf2x16, bits}); + state.builder.AddFunction({OpCompositeExtract, TypeF32(state), ret, unpacked, 0}); return ret; } diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAnalysis.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAnalysis.cpp index cced2c455..5cbe5d497 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAnalysis.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAnalysis.cpp @@ -168,45 +168,54 @@ uint32_t ImageViewSpatialComponents(ImageViewKind view) { } } -uint32_t ImageViewImageType(const EmitterState& state, ImageViewKind view, bool integer) { - return state.sampled_images[SampledImageIndex(integer, view)].image_type; +uint32_t ImageViewImageType(EmitterState& state, ImageViewKind view, bool integer) { + const auto component = integer ? TypeU32(state) : TypeF32(state); + return state.builder.Type(OpTypeImage, + {component, ImageSpirvDimension(view), 0, ImageSpirvArrayed(view), + ImageSpirvMultisampled(view), 1, ImageFormatUnknown}); } -uint32_t ImageViewSampledImageType(const EmitterState& state, ImageViewKind view, bool integer) { - return state.sampled_images[SampledImageIndex(integer, view)].sampled_image_type; +uint32_t ImageViewSampledImageType(EmitterState& state, ImageViewKind view, bool integer) { + return state.builder.Type(OpTypeSampledImage, {ImageViewImageType(state, view, integer)}); } -uint32_t ImageViewSizeType(const EmitterState& state, ImageViewKind view) { +uint32_t ImageViewSizeType(EmitterState& state, ImageViewKind view) { switch (ImageViewCoordinateComponents(view)) { - case 1u: return state.uint_type; - case 2u: return state.vec2_uint_type; - case 3u: return state.vec3_uint_type; + case 1u: return TypeU32(state); + case 2u: return TypeU32Vector(state, 2); + case 3u: return TypeU32Vector(state, 3); default: return 0; } } -uint32_t StorageImageType(const EmitterState& state, bool uint_image, ImageViewKind view) { - return state.storage_images[StorageImageIndex(uint_image, view)].image_type; +uint32_t StorageImageType(EmitterState& state, bool uint_image, ImageViewKind view) { + const auto component = uint_image ? TypeU32(state) : TypeF32(state); + const auto format = uint_image ? ImageFormatR32ui : ImageFormatUnknown; + return state.builder.Type(OpTypeImage, {component, ImageSpirvDimension(view), 0, + ImageSpirvArrayed(view), 0, 2, format}); } -uint32_t StorageImagePointerType(const EmitterState& state, bool uint_image, ImageViewKind view) { - return state.storage_images[StorageImageIndex(uint_image, view)].pointer_type; +uint32_t StorageImagePointerType(EmitterState& state, bool uint_image, ImageViewKind view) { + return state.builder.Type( + OpTypePointer, {StorageClassUniformConstant, StorageImageType(state, uint_image, view)}); } uint32_t StorageImageVariable(const EmitterState& state, bool uint_image, ImageViewKind view) { - return state.storage_images[StorageImageIndex(uint_image, view)].variable; + return state.storage_image_variables[StorageImageIndex(uint_image, view)]; } uint32_t LoadSampledImageDescriptor(EmitterState& state, const IR::MemoryInfo& mem, uint32_t use_pc, ImageViewKind view) { (void)use_pc; - const bool integer = mem.kind == IR::ResourceKind::ImageUint; - const auto kind = SampledBindingKind(integer, view); - const auto binding = ResourceForDescriptor(state, kind, mem.resource); - const auto& descriptors = state.sampled_images[SampledImageIndex(integer, view)]; - const auto pointer = DescriptorElementPointer( - state, descriptors.pointer_type, descriptors.variable, binding.array_index, kind, - mem.resource, "sampled image descriptor array was not emitted"); + const bool integer = mem.kind == IR::ResourceKind::ImageUint; + const auto kind = SampledBindingKind(integer, view); + const auto binding = ResourceForDescriptor(state, kind, mem.resource); + const auto variable = state.sampled_image_variables[SampledImageIndex(integer, view)]; + const auto pointer_type = state.builder.Type( + OpTypePointer, {StorageClassUniformConstant, ImageViewImageType(state, view, integer)}); + const auto pointer = + DescriptorElementPointer(state, pointer_type, variable, binding.array_index, kind, + mem.resource, "sampled image descriptor array was not emitted"); const auto image = state.builder.AllocateId(); state.builder.AddFunction({OpLoad, ImageViewImageType(state, view, integer), image, pointer}); return image; @@ -215,11 +224,14 @@ uint32_t LoadSampledImageDescriptor(EmitterState& state, const IR::MemoryInfo& m uint32_t LoadSamplerDescriptor(EmitterState& state, uint32_t sampler, uint32_t use_pc) { (void)use_pc; const auto binding = ResourceForDescriptor(state, IR::DescriptorBindingKind::Samplers, sampler); + const auto sampler_type = state.builder.Type(OpTypeSampler); + const auto pointer_type = + state.builder.Type(OpTypePointer, {StorageClassUniformConstant, sampler_type}); const auto pointer = DescriptorElementPointer( - state, state.ptr_uniform_sampler, state.sampler_variable, binding.array_index, + state, pointer_type, state.sampler_variable, binding.array_index, IR::DescriptorBindingKind::Samplers, sampler, "sampler descriptor array was not emitted"); const auto sampler_id = state.builder.AllocateId(); - state.builder.AddFunction({OpLoad, state.sampler_type, sampler_id, pointer}); + state.builder.AddFunction({OpLoad, sampler_type, sampler_id, pointer}); return sampler_id; } @@ -273,6 +285,9 @@ uint32_t LoadStorageImageDescriptor(EmitterState& state, uint32_t resource, bool void EmitStorageImageWrite(EmitterState& state, uint32_t resource, bool uint_image, ImageViewKind view, uint32_t mip_lod, uint32_t coord, uint32_t texel) { + if (!uint_image) { + state.builder.RequireCapability(CapabilityStorageImageWriteWithoutFormat); + } const auto kind = StorageBindingKind(uint_image, view); const auto binding = ResourceForDescriptor(state, kind, resource); const auto& image = state.program.info.images.at(resource); diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp index 60e6fdbcd..b980b6360 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp @@ -27,12 +27,12 @@ uint32_t EmitWqmLaneU32(EmitterState& state, uint32_t src) { const auto expanded = state.builder.AllocateId(); const auto combined = state.builder.AllocateId(); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, masked, src, ConstantU32(state, mask)}); + {OpBitwiseAnd, TypeU32(state), masked, src, ConstantU32(state, mask)}); state.builder.AddFunction( - {OpINotEqual, state.bool_type, non_zero, masked, ConstantU32(state, 0)}); - state.builder.AddFunction({OpSelect, state.uint_type, expanded, non_zero, + {OpINotEqual, TypeBool(state), non_zero, masked, ConstantU32(state, 0)}); + state.builder.AddFunction({OpSelect, TypeU32(state), expanded, non_zero, ConstantU32(state, mask), ConstantU32(state, 0)}); - state.builder.AddFunction({OpBitwiseOr, state.uint_type, combined, ret, expanded}); + state.builder.AddFunction({OpBitwiseOr, TypeU32(state), combined, ret, expanded}); ret = combined; } return ret; @@ -40,7 +40,7 @@ uint32_t EmitWqmLaneU32(EmitterState& state, uint32_t src) { uint32_t BoolAsU32(ValueEmitContext& ctx, uint32_t value) { const auto result = ctx.state.builder.AllocateId(); - ctx.state.builder.AddFunction({OpSelect, ctx.state.uint_type, result, value, + ctx.state.builder.AddFunction({OpSelect, TypeU32(ctx.state), result, value, ConstantU32(ctx.state, 1), ConstantU32(ctx.state, 0)}); return result; } @@ -57,26 +57,27 @@ uint32_t EmitBuiltinU32(ValueEmitContext& ctx, IR::StageInputKind kind, uint32_t if (kind == IR::StageInputKind::FrontFacing) { const auto value = state.builder.AllocateId(); const auto bits = state.builder.AllocateId(); - state.builder.AddFunction({OpLoad, state.bool_type, value, variable}); + state.builder.AddFunction({OpLoad, TypeBool(state), value, variable}); state.builder.AddFunction( - {OpSelect, state.uint_type, bits, value, ConstantU32(state, 1), ConstantU32(state, 0)}); + {OpSelect, TypeU32(state), bits, value, ConstantU32(state, 1), ConstantU32(state, 0)}); return bits; } if (kind == IR::StageInputKind::VertexIndex || kind == IR::StageInputKind::InstanceIndex) { const auto value = state.builder.AllocateId(); const auto bits = state.builder.AllocateId(); - state.builder.AddFunction({OpLoad, state.int_type, value, variable}); - state.builder.AddFunction({OpBitcast, state.uint_type, bits, value}); + state.builder.AddFunction({OpLoad, TypeI32(state), value, variable}); + state.builder.AddFunction({OpBitcast, TypeU32(state), bits, value}); return bits; } if (kind == IR::StageInputKind::FragCoord) { const auto pointer = state.builder.AllocateId(); const auto value = state.builder.AllocateId(); const auto bits = state.builder.AllocateId(); - state.builder.AddFunction({OpAccessChain, state.ptr_input_float, pointer, variable, - ConstantU32(state, component)}); - state.builder.AddFunction({OpLoad, state.float_type, value, pointer}); - state.builder.AddFunction({OpBitcast, state.uint_type, bits, value}); + state.builder.AddFunction({OpAccessChain, + TypePointer(state, StorageClassInput, TypeF32(state)), pointer, + variable, ConstantU32(state, component)}); + state.builder.AddFunction({OpLoad, TypeF32(state), value, pointer}); + state.builder.AddFunction({OpBitcast, TypeU32(state), bits, value}); return bits; } return EmitInputComponentU32(state, kind, component); @@ -85,12 +86,12 @@ uint32_t EmitBuiltinU32(ValueEmitContext& ctx, IR::StageInputKind kind, uint32_t uint32_t EmitWqm(ValueEmitContext& ctx, uint32_t active) { auto& state = ctx.state; const auto ballot = state.builder.AllocateId(); - state.builder.AddFunction({OpGroupNonUniformBallot, state.vec4_uint_type, ballot, + state.builder.AddFunction({OpGroupNonUniformBallot, TypeU32Vector(state, 4), ballot, ConstantU32(state, ScopeSubgroup), active}); const auto low = state.builder.AllocateId(); const auto high = state.builder.AllocateId(); - state.builder.AddFunction({OpCompositeExtract, state.uint_type, low, ballot, 0}); - state.builder.AddFunction({OpCompositeExtract, state.uint_type, high, ballot, 1}); + state.builder.AddFunction({OpCompositeExtract, TypeU32(state), low, ballot, 0}); + state.builder.AddFunction({OpCompositeExtract, TypeU32(state), high, ballot, 1}); const auto wqm_low = EmitWqmLaneU32(state, low); const auto wqm_high = EmitWqmLaneU32(state, high); const auto lane = EmitSubgroupLocalInvocationId(state); @@ -101,14 +102,14 @@ uint32_t EmitWqm(ValueEmitContext& ctx, uint32_t active) { const auto hit = state.builder.AllocateId(); const auto result = state.builder.AllocateId(); state.builder.AddFunction( - {OpUGreaterThanEqual, state.bool_type, upper, lane, ConstantU32(state, 32)}); - state.builder.AddFunction({OpSelect, state.uint_type, mask, upper, wqm_high, wqm_low}); + {OpUGreaterThanEqual, TypeBool(state), upper, lane, ConstantU32(state, 32)}); + state.builder.AddFunction({OpSelect, TypeU32(state), mask, upper, wqm_high, wqm_low}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, bit_lane, lane, ConstantU32(state, 31)}); + {OpBitwiseAnd, TypeU32(state), bit_lane, lane, ConstantU32(state, 31)}); state.builder.AddFunction( - {OpShiftLeftLogical, state.uint_type, bit, ConstantU32(state, 1), bit_lane}); - state.builder.AddFunction({OpBitwiseAnd, state.uint_type, hit, mask, bit}); - state.builder.AddFunction({OpINotEqual, state.bool_type, result, hit, ConstantU32(state, 0)}); + {OpShiftLeftLogical, TypeU32(state), bit, ConstantU32(state, 1), bit_lane}); + state.builder.AddFunction({OpBitwiseAnd, TypeU32(state), hit, mask, bit}); + state.builder.AddFunction({OpINotEqual, TypeBool(state), result, hit, ConstantU32(state, 0)}); return result; } @@ -128,35 +129,35 @@ uint32_t EmitDppWriteCondition(ValueEmitContext& ctx, const IR::DppMoveFlags& fl const auto row_ok = state.builder.AllocateId(); const auto masks_ok = state.builder.AllocateId(); state.builder.AddFunction( - {OpShiftRightLogical, state.uint_type, bank_shift, lane, ConstantU32(state, 2)}); + {OpShiftRightLogical, TypeU32(state), bank_shift, lane, ConstantU32(state, 2)}); state.builder.AddFunction( - {OpShiftRightLogical, state.uint_type, row_shift, lane, ConstantU32(state, 4)}); + {OpShiftRightLogical, TypeU32(state), row_shift, lane, ConstantU32(state, 4)}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, bank, bank_shift, ConstantU32(state, 3)}); + {OpBitwiseAnd, TypeU32(state), bank, bank_shift, ConstantU32(state, 3)}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, row, row_shift, ConstantU32(state, 3)}); + {OpBitwiseAnd, TypeU32(state), row, row_shift, ConstantU32(state, 3)}); state.builder.AddFunction( - {OpShiftLeftLogical, state.uint_type, bank_bit, ConstantU32(state, 1), bank}); + {OpShiftLeftLogical, TypeU32(state), bank_bit, ConstantU32(state, 1), bank}); state.builder.AddFunction( - {OpShiftLeftLogical, state.uint_type, row_bit, ConstantU32(state, 1), row}); + {OpShiftLeftLogical, TypeU32(state), row_bit, ConstantU32(state, 1), row}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, bank_hit, ConstantU32(state, flags.bank_mask), bank_bit}); + {OpBitwiseAnd, TypeU32(state), bank_hit, ConstantU32(state, flags.bank_mask), bank_bit}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, row_hit, ConstantU32(state, flags.row_mask), row_bit}); + {OpBitwiseAnd, TypeU32(state), row_hit, ConstantU32(state, flags.row_mask), row_bit}); state.builder.AddFunction( - {OpINotEqual, state.bool_type, bank_ok, bank_hit, ConstantU32(state, 0)}); + {OpINotEqual, TypeBool(state), bank_ok, bank_hit, ConstantU32(state, 0)}); state.builder.AddFunction( - {OpINotEqual, state.bool_type, row_ok, row_hit, ConstantU32(state, 0)}); - state.builder.AddFunction({OpLogicalAnd, state.bool_type, masks_ok, bank_ok, row_ok}); + {OpINotEqual, TypeBool(state), row_ok, row_hit, ConstantU32(state, 0)}); + state.builder.AddFunction({OpLogicalAnd, TypeBool(state), masks_ok, bank_ok, row_ok}); uint32_t writable = masks_ok; if (!flags.bound_control) { const auto target = EmitDppTargetLane(state, flags.control); const auto bounded = state.builder.AllocateId(); - state.builder.AddFunction({OpLogicalAnd, state.bool_type, bounded, writable, target.valid}); + state.builder.AddFunction({OpLogicalAnd, TypeBool(state), bounded, writable, target.valid}); writable = bounded; } const auto result = state.builder.AllocateId(); - state.builder.AddFunction({OpLogicalAnd, state.bool_type, result, exec, writable}); + state.builder.AddFunction({OpLogicalAnd, TypeBool(state), result, exec, writable}); return result; } @@ -172,9 +173,9 @@ uint32_t EmitAttribute(ValueEmitContext& ctx, uint32_t attr, uint32_t chan) { const auto vector = state.builder.AllocateId(); const auto component = state.builder.AllocateId(); const auto bits = state.builder.AllocateId(); - state.builder.AddFunction({OpLoad, state.vec4_float_type, vector, input->variable_id}); - state.builder.AddFunction({OpCompositeExtract, state.float_type, component, vector, chan & 3u}); - state.builder.AddFunction({OpBitcast, state.uint_type, bits, component}); + state.builder.AddFunction({OpLoad, TypeF32Vector(state, 4), vector, input->variable_id}); + state.builder.AddFunction({OpCompositeExtract, TypeF32(state), component, vector, chan & 3u}); + state.builder.AddFunction({OpBitcast, TypeU32(state), bits, component}); return bits; } @@ -187,7 +188,7 @@ bool MrtUsesUint(const EmitterState& state, const IR::ExportInfo& exp) { uint32_t ExportRawComponent(ValueEmitContext& ctx, uint32_t vector, uint32_t component) { const auto value = ctx.state.builder.AllocateId(); ctx.state.builder.AddFunction( - {OpCompositeExtract, ctx.state.uint_type, value, vector, component}); + {OpCompositeExtract, TypeU32(ctx.state), value, vector, component}); return value; } @@ -203,21 +204,21 @@ uint32_t ExportVector(ValueEmitContext& ctx, uint32_t data, const IR::ExportInfo } const auto packed = state.builder.AllocateId(); const auto unpacked = state.builder.AllocateId(); - state.builder.AddFunction({OpCompositeExtract, state.uint_type, packed, data, pair}); - state.builder.AddFunction({OpExtInst, state.vec2_float_type, unpacked, - state.glsl_std450, GlslUnpackHalf2x16, packed}); + state.builder.AddFunction({OpCompositeExtract, TypeU32(state), packed, data, pair}); + state.builder.AddFunction({OpExtInst, TypeF32Vector(state, 2), unpacked, + GlslStd450(state), GlslUnpackHalf2x16, packed}); for (uint32_t lane = 0; lane < 2u; lane++) { const auto component = pair * 2u + lane; if (((exp.en >> component) & 1u) != 0u) { f32[component] = state.builder.AllocateId(); state.builder.AddFunction( - {OpCompositeExtract, state.float_type, f32[component], unpacked, lane}); + {OpCompositeExtract, TypeF32(state), f32[component], unpacked, lane}); } } } const auto vector = state.builder.AllocateId(); - state.builder.AddFunction( - {OpCompositeConstruct, state.vec4_float_type, vector, f32[0], f32[1], f32[2], f32[3]}); + state.builder.AddFunction({OpCompositeConstruct, TypeF32Vector(state, 4), vector, f32[0], + f32[1], f32[2], f32[3]}); return vector; } uint32_t raw[4] = { @@ -238,7 +239,7 @@ uint32_t ExportVector(ValueEmitContext& ctx, uint32_t data, const IR::ExportInfo continue; } raw[component] = state.builder.AllocateId(); - state.builder.AddFunction({OpBitFieldUExtract, state.uint_type, raw[component], + state.builder.AddFunction({OpBitFieldUExtract, TypeU32(state), raw[component], packed, ConstantU32(state, lane * 16u), ConstantU32(state, 16)}); } @@ -252,18 +253,18 @@ uint32_t ExportVector(ValueEmitContext& ctx, uint32_t data, const IR::ExportInfo } if (uint_output) { const auto vector = state.builder.AllocateId(); - state.builder.AddFunction( - {OpCompositeConstruct, state.vec4_uint_type, vector, raw[0], raw[1], raw[2], raw[3]}); + state.builder.AddFunction({OpCompositeConstruct, TypeU32Vector(state, 4), vector, raw[0], + raw[1], raw[2], raw[3]}); return vector; } uint32_t f32[4] {}; for (uint32_t component = 0; component < 4u; component++) { f32[component] = state.builder.AllocateId(); - state.builder.AddFunction({OpBitcast, state.float_type, f32[component], raw[component]}); + state.builder.AddFunction({OpBitcast, TypeF32(state), f32[component], raw[component]}); } const auto vector = state.builder.AllocateId(); state.builder.AddFunction( - {OpCompositeConstruct, state.vec4_float_type, vector, f32[0], f32[1], f32[2], f32[3]}); + {OpCompositeConstruct, TypeF32Vector(state, 4), vector, f32[0], f32[1], f32[2], f32[3]}); return vector; } @@ -271,11 +272,11 @@ void EmitExport(ValueEmitContext& ctx, const IR::Inst& inst) { auto& state = ctx.state; const auto& exp = ctx.Export(inst); const auto exec = ctx.Arg(inst, 1); - if (state.stage == ShaderType::Pixel && exp.vm && state.needs_pixel_valid_mask && + if (state.stage == ShaderType::Pixel && exp.vm && state.requirements.pixel_valid_mask && state.pixel_valid_mask_variable != 0) { const auto value = state.builder.AllocateId(); state.builder.AddFunction( - {OpSelect, state.uint_type, value, exec, ConstantU32(state, 1), ConstantU32(state, 0)}); + {OpSelect, TypeU32(state), value, exec, ConstantU32(state, 1), ConstantU32(state, 0)}); state.builder.AddFunction({OpStore, state.pixel_valid_mask_variable, value}); } if (exp.kind == IR::ExportTargetKind::Null || exp.kind == IR::ExportTargetKind::Primitive || @@ -288,16 +289,17 @@ void EmitExport(ValueEmitContext& ctx, const IR::Inst& inst) { if ((exp.en & 1u) != 0u && state.depth_variable != 0) { const auto raw = ExportRawComponent(ctx, data, 0); const auto f32 = state.builder.AllocateId(); - state.builder.AddFunction({OpBitcast, state.float_type, f32, raw}); + state.builder.AddFunction({OpBitcast, TypeF32(state), f32, raw}); state.builder.AddFunction({OpStore, state.depth_variable, f32}); } if ((exp.en & 4u) != 0u && state.sample_mask_variable != 0) { const auto raw = ExportRawComponent(ctx, data, 2); const auto value = state.builder.AllocateId(); const auto pointer = state.builder.AllocateId(); - state.builder.AddFunction({OpBitcast, state.int_type, value, raw}); - state.builder.AddFunction({OpAccessChain, state.ptr_output_int, pointer, - state.sample_mask_variable, ConstantU32(state, 0)}); + state.builder.AddFunction({OpBitcast, TypeI32(state), value, raw}); + state.builder.AddFunction( + {OpAccessChain, TypePointer(state, StorageClassOutput, TypeI32(state)), pointer, + state.sample_mask_variable, ConstantU32(state, 0)}); state.builder.AddFunction({OpStore, pointer, value}); } return; @@ -307,7 +309,7 @@ void EmitExport(ValueEmitContext& ctx, const IR::Inst& inst) { return; } const bool uint_output = MrtUsesUint(state, exp); - const auto vector_type = uint_output ? state.vec4_uint_type : state.vec4_float_type; + const auto vector_type = uint_output ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4); auto value = ExportVector(ctx, data, exp, uint_output); if (state.stage == ShaderType::Pixel && exp.kind == IR::ExportTargetKind::Mrt && exp.index < state.input_info.pixel->target_export_mapping.size()) { @@ -322,8 +324,9 @@ void EmitExport(ValueEmitContext& ctx, const IR::Inst& inst) { } if (exp.kind == IR::ExportTargetKind::Position) { const auto pointer = state.builder.AllocateId(); - state.builder.AddFunction({OpAccessChain, state.ptr_output_vec4_float, pointer, - variable, ConstantU32(state, 0)}); + state.builder.AddFunction( + {OpAccessChain, TypePointer(state, StorageClassOutput, TypeF32Vector(state, 4)), + pointer, variable, ConstantU32(state, 0)}); state.builder.AddFunction({OpStore, pointer, value}); } else { state.builder.AddFunction({OpStore, variable, value}); @@ -378,7 +381,7 @@ bool EmitValueFlow(ValueEmitContext& ctx, const IR::Inst& inst) { const auto flags = inst.Flags(); const auto target = EmitDppTargetLane(state, flags.control); const auto shuffled = state.builder.AllocateId(); - state.builder.AddFunction({OpGroupNonUniformShuffle, state.uint_type, shuffled, + state.builder.AddFunction({OpGroupNonUniformShuffle, TypeU32(state), shuffled, ConstantU32(state, ScopeSubgroup), ctx.Arg(inst, 0), target.lane}); if (flags.fetch_inactive) { @@ -386,12 +389,12 @@ bool EmitValueFlow(ValueEmitContext& ctx, const IR::Inst& inst) { return true; } const auto ballot = state.builder.AllocateId(); - state.builder.AddFunction({OpGroupNonUniformBallot, state.vec4_uint_type, ballot, + state.builder.AddFunction({OpGroupNonUniformBallot, TypeU32Vector(state, 4), ballot, ConstantU32(state, ScopeSubgroup), ctx.Arg(inst, 1)}); const auto source_active = EmitBallotLaneActiveBool(state, ballot, target.lane); const auto can_fetch = state.builder.AllocateId(); state.builder.AddFunction( - {OpLogicalAnd, state.bool_type, can_fetch, target.valid, source_active}); + {OpLogicalAnd, TypeBool(state), can_fetch, target.valid, source_active}); ctx.Emit(inst, OpSelect, IR::Type::U32, {can_fetch, shuffled, ConstantU32(state, 0)}); return true; } @@ -410,7 +413,7 @@ bool EmitValueFlow(ValueEmitContext& ctx, const IR::Inst& inst) { case IR::ValueOpcode::Ballot: if (state.per_invocation_masks) { const auto result = state.builder.AllocateId(); - state.builder.AddFunction({OpCompositeConstruct, state.vec4_uint_type, result, + state.builder.AddFunction({OpCompositeConstruct, TypeU32Vector(state, 4), result, BoolAsU32(ctx, ctx.Arg(inst, 0)), ConstantU32(state, 0), ConstantU32(state, 0), ConstantU32(state, 0)}); ctx.Define(inst, result); @@ -422,9 +425,9 @@ bool EmitValueFlow(ValueEmitContext& ctx, const IR::Inst& inst) { case IR::ValueOpcode::ReadFirstLane: { const auto ballot = state.builder.AllocateId(); const auto lane = state.builder.AllocateId(); - state.builder.AddFunction({OpGroupNonUniformBallot, state.vec4_uint_type, ballot, + state.builder.AddFunction({OpGroupNonUniformBallot, TypeU32Vector(state, 4), ballot, ConstantU32(state, ScopeSubgroup), ctx.Arg(inst, 1)}); - state.builder.AddFunction({OpGroupNonUniformBallotFindLSB, state.uint_type, lane, + state.builder.AddFunction({OpGroupNonUniformBallotFindLSB, TypeU32(state), lane, ConstantU32(state, ScopeSubgroup), ballot}); ctx.Emit(inst, OpGroupNonUniformShuffle, IR::Type::U32, {ConstantU32(state, ScopeSubgroup), ctx.Arg(inst, 0), lane}); @@ -436,7 +439,7 @@ bool EmitValueFlow(ValueEmitContext& ctx, const IR::Inst& inst) { return true; case IR::ValueOpcode::WriteLane: { const auto hit = state.builder.AllocateId(); - state.builder.AddFunction({OpIEqual, state.bool_type, hit, + state.builder.AddFunction({OpIEqual, TypeBool(state), hit, EmitSubgroupLocalInvocationId(state), ctx.Arg(inst, 2)}); ctx.Emit(inst, OpSelect, IR::Type::U32, {hit, ctx.Arg(inst, 1), ctx.Arg(inst, 0)}); return true; @@ -455,40 +458,40 @@ bool EmitValueFlow(ValueEmitContext& ctx, const IR::Inst& inst) { const auto index = state.builder.AllocateId(); const auto target = state.builder.AllocateId(); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, row, subid, ConstantU32(state, 0xfffffff0u)}); + {OpBitwiseAnd, TypeU32(state), row, subid, ConstantU32(state, 0xfffffff0u)}); if (flags.x16) { state.builder.AddFunction( - {OpBitwiseXor, state.uint_type, row_value, row, ConstantU32(state, 16)}); + {OpBitwiseXor, TypeU32(state), row_value, row, ConstantU32(state, 16)}); } else { - state.builder.AddFunction({OpCopyObject, state.uint_type, row_value, row}); + state.builder.AddFunction({OpCopyObject, TypeU32(state), row_value, row}); } state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, lane, subid, ConstantU32(state, 15)}); + {OpBitwiseAnd, TypeU32(state), lane, subid, ConstantU32(state, 15)}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, lane8, lane, ConstantU32(state, 7)}); + {OpBitwiseAnd, TypeU32(state), lane8, lane, ConstantU32(state, 7)}); state.builder.AddFunction( - {OpShiftLeftLogical, state.uint_type, shift, lane8, ConstantU32(state, 2)}); + {OpShiftLeftLogical, TypeU32(state), shift, lane8, ConstantU32(state, 2)}); state.builder.AddFunction( - {OpUGreaterThanEqual, state.bool_type, upper, lane, ConstantU32(state, 8)}); + {OpUGreaterThanEqual, TypeBool(state), upper, lane, ConstantU32(state, 8)}); state.builder.AddFunction( - {OpSelect, state.uint_type, selected, upper, ctx.Arg(inst, 2), ctx.Arg(inst, 1)}); + {OpSelect, TypeU32(state), selected, upper, ctx.Arg(inst, 2), ctx.Arg(inst, 1)}); state.builder.AddFunction( - {OpShiftRightLogical, state.uint_type, shifted, selected, shift}); + {OpShiftRightLogical, TypeU32(state), shifted, selected, shift}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, index, shifted, ConstantU32(state, 15)}); - state.builder.AddFunction({OpBitwiseOr, state.uint_type, target, row_value, index}); + {OpBitwiseAnd, TypeU32(state), index, shifted, ConstantU32(state, 15)}); + state.builder.AddFunction({OpBitwiseOr, TypeU32(state), target, row_value, index}); const auto shuffled = state.builder.AllocateId(); - state.builder.AddFunction({OpGroupNonUniformShuffle, state.uint_type, shuffled, + state.builder.AddFunction({OpGroupNonUniformShuffle, TypeU32(state), shuffled, ConstantU32(state, ScopeSubgroup), ctx.Arg(inst, 0), target}); uint32_t result = shuffled; if (!flags.fetch_inactive) { const auto source_exec = state.builder.AllocateId(); - state.builder.AddFunction({OpGroupNonUniformShuffle, state.bool_type, source_exec, + state.builder.AddFunction({OpGroupNonUniformShuffle, TypeBool(state), source_exec, ConstantU32(state, ScopeSubgroup), ctx.Arg(inst, 3), target}); result = state.builder.AllocateId(); - state.builder.AddFunction({OpSelect, state.uint_type, result, source_exec, shuffled, + state.builder.AddFunction({OpSelect, TypeU32(state), result, source_exec, shuffled, ConstantU32(state, 0)}); } ctx.Define(inst, result); diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterHelpers.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterHelpers.cpp index 160dcc966..11ebc8c5f 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterHelpers.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterHelpers.cpp @@ -3,10 +3,7 @@ namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter { uint32_t EmitTrueBool(EmitterState& state) { - const auto ret = state.builder.AllocateId(); - state.builder.AddFunction( - {OpINotEqual, state.bool_type, ret, ConstantU32(state, 1), ConstantU32(state, 0)}); - return ret; + return ConstantBool(state, true); } DppTargetLane EmitDppQuadPermTargetLane(EmitterState& state, uint32_t subid, uint32_t control) { @@ -17,15 +14,15 @@ DppTargetLane EmitDppQuadPermTargetLane(EmitterState& state, uint32_t subid, uin const auto selected = state.builder.AllocateId(); const auto target = state.builder.AllocateId(); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, quad_base, subid, ConstantU32(state, 0xfffffffcu)}); - state.builder.AddFunction({OpBitwiseAnd, state.uint_type, lane, subid, ConstantU32(state, 3)}); + {OpBitwiseAnd, TypeU32(state), quad_base, subid, ConstantU32(state, 0xfffffffcu)}); + state.builder.AddFunction({OpBitwiseAnd, TypeU32(state), lane, subid, ConstantU32(state, 3)}); state.builder.AddFunction( - {OpShiftLeftLogical, state.uint_type, shift, lane, ConstantU32(state, 1)}); + {OpShiftLeftLogical, TypeU32(state), shift, lane, ConstantU32(state, 1)}); state.builder.AddFunction( - {OpShiftRightLogical, state.uint_type, selected0, ConstantU32(state, control), shift}); + {OpShiftRightLogical, TypeU32(state), selected0, ConstantU32(state, control), shift}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, selected, selected0, ConstantU32(state, 3)}); - state.builder.AddFunction({OpBitwiseOr, state.uint_type, target, quad_base, selected}); + {OpBitwiseAnd, TypeU32(state), selected, selected0, ConstantU32(state, 3)}); + state.builder.AddFunction({OpBitwiseOr, TypeU32(state), target, quad_base, selected}); return {target, EmitTrueBool(state)}; } @@ -37,20 +34,20 @@ DppTargetLane EmitDppRowShiftTargetLane(EmitterState& state, uint32_t subid, uin const auto target = state.builder.AllocateId(); const auto valid = state.builder.AllocateId(); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, row, subid, ConstantU32(state, 0xfffffff0u)}); - state.builder.AddFunction({OpBitwiseAnd, state.uint_type, lane, subid, ConstantU32(state, 15)}); + {OpBitwiseAnd, TypeU32(state), row, subid, ConstantU32(state, 0xfffffff0u)}); + state.builder.AddFunction({OpBitwiseAnd, TypeU32(state), lane, subid, ConstantU32(state, 15)}); if (left) { state.builder.AddFunction( - {OpIAdd, state.uint_type, lane_shifted, lane, ConstantU32(state, amount)}); + {OpIAdd, TypeU32(state), lane_shifted, lane, ConstantU32(state, amount)}); state.builder.AddFunction( - {OpULessThan, state.bool_type, valid, lane, ConstantU32(state, 16u - amount)}); + {OpULessThan, TypeBool(state), valid, lane, ConstantU32(state, 16u - amount)}); } else { state.builder.AddFunction( - {OpISub, state.uint_type, lane_shifted, lane, ConstantU32(state, amount)}); + {OpISub, TypeU32(state), lane_shifted, lane, ConstantU32(state, amount)}); state.builder.AddFunction( - {OpUGreaterThanEqual, state.bool_type, valid, lane, ConstantU32(state, amount)}); + {OpUGreaterThanEqual, TypeBool(state), valid, lane, ConstantU32(state, amount)}); } - state.builder.AddFunction({OpBitwiseOr, state.uint_type, target, row, lane_shifted}); + state.builder.AddFunction({OpBitwiseOr, TypeU32(state), target, row, lane_shifted}); return {target, valid}; } @@ -64,15 +61,15 @@ DppTargetLane EmitDppRowRotateRightTargetLane(EmitterState& state, uint32_t subi const auto selected = state.builder.AllocateId(); const auto target = state.builder.AllocateId(); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, row, subid, ConstantU32(state, 0xfffffff0u)}); - state.builder.AddFunction({OpBitwiseAnd, state.uint_type, lane, subid, ConstantU32(state, 15)}); + {OpBitwiseAnd, TypeU32(state), row, subid, ConstantU32(state, 0xfffffff0u)}); + state.builder.AddFunction({OpBitwiseAnd, TypeU32(state), lane, subid, ConstantU32(state, 15)}); state.builder.AddFunction( - {OpUGreaterThanEqual, state.bool_type, in_high, lane, ConstantU32(state, amount)}); - state.builder.AddFunction({OpISub, state.uint_type, minus, lane, ConstantU32(state, amount)}); + {OpUGreaterThanEqual, TypeBool(state), in_high, lane, ConstantU32(state, amount)}); + state.builder.AddFunction({OpISub, TypeU32(state), minus, lane, ConstantU32(state, amount)}); state.builder.AddFunction( - {OpIAdd, state.uint_type, plus, lane, ConstantU32(state, 16u - amount)}); - state.builder.AddFunction({OpSelect, state.uint_type, selected, in_high, minus, plus}); - state.builder.AddFunction({OpBitwiseOr, state.uint_type, target, row, selected}); + {OpIAdd, TypeU32(state), plus, lane, ConstantU32(state, 16u - amount)}); + state.builder.AddFunction({OpSelect, TypeU32(state), selected, in_high, minus, plus}); + state.builder.AddFunction({OpBitwiseOr, TypeU32(state), target, row, selected}); return {target, EmitTrueBool(state)}; } @@ -84,12 +81,12 @@ DppTargetLane EmitDppMirrorTargetLane(EmitterState& state, uint32_t subid, bool const auto mirrored = state.builder.AllocateId(); const auto target = state.builder.AllocateId(); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, base, subid, ConstantU32(state, base_mask)}); + {OpBitwiseAnd, TypeU32(state), base, subid, ConstantU32(state, base_mask)}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, lane, subid, ConstantU32(state, lane_mask)}); + {OpBitwiseAnd, TypeU32(state), lane, subid, ConstantU32(state, lane_mask)}); state.builder.AddFunction( - {OpISub, state.uint_type, mirrored, ConstantU32(state, lane_mask), lane}); - state.builder.AddFunction({OpBitwiseOr, state.uint_type, target, base, mirrored}); + {OpISub, TypeU32(state), mirrored, ConstantU32(state, lane_mask), lane}); + state.builder.AddFunction({OpBitwiseOr, TypeU32(state), target, base, mirrored}); return {target, EmitTrueBool(state)}; } @@ -122,7 +119,7 @@ uint32_t EmitSubgroupLocalInvocationId(EmitterState& state) { } const auto value = state.builder.AllocateId(); state.builder.AddFunction( - {OpLoad, state.uint_type, value, state.subgroup_local_invocation_id_variable}); + {OpLoad, TypeU32(state), value, state.subgroup_local_invocation_id_variable}); return value; } @@ -151,9 +148,9 @@ uint32_t EmitInputComponentU32(EmitterState& state, IR::StageInputKind kind, uin } const auto pointer = state.builder.AllocateId(); const auto value = state.builder.AllocateId(); - state.builder.AddFunction( - {OpAccessChain, state.ptr_input_uint, pointer, variable, ConstantU32(state, component)}); - state.builder.AddFunction({OpLoad, state.uint_type, value, pointer}); + state.builder.AddFunction({OpAccessChain, TypePointer(state, StorageClassInput, TypeU32(state)), + pointer, variable, ConstantU32(state, component)}); + state.builder.AddFunction({OpLoad, TypeU32(state), value, pointer}); return value; } @@ -163,7 +160,7 @@ uint32_t EmitLocalInvocationIndex(EmitterState& state) { return ConstantU32(state, 0); } const auto value = state.builder.AllocateId(); - state.builder.AddFunction({OpLoad, state.uint_type, value, variable}); + state.builder.AddFunction({OpLoad, TypeU32(state), value, variable}); return value; } @@ -200,47 +197,47 @@ uint32_t EmitVertexParameterComponentU32(EmitterState& state, const InputBinding } const auto bits = state.builder.AllocateId(); - state.builder.AddFunction({OpBitcast, state.uint_type, bits, raw}); + state.builder.AddFunction({OpBitcast, TypeU32(state), bits, raw}); return bits; } uint32_t EmitSubgroupLaneActiveBool(EmitterState& state, uint32_t lane) { const auto active_ballot = state.builder.AllocateId(); - state.builder.AddFunction({OpGroupNonUniformBallot, state.vec4_uint_type, active_ballot, + state.builder.AddFunction({OpGroupNonUniformBallot, TypeU32Vector(state, 4), active_ballot, ConstantU32(state, ScopeSubgroup), EmitTrueBool(state)}); return EmitBallotLaneActiveBool(state, active_ballot, lane); } uint32_t EmitBallotLaneActiveBool(EmitterState& state, uint32_t active_ballot, uint32_t lane) { const auto low = state.builder.AllocateId(); - state.builder.AddFunction({OpCompositeExtract, state.uint_type, low, active_ballot, 0}); + state.builder.AddFunction({OpCompositeExtract, TypeU32(state), low, active_ballot, 0}); uint32_t mask = low; if (state.wave_size == 64u) { const auto high = state.builder.AllocateId(); const auto in_high = state.builder.AllocateId(); const auto selected = state.builder.AllocateId(); - state.builder.AddFunction({OpCompositeExtract, state.uint_type, high, active_ballot, 1}); + state.builder.AddFunction({OpCompositeExtract, TypeU32(state), high, active_ballot, 1}); state.builder.AddFunction( - {OpUGreaterThanEqual, state.bool_type, in_high, lane, ConstantU32(state, 32)}); - state.builder.AddFunction({OpSelect, state.uint_type, selected, in_high, high, low}); + {OpUGreaterThanEqual, TypeBool(state), in_high, lane, ConstantU32(state, 32)}); + state.builder.AddFunction({OpSelect, TypeU32(state), selected, in_high, high, low}); mask = selected; } const auto lane_low = state.builder.AllocateId(); const auto bit = state.builder.AllocateId(); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, lane_low, lane, ConstantU32(state, 31)}); + {OpBitwiseAnd, TypeU32(state), lane_low, lane, ConstantU32(state, 31)}); state.builder.AddFunction( - {OpShiftLeftLogical, state.uint_type, bit, ConstantU32(state, 1), lane_low}); + {OpShiftLeftLogical, TypeU32(state), bit, ConstantU32(state, 1), lane_low}); const auto hit = state.builder.AllocateId(); const auto active = state.builder.AllocateId(); const auto in_range = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({OpBitwiseAnd, state.uint_type, hit, mask, bit}); - state.builder.AddFunction({OpINotEqual, state.bool_type, active, hit, ConstantU32(state, 0)}); + state.builder.AddFunction({OpBitwiseAnd, TypeU32(state), hit, mask, bit}); + state.builder.AddFunction({OpINotEqual, TypeBool(state), active, hit, ConstantU32(state, 0)}); state.builder.AddFunction( - {OpULessThan, state.bool_type, in_range, lane, ConstantU32(state, state.wave_size)}); - state.builder.AddFunction({OpLogicalAnd, state.bool_type, ret, active, in_range}); + {OpULessThan, TypeBool(state), in_range, lane, ConstantU32(state, state.wave_size)}); + state.builder.AddFunction({OpLogicalAnd, TypeBool(state), ret, active, in_range}); return ret; } diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp index 9528f830f..285fa41a8 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp @@ -78,10 +78,10 @@ uint32_t AddressU32(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR:: auto value = ctx.Def(address.Arg(packed)); if (ComponentUsesA16(mem, component)) { if ((half & 1u) != 0u) { - value = Binary(ctx.state, OpShiftRightLogical, ctx.state.uint_type, value, + value = Binary(ctx.state, OpShiftRightLogical, TypeU32(ctx.state), value, ConstantU32(ctx.state, 16)); } - value = Binary(ctx.state, OpBitwiseAnd, ctx.state.uint_type, value, + value = Binary(ctx.state, OpBitwiseAnd, TypeU32(ctx.state), value, ConstantU32(ctx.state, 0xffffu)); } return value; @@ -92,7 +92,7 @@ uint32_t AddressF32(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR:: const auto value = AddressU32(ctx, mem, address, component); return ComponentUsesA16(mem, component) ? EmitF16BitsToF32(ctx.state, value) - : Unary(ctx.state, OpBitcast, ctx.state.float_type, value); + : Unary(ctx.state, OpBitcast, TypeF32(ctx.state), value); } ImageSampleLayout Layout(const IR::MemoryInfo& mem, ImageViewKind view) { @@ -117,28 +117,20 @@ uint32_t ZeroF32(EmitterState& state) { return ConstantF32(state, 0); } -uint32_t ZeroU32x4(EmitterState& state) { - const auto zero = ConstantU32(state, 0); - const auto value = state.builder.AllocateId(); - state.builder.AddType( - {OpConstantComposite, state.vec4_uint_type, value, zero, zero, zero, zero}); - return value; -} - uint32_t CubeAxis(EmitterState& state, uint32_t value) { - return Binary(state, OpFSub, state.float_type, value, ConstantF32(state, 0x3f800000u)); + return Binary(state, OpFSub, TypeF32(state), value, ConstantF32(state, 0x3f800000u)); } uint32_t CubeLayer(EmitterState& state, uint32_t value) { const auto guest = state.builder.AllocateId(); - state.builder.AddFunction({OpConvertFToU, state.uint_type, guest, value}); + state.builder.AddFunction({OpConvertFToU, TypeU32(state), guest, value}); const auto padding = - Binary(state, OpShiftLeftLogical, state.uint_type, - Binary(state, OpShiftRightLogical, state.uint_type, guest, ConstantU32(state, 3)), + Binary(state, OpShiftLeftLogical, TypeU32(state), + Binary(state, OpShiftRightLogical, TypeU32(state), guest, ConstantU32(state, 3)), ConstantU32(state, 1)); - const auto host = Binary(state, OpISub, state.uint_type, guest, padding); + const auto host = Binary(state, OpISub, TypeU32(state), guest, padding); const auto result = state.builder.AllocateId(); - state.builder.AddFunction({OpConvertUToF, state.float_type, result, host}); + state.builder.AddFunction({OpConvertUToF, TypeF32(state), result, host}); return result; } @@ -159,10 +151,10 @@ uint32_t CoordF32(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR::In : ZeroF32(ctx.state); if (mem.image_cube) z = CubeLayer(ctx.state, z); ctx.state.builder.AddFunction( - {OpCompositeConstruct, ctx.state.vec3_float_type, result, x, y, z}); + {OpCompositeConstruct, TypeF32Vector(ctx.state, 3), result, x, y, z}); } else { ctx.state.builder.AddFunction( - {OpCompositeConstruct, ctx.state.vec2_float_type, result, x, y}); + {OpCompositeConstruct, TypeF32Vector(ctx.state, 2), result, x, y}); } return result; } @@ -179,10 +171,10 @@ uint32_t CoordU32(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR::In const auto z = mem.image_address_components > 2u ? AddressU32(ctx, mem, address, 2) : ConstantU32(ctx.state, 0); ctx.state.builder.AddFunction( - {OpCompositeConstruct, ctx.state.vec3_uint_type, result, x, y, z}); + {OpCompositeConstruct, TypeU32Vector(ctx.state, 3), result, x, y, z}); } else { ctx.state.builder.AddFunction( - {OpCompositeConstruct, ctx.state.vec2_uint_type, result, x, y}); + {OpCompositeConstruct, TypeU32Vector(ctx.state, 2), result, x, y}); } return result; } @@ -196,7 +188,7 @@ uint32_t LodU32(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR::Inst } uint32_t FloatBits(ValueEmitContext& ctx, uint32_t value) { - return Unary(ctx.state, OpBitcast, ctx.state.uint_type, value); + return Unary(ctx.state, OpBitcast, TypeU32(ctx.state), value); } uint32_t ResultVector(ValueEmitContext& ctx, uint32_t value, bool integer, bool dref) { @@ -208,12 +200,12 @@ uint32_t ResultVector(ValueEmitContext& ctx, uint32_t value, bool integer, bool } const auto scalar = ctx.state.builder.AllocateId(); ctx.state.builder.AddFunction({OpCompositeExtract, - integer ? ctx.state.uint_type : ctx.state.float_type, scalar, + integer ? TypeU32(ctx.state) : TypeF32(ctx.state), scalar, value, index}); component[index] = integer ? scalar : FloatBits(ctx, scalar); } const auto result = ctx.state.builder.AllocateId(); - ctx.state.builder.AddFunction({OpCompositeConstruct, ctx.state.vec4_uint_type, result, + ctx.state.builder.AddFunction({OpCompositeConstruct, TypeU32Vector(ctx.state, 4), result, component[0], component[1], component[2], component[3]}); return result; } @@ -240,15 +232,15 @@ uint32_t QueryDimensions(ValueEmitContext& ctx, const IR::Inst& inst, const IR:: for (uint32_t index = 0; index < components; index++) { result[index] = ctx.state.builder.AllocateId(); ctx.state.builder.AddFunction( - {OpCompositeExtract, ctx.state.uint_type, result[index], size, index}); + {OpCompositeExtract, TypeU32(ctx.state), result[index], size, index}); } } if (ImageSpirvMultisampled(view) == 0u) { result[3] = ctx.state.builder.AllocateId(); - ctx.state.builder.AddFunction({OpImageQueryLevels, ctx.state.uint_type, result[3], image}); + ctx.state.builder.AddFunction({OpImageQueryLevels, TypeU32(ctx.state), result[3], image}); } const auto vector = ctx.state.builder.AllocateId(); - ctx.state.builder.AddFunction({OpCompositeConstruct, ctx.state.vec4_uint_type, vector, + ctx.state.builder.AddFunction({OpCompositeConstruct, TypeU32Vector(ctx.state, 4), vector, result[0], result[1], result[2], result[3]}); return vector; } @@ -262,55 +254,51 @@ uint32_t PackedOffset(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR const auto result = ctx.state.builder.AllocateId(); if (components == 3u) { ctx.state.builder.AddFunction( - {OpCompositeConstruct, ctx.state.vec3_int_type, result, zero, zero, zero}); + {OpCompositeConstruct, TypeI32Vector(ctx.state, 3), result, zero, zero, zero}); } else { ctx.state.builder.AddFunction( - {OpCompositeConstruct, ctx.state.vec2_int_type, result, zero, zero}); + {OpCompositeConstruct, TypeI32Vector(ctx.state, 2), result, zero, zero}); } return result; } - const auto packed = Unary(ctx.state, OpBitcast, ctx.state.int_type, + const auto packed = Unary(ctx.state, OpBitcast, TypeI32(ctx.state), AddressU32(ctx, mem, address, layout.offset)); uint32_t values[3] = {zero, zero, zero}; for (uint32_t index = 0; index < components; index++) { values[index] = ctx.state.builder.AllocateId(); - ctx.state.builder.AddFunction({OpBitFieldSExtract, ctx.state.int_type, values[index], + ctx.state.builder.AddFunction({OpBitFieldSExtract, TypeI32(ctx.state), values[index], packed, ConstantU32(ctx.state, index * 8u), ConstantU32(ctx.state, 6)}); } if (components == 1u) return values[0]; const auto result = ctx.state.builder.AllocateId(); if (components == 3u) { - ctx.state.builder.AddFunction({OpCompositeConstruct, ctx.state.vec3_int_type, result, + ctx.state.builder.AddFunction({OpCompositeConstruct, TypeI32Vector(ctx.state, 3), result, values[0], values[1], values[2]}); } else { ctx.state.builder.AddFunction( - {OpCompositeConstruct, ctx.state.vec2_int_type, result, values[0], values[1]}); + {OpCompositeConstruct, TypeI32Vector(ctx.state, 2), result, values[0], values[1]}); } return result; } uint32_t HorizontalOffsets(EmitterState& state, ImageViewKind view) { const auto components = ImageViewSpatialComponents(view); - const auto array_type = state.builder.AllocateId(); const auto count = ConstantU32(state, 4); - const auto element_type = components == 1u ? state.int_type : state.vec2_int_type; - state.builder.AddType({OpTypeArray, array_type, element_type, count}); - uint32_t offsets[4] {}; + const auto element_type = components == 1u ? TypeI32(state) : TypeI32Vector(state, 2); + const auto array_type = state.builder.Type(OpTypeArray, {element_type, count}); + uint32_t offsets[4] {}; for (uint32_t index = 0; index < 4u; index++) { const auto x = ConstantI32(state, static_cast(index) - 1); if (components == 1u) { offsets[index] = x; } else { - offsets[index] = state.builder.AllocateId(); - state.builder.AddType({OpConstantComposite, state.vec2_int_type, offsets[index], x, - ConstantI32(state, 0)}); + offsets[index] = state.builder.Constant(OpConstantComposite, TypeI32Vector(state, 2), + {x, ConstantI32(state, 0)}); } } - const auto value = state.builder.AllocateId(); - state.builder.AddType( - {OpConstantComposite, array_type, value, offsets[0], offsets[1], offsets[2], offsets[3]}); - return value; + return state.builder.Constant(OpConstantComposite, array_type, + {offsets[0], offsets[1], offsets[2], offsets[3]}); } uint32_t InverseSwizzle(uint32_t swizzle, uint32_t component) { @@ -331,14 +319,14 @@ uint32_t StoreTexel(ValueEmitContext& ctx, const IR::MemoryInfo& mem, uint32_t d const auto packed_index = DmaskComponentIndex(dmask, source); raw = ctx.state.builder.AllocateId(); ctx.state.builder.AddFunction( - {OpCompositeExtract, ctx.state.uint_type, raw, data, packed_index}); + {OpCompositeExtract, TypeU32(ctx.state), raw, data, packed_index}); } - values[component] = integer ? raw : Unary(ctx.state, OpBitcast, ctx.state.float_type, raw); + values[component] = integer ? raw : Unary(ctx.state, OpBitcast, TypeF32(ctx.state), raw); } const auto texel = ctx.state.builder.AllocateId(); - ctx.state.builder.AddFunction({OpCompositeConstruct, - integer ? ctx.state.vec4_uint_type : ctx.state.vec4_float_type, - texel, values[0], values[1], values[2], values[3]}); + ctx.state.builder.AddFunction( + {OpCompositeConstruct, integer ? TypeU32Vector(ctx.state, 4) : TypeF32Vector(ctx.state, 4), + texel, values[0], values[1], values[2], values[3]}); return texel; } @@ -372,26 +360,27 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { const auto* address = ctx.ImageAddress(inst.Arg(sampled ? 2 : 1)); if (address == nullptr) return true; if (op == IR::ValueOpcode::ImageQueryDimensions) { + state.builder.RequireCapability(CapabilityImageQuery); ctx.Define(inst, QueryDimensions(ctx, inst, mem, *address)); return true; } if (op == IR::ValueOpcode::ImageQueryLod) { + state.builder.RequireCapability(CapabilityImageQuery); const auto view = SampledImageViewKind(state, mem, pc); const auto sampled = MakeSampledImage(state, mem, pc, view); const auto lod = state.builder.AllocateId(); state.builder.AddFunction( - {OpImageQueryLod, state.vec2_float_type, lod, sampled, + {OpImageQueryLod, TypeF32Vector(state, 2), lod, sampled, CoordF32(ctx, mem, *address, 0, ImageViewSpatialComponents(view))}); uint32_t values[4] = {ConstantU32(state, 0), ConstantU32(state, 0), ConstantU32(state, 0), ConstantU32(state, 0)}; for (uint32_t index = 0; index < 2u; index++) { const auto component = state.builder.AllocateId(); - state.builder.AddFunction( - {OpCompositeExtract, state.float_type, component, lod, index}); + state.builder.AddFunction({OpCompositeExtract, TypeF32(state), component, lod, index}); values[index] = FloatBits(ctx, component); } const auto result = state.builder.AllocateId(); - state.builder.AddFunction({OpCompositeConstruct, state.vec4_uint_type, result, values[0], + state.builder.AddFunction({OpCompositeConstruct, TypeU32Vector(state, 4), result, values[0], values[1], values[2], values[3]}); ctx.Define(inst, result); return true; @@ -403,20 +392,21 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { ctx.Define( inst, EmitValueOrDefaultIfCondition( - state, condition, state.vec4_uint_type, ZeroU32x4(state), [&]() { + state, condition, TypeU32Vector(state, 4), ConstantU32x4Zero(state), [&]() { const auto image = LoadSampledImageDescriptor(state, mem, pc, view); const auto color = state.builder.AllocateId(); const auto coord = CoordU32(ctx, mem, *address, view); if (ImageSpirvMultisampled(view) != 0u) { state.builder.AddFunction( - {OpImageFetch, integer ? state.vec4_uint_type : state.vec4_float_type, - color, image, coord, ImageOperandsSampleMask, + {OpImageFetch, + integer ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4), color, + image, coord, ImageOperandsSampleMask, AddressU32(ctx, mem, *address, ImageViewCoordinateComponents(view))}); } else { state.builder.AddFunction( - {OpImageFetch, integer ? state.vec4_uint_type : state.vec4_float_type, - color, image, coord, ImageOperandsLodMask, - LodU32(ctx, mem, *address, view)}); + {OpImageFetch, + integer ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4), color, + image, coord, ImageOperandsLodMask, LodU32(ctx, mem, *address, view)}); } return ResultVector(ctx, color, integer, false); })); @@ -448,7 +438,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { const auto sample = state.builder.AllocateId(); std::vector words = dref ? std::vector {OpImageDrefGather, - state.vec4_float_type, + TypeF32Vector(state, 4), sample, sampled, coord, @@ -456,9 +446,12 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { ? AddressF32(ctx, mem, *address, layout.dref) : ZeroF32(state)} : std::vector { - OpImageGather, integer ? state.vec4_uint_type : state.vec4_float_type, - sample, sampled, - coord, ConstantU32(state, ImageGatherComponent(mem.dmask))}; + OpImageGather, + integer ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4), + sample, + sampled, + coord, + ConstantU32(state, ImageGatherComponent(mem.dmask))}; if (HasFlag(mem, Decoder::ImageSampleFlagGatherHorizontal)) { words.push_back(ImageOperandsConstOffsetsMask); words.push_back(HorizontalOffsets(state, view)); @@ -478,7 +471,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { ? (dref ? OpImageSampleDrefExplicitLod : OpImageSampleExplicitLod) : (dref ? OpImageSampleDrefImplicitLod : OpImageSampleImplicitLod); const auto result_type = - dref ? state.float_type : (integer ? state.vec4_uint_type : state.vec4_float_type); + dref ? TypeF32(state) : (integer ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4)); const auto dref_value = dref ? (layout.dref != NoImageComponent ? AddressF32(ctx, mem, *address, layout.dref) : ZeroF32(state)) @@ -537,10 +530,11 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { } const auto LoadMapping = [&](uint32_t index) { const auto pointer = state.builder.AllocateId(); - state.builder.AddFunction({OpAccessChain, state.ptr_storage_buffer_uint, pointer, - state.flattened_srt_variable, ConstantU32(state, 0), index}); + state.builder.AddFunction({OpAccessChain, TypeStorageBufferElementPointer(state), + pointer, state.flattened_srt_variable, ConstantU32(state, 0), + index}); const auto value = state.builder.AllocateId(); - state.builder.AddFunction({OpLoad, state.uint_type, value, pointer}); + state.builder.AddFunction({OpLoad, TypeU32(state), value, pointer}); return value; }; const auto mapping = ConstantU32(state, image.indirect_mapping_offset); @@ -549,39 +543,38 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { auto selected = ConstantU32(state, 0u); for (uint32_t iteration = 0; iteration < std::bit_width(image.indirect_mapping_capacity); iteration++) { - const auto active = Binary(state, OpULessThan, state.bool_type, low, high); + const auto active = Binary(state, OpULessThan, TypeBool(state), low, high); const auto mid = - Binary(state, OpShiftRightLogical, state.uint_type, - Binary(state, OpIAdd, state.uint_type, low, high), ConstantU32(state, 1u)); + Binary(state, OpShiftRightLogical, TypeU32(state), + Binary(state, OpIAdd, TypeU32(state), low, high), ConstantU32(state, 1u)); const auto probe = state.builder.AllocateId(); state.builder.AddFunction( - {OpSelect, state.uint_type, probe, active, mid, ConstantU32(state, 0u)}); - const auto entry = Binary(state, OpIAdd, state.uint_type, mapping, - Binary(state, OpIAdd, state.uint_type, - Binary(state, OpShiftLeftLogical, state.uint_type, + {OpSelect, TypeU32(state), probe, active, mid, ConstantU32(state, 0u)}); + const auto entry = Binary(state, OpIAdd, TypeU32(state), mapping, + Binary(state, OpIAdd, TypeU32(state), + Binary(state, OpShiftLeftLogical, TypeU32(state), probe, ConstantU32(state, 1u)), ConstantU32(state, 1u))); const auto mapped_key = LoadMapping(entry); const auto candidate = - LoadMapping(Binary(state, OpIAdd, state.uint_type, entry, ConstantU32(state, 1u))); - const auto equal = Binary(state, OpIEqual, state.bool_type, mapped_key, key); - const auto match = Binary(state, OpLogicalAnd, state.bool_type, active, equal); + LoadMapping(Binary(state, OpIAdd, TypeU32(state), entry, ConstantU32(state, 1u))); + const auto equal = Binary(state, OpIEqual, TypeBool(state), mapped_key, key); + const auto match = Binary(state, OpLogicalAnd, TypeBool(state), active, equal); const auto next_selected = state.builder.AllocateId(); state.builder.AddFunction( - {OpSelect, state.uint_type, next_selected, match, candidate, selected}); + {OpSelect, TypeU32(state), next_selected, match, candidate, selected}); selected = next_selected; - const auto less = Binary(state, OpULessThan, state.bool_type, mapped_key, key); - const auto take_upper = Binary(state, OpLogicalAnd, state.bool_type, active, less); - const auto take_lower = Binary(state, OpLogicalAnd, state.bool_type, active, - Unary(state, OpLogicalNot, state.bool_type, less)); + const auto less = Binary(state, OpULessThan, TypeBool(state), mapped_key, key); + const auto take_upper = Binary(state, OpLogicalAnd, TypeBool(state), active, less); + const auto take_lower = Binary(state, OpLogicalAnd, TypeBool(state), active, + Unary(state, OpLogicalNot, TypeBool(state), less)); const auto next_low = state.builder.AllocateId(); state.builder.AddFunction( - {OpSelect, state.uint_type, next_low, take_upper, - Binary(state, OpIAdd, state.uint_type, mid, ConstantU32(state, 1u)), low}); + {OpSelect, TypeU32(state), next_low, take_upper, + Binary(state, OpIAdd, TypeU32(state), mid, ConstantU32(state, 1u)), low}); low = next_low; const auto next_high = state.builder.AllocateId(); - state.builder.AddFunction( - {OpSelect, state.uint_type, next_high, take_lower, mid, high}); + state.builder.AddFunction({OpSelect, TypeU32(state), next_high, take_lower, mid, high}); high = next_high; } const auto default_label = state.builder.AllocateId(); @@ -616,12 +609,14 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { const auto view = StorageImageViewKind(state, mem, true, pc); ctx.Define(inst, EmitValueOrZeroIfCondition(state, ctx.Arg(inst, 3), [&]() { const auto pointer = state.builder.AllocateId(); + const auto pointer_type = + state.builder.Type(OpTypePointer, {StorageClassImage, TypeU32(state)}); state.builder.AddFunction( - {OpImageTexelPointer, state.ptr_image_uint, pointer, + {OpImageTexelPointer, pointer_type, pointer, StorageImageDescriptorPointer(state, mem.resource, true, pc, view), CoordU32(ctx, mem, *address, view), ConstantU32(state, 0)}); const auto old = state.builder.AllocateId(); - state.builder.AddFunction({atomic_opcode, state.uint_type, old, pointer, + state.builder.AddFunction({atomic_opcode, TypeU32(state), old, pointer, ConstantU32(state, ScopeDevice), ConstantU32(state, MemorySemanticsNone), ctx.Arg(inst, 2)}); diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h index 7c199e788..c7479781e 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h @@ -132,6 +132,8 @@ enum : uint32_t { OpTypeStruct = 30, OpTypePointer = 32, OpTypeFunction = 33, + OpConstantTrue = 41, + OpConstantFalse = 42, OpConstant = 43, OpConstantComposite = 44, OpUndef = 1, @@ -294,109 +296,31 @@ struct DescriptorResourceBinding { uint32_t array_index = 0; }; -struct SampledImageDescriptors { - uint32_t image_type = 0; - uint32_t sampled_image_type = 0; - uint32_t pointer_type = 0; - uint32_t array_type = 0; - uint32_t array_pointer_type = 0; - uint32_t variable = 0; -}; - -struct StorageImageDescriptors { - uint32_t image_type = 0; - uint32_t pointer_type = 0; - uint32_t array_type = 0; - uint32_t array_pointer_type = 0; - uint32_t variable = 0; -}; - struct EmitterState { EmitterState(const IR::Program& program_, const IR::ResourceSnapshot& resources_, ShaderStageInputInfo input_info_) - : program(program_), resources(resources_), input_info(input_info_) {} + : program(program_), resources(resources_), input_info(input_info_), + requirements(*program_.spirv_requirements) {} Builder builder; const IR::Program& program; const IR::ResourceSnapshot& resources; ShaderStageInputInfo input_info; - ShaderType stage = ShaderType::Unknown; - uint32_t wave_size = 64; - bool per_invocation_masks = false; - uint32_t void_type = 0; - uint32_t bool_type = 0; - uint32_t uint_type = 0; - uint32_t uint_pair_type = 0; - uint32_t int_pair_type = 0; - uint32_t int_type = 0; - uint32_t float_type = 0; - uint32_t vec2_uint_type = 0; - uint32_t vec3_uint_type = 0; - uint32_t vec4_uint_type = 0; - uint32_t vec2_int_type = 0; - uint32_t vec3_int_type = 0; - uint32_t vec4_int_type = 0; - uint32_t vec2_float_type = 0; - uint32_t vec3_float_type = 0; - uint32_t vec4_float_type = 0; - uint32_t ptr_func_uint = 0; - uint32_t ptr_func_bool = 0; - uint32_t ptr_func_float = 0; - uint32_t ptr_func_uint_pair = 0; - uint32_t ptr_func_vec4_uint = 0; - uint32_t ptr_func_vec2_float = 0; - uint32_t ptr_input_float = 0; - uint32_t ptr_input_bool = 0; - uint32_t ptr_input_int = 0; - uint32_t ptr_input_uint = 0; - uint32_t ptr_input_vec2_float = 0; - uint32_t ptr_input_vec3_float = 0; - uint32_t ptr_input_vec2_int = 0; - uint32_t ptr_input_vec3_int = 0; - uint32_t ptr_input_vec4_int = 0; - uint32_t ptr_input_vec2_uint = 0; - uint32_t ptr_input_vec3_uint = 0; - uint32_t ptr_input_vec4_uint = 0; - uint32_t ptr_input_vec4_float = 0; - uint32_t sample_mask_array_type = 0; - uint32_t ptr_output_int = 0; - uint32_t ptr_output_sample_mask_array = 0; - uint32_t ptr_output_float = 0; - uint32_t ptr_output_vec4_float = 0; - uint32_t per_vertex_type = 0; - uint32_t ptr_output_per_vertex = 0; - uint32_t storage_runtime_array_type = 0; - uint32_t storage_buffer_type = 0; - uint32_t ptr_storage_buffer = 0; - uint32_t ptr_storage_buffer_uint = 0; - uint32_t storage_buffer_array_type = 0; - uint32_t ptr_storage_buffer_array = 0; - uint32_t storage_buffer_variable = 0; + const IR::SpirvRequirements& requirements; + ShaderType stage = ShaderType::Unknown; + uint32_t wave_size = 64; + bool per_invocation_masks = false; + uint32_t storage_buffer_variable = 0; std::array storage_buffer_offsets {}; - uint32_t address_memory_array_type = 0; - uint32_t ptr_address_memory_array = 0; - uint32_t address_memory_variable = 0; - uint32_t gds_variable = 0; - uint32_t push_constant_array_type = 0; - uint32_t push_constant_block_type = 0; - uint32_t ptr_push_constant_block = 0; - uint32_t ptr_push_constant_uint = 0; - uint32_t push_constant_variable = 0; - uint32_t vsharp_storage_variable = 0; - uint32_t flattened_srt_variable = 0; - uint32_t lds_array_type = 0; - uint32_t ptr_workgroup_array = 0; - uint32_t ptr_workgroup_uint = 0; - uint32_t lds_variable = 0; - std::array sampled_images; - std::array storage_images; - uint32_t sampler_type = 0; - uint32_t sampler_array_type = 0; - uint32_t ptr_uniform_sampler = 0; - uint32_t ptr_uniform_sampler_array = 0; + uint32_t address_memory_variable = 0; + uint32_t gds_variable = 0; + uint32_t push_constant_variable = 0; + uint32_t vsharp_storage_variable = 0; + uint32_t flattened_srt_variable = 0; + uint32_t lds_variable = 0; + std::array sampled_image_variables {}; + std::array storage_image_variables {}; uint32_t sampler_variable = 0; - uint32_t ptr_image_uint = 0; - uint32_t func_type = 0; uint32_t main_func = 0; uint32_t entry_label = 0; uint32_t pixel_valid_mask_variable = 0; @@ -408,26 +332,33 @@ struct EmitterState { uint32_t dispatch_after_switch_label = 0; uint32_t dispatch_continue_label = 0; uint32_t dispatch_merge_label = 0; - uint32_t glsl_std450 = 0; uint32_t subgroup_local_invocation_id_variable = 0; uint32_t per_vertex_variable = 0; uint32_t depth_variable = 0; uint32_t sample_mask_variable = 0; - bool needs_subgroup_ballot = false; - bool needs_subgroup_shuffle = false; - bool needs_subgroup_local_invocation_id = false; - bool needs_compute_derivatives = false; - bool needs_image_gather_extended = false; - bool needs_function_lds = false; - bool needs_pixel_valid_mask = false; std::vector inputs; std::vector outputs; std::vector interface_variables; - std::map constants; - std::map signed_constants; - std::map float_constants; }; +uint32_t TypeVoid(EmitterState& state); +uint32_t TypeBool(EmitterState& state); +uint32_t TypeU32(EmitterState& state); +uint32_t TypeU32Pair(EmitterState& state); +uint32_t TypeI32(EmitterState& state); +uint32_t TypeI32Pair(EmitterState& state); +uint32_t TypeF32(EmitterState& state); +uint32_t TypeU32Vector(EmitterState& state, uint32_t components); +uint32_t TypeI32Vector(EmitterState& state, uint32_t components); +uint32_t TypeF32Vector(EmitterState& state, uint32_t components); +uint32_t TypePointer(EmitterState& state, uint32_t storage_class, uint32_t pointee); +uint32_t TypeFunction(EmitterState& state); +uint32_t TypeStorageBufferPointer(EmitterState& state); +uint32_t TypeStorageBufferElementPointer(EmitterState& state); +uint32_t TypePushConstantElementPointer(EmitterState& state); +uint32_t TypeLdsArrayPointer(EmitterState& state, uint32_t storage_class); +uint32_t TypeLdsElementPointer(EmitterState& state, uint32_t storage_class); + struct ValueEmitContext { ValueEmitContext(EmitterState& state_, const IR::ValueProgram& program_) : state(state_), program(program_) {} @@ -597,14 +528,14 @@ VertexInputScalarKind VertexParameterScalarKind(const EmitterState& state, uint3 uint32_t VertexParameterComponentCount(const EmitterState& state, const InputBinding& input); -uint32_t VertexParameterScalarType(const EmitterState& state, VertexInputScalarKind kind); +uint32_t VertexParameterScalarType(EmitterState& state, VertexInputScalarKind kind); -uint32_t VertexParameterScalarPointerType(const EmitterState& state, VertexInputScalarKind kind); +uint32_t VertexParameterScalarPointerType(EmitterState& state, VertexInputScalarKind kind); -uint32_t VertexParameterVectorOrScalarType(const EmitterState& state, VertexInputScalarKind kind, +uint32_t VertexParameterVectorOrScalarType(EmitterState& state, VertexInputScalarKind kind, uint32_t components); -uint32_t VertexParameterInputPointerType(const EmitterState& state, VertexInputScalarKind kind, +uint32_t VertexParameterInputPointerType(EmitterState& state, VertexInputScalarKind kind, uint32_t components); void SetError(std::string* error, const char* message); @@ -638,11 +569,15 @@ uint32_t ImageViewCoordinateComponents(ImageViewKind view); uint32_t ImageViewSpatialComponents(ImageViewKind view); -uint32_t ImageViewImageType(const EmitterState& state, ImageViewKind view, bool integer); +uint32_t ImageViewImageType(EmitterState& state, ImageViewKind view, bool integer); -uint32_t ImageViewSampledImageType(const EmitterState& state, ImageViewKind view, bool integer); +uint32_t ImageViewSampledImageType(EmitterState& state, ImageViewKind view, bool integer); -uint32_t ImageViewSizeType(const EmitterState& state, ImageViewKind view); +uint32_t ImageViewSizeType(EmitterState& state, ImageViewKind view); + +uint32_t StorageImageType(EmitterState& state, bool uint_image, ImageViewKind view); + +uint32_t StorageImagePointerType(EmitterState& state, bool uint_image, ImageViewKind view); uint32_t LoadSampledImageDescriptor(EmitterState& state, const IR::MemoryInfo& mem, uint32_t use_pc, ImageViewKind view); @@ -679,6 +614,14 @@ uint32_t FloatBits(float value); uint32_t ConstantF32Value(EmitterState& state, float value); +uint32_t ConstantBool(EmitterState& state, bool value); + +uint32_t ConstantU64(EmitterState& state, uint64_t value); + +uint32_t ConstantU32x4Zero(EmitterState& state); + +uint32_t GlslStd450(EmitterState& state); + void AllocateInputVariables(EmitterState& state); void AllocateOutputVariables(EmitterState& state); @@ -694,9 +637,7 @@ void DecorateDescriptor(EmitterState& state, uint32_t variable, const char* name void AddDescriptorAnnotationsAndNames(EmitterState& state); -void EmitHeaderAndTypes(EmitterState& state); - -void AllocateDescriptorVariables(EmitterState& state); +void DefineModule(EmitterState& state); uint32_t EmitTrueBool(EmitterState& state); @@ -913,7 +854,7 @@ uint32_t EmitValueOrDefaultIfCondition(EmitterState& state, uint32_t condition, template uint32_t EmitValueOrZeroIfCondition(EmitterState& state, uint32_t condition, Fn&& fn) { - return EmitValueOrDefaultIfCondition(state, condition, state.uint_type, ConstantU32(state, 0), + return EmitValueOrDefaultIfCondition(state, condition, TypeU32(state), ConstantU32(state, 0), std::forward(fn)); } diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp index f17c18d45..86835cb3f 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp @@ -27,7 +27,7 @@ uint32_t Select(EmitterState& state, uint32_t type, uint32_t condition, uint32_t uint32_t AndCondition(EmitterState& state, uint32_t lhs, uint32_t rhs) { if (lhs == 0) return rhs; if (rhs == 0) return lhs; - return Binary(state, OpLogicalAnd, state.bool_type, lhs, rhs); + return Binary(state, OpLogicalAnd, TypeBool(state), lhs, rhs); } uint32_t BufferByteAddress(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem, @@ -37,66 +37,66 @@ uint32_t BufferByteAddress(ValueEmitContext& ctx, const IR::Inst& inst, const IR ConstantU32(state, StorageBufferPackedStride(state, mem, inst.Flags().pc)); if (mem.formatted && !mem.typed && inst.GetOpcode() == IR::ValueOpcode::StoreBufferU32) { const auto add_tid_bit = Binary( - state, OpBitwiseAnd, state.uint_type, - Binary(state, OpShiftRightLogical, state.uint_type, packed, ConstantU32(state, 20)), + state, OpBitwiseAnd, TypeU32(state), + Binary(state, OpShiftRightLogical, TypeU32(state), packed, ConstantU32(state, 20)), ConstantU32(state, 1)); const auto add_tid = - Binary(state, OpINotEqual, state.bool_type, add_tid_bit, ConstantU32(state, 0)); - const auto lane = Binary(state, OpBitwiseAnd, state.uint_type, + Binary(state, OpINotEqual, TypeBool(state), add_tid_bit, ConstantU32(state, 0)); + const auto lane = Binary(state, OpBitwiseAnd, TypeU32(state), EmitLocalInvocationIndex(state), ConstantU32(state, 63)); - index = Select(state, state.uint_type, add_tid, - Binary(state, OpIAdd, state.uint_type, index, lane), index); + index = Select(state, TypeU32(state), add_tid, + Binary(state, OpIAdd, TypeU32(state), index, lane), index); } const auto stride = - Binary(state, OpBitwiseAnd, state.uint_type, packed, ConstantU32(state, 0x3fffu)); + Binary(state, OpBitwiseAnd, TypeU32(state), packed, ConstantU32(state, 0x3fffu)); const auto swizzle = - Binary(state, OpBitwiseAnd, state.uint_type, - Binary(state, OpShiftRightLogical, state.uint_type, packed, ConstantU32(state, 14)), + Binary(state, OpBitwiseAnd, TypeU32(state), + Binary(state, OpShiftRightLogical, TypeU32(state), packed, ConstantU32(state, 14)), ConstantU32(state, 1)); const auto use_swizzle = - Binary(state, OpLogicalAnd, state.bool_type, - Binary(state, OpINotEqual, state.bool_type, stride, ConstantU32(state, 0)), - Binary(state, OpINotEqual, state.bool_type, swizzle, ConstantU32(state, 0))); + Binary(state, OpLogicalAnd, TypeBool(state), + Binary(state, OpINotEqual, TypeBool(state), stride, ConstantU32(state, 0)), + Binary(state, OpINotEqual, TypeBool(state), swizzle, ConstantU32(state, 0))); const auto stride_enum = - Binary(state, OpBitwiseAnd, state.uint_type, - Binary(state, OpShiftRightLogical, state.uint_type, packed, ConstantU32(state, 16)), + Binary(state, OpBitwiseAnd, TypeU32(state), + Binary(state, OpShiftRightLogical, TypeU32(state), packed, ConstantU32(state, 16)), ConstantU32(state, 3)); const auto index_shift = - Binary(state, OpIAdd, state.uint_type, stride_enum, ConstantU32(state, 3)); + Binary(state, OpIAdd, TypeU32(state), stride_enum, ConstantU32(state, 3)); const auto index_stride = - Binary(state, OpShiftLeftLogical, state.uint_type, ConstantU32(state, 8), stride_enum); + Binary(state, OpShiftLeftLogical, TypeU32(state), ConstantU32(state, 8), stride_enum); const auto index_mask = - Binary(state, OpISub, state.uint_type, index_stride, ConstantU32(state, 1)); - offset = Binary(state, OpIAdd, state.uint_type, offset, ConstantU32(state, mem.offset)); - const auto linear = Binary(state, OpIAdd, state.uint_type, - Binary(state, OpIMul, state.uint_type, index, stride), offset); - const auto index_msb = Binary(state, OpShiftRightLogical, state.uint_type, index, index_shift); - const auto index_lsb = Binary(state, OpBitwiseAnd, state.uint_type, index, index_mask); + Binary(state, OpISub, TypeU32(state), index_stride, ConstantU32(state, 1)); + offset = Binary(state, OpIAdd, TypeU32(state), offset, ConstantU32(state, mem.offset)); + const auto linear = Binary(state, OpIAdd, TypeU32(state), + Binary(state, OpIMul, TypeU32(state), index, stride), offset); + const auto index_msb = Binary(state, OpShiftRightLogical, TypeU32(state), index, index_shift); + const auto index_lsb = Binary(state, OpBitwiseAnd, TypeU32(state), index, index_mask); const auto offset_msb = - Binary(state, OpBitwiseAnd, state.uint_type, offset, ConstantU32(state, ~3u)); + Binary(state, OpBitwiseAnd, TypeU32(state), offset, ConstantU32(state, ~3u)); const auto offset_lsb = - Binary(state, OpBitwiseAnd, state.uint_type, offset, ConstantU32(state, 3)); + Binary(state, OpBitwiseAnd, TypeU32(state), offset, ConstantU32(state, 3)); const auto msb = - Binary(state, OpIMul, state.uint_type, - Binary(state, OpIAdd, state.uint_type, - Binary(state, OpIMul, state.uint_type, index_msb, stride), offset_msb), + Binary(state, OpIMul, TypeU32(state), + Binary(state, OpIAdd, TypeU32(state), + Binary(state, OpIMul, TypeU32(state), index_msb, stride), offset_msb), index_stride); const auto lsb = - Binary(state, OpIAdd, state.uint_type, - Binary(state, OpShiftLeftLogical, state.uint_type, index_lsb, ConstantU32(state, 2)), + Binary(state, OpIAdd, TypeU32(state), + Binary(state, OpShiftLeftLogical, TypeU32(state), index_lsb, ConstantU32(state, 2)), offset_lsb); - const auto address = Select(state, state.uint_type, use_swizzle, - Binary(state, OpIAdd, state.uint_type, msb, lsb), linear); - return Binary(state, OpIAdd, state.uint_type, address, soffset); + const auto address = Select(state, TypeU32(state), use_swizzle, + Binary(state, OpIAdd, TypeU32(state), msb, lsb), linear); + return Binary(state, OpIAdd, TypeU32(state), address, soffset); } uint32_t AddU64Low(EmitterState& state, uint32_t low, uint32_t high, uint32_t add_low, uint32_t add_high, uint32_t& out_high) { - const auto result = Binary(state, OpIAdd, state.uint_type, low, add_low); - const auto carry = Binary(state, OpULessThan, state.bool_type, result, low); - out_high = Binary( - state, OpIAdd, state.uint_type, Binary(state, OpIAdd, state.uint_type, high, add_high), - Select(state, state.uint_type, carry, ConstantU32(state, 1), ConstantU32(state, 0))); + const auto result = Binary(state, OpIAdd, TypeU32(state), low, add_low); + const auto carry = Binary(state, OpULessThan, TypeBool(state), result, low); + out_high = + Binary(state, OpIAdd, TypeU32(state), Binary(state, OpIAdd, TypeU32(state), high, add_high), + Select(state, TypeU32(state), carry, ConstantU32(state, 1), ConstantU32(state, 0))); return result; } @@ -106,15 +106,15 @@ uint32_t AddressByteAddress(ValueEmitContext& ctx, const IR::Inst& inst, const I auto immediate = static_cast(mem.offset); if (mem.kind == IR::ResourceKind::ScalarAddress) { immediate = static_cast(static_cast(immediate) & ~3u); - low = Binary(state, OpBitwiseAnd, state.uint_type, low, ConstantU32(state, ~3u)); + low = Binary(state, OpBitwiseAnd, TypeU32(state), low, ConstantU32(state, ~3u)); } const auto immediate_low = ConstantU32(state, static_cast(immediate)); const auto immediate_high = ConstantU32(state, immediate < 0 ? UINT32_MAX : 0u); const auto base = state.program.info.addresses[mem.resource].specialized_base; if (mem.kind == IR::ResourceKind::Scratch) { low = AddU64Low(state, low, high, immediate_low, immediate_high, high); - const auto valid = Binary(state, OpIEqual, state.bool_type, high, ConstantU32(state, 0)); - return Select(state, state.uint_type, valid, low, ConstantU32(state, UINT32_MAX)); + const auto valid = Binary(state, OpIEqual, TypeBool(state), high, ConstantU32(state, 0)); + return Select(state, TypeU32(state), valid, low, ConstantU32(state, UINT32_MAX)); } if (mem.kind == IR::ResourceKind::Flat || (mem.kind == IR::ResourceKind::Global && @@ -122,22 +122,22 @@ uint32_t AddressByteAddress(ValueEmitContext& ctx, const IR::Inst& inst, const I low = AddU64Low(state, low, high, immediate_low, immediate_high, high); const auto base_low = ConstantU32(state, static_cast(base)); const auto base_high = ConstantU32(state, static_cast(base >> 32u)); - const auto relative = Binary(state, OpISub, state.uint_type, low, base_low); - const auto borrow = Binary(state, OpULessThan, state.bool_type, low, base_low); + const auto relative = Binary(state, OpISub, TypeU32(state), low, base_low); + const auto borrow = Binary(state, OpULessThan, TypeBool(state), low, base_low); const auto relative_high = Binary( - state, OpISub, state.uint_type, Binary(state, OpISub, state.uint_type, high, base_high), - Select(state, state.uint_type, borrow, ConstantU32(state, 1), ConstantU32(state, 0))); + state, OpISub, TypeU32(state), Binary(state, OpISub, TypeU32(state), high, base_high), + Select(state, TypeU32(state), borrow, ConstantU32(state, 1), ConstantU32(state, 0))); const auto valid = - Binary(state, OpIEqual, state.bool_type, relative_high, ConstantU32(state, 0)); - return Select(state, state.uint_type, valid, relative, ConstantU32(state, UINT32_MAX)); + Binary(state, OpIEqual, TypeBool(state), relative_high, ConstantU32(state, 0)); + return Select(state, TypeU32(state), valid, relative, ConstantU32(state, UINT32_MAX)); } const auto initial = base + static_cast(static_cast(immediate)); auto relative_high = ConstantU32(state, static_cast(initial >> 32u)); const auto relative = AddU64Low(state, ConstantU32(state, static_cast(initial)), relative_high, low, ConstantU32(state, 0), relative_high); const auto valid = - Binary(state, OpIEqual, state.bool_type, relative_high, ConstantU32(state, 0)); - return Select(state, state.uint_type, valid, relative, ConstantU32(state, UINT32_MAX)); + Binary(state, OpIEqual, TypeBool(state), relative_high, ConstantU32(state, 0)); + return Select(state, TypeU32(state), valid, relative, ConstantU32(state, UINT32_MAX)); } uint32_t ByteAddress(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem) { @@ -152,7 +152,7 @@ uint32_t ByteAddress(ValueEmitContext& ctx, const IR::Inst& inst, const IR::Memo } uint32_t DwordIndex(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem) { - return Binary(ctx.state, OpShiftRightLogical, ctx.state.uint_type, ByteAddress(ctx, inst, mem), + return Binary(ctx.state, OpShiftRightLogical, TypeU32(ctx.state), ByteAddress(ctx, inst, mem), ConstantU32(ctx.state, 2)); } @@ -179,7 +179,7 @@ uint32_t LoadWord(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo me return EmitValueOrZeroIfCondition(ctx.state, InBounds(ctx, mem, index, pc), [&]() { const auto value = ctx.state.builder.AllocateId(); ctx.state.builder.AddFunction( - {OpLoad, ctx.state.uint_type, value, Pointer(ctx, mem, index, pc)}); + {OpLoad, TypeU32(ctx.state), value, Pointer(ctx, mem, index, pc)}); return value; }); }); @@ -191,24 +191,24 @@ uint32_t LoadSubword(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo const auto pc = inst.Flags().pc; return EmitValueOrZeroIfCondition(ctx.state, ctx.Arg(inst, inst.NumArgs() - 1), [&]() { const auto address = ByteAddress(ctx, inst, mem); - const auto index = Binary(ctx.state, OpShiftRightLogical, ctx.state.uint_type, address, + const auto index = Binary(ctx.state, OpShiftRightLogical, TypeU32(ctx.state), address, ConstantU32(ctx.state, 2)); return EmitValueOrZeroIfCondition(ctx.state, InBounds(ctx, mem, index, pc), [&]() { const auto word = ctx.state.builder.AllocateId(); ctx.state.builder.AddFunction( - {OpLoad, ctx.state.uint_type, word, Pointer(ctx, mem, index, pc)}); - const auto byte = Binary(ctx.state, OpBitwiseAnd, ctx.state.uint_type, address, + {OpLoad, TypeU32(ctx.state), word, Pointer(ctx, mem, index, pc)}); + const auto byte = Binary(ctx.state, OpBitwiseAnd, TypeU32(ctx.state), address, ConstantU32(ctx.state, 3)); - const auto shift = Binary(ctx.state, OpShiftLeftLogical, ctx.state.uint_type, byte, + const auto shift = Binary(ctx.state, OpShiftLeftLogical, TypeU32(ctx.state), byte, ConstantU32(ctx.state, 3)); const auto value = - Binary(ctx.state, OpBitwiseAnd, ctx.state.uint_type, - Binary(ctx.state, OpShiftRightLogical, ctx.state.uint_type, word, shift), + Binary(ctx.state, OpBitwiseAnd, TypeU32(ctx.state), + Binary(ctx.state, OpShiftRightLogical, TypeU32(ctx.state), word, shift), ConstantU32(ctx.state, bits == 8u ? 0xffu : 0xffffu)); if (!sign_extend) return value; - const auto left = Binary(ctx.state, OpShiftLeftLogical, ctx.state.uint_type, value, + const auto left = Binary(ctx.state, OpShiftLeftLogical, TypeU32(ctx.state), value, ConstantU32(ctx.state, 32u - bits)); - return Binary(ctx.state, OpShiftRightArithmetic, ctx.state.uint_type, left, + return Binary(ctx.state, OpShiftRightArithmetic, TypeU32(ctx.state), left, ConstantU32(ctx.state, 32u - bits)); }); }); @@ -256,16 +256,16 @@ uint32_t FormattedLoad(ValueEmitContext& ctx, const IR::Inst& inst, const IR::Me if (info.packed_bitfield) { raw = LoadWord(ctx, inst, RebaseFormattedComponent(mem, format, component)); const auto type = - IsSignedFormatComponent(info.type) ? ctx.state.int_type : ctx.state.uint_type; + IsSignedFormatComponent(info.type) ? TypeI32(ctx.state) : TypeU32(ctx.state); const auto source = - type == ctx.state.int_type ? Unary(ctx.state, OpBitcast, type, raw) : raw; + type == TypeI32(ctx.state) ? Unary(ctx.state, OpBitcast, type, raw) : raw; const auto extracted = ctx.state.builder.AllocateId(); ctx.state.builder.AddFunction( {IsSignedFormatComponent(info.type) ? OpBitFieldSExtract : OpBitFieldUExtract, type, extracted, source, ConstantU32(ctx.state, info.component_bit_offset[component]), ConstantU32(ctx.state, bits)}); - raw = type == ctx.state.int_type - ? Unary(ctx.state, OpBitcast, ctx.state.uint_type, extracted) + raw = type == TypeI32(ctx.state) + ? Unary(ctx.state, OpBitcast, TypeU32(ctx.state), extracted) : extracted; } else if (bits == 32u) { raw = LoadWord(ctx, inst, RebaseFormattedComponent(mem, format, component)); @@ -290,17 +290,17 @@ uint32_t AtomicUpdate(EmitterState& state, uint32_t pointer, IR::ResourceKind ki const auto exchanged = state.builder.AllocateId(); state.builder.AddFunction({OpBranch, preheader}); state.builder.AddFunction({OpLabel, preheader}); - state.builder.AddFunction({OpAtomicLoad, state.uint_type, initial, pointer, + state.builder.AddFunction({OpAtomicLoad, TypeU32(state), initial, pointer, ConstantU32(state, scope), ConstantU32(state, MemorySemanticsNone)}); state.builder.AddFunction({OpBranch, header}); state.builder.AddFunction({OpLabel, header}); state.builder.AddFunction( - {OpPhi, state.uint_type, observed, initial, preheader, exchanged, cont}); + {OpPhi, TypeU32(state), observed, initial, preheader, exchanged, cont}); const auto next = desired(observed); - state.builder.AddFunction({OpAtomicCompareExchange, state.uint_type, exchanged, pointer, + state.builder.AddFunction({OpAtomicCompareExchange, TypeU32(state), exchanged, pointer, ConstantU32(state, scope), ConstantU32(state, MemorySemanticsNone), ConstantU32(state, MemorySemanticsNone), next, observed}); - const auto success = Binary(state, OpIEqual, state.bool_type, exchanged, observed); + const auto success = Binary(state, OpIEqual, TypeBool(state), exchanged, observed); state.builder.AddFunction({OpLoopMerge, merge, cont, LoopControlNone}); state.builder.AddFunction({OpBranchConditional, success, merge, cont}); state.builder.AddFunction({OpLabel, cont}); @@ -317,32 +317,32 @@ void StoreSubword(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo me const auto pc = inst.Flags().pc; EmitIfCondition(ctx.state, ctx.Arg(inst, inst.NumArgs() - 1), [&]() { const auto address = ByteAddress(ctx, inst, mem); - const auto index = Binary(ctx.state, OpShiftRightLogical, ctx.state.uint_type, address, + const auto index = Binary(ctx.state, OpShiftRightLogical, TypeU32(ctx.state), address, ConstantU32(ctx.state, 2)); EmitIfCondition(ctx.state, InBounds(ctx, mem, index, pc), [&]() { const auto pointer = Pointer(ctx, mem, index, pc); - const auto shift = Binary(ctx.state, OpShiftLeftLogical, ctx.state.uint_type, - Binary(ctx.state, OpBitwiseAnd, ctx.state.uint_type, address, - ConstantU32(ctx.state, 3)), - ConstantU32(ctx.state, 3)); - const auto mask = Binary(ctx.state, OpShiftLeftLogical, ctx.state.uint_type, + const auto shift = Binary(ctx.state, OpShiftLeftLogical, TypeU32(ctx.state), + Binary(ctx.state, OpBitwiseAnd, TypeU32(ctx.state), address, + ConstantU32(ctx.state, 3)), + ConstantU32(ctx.state, 3)); + const auto mask = Binary(ctx.state, OpShiftLeftLogical, TypeU32(ctx.state), ConstantU32(ctx.state, bits == 8u ? 0xffu : 0xffffu), shift); - const auto value = Binary(ctx.state, OpShiftLeftLogical, ctx.state.uint_type, - Binary(ctx.state, OpBitwiseAnd, ctx.state.uint_type, + const auto value = Binary(ctx.state, OpShiftLeftLogical, TypeU32(ctx.state), + Binary(ctx.state, OpBitwiseAnd, TypeU32(ctx.state), ctx.Arg(inst, inst.NumArgs() - 2), ConstantU32(ctx.state, bits == 8u ? 0xffu : 0xffffu)), shift); const auto merge = [&](uint32_t old) { - return Binary(ctx.state, OpBitwiseOr, ctx.state.uint_type, - Binary(ctx.state, OpBitwiseAnd, ctx.state.uint_type, old, - Unary(ctx.state, OpNot, ctx.state.uint_type, mask)), + return Binary(ctx.state, OpBitwiseOr, TypeU32(ctx.state), + Binary(ctx.state, OpBitwiseAnd, TypeU32(ctx.state), old, + Unary(ctx.state, OpNot, TypeU32(ctx.state), mask)), value); }; if (mem.kind == IR::ResourceKind::Lds || mem.kind == IR::ResourceKind::Gds) { AtomicUpdate(ctx.state, pointer, mem.kind, merge); } else { const auto old = ctx.state.builder.AllocateId(); - ctx.state.builder.AddFunction({OpLoad, ctx.state.uint_type, old, pointer}); + ctx.state.builder.AddFunction({OpLoad, TypeU32(ctx.state), old, pointer}); ctx.state.builder.AddFunction({OpStore, pointer, merge(old)}); } }); @@ -425,7 +425,7 @@ uint32_t EmitAtomic(ValueEmitContext& ctx, const IR::Inst& inst, const IR::Memor const auto value = ctx.Arg(inst, inst.NumArgs() - 2); const auto old = ctx.state.builder.AllocateId(); const auto scope = mem.kind == IR::ResourceKind::Lds ? ScopeWorkgroup : ScopeDevice; - ctx.state.builder.AddFunction({AtomicOpcode(inst.GetOpcode()), ctx.state.uint_type, + ctx.state.builder.AddFunction({AtomicOpcode(inst.GetOpcode()), TypeU32(ctx.state), old, Pointer(ctx, mem, index, pc), ConstantU32(ctx.state, scope), ConstantU32(ctx.state, MemorySemanticsNone), value}); @@ -452,29 +452,29 @@ uint32_t FloatAtomicReplacement(EmitterState& state, uint32_t old, uint32_t sour uint32_t key; }; const auto classify = [&](uint32_t bits) { - const auto value = Unary(state, OpBitcast, state.float_type, bits); + const auto value = Unary(state, OpBitcast, TypeF32(state), bits); const auto cls = EmitClassifyF32(state, value); const auto negative = Binary( - state, OpINotEqual, state.bool_type, - Binary(state, OpBitwiseAnd, state.uint_type, bits, ConstantU32(state, 0x80000000u)), + state, OpINotEqual, TypeBool(state), + Binary(state, OpBitwiseAnd, TypeU32(state), bits, ConstantU32(state, 0x80000000u)), ConstantU32(state, 0)); - const auto negative_key = Unary(state, OpNot, state.uint_type, bits); + const auto negative_key = Unary(state, OpNot, TypeU32(state), bits); const auto positive_key = - Binary(state, OpBitwiseXor, state.uint_type, bits, ConstantU32(state, 0x80000000u)); + Binary(state, OpBitwiseXor, TypeU32(state), bits, ConstantU32(state, 0x80000000u)); return OrderedBits {cls.nan, cls.zero, - Select(state, state.uint_type, negative, negative_key, positive_key)}; + Select(state, TypeU32(state), negative, negative_key, positive_key)}; }; const auto source_class = classify(source); const auto old_class = classify(old); const auto unordered = - Binary(state, OpLogicalOr, state.bool_type, - Binary(state, OpLogicalOr, state.bool_type, source_class.nan, old_class.nan), - Binary(state, OpLogicalAnd, state.bool_type, source_class.zero, old_class.zero)); - const auto ordered = Unary(state, OpLogicalNot, state.bool_type, unordered); - const auto compare = Binary(state, max_value ? OpUGreaterThan : OpULessThan, state.bool_type, + Binary(state, OpLogicalOr, TypeBool(state), + Binary(state, OpLogicalOr, TypeBool(state), source_class.nan, old_class.nan), + Binary(state, OpLogicalAnd, TypeBool(state), source_class.zero, old_class.zero)); + const auto ordered = Unary(state, OpLogicalNot, TypeBool(state), unordered); + const auto compare = Binary(state, max_value ? OpUGreaterThan : OpULessThan, TypeBool(state), source_class.key, old_class.key); - return Select(state, state.uint_type, - Binary(state, OpLogicalAnd, state.bool_type, ordered, compare), source, old); + return Select(state, TypeU32(state), + Binary(state, OpLogicalAnd, TypeBool(state), ordered, compare), source, old); } uint32_t FloatAtomic(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem, @@ -501,17 +501,17 @@ uint32_t SharedFloatAtomic(ValueEmitContext& ctx, const IR::Inst& inst, const IR ctx.state.builder.AddFunction({OpStore, ctx.scratch_u32_variable, ctx.Arg(inst, 2)}); const auto data = ctx.state.builder.AllocateId(); ctx.state.builder.AddFunction( - {OpLoad, ctx.state.uint_type, data, ctx.scratch_u32_variable}); + {OpLoad, TypeU32(ctx.state), data, ctx.scratch_u32_variable}); AtomicUpdate(ctx.state, Pointer(ctx, mem, index, pc), mem.kind, [&](uint32_t old) { - const auto old_f = Unary(ctx.state, OpBitcast, ctx.state.float_type, old); + const auto old_f = Unary(ctx.state, OpBitcast, TypeF32(ctx.state), old); const auto compare_f = - Unary(ctx.state, OpBitcast, ctx.state.float_type, ctx.Arg(inst, 3)); - const auto data_f = Unary(ctx.state, OpBitcast, ctx.state.float_type, data); + Unary(ctx.state, OpBitcast, TypeF32(ctx.state), ctx.Arg(inst, 3)); + const auto data_f = Unary(ctx.state, OpBitcast, TypeF32(ctx.state), data); const auto compare = Binary( - ctx.state, max_value ? OpFOrdGreaterThan : OpFOrdLessThan, ctx.state.bool_type, + ctx.state, max_value ? OpFOrdGreaterThan : OpFOrdLessThan, TypeBool(ctx.state), max_value ? old_f : compare_f, max_value ? compare_f : old_f); - return Unary(ctx.state, OpBitcast, ctx.state.uint_type, - Select(ctx.state, ctx.state.float_type, compare, data_f, old_f)); + return Unary(ctx.state, OpBitcast, TypeU32(ctx.state), + Select(ctx.state, TypeF32(ctx.state), compare, data_f, old_f)); }); }); }); @@ -522,49 +522,49 @@ uint32_t AppendConsume(ValueEmitContext& ctx, const IR::Inst& inst, bool gds, bo auto& state = ctx.state; const auto m0 = ctx.Arg(inst, 1); const auto base = - Binary(state, OpShiftRightLogical, state.uint_type, m0, ConstantU32(state, 16)); - const auto size = Binary(state, OpBitwiseAnd, state.uint_type, m0, ConstantU32(state, 0xffffu)); + Binary(state, OpShiftRightLogical, TypeU32(state), m0, ConstantU32(state, 16)); + const auto size = Binary(state, OpBitwiseAnd, TypeU32(state), m0, ConstantU32(state, 0xffffu)); const auto address = - Binary(state, OpIAdd, state.uint_type, base, ConstantU32(state, ctx.Memory(inst).offset)); + Binary(state, OpIAdd, TypeU32(state), base, ConstantU32(state, ctx.Memory(inst).offset)); const auto index = - Binary(state, OpShiftRightLogical, state.uint_type, address, ConstantU32(state, 2)); + Binary(state, OpShiftRightLogical, TypeU32(state), address, ConstantU32(state, 2)); const auto exec = ctx.Arg(inst, 2); const auto ballot = state.builder.AllocateId(); - state.builder.AddFunction({OpGroupNonUniformBallot, state.vec4_uint_type, ballot, + state.builder.AddFunction({OpGroupNonUniformBallot, TypeU32Vector(state, 4), ballot, ConstantU32(state, ScopeSubgroup), exec}); const auto low = state.builder.AllocateId(); const auto high = state.builder.AllocateId(); - state.builder.AddFunction({OpCompositeExtract, state.uint_type, low, ballot, 0}); - state.builder.AddFunction({OpCompositeExtract, state.uint_type, high, ballot, 1}); + state.builder.AddFunction({OpCompositeExtract, TypeU32(state), low, ballot, 0}); + state.builder.AddFunction({OpCompositeExtract, TypeU32(state), high, ballot, 1}); const auto count_low = state.per_invocation_masks ? low : ctx.Arg(inst, 3); const auto count_high = state.per_invocation_masks ? high : ctx.Arg(inst, 4); const auto count = - Binary(state, OpIAdd, state.uint_type, Unary(state, OpBitCount, state.uint_type, count_low), - Unary(state, OpBitCount, state.uint_type, count_high)); + Binary(state, OpIAdd, TypeU32(state), Unary(state, OpBitCount, TypeU32(state), count_low), + Unary(state, OpBitCount, TypeU32(state), count_high)); const auto first = state.builder.AllocateId(); - state.builder.AddFunction({OpGroupNonUniformBallotFindLSB, state.uint_type, first, + state.builder.AddFunction({OpGroupNonUniformBallotFindLSB, TypeU32(state), first, ConstantU32(state, ScopeSubgroup), ballot}); const auto is_first = - Binary(state, OpIEqual, state.bool_type, EmitSubgroupLocalInvocationId(state), first); + Binary(state, OpIEqual, TypeBool(state), EmitSubgroupLocalInvocationId(state), first); const auto storage_bounds = gds ? EmitGdsElementInBounds(state, index) : EmitLdsElementInBounds(state, index); const auto m0_bounds = - gds ? Binary(state, OpINotEqual, state.bool_type, size, ConstantU32(state, 0)) - : Binary(state, OpULessThan, state.bool_type, + gds ? Binary(state, OpINotEqual, TypeBool(state), size, ConstantU32(state, 0)) + : Binary(state, OpULessThan, TypeBool(state), ConstantU32(state, ctx.Memory(inst).offset + 3u), size); const auto condition = AndCondition( state, is_first, AndCondition(state, exec, AndCondition(state, storage_bounds, m0_bounds))); const auto atomic = EmitValueOrZeroIfCondition(state, condition, [&]() { const auto value = state.builder.AllocateId(); state.builder.AddFunction( - {append ? OpAtomicIAdd : OpAtomicISub, state.uint_type, value, + {append ? OpAtomicIAdd : OpAtomicISub, TypeU32(state), value, gds ? EmitGdsElementPointer(state, index) : EmitLdsElementPointer(state, index), ConstantU32(state, gds ? ScopeDevice : ScopeWorkgroup), ConstantU32(state, MemorySemanticsNone), count}); return value; }); const auto result = state.builder.AllocateId(); - state.builder.AddFunction({OpGroupNonUniformShuffle, state.uint_type, result, + state.builder.AddFunction({OpGroupNonUniformShuffle, TypeU32(state), result, ConstantU32(state, ScopeSubgroup), atomic, first}); return result; } @@ -584,25 +584,25 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) { return true; } const auto pointer = state.builder.AllocateId(); - state.builder.AddFunction({OpAccessChain, state.ptr_storage_buffer_uint, pointer, + state.builder.AddFunction({OpAccessChain, TypeStorageBufferElementPointer(state), pointer, state.flattened_srt_variable, ConstantU32(state, 0), ctx.Arg(inst, 1)}); ctx.Emit(inst, OpLoad, IR::Type::U32, {pointer}); return true; } if (op == IR::ValueOpcode::ReadConstBuffer) { - auto mem = ctx.Memory(inst); - mem.kind = IR::ResourceKind::ScalarBuffer; - const auto address = Binary(state, OpIAdd, state.uint_type, ctx.Arg(inst, 1), - ConstantU32(state, mem.offset)); + auto mem = ctx.Memory(inst); + mem.kind = IR::ResourceKind::ScalarBuffer; + const auto address = + Binary(state, OpIAdd, TypeU32(state), ctx.Arg(inst, 1), ConstantU32(state, mem.offset)); const auto index = - Binary(state, OpShiftRightLogical, state.uint_type, address, ConstantU32(state, 2)); + Binary(state, OpShiftRightLogical, TypeU32(state), address, ConstantU32(state, 2)); const auto pc = inst.Flags().pc; const auto condition = EmitStorageBufferElementInBounds(state, mem, index, pc); ctx.Define(inst, EmitValueOrZeroIfCondition(state, condition, [&]() { const auto value = state.builder.AllocateId(); state.builder.AddFunction( - {OpLoad, state.uint_type, value, + {OpLoad, TypeU32(state), value, EmitStorageBufferElementPointer(state, mem, index, pc)}); return value; })); @@ -707,20 +707,20 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) { ctx.state.builder.AddFunction({OpStore, ctx.scratch_u32_variable, ctx.Arg(inst, 0)}); const auto source = ctx.state.builder.AllocateId(); ctx.state.builder.AddFunction( - {OpLoad, ctx.state.uint_type, source, ctx.scratch_u32_variable}); + {OpLoad, TypeU32(ctx.state), source, ctx.scratch_u32_variable}); const auto target = EmitDsSwizzleTargetLane(state, EmitSubgroupLocalInvocationId(state), inst.Arg(1).IsImmediate() ? inst.Arg(1).U32() : 0); const auto shuffled = state.builder.AllocateId(); - state.builder.AddFunction({OpGroupNonUniformShuffle, state.uint_type, shuffled, + state.builder.AddFunction({OpGroupNonUniformShuffle, TypeU32(state), shuffled, ConstantU32(state, ScopeSubgroup), source, target}); const auto source_exec = state.builder.AllocateId(); - state.builder.AddFunction({OpGroupNonUniformShuffle, state.bool_type, source_exec, + state.builder.AddFunction({OpGroupNonUniformShuffle, TypeBool(state), source_exec, ConstantU32(state, ScopeSubgroup), ctx.Arg(inst, 2), target}); const auto source_active = AndCondition(state, source_exec, EmitSubgroupLaneActiveBool(state, target)); ctx.Define(inst, - Select(state, state.uint_type, source_active, shuffled, ConstantU32(state, 0))); + Select(state, TypeU32(state), source_active, shuffled, ConstantU32(state, 0))); return true; } return false; diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp index 1856a4694..692753b37 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp @@ -6,19 +6,19 @@ uint32_t EmitShaderDataDwordLoad(EmitterState& state, uint32_t dword_index) { if (state.push_constant_variable != 0) { const auto pointer = state.builder.AllocateId(); const auto value = state.builder.AllocateId(); - state.builder.AddFunction({OpAccessChain, state.ptr_push_constant_uint, pointer, + state.builder.AddFunction({OpAccessChain, TypePushConstantElementPointer(state), pointer, state.push_constant_variable, ConstantU32(state, 0), ConstantU32(state, dword_index)}); - state.builder.AddFunction({OpLoad, state.uint_type, value, pointer}); + state.builder.AddFunction({OpLoad, TypeU32(state), value, pointer}); return value; } if (state.vsharp_storage_variable != 0) { const auto pointer = state.builder.AllocateId(); const auto value = state.builder.AllocateId(); - state.builder.AddFunction({OpAccessChain, state.ptr_storage_buffer_uint, pointer, + state.builder.AddFunction({OpAccessChain, TypeStorageBufferElementPointer(state), pointer, state.vsharp_storage_variable, ConstantU32(state, 0), ConstantU32(state, dword_index)}); - state.builder.AddFunction({OpLoad, state.uint_type, value, pointer}); + state.builder.AddFunction({OpLoad, TypeU32(state), value, pointer}); return value; } return ConstantU32(state, 0); @@ -26,13 +26,13 @@ uint32_t EmitShaderDataDwordLoad(EmitterState& state, uint32_t dword_index) { uint32_t EmitAddU32(EmitterState& state, uint32_t lhs, uint32_t rhs) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({OpIAdd, state.uint_type, ret, lhs, rhs}); + state.builder.AddFunction({OpIAdd, TypeU32(state), ret, lhs, rhs}); return ret; } uint32_t EmitBinaryU32(EmitterState& state, uint32_t opcode, uint32_t lhs, uint32_t rhs) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({opcode, state.uint_type, ret, lhs, rhs}); + state.builder.AddFunction({opcode, TypeU32(state), ret, lhs, rhs}); return ret; } @@ -103,14 +103,14 @@ uint32_t EmitStorageBufferObjectPointer(EmitterState& state, const IR::MemoryInf const auto binding = ResourceForDescriptor(state, IR::DescriptorBindingKind::AddressMemory, mem.resource); const auto pointer = state.builder.AllocateId(); - state.builder.AddFunction({OpAccessChain, state.ptr_storage_buffer, pointer, + state.builder.AddFunction({OpAccessChain, TypeStorageBufferPointer(state), pointer, state.address_memory_variable, ConstantU32(state, binding.array_index)}); return pointer; } const auto binding = StorageBufferBindingForMemory(state, mem, use_pc); const auto pointer = state.builder.AllocateId(); - state.builder.AddFunction({OpAccessChain, state.ptr_storage_buffer, pointer, + state.builder.AddFunction({OpAccessChain, TypeStorageBufferPointer(state), pointer, state.storage_buffer_variable, ConstantU32(state, binding.array_index)}); return pointer; @@ -122,8 +122,8 @@ uint32_t EmitStorageBufferElementInBounds(EmitterState& state, const IR::MemoryI const auto object = EmitStorageBufferObjectPointer(state, mem, use_pc); const auto length = state.builder.AllocateId(); const auto in_bounds = state.builder.AllocateId(); - state.builder.AddFunction({OpArrayLength, state.uint_type, length, object, 0}); - state.builder.AddFunction({OpULessThan, state.bool_type, in_bounds, index, length}); + state.builder.AddFunction({OpArrayLength, TypeU32(state), length, object, 0}); + state.builder.AddFunction({OpULessThan, TypeBool(state), in_bounds, index, length}); return in_bounds; } @@ -138,23 +138,18 @@ uint32_t EmitStorageBufferElementPointer(EmitterState& state, const IR::MemoryIn const auto binding = ResourceForDescriptor(state, IR::DescriptorBindingKind::AddressMemory, mem.resource); const auto pointer = state.builder.AllocateId(); - state.builder.AddFunction( - {OpAccessChain, state.ptr_storage_buffer_uint, pointer, state.address_memory_variable, - ConstantU32(state, binding.array_index), ConstantU32(state, 0), index}); + state.builder.AddFunction({OpAccessChain, TypeStorageBufferElementPointer(state), pointer, + state.address_memory_variable, + ConstantU32(state, binding.array_index), ConstantU32(state, 0), + index}); return pointer; } const auto binding = StorageBufferBindingForMemory(state, mem, use_pc); const auto pointer = state.builder.AllocateId(); - state.builder.AddFunction( - {OpAccessChain, state.ptr_storage_buffer_uint, pointer, state.storage_buffer_variable, - ConstantU32(state, binding.array_index), ConstantU32(state, 0), index}); - return pointer; -} - -uint32_t EmitLdsElementPointer(EmitterState& state, uint32_t index) { - const auto pointer = state.builder.AllocateId(); - state.builder.AddFunction( - {OpAccessChain, state.ptr_workgroup_uint, pointer, state.lds_variable, index}); + state.builder.AddFunction({OpAccessChain, TypeStorageBufferElementPointer(state), pointer, + state.storage_buffer_variable, + ConstantU32(state, binding.array_index), ConstantU32(state, 0), + index}); return pointer; } @@ -162,10 +157,34 @@ uint32_t LdsDwordCount(const EmitterState& state) { return state.stage == ShaderType::Compute ? state.input_info.compute->lds_size_dwords : 8192u; } +static void EnsureLdsStorage(EmitterState& state) { + if (state.lds_variable != 0) { + return; + } + if (state.stage != ShaderType::Compute) { + EXIT("function LDS was not prepared before SPIR-V function emission\n"); + } + state.lds_variable = state.builder.DefineGlobalVariable( + TypeLdsArrayPointer(state, StorageClassWorkgroup), StorageClassWorkgroup); + state.builder.AddName(state.lds_variable, "lds_dwords"); +} + +uint32_t EmitLdsElementPointer(EmitterState& state, uint32_t index) { + EnsureLdsStorage(state); + const auto pointer = state.builder.AllocateId(); + state.builder.AddFunction( + {OpAccessChain, + TypeLdsElementPointer(state, state.stage == ShaderType::Compute ? StorageClassWorkgroup + : StorageClassFunction), + pointer, state.lds_variable, index}); + return pointer; +} + uint32_t EmitLdsElementInBounds(EmitterState& state, uint32_t index) { + EnsureLdsStorage(state); const auto in_bounds = state.builder.AllocateId(); state.builder.AddFunction( - {OpULessThan, state.bool_type, in_bounds, index, ConstantU32(state, LdsDwordCount(state))}); + {OpULessThan, TypeBool(state), in_bounds, index, ConstantU32(state, LdsDwordCount(state))}); return in_bounds; } @@ -182,34 +201,33 @@ uint32_t EmitMemoryElementPointer(EmitterState& state, const IR::MemoryInfo& mem uint32_t EmitTBufferBitcastF32ToU32(EmitterState& state, uint32_t value) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({OpBitcast, state.uint_type, ret, value}); + state.builder.AddFunction({OpBitcast, TypeU32(state), ret, value}); return ret; } uint32_t EmitTBufferBitcastU32ToF32(EmitterState& state, uint32_t value) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({OpBitcast, state.float_type, ret, value}); + state.builder.AddFunction({OpBitcast, TypeF32(state), ret, value}); return ret; } uint32_t EmitTBufferBitcastU32ToI32(EmitterState& state, uint32_t value) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({OpBitcast, state.int_type, ret, value}); + state.builder.AddFunction({OpBitcast, TypeI32(state), ret, value}); return ret; } uint32_t EmitTBufferCompareU32Constant(EmitterState& state, uint32_t opcode, uint32_t value, uint32_t constant) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction({opcode, state.bool_type, ret, value, ConstantU32(state, constant)}); + state.builder.AddFunction({opcode, TypeBool(state), ret, value, ConstantU32(state, constant)}); return ret; } uint32_t EmitTBufferSelectF32(EmitterState& state, uint32_t condition, uint32_t true_value, uint32_t false_value) { const auto ret = state.builder.AllocateId(); - state.builder.AddFunction( - {OpSelect, state.float_type, ret, condition, true_value, false_value}); + state.builder.AddFunction({OpSelect, TypeF32(state), ret, condition, true_value, false_value}); return ret; } @@ -222,8 +240,8 @@ uint32_t EmitHalfToF32Bits(EmitterState& state, uint32_t raw) { const auto unpacked = state.builder.AllocateId(); const auto value = state.builder.AllocateId(); state.builder.AddFunction( - {OpExtInst, state.vec2_float_type, unpacked, state.glsl_std450, GlslUnpackHalf2x16, raw}); - state.builder.AddFunction({OpCompositeExtract, state.float_type, value, unpacked, 0}); + {OpExtInst, TypeF32Vector(state, 2), unpacked, GlslStd450(state), GlslUnpackHalf2x16, raw}); + state.builder.AddFunction({OpCompositeExtract, TypeF32(state), value, unpacked, 0}); return EmitTBufferBitcastF32ToU32(state, value); } @@ -251,9 +269,9 @@ uint32_t EmitUFloatToF32Bits(EmitterState& state, uint32_t raw, uint32_t bits) { const auto mantissa_f32 = state.builder.AllocateId(); const auto subnormal = state.builder.AllocateId(); - state.builder.AddFunction({OpConvertUToF, state.float_type, mantissa_f32, mantissa}); + state.builder.AddFunction({OpConvertUToF, TypeF32(state), mantissa_f32, mantissa}); state.builder.AddFunction( - {OpFMul, state.float_type, subnormal, mantissa_f32, + {OpFMul, TypeF32(state), subnormal, mantissa_f32, ConstantF32Value(state, std::ldexp(1.0f, 1 - 15 - static_cast(mantissa_bits)))}); const auto zero_exp = EmitTBufferCompareU32Constant(state, OpIEqual, exponent, 0); @@ -271,22 +289,22 @@ uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferForma case Format::ComponentType::Sint: return raw; case Format::ComponentType::Uscaled: { const auto value = state.builder.AllocateId(); - state.builder.AddFunction({OpConvertUToF, state.float_type, value, raw}); + state.builder.AddFunction({OpConvertUToF, TypeF32(state), value, raw}); return EmitTBufferBitcastF32ToU32(state, value); } case Format::ComponentType::Sscaled: { const auto signed_raw = EmitTBufferBitcastU32ToI32(state, raw); const auto value = state.builder.AllocateId(); - state.builder.AddFunction({OpConvertSToF, state.float_type, value, signed_raw}); + state.builder.AddFunction({OpConvertSToF, TypeF32(state), value, signed_raw}); return EmitTBufferBitcastF32ToU32(state, value); } case Format::ComponentType::Unorm: { const auto value = state.builder.AllocateId(); const auto normalized = state.builder.AllocateId(); const auto max_value = static_cast((1u << bits) - 1u); - state.builder.AddFunction({OpConvertUToF, state.float_type, value, raw}); + state.builder.AddFunction({OpConvertUToF, TypeF32(state), value, raw}); state.builder.AddFunction( - {OpFDiv, state.float_type, normalized, value, ConstantF32Value(state, max_value)}); + {OpFDiv, TypeF32(state), normalized, value, ConstantF32Value(state, max_value)}); return EmitTBufferBitcastF32ToU32(state, normalized); } case Format::ComponentType::Snorm: { @@ -295,10 +313,10 @@ uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferForma const auto normalized = state.builder.AllocateId(); const auto clamped = state.builder.AllocateId(); const auto max_value = static_cast((1u << (bits - 1u)) - 1u); - state.builder.AddFunction({OpConvertSToF, state.float_type, value, signed_raw}); + state.builder.AddFunction({OpConvertSToF, TypeF32(state), value, signed_raw}); state.builder.AddFunction( - {OpFDiv, state.float_type, normalized, value, ConstantF32Value(state, max_value)}); - state.builder.AddFunction({OpExtInst, state.float_type, clamped, state.glsl_std450, + {OpFDiv, TypeF32(state), normalized, value, ConstantF32Value(state, max_value)}); + state.builder.AddFunction({OpExtInst, TypeF32(state), clamped, GlslStd450(state), GlslFMax, normalized, ConstantF32Value(state, -1.0f)}); return EmitTBufferBitcastF32ToU32(state, clamped); } @@ -323,14 +341,14 @@ void EmitDeviceAtomicMemoryBarrier(EmitterState& state) { uint32_t EmitGdsElementInBounds(EmitterState& state, uint32_t index) { const auto length = state.builder.AllocateId(); const auto in_bounds = state.builder.AllocateId(); - state.builder.AddFunction({OpArrayLength, state.uint_type, length, state.gds_variable, 0}); - state.builder.AddFunction({OpULessThan, state.bool_type, in_bounds, index, length}); + state.builder.AddFunction({OpArrayLength, TypeU32(state), length, state.gds_variable, 0}); + state.builder.AddFunction({OpULessThan, TypeBool(state), in_bounds, index, length}); return in_bounds; } uint32_t EmitGdsElementPointer(EmitterState& state, uint32_t index) { const auto pointer = state.builder.AllocateId(); - state.builder.AddFunction({OpAccessChain, state.ptr_storage_buffer_uint, pointer, + state.builder.AddFunction({OpAccessChain, TypeStorageBufferElementPointer(state), pointer, state.gds_variable, ConstantU32(state, 0), index}); return pointer; } @@ -358,16 +376,16 @@ uint32_t EmitDsSwizzleTargetLane(EmitterState& state, uint32_t subid, uint32_t c const auto base = state.builder.AllocateId(); const auto target = state.builder.AllocateId(); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, lane2, subid, ConstantU32(state, 3)}); + {OpBitwiseAnd, TypeU32(state), lane2, subid, ConstantU32(state, 3)}); state.builder.AddFunction( - {OpShiftLeftLogical, state.uint_type, shift, lane2, ConstantU32(state, 1)}); + {OpShiftLeftLogical, TypeU32(state), shift, lane2, ConstantU32(state, 1)}); state.builder.AddFunction( - {OpShiftRightLogical, state.uint_type, perm0, ConstantU32(state, control), shift}); + {OpShiftRightLogical, TypeU32(state), perm0, ConstantU32(state, control), shift}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, perm, perm0, ConstantU32(state, 3)}); + {OpBitwiseAnd, TypeU32(state), perm, perm0, ConstantU32(state, 3)}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, base, subid, ConstantU32(state, 0xfffffffcu)}); - state.builder.AddFunction({OpBitwiseOr, state.uint_type, target, base, perm}); + {OpBitwiseAnd, TypeU32(state), base, subid, ConstantU32(state, 0xfffffffcu)}); + state.builder.AddFunction({OpBitwiseOr, TypeU32(state), target, base, perm}); return target; } @@ -377,16 +395,16 @@ uint32_t EmitDsSwizzleTargetLane(EmitterState& state, uint32_t subid, uint32_t c const auto xored = state.builder.AllocateId(); const auto base = state.builder.AllocateId(); const auto target = state.builder.AllocateId(); - state.builder.AddFunction({OpBitwiseAnd, state.uint_type, lane, subid, ConstantU32(state, 31)}); + state.builder.AddFunction({OpBitwiseAnd, TypeU32(state), lane, subid, ConstantU32(state, 31)}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, masked, lane, ConstantU32(state, control & 0x1fu)}); + {OpBitwiseAnd, TypeU32(state), masked, lane, ConstantU32(state, control & 0x1fu)}); state.builder.AddFunction( - {OpBitwiseOr, state.uint_type, ored, masked, ConstantU32(state, (control >> 5u) & 0x1fu)}); + {OpBitwiseOr, TypeU32(state), ored, masked, ConstantU32(state, (control >> 5u) & 0x1fu)}); state.builder.AddFunction( - {OpBitwiseXor, state.uint_type, xored, ored, ConstantU32(state, (control >> 10u) & 0x1fu)}); + {OpBitwiseXor, TypeU32(state), xored, ored, ConstantU32(state, (control >> 10u) & 0x1fu)}); state.builder.AddFunction( - {OpBitwiseAnd, state.uint_type, base, subid, ConstantU32(state, 0xffffffe0u)}); - state.builder.AddFunction({OpBitwiseOr, state.uint_type, target, base, xored}); + {OpBitwiseAnd, TypeU32(state), base, subid, ConstantU32(state, 0xffffffe0u)}); + state.builder.AddFunction({OpBitwiseOr, TypeU32(state), target, base, xored}); return target; } diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp index 428ec3fe3..f7ed75f69 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp @@ -4,6 +4,201 @@ 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(OpTypeVoid); +} + +uint32_t TypeBool(EmitterState& state) { + return state.builder.Type(OpTypeBool); +} + +uint32_t TypeU32(EmitterState& state) { + return state.builder.Type(OpTypeInt, {32, 0}); +} + +uint32_t TypeU32Pair(EmitterState& state) { + const auto element = TypeU32(state); + return state.builder.Type(OpTypeStruct, {element, element}); +} + +uint32_t TypeI32(EmitterState& state) { + return state.builder.Type(OpTypeInt, {32, 1}); +} + +uint32_t TypeI32Pair(EmitterState& state) { + const auto element = TypeI32(state); + return state.builder.Type(OpTypeStruct, {element, element}); +} + +uint32_t TypeF32(EmitterState& state) { + return state.builder.Type(OpTypeFloat, {32}); +} + +uint32_t TypeU32Vector(EmitterState& state, uint32_t components) { + return state.builder.Type(OpTypeVector, {TypeU32(state), components}); +} + +uint32_t TypeI32Vector(EmitterState& state, uint32_t components) { + return state.builder.Type(OpTypeVector, {TypeI32(state), components}); +} + +uint32_t TypeF32Vector(EmitterState& state, uint32_t components) { + return state.builder.Type(OpTypeVector, {TypeF32(state), components}); +} + +uint32_t TypePointer(EmitterState& state, uint32_t storage_class, uint32_t pointee) { + return state.builder.Type(OpTypePointer, {storage_class, pointee}); +} + +uint32_t TypeFunction(EmitterState& state) { + return state.builder.Type(OpTypeFunction, {TypeVoid(state)}); +} + +uint32_t StorageRuntimeArrayType(EmitterState& state) { + return state.builder.DecoratedType(OpTypeRuntimeArray, {TypeU32(state)}, + {{OpDecorate, {DecorationArrayStride, sizeof(uint32_t)}}}); +} + +uint32_t StorageBufferType(EmitterState& state) { + return state.builder.DecoratedType( + OpTypeStruct, {StorageRuntimeArrayType(state)}, + {{OpMemberDecorate, {0, DecorationOffset, 0}}, {OpDecorate, {DecorationBlock}}}); +} + +uint32_t TypeStorageBufferPointer(EmitterState& state) { + return TypePointer(state, StorageClassStorageBuffer, StorageBufferType(state)); +} + +uint32_t TypeStorageBufferElementPointer(EmitterState& state) { + return TypePointer(state, StorageClassStorageBuffer, TypeU32(state)); +} + +uint32_t TypePushConstantElementPointer(EmitterState& state) { + return TypePointer(state, StorageClassPushConstant, TypeU32(state)); +} + +uint32_t TypeLdsArrayPointer(EmitterState& state, uint32_t storage_class) { + const auto count = ConstantU32(state, std::max(LdsDwordCount(state), 1u)); + const auto array = state.builder.Type(OpTypeArray, {TypeU32(state), count}); + return TypePointer(state, storage_class, array); +} + +uint32_t TypeLdsElementPointer(EmitterState& state, uint32_t storage_class) { + return TypePointer(state, storage_class, TypeU32(state)); +} + +namespace { + +uint32_t PushConstantArrayType(EmitterState& state) { + const auto count = + ConstantU32(state, state.program.bindings.push_constant_size / sizeof(uint32_t)); + return state.builder.DecoratedType(OpTypeArray, {TypeU32(state), count}, + {{OpDecorate, {DecorationArrayStride, sizeof(uint32_t)}}}); +} + +uint32_t PushConstantBlockType(EmitterState& state) { + return state.builder.DecoratedType( + OpTypeStruct, {PushConstantArrayType(state)}, + {{OpMemberDecorate, {0, DecorationOffset, state.program.bindings.push_constant_offset}}, + {OpDecorate, {DecorationBlock}}}); +} + +uint32_t PerVertexType(EmitterState& state) { + return state.builder.DecoratedType(OpTypeStruct, {TypeF32Vector(state, 4)}, + {{OpMemberDecorate, {0, DecorationBuiltIn, BuiltInPosition}}, + {OpDecorate, {DecorationBlock}}}); +} + +uint32_t SampleMaskArrayType(EmitterState& state) { + return state.builder.Type(OpTypeArray, {TypeI32(state), ConstantU32(state, 1)}); +} + +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(OpTypeArray, {StorageBufferType(state), count}); + const auto pointer_type = TypePointer(state, StorageClassStorageBuffer, array_type); + state.storage_buffer_variable = + state.builder.DefineGlobalVariable(pointer_type, StorageClassStorageBuffer); + } + if (DescriptorBinding(state, IR::DescriptorBindingKind::AddressMemory) != nullptr) { + const auto count = + ConstantU32(state, DescriptorCount(state, IR::DescriptorBindingKind::AddressMemory)); + const auto array_type = state.builder.Type(OpTypeArray, {StorageBufferType(state), count}); + const auto pointer_type = TypePointer(state, StorageClassStorageBuffer, array_type); + state.address_memory_variable = + state.builder.DefineGlobalVariable(pointer_type, StorageClassStorageBuffer); + } + if (state.program.bindings.push_constant_size != 0) { + const auto pointer_type = + TypePointer(state, StorageClassPushConstant, PushConstantBlockType(state)); + state.push_constant_variable = + state.builder.DefineGlobalVariable(pointer_type, StorageClassPushConstant); + } + if (DescriptorBinding(state, IR::DescriptorBindingKind::UserData) != nullptr) { + state.vsharp_storage_variable = state.builder.DefineGlobalVariable( + TypeStorageBufferPointer(state), StorageClassStorageBuffer); + } + if (DescriptorBinding(state, IR::DescriptorBindingKind::FlattenedSrt) != nullptr) { + state.flattened_srt_variable = state.builder.DefineGlobalVariable( + TypeStorageBufferPointer(state), StorageClassStorageBuffer); + } + for (uint32_t i = 0; i < state.sampled_image_variables.size(); i++) { + const auto view = static_cast(i % SampledImageViewKindCount); + const bool integer = i >= SampledImageViewKindCount; + const auto kind = SampledBindingKind(integer, view); + if (DescriptorBinding(state, kind) == nullptr) { + continue; + } + const auto count = ConstantU32(state, DescriptorCount(state, kind)); + const auto image_type = ImageViewImageType(state, view, integer); + const auto array_type = state.builder.Type(OpTypeArray, {image_type, count}); + const auto pointer_type = TypePointer(state, StorageClassUniformConstant, array_type); + state.sampled_image_variables[i] = + state.builder.DefineGlobalVariable(pointer_type, StorageClassUniformConstant); + if (view == ImageViewKind::Dim1D || view == ImageViewKind::Dim1DArray) { + state.builder.RequireCapability(CapabilitySampled1D); + } + } + for (uint32_t i = 0; i < state.storage_image_variables.size(); i++) { + const auto view = static_cast(i % StorageImageViewKindCount); + const bool integer = i >= StorageImageViewKindCount; + const auto kind = StorageBindingKind(integer, view); + if (DescriptorBinding(state, kind) == nullptr) { + continue; + } + const auto count = ConstantU32(state, DescriptorCount(state, kind)); + const auto image_type = StorageImageType(state, integer, view); + const auto array_type = state.builder.Type(OpTypeArray, {image_type, count}); + const auto pointer_type = TypePointer(state, StorageClassUniformConstant, array_type); + state.storage_image_variables[i] = + state.builder.DefineGlobalVariable(pointer_type, StorageClassUniformConstant); + if (view == ImageViewKind::Dim1D || view == ImageViewKind::Dim1DArray) { + state.builder.RequireCapability(CapabilityImage1D); + } + } + if (DescriptorBinding(state, IR::DescriptorBindingKind::Samplers) != nullptr) { + const auto sampler_type = state.builder.Type(OpTypeSampler); + const auto count = + ConstantU32(state, DescriptorCount(state, IR::DescriptorBindingKind::Samplers)); + const auto array_type = state.builder.Type(OpTypeArray, {sampler_type, count}); + const auto pointer_type = TypePointer(state, StorageClassUniformConstant, array_type); + state.sampler_variable = + state.builder.DefineGlobalVariable(pointer_type, StorageClassUniformConstant); + } + if (DescriptorBinding(state, IR::DescriptorBindingKind::Gds) != nullptr) { + state.gds_variable = state.builder.DefineGlobalVariable(TypeStorageBufferPointer(state), + StorageClassStorageBuffer); + } +} + +} // namespace + const IR::DescriptorBinding* DescriptorBinding(const EmitterState& state, IR::DescriptorBindingKind kind) { return IR::FindBinding(state.program.bindings, kind); @@ -15,37 +210,15 @@ uint32_t DescriptorCount(const EmitterState& state, IR::DescriptorBindingKind ki } uint32_t ConstantU32(EmitterState& state, uint32_t value) { - if (auto it = state.constants.find(value); it != state.constants.end()) { - return it->second; - } - - const auto id = state.builder.AllocateId(); - state.constants.emplace(value, id); - state.builder.AddType({OpConstant, state.uint_type, id, value}); - return id; + return state.builder.Constant(OpConstant, TypeU32(state), {value}); } uint32_t ConstantI32(EmitterState& state, int32_t value) { - const auto bits = static_cast(value); - if (auto it = state.signed_constants.find(bits); it != state.signed_constants.end()) { - return it->second; - } - - const auto id = state.builder.AllocateId(); - state.signed_constants.emplace(bits, id); - state.builder.AddType({OpConstant, state.int_type, id, bits}); - return id; + return state.builder.Constant(OpConstant, TypeI32(state), {static_cast(value)}); } uint32_t ConstantF32(EmitterState& state, uint32_t bits) { - if (auto it = state.float_constants.find(bits); it != state.float_constants.end()) { - return it->second; - } - - const auto id = state.builder.AllocateId(); - state.float_constants.emplace(bits, id); - state.builder.AddType({OpConstant, state.float_type, id, bits}); - return id; + return state.builder.Constant(OpConstant, TypeF32(state), {bits}); } uint32_t FloatBits(float value) { @@ -58,6 +231,26 @@ uint32_t ConstantF32Value(EmitterState& state, float value) { return ConstantF32(state, FloatBits(value)); } +uint32_t ConstantBool(EmitterState& state, bool value) { + return state.builder.Constant(value ? OpConstantTrue : OpConstantFalse, TypeBool(state)); +} + +uint32_t ConstantU64(EmitterState& state, uint64_t value) { + return state.builder.Constant(OpConstantComposite, TypeU32Pair(state), + {ConstantU32(state, static_cast(value)), + ConstantU32(state, static_cast(value >> 32u))}); +} + +uint32_t ConstantU32x4Zero(EmitterState& state) { + const auto zero = ConstantU32(state, 0); + return state.builder.Constant(OpConstantComposite, TypeU32Vector(state, 4), + {zero, zero, zero, zero}); +} + +uint32_t GlslStd450(EmitterState& state) { + return state.builder.Import("GLSL.std.450"); +} + VertexInputScalarKind VertexParameterScalarKind(const EmitterState& state, uint32_t location) { if (state.stage != ShaderType::Vertex || location >= ShaderVertexInputInfo::RES_MAX || location >= static_cast(state.input_info.vertex->resources_num)) { @@ -100,76 +293,78 @@ uint32_t VertexParameterComponentCount(const EmitterState& state, const InputBin return std::clamp(count, 1u, 4u); } -uint32_t VertexParameterScalarType(const EmitterState& state, VertexInputScalarKind kind) { +uint32_t VertexParameterScalarType(EmitterState& state, VertexInputScalarKind kind) { switch (kind) { - case VertexInputScalarKind::Sint: return state.int_type; - case VertexInputScalarKind::Uint: return state.uint_type; + case VertexInputScalarKind::Sint: return TypeI32(state); + case VertexInputScalarKind::Uint: return TypeU32(state); case VertexInputScalarKind::Float: - default: return state.float_type; + default: return TypeF32(state); } } -uint32_t VertexParameterScalarPointerType(const EmitterState& state, VertexInputScalarKind kind) { +uint32_t VertexParameterScalarPointerType(EmitterState& state, VertexInputScalarKind kind) { switch (kind) { - case VertexInputScalarKind::Sint: return state.ptr_input_int; - case VertexInputScalarKind::Uint: return state.ptr_input_uint; + case VertexInputScalarKind::Sint: + return TypePointer(state, StorageClassInput, TypeI32(state)); + case VertexInputScalarKind::Uint: + return TypePointer(state, StorageClassInput, TypeU32(state)); case VertexInputScalarKind::Float: - default: return state.ptr_input_float; + default: return TypePointer(state, StorageClassInput, TypeF32(state)); } } -uint32_t VertexParameterVectorOrScalarType(const EmitterState& state, VertexInputScalarKind kind, +uint32_t VertexParameterVectorOrScalarType(EmitterState& state, VertexInputScalarKind kind, uint32_t components) { switch (kind) { case VertexInputScalarKind::Sint: switch (components) { - case 1: return state.int_type; - case 2: return state.vec2_int_type; - case 3: return state.vec3_int_type; - default: return state.vec4_int_type; + case 1: return TypeI32(state); + case 2: return TypeI32Vector(state, 2); + case 3: return TypeI32Vector(state, 3); + default: return TypeI32Vector(state, 4); } case VertexInputScalarKind::Uint: switch (components) { - case 1: return state.uint_type; - case 2: return state.vec2_uint_type; - case 3: return state.vec3_uint_type; - default: return state.vec4_uint_type; + case 1: return TypeU32(state); + case 2: return TypeU32Vector(state, 2); + case 3: return TypeU32Vector(state, 3); + default: return TypeU32Vector(state, 4); } case VertexInputScalarKind::Float: default: switch (components) { - case 1: return state.float_type; - case 2: return state.vec2_float_type; - case 3: return state.vec3_float_type; - default: return state.vec4_float_type; + case 1: return TypeF32(state); + case 2: return TypeF32Vector(state, 2); + case 3: return TypeF32Vector(state, 3); + default: return TypeF32Vector(state, 4); } } } -uint32_t VertexParameterInputPointerType(const EmitterState& state, VertexInputScalarKind kind, +uint32_t VertexParameterInputPointerType(EmitterState& state, VertexInputScalarKind kind, uint32_t components) { switch (kind) { case VertexInputScalarKind::Sint: switch (components) { - case 1: return state.ptr_input_int; - case 2: return state.ptr_input_vec2_int; - case 3: return state.ptr_input_vec3_int; - default: return state.ptr_input_vec4_int; + case 1: return TypePointer(state, StorageClassInput, TypeI32(state)); + case 2: return TypePointer(state, StorageClassInput, TypeI32Vector(state, 2)); + case 3: return TypePointer(state, StorageClassInput, TypeI32Vector(state, 3)); + default: return TypePointer(state, StorageClassInput, TypeI32Vector(state, 4)); } case VertexInputScalarKind::Uint: switch (components) { - case 1: return state.ptr_input_uint; - case 2: return state.ptr_input_vec2_uint; - case 3: return state.ptr_input_vec3_uint; - default: return state.ptr_input_vec4_uint; + case 1: return TypePointer(state, StorageClassInput, TypeU32(state)); + case 2: return TypePointer(state, StorageClassInput, TypeU32Vector(state, 2)); + case 3: return TypePointer(state, StorageClassInput, TypeU32Vector(state, 3)); + default: return TypePointer(state, StorageClassInput, TypeU32Vector(state, 4)); } case VertexInputScalarKind::Float: default: switch (components) { - case 1: return state.ptr_input_float; - case 2: return state.ptr_input_vec2_float; - case 3: return state.ptr_input_vec3_float; - default: return state.ptr_input_vec4_float; + case 1: return TypePointer(state, StorageClassInput, TypeF32(state)); + case 2: return TypePointer(state, StorageClassInput, TypeF32Vector(state, 2)); + case 3: return TypePointer(state, StorageClassInput, TypeF32Vector(state, 3)); + default: return TypePointer(state, StorageClassInput, TypeF32Vector(state, 4)); } } } @@ -185,7 +380,7 @@ void AllocateInputVariables(EmitterState& state) { binding.variable_id = state.builder.AllocateId(); state.interface_variables.push_back(binding.variable_id); } - if (state.needs_subgroup_local_invocation_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); } @@ -278,11 +473,8 @@ void AddInputAnnotationsAndNames(EmitterState& state) { void AddOutputAnnotationsAndNames(EmitterState& state) { if (state.per_vertex_variable != 0) { - state.builder.AddName(state.per_vertex_type, "gl_PerVertex"); + state.builder.AddName(PerVertexType(state), "gl_PerVertex"); state.builder.AddName(state.per_vertex_variable, "outPerVertex"); - state.builder.AddAnnotation( - {OpMemberDecorate, state.per_vertex_type, 0, DecorationBuiltIn, BuiltInPosition}); - state.builder.AddAnnotation({OpDecorate, state.per_vertex_type, DecorationBlock}); } if (state.depth_variable != 0) { state.builder.AddName(state.depth_variable, "gl_FragDepth"); @@ -343,9 +535,9 @@ void AddDescriptorAnnotationsAndNames(EmitterState& state) { "sampled_uint_3d", "sampled_uint_2d_msaa", "sampled_uint_2d_msaa_array"}; - for (uint32_t i = 0; i < state.sampled_images.size(); i++) { + for (uint32_t i = 0; i < state.sampled_image_variables.size(); i++) { const auto view = static_cast(i % SampledImageViewKindCount); - Decorate(state.sampled_images[i].variable, SampledNames[i], + Decorate(state.sampled_image_variables[i], SampledNames[i], SampledBindingKind(i >= SampledImageViewKindCount, view)); } constexpr const char* StorageNames[] = {"storage_1d", @@ -358,9 +550,9 @@ void AddDescriptorAnnotationsAndNames(EmitterState& state) { "storage_uint_2d", "storage_uint_2d_array", "storage_uint_3d"}; - for (uint32_t i = 0; i < state.storage_images.size(); i++) { + for (uint32_t i = 0; i < state.storage_image_variables.size(); i++) { const auto view = static_cast(i % StorageImageViewKindCount); - Decorate(state.storage_images[i].variable, StorageNames[i], + Decorate(state.storage_image_variables[i], StorageNames[i], StorageBindingKind(i >= StorageImageViewKindCount, view)); } if (state.sampler_variable != 0) { @@ -378,13 +570,8 @@ void AddDescriptorAnnotationsAndNames(EmitterState& state) { void AddVsharpAnnotationsAndNames(EmitterState& state) { const auto& bind = state.program.bindings; if (state.push_constant_variable != 0) { - state.builder.AddName(state.push_constant_block_type, "BufferResource"); + state.builder.AddName(PushConstantBlockType(state), "BufferResource"); state.builder.AddName(state.push_constant_variable, "vsharp"); - state.builder.AddAnnotation( - {OpDecorate, state.push_constant_array_type, DecorationArrayStride, 4}); - state.builder.AddAnnotation({OpMemberDecorate, state.push_constant_block_type, 0, - DecorationOffset, bind.push_constant_offset}); - state.builder.AddAnnotation({OpDecorate, state.push_constant_block_type, DecorationBlock}); } if (state.vsharp_storage_variable != 0) { const auto* storage = DescriptorBinding(state, IR::DescriptorBindingKind::UserData); @@ -395,123 +582,34 @@ void AddVsharpAnnotationsAndNames(EmitterState& state) { } } -void EmitHeaderAndTypes(EmitterState& state) { - state.void_type = state.builder.AllocateId(); - state.bool_type = state.builder.AllocateId(); - state.uint_type = state.builder.AllocateId(); - state.uint_pair_type = state.builder.AllocateId(); - state.int_type = state.builder.AllocateId(); - state.int_pair_type = state.builder.AllocateId(); - state.float_type = state.builder.AllocateId(); - state.vec2_uint_type = state.builder.AllocateId(); - state.vec3_uint_type = state.builder.AllocateId(); - state.vec4_uint_type = state.builder.AllocateId(); - state.vec2_int_type = state.builder.AllocateId(); - state.vec3_int_type = state.builder.AllocateId(); - state.vec4_int_type = state.builder.AllocateId(); - state.vec2_float_type = state.builder.AllocateId(); - state.vec3_float_type = state.builder.AllocateId(); - state.vec4_float_type = state.builder.AllocateId(); - state.ptr_func_uint = state.builder.AllocateId(); - state.ptr_func_bool = state.builder.AllocateId(); - state.ptr_func_float = state.builder.AllocateId(); - state.ptr_func_uint_pair = state.builder.AllocateId(); - state.ptr_func_vec4_uint = state.builder.AllocateId(); - state.ptr_func_vec2_float = state.builder.AllocateId(); - state.ptr_input_float = state.builder.AllocateId(); - state.ptr_input_bool = state.builder.AllocateId(); - state.ptr_input_int = state.builder.AllocateId(); - state.ptr_input_uint = state.builder.AllocateId(); - state.ptr_input_vec2_float = state.builder.AllocateId(); - state.ptr_input_vec3_float = state.builder.AllocateId(); - state.ptr_input_vec2_int = state.builder.AllocateId(); - state.ptr_input_vec3_int = state.builder.AllocateId(); - state.ptr_input_vec4_int = state.builder.AllocateId(); - state.ptr_input_vec2_uint = state.builder.AllocateId(); - state.ptr_input_vec3_uint = state.builder.AllocateId(); - state.ptr_input_vec4_uint = state.builder.AllocateId(); - state.ptr_input_vec4_float = state.builder.AllocateId(); - state.sample_mask_array_type = state.builder.AllocateId(); - state.ptr_output_int = state.builder.AllocateId(); - state.ptr_output_sample_mask_array = state.builder.AllocateId(); - state.ptr_output_float = state.builder.AllocateId(); - state.ptr_output_vec4_float = state.builder.AllocateId(); - const auto ptr_output_vec4_uint = state.builder.AllocateId(); - state.per_vertex_type = state.builder.AllocateId(); - state.ptr_output_per_vertex = state.builder.AllocateId(); - state.storage_runtime_array_type = state.builder.AllocateId(); - state.storage_buffer_type = state.builder.AllocateId(); - state.ptr_storage_buffer = state.builder.AllocateId(); - state.ptr_storage_buffer_uint = state.builder.AllocateId(); - state.storage_buffer_array_type = state.builder.AllocateId(); - state.ptr_storage_buffer_array = state.builder.AllocateId(); - if (state.address_memory_variable != 0) { - state.address_memory_array_type = state.builder.AllocateId(); - state.ptr_address_memory_array = state.builder.AllocateId(); +void DefineModule(EmitterState& state) { + DefineDescriptorVariables(state); + if (state.requirements.function_lds) { + state.lds_variable = state.builder.AllocateId(); } - if (state.push_constant_variable != 0) { - state.push_constant_array_type = state.builder.AllocateId(); - state.push_constant_block_type = state.builder.AllocateId(); - state.ptr_push_constant_block = state.builder.AllocateId(); - state.ptr_push_constant_uint = state.builder.AllocateId(); - } - state.lds_array_type = state.builder.AllocateId(); - state.ptr_workgroup_array = state.builder.AllocateId(); - state.ptr_workgroup_uint = state.builder.AllocateId(); - state.lds_variable = state.builder.AllocateId(); - for (auto& image: state.sampled_images) { - image.image_type = state.builder.AllocateId(); - image.sampled_image_type = state.builder.AllocateId(); - image.pointer_type = state.builder.AllocateId(); - image.array_type = state.builder.AllocateId(); - image.array_pointer_type = state.builder.AllocateId(); - } - for (auto& image: state.storage_images) { - image.image_type = state.builder.AllocateId(); - image.pointer_type = state.builder.AllocateId(); - image.array_type = state.builder.AllocateId(); - image.array_pointer_type = state.builder.AllocateId(); - } - state.sampler_type = state.builder.AllocateId(); - state.sampler_array_type = state.builder.AllocateId(); - state.ptr_uniform_sampler = state.builder.AllocateId(); - state.ptr_uniform_sampler_array = state.builder.AllocateId(); - state.ptr_image_uint = state.builder.AllocateId(); - state.func_type = state.builder.AllocateId(); - state.main_func = state.builder.AllocateId(); - state.entry_label = state.builder.AllocateId(); - state.glsl_std450 = state.builder.AllocateId(); + state.main_func = state.builder.AllocateId(); + state.entry_label = state.builder.AllocateId(); - state.builder.AddCapability({CapabilityShader}); - state.builder.AddCapability({CapabilitySampled1D}); - state.builder.AddCapability({CapabilityImage1D}); - state.builder.AddCapability({CapabilityImageQuery}); - state.builder.AddCapability({CapabilitySignedZeroInfNanPreserve}); - if (state.needs_image_gather_extended) { - state.builder.AddCapability({CapabilityImageGatherExtended}); + state.builder.RequireCapability(CapabilityShader); + state.builder.RequireCapability(CapabilitySignedZeroInfNanPreserve); + if (state.requirements.image_gather_extended) { + state.builder.RequireCapability(CapabilityImageGatherExtended); } - if (std::any_of(state.storage_images.begin(), - state.storage_images.begin() + StorageImageViewKindCount, - [](const auto& image) { return image.variable != 0; })) { - state.builder.AddCapability({CapabilityStorageImageReadWithoutFormat}); - state.builder.AddCapability({CapabilityStorageImageWriteWithoutFormat}); + if (state.requirements.subgroup_ballot || state.requirements.subgroup_shuffle || + state.requirements.subgroup_local_invocation_id) { + state.builder.RequireCapability(CapabilityGroupNonUniform); } - if (state.needs_subgroup_ballot || state.needs_subgroup_shuffle || - state.needs_subgroup_local_invocation_id) { - state.builder.AddCapability({CapabilityGroupNonUniform}); + if (state.requirements.subgroup_ballot) { + state.builder.RequireCapability(CapabilityGroupNonUniformBallot); } - if (state.needs_subgroup_ballot) { - state.builder.AddCapability({CapabilityGroupNonUniformBallot}); + if (state.requirements.subgroup_shuffle) { + state.builder.RequireCapability(CapabilityGroupNonUniformShuffle); } - if (state.needs_subgroup_shuffle) { - state.builder.AddCapability({CapabilityGroupNonUniformShuffle}); + if (state.requirements.compute_derivatives && state.stage == ShaderType::Compute) { + state.builder.RequireCapability(CapabilityComputeDerivativeGroupQuadsKHR); + state.builder.RequireExtension("SPV_KHR_compute_shader_derivatives"); } - if (state.needs_compute_derivatives && state.stage == ShaderType::Compute) { - state.builder.AddCapability({CapabilityComputeDerivativeGroupQuadsKHR}); - state.builder.AddExtension("SPV_KHR_compute_shader_derivatives"); - } - state.builder.AddExtension("SPV_KHR_float_controls"); - state.builder.AddExtInstImport(state.glsl_std450, "GLSL.std.450"); + state.builder.RequireExtension("SPV_KHR_float_controls"); state.builder.AddMemoryModel({AddressingModelLogical, MemoryModelGLSL450}); state.builder.AddEntryPoint(ExecutionModelForStage(state.stage), state.main_func, "main", state.interface_variables); @@ -519,8 +617,8 @@ void EmitHeaderAndTypes(EmitterState& state) { // contract prevents host compilers from treating synthesized IEEE values as finite. state.builder.AddExecutionMode({state.main_func, ExecutionModeSignedZeroInfNanPreserve, 32u}); if (state.stage == ShaderType::Compute) { - uint32_t local_x = state.needs_compute_derivatives ? 2u : 1u; - uint32_t local_y = state.needs_compute_derivatives ? 2u : 1u; + uint32_t local_x = state.requirements.compute_derivatives ? 2u : 1u; + uint32_t local_y = state.requirements.compute_derivatives ? 2u : 1u; uint32_t local_z = 1u; const auto* cs = state.input_info.compute; local_x = cs->threads_num[0] != 0u ? cs->threads_num[0] : local_x; @@ -540,304 +638,79 @@ void EmitHeaderAndTypes(EmitterState& state) { state.builder.AddExecutionMode({state.main_func, ExecutionModeEarlyFragmentTests}); } } - if (state.needs_compute_derivatives && state.stage == ShaderType::Compute) { + if (state.requirements.compute_derivatives && state.stage == ShaderType::Compute) { state.builder.AddExecutionMode({state.main_func, ExecutionModeDerivativeGroupQuadsKHR}); } state.builder.AddName(state.main_func, "main"); - if (state.stage == ShaderType::Compute || state.needs_function_lds) { + if (state.requirements.function_lds) { state.builder.AddName(state.lds_variable, "lds_dwords"); } AddInputAnnotationsAndNames(state); AddOutputAnnotationsAndNames(state); AddDescriptorAnnotationsAndNames(state); AddVsharpAnnotationsAndNames(state); - state.builder.AddAnnotation( - {OpDecorate, state.storage_runtime_array_type, DecorationArrayStride, 4}); - state.builder.AddAnnotation( - {OpMemberDecorate, state.storage_buffer_type, 0, DecorationOffset, 0}); - state.builder.AddAnnotation({OpDecorate, state.storage_buffer_type, DecorationBlock}); - state.builder.AddType({OpTypeVoid, state.void_type}); - state.builder.AddType({OpTypeBool, state.bool_type}); - state.builder.AddType({OpTypeInt, state.uint_type, 32, 0}); - state.builder.AddType({OpTypeStruct, state.uint_pair_type, state.uint_type, state.uint_type}); - state.builder.AddType({OpTypeInt, state.int_type, 32, 1}); - state.builder.AddType({OpTypeStruct, state.int_pair_type, state.int_type, state.int_type}); - state.builder.AddType({OpTypeFloat, state.float_type, 32}); - state.builder.AddType({OpTypeVector, state.vec2_uint_type, state.uint_type, 2}); - state.builder.AddType({OpTypeVector, state.vec3_uint_type, state.uint_type, 3}); - state.builder.AddType({OpTypeVector, state.vec4_uint_type, state.uint_type, 4}); - state.builder.AddType({OpTypeVector, state.vec2_int_type, state.int_type, 2}); - state.builder.AddType({OpTypeVector, state.vec3_int_type, state.int_type, 3}); - state.builder.AddType({OpTypeVector, state.vec4_int_type, state.int_type, 4}); - state.builder.AddType({OpTypeVector, state.vec2_float_type, state.float_type, 2}); - state.builder.AddType({OpTypeVector, state.vec3_float_type, state.float_type, 3}); - state.builder.AddType({OpTypeVector, state.vec4_float_type, state.float_type, 4}); - state.builder.AddType( - {OpTypePointer, state.ptr_func_uint, StorageClassFunction, state.uint_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_func_bool, StorageClassFunction, state.bool_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_func_float, StorageClassFunction, state.float_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_func_uint_pair, StorageClassFunction, state.uint_pair_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_func_vec4_uint, StorageClassFunction, state.vec4_uint_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_func_vec2_float, StorageClassFunction, state.vec2_float_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_input_float, StorageClassInput, state.float_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_input_bool, StorageClassInput, state.bool_type}); - state.builder.AddType({OpTypePointer, state.ptr_input_int, StorageClassInput, state.int_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_input_uint, StorageClassInput, state.uint_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_input_vec2_float, StorageClassInput, state.vec2_float_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_input_vec3_float, StorageClassInput, state.vec3_float_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_input_vec2_int, StorageClassInput, state.vec2_int_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_input_vec3_int, StorageClassInput, state.vec3_int_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_input_vec4_int, StorageClassInput, state.vec4_int_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_input_vec2_uint, StorageClassInput, state.vec2_uint_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_input_vec3_uint, StorageClassInput, state.vec3_uint_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_input_vec4_uint, StorageClassInput, state.vec4_uint_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_input_vec4_float, StorageClassInput, state.vec4_float_type}); if (state.subgroup_local_invocation_id_variable != 0) { - state.builder.AddType({OpVariable, state.ptr_input_uint, - state.subgroup_local_invocation_id_variable, StorageClassInput}); + state.builder.DefineGlobalVariable(state.subgroup_local_invocation_id_variable, + TypePointer(state, StorageClassInput, TypeU32(state)), + StorageClassInput); } for (const auto& input: state.inputs) { - uint32_t ptr_type = state.ptr_input_uint; + uint32_t ptr_type = TypePointer(state, StorageClassInput, TypeU32(state)); switch (input.kind) { case IR::StageInputKind::VertexIndex: - case IR::StageInputKind::InstanceIndex: ptr_type = state.ptr_input_int; break; + case IR::StageInputKind::InstanceIndex: + ptr_type = TypePointer(state, StorageClassInput, TypeI32(state)); + break; case IR::StageInputKind::WorkgroupId: case IR::StageInputKind::LocalInvocationId: case IR::StageInputKind::GlobalInvocationId: - ptr_type = state.ptr_input_vec3_uint; + ptr_type = TypePointer(state, StorageClassInput, TypeU32Vector(state, 3)); + break; + case IR::StageInputKind::FragCoord: + ptr_type = TypePointer(state, StorageClassInput, TypeF32Vector(state, 4)); + break; + case IR::StageInputKind::FrontFacing: + ptr_type = TypePointer(state, StorageClassInput, TypeBool(state)); break; - case IR::StageInputKind::FragCoord: ptr_type = state.ptr_input_vec4_float; break; - case IR::StageInputKind::FrontFacing: ptr_type = state.ptr_input_bool; break; case IR::StageInputKind::Parameter: if (state.stage == ShaderType::Vertex) { const auto kind = VertexParameterScalarKind(state, input.location); const auto components = VertexParameterComponentCount(state, input); ptr_type = VertexParameterInputPointerType(state, kind, components); } else { - ptr_type = state.ptr_input_vec4_float; + ptr_type = TypePointer(state, StorageClassInput, TypeF32Vector(state, 4)); } break; default: break; } - state.builder.AddType({OpVariable, ptr_type, input.variable_id, StorageClassInput}); + state.builder.DefineGlobalVariable(input.variable_id, ptr_type, StorageClassInput); } - state.builder.AddType( - {OpTypePointer, state.ptr_output_float, StorageClassOutput, state.float_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_output_int, StorageClassOutput, state.int_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_output_vec4_float, StorageClassOutput, state.vec4_float_type}); - state.builder.AddType( - {OpTypePointer, ptr_output_vec4_uint, StorageClassOutput, state.vec4_uint_type}); if (state.per_vertex_variable != 0) { - state.builder.AddType({OpTypeStruct, state.per_vertex_type, state.vec4_float_type}); - state.builder.AddType({OpTypePointer, state.ptr_output_per_vertex, StorageClassOutput, - state.per_vertex_type}); - state.builder.AddType({OpVariable, state.ptr_output_per_vertex, state.per_vertex_variable, - StorageClassOutput}); + state.builder.DefineGlobalVariable( + state.per_vertex_variable, TypePointer(state, StorageClassOutput, PerVertexType(state)), + 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) - ? ptr_output_vec4_uint - : state.ptr_output_vec4_float; - state.builder.AddType( - {OpVariable, pointer_type, binding.variable_id, StorageClassOutput}); + ? TypePointer(state, StorageClassOutput, TypeU32Vector(state, 4)) + : TypePointer(state, StorageClassOutput, TypeF32Vector(state, 4)); + state.builder.DefineGlobalVariable(binding.variable_id, pointer_type, + StorageClassOutput); } } if (state.depth_variable != 0) { - state.builder.AddType( - {OpVariable, state.ptr_output_float, state.depth_variable, StorageClassOutput}); + state.builder.DefineGlobalVariable(state.depth_variable, + TypePointer(state, StorageClassOutput, TypeF32(state)), + StorageClassOutput); } if (state.sample_mask_variable != 0) { - state.builder.AddType( - {OpTypeArray, state.sample_mask_array_type, state.int_type, ConstantU32(state, 1)}); - state.builder.AddType({OpTypePointer, state.ptr_output_sample_mask_array, - StorageClassOutput, state.sample_mask_array_type}); - state.builder.AddType({OpVariable, state.ptr_output_sample_mask_array, - state.sample_mask_variable, StorageClassOutput}); - } - state.builder.AddType({OpTypeRuntimeArray, state.storage_runtime_array_type, state.uint_type}); - state.builder.AddType( - {OpTypeStruct, state.storage_buffer_type, state.storage_runtime_array_type}); - state.builder.AddType({OpTypePointer, state.ptr_storage_buffer, StorageClassStorageBuffer, - state.storage_buffer_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_storage_buffer_uint, StorageClassStorageBuffer, state.uint_type}); - if (state.storage_buffer_variable != 0) { - const auto count = - ConstantU32(state, DescriptorCount(state, IR::DescriptorBindingKind::Buffers)); - state.builder.AddType( - {OpTypeArray, state.storage_buffer_array_type, state.storage_buffer_type, count}); - state.builder.AddType({OpTypePointer, state.ptr_storage_buffer_array, - StorageClassStorageBuffer, state.storage_buffer_array_type}); - state.builder.AddType({OpVariable, state.ptr_storage_buffer_array, - state.storage_buffer_variable, StorageClassStorageBuffer}); - } - if (state.gds_variable != 0) { - state.builder.AddType( - {OpVariable, state.ptr_storage_buffer, state.gds_variable, StorageClassStorageBuffer}); - } - if (state.push_constant_variable != 0) { - const auto dword_count = - ConstantU32(state, state.program.bindings.push_constant_size / sizeof(uint32_t)); - state.builder.AddType( - {OpTypeArray, state.push_constant_array_type, state.uint_type, dword_count}); - state.builder.AddType( - {OpTypeStruct, state.push_constant_block_type, state.push_constant_array_type}); - state.builder.AddType({OpTypePointer, state.ptr_push_constant_block, - StorageClassPushConstant, state.push_constant_block_type}); - state.builder.AddType({OpTypePointer, state.ptr_push_constant_uint, - StorageClassPushConstant, state.uint_type}); - state.builder.AddType({OpVariable, state.ptr_push_constant_block, - state.push_constant_variable, StorageClassPushConstant}); - } - if (state.vsharp_storage_variable != 0) { - state.builder.AddType({OpVariable, state.ptr_storage_buffer, state.vsharp_storage_variable, - StorageClassStorageBuffer}); - } - if (state.address_memory_variable != 0) { - const auto count = - ConstantU32(state, DescriptorCount(state, IR::DescriptorBindingKind::AddressMemory)); - state.builder.AddType( - {OpTypeArray, state.address_memory_array_type, state.storage_buffer_type, count}); - state.builder.AddType({OpTypePointer, state.ptr_address_memory_array, - StorageClassStorageBuffer, state.address_memory_array_type}); - state.builder.AddType({OpVariable, state.ptr_address_memory_array, - state.address_memory_variable, StorageClassStorageBuffer}); - } - if (state.flattened_srt_variable != 0) { - state.builder.AddType({OpVariable, state.ptr_storage_buffer, state.flattened_srt_variable, - StorageClassStorageBuffer}); - } - if (state.stage == ShaderType::Compute || state.needs_function_lds) { - const auto storage_class = - state.needs_function_lds ? StorageClassFunction : StorageClassWorkgroup; - const auto lds_dwords = std::max(LdsDwordCount(state), 1u); - const auto lds_size = ConstantU32(state, lds_dwords); - state.builder.AddType({OpTypeArray, state.lds_array_type, state.uint_type, lds_size}); - state.builder.AddType( - {OpTypePointer, state.ptr_workgroup_array, storage_class, state.lds_array_type}); - state.builder.AddType( - {OpTypePointer, state.ptr_workgroup_uint, storage_class, state.uint_type}); - if (state.stage == ShaderType::Compute) { - state.builder.AddType( - {OpVariable, state.ptr_workgroup_array, state.lds_variable, StorageClassWorkgroup}); - } - } - for (uint32_t i = 0; i < state.sampled_images.size(); i++) { - auto& image = state.sampled_images[i]; - const auto view = static_cast(i % SampledImageViewKindCount); - const bool integer = i >= SampledImageViewKindCount; - const auto component = integer ? state.uint_type : state.float_type; - state.builder.AddType({OpTypeImage, image.image_type, component, ImageSpirvDimension(view), - 0, ImageSpirvArrayed(view), ImageSpirvMultisampled(view), 1, - ImageFormatUnknown}); - state.builder.AddType({OpTypeSampledImage, image.sampled_image_type, image.image_type}); - state.builder.AddType( - {OpTypePointer, image.pointer_type, StorageClassUniformConstant, image.image_type}); - if (image.variable != 0) { - const auto kind = SampledBindingKind(integer, view); - const auto count = ConstantU32(state, DescriptorCount(state, kind)); - state.builder.AddType({OpTypeArray, image.array_type, image.image_type, count}); - state.builder.AddType({OpTypePointer, image.array_pointer_type, - StorageClassUniformConstant, image.array_type}); - state.builder.AddType({OpVariable, image.array_pointer_type, image.variable, - StorageClassUniformConstant}); - } - } - state.builder.AddType({OpTypeSampler, state.sampler_type}); - state.builder.AddType({OpTypePointer, state.ptr_uniform_sampler, StorageClassUniformConstant, - state.sampler_type}); - if (state.sampler_variable != 0) { - const auto count = - ConstantU32(state, DescriptorCount(state, IR::DescriptorBindingKind::Samplers)); - state.builder.AddType({OpTypeArray, state.sampler_array_type, state.sampler_type, count}); - state.builder.AddType({OpTypePointer, state.ptr_uniform_sampler_array, - StorageClassUniformConstant, state.sampler_array_type}); - state.builder.AddType({OpVariable, state.ptr_uniform_sampler_array, state.sampler_variable, - StorageClassUniformConstant}); - } - for (uint32_t i = 0; i < state.storage_images.size(); i++) { - auto& image = state.storage_images[i]; - const auto view = static_cast(i % StorageImageViewKindCount); - const bool integer = i >= StorageImageViewKindCount; - const auto component = integer ? state.uint_type : state.float_type; - const auto format = integer ? ImageFormatR32ui : ImageFormatUnknown; - state.builder.AddType({OpTypeImage, image.image_type, component, ImageSpirvDimension(view), - 0, ImageSpirvArrayed(view), 0, 2, format}); - state.builder.AddType( - {OpTypePointer, image.pointer_type, StorageClassUniformConstant, image.image_type}); - if (image.variable != 0) { - const auto kind = StorageBindingKind(integer, view); - const auto count = ConstantU32(state, DescriptorCount(state, kind)); - state.builder.AddType({OpTypeArray, image.array_type, image.image_type, count}); - state.builder.AddType({OpTypePointer, image.array_pointer_type, - StorageClassUniformConstant, image.array_type}); - state.builder.AddType({OpVariable, image.array_pointer_type, image.variable, - StorageClassUniformConstant}); - } - } - state.builder.AddType( - {OpTypePointer, state.ptr_image_uint, StorageClassImage, state.uint_type}); - state.builder.AddType({OpTypeFunction, state.func_type, state.void_type}); -} - -void AllocateDescriptorVariables(EmitterState& state) { - if (DescriptorBinding(state, IR::DescriptorBindingKind::Buffers) != nullptr) { - state.storage_buffer_variable = state.builder.AllocateId(); - } - if (DescriptorBinding(state, IR::DescriptorBindingKind::AddressMemory) != nullptr) { - state.address_memory_variable = state.builder.AllocateId(); - } - if (state.program.bindings.push_constant_size != 0) { - state.push_constant_variable = state.builder.AllocateId(); - } - if (DescriptorBinding(state, IR::DescriptorBindingKind::UserData) != nullptr) { - state.vsharp_storage_variable = state.builder.AllocateId(); - } - if (DescriptorBinding(state, IR::DescriptorBindingKind::FlattenedSrt) != nullptr) { - state.flattened_srt_variable = state.builder.AllocateId(); - } - for (uint32_t i = 0; i < state.sampled_images.size(); i++) { - const auto view = static_cast(i % SampledImageViewKindCount); - if (DescriptorBinding(state, SampledBindingKind(i >= SampledImageViewKindCount, view)) != - nullptr) { - state.sampled_images[i].variable = state.builder.AllocateId(); - } - } - for (uint32_t i = 0; i < state.storage_images.size(); i++) { - const auto view = static_cast(i % StorageImageViewKindCount); - if (DescriptorBinding(state, StorageBindingKind(i >= StorageImageViewKindCount, view)) != - nullptr) { - state.storage_images[i].variable = state.builder.AllocateId(); - } - } - if (DescriptorBinding(state, IR::DescriptorBindingKind::Samplers) != nullptr) { - state.sampler_variable = state.builder.AllocateId(); - } - if (DescriptorBinding(state, IR::DescriptorBindingKind::Gds) != nullptr) { - state.gds_variable = state.builder.AllocateId(); + state.builder.DefineGlobalVariable( + state.sample_mask_variable, + TypePointer(state, StorageClassOutput, SampleMaskArrayType(state)), StorageClassOutput); } } diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp index 1e00e1571..ee2459339 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp @@ -13,7 +13,7 @@ void EmitKillIfBoolFalse(EmitterState& state, uint32_t active) { const auto kill_label = state.builder.AllocateId(); const auto merge_label = state.builder.AllocateId(); const auto inactive = state.builder.AllocateId(); - state.builder.AddFunction({OpLogicalNot, state.bool_type, inactive, active}); + state.builder.AddFunction({OpLogicalNot, TypeBool(state), inactive, active}); state.builder.AddFunction({OpSelectionMerge, merge_label, SelectionControlNone}); state.builder.AddFunction({OpBranchConditional, inactive, kill_label, merge_label}); state.builder.AddFunction({OpLabel, kill_label}); @@ -29,38 +29,26 @@ void EmitKillIfPixelValidMaskInactive(EmitterState& state) { const auto mask_value = state.builder.AllocateId(); const auto active = state.builder.AllocateId(); state.builder.AddFunction( - {OpLoad, state.uint_type, mask_value, state.pixel_valid_mask_variable}); + {OpLoad, TypeU32(state), mask_value, state.pixel_valid_mask_variable}); state.builder.AddFunction( - {OpINotEqual, state.bool_type, active, mask_value, ConstantU32(state, 0)}); + {OpINotEqual, TypeBool(state), active, mask_value, ConstantU32(state, 0)}); EmitKillIfBoolFalse(state, active); } -uint32_t BoolConstant(EmitterState& state, bool value) { - const auto id = state.builder.AllocateId(); - state.builder.AddType({value ? 41u : 42u, state.bool_type, id}); - return id; -} - -uint32_t ConstantPair(EmitterState& state, uint64_t value) { - const auto id = state.builder.AllocateId(); - state.builder.AddType({OpConstantComposite, state.uint_pair_type, id, - ConstantU32(state, static_cast(value)), - ConstantU32(state, static_cast(value >> 32u))}); - return id; -} - -uint32_t PhiPointerType(const EmitterState& state, IR::Type type) { +uint32_t PhiPointerType(EmitterState& state, IR::Type type) { switch (type) { - case IR::Type::U1: return state.ptr_func_bool; + case IR::Type::U1: return TypePointer(state, StorageClassFunction, TypeBool(state)); case IR::Type::U8: case IR::Type::U16: case IR::Type::U32: - case IR::Type::F16: return state.ptr_func_uint; - case IR::Type::F32: return state.ptr_func_float; + case IR::Type::F16: return TypePointer(state, StorageClassFunction, TypeU32(state)); + case IR::Type::F32: return TypePointer(state, StorageClassFunction, TypeF32(state)); case IR::Type::U64: - case IR::Type::U32x2: return state.ptr_func_uint_pair; - case IR::Type::U32x4: return state.ptr_func_vec4_uint; - case IR::Type::F32x2: return state.ptr_func_vec2_float; + case IR::Type::U32x2: return TypePointer(state, StorageClassFunction, TypeU32Pair(state)); + case IR::Type::U32x4: + return TypePointer(state, StorageClassFunction, TypeU32Vector(state, 4)); + case IR::Type::F32x2: + return TypePointer(state, StorageClassFunction, TypeF32Vector(state, 2)); default: return 0; } } @@ -168,7 +156,7 @@ void EmitDispatcherTerminator(ValueEmitContext& ctx, const IR::Block* block, EmitDispatcherTarget(ctx, block, term.true_block); EmitDispatcherTarget(ctx, block, term.false_block); const auto selected = ctx.state.builder.AllocateId(); - ctx.state.builder.AddFunction({OpSelect, ctx.state.uint_type, selected, + ctx.state.builder.AddFunction({OpSelect, TypeU32(ctx.state), selected, ctx.Def(info.condition), ConstantU32(ctx.state, term.true_block), ConstantU32(ctx.state, term.false_block)}); @@ -194,9 +182,9 @@ void EmitDispatcherTerminator(ValueEmitContext& ctx, const IR::Block* block, for (size_t index = 0; index < std::min(values.size(), targets.size()); index++) { const auto match = ctx.state.builder.AllocateId(); const auto next = ctx.state.builder.AllocateId(); - ctx.state.builder.AddFunction({OpIEqual, ctx.state.bool_type, match, selector, + ctx.state.builder.AddFunction({OpIEqual, TypeBool(ctx.state), match, selector, ConstantU32(ctx.state, values[index])}); - ctx.state.builder.AddFunction({OpSelect, ctx.state.uint_type, next, match, + ctx.state.builder.AddFunction({OpSelect, TypeU32(ctx.state), next, match, ConstantU32(ctx.state, targets[index]), selected}); selected = next; @@ -263,10 +251,10 @@ void EmitDispatcherFunction(ValueEmitContext& ctx) { state.builder.AddFunction({OpLabel, state.dispatch_header_label}); const auto pc = state.builder.AllocateId(); - state.builder.AddFunction({OpLoad, state.uint_type, pc, state.dispatch_pc_variable}); + state.builder.AddFunction({OpLoad, TypeU32(state), pc, state.dispatch_pc_variable}); const auto done = state.builder.AllocateId(); state.builder.AddFunction( - {OpIEqual, state.bool_type, done, pc, ConstantU32(ctx.state, UINT32_MAX)}); + {OpIEqual, TypeBool(state), done, pc, ConstantU32(ctx.state, UINT32_MAX)}); state.builder.AddFunction( {OpLoopMerge, state.dispatch_merge_label, state.dispatch_continue_label, LoopControlNone}); state.builder.AddFunction( @@ -303,16 +291,16 @@ void EmitDispatcherFunction(ValueEmitContext& ctx) { uint32_t ValueEmitContext::TypeId(IR::Type type) const { switch (type) { - case IR::Type::U1: return state.bool_type; + case IR::Type::U1: return TypeBool(state); case IR::Type::U8: case IR::Type::U16: case IR::Type::U32: - case IR::Type::F16: return state.uint_type; + case IR::Type::F16: return TypeU32(state); case IR::Type::U64: - case IR::Type::U32x2: return state.uint_pair_type; - case IR::Type::F32: return state.float_type; - case IR::Type::U32x4: return state.vec4_uint_type; - case IR::Type::F32x2: return state.vec2_float_type; + case IR::Type::U32x2: return TypeU32Pair(state); + case IR::Type::F32: return TypeF32(state); + case IR::Type::U32x4: return TypeU32Vector(state, 4); + case IR::Type::F32x2: return TypeF32Vector(state, 2); default: return 0; } } @@ -321,11 +309,11 @@ uint32_t ValueEmitContext::Def(IR::Value value) { value = value.Resolve(); if (value.IsImmediate()) { switch (value.GetType()) { - case IR::Type::U1: return BoolConstant(state, value.U1()); + case IR::Type::U1: return ConstantBool(state, value.U1()); case IR::Type::U8: return ConstantU32(state, value.U8()); case IR::Type::U16: return ConstantU32(state, value.U16()); case IR::Type::U32: return ConstantU32(state, value.U32()); - case IR::Type::U64: return ConstantPair(state, value.U64()); + case IR::Type::U64: return ConstantU64(state, value.U64()); case IR::Type::F16: return ConstantU32(state, value.F16Bits()); case IR::Type::F32: return ConstantF32(state, std::bit_cast(value.F32Value())); @@ -430,7 +418,7 @@ void ValueEmitContext::Fail(const IR::Inst& inst, const char* reason) { bool EmitValueProgram(EmitterState& state, const IR::ValueProgram& program, std::string* error) { ValueEmitContext ctx(state, program); - if (state.stage == ShaderType::Pixel && state.needs_pixel_valid_mask) { + if (state.stage == ShaderType::Pixel && state.requirements.pixel_valid_mask) { state.pixel_valid_mask_variable = state.builder.AllocateId(); state.builder.AddName(state.pixel_valid_mask_variable, "pixel_valid_mask_active"); } @@ -502,19 +490,21 @@ bool EmitValueProgram(EmitterState& state, const IR::ValueProgram& program, std: } } state.builder.AddFunction( - {OpFunction, state.void_type, state.main_func, FunctionControlNone, state.func_type}); + {OpFunction, TypeVoid(state), state.main_func, FunctionControlNone, TypeFunction(state)}); state.builder.AddFunction({OpLabel, state.entry_label}); - if (state.needs_function_lds) { - state.builder.AddFunction( - {OpVariable, state.ptr_workgroup_array, state.lds_variable, StorageClassFunction}); + if (state.requirements.function_lds) { + state.builder.AddFunction({OpVariable, TypeLdsArrayPointer(state, StorageClassFunction), + state.lds_variable, StorageClassFunction}); } if (state.pixel_valid_mask_variable != 0) { - state.builder.AddFunction({OpVariable, state.ptr_func_uint, state.pixel_valid_mask_variable, - StorageClassFunction}); + state.builder.AddFunction({OpVariable, + TypePointer(state, StorageClassFunction, TypeU32(state)), + state.pixel_valid_mask_variable, StorageClassFunction}); } if (program.dispatcher_fallback) { - state.builder.AddFunction( - {OpVariable, state.ptr_func_uint, state.dispatch_pc_variable, StorageClassFunction}); + state.builder.AddFunction({OpVariable, + TypePointer(state, StorageClassFunction, TypeU32(state)), + state.dispatch_pc_variable, StorageClassFunction}); } for (const auto* block: program.blocks) { for (const auto& inst: *block) { @@ -537,8 +527,9 @@ bool EmitValueProgram(EmitterState& state, const IR::ValueProgram& program, std: } } if (ctx.scratch_u32_variable != 0) { - state.builder.AddFunction( - {OpVariable, state.ptr_func_uint, ctx.scratch_u32_variable, StorageClassFunction}); + state.builder.AddFunction({OpVariable, + TypePointer(state, StorageClassFunction, TypeU32(state)), + ctx.scratch_u32_variable, StorageClassFunction}); } if (state.pixel_valid_mask_variable != 0) { state.builder.AddFunction( diff --git a/src/graphics/shader/rectListShader.cpp b/src/graphics/shader/rectListShader.cpp index b84f6f80d..ef791886e 100644 --- a/src/graphics/shader/rectListShader.cpp +++ b/src/graphics/shader/rectListShader.cpp @@ -71,11 +71,11 @@ public: vec4_float_type = Type(spv::Op::OpTypeVector, float_type, 4u); function_type = Type(spv::Op::OpTypeFunction, void_type); - per_vertex_type = Type(spv::Op::OpTypeStruct, vec4_float_type); - builder.AddAnnotation({Word(spv::Op::OpMemberDecorate), per_vertex_type, 0u, - Word(spv::Decoration::BuiltIn), Word(spv::BuiltIn::Position)}); - builder.AddAnnotation( - {Word(spv::Op::OpDecorate), per_vertex_type, Word(spv::Decoration::Block)}); + per_vertex_type = builder.DecoratedType( + Word(spv::Op::OpTypeStruct), {vec4_float_type}, + {{Word(spv::Op::OpMemberDecorate), + {0u, Word(spv::Decoration::BuiltIn), Word(spv::BuiltIn::Position)}}, + {Word(spv::Op::OpDecorate), {Word(spv::Decoration::Block)}}}); ptr_input_vec4_float = Pointer(spv::StorageClass::Input, vec4_float_type); ptr_output_vec4_float = Pointer(spv::StorageClass::Output, vec4_float_type); @@ -197,15 +197,11 @@ public: private: template uint32_t Type(spv::Op opcode, Args... operands) { - const auto id = builder.AllocateId(); - builder.AddType({Word(opcode), id, Word(operands)...}); - return id; + return builder.Type(Word(opcode), {Word(operands)...}); } uint32_t Constant(uint32_t type, uint32_t value) { - const auto id = builder.AllocateId(); - builder.AddType({Word(spv::Op::OpConstant), type, id, value}); - return id; + return builder.Constant(Word(spv::Op::OpConstant), type, {value}); } uint32_t Pointer(spv::StorageClass storage, uint32_t type) { @@ -246,26 +242,12 @@ private: void Store(uint32_t pointer, uint32_t value) { Emit(spv::Op::OpStore, pointer, value); } - uint32_t Int(uint32_t value) { - auto& id = int_constants[value]; - if (id == 0) { - id = Constant(int_type, value); - } - return id; - } + uint32_t Int(uint32_t value) { return Constant(int_type, value); } - uint32_t Uint(uint32_t value) { - auto& id = uint_constants[value]; - if (id == 0) { - id = Constant(uint_type, value); - } - return id; - } + uint32_t Uint(uint32_t value) { return Constant(uint_type, value); } uint32_t AddInterface(spv::StorageClass storage, uint32_t type) { - const auto variable = builder.AllocateId(); - builder.AddType( - {Word(spv::Op::OpVariable), Pointer(storage, type), variable, Word(storage)}); + const auto variable = builder.DefineGlobalVariable(Pointer(storage, type), Word(storage)); interfaces.push_back(variable); return variable; } @@ -275,8 +257,8 @@ private: } void DefineEntry(spv::ExecutionModel model) { - builder.AddCapability({Word(spv::Capability::Shader)}); - builder.AddCapability({Word(spv::Capability::Tessellation)}); + builder.RequireCapability(Word(spv::Capability::Shader)); + builder.RequireCapability(Word(spv::Capability::Tessellation)); main = Result(spv::Op::OpFunction, void_type, spv::FunctionControlMask::MaskNone, function_type); if (model == spv::ExecutionModel::TessellationControl) { @@ -358,8 +340,6 @@ private: std::vector interfaces; std::vector inputs; std::vector outputs; - std::array int_constants {}; - std::array uint_constants {}; uint32_t main = 0; uint32_t void_type = 0; uint32_t bool_type = 0; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 3e0422987..50b1f8074 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -1297,24 +1297,34 @@ std::vector MakePassthroughVertexSpirv() { using ShaderRecompiler::Spirv::Builder; Builder b; - const auto void_type = b.AllocateId(); - const auto uint_type = b.AllocateId(); - const auto float_type = b.AllocateId(); - const auto vec2_type = b.AllocateId(); - const auto vec4_type = b.AllocateId(); - const auto per_vertex_type = b.AllocateId(); - const auto ptr_input_vec2 = b.AllocateId(); - const auto ptr_input_vec4 = b.AllocateId(); - const auto ptr_output_vec4 = b.AllocateId(); - const auto ptr_output_per_vertex = b.AllocateId(); - const auto func_type = b.AllocateId(); - const auto const_u32_0 = b.AllocateId(); - const auto const_f32_0 = b.AllocateId(); - const auto const_f32_1 = b.AllocateId(); - const auto in_pos = b.AllocateId(); - const auto in_color = b.AllocateId(); - const auto out_color = b.AllocateId(); - const auto per_vertex = b.AllocateId(); + const auto void_type = b.Type(OpTypeVoid); + const auto uint_type = b.Type(OpTypeInt, {32, 0}); + const auto float_type = b.Type(OpTypeFloat, {32}); + const auto vec2_type = b.Type(OpTypeVector, {float_type, 2}); + const auto vec4_type = b.Type(OpTypeVector, {float_type, 4}); + const auto per_vertex_type = b.DecoratedType( + OpTypeStruct, {vec4_type}, + {{OpDecorate, {DecorationBlock}}, + {OpMemberDecorate, {0, DecorationBuiltIn, BuiltInPosition}}}); + const auto ptr_input_vec2 = + b.Type(OpTypePointer, {StorageClassInput, vec2_type}); + const auto ptr_input_vec4 = + b.Type(OpTypePointer, {StorageClassInput, vec4_type}); + const auto ptr_output_vec4 = + b.Type(OpTypePointer, {StorageClassOutput, vec4_type}); + const auto ptr_output_per_vertex = + b.Type(OpTypePointer, {StorageClassOutput, per_vertex_type}); + const auto func_type = b.Type(OpTypeFunction, {void_type}); + const auto const_u32_0 = b.Constant(OpConstant, uint_type, {0}); + const auto const_f32_0 = b.Constant(OpConstant, float_type, {0x00000000u}); + const auto const_f32_1 = b.Constant(OpConstant, float_type, {0x3f800000u}); + const auto in_pos = b.DefineGlobalVariable(ptr_input_vec2, StorageClassInput); + const auto in_color = + b.DefineGlobalVariable(ptr_input_vec4, StorageClassInput); + const auto out_color = + b.DefineGlobalVariable(ptr_output_vec4, StorageClassOutput); + const auto per_vertex = + b.DefineGlobalVariable(ptr_output_per_vertex, StorageClassOutput); const auto main = b.AllocateId(); const auto label = b.AllocateId(); const auto pos2 = b.AllocateId(); @@ -1324,37 +1334,13 @@ std::vector MakePassthroughVertexSpirv() { const auto position = b.AllocateId(); const auto position_ptr = b.AllocateId(); - b.AddCapability({CapabilityShader}); + b.RequireCapability(CapabilityShader); b.AddMemoryModel({AddressingModelLogical, MemoryModelGLSL450}); b.AddEntryPoint(ExecutionModelVertex, main, "main", {in_pos, in_color, per_vertex, out_color}); b.AddAnnotation({OpDecorate, in_pos, DecorationLocation, 0}); b.AddAnnotation({OpDecorate, in_color, DecorationLocation, 1}); b.AddAnnotation({OpDecorate, out_color, DecorationLocation, 0}); - b.AddAnnotation({OpDecorate, per_vertex_type, DecorationBlock}); - b.AddAnnotation({OpMemberDecorate, per_vertex_type, 0, DecorationBuiltIn, - BuiltInPosition}); - - b.AddType({OpTypeVoid, void_type}); - b.AddType({OpTypeInt, uint_type, 32, 0}); - b.AddType({OpTypeFloat, float_type, 32}); - b.AddType({OpTypeVector, vec2_type, float_type, 2}); - b.AddType({OpTypeVector, vec4_type, float_type, 4}); - b.AddType({OpTypeStruct, per_vertex_type, vec4_type}); - b.AddType({OpTypePointer, ptr_input_vec2, StorageClassInput, vec2_type}); - b.AddType({OpTypePointer, ptr_input_vec4, StorageClassInput, vec4_type}); - b.AddType({OpTypePointer, ptr_output_vec4, StorageClassOutput, vec4_type}); - b.AddType({OpTypePointer, ptr_output_per_vertex, StorageClassOutput, - per_vertex_type}); - b.AddType({OpTypeFunction, func_type, void_type}); - b.AddType({OpConstant, uint_type, const_u32_0, 0}); - b.AddType({OpConstant, float_type, const_f32_0, 0x00000000u}); - b.AddType({OpConstant, float_type, const_f32_1, 0x3f800000u}); - b.AddType({OpVariable, ptr_input_vec2, in_pos, StorageClassInput}); - b.AddType({OpVariable, ptr_input_vec4, in_color, StorageClassInput}); - b.AddType({OpVariable, ptr_output_vec4, out_color, StorageClassOutput}); - b.AddType( - {OpVariable, ptr_output_per_vertex, per_vertex, StorageClassOutput}); b.AddFunction({OpFunction, void_type, main, FunctionControlNone, func_type}); b.AddFunction({OpLabel, label}); @@ -17471,6 +17457,7 @@ void CheckIndirectImageKeySwitch() { ShaderComputeInputInfo compute{}; std::vector spirv; + ShaderRecompiler::Spirv::AnalyzeProgramRequirements(program); Require(name, "SPIR-V emit", ShaderRecompiler::Spirv::EmitProgram( program, snapshot, {.compute = &compute}, spirv, &error), @@ -17644,9 +17631,9 @@ TestCase ImageStoreR32FloatUsesFormatlessStorageImage() { test.storage_image_dwords_per_pixel = 1; test.storage_image_rgba = std::vector(16, 0); test.expected_storage_image_rgba = expected_image; - test.required_spirv = {"OpCapability StorageImageReadWithoutFormat", - "OpCapability StorageImageWriteWithoutFormat"}; - test.forbidden_spirv = {"Rgba32f"}; + test.required_spirv = {"OpCapability StorageImageWriteWithoutFormat"}; + test.forbidden_spirv = {"OpCapability StorageImageReadWithoutFormat", + "Rgba32f", "OpTypeSampler", "OpTypeSampledImage"}; return test; } @@ -17757,6 +17744,10 @@ TestCase ImageAtomicVariants() { test.opcodes = {O::V_MOV_B32, O::IMAGE_ATOMIC_ADD, O::IMAGE_ATOMIC_UMIN, O::IMAGE_ATOMIC_AND, O::IMAGE_ATOMIC_OR, O::IMAGE_ATOMIC_XOR, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.required_spirv = {"OpImageTexelPointer", "R32ui"}; + test.forbidden_spirv = {"OpTypeSampler", "OpTypeSampledImage", + "StorageImageReadWithoutFormat", + "StorageImageWriteWithoutFormat"}; test.storage_image_rgba = MakeRgbaImage(4, 4); test.storage_image_r32ui = std::vector(16, 0); for (u32 i = 0; i < static_cast(std::size(initial)); i++) { diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index bd75d6d8a..22f264207 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -159,10 +159,16 @@ bool SpirvSourceHasInstructionUsing(const std::string &source, } bool SpirvContainsOpcode(const std::vector &binary, uint32_t opcode) { - for (uint32_t word : binary) { + for (size_t i = 5; i < binary.size();) { + const uint32_t word = binary[i]; + const uint32_t word_count = word >> 16u; + if (word_count == 0 || i + word_count > binary.size()) { + return false; + } if ((word & 0xffffu) == opcode) { return true; } + i += word_count; } return false; } @@ -188,8 +194,22 @@ uint32_t SpirvInstructionOpcodeCount(const std::vector &binary, struct SpirvMetrics { size_t words = 0; uint32_t instructions = 0; + uint32_t ext_inst_imports = 0; + uint32_t type_voids = 0; + uint32_t type_bools = 0; + uint32_t type_ints = 0; + uint32_t type_floats = 0; + uint32_t type_vectors = 0; uint32_t type_images = 0; + uint32_t type_samplers = 0; + uint32_t type_sampled_images = 0; + uint32_t runtime_arrays = 0; + uint32_t type_pointers = 0; + uint32_t type_functions = 0; + uint32_t image_pointers = 0; uint32_t variables = 0; + uint32_t function_variables = 0; + uint32_t workgroup_variables = 0; uint32_t loads = 0; uint32_t stores = 0; uint32_t array_lengths = 0; @@ -202,6 +222,12 @@ struct SpirvMetrics { uint32_t switches = 0; uint32_t ballots = 0; uint32_t quad_broadcasts = 0; + uint32_t sampled_1d_capabilities = 0; + uint32_t image_1d_capabilities = 0; + uint32_t image_query_capabilities = 0; + uint32_t storage_read_without_format_capabilities = 0; + uint32_t storage_write_without_format_capabilities = 0; + uint32_t image_texel_pointers = 0; }; SpirvMetrics MeasureSpirv(const std::vector &binary) { @@ -216,11 +242,78 @@ SpirvMetrics MeasureSpirv(const std::vector &binary) { } metrics.instructions++; switch (opcode) { + case 11u: // OpExtInstImport + metrics.ext_inst_imports++; + break; + case 17u: // OpCapability + if (word_count >= 2u) { + switch (binary[i + 1u]) { + case 43u: + metrics.sampled_1d_capabilities++; + break; + case 44u: + metrics.image_1d_capabilities++; + break; + case 50u: + metrics.image_query_capabilities++; + break; + case 55u: + metrics.storage_read_without_format_capabilities++; + break; + case 56u: + metrics.storage_write_without_format_capabilities++; + break; + default: + break; + } + } + break; + case 19u: // OpTypeVoid + metrics.type_voids++; + break; + case 20u: // OpTypeBool + metrics.type_bools++; + break; + case 21u: // OpTypeInt + metrics.type_ints++; + break; + case 22u: // OpTypeFloat + metrics.type_floats++; + break; + case 23u: // OpTypeVector + metrics.type_vectors++; + break; case 25u: // OpTypeImage metrics.type_images++; break; + case 26u: // OpTypeSampler + metrics.type_samplers++; + break; + case 27u: // OpTypeSampledImage + metrics.type_sampled_images++; + break; + case 29u: // OpTypeRuntimeArray + metrics.runtime_arrays++; + break; + case 32u: // OpTypePointer + metrics.type_pointers++; + if (word_count >= 4u && binary[i + 2u] == 11u) { + metrics.image_pointers++; + } + break; + case 33u: // OpTypeFunction + metrics.type_functions++; + break; case 59u: // OpVariable metrics.variables++; + if (word_count >= 4u && binary[i + 3u] == 7u) { + metrics.function_variables++; + } else if (word_count >= 4u && binary[i + 3u] == 4u) { + metrics.workgroup_variables++; + } + break; + case 60u: // OpImageTexelPointer + metrics.image_texel_pointers++; break; case 61u: // OpLoad metrics.loads++; @@ -272,8 +365,15 @@ void CheckSpirvBudget(const char *name, const std::vector &binary, const bool within = actual.words <= budget.words && actual.instructions <= budget.instructions && + actual.ext_inst_imports <= budget.ext_inst_imports && actual.type_images <= budget.type_images && + actual.type_samplers <= budget.type_samplers && + actual.type_sampled_images <= budget.type_sampled_images && + actual.runtime_arrays <= budget.runtime_arrays && + actual.image_pointers <= budget.image_pointers && actual.variables <= budget.variables && actual.loads <= budget.loads && + actual.function_variables <= budget.function_variables && + actual.workgroup_variables <= budget.workgroup_variables && actual.stores <= budget.stores && actual.array_lengths <= budget.array_lengths && actual.phis <= budget.phis && actual.labels <= budget.labels && @@ -282,7 +382,15 @@ void CheckSpirvBudget(const char *name, const std::vector &binary, actual.branches <= budget.branches && actual.conditional_branches <= budget.conditional_branches && actual.switches <= budget.switches && actual.ballots <= budget.ballots && - actual.quad_broadcasts <= budget.quad_broadcasts; + actual.quad_broadcasts <= budget.quad_broadcasts && + actual.sampled_1d_capabilities <= budget.sampled_1d_capabilities && + actual.image_1d_capabilities <= budget.image_1d_capabilities && + actual.image_query_capabilities <= budget.image_query_capabilities && + actual.storage_read_without_format_capabilities <= + budget.storage_read_without_format_capabilities && + actual.storage_write_without_format_capabilities <= + budget.storage_write_without_format_capabilities && + actual.image_texel_pointers <= budget.image_texel_pointers; if (!within) { std::fprintf( stderr, @@ -295,9 +403,9 @@ void CheckSpirvBudget(const char *name, const std::vector &binary, "cond=%u switches=%u ballots=%u quad=%u\n", name, actual.words, actual.instructions, actual.type_images, actual.variables, actual.loads, actual.stores, actual.array_lengths, - actual.phis, actual.labels, actual.loop_merges, - actual.selection_merges, actual.branches, actual.conditional_branches, - actual.switches, actual.ballots, actual.quad_broadcasts, budget.words, + actual.phis, actual.labels, actual.loop_merges, actual.selection_merges, + actual.branches, actual.conditional_branches, actual.switches, + actual.ballots, actual.quad_broadcasts, budget.words, budget.instructions, budget.type_images, budget.variables, budget.loads, budget.stores, budget.array_lengths, budget.phis, budget.labels, budget.loop_merges, budget.selection_merges, budget.branches, @@ -815,8 +923,8 @@ void TestSpirvRequirementsAnalysis() { block->AppendNewInst(ValueOpcode::SetAttribute, {Value(0u), Value(true)}); export_value.SetFlags(ExportFlags{.index = 0}); - const auto requirements = - ShaderRecompiler::Spirv::GetProgramRequirements(program); + ShaderRecompiler::Spirv::AnalyzeProgramRequirements(program); + const auto requirements = *program.spirv_requirements; Check(requirements.requires_exact_subgroup && requirements.subgroup_ballot && requirements.subgroup_shuffle && requirements.subgroup_local_invocation_id && @@ -826,8 +934,8 @@ void TestSpirvRequirementsAnalysis() { "consolidated SPIR-V requirements missed an IR dependency"); program.stage = ShaderType::Compute; - const auto compute_requirements = - ShaderRecompiler::Spirv::GetProgramRequirements(program); + ShaderRecompiler::Spirv::AnalyzeProgramRequirements(program); + const auto compute_requirements = *program.spirv_requirements; Check(!compute_requirements.function_lds && !compute_requirements.pixel_valid_mask, "stage-specific SPIR-V requirements leaked into compute"); @@ -835,14 +943,14 @@ void TestSpirvRequirementsAnalysis() { program.stage = ShaderType::Pixel; shared.SetFlags(MemoryFlags{.index = 1}); export_value.SetFlags(ExportFlags{.index = 1}); - const auto invalid_metadata = - ShaderRecompiler::Spirv::GetProgramRequirements(program); + ShaderRecompiler::Spirv::AnalyzeProgramRequirements(program); + const auto invalid_metadata = *program.spirv_requirements; Check(!invalid_metadata.function_lds && !invalid_metadata.pixel_valid_mask, "invalid SPIR-V requirement metadata was accepted"); Program empty; - const auto empty_requirements = - ShaderRecompiler::Spirv::GetProgramRequirements(empty); + ShaderRecompiler::Spirv::AnalyzeProgramRequirements(empty); + const auto empty_requirements = *empty.spirv_requirements; Check(!empty_requirements.requires_exact_subgroup && !empty_requirements.subgroup_ballot && !empty_requirements.subgroup_shuffle && @@ -872,9 +980,7 @@ void TestNativeSubgroupPolicy() { block->AppendNewInst(opcode, {Value(&resource), Value(0u), Value(true), Value(1u), Value(0u)}); } - program.spirv_requirements.reset(); - program.spirv_requirements = - ShaderRecompiler::Spirv::GetProgramRequirements(program); + ShaderRecompiler::Spirv::AnalyzeProgramRequirements(program); }; GraphicContext context; @@ -906,8 +1012,7 @@ void TestNativeSubgroupPolicy() { ShaderRecompiler::IR::Program cross_lane = safe; SetTypedValues(cross_lane, ShaderRecompiler::IR::ValueOpcode::ReadLane); - Check(ShaderRecompiler::Spirv::GetProgramRequirements(cross_lane) - .requires_exact_subgroup && + Check(cross_lane.spirv_requirements->requires_exact_subgroup && ConfigureShaderSubgroup(ShaderSubgroupCapabilities{context}, vk::ShaderStageFlagBits::eVertex, cross_lane) @@ -924,8 +1029,7 @@ void TestNativeSubgroupPolicy() { ShaderRecompiler::IR::Program zero_exec = safe; zero_exec.lane_mask_mode = ShaderLaneMaskMode::NativeWave; SetTypedValues(zero_exec); - Check(!ShaderRecompiler::Spirv::GetProgramRequirements(zero_exec) - .requires_exact_subgroup && + Check(!zero_exec.spirv_requirements->requires_exact_subgroup && ConfigureShaderSubgroup(ShaderSubgroupCapabilities{context}, vk::ShaderStageFlagBits::eCompute, zero_exec) @@ -935,20 +1039,17 @@ void TestNativeSubgroupPolicy() { ShaderRecompiler::IR::Program selective_exec = safe; SetTypedValues(selective_exec, ShaderRecompiler::IR::ValueOpcode::Ballot); - Check(ShaderRecompiler::Spirv::GetProgramRequirements(selective_exec) - .requires_exact_subgroup, + Check(selective_exec.spirv_requirements->requires_exact_subgroup, "selective EXEC write was classified mask-free"); ShaderRecompiler::IR::Program carry = safe; SetTypedValues(carry, ShaderRecompiler::IR::ValueOpcode::Ballot); - Check(ShaderRecompiler::Spirv::GetProgramRequirements(carry) - .requires_exact_subgroup, + Check(carry.spirv_requirements->requires_exact_subgroup, "lane-varying VCC carry producer was classified mask-free"); ShaderRecompiler::IR::Program vcc_branch = safe; SetTypedValues(vcc_branch, ShaderRecompiler::IR::ValueOpcode::Ballot); - Check(ShaderRecompiler::Spirv::GetProgramRequirements(vcc_branch) - .requires_exact_subgroup, + Check(vcc_branch.spirv_requirements->requires_exact_subgroup, "whole-wave VCC branch was classified mask-free"); ShaderRecompiler::IR::Program ds_partial = safe; @@ -3020,9 +3121,7 @@ void TestNewShaderRecompilerCapturedVop1SdwaByteConvert() { const uint32_t shader[] = { 0x7e0822f9u, 0x00040609u, // v_cvt_f32_ubyte0 v4, v9.word0 (PS 9ebba6b9) - EncodeExp0(0x00, 0x1), - EncodeExp1(4, 0, 0, 0), - EncodeSopp(0x01), + EncodeExp0(0x00, 0x1), EncodeExp1(4, 0, 0, 0), EncodeSopp(0x01), }; ShaderPixelInputInfo ps_info{}; @@ -3047,8 +3146,8 @@ void TestNewShaderRecompilerCapturedVop1SdwaByteConvert() { for (const auto &inst : *block) { extracts += inst.GetOpcode() == ShaderRecompiler::IR::ValueOpcode::BitFieldUExtract; - converts += inst.GetOpcode() == - ShaderRecompiler::IR::ValueOpcode::ConvertF32U32; + converts += + inst.GetOpcode() == ShaderRecompiler::IR::ValueOpcode::ConvertF32U32; } } Check(extracts != 0u && converts != 0u, @@ -3096,7 +3195,8 @@ void TestNewShaderRecompilerBootB16PackedAndSdwaOpcodes() { 0x4a0c0cf9u, 0x0d860688u, // v_add_nc_u32 v6, 8, sign-extended v6.hi 0x4a08c2f9u, - 0x0686156au, // captured v_add_nc_u32 v4.word1, vcc_lo, v97; preserve word0 + 0x0686156au, // captured v_add_nc_u32 v4.word1, vcc_lo, v97; preserve + // word0 0x4c1616f9u, 0x0686128du, // v_sub_nc_u32 v11.byte2, 13, v11; preserve other // destination bytes @@ -3152,9 +3252,10 @@ void TestNewShaderRecompilerBootB16PackedAndSdwaOpcodes() { Check(Common::ContainsStr(result.decoded_dump, "v_add_nc_u32 v6") && Common::ContainsStr(result.decoded_dump, "v6.sdwa(sel=5,sext=1"), "new decoder did not decode V_ADD_NC_U32 SDWA sign-extended high word"); - Check(Common::ContainsStr(result.decoded_dump, - "v_add_nc_u32 v4.sdwa(sel=5,sext=0), vcc_lo, v97"), - "new decoder did not decode captured V_ADD_NC_U32 high-word destination"); + Check( + Common::ContainsStr(result.decoded_dump, + "v_add_nc_u32 v4.sdwa(sel=5,sext=0), vcc_lo, v97"), + "new decoder did not decode captured V_ADD_NC_U32 high-word destination"); Check(Common::ContainsStr(result.decoded_dump, "v_sub_nc_u32 v11.sdwa(sel=2"), "new decoder did not decode V_SUB_NC_U32 SDWA byte-2 destination"); Check(Common::ContainsStr(result.decoded_dump, "v_min_u32 v10.sdwa(sel=4"), @@ -3695,9 +3796,9 @@ void TestNewShaderDecoderArchitecture() { Check(DecodeInstruction(vop2_sdwa_partial_dst, 0u, sdwa_add, &error), error.c_str()); Check(sdwa_add.family == Family::VOP2 && - sdwa_add.opcode == Opcode::V_ADD_NC_U32 && sdwa_add.word_count == 2u && - sdwa_add.dst.reg == 4u && sdwa_add.dst.sdwa_sel == 5u && - sdwa_add.dst.sdwa_dst_unused == 2u && + sdwa_add.opcode == Opcode::V_ADD_NC_U32 && + sdwa_add.word_count == 2u && sdwa_add.dst.reg == 4u && + sdwa_add.dst.sdwa_sel == 5u && sdwa_add.dst.sdwa_dst_unused == 2u && sdwa_add.src0.kind == OperandKind::VccLo && sdwa_add.src1.kind == OperandKind::Vgpr && sdwa_add.src1.reg == 97u, "decoder rejected or misdecoded captured partial-destination " @@ -3842,8 +3943,8 @@ void TestNewShaderRecompilerMemoryFamilyLowering() { "new decoder did not decode SMEM scalar-buffer dword load"); Check(Common::ContainsStr(result.decoded_dump, "s_buffer_load_dword s1, s8"), "SMEM scalar-buffer SBASE was not decoded as an SGPR-pair index"); - Check(Common::ContainsStr(result.decoded_dump, "buffer_load_dword"), - "new decoder did not decode MUBUF dword load"); + Check(Common::ContainsStr(result.decoded_dump, "buffer_load_dword"), + "new decoder did not decode MUBUF dword load"); Check(Common::ContainsStr(result.decoded_dump, "buffer_store_dword"), "new decoder did not decode MUBUF dword store"); Check(Common::ContainsStr(result.decoded_dump, "ds_read_b32"), @@ -3947,7 +4048,8 @@ void TestNewShaderRecompilerScalarMemoryBindingDomains() { address_binding->resources == std::vector{0u} && ShaderRecompiler::IR::FindBinding( raw.program.bindings, - ShaderRecompiler::IR::DescriptorBindingKind::Buffers) == nullptr && + ShaderRecompiler::IR::DescriptorBindingKind::Buffers) == + nullptr && ShaderRecompiler::IR::FindBinding( raw.program.bindings, ShaderRecompiler::IR::DescriptorBindingKind::FlattenedSrt) == @@ -3994,7 +4096,8 @@ void TestNewShaderRecompilerScalarMemoryBindingDomains() { ShaderRecompiler::IR::DescriptorBindingKind::Buffers); Check(buffer.program.info.buffers.size() == 1u && buffer.program.info.buffers[0].scalar && - buffer.program.info.addresses.empty() && buffer_binding != nullptr && + buffer.program.info.addresses.empty() && + buffer_binding != nullptr && buffer_binding->resources == std::vector{0u} && ShaderRecompiler::IR::FindBinding( buffer.program.bindings, @@ -4007,8 +4110,7 @@ void TestNewShaderRecompilerScalarMemoryBindingDomains() { buffer.program.bindings.buffer_offset_count == 1u, "descriptor scalar load did not use only the buffer domain"); Check(count_live_memory_ops( - buffer.program, - ShaderRecompiler::IR::ValueOpcode::ReadConstBuffer, + buffer.program, ShaderRecompiler::IR::ValueOpcode::ReadConstBuffer, ShaderRecompiler::IR::ResourceKind::ScalarBuffer) == 1u, "descriptor scalar load did not remain a live typed buffer operation"); CheckSpirvBinaryValidates(buffer.spirv); @@ -4402,6 +4504,13 @@ void TestNewShaderRecompilerPixelImageSampleLodSelection() { { const auto result = compile(0x20, 0xf); // image_sample + const auto metrics = MeasureSpirv(result.spirv); + Check(metrics.type_images == 1u && metrics.type_samplers == 1u && + metrics.type_sampled_images == 1u && + metrics.sampled_1d_capabilities == 0u && + metrics.image_1d_capabilities == 0u && + metrics.image_query_capabilities == 0u, + "plain 2D sample emitted unrelated image declarations"); Check(Common::ContainsStr(result.decoded_dump, "image_sample"), "plain pixel IMAGE_SAMPLE did not decode"); Check(SpirvInstructionOpcodeCount(result.spirv, OpImageSampleImplicitLod) == @@ -7346,13 +7455,15 @@ void TestNewShaderRecompilerCfgOverlappingEarlyExitLadder() { "early-exit ladder routing changed semantic instruction coverage"); Check(graph.blocks.size() > original_block_count, "early-exit ladder routing did not add forwarding blocks"); - const auto route_selects = std::ranges::count_if(graph.blocks, [](const auto &block) { - return block.terminator.condition == - ShaderRecompiler::CFG::BranchCondition::GotoVariable; - }); - const auto route_sets = std::ranges::count_if(graph.blocks, [](const auto &block) { - return block.terminator.goto_value >= 0; - }); + const auto route_selects = + std::ranges::count_if(graph.blocks, [](const auto &block) { + return block.terminator.condition == + ShaderRecompiler::CFG::BranchCondition::GotoVariable; + }); + const auto route_sets = + std::ranges::count_if(graph.blocks, [](const auto &block) { + return block.terminator.goto_value >= 0; + }); Check(route_selects != 0u && route_sets >= 3u, "early-exit ladder lacks explicit typed route state"); @@ -7398,13 +7509,15 @@ void TestNewShaderRecompilerCfgNestedEarlyExitSharedTerminal() { Check(CfgInstructionCoverage(graph, decoded.instructions.size()) == original_coverage, "nested early-exit structurization changed semantic coverage"); - const auto route_selects = std::ranges::count_if(graph.blocks, [](const auto &block) { - return block.terminator.condition == - ShaderRecompiler::CFG::BranchCondition::GotoVariable; - }); - const auto route_sets = std::ranges::count_if(graph.blocks, [](const auto &block) { - return block.terminator.goto_value >= 0; - }); + const auto route_selects = + std::ranges::count_if(graph.blocks, [](const auto &block) { + return block.terminator.condition == + ShaderRecompiler::CFG::BranchCondition::GotoVariable; + }); + const auto route_sets = + std::ranges::count_if(graph.blocks, [](const auto &block) { + return block.terminator.goto_value >= 0; + }); Check(route_selects == 1u && route_sets == 3u, "nested early exit lacks complete typed route state"); @@ -7799,6 +7912,126 @@ void TestNewShaderRecompilerExpVertexOutputs() { CheckSpirvBinaryValidates(result.spirv); } +void TestDemandDrivenSpirvDeclarations() { + using ShaderRecompiler::Spirv::Builder; + + Builder declarations; + const auto uint_type = declarations.Type(21u, {32u, 0u}); + Check(uint_type == declarations.Type(21u, {32u, 0u}), + "structural type interning returned different IDs"); + const auto zero = declarations.Constant(43u, uint_type, {0u}); + Check(zero == declarations.Constant(43u, uint_type, {0u}), + "constant interning returned different IDs"); + Check(declarations.Import("GLSL.std.450") == + declarations.Import("GLSL.std.450"), + "extended-instruction import was duplicated"); + declarations.RequireCapability(1u); + declarations.RequireCapability(1u); + const auto plain_array = declarations.Type(28u, {uint_type, zero}); + const auto decorated_array = declarations.DecoratedType( + 28u, {uint_type, zero}, {{71u, {6u, sizeof(uint32_t)}}}); + Check(decorated_array == + declarations.DecoratedType(28u, {uint_type, zero}, + {{71u, {6u, sizeof(uint32_t)}}}), + "decorated type interning returned different IDs"); + Check(plain_array != decorated_array, + "decorated and undecorated aggregate types were aliased"); + Check(declarations.DecoratedType(28u, {uint_type, zero}, {}) == plain_array, + "empty decorated type request bypassed structural interning"); + const auto ptr = declarations.Type(32u, {7u, uint_type}); + Check(declarations.DefineGlobalVariable(ptr, 7u) != + declarations.DefineGlobalVariable(ptr, 7u), + "distinct variables were structurally interned"); + const auto declaration_binary = declarations.Build(); + Check(SpirvInstructionOpcodeCount(declaration_binary, 17u) == 1u && + SpirvInstructionOpcodeCount(declaration_binary, 11u) == 1u && + SpirvInstructionOpcodeCount(declaration_binary, 21u) == 1u && + SpirvInstructionOpcodeCount(declaration_binary, 28u) == 2u && + SpirvInstructionOpcodeCount(declaration_binary, 43u) == 1u && + SpirvInstructionOpcodeCount(declaration_binary, 71u) == 1u && + SpirvInstructionOpcodeCount(declaration_binary, 59u) == 2u, + "canonical builder emitted duplicate declarations"); + + const auto compile = [](std::span shader) { + auto user_data = ImageTestUserData(); + auto options = MakeCompileOptions(ShaderType::Compute); + options.user_data = user_data.data(); + ShaderRecompiler::CompileResult result; + std::string error; + Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), + error.c_str()); + CheckSpirvBinaryValidates(result.spirv); + return result.spirv; + }; + + const uint32_t query[] = {EncodeMimg0(0x60, 0x3), EncodeMimg1(6, 0, 0, 1), + EncodeExp0(0x00, 0x3), EncodeExp1(6, 7, 0, 0), + EncodeSopp(0x01)}; + auto query_user_data = ImageTestUserData(); + auto query_pixel_info = RegressionPixelInputInfo(); + auto query_options = MakeCompileOptions(ShaderType::Pixel); + query_options.input_info.pixel = &query_pixel_info; + query_options.user_data = query_user_data.data(); + ShaderRecompiler::CompileResult query_result; + std::string query_error; + Check(ShaderRecompiler::TryRecompile(query, query_options, query_result, + &query_error), + query_error.c_str()); + CheckSpirvBinaryValidates(query_result.spirv); + const auto &query_spirv = query_result.spirv; + Check(SpirvInstructionOpcodeCount(query_spirv, 105u) == 1u, + "image query fixture did not reach OpImageQueryLod"); + const auto query_metrics = MeasureSpirv(query_spirv); + Check(query_metrics.type_images == 1u && query_metrics.type_samplers == 1u && + query_metrics.type_sampled_images == 1u && + query_metrics.image_query_capabilities == 1u && + query_metrics.sampled_1d_capabilities == 0u && + query_metrics.image_1d_capabilities == 0u, + "image query emitted unrelated or duplicate declarations"); + + const uint32_t store[] = {EncodeMimg0(0x08, 0xf), EncodeMimg1(0, 0, 0, 20), + EncodeSopp(0x01)}; + const auto store_spirv = compile(store); + Check(SpirvInstructionOpcodeCount(store_spirv, 99u) == 1u, + "storage image fixture did not reach OpImageWrite"); + const auto store_metrics = MeasureSpirv(store_spirv); + Check(store_metrics.type_images == 1u && store_metrics.type_samplers == 0u && + store_metrics.type_sampled_images == 0u && + store_metrics.storage_read_without_format_capabilities == 0u && + store_metrics.storage_write_without_format_capabilities == 1u, + "storage write emitted unrelated or duplicate declarations"); + + const uint32_t atomic[] = {EncodeMimg0(0x11, 0x1, true), + EncodeMimg1(0, 0, 0, 20), EncodeSopp(0x01)}; + const auto atomic_spirv = compile(atomic); + Check(SpirvInstructionOpcodeCount(atomic_spirv, 60u) == 1u, + "image atomic fixture did not reach OpImageTexelPointer"); + const auto atomic_metrics = MeasureSpirv(atomic_spirv); + Check(atomic_metrics.type_images == 1u && + atomic_metrics.type_samplers == 0u && + atomic_metrics.type_sampled_images == 0u && + atomic_metrics.image_pointers == 1u && + atomic_metrics.image_texel_pointers == 1u && + atomic_metrics.storage_read_without_format_capabilities == 0u && + atomic_metrics.storage_write_without_format_capabilities == 0u, + "image atomic emitted unrelated or duplicate declarations"); + + const uint32_t glsl[] = {EncodeVop1(0x24, 0, 1), EncodeVop1(0x24, 2, 3), + EncodeExp0(0x00, 0x3), EncodeExp1(0, 2, 0, 0), + EncodeSopp(0x01)}; + auto glsl_pixel_info = RegressionPixelInputInfo(); + auto glsl_options = MakeCompileOptions(ShaderType::Pixel); + glsl_options.input_info.pixel = &glsl_pixel_info; + ShaderRecompiler::CompileResult glsl_result; + std::string glsl_error; + Check(ShaderRecompiler::TryRecompile(glsl, glsl_options, glsl_result, + &glsl_error), + glsl_error.c_str()); + CheckSpirvBinaryValidates(glsl_result.spirv); + Check(MeasureSpirv(glsl_result.spirv).ext_inst_imports == 1u, + "multiple GLSL operations emitted duplicate imports"); +} + void TestTypedEntryStateIsMinimal() { using namespace ShaderRecompiler; @@ -7935,10 +8168,9 @@ void TestNewShaderRecompilerZeroInitialRegisterState() { CheckSpirvBinaryValidates(result.spirv); const uint32_t explicit_zero_shader[] = { - EncodeSMovB32(100, 128), // s_mov_b32 s100, 0 - EncodeVop1(0x01, 0, 100), // v_mov_b32 v0, s100 - EncodeExp0(0x0c, 0xf), - EncodeExp1(0, 1, 2, 3), // POS0 + EncodeSMovB32(100, 128), // s_mov_b32 s100, 0 + EncodeVop1(0x01, 0, 100), // v_mov_b32 v0, s100 + EncodeExp0(0x0c, 0xf), EncodeExp1(0, 1, 2, 3), // POS0 EncodeSopp(0x01), }; ShaderRecompiler::CompileResult explicit_zero; @@ -8059,8 +8291,7 @@ void TestNewShaderRecompilerPerInvocationMasksWithoutMirrors() { const uint32_t wqm_shader[] = { EncodeSop1(0x0a, 2, 126), // s_wqm_b64 s[2:3], exec EncodeVop1(0x01, 0, 2), // v_mov_b32 v0, s2 - EncodeExp0(0x0c, 0xf), - EncodeExp1(0, 1, 2, 3), // POS0 + EncodeExp0(0x0c, 0xf), EncodeExp1(0, 1, 2, 3), // POS0 EncodeSopp(0x01), }; Check(ShaderRecompiler::TryRecompile(wqm_shader, options, result, &error), @@ -8538,8 +8769,8 @@ void TestNewShaderRecompilerNativeBindingPlan() { const auto rejected_before = rejected_spirv; std::string rejected_error; Check(!ShaderRecompiler::Spirv::EmitProgram( - malformed, result.resources, options.input_info, - rejected_spirv, &rejected_error) && + malformed, result.resources, options.input_info, rejected_spirv, + &rejected_error) && rejected_spirv == rejected_before && rejected_error.find("topology") != std::string::npos, "malformed native binding plan was not rejected transactionally"); @@ -8559,8 +8790,8 @@ void TestNewShaderRecompilerNativeBindingPlan() { rejected_spirv = rejected_before; rejected_error.clear(); Check(!ShaderRecompiler::Spirv::EmitProgram( - result.program, stale_resources, options.input_info, - rejected_spirv, &rejected_error) && + result.program, stale_resources, options.input_info, rejected_spirv, + &rejected_error) && rejected_spirv == rejected_before && rejected_error.find("specialized format") != std::string::npos, "stale runtime image format re-selected an absent descriptor group"); @@ -8570,8 +8801,8 @@ void TestNewShaderRecompilerNativeBindingPlan() { rejected_spirv = rejected_before; rejected_error.clear(); Check(!ShaderRecompiler::Spirv::EmitProgram( - result.program, stale_dimension, options.input_info, - rejected_spirv, &rejected_error) && + result.program, stale_dimension, options.input_info, rejected_spirv, + &rejected_error) && rejected_spirv == rejected_before && rejected_error.find("specialized dimension") != std::string::npos, "unsupported runtime image type bypassed specialization validation"); @@ -8594,8 +8825,8 @@ void TestNewShaderRecompilerNativeBindingPlan() { rejected_spirv = rejected_before; rejected_error.clear(); const bool rejected = !ShaderRecompiler::Spirv::EmitProgram( - result.program, result.resources, options.input_info, - rejected_spirv, &rejected_error); + result.program, result.resources, options.input_info, rejected_spirv, + &rejected_error); buffer_handle->SetFlags(dense); Check(rejected && rejected_spirv == rejected_before && rejected_error.find("invalid dense resource") != std::string::npos, @@ -9387,7 +9618,8 @@ void TestNewShaderRecompilerPixelPipelineEntry() { void TestComputeLdsAllocationIdentity() { const uint32_t shader[] = { EncodeDs0(0x0d, 4288u), // ds_write_b32 v0, v1 offset:4288 - EncodeDs1(0, 1, 0), EncodeSopp(0x01), + EncodeDs1(0, 1, 0), + EncodeSopp(0x01), }; HW::ComputeShaderInfo regs{}; regs.cs_regs.data_addr = reinterpret_cast(shader); @@ -9409,6 +9641,8 @@ void TestComputeLdsAllocationIdentity() { Check(input_info.lds_size_dwords == expected_dwords, "COMPUTE_PGM_RSRC2 LDS allocation units were not decoded"); const std::vector binary(spirv.begin(), spirv.end()); + Check(MeasureSpirv(binary).workgroup_variables == 1u, + "LDS shader did not declare exactly one Workgroup variable"); Check(SpirvArrayLengthCount(binary, expected_dwords) == 1u, "SPIR-V workgroup array did not use the declared LDS allocation"); Check(SpirvUnsignedLessThanBoundCount(binary, expected_dwords) == 1u, @@ -9432,7 +9666,8 @@ void TestComputeLdsAllocationIdentity() { const uint32_t append_shader[] = { EncodeSMovB32(124, 132), // m0 = 4 bytes - EncodeDs0(0x3e), EncodeDs1(1, 0, 0), // ds_append v1 + EncodeDs0(0x3e), + EncodeDs1(1, 0, 0), // ds_append v1 EncodeSopp(0x01), }; ShaderComputeInputInfo append_info = RegressionComputeInputInfo(); @@ -9602,28 +9837,34 @@ void TestNewShaderRecompilerFlatAddressProvenanceBoundaries() { } void TestNewShaderRecompilerSpirvSizeBaselines() { - const auto compile = [](const char *name, std::span shader, - const SpirvMetrics &budget, - ShaderType stage = ShaderType::Compute, - ShaderLaneMaskMode lane_mask_mode = - ShaderLaneMaskMode::NativeWave) { - auto options = MakeCompileOptions(stage); - options.dump_ir = true; - options.lane_mask_mode = lane_mask_mode; + const auto compile = + [](const char *name, std::span shader, + const SpirvMetrics &budget, ShaderType stage = ShaderType::Compute, + ShaderLaneMaskMode lane_mask_mode = ShaderLaneMaskMode::NativeWave) { + auto options = MakeCompileOptions(stage); + options.dump_ir = true; + options.lane_mask_mode = lane_mask_mode; - ShaderRecompiler::CompileResult result; - std::string error; - Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), - error.c_str()); - CheckSpirvBinaryValidates(result.spirv); - CheckSpirvBudget(name, result.spirv, budget); - return result; - }; + ShaderRecompiler::CompileResult result; + std::string error; + Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), + error.c_str()); + CheckSpirvBinaryValidates(result.spirv); + CheckSpirvBudget(name, result.spirv, budget); + return result; + }; const uint32_t empty[] = {EncodeSopp(0x01)}; - compile("empty", empty, - {.words = 640, .instructions = 140, .type_images = 24, - .variables = 1, .labels = 4, .branches = 3}); + const auto empty_result = + compile("empty", empty, + {.words = 64, .instructions = 20, .labels = 4, .branches = 3}); + const auto empty_metrics = MeasureSpirv(empty_result.spirv); + Check(empty_metrics.type_voids == 1u && empty_metrics.type_functions == 1u && + empty_metrics.type_bools == 0u && empty_metrics.type_ints == 0u && + empty_metrics.type_floats == 0u && + empty_metrics.type_vectors == 0u && + empty_metrics.type_pointers == 0u, + "empty shader emitted unused core types or pointers"); const uint32_t structured_phi[] = { EncodeSMovB32(0, 128), // s0 = 0 @@ -9633,19 +9874,17 @@ void TestNewShaderRecompilerSpirvSizeBaselines() { EncodeSopp(0x02, 0xfffcu), // continue/backedge EncodeSopp(0x01), }; - const auto structured_result = - compile("structured-phi", structured_phi, - {.words = 705, - .instructions = 158, - .type_images = 24, - .variables = 2, - .loads = 1, - .stores = 2, - .phis = 1, - .labels = 7, - .loop_merges = 1, - .branches = 5, - .conditional_branches = 1}); + const auto structured_result = compile("structured-phi", structured_phi, + {.words = 147, + .instructions = 43, + .variables = 1, + .function_variables = 1, + .loads = 1, + .stores = 2, + .labels = 7, + .loop_merges = 1, + .branches = 5, + .conditional_branches = 1}); Check(Common::ContainsStr(structured_result.ir_dump, "Phi"), "structured Phi size fixture no longer contains an IR Phi"); @@ -9656,25 +9895,22 @@ void TestNewShaderRecompilerSpirvSizeBaselines() { EncodeMubuf1(0, 0, 1), // buffer_store_dwordx4 v[0:3] EncodeSopp(0x01), }; - const auto wide_result = - compile("wide-buffer", wide_buffer, - {.words = 2549, - .instructions = 583, - .type_images = 24, - .variables = 3, - .loads = 9, - .stores = 4, - .array_lengths = 8, - .phis = 8, - .labels = 52, - .selection_merges = 16, - .branches = 35, - .conditional_branches = 16}); - Check(Common::ContainsStr(wide_result.decoded_dump, - "BUFFER_LOAD_DWORDX4"), + const auto wide_result = compile("wide-buffer", wide_buffer, + {.words = 1988, + .instructions = 466, + .runtime_arrays = 1, + .variables = 2, + .loads = 9, + .stores = 4, + .array_lengths = 8, + .phis = 8, + .labels = 52, + .selection_merges = 16, + .branches = 35, + .conditional_branches = 16}); + Check(Common::ContainsStr(wide_result.decoded_dump, "BUFFER_LOAD_DWORDX4"), "wide buffer size fixture no longer decodes its x4 load"); - Check(Common::ContainsStr(wide_result.decoded_dump, - "BUFFER_STORE_DWORDX4"), + Check(Common::ContainsStr(wide_result.decoded_dump, "BUFFER_STORE_DWORDX4"), "wide buffer size fixture no longer decodes its x4 store"); const uint32_t wqm[] = { @@ -9687,9 +9923,8 @@ void TestNewShaderRecompilerSpirvSizeBaselines() { }; const auto wqm_result = compile("wqm", wqm, - {.words = 1221, - .instructions = 260, - .type_images = 24, + {.words = 703, + .instructions = 155, .variables = 4, .loads = 3, .stores = 1, @@ -9697,8 +9932,7 @@ void TestNewShaderRecompilerSpirvSizeBaselines() { .selection_merges = 1, .branches = 4, .conditional_branches = 1, - .ballots = 1, - .quad_broadcasts = 4}, + .ballots = 1}, ShaderType::Vertex, ShaderLaneMaskMode::PerInvocation); Check(Common::ContainsStr(wqm_result.ir_dump, "WqmMask"), "WQM size fixture no longer reaches per-invocation WqmMask IR"); @@ -9710,20 +9944,19 @@ void TestNewShaderRecompilerSpirvSizeBaselines() { EncodeSopp(0x02, 0xfffeu), // B -> C EncodeSopp(0x01), }; - const auto dispatcher_result = - compile("dispatcher", dispatcher, - {.words = 822, - .instructions = 195, - .type_images = 24, - .variables = 5, - .loads = 4, - .stores = 14, - .labels = 14, - .loop_merges = 1, - .selection_merges = 1, - .branches = 11, - .conditional_branches = 1, - .switches = 1}); + const auto dispatcher_result = compile("dispatcher", dispatcher, + {.words = 258, + .instructions = 78, + .variables = 4, + .function_variables = 4, + .loads = 4, + .stores = 14, + .labels = 14, + .loop_merges = 1, + .selection_merges = 1, + .branches = 11, + .conditional_branches = 1, + .switches = 1}); Check(dispatcher_result.program.dispatcher_fallback && SpirvInstructionOpcodeCount(dispatcher_result.spirv, 251u) == 1u, "dispatcher size fixture no longer exercises whole-function dispatch"); @@ -9741,6 +9974,7 @@ int main() { TestNormalizedImageContracts(); TestSpirvRequirementsAnalysis(); TestNewShaderRecompilerSpirvSizeBaselines(); + TestDemandDrivenSpirvDeclarations(); TestNativeSubgroupPolicy(); TestNewShaderRecompilerSMovB32(); TestNewShaderRecompilerCapturedVop1SdwaByteConvert();