spirv_hlsl.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. /*
  2. * Copyright 2016-2021 Robert Konrad
  3. * SPDX-License-Identifier: Apache-2.0 OR MIT
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /*
  18. * At your option, you may choose to accept this material under either:
  19. * 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  20. * 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  21. */
  22. #ifndef SPIRV_HLSL_HPP
  23. #define SPIRV_HLSL_HPP
  24. #include "spirv_glsl.hpp"
  25. #include <utility>
  26. namespace SPIRV_CROSS_NAMESPACE
  27. {
  28. using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE;
  29. // Interface which remaps vertex inputs to a fixed semantic name to make linking easier.
  30. struct HLSLVertexAttributeRemap
  31. {
  32. uint32_t location;
  33. std::string semantic;
  34. };
  35. // Specifying a root constant (d3d12) or push constant range (vulkan).
  36. //
  37. // `start` and `end` denotes the range of the root constant in bytes.
  38. // Both values need to be multiple of 4.
  39. struct RootConstants
  40. {
  41. uint32_t start;
  42. uint32_t end;
  43. uint32_t binding;
  44. uint32_t space;
  45. };
  46. // For finer control, decorations may be removed from specific resources instead with unset_decoration().
  47. enum HLSLBindingFlagBits
  48. {
  49. HLSL_BINDING_AUTO_NONE_BIT = 0,
  50. // Push constant (root constant) resources will be declared as CBVs (b-space) without a register() declaration.
  51. // A register will be automatically assigned by the D3D compiler, but must therefore be reflected in D3D-land.
  52. // Push constants do not normally have a DecorationBinding set, but if they do, this can be used to ignore it.
  53. HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT = 1 << 0,
  54. // cbuffer resources will be declared as CBVs (b-space) without a register() declaration.
  55. // A register will be automatically assigned, but must be reflected in D3D-land.
  56. HLSL_BINDING_AUTO_CBV_BIT = 1 << 1,
  57. // All SRVs (t-space) will be declared without a register() declaration.
  58. HLSL_BINDING_AUTO_SRV_BIT = 1 << 2,
  59. // All UAVs (u-space) will be declared without a register() declaration.
  60. HLSL_BINDING_AUTO_UAV_BIT = 1 << 3,
  61. // All samplers (s-space) will be declared without a register() declaration.
  62. HLSL_BINDING_AUTO_SAMPLER_BIT = 1 << 4,
  63. // No resources will be declared with register().
  64. HLSL_BINDING_AUTO_ALL = 0x7fffffff
  65. };
  66. using HLSLBindingFlags = uint32_t;
  67. // By matching stage, desc_set and binding for a SPIR-V resource,
  68. // register bindings are set based on whether the HLSL resource is a
  69. // CBV, UAV, SRV or Sampler. A single binding in SPIR-V might contain multiple
  70. // resource types, e.g. COMBINED_IMAGE_SAMPLER, and SRV/Sampler bindings will be used respectively.
  71. // On SM 5.0 and lower, register_space is ignored.
  72. //
  73. // To remap a push constant block which does not have any desc_set/binding associated with it,
  74. // use ResourceBindingPushConstant{DescriptorSet,Binding} as values for desc_set/binding.
  75. // For deeper control of push constants, set_root_constant_layouts() can be used instead.
  76. struct HLSLResourceBinding
  77. {
  78. ExecutionModel stage = ExecutionModelMax;
  79. uint32_t desc_set = 0;
  80. uint32_t binding = 0;
  81. struct Binding
  82. {
  83. uint32_t register_space = 0;
  84. uint32_t register_binding = 0;
  85. } cbv, uav, srv, sampler;
  86. };
  87. enum HLSLAuxBinding
  88. {
  89. HLSL_AUX_BINDING_BASE_VERTEX_INSTANCE = 0
  90. };
  91. class CompilerHLSL : public CompilerGLSL
  92. {
  93. public:
  94. struct Options
  95. {
  96. uint32_t shader_model = 30; // TODO: map ps_4_0_level_9_0,... somehow
  97. // Allows the PointSize builtin in SM 4.0+, and ignores it, as PointSize is not supported in SM 4+.
  98. bool point_size_compat = false;
  99. // Allows the PointCoord builtin, returns float2(0.5, 0.5), as PointCoord is not supported in HLSL.
  100. bool point_coord_compat = false;
  101. // If true, the backend will assume that VertexIndex and InstanceIndex will need to apply
  102. // a base offset, and you will need to fill in a cbuffer with offsets.
  103. // Set to false if you know you will never use base instance or base vertex
  104. // functionality as it might remove an internal cbuffer.
  105. bool support_nonzero_base_vertex_base_instance = false;
  106. // Forces a storage buffer to always be declared as UAV, even if the readonly decoration is used.
  107. // By default, a readonly storage buffer will be declared as ByteAddressBuffer (SRV) instead.
  108. // Alternatively, use set_hlsl_force_storage_buffer_as_uav to specify individually.
  109. bool force_storage_buffer_as_uav = false;
  110. // Forces any storage image type marked as NonWritable to be considered an SRV instead.
  111. // For this to work with function call parameters, NonWritable must be considered to be part of the type system
  112. // so that NonWritable image arguments are also translated to Texture rather than RWTexture.
  113. bool nonwritable_uav_texture_as_srv = false;
  114. // Enables native 16-bit types. Needs SM 6.2.
  115. // Uses half/int16_t/uint16_t instead of min16* types.
  116. // Also adds support for 16-bit load-store from (RW)ByteAddressBuffer.
  117. bool enable_16bit_types = false;
  118. // If matrices are used as IO variables, flatten the attribute declaration to use
  119. // TEXCOORD{N,N+1,N+2,...} rather than TEXCOORDN_{0,1,2,3}.
  120. // If add_vertex_attribute_remap is used and this feature is used,
  121. // the semantic name will be queried once per active location.
  122. bool flatten_matrix_vertex_input_semantics = false;
  123. // Rather than emitting main() for the entry point, use the name in SPIR-V.
  124. bool use_entry_point_name = false;
  125. // Preserve (RW)StructuredBuffer types if the input source was HLSL.
  126. // This relies on UserTypeGOOGLE to encode the buffer type either as "structuredbuffer" or "rwstructuredbuffer"
  127. // whereas the type can be extended with an optional subtype, e.g. "structuredbuffer:int".
  128. bool preserve_structured_buffers = false;
  129. // Use UserSemantic decoration info (if specified), otherwise use default mechanism (such as add_vertex_attribute_remap or TEXCOORD#).
  130. bool user_semantic = false;
  131. };
  132. explicit CompilerHLSL(std::vector<uint32_t> spirv_)
  133. : CompilerGLSL(std::move(spirv_))
  134. {
  135. }
  136. CompilerHLSL(const uint32_t *ir_, size_t size)
  137. : CompilerGLSL(ir_, size)
  138. {
  139. }
  140. explicit CompilerHLSL(const ParsedIR &ir_)
  141. : CompilerGLSL(ir_)
  142. {
  143. }
  144. explicit CompilerHLSL(ParsedIR &&ir_)
  145. : CompilerGLSL(std::move(ir_))
  146. {
  147. }
  148. const Options &get_hlsl_options() const
  149. {
  150. return hlsl_options;
  151. }
  152. void set_hlsl_options(const Options &opts)
  153. {
  154. hlsl_options = opts;
  155. }
  156. // Optionally specify a custom root constant layout.
  157. //
  158. // Push constants ranges will be split up according to the
  159. // layout specified.
  160. void set_root_constant_layouts(std::vector<RootConstants> layout);
  161. // Compiles and remaps vertex attributes at specific locations to a fixed semantic.
  162. // The default is TEXCOORD# where # denotes location.
  163. // Matrices are unrolled to vectors with notation ${SEMANTIC}_#, where # denotes row.
  164. // $SEMANTIC is either TEXCOORD# or a semantic name specified here.
  165. void add_vertex_attribute_remap(const HLSLVertexAttributeRemap &vertex_attributes);
  166. std::string compile() override;
  167. // This is a special HLSL workaround for the NumWorkGroups builtin.
  168. // This does not exist in HLSL, so the calling application must create a dummy cbuffer in
  169. // which the application will store this builtin.
  170. // The cbuffer layout will be:
  171. // cbuffer SPIRV_Cross_NumWorkgroups : register(b#, space#) { uint3 SPIRV_Cross_NumWorkgroups_count; };
  172. // This must be called before compile().
  173. // The function returns 0 if NumWorkGroups builtin is not statically used in the shader from the current entry point.
  174. // If non-zero, this returns the variable ID of a cbuffer which corresponds to
  175. // the cbuffer declared above. By default, no binding or descriptor set decoration is set,
  176. // so the calling application should declare explicit bindings on this ID before calling compile().
  177. VariableID remap_num_workgroups_builtin();
  178. // Controls how resource bindings are declared in the output HLSL.
  179. void set_resource_binding_flags(HLSLBindingFlags flags);
  180. // resource is a resource binding to indicate the HLSL CBV, SRV, UAV or sampler binding
  181. // to use for a particular SPIR-V description set
  182. // and binding. If resource bindings are provided,
  183. // is_hlsl_resource_binding_used() will return true after calling ::compile() if
  184. // the set/binding combination was used by the HLSL code.
  185. void add_hlsl_resource_binding(const HLSLResourceBinding &resource);
  186. bool is_hlsl_resource_binding_used(ExecutionModel model, uint32_t set, uint32_t binding) const;
  187. // Controls which storage buffer bindings will be forced to be declared as UAVs.
  188. void set_hlsl_force_storage_buffer_as_uav(uint32_t desc_set, uint32_t binding);
  189. // By default, these magic buffers are not assigned a specific binding.
  190. void set_hlsl_aux_buffer_binding(HLSLAuxBinding binding, uint32_t register_index, uint32_t register_space);
  191. void unset_hlsl_aux_buffer_binding(HLSLAuxBinding binding);
  192. bool is_hlsl_aux_buffer_binding_used(HLSLAuxBinding binding) const;
  193. private:
  194. std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override;
  195. std::string image_type_hlsl(const SPIRType &type, uint32_t id);
  196. std::string image_type_hlsl_modern(const SPIRType &type, uint32_t id);
  197. std::string image_type_hlsl_legacy(const SPIRType &type, uint32_t id);
  198. uint32_t input_vertices_from_execution_mode(SPIREntryPoint &execution) const;
  199. void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override;
  200. void emit_hlsl_entry_point();
  201. void emit_header() override;
  202. void emit_resources();
  203. void emit_interface_block_globally(const SPIRVariable &type);
  204. void emit_interface_block_in_struct(const SPIRVariable &var, std::unordered_set<uint32_t> &active_locations);
  205. void emit_interface_block_member_in_struct(const SPIRVariable &var, uint32_t member_index, uint32_t location,
  206. std::unordered_set<uint32_t> &active_locations);
  207. void emit_builtin_inputs_in_struct();
  208. void emit_builtin_outputs_in_struct();
  209. void emit_builtin_primitive_outputs_in_struct();
  210. void emit_texture_op(const Instruction &i, bool sparse) override;
  211. void emit_instruction(const Instruction &instruction) override;
  212. void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args,
  213. uint32_t count) override;
  214. void emit_buffer_block(const SPIRVariable &type) override;
  215. void emit_push_constant_block(const SPIRVariable &var) override;
  216. void emit_uniform(const SPIRVariable &var) override;
  217. void emit_modern_uniform(const SPIRVariable &var);
  218. void emit_legacy_uniform(const SPIRVariable &var);
  219. void emit_specialization_constants_and_structs();
  220. void emit_composite_constants();
  221. void emit_fixup() override;
  222. std::string builtin_to_glsl(BuiltIn builtin, StorageClass storage) override;
  223. std::string layout_for_member(const SPIRType &type, uint32_t index) override;
  224. std::string to_interpolation_qualifiers(const Bitset &flags) override;
  225. std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override;
  226. bool emit_complex_bitcast(uint32_t result_type, uint32_t id, uint32_t op0) override;
  227. void append_global_func_args(const SPIRFunction &func, uint32_t index, SmallVector<std::string> &arglist) override;
  228. std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) override;
  229. std::string to_sampler_expression(uint32_t id);
  230. std::string to_resource_binding(const SPIRVariable &var);
  231. std::string to_resource_binding_sampler(const SPIRVariable &var);
  232. std::string to_resource_register(HLSLBindingFlagBits flag, char space, uint32_t binding, uint32_t set);
  233. std::string to_initializer_expression(const SPIRVariable &var) override;
  234. void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override;
  235. void emit_access_chain(const Instruction &instruction);
  236. void emit_load(const Instruction &instruction);
  237. void read_access_chain(std::string *expr, const std::string &lhs, const SPIRAccessChain &chain);
  238. void read_access_chain_struct(const std::string &lhs, const SPIRAccessChain &chain);
  239. void read_access_chain_array(const std::string &lhs, const SPIRAccessChain &chain);
  240. void write_access_chain(const SPIRAccessChain &chain, uint32_t value, const SmallVector<uint32_t> &composite_chain);
  241. void write_access_chain_struct(const SPIRAccessChain &chain, uint32_t value,
  242. const SmallVector<uint32_t> &composite_chain);
  243. void write_access_chain_array(const SPIRAccessChain &chain, uint32_t value,
  244. const SmallVector<uint32_t> &composite_chain);
  245. std::string write_access_chain_value(uint32_t value, const SmallVector<uint32_t> &composite_chain, bool enclose);
  246. void emit_store(const Instruction &instruction);
  247. void emit_atomic(const uint32_t *ops, uint32_t length, Op op);
  248. void emit_subgroup_op(const Instruction &i) override;
  249. void emit_block_hints(const SPIRBlock &block) override;
  250. void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, const std::string &qualifier,
  251. uint32_t base_offset = 0) override;
  252. void emit_rayquery_function(const char *commited, const char *candidate, const uint32_t *ops);
  253. void emit_mesh_tasks(SPIRBlock &block) override;
  254. void emit_geometry_stream_append();
  255. const char *to_storage_qualifiers_glsl(const SPIRVariable &var) override;
  256. void replace_illegal_names() override;
  257. SPIRType::BaseType get_builtin_basetype(BuiltIn builtin, SPIRType::BaseType default_type) override;
  258. bool is_hlsl_force_storage_buffer_as_uav(ID id) const;
  259. bool is_hidden_io_variable(const SPIRVariable &var) const;
  260. Options hlsl_options;
  261. // TODO: Refactor this to be more similar to MSL, maybe have some common system in place?
  262. bool requires_op_fmod = false;
  263. bool requires_fp16_packing = false;
  264. bool requires_uint2_packing = false;
  265. bool requires_explicit_fp16_packing = false;
  266. bool requires_unorm8_packing = false;
  267. bool requires_snorm8_packing = false;
  268. bool requires_unorm16_packing = false;
  269. bool requires_snorm16_packing = false;
  270. bool requires_bitfield_insert = false;
  271. bool requires_bitfield_extract = false;
  272. bool requires_inverse_2x2 = false;
  273. bool requires_inverse_3x3 = false;
  274. bool requires_inverse_4x4 = false;
  275. bool requires_scalar_reflect = false;
  276. bool requires_scalar_refract = false;
  277. bool requires_scalar_faceforward = false;
  278. struct TextureSizeVariants
  279. {
  280. // MSVC 2013 workaround.
  281. TextureSizeVariants()
  282. {
  283. srv = 0;
  284. for (auto &unorm : uav)
  285. for (auto &u : unorm)
  286. u = 0;
  287. }
  288. uint64_t srv;
  289. uint64_t uav[3][4];
  290. } required_texture_size_variants;
  291. void require_texture_query_variant(uint32_t var_id);
  292. void emit_texture_size_variants(uint64_t variant_mask, const char *vecsize_qualifier, bool uav,
  293. const char *type_qualifier);
  294. enum TextureQueryVariantDim
  295. {
  296. Query1D = 0,
  297. Query1DArray,
  298. Query2D,
  299. Query2DArray,
  300. Query3D,
  301. QueryBuffer,
  302. QueryCube,
  303. QueryCubeArray,
  304. Query2DMS,
  305. Query2DMSArray,
  306. QueryDimCount
  307. };
  308. enum TextureQueryVariantType
  309. {
  310. QueryTypeFloat = 0,
  311. QueryTypeInt = 16,
  312. QueryTypeUInt = 32,
  313. QueryTypeCount = 3
  314. };
  315. enum BitcastType
  316. {
  317. TypeNormal,
  318. TypePackUint2x32,
  319. TypeUnpackUint64
  320. };
  321. void analyze_meshlet_writes();
  322. void analyze_meshlet_writes(uint32_t func_id, uint32_t id_per_vertex, uint32_t id_per_primitive,
  323. std::unordered_set<uint32_t> &processed_func_ids);
  324. BitcastType get_bitcast_type(uint32_t result_type, uint32_t op0);
  325. void emit_builtin_variables();
  326. bool require_output = false;
  327. bool require_input = false;
  328. SmallVector<HLSLVertexAttributeRemap> remap_vertex_attributes;
  329. uint32_t type_to_consumed_locations(const SPIRType &type) const;
  330. std::string to_semantic(uint32_t location, ExecutionModel em, StorageClass sc);
  331. uint32_t num_workgroups_builtin = 0;
  332. HLSLBindingFlags resource_binding_flags = 0;
  333. // Custom root constant layout, which should be emitted
  334. // when translating push constant ranges.
  335. std::vector<RootConstants> root_constants_layout;
  336. void validate_shader_model();
  337. std::string get_unique_identifier();
  338. uint32_t unique_identifier_count = 0;
  339. std::unordered_map<StageSetBinding, std::pair<HLSLResourceBinding, bool>, InternalHasher> resource_bindings;
  340. void remap_hlsl_resource_binding(HLSLBindingFlagBits type, uint32_t &desc_set, uint32_t &binding);
  341. std::unordered_set<SetBindingPair, InternalHasher> force_uav_buffer_bindings;
  342. struct
  343. {
  344. uint32_t register_index = 0;
  345. uint32_t register_space = 0;
  346. bool explicit_binding = false;
  347. bool used = false;
  348. } base_vertex_info;
  349. // Returns true if the specified ID has a UserTypeGOOGLE decoration for StructuredBuffer or RWStructuredBuffer resources.
  350. bool is_user_type_structured(uint32_t id) const override;
  351. std::vector<TypeID> composite_selection_workaround_types;
  352. std::string get_inner_entry_point_name() const;
  353. void cast_to_variable_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) override;
  354. };
  355. } // namespace SPIRV_CROSS_NAMESPACE
  356. #endif