| // |
| // 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/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 |
| 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; |
| |
| // 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; |
| |
| // 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; |
| TLayoutMatrixPacking matrixPacking = EmpColumnMajor; |
| |
| 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. `imageOnly` 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; |
| }; |
| |
| 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.blockStorage == sh::EbsUnspecified || type.block != nullptr || |
| !type.arraySizes.empty()); |
| |
| 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)) ^ |
| type.blockStorage; |
| } |
| |
| 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"); |
| static_assert(sh::EmpLast < 4, "Matrix packing doesn't fit in 2 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 | type.matrixPacking << 5), |
| static_cast<uint8_t>(type.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; |
| // The base alignment and size of the type based on the storage block it's used in (if |
| // applicable) |
| uint32_t baseAlignment; |
| uint32_t sizeInStorageBlock; |
| }; |
| |
| // Helper class to construct SPIR-V |
| class SPIRVBuilder : angle::NonCopyable |
| { |
| public: |
| SPIRVBuilder(gl::ShaderType shaderType, ShHashFunction64 hashFunction, NameMap &nameMap) |
| : mShaderType(shaderType), |
| mNextAvailableId(1), |
| mHashFunction(hashFunction), |
| mNameMap(nameMap), |
| mNextUnusedBinding(0), |
| mNextUnusedInputLocation(0), |
| mNextUnusedOutputLocation(0) |
| {} |
| |
| spirv::IdRef getNewId(); |
| const SpirvTypeData &getTypeData(const TType &type, TLayoutBlockStorage blockStorage); |
| const SpirvTypeData &getSpirvTypeData(const SpirvType &type, const char *blockName); |
| spirv::IdRef getTypePointerId(spirv::IdRef typeId, spv::StorageClass storageClass); |
| spirv::IdRef getFunctionTypeId(spirv::IdRef returnTypeId, const spirv::IdRefList ¶mTypeIds); |
| |
| spirv::Blob *getSpirvExecutionModes() { return &mSpirvExecutionModes; } |
| spirv::Blob *getSpirvDebug() { return &mSpirvDebug; } |
| spirv::Blob *getSpirvDecorations() { return &mSpirvDecorations; } |
| spirv::Blob *getSpirvVariableDecls() { return &mSpirvVariableDecls; } |
| spirv::Blob *getSpirvFunctions() { return &mSpirvFunctions; } |
| |
| void addCapability(spv::Capability capability); |
| void addExecutionMode(spv::ExecutionMode executionMode); |
| 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); |
| |
| uint32_t calculateBaseAlignmentAndSize(const SpirvType &type, uint32_t *sizeInStorageBlockOut); |
| uint32_t calculateSizeAndWriteOffsetDecorations(const SpirvType &type, spirv::IdRef typeId); |
| |
| // 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 char *blockName); |
| |
| // 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 getBoolConstant(bool value); |
| spirv::IdRef getBasicConstantHelper(uint32_t value, |
| TBasicType type, |
| angle::HashMap<uint32_t, spirv::IdRef> *constants); |
| spirv::IdRef getUintConstant(uint32_t value); |
| spirv::IdRef getIntConstant(int32_t value); |
| spirv::IdRef getFloatConstant(float value); |
| spirv::IdRef getCompositeConstant(spirv::IdRef typeId, const spirv::IdRefList &values); |
| |
| uint32_t nextUnusedBinding(); |
| uint32_t nextUnusedInputLocation(uint32_t consumedCount); |
| uint32_t nextUnusedOutputLocation(uint32_t consumedCount); |
| |
| 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 enabling. Accumulated as the instructions are generated. |
| // Execution mode instructions that require a parameter are written to mSpirvExecutionModes as |
| // instructions; they are always generated once so don't benefit from being in a std::set. |
| std::set<spv::ExecutionMode> mExecutionModes; |
| |
| // 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; |
| |
| // 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 stiching the sections together. This puts the instructions in the order |
| // required by the spec. |
| spirv::Blob mSpirvExecutionModes; |
| spirv::Blob mSpirvDebug; |
| spirv::Blob mSpirvDecorations; |
| spirv::Blob mSpirvTypeAndConstantDecls; |
| spirv::Blob mSpirvTypePointerDecls; |
| spirv::Blob mSpirvVariableDecls; |
| spirv::Blob mSpirvFunctions; |
| |
| // 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; |
| // TODO: Use null constants as optimization for when complex types are initialized with all |
| // zeros. http://anglebug.com/4889 |
| |
| // 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; |
| |
| // 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_ |