| // |
| // Copyright (C) 2014-2016 LunarG, Inc. |
| // Copyright (C) 2015-2020 Google, Inc. |
| // Copyright (C) 2017 ARM Limited. |
| // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. |
| // |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions |
| // are met: |
| // |
| // Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // |
| // Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following |
| // disclaimer in the documentation and/or other materials provided |
| // with the distribution. |
| // |
| // Neither the name of 3Dlabs Inc. Ltd. nor the names of its |
| // contributors may be used to endorse or promote products derived |
| // from this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| // POSSIBILITY OF SUCH DAMAGE. |
| |
| // |
| // Visit the nodes in the glslang intermediate tree representation to |
| // translate them to SPIR-V. |
| // |
| |
| #include "spirv.hpp" |
| #include "GlslangToSpv.h" |
| #include "SpvBuilder.h" |
| namespace spv { |
| #include "GLSL.std.450.h" |
| #include "GLSL.ext.KHR.h" |
| #include "GLSL.ext.EXT.h" |
| #include "GLSL.ext.AMD.h" |
| #include "GLSL.ext.NV.h" |
| #include "NonSemanticDebugPrintf.h" |
| } |
| |
| // Glslang includes |
| #include "../glslang/MachineIndependent/localintermediate.h" |
| #include "../glslang/MachineIndependent/SymbolTable.h" |
| #include "../glslang/Include/Common.h" |
| |
| // Build-time generated includes |
| #include "glslang/build_info.h" |
| |
| #include <fstream> |
| #include <iomanip> |
| #include <list> |
| #include <map> |
| #include <stack> |
| #include <string> |
| #include <vector> |
| |
| namespace { |
| |
| namespace { |
| class SpecConstantOpModeGuard { |
| public: |
| SpecConstantOpModeGuard(spv::Builder* builder) |
| : builder_(builder) { |
| previous_flag_ = builder->isInSpecConstCodeGenMode(); |
| } |
| ~SpecConstantOpModeGuard() { |
| previous_flag_ ? builder_->setToSpecConstCodeGenMode() |
| : builder_->setToNormalCodeGenMode(); |
| } |
| void turnOnSpecConstantOpMode() { |
| builder_->setToSpecConstCodeGenMode(); |
| } |
| |
| private: |
| spv::Builder* builder_; |
| bool previous_flag_; |
| }; |
| |
| struct OpDecorations { |
| public: |
| OpDecorations(spv::Decoration precision, spv::Decoration noContraction, spv::Decoration nonUniform) : |
| precision(precision) |
| #ifndef GLSLANG_WEB |
| , |
| noContraction(noContraction), |
| nonUniform(nonUniform) |
| #endif |
| { } |
| |
| spv::Decoration precision; |
| |
| #ifdef GLSLANG_WEB |
| void addNoContraction(spv::Builder&, spv::Id) const { } |
| void addNonUniform(spv::Builder&, spv::Id) const { } |
| #else |
| void addNoContraction(spv::Builder& builder, spv::Id t) { builder.addDecoration(t, noContraction); } |
| void addNonUniform(spv::Builder& builder, spv::Id t) { builder.addDecoration(t, nonUniform); } |
| protected: |
| spv::Decoration noContraction; |
| spv::Decoration nonUniform; |
| #endif |
| |
| }; |
| |
| } // namespace |
| |
| // |
| // The main holder of information for translating glslang to SPIR-V. |
| // |
| // Derives from the AST walking base class. |
| // |
| class TGlslangToSpvTraverser : public glslang::TIntermTraverser { |
| public: |
| TGlslangToSpvTraverser(unsigned int spvVersion, const glslang::TIntermediate*, spv::SpvBuildLogger* logger, |
| glslang::SpvOptions& options); |
| virtual ~TGlslangToSpvTraverser() { } |
| |
| bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*); |
| bool visitBinary(glslang::TVisit, glslang::TIntermBinary*); |
| void visitConstantUnion(glslang::TIntermConstantUnion*); |
| bool visitSelection(glslang::TVisit, glslang::TIntermSelection*); |
| bool visitSwitch(glslang::TVisit, glslang::TIntermSwitch*); |
| void visitSymbol(glslang::TIntermSymbol* symbol); |
| bool visitUnary(glslang::TVisit, glslang::TIntermUnary*); |
| bool visitLoop(glslang::TVisit, glslang::TIntermLoop*); |
| bool visitBranch(glslang::TVisit visit, glslang::TIntermBranch*); |
| |
| void finishSpv(); |
| void dumpSpv(std::vector<unsigned int>& out); |
| |
| protected: |
| TGlslangToSpvTraverser(TGlslangToSpvTraverser&); |
| TGlslangToSpvTraverser& operator=(TGlslangToSpvTraverser&); |
| |
| spv::Decoration TranslateInterpolationDecoration(const glslang::TQualifier& qualifier); |
| spv::Decoration TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier); |
| spv::Decoration TranslateNonUniformDecoration(const glslang::TQualifier& qualifier); |
| spv::Decoration TranslateNonUniformDecoration(const spv::Builder::AccessChain::CoherentFlags& coherentFlags); |
| spv::Builder::AccessChain::CoherentFlags TranslateCoherent(const glslang::TType& type); |
| spv::MemoryAccessMask TranslateMemoryAccess(const spv::Builder::AccessChain::CoherentFlags &coherentFlags); |
| spv::ImageOperandsMask TranslateImageOperands(const spv::Builder::AccessChain::CoherentFlags &coherentFlags); |
| spv::Scope TranslateMemoryScope(const spv::Builder::AccessChain::CoherentFlags &coherentFlags); |
| spv::BuiltIn TranslateBuiltInDecoration(glslang::TBuiltInVariable, bool memberDeclaration); |
| spv::ImageFormat TranslateImageFormat(const glslang::TType& type); |
| spv::SelectionControlMask TranslateSelectionControl(const glslang::TIntermSelection&) const; |
| spv::SelectionControlMask TranslateSwitchControl(const glslang::TIntermSwitch&) const; |
| spv::LoopControlMask TranslateLoopControl(const glslang::TIntermLoop&, std::vector<unsigned int>& operands) const; |
| spv::StorageClass TranslateStorageClass(const glslang::TType&); |
| void TranslateLiterals(const glslang::TVector<const glslang::TIntermConstantUnion*>&, std::vector<unsigned>&) const; |
| void addIndirectionIndexCapabilities(const glslang::TType& baseType, const glslang::TType& indexType); |
| spv::Id createSpvVariable(const glslang::TIntermSymbol*, spv::Id forcedType); |
| spv::Id getSampledType(const glslang::TSampler&); |
| spv::Id getInvertedSwizzleType(const glslang::TIntermTyped&); |
| spv::Id createInvertedSwizzle(spv::Decoration precision, const glslang::TIntermTyped&, spv::Id parentResult); |
| void convertSwizzle(const glslang::TIntermAggregate&, std::vector<unsigned>& swizzle); |
| spv::Id convertGlslangToSpvType(const glslang::TType& type, bool forwardReferenceOnly = false); |
| spv::Id convertGlslangToSpvType(const glslang::TType& type, glslang::TLayoutPacking, const glslang::TQualifier&, |
| bool lastBufferBlockMember, bool forwardReferenceOnly = false); |
| bool filterMember(const glslang::TType& member); |
| spv::Id convertGlslangStructToSpvType(const glslang::TType&, const glslang::TTypeList* glslangStruct, |
| glslang::TLayoutPacking, const glslang::TQualifier&); |
| void decorateStructType(const glslang::TType&, const glslang::TTypeList* glslangStruct, glslang::TLayoutPacking, |
| const glslang::TQualifier&, spv::Id); |
| spv::Id makeArraySizeId(const glslang::TArraySizes&, int dim); |
| spv::Id accessChainLoad(const glslang::TType& type); |
| void accessChainStore(const glslang::TType& type, spv::Id rvalue); |
| void multiTypeStore(const glslang::TType&, spv::Id rValue); |
| spv::Id convertLoadedBoolInUniformToUint(const glslang::TType& type, spv::Id nominalTypeId, spv::Id loadedId); |
| glslang::TLayoutPacking getExplicitLayout(const glslang::TType& type) const; |
| int getArrayStride(const glslang::TType& arrayType, glslang::TLayoutPacking, glslang::TLayoutMatrix); |
| int getMatrixStride(const glslang::TType& matrixType, glslang::TLayoutPacking, glslang::TLayoutMatrix); |
| void updateMemberOffset(const glslang::TType& structType, const glslang::TType& memberType, int& currentOffset, |
| int& nextOffset, glslang::TLayoutPacking, glslang::TLayoutMatrix); |
| void declareUseOfStructMember(const glslang::TTypeList& members, int glslangMember); |
| |
| bool isShaderEntryPoint(const glslang::TIntermAggregate* node); |
| bool writableParam(glslang::TStorageQualifier) const; |
| bool originalParam(glslang::TStorageQualifier, const glslang::TType&, bool implicitThisParam); |
| void makeFunctions(const glslang::TIntermSequence&); |
| void makeGlobalInitializers(const glslang::TIntermSequence&); |
| void collectRayTracingLinkerObjects(); |
| void visitFunctions(const glslang::TIntermSequence&); |
| void handleFunctionEntry(const glslang::TIntermAggregate* node); |
| void translateArguments(const glslang::TIntermAggregate& node, std::vector<spv::Id>& arguments, |
| spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags); |
| void translateArguments(glslang::TIntermUnary& node, std::vector<spv::Id>& arguments); |
| spv::Id createImageTextureFunctionCall(glslang::TIntermOperator* node); |
| spv::Id handleUserFunctionCall(const glslang::TIntermAggregate*); |
| |
| spv::Id createBinaryOperation(glslang::TOperator op, OpDecorations&, spv::Id typeId, spv::Id left, spv::Id right, |
| glslang::TBasicType typeProxy, bool reduceComparison = true); |
| spv::Id createBinaryMatrixOperation(spv::Op, OpDecorations&, spv::Id typeId, spv::Id left, spv::Id right); |
| spv::Id createUnaryOperation(glslang::TOperator op, OpDecorations&, spv::Id typeId, spv::Id operand, |
| glslang::TBasicType typeProxy, |
| const spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags); |
| spv::Id createUnaryMatrixOperation(spv::Op op, OpDecorations&, spv::Id typeId, spv::Id operand, |
| glslang::TBasicType typeProxy); |
| spv::Id createConversion(glslang::TOperator op, OpDecorations&, spv::Id destTypeId, spv::Id operand, |
| glslang::TBasicType typeProxy); |
| spv::Id createIntWidthConversion(glslang::TOperator op, spv::Id operand, int vectorSize); |
| spv::Id makeSmearedConstant(spv::Id constant, int vectorSize); |
| spv::Id createAtomicOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, |
| std::vector<spv::Id>& operands, glslang::TBasicType typeProxy, |
| const spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags); |
| spv::Id createInvocationsOperation(glslang::TOperator op, spv::Id typeId, std::vector<spv::Id>& operands, |
| glslang::TBasicType typeProxy); |
| spv::Id CreateInvocationsVectorOperation(spv::Op op, spv::GroupOperation groupOperation, |
| spv::Id typeId, std::vector<spv::Id>& operands); |
| spv::Id createSubgroupOperation(glslang::TOperator op, spv::Id typeId, std::vector<spv::Id>& operands, |
| glslang::TBasicType typeProxy); |
| spv::Id createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, |
| std::vector<spv::Id>& operands, glslang::TBasicType typeProxy); |
| spv::Id createNoArgOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId); |
| spv::Id getSymbolId(const glslang::TIntermSymbol* node); |
| void addMeshNVDecoration(spv::Id id, int member, const glslang::TQualifier & qualifier); |
| spv::Id createSpvConstant(const glslang::TIntermTyped&); |
| spv::Id createSpvConstantFromConstUnionArray(const glslang::TType& type, const glslang::TConstUnionArray&, |
| int& nextConst, bool specConstant); |
| bool isTrivialLeaf(const glslang::TIntermTyped* node); |
| bool isTrivial(const glslang::TIntermTyped* node); |
| spv::Id createShortCircuit(glslang::TOperator, glslang::TIntermTyped& left, glslang::TIntermTyped& right); |
| spv::Id getExtBuiltins(const char* name); |
| std::pair<spv::Id, spv::Id> getForcedType(glslang::TBuiltInVariable builtIn, const glslang::TType&); |
| spv::Id translateForcedType(spv::Id object); |
| spv::Id createCompositeConstruct(spv::Id typeId, std::vector<spv::Id> constituents); |
| |
| glslang::SpvOptions& options; |
| spv::Function* shaderEntry; |
| spv::Function* currentFunction; |
| spv::Instruction* entryPoint; |
| int sequenceDepth; |
| |
| spv::SpvBuildLogger* logger; |
| |
| // There is a 1:1 mapping between a spv builder and a module; this is thread safe |
| spv::Builder builder; |
| bool inEntryPoint; |
| bool entryPointTerminated; |
| bool linkageOnly; // true when visiting the set of objects in the AST present only for |
| // establishing interface, whether or not they were statically used |
| std::set<spv::Id> iOSet; // all input/output variables from either static use or declaration of interface |
| const glslang::TIntermediate* glslangIntermediate; |
| bool nanMinMaxClamp; // true if use NMin/NMax/NClamp instead of FMin/FMax/FClamp |
| spv::Id stdBuiltins; |
| spv::Id nonSemanticDebugPrintf; |
| std::unordered_map<std::string, spv::Id> extBuiltinMap; |
| |
| std::unordered_map<long long, spv::Id> symbolValues; |
| std::unordered_map<uint32_t, spv::Id> builtInVariableIds; |
| std::unordered_set<long long> rValueParameters; // set of formal function parameters passed as rValues, |
| // rather than a pointer |
| std::unordered_map<std::string, spv::Function*> functionMap; |
| std::unordered_map<const glslang::TTypeList*, spv::Id> structMap[glslang::ElpCount][glslang::ElmCount]; |
| // for mapping glslang block indices to spv indices (e.g., due to hidden members): |
| std::unordered_map<long long, std::vector<int>> memberRemapper; |
| // for mapping glslang symbol struct to symbol Id |
| std::unordered_map<const glslang::TTypeList*, long long> glslangTypeToIdMap; |
| std::stack<bool> breakForLoop; // false means break for switch |
| std::unordered_map<std::string, const glslang::TIntermSymbol*> counterOriginator; |
| // Map pointee types for EbtReference to their forward pointers |
| std::map<const glslang::TType *, spv::Id> forwardPointers; |
| // Type forcing, for when SPIR-V wants a different type than the AST, |
| // requiring local translation to and from SPIR-V type on every access. |
| // Maps <builtin-variable-id -> AST-required-type-id> |
| std::unordered_map<spv::Id, spv::Id> forceType; |
| |
| // Used later for generating OpTraceKHR/OpExecuteCallableKHR |
| std::unordered_map<unsigned int, glslang::TIntermSymbol *> locationToSymbol[2]; |
| |
| // Used by Task shader while generating opearnds for OpEmitMeshTasksEXT |
| spv::Id taskPayloadID; |
| }; |
| |
| // |
| // Helper functions for translating glslang representations to SPIR-V enumerants. |
| // |
| |
| // Translate glslang profile to SPIR-V source language. |
| spv::SourceLanguage TranslateSourceLanguage(glslang::EShSource source, EProfile profile) |
| { |
| #ifdef GLSLANG_WEB |
| return spv::SourceLanguageESSL; |
| #elif defined(GLSLANG_ANGLE) |
| return spv::SourceLanguageGLSL; |
| #endif |
| |
| switch (source) { |
| case glslang::EShSourceGlsl: |
| switch (profile) { |
| case ENoProfile: |
| case ECoreProfile: |
| case ECompatibilityProfile: |
| return spv::SourceLanguageGLSL; |
| case EEsProfile: |
| return spv::SourceLanguageESSL; |
| default: |
| return spv::SourceLanguageUnknown; |
| } |
| case glslang::EShSourceHlsl: |
| return spv::SourceLanguageHLSL; |
| default: |
| return spv::SourceLanguageUnknown; |
| } |
| } |
| |
| // Translate glslang language (stage) to SPIR-V execution model. |
| spv::ExecutionModel TranslateExecutionModel(EShLanguage stage, bool isMeshShaderEXT = false) |
| { |
| switch (stage) { |
| case EShLangVertex: return spv::ExecutionModelVertex; |
| case EShLangFragment: return spv::ExecutionModelFragment; |
| case EShLangCompute: return spv::ExecutionModelGLCompute; |
| #ifndef GLSLANG_WEB |
| case EShLangTessControl: return spv::ExecutionModelTessellationControl; |
| case EShLangTessEvaluation: return spv::ExecutionModelTessellationEvaluation; |
| case EShLangGeometry: return spv::ExecutionModelGeometry; |
| case EShLangRayGen: return spv::ExecutionModelRayGenerationKHR; |
| case EShLangIntersect: return spv::ExecutionModelIntersectionKHR; |
| case EShLangAnyHit: return spv::ExecutionModelAnyHitKHR; |
| case EShLangClosestHit: return spv::ExecutionModelClosestHitKHR; |
| case EShLangMiss: return spv::ExecutionModelMissKHR; |
| case EShLangCallable: return spv::ExecutionModelCallableKHR; |
| case EShLangTask: return (isMeshShaderEXT)? spv::ExecutionModelTaskEXT : spv::ExecutionModelTaskNV; |
| case EShLangMesh: return (isMeshShaderEXT)? spv::ExecutionModelMeshEXT: spv::ExecutionModelMeshNV; |
| #endif |
| default: |
| assert(0); |
| return spv::ExecutionModelFragment; |
| } |
| } |
| |
| // Translate glslang sampler type to SPIR-V dimensionality. |
| spv::Dim TranslateDimensionality(const glslang::TSampler& sampler) |
| { |
| switch (sampler.dim) { |
| case glslang::Esd1D: return spv::Dim1D; |
| case glslang::Esd2D: return spv::Dim2D; |
| case glslang::Esd3D: return spv::Dim3D; |
| case glslang::EsdCube: return spv::DimCube; |
| case glslang::EsdRect: return spv::DimRect; |
| case glslang::EsdBuffer: return spv::DimBuffer; |
| case glslang::EsdSubpass: return spv::DimSubpassData; |
| default: |
| assert(0); |
| return spv::Dim2D; |
| } |
| } |
| |
| // Translate glslang precision to SPIR-V precision decorations. |
| spv::Decoration TranslatePrecisionDecoration(glslang::TPrecisionQualifier glslangPrecision) |
| { |
| switch (glslangPrecision) { |
| case glslang::EpqLow: return spv::DecorationRelaxedPrecision; |
| case glslang::EpqMedium: return spv::DecorationRelaxedPrecision; |
| default: |
| return spv::NoPrecision; |
| } |
| } |
| |
| // Translate glslang type to SPIR-V precision decorations. |
| spv::Decoration TranslatePrecisionDecoration(const glslang::TType& type) |
| { |
| return TranslatePrecisionDecoration(type.getQualifier().precision); |
| } |
| |
| // Translate glslang type to SPIR-V block decorations. |
| spv::Decoration TranslateBlockDecoration(const glslang::TType& type, bool useStorageBuffer) |
| { |
| if (type.getBasicType() == glslang::EbtBlock) { |
| switch (type.getQualifier().storage) { |
| case glslang::EvqUniform: return spv::DecorationBlock; |
| case glslang::EvqBuffer: return useStorageBuffer ? spv::DecorationBlock : spv::DecorationBufferBlock; |
| case glslang::EvqVaryingIn: return spv::DecorationBlock; |
| case glslang::EvqVaryingOut: return spv::DecorationBlock; |
| case glslang::EvqShared: return spv::DecorationBlock; |
| #ifndef GLSLANG_WEB |
| case glslang::EvqPayload: return spv::DecorationBlock; |
| case glslang::EvqPayloadIn: return spv::DecorationBlock; |
| case glslang::EvqHitAttr: return spv::DecorationBlock; |
| case glslang::EvqCallableData: return spv::DecorationBlock; |
| case glslang::EvqCallableDataIn: return spv::DecorationBlock; |
| #endif |
| default: |
| assert(0); |
| break; |
| } |
| } |
| |
| return spv::DecorationMax; |
| } |
| |
| // Translate glslang type to SPIR-V memory decorations. |
| void TranslateMemoryDecoration(const glslang::TQualifier& qualifier, std::vector<spv::Decoration>& memory, |
| bool useVulkanMemoryModel) |
| { |
| if (!useVulkanMemoryModel) { |
| if (qualifier.isCoherent()) |
| memory.push_back(spv::DecorationCoherent); |
| if (qualifier.isVolatile()) { |
| memory.push_back(spv::DecorationVolatile); |
| memory.push_back(spv::DecorationCoherent); |
| } |
| } |
| if (qualifier.isRestrict()) |
| memory.push_back(spv::DecorationRestrict); |
| if (qualifier.isReadOnly()) |
| memory.push_back(spv::DecorationNonWritable); |
| if (qualifier.isWriteOnly()) |
| memory.push_back(spv::DecorationNonReadable); |
| } |
| |
| // Translate glslang type to SPIR-V layout decorations. |
| spv::Decoration TranslateLayoutDecoration(const glslang::TType& type, glslang::TLayoutMatrix matrixLayout) |
| { |
| if (type.isMatrix()) { |
| switch (matrixLayout) { |
| case glslang::ElmRowMajor: |
| return spv::DecorationRowMajor; |
| case glslang::ElmColumnMajor: |
| return spv::DecorationColMajor; |
| default: |
| // opaque layouts don't need a majorness |
| return spv::DecorationMax; |
| } |
| } else { |
| switch (type.getBasicType()) { |
| default: |
| return spv::DecorationMax; |
| break; |
| case glslang::EbtBlock: |
| switch (type.getQualifier().storage) { |
| case glslang::EvqShared: |
| case glslang::EvqUniform: |
| case glslang::EvqBuffer: |
| switch (type.getQualifier().layoutPacking) { |
| case glslang::ElpShared: return spv::DecorationGLSLShared; |
| case glslang::ElpPacked: return spv::DecorationGLSLPacked; |
| default: |
| return spv::DecorationMax; |
| } |
| case glslang::EvqVaryingIn: |
| case glslang::EvqVaryingOut: |
| if (type.getQualifier().isTaskMemory()) { |
| switch (type.getQualifier().layoutPacking) { |
| case glslang::ElpShared: return spv::DecorationGLSLShared; |
| case glslang::ElpPacked: return spv::DecorationGLSLPacked; |
| default: break; |
| } |
| } else { |
| assert(type.getQualifier().layoutPacking == glslang::ElpNone); |
| } |
| return spv::DecorationMax; |
| #ifndef GLSLANG_WEB |
| case glslang::EvqPayload: |
| case glslang::EvqPayloadIn: |
| case glslang::EvqHitAttr: |
| case glslang::EvqCallableData: |
| case glslang::EvqCallableDataIn: |
| return spv::DecorationMax; |
| #endif |
| default: |
| assert(0); |
| return spv::DecorationMax; |
| } |
| } |
| } |
| } |
| |
| // Translate glslang type to SPIR-V interpolation decorations. |
| // Returns spv::DecorationMax when no decoration |
| // should be applied. |
| spv::Decoration TGlslangToSpvTraverser::TranslateInterpolationDecoration(const glslang::TQualifier& qualifier) |
| { |
| if (qualifier.smooth) |
| // Smooth decoration doesn't exist in SPIR-V 1.0 |
| return spv::DecorationMax; |
| else if (qualifier.isNonPerspective()) |
| return spv::DecorationNoPerspective; |
| else if (qualifier.flat) |
| return spv::DecorationFlat; |
| else if (qualifier.isExplicitInterpolation()) { |
| builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); |
| return spv::DecorationExplicitInterpAMD; |
| } |
| else |
| return spv::DecorationMax; |
| } |
| |
| // Translate glslang type to SPIR-V auxiliary storage decorations. |
| // Returns spv::DecorationMax when no decoration |
| // should be applied. |
| spv::Decoration TGlslangToSpvTraverser::TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier) |
| { |
| if (qualifier.centroid) |
| return spv::DecorationCentroid; |
| #ifndef GLSLANG_WEB |
| else if (qualifier.patch) |
| return spv::DecorationPatch; |
| else if (qualifier.sample) { |
| builder.addCapability(spv::CapabilitySampleRateShading); |
| return spv::DecorationSample; |
| } |
| #endif |
| |
| return spv::DecorationMax; |
| } |
| |
| // If glslang type is invariant, return SPIR-V invariant decoration. |
| spv::Decoration TranslateInvariantDecoration(const glslang::TQualifier& qualifier) |
| { |
| if (qualifier.invariant) |
| return spv::DecorationInvariant; |
| else |
| return spv::DecorationMax; |
| } |
| |
| // If glslang type is noContraction, return SPIR-V NoContraction decoration. |
| spv::Decoration TranslateNoContractionDecoration(const glslang::TQualifier& qualifier) |
| { |
| #ifndef GLSLANG_WEB |
| if (qualifier.isNoContraction()) |
| return spv::DecorationNoContraction; |
| else |
| #endif |
| return spv::DecorationMax; |
| } |
| |
| // If glslang type is nonUniform, return SPIR-V NonUniform decoration. |
| spv::Decoration TGlslangToSpvTraverser::TranslateNonUniformDecoration(const glslang::TQualifier& qualifier) |
| { |
| #ifndef GLSLANG_WEB |
| if (qualifier.isNonUniform()) { |
| builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); |
| builder.addCapability(spv::CapabilityShaderNonUniformEXT); |
| return spv::DecorationNonUniformEXT; |
| } else |
| #endif |
| return spv::DecorationMax; |
| } |
| |
| // If lvalue flags contains nonUniform, return SPIR-V NonUniform decoration. |
| spv::Decoration TGlslangToSpvTraverser::TranslateNonUniformDecoration( |
| const spv::Builder::AccessChain::CoherentFlags& coherentFlags) |
| { |
| #ifndef GLSLANG_WEB |
| if (coherentFlags.isNonUniform()) { |
| builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); |
| builder.addCapability(spv::CapabilityShaderNonUniformEXT); |
| return spv::DecorationNonUniformEXT; |
| } else |
| #endif |
| return spv::DecorationMax; |
| } |
| |
| spv::MemoryAccessMask TGlslangToSpvTraverser::TranslateMemoryAccess( |
| const spv::Builder::AccessChain::CoherentFlags &coherentFlags) |
| { |
| spv::MemoryAccessMask mask = spv::MemoryAccessMaskNone; |
| |
| #ifndef GLSLANG_WEB |
| if (!glslangIntermediate->usingVulkanMemoryModel() || coherentFlags.isImage) |
| return mask; |
| |
| if (coherentFlags.isVolatile() || coherentFlags.anyCoherent()) { |
| mask = mask | spv::MemoryAccessMakePointerAvailableKHRMask | |
| spv::MemoryAccessMakePointerVisibleKHRMask; |
| } |
| |
| if (coherentFlags.nonprivate) { |
| mask = mask | spv::MemoryAccessNonPrivatePointerKHRMask; |
| } |
| if (coherentFlags.volatil) { |
| mask = mask | spv::MemoryAccessVolatileMask; |
| } |
| if (mask != spv::MemoryAccessMaskNone) { |
| builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); |
| } |
| #endif |
| |
| return mask; |
| } |
| |
| spv::ImageOperandsMask TGlslangToSpvTraverser::TranslateImageOperands( |
| const spv::Builder::AccessChain::CoherentFlags &coherentFlags) |
| { |
| spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone; |
| |
| #ifndef GLSLANG_WEB |
| if (!glslangIntermediate->usingVulkanMemoryModel()) |
| return mask; |
| |
| if (coherentFlags.volatil || |
| coherentFlags.anyCoherent()) { |
| mask = mask | spv::ImageOperandsMakeTexelAvailableKHRMask | |
| spv::ImageOperandsMakeTexelVisibleKHRMask; |
| } |
| if (coherentFlags.nonprivate) { |
| mask = mask | spv::ImageOperandsNonPrivateTexelKHRMask; |
| } |
| if (coherentFlags.volatil) { |
| mask = mask | spv::ImageOperandsVolatileTexelKHRMask; |
| } |
| if (mask != spv::ImageOperandsMaskNone) { |
| builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); |
| } |
| #endif |
| |
| return mask; |
| } |
| |
| spv::Builder::AccessChain::CoherentFlags TGlslangToSpvTraverser::TranslateCoherent(const glslang::TType& type) |
| { |
| spv::Builder::AccessChain::CoherentFlags flags = {}; |
| #ifndef GLSLANG_WEB |
| flags.coherent = type.getQualifier().coherent; |
| flags.devicecoherent = type.getQualifier().devicecoherent; |
| flags.queuefamilycoherent = type.getQualifier().queuefamilycoherent; |
| // shared variables are implicitly workgroupcoherent in GLSL. |
| flags.workgroupcoherent = type.getQualifier().workgroupcoherent || |
| type.getQualifier().storage == glslang::EvqShared; |
| flags.subgroupcoherent = type.getQualifier().subgroupcoherent; |
| flags.shadercallcoherent = type.getQualifier().shadercallcoherent; |
| flags.volatil = type.getQualifier().volatil; |
| // *coherent variables are implicitly nonprivate in GLSL |
| flags.nonprivate = type.getQualifier().nonprivate || |
| flags.anyCoherent() || |
| flags.volatil; |
| flags.isImage = type.getBasicType() == glslang::EbtSampler; |
| #endif |
| flags.nonUniform = type.getQualifier().nonUniform; |
| return flags; |
| } |
| |
| spv::Scope TGlslangToSpvTraverser::TranslateMemoryScope( |
| const spv::Builder::AccessChain::CoherentFlags &coherentFlags) |
| { |
| spv::Scope scope = spv::ScopeMax; |
| |
| #ifndef GLSLANG_WEB |
| if (coherentFlags.volatil || coherentFlags.coherent) { |
| // coherent defaults to Device scope in the old model, QueueFamilyKHR scope in the new model |
| scope = glslangIntermediate->usingVulkanMemoryModel() ? spv::ScopeQueueFamilyKHR : spv::ScopeDevice; |
| } else if (coherentFlags.devicecoherent) { |
| scope = spv::ScopeDevice; |
| } else if (coherentFlags.queuefamilycoherent) { |
| scope = spv::ScopeQueueFamilyKHR; |
| } else if (coherentFlags.workgroupcoherent) { |
| scope = spv::ScopeWorkgroup; |
| } else if (coherentFlags.subgroupcoherent) { |
| scope = spv::ScopeSubgroup; |
| } else if (coherentFlags.shadercallcoherent) { |
| scope = spv::ScopeShaderCallKHR; |
| } |
| if (glslangIntermediate->usingVulkanMemoryModel() && scope == spv::ScopeDevice) { |
| builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR); |
| } |
| #endif |
| |
| return scope; |
| } |
| |
| // Translate a glslang built-in variable to a SPIR-V built in decoration. Also generate |
| // associated capabilities when required. For some built-in variables, a capability |
| // is generated only when using the variable in an executable instruction, but not when |
| // just declaring a struct member variable with it. This is true for PointSize, |
| // ClipDistance, and CullDistance. |
| spv::BuiltIn TGlslangToSpvTraverser::TranslateBuiltInDecoration(glslang::TBuiltInVariable builtIn, |
| bool memberDeclaration) |
| { |
| switch (builtIn) { |
| case glslang::EbvPointSize: |
| #ifndef GLSLANG_WEB |
| // Defer adding the capability until the built-in is actually used. |
| if (! memberDeclaration) { |
| switch (glslangIntermediate->getStage()) { |
| case EShLangGeometry: |
| builder.addCapability(spv::CapabilityGeometryPointSize); |
| break; |
| case EShLangTessControl: |
| case EShLangTessEvaluation: |
| builder.addCapability(spv::CapabilityTessellationPointSize); |
| break; |
| default: |
| break; |
| } |
| } |
| #endif |
| return spv::BuiltInPointSize; |
| |
| case glslang::EbvPosition: return spv::BuiltInPosition; |
| case glslang::EbvVertexId: return spv::BuiltInVertexId; |
| case glslang::EbvInstanceId: return spv::BuiltInInstanceId; |
| case glslang::EbvVertexIndex: return spv::BuiltInVertexIndex; |
| case glslang::EbvInstanceIndex: return spv::BuiltInInstanceIndex; |
| |
| case glslang::EbvFragCoord: return spv::BuiltInFragCoord; |
| case glslang::EbvPointCoord: return spv::BuiltInPointCoord; |
| case glslang::EbvFace: return spv::BuiltInFrontFacing; |
| case glslang::EbvFragDepth: return spv::BuiltInFragDepth; |
| |
| case glslang::EbvNumWorkGroups: return spv::BuiltInNumWorkgroups; |
| case glslang::EbvWorkGroupSize: return spv::BuiltInWorkgroupSize; |
| case glslang::EbvWorkGroupId: return spv::BuiltInWorkgroupId; |
| case glslang::EbvLocalInvocationId: return spv::BuiltInLocalInvocationId; |
| case glslang::EbvLocalInvocationIndex: return spv::BuiltInLocalInvocationIndex; |
| case glslang::EbvGlobalInvocationId: return spv::BuiltInGlobalInvocationId; |
| |
| #ifndef GLSLANG_WEB |
| // These *Distance capabilities logically belong here, but if the member is declared and |
| // then never used, consumers of SPIR-V prefer the capability not be declared. |
| // They are now generated when used, rather than here when declared. |
| // Potentially, the specification should be more clear what the minimum |
| // use needed is to trigger the capability. |
| // |
| case glslang::EbvClipDistance: |
| if (!memberDeclaration) |
| builder.addCapability(spv::CapabilityClipDistance); |
| return spv::BuiltInClipDistance; |
| |
| case glslang::EbvCullDistance: |
| if (!memberDeclaration) |
| builder.addCapability(spv::CapabilityCullDistance); |
| return spv::BuiltInCullDistance; |
| |
| case glslang::EbvViewportIndex: |
| if (glslangIntermediate->getStage() == EShLangGeometry || |
| glslangIntermediate->getStage() == EShLangFragment) { |
| builder.addCapability(spv::CapabilityMultiViewport); |
| } |
| if (glslangIntermediate->getStage() == EShLangVertex || |
| glslangIntermediate->getStage() == EShLangTessControl || |
| glslangIntermediate->getStage() == EShLangTessEvaluation) { |
| |
| if (builder.getSpvVersion() < spv::Spv_1_5) { |
| builder.addIncorporatedExtension(spv::E_SPV_EXT_shader_viewport_index_layer, spv::Spv_1_5); |
| builder.addCapability(spv::CapabilityShaderViewportIndexLayerEXT); |
| } |
| else |
| builder.addCapability(spv::CapabilityShaderViewportIndex); |
| } |
| return spv::BuiltInViewportIndex; |
| |
| case glslang::EbvSampleId: |
| builder.addCapability(spv::CapabilitySampleRateShading); |
| return spv::BuiltInSampleId; |
| |
| case glslang::EbvSamplePosition: |
| builder.addCapability(spv::CapabilitySampleRateShading); |
| return spv::BuiltInSamplePosition; |
| |
| case glslang::EbvSampleMask: |
| return spv::BuiltInSampleMask; |
| |
| case glslang::EbvLayer: |
| if (glslangIntermediate->getStage() == EShLangMesh) { |
| return spv::BuiltInLayer; |
| } |
| if (glslangIntermediate->getStage() == EShLangGeometry || |
| glslangIntermediate->getStage() == EShLangFragment) { |
| builder.addCapability(spv::CapabilityGeometry); |
| } |
| if (glslangIntermediate->getStage() == EShLangVertex || |
| glslangIntermediate->getStage() == EShLangTessControl || |
| glslangIntermediate->getStage() == EShLangTessEvaluation) { |
| |
| if (builder.getSpvVersion() < spv::Spv_1_5) { |
| builder.addIncorporatedExtension(spv::E_SPV_EXT_shader_viewport_index_layer, spv::Spv_1_5); |
| builder.addCapability(spv::CapabilityShaderViewportIndexLayerEXT); |
| } else |
| builder.addCapability(spv::CapabilityShaderLayer); |
| } |
| return spv::BuiltInLayer; |
| |
| case glslang::EbvBaseVertex: |
| builder.addIncorporatedExtension(spv::E_SPV_KHR_shader_draw_parameters, spv::Spv_1_3); |
| builder.addCapability(spv::CapabilityDrawParameters); |
| return spv::BuiltInBaseVertex; |
| |
| case glslang::EbvBaseInstance: |
| builder.addIncorporatedExtension(spv::E_SPV_KHR_shader_draw_parameters, spv::Spv_1_3); |
| builder.addCapability(spv::CapabilityDrawParameters); |
| return spv::BuiltInBaseInstance; |
| |
| case glslang::EbvDrawId: |
| builder.addIncorporatedExtension(spv::E_SPV_KHR_shader_draw_parameters, spv::Spv_1_3); |
| builder.addCapability(spv::CapabilityDrawParameters); |
| return spv::BuiltInDrawIndex; |
| |
| case glslang::EbvPrimitiveId: |
| if (glslangIntermediate->getStage() == EShLangFragment) |
| builder.addCapability(spv::CapabilityGeometry); |
| return spv::BuiltInPrimitiveId; |
| |
| case glslang::EbvFragStencilRef: |
| builder.addExtension(spv::E_SPV_EXT_shader_stencil_export); |
| builder.addCapability(spv::CapabilityStencilExportEXT); |
| return spv::BuiltInFragStencilRefEXT; |
| |
| case glslang::EbvShadingRateKHR: |
| builder.addExtension(spv::E_SPV_KHR_fragment_shading_rate); |
| builder.addCapability(spv::CapabilityFragmentShadingRateKHR); |
| return spv::BuiltInShadingRateKHR; |
| |
| case glslang::EbvPrimitiveShadingRateKHR: |
| builder.addExtension(spv::E_SPV_KHR_fragment_shading_rate); |
| builder.addCapability(spv::CapabilityFragmentShadingRateKHR); |
| return spv::BuiltInPrimitiveShadingRateKHR; |
| |
| case glslang::EbvInvocationId: return spv::BuiltInInvocationId; |
| case glslang::EbvTessLevelInner: return spv::BuiltInTessLevelInner; |
| case glslang::EbvTessLevelOuter: return spv::BuiltInTessLevelOuter; |
| case glslang::EbvTessCoord: return spv::BuiltInTessCoord; |
| case glslang::EbvPatchVertices: return spv::BuiltInPatchVertices; |
| case glslang::EbvHelperInvocation: return spv::BuiltInHelperInvocation; |
| |
| case glslang::EbvSubGroupSize: |
| builder.addExtension(spv::E_SPV_KHR_shader_ballot); |
| builder.addCapability(spv::CapabilitySubgroupBallotKHR); |
| return spv::BuiltInSubgroupSize; |
| |
| case glslang::EbvSubGroupInvocation: |
| builder.addExtension(spv::E_SPV_KHR_shader_ballot); |
| builder.addCapability(spv::CapabilitySubgroupBallotKHR); |
| return spv::BuiltInSubgroupLocalInvocationId; |
| |
| case glslang::EbvSubGroupEqMask: |
| builder.addExtension(spv::E_SPV_KHR_shader_ballot); |
| builder.addCapability(spv::CapabilitySubgroupBallotKHR); |
| return spv::BuiltInSubgroupEqMask; |
| |
| case glslang::EbvSubGroupGeMask: |
| builder.addExtension(spv::E_SPV_KHR_shader_ballot); |
| builder.addCapability(spv::CapabilitySubgroupBallotKHR); |
| return spv::BuiltInSubgroupGeMask; |
| |
| case glslang::EbvSubGroupGtMask: |
| builder.addExtension(spv::E_SPV_KHR_shader_ballot); |
| builder.addCapability(spv::CapabilitySubgroupBallotKHR); |
| return spv::BuiltInSubgroupGtMask; |
| |
| case glslang::EbvSubGroupLeMask: |
| builder.addExtension(spv::E_SPV_KHR_shader_ballot); |
| builder.addCapability(spv::CapabilitySubgroupBallotKHR); |
| return spv::BuiltInSubgroupLeMask; |
| |
| case glslang::EbvSubGroupLtMask: |
| builder.addExtension(spv::E_SPV_KHR_shader_ballot); |
| builder.addCapability(spv::CapabilitySubgroupBallotKHR); |
| return spv::BuiltInSubgroupLtMask; |
| |
| case glslang::EbvNumSubgroups: |
| builder.addCapability(spv::CapabilityGroupNonUniform); |
| return spv::BuiltInNumSubgroups; |
| |
| case glslang::EbvSubgroupID: |
| builder.addCapability(spv::CapabilityGroupNonUniform); |
| return spv::BuiltInSubgroupId; |
| |
| case glslang::EbvSubgroupSize2: |
| builder.addCapability(spv::CapabilityGroupNonUniform); |
| return spv::BuiltInSubgroupSize; |
| |
| case glslang::EbvSubgroupInvocation2: |
| builder.addCapability(spv::CapabilityGroupNonUniform); |
| return spv::BuiltInSubgroupLocalInvocationId; |
| |
| case glslang::EbvSubgroupEqMask2: |
| builder.addCapability(spv::CapabilityGroupNonUniform); |
| builder.addCapability(spv::CapabilityGroupNonUniformBallot); |
| return spv::BuiltInSubgroupEqMask; |
| |
| case glslang::EbvSubgroupGeMask2: |
| builder.addCapability(spv::CapabilityGroupNonUniform); |
| builder.addCapability(spv::CapabilityGroupNonUniformBallot); |
| return spv::BuiltInSubgroupGeMask; |
| |
| case glslang::EbvSubgroupGtMask2: |
| builder.addCapability(spv::CapabilityGroupNonUniform); |
| builder.addCapability(spv::CapabilityGroupNonUniformBallot); |
| return spv::BuiltInSubgroupGtMask; |
| |
| case glslang::EbvSubgroupLeMask2: |
| builder.addCapability(spv::CapabilityGroupNonUniform); |
| builder.addCapability(spv::CapabilityGroupNonUniformBallot); |
| return spv::BuiltInSubgroupLeMask; |
| |
| case glslang::EbvSubgroupLtMask2: |
| builder.addCapability(spv::CapabilityGroupNonUniform); |
| builder.addCapability(spv::CapabilityGroupNonUniformBallot); |
| return spv::BuiltInSubgroupLtMask; |
| |
| case glslang::EbvBaryCoordNoPersp: |
| builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); |
| return spv::BuiltInBaryCoordNoPerspAMD; |
| |
| case glslang::EbvBaryCoordNoPerspCentroid: |
| builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); |
| return spv::BuiltInBaryCoordNoPerspCentroidAMD; |
| |
| case glslang::EbvBaryCoordNoPerspSample: |
| builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); |
| return spv::BuiltInBaryCoordNoPerspSampleAMD; |
| |
| case glslang::EbvBaryCoordSmooth: |
| builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); |
| return spv::BuiltInBaryCoordSmoothAMD; |
| |
| case glslang::EbvBaryCoordSmoothCentroid: |
| builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); |
| return spv::BuiltInBaryCoordSmoothCentroidAMD; |
| |
| case glslang::EbvBaryCoordSmoothSample: |
| builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); |
| return spv::BuiltInBaryCoordSmoothSampleAMD; |
| |
| case glslang::EbvBaryCoordPullModel: |
| builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); |
| return spv::BuiltInBaryCoordPullModelAMD; |
| |
| case glslang::EbvDeviceIndex: |
| builder.addIncorporatedExtension(spv::E_SPV_KHR_device_group, spv::Spv_1_3); |
| builder.addCapability(spv::CapabilityDeviceGroup); |
| return spv::BuiltInDeviceIndex; |
| |
| case glslang::EbvViewIndex: |
| builder.addIncorporatedExtension(spv::E_SPV_KHR_multiview, spv::Spv_1_3); |
| builder.addCapability(spv::CapabilityMultiView); |
| return spv::BuiltInViewIndex; |
| |
| case glslang::EbvFragSizeEXT: |
| builder.addExtension(spv::E_SPV_EXT_fragment_invocation_density); |
| builder.addCapability(spv::CapabilityFragmentDensityEXT); |
| return spv::BuiltInFragSizeEXT; |
| |
| case glslang::EbvFragInvocationCountEXT: |
| builder.addExtension(spv::E_SPV_EXT_fragment_invocation_density); |
| builder.addCapability(spv::CapabilityFragmentDensityEXT); |
| return spv::BuiltInFragInvocationCountEXT; |
| |
| case glslang::EbvViewportMaskNV: |
| if (!memberDeclaration) { |
| builder.addExtension(spv::E_SPV_NV_viewport_array2); |
| builder.addCapability(spv::CapabilityShaderViewportMaskNV); |
| } |
| return spv::BuiltInViewportMaskNV; |
| case glslang::EbvSecondaryPositionNV: |
| if (!memberDeclaration) { |
| builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); |
| builder.addCapability(spv::CapabilityShaderStereoViewNV); |
| } |
| return spv::BuiltInSecondaryPositionNV; |
| case glslang::EbvSecondaryViewportMaskNV: |
| if (!memberDeclaration) { |
| builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); |
| builder.addCapability(spv::CapabilityShaderStereoViewNV); |
| } |
| return spv::BuiltInSecondaryViewportMaskNV; |
| case glslang::EbvPositionPerViewNV: |
| if (!memberDeclaration) { |
| builder.addExtension(spv::E_SPV_NVX_multiview_per_view_attributes); |
| builder.addCapability(spv::CapabilityPerViewAttributesNV); |
| } |
| return spv::BuiltInPositionPerViewNV; |
| case glslang::EbvViewportMaskPerViewNV: |
| if (!memberDeclaration) { |
| builder.addExtension(spv::E_SPV_NVX_multiview_per_view_attributes); |
| builder.addCapability(spv::CapabilityPerViewAttributesNV); |
| } |
| return spv::BuiltInViewportMaskPerViewNV; |
| case glslang::EbvFragFullyCoveredNV: |
| builder.addExtension(spv::E_SPV_EXT_fragment_fully_covered); |
| builder.addCapability(spv::CapabilityFragmentFullyCoveredEXT); |
| return spv::BuiltInFullyCoveredEXT; |
| case glslang::EbvFragmentSizeNV: |
| builder.addExtension(spv::E_SPV_NV_shading_rate); |
| builder.addCapability(spv::CapabilityShadingRateNV); |
| return spv::BuiltInFragmentSizeNV; |
| case glslang::EbvInvocationsPerPixelNV: |
| builder.addExtension(spv::E_SPV_NV_shading_rate); |
| builder.addCapability(spv::CapabilityShadingRateNV); |
| return spv::BuiltInInvocationsPerPixelNV; |
| |
| // ray tracing |
| case glslang::EbvLaunchId: |
| return spv::BuiltInLaunchIdKHR; |
| case glslang::EbvLaunchSize: |
| return spv::BuiltInLaunchSizeKHR; |
| case glslang::EbvWorldRayOrigin: |
| return spv::BuiltInWorldRayOriginKHR; |
| case glslang::EbvWorldRayDirection: |
| return spv::BuiltInWorldRayDirectionKHR; |
| case glslang::EbvObjectRayOrigin: |
| return spv::BuiltInObjectRayOriginKHR; |
| case glslang::EbvObjectRayDirection: |
| return spv::BuiltInObjectRayDirectionKHR; |
| case glslang::EbvRayTmin: |
| return spv::BuiltInRayTminKHR; |
| case glslang::EbvRayTmax: |
| return spv::BuiltInRayTmaxKHR; |
| case glslang::EbvCullMask: |
| return spv::BuiltInCullMaskKHR; |
| case glslang::EbvInstanceCustomIndex: |
| return spv::BuiltInInstanceCustomIndexKHR; |
| case glslang::EbvHitT: |
| { |
| // this is a GLSL alias of RayTmax |
| // in SPV_NV_ray_tracing it has a dedicated builtin |
| // but in SPV_KHR_ray_tracing it gets mapped to RayTmax |
| auto& extensions = glslangIntermediate->getRequestedExtensions(); |
| if (extensions.find("GL_NV_ray_tracing") != extensions.end()) { |
| return spv::BuiltInHitTNV; |
| } else { |
| return spv::BuiltInRayTmaxKHR; |
| } |
| } |
| case glslang::EbvHitKind: |
| return spv::BuiltInHitKindKHR; |
| case glslang::EbvObjectToWorld: |
| case glslang::EbvObjectToWorld3x4: |
| return spv::BuiltInObjectToWorldKHR; |
| case glslang::EbvWorldToObject: |
| case glslang::EbvWorldToObject3x4: |
| return spv::BuiltInWorldToObjectKHR; |
| case glslang::EbvIncomingRayFlags: |
| return spv::BuiltInIncomingRayFlagsKHR; |
| case glslang::EbvGeometryIndex: |
| return spv::BuiltInRayGeometryIndexKHR; |
| case glslang::EbvCurrentRayTimeNV: |
| builder.addExtension(spv::E_SPV_NV_ray_tracing_motion_blur); |
| builder.addCapability(spv::CapabilityRayTracingMotionBlurNV); |
| return spv::BuiltInCurrentRayTimeNV; |
| |
| // barycentrics |
| case glslang::EbvBaryCoordNV: |
| builder.addExtension(spv::E_SPV_NV_fragment_shader_barycentric); |
| builder.addCapability(spv::CapabilityFragmentBarycentricNV); |
| return spv::BuiltInBaryCoordNV; |
| case glslang::EbvBaryCoordNoPerspNV: |
| builder.addExtension(spv::E_SPV_NV_fragment_shader_barycentric); |
| builder.addCapability(spv::CapabilityFragmentBarycentricNV); |
| return spv::BuiltInBaryCoordNoPerspNV; |
| |
| case glslang::EbvBaryCoordEXT: |
| builder.addExtension(spv::E_SPV_KHR_fragment_shader_barycentric); |
| builder.addCapability(spv::CapabilityFragmentBarycentricKHR); |
| return spv::BuiltInBaryCoordKHR; |
| case glslang::EbvBaryCoordNoPerspEXT: |
| builder.addExtension(spv::E_SPV_KHR_fragment_shader_barycentric); |
| builder.addCapability(spv::CapabilityFragmentBarycentricKHR); |
| return spv::BuiltInBaryCoordNoPerspKHR; |
| |
| // mesh shaders |
| case glslang::EbvTaskCountNV: |
| return spv::BuiltInTaskCountNV; |
| case glslang::EbvPrimitiveCountNV: |
| return spv::BuiltInPrimitiveCountNV; |
| case glslang::EbvPrimitiveIndicesNV: |
| return spv::BuiltInPrimitiveIndicesNV; |
| case glslang::EbvClipDistancePerViewNV: |
| return spv::BuiltInClipDistancePerViewNV; |
| case glslang::EbvCullDistancePerViewNV: |
| return spv::BuiltInCullDistancePerViewNV; |
| case glslang::EbvLayerPerViewNV: |
| return spv::BuiltInLayerPerViewNV; |
| case glslang::EbvMeshViewCountNV: |
| return spv::BuiltInMeshViewCountNV; |
| case glslang::EbvMeshViewIndicesNV: |
| return spv::BuiltInMeshViewIndicesNV; |
| |
| // SPV_EXT_mesh_shader |
| case glslang::EbvPrimitivePointIndicesEXT: |
| return spv::BuiltInPrimitivePointIndicesEXT; |
| case glslang::EbvPrimitiveLineIndicesEXT: |
| return spv::BuiltInPrimitiveLineIndicesEXT; |
| case glslang::EbvPrimitiveTriangleIndicesEXT: |
| return spv::BuiltInPrimitiveTriangleIndicesEXT; |
| case glslang::EbvCullPrimitiveEXT: |
| return spv::BuiltInCullPrimitiveEXT; |
| |
| // sm builtins |
| case glslang::EbvWarpsPerSM: |
| builder.addExtension(spv::E_SPV_NV_shader_sm_builtins); |
| builder.addCapability(spv::CapabilityShaderSMBuiltinsNV); |
| return spv::BuiltInWarpsPerSMNV; |
| case glslang::EbvSMCount: |
| builder.addExtension(spv::E_SPV_NV_shader_sm_builtins); |
| builder.addCapability(spv::CapabilityShaderSMBuiltinsNV); |
| return spv::BuiltInSMCountNV; |
| case glslang::EbvWarpID: |
| builder.addExtension(spv::E_SPV_NV_shader_sm_builtins); |
| builder.addCapability(spv::CapabilityShaderSMBuiltinsNV); |
| return spv::BuiltInWarpIDNV; |
| case glslang::EbvSMID: |
| builder.addExtension(spv::E_SPV_NV_shader_sm_builtins); |
| builder.addCapability(spv::CapabilityShaderSMBuiltinsNV); |
| return spv::BuiltInSMIDNV; |
| #endif |
| |
| default: |
| return spv::BuiltInMax; |
| } |
| } |
| |
| // Translate glslang image layout format to SPIR-V image format. |
| spv::ImageFormat TGlslangToSpvTraverser::TranslateImageFormat(const glslang::TType& type) |
| { |
| assert(type.getBasicType() == glslang::EbtSampler); |
| |
| #ifdef GLSLANG_WEB |
| return spv::ImageFormatUnknown; |
| #endif |
| |
| // Check for capabilities |
| switch (type.getQualifier().getFormat()) { |
| case glslang::ElfRg32f: |
| case glslang::ElfRg16f: |
| case glslang::ElfR11fG11fB10f: |
| case glslang::ElfR16f: |
| case glslang::ElfRgba16: |
| case glslang::ElfRgb10A2: |
| case glslang::ElfRg16: |
| case glslang::ElfRg8: |
| case glslang::ElfR16: |
| case glslang::ElfR8: |
| case glslang::ElfRgba16Snorm: |
| case glslang::ElfRg16Snorm: |
| case glslang::ElfRg8Snorm: |
| case glslang::ElfR16Snorm: |
| case glslang::ElfR8Snorm: |
| |
| case glslang::ElfRg32i: |
| case glslang::ElfRg16i: |
| case glslang::ElfRg8i: |
| case glslang::ElfR16i: |
| case glslang::ElfR8i: |
| |
| case glslang::ElfRgb10a2ui: |
| case glslang::ElfRg32ui: |
| case glslang::ElfRg16ui: |
| case glslang::ElfRg8ui: |
| case glslang::ElfR16ui: |
| case glslang::ElfR8ui: |
| builder.addCapability(spv::CapabilityStorageImageExtendedFormats); |
| break; |
| |
| case glslang::ElfR64ui: |
| case glslang::ElfR64i: |
| builder.addExtension(spv::E_SPV_EXT_shader_image_int64); |
| builder.addCapability(spv::CapabilityInt64ImageEXT); |
| default: |
| break; |
| } |
| |
| // do the translation |
| switch (type.getQualifier().getFormat()) { |
| case glslang::ElfNone: return spv::ImageFormatUnknown; |
| case glslang::ElfRgba32f: return spv::ImageFormatRgba32f; |
| case glslang::ElfRgba16f: return spv::ImageFormatRgba16f; |
| case glslang::ElfR32f: return spv::ImageFormatR32f; |
| case glslang::ElfRgba8: return spv::ImageFormatRgba8; |
| case glslang::ElfRgba8Snorm: return spv::ImageFormatRgba8Snorm; |
| case glslang::ElfRg32f: return spv::ImageFormatRg32f; |
| case glslang::ElfRg16f: return spv::ImageFormatRg16f; |
| case glslang::ElfR11fG11fB10f: return spv::ImageFormatR11fG11fB10f; |
| case glslang::ElfR16f: return spv::ImageFormatR16f; |
| case glslang::ElfRgba16: return spv::ImageFormatRgba16; |
| case glslang::ElfRgb10A2: return spv::ImageFormatRgb10A2; |
| case glslang::ElfRg16: return spv::ImageFormatRg16; |
| case glslang::ElfRg8: return spv::ImageFormatRg8; |
| case glslang::ElfR16: return spv::ImageFormatR16; |
| case glslang::ElfR8: return spv::ImageFormatR8; |
| case glslang::ElfRgba16Snorm: return spv::ImageFormatRgba16Snorm; |
| case glslang::ElfRg16Snorm: return spv::ImageFormatRg16Snorm; |
| case glslang::ElfRg8Snorm: return spv::ImageFormatRg8Snorm; |
| case glslang::ElfR16Snorm: return spv::ImageFormatR16Snorm; |
| case glslang::ElfR8Snorm: return spv::ImageFormatR8Snorm; |
| case glslang::ElfRgba32i: return spv::ImageFormatRgba32i; |
| case glslang::ElfRgba16i: return spv::ImageFormatRgba16i; |
| case glslang::ElfRgba8i: return spv::ImageFormatRgba8i; |
| case glslang::ElfR32i: return spv::ImageFormatR32i; |
| case glslang::ElfRg32i: return spv::ImageFormatRg32i; |
| case glslang::ElfRg16i: return spv::ImageFormatRg16i; |
| case glslang::ElfRg8i: return spv::ImageFormatRg8i; |
| case glslang::ElfR16i: return spv::ImageFormatR16i; |
| case glslang::ElfR8i: return spv::ImageFormatR8i; |
| case glslang::ElfRgba32ui: return spv::ImageFormatRgba32ui; |
| case glslang::ElfRgba16ui: return spv::ImageFormatRgba16ui; |
| case glslang::ElfRgba8ui: return spv::ImageFormatRgba8ui; |
| case glslang::ElfR32ui: return spv::ImageFormatR32ui; |
| case glslang::ElfRg32ui: return spv::ImageFormatRg32ui; |
| case glslang::ElfRg16ui: return spv::ImageFormatRg16ui; |
| case glslang::ElfRgb10a2ui: return spv::ImageFormatRgb10a2ui; |
| case glslang::ElfRg8ui: return spv::ImageFormatRg8ui; |
| case glslang::ElfR16ui: return spv::ImageFormatR16ui; |
| case glslang::ElfR8ui: return spv::ImageFormatR8ui; |
| case glslang::ElfR64ui: return spv::ImageFormatR64ui; |
| case glslang::ElfR64i: return spv::ImageFormatR64i; |
| default: return spv::ImageFormatMax; |
| } |
| } |
| |
| spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSelectionControl( |
| const glslang::TIntermSelection& selectionNode) const |
| { |
| if (selectionNode.getFlatten()) |
| return spv::SelectionControlFlattenMask; |
| if (selectionNode.getDontFlatten()) |
| return spv::SelectionControlDontFlattenMask; |
| return spv::SelectionControlMaskNone; |
| } |
| |
| spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSwitchControl(const glslang::TIntermSwitch& switchNode) |
| const |
| { |
| if (switchNode.getFlatten()) |
| return spv::SelectionControlFlattenMask; |
| if (switchNode.getDontFlatten()) |
| return spv::SelectionControlDontFlattenMask; |
| return spv::SelectionControlMaskNone; |
| } |
| |
| // return a non-0 dependency if the dependency argument must be set |
| spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(const glslang::TIntermLoop& loopNode, |
| std::vector<unsigned int>& operands) const |
| { |
| spv::LoopControlMask control = spv::LoopControlMaskNone; |
| |
| if (loopNode.getDontUnroll()) |
| control = control | spv::LoopControlDontUnrollMask; |
| if (loopNode.getUnroll()) |
| control = control | spv::LoopControlUnrollMask; |
| if (unsigned(loopNode.getLoopDependency()) == glslang::TIntermLoop::dependencyInfinite) |
| control = control | spv::LoopControlDependencyInfiniteMask; |
| else if (loopNode.getLoopDependency() > 0) { |
| control = control | spv::LoopControlDependencyLengthMask; |
| operands.push_back((unsigned int)loopNode.getLoopDependency()); |
| } |
| if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) { |
| if (loopNode.getMinIterations() > 0) { |
| control = control | spv::LoopControlMinIterationsMask; |
| operands.push_back(loopNode.getMinIterations()); |
| } |
| if (loopNode.getMaxIterations() < glslang::TIntermLoop::iterationsInfinite) { |
| control = control | spv::LoopControlMaxIterationsMask; |
| operands.push_back(loopNode.getMaxIterations()); |
| } |
| if (loopNode.getIterationMultiple() > 1) { |
| control = control | spv::LoopControlIterationMultipleMask; |
| operands.push_back(loopNode.getIterationMultiple()); |
| } |
| if (loopNode.getPeelCount() > 0) { |
| control = control | spv::LoopControlPeelCountMask; |
| operands.push_back(loopNode.getPeelCount()); |
| } |
| if (loopNode.getPartialCount() > 0) { |
| control = control | spv::LoopControlPartialCountMask; |
| operands.push_back(loopNode.getPartialCount()); |
| } |
| } |
| |
| return control; |
| } |
| |
| // Translate glslang type to SPIR-V storage class. |
| spv::StorageClass TGlslangToSpvTraverser::TranslateStorageClass(const glslang::TType& type) |
| { |
| if (type.getBasicType() == glslang::EbtRayQuery) |
| return spv::StorageClassPrivate; |
| #ifndef GLSLANG_WEB |
| if (type.getQualifier().isSpirvByReference()) { |
| if (type.getQualifier().isParamInput() || type.getQualifier().isParamOutput()) |
| return spv::StorageClassFunction; |
| } |
| #endif |
| if (type.getQualifier().isPipeInput()) |
| return spv::StorageClassInput; |
| if (type.getQualifier().isPipeOutput()) |
| return spv::StorageClassOutput; |
| |
| if (glslangIntermediate->getSource() != glslang::EShSourceHlsl || |
| type.getQualifier().storage == glslang::EvqUniform) { |
| if (type.isAtomic()) |
| return spv::StorageClassAtomicCounter; |
| if (type.containsOpaque()) |
| return spv::StorageClassUniformConstant; |
| } |
| |
| if (type.getQualifier().isUniformOrBuffer() && |
| type.getQualifier().isShaderRecord()) { |
| return spv::StorageClassShaderRecordBufferKHR; |
| } |
| |
| if (glslangIntermediate->usingStorageBuffer() && type.getQualifier().storage == glslang::EvqBuffer) { |
| builder.addIncorporatedExtension(spv::E_SPV_KHR_storage_buffer_storage_class, spv::Spv_1_3); |
| return spv::StorageClassStorageBuffer; |
| } |
| |
| if (type.getQualifier().isUniformOrBuffer()) { |
| if (type.getQualifier().isPushConstant()) |
| return spv::StorageClassPushConstant; |
| if (type.getBasicType() == glslang::EbtBlock) |
| return spv::StorageClassUniform; |
| return spv::StorageClassUniformConstant; |
| } |
| |
| if (type.getQualifier().storage == glslang::EvqShared && type.getBasicType() == glslang::EbtBlock) { |
| builder.addExtension(spv::E_SPV_KHR_workgroup_memory_explicit_layout); |
| builder.addCapability(spv::CapabilityWorkgroupMemoryExplicitLayoutKHR); |
| return spv::StorageClassWorkgroup; |
| } |
| |
| switch (type.getQualifier().storage) { |
| case glslang::EvqGlobal: return spv::StorageClassPrivate; |
| case glslang::EvqConstReadOnly: return spv::StorageClassFunction; |
| case glslang::EvqTemporary: return spv::StorageClassFunction; |
| case glslang::EvqShared: return spv::StorageClassWorkgroup; |
| #ifndef GLSLANG_WEB |
| case glslang::EvqPayload: return spv::StorageClassRayPayloadKHR; |
| case glslang::EvqPayloadIn: return spv::StorageClassIncomingRayPayloadKHR; |
| case glslang::EvqHitAttr: return spv::StorageClassHitAttributeKHR; |
| case glslang::EvqCallableData: return spv::StorageClassCallableDataKHR; |
| case glslang::EvqCallableDataIn: return spv::StorageClassIncomingCallableDataKHR; |
| case glslang::EvqtaskPayloadSharedEXT : return spv::StorageClassTaskPayloadWorkgroupEXT; |
| case glslang::EvqSpirvStorageClass: return static_cast<spv::StorageClass>(type.getQualifier().spirvStorageClass); |
| #endif |
| default: |
| assert(0); |
| break; |
| } |
| |
| return spv::StorageClassFunction; |
| } |
| |
| // Translate glslang constants to SPIR-V literals |
| void TGlslangToSpvTraverser::TranslateLiterals(const glslang::TVector<const glslang::TIntermConstantUnion*>& constants, |
| std::vector<unsigned>& literals) const |
| { |
| for (auto constant : constants) { |
| if (constant->getBasicType() == glslang::EbtFloat) { |
| float floatValue = static_cast<float>(constant->getConstArray()[0].getDConst()); |
| unsigned literal; |
| static_assert(sizeof(literal) == sizeof(floatValue), "sizeof(unsigned) != sizeof(float)"); |
| memcpy(&literal, &floatValue, sizeof(literal)); |
| literals.push_back(literal); |
| } else if (constant->getBasicType() == glslang::EbtInt) { |
| unsigned literal = constant->getConstArray()[0].getIConst(); |
| literals.push_back(literal); |
| } else if (constant->getBasicType() == glslang::EbtUint) { |
| unsigned literal = constant->getConstArray()[0].getUConst(); |
| literals.push_back(literal); |
| } else if (constant->getBasicType() == glslang::EbtBool) { |
| unsigned literal = constant->getConstArray()[0].getBConst(); |
| literals.push_back(literal); |
| } else if (constant->getBasicType() == glslang::EbtString) { |
| auto str = constant->getConstArray()[0].getSConst()->c_str(); |
| unsigned literal = 0; |
| char* literalPtr = reinterpret_cast<char*>(&literal); |
| unsigned charCount = 0; |
| char ch = 0; |
| do { |
| ch = *(str++); |
| *(literalPtr++) = ch; |
| ++charCount; |
| if (charCount == 4) { |
| literals.push_back(literal); |
| literalPtr = reinterpret_cast<char*>(&literal); |
| charCount = 0; |
| } |
| } while (ch != 0); |
| |
| // Partial literal is padded with 0 |
| if (charCount > 0) { |
| for (; charCount < 4; ++charCount) |
| *(literalPtr++) = 0; |
| literals.push_back(literal); |
| } |
| } else |
| assert(0); // Unexpected type |
| } |
| } |
| |
| // Add capabilities pertaining to how an array is indexed. |
| void TGlslangToSpvTraverser::addIndirectionIndexCapabilities(const glslang::TType& baseType, |
| const glslang::TType& indexType) |
| { |
| #ifndef GLSLANG_WEB |
| if (indexType.getQualifier().isNonUniform()) { |
| // deal with an asserted non-uniform index |
| // SPV_EXT_descriptor_indexing already added in TranslateNonUniformDecoration |
| if (baseType.getBasicType() == glslang::EbtSampler) { |
| if (baseType.getQualifier().hasAttachment()) |
| builder.addCapability(spv::CapabilityInputAttachmentArrayNonUniformIndexingEXT); |
| else if (baseType.isImage() && baseType.getSampler().isBuffer()) |
| builder.addCapability(spv::CapabilityStorageTexelBufferArrayNonUniformIndexingEXT); |
| else if (baseType.isTexture() && baseType.getSampler().isBuffer()) |
| builder.addCapability(spv::CapabilityUniformTexelBufferArrayNonUniformIndexingEXT); |
| else if (baseType.isImage()) |
| builder.addCapability(spv::CapabilityStorageImageArrayNonUniformIndexingEXT); |
| else if (baseType.isTexture()) |
| builder.addCapability(spv::CapabilitySampledImageArrayNonUniformIndexingEXT); |
| } else if (baseType.getBasicType() == glslang::EbtBlock) { |
| if (baseType.getQualifier().storage == glslang::EvqBuffer) |
| builder.addCapability(spv::CapabilityStorageBufferArrayNonUniformIndexingEXT); |
| else if (baseType.getQualifier().storage == glslang::EvqUniform) |
| builder.addCapability(spv::CapabilityUniformBufferArrayNonUniformIndexingEXT); |
| } |
| } else { |
| // assume a dynamically uniform index |
| if (baseType.getBasicType() == glslang::EbtSampler) { |
| if (baseType.getQualifier().hasAttachment()) { |
| builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); |
| builder.addCapability(spv::CapabilityInputAttachmentArrayDynamicIndexingEXT); |
| } else if (baseType.isImage() && baseType.getSampler().isBuffer()) { |
| builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); |
| builder.addCapability(spv::CapabilityStorageTexelBufferArrayDynamicIndexingEXT); |
| } else if (baseType.isTexture() && baseType.getSampler().isBuffer()) { |
| builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); |
| builder.addCapability(spv::CapabilityUniformTexelBufferArrayDynamicIndexingEXT); |
| } |
| } |
| } |
| #endif |
| } |
| |
| // Return whether or not the given type is something that should be tied to a |
| // descriptor set. |
| bool IsDescriptorResource(const glslang::TType& type) |
| { |
| // uniform and buffer blocks are included, unless it is a push_constant |
| if (type.getBasicType() == glslang::EbtBlock) |
| return type.getQualifier().isUniformOrBuffer() && |
| ! type.getQualifier().isShaderRecord() && |
| ! type.getQualifier().isPushConstant(); |
| |
| // non block... |
| // basically samplerXXX/subpass/sampler/texture are all included |
| // if they are the global-scope-class, not the function parameter |
| // (or local, if they ever exist) class. |
| if (type.getBasicType() == glslang::EbtSampler || |
| type.getBasicType() == glslang::EbtAccStruct) |
| return type.getQualifier().isUniformOrBuffer(); |
| |
| // None of the above. |
| return false; |
| } |
| |
| void InheritQualifiers(glslang::TQualifier& child, const glslang::TQualifier& parent) |
| { |
| if (child.layoutMatrix == glslang::ElmNone) |
| child.layoutMatrix = parent.layoutMatrix; |
| |
| if (parent.invariant) |
| child.invariant = true; |
| if (parent.flat) |
| child.flat = true; |
| if (parent.centroid) |
| child.centroid = true; |
| #ifndef GLSLANG_WEB |
| if (parent.nopersp) |
| child.nopersp = true; |
| if (parent.explicitInterp) |
| child.explicitInterp = true; |
| if (parent.perPrimitiveNV) |
| child.perPrimitiveNV = true; |
| if (parent.perViewNV) |
| child.perViewNV = true; |
| if (parent.perTaskNV) |
| child.perTaskNV = true; |
| if (parent.storage == glslang::EvqtaskPayloadSharedEXT) |
| child.storage = glslang::EvqtaskPayloadSharedEXT; |
| if (parent.patch) |
| child.patch = true; |
| if (parent.sample) |
| child.sample = true; |
| if (parent.coherent) |
| child.coherent = true; |
| if (parent.devicecoherent) |
| child.devicecoherent = true; |
| if (parent.queuefamilycoherent) |
| child.queuefamilycoherent = true; |
| if (parent.workgroupcoherent) |
| child.workgroupcoherent = true; |
| if (parent.subgroupcoherent) |
| child.subgroupcoherent = true; |
| if (parent.shadercallcoherent) |
| child.shadercallcoherent = true; |
| if (parent.nonprivate) |
| child.nonprivate = true; |
| if (parent.volatil) |
| child.volatil = true; |
| if (parent.restrict) |
| child.restrict = true; |
| if (parent.readonly) |
| child.readonly = true; |
| if (parent.writeonly) |
| child.writeonly = true; |
| #endif |
| if (parent.nonUniform) |
| child.nonUniform = true; |
| } |
| |
| bool HasNonLayoutQualifiers(const glslang::TType& type, const glslang::TQualifier& qualifier) |
| { |
| // This should list qualifiers that simultaneous satisfy: |
| // - struct members might inherit from a struct declaration |
| // (note that non-block structs don't explicitly inherit, |
| // only implicitly, meaning no decoration involved) |
| // - affect decorations on the struct members |
| // (note smooth does not, and expecting something like volatile |
| // to effect the whole object) |
| // - are not part of the offset/st430/etc or row/column-major layout |
| return qualifier.invariant || (qualifier.hasLocation() && type.getBasicType() == glslang::EbtBlock); |
| } |
| |
| // |
| // Implement the TGlslangToSpvTraverser class. |
| // |
| |
| TGlslangToSpvTraverser::TGlslangToSpvTraverser(unsigned int spvVersion, |
| const glslang::TIntermediate* glslangIntermediate, |
| spv::SpvBuildLogger* buildLogger, glslang::SpvOptions& options) : |
| TIntermTraverser(true, false, true), |
| options(options), |
| shaderEntry(nullptr), currentFunction(nullptr), |
| sequenceDepth(0), logger(buildLogger), |
| builder(spvVersion, (glslang::GetKhronosToolId() << 16) | glslang::GetSpirvGeneratorVersion(), logger), |
| inEntryPoint(false), entryPointTerminated(false), linkageOnly(false), |
| glslangIntermediate(glslangIntermediate), |
| nanMinMaxClamp(glslangIntermediate->getNanMinMaxClamp()), |
| nonSemanticDebugPrintf(0), |
| taskPayloadID(0) |
| { |
| bool isMeshShaderExt = (glslangIntermediate->getRequestedExtensions().find(glslang::E_GL_EXT_mesh_shader) != |
| glslangIntermediate->getRequestedExtensions().end()); |
| spv::ExecutionModel executionModel = TranslateExecutionModel(glslangIntermediate->getStage(), isMeshShaderExt); |
| |
| builder.clearAccessChain(); |
| builder.setSource(TranslateSourceLanguage(glslangIntermediate->getSource(), glslangIntermediate->getProfile()), |
| glslangIntermediate->getVersion()); |
| |
| if (options.generateDebugInfo) { |
| builder.setEmitOpLines(); |
| builder.setSourceFile(glslangIntermediate->getSourceFile()); |
| |
| // Set the source shader's text. If for SPV version 1.0, include |
| // a preamble in comments stating the OpModuleProcessed instructions. |
| // Otherwise, emit those as actual instructions. |
| std::string text; |
| const std::vector<std::string>& processes = glslangIntermediate->getProcesses(); |
| for (int p = 0; p < (int)processes.size(); ++p) { |
| if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_1) { |
| text.append("// OpModuleProcessed "); |
| text.append(processes[p]); |
| text.append("\n"); |
| } else |
| builder.addModuleProcessed(processes[p]); |
| } |
| if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_1 && (int)processes.size() > 0) |
| text.append("#line 1\n"); |
| text.append(glslangIntermediate->getSourceText()); |
| builder.setSourceText(text); |
| // Pass name and text for all included files |
| const std::map<std::string, std::string>& include_txt = glslangIntermediate->getIncludeText(); |
| for (auto iItr = include_txt.begin(); iItr != include_txt.end(); ++iItr) |
| builder.addInclude(iItr->first, iItr->second); |
| } |
| |
| builder.setEmitNonSemanticShaderDebugInfo(options.emitNonSemanticShaderDebugInfo); |
| builder.setEmitNonSemanticShaderDebugSource(options.emitNonSemanticShaderDebugSource); |
| |
| stdBuiltins = builder.import("GLSL.std.450"); |
| |
| spv::AddressingModel addressingModel = spv::AddressingModelLogical; |
| spv::MemoryModel memoryModel = spv::MemoryModelGLSL450; |
| |
| if (glslangIntermediate->usingPhysicalStorageBuffer()) { |
| addressingModel = spv::AddressingModelPhysicalStorageBuffer64EXT; |
| builder.addIncorporatedExtension(spv::E_SPV_KHR_physical_storage_buffer, spv::Spv_1_5); |
| builder.addCapability(spv::CapabilityPhysicalStorageBufferAddressesEXT); |
| } |
| if (glslangIntermediate->usingVulkanMemoryModel()) { |
| memoryModel = spv::MemoryModelVulkanKHR; |
| builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); |
| builder.addIncorporatedExtension(spv::E_SPV_KHR_vulkan_memory_model, spv::Spv_1_5); |
| } |
| builder.setMemoryModel(addressingModel, memoryModel); |
| |
| if (glslangIntermediate->usingVariablePointers()) { |
| builder.addCapability(spv::CapabilityVariablePointers); |
| } |
| |
| shaderEntry = builder.makeEntryPoint(glslangIntermediate->getEntryPointName().c_str()); |
| entryPoint = builder.addEntryPoint(executionModel, shaderEntry, glslangIntermediate->getEntryPointName().c_str()); |
| |
| // Add the source extensions |
| const auto& sourceExtensions = glslangIntermediate->getRequestedExtensions(); |
| for (auto it = sourceExtensions.begin(); it != sourceExtensions.end(); ++it) |
| builder.addSourceExtension(it->c_str()); |
| |
| // Add the top-level modes for this shader. |
| |
| if (glslangIntermediate->getXfbMode()) { |
| builder.addCapability(spv::CapabilityTransformFeedback); |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeXfb); |
| } |
| |
| if (glslangIntermediate->getLayoutPrimitiveCulling()) { |
| builder.addCapability(spv::CapabilityRayTraversalPrimitiveCullingKHR); |
| } |
| |
| #ifndef GLSLANG_WEB |
| if (glslangIntermediate->getSubgroupUniformControlFlow()) { |
| builder.addExtension(spv::E_SPV_KHR_subgroup_uniform_control_flow); |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeSubgroupUniformControlFlowKHR); |
| } |
| #endif |
| |
| unsigned int mode; |
| switch (glslangIntermediate->getStage()) { |
| case EShLangVertex: |
| builder.addCapability(spv::CapabilityShader); |
| break; |
| |
| case EShLangFragment: |
| builder.addCapability(spv::CapabilityShader); |
| if (glslangIntermediate->getPixelCenterInteger()) |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModePixelCenterInteger); |
| |
| if (glslangIntermediate->getOriginUpperLeft()) |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeOriginUpperLeft); |
| else |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeOriginLowerLeft); |
| |
| if (glslangIntermediate->getEarlyFragmentTests()) |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeEarlyFragmentTests); |
| |
| if (glslangIntermediate->getEarlyAndLateFragmentTestsAMD()) |
| { |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeEarlyAndLateFragmentTestsAMD); |
| builder.addExtension(spv::E_SPV_AMD_shader_early_and_late_fragment_tests); |
| } |
| |
| if (glslangIntermediate->getPostDepthCoverage()) { |
| builder.addCapability(spv::CapabilitySampleMaskPostDepthCoverage); |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModePostDepthCoverage); |
| builder.addExtension(spv::E_SPV_KHR_post_depth_coverage); |
| } |
| |
| if (glslangIntermediate->isDepthReplacing()) |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeDepthReplacing); |
| |
| if (glslangIntermediate->isStencilReplacing()) |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeStencilRefReplacingEXT); |
| |
| #ifndef GLSLANG_WEB |
| |
| switch(glslangIntermediate->getDepth()) { |
| case glslang::EldGreater: mode = spv::ExecutionModeDepthGreater; break; |
| case glslang::EldLess: mode = spv::ExecutionModeDepthLess; break; |
| case glslang::EldUnchanged: mode = spv::ExecutionModeDepthUnchanged; break; |
| default: mode = spv::ExecutionModeMax; break; |
| } |
| |
| if (mode != spv::ExecutionModeMax) |
| builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); |
| |
| switch (glslangIntermediate->getStencil()) { |
| case glslang::ElsRefUnchangedFrontAMD: mode = spv::ExecutionModeStencilRefUnchangedFrontAMD; break; |
| case glslang::ElsRefGreaterFrontAMD: mode = spv::ExecutionModeStencilRefGreaterFrontAMD; break; |
| case glslang::ElsRefLessFrontAMD: mode = spv::ExecutionModeStencilRefLessFrontAMD; break; |
| case glslang::ElsRefUnchangedBackAMD: mode = spv::ExecutionModeStencilRefUnchangedBackAMD; break; |
| case glslang::ElsRefGreaterBackAMD: mode = spv::ExecutionModeStencilRefGreaterBackAMD; break; |
| case glslang::ElsRefLessBackAMD: mode = spv::ExecutionModeStencilRefLessBackAMD; break; |
| default: mode = spv::ExecutionModeMax; break; |
| } |
| |
| if (mode != spv::ExecutionModeMax) |
| builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); |
| switch (glslangIntermediate->getInterlockOrdering()) { |
| case glslang::EioPixelInterlockOrdered: mode = spv::ExecutionModePixelInterlockOrderedEXT; |
| break; |
| case glslang::EioPixelInterlockUnordered: mode = spv::ExecutionModePixelInterlockUnorderedEXT; |
| break; |
| case glslang::EioSampleInterlockOrdered: mode = spv::ExecutionModeSampleInterlockOrderedEXT; |
| break; |
| case glslang::EioSampleInterlockUnordered: mode = spv::ExecutionModeSampleInterlockUnorderedEXT; |
| break; |
| case glslang::EioShadingRateInterlockOrdered: mode = spv::ExecutionModeShadingRateInterlockOrderedEXT; |
| break; |
| case glslang::EioShadingRateInterlockUnordered: mode = spv::ExecutionModeShadingRateInterlockUnorderedEXT; |
| break; |
| default: mode = spv::ExecutionModeMax; |
| break; |
| } |
| if (mode != spv::ExecutionModeMax) { |
| builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); |
| if (mode == spv::ExecutionModeShadingRateInterlockOrderedEXT || |
| mode == spv::ExecutionModeShadingRateInterlockUnorderedEXT) { |
| builder.addCapability(spv::CapabilityFragmentShaderShadingRateInterlockEXT); |
| } else if (mode == spv::ExecutionModePixelInterlockOrderedEXT || |
| mode == spv::ExecutionModePixelInterlockUnorderedEXT) { |
| builder.addCapability(spv::CapabilityFragmentShaderPixelInterlockEXT); |
| } else { |
| builder.addCapability(spv::CapabilityFragmentShaderSampleInterlockEXT); |
| } |
| builder.addExtension(spv::E_SPV_EXT_fragment_shader_interlock); |
| } |
| #endif |
| break; |
| |
| case EShLangCompute: |
| builder.addCapability(spv::CapabilityShader); |
| if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_6) { |
| std::vector<spv::Id> dimConstId; |
| for (int dim = 0; dim < 3; ++dim) { |
| bool specConst = (glslangIntermediate->getLocalSizeSpecId(dim) != glslang::TQualifier::layoutNotSet); |
| dimConstId.push_back(builder.makeUintConstant(glslangIntermediate->getLocalSize(dim), specConst)); |
| if (specConst) { |
| builder.addDecoration(dimConstId.back(), spv::DecorationSpecId, |
| glslangIntermediate->getLocalSizeSpecId(dim)); |
| } |
| } |
| builder.addExecutionModeId(shaderEntry, spv::ExecutionModeLocalSizeId, dimConstId); |
| } else { |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeLocalSize, glslangIntermediate->getLocalSize(0), |
| glslangIntermediate->getLocalSize(1), |
| glslangIntermediate->getLocalSize(2)); |
| } |
| if (glslangIntermediate->getLayoutDerivativeModeNone() == glslang::LayoutDerivativeGroupQuads) { |
| builder.addCapability(spv::CapabilityComputeDerivativeGroupQuadsNV); |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeDerivativeGroupQuadsNV); |
| builder.addExtension(spv::E_SPV_NV_compute_shader_derivatives); |
| } else if (glslangIntermediate->getLayoutDerivativeModeNone() == glslang::LayoutDerivativeGroupLinear) { |
| builder.addCapability(spv::CapabilityComputeDerivativeGroupLinearNV); |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeDerivativeGroupLinearNV); |
| builder.addExtension(spv::E_SPV_NV_compute_shader_derivatives); |
| } |
| break; |
| #ifndef GLSLANG_WEB |
| case EShLangTessEvaluation: |
| case EShLangTessControl: |
| builder.addCapability(spv::CapabilityTessellation); |
| |
| glslang::TLayoutGeometry primitive; |
| |
| if (glslangIntermediate->getStage() == EShLangTessControl) { |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, |
| glslangIntermediate->getVertices()); |
| primitive = glslangIntermediate->getOutputPrimitive(); |
| } else { |
| primitive = glslangIntermediate->getInputPrimitive(); |
| } |
| |
| switch (primitive) { |
| case glslang::ElgTriangles: mode = spv::ExecutionModeTriangles; break; |
| case glslang::ElgQuads: mode = spv::ExecutionModeQuads; break; |
| case glslang::ElgIsolines: mode = spv::ExecutionModeIsolines; break; |
| default: mode = spv::ExecutionModeMax; break; |
| } |
| if (mode != spv::ExecutionModeMax) |
| builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); |
| |
| switch (glslangIntermediate->getVertexSpacing()) { |
| case glslang::EvsEqual: mode = spv::ExecutionModeSpacingEqual; break; |
| case glslang::EvsFractionalEven: mode = spv::ExecutionModeSpacingFractionalEven; break; |
| case glslang::EvsFractionalOdd: mode = spv::ExecutionModeSpacingFractionalOdd; break; |
| default: mode = spv::ExecutionModeMax; break; |
| } |
| if (mode != spv::ExecutionModeMax) |
| builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); |
| |
| switch (glslangIntermediate->getVertexOrder()) { |
| case glslang::EvoCw: mode = spv::ExecutionModeVertexOrderCw; break; |
| case glslang::EvoCcw: mode = spv::ExecutionModeVertexOrderCcw; break; |
| default: mode = spv::ExecutionModeMax; break; |
| } |
| if (mode != spv::ExecutionModeMax) |
| builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); |
| |
| if (glslangIntermediate->getPointMode()) |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModePointMode); |
| break; |
| |
| case EShLangGeometry: |
| builder.addCapability(spv::CapabilityGeometry); |
| switch (glslangIntermediate->getInputPrimitive()) { |
| case glslang::ElgPoints: mode = spv::ExecutionModeInputPoints; break; |
| case glslang::ElgLines: mode = spv::ExecutionModeInputLines; break; |
| case glslang::ElgLinesAdjacency: mode = spv::ExecutionModeInputLinesAdjacency; break; |
| case glslang::ElgTriangles: mode = spv::ExecutionModeTriangles; break; |
| case glslang::ElgTrianglesAdjacency: mode = spv::ExecutionModeInputTrianglesAdjacency; break; |
| default: mode = spv::ExecutionModeMax; break; |
| } |
| if (mode != spv::ExecutionModeMax) |
| builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); |
| |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeInvocations, glslangIntermediate->getInvocations()); |
| |
| switch (glslangIntermediate->getOutputPrimitive()) { |
| case glslang::ElgPoints: mode = spv::ExecutionModeOutputPoints; break; |
| case glslang::ElgLineStrip: mode = spv::ExecutionModeOutputLineStrip; break; |
| case glslang::ElgTriangleStrip: mode = spv::ExecutionModeOutputTriangleStrip; break; |
| default: mode = spv::ExecutionModeMax; break; |
| } |
| if (mode != spv::ExecutionModeMax) |
| builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, glslangIntermediate->getVertices()); |
| break; |
| |
| case EShLangRayGen: |
| case EShLangIntersect: |
| case EShLangAnyHit: |
| case EShLangClosestHit: |
| case EShLangMiss: |
| case EShLangCallable: |
| { |
| auto& extensions = glslangIntermediate->getRequestedExtensions(); |
| if (extensions.find("GL_NV_ray_tracing") == extensions.end()) { |
| builder.addCapability(spv::CapabilityRayTracingKHR); |
| builder.addExtension("SPV_KHR_ray_tracing"); |
| } |
| else { |
| builder.addCapability(spv::CapabilityRayTracingNV); |
| builder.addExtension("SPV_NV_ray_tracing"); |
| } |
| if (glslangIntermediate->getStage() != EShLangRayGen && glslangIntermediate->getStage() != EShLangCallable) |
| { |
| if (extensions.find("GL_EXT_ray_cull_mask") != extensions.end()) { |
| builder.addCapability(spv::CapabilityRayCullMaskKHR); |
| builder.addExtension("SPV_KHR_ray_cull_mask"); |
| } |
| } |
| break; |
| } |
| case EShLangTask: |
| case EShLangMesh: |
| if(isMeshShaderExt) { |
| builder.addCapability(spv::CapabilityMeshShadingEXT); |
| builder.addExtension(spv::E_SPV_EXT_mesh_shader); |
| } else { |
| builder.addCapability(spv::CapabilityMeshShadingNV); |
| builder.addExtension(spv::E_SPV_NV_mesh_shader); |
| } |
| if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_6) { |
| std::vector<spv::Id> dimConstId; |
| for (int dim = 0; dim < 3; ++dim) { |
| bool specConst = (glslangIntermediate->getLocalSizeSpecId(dim) != glslang::TQualifier::layoutNotSet); |
| dimConstId.push_back(builder.makeUintConstant(glslangIntermediate->getLocalSize(dim), specConst)); |
| if (specConst) { |
| builder.addDecoration(dimConstId.back(), spv::DecorationSpecId, |
| glslangIntermediate->getLocalSizeSpecId(dim)); |
| } |
| } |
| builder.addExecutionModeId(shaderEntry, spv::ExecutionModeLocalSizeId, dimConstId); |
| } else { |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeLocalSize, glslangIntermediate->getLocalSize(0), |
| glslangIntermediate->getLocalSize(1), |
| glslangIntermediate->getLocalSize(2)); |
| } |
| if (glslangIntermediate->getStage() == EShLangMesh) { |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, |
| glslangIntermediate->getVertices()); |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputPrimitivesNV, |
| glslangIntermediate->getPrimitives()); |
| |
| switch (glslangIntermediate->getOutputPrimitive()) { |
| case glslang::ElgPoints: mode = spv::ExecutionModeOutputPoints; break; |
| case glslang::ElgLines: mode = spv::ExecutionModeOutputLinesNV; break; |
| case glslang::ElgTriangles: mode = spv::ExecutionModeOutputTrianglesNV; break; |
| default: mode = spv::ExecutionModeMax; break; |
| } |
| if (mode != spv::ExecutionModeMax) |
| builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); |
| } |
| break; |
| #endif |
| |
| default: |
| break; |
| } |
| |
| #ifndef GLSLANG_WEB |
| // |
| // Add SPIR-V requirements (GL_EXT_spirv_intrinsics) |
| // |
| if (glslangIntermediate->hasSpirvRequirement()) { |
| const glslang::TSpirvRequirement& spirvRequirement = glslangIntermediate->getSpirvRequirement(); |
| |
| // Add SPIR-V extension requirement |
| for (auto& extension : spirvRequirement.extensions) |
| builder.addExtension(extension.c_str()); |
| |
| // Add SPIR-V capability requirement |
| for (auto capability : spirvRequirement.capabilities) |
| builder.addCapability(static_cast<spv::Capability>(capability)); |
| } |
| |
| // |
| // Add SPIR-V execution mode qualifiers (GL_EXT_spirv_intrinsics) |
| // |
| if (glslangIntermediate->hasSpirvExecutionMode()) { |
| const glslang::TSpirvExecutionMode spirvExecutionMode = glslangIntermediate->getSpirvExecutionMode(); |
| |
| // Add spirv_execution_mode |
| for (auto& mode : spirvExecutionMode.modes) { |
| if (!mode.second.empty()) { |
| std::vector<unsigned> literals; |
| TranslateLiterals(mode.second, literals); |
| builder.addExecutionMode(shaderEntry, static_cast<spv::ExecutionMode>(mode.first), literals); |
| } else |
| builder.addExecutionMode(shaderEntry, static_cast<spv::ExecutionMode>(mode.first)); |
| } |
| |
| // Add spirv_execution_mode_id |
| for (auto& modeId : spirvExecutionMode.modeIds) { |
| std::vector<spv::Id> operandIds; |
| assert(!modeId.second.empty()); |
| for (auto extraOperand : modeId.second) { |
| if (extraOperand->getType().getQualifier().isSpecConstant()) |
| operandIds.push_back(getSymbolId(extraOperand->getAsSymbolNode())); |
| else |
| operandIds.push_back(createSpvConstant(*extraOperand)); |
| } |
| builder.addExecutionModeId(shaderEntry, static_cast<spv::ExecutionMode>(modeId.first), operandIds); |
| } |
| } |
| #endif |
| } |
| |
| // Finish creating SPV, after the traversal is complete. |
| void TGlslangToSpvTraverser::finishSpv() |
| { |
| // Finish the entry point function |
| if (! entryPointTerminated) { |
| builder.setBuildPoint(shaderEntry->getLastBlock()); |
| builder.leaveFunction(); |
| } |
| |
| // finish off the entry-point SPV instruction by adding the Input/Output <id> |
| for (auto it = iOSet.cbegin(); it != iOSet.cend(); ++it) |
| entryPoint->addIdOperand(*it); |
| |
| // Add capabilities, extensions, remove unneeded decorations, etc., |
| // based on the resulting SPIR-V. |
| // Note: WebGPU code generation must have the opportunity to aggressively |
| // prune unreachable merge blocks and continue targets. |
| builder.postProcess(); |
| } |
| |
| // Write the SPV into 'out'. |
| void TGlslangToSpvTraverser::dumpSpv(std::vector<unsigned int>& out) |
| { |
| builder.dump(out); |
| } |
| |
| // |
| // Implement the traversal functions. |
| // |
| // Return true from interior nodes to have the external traversal |
| // continue on to children. Return false if children were |
| // already processed. |
| // |
| |
| // |
| // Symbols can turn into |
| // - uniform/input reads |
| // - output writes |
| // - complex lvalue base setups: foo.bar[3].... , where we see foo and start up an access chain |
| // - something simple that degenerates into the last bullet |
| // |
| void TGlslangToSpvTraverser::visitSymbol(glslang::TIntermSymbol* symbol) |
| { |
| SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); |
| if (symbol->getType().isStruct()) |
| glslangTypeToIdMap[symbol->getType().getStruct()] = symbol->getId(); |
| |
| if (symbol->getType().getQualifier().isSpecConstant()) |
| spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); |
| #ifdef ENABLE_HLSL |
| // Skip symbol handling if it is string-typed |
| if (symbol->getBasicType() == glslang::EbtString) |
| return; |
| #endif |
| |
| // getSymbolId() will set up all the IO decorations on the first call. |
| // Formal function parameters were mapped during makeFunctions(). |
| spv::Id id = getSymbolId(symbol); |
| |
| if (symbol->getType().getQualifier().isTaskPayload()) |
| taskPayloadID = id; // cache the taskPayloadID to be used it as operand for OpEmitMeshTasksEXT |
| |
| if (builder.isPointer(id)) { |
| if (!symbol->getType().getQualifier().isParamInput() && |
| !symbol->getType().getQualifier().isParamOutput()) { |
| // Include all "static use" and "linkage only" interface variables on the OpEntryPoint instruction |
| // Consider adding to the OpEntryPoint interface list. |
| // Only looking at structures if they have at least one member. |
| if (!symbol->getType().isStruct() || symbol->getType().getStruct()->size() > 0) { |
| spv::StorageClass sc = builder.getStorageClass(id); |
| // Before SPIR-V 1.4, we only want to include Input and Output. |
| // Starting with SPIR-V 1.4, we want all globals. |
| if ((glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4 && builder.isGlobalStorage(id)) || |
| (sc == spv::StorageClassInput || sc == spv::StorageClassOutput)) { |
| iOSet.insert(id); |
| } |
| } |
| } |
| |
| // If the SPIR-V type is required to be different than the AST type |
| // (for ex SubgroupMasks or 3x4 ObjectToWorld/WorldToObject matrices), |
| // translate now from the SPIR-V type to the AST type, for the consuming |
| // operation. |
| // Note this turns it from an l-value to an r-value. |
| // Currently, all symbols needing this are inputs; avoid the map lookup when non-input. |
| if (symbol->getType().getQualifier().storage == glslang::EvqVaryingIn) |
| id = translateForcedType(id); |
| } |
| |
| // Only process non-linkage-only nodes for generating actual static uses |
| if (! linkageOnly || symbol->getQualifier().isSpecConstant()) { |
| // Prepare to generate code for the access |
| |
| // L-value chains will be computed left to right. We're on the symbol now, |
| // which is the left-most part of the access chain, so now is "clear" time, |
| // followed by setting the base. |
| builder.clearAccessChain(); |
| |
| // For now, we consider all user variables as being in memory, so they are pointers, |
| // except for |
| // A) R-Value arguments to a function, which are an intermediate object. |
| // See comments in handleUserFunctionCall(). |
| // B) Specialization constants (normal constants don't even come in as a variable), |
| // These are also pure R-values. |
| // C) R-Values from type translation, see above call to translateForcedType() |
| glslang::TQualifier qualifier = symbol->getQualifier(); |
| if (qualifier.isSpecConstant() || rValueParameters.find(symbol->getId()) != rValueParameters.end() || |
| !builder.isPointerType(builder.getTypeId(id))) |
| builder.setAccessChainRValue(id); |
| else |
| builder.setAccessChainLValue(id); |
| } |
| |
| #ifdef ENABLE_HLSL |
| // Process linkage-only nodes for any special additional interface work. |
| if (linkageOnly) { |
| if (glslangIntermediate->getHlslFunctionality1()) { |
| // Map implicit counter buffers to their originating buffers, which should have been |
| // seen by now, given earlier pruning of unused counters, and preservation of order |
| // of declaration. |
| if (symbol->getType().getQualifier().isUniformOrBuffer()) { |
| if (!glslangIntermediate->hasCounterBufferName(symbol->getName())) { |
| // Save possible originating buffers for counter buffers, keyed by |
| // making the potential counter-buffer name. |
| std::string keyName = symbol->getName().c_str(); |
| keyName = glslangIntermediate->addCounterBufferName(keyName); |
| counterOriginator[keyName] = symbol; |
| } else { |
| // Handle a counter buffer, by finding the saved originating buffer. |
| std::string keyName = symbol->getName().c_str(); |
| auto it = counterOriginator.find(keyName); |
| if (it != counterOriginator.end()) { |
| id = getSymbolId(it->second); |
| if (id != spv::NoResult) { |
| spv::Id counterId = getSymbolId(symbol); |
| if (counterId != spv::NoResult) { |
| builder.addExtension("SPV_GOOGLE_hlsl_functionality1"); |
| builder.addDecorationId(id, spv::DecorationHlslCounterBufferGOOGLE, counterId); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| #endif |
| } |
| |
| bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::TIntermBinary* node) |
| { |
| builder.setLine(node->getLoc().line, node->getLoc().getFilename()); |
| if (node->getLeft()->getAsSymbolNode() != nullptr && node->getLeft()->getType().isStruct()) { |
| glslangTypeToIdMap[node->getLeft()->getType().getStruct()] = node->getLeft()->getAsSymbolNode()->getId(); |
| } |
| if (node->getRight()->getAsSymbolNode() != nullptr && node->getRight()->getType().isStruct()) { |
| glslangTypeToIdMap[node->getRight()->getType().getStruct()] = node->getRight()->getAsSymbolNode()->getId(); |
| } |
| |
| SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); |
| if (node->getType().getQualifier().isSpecConstant()) |
| spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); |
| |
| // First, handle special cases |
| switch (node->getOp()) { |
| case glslang::EOpAssign: |
| case glslang::EOpAddAssign: |
| case glslang::EOpSubAssign: |
| case glslang::EOpMulAssign: |
| case glslang::EOpVectorTimesMatrixAssign: |
| case glslang::EOpVectorTimesScalarAssign: |
| case glslang::EOpMatrixTimesScalarAssign: |
| case glslang::EOpMatrixTimesMatrixAssign: |
| case glslang::EOpDivAssign: |
| case glslang::EOpModAssign: |
| case glslang::EOpAndAssign: |
| case glslang::EOpInclusiveOrAssign: |
| case glslang::EOpExclusiveOrAssign: |
| case glslang::EOpLeftShiftAssign: |
| case glslang::EOpRightShiftAssign: |
| // A bin-op assign "a += b" means the same thing as "a = a + b" |
| // where a is evaluated before b. For a simple assignment, GLSL |
| // says to evaluate the left before the right. So, always, left |
| // node then right node. |
| { |
| // get the left l-value, save it away |
| builder.clearAccessChain(); |
| node->getLeft()->traverse(this); |
| spv::Builder::AccessChain lValue = builder.getAccessChain(); |
| |
| // evaluate the right |
| builder.clearAccessChain(); |
| node->getRight()->traverse(this); |
| spv::Id rValue = accessChainLoad(node->getRight()->getType()); |
| |
| if (node->getOp() != glslang::EOpAssign) { |
| // the left is also an r-value |
| builder.setAccessChain(lValue); |
| spv::Id leftRValue = accessChainLoad(node->getLeft()->getType()); |
| |
| // do the operation |
| spv::Builder::AccessChain::CoherentFlags coherentFlags = TranslateCoherent(node->getLeft()->getType()); |
| coherentFlags |= TranslateCoherent(node->getRight()->getType()); |
| OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()), |
| TranslateNoContractionDecoration(node->getType().getQualifier()), |
| TranslateNonUniformDecoration(coherentFlags) }; |
| rValue = createBinaryOperation(node->getOp(), decorations, |
| convertGlslangToSpvType(node->getType()), leftRValue, rValue, |
| node->getType().getBasicType()); |
| |
| // these all need their counterparts in createBinaryOperation() |
| assert(rValue != spv::NoResult); |
| } |
| |
| // store the result |
| builder.setAccessChain(lValue); |
| multiTypeStore(node->getLeft()->getType(), rValue); |
| |
| // assignments are expressions having an rValue after they are evaluated... |
| builder.clearAccessChain(); |
| builder.setAccessChainRValue(rValue); |
| } |
| return false; |
| case glslang::EOpIndexDirect: |
| case glslang::EOpIndexDirectStruct: |
| { |
| // Structure, array, matrix, or vector indirection with statically known index. |
| // Get the left part of the access chain. |
| node->getLeft()->traverse(this); |
| |
| // Add the next element in the chain |
| |
| const int glslangIndex = node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); |
| if (! node->getLeft()->getType().isArray() && |
| node->getLeft()->getType().isVector() && |
| node->getOp() == glslang::EOpIndexDirect) { |
| // Swizzle is uniform so propagate uniform into access chain |
| spv::Builder::AccessChain::CoherentFlags coherentFlags = TranslateCoherent(node->getLeft()->getType()); |
| coherentFlags.nonUniform = 0; |
| // This is essentially a hard-coded vector swizzle of size 1, |
| // so short circuit the access-chain stuff with a swizzle. |
| std::vector<unsigned> swizzle; |
| swizzle.push_back(glslangIndex); |
| int dummySize; |
| builder.accessChainPushSwizzle(swizzle, convertGlslangToSpvType(node->getLeft()->getType()), |
| coherentFlags, |
| glslangIntermediate->getBaseAlignmentScalar( |
| node->getLeft()->getType(), dummySize)); |
| } else { |
| |
| // Load through a block reference is performed with a dot operator that |
| // is mapped to EOpIndexDirectStruct. When we get to the actual reference, |
| // do a load and reset the access chain. |
| if (node->getLeft()->isReference() && |
| !node->getLeft()->getType().isArray() && |
| node->getOp() == glslang::EOpIndexDirectStruct) |
| { |
| spv::Id left = accessChainLoad(node->getLeft()->getType()); |
| builder.clearAccessChain(); |
| builder.setAccessChainLValue(left); |
| } |
| |
| int spvIndex = glslangIndex; |
| if (node->getLeft()->getBasicType() == glslang::EbtBlock && |
| node->getOp() == glslang::EOpIndexDirectStruct) |
| { |
| // This may be, e.g., an anonymous block-member selection, which generally need |
| // index remapping due to hidden members in anonymous blocks. |
| long long glslangId = glslangTypeToIdMap[node->getLeft()->getType().getStruct()]; |
| if (memberRemapper.find(glslangId) != memberRemapper.end()) { |
| std::vector<int>& remapper = memberRemapper[glslangId]; |
| assert(remapper.size() > 0); |
| spvIndex = remapper[glslangIndex]; |
| } |
| } |
| |
| // Struct reference propagates uniform lvalue |
| spv::Builder::AccessChain::CoherentFlags coherentFlags = |
| TranslateCoherent(node->getLeft()->getType()); |
| coherentFlags.nonUniform = 0; |
| |
| // normal case for indexing array or structure or block |
| builder.accessChainPush(builder.makeIntConstant(spvIndex), |
| coherentFlags, |
| node->getLeft()->getType().getBufferReferenceAlignment()); |
| |
| // Add capabilities here for accessing PointSize and clip/cull distance. |
| // We have deferred generation of associated capabilities until now. |
| if (node->getLeft()->getType().isStruct() && ! node->getLeft()->getType().isArray()) |
| declareUseOfStructMember(*(node->getLeft()->getType().getStruct()), glslangIndex); |
| } |
| } |
| return false; |
| case glslang::EOpIndexIndirect: |
| { |
| // Array, matrix, or vector indirection with variable index. |
| // Will use native SPIR-V access-chain for and array indirection; |
| // matrices are arrays of vectors, so will also work for a matrix. |
| // Will use the access chain's 'component' for variable index into a vector. |
| |
| // This adapter is building access chains left to right. |
| // Set up the access chain to the left. |
| node->getLeft()->traverse(this); |
| |
| // save it so that computing the right side doesn't trash it |
| spv::Builder::AccessChain partial = builder.getAccessChain(); |
| |
| // compute the next index in the chain |
| builder.clearAccessChain(); |
| node->getRight()->traverse(this); |
| spv::Id index = accessChainLoad(node->getRight()->getType()); |
| |
| addIndirectionIndexCapabilities(node->getLeft()->getType(), node->getRight()->getType()); |
| |
| // restore the saved access chain |
| builder.setAccessChain(partial); |
| |
| // Only if index is nonUniform should we propagate nonUniform into access chain |
| spv::Builder::AccessChain::CoherentFlags index_flags = TranslateCoherent(node->getRight()->getType()); |
| spv::Builder::AccessChain::CoherentFlags coherent_flags = TranslateCoherent(node->getLeft()->getType()); |
| coherent_flags.nonUniform = index_flags.nonUniform; |
| |
| if (! node->getLeft()->getType().isArray() && node->getLeft()->getType().isVector()) { |
| int dummySize; |
| builder.accessChainPushComponent( |
| index, convertGlslangToSpvType(node->getLeft()->getType()), coherent_flags, |
| glslangIntermediate->getBaseAlignmentScalar(node->getLeft()->getType(), |
| dummySize)); |
| } else |
| builder.accessChainPush(index, coherent_flags, |
| node->getLeft()->getType().getBufferReferenceAlignment()); |
| } |
| return false; |
| case glslang::EOpVectorSwizzle: |
| { |
| node->getLeft()->traverse(this); |
| std::vector<unsigned> swizzle; |
| convertSwizzle(*node->getRight()->getAsAggregate(), swizzle); |
| int dummySize; |
| builder.accessChainPushSwizzle(swizzle, convertGlslangToSpvType(node->getLeft()->getType()), |
| TranslateCoherent(node->getLeft()->getType()), |
| glslangIntermediate->getBaseAlignmentScalar(node->getLeft()->getType(), |
| dummySize)); |
| } |
| return false; |
| case glslang::EOpMatrixSwizzle: |
| logger->missingFunctionality("matrix swizzle"); |
| return true; |
| case glslang::EOpLogicalOr: |
| case glslang::EOpLogicalAnd: |
| { |
| |
| // These may require short circuiting, but can sometimes be done as straight |
| // binary operations. The right operand must be short circuited if it has |
| // side effects, and should probably be if it is complex. |
| if (isTrivial(node->getRight()->getAsTyped())) |
| break; // handle below as a normal binary operation |
| // otherwise, we need to do dynamic short circuiting on the right operand |
| spv::Id result = createShortCircuit(node->getOp(), *node->getLeft()->getAsTyped(), |
| *node->getRight()->getAsTyped()); |
| builder.clearAccessChain(); |
| builder.setAccessChainRValue(result); |
| } |
| return false; |
| default: |
| break; |
| } |
| |
| // Assume generic binary op... |
| |
| // get right operand |
| builder.clearAccessChain(); |
| node->getLeft()->traverse(this); |
| spv::Id left = accessChainLoad(node->getLeft()->getType()); |
| |
| // get left operand |
| builder.clearAccessChain(); |
| node->getRight()->traverse(this); |
| spv::Id right = accessChainLoad(node->getRight()->getType()); |
| |
| // get result |
| OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()), |
| TranslateNoContractionDecoration(node->getType().getQualifier()), |
| TranslateNonUniformDecoration(node->getType().getQualifier()) }; |
| spv::Id result = createBinaryOperation(node->getOp(), decorations, |
| convertGlslangToSpvType(node->getType()), left, right, |
| node->getLeft()->getType().getBasicType()); |
| |
| builder.clearAccessChain(); |
| if (! result) { |
| logger->missingFunctionality("unknown glslang binary operation"); |
| return true; // pick up a child as the place-holder result |
| } else { |
| builder.setAccessChainRValue(result); |
| return false; |
| } |
| } |
| |
| spv::Id TGlslangToSpvTraverser::convertLoadedBoolInUniformToUint(const glslang::TType& type, |
| spv::Id nominalTypeId, |
| spv::Id loadedId) |
| { |
| if (builder.isScalarType(nominalTypeId)) { |
| // Conversion for bool |
| spv::Id boolType = builder.makeBoolType(); |
| if (nominalTypeId != boolType) |
| return builder.createBinOp(spv::OpINotEqual, boolType, loadedId, builder.makeUintConstant(0)); |
| } else if (builder.isVectorType(nominalTypeId)) { |
| // Conversion for bvec |
| int vecSize = builder.getNumTypeComponents(nominalTypeId); |
| spv::Id bvecType = builder.makeVectorType(builder.makeBoolType(), vecSize); |
| if (nominalTypeId != bvecType) |
| loadedId = builder.createBinOp(spv::OpINotEqual, bvecType, loadedId, |
| makeSmearedConstant(builder.makeUintConstant(0), vecSize)); |
| } else if (builder.isArrayType(nominalTypeId)) { |
| // Conversion for bool array |
| spv::Id boolArrayTypeId = convertGlslangToSpvType(type); |
| if (nominalTypeId != boolArrayTypeId) |
| { |
| // Use OpCopyLogical from SPIR-V 1.4 if available. |
| if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) |
| return builder.createUnaryOp(spv::OpCopyLogical, boolArrayTypeId, loadedId); |
| |
| glslang::TType glslangElementType(type, 0); |
| spv::Id elementNominalTypeId = builder.getContainedTypeId(nominalTypeId); |
| std::vector<spv::Id> constituents; |
| for (int index = 0; index < type.getOuterArraySize(); ++index) { |
| // get the element |
| spv::Id elementValue = builder.createCompositeExtract(loadedId, elementNominalTypeId, index); |
| |
| // recursively convert it |
| spv::Id elementConvertedValue = convertLoadedBoolInUniformToUint(glslangElementType, elementNominalTypeId, elementValue); |
| constituents.push_back(elementConvertedValue); |
| } |
| return builder.createCompositeConstruct(boolArrayTypeId, constituents); |
| } |
| } |
| |
| return loadedId; |
| } |
| |
| // Figure out what, if any, type changes are needed when accessing a specific built-in. |
| // Returns <the type SPIR-V requires for declarion, the type to translate to on use>. |
| // Also see comment for 'forceType', regarding tracking SPIR-V-required types. |
| std::pair<spv::Id, spv::Id> TGlslangToSpvTraverser::getForcedType(glslang::TBuiltInVariable glslangBuiltIn, |
| const glslang::TType& glslangType) |
| { |
| switch(glslangBuiltIn) |
| { |
| case glslang::EbvSubGroupEqMask: |
| case glslang::EbvSubGroupGeMask: |
| case glslang::EbvSubGroupGtMask: |
| case glslang::EbvSubGroupLeMask: |
| case glslang::EbvSubGroupLtMask: { |
| // these require changing a 64-bit scaler -> a vector of 32-bit components |
| if (glslangType.isVector()) |
| break; |
| spv::Id ivec4_type = builder.makeVectorType(builder.makeUintType(32), 4); |
| spv::Id uint64_type = builder.makeUintType(64); |
| std::pair<spv::Id, spv::Id> ret(ivec4_type, uint64_type); |
| return ret; |
| } |
| // There are no SPIR-V builtins defined for these and map onto original non-transposed |
| // builtins. During visitBinary we insert a transpose |
| case glslang::EbvWorldToObject3x4: |
| case glslang::EbvObjectToWorld3x4: { |
| spv::Id mat43 = builder.makeMatrixType(builder.makeFloatType(32), 4, 3); |
| spv::Id mat34 = builder.makeMatrixType(builder.makeFloatType(32), 3, 4); |
| std::pair<spv::Id, spv::Id> ret(mat43, mat34); |
| return ret; |
| } |
| default: |
| break; |
| } |
| |
| std::pair<spv::Id, spv::Id> ret(spv::NoType, spv::NoType); |
| return ret; |
| } |
| |
| // For an object previously identified (see getForcedType() and forceType) |
| // as needing type translations, do the translation needed for a load, turning |
| // an L-value into in R-value. |
| spv::Id TGlslangToSpvTraverser::translateForcedType(spv::Id object) |
| { |
| const auto forceIt = forceType.find(object); |
| if (forceIt == forceType.end()) |
| return object; |
| |
| spv::Id desiredTypeId = forceIt->second; |
| spv::Id objectTypeId = builder.getTypeId(object); |
| assert(builder.isPointerType(objectTypeId)); |
| objectTypeId = builder.getContainedTypeId(objectTypeId); |
| if (builder.isVectorType(objectTypeId) && |
| builder.getScalarTypeWidth(builder.getContainedTypeId(objectTypeId)) == 32) { |
| if (builder.getScalarTypeWidth(desiredTypeId) == 64) { |
| // handle 32-bit v.xy* -> 64-bit |
| builder.clearAccessChain(); |
| builder.setAccessChainLValue(object); |
| object = builder.accessChainLoad(spv::NoPrecision, spv::DecorationMax, spv::DecorationMax, objectTypeId); |
| std::vector<spv::Id> components; |
| components.push_back(builder.createCompositeExtract(object, builder.getContainedTypeId(objectTypeId), 0)); |
| components.push_back(builder.createCompositeExtract(object, builder.getContainedTypeId(objectTypeId), 1)); |
| |
| spv::Id vecType = builder.makeVectorType(builder.getContainedTypeId(objectTypeId), 2); |
| return builder.createUnaryOp(spv::OpBitcast, desiredTypeId, |
| builder.createCompositeConstruct(vecType, components)); |
| } else { |
| logger->missingFunctionality("forcing 32-bit vector type to non 64-bit scalar"); |
| } |
| } else if (builder.isMatrixType(objectTypeId)) { |
| // There are no SPIR-V builtins defined for 3x4 variants of ObjectToWorld/WorldToObject |
| // and we insert a transpose after loading the original non-transposed builtins |
| builder.clearAccessChain(); |
| builder.setAccessChainLValue(object); |
| object = builder.accessChainLoad(spv::NoPrecision, spv::DecorationMax, spv::DecorationMax, objectTypeId); |
| return builder.createUnaryOp(spv::OpTranspose, desiredTypeId, object); |
| |
| } else { |
| logger->missingFunctionality("forcing non 32-bit vector type"); |
| } |
| |
| return object; |
| } |
| |
| bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node) |
| { |
| builder.setLine(node->getLoc().line, node->getLoc().getFilename()); |
| |
| SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); |
| if (node->getType().getQualifier().isSpecConstant()) |
| spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); |
| |
| spv::Id result = spv::NoResult; |
| |
| // try texturing first |
| result = createImageTextureFunctionCall(node); |
| if (result != spv::NoResult) { |
| builder.clearAccessChain(); |
| builder.setAccessChainRValue(result); |
| |
| return false; // done with this node |
| } |
| |
| // Non-texturing. |
| |
| if (node->getOp() == glslang::EOpArrayLength) { |
| // Quite special; won't want to evaluate the operand. |
| |
| // Currently, the front-end does not allow .length() on an array until it is sized, |
| // except for the last block membeor of an SSBO. |
| // TODO: If this changes, link-time sized arrays might show up here, and need their |
| // size extracted. |
| |
| // Normal .length() would have been constant folded by the front-end. |
| // So, this has to be block.lastMember.length(). |
| // SPV wants "block" and member number as the operands, go get them. |
| |
| spv::Id length; |
| if (node->getOperand()->getType().isCoopMat()) { |
| spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); |
| |
| spv::Id typeId = convertGlslangToSpvType(node->getOperand()->getType()); |
| assert(builder.isCooperativeMatrixType(typeId)); |
| |
| length = builder.createCooperativeMatrixLength(typeId); |
| } else { |
| glslang::TIntermTyped* block = node->getOperand()->getAsBinaryNode()->getLeft(); |
| block->traverse(this); |
| unsigned int member = node->getOperand()->getAsBinaryNode()->getRight()->getAsConstantUnion() |
| ->getConstArray()[0].getUConst(); |
| length = builder.createArrayLength(builder.accessChainGetLValue(), member); |
| } |
| |
| // GLSL semantics say the result of .length() is an int, while SPIR-V says |
| // signedness must be 0. So, convert from SPIR-V unsigned back to GLSL's |
| // AST expectation of a signed result. |
| if (glslangIntermediate->getSource() == glslang::EShSourceGlsl) { |
| if (builder.isInSpecConstCodeGenMode()) { |
| length = builder.createBinOp(spv::OpIAdd, builder.makeIntType(32), length, builder.makeIntConstant(0)); |
| } else { |
| length = builder.createUnaryOp(spv::OpBitcast, builder.makeIntType(32), length); |
| } |
| } |
| |
| builder.clearAccessChain(); |
| builder.setAccessChainRValue(length); |
| |
| return false; |
| } |
| |
| // Force variable declaration - Debug Mode Only |
| if (node->getOp() == glslang::EOpDeclare) { |
| builder.clearAccessChain(); |
| node->getOperand()->traverse(this); |
| builder.clearAccessChain(); |
| return false; |
| } |
| |
| // Start by evaluating the operand |
| |
| // Does it need a swizzle inversion? If so, evaluation is inverted; |
| // operate first on the swizzle base, then apply the swizzle. |
| spv::Id invertedType = spv::NoType; |
| auto resultType = [&invertedType, &node, this](){ return invertedType != spv::NoType ? |
| invertedType : convertGlslangToSpvType(node->getType()); }; |
| if (node->getOp() == glslang::EOpInterpolateAtCentroid) |
| invertedType = getInvertedSwizzleType(*node->getOperand()); |
| |
| builder.clearAccessChain(); |
| TIntermNode *operandNode; |
| if (invertedType != spv::NoType) |
| operandNode = node->getOperand()->getAsBinaryNode()->getLeft(); |
| else |
| operandNode = node->getOperand(); |
| |
| operandNode->traverse(this); |
| |
| spv::Id operand = spv::NoResult; |
| |
| spv::Builder::AccessChain::CoherentFlags lvalueCoherentFlags; |
| |
| #ifndef GLSLANG_WEB |
| if (node->getOp() == glslang::EOpAtomicCounterIncrement || |
| node->getOp() == glslang::EOpAtomicCounterDecrement || |
| node->getOp() == glslang::EOpAtomicCounter || |
| (node->getOp() == glslang::EOpInterpolateAtCentroid && |
| glslangIntermediate->getSource() != glslang::EShSourceHlsl) || |
| node->getOp() == glslang::EOpRayQueryProceed || |
| node->getOp() == glslang::EOpRayQueryGetRayTMin || |
| node->getOp() == glslang::EOpRayQueryGetRayFlags || |
| node->getOp() == glslang::EOpRayQueryGetWorldRayOrigin || |
| node->getOp() == glslang::EOpRayQueryGetWorldRayDirection || |
| node->getOp() == glslang::EOpRayQueryGetIntersectionCandidateAABBOpaque || |
| node->getOp() == glslang::EOpRayQueryTerminate || |
| node->getOp() == glslang::EOpRayQueryConfirmIntersection || |
| (node->getOp() == glslang::EOpSpirvInst && operandNode->getAsTyped()->getQualifier().isSpirvByReference())) { |
| operand = builder.accessChainGetLValue(); // Special case l-value operands |
| lvalueCoherentFlags = builder.getAccessChain().coherentFlags; |
| lvalueCoherentFlags |= TranslateCoherent(operandNode->getAsTyped()->getType()); |
| } else if (operandNode->getAsTyped()->getQualifier().isSpirvLiteral()) { |
| // Will be translated to a literal value, make a placeholder here |
| operand = spv::NoResult; |
| } else |
| #endif |
| { |
| operand = accessChainLoad(node->getOperand()->getType()); |
| } |
| |
| OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()), |
| TranslateNoContractionDecoration(node->getType().getQualifier()), |
| TranslateNonUniformDecoration(node->getType().getQualifier()) }; |
| |
| // it could be a conversion |
| if (! result) |
| result = createConversion(node->getOp(), decorations, resultType(), operand, |
| node->getOperand()->getBasicType()); |
| |
| // if not, then possibly an operation |
| if (! result) |
| result = createUnaryOperation(node->getOp(), decorations, resultType(), operand, |
| node->getOperand()->getBasicType(), lvalueCoherentFlags); |
| |
| #ifndef GLSLANG_WEB |
| // it could be attached to a SPIR-V intruction |
| if (!result) { |
| if (node->getOp() == glslang::EOpSpirvInst) { |
| const auto& spirvInst = node->getSpirvInstruction(); |
| if (spirvInst.set == "") { |
| spv::IdImmediate idImmOp = {true, operand}; |
| if (operandNode->getAsTyped()->getQualifier().isSpirvLiteral()) { |
| // Translate the constant to a literal value |
| std::vector<unsigned> literals; |
|