1 /*
2 * Copyright 2015-2019 Arm Limited
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef SPIRV_CROSS_COMMON_HPP
18 #define SPIRV_CROSS_COMMON_HPP
19
20 #include "spirv.hpp"
21 #include "spirv_cross_containers.hpp"
22 #include "spirv_cross_error_handling.hpp"
23 #include <functional>
24
25 // A bit crude, but allows projects which embed SPIRV-Cross statically to
26 // effectively hide all the symbols from other projects.
27 // There is a case where we have:
28 // - Project A links against SPIRV-Cross statically.
29 // - Project A links against Project B statically.
30 // - Project B links against SPIRV-Cross statically (might be a different version).
31 // This leads to a conflict with extremely bizarre results.
32 // By overriding the namespace in one of the project builds, we can work around this.
33 // If SPIRV-Cross is embedded in dynamic libraries,
34 // prefer using -fvisibility=hidden on GCC/Clang instead.
35 #ifdef SPIRV_CROSS_NAMESPACE_OVERRIDE
36 #define SPIRV_CROSS_NAMESPACE SPIRV_CROSS_NAMESPACE_OVERRIDE
37 #else
38 #define SPIRV_CROSS_NAMESPACE spirv_cross
39 #endif
40
41 namespace SPIRV_CROSS_NAMESPACE
42 {
43 namespace inner
44 {
45 template <typename T>
join_helper(StringStream<> & stream,T && t)46 void join_helper(StringStream<> &stream, T &&t)
47 {
48 stream << std::forward<T>(t);
49 }
50
51 template <typename T, typename... Ts>
join_helper(StringStream<> & stream,T && t,Ts &&...ts)52 void join_helper(StringStream<> &stream, T &&t, Ts &&... ts)
53 {
54 stream << std::forward<T>(t);
55 join_helper(stream, std::forward<Ts>(ts)...);
56 }
57 } // namespace inner
58
59 class Bitset
60 {
61 public:
62 Bitset() = default;
Bitset(uint64_t lower_)63 explicit inline Bitset(uint64_t lower_)
64 : lower(lower_)
65 {
66 }
67
get(uint32_t bit) const68 inline bool get(uint32_t bit) const
69 {
70 if (bit < 64)
71 return (lower & (1ull << bit)) != 0;
72 else
73 return higher.count(bit) != 0;
74 }
75
set(uint32_t bit)76 inline void set(uint32_t bit)
77 {
78 if (bit < 64)
79 lower |= 1ull << bit;
80 else
81 higher.insert(bit);
82 }
83
clear(uint32_t bit)84 inline void clear(uint32_t bit)
85 {
86 if (bit < 64)
87 lower &= ~(1ull << bit);
88 else
89 higher.erase(bit);
90 }
91
get_lower() const92 inline uint64_t get_lower() const
93 {
94 return lower;
95 }
96
reset()97 inline void reset()
98 {
99 lower = 0;
100 higher.clear();
101 }
102
merge_and(const Bitset & other)103 inline void merge_and(const Bitset &other)
104 {
105 lower &= other.lower;
106 std::unordered_set<uint32_t> tmp_set;
107 for (auto &v : higher)
108 if (other.higher.count(v) != 0)
109 tmp_set.insert(v);
110 higher = std::move(tmp_set);
111 }
112
merge_or(const Bitset & other)113 inline void merge_or(const Bitset &other)
114 {
115 lower |= other.lower;
116 for (auto &v : other.higher)
117 higher.insert(v);
118 }
119
operator ==(const Bitset & other) const120 inline bool operator==(const Bitset &other) const
121 {
122 if (lower != other.lower)
123 return false;
124
125 if (higher.size() != other.higher.size())
126 return false;
127
128 for (auto &v : higher)
129 if (other.higher.count(v) == 0)
130 return false;
131
132 return true;
133 }
134
operator !=(const Bitset & other) const135 inline bool operator!=(const Bitset &other) const
136 {
137 return !(*this == other);
138 }
139
140 template <typename Op>
for_each_bit(const Op & op) const141 void for_each_bit(const Op &op) const
142 {
143 // TODO: Add ctz-based iteration.
144 for (uint32_t i = 0; i < 64; i++)
145 {
146 if (lower & (1ull << i))
147 op(i);
148 }
149
150 if (higher.empty())
151 return;
152
153 // Need to enforce an order here for reproducible results,
154 // but hitting this path should happen extremely rarely, so having this slow path is fine.
155 SmallVector<uint32_t> bits;
156 bits.reserve(higher.size());
157 for (auto &v : higher)
158 bits.push_back(v);
159 std::sort(std::begin(bits), std::end(bits));
160
161 for (auto &v : bits)
162 op(v);
163 }
164
empty() const165 inline bool empty() const
166 {
167 return lower == 0 && higher.empty();
168 }
169
170 private:
171 // The most common bits to set are all lower than 64,
172 // so optimize for this case. Bits spilling outside 64 go into a slower data structure.
173 // In almost all cases, higher data structure will not be used.
174 uint64_t lower = 0;
175 std::unordered_set<uint32_t> higher;
176 };
177
178 // Helper template to avoid lots of nasty string temporary munging.
179 template <typename... Ts>
join(Ts &&...ts)180 std::string join(Ts &&... ts)
181 {
182 StringStream<> stream;
183 inner::join_helper(stream, std::forward<Ts>(ts)...);
184 return stream.str();
185 }
186
merge(const SmallVector<std::string> & list,const char * between=", ")187 inline std::string merge(const SmallVector<std::string> &list, const char *between = ", ")
188 {
189 StringStream<> stream;
190 for (auto &elem : list)
191 {
192 stream << elem;
193 if (&elem != &list.back())
194 stream << between;
195 }
196 return stream.str();
197 }
198
199 // Make sure we don't accidentally call this with float or doubles with SFINAE.
200 // Have to use the radix-aware overload.
201 template <typename T, typename std::enable_if<!std::is_floating_point<T>::value, int>::type = 0>
convert_to_string(const T & t)202 inline std::string convert_to_string(const T &t)
203 {
204 return std::to_string(t);
205 }
206
207 // Allow implementations to set a convenient standard precision
208 #ifndef SPIRV_CROSS_FLT_FMT
209 #define SPIRV_CROSS_FLT_FMT "%.32g"
210 #endif
211
212 #ifdef _MSC_VER
213 // sprintf warning.
214 // We cannot rely on snprintf existing because, ..., MSVC.
215 #pragma warning(push)
216 #pragma warning(disable : 4996)
217 #endif
218
fixup_radix_point(char * str,char radix_point)219 static inline void fixup_radix_point(char *str, char radix_point)
220 {
221 // Setting locales is a very risky business in multi-threaded program,
222 // so just fixup locales instead. We only need to care about the radix point.
223 if (radix_point != '.')
224 {
225 while (*str != '\0')
226 {
227 if (*str == radix_point)
228 *str = '.';
229 str++;
230 }
231 }
232 }
233
convert_to_string(float t,char locale_radix_point)234 inline std::string convert_to_string(float t, char locale_radix_point)
235 {
236 // std::to_string for floating point values is broken.
237 // Fallback to something more sane.
238 char buf[64];
239 sprintf(buf, SPIRV_CROSS_FLT_FMT, t);
240 fixup_radix_point(buf, locale_radix_point);
241
242 // Ensure that the literal is float.
243 if (!strchr(buf, '.') && !strchr(buf, 'e'))
244 strcat(buf, ".0");
245 return buf;
246 }
247
convert_to_string(double t,char locale_radix_point)248 inline std::string convert_to_string(double t, char locale_radix_point)
249 {
250 // std::to_string for floating point values is broken.
251 // Fallback to something more sane.
252 char buf[64];
253 sprintf(buf, SPIRV_CROSS_FLT_FMT, t);
254 fixup_radix_point(buf, locale_radix_point);
255
256 // Ensure that the literal is float.
257 if (!strchr(buf, '.') && !strchr(buf, 'e'))
258 strcat(buf, ".0");
259 return buf;
260 }
261
262 #ifdef _MSC_VER
263 #pragma warning(pop)
264 #endif
265
266 struct Instruction
267 {
268 uint16_t op = 0;
269 uint16_t count = 0;
270 uint32_t offset = 0;
271 uint32_t length = 0;
272 };
273
274 enum Types
275 {
276 TypeNone,
277 TypeType,
278 TypeVariable,
279 TypeConstant,
280 TypeFunction,
281 TypeFunctionPrototype,
282 TypeBlock,
283 TypeExtension,
284 TypeExpression,
285 TypeConstantOp,
286 TypeCombinedImageSampler,
287 TypeAccessChain,
288 TypeUndef,
289 TypeString,
290 TypeCount
291 };
292
293 template <Types type>
294 class TypedID;
295
296 template <>
297 class TypedID<TypeNone>
298 {
299 public:
300 TypedID() = default;
TypedID(uint32_t id_)301 TypedID(uint32_t id_)
302 : id(id_)
303 {
304 }
305
306 template <Types U>
TypedID(const TypedID<U> & other)307 TypedID(const TypedID<U> &other)
308 {
309 *this = other;
310 }
311
312 template <Types U>
operator =(const TypedID<U> & other)313 TypedID &operator=(const TypedID<U> &other)
314 {
315 id = uint32_t(other);
316 return *this;
317 }
318
319 // Implicit conversion to u32 is desired here.
320 // As long as we block implicit conversion between TypedID<A> and TypedID<B> we're good.
operator uint32_t() const321 operator uint32_t() const
322 {
323 return id;
324 }
325
326 template <Types U>
operator TypedID<U>() const327 operator TypedID<U>() const
328 {
329 return TypedID<U>(*this);
330 }
331
operator ==(const TypedID & other) const332 bool operator==(const TypedID &other) const
333 {
334 return id == other.id;
335 }
336
operator !=(const TypedID & other) const337 bool operator!=(const TypedID &other) const
338 {
339 return id != other.id;
340 }
341
342 template <Types type>
operator ==(const TypedID<type> & other) const343 bool operator==(const TypedID<type> &other) const
344 {
345 return id == uint32_t(other);
346 }
347
348 template <Types type>
operator !=(const TypedID<type> & other) const349 bool operator!=(const TypedID<type> &other) const
350 {
351 return id != uint32_t(other);
352 }
353
354 private:
355 uint32_t id = 0;
356 };
357
358 template <Types type>
359 class TypedID
360 {
361 public:
362 TypedID() = default;
TypedID(uint32_t id_)363 TypedID(uint32_t id_)
364 : id(id_)
365 {
366 }
367
TypedID(const TypedID<TypeNone> & other)368 explicit TypedID(const TypedID<TypeNone> &other)
369 : id(uint32_t(other))
370 {
371 }
372
operator uint32_t() const373 operator uint32_t() const
374 {
375 return id;
376 }
377
operator ==(const TypedID & other) const378 bool operator==(const TypedID &other) const
379 {
380 return id == other.id;
381 }
382
operator !=(const TypedID & other) const383 bool operator!=(const TypedID &other) const
384 {
385 return id != other.id;
386 }
387
operator ==(const TypedID<TypeNone> & other) const388 bool operator==(const TypedID<TypeNone> &other) const
389 {
390 return id == uint32_t(other);
391 }
392
operator !=(const TypedID<TypeNone> & other) const393 bool operator!=(const TypedID<TypeNone> &other) const
394 {
395 return id != uint32_t(other);
396 }
397
398 private:
399 uint32_t id = 0;
400 };
401
402 using VariableID = TypedID<TypeVariable>;
403 using TypeID = TypedID<TypeType>;
404 using ConstantID = TypedID<TypeConstant>;
405 using FunctionID = TypedID<TypeFunction>;
406 using BlockID = TypedID<TypeBlock>;
407 using ID = TypedID<TypeNone>;
408
409 // Helper for Variant interface.
410 struct IVariant
411 {
412 virtual ~IVariant() = default;
413 virtual IVariant *clone(ObjectPoolBase *pool) = 0;
414 ID self = 0;
415 };
416
417 #define SPIRV_CROSS_DECLARE_CLONE(T) \
418 IVariant *clone(ObjectPoolBase *pool) override \
419 { \
420 return static_cast<ObjectPool<T> *>(pool)->allocate(*this); \
421 }
422
423 struct SPIRUndef : IVariant
424 {
425 enum
426 {
427 type = TypeUndef
428 };
429
SPIRUndefSPIRV_CROSS_NAMESPACE::SPIRUndef430 explicit SPIRUndef(TypeID basetype_)
431 : basetype(basetype_)
432 {
433 }
434 TypeID basetype;
435
436 SPIRV_CROSS_DECLARE_CLONE(SPIRUndef)
437 };
438
439 struct SPIRString : IVariant
440 {
441 enum
442 {
443 type = TypeString
444 };
445
SPIRStringSPIRV_CROSS_NAMESPACE::SPIRString446 explicit SPIRString(std::string str_)
447 : str(std::move(str_))
448 {
449 }
450
451 std::string str;
452
453 SPIRV_CROSS_DECLARE_CLONE(SPIRString)
454 };
455
456 // This type is only used by backends which need to access the combined image and sampler IDs separately after
457 // the OpSampledImage opcode.
458 struct SPIRCombinedImageSampler : IVariant
459 {
460 enum
461 {
462 type = TypeCombinedImageSampler
463 };
SPIRCombinedImageSamplerSPIRV_CROSS_NAMESPACE::SPIRCombinedImageSampler464 SPIRCombinedImageSampler(TypeID type_, VariableID image_, VariableID sampler_)
465 : combined_type(type_)
466 , image(image_)
467 , sampler(sampler_)
468 {
469 }
470 TypeID combined_type;
471 VariableID image;
472 VariableID sampler;
473
474 SPIRV_CROSS_DECLARE_CLONE(SPIRCombinedImageSampler)
475 };
476
477 struct SPIRConstantOp : IVariant
478 {
479 enum
480 {
481 type = TypeConstantOp
482 };
483
SPIRConstantOpSPIRV_CROSS_NAMESPACE::SPIRConstantOp484 SPIRConstantOp(TypeID result_type, spv::Op op, const uint32_t *args, uint32_t length)
485 : opcode(op)
486 , basetype(result_type)
487 {
488 arguments.reserve(length);
489 for (uint32_t i = 0; i < length; i++)
490 arguments.push_back(args[i]);
491 }
492
493 spv::Op opcode;
494 SmallVector<uint32_t> arguments;
495 TypeID basetype;
496
497 SPIRV_CROSS_DECLARE_CLONE(SPIRConstantOp)
498 };
499
500 struct SPIRType : IVariant
501 {
502 enum
503 {
504 type = TypeType
505 };
506
507 enum BaseType
508 {
509 Unknown,
510 Void,
511 Boolean,
512 SByte,
513 UByte,
514 Short,
515 UShort,
516 Int,
517 UInt,
518 Int64,
519 UInt64,
520 AtomicCounter,
521 Half,
522 Float,
523 Double,
524 Struct,
525 Image,
526 SampledImage,
527 Sampler,
528 AccelerationStructureNV,
529
530 // Keep internal types at the end.
531 ControlPointArray,
532 Char
533 };
534
535 // Scalar/vector/matrix support.
536 BaseType basetype = Unknown;
537 uint32_t width = 0;
538 uint32_t vecsize = 1;
539 uint32_t columns = 1;
540
541 // Arrays, support array of arrays by having a vector of array sizes.
542 SmallVector<uint32_t> array;
543
544 // Array elements can be either specialization constants or specialization ops.
545 // This array determines how to interpret the array size.
546 // If an element is true, the element is a literal,
547 // otherwise, it's an expression, which must be resolved on demand.
548 // The actual size is not really known until runtime.
549 SmallVector<bool> array_size_literal;
550
551 // Pointers
552 // Keep track of how many pointer layers we have.
553 uint32_t pointer_depth = 0;
554 bool pointer = false;
555
556 spv::StorageClass storage = spv::StorageClassGeneric;
557
558 SmallVector<TypeID> member_types;
559
560 struct ImageType
561 {
562 TypeID type;
563 spv::Dim dim;
564 bool depth;
565 bool arrayed;
566 bool ms;
567 uint32_t sampled;
568 spv::ImageFormat format;
569 spv::AccessQualifier access;
570 } image;
571
572 // Structs can be declared multiple times if they are used as part of interface blocks.
573 // We want to detect this so that we only emit the struct definition once.
574 // Since we cannot rely on OpName to be equal, we need to figure out aliases.
575 TypeID type_alias = 0;
576
577 // Denotes the type which this type is based on.
578 // Allows the backend to traverse how a complex type is built up during access chains.
579 TypeID parent_type = 0;
580
581 // Used in backends to avoid emitting members with conflicting names.
582 std::unordered_set<std::string> member_name_cache;
583
584 SPIRV_CROSS_DECLARE_CLONE(SPIRType)
585 };
586
587 struct SPIRExtension : IVariant
588 {
589 enum
590 {
591 type = TypeExtension
592 };
593
594 enum Extension
595 {
596 Unsupported,
597 GLSL,
598 SPV_debug_info,
599 SPV_AMD_shader_ballot,
600 SPV_AMD_shader_explicit_vertex_parameter,
601 SPV_AMD_shader_trinary_minmax,
602 SPV_AMD_gcn_shader
603 };
604
SPIRExtensionSPIRV_CROSS_NAMESPACE::SPIRExtension605 explicit SPIRExtension(Extension ext_)
606 : ext(ext_)
607 {
608 }
609
610 Extension ext;
611 SPIRV_CROSS_DECLARE_CLONE(SPIRExtension)
612 };
613
614 // SPIREntryPoint is not a variant since its IDs are used to decorate OpFunction,
615 // so in order to avoid conflicts, we can't stick them in the ids array.
616 struct SPIREntryPoint
617 {
SPIREntryPointSPIRV_CROSS_NAMESPACE::SPIREntryPoint618 SPIREntryPoint(FunctionID self_, spv::ExecutionModel execution_model, const std::string &entry_name)
619 : self(self_)
620 , name(entry_name)
621 , orig_name(entry_name)
622 , model(execution_model)
623 {
624 }
625 SPIREntryPoint() = default;
626
627 FunctionID self = 0;
628 std::string name;
629 std::string orig_name;
630 SmallVector<VariableID> interface_variables;
631
632 Bitset flags;
633 struct
634 {
635 uint32_t x = 0, y = 0, z = 0;
636 uint32_t constant = 0; // Workgroup size can be expressed as a constant/spec-constant instead.
637 } workgroup_size;
638 uint32_t invocations = 0;
639 uint32_t output_vertices = 0;
640 spv::ExecutionModel model = spv::ExecutionModelMax;
641 };
642
643 struct SPIRExpression : IVariant
644 {
645 enum
646 {
647 type = TypeExpression
648 };
649
650 // Only created by the backend target to avoid creating tons of temporaries.
SPIRExpressionSPIRV_CROSS_NAMESPACE::SPIRExpression651 SPIRExpression(std::string expr, TypeID expression_type_, bool immutable_)
652 : expression(move(expr))
653 , expression_type(expression_type_)
654 , immutable(immutable_)
655 {
656 }
657
658 // If non-zero, prepend expression with to_expression(base_expression).
659 // Used in amortizing multiple calls to to_expression()
660 // where in certain cases that would quickly force a temporary when not needed.
661 ID base_expression = 0;
662
663 std::string expression;
664 TypeID expression_type = 0;
665
666 // If this expression is a forwarded load,
667 // allow us to reference the original variable.
668 ID loaded_from = 0;
669
670 // If this expression will never change, we can avoid lots of temporaries
671 // in high level source.
672 // An expression being immutable can be speculative,
673 // it is assumed that this is true almost always.
674 bool immutable = false;
675
676 // Before use, this expression must be transposed.
677 // This is needed for targets which don't support row_major layouts.
678 bool need_transpose = false;
679
680 // Whether or not this is an access chain expression.
681 bool access_chain = false;
682
683 // A list of expressions which this expression depends on.
684 SmallVector<ID> expression_dependencies;
685
686 // By reading this expression, we implicitly read these expressions as well.
687 // Used by access chain Store and Load since we read multiple expressions in this case.
688 SmallVector<ID> implied_read_expressions;
689
690 SPIRV_CROSS_DECLARE_CLONE(SPIRExpression)
691 };
692
693 struct SPIRFunctionPrototype : IVariant
694 {
695 enum
696 {
697 type = TypeFunctionPrototype
698 };
699
SPIRFunctionPrototypeSPIRV_CROSS_NAMESPACE::SPIRFunctionPrototype700 explicit SPIRFunctionPrototype(TypeID return_type_)
701 : return_type(return_type_)
702 {
703 }
704
705 TypeID return_type;
706 SmallVector<uint32_t> parameter_types;
707
708 SPIRV_CROSS_DECLARE_CLONE(SPIRFunctionPrototype)
709 };
710
711 struct SPIRBlock : IVariant
712 {
713 enum
714 {
715 type = TypeBlock
716 };
717
718 enum Terminator
719 {
720 Unknown,
721 Direct, // Emit next block directly without a particular condition.
722
723 Select, // Block ends with an if/else block.
724 MultiSelect, // Block ends with switch statement.
725
726 Return, // Block ends with return.
727 Unreachable, // Noop
728 Kill // Discard
729 };
730
731 enum Merge
732 {
733 MergeNone,
734 MergeLoop,
735 MergeSelection
736 };
737
738 enum Hints
739 {
740 HintNone,
741 HintUnroll,
742 HintDontUnroll,
743 HintFlatten,
744 HintDontFlatten
745 };
746
747 enum Method
748 {
749 MergeToSelectForLoop,
750 MergeToDirectForLoop,
751 MergeToSelectContinueForLoop
752 };
753
754 enum ContinueBlockType
755 {
756 ContinueNone,
757
758 // Continue block is branchless and has at least one instruction.
759 ForLoop,
760
761 // Noop continue block.
762 WhileLoop,
763
764 // Continue block is conditional.
765 DoWhileLoop,
766
767 // Highly unlikely that anything will use this,
768 // since it is really awkward/impossible to express in GLSL.
769 ComplexLoop
770 };
771
772 enum
773 {
774 NoDominator = 0xffffffffu
775 };
776
777 Terminator terminator = Unknown;
778 Merge merge = MergeNone;
779 Hints hint = HintNone;
780 BlockID next_block = 0;
781 BlockID merge_block = 0;
782 BlockID continue_block = 0;
783
784 ID return_value = 0; // If 0, return nothing (void).
785 ID condition = 0;
786 BlockID true_block = 0;
787 BlockID false_block = 0;
788 BlockID default_block = 0;
789
790 SmallVector<Instruction> ops;
791
792 struct Phi
793 {
794 ID local_variable; // flush local variable ...
795 BlockID parent; // If we're in from_block and want to branch into this block ...
796 VariableID function_variable; // to this function-global "phi" variable first.
797 };
798
799 // Before entering this block flush out local variables to magical "phi" variables.
800 SmallVector<Phi> phi_variables;
801
802 // Declare these temporaries before beginning the block.
803 // Used for handling complex continue blocks which have side effects.
804 SmallVector<std::pair<TypeID, ID>> declare_temporary;
805
806 // Declare these temporaries, but only conditionally if this block turns out to be
807 // a complex loop header.
808 SmallVector<std::pair<TypeID, ID>> potential_declare_temporary;
809
810 struct Case
811 {
812 uint32_t value;
813 BlockID block;
814 };
815 SmallVector<Case> cases;
816
817 // If we have tried to optimize code for this block but failed,
818 // keep track of this.
819 bool disable_block_optimization = false;
820
821 // If the continue block is complex, fallback to "dumb" for loops.
822 bool complex_continue = false;
823
824 // Do we need a ladder variable to defer breaking out of a loop construct after a switch block?
825 bool need_ladder_break = false;
826
827 // If marked, we have explicitly handled Phi from this block, so skip any flushes related to that on a branch.
828 // Used to handle an edge case with switch and case-label fallthrough where fall-through writes to Phi.
829 BlockID ignore_phi_from_block = 0;
830
831 // The dominating block which this block might be within.
832 // Used in continue; blocks to determine if we really need to write continue.
833 BlockID loop_dominator = 0;
834
835 // All access to these variables are dominated by this block,
836 // so before branching anywhere we need to make sure that we declare these variables.
837 SmallVector<VariableID> dominated_variables;
838
839 // These are variables which should be declared in a for loop header, if we
840 // fail to use a classic for-loop,
841 // we remove these variables, and fall back to regular variables outside the loop.
842 SmallVector<VariableID> loop_variables;
843
844 // Some expressions are control-flow dependent, i.e. any instruction which relies on derivatives or
845 // sub-group-like operations.
846 // Make sure that we only use these expressions in the original block.
847 SmallVector<ID> invalidate_expressions;
848
849 SPIRV_CROSS_DECLARE_CLONE(SPIRBlock)
850 };
851
852 struct SPIRFunction : IVariant
853 {
854 enum
855 {
856 type = TypeFunction
857 };
858
SPIRFunctionSPIRV_CROSS_NAMESPACE::SPIRFunction859 SPIRFunction(TypeID return_type_, TypeID function_type_)
860 : return_type(return_type_)
861 , function_type(function_type_)
862 {
863 }
864
865 struct Parameter
866 {
867 TypeID type;
868 ID id;
869 uint32_t read_count;
870 uint32_t write_count;
871
872 // Set to true if this parameter aliases a global variable,
873 // used mostly in Metal where global variables
874 // have to be passed down to functions as regular arguments.
875 // However, for this kind of variable, we should not care about
876 // read and write counts as access to the function arguments
877 // is not local to the function in question.
878 bool alias_global_variable;
879 };
880
881 // When calling a function, and we're remapping separate image samplers,
882 // resolve these arguments into combined image samplers and pass them
883 // as additional arguments in this order.
884 // It gets more complicated as functions can pull in their own globals
885 // and combine them with parameters,
886 // so we need to distinguish if something is local parameter index
887 // or a global ID.
888 struct CombinedImageSamplerParameter
889 {
890 VariableID id;
891 VariableID image_id;
892 VariableID sampler_id;
893 bool global_image;
894 bool global_sampler;
895 bool depth;
896 };
897
898 TypeID return_type;
899 TypeID function_type;
900 SmallVector<Parameter> arguments;
901
902 // Can be used by backends to add magic arguments.
903 // Currently used by combined image/sampler implementation.
904
905 SmallVector<Parameter> shadow_arguments;
906 SmallVector<VariableID> local_variables;
907 BlockID entry_block = 0;
908 SmallVector<BlockID> blocks;
909 SmallVector<CombinedImageSamplerParameter> combined_parameters;
910
911 struct EntryLine
912 {
913 uint32_t file_id = 0;
914 uint32_t line_literal = 0;
915 };
916 EntryLine entry_line;
917
add_local_variableSPIRV_CROSS_NAMESPACE::SPIRFunction918 void add_local_variable(VariableID id)
919 {
920 local_variables.push_back(id);
921 }
922
add_parameterSPIRV_CROSS_NAMESPACE::SPIRFunction923 void add_parameter(TypeID parameter_type, ID id, bool alias_global_variable = false)
924 {
925 // Arguments are read-only until proven otherwise.
926 arguments.push_back({ parameter_type, id, 0u, 0u, alias_global_variable });
927 }
928
929 // Hooks to be run when the function returns.
930 // Mostly used for lowering internal data structures onto flattened structures.
931 // Need to defer this, because they might rely on things which change during compilation.
932 // Intentionally not a small vector, this one is rare, and std::function can be large.
933 Vector<std::function<void()>> fixup_hooks_out;
934
935 // Hooks to be run when the function begins.
936 // Mostly used for populating internal data structures from flattened structures.
937 // Need to defer this, because they might rely on things which change during compilation.
938 // Intentionally not a small vector, this one is rare, and std::function can be large.
939 Vector<std::function<void()>> fixup_hooks_in;
940
941 bool active = false;
942 bool flush_undeclared = true;
943 bool do_combined_parameters = true;
944
945 SPIRV_CROSS_DECLARE_CLONE(SPIRFunction)
946 };
947
948 struct SPIRAccessChain : IVariant
949 {
950 enum
951 {
952 type = TypeAccessChain
953 };
954
SPIRAccessChainSPIRV_CROSS_NAMESPACE::SPIRAccessChain955 SPIRAccessChain(TypeID basetype_, spv::StorageClass storage_, std::string base_, std::string dynamic_index_,
956 int32_t static_index_)
957 : basetype(basetype_)
958 , storage(storage_)
959 , base(std::move(base_))
960 , dynamic_index(std::move(dynamic_index_))
961 , static_index(static_index_)
962 {
963 }
964
965 // The access chain represents an offset into a buffer.
966 // Some backends need more complicated handling of access chains to be able to use buffers, like HLSL
967 // which has no usable buffer type ala GLSL SSBOs.
968 // StructuredBuffer is too limited, so our only option is to deal with ByteAddressBuffer which works with raw addresses.
969
970 TypeID basetype;
971 spv::StorageClass storage;
972 std::string base;
973 std::string dynamic_index;
974 int32_t static_index;
975
976 VariableID loaded_from = 0;
977 uint32_t matrix_stride = 0;
978 bool row_major_matrix = false;
979 bool immutable = false;
980
981 // By reading this expression, we implicitly read these expressions as well.
982 // Used by access chain Store and Load since we read multiple expressions in this case.
983 SmallVector<ID> implied_read_expressions;
984
985 SPIRV_CROSS_DECLARE_CLONE(SPIRAccessChain)
986 };
987
988 struct SPIRVariable : IVariant
989 {
990 enum
991 {
992 type = TypeVariable
993 };
994
995 SPIRVariable() = default;
SPIRVariableSPIRV_CROSS_NAMESPACE::SPIRVariable996 SPIRVariable(TypeID basetype_, spv::StorageClass storage_, ID initializer_ = 0, VariableID basevariable_ = 0)
997 : basetype(basetype_)
998 , storage(storage_)
999 , initializer(initializer_)
1000 , basevariable(basevariable_)
1001 {
1002 }
1003
1004 TypeID basetype = 0;
1005 spv::StorageClass storage = spv::StorageClassGeneric;
1006 uint32_t decoration = 0;
1007 ID initializer = 0;
1008 VariableID basevariable = 0;
1009
1010 SmallVector<uint32_t> dereference_chain;
1011 bool compat_builtin = false;
1012
1013 // If a variable is shadowed, we only statically assign to it
1014 // and never actually emit a statement for it.
1015 // When we read the variable as an expression, just forward
1016 // shadowed_id as the expression.
1017 bool statically_assigned = false;
1018 ID static_expression = 0;
1019
1020 // Temporaries which can remain forwarded as long as this variable is not modified.
1021 SmallVector<ID> dependees;
1022 bool forwardable = true;
1023
1024 bool deferred_declaration = false;
1025 bool phi_variable = false;
1026
1027 // Used to deal with Phi variable flushes. See flush_phi().
1028 bool allocate_temporary_copy = false;
1029
1030 bool remapped_variable = false;
1031 uint32_t remapped_components = 0;
1032
1033 // The block which dominates all access to this variable.
1034 BlockID dominator = 0;
1035 // If true, this variable is a loop variable, when accessing the variable
1036 // outside a loop,
1037 // we should statically forward it.
1038 bool loop_variable = false;
1039 // Set to true while we're inside the for loop.
1040 bool loop_variable_enable = false;
1041
1042 SPIRFunction::Parameter *parameter = nullptr;
1043
1044 SPIRV_CROSS_DECLARE_CLONE(SPIRVariable)
1045 };
1046
1047 struct SPIRConstant : IVariant
1048 {
1049 enum
1050 {
1051 type = TypeConstant
1052 };
1053
1054 union Constant
1055 {
1056 uint32_t u32;
1057 int32_t i32;
1058 float f32;
1059
1060 uint64_t u64;
1061 int64_t i64;
1062 double f64;
1063 };
1064
1065 struct ConstantVector
1066 {
1067 Constant r[4];
1068 // If != 0, this element is a specialization constant, and we should keep track of it as such.
1069 ID id[4];
1070 uint32_t vecsize = 1;
1071
ConstantVectorSPIRV_CROSS_NAMESPACE::SPIRConstant::ConstantVector1072 ConstantVector()
1073 {
1074 memset(r, 0, sizeof(r));
1075 }
1076 };
1077
1078 struct ConstantMatrix
1079 {
1080 ConstantVector c[4];
1081 // If != 0, this column is a specialization constant, and we should keep track of it as such.
1082 ID id[4];
1083 uint32_t columns = 1;
1084 };
1085
f16_to_f32SPIRV_CROSS_NAMESPACE::SPIRConstant1086 static inline float f16_to_f32(uint16_t u16_value)
1087 {
1088 // Based on the GLM implementation.
1089 int s = (u16_value >> 15) & 0x1;
1090 int e = (u16_value >> 10) & 0x1f;
1091 int m = (u16_value >> 0) & 0x3ff;
1092
1093 union
1094 {
1095 float f32;
1096 uint32_t u32;
1097 } u;
1098
1099 if (e == 0)
1100 {
1101 if (m == 0)
1102 {
1103 u.u32 = uint32_t(s) << 31;
1104 return u.f32;
1105 }
1106 else
1107 {
1108 while ((m & 0x400) == 0)
1109 {
1110 m <<= 1;
1111 e--;
1112 }
1113
1114 e++;
1115 m &= ~0x400;
1116 }
1117 }
1118 else if (e == 31)
1119 {
1120 if (m == 0)
1121 {
1122 u.u32 = (uint32_t(s) << 31) | 0x7f800000u;
1123 return u.f32;
1124 }
1125 else
1126 {
1127 u.u32 = (uint32_t(s) << 31) | 0x7f800000u | (m << 13);
1128 return u.f32;
1129 }
1130 }
1131
1132 e += 127 - 15;
1133 m <<= 13;
1134 u.u32 = (uint32_t(s) << 31) | (e << 23) | m;
1135 return u.f32;
1136 }
1137
specialization_constant_idSPIRV_CROSS_NAMESPACE::SPIRConstant1138 inline uint32_t specialization_constant_id(uint32_t col, uint32_t row) const
1139 {
1140 return m.c[col].id[row];
1141 }
1142
specialization_constant_idSPIRV_CROSS_NAMESPACE::SPIRConstant1143 inline uint32_t specialization_constant_id(uint32_t col) const
1144 {
1145 return m.id[col];
1146 }
1147
scalarSPIRV_CROSS_NAMESPACE::SPIRConstant1148 inline uint32_t scalar(uint32_t col = 0, uint32_t row = 0) const
1149 {
1150 return m.c[col].r[row].u32;
1151 }
1152
scalar_i16SPIRV_CROSS_NAMESPACE::SPIRConstant1153 inline int16_t scalar_i16(uint32_t col = 0, uint32_t row = 0) const
1154 {
1155 return int16_t(m.c[col].r[row].u32 & 0xffffu);
1156 }
1157
scalar_u16SPIRV_CROSS_NAMESPACE::SPIRConstant1158 inline uint16_t scalar_u16(uint32_t col = 0, uint32_t row = 0) const
1159 {
1160 return uint16_t(m.c[col].r[row].u32 & 0xffffu);
1161 }
1162
scalar_i8SPIRV_CROSS_NAMESPACE::SPIRConstant1163 inline int8_t scalar_i8(uint32_t col = 0, uint32_t row = 0) const
1164 {
1165 return int8_t(m.c[col].r[row].u32 & 0xffu);
1166 }
1167
scalar_u8SPIRV_CROSS_NAMESPACE::SPIRConstant1168 inline uint8_t scalar_u8(uint32_t col = 0, uint32_t row = 0) const
1169 {
1170 return uint8_t(m.c[col].r[row].u32 & 0xffu);
1171 }
1172
scalar_f16SPIRV_CROSS_NAMESPACE::SPIRConstant1173 inline float scalar_f16(uint32_t col = 0, uint32_t row = 0) const
1174 {
1175 return f16_to_f32(scalar_u16(col, row));
1176 }
1177
scalar_f32SPIRV_CROSS_NAMESPACE::SPIRConstant1178 inline float scalar_f32(uint32_t col = 0, uint32_t row = 0) const
1179 {
1180 return m.c[col].r[row].f32;
1181 }
1182
scalar_i32SPIRV_CROSS_NAMESPACE::SPIRConstant1183 inline int32_t scalar_i32(uint32_t col = 0, uint32_t row = 0) const
1184 {
1185 return m.c[col].r[row].i32;
1186 }
1187
scalar_f64SPIRV_CROSS_NAMESPACE::SPIRConstant1188 inline double scalar_f64(uint32_t col = 0, uint32_t row = 0) const
1189 {
1190 return m.c[col].r[row].f64;
1191 }
1192
scalar_i64SPIRV_CROSS_NAMESPACE::SPIRConstant1193 inline int64_t scalar_i64(uint32_t col = 0, uint32_t row = 0) const
1194 {
1195 return m.c[col].r[row].i64;
1196 }
1197
scalar_u64SPIRV_CROSS_NAMESPACE::SPIRConstant1198 inline uint64_t scalar_u64(uint32_t col = 0, uint32_t row = 0) const
1199 {
1200 return m.c[col].r[row].u64;
1201 }
1202
vectorSPIRV_CROSS_NAMESPACE::SPIRConstant1203 inline const ConstantVector &vector() const
1204 {
1205 return m.c[0];
1206 }
1207
vector_sizeSPIRV_CROSS_NAMESPACE::SPIRConstant1208 inline uint32_t vector_size() const
1209 {
1210 return m.c[0].vecsize;
1211 }
1212
columnsSPIRV_CROSS_NAMESPACE::SPIRConstant1213 inline uint32_t columns() const
1214 {
1215 return m.columns;
1216 }
1217
make_nullSPIRV_CROSS_NAMESPACE::SPIRConstant1218 inline void make_null(const SPIRType &constant_type_)
1219 {
1220 m = {};
1221 m.columns = constant_type_.columns;
1222 for (auto &c : m.c)
1223 c.vecsize = constant_type_.vecsize;
1224 }
1225
constant_is_nullSPIRV_CROSS_NAMESPACE::SPIRConstant1226 inline bool constant_is_null() const
1227 {
1228 if (specialization)
1229 return false;
1230 if (!subconstants.empty())
1231 return false;
1232
1233 for (uint32_t col = 0; col < columns(); col++)
1234 for (uint32_t row = 0; row < vector_size(); row++)
1235 if (scalar_u64(col, row) != 0)
1236 return false;
1237
1238 return true;
1239 }
1240
SPIRConstantSPIRV_CROSS_NAMESPACE::SPIRConstant1241 explicit SPIRConstant(uint32_t constant_type_)
1242 : constant_type(constant_type_)
1243 {
1244 }
1245
1246 SPIRConstant() = default;
1247
SPIRConstantSPIRV_CROSS_NAMESPACE::SPIRConstant1248 SPIRConstant(TypeID constant_type_, const uint32_t *elements, uint32_t num_elements, bool specialized)
1249 : constant_type(constant_type_)
1250 , specialization(specialized)
1251 {
1252 subconstants.reserve(num_elements);
1253 for (uint32_t i = 0; i < num_elements; i++)
1254 subconstants.push_back(elements[i]);
1255 specialization = specialized;
1256 }
1257
1258 // Construct scalar (32-bit).
SPIRConstantSPIRV_CROSS_NAMESPACE::SPIRConstant1259 SPIRConstant(TypeID constant_type_, uint32_t v0, bool specialized)
1260 : constant_type(constant_type_)
1261 , specialization(specialized)
1262 {
1263 m.c[0].r[0].u32 = v0;
1264 m.c[0].vecsize = 1;
1265 m.columns = 1;
1266 }
1267
1268 // Construct scalar (64-bit).
SPIRConstantSPIRV_CROSS_NAMESPACE::SPIRConstant1269 SPIRConstant(TypeID constant_type_, uint64_t v0, bool specialized)
1270 : constant_type(constant_type_)
1271 , specialization(specialized)
1272 {
1273 m.c[0].r[0].u64 = v0;
1274 m.c[0].vecsize = 1;
1275 m.columns = 1;
1276 }
1277
1278 // Construct vectors and matrices.
SPIRConstantSPIRV_CROSS_NAMESPACE::SPIRConstant1279 SPIRConstant(TypeID constant_type_, const SPIRConstant *const *vector_elements, uint32_t num_elements,
1280 bool specialized)
1281 : constant_type(constant_type_)
1282 , specialization(specialized)
1283 {
1284 bool matrix = vector_elements[0]->m.c[0].vecsize > 1;
1285
1286 if (matrix)
1287 {
1288 m.columns = num_elements;
1289
1290 for (uint32_t i = 0; i < num_elements; i++)
1291 {
1292 m.c[i] = vector_elements[i]->m.c[0];
1293 if (vector_elements[i]->specialization)
1294 m.id[i] = vector_elements[i]->self;
1295 }
1296 }
1297 else
1298 {
1299 m.c[0].vecsize = num_elements;
1300 m.columns = 1;
1301
1302 for (uint32_t i = 0; i < num_elements; i++)
1303 {
1304 m.c[0].r[i] = vector_elements[i]->m.c[0].r[0];
1305 if (vector_elements[i]->specialization)
1306 m.c[0].id[i] = vector_elements[i]->self;
1307 }
1308 }
1309 }
1310
1311 TypeID constant_type = 0;
1312 ConstantMatrix m;
1313
1314 // If this constant is a specialization constant (i.e. created with OpSpecConstant*).
1315 bool specialization = false;
1316 // If this constant is used as an array length which creates specialization restrictions on some backends.
1317 bool is_used_as_array_length = false;
1318
1319 // If true, this is a LUT, and should always be declared in the outer scope.
1320 bool is_used_as_lut = false;
1321
1322 // For composites which are constant arrays, etc.
1323 SmallVector<ConstantID> subconstants;
1324
1325 // Non-Vulkan GLSL, HLSL and sometimes MSL emits defines for each specialization constant,
1326 // and uses them to initialize the constant. This allows the user
1327 // to still be able to specialize the value by supplying corresponding
1328 // preprocessor directives before compiling the shader.
1329 std::string specialization_constant_macro_name;
1330
1331 SPIRV_CROSS_DECLARE_CLONE(SPIRConstant)
1332 };
1333
1334 // Variants have a very specific allocation scheme.
1335 struct ObjectPoolGroup
1336 {
1337 std::unique_ptr<ObjectPoolBase> pools[TypeCount];
1338 };
1339
1340 class Variant
1341 {
1342 public:
Variant(ObjectPoolGroup * group_)1343 explicit Variant(ObjectPoolGroup *group_)
1344 : group(group_)
1345 {
1346 }
1347
~Variant()1348 ~Variant()
1349 {
1350 if (holder)
1351 group->pools[type]->free_opaque(holder);
1352 }
1353
1354 // Marking custom move constructor as noexcept is important.
Variant(Variant && other)1355 Variant(Variant &&other) SPIRV_CROSS_NOEXCEPT
1356 {
1357 *this = std::move(other);
1358 }
1359
1360 // We cannot copy from other variant without our own pool group.
1361 // Have to explicitly copy.
1362 Variant(const Variant &variant) = delete;
1363
1364 // Marking custom move constructor as noexcept is important.
operator =(Variant && other)1365 Variant &operator=(Variant &&other) SPIRV_CROSS_NOEXCEPT
1366 {
1367 if (this != &other)
1368 {
1369 if (holder)
1370 group->pools[type]->free_opaque(holder);
1371 holder = other.holder;
1372 group = other.group;
1373 type = other.type;
1374 allow_type_rewrite = other.allow_type_rewrite;
1375
1376 other.holder = nullptr;
1377 other.type = TypeNone;
1378 }
1379 return *this;
1380 }
1381
1382 // This copy/clone should only be called in the Compiler constructor.
1383 // If this is called inside ::compile(), we invalidate any references we took higher in the stack.
1384 // This should never happen.
operator =(const Variant & other)1385 Variant &operator=(const Variant &other)
1386 {
1387 //#define SPIRV_CROSS_COPY_CONSTRUCTOR_SANITIZE
1388 #ifdef SPIRV_CROSS_COPY_CONSTRUCTOR_SANITIZE
1389 abort();
1390 #endif
1391 if (this != &other)
1392 {
1393 if (holder)
1394 group->pools[type]->free_opaque(holder);
1395
1396 if (other.holder)
1397 holder = other.holder->clone(group->pools[other.type].get());
1398 else
1399 holder = nullptr;
1400
1401 type = other.type;
1402 allow_type_rewrite = other.allow_type_rewrite;
1403 }
1404 return *this;
1405 }
1406
set(IVariant * val,Types new_type)1407 void set(IVariant *val, Types new_type)
1408 {
1409 if (holder)
1410 group->pools[type]->free_opaque(holder);
1411 holder = nullptr;
1412
1413 if (!allow_type_rewrite && type != TypeNone && type != new_type)
1414 {
1415 if (val)
1416 group->pools[new_type]->free_opaque(val);
1417 SPIRV_CROSS_THROW("Overwriting a variant with new type.");
1418 }
1419
1420 holder = val;
1421 type = new_type;
1422 allow_type_rewrite = false;
1423 }
1424
1425 template <typename T, typename... Ts>
1426 T *allocate_and_set(Types new_type, Ts &&... ts)
1427 {
1428 T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1429 set(val, new_type);
1430 return val;
1431 }
1432
1433 template <typename T>
get()1434 T &get()
1435 {
1436 if (!holder)
1437 SPIRV_CROSS_THROW("nullptr");
1438 if (static_cast<Types>(T::type) != type)
1439 SPIRV_CROSS_THROW("Bad cast");
1440 return *static_cast<T *>(holder);
1441 }
1442
1443 template <typename T>
get() const1444 const T &get() const
1445 {
1446 if (!holder)
1447 SPIRV_CROSS_THROW("nullptr");
1448 if (static_cast<Types>(T::type) != type)
1449 SPIRV_CROSS_THROW("Bad cast");
1450 return *static_cast<const T *>(holder);
1451 }
1452
get_type() const1453 Types get_type() const
1454 {
1455 return type;
1456 }
1457
get_id() const1458 ID get_id() const
1459 {
1460 return holder ? holder->self : ID(0);
1461 }
1462
empty() const1463 bool empty() const
1464 {
1465 return !holder;
1466 }
1467
reset()1468 void reset()
1469 {
1470 if (holder)
1471 group->pools[type]->free_opaque(holder);
1472 holder = nullptr;
1473 type = TypeNone;
1474 }
1475
set_allow_type_rewrite()1476 void set_allow_type_rewrite()
1477 {
1478 allow_type_rewrite = true;
1479 }
1480
1481 private:
1482 ObjectPoolGroup *group = nullptr;
1483 IVariant *holder = nullptr;
1484 Types type = TypeNone;
1485 bool allow_type_rewrite = false;
1486 };
1487
1488 template <typename T>
variant_get(Variant & var)1489 T &variant_get(Variant &var)
1490 {
1491 return var.get<T>();
1492 }
1493
1494 template <typename T>
variant_get(const Variant & var)1495 const T &variant_get(const Variant &var)
1496 {
1497 return var.get<T>();
1498 }
1499
1500 template <typename T, typename... P>
1501 T &variant_set(Variant &var, P &&... args)
1502 {
1503 auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1504 return *ptr;
1505 }
1506
1507 struct AccessChainMeta
1508 {
1509 uint32_t storage_physical_type = 0;
1510 bool need_transpose = false;
1511 bool storage_is_packed = false;
1512 bool storage_is_invariant = false;
1513 };
1514
1515 enum ExtendedDecorations
1516 {
1517 // Marks if a buffer block is re-packed, i.e. member declaration might be subject to PhysicalTypeID remapping and padding.
1518 SPIRVCrossDecorationBufferBlockRepacked = 0,
1519
1520 // A type in a buffer block might be declared with a different physical type than the logical type.
1521 // If this is not set, PhysicalTypeID == the SPIR-V type as declared.
1522 SPIRVCrossDecorationPhysicalTypeID,
1523
1524 // Marks if the physical type is to be declared with tight packing rules, i.e. packed_floatN on MSL and friends.
1525 // If this is set, PhysicalTypeID might also be set. It can be set to same as logical type if all we're doing
1526 // is converting float3 to packed_float3 for example.
1527 // If this is marked on a struct, it means the struct itself must use only Packed types for all its members.
1528 SPIRVCrossDecorationPhysicalTypePacked,
1529
1530 // The padding in bytes before declaring this struct member.
1531 // If used on a struct type, marks the target size of a struct.
1532 SPIRVCrossDecorationPaddingTarget,
1533
1534 SPIRVCrossDecorationInterfaceMemberIndex,
1535 SPIRVCrossDecorationInterfaceOrigID,
1536 SPIRVCrossDecorationResourceIndexPrimary,
1537 // Used for decorations like resource indices for samplers when part of combined image samplers.
1538 // A variable might need to hold two resource indices in this case.
1539 SPIRVCrossDecorationResourceIndexSecondary,
1540 // Used for resource indices for multiplanar images when part of combined image samplers.
1541 SPIRVCrossDecorationResourceIndexTertiary,
1542 SPIRVCrossDecorationResourceIndexQuaternary,
1543
1544 // Marks a buffer block for using explicit offsets (GLSL/HLSL).
1545 SPIRVCrossDecorationExplicitOffset,
1546
1547 // Apply to a variable in the Input storage class; marks it as holding the base group passed to vkCmdDispatchBase().
1548 // In MSL, this is used to adjust the WorkgroupId and GlobalInvocationId variables.
1549 SPIRVCrossDecorationBuiltInDispatchBase,
1550
1551 // Apply to a variable that is a function parameter; marks it as being a "dynamic"
1552 // combined image-sampler. In MSL, this is used when a function parameter might hold
1553 // either a regular combined image-sampler or one that has an attached sampler
1554 // Y'CbCr conversion.
1555 SPIRVCrossDecorationDynamicImageSampler,
1556
1557 SPIRVCrossDecorationCount
1558 };
1559
1560 struct Meta
1561 {
1562 struct Decoration
1563 {
1564 std::string alias;
1565 std::string qualified_alias;
1566 std::string hlsl_semantic;
1567 Bitset decoration_flags;
1568 spv::BuiltIn builtin_type = spv::BuiltInMax;
1569 uint32_t location = 0;
1570 uint32_t component = 0;
1571 uint32_t set = 0;
1572 uint32_t binding = 0;
1573 uint32_t offset = 0;
1574 uint32_t array_stride = 0;
1575 uint32_t matrix_stride = 0;
1576 uint32_t input_attachment = 0;
1577 uint32_t spec_id = 0;
1578 uint32_t index = 0;
1579 spv::FPRoundingMode fp_rounding_mode = spv::FPRoundingModeMax;
1580 bool builtin = false;
1581
1582 struct Extended
1583 {
ExtendedSPIRV_CROSS_NAMESPACE::Meta::Decoration::Extended1584 Extended()
1585 {
1586 // MSVC 2013 workaround to init like this.
1587 for (auto &v : values)
1588 v = 0;
1589 }
1590
1591 Bitset flags;
1592 uint32_t values[SPIRVCrossDecorationCount];
1593 } extended;
1594 };
1595
1596 Decoration decoration;
1597
1598 // Intentionally not a SmallVector. Decoration is large and somewhat rare.
1599 Vector<Decoration> members;
1600
1601 std::unordered_map<uint32_t, uint32_t> decoration_word_offset;
1602
1603 // For SPV_GOOGLE_hlsl_functionality1.
1604 bool hlsl_is_magic_counter_buffer = false;
1605 // ID for the sibling counter buffer.
1606 uint32_t hlsl_magic_counter_buffer = 0;
1607 };
1608
1609 // A user callback that remaps the type of any variable.
1610 // var_name is the declared name of the variable.
1611 // name_of_type is the textual name of the type which will be used in the code unless written to by the callback.
1612 using VariableTypeRemapCallback =
1613 std::function<void(const SPIRType &type, const std::string &var_name, std::string &name_of_type)>;
1614
1615 class Hasher
1616 {
1617 public:
u32(uint32_t value)1618 inline void u32(uint32_t value)
1619 {
1620 h = (h * 0x100000001b3ull) ^ value;
1621 }
1622
get() const1623 inline uint64_t get() const
1624 {
1625 return h;
1626 }
1627
1628 private:
1629 uint64_t h = 0xcbf29ce484222325ull;
1630 };
1631
type_is_floating_point(const SPIRType & type)1632 static inline bool type_is_floating_point(const SPIRType &type)
1633 {
1634 return type.basetype == SPIRType::Half || type.basetype == SPIRType::Float || type.basetype == SPIRType::Double;
1635 }
1636
type_is_integral(const SPIRType & type)1637 static inline bool type_is_integral(const SPIRType &type)
1638 {
1639 return type.basetype == SPIRType::SByte || type.basetype == SPIRType::UByte || type.basetype == SPIRType::Short ||
1640 type.basetype == SPIRType::UShort || type.basetype == SPIRType::Int || type.basetype == SPIRType::UInt ||
1641 type.basetype == SPIRType::Int64 || type.basetype == SPIRType::UInt64;
1642 }
1643
to_signed_basetype(uint32_t width)1644 static inline SPIRType::BaseType to_signed_basetype(uint32_t width)
1645 {
1646 switch (width)
1647 {
1648 case 8:
1649 return SPIRType::SByte;
1650 case 16:
1651 return SPIRType::Short;
1652 case 32:
1653 return SPIRType::Int;
1654 case 64:
1655 return SPIRType::Int64;
1656 default:
1657 SPIRV_CROSS_THROW("Invalid bit width.");
1658 }
1659 }
1660
to_unsigned_basetype(uint32_t width)1661 static inline SPIRType::BaseType to_unsigned_basetype(uint32_t width)
1662 {
1663 switch (width)
1664 {
1665 case 8:
1666 return SPIRType::UByte;
1667 case 16:
1668 return SPIRType::UShort;
1669 case 32:
1670 return SPIRType::UInt;
1671 case 64:
1672 return SPIRType::UInt64;
1673 default:
1674 SPIRV_CROSS_THROW("Invalid bit width.");
1675 }
1676 }
1677
1678 // Returns true if an arithmetic operation does not change behavior depending on signedness.
opcode_is_sign_invariant(spv::Op opcode)1679 static inline bool opcode_is_sign_invariant(spv::Op opcode)
1680 {
1681 switch (opcode)
1682 {
1683 case spv::OpIEqual:
1684 case spv::OpINotEqual:
1685 case spv::OpISub:
1686 case spv::OpIAdd:
1687 case spv::OpIMul:
1688 case spv::OpShiftLeftLogical:
1689 case spv::OpBitwiseOr:
1690 case spv::OpBitwiseXor:
1691 case spv::OpBitwiseAnd:
1692 return true;
1693
1694 default:
1695 return false;
1696 }
1697 }
1698 } // namespace SPIRV_CROSS_NAMESPACE
1699
1700 namespace std
1701 {
1702 template <SPIRV_CROSS_NAMESPACE::Types type>
1703 struct hash<SPIRV_CROSS_NAMESPACE::TypedID<type>>
1704 {
operator ()std::hash1705 size_t operator()(const SPIRV_CROSS_NAMESPACE::TypedID<type> &value) const
1706 {
1707 return std::hash<uint32_t>()(value);
1708 }
1709 };
1710 } // namespace std
1711
1712 #endif
1713