| // |
| // Copyright 2021 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // BuildSPIRV: Helper for OutputSPIRV to build SPIR-V. |
| // |
| |
| #ifndef COMPILER_TRANSLATOR_BUILDSPIRV_H_ |
| #define COMPILER_TRANSLATOR_BUILDSPIRV_H_ |
| |
| #include "common/FixedVector.h" |
| #include "common/PackedEnums.h" |
| #include "common/bitset_utils.h" |
| #include "common/hash_utils.h" |
| #include "common/spirv/spirv_instruction_builder_autogen.h" |
| #include "compiler/translator/Compiler.h" |
| |
| namespace spirv = angle::spirv; |
| |
| namespace sh |
| { |
| // Helper classes to map types to ids |
| |
| // The same GLSL type may map to multiple SPIR-V types when said GLSL type is used differently in |
| // the shader source, for example used with |invariant| and without, used in an interface block etc. |
| // This type contains the pieces of information that differentiate SPIR-V types derived from the |
| // same GLSL type. This is referred to as "SPIR-V type specialization" henceforth. |
| struct SpirvType; |
| class SpirvTypeSpec |
| { |
| public: |
| // Some of the properties that specialize SPIR-V types apply to structs or arrays, but not to |
| // their fields or basic types. When extracting fields, array elements, columns or basic types |
| // from a type, the following helpers are used to remove any ineffective (and thus incorrect) |
| // specialization. |
| void inferDefaults(const TType &type, TCompiler *compiler); |
| void onArrayElementSelection(bool isElementTypeBlock, bool isElementTypeArray); |
| void onBlockFieldSelection(const TType &fieldType); |
| void onMatrixColumnSelection(); |
| void onVectorComponentSelection(); |
| |
| // If a structure is used in two interface blocks with different layouts, it would have |
| // to generate two SPIR-V types, as its fields' Offset decorations could be different. |
| // For non-block types, when used in an interface block as an array, they could generate |
| // different ArrayStride decorations. As such, the block storage is part of the SPIR-V type |
| // except for non-block non-array types. |
| TLayoutBlockStorage blockStorage = EbsUnspecified; |
| |
| // If a structure is used in two I/O blocks or output varyings with and without the invariant |
| // qualifier, it would also have to generate two SPIR-V types, as its fields' Invariant |
| // decorations would be different. |
| bool isInvariantBlock = false; |
| |
| // Similarly, a structure containing matrices may be used both with the column_major and |
| // row_major layout qualifier, generating two SPIR-V types with different decorations on its |
| // fields. |
| bool isRowMajorQualifiedBlock = false; |
| |
| // Arrays when used in an interface block produce a different type which is decorated with an |
| // ArrayStride. Row-major qualified arrays of matrices can potentially produce a different |
| // stride from column-major ones. |
| bool isRowMajorQualifiedArray = false; |
| |
| // Bool is disallowed in interface blocks in SPIR-V. This type is emulated with uint. This |
| // property applies to both blocks with bools in them and the bool type inside the block itself. |
| bool isOrHasBoolInInterfaceBlock = false; |
| |
| // When |patch| is specified on an I/O block, the members of the type itself are decorated with |
| // it. This is not recursively applied, and since each I/O block has a unique type, this |
| // doesn't actually result in duplicated types even if it's specializing the type. |
| bool isPatchIOBlock = false; |
| }; |
| |
| struct SpirvType |
| { |
| // If struct or interface block, the type is identified by the pointer. Note that both |
| // TStructure and TInterfaceBlock inherit from TFieldListCollection, and their difference is |
| // irrelevant as far as SPIR-V type is concerned. |
| const TFieldListCollection *block = nullptr; |
| |
| // Otherwise, it's a basic type + column, row and array dimensions, or it's an image |
| // declaration. |
| // |
| // Notes: |
| // |
| // - `precision` turns into a RelaxedPrecision decoration on the variable and instructions. |
| // - `precise` turns into a NoContraction decoration on the instructions. |
| // - `readonly`, `writeonly`, `coherent`, `volatile` and `restrict` only apply to memory object |
| // declarations |
| // - `invariant` only applies to variable or members of a block |
| // - `matrixPacking` only applies to members of a struct |
| TBasicType type = EbtFloat; |
| |
| uint8_t primarySize = 1; |
| uint8_t secondarySize = 1; |
| |
| TSpan<const unsigned int> arraySizes; |
| |
| // Only useful for image types. |
| TLayoutImageInternalFormat imageInternalFormat = EiifUnspecified; |
| |
| // For sampled images (i.e. GLSL samplers), there are two type ids; one is the OpTypeImage that |
| // declares the image itself, and one OpTypeSampledImage. `isSamplerBaseImage` distinguishes |
| // between these two types. Note that for the former, the basic type is still Ebt*Sampler* to |
| // distinguish it from storage images (which have a basic type of Ebt*Image*). |
| bool isSamplerBaseImage = false; |
| |
| // Anything that can cause the same GLSL type to produce different SPIR-V types. |
| SpirvTypeSpec typeSpec; |
| }; |
| |
| struct SpirvIdAndIdList |
| { |
| spirv::IdRef id; |
| spirv::IdRefList idList; |
| |
| bool operator==(const SpirvIdAndIdList &other) const |
| { |
| return id == other.id && idList == other.idList; |
| } |
| }; |
| |
| struct SpirvIdAndStorageClass |
| { |
| spirv::IdRef id; |
| spv::StorageClass storageClass; |
| |
| bool operator==(const SpirvIdAndStorageClass &other) const |
| { |
| return id == other.id && storageClass == other.storageClass; |
| } |
| }; |
| |
| struct SpirvTypeHash |
| { |
| size_t operator()(const sh::SpirvType &type) const |
| { |
| // Block storage must only affect the type if it's a block type or array type (in a block). |
| ASSERT(type.typeSpec.blockStorage == sh::EbsUnspecified || type.block != nullptr || |
| !type.arraySizes.empty()); |
| |
| // Invariant must only affect the type if it's a block type. |
| ASSERT(!type.typeSpec.isInvariantBlock || type.block != nullptr); |
| |
| // Row-major block must only affect the type if it's a block type. |
| ASSERT(!type.typeSpec.isRowMajorQualifiedBlock || type.block != nullptr); |
| |
| // Patch must only affect the type if it's a block type. |
| ASSERT(!type.typeSpec.isPatchIOBlock || type.block != nullptr); |
| |
| // Row-major array must only affect the type if it's an array of non-square matrices in |
| // an std140 or std430 block. |
| ASSERT(!type.typeSpec.isRowMajorQualifiedArray || |
| (type.block == nullptr && !type.arraySizes.empty() && type.secondarySize > 1 && |
| type.primarySize != type.secondarySize && |
| type.typeSpec.blockStorage != sh::EbsUnspecified)); |
| |
| size_t result = 0; |
| |
| if (!type.arraySizes.empty()) |
| { |
| result = angle::ComputeGenericHash(type.arraySizes.data(), |
| type.arraySizes.size() * sizeof(type.arraySizes[0])); |
| } |
| |
| if (type.block != nullptr) |
| { |
| return result ^ angle::ComputeGenericHash(&type.block, sizeof(type.block)) ^ |
| static_cast<size_t>(type.typeSpec.isInvariantBlock) ^ |
| (static_cast<size_t>(type.typeSpec.isRowMajorQualifiedBlock) << 1) ^ |
| (static_cast<size_t>(type.typeSpec.isRowMajorQualifiedArray) << 2) ^ |
| (static_cast<size_t>(type.typeSpec.isPatchIOBlock) << 3) ^ |
| (type.typeSpec.blockStorage << 4); |
| } |
| |
| static_assert(sh::EbtLast < 256, "Basic type doesn't fit in uint8_t"); |
| static_assert(sh::EbsLast < 8, "Block storage doesn't fit in 3 bits"); |
| static_assert(sh::EiifLast < 32, "Image format doesn't fit in 5 bits"); |
| ASSERT(type.primarySize > 0 && type.primarySize <= 4); |
| ASSERT(type.secondarySize > 0 && type.secondarySize <= 4); |
| |
| const uint8_t properties[4] = { |
| static_cast<uint8_t>(type.type), |
| static_cast<uint8_t>((type.primarySize - 1) | (type.secondarySize - 1) << 2 | |
| type.isSamplerBaseImage << 4), |
| static_cast<uint8_t>(type.typeSpec.blockStorage | type.imageInternalFormat << 3), |
| // Padding because ComputeGenericHash expects a key size divisible by 4 |
| }; |
| |
| return result ^ angle::ComputeGenericHash(properties, sizeof(properties)); |
| } |
| }; |
| |
| struct SpirvIdAndIdListHash |
| { |
| size_t operator()(const SpirvIdAndIdList &key) const |
| { |
| return angle::ComputeGenericHash(key.idList.data(), |
| key.idList.size() * sizeof(key.idList[0])) ^ |
| key.id; |
| } |
| }; |
| |
| struct SpirvIdAndStorageClassHash |
| { |
| size_t operator()(const SpirvIdAndStorageClass &key) const |
| { |
| ASSERT(key.storageClass < 16); |
| return key.storageClass | key.id << 4; |
| } |
| }; |
| |
| // Data tracked per SPIR-V type (keyed by SpirvType). |
| struct SpirvTypeData |
| { |
| // The SPIR-V id corresponding to the type. |
| spirv::IdRef id; |
| }; |
| |
| // Decorations to be applied to variable or intermediate ids which are not part of the SPIR-V type |
| // and are not specific enough (like DescriptorSet) to be handled automatically. Currently, these |
| // are: |
| // |
| // RelaxedPrecision: used to implement |lowp| and |mediump| |
| // NoContraction: used to implement |precise|. TODO: support this. It requires the precise |
| // property to be promoted through the nodes in the AST, which currently isn't. |
| // http://anglebug.com/4889 |
| // Invariant: used to implement |invariant|, which is applied to output variables. |
| // Memory qualifiers: used to implement |coherent, volatile, restrict, readonly, writeonly|, |
| // which apply to shader storage blocks, variables declared within shader |
| // storage blocks, and images. |
| // |
| // Note that Invariant applies to output variables, NoContraction to arithmetic instructions, and |
| // memory qualifiers to shader storage and images, so they are mutually exclusive. A maximum of 6 |
| // decorations are possible. FixedVector::push_back will ASSERT if the given size is ever not |
| // enough. |
| using SpirvDecorations = angle::FixedVector<spv::Decoration, 6>; |
| |
| // A block of code. SPIR-V produces forward references to blocks, such as OpBranchConditional |
| // specifying the id of the if and else blocks, each of those referencing the id of the block after |
| // the else. Additionally, local variable declarations are accumulated at the top of the first |
| // block in a function. For these reasons, each block of SPIR-V is generated separately and |
| // assembled at the end of the function, allowing prior blocks to be modified when necessary. |
| struct SpirvBlock |
| { |
| // Id of the block |
| spirv::IdRef labelId; |
| |
| // Local variable declarations. Only the first block of a function is allowed to contain any |
| // instructions here. |
| spirv::Blob localVariables; |
| |
| // Everything *after* OpLabel (which itself is not generated until blocks are assembled) and |
| // local variables. |
| spirv::Blob body; |
| |
| // Whether the block is terminated. Useful for functions without return, asserting that code is |
| // not added after return/break/continue etc (i.e. dead code, which should really be removed |
| // earlier by a transformation, but could also be hacked by returning a bogus block to contain |
| // all the "garbage" to throw away), last switch case without a break, etc. |
| bool isTerminated = false; |
| }; |
| |
| // Conditional code, constituting ifs, switches and loops. |
| struct SpirvConditional |
| { |
| // The id of blocks that make up the conditional. |
| // |
| // - For if, there are three blocks: the then, else and merge blocks |
| // - For loops, there are four blocks: the condition, body, continue and merge blocks |
| // - For switch, there are a number of blocks based on the cases. |
| // |
| // In all cases, the merge block is the last block in this list. When the conditional is done |
| // with, that's the block that will be made "current" and future instructions written to. The |
| // merge block is also the branch target of "break" instructions. |
| // |
| // For loops, the continue target block is the one before last block in this list. |
| std::vector<spirv::IdRef> blockIds; |
| |
| // Up to which block is already generated. Used by nextConditionalBlock() to generate a block |
| // and give it an id pre-determined in blockIds. |
| size_t nextBlockToWrite = 0; |
| |
| // Used to determine if continue will affect this (i.e. it's a loop). |
| bool isContinuable = false; |
| // Used to determine if break will affect this (i.e. it's a loop or switch). |
| bool isBreakable = false; |
| }; |
| |
| // List of known extensions |
| enum class SPIRVExtensions |
| { |
| // GL_OVR_multiview / SPV_KHR_multiview |
| MultiviewOVR = 0, |
| |
| InvalidEnum = 1, |
| EnumCount = 1, |
| }; |
| |
| // Helper class to construct SPIR-V |
| class SPIRVBuilder : angle::NonCopyable |
| { |
| public: |
| SPIRVBuilder(TCompiler *compiler, |
| const ShCompileOptions &compileOptions, |
| ShHashFunction64 hashFunction, |
| NameMap &nameMap); |
| |
| spirv::IdRef getNewId(const SpirvDecorations &decorations); |
| SpirvType getSpirvType(const TType &type, const SpirvTypeSpec &typeSpec) const; |
| const SpirvTypeData &getTypeData(const TType &type, const SpirvTypeSpec &typeSpec); |
| const SpirvTypeData &getTypeDataOverrideTypeSpec(const TType &type, |
| const SpirvTypeSpec &typeSpec); |
| const SpirvTypeData &getSpirvTypeData(const SpirvType &type, const TSymbol *block); |
| spirv::IdRef getBasicTypeId(TBasicType basicType, size_t size); |
| spirv::IdRef getTypePointerId(spirv::IdRef typeId, spv::StorageClass storageClass); |
| spirv::IdRef getFunctionTypeId(spirv::IdRef returnTypeId, const spirv::IdRefList ¶mTypeIds); |
| |
| // Decorations that may apply to intermediate instructions (in addition to variables). |
| // |precise| is only applicable to arithmetic nodes. |
| SpirvDecorations getDecorations(const TType &type); |
| SpirvDecorations getArithmeticDecorations(const TType &type, bool isPrecise, TOperator op); |
| |
| // Extended instructions |
| spirv::IdRef getExtInstImportIdStd(); |
| |
| spirv::Blob *getSpirvDebug() { return &mSpirvDebug; } |
| spirv::Blob *getSpirvDecorations() { return &mSpirvDecorations; } |
| spirv::Blob *getSpirvTypeAndConstantDecls() { return &mSpirvTypeAndConstantDecls; } |
| spirv::Blob *getSpirvTypePointerDecls() { return &mSpirvTypePointerDecls; } |
| spirv::Blob *getSpirvFunctionTypeDecls() { return &mSpirvFunctionTypeDecls; } |
| spirv::Blob *getSpirvVariableDecls() { return &mSpirvVariableDecls; } |
| spirv::Blob *getSpirvFunctions() { return &mSpirvFunctions; } |
| spirv::Blob *getSpirvCurrentFunctionBlock() |
| { |
| ASSERT(!mSpirvCurrentFunctionBlocks.empty() && |
| !mSpirvCurrentFunctionBlocks.back().isTerminated); |
| return &mSpirvCurrentFunctionBlocks.back().body; |
| } |
| spirv::IdRef getSpirvCurrentFunctionBlockId() |
| { |
| ASSERT(!mSpirvCurrentFunctionBlocks.empty() && |
| !mSpirvCurrentFunctionBlocks.back().isTerminated); |
| return mSpirvCurrentFunctionBlocks.back().labelId; |
| } |
| bool isCurrentFunctionBlockTerminated() const |
| { |
| ASSERT(!mSpirvCurrentFunctionBlocks.empty()); |
| return mSpirvCurrentFunctionBlocks.back().isTerminated; |
| } |
| void terminateCurrentFunctionBlock() |
| { |
| ASSERT(!mSpirvCurrentFunctionBlocks.empty()); |
| mSpirvCurrentFunctionBlocks.back().isTerminated = true; |
| } |
| const SpirvConditional *getCurrentConditional() { return &mConditionalStack.back(); } |
| |
| bool isInvariantOutput(const TType &type) const; |
| |
| void addCapability(spv::Capability capability); |
| void addExecutionMode(spv::ExecutionMode executionMode); |
| void addExtension(SPIRVExtensions extension); |
| void setEntryPointId(spirv::IdRef id); |
| void addEntryPointInterfaceVariableId(spirv::IdRef id); |
| void writePerVertexBuiltIns(const TType &type, spirv::IdRef typeId); |
| void writeInterfaceVariableDecorations(const TType &type, spirv::IdRef variableId); |
| void writeBranchConditional(spirv::IdRef conditionValue, |
| spirv::IdRef trueBlock, |
| spirv::IdRef falseBlock, |
| spirv::IdRef mergeBlock); |
| void writeBranchConditionalBlockEnd(); |
| void writeLoopHeader(spirv::IdRef branchToBlock, |
| spirv::IdRef continueBlock, |
| spirv::IdRef mergeBlock); |
| void writeLoopConditionEnd(spirv::IdRef conditionValue, |
| spirv::IdRef branchToBlock, |
| spirv::IdRef mergeBlock); |
| void writeLoopContinueEnd(spirv::IdRef headerBlock); |
| void writeLoopBodyEnd(spirv::IdRef continueBlock); |
| void writeSwitch(spirv::IdRef conditionValue, |
| spirv::IdRef defaultBlock, |
| const spirv::PairLiteralIntegerIdRefList &targetPairList, |
| spirv::IdRef mergeBlock); |
| void writeSwitchCaseBlockEnd(); |
| |
| spirv::IdRef getBoolConstant(bool value); |
| spirv::IdRef getUintConstant(uint32_t value); |
| spirv::IdRef getIntConstant(int32_t value); |
| spirv::IdRef getFloatConstant(float value); |
| spirv::IdRef getUvecConstant(uint32_t value, int size); |
| spirv::IdRef getIvecConstant(int32_t value, int size); |
| spirv::IdRef getVecConstant(float value, int size); |
| spirv::IdRef getCompositeConstant(spirv::IdRef typeId, const spirv::IdRefList &values); |
| spirv::IdRef getNullConstant(spirv::IdRef typeId); |
| |
| // Helpers to start and end a function. |
| void startNewFunction(spirv::IdRef functionId, const TFunction *func); |
| void assembleSpirvFunctionBlocks(); |
| |
| // Helper to declare a variable. Function-local variables must be placed in the first block of |
| // the current function. |
| spirv::IdRef declareVariable(spirv::IdRef typeId, |
| spv::StorageClass storageClass, |
| const SpirvDecorations &decorations, |
| spirv::IdRef *initializerId, |
| const char *name); |
| // Helper to declare specialization constants. |
| spirv::IdRef declareSpecConst(TBasicType type, int id, const char *name); |
| |
| // Helpers for conditionals. |
| void startConditional(size_t blockCount, bool isContinuable, bool isBreakable); |
| void nextConditionalBlock(); |
| void endConditional(); |
| bool isInLoop() const; |
| spirv::IdRef getBreakTargetId() const; |
| spirv::IdRef getContinueTargetId() const; |
| |
| // TODO: remove name hashing once translation through glslang is removed. That is necessary to |
| // avoid name collision between ANGLE's internal symbols and user-defined ones when compiling |
| // the generated GLSL, but is irrelevant when generating SPIR-V directly. Currently, the SPIR-V |
| // transformer relies on the "mapped" names, which should also be changed when this hashing is |
| // removed. |
| ImmutableString hashName(const TSymbol *symbol); |
| ImmutableString hashTypeName(const TType &type); |
| ImmutableString hashFieldName(const TField *field); |
| ImmutableString hashFunctionName(const TFunction *func); |
| |
| spirv::Blob getSpirv(); |
| |
| private: |
| SpirvTypeData declareType(const SpirvType &type, const TSymbol *block); |
| |
| uint32_t calculateBaseAlignmentAndSize(const SpirvType &type, uint32_t *sizeInStorageBlockOut); |
| uint32_t calculateSizeAndWriteOffsetDecorations(const SpirvType &type, |
| spirv::IdRef typeId, |
| uint32_t blockBaseAlignment); |
| void writeMemberDecorations(const SpirvType &type, spirv::IdRef typeId); |
| void writeInterpolationDecoration(TQualifier qualifier, spirv::IdRef id, uint32_t fieldIndex); |
| |
| // Helpers for type declaration. |
| void getImageTypeParameters(TBasicType type, |
| spirv::IdRef *sampledTypeOut, |
| spv::Dim *dimOut, |
| spirv::LiteralInteger *depthOut, |
| spirv::LiteralInteger *arrayedOut, |
| spirv::LiteralInteger *multisampledOut, |
| spirv::LiteralInteger *sampledOut); |
| spv::ImageFormat getImageFormat(TLayoutImageInternalFormat imageInternalFormat); |
| |
| spirv::IdRef getBasicConstantHelper(uint32_t value, |
| TBasicType type, |
| angle::HashMap<uint32_t, spirv::IdRef> *constants); |
| spirv::IdRef getNullVectorConstantHelper(TBasicType type, int size); |
| spirv::IdRef getVectorConstantHelper(spirv::IdRef valueId, TBasicType type, int size); |
| |
| uint32_t nextUnusedBinding(); |
| uint32_t nextUnusedInputLocation(uint32_t consumedCount); |
| uint32_t nextUnusedOutputLocation(uint32_t consumedCount); |
| |
| void writeExecutionModes(spirv::Blob *blob); |
| void writeExtensions(spirv::Blob *blob); |
| void writeSourceExtensions(spirv::Blob *blob); |
| |
| [[maybe_unused]] TCompiler *mCompiler; |
| const ShCompileOptions &mCompileOptions; |
| gl::ShaderType mShaderType; |
| |
| // Capabilities the shader is using. Accumulated as the instructions are generated. The Shader |
| // capability is unconditionally generated, so it's not tracked. |
| std::set<spv::Capability> mCapabilities; |
| // Execution modes the shader is using. Most execution modes are automatically derived from |
| // shader metadata, but some are only discovered while traversing the tree. Only the latter |
| // execution modes are stored here. |
| angle::BitSet<32> mExecutionModes; |
| // Extensions used by the shader. |
| angle::PackedEnumBitSet<SPIRVExtensions> mExtensions; |
| |
| // The list of interface variables and the id of main() populated as the instructions are |
| // generated. Used for the OpEntryPoint instruction. |
| spirv::IdRefList mEntryPointInterfaceList; |
| spirv::IdRef mEntryPointId; |
| |
| // Id of imported instructions, if used. |
| spirv::IdRef mExtInstImportIdStd; |
| |
| // Current ID bound, used to allocate new ids. |
| spirv::IdRef mNextAvailableId; |
| |
| // A map from the AST type to the corresponding SPIR-V ID and associated data. Note that TType |
| // includes a lot of information that pertains to the variable that has the type, not the type |
| // itself. SpirvType instead contains only information that can identify the type itself. |
| angle::HashMap<SpirvType, SpirvTypeData, SpirvTypeHash> mTypeMap; |
| |
| // Various sections of SPIR-V. Each section grows as SPIR-V is generated, and the final result |
| // is obtained by stitching the sections together. This puts the instructions in the order |
| // required by the spec. |
| spirv::Blob mSpirvDebug; |
| spirv::Blob mSpirvDecorations; |
| spirv::Blob mSpirvTypeAndConstantDecls; |
| spirv::Blob mSpirvTypePointerDecls; |
| spirv::Blob mSpirvFunctionTypeDecls; |
| spirv::Blob mSpirvVariableDecls; |
| spirv::Blob mSpirvFunctions; |
| // A list of blocks created for the current function. These are assembled by |
| // assembleSpirvFunctionBlocks() when the function is entirely visited. Local variables need to |
| // be inserted at the beginning of the first function block, so the entire SPIR-V of the |
| // function cannot be obtained until it's fully visited. |
| // |
| // The last block in this list is the one currently being written to. |
| std::vector<SpirvBlock> mSpirvCurrentFunctionBlocks; |
| |
| // List of constants that are already defined (for reuse). |
| spirv::IdRef mBoolConstants[2]; |
| angle::HashMap<uint32_t, spirv::IdRef> mUintConstants; |
| angle::HashMap<uint32_t, spirv::IdRef> mIntConstants; |
| angle::HashMap<uint32_t, spirv::IdRef> mFloatConstants; |
| angle::HashMap<SpirvIdAndIdList, spirv::IdRef, SpirvIdAndIdListHash> mCompositeConstants; |
| // Keyed by typeId, returns the null constant corresponding to that type. |
| std::vector<spirv::IdRef> mNullConstants; |
| |
| // List of type pointers that are already defined. |
| // TODO: if all users call getTypeData(), move to SpirvTypeData. http://anglebug.com/4889 |
| angle::HashMap<SpirvIdAndStorageClass, spirv::IdRef, SpirvIdAndStorageClassHash> |
| mTypePointerIdMap; |
| |
| // List of function types that are already defined. |
| angle::HashMap<SpirvIdAndIdList, spirv::IdRef, SpirvIdAndIdListHash> mFunctionTypeIdMap; |
| |
| // Stack of conditionals. When an if, loop or switch is visited, a new conditional scope is |
| // added. When the conditional construct is entirely visited, it's popped. As the blocks of |
| // the conditional constructs are visited, ids are consumed from the top of the stack. When |
| // break or continue is visited, the stack is traversed backwards until a loop or switch is |
| // found. |
| std::vector<SpirvConditional> mConditionalStack; |
| |
| // name hashing. |
| ShHashFunction64 mHashFunction; |
| NameMap &mNameMap; |
| |
| // Every resource that requires set & binding layout qualifiers is assigned set 0 and an |
| // arbitrary binding. Every input/output that requires a location layout qualifier is assigned |
| // an arbitrary location as well. |
| // |
| // The link-time SPIR-V transformer modifies set, binding and location decorations in SPIR-V |
| // directly. |
| uint32_t mNextUnusedBinding; |
| uint32_t mNextUnusedInputLocation; |
| uint32_t mNextUnusedOutputLocation; |
| }; |
| } // namespace sh |
| |
| #endif // COMPILER_TRANSLATOR_BUILDSPIRV_H_ |