diff --git a/spirv_cfg.hpp b/spirv_cfg.hpp index 1d85fe0a..16b7774b 100644 --- a/spirv_cfg.hpp +++ b/spirv_cfg.hpp @@ -33,135 +33,135 @@ class Compiler; class CFG { public: - CFG(Compiler &compiler, const SPIRFunction &function); + CFG(Compiler &compiler, const SPIRFunction &function); - Compiler &get_compiler() - { - return compiler; - } + Compiler &get_compiler() + { + return compiler; + } - const Compiler &get_compiler() const - { - return compiler; - } + const Compiler &get_compiler() const + { + return compiler; + } - const SPIRFunction &get_function() const - { - return func; - } + const SPIRFunction &get_function() const + { + return func; + } - uint32_t get_immediate_dominator(uint32_t block) const - { - auto itr = immediate_dominators.find(block); - if (itr != std::end(immediate_dominators)) - return itr->second; - else - return 0; - } + uint32_t get_immediate_dominator(uint32_t block) const + { + auto itr = immediate_dominators.find(block); + if (itr != std::end(immediate_dominators)) + return itr->second; + else + return 0; + } - bool is_reachable(uint32_t block) const - { - return visit_order.count(block) != 0; - } + bool is_reachable(uint32_t block) const + { + return visit_order.count(block) != 0; + } - uint32_t get_visit_order(uint32_t block) const - { - auto itr = visit_order.find(block); - assert(itr != std::end(visit_order)); - int v = itr->second.get(); - assert(v > 0); - return uint32_t(v); - } + uint32_t get_visit_order(uint32_t block) const + { + auto itr = visit_order.find(block); + assert(itr != std::end(visit_order)); + int v = itr->second.get(); + assert(v > 0); + return uint32_t(v); + } - uint32_t find_common_dominator(uint32_t a, uint32_t b) const; + uint32_t find_common_dominator(uint32_t a, uint32_t b) const; - const SmallVector &get_preceding_edges(uint32_t block) const - { - auto itr = preceding_edges.find(block); - if (itr != std::end(preceding_edges)) - return itr->second; - else - return empty_vector; - } + const SmallVector &get_preceding_edges(uint32_t block) const + { + auto itr = preceding_edges.find(block); + if (itr != std::end(preceding_edges)) + return itr->second; + else + return empty_vector; + } - const SmallVector &get_succeeding_edges(uint32_t block) const - { - auto itr = succeeding_edges.find(block); - if (itr != std::end(succeeding_edges)) - return itr->second; - else - return empty_vector; - } + const SmallVector &get_succeeding_edges(uint32_t block) const + { + auto itr = succeeding_edges.find(block); + if (itr != std::end(succeeding_edges)) + return itr->second; + else + return empty_vector; + } - template - void walk_from(std::unordered_set &seen_blocks, uint32_t block, const Op &op) const - { - if (seen_blocks.count(block)) - return; - seen_blocks.insert(block); + template + void walk_from(std::unordered_set &seen_blocks, uint32_t block, const Op &op) const + { + if (seen_blocks.count(block)) + return; + seen_blocks.insert(block); - if (op(block)) - { - for (auto b : get_succeeding_edges(block)) - walk_from(seen_blocks, b, op); - } - } + if (op(block)) + { + for (auto b : get_succeeding_edges(block)) + walk_from(seen_blocks, b, op); + } + } - uint32_t find_loop_dominator(uint32_t block) const; + uint32_t find_loop_dominator(uint32_t block) const; - bool node_terminates_control_flow_in_sub_graph(BlockID from, BlockID to) const; + bool node_terminates_control_flow_in_sub_graph(BlockID from, BlockID to) const; private: - struct VisitOrder - { - int &get() - { - return v; - } + struct VisitOrder + { + int &get() + { + return v; + } - const int &get() const - { - return v; - } + const int &get() const + { + return v; + } - int v = -1; - }; + int v = -1; + }; - Compiler &compiler; - const SPIRFunction &func; - std::unordered_map> preceding_edges; - std::unordered_map> succeeding_edges; - std::unordered_map immediate_dominators; - std::unordered_map visit_order; - SmallVector post_order; - SmallVector empty_vector; + Compiler &compiler; + const SPIRFunction &func; + std::unordered_map> preceding_edges; + std::unordered_map> succeeding_edges; + std::unordered_map immediate_dominators; + std::unordered_map visit_order; + SmallVector post_order; + SmallVector empty_vector; - void add_branch(uint32_t from, uint32_t to); - void build_post_order_visit_order(); - void build_immediate_dominators(); - bool post_order_visit(uint32_t block); - uint32_t visit_count = 0; + void add_branch(uint32_t from, uint32_t to); + void build_post_order_visit_order(); + void build_immediate_dominators(); + bool post_order_visit(uint32_t block); + uint32_t visit_count = 0; - bool is_back_edge(uint32_t to) const; - bool has_visited_forward_edge(uint32_t to) const; + bool is_back_edge(uint32_t to) const; + bool has_visited_forward_edge(uint32_t to) const; }; class DominatorBuilder { public: - DominatorBuilder(const CFG &cfg); + DominatorBuilder(const CFG &cfg); - void add_block(uint32_t block); - uint32_t get_dominator() const - { - return dominator; - } + void add_block(uint32_t block); + uint32_t get_dominator() const + { + return dominator; + } - void lift_continue_block_dominator(); + void lift_continue_block_dominator(); private: - const CFG &cfg; - uint32_t dominator = 0; + const CFG &cfg; + uint32_t dominator = 0; }; } // namespace SPIRV_CROSS_NAMESPACE diff --git a/spirv_cross.hpp b/spirv_cross.hpp index e9062b48..3388c980 100644 --- a/spirv_cross.hpp +++ b/spirv_cross.hpp @@ -35,1147 +35,1147 @@ namespace SPIRV_CROSS_NAMESPACE { struct Resource { - // Resources are identified with their SPIR-V ID. - // This is the ID of the OpVariable. - ID id; + // Resources are identified with their SPIR-V ID. + // This is the ID of the OpVariable. + ID id; - // The type ID of the variable which includes arrays and all type modifications. - // This type ID is not suitable for parsing OpMemberDecoration of a struct and other decorations in general - // since these modifications typically happen on the base_type_id. - TypeID type_id; + // The type ID of the variable which includes arrays and all type modifications. + // This type ID is not suitable for parsing OpMemberDecoration of a struct and other decorations in general + // since these modifications typically happen on the base_type_id. + TypeID type_id; - // The base type of the declared resource. - // This type is the base type which ignores pointers and arrays of the type_id. - // This is mostly useful to parse decorations of the underlying type. - // base_type_id can also be obtained with get_type(get_type(type_id).self). - TypeID base_type_id; + // The base type of the declared resource. + // This type is the base type which ignores pointers and arrays of the type_id. + // This is mostly useful to parse decorations of the underlying type. + // base_type_id can also be obtained with get_type(get_type(type_id).self). + TypeID base_type_id; - // The declared name (OpName) of the resource. - // For Buffer blocks, the name actually reflects the externally - // visible Block name. - // - // This name can be retrieved again by using either - // get_name(id) or get_name(base_type_id) depending if it's a buffer block or not. - // - // This name can be an empty string in which case get_fallback_name(id) can be - // used which obtains a suitable fallback identifier for an ID. - std::string name; + // The declared name (OpName) of the resource. + // For Buffer blocks, the name actually reflects the externally + // visible Block name. + // + // This name can be retrieved again by using either + // get_name(id) or get_name(base_type_id) depending if it's a buffer block or not. + // + // This name can be an empty string in which case get_fallback_name(id) can be + // used which obtains a suitable fallback identifier for an ID. + std::string name; }; struct BuiltInResource { - // This is mostly here to support reflection of builtins such as Position/PointSize/CullDistance/ClipDistance. - // This needs to be different from Resource since we can collect builtins from blocks. - // A builtin present here does not necessarily mean it's considered an active builtin, - // since variable ID "activeness" is only tracked on OpVariable level, not Block members. - // For that, update_active_builtins() -> has_active_builtin() can be used to further refine the reflection. - spv::BuiltIn builtin; + // This is mostly here to support reflection of builtins such as Position/PointSize/CullDistance/ClipDistance. + // This needs to be different from Resource since we can collect builtins from blocks. + // A builtin present here does not necessarily mean it's considered an active builtin, + // since variable ID "activeness" is only tracked on OpVariable level, not Block members. + // For that, update_active_builtins() -> has_active_builtin() can be used to further refine the reflection. + spv::BuiltIn builtin; - // This is the actual value type of the builtin. - // Typically float4, float, array for the gl_PerVertex builtins. - // If the builtin is a control point, the control point array type will be stripped away here as appropriate. - TypeID value_type_id; + // This is the actual value type of the builtin. + // Typically float4, float, array for the gl_PerVertex builtins. + // If the builtin is a control point, the control point array type will be stripped away here as appropriate. + TypeID value_type_id; - // This refers to the base resource which contains the builtin. - // If resource is a Block, it can hold multiple builtins, or it might not be a block. - // For advanced reflection scenarios, all information in builtin/value_type_id can be deduced, - // it's just more convenient this way. - Resource resource; + // This refers to the base resource which contains the builtin. + // If resource is a Block, it can hold multiple builtins, or it might not be a block. + // For advanced reflection scenarios, all information in builtin/value_type_id can be deduced, + // it's just more convenient this way. + Resource resource; }; struct ShaderResources { - SmallVector uniform_buffers; - SmallVector storage_buffers; - SmallVector stage_inputs; - SmallVector stage_outputs; - SmallVector subpass_inputs; - SmallVector storage_images; - SmallVector sampled_images; - SmallVector atomic_counters; - SmallVector acceleration_structures; - SmallVector gl_plain_uniforms; + SmallVector uniform_buffers; + SmallVector storage_buffers; + SmallVector stage_inputs; + SmallVector stage_outputs; + SmallVector subpass_inputs; + SmallVector storage_images; + SmallVector sampled_images; + SmallVector atomic_counters; + SmallVector acceleration_structures; + SmallVector gl_plain_uniforms; - // There can only be one push constant block, - // but keep the vector in case this restriction is lifted in the future. - SmallVector push_constant_buffers; + // There can only be one push constant block, + // but keep the vector in case this restriction is lifted in the future. + SmallVector push_constant_buffers; - SmallVector shader_record_buffers; + SmallVector shader_record_buffers; - // For Vulkan GLSL and HLSL source, - // these correspond to separate texture2D and samplers respectively. - SmallVector separate_images; - SmallVector separate_samplers; + // For Vulkan GLSL and HLSL source, + // these correspond to separate texture2D and samplers respectively. + SmallVector separate_images; + SmallVector separate_samplers; - SmallVector builtin_inputs; - SmallVector builtin_outputs; + SmallVector builtin_inputs; + SmallVector builtin_outputs; }; struct CombinedImageSampler { - // The ID of the sampler2D variable. - VariableID combined_id; - // The ID of the texture2D variable. - VariableID image_id; - // The ID of the sampler variable. - VariableID sampler_id; + // The ID of the sampler2D variable. + VariableID combined_id; + // The ID of the texture2D variable. + VariableID image_id; + // The ID of the sampler variable. + VariableID sampler_id; }; struct SpecializationConstant { - // The ID of the specialization constant. - ConstantID id; - // The constant ID of the constant, used in Vulkan during pipeline creation. - uint32_t constant_id; + // The ID of the specialization constant. + ConstantID id; + // The constant ID of the constant, used in Vulkan during pipeline creation. + uint32_t constant_id; }; struct BufferRange { - unsigned index; - size_t offset; - size_t range; + unsigned index; + size_t offset; + size_t range; }; enum BufferPackingStandard { - BufferPackingStd140, - BufferPackingStd430, - BufferPackingStd140EnhancedLayout, - BufferPackingStd430EnhancedLayout, - BufferPackingHLSLCbuffer, - BufferPackingHLSLCbufferPackOffset, - BufferPackingScalar, - BufferPackingScalarEnhancedLayout + BufferPackingStd140, + BufferPackingStd430, + BufferPackingStd140EnhancedLayout, + BufferPackingStd430EnhancedLayout, + BufferPackingHLSLCbuffer, + BufferPackingHLSLCbufferPackOffset, + BufferPackingScalar, + BufferPackingScalarEnhancedLayout }; struct EntryPoint { - std::string name; - spv::ExecutionModel execution_model; + std::string name; + spv::ExecutionModel execution_model; }; class Compiler { public: - friend class CFG; - friend class DominatorBuilder; + friend class CFG; + friend class DominatorBuilder; - // The constructor takes a buffer of SPIR-V words and parses it. - // It will create its own parser, parse the SPIR-V and move the parsed IR - // as if you had called the constructors taking ParsedIR directly. - explicit Compiler(std::vector ir); - Compiler(const uint32_t *ir, size_t word_count); + // The constructor takes a buffer of SPIR-V words and parses it. + // It will create its own parser, parse the SPIR-V and move the parsed IR + // as if you had called the constructors taking ParsedIR directly. + explicit Compiler(std::vector ir); + Compiler(const uint32_t *ir, size_t word_count); - // This is more modular. We can also consume a ParsedIR structure directly, either as a move, or copy. - // With copy, we can reuse the same parsed IR for multiple Compiler instances. - explicit Compiler(const ParsedIR &ir); - explicit Compiler(ParsedIR &&ir); + // This is more modular. We can also consume a ParsedIR structure directly, either as a move, or copy. + // With copy, we can reuse the same parsed IR for multiple Compiler instances. + explicit Compiler(const ParsedIR &ir); + explicit Compiler(ParsedIR &&ir); - virtual ~Compiler() = default; + virtual ~Compiler() = default; - // After parsing, API users can modify the SPIR-V via reflection and call this - // to disassemble the SPIR-V into the desired langauage. - // Sub-classes actually implement this. - virtual std::string compile(); + // After parsing, API users can modify the SPIR-V via reflection and call this + // to disassemble the SPIR-V into the desired langauage. + // Sub-classes actually implement this. + virtual std::string compile(); - // Gets the identifier (OpName) of an ID. If not defined, an empty string will be returned. - const std::string &get_name(ID id) const; + // Gets the identifier (OpName) of an ID. If not defined, an empty string will be returned. + const std::string &get_name(ID id) const; - // Applies a decoration to an ID. Effectively injects OpDecorate. - void set_decoration(ID id, spv::Decoration decoration, uint32_t argument = 0); - void set_decoration_string(ID id, spv::Decoration decoration, const std::string &argument); + // Applies a decoration to an ID. Effectively injects OpDecorate. + void set_decoration(ID id, spv::Decoration decoration, uint32_t argument = 0); + void set_decoration_string(ID id, spv::Decoration decoration, const std::string &argument); - // Overrides the identifier OpName of an ID. - // Identifiers beginning with underscores or identifiers which contain double underscores - // are reserved by the implementation. - void set_name(ID id, const std::string &name); + // Overrides the identifier OpName of an ID. + // Identifiers beginning with underscores or identifiers which contain double underscores + // are reserved by the implementation. + void set_name(ID id, const std::string &name); - // Gets a bitmask for the decorations which are applied to ID. - // I.e. (1ull << spv::DecorationFoo) | (1ull << spv::DecorationBar) - const Bitset &get_decoration_bitset(ID id) const; + // Gets a bitmask for the decorations which are applied to ID. + // I.e. (1ull << spv::DecorationFoo) | (1ull << spv::DecorationBar) + const Bitset &get_decoration_bitset(ID id) const; - // Returns whether the decoration has been applied to the ID. - bool has_decoration(ID id, spv::Decoration decoration) const; + // Returns whether the decoration has been applied to the ID. + bool has_decoration(ID id, spv::Decoration decoration) const; - // Gets the value for decorations which take arguments. - // If the decoration is a boolean (i.e. spv::DecorationNonWritable), - // 1 will be returned. - // If decoration doesn't exist or decoration is not recognized, - // 0 will be returned. - uint32_t get_decoration(ID id, spv::Decoration decoration) const; - const std::string &get_decoration_string(ID id, spv::Decoration decoration) const; + // Gets the value for decorations which take arguments. + // If the decoration is a boolean (i.e. spv::DecorationNonWritable), + // 1 will be returned. + // If decoration doesn't exist or decoration is not recognized, + // 0 will be returned. + uint32_t get_decoration(ID id, spv::Decoration decoration) const; + const std::string &get_decoration_string(ID id, spv::Decoration decoration) const; - // Removes the decoration for an ID. - void unset_decoration(ID id, spv::Decoration decoration); + // Removes the decoration for an ID. + void unset_decoration(ID id, spv::Decoration decoration); - // Gets the SPIR-V type associated with ID. - // Mostly used with Resource::type_id and Resource::base_type_id to parse the underlying type of a resource. - const SPIRType &get_type(TypeID id) const; + // Gets the SPIR-V type associated with ID. + // Mostly used with Resource::type_id and Resource::base_type_id to parse the underlying type of a resource. + const SPIRType &get_type(TypeID id) const; - // Gets the SPIR-V type of a variable. - const SPIRType &get_type_from_variable(VariableID id) const; + // Gets the SPIR-V type of a variable. + const SPIRType &get_type_from_variable(VariableID id) const; - // Gets the underlying storage class for an OpVariable. - spv::StorageClass get_storage_class(VariableID id) const; + // Gets the underlying storage class for an OpVariable. + spv::StorageClass get_storage_class(VariableID id) const; - // If get_name() is an empty string, get the fallback name which will be used - // instead in the disassembled source. - virtual const std::string get_fallback_name(ID id) const; + // If get_name() is an empty string, get the fallback name which will be used + // instead in the disassembled source. + virtual const std::string get_fallback_name(ID id) const; - // If get_name() of a Block struct is an empty string, get the fallback name. - // This needs to be per-variable as multiple variables can use the same block type. - virtual const std::string get_block_fallback_name(VariableID id) const; + // If get_name() of a Block struct is an empty string, get the fallback name. + // This needs to be per-variable as multiple variables can use the same block type. + virtual const std::string get_block_fallback_name(VariableID id) const; - // Given an OpTypeStruct in ID, obtain the identifier for member number "index". - // This may be an empty string. - const std::string &get_member_name(TypeID id, uint32_t index) const; + // Given an OpTypeStruct in ID, obtain the identifier for member number "index". + // This may be an empty string. + const std::string &get_member_name(TypeID id, uint32_t index) const; - // Given an OpTypeStruct in ID, obtain the OpMemberDecoration for member number "index". - uint32_t get_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const; - const std::string &get_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration) const; + // Given an OpTypeStruct in ID, obtain the OpMemberDecoration for member number "index". + uint32_t get_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const; + const std::string &get_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration) const; - // Sets the member identifier for OpTypeStruct ID, member number "index". - void set_member_name(TypeID id, uint32_t index, const std::string &name); + // Sets the member identifier for OpTypeStruct ID, member number "index". + void set_member_name(TypeID id, uint32_t index, const std::string &name); - // Returns the qualified member identifier for OpTypeStruct ID, member number "index", - // or an empty string if no qualified alias exists - const std::string &get_member_qualified_name(TypeID type_id, uint32_t index) const; + // Returns the qualified member identifier for OpTypeStruct ID, member number "index", + // or an empty string if no qualified alias exists + const std::string &get_member_qualified_name(TypeID type_id, uint32_t index) const; - // Gets the decoration mask for a member of a struct, similar to get_decoration_mask. - const Bitset &get_member_decoration_bitset(TypeID id, uint32_t index) const; + // Gets the decoration mask for a member of a struct, similar to get_decoration_mask. + const Bitset &get_member_decoration_bitset(TypeID id, uint32_t index) const; - // Returns whether the decoration has been applied to a member of a struct. - bool has_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const; + // Returns whether the decoration has been applied to a member of a struct. + bool has_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const; - // Similar to set_decoration, but for struct members. - void set_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration, uint32_t argument = 0); - void set_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration, - const std::string &argument); + // Similar to set_decoration, but for struct members. + void set_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration, uint32_t argument = 0); + void set_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration, + const std::string &argument); - // Unsets a member decoration, similar to unset_decoration. - void unset_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration); + // Unsets a member decoration, similar to unset_decoration. + void unset_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration); - // Gets the fallback name for a member, similar to get_fallback_name. - virtual const std::string get_fallback_member_name(uint32_t index) const - { - return join("_", index); - } + // Gets the fallback name for a member, similar to get_fallback_name. + virtual const std::string get_fallback_member_name(uint32_t index) const + { + return join("_", index); + } - // Returns a vector of which members of a struct are potentially in use by a - // SPIR-V shader. The granularity of this analysis is per-member of a struct. - // This can be used for Buffer (UBO), BufferBlock/StorageBuffer (SSBO) and PushConstant blocks. - // ID is the Resource::id obtained from get_shader_resources(). - SmallVector get_active_buffer_ranges(VariableID id) const; + // Returns a vector of which members of a struct are potentially in use by a + // SPIR-V shader. The granularity of this analysis is per-member of a struct. + // This can be used for Buffer (UBO), BufferBlock/StorageBuffer (SSBO) and PushConstant blocks. + // ID is the Resource::id obtained from get_shader_resources(). + SmallVector get_active_buffer_ranges(VariableID id) const; - // Returns the effective size of a buffer block. - size_t get_declared_struct_size(const SPIRType &struct_type) const; + // Returns the effective size of a buffer block. + size_t get_declared_struct_size(const SPIRType &struct_type) const; - // Returns the effective size of a buffer block, with a given array size - // for a runtime array. - // SSBOs are typically declared as runtime arrays. get_declared_struct_size() will return 0 for the size. - // This is not very helpful for applications which might need to know the array stride of its last member. - // This can be done through the API, but it is not very intuitive how to accomplish this, so here we provide a helper function - // to query the size of the buffer, assuming that the last member has a certain size. - // If the buffer does not contain a runtime array, array_size is ignored, and the function will behave as - // get_declared_struct_size(). - // To get the array stride of the last member, something like: - // get_declared_struct_size_runtime_array(type, 1) - get_declared_struct_size_runtime_array(type, 0) will work. - size_t get_declared_struct_size_runtime_array(const SPIRType &struct_type, size_t array_size) const; + // Returns the effective size of a buffer block, with a given array size + // for a runtime array. + // SSBOs are typically declared as runtime arrays. get_declared_struct_size() will return 0 for the size. + // This is not very helpful for applications which might need to know the array stride of its last member. + // This can be done through the API, but it is not very intuitive how to accomplish this, so here we provide a helper function + // to query the size of the buffer, assuming that the last member has a certain size. + // If the buffer does not contain a runtime array, array_size is ignored, and the function will behave as + // get_declared_struct_size(). + // To get the array stride of the last member, something like: + // get_declared_struct_size_runtime_array(type, 1) - get_declared_struct_size_runtime_array(type, 0) will work. + size_t get_declared_struct_size_runtime_array(const SPIRType &struct_type, size_t array_size) const; - // Returns the effective size of a buffer block struct member. - size_t get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const; + // Returns the effective size of a buffer block struct member. + size_t get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const; - // Returns a set of all global variables which are statically accessed - // by the control flow graph from the current entry point. - // Only variables which change the interface for a shader are returned, that is, - // variables with storage class of Input, Output, Uniform, UniformConstant, PushConstant and AtomicCounter - // storage classes are returned. - // - // To use the returned set as the filter for which variables are used during compilation, - // this set can be moved to set_enabled_interface_variables(). - std::unordered_set get_active_interface_variables() const; + // Returns a set of all global variables which are statically accessed + // by the control flow graph from the current entry point. + // Only variables which change the interface for a shader are returned, that is, + // variables with storage class of Input, Output, Uniform, UniformConstant, PushConstant and AtomicCounter + // storage classes are returned. + // + // To use the returned set as the filter for which variables are used during compilation, + // this set can be moved to set_enabled_interface_variables(). + std::unordered_set get_active_interface_variables() const; - // Sets the interface variables which are used during compilation. - // By default, all variables are used. - // Once set, compile() will only consider the set in active_variables. - void set_enabled_interface_variables(std::unordered_set active_variables); + // Sets the interface variables which are used during compilation. + // By default, all variables are used. + // Once set, compile() will only consider the set in active_variables. + void set_enabled_interface_variables(std::unordered_set active_variables); - // Query shader resources, use ids with reflection interface to modify or query binding points, etc. - ShaderResources get_shader_resources() const; + // Query shader resources, use ids with reflection interface to modify or query binding points, etc. + ShaderResources get_shader_resources() const; - // Query shader resources, but only return the variables which are part of active_variables. - // E.g.: get_shader_resources(get_active_variables()) to only return the variables which are statically - // accessed. - ShaderResources get_shader_resources(const std::unordered_set &active_variables) const; + // Query shader resources, but only return the variables which are part of active_variables. + // E.g.: get_shader_resources(get_active_variables()) to only return the variables which are statically + // accessed. + ShaderResources get_shader_resources(const std::unordered_set &active_variables) const; - // Remapped variables are considered built-in variables and a backend will - // not emit a declaration for this variable. - // This is mostly useful for making use of builtins which are dependent on extensions. - void set_remapped_variable_state(VariableID id, bool remap_enable); - bool get_remapped_variable_state(VariableID id) const; + // Remapped variables are considered built-in variables and a backend will + // not emit a declaration for this variable. + // This is mostly useful for making use of builtins which are dependent on extensions. + void set_remapped_variable_state(VariableID id, bool remap_enable); + bool get_remapped_variable_state(VariableID id) const; - // For subpassInput variables which are remapped to plain variables, - // the number of components in the remapped - // variable must be specified as the backing type of subpass inputs are opaque. - void set_subpass_input_remapped_components(VariableID id, uint32_t components); - uint32_t get_subpass_input_remapped_components(VariableID id) const; + // For subpassInput variables which are remapped to plain variables, + // the number of components in the remapped + // variable must be specified as the backing type of subpass inputs are opaque. + void set_subpass_input_remapped_components(VariableID id, uint32_t components); + uint32_t get_subpass_input_remapped_components(VariableID id) const; - // All operations work on the current entry point. - // Entry points can be swapped out with set_entry_point(). - // Entry points should be set right after the constructor completes as some reflection functions traverse the graph from the entry point. - // Resource reflection also depends on the entry point. - // By default, the current entry point is set to the first OpEntryPoint which appears in the SPIR-V module. + // All operations work on the current entry point. + // Entry points can be swapped out with set_entry_point(). + // Entry points should be set right after the constructor completes as some reflection functions traverse the graph from the entry point. + // Resource reflection also depends on the entry point. + // By default, the current entry point is set to the first OpEntryPoint which appears in the SPIR-V module. - // Some shader languages restrict the names that can be given to entry points, and the - // corresponding backend will automatically rename an entry point name, during the call - // to compile() if it is illegal. For example, the common entry point name main() is - // illegal in MSL, and is renamed to an alternate name by the MSL backend. - // Given the original entry point name contained in the SPIR-V, this function returns - // the name, as updated by the backend during the call to compile(). If the name is not - // illegal, and has not been renamed, or if this function is called before compile(), - // this function will simply return the same name. + // Some shader languages restrict the names that can be given to entry points, and the + // corresponding backend will automatically rename an entry point name, during the call + // to compile() if it is illegal. For example, the common entry point name main() is + // illegal in MSL, and is renamed to an alternate name by the MSL backend. + // Given the original entry point name contained in the SPIR-V, this function returns + // the name, as updated by the backend during the call to compile(). If the name is not + // illegal, and has not been renamed, or if this function is called before compile(), + // this function will simply return the same name. - // New variants of entry point query and reflection. - // Names for entry points in the SPIR-V module may alias if they belong to different execution models. - // To disambiguate, we must pass along with the entry point names the execution model. - SmallVector get_entry_points_and_stages() const; - void set_entry_point(const std::string &entry, spv::ExecutionModel execution_model); + // New variants of entry point query and reflection. + // Names for entry points in the SPIR-V module may alias if they belong to different execution models. + // To disambiguate, we must pass along with the entry point names the execution model. + SmallVector get_entry_points_and_stages() const; + void set_entry_point(const std::string &entry, spv::ExecutionModel execution_model); - // Renames an entry point from old_name to new_name. - // If old_name is currently selected as the current entry point, it will continue to be the current entry point, - // albeit with a new name. - // get_entry_points() is essentially invalidated at this point. - void rename_entry_point(const std::string &old_name, const std::string &new_name, - spv::ExecutionModel execution_model); - const SPIREntryPoint &get_entry_point(const std::string &name, spv::ExecutionModel execution_model) const; - SPIREntryPoint &get_entry_point(const std::string &name, spv::ExecutionModel execution_model); - const std::string &get_cleansed_entry_point_name(const std::string &name, - spv::ExecutionModel execution_model) const; + // Renames an entry point from old_name to new_name. + // If old_name is currently selected as the current entry point, it will continue to be the current entry point, + // albeit with a new name. + // get_entry_points() is essentially invalidated at this point. + void rename_entry_point(const std::string &old_name, const std::string &new_name, + spv::ExecutionModel execution_model); + const SPIREntryPoint &get_entry_point(const std::string &name, spv::ExecutionModel execution_model) const; + SPIREntryPoint &get_entry_point(const std::string &name, spv::ExecutionModel execution_model); + const std::string &get_cleansed_entry_point_name(const std::string &name, + spv::ExecutionModel execution_model) const; - // Traverses all reachable opcodes and sets active_builtins to a bitmask of all builtin variables which are accessed in the shader. - void update_active_builtins(); - bool has_active_builtin(spv::BuiltIn builtin, spv::StorageClass storage) const; + // Traverses all reachable opcodes and sets active_builtins to a bitmask of all builtin variables which are accessed in the shader. + void update_active_builtins(); + bool has_active_builtin(spv::BuiltIn builtin, spv::StorageClass storage) const; - // Query and modify OpExecutionMode. - const Bitset &get_execution_mode_bitset() const; + // Query and modify OpExecutionMode. + const Bitset &get_execution_mode_bitset() const; - void unset_execution_mode(spv::ExecutionMode mode); - void set_execution_mode(spv::ExecutionMode mode, uint32_t arg0 = 0, uint32_t arg1 = 0, uint32_t arg2 = 0); + void unset_execution_mode(spv::ExecutionMode mode); + void set_execution_mode(spv::ExecutionMode mode, uint32_t arg0 = 0, uint32_t arg1 = 0, uint32_t arg2 = 0); - // Gets argument for an execution mode (LocalSize, Invocations, OutputVertices). - // For LocalSize or LocalSizeId, the index argument is used to select the dimension (X = 0, Y = 1, Z = 2). - // For execution modes which do not have arguments, 0 is returned. - // LocalSizeId query returns an ID. If LocalSizeId execution mode is not used, it returns 0. - // LocalSize always returns a literal. If execution mode is LocalSizeId, - // the literal (spec constant or not) is still returned. - uint32_t get_execution_mode_argument(spv::ExecutionMode mode, uint32_t index = 0) const; - spv::ExecutionModel get_execution_model() const; + // Gets argument for an execution mode (LocalSize, Invocations, OutputVertices). + // For LocalSize or LocalSizeId, the index argument is used to select the dimension (X = 0, Y = 1, Z = 2). + // For execution modes which do not have arguments, 0 is returned. + // LocalSizeId query returns an ID. If LocalSizeId execution mode is not used, it returns 0. + // LocalSize always returns a literal. If execution mode is LocalSizeId, + // the literal (spec constant or not) is still returned. + uint32_t get_execution_mode_argument(spv::ExecutionMode mode, uint32_t index = 0) const; + spv::ExecutionModel get_execution_model() const; - bool is_tessellation_shader() const; - bool is_tessellating_triangles() const; + bool is_tessellation_shader() const; + bool is_tessellating_triangles() const; - // In SPIR-V, the compute work group size can be represented by a constant vector, in which case - // the LocalSize execution mode is ignored. - // - // This constant vector can be a constant vector, specialization constant vector, or partly specialized constant vector. - // To modify and query work group dimensions which are specialization constants, SPIRConstant values must be modified - // directly via get_constant() rather than using LocalSize directly. This function will return which constants should be modified. - // - // To modify dimensions which are *not* specialization constants, set_execution_mode should be used directly. - // Arguments to set_execution_mode which are specialization constants are effectively ignored during compilation. - // NOTE: This is somewhat different from how SPIR-V works. In SPIR-V, the constant vector will completely replace LocalSize, - // while in this interface, LocalSize is only ignored for specialization constants. - // - // The specialization constant will be written to x, y and z arguments. - // If the component is not a specialization constant, a zeroed out struct will be written. - // The return value is the constant ID of the builtin WorkGroupSize, but this is not expected to be useful - // for most use cases. - // If LocalSizeId is used, there is no uvec3 value representing the workgroup size, so the return value is 0, - // but x, y and z are written as normal if the components are specialization constants. - uint32_t get_work_group_size_specialization_constants(SpecializationConstant &x, SpecializationConstant &y, - SpecializationConstant &z) const; + // In SPIR-V, the compute work group size can be represented by a constant vector, in which case + // the LocalSize execution mode is ignored. + // + // This constant vector can be a constant vector, specialization constant vector, or partly specialized constant vector. + // To modify and query work group dimensions which are specialization constants, SPIRConstant values must be modified + // directly via get_constant() rather than using LocalSize directly. This function will return which constants should be modified. + // + // To modify dimensions which are *not* specialization constants, set_execution_mode should be used directly. + // Arguments to set_execution_mode which are specialization constants are effectively ignored during compilation. + // NOTE: This is somewhat different from how SPIR-V works. In SPIR-V, the constant vector will completely replace LocalSize, + // while in this interface, LocalSize is only ignored for specialization constants. + // + // The specialization constant will be written to x, y and z arguments. + // If the component is not a specialization constant, a zeroed out struct will be written. + // The return value is the constant ID of the builtin WorkGroupSize, but this is not expected to be useful + // for most use cases. + // If LocalSizeId is used, there is no uvec3 value representing the workgroup size, so the return value is 0, + // but x, y and z are written as normal if the components are specialization constants. + uint32_t get_work_group_size_specialization_constants(SpecializationConstant &x, SpecializationConstant &y, + SpecializationConstant &z) const; - // Analyzes all OpImageFetch (texelFetch) opcodes and checks if there are instances where - // said instruction is used without a combined image sampler. - // GLSL targets do not support the use of texelFetch without a sampler. - // To workaround this, we must inject a dummy sampler which can be used to form a sampler2D at the call-site of - // texelFetch as necessary. - // - // This must be called before build_combined_image_samplers(). - // build_combined_image_samplers() may refer to the ID returned by this method if the returned ID is non-zero. - // The return value will be the ID of a sampler object if a dummy sampler is necessary, or 0 if no sampler object - // is required. - // - // If the returned ID is non-zero, it can be decorated with set/bindings as desired before calling compile(). - // Calling this function also invalidates get_active_interface_variables(), so this should be called - // before that function. - VariableID build_dummy_sampler_for_combined_images(); + // Analyzes all OpImageFetch (texelFetch) opcodes and checks if there are instances where + // said instruction is used without a combined image sampler. + // GLSL targets do not support the use of texelFetch without a sampler. + // To workaround this, we must inject a dummy sampler which can be used to form a sampler2D at the call-site of + // texelFetch as necessary. + // + // This must be called before build_combined_image_samplers(). + // build_combined_image_samplers() may refer to the ID returned by this method if the returned ID is non-zero. + // The return value will be the ID of a sampler object if a dummy sampler is necessary, or 0 if no sampler object + // is required. + // + // If the returned ID is non-zero, it can be decorated with set/bindings as desired before calling compile(). + // Calling this function also invalidates get_active_interface_variables(), so this should be called + // before that function. + VariableID build_dummy_sampler_for_combined_images(); - // Analyzes all separate image and samplers used from the currently selected entry point, - // and re-routes them all to a combined image sampler instead. - // This is required to "support" separate image samplers in targets which do not natively support - // this feature, like GLSL/ESSL. - // - // This must be called before compile() if such remapping is desired. - // This call will add new sampled images to the SPIR-V, - // so it will appear in reflection if get_shader_resources() is called after build_combined_image_samplers. - // - // If any image/sampler remapping was found, no separate image/samplers will appear in the decompiled output, - // but will still appear in reflection. - // - // The resulting samplers will be void of any decorations like name, descriptor sets and binding points, - // so this can be added before compile() if desired. - // - // Combined image samplers originating from this set are always considered active variables. - // Arrays of separate samplers are not supported, but arrays of separate images are supported. - // Array of images + sampler -> Array of combined image samplers. - void build_combined_image_samplers(); + // Analyzes all separate image and samplers used from the currently selected entry point, + // and re-routes them all to a combined image sampler instead. + // This is required to "support" separate image samplers in targets which do not natively support + // this feature, like GLSL/ESSL. + // + // This must be called before compile() if such remapping is desired. + // This call will add new sampled images to the SPIR-V, + // so it will appear in reflection if get_shader_resources() is called after build_combined_image_samplers. + // + // If any image/sampler remapping was found, no separate image/samplers will appear in the decompiled output, + // but will still appear in reflection. + // + // The resulting samplers will be void of any decorations like name, descriptor sets and binding points, + // so this can be added before compile() if desired. + // + // Combined image samplers originating from this set are always considered active variables. + // Arrays of separate samplers are not supported, but arrays of separate images are supported. + // Array of images + sampler -> Array of combined image samplers. + void build_combined_image_samplers(); - // Gets a remapping for the combined image samplers. - const SmallVector &get_combined_image_samplers() const - { - return combined_image_samplers; - } + // Gets a remapping for the combined image samplers. + const SmallVector &get_combined_image_samplers() const + { + return combined_image_samplers; + } - // Set a new variable type remap callback. - // The type remapping is designed to allow global interface variable to assume more special types. - // A typical example here is to remap sampler2D into samplerExternalOES, which currently isn't supported - // directly by SPIR-V. - // - // In compile() while emitting code, - // for every variable that is declared, including function parameters, the callback will be called - // and the API user has a chance to change the textual representation of the type used to declare the variable. - // The API user can detect special patterns in names to guide the remapping. - void set_variable_type_remap_callback(VariableTypeRemapCallback cb) - { - variable_remap_callback = std::move(cb); - } + // Set a new variable type remap callback. + // The type remapping is designed to allow global interface variable to assume more special types. + // A typical example here is to remap sampler2D into samplerExternalOES, which currently isn't supported + // directly by SPIR-V. + // + // In compile() while emitting code, + // for every variable that is declared, including function parameters, the callback will be called + // and the API user has a chance to change the textual representation of the type used to declare the variable. + // The API user can detect special patterns in names to guide the remapping. + void set_variable_type_remap_callback(VariableTypeRemapCallback cb) + { + variable_remap_callback = std::move(cb); + } - // API for querying which specialization constants exist. - // To modify a specialization constant before compile(), use get_constant(constant.id), - // then update constants directly in the SPIRConstant data structure. - // For composite types, the subconstants can be iterated over and modified. - // constant_type is the SPIRType for the specialization constant, - // which can be queried to determine which fields in the unions should be poked at. - SmallVector get_specialization_constants() const; - SPIRConstant &get_constant(ConstantID id); - const SPIRConstant &get_constant(ConstantID id) const; + // API for querying which specialization constants exist. + // To modify a specialization constant before compile(), use get_constant(constant.id), + // then update constants directly in the SPIRConstant data structure. + // For composite types, the subconstants can be iterated over and modified. + // constant_type is the SPIRType for the specialization constant, + // which can be queried to determine which fields in the unions should be poked at. + SmallVector get_specialization_constants() const; + SPIRConstant &get_constant(ConstantID id); + const SPIRConstant &get_constant(ConstantID id) const; - uint32_t get_current_id_bound() const - { - return uint32_t(ir.ids.size()); - } + uint32_t get_current_id_bound() const + { + return uint32_t(ir.ids.size()); + } - // API for querying buffer objects. - // The type passed in here should be the base type of a resource, i.e. - // get_type(resource.base_type_id) - // as decorations are set in the basic Block type. - // The type passed in here must have these decorations set, or an exception is raised. - // Only UBOs and SSBOs or sub-structs which are part of these buffer types will have these decorations set. - uint32_t type_struct_member_offset(const SPIRType &type, uint32_t index) const; - uint32_t type_struct_member_array_stride(const SPIRType &type, uint32_t index) const; - uint32_t type_struct_member_matrix_stride(const SPIRType &type, uint32_t index) const; + // API for querying buffer objects. + // The type passed in here should be the base type of a resource, i.e. + // get_type(resource.base_type_id) + // as decorations are set in the basic Block type. + // The type passed in here must have these decorations set, or an exception is raised. + // Only UBOs and SSBOs or sub-structs which are part of these buffer types will have these decorations set. + uint32_t type_struct_member_offset(const SPIRType &type, uint32_t index) const; + uint32_t type_struct_member_array_stride(const SPIRType &type, uint32_t index) const; + uint32_t type_struct_member_matrix_stride(const SPIRType &type, uint32_t index) const; - // Gets the offset in SPIR-V words (uint32_t) for a decoration which was originally declared in the SPIR-V binary. - // The offset will point to one or more uint32_t literals which can be modified in-place before using the SPIR-V binary. - // Note that adding or removing decorations using the reflection API will not change the behavior of this function. - // If the decoration was declared, sets the word_offset to an offset into the provided SPIR-V binary buffer and returns true, - // otherwise, returns false. - // If the decoration does not have any value attached to it (e.g. DecorationRelaxedPrecision), this function will also return false. - bool get_binary_offset_for_decoration(VariableID id, spv::Decoration decoration, uint32_t &word_offset) const; + // Gets the offset in SPIR-V words (uint32_t) for a decoration which was originally declared in the SPIR-V binary. + // The offset will point to one or more uint32_t literals which can be modified in-place before using the SPIR-V binary. + // Note that adding or removing decorations using the reflection API will not change the behavior of this function. + // If the decoration was declared, sets the word_offset to an offset into the provided SPIR-V binary buffer and returns true, + // otherwise, returns false. + // If the decoration does not have any value attached to it (e.g. DecorationRelaxedPrecision), this function will also return false. + bool get_binary_offset_for_decoration(VariableID id, spv::Decoration decoration, uint32_t &word_offset) const; - // HLSL counter buffer reflection interface. - // Append/Consume/Increment/Decrement in HLSL is implemented as two "neighbor" buffer objects where - // one buffer implements the storage, and a single buffer containing just a lone "int" implements the counter. - // To SPIR-V these will be exposed as two separate buffers, but glslang HLSL frontend emits a special indentifier - // which lets us link the two buffers together. + // HLSL counter buffer reflection interface. + // Append/Consume/Increment/Decrement in HLSL is implemented as two "neighbor" buffer objects where + // one buffer implements the storage, and a single buffer containing just a lone "int" implements the counter. + // To SPIR-V these will be exposed as two separate buffers, but glslang HLSL frontend emits a special indentifier + // which lets us link the two buffers together. - // Queries if a variable ID is a counter buffer which "belongs" to a regular buffer object. + // Queries if a variable ID is a counter buffer which "belongs" to a regular buffer object. - // If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module. - // Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will - // only return true if OpSource was reported HLSL. - // To rely on this functionality, ensure that the SPIR-V module is not stripped. + // If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module. + // Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will + // only return true if OpSource was reported HLSL. + // To rely on this functionality, ensure that the SPIR-V module is not stripped. - bool buffer_is_hlsl_counter_buffer(VariableID id) const; + bool buffer_is_hlsl_counter_buffer(VariableID id) const; - // Queries if a buffer object has a neighbor "counter" buffer. - // If so, the ID of that counter buffer will be returned in counter_id. - // If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module. - // Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will - // only return true if OpSource was reported HLSL. - // To rely on this functionality, ensure that the SPIR-V module is not stripped. - bool buffer_get_hlsl_counter_buffer(VariableID id, uint32_t &counter_id) const; + // Queries if a buffer object has a neighbor "counter" buffer. + // If so, the ID of that counter buffer will be returned in counter_id. + // If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module. + // Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will + // only return true if OpSource was reported HLSL. + // To rely on this functionality, ensure that the SPIR-V module is not stripped. + bool buffer_get_hlsl_counter_buffer(VariableID id, uint32_t &counter_id) const; - // Gets the list of all SPIR-V Capabilities which were declared in the SPIR-V module. - const SmallVector &get_declared_capabilities() const; + // Gets the list of all SPIR-V Capabilities which were declared in the SPIR-V module. + const SmallVector &get_declared_capabilities() const; - // Gets the list of all SPIR-V extensions which were declared in the SPIR-V module. - const SmallVector &get_declared_extensions() const; + // Gets the list of all SPIR-V extensions which were declared in the SPIR-V module. + const SmallVector &get_declared_extensions() const; - // When declaring buffer blocks in GLSL, the name declared in the GLSL source - // might not be the same as the name declared in the SPIR-V module due to naming conflicts. - // In this case, SPIRV-Cross needs to find a fallback-name, and it might only - // be possible to know this name after compiling to GLSL. - // This is particularly important for HLSL input and UAVs which tends to reuse the same block type - // for multiple distinct blocks. For these cases it is not possible to modify the name of the type itself - // because it might be unique. Instead, you can use this interface to check after compilation which - // name was actually used if your input SPIR-V tends to have this problem. - // For other names like remapped names for variables, etc, it's generally enough to query the name of the variables - // after compiling, block names are an exception to this rule. - // ID is the name of a variable as returned by Resource::id, and must be a variable with a Block-like type. - // - // This also applies to HLSL cbuffers. - std::string get_remapped_declared_block_name(VariableID id) const; + // When declaring buffer blocks in GLSL, the name declared in the GLSL source + // might not be the same as the name declared in the SPIR-V module due to naming conflicts. + // In this case, SPIRV-Cross needs to find a fallback-name, and it might only + // be possible to know this name after compiling to GLSL. + // This is particularly important for HLSL input and UAVs which tends to reuse the same block type + // for multiple distinct blocks. For these cases it is not possible to modify the name of the type itself + // because it might be unique. Instead, you can use this interface to check after compilation which + // name was actually used if your input SPIR-V tends to have this problem. + // For other names like remapped names for variables, etc, it's generally enough to query the name of the variables + // after compiling, block names are an exception to this rule. + // ID is the name of a variable as returned by Resource::id, and must be a variable with a Block-like type. + // + // This also applies to HLSL cbuffers. + std::string get_remapped_declared_block_name(VariableID id) const; - // For buffer block variables, get the decorations for that variable. - // Sometimes, decorations for buffer blocks are found in member decorations instead - // of direct decorations on the variable itself. - // The most common use here is to check if a buffer is readonly or writeonly. - Bitset get_buffer_block_flags(VariableID id) const; + // For buffer block variables, get the decorations for that variable. + // Sometimes, decorations for buffer blocks are found in member decorations instead + // of direct decorations on the variable itself. + // The most common use here is to check if a buffer is readonly or writeonly. + Bitset get_buffer_block_flags(VariableID id) const; - // Returns whether the position output is invariant - bool is_position_invariant() const - { - return position_invariant; - } + // Returns whether the position output is invariant + bool is_position_invariant() const + { + return position_invariant; + } protected: - const uint32_t *stream(const Instruction &instr) const - { - // If we're not going to use any arguments, just return nullptr. - // We want to avoid case where we return an out of range pointer - // that trips debug assertions on some platforms. - if (!instr.length) - return nullptr; - - if (instr.is_embedded()) - { - auto &embedded = static_cast(instr); - assert(embedded.ops.size() == instr.length); - return embedded.ops.data(); - } - else - { - if (instr.offset + instr.length > ir.spirv.size()) - SPIRV_CROSS_THROW("Compiler::stream() out of range."); - return &ir.spirv[instr.offset]; - } - } - - uint32_t *stream_mutable(const Instruction &instr) const - { - return const_cast(stream(instr)); - } - - ParsedIR ir; - // Marks variables which have global scope and variables which can alias with other variables - // (SSBO, image load store, etc) - SmallVector global_variables; - SmallVector aliased_variables; - - SPIRFunction *current_function = nullptr; - SPIRBlock *current_block = nullptr; - uint32_t current_loop_level = 0; - std::unordered_set active_interface_variables; - bool check_active_interface_variables = false; - - void add_loop_level(); - - void set_initializers(SPIRExpression &e) - { - e.emitted_loop_level = current_loop_level; - } - - template - void set_initializers(const T &) - { - } - - // If our IDs are out of range here as part of opcodes, throw instead of - // undefined behavior. - template - T &set(uint32_t id, P &&... args) - { - ir.add_typed_id(static_cast(T::type), id); - auto &var = variant_set(ir.ids[id], std::forward

(args)...); - var.self = id; - set_initializers(var); - return var; - } - - template - T &get(uint32_t id) - { - return variant_get(ir.ids[id]); - } - - template - T *maybe_get(uint32_t id) - { - if (id >= ir.ids.size()) - return nullptr; - else if (ir.ids[id].get_type() == static_cast(T::type)) - return &get(id); - else - return nullptr; - } - - template - const T &get(uint32_t id) const - { - return variant_get(ir.ids[id]); - } - - template - const T *maybe_get(uint32_t id) const - { - if (id >= ir.ids.size()) - return nullptr; - else if (ir.ids[id].get_type() == static_cast(T::type)) - return &get(id); - else - return nullptr; - } - - // Gets the id of SPIR-V type underlying the given type_id, which might be a pointer. - uint32_t get_pointee_type_id(uint32_t type_id) const; - - // Gets the SPIR-V type underlying the given type, which might be a pointer. - const SPIRType &get_pointee_type(const SPIRType &type) const; - - // Gets the SPIR-V type underlying the given type_id, which might be a pointer. - const SPIRType &get_pointee_type(uint32_t type_id) const; - - // Gets the ID of the SPIR-V type underlying a variable. - uint32_t get_variable_data_type_id(const SPIRVariable &var) const; - - // Gets the SPIR-V type underlying a variable. - SPIRType &get_variable_data_type(const SPIRVariable &var); - - // Gets the SPIR-V type underlying a variable. - const SPIRType &get_variable_data_type(const SPIRVariable &var) const; - - // Gets the SPIR-V element type underlying an array variable. - SPIRType &get_variable_element_type(const SPIRVariable &var); - - // Gets the SPIR-V element type underlying an array variable. - const SPIRType &get_variable_element_type(const SPIRVariable &var) const; - - // Sets the qualified member identifier for OpTypeStruct ID, member number "index". - void set_member_qualified_name(uint32_t type_id, uint32_t index, const std::string &name); - void set_qualified_name(uint32_t id, const std::string &name); - - // Returns if the given type refers to a sampled image. - bool is_sampled_image_type(const SPIRType &type); - - const SPIREntryPoint &get_entry_point() const; - SPIREntryPoint &get_entry_point(); - static bool is_tessellation_shader(spv::ExecutionModel model); - - virtual std::string to_name(uint32_t id, bool allow_alias = true) const; - bool is_builtin_variable(const SPIRVariable &var) const; - bool is_builtin_type(const SPIRType &type) const; - bool is_hidden_variable(const SPIRVariable &var, bool include_builtins = false) const; - bool is_immutable(uint32_t id) const; - bool is_member_builtin(const SPIRType &type, uint32_t index, spv::BuiltIn *builtin) const; - bool is_scalar(const SPIRType &type) const; - bool is_vector(const SPIRType &type) const; - bool is_matrix(const SPIRType &type) const; - bool is_array(const SPIRType &type) const; - bool is_pointer(const SPIRType &type) const; - bool is_physical_pointer(const SPIRType &type) const; - bool is_physical_pointer_to_buffer_block(const SPIRType &type) const; - static bool is_runtime_size_array(const SPIRType &type); - uint32_t expression_type_id(uint32_t id) const; - const SPIRType &expression_type(uint32_t id) const; - bool expression_is_lvalue(uint32_t id) const; - bool variable_storage_is_aliased(const SPIRVariable &var); - SPIRVariable *maybe_get_backing_variable(uint32_t chain); - - void register_read(uint32_t expr, uint32_t chain, bool forwarded); - void register_write(uint32_t chain); - - inline bool is_continue(uint32_t next) const - { - return (ir.block_meta[next] & ParsedIR::BLOCK_META_CONTINUE_BIT) != 0; - } - - inline bool is_single_block_loop(uint32_t next) const - { - auto &block = get(next); - return block.merge == SPIRBlock::MergeLoop && block.continue_block == ID(next); - } - - inline bool is_break(uint32_t next) const - { - return (ir.block_meta[next] & - (ParsedIR::BLOCK_META_LOOP_MERGE_BIT | ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT)) != 0; - } - - inline bool is_loop_break(uint32_t next) const - { - return (ir.block_meta[next] & ParsedIR::BLOCK_META_LOOP_MERGE_BIT) != 0; - } - - inline bool is_conditional(uint32_t next) const - { - return (ir.block_meta[next] & - (ParsedIR::BLOCK_META_SELECTION_MERGE_BIT | ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT)) != 0; - } - - // Dependency tracking for temporaries read from variables. - void flush_dependees(SPIRVariable &var); - void flush_all_active_variables(); - void flush_control_dependent_expressions(uint32_t block); - void flush_all_atomic_capable_variables(); - void flush_all_aliased_variables(); - void register_global_read_dependencies(const SPIRBlock &func, uint32_t id); - void register_global_read_dependencies(const SPIRFunction &func, uint32_t id); - std::unordered_set invalid_expressions; - - void update_name_cache(std::unordered_set &cache, std::string &name); - - // A variant which takes two sets of names. The secondary is only used to verify there are no collisions, - // but the set is not updated when we have found a new name. - // Used primarily when adding block interface names. - void update_name_cache(std::unordered_set &cache_primary, - const std::unordered_set &cache_secondary, std::string &name); - - bool function_is_pure(const SPIRFunction &func); - bool block_is_pure(const SPIRBlock &block); - bool function_is_control_dependent(const SPIRFunction &func); - bool block_is_control_dependent(const SPIRBlock &block); - - bool execution_is_branchless(const SPIRBlock &from, const SPIRBlock &to) const; - bool execution_is_direct_branch(const SPIRBlock &from, const SPIRBlock &to) const; - bool execution_is_noop(const SPIRBlock &from, const SPIRBlock &to) const; - SPIRBlock::ContinueBlockType continue_block_type(const SPIRBlock &continue_block) const; - - void force_recompile(); - void force_recompile_guarantee_forward_progress(); - void clear_force_recompile(); - bool is_forcing_recompilation() const; - bool is_force_recompile = false; - bool is_force_recompile_forward_progress = false; - - bool block_is_noop(const SPIRBlock &block) const; - bool block_is_loop_candidate(const SPIRBlock &block, SPIRBlock::Method method) const; - - bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const; - void inherit_expression_dependencies(uint32_t dst, uint32_t source); - void add_implied_read_expression(SPIRExpression &e, uint32_t source); - void add_implied_read_expression(SPIRAccessChain &e, uint32_t source); - void add_active_interface_variable(uint32_t var_id); - - // For proper multiple entry point support, allow querying if an Input or Output - // variable is part of that entry points interface. - bool interface_variable_exists_in_entry_point(uint32_t id) const; - - SmallVector combined_image_samplers; - - void remap_variable_type_name(const SPIRType &type, const std::string &var_name, std::string &type_name) const - { - if (variable_remap_callback) - variable_remap_callback(type, var_name, type_name); - } - - void set_ir(const ParsedIR &parsed); - void set_ir(ParsedIR &&parsed); - void parse_fixup(); - - // Used internally to implement various traversals for queries. - struct OpcodeHandler - { - virtual ~OpcodeHandler() = default; - - // Return true if traversal should continue. - // If false, traversal will end immediately. - virtual bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) = 0; - virtual bool handle_terminator(const SPIRBlock &) - { - return true; - } - - virtual bool follow_function_call(const SPIRFunction &) - { - return true; - } - - virtual void set_current_block(const SPIRBlock &) - { - } - - // Called after returning from a function or when entering a block, - // can be called multiple times per block, - // while set_current_block is only called on block entry. - virtual void rearm_current_block(const SPIRBlock &) - { - } - - virtual bool begin_function_scope(const uint32_t *, uint32_t) - { - return true; - } - - virtual bool end_function_scope(const uint32_t *, uint32_t) - { - return true; - } - }; - - struct BufferAccessHandler : OpcodeHandler - { - BufferAccessHandler(const Compiler &compiler_, SmallVector &ranges_, uint32_t id_) - : compiler(compiler_) - , ranges(ranges_) - , id(id_) - { - } - - bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; - - const Compiler &compiler; - SmallVector &ranges; - uint32_t id; - - std::unordered_set seen; - }; - - struct InterfaceVariableAccessHandler : OpcodeHandler - { - InterfaceVariableAccessHandler(const Compiler &compiler_, std::unordered_set &variables_) - : compiler(compiler_) - , variables(variables_) - { - } - - bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; - - const Compiler &compiler; - std::unordered_set &variables; - }; - - struct CombinedImageSamplerHandler : OpcodeHandler - { - CombinedImageSamplerHandler(Compiler &compiler_) - : compiler(compiler_) - { - } - bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; - bool begin_function_scope(const uint32_t *args, uint32_t length) override; - bool end_function_scope(const uint32_t *args, uint32_t length) override; - - Compiler &compiler; - - // Each function in the call stack needs its own remapping for parameters so we can deduce which global variable each texture/sampler the parameter is statically bound to. - std::stack> parameter_remapping; - std::stack functions; - - uint32_t remap_parameter(uint32_t id); - void push_remap_parameters(const SPIRFunction &func, const uint32_t *args, uint32_t length); - void pop_remap_parameters(); - void register_combined_image_sampler(SPIRFunction &caller, VariableID combined_id, VariableID texture_id, - VariableID sampler_id, bool depth); - }; - - struct DummySamplerForCombinedImageHandler : OpcodeHandler - { - DummySamplerForCombinedImageHandler(Compiler &compiler_) - : compiler(compiler_) - { - } - bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; - - Compiler &compiler; - bool need_dummy_sampler = false; - }; - - struct ActiveBuiltinHandler : OpcodeHandler - { - ActiveBuiltinHandler(Compiler &compiler_) - : compiler(compiler_) - { - } - - bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; - Compiler &compiler; - - void handle_builtin(const SPIRType &type, spv::BuiltIn builtin, const Bitset &decoration_flags); - void add_if_builtin(uint32_t id); - void add_if_builtin_or_block(uint32_t id); - void add_if_builtin(uint32_t id, bool allow_blocks); - }; - - bool traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const; - bool traverse_all_reachable_opcodes(const SPIRFunction &block, OpcodeHandler &handler) const; - // This must be an ordered data structure so we always pick the same type aliases. - SmallVector global_struct_cache; - - ShaderResources get_shader_resources(const std::unordered_set *active_variables) const; - - VariableTypeRemapCallback variable_remap_callback; - - bool get_common_basic_type(const SPIRType &type, SPIRType::BaseType &base_type); - - std::unordered_set forced_temporaries; - std::unordered_set forwarded_temporaries; - std::unordered_set suppressed_usage_tracking; - std::unordered_set hoisted_temporaries; - std::unordered_set forced_invariant_temporaries; - - Bitset active_input_builtins; - Bitset active_output_builtins; - uint32_t clip_distance_count = 0; - uint32_t cull_distance_count = 0; - bool position_invariant = false; - - void analyze_parameter_preservation( - SPIRFunction &entry, const CFG &cfg, - const std::unordered_map> &variable_to_blocks, - const std::unordered_map> &complete_write_blocks); - - // If a variable ID or parameter ID is found in this set, a sampler is actually a shadow/comparison sampler. - // SPIR-V does not support this distinction, so we must keep track of this information outside the type system. - // There might be unrelated IDs found in this set which do not correspond to actual variables. - // This set should only be queried for the existence of samplers which are already known to be variables or parameter IDs. - // Similar is implemented for images, as well as if subpass inputs are needed. - std::unordered_set comparison_ids; - bool need_subpass_input = false; - bool need_subpass_input_ms = false; - - // In certain backends, we will need to use a dummy sampler to be able to emit code. - // GLSL does not support texelFetch on texture2D objects, but SPIR-V does, - // so we need to workaround by having the application inject a dummy sampler. - uint32_t dummy_sampler_id = 0; - - void analyze_image_and_sampler_usage(); - - struct CombinedImageSamplerDrefHandler : OpcodeHandler - { - CombinedImageSamplerDrefHandler(Compiler &compiler_) - : compiler(compiler_) - { - } - bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; - - Compiler &compiler; - std::unordered_set dref_combined_samplers; - }; - - struct CombinedImageSamplerUsageHandler : OpcodeHandler - { - CombinedImageSamplerUsageHandler(Compiler &compiler_, - const std::unordered_set &dref_combined_samplers_) - : compiler(compiler_) - , dref_combined_samplers(dref_combined_samplers_) - { - } - - bool begin_function_scope(const uint32_t *args, uint32_t length) override; - bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; - Compiler &compiler; - const std::unordered_set &dref_combined_samplers; - - std::unordered_map> dependency_hierarchy; - std::unordered_set comparison_ids; - - void add_hierarchy_to_comparison_ids(uint32_t ids); - bool need_subpass_input = false; - bool need_subpass_input_ms = false; - void add_dependency(uint32_t dst, uint32_t src); - }; - - void build_function_control_flow_graphs_and_analyze(); - std::unordered_map> function_cfgs; - const CFG &get_cfg_for_current_function() const; - const CFG &get_cfg_for_function(uint32_t id) const; - - struct CFGBuilder : OpcodeHandler - { - explicit CFGBuilder(Compiler &compiler_); - - bool follow_function_call(const SPIRFunction &func) override; - bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; - Compiler &compiler; - std::unordered_map> function_cfgs; - }; - - struct AnalyzeVariableScopeAccessHandler : OpcodeHandler - { - AnalyzeVariableScopeAccessHandler(Compiler &compiler_, SPIRFunction &entry_); - - bool follow_function_call(const SPIRFunction &) override; - void set_current_block(const SPIRBlock &block) override; - - void notify_variable_access(uint32_t id, uint32_t block); - bool id_is_phi_variable(uint32_t id) const; - bool id_is_potential_temporary(uint32_t id) const; - bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; - bool handle_terminator(const SPIRBlock &block) override; - - Compiler &compiler; - SPIRFunction &entry; - std::unordered_map> accessed_variables_to_block; - std::unordered_map> accessed_temporaries_to_block; - std::unordered_map result_id_to_type; - std::unordered_map> complete_write_variables_to_block; - std::unordered_map> partial_write_variables_to_block; - std::unordered_set access_chain_expressions; - // Access chains used in multiple blocks mean hoisting all the variables used to construct the access chain as not all backends can use pointers. - // This is also relevant when forwarding opaque objects since we cannot lower these to temporaries. - std::unordered_map> rvalue_forward_children; - const SPIRBlock *current_block = nullptr; - }; - - struct StaticExpressionAccessHandler : OpcodeHandler - { - StaticExpressionAccessHandler(Compiler &compiler_, uint32_t variable_id_); - bool follow_function_call(const SPIRFunction &) override; - bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; - - Compiler &compiler; - uint32_t variable_id; - uint32_t static_expression = 0; - uint32_t write_count = 0; - }; - - struct PhysicalBlockMeta - { - uint32_t alignment = 0; - }; - - struct PhysicalStorageBufferPointerHandler : OpcodeHandler - { - explicit PhysicalStorageBufferPointerHandler(Compiler &compiler_); - bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; - Compiler &compiler; - - std::unordered_set non_block_types; - std::unordered_map physical_block_type_meta; - std::unordered_map access_chain_to_physical_block; - - void mark_aligned_access(uint32_t id, const uint32_t *args, uint32_t length); - PhysicalBlockMeta *find_block_meta(uint32_t id) const; - bool type_is_bda_block_entry(uint32_t type_id) const; - void setup_meta_chain(uint32_t type_id, uint32_t var_id); - uint32_t get_minimum_scalar_alignment(const SPIRType &type) const; - void analyze_non_block_types_from_block(const SPIRType &type); - uint32_t get_base_non_block_type_id(uint32_t type_id) const; - }; - void analyze_non_block_pointer_types(); - SmallVector physical_storage_non_block_pointer_types; - std::unordered_map physical_storage_type_to_alignment; - - void analyze_variable_scope(SPIRFunction &function, AnalyzeVariableScopeAccessHandler &handler); - void find_function_local_luts(SPIRFunction &function, const AnalyzeVariableScopeAccessHandler &handler, - bool single_function); - bool may_read_undefined_variable_in_block(const SPIRBlock &block, uint32_t var); - - // Finds all resources that are written to from inside the critical section, if present. - // The critical section is delimited by OpBeginInvocationInterlockEXT and - // OpEndInvocationInterlockEXT instructions. In MSL and HLSL, any resources written - // while inside the critical section must be placed in a raster order group. - struct InterlockedResourceAccessHandler : OpcodeHandler - { - InterlockedResourceAccessHandler(Compiler &compiler_, uint32_t entry_point_id) - : compiler(compiler_) - { - call_stack.push_back(entry_point_id); - } - - bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; - bool begin_function_scope(const uint32_t *args, uint32_t length) override; - bool end_function_scope(const uint32_t *args, uint32_t length) override; - - Compiler &compiler; - bool in_crit_sec = false; - - uint32_t interlock_function_id = 0; - bool split_function_case = false; - bool control_flow_interlock = false; - bool use_critical_section = false; - bool call_stack_is_interlocked = false; - SmallVector call_stack; - - void access_potential_resource(uint32_t id); - }; - - struct InterlockedResourceAccessPrepassHandler : OpcodeHandler - { - InterlockedResourceAccessPrepassHandler(Compiler &compiler_, uint32_t entry_point_id) - : compiler(compiler_) - { - call_stack.push_back(entry_point_id); - } - - void rearm_current_block(const SPIRBlock &block) override; - bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; - bool begin_function_scope(const uint32_t *args, uint32_t length) override; - bool end_function_scope(const uint32_t *args, uint32_t length) override; - - Compiler &compiler; - uint32_t interlock_function_id = 0; - uint32_t current_block_id = 0; - bool split_function_case = false; - bool control_flow_interlock = false; - SmallVector call_stack; - }; - - void analyze_interlocked_resource_usage(); - // The set of all resources written while inside the critical section, if present. - std::unordered_set interlocked_resources; - bool interlocked_is_complex = false; - - void make_constant_null(uint32_t id, uint32_t type); - - std::unordered_map declared_block_names; - - bool instruction_to_result_type(uint32_t &result_type, uint32_t &result_id, spv::Op op, const uint32_t *args, - uint32_t length); - - Bitset combined_decoration_for_member(const SPIRType &type, uint32_t index) const; - static bool is_desktop_only_format(spv::ImageFormat format); - - bool is_depth_image(const SPIRType &type, uint32_t id) const; - - void set_extended_decoration(uint32_t id, ExtendedDecorations decoration, uint32_t value = 0); - uint32_t get_extended_decoration(uint32_t id, ExtendedDecorations decoration) const; - bool has_extended_decoration(uint32_t id, ExtendedDecorations decoration) const; - void unset_extended_decoration(uint32_t id, ExtendedDecorations decoration); - - void set_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration, - uint32_t value = 0); - uint32_t get_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const; - bool has_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const; - void unset_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration); - - bool check_internal_recursion(const SPIRType &type, std::unordered_set &checked_ids); - bool type_contains_recursion(const SPIRType &type); - bool type_is_array_of_pointers(const SPIRType &type) const; - bool type_is_block_like(const SPIRType &type) const; - bool type_is_top_level_block(const SPIRType &type) const; - bool type_is_opaque_value(const SPIRType &type) const; - - bool reflection_ssbo_instance_name_is_significant() const; - std::string get_remapped_declared_block_name(uint32_t id, bool fallback_prefer_instance_name) const; - - bool flush_phi_required(BlockID from, BlockID to) const; - - uint32_t evaluate_spec_constant_u32(const SPIRConstantOp &spec) const; - uint32_t evaluate_constant_u32(uint32_t id) const; - - bool is_vertex_like_shader() const; - - // Get the correct case list for the OpSwitch, since it can be either a - // 32 bit wide condition or a 64 bit, but the type is not embedded in the - // instruction itself. - const SmallVector &get_case_list(const SPIRBlock &block) const; + const uint32_t *stream(const Instruction &instr) const + { + // If we're not going to use any arguments, just return nullptr. + // We want to avoid case where we return an out of range pointer + // that trips debug assertions on some platforms. + if (!instr.length) + return nullptr; + + if (instr.is_embedded()) + { + auto &embedded = static_cast(instr); + assert(embedded.ops.size() == instr.length); + return embedded.ops.data(); + } + else + { + if (instr.offset + instr.length > ir.spirv.size()) + SPIRV_CROSS_THROW("Compiler::stream() out of range."); + return &ir.spirv[instr.offset]; + } + } + + uint32_t *stream_mutable(const Instruction &instr) const + { + return const_cast(stream(instr)); + } + + ParsedIR ir; + // Marks variables which have global scope and variables which can alias with other variables + // (SSBO, image load store, etc) + SmallVector global_variables; + SmallVector aliased_variables; + + SPIRFunction *current_function = nullptr; + SPIRBlock *current_block = nullptr; + uint32_t current_loop_level = 0; + std::unordered_set active_interface_variables; + bool check_active_interface_variables = false; + + void add_loop_level(); + + void set_initializers(SPIRExpression &e) + { + e.emitted_loop_level = current_loop_level; + } + + template + void set_initializers(const T &) + { + } + + // If our IDs are out of range here as part of opcodes, throw instead of + // undefined behavior. + template + T &set(uint32_t id, P &&... args) + { + ir.add_typed_id(static_cast(T::type), id); + auto &var = variant_set(ir.ids[id], std::forward

(args)...); + var.self = id; + set_initializers(var); + return var; + } + + template + T &get(uint32_t id) + { + return variant_get(ir.ids[id]); + } + + template + T *maybe_get(uint32_t id) + { + if (id >= ir.ids.size()) + return nullptr; + else if (ir.ids[id].get_type() == static_cast(T::type)) + return &get(id); + else + return nullptr; + } + + template + const T &get(uint32_t id) const + { + return variant_get(ir.ids[id]); + } + + template + const T *maybe_get(uint32_t id) const + { + if (id >= ir.ids.size()) + return nullptr; + else if (ir.ids[id].get_type() == static_cast(T::type)) + return &get(id); + else + return nullptr; + } + + // Gets the id of SPIR-V type underlying the given type_id, which might be a pointer. + uint32_t get_pointee_type_id(uint32_t type_id) const; + + // Gets the SPIR-V type underlying the given type, which might be a pointer. + const SPIRType &get_pointee_type(const SPIRType &type) const; + + // Gets the SPIR-V type underlying the given type_id, which might be a pointer. + const SPIRType &get_pointee_type(uint32_t type_id) const; + + // Gets the ID of the SPIR-V type underlying a variable. + uint32_t get_variable_data_type_id(const SPIRVariable &var) const; + + // Gets the SPIR-V type underlying a variable. + SPIRType &get_variable_data_type(const SPIRVariable &var); + + // Gets the SPIR-V type underlying a variable. + const SPIRType &get_variable_data_type(const SPIRVariable &var) const; + + // Gets the SPIR-V element type underlying an array variable. + SPIRType &get_variable_element_type(const SPIRVariable &var); + + // Gets the SPIR-V element type underlying an array variable. + const SPIRType &get_variable_element_type(const SPIRVariable &var) const; + + // Sets the qualified member identifier for OpTypeStruct ID, member number "index". + void set_member_qualified_name(uint32_t type_id, uint32_t index, const std::string &name); + void set_qualified_name(uint32_t id, const std::string &name); + + // Returns if the given type refers to a sampled image. + bool is_sampled_image_type(const SPIRType &type); + + const SPIREntryPoint &get_entry_point() const; + SPIREntryPoint &get_entry_point(); + static bool is_tessellation_shader(spv::ExecutionModel model); + + virtual std::string to_name(uint32_t id, bool allow_alias = true) const; + bool is_builtin_variable(const SPIRVariable &var) const; + bool is_builtin_type(const SPIRType &type) const; + bool is_hidden_variable(const SPIRVariable &var, bool include_builtins = false) const; + bool is_immutable(uint32_t id) const; + bool is_member_builtin(const SPIRType &type, uint32_t index, spv::BuiltIn *builtin) const; + bool is_scalar(const SPIRType &type) const; + bool is_vector(const SPIRType &type) const; + bool is_matrix(const SPIRType &type) const; + bool is_array(const SPIRType &type) const; + bool is_pointer(const SPIRType &type) const; + bool is_physical_pointer(const SPIRType &type) const; + bool is_physical_pointer_to_buffer_block(const SPIRType &type) const; + static bool is_runtime_size_array(const SPIRType &type); + uint32_t expression_type_id(uint32_t id) const; + const SPIRType &expression_type(uint32_t id) const; + bool expression_is_lvalue(uint32_t id) const; + bool variable_storage_is_aliased(const SPIRVariable &var); + SPIRVariable *maybe_get_backing_variable(uint32_t chain); + + void register_read(uint32_t expr, uint32_t chain, bool forwarded); + void register_write(uint32_t chain); + + inline bool is_continue(uint32_t next) const + { + return (ir.block_meta[next] & ParsedIR::BLOCK_META_CONTINUE_BIT) != 0; + } + + inline bool is_single_block_loop(uint32_t next) const + { + auto &block = get(next); + return block.merge == SPIRBlock::MergeLoop && block.continue_block == ID(next); + } + + inline bool is_break(uint32_t next) const + { + return (ir.block_meta[next] & + (ParsedIR::BLOCK_META_LOOP_MERGE_BIT | ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT)) != 0; + } + + inline bool is_loop_break(uint32_t next) const + { + return (ir.block_meta[next] & ParsedIR::BLOCK_META_LOOP_MERGE_BIT) != 0; + } + + inline bool is_conditional(uint32_t next) const + { + return (ir.block_meta[next] & + (ParsedIR::BLOCK_META_SELECTION_MERGE_BIT | ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT)) != 0; + } + + // Dependency tracking for temporaries read from variables. + void flush_dependees(SPIRVariable &var); + void flush_all_active_variables(); + void flush_control_dependent_expressions(uint32_t block); + void flush_all_atomic_capable_variables(); + void flush_all_aliased_variables(); + void register_global_read_dependencies(const SPIRBlock &func, uint32_t id); + void register_global_read_dependencies(const SPIRFunction &func, uint32_t id); + std::unordered_set invalid_expressions; + + void update_name_cache(std::unordered_set &cache, std::string &name); + + // A variant which takes two sets of names. The secondary is only used to verify there are no collisions, + // but the set is not updated when we have found a new name. + // Used primarily when adding block interface names. + void update_name_cache(std::unordered_set &cache_primary, + const std::unordered_set &cache_secondary, std::string &name); + + bool function_is_pure(const SPIRFunction &func); + bool block_is_pure(const SPIRBlock &block); + bool function_is_control_dependent(const SPIRFunction &func); + bool block_is_control_dependent(const SPIRBlock &block); + + bool execution_is_branchless(const SPIRBlock &from, const SPIRBlock &to) const; + bool execution_is_direct_branch(const SPIRBlock &from, const SPIRBlock &to) const; + bool execution_is_noop(const SPIRBlock &from, const SPIRBlock &to) const; + SPIRBlock::ContinueBlockType continue_block_type(const SPIRBlock &continue_block) const; + + void force_recompile(); + void force_recompile_guarantee_forward_progress(); + void clear_force_recompile(); + bool is_forcing_recompilation() const; + bool is_force_recompile = false; + bool is_force_recompile_forward_progress = false; + + bool block_is_noop(const SPIRBlock &block) const; + bool block_is_loop_candidate(const SPIRBlock &block, SPIRBlock::Method method) const; + + bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const; + void inherit_expression_dependencies(uint32_t dst, uint32_t source); + void add_implied_read_expression(SPIRExpression &e, uint32_t source); + void add_implied_read_expression(SPIRAccessChain &e, uint32_t source); + void add_active_interface_variable(uint32_t var_id); + + // For proper multiple entry point support, allow querying if an Input or Output + // variable is part of that entry points interface. + bool interface_variable_exists_in_entry_point(uint32_t id) const; + + SmallVector combined_image_samplers; + + void remap_variable_type_name(const SPIRType &type, const std::string &var_name, std::string &type_name) const + { + if (variable_remap_callback) + variable_remap_callback(type, var_name, type_name); + } + + void set_ir(const ParsedIR &parsed); + void set_ir(ParsedIR &&parsed); + void parse_fixup(); + + // Used internally to implement various traversals for queries. + struct OpcodeHandler + { + virtual ~OpcodeHandler() = default; + + // Return true if traversal should continue. + // If false, traversal will end immediately. + virtual bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) = 0; + virtual bool handle_terminator(const SPIRBlock &) + { + return true; + } + + virtual bool follow_function_call(const SPIRFunction &) + { + return true; + } + + virtual void set_current_block(const SPIRBlock &) + { + } + + // Called after returning from a function or when entering a block, + // can be called multiple times per block, + // while set_current_block is only called on block entry. + virtual void rearm_current_block(const SPIRBlock &) + { + } + + virtual bool begin_function_scope(const uint32_t *, uint32_t) + { + return true; + } + + virtual bool end_function_scope(const uint32_t *, uint32_t) + { + return true; + } + }; + + struct BufferAccessHandler : OpcodeHandler + { + BufferAccessHandler(const Compiler &compiler_, SmallVector &ranges_, uint32_t id_) + : compiler(compiler_) + , ranges(ranges_) + , id(id_) + { + } + + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + + const Compiler &compiler; + SmallVector &ranges; + uint32_t id; + + std::unordered_set seen; + }; + + struct InterfaceVariableAccessHandler : OpcodeHandler + { + InterfaceVariableAccessHandler(const Compiler &compiler_, std::unordered_set &variables_) + : compiler(compiler_) + , variables(variables_) + { + } + + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + + const Compiler &compiler; + std::unordered_set &variables; + }; + + struct CombinedImageSamplerHandler : OpcodeHandler + { + CombinedImageSamplerHandler(Compiler &compiler_) + : compiler(compiler_) + { + } + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + bool begin_function_scope(const uint32_t *args, uint32_t length) override; + bool end_function_scope(const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + + // Each function in the call stack needs its own remapping for parameters so we can deduce which global variable each texture/sampler the parameter is statically bound to. + std::stack> parameter_remapping; + std::stack functions; + + uint32_t remap_parameter(uint32_t id); + void push_remap_parameters(const SPIRFunction &func, const uint32_t *args, uint32_t length); + void pop_remap_parameters(); + void register_combined_image_sampler(SPIRFunction &caller, VariableID combined_id, VariableID texture_id, + VariableID sampler_id, bool depth); + }; + + struct DummySamplerForCombinedImageHandler : OpcodeHandler + { + DummySamplerForCombinedImageHandler(Compiler &compiler_) + : compiler(compiler_) + { + } + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + bool need_dummy_sampler = false; + }; + + struct ActiveBuiltinHandler : OpcodeHandler + { + ActiveBuiltinHandler(Compiler &compiler_) + : compiler(compiler_) + { + } + + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + Compiler &compiler; + + void handle_builtin(const SPIRType &type, spv::BuiltIn builtin, const Bitset &decoration_flags); + void add_if_builtin(uint32_t id); + void add_if_builtin_or_block(uint32_t id); + void add_if_builtin(uint32_t id, bool allow_blocks); + }; + + bool traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const; + bool traverse_all_reachable_opcodes(const SPIRFunction &block, OpcodeHandler &handler) const; + // This must be an ordered data structure so we always pick the same type aliases. + SmallVector global_struct_cache; + + ShaderResources get_shader_resources(const std::unordered_set *active_variables) const; + + VariableTypeRemapCallback variable_remap_callback; + + bool get_common_basic_type(const SPIRType &type, SPIRType::BaseType &base_type); + + std::unordered_set forced_temporaries; + std::unordered_set forwarded_temporaries; + std::unordered_set suppressed_usage_tracking; + std::unordered_set hoisted_temporaries; + std::unordered_set forced_invariant_temporaries; + + Bitset active_input_builtins; + Bitset active_output_builtins; + uint32_t clip_distance_count = 0; + uint32_t cull_distance_count = 0; + bool position_invariant = false; + + void analyze_parameter_preservation( + SPIRFunction &entry, const CFG &cfg, + const std::unordered_map> &variable_to_blocks, + const std::unordered_map> &complete_write_blocks); + + // If a variable ID or parameter ID is found in this set, a sampler is actually a shadow/comparison sampler. + // SPIR-V does not support this distinction, so we must keep track of this information outside the type system. + // There might be unrelated IDs found in this set which do not correspond to actual variables. + // This set should only be queried for the existence of samplers which are already known to be variables or parameter IDs. + // Similar is implemented for images, as well as if subpass inputs are needed. + std::unordered_set comparison_ids; + bool need_subpass_input = false; + bool need_subpass_input_ms = false; + + // In certain backends, we will need to use a dummy sampler to be able to emit code. + // GLSL does not support texelFetch on texture2D objects, but SPIR-V does, + // so we need to workaround by having the application inject a dummy sampler. + uint32_t dummy_sampler_id = 0; + + void analyze_image_and_sampler_usage(); + + struct CombinedImageSamplerDrefHandler : OpcodeHandler + { + CombinedImageSamplerDrefHandler(Compiler &compiler_) + : compiler(compiler_) + { + } + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + std::unordered_set dref_combined_samplers; + }; + + struct CombinedImageSamplerUsageHandler : OpcodeHandler + { + CombinedImageSamplerUsageHandler(Compiler &compiler_, + const std::unordered_set &dref_combined_samplers_) + : compiler(compiler_) + , dref_combined_samplers(dref_combined_samplers_) + { + } + + bool begin_function_scope(const uint32_t *args, uint32_t length) override; + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + Compiler &compiler; + const std::unordered_set &dref_combined_samplers; + + std::unordered_map> dependency_hierarchy; + std::unordered_set comparison_ids; + + void add_hierarchy_to_comparison_ids(uint32_t ids); + bool need_subpass_input = false; + bool need_subpass_input_ms = false; + void add_dependency(uint32_t dst, uint32_t src); + }; + + void build_function_control_flow_graphs_and_analyze(); + std::unordered_map> function_cfgs; + const CFG &get_cfg_for_current_function() const; + const CFG &get_cfg_for_function(uint32_t id) const; + + struct CFGBuilder : OpcodeHandler + { + explicit CFGBuilder(Compiler &compiler_); + + bool follow_function_call(const SPIRFunction &func) override; + bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; + Compiler &compiler; + std::unordered_map> function_cfgs; + }; + + struct AnalyzeVariableScopeAccessHandler : OpcodeHandler + { + AnalyzeVariableScopeAccessHandler(Compiler &compiler_, SPIRFunction &entry_); + + bool follow_function_call(const SPIRFunction &) override; + void set_current_block(const SPIRBlock &block) override; + + void notify_variable_access(uint32_t id, uint32_t block); + bool id_is_phi_variable(uint32_t id) const; + bool id_is_potential_temporary(uint32_t id) const; + bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; + bool handle_terminator(const SPIRBlock &block) override; + + Compiler &compiler; + SPIRFunction &entry; + std::unordered_map> accessed_variables_to_block; + std::unordered_map> accessed_temporaries_to_block; + std::unordered_map result_id_to_type; + std::unordered_map> complete_write_variables_to_block; + std::unordered_map> partial_write_variables_to_block; + std::unordered_set access_chain_expressions; + // Access chains used in multiple blocks mean hoisting all the variables used to construct the access chain as not all backends can use pointers. + // This is also relevant when forwarding opaque objects since we cannot lower these to temporaries. + std::unordered_map> rvalue_forward_children; + const SPIRBlock *current_block = nullptr; + }; + + struct StaticExpressionAccessHandler : OpcodeHandler + { + StaticExpressionAccessHandler(Compiler &compiler_, uint32_t variable_id_); + bool follow_function_call(const SPIRFunction &) override; + bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + uint32_t variable_id; + uint32_t static_expression = 0; + uint32_t write_count = 0; + }; + + struct PhysicalBlockMeta + { + uint32_t alignment = 0; + }; + + struct PhysicalStorageBufferPointerHandler : OpcodeHandler + { + explicit PhysicalStorageBufferPointerHandler(Compiler &compiler_); + bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; + Compiler &compiler; + + std::unordered_set non_block_types; + std::unordered_map physical_block_type_meta; + std::unordered_map access_chain_to_physical_block; + + void mark_aligned_access(uint32_t id, const uint32_t *args, uint32_t length); + PhysicalBlockMeta *find_block_meta(uint32_t id) const; + bool type_is_bda_block_entry(uint32_t type_id) const; + void setup_meta_chain(uint32_t type_id, uint32_t var_id); + uint32_t get_minimum_scalar_alignment(const SPIRType &type) const; + void analyze_non_block_types_from_block(const SPIRType &type); + uint32_t get_base_non_block_type_id(uint32_t type_id) const; + }; + void analyze_non_block_pointer_types(); + SmallVector physical_storage_non_block_pointer_types; + std::unordered_map physical_storage_type_to_alignment; + + void analyze_variable_scope(SPIRFunction &function, AnalyzeVariableScopeAccessHandler &handler); + void find_function_local_luts(SPIRFunction &function, const AnalyzeVariableScopeAccessHandler &handler, + bool single_function); + bool may_read_undefined_variable_in_block(const SPIRBlock &block, uint32_t var); + + // Finds all resources that are written to from inside the critical section, if present. + // The critical section is delimited by OpBeginInvocationInterlockEXT and + // OpEndInvocationInterlockEXT instructions. In MSL and HLSL, any resources written + // while inside the critical section must be placed in a raster order group. + struct InterlockedResourceAccessHandler : OpcodeHandler + { + InterlockedResourceAccessHandler(Compiler &compiler_, uint32_t entry_point_id) + : compiler(compiler_) + { + call_stack.push_back(entry_point_id); + } + + bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; + bool begin_function_scope(const uint32_t *args, uint32_t length) override; + bool end_function_scope(const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + bool in_crit_sec = false; + + uint32_t interlock_function_id = 0; + bool split_function_case = false; + bool control_flow_interlock = false; + bool use_critical_section = false; + bool call_stack_is_interlocked = false; + SmallVector call_stack; + + void access_potential_resource(uint32_t id); + }; + + struct InterlockedResourceAccessPrepassHandler : OpcodeHandler + { + InterlockedResourceAccessPrepassHandler(Compiler &compiler_, uint32_t entry_point_id) + : compiler(compiler_) + { + call_stack.push_back(entry_point_id); + } + + void rearm_current_block(const SPIRBlock &block) override; + bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; + bool begin_function_scope(const uint32_t *args, uint32_t length) override; + bool end_function_scope(const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + uint32_t interlock_function_id = 0; + uint32_t current_block_id = 0; + bool split_function_case = false; + bool control_flow_interlock = false; + SmallVector call_stack; + }; + + void analyze_interlocked_resource_usage(); + // The set of all resources written while inside the critical section, if present. + std::unordered_set interlocked_resources; + bool interlocked_is_complex = false; + + void make_constant_null(uint32_t id, uint32_t type); + + std::unordered_map declared_block_names; + + bool instruction_to_result_type(uint32_t &result_type, uint32_t &result_id, spv::Op op, const uint32_t *args, + uint32_t length); + + Bitset combined_decoration_for_member(const SPIRType &type, uint32_t index) const; + static bool is_desktop_only_format(spv::ImageFormat format); + + bool is_depth_image(const SPIRType &type, uint32_t id) const; + + void set_extended_decoration(uint32_t id, ExtendedDecorations decoration, uint32_t value = 0); + uint32_t get_extended_decoration(uint32_t id, ExtendedDecorations decoration) const; + bool has_extended_decoration(uint32_t id, ExtendedDecorations decoration) const; + void unset_extended_decoration(uint32_t id, ExtendedDecorations decoration); + + void set_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration, + uint32_t value = 0); + uint32_t get_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const; + bool has_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const; + void unset_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration); + + bool check_internal_recursion(const SPIRType &type, std::unordered_set &checked_ids); + bool type_contains_recursion(const SPIRType &type); + bool type_is_array_of_pointers(const SPIRType &type) const; + bool type_is_block_like(const SPIRType &type) const; + bool type_is_top_level_block(const SPIRType &type) const; + bool type_is_opaque_value(const SPIRType &type) const; + + bool reflection_ssbo_instance_name_is_significant() const; + std::string get_remapped_declared_block_name(uint32_t id, bool fallback_prefer_instance_name) const; + + bool flush_phi_required(BlockID from, BlockID to) const; + + uint32_t evaluate_spec_constant_u32(const SPIRConstantOp &spec) const; + uint32_t evaluate_constant_u32(uint32_t id) const; + + bool is_vertex_like_shader() const; + + // Get the correct case list for the OpSwitch, since it can be either a + // 32 bit wide condition or a 64 bit, but the type is not embedded in the + // instruction itself. + const SmallVector &get_case_list(const SPIRBlock &block) const; private: - // Used only to implement the old deprecated get_entry_point() interface. - const SPIREntryPoint &get_first_entry_point(const std::string &name) const; - SPIREntryPoint &get_first_entry_point(const std::string &name); + // Used only to implement the old deprecated get_entry_point() interface. + const SPIREntryPoint &get_first_entry_point(const std::string &name) const; + SPIREntryPoint &get_first_entry_point(const std::string &name); }; } // namespace SPIRV_CROSS_NAMESPACE diff --git a/spirv_cross_containers.hpp b/spirv_cross_containers.hpp index c496cb75..420f0540 100644 --- a/spirv_cross_containers.hpp +++ b/spirv_cross_containers.hpp @@ -56,28 +56,28 @@ template class AlignedBuffer { public: - T *data() - { + T *data() + { #if defined(_MSC_VER) && _MSC_VER < 1900 - // MSVC 2013 workarounds, sigh ... - // Only use this workaround on MSVC 2013 due to some confusion around default initialized unions. - // Spec seems to suggest the memory will be zero-initialized, which is *not* what we want. - return reinterpret_cast(u.aligned_char); + // MSVC 2013 workarounds, sigh ... + // Only use this workaround on MSVC 2013 due to some confusion around default initialized unions. + // Spec seems to suggest the memory will be zero-initialized, which is *not* what we want. + return reinterpret_cast(u.aligned_char); #else - return reinterpret_cast(aligned_char); + return reinterpret_cast(aligned_char); #endif - } + } private: #if defined(_MSC_VER) && _MSC_VER < 1900 - // MSVC 2013 workarounds, sigh ... - union - { - char aligned_char[sizeof(T) * N]; - double dummy_aligner; - } u; + // MSVC 2013 workarounds, sigh ... + union + { + char aligned_char[sizeof(T) * N]; + double dummy_aligner; + } u; #else - alignas(T) char aligned_char[sizeof(T) * N]; + alignas(T) char aligned_char[sizeof(T) * N]; #endif }; @@ -85,10 +85,10 @@ template class AlignedBuffer { public: - T *data() - { - return nullptr; - } + T *data() + { + return nullptr; + } }; // An immutable version of SmallVector which erases type information about storage. @@ -96,105 +96,105 @@ template class VectorView { public: - T &operator[](size_t i) SPIRV_CROSS_NOEXCEPT - { - return ptr[i]; - } + T &operator[](size_t i) SPIRV_CROSS_NOEXCEPT + { + return ptr[i]; + } - const T &operator[](size_t i) const SPIRV_CROSS_NOEXCEPT - { - return ptr[i]; - } + const T &operator[](size_t i) const SPIRV_CROSS_NOEXCEPT + { + return ptr[i]; + } - bool empty() const SPIRV_CROSS_NOEXCEPT - { - return buffer_size == 0; - } + bool empty() const SPIRV_CROSS_NOEXCEPT + { + return buffer_size == 0; + } - size_t size() const SPIRV_CROSS_NOEXCEPT - { - return buffer_size; - } + size_t size() const SPIRV_CROSS_NOEXCEPT + { + return buffer_size; + } - T *data() SPIRV_CROSS_NOEXCEPT - { - return ptr; - } + T *data() SPIRV_CROSS_NOEXCEPT + { + return ptr; + } - const T *data() const SPIRV_CROSS_NOEXCEPT - { - return ptr; - } + const T *data() const SPIRV_CROSS_NOEXCEPT + { + return ptr; + } - T *begin() SPIRV_CROSS_NOEXCEPT - { - return ptr; - } + T *begin() SPIRV_CROSS_NOEXCEPT + { + return ptr; + } - T *end() SPIRV_CROSS_NOEXCEPT - { - return ptr + buffer_size; - } + T *end() SPIRV_CROSS_NOEXCEPT + { + return ptr + buffer_size; + } - const T *begin() const SPIRV_CROSS_NOEXCEPT - { - return ptr; - } + const T *begin() const SPIRV_CROSS_NOEXCEPT + { + return ptr; + } - const T *end() const SPIRV_CROSS_NOEXCEPT - { - return ptr + buffer_size; - } + const T *end() const SPIRV_CROSS_NOEXCEPT + { + return ptr + buffer_size; + } - T &front() SPIRV_CROSS_NOEXCEPT - { - return ptr[0]; - } + T &front() SPIRV_CROSS_NOEXCEPT + { + return ptr[0]; + } - const T &front() const SPIRV_CROSS_NOEXCEPT - { - return ptr[0]; - } + const T &front() const SPIRV_CROSS_NOEXCEPT + { + return ptr[0]; + } - T &back() SPIRV_CROSS_NOEXCEPT - { - return ptr[buffer_size - 1]; - } + T &back() SPIRV_CROSS_NOEXCEPT + { + return ptr[buffer_size - 1]; + } - const T &back() const SPIRV_CROSS_NOEXCEPT - { - return ptr[buffer_size - 1]; - } + const T &back() const SPIRV_CROSS_NOEXCEPT + { + return ptr[buffer_size - 1]; + } - // Makes it easier to consume SmallVector. + // Makes it easier to consume SmallVector. #if defined(_MSC_VER) && _MSC_VER < 1900 - explicit operator std::vector() const - { - // Another MSVC 2013 workaround. It does not understand lvalue/rvalue qualified operations. - return std::vector(ptr, ptr + buffer_size); - } + explicit operator std::vector() const + { + // Another MSVC 2013 workaround. It does not understand lvalue/rvalue qualified operations. + return std::vector(ptr, ptr + buffer_size); + } #else - // Makes it easier to consume SmallVector. - explicit operator std::vector() const & - { - return std::vector(ptr, ptr + buffer_size); - } + // Makes it easier to consume SmallVector. + explicit operator std::vector() const & + { + return std::vector(ptr, ptr + buffer_size); + } - // If we are converting as an r-value, we can pilfer our elements. - explicit operator std::vector() && - { - return std::vector(std::make_move_iterator(ptr), std::make_move_iterator(ptr + buffer_size)); - } + // If we are converting as an r-value, we can pilfer our elements. + explicit operator std::vector() && + { + return std::vector(std::make_move_iterator(ptr), std::make_move_iterator(ptr + buffer_size)); + } #endif - // Avoid sliced copies. Base class should only be read as a reference. - VectorView(const VectorView &) = delete; - void operator=(const VectorView &) = delete; + // Avoid sliced copies. Base class should only be read as a reference. + VectorView(const VectorView &) = delete; + void operator=(const VectorView &) = delete; protected: - VectorView() = default; - T *ptr = nullptr; - size_t buffer_size = 0; + VectorView() = default; + T *ptr = nullptr; + size_t buffer_size = 0; }; // Simple vector which supports up to N elements inline, without malloc/free. @@ -205,329 +205,329 @@ template class SmallVector : public VectorView { public: - SmallVector() SPIRV_CROSS_NOEXCEPT - { - this->ptr = stack_storage.data(); - buffer_capacity = N; - } + SmallVector() SPIRV_CROSS_NOEXCEPT + { + this->ptr = stack_storage.data(); + buffer_capacity = N; + } - template - SmallVector(const U *arg_list_begin, const U *arg_list_end) SPIRV_CROSS_NOEXCEPT : SmallVector() - { - auto count = size_t(arg_list_end - arg_list_begin); - reserve(count); - for (size_t i = 0; i < count; i++, arg_list_begin++) - new (&this->ptr[i]) T(*arg_list_begin); - this->buffer_size = count; - } + template + SmallVector(const U *arg_list_begin, const U *arg_list_end) SPIRV_CROSS_NOEXCEPT : SmallVector() + { + auto count = size_t(arg_list_end - arg_list_begin); + reserve(count); + for (size_t i = 0; i < count; i++, arg_list_begin++) + new (&this->ptr[i]) T(*arg_list_begin); + this->buffer_size = count; + } - template - SmallVector(std::initializer_list init) SPIRV_CROSS_NOEXCEPT : SmallVector(init.begin(), init.end()) - { - } + template + SmallVector(std::initializer_list init) SPIRV_CROSS_NOEXCEPT : SmallVector(init.begin(), init.end()) + { + } - template - explicit SmallVector(const U (&init)[M]) SPIRV_CROSS_NOEXCEPT : SmallVector(init, init + M) - { - } + template + explicit SmallVector(const U (&init)[M]) SPIRV_CROSS_NOEXCEPT : SmallVector(init, init + M) + { + } - SmallVector(SmallVector &&other) SPIRV_CROSS_NOEXCEPT : SmallVector() - { - *this = std::move(other); - } + SmallVector(SmallVector &&other) SPIRV_CROSS_NOEXCEPT : SmallVector() + { + *this = std::move(other); + } - SmallVector &operator=(SmallVector &&other) SPIRV_CROSS_NOEXCEPT - { - clear(); - if (other.ptr != other.stack_storage.data()) - { - // Pilfer allocated pointer. - if (this->ptr != stack_storage.data()) - free(this->ptr); - this->ptr = other.ptr; - this->buffer_size = other.buffer_size; - buffer_capacity = other.buffer_capacity; - other.ptr = nullptr; - other.buffer_size = 0; - other.buffer_capacity = 0; - } - else - { - // Need to move the stack contents individually. - reserve(other.buffer_size); - for (size_t i = 0; i < other.buffer_size; i++) - { - new (&this->ptr[i]) T(std::move(other.ptr[i])); - other.ptr[i].~T(); - } - this->buffer_size = other.buffer_size; - other.buffer_size = 0; - } - return *this; - } + SmallVector &operator=(SmallVector &&other) SPIRV_CROSS_NOEXCEPT + { + clear(); + if (other.ptr != other.stack_storage.data()) + { + // Pilfer allocated pointer. + if (this->ptr != stack_storage.data()) + free(this->ptr); + this->ptr = other.ptr; + this->buffer_size = other.buffer_size; + buffer_capacity = other.buffer_capacity; + other.ptr = nullptr; + other.buffer_size = 0; + other.buffer_capacity = 0; + } + else + { + // Need to move the stack contents individually. + reserve(other.buffer_size); + for (size_t i = 0; i < other.buffer_size; i++) + { + new (&this->ptr[i]) T(std::move(other.ptr[i])); + other.ptr[i].~T(); + } + this->buffer_size = other.buffer_size; + other.buffer_size = 0; + } + return *this; + } - SmallVector(const SmallVector &other) SPIRV_CROSS_NOEXCEPT : SmallVector() - { - *this = other; - } + SmallVector(const SmallVector &other) SPIRV_CROSS_NOEXCEPT : SmallVector() + { + *this = other; + } - SmallVector &operator=(const SmallVector &other) SPIRV_CROSS_NOEXCEPT - { - if (this == &other) - return *this; + SmallVector &operator=(const SmallVector &other) SPIRV_CROSS_NOEXCEPT + { + if (this == &other) + return *this; - clear(); - reserve(other.buffer_size); - for (size_t i = 0; i < other.buffer_size; i++) - new (&this->ptr[i]) T(other.ptr[i]); - this->buffer_size = other.buffer_size; - return *this; - } + clear(); + reserve(other.buffer_size); + for (size_t i = 0; i < other.buffer_size; i++) + new (&this->ptr[i]) T(other.ptr[i]); + this->buffer_size = other.buffer_size; + return *this; + } - explicit SmallVector(size_t count) SPIRV_CROSS_NOEXCEPT : SmallVector() - { - resize(count); - } + explicit SmallVector(size_t count) SPIRV_CROSS_NOEXCEPT : SmallVector() + { + resize(count); + } - ~SmallVector() - { - clear(); - if (this->ptr != stack_storage.data()) - free(this->ptr); - } + ~SmallVector() + { + clear(); + if (this->ptr != stack_storage.data()) + free(this->ptr); + } - void clear() SPIRV_CROSS_NOEXCEPT - { - for (size_t i = 0; i < this->buffer_size; i++) - this->ptr[i].~T(); - this->buffer_size = 0; - } + void clear() SPIRV_CROSS_NOEXCEPT + { + for (size_t i = 0; i < this->buffer_size; i++) + this->ptr[i].~T(); + this->buffer_size = 0; + } - void push_back(const T &t) SPIRV_CROSS_NOEXCEPT - { - reserve(this->buffer_size + 1); - new (&this->ptr[this->buffer_size]) T(t); - this->buffer_size++; - } + void push_back(const T &t) SPIRV_CROSS_NOEXCEPT + { + reserve(this->buffer_size + 1); + new (&this->ptr[this->buffer_size]) T(t); + this->buffer_size++; + } - void push_back(T &&t) SPIRV_CROSS_NOEXCEPT - { - reserve(this->buffer_size + 1); - new (&this->ptr[this->buffer_size]) T(std::move(t)); - this->buffer_size++; - } + void push_back(T &&t) SPIRV_CROSS_NOEXCEPT + { + reserve(this->buffer_size + 1); + new (&this->ptr[this->buffer_size]) T(std::move(t)); + this->buffer_size++; + } - void pop_back() SPIRV_CROSS_NOEXCEPT - { - // Work around false positive warning on GCC 8.3. - // Calling pop_back on empty vector is undefined. - if (!this->empty()) - resize(this->buffer_size - 1); - } + void pop_back() SPIRV_CROSS_NOEXCEPT + { + // Work around false positive warning on GCC 8.3. + // Calling pop_back on empty vector is undefined. + if (!this->empty()) + resize(this->buffer_size - 1); + } - template - void emplace_back(Ts &&... ts) SPIRV_CROSS_NOEXCEPT - { - reserve(this->buffer_size + 1); - new (&this->ptr[this->buffer_size]) T(std::forward(ts)...); - this->buffer_size++; - } + template + void emplace_back(Ts &&... ts) SPIRV_CROSS_NOEXCEPT + { + reserve(this->buffer_size + 1); + new (&this->ptr[this->buffer_size]) T(std::forward(ts)...); + this->buffer_size++; + } - void reserve(size_t count) SPIRV_CROSS_NOEXCEPT - { - if ((count > (std::numeric_limits::max)() / sizeof(T)) || - (count > (std::numeric_limits::max)() / 2)) - { - // Only way this should ever happen is with garbage input, terminate. - std::terminate(); - } + void reserve(size_t count) SPIRV_CROSS_NOEXCEPT + { + if ((count > (std::numeric_limits::max)() / sizeof(T)) || + (count > (std::numeric_limits::max)() / 2)) + { + // Only way this should ever happen is with garbage input, terminate. + std::terminate(); + } - if (count > buffer_capacity) - { - size_t target_capacity = buffer_capacity; - if (target_capacity == 0) - target_capacity = 1; + if (count > buffer_capacity) + { + size_t target_capacity = buffer_capacity; + if (target_capacity == 0) + target_capacity = 1; - // Weird parens works around macro issues on Windows if NOMINMAX is not used. - target_capacity = (std::max)(target_capacity, N); + // Weird parens works around macro issues on Windows if NOMINMAX is not used. + target_capacity = (std::max)(target_capacity, N); - // Need to ensure there is a POT value of target capacity which is larger than count, - // otherwise this will overflow. - while (target_capacity < count) - target_capacity <<= 1u; + // Need to ensure there is a POT value of target capacity which is larger than count, + // otherwise this will overflow. + while (target_capacity < count) + target_capacity <<= 1u; - T *new_buffer = - target_capacity > N ? static_cast(malloc(target_capacity * sizeof(T))) : stack_storage.data(); + T *new_buffer = + target_capacity > N ? static_cast(malloc(target_capacity * sizeof(T))) : stack_storage.data(); - // If we actually fail this malloc, we are hosed anyways, there is no reason to attempt recovery. - if (!new_buffer) - std::terminate(); + // If we actually fail this malloc, we are hosed anyways, there is no reason to attempt recovery. + if (!new_buffer) + std::terminate(); - // In case for some reason two allocations both come from same stack. - if (new_buffer != this->ptr) - { - // We don't deal with types which can throw in move constructor. - for (size_t i = 0; i < this->buffer_size; i++) - { - new (&new_buffer[i]) T(std::move(this->ptr[i])); - this->ptr[i].~T(); - } - } + // In case for some reason two allocations both come from same stack. + if (new_buffer != this->ptr) + { + // We don't deal with types which can throw in move constructor. + for (size_t i = 0; i < this->buffer_size; i++) + { + new (&new_buffer[i]) T(std::move(this->ptr[i])); + this->ptr[i].~T(); + } + } - if (this->ptr != stack_storage.data()) - free(this->ptr); - this->ptr = new_buffer; - buffer_capacity = target_capacity; - } - } + if (this->ptr != stack_storage.data()) + free(this->ptr); + this->ptr = new_buffer; + buffer_capacity = target_capacity; + } + } - void insert(T *itr, const T *insert_begin, const T *insert_end) SPIRV_CROSS_NOEXCEPT - { - auto count = size_t(insert_end - insert_begin); - if (itr == this->end()) - { - reserve(this->buffer_size + count); - for (size_t i = 0; i < count; i++, insert_begin++) - new (&this->ptr[this->buffer_size + i]) T(*insert_begin); - this->buffer_size += count; - } - else - { - if (this->buffer_size + count > buffer_capacity) - { - auto target_capacity = this->buffer_size + count; - if (target_capacity == 0) - target_capacity = 1; - if (target_capacity < N) - target_capacity = N; + void insert(T *itr, const T *insert_begin, const T *insert_end) SPIRV_CROSS_NOEXCEPT + { + auto count = size_t(insert_end - insert_begin); + if (itr == this->end()) + { + reserve(this->buffer_size + count); + for (size_t i = 0; i < count; i++, insert_begin++) + new (&this->ptr[this->buffer_size + i]) T(*insert_begin); + this->buffer_size += count; + } + else + { + if (this->buffer_size + count > buffer_capacity) + { + auto target_capacity = this->buffer_size + count; + if (target_capacity == 0) + target_capacity = 1; + if (target_capacity < N) + target_capacity = N; - while (target_capacity < count) - target_capacity <<= 1u; + while (target_capacity < count) + target_capacity <<= 1u; - // Need to allocate new buffer. Move everything to a new buffer. - T *new_buffer = - target_capacity > N ? static_cast(malloc(target_capacity * sizeof(T))) : stack_storage.data(); + // Need to allocate new buffer. Move everything to a new buffer. + T *new_buffer = + target_capacity > N ? static_cast(malloc(target_capacity * sizeof(T))) : stack_storage.data(); - // If we actually fail this malloc, we are hosed anyways, there is no reason to attempt recovery. - if (!new_buffer) - std::terminate(); + // If we actually fail this malloc, we are hosed anyways, there is no reason to attempt recovery. + if (!new_buffer) + std::terminate(); - // First, move elements from source buffer to new buffer. - // We don't deal with types which can throw in move constructor. - auto *target_itr = new_buffer; - auto *original_source_itr = this->begin(); + // First, move elements from source buffer to new buffer. + // We don't deal with types which can throw in move constructor. + auto *target_itr = new_buffer; + auto *original_source_itr = this->begin(); - if (new_buffer != this->ptr) - { - while (original_source_itr != itr) - { - new (target_itr) T(std::move(*original_source_itr)); - original_source_itr->~T(); - ++original_source_itr; - ++target_itr; - } - } + if (new_buffer != this->ptr) + { + while (original_source_itr != itr) + { + new (target_itr) T(std::move(*original_source_itr)); + original_source_itr->~T(); + ++original_source_itr; + ++target_itr; + } + } - // Copy-construct new elements. - for (auto *source_itr = insert_begin; source_itr != insert_end; ++source_itr, ++target_itr) - new (target_itr) T(*source_itr); + // Copy-construct new elements. + for (auto *source_itr = insert_begin; source_itr != insert_end; ++source_itr, ++target_itr) + new (target_itr) T(*source_itr); - // Move over the other half. - if (new_buffer != this->ptr || insert_begin != insert_end) - { - while (original_source_itr != this->end()) - { - new (target_itr) T(std::move(*original_source_itr)); - original_source_itr->~T(); - ++original_source_itr; - ++target_itr; - } - } + // Move over the other half. + if (new_buffer != this->ptr || insert_begin != insert_end) + { + while (original_source_itr != this->end()) + { + new (target_itr) T(std::move(*original_source_itr)); + original_source_itr->~T(); + ++original_source_itr; + ++target_itr; + } + } - if (this->ptr != stack_storage.data()) - free(this->ptr); - this->ptr = new_buffer; - buffer_capacity = target_capacity; - } - else - { - // Move in place, need to be a bit careful about which elements are constructed and which are not. - // Move the end and construct the new elements. - auto *target_itr = this->end() + count; - auto *source_itr = this->end(); - while (target_itr != this->end() && source_itr != itr) - { - --target_itr; - --source_itr; - new (target_itr) T(std::move(*source_itr)); - } + if (this->ptr != stack_storage.data()) + free(this->ptr); + this->ptr = new_buffer; + buffer_capacity = target_capacity; + } + else + { + // Move in place, need to be a bit careful about which elements are constructed and which are not. + // Move the end and construct the new elements. + auto *target_itr = this->end() + count; + auto *source_itr = this->end(); + while (target_itr != this->end() && source_itr != itr) + { + --target_itr; + --source_itr; + new (target_itr) T(std::move(*source_itr)); + } - // For already constructed elements we can move-assign. - std::move_backward(itr, source_itr, target_itr); + // For already constructed elements we can move-assign. + std::move_backward(itr, source_itr, target_itr); - // For the inserts which go to already constructed elements, we can do a plain copy. - while (itr != this->end() && insert_begin != insert_end) - *itr++ = *insert_begin++; + // For the inserts which go to already constructed elements, we can do a plain copy. + while (itr != this->end() && insert_begin != insert_end) + *itr++ = *insert_begin++; - // For inserts into newly allocated memory, we must copy-construct instead. - while (insert_begin != insert_end) - { - new (itr) T(*insert_begin); - ++itr; - ++insert_begin; - } - } + // For inserts into newly allocated memory, we must copy-construct instead. + while (insert_begin != insert_end) + { + new (itr) T(*insert_begin); + ++itr; + ++insert_begin; + } + } - this->buffer_size += count; - } - } + this->buffer_size += count; + } + } - void insert(T *itr, const T &value) SPIRV_CROSS_NOEXCEPT - { - insert(itr, &value, &value + 1); - } + void insert(T *itr, const T &value) SPIRV_CROSS_NOEXCEPT + { + insert(itr, &value, &value + 1); + } - T *erase(T *itr) SPIRV_CROSS_NOEXCEPT - { - std::move(itr + 1, this->end(), itr); - this->ptr[--this->buffer_size].~T(); - return itr; - } + T *erase(T *itr) SPIRV_CROSS_NOEXCEPT + { + std::move(itr + 1, this->end(), itr); + this->ptr[--this->buffer_size].~T(); + return itr; + } - void erase(T *start_erase, T *end_erase) SPIRV_CROSS_NOEXCEPT - { - if (end_erase == this->end()) - { - resize(size_t(start_erase - this->begin())); - } - else - { - auto new_size = this->buffer_size - (end_erase - start_erase); - std::move(end_erase, this->end(), start_erase); - resize(new_size); - } - } + void erase(T *start_erase, T *end_erase) SPIRV_CROSS_NOEXCEPT + { + if (end_erase == this->end()) + { + resize(size_t(start_erase - this->begin())); + } + else + { + auto new_size = this->buffer_size - (end_erase - start_erase); + std::move(end_erase, this->end(), start_erase); + resize(new_size); + } + } - void resize(size_t new_size) SPIRV_CROSS_NOEXCEPT - { - if (new_size < this->buffer_size) - { - for (size_t i = new_size; i < this->buffer_size; i++) - this->ptr[i].~T(); - } - else if (new_size > this->buffer_size) - { - reserve(new_size); - for (size_t i = this->buffer_size; i < new_size; i++) - new (&this->ptr[i]) T(); - } + void resize(size_t new_size) SPIRV_CROSS_NOEXCEPT + { + if (new_size < this->buffer_size) + { + for (size_t i = new_size; i < this->buffer_size; i++) + this->ptr[i].~T(); + } + else if (new_size > this->buffer_size) + { + reserve(new_size); + for (size_t i = this->buffer_size; i < new_size; i++) + new (&this->ptr[i]) T(); + } - this->buffer_size = new_size; - } + this->buffer_size = new_size; + } private: - size_t buffer_capacity = 0; - AlignedBuffer stack_storage; + size_t buffer_capacity = 0; + AlignedBuffer stack_storage; }; // A vector without stack storage. @@ -553,202 +553,202 @@ using VectorView = std::vector; class ObjectPoolBase { public: - virtual ~ObjectPoolBase() = default; - virtual void deallocate_opaque(void *ptr) = 0; + virtual ~ObjectPoolBase() = default; + virtual void deallocate_opaque(void *ptr) = 0; }; template class ObjectPool : public ObjectPoolBase { public: - explicit ObjectPool(unsigned start_object_count_ = 16) - : start_object_count(start_object_count_) - { - } + explicit ObjectPool(unsigned start_object_count_ = 16) + : start_object_count(start_object_count_) + { + } - template - T *allocate(P &&... p) - { - if (vacants.empty()) - { - unsigned num_objects = start_object_count << memory.size(); - T *ptr = static_cast(malloc(num_objects * sizeof(T))); - if (!ptr) - return nullptr; + template + T *allocate(P &&... p) + { + if (vacants.empty()) + { + unsigned num_objects = start_object_count << memory.size(); + T *ptr = static_cast(malloc(num_objects * sizeof(T))); + if (!ptr) + return nullptr; - vacants.reserve(num_objects); - for (unsigned i = 0; i < num_objects; i++) - vacants.push_back(&ptr[i]); + vacants.reserve(num_objects); + for (unsigned i = 0; i < num_objects; i++) + vacants.push_back(&ptr[i]); - memory.emplace_back(ptr); - } + memory.emplace_back(ptr); + } - T *ptr = vacants.back(); - vacants.pop_back(); - new (ptr) T(std::forward

(p)...); - return ptr; - } + T *ptr = vacants.back(); + vacants.pop_back(); + new (ptr) T(std::forward

(p)...); + return ptr; + } - void deallocate(T *ptr) - { - ptr->~T(); - vacants.push_back(ptr); - } + void deallocate(T *ptr) + { + ptr->~T(); + vacants.push_back(ptr); + } - void deallocate_opaque(void *ptr) override - { - deallocate(static_cast(ptr)); - } + void deallocate_opaque(void *ptr) override + { + deallocate(static_cast(ptr)); + } - void clear() - { - vacants.clear(); - memory.clear(); - } + void clear() + { + vacants.clear(); + memory.clear(); + } protected: - Vector vacants; + Vector vacants; - struct MallocDeleter - { - void operator()(T *ptr) - { - ::free(ptr); - } - }; + struct MallocDeleter + { + void operator()(T *ptr) + { + ::free(ptr); + } + }; - SmallVector> memory; - unsigned start_object_count; + SmallVector> memory; + unsigned start_object_count; }; template class StringStream { public: - StringStream() - { - reset(); - } + StringStream() + { + reset(); + } - ~StringStream() - { - reset(); - } + ~StringStream() + { + reset(); + } - // Disable copies and moves. Makes it easier to implement, and we don't need it. - StringStream(const StringStream &) = delete; - void operator=(const StringStream &) = delete; + // Disable copies and moves. Makes it easier to implement, and we don't need it. + StringStream(const StringStream &) = delete; + void operator=(const StringStream &) = delete; - template ::value, int>::type = 0> - StringStream &operator<<(const T &t) - { - auto s = std::to_string(t); - append(s.data(), s.size()); - return *this; - } + template ::value, int>::type = 0> + StringStream &operator<<(const T &t) + { + auto s = std::to_string(t); + append(s.data(), s.size()); + return *this; + } - // Only overload this to make float/double conversions ambiguous. - StringStream &operator<<(uint32_t v) - { - auto s = std::to_string(v); - append(s.data(), s.size()); - return *this; - } + // Only overload this to make float/double conversions ambiguous. + StringStream &operator<<(uint32_t v) + { + auto s = std::to_string(v); + append(s.data(), s.size()); + return *this; + } - StringStream &operator<<(char c) - { - append(&c, 1); - return *this; - } + StringStream &operator<<(char c) + { + append(&c, 1); + return *this; + } - StringStream &operator<<(const std::string &s) - { - append(s.data(), s.size()); - return *this; - } + StringStream &operator<<(const std::string &s) + { + append(s.data(), s.size()); + return *this; + } - StringStream &operator<<(const char *s) - { - append(s, strlen(s)); - return *this; - } + StringStream &operator<<(const char *s) + { + append(s, strlen(s)); + return *this; + } - template - StringStream &operator<<(const char (&s)[N]) - { - append(s, strlen(s)); - return *this; - } + template + StringStream &operator<<(const char (&s)[N]) + { + append(s, strlen(s)); + return *this; + } - std::string str() const - { - std::string ret; - size_t target_size = 0; - for (auto &saved : saved_buffers) - target_size += saved.offset; - target_size += current_buffer.offset; - ret.reserve(target_size); + std::string str() const + { + std::string ret; + size_t target_size = 0; + for (auto &saved : saved_buffers) + target_size += saved.offset; + target_size += current_buffer.offset; + ret.reserve(target_size); - for (auto &saved : saved_buffers) - ret.insert(ret.end(), saved.buffer, saved.buffer + saved.offset); - ret.insert(ret.end(), current_buffer.buffer, current_buffer.buffer + current_buffer.offset); - return ret; - } + for (auto &saved : saved_buffers) + ret.insert(ret.end(), saved.buffer, saved.buffer + saved.offset); + ret.insert(ret.end(), current_buffer.buffer, current_buffer.buffer + current_buffer.offset); + return ret; + } - void reset() - { - for (auto &saved : saved_buffers) - if (saved.buffer != stack_buffer) - free(saved.buffer); - if (current_buffer.buffer != stack_buffer) - free(current_buffer.buffer); + void reset() + { + for (auto &saved : saved_buffers) + if (saved.buffer != stack_buffer) + free(saved.buffer); + if (current_buffer.buffer != stack_buffer) + free(current_buffer.buffer); - saved_buffers.clear(); - current_buffer.buffer = stack_buffer; - current_buffer.offset = 0; - current_buffer.size = sizeof(stack_buffer); - } + saved_buffers.clear(); + current_buffer.buffer = stack_buffer; + current_buffer.offset = 0; + current_buffer.size = sizeof(stack_buffer); + } private: - struct Buffer - { - char *buffer = nullptr; - size_t offset = 0; - size_t size = 0; - }; - Buffer current_buffer; - char stack_buffer[StackSize]; - SmallVector saved_buffers; + struct Buffer + { + char *buffer = nullptr; + size_t offset = 0; + size_t size = 0; + }; + Buffer current_buffer; + char stack_buffer[StackSize]; + SmallVector saved_buffers; - void append(const char *s, size_t len) - { - size_t avail = current_buffer.size - current_buffer.offset; - if (avail < len) - { - if (avail > 0) - { - memcpy(current_buffer.buffer + current_buffer.offset, s, avail); - s += avail; - len -= avail; - current_buffer.offset += avail; - } + void append(const char *s, size_t len) + { + size_t avail = current_buffer.size - current_buffer.offset; + if (avail < len) + { + if (avail > 0) + { + memcpy(current_buffer.buffer + current_buffer.offset, s, avail); + s += avail; + len -= avail; + current_buffer.offset += avail; + } - saved_buffers.push_back(current_buffer); - size_t target_size = len > BlockSize ? len : BlockSize; - current_buffer.buffer = static_cast(malloc(target_size)); - if (!current_buffer.buffer) - SPIRV_CROSS_THROW("Out of memory."); + saved_buffers.push_back(current_buffer); + size_t target_size = len > BlockSize ? len : BlockSize; + current_buffer.buffer = static_cast(malloc(target_size)); + if (!current_buffer.buffer) + SPIRV_CROSS_THROW("Out of memory."); - memcpy(current_buffer.buffer, s, len); - current_buffer.offset = len; - current_buffer.size = target_size; - } - else - { - memcpy(current_buffer.buffer + current_buffer.offset, s, len); - current_buffer.offset += len; - } - } + memcpy(current_buffer.buffer, s, len); + current_buffer.offset = len; + current_buffer.size = target_size; + } + else + { + memcpy(current_buffer.buffer + current_buffer.offset, s, len); + current_buffer.offset += len; + } + } }; } // namespace SPIRV_CROSS_NAMESPACE diff --git a/spirv_cross_error_handling.hpp b/spirv_cross_error_handling.hpp index 91e6cf4f..990f7079 100644 --- a/spirv_cross_error_handling.hpp +++ b/spirv_cross_error_handling.hpp @@ -49,12 +49,12 @@ inline void report_and_abort(const std::string &msg) { #ifdef NDEBUG - (void)msg; + (void)msg; #else - fprintf(stderr, "There was a compiler error: %s\n", msg.c_str()); + fprintf(stderr, "There was a compiler error: %s\n", msg.c_str()); #endif - fflush(stderr); - abort(); + fflush(stderr); + abort(); } #define SPIRV_CROSS_THROW(x) report_and_abort(x) @@ -62,15 +62,15 @@ report_and_abort(const std::string &msg) class CompilerError : public std::runtime_error { public: - explicit CompilerError(const std::string &str) - : std::runtime_error(str) - { - } + explicit CompilerError(const std::string &str) + : std::runtime_error(str) + { + } - explicit CompilerError(const char *str) - : std::runtime_error(str) - { - } + explicit CompilerError(const char *str) + : std::runtime_error(str) + { + } }; #define SPIRV_CROSS_THROW(x) throw CompilerError(x) diff --git a/spirv_cross_parsed_ir.hpp b/spirv_cross_parsed_ir.hpp index 3892248a..58de376a 100644 --- a/spirv_cross_parsed_ir.hpp +++ b/spirv_cross_parsed_ir.hpp @@ -39,217 +39,217 @@ namespace SPIRV_CROSS_NAMESPACE class ParsedIR { private: - // This must be destroyed after the "ids" vector. - std::unique_ptr pool_group; + // This must be destroyed after the "ids" vector. + std::unique_ptr pool_group; public: - ParsedIR(); + ParsedIR(); - // Due to custom allocations from object pools, we cannot use a default copy constructor. - ParsedIR(const ParsedIR &other); - ParsedIR &operator=(const ParsedIR &other); + // Due to custom allocations from object pools, we cannot use a default copy constructor. + ParsedIR(const ParsedIR &other); + ParsedIR &operator=(const ParsedIR &other); - // Moves are unproblematic, but we need to implement it anyways, since MSVC 2013 does not understand - // how to default-implement these. - ParsedIR(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT; - ParsedIR &operator=(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT; + // Moves are unproblematic, but we need to implement it anyways, since MSVC 2013 does not understand + // how to default-implement these. + ParsedIR(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT; + ParsedIR &operator=(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT; - // Resizes ids, meta and block_meta. - void set_id_bounds(uint32_t bounds); + // Resizes ids, meta and block_meta. + void set_id_bounds(uint32_t bounds); - // The raw SPIR-V, instructions and opcodes refer to this by offset + count. - std::vector spirv; + // The raw SPIR-V, instructions and opcodes refer to this by offset + count. + std::vector spirv; - // Holds various data structures which inherit from IVariant. - SmallVector ids; + // Holds various data structures which inherit from IVariant. + SmallVector ids; - // Various meta data for IDs, decorations, names, etc. - std::unordered_map meta; + // Various meta data for IDs, decorations, names, etc. + std::unordered_map meta; - // Holds all IDs which have a certain type. - // This is needed so we can iterate through a specific kind of resource quickly, - // and in-order of module declaration. - SmallVector ids_for_type[TypeCount]; + // Holds all IDs which have a certain type. + // This is needed so we can iterate through a specific kind of resource quickly, + // and in-order of module declaration. + SmallVector ids_for_type[TypeCount]; - // Special purpose lists which contain a union of types. - // This is needed so we can declare specialization constants and structs in an interleaved fashion, - // among other things. - // Constants can be undef or of struct type, and struct array sizes can use specialization constants. - SmallVector ids_for_constant_undef_or_type; - SmallVector ids_for_constant_or_variable; + // Special purpose lists which contain a union of types. + // This is needed so we can declare specialization constants and structs in an interleaved fashion, + // among other things. + // Constants can be undef or of struct type, and struct array sizes can use specialization constants. + SmallVector ids_for_constant_undef_or_type; + SmallVector ids_for_constant_or_variable; - // We need to keep track of the width the Ops that contains a type for the - // OpSwitch instruction, since this one doesn't contains the type in the - // instruction itself. And in some case we need to cast the condition to - // wider types. We only need the width to do the branch fixup since the - // type check itself can be done at runtime - std::unordered_map load_type_width; + // We need to keep track of the width the Ops that contains a type for the + // OpSwitch instruction, since this one doesn't contains the type in the + // instruction itself. And in some case we need to cast the condition to + // wider types. We only need the width to do the branch fixup since the + // type check itself can be done at runtime + std::unordered_map load_type_width; - // Declared capabilities and extensions in the SPIR-V module. - // Not really used except for reflection at the moment. - SmallVector declared_capabilities; - SmallVector declared_extensions; + // Declared capabilities and extensions in the SPIR-V module. + // Not really used except for reflection at the moment. + SmallVector declared_capabilities; + SmallVector declared_extensions; - // Meta data about blocks. The cross-compiler needs to query if a block is either of these types. - // It is a bitset as there can be more than one tag per block. - enum BlockMetaFlagBits - { - BLOCK_META_LOOP_HEADER_BIT = 1 << 0, - BLOCK_META_CONTINUE_BIT = 1 << 1, - BLOCK_META_LOOP_MERGE_BIT = 1 << 2, - BLOCK_META_SELECTION_MERGE_BIT = 1 << 3, - BLOCK_META_MULTISELECT_MERGE_BIT = 1 << 4 - }; - using BlockMetaFlags = uint8_t; - SmallVector block_meta; - std::unordered_map continue_block_to_loop_header; + // Meta data about blocks. The cross-compiler needs to query if a block is either of these types. + // It is a bitset as there can be more than one tag per block. + enum BlockMetaFlagBits + { + BLOCK_META_LOOP_HEADER_BIT = 1 << 0, + BLOCK_META_CONTINUE_BIT = 1 << 1, + BLOCK_META_LOOP_MERGE_BIT = 1 << 2, + BLOCK_META_SELECTION_MERGE_BIT = 1 << 3, + BLOCK_META_MULTISELECT_MERGE_BIT = 1 << 4 + }; + using BlockMetaFlags = uint8_t; + SmallVector block_meta; + std::unordered_map continue_block_to_loop_header; - // Normally, we'd stick SPIREntryPoint in ids array, but it conflicts with SPIRFunction. - // Entry points can therefore be seen as some sort of meta structure. - std::unordered_map entry_points; - FunctionID default_entry_point = 0; + // Normally, we'd stick SPIREntryPoint in ids array, but it conflicts with SPIRFunction. + // Entry points can therefore be seen as some sort of meta structure. + std::unordered_map entry_points; + FunctionID default_entry_point = 0; - struct Source - { - uint32_t version = 0; - bool es = false; - bool known = false; - bool hlsl = false; + struct Source + { + uint32_t version = 0; + bool es = false; + bool known = false; + bool hlsl = false; - Source() = default; - }; + Source() = default; + }; - Source source; + Source source; - spv::AddressingModel addressing_model = spv::AddressingModelMax; - spv::MemoryModel memory_model = spv::MemoryModelMax; + spv::AddressingModel addressing_model = spv::AddressingModelMax; + spv::MemoryModel memory_model = spv::MemoryModelMax; - // Decoration handling methods. - // Can be useful for simple "raw" reflection. - // However, most members are here because the Parser needs most of these, - // and might as well just have the whole suite of decoration/name handling in one place. - void set_name(ID id, const std::string &name); - const std::string &get_name(ID id) const; - void set_decoration(ID id, spv::Decoration decoration, uint32_t argument = 0); - void set_decoration_string(ID id, spv::Decoration decoration, const std::string &argument); - bool has_decoration(ID id, spv::Decoration decoration) const; - uint32_t get_decoration(ID id, spv::Decoration decoration) const; - const std::string &get_decoration_string(ID id, spv::Decoration decoration) const; - const Bitset &get_decoration_bitset(ID id) const; - void unset_decoration(ID id, spv::Decoration decoration); + // Decoration handling methods. + // Can be useful for simple "raw" reflection. + // However, most members are here because the Parser needs most of these, + // and might as well just have the whole suite of decoration/name handling in one place. + void set_name(ID id, const std::string &name); + const std::string &get_name(ID id) const; + void set_decoration(ID id, spv::Decoration decoration, uint32_t argument = 0); + void set_decoration_string(ID id, spv::Decoration decoration, const std::string &argument); + bool has_decoration(ID id, spv::Decoration decoration) const; + uint32_t get_decoration(ID id, spv::Decoration decoration) const; + const std::string &get_decoration_string(ID id, spv::Decoration decoration) const; + const Bitset &get_decoration_bitset(ID id) const; + void unset_decoration(ID id, spv::Decoration decoration); - // Decoration handling methods (for members of a struct). - void set_member_name(TypeID id, uint32_t index, const std::string &name); - const std::string &get_member_name(TypeID id, uint32_t index) const; - void set_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration, uint32_t argument = 0); - void set_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration, - const std::string &argument); - uint32_t get_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const; - const std::string &get_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration) const; - bool has_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const; - const Bitset &get_member_decoration_bitset(TypeID id, uint32_t index) const; - void unset_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration); + // Decoration handling methods (for members of a struct). + void set_member_name(TypeID id, uint32_t index, const std::string &name); + const std::string &get_member_name(TypeID id, uint32_t index) const; + void set_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration, uint32_t argument = 0); + void set_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration, + const std::string &argument); + uint32_t get_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const; + const std::string &get_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration) const; + bool has_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const; + const Bitset &get_member_decoration_bitset(TypeID id, uint32_t index) const; + void unset_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration); - void mark_used_as_array_length(ID id); - uint32_t increase_bound_by(uint32_t count); - Bitset get_buffer_block_flags(const SPIRVariable &var) const; - Bitset get_buffer_block_type_flags(const SPIRType &type) const; + void mark_used_as_array_length(ID id); + uint32_t increase_bound_by(uint32_t count); + Bitset get_buffer_block_flags(const SPIRVariable &var) const; + Bitset get_buffer_block_type_flags(const SPIRType &type) const; - void add_typed_id(Types type, ID id); - void remove_typed_id(Types type, ID id); + void add_typed_id(Types type, ID id); + void remove_typed_id(Types type, ID id); - class LoopLock - { - public: - explicit LoopLock(uint32_t *counter); - LoopLock(const LoopLock &) = delete; - void operator=(const LoopLock &) = delete; - LoopLock(LoopLock &&other) SPIRV_CROSS_NOEXCEPT; - LoopLock &operator=(LoopLock &&other) SPIRV_CROSS_NOEXCEPT; - ~LoopLock(); + class LoopLock + { + public: + explicit LoopLock(uint32_t *counter); + LoopLock(const LoopLock &) = delete; + void operator=(const LoopLock &) = delete; + LoopLock(LoopLock &&other) SPIRV_CROSS_NOEXCEPT; + LoopLock &operator=(LoopLock &&other) SPIRV_CROSS_NOEXCEPT; + ~LoopLock(); - private: - uint32_t *lock = nullptr; - }; + private: + uint32_t *lock = nullptr; + }; - // This must be held while iterating over a type ID array. - // It is undefined if someone calls set<>() while we're iterating over a data structure, so we must - // make sure that this case is avoided. + // This must be held while iterating over a type ID array. + // It is undefined if someone calls set<>() while we're iterating over a data structure, so we must + // make sure that this case is avoided. - // If we have a hard lock, it is an error to call set<>(), and an exception is thrown. - // If we have a soft lock, we silently ignore any additions to the typed arrays. - // This should only be used for physical ID remapping where we need to create an ID, but we will never - // care about iterating over them. - LoopLock create_loop_hard_lock() const; - LoopLock create_loop_soft_lock() const; + // If we have a hard lock, it is an error to call set<>(), and an exception is thrown. + // If we have a soft lock, we silently ignore any additions to the typed arrays. + // This should only be used for physical ID remapping where we need to create an ID, but we will never + // care about iterating over them. + LoopLock create_loop_hard_lock() const; + LoopLock create_loop_soft_lock() const; - template - void for_each_typed_id(const Op &op) - { - auto loop_lock = create_loop_hard_lock(); - for (auto &id : ids_for_type[T::type]) - { - if (ids[id].get_type() == static_cast(T::type)) - op(id, get(id)); - } - } + template + void for_each_typed_id(const Op &op) + { + auto loop_lock = create_loop_hard_lock(); + for (auto &id : ids_for_type[T::type]) + { + if (ids[id].get_type() == static_cast(T::type)) + op(id, get(id)); + } + } - template - void for_each_typed_id(const Op &op) const - { - auto loop_lock = create_loop_hard_lock(); - for (auto &id : ids_for_type[T::type]) - { - if (ids[id].get_type() == static_cast(T::type)) - op(id, get(id)); - } - } + template + void for_each_typed_id(const Op &op) const + { + auto loop_lock = create_loop_hard_lock(); + for (auto &id : ids_for_type[T::type]) + { + if (ids[id].get_type() == static_cast(T::type)) + op(id, get(id)); + } + } - template - void reset_all_of_type() - { - reset_all_of_type(static_cast(T::type)); - } + template + void reset_all_of_type() + { + reset_all_of_type(static_cast(T::type)); + } - void reset_all_of_type(Types type); + void reset_all_of_type(Types type); - Meta *find_meta(ID id); - const Meta *find_meta(ID id) const; + Meta *find_meta(ID id); + const Meta *find_meta(ID id) const; - const std::string &get_empty_string() const - { - return empty_string; - } + const std::string &get_empty_string() const + { + return empty_string; + } - void make_constant_null(uint32_t id, uint32_t type, bool add_to_typed_id_set); + void make_constant_null(uint32_t id, uint32_t type, bool add_to_typed_id_set); - void fixup_reserved_names(); + void fixup_reserved_names(); - static void sanitize_underscores(std::string &str); - static void sanitize_identifier(std::string &str, bool member, bool allow_reserved_prefixes); - static bool is_globally_reserved_identifier(std::string &str, bool allow_reserved_prefixes); + static void sanitize_underscores(std::string &str); + static void sanitize_identifier(std::string &str, bool member, bool allow_reserved_prefixes); + static bool is_globally_reserved_identifier(std::string &str, bool allow_reserved_prefixes); - uint32_t get_spirv_version() const; + uint32_t get_spirv_version() const; private: - template - T &get(uint32_t id) - { - return variant_get(ids[id]); - } + template + T &get(uint32_t id) + { + return variant_get(ids[id]); + } - template - const T &get(uint32_t id) const - { - return variant_get(ids[id]); - } + template + const T &get(uint32_t id) const + { + return variant_get(ids[id]); + } - mutable uint32_t loop_iteration_depth_hard = 0; - mutable uint32_t loop_iteration_depth_soft = 0; - std::string empty_string; - Bitset cleared_bitset; + mutable uint32_t loop_iteration_depth_hard = 0; + mutable uint32_t loop_iteration_depth_soft = 0; + std::string empty_string; + Bitset cleared_bitset; - std::unordered_set meta_needing_name_fixup; + std::unordered_set meta_needing_name_fixup; }; } // namespace SPIRV_CROSS_NAMESPACE diff --git a/spirv_glsl.hpp b/spirv_glsl.hpp index 8a002632..93df99f9 100644 --- a/spirv_glsl.hpp +++ b/spirv_glsl.hpp @@ -34,1040 +34,1040 @@ namespace SPIRV_CROSS_NAMESPACE { enum PlsFormat { - PlsNone = 0, + PlsNone = 0, - PlsR11FG11FB10F, - PlsR32F, - PlsRG16F, - PlsRGB10A2, - PlsRGBA8, - PlsRG16, + PlsR11FG11FB10F, + PlsR32F, + PlsRG16F, + PlsRGB10A2, + PlsRGBA8, + PlsRG16, - PlsRGBA8I, - PlsRG16I, + PlsRGBA8I, + PlsRG16I, - PlsRGB10A2UI, - PlsRGBA8UI, - PlsRG16UI, - PlsR32UI + PlsRGB10A2UI, + PlsRGBA8UI, + PlsRG16UI, + PlsR32UI }; struct PlsRemap { - uint32_t id; - PlsFormat format; + uint32_t id; + PlsFormat format; }; enum AccessChainFlagBits { - ACCESS_CHAIN_INDEX_IS_LITERAL_BIT = 1 << 0, - ACCESS_CHAIN_CHAIN_ONLY_BIT = 1 << 1, - ACCESS_CHAIN_PTR_CHAIN_BIT = 1 << 2, - ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT = 1 << 3, - ACCESS_CHAIN_LITERAL_MSB_FORCE_ID = 1 << 4, - ACCESS_CHAIN_FLATTEN_ALL_MEMBERS_BIT = 1 << 5, - ACCESS_CHAIN_FORCE_COMPOSITE_BIT = 1 << 6, - ACCESS_CHAIN_PTR_CHAIN_POINTER_ARITH_BIT = 1 << 7, - ACCESS_CHAIN_PTR_CHAIN_CAST_TO_SCALAR_BIT = 1 << 8 + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT = 1 << 0, + ACCESS_CHAIN_CHAIN_ONLY_BIT = 1 << 1, + ACCESS_CHAIN_PTR_CHAIN_BIT = 1 << 2, + ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT = 1 << 3, + ACCESS_CHAIN_LITERAL_MSB_FORCE_ID = 1 << 4, + ACCESS_CHAIN_FLATTEN_ALL_MEMBERS_BIT = 1 << 5, + ACCESS_CHAIN_FORCE_COMPOSITE_BIT = 1 << 6, + ACCESS_CHAIN_PTR_CHAIN_POINTER_ARITH_BIT = 1 << 7, + ACCESS_CHAIN_PTR_CHAIN_CAST_TO_SCALAR_BIT = 1 << 8 }; typedef uint32_t AccessChainFlags; class CompilerGLSL : public Compiler { public: - struct Options - { - // The shading language version. Corresponds to #version $VALUE. - uint32_t version = 450; + struct Options + { + // The shading language version. Corresponds to #version $VALUE. + uint32_t version = 450; - // Emit the OpenGL ES shading language instead of desktop OpenGL. - bool es = false; + // Emit the OpenGL ES shading language instead of desktop OpenGL. + bool es = false; - // Debug option to always emit temporary variables for all expressions. - bool force_temporary = false; - // Debug option, can be increased in an attempt to workaround SPIRV-Cross bugs temporarily. - // If this limit has to be increased, it points to an implementation bug. - // In certain scenarios, the maximum number of debug iterations may increase beyond this limit - // as long as we can prove we're making certain kinds of forward progress. - uint32_t force_recompile_max_debug_iterations = 3; + // Debug option to always emit temporary variables for all expressions. + bool force_temporary = false; + // Debug option, can be increased in an attempt to workaround SPIRV-Cross bugs temporarily. + // If this limit has to be increased, it points to an implementation bug. + // In certain scenarios, the maximum number of debug iterations may increase beyond this limit + // as long as we can prove we're making certain kinds of forward progress. + uint32_t force_recompile_max_debug_iterations = 3; - // If true, Vulkan GLSL features are used instead of GL-compatible features. - // Mostly useful for debugging SPIR-V files. - bool vulkan_semantics = false; + // If true, Vulkan GLSL features are used instead of GL-compatible features. + // Mostly useful for debugging SPIR-V files. + bool vulkan_semantics = false; - // If true, gl_PerVertex is explicitly redeclared in vertex, geometry and tessellation shaders. - // The members of gl_PerVertex is determined by which built-ins are declared by the shader. - // This option is ignored in ES versions, as redeclaration in ES is not required, and it depends on a different extension - // (EXT_shader_io_blocks) which makes things a bit more fuzzy. - bool separate_shader_objects = false; + // If true, gl_PerVertex is explicitly redeclared in vertex, geometry and tessellation shaders. + // The members of gl_PerVertex is determined by which built-ins are declared by the shader. + // This option is ignored in ES versions, as redeclaration in ES is not required, and it depends on a different extension + // (EXT_shader_io_blocks) which makes things a bit more fuzzy. + bool separate_shader_objects = false; - // Flattens multidimensional arrays, e.g. float foo[a][b][c] into single-dimensional arrays, - // e.g. float foo[a * b * c]. - // This function does not change the actual SPIRType of any object. - // Only the generated code, including declarations of interface variables are changed to be single array dimension. - bool flatten_multidimensional_arrays = false; + // Flattens multidimensional arrays, e.g. float foo[a][b][c] into single-dimensional arrays, + // e.g. float foo[a * b * c]. + // This function does not change the actual SPIRType of any object. + // Only the generated code, including declarations of interface variables are changed to be single array dimension. + bool flatten_multidimensional_arrays = false; - // For older desktop GLSL targets than version 420, the - // GL_ARB_shading_language_420pack extensions is used to be able to support - // layout(binding) on UBOs and samplers. - // If disabled on older targets, binding decorations will be stripped. - bool enable_420pack_extension = true; + // For older desktop GLSL targets than version 420, the + // GL_ARB_shading_language_420pack extensions is used to be able to support + // layout(binding) on UBOs and samplers. + // If disabled on older targets, binding decorations will be stripped. + bool enable_420pack_extension = true; - // In non-Vulkan GLSL, emit push constant blocks as UBOs rather than plain uniforms. - bool emit_push_constant_as_uniform_buffer = false; + // In non-Vulkan GLSL, emit push constant blocks as UBOs rather than plain uniforms. + bool emit_push_constant_as_uniform_buffer = false; - // Always emit uniform blocks as plain uniforms, regardless of the GLSL version, even when UBOs are supported. - // Does not apply to shader storage or push constant blocks. - bool emit_uniform_buffer_as_plain_uniforms = false; + // Always emit uniform blocks as plain uniforms, regardless of the GLSL version, even when UBOs are supported. + // Does not apply to shader storage or push constant blocks. + bool emit_uniform_buffer_as_plain_uniforms = false; - // Emit OpLine directives if present in the module. - // May not correspond exactly to original source, but should be a good approximation. - bool emit_line_directives = false; + // Emit OpLine directives if present in the module. + // May not correspond exactly to original source, but should be a good approximation. + bool emit_line_directives = false; - // In cases where readonly/writeonly decoration are not used at all, - // we try to deduce which qualifier(s) we should actually used, since actually emitting - // read-write decoration is very rare, and older glslang/HLSL compilers tend to just emit readwrite as a matter of fact. - // The default (true) is to enable automatic deduction for these cases, but if you trust the decorations set - // by the SPIR-V, it's recommended to set this to false. - bool enable_storage_image_qualifier_deduction = true; + // In cases where readonly/writeonly decoration are not used at all, + // we try to deduce which qualifier(s) we should actually used, since actually emitting + // read-write decoration is very rare, and older glslang/HLSL compilers tend to just emit readwrite as a matter of fact. + // The default (true) is to enable automatic deduction for these cases, but if you trust the decorations set + // by the SPIR-V, it's recommended to set this to false. + bool enable_storage_image_qualifier_deduction = true; - // On some targets (WebGPU), uninitialized variables are banned. - // If this is enabled, all variables (temporaries, Private, Function) - // which would otherwise be uninitialized will now be initialized to 0 instead. - bool force_zero_initialized_variables = false; + // On some targets (WebGPU), uninitialized variables are banned. + // If this is enabled, all variables (temporaries, Private, Function) + // which would otherwise be uninitialized will now be initialized to 0 instead. + bool force_zero_initialized_variables = false; - // In GLSL, force use of I/O block flattening, similar to - // what happens on legacy GLSL targets for blocks and structs. - bool force_flattened_io_blocks = false; + // In GLSL, force use of I/O block flattening, similar to + // what happens on legacy GLSL targets for blocks and structs. + bool force_flattened_io_blocks = false; - // For opcodes where we have to perform explicit additional nan checks, very ugly code is generated. - // If we opt-in, ignore these requirements. - // In opcodes like NClamp/NMin/NMax and FP compare, ignore NaN behavior. - // Use FClamp/FMin/FMax semantics for clamps and lets implementation choose ordered or unordered - // compares. - bool relax_nan_checks = false; + // For opcodes where we have to perform explicit additional nan checks, very ugly code is generated. + // If we opt-in, ignore these requirements. + // In opcodes like NClamp/NMin/NMax and FP compare, ignore NaN behavior. + // Use FClamp/FMin/FMax semantics for clamps and lets implementation choose ordered or unordered + // compares. + bool relax_nan_checks = false; - // Loading row-major matrices from UBOs on older AMD Windows OpenGL drivers is problematic. - // To load these types correctly, we must generate a wrapper. them in a dummy function which only purpose is to - // ensure row_major decoration is actually respected. - // This workaround may cause significant performance degeneration on some Android devices. - bool enable_row_major_load_workaround = true; + // Loading row-major matrices from UBOs on older AMD Windows OpenGL drivers is problematic. + // To load these types correctly, we must generate a wrapper. them in a dummy function which only purpose is to + // ensure row_major decoration is actually respected. + // This workaround may cause significant performance degeneration on some Android devices. + bool enable_row_major_load_workaround = true; - // If non-zero, controls layout(num_views = N) in; in GL_OVR_multiview2. - uint32_t ovr_multiview_view_count = 0; + // If non-zero, controls layout(num_views = N) in; in GL_OVR_multiview2. + uint32_t ovr_multiview_view_count = 0; - enum Precision - { - DontCare, - Lowp, - Mediump, - Highp - }; + enum Precision + { + DontCare, + Lowp, + Mediump, + Highp + }; - struct VertexOptions - { - // "Vertex-like shader" here is any shader stage that can write BuiltInPosition. + struct VertexOptions + { + // "Vertex-like shader" here is any shader stage that can write BuiltInPosition. - // GLSL: In vertex-like shaders, rewrite [0, w] depth (Vulkan/D3D style) to [-w, w] depth (GL style). - // MSL: In vertex-like shaders, rewrite [-w, w] depth (GL style) to [0, w] depth. - // HLSL: In vertex-like shaders, rewrite [-w, w] depth (GL style) to [0, w] depth. - bool fixup_clipspace = false; + // GLSL: In vertex-like shaders, rewrite [0, w] depth (Vulkan/D3D style) to [-w, w] depth (GL style). + // MSL: In vertex-like shaders, rewrite [-w, w] depth (GL style) to [0, w] depth. + // HLSL: In vertex-like shaders, rewrite [-w, w] depth (GL style) to [0, w] depth. + bool fixup_clipspace = false; - // In vertex-like shaders, inverts gl_Position.y or equivalent. - bool flip_vert_y = false; + // In vertex-like shaders, inverts gl_Position.y or equivalent. + bool flip_vert_y = false; - // GLSL only, for HLSL version of this option, see CompilerHLSL. - // If true, the backend will assume that InstanceIndex will need to apply - // a base instance offset. Set to false if you know you will never use base instance - // functionality as it might remove some internal uniforms. - bool support_nonzero_base_instance = true; - } vertex; + // GLSL only, for HLSL version of this option, see CompilerHLSL. + // If true, the backend will assume that InstanceIndex will need to apply + // a base instance offset. Set to false if you know you will never use base instance + // functionality as it might remove some internal uniforms. + bool support_nonzero_base_instance = true; + } vertex; - struct FragmentOptions - { - // Add precision mediump float in ES targets when emitting GLES source. - // Add precision highp int in ES targets when emitting GLES source. - Precision default_float_precision = Mediump; - Precision default_int_precision = Highp; - } fragment; - }; + struct FragmentOptions + { + // Add precision mediump float in ES targets when emitting GLES source. + // Add precision highp int in ES targets when emitting GLES source. + Precision default_float_precision = Mediump; + Precision default_int_precision = Highp; + } fragment; + }; - void remap_pixel_local_storage(std::vector inputs, std::vector outputs) - { - pls_inputs = std::move(inputs); - pls_outputs = std::move(outputs); - remap_pls_variables(); - } + void remap_pixel_local_storage(std::vector inputs, std::vector outputs) + { + pls_inputs = std::move(inputs); + pls_outputs = std::move(outputs); + remap_pls_variables(); + } - // Redirect a subpassInput reading from input_attachment_index to instead load its value from - // the color attachment at location = color_location. Requires ESSL. - // If coherent, uses GL_EXT_shader_framebuffer_fetch, if not, uses noncoherent variant. - void remap_ext_framebuffer_fetch(uint32_t input_attachment_index, uint32_t color_location, bool coherent); + // Redirect a subpassInput reading from input_attachment_index to instead load its value from + // the color attachment at location = color_location. Requires ESSL. + // If coherent, uses GL_EXT_shader_framebuffer_fetch, if not, uses noncoherent variant. + void remap_ext_framebuffer_fetch(uint32_t input_attachment_index, uint32_t color_location, bool coherent); - explicit CompilerGLSL(std::vector spirv_) - : Compiler(std::move(spirv_)) - { - init(); - } + explicit CompilerGLSL(std::vector spirv_) + : Compiler(std::move(spirv_)) + { + init(); + } - CompilerGLSL(const uint32_t *ir_, size_t word_count) - : Compiler(ir_, word_count) - { - init(); - } + CompilerGLSL(const uint32_t *ir_, size_t word_count) + : Compiler(ir_, word_count) + { + init(); + } - explicit CompilerGLSL(const ParsedIR &ir_) - : Compiler(ir_) - { - init(); - } + explicit CompilerGLSL(const ParsedIR &ir_) + : Compiler(ir_) + { + init(); + } - explicit CompilerGLSL(ParsedIR &&ir_) - : Compiler(std::move(ir_)) - { - init(); - } + explicit CompilerGLSL(ParsedIR &&ir_) + : Compiler(std::move(ir_)) + { + init(); + } - const Options &get_common_options() const - { - return options; - } + const Options &get_common_options() const + { + return options; + } - void set_common_options(const Options &opts) - { - options = opts; - } + void set_common_options(const Options &opts) + { + options = opts; + } - std::string compile() override; + std::string compile() override; - // Returns the current string held in the conversion buffer. Useful for - // capturing what has been converted so far when compile() throws an error. - std::string get_partial_source(); + // Returns the current string held in the conversion buffer. Useful for + // capturing what has been converted so far when compile() throws an error. + std::string get_partial_source(); - // Adds a line to be added right after #version in GLSL backend. - // This is useful for enabling custom extensions which are outside the scope of SPIRV-Cross. - // This can be combined with variable remapping. - // A new-line will be added. - // - // While add_header_line() is a more generic way of adding arbitrary text to the header - // of a GLSL file, require_extension() should be used when adding extensions since it will - // avoid creating collisions with SPIRV-Cross generated extensions. - // - // Code added via add_header_line() is typically backend-specific. - void add_header_line(const std::string &str); + // Adds a line to be added right after #version in GLSL backend. + // This is useful for enabling custom extensions which are outside the scope of SPIRV-Cross. + // This can be combined with variable remapping. + // A new-line will be added. + // + // While add_header_line() is a more generic way of adding arbitrary text to the header + // of a GLSL file, require_extension() should be used when adding extensions since it will + // avoid creating collisions with SPIRV-Cross generated extensions. + // + // Code added via add_header_line() is typically backend-specific. + void add_header_line(const std::string &str); - // Adds an extension which is required to run this shader, e.g. - // require_extension("GL_KHR_my_extension"); - void require_extension(const std::string &ext); + // Adds an extension which is required to run this shader, e.g. + // require_extension("GL_KHR_my_extension"); + void require_extension(const std::string &ext); - // Returns the list of required extensions. After compilation this will contains any other - // extensions that the compiler used automatically, in addition to the user specified ones. - const SmallVector &get_required_extensions() const; + // Returns the list of required extensions. After compilation this will contains any other + // extensions that the compiler used automatically, in addition to the user specified ones. + const SmallVector &get_required_extensions() const; - // Legacy GLSL compatibility method. - // Takes a uniform or push constant variable and flattens it into a (i|u)vec4 array[N]; array instead. - // For this to work, all types in the block must be the same basic type, e.g. mixing vec2 and vec4 is fine, but - // mixing int and float is not. - // The name of the uniform array will be the same as the interface block name. - void flatten_buffer_block(VariableID id); + // Legacy GLSL compatibility method. + // Takes a uniform or push constant variable and flattens it into a (i|u)vec4 array[N]; array instead. + // For this to work, all types in the block must be the same basic type, e.g. mixing vec2 and vec4 is fine, but + // mixing int and float is not. + // The name of the uniform array will be the same as the interface block name. + void flatten_buffer_block(VariableID id); - // After compilation, query if a variable ID was used as a depth resource. - // This is meaningful for MSL since descriptor types depend on this knowledge. - // Cases which return true: - // - Images which are declared with depth = 1 image type. - // - Samplers which are statically used at least once with Dref opcodes. - // - Images which are statically used at least once with Dref opcodes. - bool variable_is_depth_or_compare(VariableID id) const; + // After compilation, query if a variable ID was used as a depth resource. + // This is meaningful for MSL since descriptor types depend on this knowledge. + // Cases which return true: + // - Images which are declared with depth = 1 image type. + // - Samplers which are statically used at least once with Dref opcodes. + // - Images which are statically used at least once with Dref opcodes. + bool variable_is_depth_or_compare(VariableID id) const; - // If a shader output is active in this stage, but inactive in a subsequent stage, - // this can be signalled here. This can be used to work around certain cross-stage matching problems - // which plagues MSL and HLSL in certain scenarios. - // An output which matches one of these will not be emitted in stage output interfaces, but rather treated as a private - // variable. - // This option is only meaningful for MSL and HLSL, since GLSL matches by location directly. - // Masking builtins only takes effect if the builtin in question is part of the stage output interface. - void mask_stage_output_by_location(uint32_t location, uint32_t component); - void mask_stage_output_by_builtin(spv::BuiltIn builtin); + // If a shader output is active in this stage, but inactive in a subsequent stage, + // this can be signalled here. This can be used to work around certain cross-stage matching problems + // which plagues MSL and HLSL in certain scenarios. + // An output which matches one of these will not be emitted in stage output interfaces, but rather treated as a private + // variable. + // This option is only meaningful for MSL and HLSL, since GLSL matches by location directly. + // Masking builtins only takes effect if the builtin in question is part of the stage output interface. + void mask_stage_output_by_location(uint32_t location, uint32_t component); + void mask_stage_output_by_builtin(spv::BuiltIn builtin); - // Allow to control how to format float literals in the output. - // Set to "nullptr" to use the default "convert_to_string" function. - // This handle is not owned by SPIRV-Cross and must remain valid until compile() has been called. - void set_float_formatter(FloatFormatter *formatter) - { - float_formatter = formatter; - } + // Allow to control how to format float literals in the output. + // Set to "nullptr" to use the default "convert_to_string" function. + // This handle is not owned by SPIRV-Cross and must remain valid until compile() has been called. + void set_float_formatter(FloatFormatter *formatter) + { + float_formatter = formatter; + } protected: - struct ShaderSubgroupSupportHelper - { - // lower enum value = greater priority - enum Candidate - { - KHR_shader_subgroup_ballot, - KHR_shader_subgroup_basic, - KHR_shader_subgroup_vote, - KHR_shader_subgroup_arithmetic, - NV_gpu_shader_5, - NV_shader_thread_group, - NV_shader_thread_shuffle, - ARB_shader_ballot, - ARB_shader_group_vote, - AMD_gcn_shader, - - CandidateCount - }; - - static const char *get_extension_name(Candidate c); - static SmallVector get_extra_required_extension_names(Candidate c); - static const char *get_extra_required_extension_predicate(Candidate c); - - enum Feature - { - SubgroupMask = 0, - SubgroupSize = 1, - SubgroupInvocationID = 2, - SubgroupID = 3, - NumSubgroups = 4, - SubgroupBroadcast_First = 5, - SubgroupBallotFindLSB_MSB = 6, - SubgroupAll_Any_AllEqualBool = 7, - SubgroupAllEqualT = 8, - SubgroupElect = 9, - SubgroupBarrier = 10, - SubgroupMemBarrier = 11, - SubgroupBallot = 12, - SubgroupInverseBallot_InclBitCount_ExclBitCout = 13, - SubgroupBallotBitExtract = 14, - SubgroupBallotBitCount = 15, - SubgroupArithmeticIAddReduce = 16, - SubgroupArithmeticIAddExclusiveScan = 17, - SubgroupArithmeticIAddInclusiveScan = 18, - SubgroupArithmeticFAddReduce = 19, - SubgroupArithmeticFAddExclusiveScan = 20, - SubgroupArithmeticFAddInclusiveScan = 21, - SubgroupArithmeticIMulReduce = 22, - SubgroupArithmeticIMulExclusiveScan = 23, - SubgroupArithmeticIMulInclusiveScan = 24, - SubgroupArithmeticFMulReduce = 25, - SubgroupArithmeticFMulExclusiveScan = 26, - SubgroupArithmeticFMulInclusiveScan = 27, - FeatureCount - }; - - using FeatureMask = uint32_t; - static_assert(sizeof(FeatureMask) * 8u >= FeatureCount, "Mask type needs more bits."); - - using CandidateVector = SmallVector; - using FeatureVector = SmallVector; - - static FeatureVector get_feature_dependencies(Feature feature); - static FeatureMask get_feature_dependency_mask(Feature feature); - static bool can_feature_be_implemented_without_extensions(Feature feature); - static Candidate get_KHR_extension_for_feature(Feature feature); - - struct Result - { - Result(); - uint32_t weights[CandidateCount]; - }; - - void request_feature(Feature feature); - bool is_feature_requested(Feature feature) const; - Result resolve() const; - - static CandidateVector get_candidates_for_feature(Feature ft, const Result &r); - - private: - static CandidateVector get_candidates_for_feature(Feature ft); - static FeatureMask build_mask(const SmallVector &features); - FeatureMask feature_mask = 0; - }; - - // TODO remove this function when all subgroup ops are supported (or make it always return true) - static bool is_supported_subgroup_op_in_opengl(spv::Op op, const uint32_t *ops); - - void reset(uint32_t iteration_count); - void emit_function(SPIRFunction &func, const Bitset &return_flags); - - bool has_extension(const std::string &ext) const; - void require_extension_internal(const std::string &ext); - - // Virtualize methods which need to be overridden by subclass targets like C++ and such. - virtual void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags); - - SPIRBlock *current_emitting_block = nullptr; - SmallVector current_emitting_switch_stack; - bool current_emitting_switch_fallthrough = false; - - virtual void emit_instruction(const Instruction &instr); - struct TemporaryCopy - { - uint32_t dst_id; - uint32_t src_id; - }; - TemporaryCopy handle_instruction_precision(const Instruction &instr); - void emit_block_instructions(SPIRBlock &block); - void emit_block_instructions_with_masked_debug(SPIRBlock &block); - - // For relax_nan_checks. - GLSLstd450 get_remapped_glsl_op(GLSLstd450 std450_op) const; - spv::Op get_remapped_spirv_op(spv::Op op) const; - - virtual void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, - uint32_t count); - virtual void emit_spv_amd_shader_ballot_op(uint32_t result_type, uint32_t result_id, uint32_t op, - const uint32_t *args, uint32_t count); - virtual void emit_spv_amd_shader_explicit_vertex_parameter_op(uint32_t result_type, uint32_t result_id, uint32_t op, - const uint32_t *args, uint32_t count); - virtual void emit_spv_amd_shader_trinary_minmax_op(uint32_t result_type, uint32_t result_id, uint32_t op, - const uint32_t *args, uint32_t count); - virtual void emit_spv_amd_gcn_shader_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, - uint32_t count); - virtual void emit_header(); - void emit_line_directive(uint32_t file_id, uint32_t line_literal); - void build_workgroup_size(SmallVector &arguments, const SpecializationConstant &x, - const SpecializationConstant &y, const SpecializationConstant &z); - - void request_subgroup_feature(ShaderSubgroupSupportHelper::Feature feature); - - virtual void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id); - virtual void emit_texture_op(const Instruction &i, bool sparse); - virtual std::string to_texture_op(const Instruction &i, bool sparse, bool *forward, - SmallVector &inherited_expressions); - virtual void emit_subgroup_op(const Instruction &i); - virtual std::string type_to_glsl(const SPIRType &type, uint32_t id = 0); - virtual std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage); - virtual void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, - const std::string &qualifier = "", uint32_t base_offset = 0); - virtual void emit_struct_padding_target(const SPIRType &type); - virtual std::string image_type_glsl(const SPIRType &type, uint32_t id = 0, bool member = false); - std::string constant_expression(const SPIRConstant &c, - bool inside_block_like_struct_scope = false, - bool inside_struct_scope = false); - virtual std::string constant_op_expression(const SPIRConstantOp &cop); - virtual std::string constant_expression_vector(const SPIRConstant &c, uint32_t vector); - virtual void emit_fixup(); - virtual std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0); - virtual bool variable_decl_is_remapped_storage(const SPIRVariable &var, spv::StorageClass storage) const; - virtual std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id); - - struct TextureFunctionBaseArguments - { - // GCC 4.8 workarounds, it doesn't understand '{}' constructor here, use explicit default constructor. - TextureFunctionBaseArguments() = default; - VariableID img = 0; - const SPIRType *imgtype = nullptr; - bool is_fetch = false, is_gather = false, is_proj = false; - }; - - struct TextureFunctionNameArguments - { - // GCC 4.8 workarounds, it doesn't understand '{}' constructor here, use explicit default constructor. - TextureFunctionNameArguments() = default; - TextureFunctionBaseArguments base; - bool has_array_offsets = false, has_offset = false, has_grad = false; - bool has_dref = false, is_sparse_feedback = false, has_min_lod = false; - uint32_t lod = 0; - }; - virtual std::string to_function_name(const TextureFunctionNameArguments &args); - - struct TextureFunctionArguments - { - // GCC 4.8 workarounds, it doesn't understand '{}' constructor here, use explicit default constructor. - TextureFunctionArguments() = default; - TextureFunctionBaseArguments base; - uint32_t coord = 0, coord_components = 0, dref = 0; - uint32_t grad_x = 0, grad_y = 0, lod = 0, offset = 0; - uint32_t bias = 0, component = 0, sample = 0, sparse_texel = 0, min_lod = 0; - bool nonuniform_expression = false, has_array_offsets = false; - }; - virtual std::string to_function_args(const TextureFunctionArguments &args, bool *p_forward); - - void emit_sparse_feedback_temporaries(uint32_t result_type_id, uint32_t id, uint32_t &feedback_id, - uint32_t &texel_id); - uint32_t get_sparse_feedback_texel_id(uint32_t id) const; - virtual void emit_buffer_block(const SPIRVariable &type); - virtual void emit_push_constant_block(const SPIRVariable &var); - virtual void emit_uniform(const SPIRVariable &var); - virtual std::string unpack_expression_type(std::string expr_str, const SPIRType &type, uint32_t physical_type_id, - bool packed_type, bool row_major); - - virtual bool builtin_translates_to_nonarray(spv::BuiltIn builtin) const; - - virtual bool is_user_type_structured(uint32_t id) const; - - void emit_copy_logical_type(uint32_t lhs_id, uint32_t lhs_type_id, uint32_t rhs_id, uint32_t rhs_type_id, - SmallVector chain); - - StringStream<> buffer; - - template - inline void statement_inner(T &&t) - { - buffer << std::forward(t); - statement_count++; - } - - template - inline void statement_inner(T &&t, Ts &&... ts) - { - buffer << std::forward(t); - statement_count++; - statement_inner(std::forward(ts)...); - } - - template - inline void statement(Ts &&... ts) - { - if (is_forcing_recompilation()) - { - // Do not bother emitting code while force_recompile is active. - // We will compile again. - statement_count++; - return; - } - - if (redirect_statement) - { - redirect_statement->push_back(join(std::forward(ts)...)); - statement_count++; - } - else - { - for (uint32_t i = 0; i < indent; i++) - buffer << " "; - statement_inner(std::forward(ts)...); - buffer << '\n'; - } - } - - template - inline void statement_no_indent(Ts &&... ts) - { - auto old_indent = indent; - indent = 0; - statement(std::forward(ts)...); - indent = old_indent; - } - - // Used for implementing continue blocks where - // we want to obtain a list of statements we can merge - // on a single line separated by comma. - SmallVector *redirect_statement = nullptr; - const SPIRBlock *current_continue_block = nullptr; - bool block_temporary_hoisting = false; - bool block_debug_directives = false; - - void begin_scope(); - void end_scope(); - void end_scope(const std::string &trailer); - void end_scope_decl(); - void end_scope_decl(const std::string &decl); - - Options options; - - // Allow Metal to use the array template to make arrays a value type - virtual std::string type_to_array_glsl(const SPIRType &type, uint32_t variable_id); - std::string to_array_size(const SPIRType &type, uint32_t index); - uint32_t to_array_size_literal(const SPIRType &type, uint32_t index) const; - uint32_t to_array_size_literal(const SPIRType &type) const; - virtual std::string variable_decl(const SPIRVariable &variable); // Threadgroup arrays can't have a wrapper type - std::string variable_decl_function_local(SPIRVariable &variable); - - void add_local_variable_name(uint32_t id); - void add_resource_name(uint32_t id); - void add_member_name(SPIRType &type, uint32_t name); - void add_function_overload(const SPIRFunction &func); - - virtual bool is_non_native_row_major_matrix(uint32_t id); - virtual bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index); - bool member_is_remapped_physical_type(const SPIRType &type, uint32_t index) const; - bool member_is_packed_physical_type(const SPIRType &type, uint32_t index) const; - virtual std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type, - uint32_t physical_type_id, bool is_packed, - bool relaxed = false); - - std::unordered_set local_variable_names; - std::unordered_set resource_names; - std::unordered_set block_input_names; - std::unordered_set block_output_names; - std::unordered_set block_ubo_names; - std::unordered_set block_ssbo_names; - std::unordered_set block_names; // A union of all block_*_names. - std::unordered_map> function_overloads; - std::unordered_map preserved_aliases; - void preserve_alias_on_reset(uint32_t id); - void reset_name_caches(); - - bool processing_entry_point = false; - - // Can be overriden by subclass backends for trivial things which - // shouldn't need polymorphism. - struct BackendVariations - { - std::string discard_literal = "discard"; - std::string demote_literal = "demote"; - std::string null_pointer_literal = ""; - bool float_literal_suffix = false; - bool double_literal_suffix = true; - bool uint32_t_literal_suffix = true; - bool long_long_literal_suffix = false; - const char *basic_int_type = "int"; - const char *basic_uint_type = "uint"; - const char *basic_int8_type = "int8_t"; - const char *basic_uint8_type = "uint8_t"; - const char *basic_int16_type = "int16_t"; - const char *basic_uint16_type = "uint16_t"; - const char *int16_t_literal_suffix = "s"; - const char *uint16_t_literal_suffix = "us"; - const char *nonuniform_qualifier = "nonuniformEXT"; - const char *boolean_mix_function = "mix"; - SPIRType::BaseType boolean_in_struct_remapped_type = SPIRType::Boolean; - bool swizzle_is_function = false; - bool shared_is_implied = false; - bool unsized_array_supported = true; - bool explicit_struct_type = false; - bool use_initializer_list = false; - bool use_typed_initializer_list = false; - bool can_declare_struct_inline = true; - bool can_declare_arrays_inline = true; - bool native_row_major_matrix = true; - bool use_constructor_splatting = true; - bool allow_precision_qualifiers = false; - bool can_swizzle_scalar = false; - bool force_gl_in_out_block = false; - bool force_merged_mesh_block = false; - bool can_return_array = true; - bool allow_truncated_access_chain = false; - bool supports_extensions = false; - bool supports_empty_struct = false; - bool array_is_value_type = true; - bool array_is_value_type_in_buffer_blocks = true; - bool comparison_image_samples_scalar = false; - bool native_pointers = false; - bool support_small_type_sampling_result = false; - bool support_case_fallthrough = true; - bool use_array_constructor = false; - bool needs_row_major_load_workaround = false; - bool support_pointer_to_pointer = false; - bool support_precise_qualifier = false; - bool support_64bit_switch = false; - bool workgroup_size_is_hidden = false; - bool requires_relaxed_precision_analysis = false; - bool implicit_c_integer_promotion_rules = false; - } backend; - - void emit_struct(SPIRType &type); - void emit_resources(); - void emit_extension_workarounds(spv::ExecutionModel model); - void emit_subgroup_arithmetic_workaround(const std::string &func, spv::Op op, spv::GroupOperation group_op); - void emit_polyfills(uint32_t polyfills, bool relaxed); - void emit_buffer_block_native(const SPIRVariable &var); - void emit_buffer_reference_block(uint32_t type_id, bool forward_declaration); - void emit_buffer_block_legacy(const SPIRVariable &var); - void emit_buffer_block_flattened(const SPIRVariable &type); - void fixup_implicit_builtin_block_names(spv::ExecutionModel model); - void emit_declared_builtin_block(spv::StorageClass storage, spv::ExecutionModel model); - bool should_force_emit_builtin_block(spv::StorageClass storage); - void emit_push_constant_block_vulkan(const SPIRVariable &var); - void emit_push_constant_block_glsl(const SPIRVariable &var); - void emit_interface_block(const SPIRVariable &type); - void emit_flattened_io_block(const SPIRVariable &var, const char *qual); - void emit_flattened_io_block_struct(const std::string &basename, const SPIRType &type, const char *qual, - const SmallVector &indices); - void emit_flattened_io_block_member(const std::string &basename, const SPIRType &type, const char *qual, - const SmallVector &indices); - void emit_block_chain(SPIRBlock &block); - void emit_hoisted_temporaries(SmallVector> &temporaries); - std::string constant_value_macro_name(uint32_t id); - int get_constant_mapping_to_workgroup_component(const SPIRConstant &constant) const; - void emit_constant(const SPIRConstant &constant); - void emit_specialization_constant_op(const SPIRConstantOp &constant); - std::string emit_continue_block(uint32_t continue_block, bool follow_true_block, bool follow_false_block); - bool attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method method); - - void branch(BlockID from, BlockID to); - void branch_to_continue(BlockID from, BlockID to); - void branch(BlockID from, uint32_t cond, BlockID true_block, BlockID false_block); - void flush_phi(BlockID from, BlockID to); - void flush_variable_declaration(uint32_t id); - void flush_undeclared_variables(SPIRBlock &block); - void emit_variable_temporary_copies(const SPIRVariable &var); - - bool should_dereference(uint32_t id); - bool should_forward(uint32_t id) const; - bool should_suppress_usage_tracking(uint32_t id) const; - void emit_mix_op(uint32_t result_type, uint32_t id, uint32_t left, uint32_t right, uint32_t lerp); - void emit_nminmax_op(uint32_t result_type, uint32_t id, uint32_t op0, uint32_t op1, GLSLstd450 op); - void emit_emulated_ahyper_op(uint32_t result_type, uint32_t result_id, uint32_t op0, GLSLstd450 op); - bool to_trivial_mix_op(const SPIRType &type, std::string &op, uint32_t left, uint32_t right, uint32_t lerp); - void emit_quaternary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, - uint32_t op3, const char *op); - void emit_trinary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, - const char *op); - void emit_binary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); - void emit_atomic_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); - void emit_atomic_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, const char *op); - - void emit_unary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op, - SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type); - void emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op, - SPIRType::BaseType input_type, bool skip_cast_if_equal_type); - void emit_binary_func_op_cast_clustered(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, - const char *op, SPIRType::BaseType input_type); - void emit_trinary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, - const char *op, SPIRType::BaseType input_type); - void emit_trinary_func_op_bitextract(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, - uint32_t op2, const char *op, SPIRType::BaseType expected_result_type, - SPIRType::BaseType input_type0, SPIRType::BaseType input_type1, - SPIRType::BaseType input_type2); - void emit_bitfield_insert_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, - uint32_t op3, const char *op, SPIRType::BaseType offset_count_type); - - void emit_unary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op); - void emit_unrolled_unary_op(uint32_t result_type, uint32_t result_id, uint32_t operand, const char *op); - void emit_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); - void emit_unrolled_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op, - bool negate, SPIRType::BaseType expected_type); - void emit_binary_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op, - SPIRType::BaseType input_type, bool skip_cast_if_equal_type, bool implicit_integer_promotion); - - SPIRType binary_op_bitcast_helper(std::string &cast_op0, std::string &cast_op1, SPIRType::BaseType &input_type, - uint32_t op0, uint32_t op1, bool skip_cast_if_equal_type); - - virtual bool emit_complex_bitcast(uint32_t result_type, uint32_t id, uint32_t op0); - - std::string to_ternary_expression(const SPIRType &result_type, uint32_t select, uint32_t true_value, - uint32_t false_value); - - void emit_unary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op); - void emit_unary_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op); - virtual void emit_mesh_tasks(SPIRBlock &block); - bool expression_is_forwarded(uint32_t id) const; - bool expression_suppresses_usage_tracking(uint32_t id) const; - bool expression_read_implies_multiple_reads(uint32_t id) const; - SPIRExpression &emit_op(uint32_t result_type, uint32_t result_id, const std::string &rhs, bool forward_rhs, - bool suppress_usage_tracking = false); - - void access_chain_internal_append_index(std::string &expr, uint32_t base, const SPIRType *type, - AccessChainFlags flags, bool &access_chain_is_arrayed, uint32_t index); - - std::string access_chain_internal(uint32_t base, const uint32_t *indices, uint32_t count, AccessChainFlags flags, - AccessChainMeta *meta); - - // Only meaningful on backends with physical pointer support ala MSL. - // Relevant for PtrAccessChain / BDA. - virtual uint32_t get_physical_type_stride(const SPIRType &type) const; - - spv::StorageClass get_expression_effective_storage_class(uint32_t ptr); - virtual bool access_chain_needs_stage_io_builtin_translation(uint32_t base); - - virtual void check_physical_type_cast(std::string &expr, const SPIRType *type, uint32_t physical_type); - virtual bool prepare_access_chain_for_scalar_access(std::string &expr, const SPIRType &type, - spv::StorageClass storage, bool &is_packed); - - std::string access_chain(uint32_t base, const uint32_t *indices, uint32_t count, const SPIRType &target_type, - AccessChainMeta *meta = nullptr, bool ptr_chain = false); - - std::string flattened_access_chain(uint32_t base, const uint32_t *indices, uint32_t count, - const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride, - uint32_t array_stride, bool need_transpose); - std::string flattened_access_chain_struct(uint32_t base, const uint32_t *indices, uint32_t count, - const SPIRType &target_type, uint32_t offset); - std::string flattened_access_chain_matrix(uint32_t base, const uint32_t *indices, uint32_t count, - const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride, - bool need_transpose); - std::string flattened_access_chain_vector(uint32_t base, const uint32_t *indices, uint32_t count, - const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride, - bool need_transpose); - std::pair flattened_access_chain_offset(const SPIRType &basetype, const uint32_t *indices, - uint32_t count, uint32_t offset, - uint32_t word_stride, bool *need_transpose = nullptr, - uint32_t *matrix_stride = nullptr, - uint32_t *array_stride = nullptr, - bool ptr_chain = false); - - const char *index_to_swizzle(uint32_t index); - std::string remap_swizzle(const SPIRType &result_type, uint32_t input_components, const std::string &expr); - std::string declare_temporary(uint32_t type, uint32_t id); - void emit_uninitialized_temporary(uint32_t type, uint32_t id); - SPIRExpression &emit_uninitialized_temporary_expression(uint32_t type, uint32_t id); - void append_global_func_args(const SPIRFunction &func, uint32_t index, SmallVector &arglist); - std::string to_non_uniform_aware_expression(uint32_t id); - std::string to_expression(uint32_t id, bool register_expression_read = true); - std::string to_composite_constructor_expression(const SPIRType &parent_type, uint32_t id, bool block_like_type); - std::string to_rerolled_array_expression(const SPIRType &parent_type, const std::string &expr, const SPIRType &type); - std::string to_enclosed_expression(uint32_t id, bool register_expression_read = true); - std::string to_unpacked_expression(uint32_t id, bool register_expression_read = true); - std::string to_unpacked_row_major_matrix_expression(uint32_t id); - std::string to_enclosed_unpacked_expression(uint32_t id, bool register_expression_read = true); - std::string to_dereferenced_expression(uint32_t id, bool register_expression_read = true); - std::string to_pointer_expression(uint32_t id, bool register_expression_read = true); - std::string to_enclosed_pointer_expression(uint32_t id, bool register_expression_read = true); - std::string to_extract_component_expression(uint32_t id, uint32_t index); - std::string to_extract_constant_composite_expression(uint32_t result_type, const SPIRConstant &c, - const uint32_t *chain, uint32_t length); - static bool needs_enclose_expression(const std::string &expr); - std::string enclose_expression(const std::string &expr); - std::string dereference_expression(const SPIRType &expression_type, const std::string &expr); - std::string address_of_expression(const std::string &expr); - void strip_enclosed_expression(std::string &expr); - std::string to_member_name(const SPIRType &type, uint32_t index); - virtual std::string to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain_is_resolved); - std::string to_multi_member_reference(const SPIRType &type, const SmallVector &indices); - std::string type_to_glsl_constructor(const SPIRType &type); - std::string argument_decl(const SPIRFunction::Parameter &arg); - virtual std::string to_qualifiers_glsl(uint32_t id); - void fixup_io_block_patch_primitive_qualifiers(const SPIRVariable &var); - void emit_output_variable_initializer(const SPIRVariable &var); - std::string to_precision_qualifiers_glsl(uint32_t id); - virtual const char *to_storage_qualifiers_glsl(const SPIRVariable &var); - std::string flags_to_qualifiers_glsl(const SPIRType &type, const Bitset &flags); - const char *format_to_glsl(spv::ImageFormat format); - virtual std::string layout_for_member(const SPIRType &type, uint32_t index); - virtual std::string to_interpolation_qualifiers(const Bitset &flags); - std::string layout_for_variable(const SPIRVariable &variable); - std::string to_combined_image_sampler(VariableID image_id, VariableID samp_id); - virtual bool skip_argument(uint32_t id) const; - virtual bool emit_array_copy(const char *expr, uint32_t lhs_id, uint32_t rhs_id, - spv::StorageClass lhs_storage, spv::StorageClass rhs_storage); - virtual void emit_block_hints(const SPIRBlock &block); - virtual std::string to_initializer_expression(const SPIRVariable &var); - virtual std::string to_zero_initialized_expression(uint32_t type_id); - bool type_can_zero_initialize(const SPIRType &type) const; - - bool buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing, - uint32_t *failed_index = nullptr, uint32_t start_offset = 0, - uint32_t end_offset = ~(0u)); - std::string buffer_to_packing_standard(const SPIRType &type, - bool support_std430_without_scalar_layout, - bool support_enhanced_layouts); - - uint32_t type_to_packed_base_size(const SPIRType &type, BufferPackingStandard packing); - uint32_t type_to_packed_alignment(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing); - uint32_t type_to_packed_array_stride(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing); - uint32_t type_to_packed_size(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing); - uint32_t type_to_location_count(const SPIRType &type) const; - - std::string bitcast_glsl(const SPIRType &result_type, uint32_t arg); - virtual std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type); - - std::string bitcast_expression(SPIRType::BaseType target_type, uint32_t arg); - std::string bitcast_expression(const SPIRType &target_type, SPIRType::BaseType expr_type, const std::string &expr); - - std::string build_composite_combiner(uint32_t result_type, const uint32_t *elems, uint32_t length); - bool remove_duplicate_swizzle(std::string &op); - bool remove_unity_swizzle(uint32_t base, std::string &op); - - // Can modify flags to remote readonly/writeonly if image type - // and force recompile. - bool check_atomic_image(uint32_t id); - - virtual void replace_illegal_names(); - void replace_illegal_names(const std::unordered_set &keywords); - virtual void emit_entry_point_declarations(); - - void replace_fragment_output(SPIRVariable &var); - void replace_fragment_outputs(); - std::string legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t id); - - void forward_relaxed_precision(uint32_t dst_id, const uint32_t *args, uint32_t length); - void analyze_precision_requirements(uint32_t type_id, uint32_t dst_id, uint32_t *args, uint32_t length); - Options::Precision analyze_expression_precision(const uint32_t *args, uint32_t length) const; - - uint32_t indent = 0; - - std::unordered_set emitted_functions; - - // Ensure that we declare phi-variable copies even if the original declaration isn't deferred - std::unordered_set flushed_phi_variables; - - std::unordered_set flattened_buffer_blocks; - std::unordered_map flattened_structs; - - ShaderSubgroupSupportHelper shader_subgroup_supporter; - - std::string load_flattened_struct(const std::string &basename, const SPIRType &type); - std::string to_flattened_struct_member(const std::string &basename, const SPIRType &type, uint32_t index); - void store_flattened_struct(uint32_t lhs_id, uint32_t value); - void store_flattened_struct(const std::string &basename, uint32_t rhs, const SPIRType &type, - const SmallVector &indices); - std::string to_flattened_access_chain_expression(uint32_t id); - - // Usage tracking. If a temporary is used more than once, use the temporary instead to - // avoid AST explosion when SPIRV is generated with pure SSA and doesn't write stuff to variables. - std::unordered_map expression_usage_counts; - void track_expression_read(uint32_t id); - - SmallVector forced_extensions; - SmallVector header_lines; - - // Used when expressions emit extra opcodes with their own unique IDs, - // and we need to reuse the IDs across recompilation loops. - // Currently used by NMin/Max/Clamp implementations. - std::unordered_map extra_sub_expressions; - - SmallVector workaround_ubo_load_overload_types; - void request_workaround_wrapper_overload(TypeID id); - void rewrite_load_for_wrapped_row_major(std::string &expr, TypeID loaded_type, ID ptr); - - uint32_t statement_count = 0; - - inline bool is_legacy() const - { - return (options.es && options.version < 300) || (!options.es && options.version < 130); - } - - inline bool is_legacy_es() const - { - return options.es && options.version < 300; - } - - inline bool is_legacy_desktop() const - { - return !options.es && options.version < 130; - } - - enum Polyfill : uint32_t - { - PolyfillTranspose2x2 = 1 << 0, - PolyfillTranspose3x3 = 1 << 1, - PolyfillTranspose4x4 = 1 << 2, - PolyfillDeterminant2x2 = 1 << 3, - PolyfillDeterminant3x3 = 1 << 4, - PolyfillDeterminant4x4 = 1 << 5, - PolyfillMatrixInverse2x2 = 1 << 6, - PolyfillMatrixInverse3x3 = 1 << 7, - PolyfillMatrixInverse4x4 = 1 << 8, - PolyfillNMin16 = 1 << 9, - PolyfillNMin32 = 1 << 10, - PolyfillNMin64 = 1 << 11, - PolyfillNMax16 = 1 << 12, - PolyfillNMax32 = 1 << 13, - PolyfillNMax64 = 1 << 14, - PolyfillNClamp16 = 1 << 15, - PolyfillNClamp32 = 1 << 16, - PolyfillNClamp64 = 1 << 17, - }; - - uint32_t required_polyfills = 0; - uint32_t required_polyfills_relaxed = 0; - void require_polyfill(Polyfill polyfill, bool relaxed); - - bool ray_tracing_is_khr = false; - bool barycentric_is_nv = false; - void ray_tracing_khr_fixup_locations(); - - bool args_will_forward(uint32_t id, const uint32_t *args, uint32_t num_args, bool pure); - void register_call_out_argument(uint32_t id); - void register_impure_function_call(); - void register_control_dependent_expression(uint32_t expr); - - // GL_EXT_shader_pixel_local_storage support. - std::vector pls_inputs; - std::vector pls_outputs; - std::string pls_decl(const PlsRemap &variable); - const char *to_pls_qualifiers_glsl(const SPIRVariable &variable); - void emit_pls(); - void remap_pls_variables(); - - // GL_EXT_shader_framebuffer_fetch support. - std::vector> subpass_to_framebuffer_fetch_attachment; - std::vector> inout_color_attachments; - bool location_is_framebuffer_fetch(uint32_t location) const; - bool location_is_non_coherent_framebuffer_fetch(uint32_t location) const; - bool subpass_input_is_framebuffer_fetch(uint32_t id) const; - void emit_inout_fragment_outputs_copy_to_subpass_inputs(); - const SPIRVariable *find_subpass_input_by_attachment_index(uint32_t index) const; - const SPIRVariable *find_color_output_by_location(uint32_t location) const; - - // A variant which takes two sets of name. The secondary is only used to verify there are no collisions, - // but the set is not updated when we have found a new name. - // Used primarily when adding block interface names. - void add_variable(std::unordered_set &variables_primary, - const std::unordered_set &variables_secondary, std::string &name); - - void check_function_call_constraints(const uint32_t *args, uint32_t length); - void handle_invalid_expression(uint32_t id); - void force_temporary_and_recompile(uint32_t id); - void find_static_extensions(); - - uint32_t consume_temporary_in_precision_context(uint32_t type_id, uint32_t id, Options::Precision precision); - std::unordered_map temporary_to_mirror_precision_alias; - std::unordered_set composite_insert_overwritten; - std::unordered_set block_composite_insert_overwrite; - - std::string emit_for_loop_initializers(const SPIRBlock &block); - void emit_while_loop_initializers(const SPIRBlock &block); - bool for_loop_initializers_are_same_type(const SPIRBlock &block); - bool optimize_read_modify_write(const SPIRType &type, const std::string &lhs, const std::string &rhs); - void fixup_image_load_store_access(); - - bool type_is_empty(const SPIRType &type); - - bool can_use_io_location(spv::StorageClass storage, bool block); - const Instruction *get_next_instruction_in_block(const Instruction &instr); - static uint32_t mask_relevant_memory_semantics(uint32_t semantics); - - std::string convert_half_to_string(const SPIRConstant &value, uint32_t col, uint32_t row); - std::string convert_float_to_string(const SPIRConstant &value, uint32_t col, uint32_t row); - std::string convert_double_to_string(const SPIRConstant &value, uint32_t col, uint32_t row); - - std::string convert_separate_image_to_expression(uint32_t id); - - // Builtins in GLSL are always specific signedness, but the SPIR-V can declare them - // as either unsigned or signed. - // Sometimes we will need to automatically perform casts on load and store to make this work. - virtual SPIRType::BaseType get_builtin_basetype(spv::BuiltIn builtin, SPIRType::BaseType default_type); - virtual void cast_to_variable_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type); - virtual void cast_from_variable_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type); - void unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr); - bool unroll_array_to_complex_store(uint32_t target_id, uint32_t source_id); - void convert_non_uniform_expression(std::string &expr, uint32_t ptr_id); - - void handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id); - void disallow_forwarding_in_expression_chain(const SPIRExpression &expr); - - bool expression_is_constant_null(uint32_t id) const; - bool expression_is_non_value_type_array(uint32_t ptr); - virtual void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression); - - uint32_t get_integer_width_for_instruction(const Instruction &instr) const; - uint32_t get_integer_width_for_glsl_instruction(GLSLstd450 op, const uint32_t *arguments, uint32_t length) const; - - bool variable_is_lut(const SPIRVariable &var) const; - - char current_locale_radix_character = '.'; - - void fixup_type_alias(); - void reorder_type_alias(); - void fixup_anonymous_struct_names(); - void fixup_anonymous_struct_names(std::unordered_set &visited, const SPIRType &type); - - static const char *vector_swizzle(int vecsize, int index); - - bool is_stage_output_location_masked(uint32_t location, uint32_t component) const; - bool is_stage_output_builtin_masked(spv::BuiltIn builtin) const; - bool is_stage_output_variable_masked(const SPIRVariable &var) const; - bool is_stage_output_block_member_masked(const SPIRVariable &var, uint32_t index, bool strip_array) const; - bool is_per_primitive_variable(const SPIRVariable &var) const; - uint32_t get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array) const; - uint32_t get_declared_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array) const; - std::unordered_set masked_output_locations; - std::unordered_set masked_output_builtins; - - FloatFormatter *float_formatter = nullptr; - std::string format_float(float value) const; - std::string format_double(double value) const; + struct ShaderSubgroupSupportHelper + { + // lower enum value = greater priority + enum Candidate + { + KHR_shader_subgroup_ballot, + KHR_shader_subgroup_basic, + KHR_shader_subgroup_vote, + KHR_shader_subgroup_arithmetic, + NV_gpu_shader_5, + NV_shader_thread_group, + NV_shader_thread_shuffle, + ARB_shader_ballot, + ARB_shader_group_vote, + AMD_gcn_shader, + + CandidateCount + }; + + static const char *get_extension_name(Candidate c); + static SmallVector get_extra_required_extension_names(Candidate c); + static const char *get_extra_required_extension_predicate(Candidate c); + + enum Feature + { + SubgroupMask = 0, + SubgroupSize = 1, + SubgroupInvocationID = 2, + SubgroupID = 3, + NumSubgroups = 4, + SubgroupBroadcast_First = 5, + SubgroupBallotFindLSB_MSB = 6, + SubgroupAll_Any_AllEqualBool = 7, + SubgroupAllEqualT = 8, + SubgroupElect = 9, + SubgroupBarrier = 10, + SubgroupMemBarrier = 11, + SubgroupBallot = 12, + SubgroupInverseBallot_InclBitCount_ExclBitCout = 13, + SubgroupBallotBitExtract = 14, + SubgroupBallotBitCount = 15, + SubgroupArithmeticIAddReduce = 16, + SubgroupArithmeticIAddExclusiveScan = 17, + SubgroupArithmeticIAddInclusiveScan = 18, + SubgroupArithmeticFAddReduce = 19, + SubgroupArithmeticFAddExclusiveScan = 20, + SubgroupArithmeticFAddInclusiveScan = 21, + SubgroupArithmeticIMulReduce = 22, + SubgroupArithmeticIMulExclusiveScan = 23, + SubgroupArithmeticIMulInclusiveScan = 24, + SubgroupArithmeticFMulReduce = 25, + SubgroupArithmeticFMulExclusiveScan = 26, + SubgroupArithmeticFMulInclusiveScan = 27, + FeatureCount + }; + + using FeatureMask = uint32_t; + static_assert(sizeof(FeatureMask) * 8u >= FeatureCount, "Mask type needs more bits."); + + using CandidateVector = SmallVector; + using FeatureVector = SmallVector; + + static FeatureVector get_feature_dependencies(Feature feature); + static FeatureMask get_feature_dependency_mask(Feature feature); + static bool can_feature_be_implemented_without_extensions(Feature feature); + static Candidate get_KHR_extension_for_feature(Feature feature); + + struct Result + { + Result(); + uint32_t weights[CandidateCount]; + }; + + void request_feature(Feature feature); + bool is_feature_requested(Feature feature) const; + Result resolve() const; + + static CandidateVector get_candidates_for_feature(Feature ft, const Result &r); + + private: + static CandidateVector get_candidates_for_feature(Feature ft); + static FeatureMask build_mask(const SmallVector &features); + FeatureMask feature_mask = 0; + }; + + // TODO remove this function when all subgroup ops are supported (or make it always return true) + static bool is_supported_subgroup_op_in_opengl(spv::Op op, const uint32_t *ops); + + void reset(uint32_t iteration_count); + void emit_function(SPIRFunction &func, const Bitset &return_flags); + + bool has_extension(const std::string &ext) const; + void require_extension_internal(const std::string &ext); + + // Virtualize methods which need to be overridden by subclass targets like C++ and such. + virtual void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags); + + SPIRBlock *current_emitting_block = nullptr; + SmallVector current_emitting_switch_stack; + bool current_emitting_switch_fallthrough = false; + + virtual void emit_instruction(const Instruction &instr); + struct TemporaryCopy + { + uint32_t dst_id; + uint32_t src_id; + }; + TemporaryCopy handle_instruction_precision(const Instruction &instr); + void emit_block_instructions(SPIRBlock &block); + void emit_block_instructions_with_masked_debug(SPIRBlock &block); + + // For relax_nan_checks. + GLSLstd450 get_remapped_glsl_op(GLSLstd450 std450_op) const; + spv::Op get_remapped_spirv_op(spv::Op op) const; + + virtual void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, + uint32_t count); + virtual void emit_spv_amd_shader_ballot_op(uint32_t result_type, uint32_t result_id, uint32_t op, + const uint32_t *args, uint32_t count); + virtual void emit_spv_amd_shader_explicit_vertex_parameter_op(uint32_t result_type, uint32_t result_id, uint32_t op, + const uint32_t *args, uint32_t count); + virtual void emit_spv_amd_shader_trinary_minmax_op(uint32_t result_type, uint32_t result_id, uint32_t op, + const uint32_t *args, uint32_t count); + virtual void emit_spv_amd_gcn_shader_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, + uint32_t count); + virtual void emit_header(); + void emit_line_directive(uint32_t file_id, uint32_t line_literal); + void build_workgroup_size(SmallVector &arguments, const SpecializationConstant &x, + const SpecializationConstant &y, const SpecializationConstant &z); + + void request_subgroup_feature(ShaderSubgroupSupportHelper::Feature feature); + + virtual void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id); + virtual void emit_texture_op(const Instruction &i, bool sparse); + virtual std::string to_texture_op(const Instruction &i, bool sparse, bool *forward, + SmallVector &inherited_expressions); + virtual void emit_subgroup_op(const Instruction &i); + virtual std::string type_to_glsl(const SPIRType &type, uint32_t id = 0); + virtual std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage); + virtual void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, + const std::string &qualifier = "", uint32_t base_offset = 0); + virtual void emit_struct_padding_target(const SPIRType &type); + virtual std::string image_type_glsl(const SPIRType &type, uint32_t id = 0, bool member = false); + std::string constant_expression(const SPIRConstant &c, + bool inside_block_like_struct_scope = false, + bool inside_struct_scope = false); + virtual std::string constant_op_expression(const SPIRConstantOp &cop); + virtual std::string constant_expression_vector(const SPIRConstant &c, uint32_t vector); + virtual void emit_fixup(); + virtual std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0); + virtual bool variable_decl_is_remapped_storage(const SPIRVariable &var, spv::StorageClass storage) const; + virtual std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id); + + struct TextureFunctionBaseArguments + { + // GCC 4.8 workarounds, it doesn't understand '{}' constructor here, use explicit default constructor. + TextureFunctionBaseArguments() = default; + VariableID img = 0; + const SPIRType *imgtype = nullptr; + bool is_fetch = false, is_gather = false, is_proj = false; + }; + + struct TextureFunctionNameArguments + { + // GCC 4.8 workarounds, it doesn't understand '{}' constructor here, use explicit default constructor. + TextureFunctionNameArguments() = default; + TextureFunctionBaseArguments base; + bool has_array_offsets = false, has_offset = false, has_grad = false; + bool has_dref = false, is_sparse_feedback = false, has_min_lod = false; + uint32_t lod = 0; + }; + virtual std::string to_function_name(const TextureFunctionNameArguments &args); + + struct TextureFunctionArguments + { + // GCC 4.8 workarounds, it doesn't understand '{}' constructor here, use explicit default constructor. + TextureFunctionArguments() = default; + TextureFunctionBaseArguments base; + uint32_t coord = 0, coord_components = 0, dref = 0; + uint32_t grad_x = 0, grad_y = 0, lod = 0, offset = 0; + uint32_t bias = 0, component = 0, sample = 0, sparse_texel = 0, min_lod = 0; + bool nonuniform_expression = false, has_array_offsets = false; + }; + virtual std::string to_function_args(const TextureFunctionArguments &args, bool *p_forward); + + void emit_sparse_feedback_temporaries(uint32_t result_type_id, uint32_t id, uint32_t &feedback_id, + uint32_t &texel_id); + uint32_t get_sparse_feedback_texel_id(uint32_t id) const; + virtual void emit_buffer_block(const SPIRVariable &type); + virtual void emit_push_constant_block(const SPIRVariable &var); + virtual void emit_uniform(const SPIRVariable &var); + virtual std::string unpack_expression_type(std::string expr_str, const SPIRType &type, uint32_t physical_type_id, + bool packed_type, bool row_major); + + virtual bool builtin_translates_to_nonarray(spv::BuiltIn builtin) const; + + virtual bool is_user_type_structured(uint32_t id) const; + + void emit_copy_logical_type(uint32_t lhs_id, uint32_t lhs_type_id, uint32_t rhs_id, uint32_t rhs_type_id, + SmallVector chain); + + StringStream<> buffer; + + template + inline void statement_inner(T &&t) + { + buffer << std::forward(t); + statement_count++; + } + + template + inline void statement_inner(T &&t, Ts &&... ts) + { + buffer << std::forward(t); + statement_count++; + statement_inner(std::forward(ts)...); + } + + template + inline void statement(Ts &&... ts) + { + if (is_forcing_recompilation()) + { + // Do not bother emitting code while force_recompile is active. + // We will compile again. + statement_count++; + return; + } + + if (redirect_statement) + { + redirect_statement->push_back(join(std::forward(ts)...)); + statement_count++; + } + else + { + for (uint32_t i = 0; i < indent; i++) + buffer << " "; + statement_inner(std::forward(ts)...); + buffer << '\n'; + } + } + + template + inline void statement_no_indent(Ts &&... ts) + { + auto old_indent = indent; + indent = 0; + statement(std::forward(ts)...); + indent = old_indent; + } + + // Used for implementing continue blocks where + // we want to obtain a list of statements we can merge + // on a single line separated by comma. + SmallVector *redirect_statement = nullptr; + const SPIRBlock *current_continue_block = nullptr; + bool block_temporary_hoisting = false; + bool block_debug_directives = false; + + void begin_scope(); + void end_scope(); + void end_scope(const std::string &trailer); + void end_scope_decl(); + void end_scope_decl(const std::string &decl); + + Options options; + + // Allow Metal to use the array template to make arrays a value type + virtual std::string type_to_array_glsl(const SPIRType &type, uint32_t variable_id); + std::string to_array_size(const SPIRType &type, uint32_t index); + uint32_t to_array_size_literal(const SPIRType &type, uint32_t index) const; + uint32_t to_array_size_literal(const SPIRType &type) const; + virtual std::string variable_decl(const SPIRVariable &variable); // Threadgroup arrays can't have a wrapper type + std::string variable_decl_function_local(SPIRVariable &variable); + + void add_local_variable_name(uint32_t id); + void add_resource_name(uint32_t id); + void add_member_name(SPIRType &type, uint32_t name); + void add_function_overload(const SPIRFunction &func); + + virtual bool is_non_native_row_major_matrix(uint32_t id); + virtual bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index); + bool member_is_remapped_physical_type(const SPIRType &type, uint32_t index) const; + bool member_is_packed_physical_type(const SPIRType &type, uint32_t index) const; + virtual std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type, + uint32_t physical_type_id, bool is_packed, + bool relaxed = false); + + std::unordered_set local_variable_names; + std::unordered_set resource_names; + std::unordered_set block_input_names; + std::unordered_set block_output_names; + std::unordered_set block_ubo_names; + std::unordered_set block_ssbo_names; + std::unordered_set block_names; // A union of all block_*_names. + std::unordered_map> function_overloads; + std::unordered_map preserved_aliases; + void preserve_alias_on_reset(uint32_t id); + void reset_name_caches(); + + bool processing_entry_point = false; + + // Can be overriden by subclass backends for trivial things which + // shouldn't need polymorphism. + struct BackendVariations + { + std::string discard_literal = "discard"; + std::string demote_literal = "demote"; + std::string null_pointer_literal = ""; + bool float_literal_suffix = false; + bool double_literal_suffix = true; + bool uint32_t_literal_suffix = true; + bool long_long_literal_suffix = false; + const char *basic_int_type = "int"; + const char *basic_uint_type = "uint"; + const char *basic_int8_type = "int8_t"; + const char *basic_uint8_type = "uint8_t"; + const char *basic_int16_type = "int16_t"; + const char *basic_uint16_type = "uint16_t"; + const char *int16_t_literal_suffix = "s"; + const char *uint16_t_literal_suffix = "us"; + const char *nonuniform_qualifier = "nonuniformEXT"; + const char *boolean_mix_function = "mix"; + SPIRType::BaseType boolean_in_struct_remapped_type = SPIRType::Boolean; + bool swizzle_is_function = false; + bool shared_is_implied = false; + bool unsized_array_supported = true; + bool explicit_struct_type = false; + bool use_initializer_list = false; + bool use_typed_initializer_list = false; + bool can_declare_struct_inline = true; + bool can_declare_arrays_inline = true; + bool native_row_major_matrix = true; + bool use_constructor_splatting = true; + bool allow_precision_qualifiers = false; + bool can_swizzle_scalar = false; + bool force_gl_in_out_block = false; + bool force_merged_mesh_block = false; + bool can_return_array = true; + bool allow_truncated_access_chain = false; + bool supports_extensions = false; + bool supports_empty_struct = false; + bool array_is_value_type = true; + bool array_is_value_type_in_buffer_blocks = true; + bool comparison_image_samples_scalar = false; + bool native_pointers = false; + bool support_small_type_sampling_result = false; + bool support_case_fallthrough = true; + bool use_array_constructor = false; + bool needs_row_major_load_workaround = false; + bool support_pointer_to_pointer = false; + bool support_precise_qualifier = false; + bool support_64bit_switch = false; + bool workgroup_size_is_hidden = false; + bool requires_relaxed_precision_analysis = false; + bool implicit_c_integer_promotion_rules = false; + } backend; + + void emit_struct(SPIRType &type); + void emit_resources(); + void emit_extension_workarounds(spv::ExecutionModel model); + void emit_subgroup_arithmetic_workaround(const std::string &func, spv::Op op, spv::GroupOperation group_op); + void emit_polyfills(uint32_t polyfills, bool relaxed); + void emit_buffer_block_native(const SPIRVariable &var); + void emit_buffer_reference_block(uint32_t type_id, bool forward_declaration); + void emit_buffer_block_legacy(const SPIRVariable &var); + void emit_buffer_block_flattened(const SPIRVariable &type); + void fixup_implicit_builtin_block_names(spv::ExecutionModel model); + void emit_declared_builtin_block(spv::StorageClass storage, spv::ExecutionModel model); + bool should_force_emit_builtin_block(spv::StorageClass storage); + void emit_push_constant_block_vulkan(const SPIRVariable &var); + void emit_push_constant_block_glsl(const SPIRVariable &var); + void emit_interface_block(const SPIRVariable &type); + void emit_flattened_io_block(const SPIRVariable &var, const char *qual); + void emit_flattened_io_block_struct(const std::string &basename, const SPIRType &type, const char *qual, + const SmallVector &indices); + void emit_flattened_io_block_member(const std::string &basename, const SPIRType &type, const char *qual, + const SmallVector &indices); + void emit_block_chain(SPIRBlock &block); + void emit_hoisted_temporaries(SmallVector> &temporaries); + std::string constant_value_macro_name(uint32_t id); + int get_constant_mapping_to_workgroup_component(const SPIRConstant &constant) const; + void emit_constant(const SPIRConstant &constant); + void emit_specialization_constant_op(const SPIRConstantOp &constant); + std::string emit_continue_block(uint32_t continue_block, bool follow_true_block, bool follow_false_block); + bool attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method method); + + void branch(BlockID from, BlockID to); + void branch_to_continue(BlockID from, BlockID to); + void branch(BlockID from, uint32_t cond, BlockID true_block, BlockID false_block); + void flush_phi(BlockID from, BlockID to); + void flush_variable_declaration(uint32_t id); + void flush_undeclared_variables(SPIRBlock &block); + void emit_variable_temporary_copies(const SPIRVariable &var); + + bool should_dereference(uint32_t id); + bool should_forward(uint32_t id) const; + bool should_suppress_usage_tracking(uint32_t id) const; + void emit_mix_op(uint32_t result_type, uint32_t id, uint32_t left, uint32_t right, uint32_t lerp); + void emit_nminmax_op(uint32_t result_type, uint32_t id, uint32_t op0, uint32_t op1, GLSLstd450 op); + void emit_emulated_ahyper_op(uint32_t result_type, uint32_t result_id, uint32_t op0, GLSLstd450 op); + bool to_trivial_mix_op(const SPIRType &type, std::string &op, uint32_t left, uint32_t right, uint32_t lerp); + void emit_quaternary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, + uint32_t op3, const char *op); + void emit_trinary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, + const char *op); + void emit_binary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); + void emit_atomic_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); + void emit_atomic_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, const char *op); + + void emit_unary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op, + SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type); + void emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op, + SPIRType::BaseType input_type, bool skip_cast_if_equal_type); + void emit_binary_func_op_cast_clustered(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + const char *op, SPIRType::BaseType input_type); + void emit_trinary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, + const char *op, SPIRType::BaseType input_type); + void emit_trinary_func_op_bitextract(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + uint32_t op2, const char *op, SPIRType::BaseType expected_result_type, + SPIRType::BaseType input_type0, SPIRType::BaseType input_type1, + SPIRType::BaseType input_type2); + void emit_bitfield_insert_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, + uint32_t op3, const char *op, SPIRType::BaseType offset_count_type); + + void emit_unary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op); + void emit_unrolled_unary_op(uint32_t result_type, uint32_t result_id, uint32_t operand, const char *op); + void emit_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); + void emit_unrolled_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op, + bool negate, SPIRType::BaseType expected_type); + void emit_binary_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op, + SPIRType::BaseType input_type, bool skip_cast_if_equal_type, bool implicit_integer_promotion); + + SPIRType binary_op_bitcast_helper(std::string &cast_op0, std::string &cast_op1, SPIRType::BaseType &input_type, + uint32_t op0, uint32_t op1, bool skip_cast_if_equal_type); + + virtual bool emit_complex_bitcast(uint32_t result_type, uint32_t id, uint32_t op0); + + std::string to_ternary_expression(const SPIRType &result_type, uint32_t select, uint32_t true_value, + uint32_t false_value); + + void emit_unary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op); + void emit_unary_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op); + virtual void emit_mesh_tasks(SPIRBlock &block); + bool expression_is_forwarded(uint32_t id) const; + bool expression_suppresses_usage_tracking(uint32_t id) const; + bool expression_read_implies_multiple_reads(uint32_t id) const; + SPIRExpression &emit_op(uint32_t result_type, uint32_t result_id, const std::string &rhs, bool forward_rhs, + bool suppress_usage_tracking = false); + + void access_chain_internal_append_index(std::string &expr, uint32_t base, const SPIRType *type, + AccessChainFlags flags, bool &access_chain_is_arrayed, uint32_t index); + + std::string access_chain_internal(uint32_t base, const uint32_t *indices, uint32_t count, AccessChainFlags flags, + AccessChainMeta *meta); + + // Only meaningful on backends with physical pointer support ala MSL. + // Relevant for PtrAccessChain / BDA. + virtual uint32_t get_physical_type_stride(const SPIRType &type) const; + + spv::StorageClass get_expression_effective_storage_class(uint32_t ptr); + virtual bool access_chain_needs_stage_io_builtin_translation(uint32_t base); + + virtual void check_physical_type_cast(std::string &expr, const SPIRType *type, uint32_t physical_type); + virtual bool prepare_access_chain_for_scalar_access(std::string &expr, const SPIRType &type, + spv::StorageClass storage, bool &is_packed); + + std::string access_chain(uint32_t base, const uint32_t *indices, uint32_t count, const SPIRType &target_type, + AccessChainMeta *meta = nullptr, bool ptr_chain = false); + + std::string flattened_access_chain(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride, + uint32_t array_stride, bool need_transpose); + std::string flattened_access_chain_struct(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset); + std::string flattened_access_chain_matrix(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride, + bool need_transpose); + std::string flattened_access_chain_vector(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride, + bool need_transpose); + std::pair flattened_access_chain_offset(const SPIRType &basetype, const uint32_t *indices, + uint32_t count, uint32_t offset, + uint32_t word_stride, bool *need_transpose = nullptr, + uint32_t *matrix_stride = nullptr, + uint32_t *array_stride = nullptr, + bool ptr_chain = false); + + const char *index_to_swizzle(uint32_t index); + std::string remap_swizzle(const SPIRType &result_type, uint32_t input_components, const std::string &expr); + std::string declare_temporary(uint32_t type, uint32_t id); + void emit_uninitialized_temporary(uint32_t type, uint32_t id); + SPIRExpression &emit_uninitialized_temporary_expression(uint32_t type, uint32_t id); + void append_global_func_args(const SPIRFunction &func, uint32_t index, SmallVector &arglist); + std::string to_non_uniform_aware_expression(uint32_t id); + std::string to_expression(uint32_t id, bool register_expression_read = true); + std::string to_composite_constructor_expression(const SPIRType &parent_type, uint32_t id, bool block_like_type); + std::string to_rerolled_array_expression(const SPIRType &parent_type, const std::string &expr, const SPIRType &type); + std::string to_enclosed_expression(uint32_t id, bool register_expression_read = true); + std::string to_unpacked_expression(uint32_t id, bool register_expression_read = true); + std::string to_unpacked_row_major_matrix_expression(uint32_t id); + std::string to_enclosed_unpacked_expression(uint32_t id, bool register_expression_read = true); + std::string to_dereferenced_expression(uint32_t id, bool register_expression_read = true); + std::string to_pointer_expression(uint32_t id, bool register_expression_read = true); + std::string to_enclosed_pointer_expression(uint32_t id, bool register_expression_read = true); + std::string to_extract_component_expression(uint32_t id, uint32_t index); + std::string to_extract_constant_composite_expression(uint32_t result_type, const SPIRConstant &c, + const uint32_t *chain, uint32_t length); + static bool needs_enclose_expression(const std::string &expr); + std::string enclose_expression(const std::string &expr); + std::string dereference_expression(const SPIRType &expression_type, const std::string &expr); + std::string address_of_expression(const std::string &expr); + void strip_enclosed_expression(std::string &expr); + std::string to_member_name(const SPIRType &type, uint32_t index); + virtual std::string to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain_is_resolved); + std::string to_multi_member_reference(const SPIRType &type, const SmallVector &indices); + std::string type_to_glsl_constructor(const SPIRType &type); + std::string argument_decl(const SPIRFunction::Parameter &arg); + virtual std::string to_qualifiers_glsl(uint32_t id); + void fixup_io_block_patch_primitive_qualifiers(const SPIRVariable &var); + void emit_output_variable_initializer(const SPIRVariable &var); + std::string to_precision_qualifiers_glsl(uint32_t id); + virtual const char *to_storage_qualifiers_glsl(const SPIRVariable &var); + std::string flags_to_qualifiers_glsl(const SPIRType &type, const Bitset &flags); + const char *format_to_glsl(spv::ImageFormat format); + virtual std::string layout_for_member(const SPIRType &type, uint32_t index); + virtual std::string to_interpolation_qualifiers(const Bitset &flags); + std::string layout_for_variable(const SPIRVariable &variable); + std::string to_combined_image_sampler(VariableID image_id, VariableID samp_id); + virtual bool skip_argument(uint32_t id) const; + virtual bool emit_array_copy(const char *expr, uint32_t lhs_id, uint32_t rhs_id, + spv::StorageClass lhs_storage, spv::StorageClass rhs_storage); + virtual void emit_block_hints(const SPIRBlock &block); + virtual std::string to_initializer_expression(const SPIRVariable &var); + virtual std::string to_zero_initialized_expression(uint32_t type_id); + bool type_can_zero_initialize(const SPIRType &type) const; + + bool buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing, + uint32_t *failed_index = nullptr, uint32_t start_offset = 0, + uint32_t end_offset = ~(0u)); + std::string buffer_to_packing_standard(const SPIRType &type, + bool support_std430_without_scalar_layout, + bool support_enhanced_layouts); + + uint32_t type_to_packed_base_size(const SPIRType &type, BufferPackingStandard packing); + uint32_t type_to_packed_alignment(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing); + uint32_t type_to_packed_array_stride(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing); + uint32_t type_to_packed_size(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing); + uint32_t type_to_location_count(const SPIRType &type) const; + + std::string bitcast_glsl(const SPIRType &result_type, uint32_t arg); + virtual std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type); + + std::string bitcast_expression(SPIRType::BaseType target_type, uint32_t arg); + std::string bitcast_expression(const SPIRType &target_type, SPIRType::BaseType expr_type, const std::string &expr); + + std::string build_composite_combiner(uint32_t result_type, const uint32_t *elems, uint32_t length); + bool remove_duplicate_swizzle(std::string &op); + bool remove_unity_swizzle(uint32_t base, std::string &op); + + // Can modify flags to remote readonly/writeonly if image type + // and force recompile. + bool check_atomic_image(uint32_t id); + + virtual void replace_illegal_names(); + void replace_illegal_names(const std::unordered_set &keywords); + virtual void emit_entry_point_declarations(); + + void replace_fragment_output(SPIRVariable &var); + void replace_fragment_outputs(); + std::string legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t id); + + void forward_relaxed_precision(uint32_t dst_id, const uint32_t *args, uint32_t length); + void analyze_precision_requirements(uint32_t type_id, uint32_t dst_id, uint32_t *args, uint32_t length); + Options::Precision analyze_expression_precision(const uint32_t *args, uint32_t length) const; + + uint32_t indent = 0; + + std::unordered_set emitted_functions; + + // Ensure that we declare phi-variable copies even if the original declaration isn't deferred + std::unordered_set flushed_phi_variables; + + std::unordered_set flattened_buffer_blocks; + std::unordered_map flattened_structs; + + ShaderSubgroupSupportHelper shader_subgroup_supporter; + + std::string load_flattened_struct(const std::string &basename, const SPIRType &type); + std::string to_flattened_struct_member(const std::string &basename, const SPIRType &type, uint32_t index); + void store_flattened_struct(uint32_t lhs_id, uint32_t value); + void store_flattened_struct(const std::string &basename, uint32_t rhs, const SPIRType &type, + const SmallVector &indices); + std::string to_flattened_access_chain_expression(uint32_t id); + + // Usage tracking. If a temporary is used more than once, use the temporary instead to + // avoid AST explosion when SPIRV is generated with pure SSA and doesn't write stuff to variables. + std::unordered_map expression_usage_counts; + void track_expression_read(uint32_t id); + + SmallVector forced_extensions; + SmallVector header_lines; + + // Used when expressions emit extra opcodes with their own unique IDs, + // and we need to reuse the IDs across recompilation loops. + // Currently used by NMin/Max/Clamp implementations. + std::unordered_map extra_sub_expressions; + + SmallVector workaround_ubo_load_overload_types; + void request_workaround_wrapper_overload(TypeID id); + void rewrite_load_for_wrapped_row_major(std::string &expr, TypeID loaded_type, ID ptr); + + uint32_t statement_count = 0; + + inline bool is_legacy() const + { + return (options.es && options.version < 300) || (!options.es && options.version < 130); + } + + inline bool is_legacy_es() const + { + return options.es && options.version < 300; + } + + inline bool is_legacy_desktop() const + { + return !options.es && options.version < 130; + } + + enum Polyfill : uint32_t + { + PolyfillTranspose2x2 = 1 << 0, + PolyfillTranspose3x3 = 1 << 1, + PolyfillTranspose4x4 = 1 << 2, + PolyfillDeterminant2x2 = 1 << 3, + PolyfillDeterminant3x3 = 1 << 4, + PolyfillDeterminant4x4 = 1 << 5, + PolyfillMatrixInverse2x2 = 1 << 6, + PolyfillMatrixInverse3x3 = 1 << 7, + PolyfillMatrixInverse4x4 = 1 << 8, + PolyfillNMin16 = 1 << 9, + PolyfillNMin32 = 1 << 10, + PolyfillNMin64 = 1 << 11, + PolyfillNMax16 = 1 << 12, + PolyfillNMax32 = 1 << 13, + PolyfillNMax64 = 1 << 14, + PolyfillNClamp16 = 1 << 15, + PolyfillNClamp32 = 1 << 16, + PolyfillNClamp64 = 1 << 17, + }; + + uint32_t required_polyfills = 0; + uint32_t required_polyfills_relaxed = 0; + void require_polyfill(Polyfill polyfill, bool relaxed); + + bool ray_tracing_is_khr = false; + bool barycentric_is_nv = false; + void ray_tracing_khr_fixup_locations(); + + bool args_will_forward(uint32_t id, const uint32_t *args, uint32_t num_args, bool pure); + void register_call_out_argument(uint32_t id); + void register_impure_function_call(); + void register_control_dependent_expression(uint32_t expr); + + // GL_EXT_shader_pixel_local_storage support. + std::vector pls_inputs; + std::vector pls_outputs; + std::string pls_decl(const PlsRemap &variable); + const char *to_pls_qualifiers_glsl(const SPIRVariable &variable); + void emit_pls(); + void remap_pls_variables(); + + // GL_EXT_shader_framebuffer_fetch support. + std::vector> subpass_to_framebuffer_fetch_attachment; + std::vector> inout_color_attachments; + bool location_is_framebuffer_fetch(uint32_t location) const; + bool location_is_non_coherent_framebuffer_fetch(uint32_t location) const; + bool subpass_input_is_framebuffer_fetch(uint32_t id) const; + void emit_inout_fragment_outputs_copy_to_subpass_inputs(); + const SPIRVariable *find_subpass_input_by_attachment_index(uint32_t index) const; + const SPIRVariable *find_color_output_by_location(uint32_t location) const; + + // A variant which takes two sets of name. The secondary is only used to verify there are no collisions, + // but the set is not updated when we have found a new name. + // Used primarily when adding block interface names. + void add_variable(std::unordered_set &variables_primary, + const std::unordered_set &variables_secondary, std::string &name); + + void check_function_call_constraints(const uint32_t *args, uint32_t length); + void handle_invalid_expression(uint32_t id); + void force_temporary_and_recompile(uint32_t id); + void find_static_extensions(); + + uint32_t consume_temporary_in_precision_context(uint32_t type_id, uint32_t id, Options::Precision precision); + std::unordered_map temporary_to_mirror_precision_alias; + std::unordered_set composite_insert_overwritten; + std::unordered_set block_composite_insert_overwrite; + + std::string emit_for_loop_initializers(const SPIRBlock &block); + void emit_while_loop_initializers(const SPIRBlock &block); + bool for_loop_initializers_are_same_type(const SPIRBlock &block); + bool optimize_read_modify_write(const SPIRType &type, const std::string &lhs, const std::string &rhs); + void fixup_image_load_store_access(); + + bool type_is_empty(const SPIRType &type); + + bool can_use_io_location(spv::StorageClass storage, bool block); + const Instruction *get_next_instruction_in_block(const Instruction &instr); + static uint32_t mask_relevant_memory_semantics(uint32_t semantics); + + std::string convert_half_to_string(const SPIRConstant &value, uint32_t col, uint32_t row); + std::string convert_float_to_string(const SPIRConstant &value, uint32_t col, uint32_t row); + std::string convert_double_to_string(const SPIRConstant &value, uint32_t col, uint32_t row); + + std::string convert_separate_image_to_expression(uint32_t id); + + // Builtins in GLSL are always specific signedness, but the SPIR-V can declare them + // as either unsigned or signed. + // Sometimes we will need to automatically perform casts on load and store to make this work. + virtual SPIRType::BaseType get_builtin_basetype(spv::BuiltIn builtin, SPIRType::BaseType default_type); + virtual void cast_to_variable_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type); + virtual void cast_from_variable_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type); + void unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr); + bool unroll_array_to_complex_store(uint32_t target_id, uint32_t source_id); + void convert_non_uniform_expression(std::string &expr, uint32_t ptr_id); + + void handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id); + void disallow_forwarding_in_expression_chain(const SPIRExpression &expr); + + bool expression_is_constant_null(uint32_t id) const; + bool expression_is_non_value_type_array(uint32_t ptr); + virtual void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression); + + uint32_t get_integer_width_for_instruction(const Instruction &instr) const; + uint32_t get_integer_width_for_glsl_instruction(GLSLstd450 op, const uint32_t *arguments, uint32_t length) const; + + bool variable_is_lut(const SPIRVariable &var) const; + + char current_locale_radix_character = '.'; + + void fixup_type_alias(); + void reorder_type_alias(); + void fixup_anonymous_struct_names(); + void fixup_anonymous_struct_names(std::unordered_set &visited, const SPIRType &type); + + static const char *vector_swizzle(int vecsize, int index); + + bool is_stage_output_location_masked(uint32_t location, uint32_t component) const; + bool is_stage_output_builtin_masked(spv::BuiltIn builtin) const; + bool is_stage_output_variable_masked(const SPIRVariable &var) const; + bool is_stage_output_block_member_masked(const SPIRVariable &var, uint32_t index, bool strip_array) const; + bool is_per_primitive_variable(const SPIRVariable &var) const; + uint32_t get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array) const; + uint32_t get_declared_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array) const; + std::unordered_set masked_output_locations; + std::unordered_set masked_output_builtins; + + FloatFormatter *float_formatter = nullptr; + std::string format_float(float value) const; + std::string format_double(double value) const; private: - void init(); + void init(); - SmallVector get_composite_constant_ids(ConstantID const_id); - void fill_composite_constant(SPIRConstant &constant, TypeID type_id, const SmallVector &initializers); - void set_composite_constant(ConstantID const_id, TypeID type_id, const SmallVector &initializers); - TypeID get_composite_member_type(TypeID type_id, uint32_t member_idx); - std::unordered_map> const_composite_insert_ids; + SmallVector get_composite_constant_ids(ConstantID const_id); + void fill_composite_constant(SPIRConstant &constant, TypeID type_id, const SmallVector &initializers); + void set_composite_constant(ConstantID const_id, TypeID type_id, const SmallVector &initializers); + TypeID get_composite_member_type(TypeID type_id, uint32_t member_idx); + std::unordered_map> const_composite_insert_ids; }; } // namespace SPIRV_CROSS_NAMESPACE diff --git a/spirv_hlsl.hpp b/spirv_hlsl.hpp index bec458c6..d00b2694 100644 --- a/spirv_hlsl.hpp +++ b/spirv_hlsl.hpp @@ -32,8 +32,8 @@ namespace SPIRV_CROSS_NAMESPACE // Interface which remaps vertex inputs to a fixed semantic name to make linking easier. struct HLSLVertexAttributeRemap { - uint32_t location; - std::string semantic; + uint32_t location; + std::string semantic; }; // Specifying a root constant (d3d12) or push constant range (vulkan). // @@ -41,38 +41,38 @@ struct HLSLVertexAttributeRemap // Both values need to be multiple of 4. struct RootConstants { - uint32_t start; - uint32_t end; + uint32_t start; + uint32_t end; - uint32_t binding; - uint32_t space; + uint32_t binding; + uint32_t space; }; // For finer control, decorations may be removed from specific resources instead with unset_decoration(). enum HLSLBindingFlagBits { - HLSL_BINDING_AUTO_NONE_BIT = 0, + HLSL_BINDING_AUTO_NONE_BIT = 0, - // Push constant (root constant) resources will be declared as CBVs (b-space) without a register() declaration. - // A register will be automatically assigned by the D3D compiler, but must therefore be reflected in D3D-land. - // Push constants do not normally have a DecorationBinding set, but if they do, this can be used to ignore it. - HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT = 1 << 0, + // Push constant (root constant) resources will be declared as CBVs (b-space) without a register() declaration. + // A register will be automatically assigned by the D3D compiler, but must therefore be reflected in D3D-land. + // Push constants do not normally have a DecorationBinding set, but if they do, this can be used to ignore it. + HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT = 1 << 0, - // cbuffer resources will be declared as CBVs (b-space) without a register() declaration. - // A register will be automatically assigned, but must be reflected in D3D-land. - HLSL_BINDING_AUTO_CBV_BIT = 1 << 1, + // cbuffer resources will be declared as CBVs (b-space) without a register() declaration. + // A register will be automatically assigned, but must be reflected in D3D-land. + HLSL_BINDING_AUTO_CBV_BIT = 1 << 1, - // All SRVs (t-space) will be declared without a register() declaration. - HLSL_BINDING_AUTO_SRV_BIT = 1 << 2, + // All SRVs (t-space) will be declared without a register() declaration. + HLSL_BINDING_AUTO_SRV_BIT = 1 << 2, - // All UAVs (u-space) will be declared without a register() declaration. - HLSL_BINDING_AUTO_UAV_BIT = 1 << 3, + // All UAVs (u-space) will be declared without a register() declaration. + HLSL_BINDING_AUTO_UAV_BIT = 1 << 3, - // All samplers (s-space) will be declared without a register() declaration. - HLSL_BINDING_AUTO_SAMPLER_BIT = 1 << 4, + // All samplers (s-space) will be declared without a register() declaration. + HLSL_BINDING_AUTO_SAMPLER_BIT = 1 << 4, - // No resources will be declared with register(). - HLSL_BINDING_AUTO_ALL = 0x7fffffff + // No resources will be declared with register(). + HLSL_BINDING_AUTO_ALL = 0x7fffffff }; using HLSLBindingFlags = uint32_t; @@ -87,327 +87,327 @@ using HLSLBindingFlags = uint32_t; // For deeper control of push constants, set_root_constant_layouts() can be used instead. struct HLSLResourceBinding { - spv::ExecutionModel stage = spv::ExecutionModelMax; - uint32_t desc_set = 0; - uint32_t binding = 0; + spv::ExecutionModel stage = spv::ExecutionModelMax; + uint32_t desc_set = 0; + uint32_t binding = 0; - struct Binding - { - uint32_t register_space = 0; - uint32_t register_binding = 0; - } cbv, uav, srv, sampler; + struct Binding + { + uint32_t register_space = 0; + uint32_t register_binding = 0; + } cbv, uav, srv, sampler; }; enum HLSLAuxBinding { - HLSL_AUX_BINDING_BASE_VERTEX_INSTANCE = 0 + HLSL_AUX_BINDING_BASE_VERTEX_INSTANCE = 0 }; class CompilerHLSL : public CompilerGLSL { public: - struct Options - { - uint32_t shader_model = 30; // TODO: map ps_4_0_level_9_0,... somehow + struct Options + { + uint32_t shader_model = 30; // TODO: map ps_4_0_level_9_0,... somehow - // Allows the PointSize builtin in SM 4.0+, and ignores it, as PointSize is not supported in SM 4+. - bool point_size_compat = false; + // Allows the PointSize builtin in SM 4.0+, and ignores it, as PointSize is not supported in SM 4+. + bool point_size_compat = false; - // Allows the PointCoord builtin, returns float2(0.5, 0.5), as PointCoord is not supported in HLSL. - bool point_coord_compat = false; + // Allows the PointCoord builtin, returns float2(0.5, 0.5), as PointCoord is not supported in HLSL. + bool point_coord_compat = false; - // If true, the backend will assume that VertexIndex and InstanceIndex will need to apply - // a base offset, and you will need to fill in a cbuffer with offsets. - // Set to false if you know you will never use base instance or base vertex - // functionality as it might remove an internal cbuffer. - bool support_nonzero_base_vertex_base_instance = false; + // If true, the backend will assume that VertexIndex and InstanceIndex will need to apply + // a base offset, and you will need to fill in a cbuffer with offsets. + // Set to false if you know you will never use base instance or base vertex + // functionality as it might remove an internal cbuffer. + bool support_nonzero_base_vertex_base_instance = false; - // Forces a storage buffer to always be declared as UAV, even if the readonly decoration is used. - // By default, a readonly storage buffer will be declared as ByteAddressBuffer (SRV) instead. - // Alternatively, use set_hlsl_force_storage_buffer_as_uav to specify individually. - bool force_storage_buffer_as_uav = false; - - // Forces any storage image type marked as NonWritable to be considered an SRV instead. - // For this to work with function call parameters, NonWritable must be considered to be part of the type system - // so that NonWritable image arguments are also translated to Texture rather than RWTexture. - bool nonwritable_uav_texture_as_srv = false; + // Forces a storage buffer to always be declared as UAV, even if the readonly decoration is used. + // By default, a readonly storage buffer will be declared as ByteAddressBuffer (SRV) instead. + // Alternatively, use set_hlsl_force_storage_buffer_as_uav to specify individually. + bool force_storage_buffer_as_uav = false; + + // Forces any storage image type marked as NonWritable to be considered an SRV instead. + // For this to work with function call parameters, NonWritable must be considered to be part of the type system + // so that NonWritable image arguments are also translated to Texture rather than RWTexture. + bool nonwritable_uav_texture_as_srv = false; - // Enables native 16-bit types. Needs SM 6.2. - // Uses half/int16_t/uint16_t instead of min16* types. - // Also adds support for 16-bit load-store from (RW)ByteAddressBuffer. - bool enable_16bit_types = false; + // Enables native 16-bit types. Needs SM 6.2. + // Uses half/int16_t/uint16_t instead of min16* types. + // Also adds support for 16-bit load-store from (RW)ByteAddressBuffer. + bool enable_16bit_types = false; - // If matrices are used as IO variables, flatten the attribute declaration to use - // TEXCOORD{N,N+1,N+2,...} rather than TEXCOORDN_{0,1,2,3}. - // If add_vertex_attribute_remap is used and this feature is used, - // the semantic name will be queried once per active location. - bool flatten_matrix_vertex_input_semantics = false; + // If matrices are used as IO variables, flatten the attribute declaration to use + // TEXCOORD{N,N+1,N+2,...} rather than TEXCOORDN_{0,1,2,3}. + // If add_vertex_attribute_remap is used and this feature is used, + // the semantic name will be queried once per active location. + bool flatten_matrix_vertex_input_semantics = false; - // Rather than emitting main() for the entry point, use the name in SPIR-V. - bool use_entry_point_name = false; + // Rather than emitting main() for the entry point, use the name in SPIR-V. + bool use_entry_point_name = false; - // Preserve (RW)StructuredBuffer types if the input source was HLSL. - // This relies on UserTypeGOOGLE to encode the buffer type either as "structuredbuffer" or "rwstructuredbuffer" - // whereas the type can be extended with an optional subtype, e.g. "structuredbuffer:int". - bool preserve_structured_buffers = false; - }; + // Preserve (RW)StructuredBuffer types if the input source was HLSL. + // This relies on UserTypeGOOGLE to encode the buffer type either as "structuredbuffer" or "rwstructuredbuffer" + // whereas the type can be extended with an optional subtype, e.g. "structuredbuffer:int". + bool preserve_structured_buffers = false; + }; - explicit CompilerHLSL(std::vector spirv_) - : CompilerGLSL(std::move(spirv_)) - { - } + explicit CompilerHLSL(std::vector spirv_) + : CompilerGLSL(std::move(spirv_)) + { + } - CompilerHLSL(const uint32_t *ir_, size_t size) - : CompilerGLSL(ir_, size) - { - } + CompilerHLSL(const uint32_t *ir_, size_t size) + : CompilerGLSL(ir_, size) + { + } - explicit CompilerHLSL(const ParsedIR &ir_) - : CompilerGLSL(ir_) - { - } + explicit CompilerHLSL(const ParsedIR &ir_) + : CompilerGLSL(ir_) + { + } - explicit CompilerHLSL(ParsedIR &&ir_) - : CompilerGLSL(std::move(ir_)) - { - } + explicit CompilerHLSL(ParsedIR &&ir_) + : CompilerGLSL(std::move(ir_)) + { + } - const Options &get_hlsl_options() const - { - return hlsl_options; - } + const Options &get_hlsl_options() const + { + return hlsl_options; + } - void set_hlsl_options(const Options &opts) - { - hlsl_options = opts; - } + void set_hlsl_options(const Options &opts) + { + hlsl_options = opts; + } - // Optionally specify a custom root constant layout. - // - // Push constants ranges will be split up according to the - // layout specified. - void set_root_constant_layouts(std::vector layout); + // Optionally specify a custom root constant layout. + // + // Push constants ranges will be split up according to the + // layout specified. + void set_root_constant_layouts(std::vector layout); - // Compiles and remaps vertex attributes at specific locations to a fixed semantic. - // The default is TEXCOORD# where # denotes location. - // Matrices are unrolled to vectors with notation ${SEMANTIC}_#, where # denotes row. - // $SEMANTIC is either TEXCOORD# or a semantic name specified here. - void add_vertex_attribute_remap(const HLSLVertexAttributeRemap &vertex_attributes); - std::string compile() override; + // Compiles and remaps vertex attributes at specific locations to a fixed semantic. + // The default is TEXCOORD# where # denotes location. + // Matrices are unrolled to vectors with notation ${SEMANTIC}_#, where # denotes row. + // $SEMANTIC is either TEXCOORD# or a semantic name specified here. + void add_vertex_attribute_remap(const HLSLVertexAttributeRemap &vertex_attributes); + std::string compile() override; - // This is a special HLSL workaround for the NumWorkGroups builtin. - // This does not exist in HLSL, so the calling application must create a dummy cbuffer in - // which the application will store this builtin. - // The cbuffer layout will be: - // cbuffer SPIRV_Cross_NumWorkgroups : register(b#, space#) { uint3 SPIRV_Cross_NumWorkgroups_count; }; - // This must be called before compile(). - // The function returns 0 if NumWorkGroups builtin is not statically used in the shader from the current entry point. - // If non-zero, this returns the variable ID of a cbuffer which corresponds to - // the cbuffer declared above. By default, no binding or descriptor set decoration is set, - // so the calling application should declare explicit bindings on this ID before calling compile(). - VariableID remap_num_workgroups_builtin(); + // This is a special HLSL workaround for the NumWorkGroups builtin. + // This does not exist in HLSL, so the calling application must create a dummy cbuffer in + // which the application will store this builtin. + // The cbuffer layout will be: + // cbuffer SPIRV_Cross_NumWorkgroups : register(b#, space#) { uint3 SPIRV_Cross_NumWorkgroups_count; }; + // This must be called before compile(). + // The function returns 0 if NumWorkGroups builtin is not statically used in the shader from the current entry point. + // If non-zero, this returns the variable ID of a cbuffer which corresponds to + // the cbuffer declared above. By default, no binding or descriptor set decoration is set, + // so the calling application should declare explicit bindings on this ID before calling compile(). + VariableID remap_num_workgroups_builtin(); - // Controls how resource bindings are declared in the output HLSL. - void set_resource_binding_flags(HLSLBindingFlags flags); + // Controls how resource bindings are declared in the output HLSL. + void set_resource_binding_flags(HLSLBindingFlags flags); - // resource is a resource binding to indicate the HLSL CBV, SRV, UAV or sampler binding - // to use for a particular SPIR-V description set - // and binding. If resource bindings are provided, - // is_hlsl_resource_binding_used() will return true after calling ::compile() if - // the set/binding combination was used by the HLSL code. - void add_hlsl_resource_binding(const HLSLResourceBinding &resource); - bool is_hlsl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding) const; + // resource is a resource binding to indicate the HLSL CBV, SRV, UAV or sampler binding + // to use for a particular SPIR-V description set + // and binding. If resource bindings are provided, + // is_hlsl_resource_binding_used() will return true after calling ::compile() if + // the set/binding combination was used by the HLSL code. + void add_hlsl_resource_binding(const HLSLResourceBinding &resource); + bool is_hlsl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding) const; - // Controls which storage buffer bindings will be forced to be declared as UAVs. - void set_hlsl_force_storage_buffer_as_uav(uint32_t desc_set, uint32_t binding); + // Controls which storage buffer bindings will be forced to be declared as UAVs. + void set_hlsl_force_storage_buffer_as_uav(uint32_t desc_set, uint32_t binding); - // By default, these magic buffers are not assigned a specific binding. - void set_hlsl_aux_buffer_binding(HLSLAuxBinding binding, uint32_t register_index, uint32_t register_space); - void unset_hlsl_aux_buffer_binding(HLSLAuxBinding binding); - bool is_hlsl_aux_buffer_binding_used(HLSLAuxBinding binding) const; + // By default, these magic buffers are not assigned a specific binding. + void set_hlsl_aux_buffer_binding(HLSLAuxBinding binding, uint32_t register_index, uint32_t register_space); + void unset_hlsl_aux_buffer_binding(HLSLAuxBinding binding); + bool is_hlsl_aux_buffer_binding_used(HLSLAuxBinding binding) const; private: - std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override; - std::string image_type_hlsl(const SPIRType &type, uint32_t id); - std::string image_type_hlsl_modern(const SPIRType &type, uint32_t id); - std::string image_type_hlsl_legacy(const SPIRType &type, uint32_t id); - void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override; - void emit_hlsl_entry_point(); - void emit_header() override; - void emit_resources(); - void emit_interface_block_globally(const SPIRVariable &type); - void emit_interface_block_in_struct(const SPIRVariable &var, std::unordered_set &active_locations); - void emit_interface_block_member_in_struct(const SPIRVariable &var, uint32_t member_index, uint32_t location, - std::unordered_set &active_locations); - void emit_builtin_inputs_in_struct(); - void emit_builtin_outputs_in_struct(); - void emit_builtin_primitive_outputs_in_struct(); - void emit_texture_op(const Instruction &i, bool sparse) override; - void emit_instruction(const Instruction &instruction) override; - void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, - uint32_t count) override; - void emit_buffer_block(const SPIRVariable &type) override; - void emit_push_constant_block(const SPIRVariable &var) override; - void emit_uniform(const SPIRVariable &var) override; - void emit_modern_uniform(const SPIRVariable &var); - void emit_legacy_uniform(const SPIRVariable &var); - void emit_specialization_constants_and_structs(); - void emit_composite_constants(); - void emit_fixup() override; - std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override; - std::string layout_for_member(const SPIRType &type, uint32_t index) override; - std::string to_interpolation_qualifiers(const Bitset &flags) override; - std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override; - bool emit_complex_bitcast(uint32_t result_type, uint32_t id, uint32_t op0) override; - std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) override; - std::string to_sampler_expression(uint32_t id); - std::string to_resource_binding(const SPIRVariable &var); - std::string to_resource_binding_sampler(const SPIRVariable &var); - std::string to_resource_register(HLSLBindingFlagBits flag, char space, uint32_t binding, uint32_t set); - std::string to_initializer_expression(const SPIRVariable &var) override; - void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override; - void emit_access_chain(const Instruction &instruction); - void emit_load(const Instruction &instruction); - void read_access_chain(std::string *expr, const std::string &lhs, const SPIRAccessChain &chain); - void read_access_chain_struct(const std::string &lhs, const SPIRAccessChain &chain); - void read_access_chain_array(const std::string &lhs, const SPIRAccessChain &chain); - void write_access_chain(const SPIRAccessChain &chain, uint32_t value, const SmallVector &composite_chain); - void write_access_chain_struct(const SPIRAccessChain &chain, uint32_t value, - const SmallVector &composite_chain); - void write_access_chain_array(const SPIRAccessChain &chain, uint32_t value, - const SmallVector &composite_chain); - std::string write_access_chain_value(uint32_t value, const SmallVector &composite_chain, bool enclose); - void emit_store(const Instruction &instruction); - void emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op); - void emit_subgroup_op(const Instruction &i) override; - void emit_block_hints(const SPIRBlock &block) override; + std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override; + std::string image_type_hlsl(const SPIRType &type, uint32_t id); + std::string image_type_hlsl_modern(const SPIRType &type, uint32_t id); + std::string image_type_hlsl_legacy(const SPIRType &type, uint32_t id); + void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override; + void emit_hlsl_entry_point(); + void emit_header() override; + void emit_resources(); + void emit_interface_block_globally(const SPIRVariable &type); + void emit_interface_block_in_struct(const SPIRVariable &var, std::unordered_set &active_locations); + void emit_interface_block_member_in_struct(const SPIRVariable &var, uint32_t member_index, uint32_t location, + std::unordered_set &active_locations); + void emit_builtin_inputs_in_struct(); + void emit_builtin_outputs_in_struct(); + void emit_builtin_primitive_outputs_in_struct(); + void emit_texture_op(const Instruction &i, bool sparse) override; + void emit_instruction(const Instruction &instruction) override; + void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, + uint32_t count) override; + void emit_buffer_block(const SPIRVariable &type) override; + void emit_push_constant_block(const SPIRVariable &var) override; + void emit_uniform(const SPIRVariable &var) override; + void emit_modern_uniform(const SPIRVariable &var); + void emit_legacy_uniform(const SPIRVariable &var); + void emit_specialization_constants_and_structs(); + void emit_composite_constants(); + void emit_fixup() override; + std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override; + std::string layout_for_member(const SPIRType &type, uint32_t index) override; + std::string to_interpolation_qualifiers(const Bitset &flags) override; + std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override; + bool emit_complex_bitcast(uint32_t result_type, uint32_t id, uint32_t op0) override; + std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) override; + std::string to_sampler_expression(uint32_t id); + std::string to_resource_binding(const SPIRVariable &var); + std::string to_resource_binding_sampler(const SPIRVariable &var); + std::string to_resource_register(HLSLBindingFlagBits flag, char space, uint32_t binding, uint32_t set); + std::string to_initializer_expression(const SPIRVariable &var) override; + void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override; + void emit_access_chain(const Instruction &instruction); + void emit_load(const Instruction &instruction); + void read_access_chain(std::string *expr, const std::string &lhs, const SPIRAccessChain &chain); + void read_access_chain_struct(const std::string &lhs, const SPIRAccessChain &chain); + void read_access_chain_array(const std::string &lhs, const SPIRAccessChain &chain); + void write_access_chain(const SPIRAccessChain &chain, uint32_t value, const SmallVector &composite_chain); + void write_access_chain_struct(const SPIRAccessChain &chain, uint32_t value, + const SmallVector &composite_chain); + void write_access_chain_array(const SPIRAccessChain &chain, uint32_t value, + const SmallVector &composite_chain); + std::string write_access_chain_value(uint32_t value, const SmallVector &composite_chain, bool enclose); + void emit_store(const Instruction &instruction); + void emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op); + void emit_subgroup_op(const Instruction &i) override; + void emit_block_hints(const SPIRBlock &block) override; - void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, const std::string &qualifier, - uint32_t base_offset = 0) override; - void emit_rayquery_function(const char *commited, const char *candidate, const uint32_t *ops); - void emit_mesh_tasks(SPIRBlock &block) override; + void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, const std::string &qualifier, + uint32_t base_offset = 0) override; + void emit_rayquery_function(const char *commited, const char *candidate, const uint32_t *ops); + void emit_mesh_tasks(SPIRBlock &block) override; - const char *to_storage_qualifiers_glsl(const SPIRVariable &var) override; - void replace_illegal_names() override; + const char *to_storage_qualifiers_glsl(const SPIRVariable &var) override; + void replace_illegal_names() override; - SPIRType::BaseType get_builtin_basetype(spv::BuiltIn builtin, SPIRType::BaseType default_type) override; + SPIRType::BaseType get_builtin_basetype(spv::BuiltIn builtin, SPIRType::BaseType default_type) override; - bool is_hlsl_force_storage_buffer_as_uav(ID id) const; + bool is_hlsl_force_storage_buffer_as_uav(ID id) const; - Options hlsl_options; + Options hlsl_options; - // TODO: Refactor this to be more similar to MSL, maybe have some common system in place? - bool requires_op_fmod = false; - bool requires_fp16_packing = false; - bool requires_uint2_packing = false; - bool requires_explicit_fp16_packing = false; - bool requires_unorm8_packing = false; - bool requires_snorm8_packing = false; - bool requires_unorm16_packing = false; - bool requires_snorm16_packing = false; - bool requires_bitfield_insert = false; - bool requires_bitfield_extract = false; - bool requires_inverse_2x2 = false; - bool requires_inverse_3x3 = false; - bool requires_inverse_4x4 = false; - bool requires_scalar_reflect = false; - bool requires_scalar_refract = false; - bool requires_scalar_faceforward = false; + // TODO: Refactor this to be more similar to MSL, maybe have some common system in place? + bool requires_op_fmod = false; + bool requires_fp16_packing = false; + bool requires_uint2_packing = false; + bool requires_explicit_fp16_packing = false; + bool requires_unorm8_packing = false; + bool requires_snorm8_packing = false; + bool requires_unorm16_packing = false; + bool requires_snorm16_packing = false; + bool requires_bitfield_insert = false; + bool requires_bitfield_extract = false; + bool requires_inverse_2x2 = false; + bool requires_inverse_3x3 = false; + bool requires_inverse_4x4 = false; + bool requires_scalar_reflect = false; + bool requires_scalar_refract = false; + bool requires_scalar_faceforward = false; - struct TextureSizeVariants - { - // MSVC 2013 workaround. - TextureSizeVariants() - { - srv = 0; - for (auto &unorm : uav) - for (auto &u : unorm) - u = 0; - } - uint64_t srv; - uint64_t uav[3][4]; - } required_texture_size_variants; + struct TextureSizeVariants + { + // MSVC 2013 workaround. + TextureSizeVariants() + { + srv = 0; + for (auto &unorm : uav) + for (auto &u : unorm) + u = 0; + } + uint64_t srv; + uint64_t uav[3][4]; + } required_texture_size_variants; - void require_texture_query_variant(uint32_t var_id); - void emit_texture_size_variants(uint64_t variant_mask, const char *vecsize_qualifier, bool uav, - const char *type_qualifier); + void require_texture_query_variant(uint32_t var_id); + void emit_texture_size_variants(uint64_t variant_mask, const char *vecsize_qualifier, bool uav, + const char *type_qualifier); - enum TextureQueryVariantDim - { - Query1D = 0, - Query1DArray, - Query2D, - Query2DArray, - Query3D, - QueryBuffer, - QueryCube, - QueryCubeArray, - Query2DMS, - Query2DMSArray, - QueryDimCount - }; + enum TextureQueryVariantDim + { + Query1D = 0, + Query1DArray, + Query2D, + Query2DArray, + Query3D, + QueryBuffer, + QueryCube, + QueryCubeArray, + Query2DMS, + Query2DMSArray, + QueryDimCount + }; - enum TextureQueryVariantType - { - QueryTypeFloat = 0, - QueryTypeInt = 16, - QueryTypeUInt = 32, - QueryTypeCount = 3 - }; + enum TextureQueryVariantType + { + QueryTypeFloat = 0, + QueryTypeInt = 16, + QueryTypeUInt = 32, + QueryTypeCount = 3 + }; - enum BitcastType - { - TypeNormal, - TypePackUint2x32, - TypeUnpackUint64 - }; + enum BitcastType + { + TypeNormal, + TypePackUint2x32, + TypeUnpackUint64 + }; - void analyze_meshlet_writes(); - void analyze_meshlet_writes(uint32_t func_id, uint32_t id_per_vertex, uint32_t id_per_primitive, - std::unordered_set &processed_func_ids); + void analyze_meshlet_writes(); + void analyze_meshlet_writes(uint32_t func_id, uint32_t id_per_vertex, uint32_t id_per_primitive, + std::unordered_set &processed_func_ids); - BitcastType get_bitcast_type(uint32_t result_type, uint32_t op0); + BitcastType get_bitcast_type(uint32_t result_type, uint32_t op0); - void emit_builtin_variables(); - bool require_output = false; - bool require_input = false; - SmallVector remap_vertex_attributes; + void emit_builtin_variables(); + bool require_output = false; + bool require_input = false; + SmallVector remap_vertex_attributes; - uint32_t type_to_consumed_locations(const SPIRType &type) const; + uint32_t type_to_consumed_locations(const SPIRType &type) const; - std::string to_semantic(uint32_t location, spv::ExecutionModel em, spv::StorageClass sc); + std::string to_semantic(uint32_t location, spv::ExecutionModel em, spv::StorageClass sc); - uint32_t num_workgroups_builtin = 0; - HLSLBindingFlags resource_binding_flags = 0; + uint32_t num_workgroups_builtin = 0; + HLSLBindingFlags resource_binding_flags = 0; - // Custom root constant layout, which should be emitted - // when translating push constant ranges. - std::vector root_constants_layout; + // Custom root constant layout, which should be emitted + // when translating push constant ranges. + std::vector root_constants_layout; - void validate_shader_model(); + void validate_shader_model(); - std::string get_unique_identifier(); - uint32_t unique_identifier_count = 0; + std::string get_unique_identifier(); + uint32_t unique_identifier_count = 0; - std::unordered_map, InternalHasher> resource_bindings; - void remap_hlsl_resource_binding(HLSLBindingFlagBits type, uint32_t &desc_set, uint32_t &binding); + std::unordered_map, InternalHasher> resource_bindings; + void remap_hlsl_resource_binding(HLSLBindingFlagBits type, uint32_t &desc_set, uint32_t &binding); - std::unordered_set force_uav_buffer_bindings; + std::unordered_set force_uav_buffer_bindings; - struct - { - uint32_t register_index = 0; - uint32_t register_space = 0; - bool explicit_binding = false; - bool used = false; - } base_vertex_info; + struct + { + uint32_t register_index = 0; + uint32_t register_space = 0; + bool explicit_binding = false; + bool used = false; + } base_vertex_info; - // Returns true if the specified ID has a UserTypeGOOGLE decoration for StructuredBuffer or RWStructuredBuffer resources. - bool is_user_type_structured(uint32_t id) const override; + // Returns true if the specified ID has a UserTypeGOOGLE decoration for StructuredBuffer or RWStructuredBuffer resources. + bool is_user_type_structured(uint32_t id) const override; - std::vector composite_selection_workaround_types; + std::vector composite_selection_workaround_types; - std::string get_inner_entry_point_name() const; + std::string get_inner_entry_point_name() const; }; } // namespace SPIRV_CROSS_NAMESPACE diff --git a/spirv_msl.hpp b/spirv_msl.hpp index 2d970c0d..61c0c3db 100644 --- a/spirv_msl.hpp +++ b/spirv_msl.hpp @@ -39,34 +39,34 @@ namespace SPIRV_CROSS_NAMESPACE // some other format. enum MSLShaderVariableFormat { - MSL_SHADER_VARIABLE_FORMAT_OTHER = 0, - MSL_SHADER_VARIABLE_FORMAT_UINT8 = 1, - MSL_SHADER_VARIABLE_FORMAT_UINT16 = 2, - MSL_SHADER_VARIABLE_FORMAT_ANY16 = 3, - MSL_SHADER_VARIABLE_FORMAT_ANY32 = 4, + MSL_SHADER_VARIABLE_FORMAT_OTHER = 0, + MSL_SHADER_VARIABLE_FORMAT_UINT8 = 1, + MSL_SHADER_VARIABLE_FORMAT_UINT16 = 2, + MSL_SHADER_VARIABLE_FORMAT_ANY16 = 3, + MSL_SHADER_VARIABLE_FORMAT_ANY32 = 4, - // Deprecated aliases. - MSL_VERTEX_FORMAT_OTHER = MSL_SHADER_VARIABLE_FORMAT_OTHER, - MSL_VERTEX_FORMAT_UINT8 = MSL_SHADER_VARIABLE_FORMAT_UINT8, - MSL_VERTEX_FORMAT_UINT16 = MSL_SHADER_VARIABLE_FORMAT_UINT16, - MSL_SHADER_INPUT_FORMAT_OTHER = MSL_SHADER_VARIABLE_FORMAT_OTHER, - MSL_SHADER_INPUT_FORMAT_UINT8 = MSL_SHADER_VARIABLE_FORMAT_UINT8, - MSL_SHADER_INPUT_FORMAT_UINT16 = MSL_SHADER_VARIABLE_FORMAT_UINT16, - MSL_SHADER_INPUT_FORMAT_ANY16 = MSL_SHADER_VARIABLE_FORMAT_ANY16, - MSL_SHADER_INPUT_FORMAT_ANY32 = MSL_SHADER_VARIABLE_FORMAT_ANY32, + // Deprecated aliases. + MSL_VERTEX_FORMAT_OTHER = MSL_SHADER_VARIABLE_FORMAT_OTHER, + MSL_VERTEX_FORMAT_UINT8 = MSL_SHADER_VARIABLE_FORMAT_UINT8, + MSL_VERTEX_FORMAT_UINT16 = MSL_SHADER_VARIABLE_FORMAT_UINT16, + MSL_SHADER_INPUT_FORMAT_OTHER = MSL_SHADER_VARIABLE_FORMAT_OTHER, + MSL_SHADER_INPUT_FORMAT_UINT8 = MSL_SHADER_VARIABLE_FORMAT_UINT8, + MSL_SHADER_INPUT_FORMAT_UINT16 = MSL_SHADER_VARIABLE_FORMAT_UINT16, + MSL_SHADER_INPUT_FORMAT_ANY16 = MSL_SHADER_VARIABLE_FORMAT_ANY16, + MSL_SHADER_INPUT_FORMAT_ANY32 = MSL_SHADER_VARIABLE_FORMAT_ANY32, - MSL_SHADER_VARIABLE_FORMAT_INT_MAX = 0x7fffffff + MSL_SHADER_VARIABLE_FORMAT_INT_MAX = 0x7fffffff }; // Indicates the rate at which a variable changes value, one of: per-vertex, // per-primitive, or per-patch. enum MSLShaderVariableRate { - MSL_SHADER_VARIABLE_RATE_PER_VERTEX = 0, - MSL_SHADER_VARIABLE_RATE_PER_PRIMITIVE = 1, - MSL_SHADER_VARIABLE_RATE_PER_PATCH = 2, + MSL_SHADER_VARIABLE_RATE_PER_VERTEX = 0, + MSL_SHADER_VARIABLE_RATE_PER_PRIMITIVE = 1, + MSL_SHADER_VARIABLE_RATE_PER_PATCH = 2, - MSL_SHADER_VARIABLE_RATE_INT_MAX = 0x7fffffff, + MSL_SHADER_VARIABLE_RATE_INT_MAX = 0x7fffffff, }; // Defines MSL characteristics of a shader interface variable at a particular location. @@ -75,12 +75,12 @@ enum MSLShaderVariableRate // or behavior is undefined. struct MSLShaderInterfaceVariable { - uint32_t location = 0; - uint32_t component = 0; - MSLShaderVariableFormat format = MSL_SHADER_VARIABLE_FORMAT_OTHER; - spv::BuiltIn builtin = spv::BuiltInMax; - uint32_t vecsize = 0; - MSLShaderVariableRate rate = MSL_SHADER_VARIABLE_RATE_PER_VERTEX; + uint32_t location = 0; + uint32_t component = 0; + MSLShaderVariableFormat format = MSL_SHADER_VARIABLE_FORMAT_OTHER; + spv::BuiltIn builtin = spv::BuiltInMax; + uint32_t vecsize = 0; + MSLShaderVariableRate rate = MSL_SHADER_VARIABLE_RATE_PER_VERTEX; }; // Matches the binding index of a MSL resource for a binding within a descriptor set. @@ -104,161 +104,161 @@ struct MSLShaderInterfaceVariable // become a [[buffer(N)]], [[texture(N)]] or [[sampler(N)]] depending on the resource types used. struct MSLResourceBinding { - spv::ExecutionModel stage = spv::ExecutionModelMax; - SPIRType::BaseType basetype = SPIRType::Unknown; - uint32_t desc_set = 0; - uint32_t binding = 0; - uint32_t count = 0; - uint32_t msl_buffer = 0; - uint32_t msl_texture = 0; - uint32_t msl_sampler = 0; + spv::ExecutionModel stage = spv::ExecutionModelMax; + SPIRType::BaseType basetype = SPIRType::Unknown; + uint32_t desc_set = 0; + uint32_t binding = 0; + uint32_t count = 0; + uint32_t msl_buffer = 0; + uint32_t msl_texture = 0; + uint32_t msl_sampler = 0; }; enum MSLSamplerCoord { - MSL_SAMPLER_COORD_NORMALIZED = 0, - MSL_SAMPLER_COORD_PIXEL = 1, - MSL_SAMPLER_INT_MAX = 0x7fffffff + MSL_SAMPLER_COORD_NORMALIZED = 0, + MSL_SAMPLER_COORD_PIXEL = 1, + MSL_SAMPLER_INT_MAX = 0x7fffffff }; enum MSLSamplerFilter { - MSL_SAMPLER_FILTER_NEAREST = 0, - MSL_SAMPLER_FILTER_LINEAR = 1, - MSL_SAMPLER_FILTER_INT_MAX = 0x7fffffff + MSL_SAMPLER_FILTER_NEAREST = 0, + MSL_SAMPLER_FILTER_LINEAR = 1, + MSL_SAMPLER_FILTER_INT_MAX = 0x7fffffff }; enum MSLSamplerMipFilter { - MSL_SAMPLER_MIP_FILTER_NONE = 0, - MSL_SAMPLER_MIP_FILTER_NEAREST = 1, - MSL_SAMPLER_MIP_FILTER_LINEAR = 2, - MSL_SAMPLER_MIP_FILTER_INT_MAX = 0x7fffffff + MSL_SAMPLER_MIP_FILTER_NONE = 0, + MSL_SAMPLER_MIP_FILTER_NEAREST = 1, + MSL_SAMPLER_MIP_FILTER_LINEAR = 2, + MSL_SAMPLER_MIP_FILTER_INT_MAX = 0x7fffffff }; enum MSLSamplerAddress { - MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO = 0, - MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE = 1, - MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER = 2, - MSL_SAMPLER_ADDRESS_REPEAT = 3, - MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT = 4, - MSL_SAMPLER_ADDRESS_INT_MAX = 0x7fffffff + MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO = 0, + MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE = 1, + MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER = 2, + MSL_SAMPLER_ADDRESS_REPEAT = 3, + MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT = 4, + MSL_SAMPLER_ADDRESS_INT_MAX = 0x7fffffff }; enum MSLSamplerCompareFunc { - MSL_SAMPLER_COMPARE_FUNC_NEVER = 0, - MSL_SAMPLER_COMPARE_FUNC_LESS = 1, - MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL = 2, - MSL_SAMPLER_COMPARE_FUNC_GREATER = 3, - MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL = 4, - MSL_SAMPLER_COMPARE_FUNC_EQUAL = 5, - MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL = 6, - MSL_SAMPLER_COMPARE_FUNC_ALWAYS = 7, - MSL_SAMPLER_COMPARE_FUNC_INT_MAX = 0x7fffffff + MSL_SAMPLER_COMPARE_FUNC_NEVER = 0, + MSL_SAMPLER_COMPARE_FUNC_LESS = 1, + MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL = 2, + MSL_SAMPLER_COMPARE_FUNC_GREATER = 3, + MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL = 4, + MSL_SAMPLER_COMPARE_FUNC_EQUAL = 5, + MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL = 6, + MSL_SAMPLER_COMPARE_FUNC_ALWAYS = 7, + MSL_SAMPLER_COMPARE_FUNC_INT_MAX = 0x7fffffff }; enum MSLSamplerBorderColor { - MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK = 0, - MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK = 1, - MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE = 2, - MSL_SAMPLER_BORDER_COLOR_INT_MAX = 0x7fffffff + MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK = 0, + MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK = 1, + MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE = 2, + MSL_SAMPLER_BORDER_COLOR_INT_MAX = 0x7fffffff }; enum MSLFormatResolution { - MSL_FORMAT_RESOLUTION_444 = 0, - MSL_FORMAT_RESOLUTION_422, - MSL_FORMAT_RESOLUTION_420, - MSL_FORMAT_RESOLUTION_INT_MAX = 0x7fffffff + MSL_FORMAT_RESOLUTION_444 = 0, + MSL_FORMAT_RESOLUTION_422, + MSL_FORMAT_RESOLUTION_420, + MSL_FORMAT_RESOLUTION_INT_MAX = 0x7fffffff }; enum MSLChromaLocation { - MSL_CHROMA_LOCATION_COSITED_EVEN = 0, - MSL_CHROMA_LOCATION_MIDPOINT, - MSL_CHROMA_LOCATION_INT_MAX = 0x7fffffff + MSL_CHROMA_LOCATION_COSITED_EVEN = 0, + MSL_CHROMA_LOCATION_MIDPOINT, + MSL_CHROMA_LOCATION_INT_MAX = 0x7fffffff }; enum MSLComponentSwizzle { - MSL_COMPONENT_SWIZZLE_IDENTITY = 0, - MSL_COMPONENT_SWIZZLE_ZERO, - MSL_COMPONENT_SWIZZLE_ONE, - MSL_COMPONENT_SWIZZLE_R, - MSL_COMPONENT_SWIZZLE_G, - MSL_COMPONENT_SWIZZLE_B, - MSL_COMPONENT_SWIZZLE_A, - MSL_COMPONENT_SWIZZLE_INT_MAX = 0x7fffffff + MSL_COMPONENT_SWIZZLE_IDENTITY = 0, + MSL_COMPONENT_SWIZZLE_ZERO, + MSL_COMPONENT_SWIZZLE_ONE, + MSL_COMPONENT_SWIZZLE_R, + MSL_COMPONENT_SWIZZLE_G, + MSL_COMPONENT_SWIZZLE_B, + MSL_COMPONENT_SWIZZLE_A, + MSL_COMPONENT_SWIZZLE_INT_MAX = 0x7fffffff }; enum MSLSamplerYCbCrModelConversion { - MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY = 0, - MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY, - MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_709, - MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_601, - MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_2020, - MSL_SAMPLER_YCBCR_MODEL_CONVERSION_INT_MAX = 0x7fffffff + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY = 0, + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY, + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_709, + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_601, + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_2020, + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_INT_MAX = 0x7fffffff }; enum MSLSamplerYCbCrRange { - MSL_SAMPLER_YCBCR_RANGE_ITU_FULL = 0, - MSL_SAMPLER_YCBCR_RANGE_ITU_NARROW, - MSL_SAMPLER_YCBCR_RANGE_INT_MAX = 0x7fffffff + MSL_SAMPLER_YCBCR_RANGE_ITU_FULL = 0, + MSL_SAMPLER_YCBCR_RANGE_ITU_NARROW, + MSL_SAMPLER_YCBCR_RANGE_INT_MAX = 0x7fffffff }; struct MSLConstexprSampler { - MSLSamplerCoord coord = MSL_SAMPLER_COORD_NORMALIZED; - MSLSamplerFilter min_filter = MSL_SAMPLER_FILTER_NEAREST; - MSLSamplerFilter mag_filter = MSL_SAMPLER_FILTER_NEAREST; - MSLSamplerMipFilter mip_filter = MSL_SAMPLER_MIP_FILTER_NONE; - MSLSamplerAddress s_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; - MSLSamplerAddress t_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; - MSLSamplerAddress r_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; - MSLSamplerCompareFunc compare_func = MSL_SAMPLER_COMPARE_FUNC_NEVER; - MSLSamplerBorderColor border_color = MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK; - float lod_clamp_min = 0.0f; - float lod_clamp_max = 1000.0f; - int max_anisotropy = 1; + MSLSamplerCoord coord = MSL_SAMPLER_COORD_NORMALIZED; + MSLSamplerFilter min_filter = MSL_SAMPLER_FILTER_NEAREST; + MSLSamplerFilter mag_filter = MSL_SAMPLER_FILTER_NEAREST; + MSLSamplerMipFilter mip_filter = MSL_SAMPLER_MIP_FILTER_NONE; + MSLSamplerAddress s_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + MSLSamplerAddress t_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + MSLSamplerAddress r_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + MSLSamplerCompareFunc compare_func = MSL_SAMPLER_COMPARE_FUNC_NEVER; + MSLSamplerBorderColor border_color = MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK; + float lod_clamp_min = 0.0f; + float lod_clamp_max = 1000.0f; + int max_anisotropy = 1; - // Sampler Y'CbCr conversion parameters - uint32_t planes = 0; - MSLFormatResolution resolution = MSL_FORMAT_RESOLUTION_444; - MSLSamplerFilter chroma_filter = MSL_SAMPLER_FILTER_NEAREST; - MSLChromaLocation x_chroma_offset = MSL_CHROMA_LOCATION_COSITED_EVEN; - MSLChromaLocation y_chroma_offset = MSL_CHROMA_LOCATION_COSITED_EVEN; - MSLComponentSwizzle swizzle[4]; // IDENTITY, IDENTITY, IDENTITY, IDENTITY - MSLSamplerYCbCrModelConversion ycbcr_model = MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY; - MSLSamplerYCbCrRange ycbcr_range = MSL_SAMPLER_YCBCR_RANGE_ITU_FULL; - uint32_t bpc = 8; + // Sampler Y'CbCr conversion parameters + uint32_t planes = 0; + MSLFormatResolution resolution = MSL_FORMAT_RESOLUTION_444; + MSLSamplerFilter chroma_filter = MSL_SAMPLER_FILTER_NEAREST; + MSLChromaLocation x_chroma_offset = MSL_CHROMA_LOCATION_COSITED_EVEN; + MSLChromaLocation y_chroma_offset = MSL_CHROMA_LOCATION_COSITED_EVEN; + MSLComponentSwizzle swizzle[4]; // IDENTITY, IDENTITY, IDENTITY, IDENTITY + MSLSamplerYCbCrModelConversion ycbcr_model = MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY; + MSLSamplerYCbCrRange ycbcr_range = MSL_SAMPLER_YCBCR_RANGE_ITU_FULL; + uint32_t bpc = 8; - bool compare_enable = false; - bool lod_clamp_enable = false; - bool anisotropy_enable = false; - bool ycbcr_conversion_enable = false; + bool compare_enable = false; + bool lod_clamp_enable = false; + bool anisotropy_enable = false; + bool ycbcr_conversion_enable = false; - MSLConstexprSampler() - { - for (uint32_t i = 0; i < 4; i++) - swizzle[i] = MSL_COMPONENT_SWIZZLE_IDENTITY; - } - bool swizzle_is_identity() const - { - return (swizzle[0] == MSL_COMPONENT_SWIZZLE_IDENTITY && swizzle[1] == MSL_COMPONENT_SWIZZLE_IDENTITY && - swizzle[2] == MSL_COMPONENT_SWIZZLE_IDENTITY && swizzle[3] == MSL_COMPONENT_SWIZZLE_IDENTITY); - } - bool swizzle_has_one_or_zero() const - { - return (swizzle[0] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[0] == MSL_COMPONENT_SWIZZLE_ONE || - swizzle[1] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[1] == MSL_COMPONENT_SWIZZLE_ONE || - swizzle[2] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[2] == MSL_COMPONENT_SWIZZLE_ONE || - swizzle[3] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[3] == MSL_COMPONENT_SWIZZLE_ONE); - } + MSLConstexprSampler() + { + for (uint32_t i = 0; i < 4; i++) + swizzle[i] = MSL_COMPONENT_SWIZZLE_IDENTITY; + } + bool swizzle_is_identity() const + { + return (swizzle[0] == MSL_COMPONENT_SWIZZLE_IDENTITY && swizzle[1] == MSL_COMPONENT_SWIZZLE_IDENTITY && + swizzle[2] == MSL_COMPONENT_SWIZZLE_IDENTITY && swizzle[3] == MSL_COMPONENT_SWIZZLE_IDENTITY); + } + bool swizzle_has_one_or_zero() const + { + return (swizzle[0] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[0] == MSL_COMPONENT_SWIZZLE_ONE || + swizzle[1] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[1] == MSL_COMPONENT_SWIZZLE_ONE || + swizzle[2] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[2] == MSL_COMPONENT_SWIZZLE_ONE || + swizzle[3] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[3] == MSL_COMPONENT_SWIZZLE_ONE); + } }; // Special constant used in a MSLResourceBinding desc_set @@ -291,1058 +291,1058 @@ static const uint32_t kMaxArgumentBuffers = 8; class CompilerMSL : public CompilerGLSL { public: - // Options for compiling to Metal Shading Language - struct Options - { - typedef enum - { - iOS = 0, - macOS = 1 - } Platform; - - Platform platform = macOS; - uint32_t msl_version = make_msl_version(1, 2); - uint32_t texel_buffer_texture_width = 4096; // Width of 2D Metal textures used as 1D texel buffers - uint32_t r32ui_linear_texture_alignment = 4; - uint32_t r32ui_alignment_constant_id = 65535; - uint32_t swizzle_buffer_index = 30; - uint32_t indirect_params_buffer_index = 29; - uint32_t shader_output_buffer_index = 28; - uint32_t shader_patch_output_buffer_index = 27; - uint32_t shader_tess_factor_buffer_index = 26; - uint32_t buffer_size_buffer_index = 25; - uint32_t view_mask_buffer_index = 24; - uint32_t dynamic_offsets_buffer_index = 23; - uint32_t shader_input_buffer_index = 22; - uint32_t shader_index_buffer_index = 21; - uint32_t shader_patch_input_buffer_index = 20; - uint32_t shader_input_wg_index = 0; - uint32_t device_index = 0; - uint32_t enable_frag_output_mask = 0xffffffff; - // Metal doesn't allow setting a fixed sample mask directly in the pipeline. - // We can evade this restriction by ANDing the internal sample_mask output - // of the shader with the additional fixed sample mask. - uint32_t additional_fixed_sample_mask = 0xffffffff; - bool enable_point_size_builtin = true; - bool enable_frag_depth_builtin = true; - bool enable_frag_stencil_ref_builtin = true; - bool disable_rasterization = false; - bool capture_output_to_buffer = false; - bool swizzle_texture_samples = false; - bool tess_domain_origin_lower_left = false; - bool multiview = false; - bool multiview_layered_rendering = true; - bool view_index_from_device_index = false; - bool dispatch_base = false; - bool texture_1D_as_2D = false; - - // Enable use of Metal argument buffers. - // MSL 2.0 must also be enabled. - bool argument_buffers = false; - - // Defines Metal argument buffer tier levels. - // Uses same values as Metal MTLArgumentBuffersTier enumeration. - enum class ArgumentBuffersTier - { - Tier1 = 0, - Tier2 = 1, - }; - - // When using Metal argument buffers, indicates the Metal argument buffer tier level supported by the Metal platform. - // Ignored when Options::argument_buffers is disabled. - // - Tier1 supports writable images on macOS, but not on iOS. - // - Tier2 supports writable images on macOS and iOS, and higher resource count limits. - // Tier capabilities based on recommendations from Apple engineering. - ArgumentBuffersTier argument_buffers_tier = ArgumentBuffersTier::Tier1; - - // Enables specifick argument buffer format with extra information to track SSBO-length - bool runtime_array_rich_descriptor = false; - - // Ensures vertex and instance indices start at zero. This reflects the behavior of HLSL with SV_VertexID and SV_InstanceID. - bool enable_base_index_zero = false; - - // Fragment output in MSL must have at least as many components as the render pass. - // Add support to explicit pad out components. - bool pad_fragment_output_components = false; - - // Specifies whether the iOS target version supports the [[base_vertex]] and [[base_instance]] attributes. - bool ios_support_base_vertex_instance = false; - - // Use Metal's native frame-buffer fetch API for subpass inputs. - bool use_framebuffer_fetch_subpasses = false; - - // Enables use of "fma" intrinsic for invariant float math - bool invariant_float_math = false; - - // Emulate texturecube_array with texture2d_array for iOS where this type is not available - bool emulate_cube_array = false; - - // Allow user to enable decoration binding - bool enable_decoration_binding = false; - - // Requires MSL 2.1, use the native support for texel buffers. - bool texture_buffer_native = false; - - // Forces all resources which are part of an argument buffer to be considered active. - // This ensures ABI compatibility between shaders where some resources might be unused, - // and would otherwise declare a different IAB. - bool force_active_argument_buffer_resources = false; - - // Aligns each resource in an argument buffer to its assigned index value, id(N), - // by adding synthetic padding members in the argument buffer struct for any resources - // in the argument buffer that are not defined and used by the shader. This allows - // the shader to index into the correct argument in a descriptor set argument buffer - // that is shared across shaders, where not all resources in the argument buffer are - // defined in each shader. For this to work, an MSLResourceBinding must be provided for - // all descriptors in any descriptor set held in an argument buffer in the shader, and - // that MSLResourceBinding must have the basetype and count members populated correctly. - // The implementation here assumes any inline blocks in the argument buffer is provided - // in a Metal buffer, and doesn't take into consideration inline blocks that are - // optionally embedded directly into the argument buffer via add_inline_uniform_block(). - bool pad_argument_buffer_resources = false; - - // Forces the use of plain arrays, which works around certain driver bugs on certain versions - // of Intel Macbooks. See https://github.com/KhronosGroup/SPIRV-Cross/issues/1210. - // May reduce performance in scenarios where arrays are copied around as value-types. - bool force_native_arrays = false; - - // If a shader writes clip distance, also emit user varyings which - // can be read in subsequent stages. - bool enable_clip_distance_user_varying = true; - - // In a tessellation control shader, assume that more than one patch can be processed in a - // single workgroup. This requires changes to the way the InvocationId and PrimitiveId - // builtins are processed, but should result in more efficient usage of the GPU. - bool multi_patch_workgroup = false; - - // Use storage buffers instead of vertex-style attributes for tessellation evaluation - // input. This may require conversion of inputs in the generated post-tessellation - // vertex shader, but allows the use of nested arrays. - bool raw_buffer_tese_input = false; - - // If set, a vertex shader will be compiled as part of a tessellation pipeline. - // It will be translated as a compute kernel, so it can use the global invocation ID - // to index the output buffer. - bool vertex_for_tessellation = false; - - // Assume that SubpassData images have multiple layers. Layered input attachments - // are addressed relative to the Layer output from the vertex pipeline. This option - // has no effect with multiview, since all input attachments are assumed to be layered - // and will be addressed using the current ViewIndex. - bool arrayed_subpass_input = false; - - // Whether to use SIMD-group or quadgroup functions to implement group non-uniform - // operations. Some GPUs on iOS do not support the SIMD-group functions, only the - // quadgroup functions. - bool ios_use_simdgroup_functions = false; - - // If set, the subgroup size will be assumed to be one, and subgroup-related - // builtins and operations will be emitted accordingly. This mode is intended to - // be used by MoltenVK on hardware/software configurations which do not provide - // sufficient support for subgroups. - bool emulate_subgroups = false; - - // If nonzero, a fixed subgroup size to assume. Metal, similarly to VK_EXT_subgroup_size_control, - // allows the SIMD-group size (aka thread execution width) to vary depending on - // register usage and requirements. In certain circumstances--for example, a pipeline - // in MoltenVK without VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT-- - // this is undesirable. This fixes the value of the SubgroupSize builtin, instead of - // mapping it to the Metal builtin [[thread_execution_width]]. If the thread - // execution width is reduced, the extra invocations will appear to be inactive. - // If zero, the SubgroupSize will be allowed to vary, and the builtin will be mapped - // to the Metal [[thread_execution_width]] builtin. - uint32_t fixed_subgroup_size = 0; - - enum class IndexType - { - None = 0, - UInt16 = 1, - UInt32 = 2 - }; - - // The type of index in the index buffer, if present. For a compute shader, Metal - // requires specifying the indexing at pipeline creation, rather than at draw time - // as with graphics pipelines. This means we must create three different pipelines, - // for no indexing, 16-bit indices, and 32-bit indices. Each requires different - // handling for the gl_VertexIndex builtin. We may as well, then, create three - // different shaders for these three scenarios. - IndexType vertex_index_type = IndexType::None; - - // If set, a dummy [[sample_id]] input is added to a fragment shader if none is present. - // This will force the shader to run at sample rate, assuming Metal does not optimize - // the extra threads away. - bool force_sample_rate_shading = false; - - // If set, gl_HelperInvocation will be set manually whenever a fragment is discarded. - // Some Metal devices have a bug where simd_is_helper_thread() does not return true - // after a fragment has been discarded. This is a workaround that is only expected to be needed - // until the bug is fixed in Metal; it is provided as an option to allow disabling it when that occurs. - bool manual_helper_invocation_updates = true; - - // If set, extra checks will be emitted in fragment shaders to prevent writes - // from discarded fragments. Some Metal devices have a bug where writes to storage resources - // from discarded fragment threads continue to occur, despite the fragment being - // discarded. This is a workaround that is only expected to be needed until the - // bug is fixed in Metal; it is provided as an option so it can be enabled - // only when the bug is present. - bool check_discarded_frag_stores = false; - - // If set, Lod operands to OpImageSample*DrefExplicitLod for 1D and 2D array images - // will be implemented using a gradient instead of passing the level operand directly. - // Some Metal devices have a bug where the level() argument to depth2d_array::sample_compare() - // in a fragment shader is biased by some unknown amount, possibly dependent on the - // partial derivatives of the texture coordinates. This is a workaround that is only - // expected to be needed until the bug is fixed in Metal; it is provided as an option - // so it can be enabled only when the bug is present. - bool sample_dref_lod_array_as_grad = false; - - // MSL doesn't guarantee coherence between writes and subsequent reads of read_write textures. - // This inserts fences before each read of a read_write texture to ensure coherency. - // If you're sure you never rely on this, you can set this to false for a possible performance improvement. - // Note: Only Apple's GPU compiler takes advantage of the lack of coherency, so make sure to test on Apple GPUs if you disable this. - bool readwrite_texture_fences = true; - - // Metal 3.1 introduced a Metal regression bug which causes infinite recursion during - // Metal's analysis of an entry point input structure that is itself recursive. Enabling - // this option will replace the recursive input declaration with a alternate variable of - // type void*, and then cast to the correct type at the top of the entry point function. - // The bug has been reported to Apple, and will hopefully be fixed in future releases. - bool replace_recursive_inputs = false; - - // If set, manual fixups of gradient vectors for cube texture lookups will be performed. - // All released Apple Silicon GPUs to date behave incorrectly when sampling a cube texture - // with explicit gradients. They will ignore one of the three partial derivatives based - // on the selected major axis, and expect the remaining derivatives to be partially - // transformed. - bool agx_manual_cube_grad_fixup = false; - - // Metal will discard fragments with side effects under certain circumstances prematurely. - // Example: CTS test dEQP-VK.fragment_operations.early_fragment.discard_no_early_fragment_tests_depth - // Test will render a full screen quad with varying depth [0,1] for each fragment. - // Each fragment will do an operation with side effects, modify the depth value and - // discard the fragment. The test expects the fragment to be run due to: - // https://registry.khronos.org/vulkan/specs/1.0-extensions/html/vkspec.html#fragops-shader-depthreplacement - // which states that the fragment shader must be run due to replacing the depth in shader. - // However, Metal may prematurely discards fragments without executing them - // (I believe this to be due to a greedy optimization on their end) making the test fail. - // This option enforces fragment execution for such cases where the fragment has operations - // with side effects. Provided as an option hoping Metal will fix this issue in the future. - bool force_fragment_with_side_effects_execution = false; - - // If set, adds a depth pass through statement to circumvent the following issue: - // When the same depth/stencil is used as input and depth/stencil attachment, we need to - // force Metal to perform the depth/stencil write after fragment execution. Otherwise, - // Metal will write to the depth attachment before fragment execution. This happens - // if the fragment does not modify the depth value. - bool input_attachment_is_ds_attachment = false; - - bool is_ios() const - { - return platform == iOS; - } - - bool is_macos() const - { - return platform == macOS; - } - - bool use_quadgroup_operation() const - { - return is_ios() && !ios_use_simdgroup_functions; - } - - void set_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0) - { - msl_version = make_msl_version(major, minor, patch); - } - - bool supports_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0) const - { - return msl_version >= make_msl_version(major, minor, patch); - } - - static uint32_t make_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0) - { - return (major * 10000) + (minor * 100) + patch; - } - }; - - const Options &get_msl_options() const - { - return msl_options; - } - - void set_msl_options(const Options &opts) - { - msl_options = opts; - } - - // Provide feedback to calling API to allow runtime to disable pipeline - // rasterization if vertex shader requires rasterization to be disabled. - bool get_is_rasterization_disabled() const - { - return is_rasterization_disabled && (get_entry_point().model == spv::ExecutionModelVertex || - get_entry_point().model == spv::ExecutionModelTessellationControl || - get_entry_point().model == spv::ExecutionModelTessellationEvaluation); - } - - // Provide feedback to calling API to allow it to pass an auxiliary - // swizzle buffer if the shader needs it. - bool needs_swizzle_buffer() const - { - return used_swizzle_buffer; - } - - // Provide feedback to calling API to allow it to pass a buffer - // containing STORAGE_BUFFER buffer sizes to support OpArrayLength. - bool needs_buffer_size_buffer() const - { - return !buffers_requiring_array_length.empty(); - } - - bool buffer_requires_array_length(VariableID id) const - { - return buffers_requiring_array_length.count(id) != 0; - } - - // Provide feedback to calling API to allow it to pass a buffer - // containing the view mask for the current multiview subpass. - bool needs_view_mask_buffer() const - { - return msl_options.multiview && !msl_options.view_index_from_device_index; - } - - // Provide feedback to calling API to allow it to pass a buffer - // containing the dispatch base workgroup ID. - bool needs_dispatch_base_buffer() const - { - return msl_options.dispatch_base && !msl_options.supports_msl_version(1, 2); - } - - // Provide feedback to calling API to allow it to pass an output - // buffer if the shader needs it. - bool needs_output_buffer() const - { - return capture_output_to_buffer && stage_out_var_id != ID(0); - } - - // Provide feedback to calling API to allow it to pass a patch output - // buffer if the shader needs it. - bool needs_patch_output_buffer() const - { - return capture_output_to_buffer && patch_stage_out_var_id != ID(0); - } - - // Provide feedback to calling API to allow it to pass an input threadgroup - // buffer if the shader needs it. - bool needs_input_threadgroup_mem() const - { - return capture_output_to_buffer && stage_in_var_id != ID(0); - } - - explicit CompilerMSL(std::vector spirv); - CompilerMSL(const uint32_t *ir, size_t word_count); - explicit CompilerMSL(const ParsedIR &ir); - explicit CompilerMSL(ParsedIR &&ir); - - // input is a shader interface variable description used to fix up shader input variables. - // If shader inputs are provided, is_msl_shader_input_used() will return true after - // calling ::compile() if the location were used by the MSL code. - void add_msl_shader_input(const MSLShaderInterfaceVariable &input); - - // output is a shader interface variable description used to fix up shader output variables. - // If shader outputs are provided, is_msl_shader_output_used() will return true after - // calling ::compile() if the location were used by the MSL code. - void add_msl_shader_output(const MSLShaderInterfaceVariable &output); - - // resource is a resource binding to indicate the MSL buffer, - // texture or sampler index to use for a particular SPIR-V description set - // and binding. If resource bindings are provided, - // is_msl_resource_binding_used() will return true after calling ::compile() if - // the set/binding combination was used by the MSL code. - void add_msl_resource_binding(const MSLResourceBinding &resource); - - // desc_set and binding are the SPIR-V descriptor set and binding of a buffer resource - // in this shader. index is the index within the dynamic offset buffer to use. This - // function marks that resource as using a dynamic offset (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC - // or VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC). This function only has any effect if argument buffers - // are enabled. If so, the buffer will have its address adjusted at the beginning of the shader with - // an offset taken from the dynamic offset buffer. - void add_dynamic_buffer(uint32_t desc_set, uint32_t binding, uint32_t index); - - // desc_set and binding are the SPIR-V descriptor set and binding of a buffer resource - // in this shader. This function marks that resource as an inline uniform block - // (VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT). This function only has any effect if argument buffers - // are enabled. If so, the buffer block will be directly embedded into the argument - // buffer, instead of being referenced indirectly via pointer. - void add_inline_uniform_block(uint32_t desc_set, uint32_t binding); - - // When using MSL argument buffers, we can force "classic" MSL 1.0 binding schemes for certain descriptor sets. - // This corresponds to VK_KHR_push_descriptor in Vulkan. - void add_discrete_descriptor_set(uint32_t desc_set); - - // If an argument buffer is large enough, it may need to be in the device storage space rather than - // constant. Opt-in to this behavior here on a per set basis. - void set_argument_buffer_device_address_space(uint32_t desc_set, bool device_storage); - - // Query after compilation is done. This allows you to check if an input location was used by the shader. - bool is_msl_shader_input_used(uint32_t location); - - // Query after compilation is done. This allows you to check if an output location were used by the shader. - bool is_msl_shader_output_used(uint32_t location); - - // If not using add_msl_shader_input, it's possible - // that certain builtin attributes need to be automatically assigned locations. - // This is typical for tessellation builtin inputs such as tess levels, gl_Position, etc. - // This returns k_unknown_location if the location was explicitly assigned with - // add_msl_shader_input or the builtin is not used, otherwise returns N in [[attribute(N)]]. - uint32_t get_automatic_builtin_input_location(spv::BuiltIn builtin) const; - - // If not using add_msl_shader_output, it's possible - // that certain builtin attributes need to be automatically assigned locations. - // This is typical for tessellation builtin outputs such as tess levels, gl_Position, etc. - // This returns k_unknown_location if the location were explicitly assigned with - // add_msl_shader_output or the builtin were not used, otherwise returns N in [[attribute(N)]]. - uint32_t get_automatic_builtin_output_location(spv::BuiltIn builtin) const; - - // NOTE: Only resources which are remapped using add_msl_resource_binding will be reported here. - // Constexpr samplers are always assumed to be emitted. - // No specific MSLResourceBinding remapping is required for constexpr samplers as long as they are remapped - // by remap_constexpr_sampler(_by_binding). - bool is_msl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding) const; - - // This must only be called after a successful call to CompilerMSL::compile(). - // For a variable resource ID obtained through reflection API, report the automatically assigned resource index. - // If the descriptor set was part of an argument buffer, report the [[id(N)]], - // or [[buffer/texture/sampler]] binding for other resources. - // If the resource was a combined image sampler, report the image binding here, - // use the _secondary version of this call to query the sampler half of the resource. - // If no binding exists, uint32_t(-1) is returned. - uint32_t get_automatic_msl_resource_binding(uint32_t id) const; - - // Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers, in which case the - // sampler's binding is returned instead. For any other resource type, -1 is returned. - // Secondary bindings are also used for the auxillary image atomic buffer. - uint32_t get_automatic_msl_resource_binding_secondary(uint32_t id) const; - - // Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers for multiplanar images, - // in which case the second plane's binding is returned instead. For any other resource type, -1 is returned. - uint32_t get_automatic_msl_resource_binding_tertiary(uint32_t id) const; - - // Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers for triplanar images, - // in which case the third plane's binding is returned instead. For any other resource type, -1 is returned. - uint32_t get_automatic_msl_resource_binding_quaternary(uint32_t id) const; - - // Compiles the SPIR-V code into Metal Shading Language. - std::string compile() override; - - // Remap a sampler with ID to a constexpr sampler. - // Older iOS targets must use constexpr samplers in certain cases (PCF), - // so a static sampler must be used. - // The sampler will not consume a binding, but be declared in the entry point as a constexpr sampler. - // This can be used on both combined image/samplers (sampler2D) or standalone samplers. - // The remapped sampler must not be an array of samplers. - // Prefer remap_constexpr_sampler_by_binding unless you're also doing reflection anyways. - void remap_constexpr_sampler(VariableID id, const MSLConstexprSampler &sampler); - - // Same as remap_constexpr_sampler, except you provide set/binding, rather than variable ID. - // Remaps based on ID take priority over set/binding remaps. - void remap_constexpr_sampler_by_binding(uint32_t desc_set, uint32_t binding, const MSLConstexprSampler &sampler); - - // If using CompilerMSL::Options::pad_fragment_output_components, override the number of components we expect - // to use for a particular location. The default is 4 if number of components is not overridden. - void set_fragment_output_components(uint32_t location, uint32_t components); - - void set_combined_sampler_suffix(const char *suffix); - const char *get_combined_sampler_suffix() const; + // Options for compiling to Metal Shading Language + struct Options + { + typedef enum + { + iOS = 0, + macOS = 1 + } Platform; + + Platform platform = macOS; + uint32_t msl_version = make_msl_version(1, 2); + uint32_t texel_buffer_texture_width = 4096; // Width of 2D Metal textures used as 1D texel buffers + uint32_t r32ui_linear_texture_alignment = 4; + uint32_t r32ui_alignment_constant_id = 65535; + uint32_t swizzle_buffer_index = 30; + uint32_t indirect_params_buffer_index = 29; + uint32_t shader_output_buffer_index = 28; + uint32_t shader_patch_output_buffer_index = 27; + uint32_t shader_tess_factor_buffer_index = 26; + uint32_t buffer_size_buffer_index = 25; + uint32_t view_mask_buffer_index = 24; + uint32_t dynamic_offsets_buffer_index = 23; + uint32_t shader_input_buffer_index = 22; + uint32_t shader_index_buffer_index = 21; + uint32_t shader_patch_input_buffer_index = 20; + uint32_t shader_input_wg_index = 0; + uint32_t device_index = 0; + uint32_t enable_frag_output_mask = 0xffffffff; + // Metal doesn't allow setting a fixed sample mask directly in the pipeline. + // We can evade this restriction by ANDing the internal sample_mask output + // of the shader with the additional fixed sample mask. + uint32_t additional_fixed_sample_mask = 0xffffffff; + bool enable_point_size_builtin = true; + bool enable_frag_depth_builtin = true; + bool enable_frag_stencil_ref_builtin = true; + bool disable_rasterization = false; + bool capture_output_to_buffer = false; + bool swizzle_texture_samples = false; + bool tess_domain_origin_lower_left = false; + bool multiview = false; + bool multiview_layered_rendering = true; + bool view_index_from_device_index = false; + bool dispatch_base = false; + bool texture_1D_as_2D = false; + + // Enable use of Metal argument buffers. + // MSL 2.0 must also be enabled. + bool argument_buffers = false; + + // Defines Metal argument buffer tier levels. + // Uses same values as Metal MTLArgumentBuffersTier enumeration. + enum class ArgumentBuffersTier + { + Tier1 = 0, + Tier2 = 1, + }; + + // When using Metal argument buffers, indicates the Metal argument buffer tier level supported by the Metal platform. + // Ignored when Options::argument_buffers is disabled. + // - Tier1 supports writable images on macOS, but not on iOS. + // - Tier2 supports writable images on macOS and iOS, and higher resource count limits. + // Tier capabilities based on recommendations from Apple engineering. + ArgumentBuffersTier argument_buffers_tier = ArgumentBuffersTier::Tier1; + + // Enables specifick argument buffer format with extra information to track SSBO-length + bool runtime_array_rich_descriptor = false; + + // Ensures vertex and instance indices start at zero. This reflects the behavior of HLSL with SV_VertexID and SV_InstanceID. + bool enable_base_index_zero = false; + + // Fragment output in MSL must have at least as many components as the render pass. + // Add support to explicit pad out components. + bool pad_fragment_output_components = false; + + // Specifies whether the iOS target version supports the [[base_vertex]] and [[base_instance]] attributes. + bool ios_support_base_vertex_instance = false; + + // Use Metal's native frame-buffer fetch API for subpass inputs. + bool use_framebuffer_fetch_subpasses = false; + + // Enables use of "fma" intrinsic for invariant float math + bool invariant_float_math = false; + + // Emulate texturecube_array with texture2d_array for iOS where this type is not available + bool emulate_cube_array = false; + + // Allow user to enable decoration binding + bool enable_decoration_binding = false; + + // Requires MSL 2.1, use the native support for texel buffers. + bool texture_buffer_native = false; + + // Forces all resources which are part of an argument buffer to be considered active. + // This ensures ABI compatibility between shaders where some resources might be unused, + // and would otherwise declare a different IAB. + bool force_active_argument_buffer_resources = false; + + // Aligns each resource in an argument buffer to its assigned index value, id(N), + // by adding synthetic padding members in the argument buffer struct for any resources + // in the argument buffer that are not defined and used by the shader. This allows + // the shader to index into the correct argument in a descriptor set argument buffer + // that is shared across shaders, where not all resources in the argument buffer are + // defined in each shader. For this to work, an MSLResourceBinding must be provided for + // all descriptors in any descriptor set held in an argument buffer in the shader, and + // that MSLResourceBinding must have the basetype and count members populated correctly. + // The implementation here assumes any inline blocks in the argument buffer is provided + // in a Metal buffer, and doesn't take into consideration inline blocks that are + // optionally embedded directly into the argument buffer via add_inline_uniform_block(). + bool pad_argument_buffer_resources = false; + + // Forces the use of plain arrays, which works around certain driver bugs on certain versions + // of Intel Macbooks. See https://github.com/KhronosGroup/SPIRV-Cross/issues/1210. + // May reduce performance in scenarios where arrays are copied around as value-types. + bool force_native_arrays = false; + + // If a shader writes clip distance, also emit user varyings which + // can be read in subsequent stages. + bool enable_clip_distance_user_varying = true; + + // In a tessellation control shader, assume that more than one patch can be processed in a + // single workgroup. This requires changes to the way the InvocationId and PrimitiveId + // builtins are processed, but should result in more efficient usage of the GPU. + bool multi_patch_workgroup = false; + + // Use storage buffers instead of vertex-style attributes for tessellation evaluation + // input. This may require conversion of inputs in the generated post-tessellation + // vertex shader, but allows the use of nested arrays. + bool raw_buffer_tese_input = false; + + // If set, a vertex shader will be compiled as part of a tessellation pipeline. + // It will be translated as a compute kernel, so it can use the global invocation ID + // to index the output buffer. + bool vertex_for_tessellation = false; + + // Assume that SubpassData images have multiple layers. Layered input attachments + // are addressed relative to the Layer output from the vertex pipeline. This option + // has no effect with multiview, since all input attachments are assumed to be layered + // and will be addressed using the current ViewIndex. + bool arrayed_subpass_input = false; + + // Whether to use SIMD-group or quadgroup functions to implement group non-uniform + // operations. Some GPUs on iOS do not support the SIMD-group functions, only the + // quadgroup functions. + bool ios_use_simdgroup_functions = false; + + // If set, the subgroup size will be assumed to be one, and subgroup-related + // builtins and operations will be emitted accordingly. This mode is intended to + // be used by MoltenVK on hardware/software configurations which do not provide + // sufficient support for subgroups. + bool emulate_subgroups = false; + + // If nonzero, a fixed subgroup size to assume. Metal, similarly to VK_EXT_subgroup_size_control, + // allows the SIMD-group size (aka thread execution width) to vary depending on + // register usage and requirements. In certain circumstances--for example, a pipeline + // in MoltenVK without VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT-- + // this is undesirable. This fixes the value of the SubgroupSize builtin, instead of + // mapping it to the Metal builtin [[thread_execution_width]]. If the thread + // execution width is reduced, the extra invocations will appear to be inactive. + // If zero, the SubgroupSize will be allowed to vary, and the builtin will be mapped + // to the Metal [[thread_execution_width]] builtin. + uint32_t fixed_subgroup_size = 0; + + enum class IndexType + { + None = 0, + UInt16 = 1, + UInt32 = 2 + }; + + // The type of index in the index buffer, if present. For a compute shader, Metal + // requires specifying the indexing at pipeline creation, rather than at draw time + // as with graphics pipelines. This means we must create three different pipelines, + // for no indexing, 16-bit indices, and 32-bit indices. Each requires different + // handling for the gl_VertexIndex builtin. We may as well, then, create three + // different shaders for these three scenarios. + IndexType vertex_index_type = IndexType::None; + + // If set, a dummy [[sample_id]] input is added to a fragment shader if none is present. + // This will force the shader to run at sample rate, assuming Metal does not optimize + // the extra threads away. + bool force_sample_rate_shading = false; + + // If set, gl_HelperInvocation will be set manually whenever a fragment is discarded. + // Some Metal devices have a bug where simd_is_helper_thread() does not return true + // after a fragment has been discarded. This is a workaround that is only expected to be needed + // until the bug is fixed in Metal; it is provided as an option to allow disabling it when that occurs. + bool manual_helper_invocation_updates = true; + + // If set, extra checks will be emitted in fragment shaders to prevent writes + // from discarded fragments. Some Metal devices have a bug where writes to storage resources + // from discarded fragment threads continue to occur, despite the fragment being + // discarded. This is a workaround that is only expected to be needed until the + // bug is fixed in Metal; it is provided as an option so it can be enabled + // only when the bug is present. + bool check_discarded_frag_stores = false; + + // If set, Lod operands to OpImageSample*DrefExplicitLod for 1D and 2D array images + // will be implemented using a gradient instead of passing the level operand directly. + // Some Metal devices have a bug where the level() argument to depth2d_array::sample_compare() + // in a fragment shader is biased by some unknown amount, possibly dependent on the + // partial derivatives of the texture coordinates. This is a workaround that is only + // expected to be needed until the bug is fixed in Metal; it is provided as an option + // so it can be enabled only when the bug is present. + bool sample_dref_lod_array_as_grad = false; + + // MSL doesn't guarantee coherence between writes and subsequent reads of read_write textures. + // This inserts fences before each read of a read_write texture to ensure coherency. + // If you're sure you never rely on this, you can set this to false for a possible performance improvement. + // Note: Only Apple's GPU compiler takes advantage of the lack of coherency, so make sure to test on Apple GPUs if you disable this. + bool readwrite_texture_fences = true; + + // Metal 3.1 introduced a Metal regression bug which causes infinite recursion during + // Metal's analysis of an entry point input structure that is itself recursive. Enabling + // this option will replace the recursive input declaration with a alternate variable of + // type void*, and then cast to the correct type at the top of the entry point function. + // The bug has been reported to Apple, and will hopefully be fixed in future releases. + bool replace_recursive_inputs = false; + + // If set, manual fixups of gradient vectors for cube texture lookups will be performed. + // All released Apple Silicon GPUs to date behave incorrectly when sampling a cube texture + // with explicit gradients. They will ignore one of the three partial derivatives based + // on the selected major axis, and expect the remaining derivatives to be partially + // transformed. + bool agx_manual_cube_grad_fixup = false; + + // Metal will discard fragments with side effects under certain circumstances prematurely. + // Example: CTS test dEQP-VK.fragment_operations.early_fragment.discard_no_early_fragment_tests_depth + // Test will render a full screen quad with varying depth [0,1] for each fragment. + // Each fragment will do an operation with side effects, modify the depth value and + // discard the fragment. The test expects the fragment to be run due to: + // https://registry.khronos.org/vulkan/specs/1.0-extensions/html/vkspec.html#fragops-shader-depthreplacement + // which states that the fragment shader must be run due to replacing the depth in shader. + // However, Metal may prematurely discards fragments without executing them + // (I believe this to be due to a greedy optimization on their end) making the test fail. + // This option enforces fragment execution for such cases where the fragment has operations + // with side effects. Provided as an option hoping Metal will fix this issue in the future. + bool force_fragment_with_side_effects_execution = false; + + // If set, adds a depth pass through statement to circumvent the following issue: + // When the same depth/stencil is used as input and depth/stencil attachment, we need to + // force Metal to perform the depth/stencil write after fragment execution. Otherwise, + // Metal will write to the depth attachment before fragment execution. This happens + // if the fragment does not modify the depth value. + bool input_attachment_is_ds_attachment = false; + + bool is_ios() const + { + return platform == iOS; + } + + bool is_macos() const + { + return platform == macOS; + } + + bool use_quadgroup_operation() const + { + return is_ios() && !ios_use_simdgroup_functions; + } + + void set_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0) + { + msl_version = make_msl_version(major, minor, patch); + } + + bool supports_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0) const + { + return msl_version >= make_msl_version(major, minor, patch); + } + + static uint32_t make_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0) + { + return (major * 10000) + (minor * 100) + patch; + } + }; + + const Options &get_msl_options() const + { + return msl_options; + } + + void set_msl_options(const Options &opts) + { + msl_options = opts; + } + + // Provide feedback to calling API to allow runtime to disable pipeline + // rasterization if vertex shader requires rasterization to be disabled. + bool get_is_rasterization_disabled() const + { + return is_rasterization_disabled && (get_entry_point().model == spv::ExecutionModelVertex || + get_entry_point().model == spv::ExecutionModelTessellationControl || + get_entry_point().model == spv::ExecutionModelTessellationEvaluation); + } + + // Provide feedback to calling API to allow it to pass an auxiliary + // swizzle buffer if the shader needs it. + bool needs_swizzle_buffer() const + { + return used_swizzle_buffer; + } + + // Provide feedback to calling API to allow it to pass a buffer + // containing STORAGE_BUFFER buffer sizes to support OpArrayLength. + bool needs_buffer_size_buffer() const + { + return !buffers_requiring_array_length.empty(); + } + + bool buffer_requires_array_length(VariableID id) const + { + return buffers_requiring_array_length.count(id) != 0; + } + + // Provide feedback to calling API to allow it to pass a buffer + // containing the view mask for the current multiview subpass. + bool needs_view_mask_buffer() const + { + return msl_options.multiview && !msl_options.view_index_from_device_index; + } + + // Provide feedback to calling API to allow it to pass a buffer + // containing the dispatch base workgroup ID. + bool needs_dispatch_base_buffer() const + { + return msl_options.dispatch_base && !msl_options.supports_msl_version(1, 2); + } + + // Provide feedback to calling API to allow it to pass an output + // buffer if the shader needs it. + bool needs_output_buffer() const + { + return capture_output_to_buffer && stage_out_var_id != ID(0); + } + + // Provide feedback to calling API to allow it to pass a patch output + // buffer if the shader needs it. + bool needs_patch_output_buffer() const + { + return capture_output_to_buffer && patch_stage_out_var_id != ID(0); + } + + // Provide feedback to calling API to allow it to pass an input threadgroup + // buffer if the shader needs it. + bool needs_input_threadgroup_mem() const + { + return capture_output_to_buffer && stage_in_var_id != ID(0); + } + + explicit CompilerMSL(std::vector spirv); + CompilerMSL(const uint32_t *ir, size_t word_count); + explicit CompilerMSL(const ParsedIR &ir); + explicit CompilerMSL(ParsedIR &&ir); + + // input is a shader interface variable description used to fix up shader input variables. + // If shader inputs are provided, is_msl_shader_input_used() will return true after + // calling ::compile() if the location were used by the MSL code. + void add_msl_shader_input(const MSLShaderInterfaceVariable &input); + + // output is a shader interface variable description used to fix up shader output variables. + // If shader outputs are provided, is_msl_shader_output_used() will return true after + // calling ::compile() if the location were used by the MSL code. + void add_msl_shader_output(const MSLShaderInterfaceVariable &output); + + // resource is a resource binding to indicate the MSL buffer, + // texture or sampler index to use for a particular SPIR-V description set + // and binding. If resource bindings are provided, + // is_msl_resource_binding_used() will return true after calling ::compile() if + // the set/binding combination was used by the MSL code. + void add_msl_resource_binding(const MSLResourceBinding &resource); + + // desc_set and binding are the SPIR-V descriptor set and binding of a buffer resource + // in this shader. index is the index within the dynamic offset buffer to use. This + // function marks that resource as using a dynamic offset (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC + // or VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC). This function only has any effect if argument buffers + // are enabled. If so, the buffer will have its address adjusted at the beginning of the shader with + // an offset taken from the dynamic offset buffer. + void add_dynamic_buffer(uint32_t desc_set, uint32_t binding, uint32_t index); + + // desc_set and binding are the SPIR-V descriptor set and binding of a buffer resource + // in this shader. This function marks that resource as an inline uniform block + // (VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT). This function only has any effect if argument buffers + // are enabled. If so, the buffer block will be directly embedded into the argument + // buffer, instead of being referenced indirectly via pointer. + void add_inline_uniform_block(uint32_t desc_set, uint32_t binding); + + // When using MSL argument buffers, we can force "classic" MSL 1.0 binding schemes for certain descriptor sets. + // This corresponds to VK_KHR_push_descriptor in Vulkan. + void add_discrete_descriptor_set(uint32_t desc_set); + + // If an argument buffer is large enough, it may need to be in the device storage space rather than + // constant. Opt-in to this behavior here on a per set basis. + void set_argument_buffer_device_address_space(uint32_t desc_set, bool device_storage); + + // Query after compilation is done. This allows you to check if an input location was used by the shader. + bool is_msl_shader_input_used(uint32_t location); + + // Query after compilation is done. This allows you to check if an output location were used by the shader. + bool is_msl_shader_output_used(uint32_t location); + + // If not using add_msl_shader_input, it's possible + // that certain builtin attributes need to be automatically assigned locations. + // This is typical for tessellation builtin inputs such as tess levels, gl_Position, etc. + // This returns k_unknown_location if the location was explicitly assigned with + // add_msl_shader_input or the builtin is not used, otherwise returns N in [[attribute(N)]]. + uint32_t get_automatic_builtin_input_location(spv::BuiltIn builtin) const; + + // If not using add_msl_shader_output, it's possible + // that certain builtin attributes need to be automatically assigned locations. + // This is typical for tessellation builtin outputs such as tess levels, gl_Position, etc. + // This returns k_unknown_location if the location were explicitly assigned with + // add_msl_shader_output or the builtin were not used, otherwise returns N in [[attribute(N)]]. + uint32_t get_automatic_builtin_output_location(spv::BuiltIn builtin) const; + + // NOTE: Only resources which are remapped using add_msl_resource_binding will be reported here. + // Constexpr samplers are always assumed to be emitted. + // No specific MSLResourceBinding remapping is required for constexpr samplers as long as they are remapped + // by remap_constexpr_sampler(_by_binding). + bool is_msl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding) const; + + // This must only be called after a successful call to CompilerMSL::compile(). + // For a variable resource ID obtained through reflection API, report the automatically assigned resource index. + // If the descriptor set was part of an argument buffer, report the [[id(N)]], + // or [[buffer/texture/sampler]] binding for other resources. + // If the resource was a combined image sampler, report the image binding here, + // use the _secondary version of this call to query the sampler half of the resource. + // If no binding exists, uint32_t(-1) is returned. + uint32_t get_automatic_msl_resource_binding(uint32_t id) const; + + // Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers, in which case the + // sampler's binding is returned instead. For any other resource type, -1 is returned. + // Secondary bindings are also used for the auxillary image atomic buffer. + uint32_t get_automatic_msl_resource_binding_secondary(uint32_t id) const; + + // Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers for multiplanar images, + // in which case the second plane's binding is returned instead. For any other resource type, -1 is returned. + uint32_t get_automatic_msl_resource_binding_tertiary(uint32_t id) const; + + // Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers for triplanar images, + // in which case the third plane's binding is returned instead. For any other resource type, -1 is returned. + uint32_t get_automatic_msl_resource_binding_quaternary(uint32_t id) const; + + // Compiles the SPIR-V code into Metal Shading Language. + std::string compile() override; + + // Remap a sampler with ID to a constexpr sampler. + // Older iOS targets must use constexpr samplers in certain cases (PCF), + // so a static sampler must be used. + // The sampler will not consume a binding, but be declared in the entry point as a constexpr sampler. + // This can be used on both combined image/samplers (sampler2D) or standalone samplers. + // The remapped sampler must not be an array of samplers. + // Prefer remap_constexpr_sampler_by_binding unless you're also doing reflection anyways. + void remap_constexpr_sampler(VariableID id, const MSLConstexprSampler &sampler); + + // Same as remap_constexpr_sampler, except you provide set/binding, rather than variable ID. + // Remaps based on ID take priority over set/binding remaps. + void remap_constexpr_sampler_by_binding(uint32_t desc_set, uint32_t binding, const MSLConstexprSampler &sampler); + + // If using CompilerMSL::Options::pad_fragment_output_components, override the number of components we expect + // to use for a particular location. The default is 4 if number of components is not overridden. + void set_fragment_output_components(uint32_t location, uint32_t components); + + void set_combined_sampler_suffix(const char *suffix); + const char *get_combined_sampler_suffix() const; protected: - // An enum of SPIR-V functions that are implemented in additional - // source code that is added to the shader if necessary. - enum SPVFuncImpl : uint8_t - { - SPVFuncImplNone, - SPVFuncImplMod, - SPVFuncImplRadians, - SPVFuncImplDegrees, - SPVFuncImplFindILsb, - SPVFuncImplFindSMsb, - SPVFuncImplFindUMsb, - SPVFuncImplSSign, - SPVFuncImplArrayCopy, - SPVFuncImplArrayCopyMultidim, - SPVFuncImplTexelBufferCoords, - SPVFuncImplImage2DAtomicCoords, // Emulate texture2D atomic operations - SPVFuncImplGradientCube, - SPVFuncImplFMul, - SPVFuncImplFAdd, - SPVFuncImplFSub, - SPVFuncImplQuantizeToF16, - SPVFuncImplCubemapTo2DArrayFace, - SPVFuncImplUnsafeArray, // Allow Metal to use the array template to make arrays a value type - SPVFuncImplStorageMatrix, // Allow threadgroup construction of matrices - SPVFuncImplInverse4x4, - SPVFuncImplInverse3x3, - SPVFuncImplInverse2x2, - // It is very important that this come before *Swizzle and ChromaReconstruct*, to ensure it's - // emitted before them. - SPVFuncImplForwardArgs, - // Likewise, this must come before *Swizzle. - SPVFuncImplGetSwizzle, - SPVFuncImplTextureSwizzle, - SPVFuncImplGatherSwizzle, - SPVFuncImplGatherCompareSwizzle, - SPVFuncImplGatherConstOffsets, - SPVFuncImplGatherCompareConstOffsets, - SPVFuncImplSubgroupBroadcast, - SPVFuncImplSubgroupBroadcastFirst, - SPVFuncImplSubgroupBallot, - SPVFuncImplSubgroupBallotBitExtract, - SPVFuncImplSubgroupBallotFindLSB, - SPVFuncImplSubgroupBallotFindMSB, - SPVFuncImplSubgroupBallotBitCount, - SPVFuncImplSubgroupAllEqual, - SPVFuncImplSubgroupShuffle, - SPVFuncImplSubgroupShuffleXor, - SPVFuncImplSubgroupShuffleUp, - SPVFuncImplSubgroupShuffleDown, - SPVFuncImplQuadBroadcast, - SPVFuncImplQuadSwap, - SPVFuncImplReflectScalar, - SPVFuncImplRefractScalar, - SPVFuncImplFaceForwardScalar, - SPVFuncImplChromaReconstructNearest2Plane, - SPVFuncImplChromaReconstructNearest3Plane, - SPVFuncImplChromaReconstructLinear422CositedEven2Plane, - SPVFuncImplChromaReconstructLinear422CositedEven3Plane, - SPVFuncImplChromaReconstructLinear422Midpoint2Plane, - SPVFuncImplChromaReconstructLinear422Midpoint3Plane, - SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven2Plane, - SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven3Plane, - SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven2Plane, - SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven3Plane, - SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint2Plane, - SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint3Plane, - SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint2Plane, - SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint3Plane, - SPVFuncImplExpandITUFullRange, - SPVFuncImplExpandITUNarrowRange, - SPVFuncImplConvertYCbCrBT709, - SPVFuncImplConvertYCbCrBT601, - SPVFuncImplConvertYCbCrBT2020, - SPVFuncImplDynamicImageSampler, - SPVFuncImplRayQueryIntersectionParams, - SPVFuncImplVariableDescriptor, - SPVFuncImplVariableSizedDescriptor, - SPVFuncImplVariableDescriptorArray, - SPVFuncImplPaddedStd140, - SPVFuncImplReduceAdd, - SPVFuncImplImageFence, - SPVFuncImplTextureCast - }; - - // If the underlying resource has been used for comparison then duplicate loads of that resource must be too - // Use Metal's native frame-buffer fetch API for subpass inputs. - void emit_texture_op(const Instruction &i, bool sparse) override; - void emit_binary_ptr_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); - std::string to_ptr_expression(uint32_t id, bool register_expression_read = true); - void emit_binary_unord_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); - void emit_instruction(const Instruction &instr) override; - void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, - uint32_t count) override; - void emit_spv_amd_shader_trinary_minmax_op(uint32_t result_type, uint32_t result_id, uint32_t op, - const uint32_t *args, uint32_t count) override; - void emit_header() override; - void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override; - void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override; - void emit_subgroup_op(const Instruction &i) override; - std::string to_texture_op(const Instruction &i, bool sparse, bool *forward, - SmallVector &inherited_expressions) override; - void emit_fixup() override; - std::string to_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, - const std::string &qualifier = ""); - void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, - const std::string &qualifier = "", uint32_t base_offset = 0) override; - void emit_struct_padding_target(const SPIRType &type) override; - std::string type_to_glsl(const SPIRType &type, uint32_t id, bool member); - std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override; - void emit_block_hints(const SPIRBlock &block) override; - - // Allow Metal to use the array template to make arrays a value type - std::string type_to_array_glsl(const SPIRType &type, uint32_t variable_id) override; - std::string constant_op_expression(const SPIRConstantOp &cop) override; - - bool variable_decl_is_remapped_storage(const SPIRVariable &variable, spv::StorageClass storage) const override; - - // GCC workaround of lambdas calling protected functions (for older GCC versions) - std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0) override; - - std::string image_type_glsl(const SPIRType &type, uint32_t id, bool member) override; - std::string sampler_type(const SPIRType &type, uint32_t id, bool member); - std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override; - std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) override; - std::string to_name(uint32_t id, bool allow_alias = true) const override; - std::string to_function_name(const TextureFunctionNameArguments &args) override; - std::string to_function_args(const TextureFunctionArguments &args, bool *p_forward) override; - std::string to_initializer_expression(const SPIRVariable &var) override; - std::string to_zero_initialized_expression(uint32_t type_id) override; - - std::string unpack_expression_type(std::string expr_str, const SPIRType &type, uint32_t physical_type_id, - bool is_packed, bool row_major) override; - - // Returns true for BuiltInSampleMask because gl_SampleMask[] is an array in SPIR-V, but [[sample_mask]] is a scalar in Metal. - bool builtin_translates_to_nonarray(spv::BuiltIn builtin) const override; - - std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override; - bool emit_complex_bitcast(uint32_t result_id, uint32_t id, uint32_t op0) override; - bool skip_argument(uint32_t id) const override; - std::string to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain_is_resolved) override; - std::string to_qualifiers_glsl(uint32_t id) override; - void replace_illegal_names() override; - void declare_constant_arrays(); - - void replace_illegal_entry_point_names(); - void sync_entry_point_aliases_and_names(); - - static const std::unordered_set &get_reserved_keyword_set(); - static const std::unordered_set &get_illegal_func_names(); - - // Constant arrays of non-primitive types (i.e. matrices) won't link properly into Metal libraries - void declare_complex_constant_arrays(); - - bool is_patch_block(const SPIRType &type); - bool is_non_native_row_major_matrix(uint32_t id) override; - bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index) override; - std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type, uint32_t physical_type_id, - bool is_packed, bool relaxed) override; - - bool is_tesc_shader() const; - bool is_tese_shader() const; - - void preprocess_op_codes(); - void localize_global_variables(); - void extract_global_variables_from_functions(); - void mark_packable_structs(); - void mark_as_packable(SPIRType &type); - void mark_as_workgroup_struct(SPIRType &type); - - std::unordered_map> function_global_vars; - void extract_global_variables_from_function(uint32_t func_id, std::set &added_arg_ids, - std::unordered_set &global_var_ids, - std::unordered_set &processed_func_ids); - uint32_t add_interface_block(spv::StorageClass storage, bool patch = false); - uint32_t add_interface_block_pointer(uint32_t ib_var_id, spv::StorageClass storage); - - struct InterfaceBlockMeta - { - struct LocationMeta - { - uint32_t base_type_id = 0; - uint32_t num_components = 0; - bool flat = false; - bool noperspective = false; - bool centroid = false; - bool sample = false; - }; - std::unordered_map location_meta; - bool strip_array = false; - bool allow_local_declaration = false; - }; - - std::string to_tesc_invocation_id(); - void emit_local_masked_variable(const SPIRVariable &masked_var, bool strip_array); - void add_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, SPIRType &ib_type, - SPIRVariable &var, InterfaceBlockMeta &meta); - void add_composite_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, - SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta); - void add_plain_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, - SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta); - bool add_component_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, - SPIRVariable &var, const SPIRType &type, - InterfaceBlockMeta &meta); - void add_plain_member_variable_to_interface_block(spv::StorageClass storage, - const std::string &ib_var_ref, SPIRType &ib_type, - SPIRVariable &var, SPIRType &var_type, - uint32_t mbr_idx, InterfaceBlockMeta &meta, - const std::string &mbr_name_qual, - const std::string &var_chain_qual, - uint32_t &location, uint32_t &var_mbr_idx); - void add_composite_member_variable_to_interface_block(spv::StorageClass storage, - const std::string &ib_var_ref, SPIRType &ib_type, - SPIRVariable &var, SPIRType &var_type, - uint32_t mbr_idx, InterfaceBlockMeta &meta, - const std::string &mbr_name_qual, - const std::string &var_chain_qual, - uint32_t &location, uint32_t &var_mbr_idx, - const Bitset &interpolation_qual); - void add_tess_level_input_to_interface_block(const std::string &ib_var_ref, SPIRType &ib_type, SPIRVariable &var); - void add_tess_level_input(const std::string &base_ref, const std::string &mbr_name, SPIRVariable &var); - - void fix_up_interface_member_indices(spv::StorageClass storage, uint32_t ib_type_id); - - void mark_location_as_used_by_shader(uint32_t location, const SPIRType &type, - spv::StorageClass storage, bool fallback = false); - uint32_t ensure_correct_builtin_type(uint32_t type_id, spv::BuiltIn builtin); - uint32_t ensure_correct_input_type(uint32_t type_id, uint32_t location, uint32_t component, - uint32_t num_components, bool strip_array); - - void emit_custom_templates(); - void emit_custom_functions(); - void emit_resources(); - void emit_specialization_constants_and_structs(); - void emit_interface_block(uint32_t ib_var_id); - bool maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs); - bool is_var_runtime_size_array(const SPIRVariable &var) const; - uint32_t get_resource_array_size(const SPIRType &type, uint32_t id) const; - - void fix_up_shader_inputs_outputs(); - - std::string func_type_decl(SPIRType &type); - std::string entry_point_args_classic(bool append_comma); - std::string entry_point_args_argument_buffer(bool append_comma); - std::string entry_point_arg_stage_in(); - void entry_point_args_builtin(std::string &args); - void entry_point_args_discrete_descriptors(std::string &args); - std::string append_member_name(const std::string &qualifier, const SPIRType &type, uint32_t index); - std::string ensure_valid_name(std::string name, std::string pfx); - std::string to_sampler_expression(uint32_t id); - std::string to_swizzle_expression(uint32_t id); - std::string to_buffer_size_expression(uint32_t id); - bool is_sample_rate() const; - bool is_intersection_query() const; - bool is_direct_input_builtin(spv::BuiltIn builtin); - std::string builtin_qualifier(spv::BuiltIn builtin); - std::string builtin_type_decl(spv::BuiltIn builtin, uint32_t id = 0); - std::string built_in_func_arg(spv::BuiltIn builtin, bool prefix_comma); - std::string member_attribute_qualifier(const SPIRType &type, uint32_t index); - std::string member_location_attribute_qualifier(const SPIRType &type, uint32_t index); - std::string argument_decl(const SPIRFunction::Parameter &arg); - const char *descriptor_address_space(uint32_t id, spv::StorageClass storage, const char *plain_address_space) const; - std::string round_fp_tex_coords(std::string tex_coords, bool coord_is_fp); - uint32_t get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype, uint32_t plane = 0); - uint32_t get_member_location(uint32_t type_id, uint32_t index, uint32_t *comp = nullptr) const; - uint32_t get_or_allocate_builtin_input_member_location(spv::BuiltIn builtin, - uint32_t type_id, uint32_t index, uint32_t *comp = nullptr); - uint32_t get_or_allocate_builtin_output_member_location(spv::BuiltIn builtin, - uint32_t type_id, uint32_t index, uint32_t *comp = nullptr); - - uint32_t get_physical_tess_level_array_size(spv::BuiltIn builtin) const; - - uint32_t get_physical_type_stride(const SPIRType &type) const override; - - // MSL packing rules. These compute the effective packing rules as observed by the MSL compiler in the MSL output. - // These values can change depending on various extended decorations which control packing rules. - // We need to make these rules match up with SPIR-V declared rules. - uint32_t get_declared_type_size_msl(const SPIRType &type, bool packed, bool row_major) const; - uint32_t get_declared_type_array_stride_msl(const SPIRType &type, bool packed, bool row_major) const; - uint32_t get_declared_type_matrix_stride_msl(const SPIRType &type, bool packed, bool row_major) const; - uint32_t get_declared_type_alignment_msl(const SPIRType &type, bool packed, bool row_major) const; - - uint32_t get_declared_struct_member_size_msl(const SPIRType &struct_type, uint32_t index) const; - uint32_t get_declared_struct_member_array_stride_msl(const SPIRType &struct_type, uint32_t index) const; - uint32_t get_declared_struct_member_matrix_stride_msl(const SPIRType &struct_type, uint32_t index) const; - uint32_t get_declared_struct_member_alignment_msl(const SPIRType &struct_type, uint32_t index) const; - - uint32_t get_declared_input_size_msl(const SPIRType &struct_type, uint32_t index) const; - uint32_t get_declared_input_array_stride_msl(const SPIRType &struct_type, uint32_t index) const; - uint32_t get_declared_input_matrix_stride_msl(const SPIRType &struct_type, uint32_t index) const; - uint32_t get_declared_input_alignment_msl(const SPIRType &struct_type, uint32_t index) const; - - const SPIRType &get_physical_member_type(const SPIRType &struct_type, uint32_t index) const; - SPIRType get_presumed_input_type(const SPIRType &struct_type, uint32_t index) const; - - uint32_t get_declared_struct_size_msl(const SPIRType &struct_type, bool ignore_alignment = false, - bool ignore_padding = false) const; - - std::string to_component_argument(uint32_t id); - void align_struct(SPIRType &ib_type, std::unordered_set &aligned_structs); - void mark_scalar_layout_structs(const SPIRType &ib_type); - void mark_struct_members_packed(const SPIRType &type); - void ensure_member_packing_rules_msl(SPIRType &ib_type, uint32_t index); - bool validate_member_packing_rules_msl(const SPIRType &type, uint32_t index) const; - std::string get_argument_address_space(const SPIRVariable &argument); - std::string get_type_address_space(const SPIRType &type, uint32_t id, bool argument = false); - static bool decoration_flags_signal_volatile(const Bitset &flags); - const char *to_restrict(uint32_t id, bool space); - SPIRType &get_stage_in_struct_type(); - SPIRType &get_stage_out_struct_type(); - SPIRType &get_patch_stage_in_struct_type(); - SPIRType &get_patch_stage_out_struct_type(); - std::string get_tess_factor_struct_name(); - SPIRType &get_uint_type(); - uint32_t get_uint_type_id(); - void emit_atomic_func_op(uint32_t result_type, uint32_t result_id, const char *op, spv::Op opcode, - uint32_t mem_order_1, uint32_t mem_order_2, bool has_mem_order_2, uint32_t op0, uint32_t op1 = 0, - bool op1_is_pointer = false, bool op1_is_literal = false, uint32_t op2 = 0); - const char *get_memory_order(uint32_t spv_mem_sem); - void add_pragma_line(const std::string &line); - void add_typedef_line(const std::string &line); - void emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem); - bool emit_array_copy(const char *expr, uint32_t lhs_id, uint32_t rhs_id, - spv::StorageClass lhs_storage, spv::StorageClass rhs_storage) override; - void build_implicit_builtins(); - uint32_t build_constant_uint_array_pointer(); - void emit_entry_point_declarations() override; - bool uses_explicit_early_fragment_test(); - - uint32_t builtin_frag_coord_id = 0; - uint32_t builtin_sample_id_id = 0; - uint32_t builtin_sample_mask_id = 0; - uint32_t builtin_helper_invocation_id = 0; - uint32_t builtin_vertex_idx_id = 0; - uint32_t builtin_base_vertex_id = 0; - uint32_t builtin_instance_idx_id = 0; - uint32_t builtin_base_instance_id = 0; - uint32_t builtin_view_idx_id = 0; - uint32_t builtin_layer_id = 0; - uint32_t builtin_invocation_id_id = 0; - uint32_t builtin_primitive_id_id = 0; - uint32_t builtin_subgroup_invocation_id_id = 0; - uint32_t builtin_subgroup_size_id = 0; - uint32_t builtin_dispatch_base_id = 0; - uint32_t builtin_stage_input_size_id = 0; - uint32_t builtin_local_invocation_index_id = 0; - uint32_t builtin_workgroup_size_id = 0; - uint32_t builtin_frag_depth_id = 0; - uint32_t swizzle_buffer_id = 0; - uint32_t buffer_size_buffer_id = 0; - uint32_t view_mask_buffer_id = 0; - uint32_t dynamic_offsets_buffer_id = 0; - uint32_t uint_type_id = 0; - uint32_t argument_buffer_padding_buffer_type_id = 0; - uint32_t argument_buffer_padding_image_type_id = 0; - uint32_t argument_buffer_padding_sampler_type_id = 0; - - bool does_shader_write_sample_mask = false; - bool frag_shader_needs_discard_checks = false; - - void cast_to_variable_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) override; - void cast_from_variable_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) override; - void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression) override; - - void analyze_sampled_image_usage(); - - bool access_chain_needs_stage_io_builtin_translation(uint32_t base) override; - bool prepare_access_chain_for_scalar_access(std::string &expr, const SPIRType &type, spv::StorageClass storage, - bool &is_packed) override; - void fix_up_interpolant_access_chain(const uint32_t *ops, uint32_t length); - void check_physical_type_cast(std::string &expr, const SPIRType *type, uint32_t physical_type) override; - - bool emit_tessellation_access_chain(const uint32_t *ops, uint32_t length); - bool emit_tessellation_io_load(uint32_t result_type, uint32_t id, uint32_t ptr); - bool is_out_of_bounds_tessellation_level(uint32_t id_lhs); - - void ensure_builtin(spv::StorageClass storage, spv::BuiltIn builtin); - - void mark_implicit_builtin(spv::StorageClass storage, spv::BuiltIn builtin, uint32_t id); - - std::string convert_to_f32(const std::string &expr, uint32_t components); - - Options msl_options; - std::set spv_function_implementations; - // Must be ordered to ensure declarations are in a specific order. - std::map inputs_by_location; - std::unordered_map inputs_by_builtin; - std::map outputs_by_location; - std::unordered_map outputs_by_builtin; - std::unordered_set location_inputs_in_use; - std::unordered_set location_inputs_in_use_fallback; - std::unordered_set location_outputs_in_use; - std::unordered_set location_outputs_in_use_fallback; - std::unordered_map fragment_output_components; - std::unordered_map builtin_to_automatic_input_location; - std::unordered_map builtin_to_automatic_output_location; - std::set pragma_lines; - std::set typedef_lines; - SmallVector vars_needing_early_declaration; - - std::unordered_map, InternalHasher> resource_bindings; - std::unordered_map resource_arg_buff_idx_to_binding_number; - - uint32_t next_metal_resource_index_buffer = 0; - uint32_t next_metal_resource_index_texture = 0; - uint32_t next_metal_resource_index_sampler = 0; - // Intentionally uninitialized, works around MSVC 2013 bug. - uint32_t next_metal_resource_ids[kMaxArgumentBuffers]; - - VariableID stage_in_var_id = 0; - VariableID stage_out_var_id = 0; - VariableID patch_stage_in_var_id = 0; - VariableID patch_stage_out_var_id = 0; - VariableID stage_in_ptr_var_id = 0; - VariableID stage_out_ptr_var_id = 0; - VariableID tess_level_inner_var_id = 0; - VariableID tess_level_outer_var_id = 0; - VariableID stage_out_masked_builtin_type_id = 0; - - // Handle HLSL-style 0-based vertex/instance index. - enum class TriState - { - Neutral, - No, - Yes - }; - TriState needs_base_vertex_arg = TriState::Neutral; - TriState needs_base_instance_arg = TriState::Neutral; - - bool has_sampled_images = false; - bool builtin_declaration = false; // Handle HLSL-style 0-based vertex/instance index. - - bool is_using_builtin_array = false; // Force the use of C style array declaration. - bool using_builtin_array() const; - - bool is_rasterization_disabled = false; - bool capture_output_to_buffer = false; - bool needs_swizzle_buffer_def = false; - bool used_swizzle_buffer = false; - bool added_builtin_tess_level = false; - bool needs_subgroup_invocation_id = false; - bool needs_subgroup_size = false; - bool needs_sample_id = false; - bool needs_helper_invocation = false; - bool writes_to_depth = false; - std::string qual_pos_var_name; - std::string stage_in_var_name = "in"; - std::string stage_out_var_name = "out"; - std::string patch_stage_in_var_name = "patchIn"; - std::string patch_stage_out_var_name = "patchOut"; - std::string sampler_name_suffix = "Smplr"; - std::string swizzle_name_suffix = "Swzl"; - std::string buffer_size_name_suffix = "BufferSize"; - std::string plane_name_suffix = "Plane"; - std::string input_wg_var_name = "gl_in"; - std::string input_buffer_var_name = "spvIn"; - std::string output_buffer_var_name = "spvOut"; - std::string patch_input_buffer_var_name = "spvPatchIn"; - std::string patch_output_buffer_var_name = "spvPatchOut"; - std::string tess_factor_buffer_var_name = "spvTessLevel"; - std::string index_buffer_var_name = "spvIndices"; - spv::Op previous_instruction_opcode = spv::OpNop; - - // Must be ordered since declaration is in a specific order. - std::map constexpr_samplers_by_id; - std::unordered_map constexpr_samplers_by_binding; - const MSLConstexprSampler *find_constexpr_sampler(uint32_t id) const; - - std::unordered_set buffers_requiring_array_length; - SmallVector> buffer_aliases_argument; - SmallVector buffer_aliases_discrete; - std::unordered_set atomic_image_vars_emulated; // Emulate texture2D atomic operations - std::unordered_set pull_model_inputs; - std::unordered_set recursive_inputs; - - SmallVector entry_point_bindings; - - // Must be ordered since array is in a specific order. - std::map> buffers_requiring_dynamic_offset; - - SmallVector disabled_frag_outputs; - - std::unordered_set inline_uniform_blocks; - - uint32_t argument_buffer_ids[kMaxArgumentBuffers]; - uint32_t argument_buffer_discrete_mask = 0; - uint32_t argument_buffer_device_storage_mask = 0; - - void emit_argument_buffer_aliased_descriptor(const SPIRVariable &aliased_var, - const SPIRVariable &base_var); - - void analyze_argument_buffers(); - bool descriptor_set_is_argument_buffer(uint32_t desc_set) const; - const MSLResourceBinding &get_argument_buffer_resource(uint32_t desc_set, uint32_t arg_idx) const; - void add_argument_buffer_padding_buffer_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind); - void add_argument_buffer_padding_image_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind); - void add_argument_buffer_padding_sampler_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind); - void add_argument_buffer_padding_type(uint32_t mbr_type_id, SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, uint32_t count); - - uint32_t get_target_components_for_fragment_location(uint32_t location) const; - uint32_t build_extended_vector_type(uint32_t type_id, uint32_t components, - SPIRType::BaseType basetype = SPIRType::Unknown); - uint32_t build_msl_interpolant_type(uint32_t type_id, bool is_noperspective); - - bool suppress_missing_prototypes = false; - bool suppress_incompatible_pointer_types_discard_qualifiers = false; - - void add_spv_func_and_recompile(SPVFuncImpl spv_func); - - void activate_argument_buffer_resources(); - - bool type_is_msl_framebuffer_fetch(const SPIRType &type) const; - bool is_supported_argument_buffer_type(const SPIRType &type) const; - - bool variable_storage_requires_stage_io(spv::StorageClass storage) const; - - bool needs_manual_helper_invocation_updates() const - { - return msl_options.manual_helper_invocation_updates && msl_options.supports_msl_version(2, 3); - } - bool needs_frag_discard_checks() const - { - return get_execution_model() == spv::ExecutionModelFragment && msl_options.supports_msl_version(2, 3) && - msl_options.check_discarded_frag_stores && frag_shader_needs_discard_checks; - } - - bool has_additional_fixed_sample_mask() const { return msl_options.additional_fixed_sample_mask != 0xffffffff; } - std::string additional_fixed_sample_mask_str() const; - - // OpcodeHandler that handles several MSL preprocessing operations. - struct OpCodePreprocessor : OpcodeHandler - { - OpCodePreprocessor(CompilerMSL &compiler_) - : compiler(compiler_) - { - } - - bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; - CompilerMSL::SPVFuncImpl get_spv_func_impl(spv::Op opcode, const uint32_t *args); - void check_resource_write(uint32_t var_id); - - CompilerMSL &compiler; - std::unordered_map result_types; - std::unordered_map image_pointers_emulated; // Emulate texture2D atomic operations - bool suppress_missing_prototypes = false; - bool uses_atomics = false; - bool uses_image_write = false; - bool uses_buffer_write = false; - bool uses_discard = false; - bool needs_subgroup_invocation_id = false; - bool needs_subgroup_size = false; - bool needs_sample_id = false; - bool needs_helper_invocation = false; - }; - - // OpcodeHandler that scans for uses of sampled images - struct SampledImageScanner : OpcodeHandler - { - SampledImageScanner(CompilerMSL &compiler_) - : compiler(compiler_) - { - } - - bool handle(spv::Op opcode, const uint32_t *args, uint32_t) override; - - CompilerMSL &compiler; - }; - - // Sorts the members of a SPIRType and associated Meta info based on a settable sorting - // aspect, which defines which aspect of the struct members will be used to sort them. - // Regardless of the sorting aspect, built-in members always appear at the end of the struct. - struct MemberSorter - { - enum SortAspect - { - LocationThenBuiltInType, - Offset - }; - - void sort(); - bool operator()(uint32_t mbr_idx1, uint32_t mbr_idx2); - MemberSorter(SPIRType &t, Meta &m, SortAspect sa); - - SPIRType &type; - Meta &meta; - SortAspect sort_aspect; - }; + // An enum of SPIR-V functions that are implemented in additional + // source code that is added to the shader if necessary. + enum SPVFuncImpl : uint8_t + { + SPVFuncImplNone, + SPVFuncImplMod, + SPVFuncImplRadians, + SPVFuncImplDegrees, + SPVFuncImplFindILsb, + SPVFuncImplFindSMsb, + SPVFuncImplFindUMsb, + SPVFuncImplSSign, + SPVFuncImplArrayCopy, + SPVFuncImplArrayCopyMultidim, + SPVFuncImplTexelBufferCoords, + SPVFuncImplImage2DAtomicCoords, // Emulate texture2D atomic operations + SPVFuncImplGradientCube, + SPVFuncImplFMul, + SPVFuncImplFAdd, + SPVFuncImplFSub, + SPVFuncImplQuantizeToF16, + SPVFuncImplCubemapTo2DArrayFace, + SPVFuncImplUnsafeArray, // Allow Metal to use the array template to make arrays a value type + SPVFuncImplStorageMatrix, // Allow threadgroup construction of matrices + SPVFuncImplInverse4x4, + SPVFuncImplInverse3x3, + SPVFuncImplInverse2x2, + // It is very important that this come before *Swizzle and ChromaReconstruct*, to ensure it's + // emitted before them. + SPVFuncImplForwardArgs, + // Likewise, this must come before *Swizzle. + SPVFuncImplGetSwizzle, + SPVFuncImplTextureSwizzle, + SPVFuncImplGatherSwizzle, + SPVFuncImplGatherCompareSwizzle, + SPVFuncImplGatherConstOffsets, + SPVFuncImplGatherCompareConstOffsets, + SPVFuncImplSubgroupBroadcast, + SPVFuncImplSubgroupBroadcastFirst, + SPVFuncImplSubgroupBallot, + SPVFuncImplSubgroupBallotBitExtract, + SPVFuncImplSubgroupBallotFindLSB, + SPVFuncImplSubgroupBallotFindMSB, + SPVFuncImplSubgroupBallotBitCount, + SPVFuncImplSubgroupAllEqual, + SPVFuncImplSubgroupShuffle, + SPVFuncImplSubgroupShuffleXor, + SPVFuncImplSubgroupShuffleUp, + SPVFuncImplSubgroupShuffleDown, + SPVFuncImplQuadBroadcast, + SPVFuncImplQuadSwap, + SPVFuncImplReflectScalar, + SPVFuncImplRefractScalar, + SPVFuncImplFaceForwardScalar, + SPVFuncImplChromaReconstructNearest2Plane, + SPVFuncImplChromaReconstructNearest3Plane, + SPVFuncImplChromaReconstructLinear422CositedEven2Plane, + SPVFuncImplChromaReconstructLinear422CositedEven3Plane, + SPVFuncImplChromaReconstructLinear422Midpoint2Plane, + SPVFuncImplChromaReconstructLinear422Midpoint3Plane, + SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven2Plane, + SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven3Plane, + SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven2Plane, + SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven3Plane, + SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint2Plane, + SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint3Plane, + SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint2Plane, + SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint3Plane, + SPVFuncImplExpandITUFullRange, + SPVFuncImplExpandITUNarrowRange, + SPVFuncImplConvertYCbCrBT709, + SPVFuncImplConvertYCbCrBT601, + SPVFuncImplConvertYCbCrBT2020, + SPVFuncImplDynamicImageSampler, + SPVFuncImplRayQueryIntersectionParams, + SPVFuncImplVariableDescriptor, + SPVFuncImplVariableSizedDescriptor, + SPVFuncImplVariableDescriptorArray, + SPVFuncImplPaddedStd140, + SPVFuncImplReduceAdd, + SPVFuncImplImageFence, + SPVFuncImplTextureCast + }; + + // If the underlying resource has been used for comparison then duplicate loads of that resource must be too + // Use Metal's native frame-buffer fetch API for subpass inputs. + void emit_texture_op(const Instruction &i, bool sparse) override; + void emit_binary_ptr_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); + std::string to_ptr_expression(uint32_t id, bool register_expression_read = true); + void emit_binary_unord_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); + void emit_instruction(const Instruction &instr) override; + void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, + uint32_t count) override; + void emit_spv_amd_shader_trinary_minmax_op(uint32_t result_type, uint32_t result_id, uint32_t op, + const uint32_t *args, uint32_t count) override; + void emit_header() override; + void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override; + void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override; + void emit_subgroup_op(const Instruction &i) override; + std::string to_texture_op(const Instruction &i, bool sparse, bool *forward, + SmallVector &inherited_expressions) override; + void emit_fixup() override; + std::string to_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, + const std::string &qualifier = ""); + void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, + const std::string &qualifier = "", uint32_t base_offset = 0) override; + void emit_struct_padding_target(const SPIRType &type) override; + std::string type_to_glsl(const SPIRType &type, uint32_t id, bool member); + std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override; + void emit_block_hints(const SPIRBlock &block) override; + + // Allow Metal to use the array template to make arrays a value type + std::string type_to_array_glsl(const SPIRType &type, uint32_t variable_id) override; + std::string constant_op_expression(const SPIRConstantOp &cop) override; + + bool variable_decl_is_remapped_storage(const SPIRVariable &variable, spv::StorageClass storage) const override; + + // GCC workaround of lambdas calling protected functions (for older GCC versions) + std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0) override; + + std::string image_type_glsl(const SPIRType &type, uint32_t id, bool member) override; + std::string sampler_type(const SPIRType &type, uint32_t id, bool member); + std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override; + std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) override; + std::string to_name(uint32_t id, bool allow_alias = true) const override; + std::string to_function_name(const TextureFunctionNameArguments &args) override; + std::string to_function_args(const TextureFunctionArguments &args, bool *p_forward) override; + std::string to_initializer_expression(const SPIRVariable &var) override; + std::string to_zero_initialized_expression(uint32_t type_id) override; + + std::string unpack_expression_type(std::string expr_str, const SPIRType &type, uint32_t physical_type_id, + bool is_packed, bool row_major) override; + + // Returns true for BuiltInSampleMask because gl_SampleMask[] is an array in SPIR-V, but [[sample_mask]] is a scalar in Metal. + bool builtin_translates_to_nonarray(spv::BuiltIn builtin) const override; + + std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override; + bool emit_complex_bitcast(uint32_t result_id, uint32_t id, uint32_t op0) override; + bool skip_argument(uint32_t id) const override; + std::string to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain_is_resolved) override; + std::string to_qualifiers_glsl(uint32_t id) override; + void replace_illegal_names() override; + void declare_constant_arrays(); + + void replace_illegal_entry_point_names(); + void sync_entry_point_aliases_and_names(); + + static const std::unordered_set &get_reserved_keyword_set(); + static const std::unordered_set &get_illegal_func_names(); + + // Constant arrays of non-primitive types (i.e. matrices) won't link properly into Metal libraries + void declare_complex_constant_arrays(); + + bool is_patch_block(const SPIRType &type); + bool is_non_native_row_major_matrix(uint32_t id) override; + bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index) override; + std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type, uint32_t physical_type_id, + bool is_packed, bool relaxed) override; + + bool is_tesc_shader() const; + bool is_tese_shader() const; + + void preprocess_op_codes(); + void localize_global_variables(); + void extract_global_variables_from_functions(); + void mark_packable_structs(); + void mark_as_packable(SPIRType &type); + void mark_as_workgroup_struct(SPIRType &type); + + std::unordered_map> function_global_vars; + void extract_global_variables_from_function(uint32_t func_id, std::set &added_arg_ids, + std::unordered_set &global_var_ids, + std::unordered_set &processed_func_ids); + uint32_t add_interface_block(spv::StorageClass storage, bool patch = false); + uint32_t add_interface_block_pointer(uint32_t ib_var_id, spv::StorageClass storage); + + struct InterfaceBlockMeta + { + struct LocationMeta + { + uint32_t base_type_id = 0; + uint32_t num_components = 0; + bool flat = false; + bool noperspective = false; + bool centroid = false; + bool sample = false; + }; + std::unordered_map location_meta; + bool strip_array = false; + bool allow_local_declaration = false; + }; + + std::string to_tesc_invocation_id(); + void emit_local_masked_variable(const SPIRVariable &masked_var, bool strip_array); + void add_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, SPIRType &ib_type, + SPIRVariable &var, InterfaceBlockMeta &meta); + void add_composite_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, + SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta); + void add_plain_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, + SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta); + bool add_component_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, + SPIRVariable &var, const SPIRType &type, + InterfaceBlockMeta &meta); + void add_plain_member_variable_to_interface_block(spv::StorageClass storage, + const std::string &ib_var_ref, SPIRType &ib_type, + SPIRVariable &var, SPIRType &var_type, + uint32_t mbr_idx, InterfaceBlockMeta &meta, + const std::string &mbr_name_qual, + const std::string &var_chain_qual, + uint32_t &location, uint32_t &var_mbr_idx); + void add_composite_member_variable_to_interface_block(spv::StorageClass storage, + const std::string &ib_var_ref, SPIRType &ib_type, + SPIRVariable &var, SPIRType &var_type, + uint32_t mbr_idx, InterfaceBlockMeta &meta, + const std::string &mbr_name_qual, + const std::string &var_chain_qual, + uint32_t &location, uint32_t &var_mbr_idx, + const Bitset &interpolation_qual); + void add_tess_level_input_to_interface_block(const std::string &ib_var_ref, SPIRType &ib_type, SPIRVariable &var); + void add_tess_level_input(const std::string &base_ref, const std::string &mbr_name, SPIRVariable &var); + + void fix_up_interface_member_indices(spv::StorageClass storage, uint32_t ib_type_id); + + void mark_location_as_used_by_shader(uint32_t location, const SPIRType &type, + spv::StorageClass storage, bool fallback = false); + uint32_t ensure_correct_builtin_type(uint32_t type_id, spv::BuiltIn builtin); + uint32_t ensure_correct_input_type(uint32_t type_id, uint32_t location, uint32_t component, + uint32_t num_components, bool strip_array); + + void emit_custom_templates(); + void emit_custom_functions(); + void emit_resources(); + void emit_specialization_constants_and_structs(); + void emit_interface_block(uint32_t ib_var_id); + bool maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs); + bool is_var_runtime_size_array(const SPIRVariable &var) const; + uint32_t get_resource_array_size(const SPIRType &type, uint32_t id) const; + + void fix_up_shader_inputs_outputs(); + + std::string func_type_decl(SPIRType &type); + std::string entry_point_args_classic(bool append_comma); + std::string entry_point_args_argument_buffer(bool append_comma); + std::string entry_point_arg_stage_in(); + void entry_point_args_builtin(std::string &args); + void entry_point_args_discrete_descriptors(std::string &args); + std::string append_member_name(const std::string &qualifier, const SPIRType &type, uint32_t index); + std::string ensure_valid_name(std::string name, std::string pfx); + std::string to_sampler_expression(uint32_t id); + std::string to_swizzle_expression(uint32_t id); + std::string to_buffer_size_expression(uint32_t id); + bool is_sample_rate() const; + bool is_intersection_query() const; + bool is_direct_input_builtin(spv::BuiltIn builtin); + std::string builtin_qualifier(spv::BuiltIn builtin); + std::string builtin_type_decl(spv::BuiltIn builtin, uint32_t id = 0); + std::string built_in_func_arg(spv::BuiltIn builtin, bool prefix_comma); + std::string member_attribute_qualifier(const SPIRType &type, uint32_t index); + std::string member_location_attribute_qualifier(const SPIRType &type, uint32_t index); + std::string argument_decl(const SPIRFunction::Parameter &arg); + const char *descriptor_address_space(uint32_t id, spv::StorageClass storage, const char *plain_address_space) const; + std::string round_fp_tex_coords(std::string tex_coords, bool coord_is_fp); + uint32_t get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype, uint32_t plane = 0); + uint32_t get_member_location(uint32_t type_id, uint32_t index, uint32_t *comp = nullptr) const; + uint32_t get_or_allocate_builtin_input_member_location(spv::BuiltIn builtin, + uint32_t type_id, uint32_t index, uint32_t *comp = nullptr); + uint32_t get_or_allocate_builtin_output_member_location(spv::BuiltIn builtin, + uint32_t type_id, uint32_t index, uint32_t *comp = nullptr); + + uint32_t get_physical_tess_level_array_size(spv::BuiltIn builtin) const; + + uint32_t get_physical_type_stride(const SPIRType &type) const override; + + // MSL packing rules. These compute the effective packing rules as observed by the MSL compiler in the MSL output. + // These values can change depending on various extended decorations which control packing rules. + // We need to make these rules match up with SPIR-V declared rules. + uint32_t get_declared_type_size_msl(const SPIRType &type, bool packed, bool row_major) const; + uint32_t get_declared_type_array_stride_msl(const SPIRType &type, bool packed, bool row_major) const; + uint32_t get_declared_type_matrix_stride_msl(const SPIRType &type, bool packed, bool row_major) const; + uint32_t get_declared_type_alignment_msl(const SPIRType &type, bool packed, bool row_major) const; + + uint32_t get_declared_struct_member_size_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_struct_member_array_stride_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_struct_member_matrix_stride_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_struct_member_alignment_msl(const SPIRType &struct_type, uint32_t index) const; + + uint32_t get_declared_input_size_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_input_array_stride_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_input_matrix_stride_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_input_alignment_msl(const SPIRType &struct_type, uint32_t index) const; + + const SPIRType &get_physical_member_type(const SPIRType &struct_type, uint32_t index) const; + SPIRType get_presumed_input_type(const SPIRType &struct_type, uint32_t index) const; + + uint32_t get_declared_struct_size_msl(const SPIRType &struct_type, bool ignore_alignment = false, + bool ignore_padding = false) const; + + std::string to_component_argument(uint32_t id); + void align_struct(SPIRType &ib_type, std::unordered_set &aligned_structs); + void mark_scalar_layout_structs(const SPIRType &ib_type); + void mark_struct_members_packed(const SPIRType &type); + void ensure_member_packing_rules_msl(SPIRType &ib_type, uint32_t index); + bool validate_member_packing_rules_msl(const SPIRType &type, uint32_t index) const; + std::string get_argument_address_space(const SPIRVariable &argument); + std::string get_type_address_space(const SPIRType &type, uint32_t id, bool argument = false); + static bool decoration_flags_signal_volatile(const Bitset &flags); + const char *to_restrict(uint32_t id, bool space); + SPIRType &get_stage_in_struct_type(); + SPIRType &get_stage_out_struct_type(); + SPIRType &get_patch_stage_in_struct_type(); + SPIRType &get_patch_stage_out_struct_type(); + std::string get_tess_factor_struct_name(); + SPIRType &get_uint_type(); + uint32_t get_uint_type_id(); + void emit_atomic_func_op(uint32_t result_type, uint32_t result_id, const char *op, spv::Op opcode, + uint32_t mem_order_1, uint32_t mem_order_2, bool has_mem_order_2, uint32_t op0, uint32_t op1 = 0, + bool op1_is_pointer = false, bool op1_is_literal = false, uint32_t op2 = 0); + const char *get_memory_order(uint32_t spv_mem_sem); + void add_pragma_line(const std::string &line); + void add_typedef_line(const std::string &line); + void emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem); + bool emit_array_copy(const char *expr, uint32_t lhs_id, uint32_t rhs_id, + spv::StorageClass lhs_storage, spv::StorageClass rhs_storage) override; + void build_implicit_builtins(); + uint32_t build_constant_uint_array_pointer(); + void emit_entry_point_declarations() override; + bool uses_explicit_early_fragment_test(); + + uint32_t builtin_frag_coord_id = 0; + uint32_t builtin_sample_id_id = 0; + uint32_t builtin_sample_mask_id = 0; + uint32_t builtin_helper_invocation_id = 0; + uint32_t builtin_vertex_idx_id = 0; + uint32_t builtin_base_vertex_id = 0; + uint32_t builtin_instance_idx_id = 0; + uint32_t builtin_base_instance_id = 0; + uint32_t builtin_view_idx_id = 0; + uint32_t builtin_layer_id = 0; + uint32_t builtin_invocation_id_id = 0; + uint32_t builtin_primitive_id_id = 0; + uint32_t builtin_subgroup_invocation_id_id = 0; + uint32_t builtin_subgroup_size_id = 0; + uint32_t builtin_dispatch_base_id = 0; + uint32_t builtin_stage_input_size_id = 0; + uint32_t builtin_local_invocation_index_id = 0; + uint32_t builtin_workgroup_size_id = 0; + uint32_t builtin_frag_depth_id = 0; + uint32_t swizzle_buffer_id = 0; + uint32_t buffer_size_buffer_id = 0; + uint32_t view_mask_buffer_id = 0; + uint32_t dynamic_offsets_buffer_id = 0; + uint32_t uint_type_id = 0; + uint32_t argument_buffer_padding_buffer_type_id = 0; + uint32_t argument_buffer_padding_image_type_id = 0; + uint32_t argument_buffer_padding_sampler_type_id = 0; + + bool does_shader_write_sample_mask = false; + bool frag_shader_needs_discard_checks = false; + + void cast_to_variable_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) override; + void cast_from_variable_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) override; + void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression) override; + + void analyze_sampled_image_usage(); + + bool access_chain_needs_stage_io_builtin_translation(uint32_t base) override; + bool prepare_access_chain_for_scalar_access(std::string &expr, const SPIRType &type, spv::StorageClass storage, + bool &is_packed) override; + void fix_up_interpolant_access_chain(const uint32_t *ops, uint32_t length); + void check_physical_type_cast(std::string &expr, const SPIRType *type, uint32_t physical_type) override; + + bool emit_tessellation_access_chain(const uint32_t *ops, uint32_t length); + bool emit_tessellation_io_load(uint32_t result_type, uint32_t id, uint32_t ptr); + bool is_out_of_bounds_tessellation_level(uint32_t id_lhs); + + void ensure_builtin(spv::StorageClass storage, spv::BuiltIn builtin); + + void mark_implicit_builtin(spv::StorageClass storage, spv::BuiltIn builtin, uint32_t id); + + std::string convert_to_f32(const std::string &expr, uint32_t components); + + Options msl_options; + std::set spv_function_implementations; + // Must be ordered to ensure declarations are in a specific order. + std::map inputs_by_location; + std::unordered_map inputs_by_builtin; + std::map outputs_by_location; + std::unordered_map outputs_by_builtin; + std::unordered_set location_inputs_in_use; + std::unordered_set location_inputs_in_use_fallback; + std::unordered_set location_outputs_in_use; + std::unordered_set location_outputs_in_use_fallback; + std::unordered_map fragment_output_components; + std::unordered_map builtin_to_automatic_input_location; + std::unordered_map builtin_to_automatic_output_location; + std::set pragma_lines; + std::set typedef_lines; + SmallVector vars_needing_early_declaration; + + std::unordered_map, InternalHasher> resource_bindings; + std::unordered_map resource_arg_buff_idx_to_binding_number; + + uint32_t next_metal_resource_index_buffer = 0; + uint32_t next_metal_resource_index_texture = 0; + uint32_t next_metal_resource_index_sampler = 0; + // Intentionally uninitialized, works around MSVC 2013 bug. + uint32_t next_metal_resource_ids[kMaxArgumentBuffers]; + + VariableID stage_in_var_id = 0; + VariableID stage_out_var_id = 0; + VariableID patch_stage_in_var_id = 0; + VariableID patch_stage_out_var_id = 0; + VariableID stage_in_ptr_var_id = 0; + VariableID stage_out_ptr_var_id = 0; + VariableID tess_level_inner_var_id = 0; + VariableID tess_level_outer_var_id = 0; + VariableID stage_out_masked_builtin_type_id = 0; + + // Handle HLSL-style 0-based vertex/instance index. + enum class TriState + { + Neutral, + No, + Yes + }; + TriState needs_base_vertex_arg = TriState::Neutral; + TriState needs_base_instance_arg = TriState::Neutral; + + bool has_sampled_images = false; + bool builtin_declaration = false; // Handle HLSL-style 0-based vertex/instance index. + + bool is_using_builtin_array = false; // Force the use of C style array declaration. + bool using_builtin_array() const; + + bool is_rasterization_disabled = false; + bool capture_output_to_buffer = false; + bool needs_swizzle_buffer_def = false; + bool used_swizzle_buffer = false; + bool added_builtin_tess_level = false; + bool needs_subgroup_invocation_id = false; + bool needs_subgroup_size = false; + bool needs_sample_id = false; + bool needs_helper_invocation = false; + bool writes_to_depth = false; + std::string qual_pos_var_name; + std::string stage_in_var_name = "in"; + std::string stage_out_var_name = "out"; + std::string patch_stage_in_var_name = "patchIn"; + std::string patch_stage_out_var_name = "patchOut"; + std::string sampler_name_suffix = "Smplr"; + std::string swizzle_name_suffix = "Swzl"; + std::string buffer_size_name_suffix = "BufferSize"; + std::string plane_name_suffix = "Plane"; + std::string input_wg_var_name = "gl_in"; + std::string input_buffer_var_name = "spvIn"; + std::string output_buffer_var_name = "spvOut"; + std::string patch_input_buffer_var_name = "spvPatchIn"; + std::string patch_output_buffer_var_name = "spvPatchOut"; + std::string tess_factor_buffer_var_name = "spvTessLevel"; + std::string index_buffer_var_name = "spvIndices"; + spv::Op previous_instruction_opcode = spv::OpNop; + + // Must be ordered since declaration is in a specific order. + std::map constexpr_samplers_by_id; + std::unordered_map constexpr_samplers_by_binding; + const MSLConstexprSampler *find_constexpr_sampler(uint32_t id) const; + + std::unordered_set buffers_requiring_array_length; + SmallVector> buffer_aliases_argument; + SmallVector buffer_aliases_discrete; + std::unordered_set atomic_image_vars_emulated; // Emulate texture2D atomic operations + std::unordered_set pull_model_inputs; + std::unordered_set recursive_inputs; + + SmallVector entry_point_bindings; + + // Must be ordered since array is in a specific order. + std::map> buffers_requiring_dynamic_offset; + + SmallVector disabled_frag_outputs; + + std::unordered_set inline_uniform_blocks; + + uint32_t argument_buffer_ids[kMaxArgumentBuffers]; + uint32_t argument_buffer_discrete_mask = 0; + uint32_t argument_buffer_device_storage_mask = 0; + + void emit_argument_buffer_aliased_descriptor(const SPIRVariable &aliased_var, + const SPIRVariable &base_var); + + void analyze_argument_buffers(); + bool descriptor_set_is_argument_buffer(uint32_t desc_set) const; + const MSLResourceBinding &get_argument_buffer_resource(uint32_t desc_set, uint32_t arg_idx) const; + void add_argument_buffer_padding_buffer_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind); + void add_argument_buffer_padding_image_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind); + void add_argument_buffer_padding_sampler_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind); + void add_argument_buffer_padding_type(uint32_t mbr_type_id, SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, uint32_t count); + + uint32_t get_target_components_for_fragment_location(uint32_t location) const; + uint32_t build_extended_vector_type(uint32_t type_id, uint32_t components, + SPIRType::BaseType basetype = SPIRType::Unknown); + uint32_t build_msl_interpolant_type(uint32_t type_id, bool is_noperspective); + + bool suppress_missing_prototypes = false; + bool suppress_incompatible_pointer_types_discard_qualifiers = false; + + void add_spv_func_and_recompile(SPVFuncImpl spv_func); + + void activate_argument_buffer_resources(); + + bool type_is_msl_framebuffer_fetch(const SPIRType &type) const; + bool is_supported_argument_buffer_type(const SPIRType &type) const; + + bool variable_storage_requires_stage_io(spv::StorageClass storage) const; + + bool needs_manual_helper_invocation_updates() const + { + return msl_options.manual_helper_invocation_updates && msl_options.supports_msl_version(2, 3); + } + bool needs_frag_discard_checks() const + { + return get_execution_model() == spv::ExecutionModelFragment && msl_options.supports_msl_version(2, 3) && + msl_options.check_discarded_frag_stores && frag_shader_needs_discard_checks; + } + + bool has_additional_fixed_sample_mask() const { return msl_options.additional_fixed_sample_mask != 0xffffffff; } + std::string additional_fixed_sample_mask_str() const; + + // OpcodeHandler that handles several MSL preprocessing operations. + struct OpCodePreprocessor : OpcodeHandler + { + OpCodePreprocessor(CompilerMSL &compiler_) + : compiler(compiler_) + { + } + + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + CompilerMSL::SPVFuncImpl get_spv_func_impl(spv::Op opcode, const uint32_t *args); + void check_resource_write(uint32_t var_id); + + CompilerMSL &compiler; + std::unordered_map result_types; + std::unordered_map image_pointers_emulated; // Emulate texture2D atomic operations + bool suppress_missing_prototypes = false; + bool uses_atomics = false; + bool uses_image_write = false; + bool uses_buffer_write = false; + bool uses_discard = false; + bool needs_subgroup_invocation_id = false; + bool needs_subgroup_size = false; + bool needs_sample_id = false; + bool needs_helper_invocation = false; + }; + + // OpcodeHandler that scans for uses of sampled images + struct SampledImageScanner : OpcodeHandler + { + SampledImageScanner(CompilerMSL &compiler_) + : compiler(compiler_) + { + } + + bool handle(spv::Op opcode, const uint32_t *args, uint32_t) override; + + CompilerMSL &compiler; + }; + + // Sorts the members of a SPIRType and associated Meta info based on a settable sorting + // aspect, which defines which aspect of the struct members will be used to sort them. + // Regardless of the sorting aspect, built-in members always appear at the end of the struct. + struct MemberSorter + { + enum SortAspect + { + LocationThenBuiltInType, + Offset + }; + + void sort(); + bool operator()(uint32_t mbr_idx1, uint32_t mbr_idx2); + MemberSorter(SPIRType &t, Meta &m, SortAspect sa); + + SPIRType &type; + Meta &meta; + SortAspect sort_aspect; + }; }; } // namespace SPIRV_CROSS_NAMESPACE diff --git a/spirv_parser.hpp b/spirv_parser.hpp index dabc0e22..2eaf699c 100644 --- a/spirv_parser.hpp +++ b/spirv_parser.hpp @@ -32,71 +32,71 @@ namespace SPIRV_CROSS_NAMESPACE class Parser { public: - Parser(const uint32_t *spirv_data, size_t word_count); - Parser(std::vector spirv); + Parser(const uint32_t *spirv_data, size_t word_count); + Parser(std::vector spirv); - void parse(); + void parse(); - ParsedIR &get_parsed_ir() - { - return ir; - } + ParsedIR &get_parsed_ir() + { + return ir; + } private: - ParsedIR ir; - SPIRFunction *current_function = nullptr; - SPIRBlock *current_block = nullptr; - // For workarounds. - bool ignore_trailing_block_opcodes = false; + ParsedIR ir; + SPIRFunction *current_function = nullptr; + SPIRBlock *current_block = nullptr; + // For workarounds. + bool ignore_trailing_block_opcodes = false; - void parse(const Instruction &instr); - const uint32_t *stream(const Instruction &instr) const; + void parse(const Instruction &instr); + const uint32_t *stream(const Instruction &instr) const; - template - T &set(uint32_t id, P &&... args) - { - ir.add_typed_id(static_cast(T::type), id); - auto &var = variant_set(ir.ids[id], std::forward

(args)...); - var.self = id; - return var; - } + template + T &set(uint32_t id, P &&... args) + { + ir.add_typed_id(static_cast(T::type), id); + auto &var = variant_set(ir.ids[id], std::forward

(args)...); + var.self = id; + return var; + } - template - T &get(uint32_t id) - { - return variant_get(ir.ids[id]); - } + template + T &get(uint32_t id) + { + return variant_get(ir.ids[id]); + } - template - T *maybe_get(uint32_t id) - { - if (ir.ids[id].get_type() == static_cast(T::type)) - return &get(id); - else - return nullptr; - } + template + T *maybe_get(uint32_t id) + { + if (ir.ids[id].get_type() == static_cast(T::type)) + return &get(id); + else + return nullptr; + } - template - const T &get(uint32_t id) const - { - return variant_get(ir.ids[id]); - } + template + const T &get(uint32_t id) const + { + return variant_get(ir.ids[id]); + } - template - const T *maybe_get(uint32_t id) const - { - if (ir.ids[id].get_type() == T::type) - return &get(id); - else - return nullptr; - } + template + const T *maybe_get(uint32_t id) const + { + if (ir.ids[id].get_type() == T::type) + return &get(id); + else + return nullptr; + } - // This must be an ordered data structure so we always pick the same type aliases. - SmallVector global_struct_cache; - SmallVector> forward_pointer_fixups; + // This must be an ordered data structure so we always pick the same type aliases. + SmallVector global_struct_cache; + SmallVector> forward_pointer_fixups; - bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const; - bool variable_storage_is_aliased(const SPIRVariable &v) const; + bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const; + bool variable_storage_is_aliased(const SPIRVariable &v) const; }; } // namespace SPIRV_CROSS_NAMESPACE diff --git a/spirv_reflect.hpp b/spirv_reflect.hpp index a129ba54..2f6b9077 100644 --- a/spirv_reflect.hpp +++ b/spirv_reflect.hpp @@ -36,54 +36,54 @@ namespace SPIRV_CROSS_NAMESPACE { class CompilerReflection : public CompilerGLSL { - using Parent = CompilerGLSL; + using Parent = CompilerGLSL; public: - explicit CompilerReflection(std::vector spirv_) - : Parent(std::move(spirv_)) - { - options.vulkan_semantics = true; - } + explicit CompilerReflection(std::vector spirv_) + : Parent(std::move(spirv_)) + { + options.vulkan_semantics = true; + } - CompilerReflection(const uint32_t *ir_, size_t word_count) - : Parent(ir_, word_count) - { - options.vulkan_semantics = true; - } + CompilerReflection(const uint32_t *ir_, size_t word_count) + : Parent(ir_, word_count) + { + options.vulkan_semantics = true; + } - explicit CompilerReflection(const ParsedIR &ir_) - : CompilerGLSL(ir_) - { - options.vulkan_semantics = true; - } + explicit CompilerReflection(const ParsedIR &ir_) + : CompilerGLSL(ir_) + { + options.vulkan_semantics = true; + } - explicit CompilerReflection(ParsedIR &&ir_) - : CompilerGLSL(std::move(ir_)) - { - options.vulkan_semantics = true; - } + explicit CompilerReflection(ParsedIR &&ir_) + : CompilerGLSL(std::move(ir_)) + { + options.vulkan_semantics = true; + } - void set_format(const std::string &format); - std::string compile() override; + void set_format(const std::string &format); + std::string compile() override; private: - static std::string execution_model_to_str(spv::ExecutionModel model); + static std::string execution_model_to_str(spv::ExecutionModel model); - void emit_entry_points(); - void emit_types(); - void emit_resources(); - void emit_specialization_constants(); + void emit_entry_points(); + void emit_types(); + void emit_resources(); + void emit_specialization_constants(); - void emit_type(uint32_t type_id, bool &emitted_open_tag); - void emit_type_member(const SPIRType &type, uint32_t index); - void emit_type_member_qualifiers(const SPIRType &type, uint32_t index); - void emit_type_array(const SPIRType &type); - void emit_resources(const char *tag, const SmallVector &resources); - bool type_is_reference(const SPIRType &type) const; + void emit_type(uint32_t type_id, bool &emitted_open_tag); + void emit_type_member(const SPIRType &type, uint32_t index); + void emit_type_member_qualifiers(const SPIRType &type, uint32_t index); + void emit_type_array(const SPIRType &type); + void emit_resources(const char *tag, const SmallVector &resources); + bool type_is_reference(const SPIRType &type) const; - std::string to_member_name(const SPIRType &type, uint32_t index) const; + std::string to_member_name(const SPIRType &type, uint32_t index) const; - std::shared_ptr json_stream; + std::shared_ptr json_stream; }; } // namespace SPIRV_CROSS_NAMESPACE