| // |
| // Copyright (C) 2014-2016 LunarG, Inc. |
| // Copyright (C) 2015-2018 Google, Inc. |
| // Copyright (C) 2017 ARM Limited. |
| // |
| // 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" |
| } |
| |
| // Glslang includes |
| #include "../glslang/MachineIndependent/localintermediate.h" |
| #include "../glslang/MachineIndependent/SymbolTable.h" |
| #include "../glslang/Include/Common.h" |
| #include "../glslang/Include/revision.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::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 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); |
| 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 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(spv::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; |
| std::unordered_map<const char*, spv::Id> extBuiltinMap; |
| |
| std::unordered_map<int, spv::Id> symbolValues; |
| std::unordered_set<int> 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<const glslang::TTypeList*, std::vector<int> > memberRemapper; |
| 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; |
| }; |
| |
| // |
| // 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; |
| #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) |
| { |
| switch (stage) { |
| case EShLangVertex: return spv::ExecutionModelVertex; |
| case EShLangFragment: return spv::ExecutionModelFragment; |
| #ifndef GLSLANG_WEB |
| case EShLangCompute: return spv::ExecutionModelGLCompute; |
| case EShLangTessControl: return spv::ExecutionModelTessellationControl; |
| case EShLangTessEvaluation: return spv::ExecutionModelTessellationEvaluation; |
| case EShLangGeometry: return spv::ExecutionModelGeometry; |
| case EShLangRayGenNV: return spv::ExecutionModelRayGenerationNV; |
| case EShLangIntersectNV: return spv::ExecutionModelIntersectionNV; |
| case EShLangAnyHitNV: return spv::ExecutionModelAnyHitNV; |
| case EShLangClosestHitNV: return spv::ExecutionModelClosestHitNV; |
| case EShLangMissNV: return spv::ExecutionModelMissNV; |
| case EShLangCallableNV: return spv::ExecutionModelCallableNV; |
| case EShLangTaskNV: return spv::ExecutionModelTaskNV; |
| case EShLangMeshNV: return 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; |
| #ifndef GLSLANG_WEB |
| case glslang::EvqPayloadNV: return spv::DecorationBlock; |
| case glslang::EvqPayloadInNV: return spv::DecorationBlock; |
| case glslang::EvqHitAttrNV: return spv::DecorationBlock; |
| case glslang::EvqCallableDataNV: return spv::DecorationBlock; |
| case glslang::EvqCallableDataInNV: 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) |
| { |
| #ifndef GLSLANG_WEB |
| if (!useVulkanMemoryModel) { |
| if (qualifier.coherent) |
| memory.push_back(spv::DecorationCoherent); |
| if (qualifier.volatil) { |
| memory.push_back(spv::DecorationVolatile); |
| memory.push_back(spv::DecorationCoherent); |
| } |
| } |
| if (qualifier.restrict) |
| memory.push_back(spv::DecorationRestrict); |
| if (qualifier.isReadOnly()) |
| memory.push_back(spv::DecorationNonWritable); |
| if (qualifier.isWriteOnly()) |
| memory.push_back(spv::DecorationNonReadable); |
| #endif |
| } |
| |
| // 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::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::EvqPayloadNV: |
| case glslang::EvqPayloadInNV: |
| case glslang::EvqHitAttrNV: |
| case glslang::EvqCallableDataNV: |
| case glslang::EvqCallableDataInNV: |
| 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; |
| } |
| |
| 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.volatil || |
| coherentFlags.coherent || |
| coherentFlags.devicecoherent || |
| coherentFlags.queuefamilycoherent || |
| coherentFlags.workgroupcoherent || |
| coherentFlags.subgroupcoherent) { |
| 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.coherent || |
| coherentFlags.devicecoherent || |
| coherentFlags.queuefamilycoherent || |
| coherentFlags.workgroupcoherent || |
| coherentFlags.subgroupcoherent) { |
| 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.volatil = type.getQualifier().volatil; |
| // *coherent variables are implicitly nonprivate in GLSL |
| flags.nonprivate = type.getQualifier().nonprivate || |
| flags.subgroupcoherent || |
| flags.workgroupcoherent || |
| flags.queuefamilycoherent || |
| flags.devicecoherent || |
| flags.coherent || |
| flags.volatil; |
| flags.isImage = type.getBasicType() == glslang::EbtSampler; |
| #endif |
| 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; |
| } |
| 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; |
| |
| #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: |
| builder.addCapability(spv::CapabilityMultiViewport); |
| if (glslangIntermediate->getStage() == EShLangVertex || |
| glslangIntermediate->getStage() == EShLangTessControl || |
| glslangIntermediate->getStage() == EShLangTessEvaluation) { |
| |
| builder.addIncorporatedExtension(spv::E_SPV_EXT_shader_viewport_index_layer, spv::Spv_1_5); |
| builder.addCapability(spv::CapabilityShaderViewportIndexLayerEXT); |
| } |
| 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() == EShLangMeshNV) { |
| return spv::BuiltInLayer; |
| } |
| builder.addCapability(spv::CapabilityGeometry); |
| if (glslangIntermediate->getStage() == EShLangVertex || |
| glslangIntermediate->getStage() == EShLangTessControl || |
| glslangIntermediate->getStage() == EShLangTessEvaluation) { |
| |
| builder.addIncorporatedExtension(spv::E_SPV_EXT_shader_viewport_index_layer, spv::Spv_1_5); |
| builder.addCapability(spv::CapabilityShaderViewportIndexLayerEXT); |
| } |
| 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::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::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; |
| |
| 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::EbvLaunchIdNV: |
| return spv::BuiltInLaunchIdNV; |
| case glslang::EbvLaunchSizeNV: |
| return spv::BuiltInLaunchSizeNV; |
| case glslang::EbvWorldRayOriginNV: |
| return spv::BuiltInWorldRayOriginNV; |
| case glslang::EbvWorldRayDirectionNV: |
| return spv::BuiltInWorldRayDirectionNV; |
| case glslang::EbvObjectRayOriginNV: |
| return spv::BuiltInObjectRayOriginNV; |
| case glslang::EbvObjectRayDirectionNV: |
| return spv::BuiltInObjectRayDirectionNV; |
| case glslang::EbvRayTminNV: |
| return spv::BuiltInRayTminNV; |
| case glslang::EbvRayTmaxNV: |
| return spv::BuiltInRayTmaxNV; |
| case glslang::EbvInstanceCustomIndexNV: |
| return spv::BuiltInInstanceCustomIndexNV; |
| case glslang::EbvHitTNV: |
| return spv::BuiltInHitTNV; |
| case glslang::EbvHitKindNV: |
| return spv::BuiltInHitKindNV; |
| case glslang::EbvObjectToWorldNV: |
| return spv::BuiltInObjectToWorldNV; |
| case glslang::EbvWorldToObjectNV: |
| return spv::BuiltInWorldToObjectNV; |
| case glslang::EbvIncomingRayFlagsNV: |
| return spv::BuiltInIncomingRayFlagsNV; |
| |
| // 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; |
| |
| // 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; |
| |
| // 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; |
| |
| 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; |
| 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.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().isShaderRecordNV()) { |
| return spv::StorageClassShaderRecordBufferNV; |
| } |
| |
| 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; |
| } |
| |
| switch (type.getQualifier().storage) { |
| case glslang::EvqGlobal: return spv::StorageClassPrivate; |
| case glslang::EvqConstReadOnly: return spv::StorageClassFunction; |
| case glslang::EvqTemporary: return spv::StorageClassFunction; |
| #ifndef GLSLANG_WEB |
| case glslang::EvqShared: return spv::StorageClassWorkgroup; |
| case glslang::EvqPayloadNV: return spv::StorageClassRayPayloadNV; |
| case glslang::EvqPayloadInNV: return spv::StorageClassIncomingRayPayloadNV; |
| case glslang::EvqHitAttrNV: return spv::StorageClassHitAttributeNV; |
| case glslang::EvqCallableDataNV: return spv::StorageClassCallableDataNV; |
| case glslang::EvqCallableDataInNV: return spv::StorageClassIncomingCallableDataNV; |
| #endif |
| default: |
| assert(0); |
| break; |
| } |
| |
| return spv::StorageClassFunction; |
| } |
| |
| // 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().isShaderRecordNV() && |
| ! 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) |
| 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.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.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 |
| } |
| |
| 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()) |
| { |
| spv::ExecutionModel executionModel = TranslateExecutionModel(glslangIntermediate->getStage()); |
| |
| 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); |
| } |
| 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); |
| } |
| |
| 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->getPostDepthCoverage()) { |
| builder.addCapability(spv::CapabilitySampleMaskPostDepthCoverage); |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModePostDepthCoverage); |
| builder.addExtension(spv::E_SPV_KHR_post_depth_coverage); |
| } |
| |
| if (glslangIntermediate->getDepth() != glslang::EldUnchanged && glslangIntermediate->isDepthReplacing()) |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeDepthReplacing); |
| |
| #ifndef GLSLANG_WEB |
| switch(glslangIntermediate->getDepth()) { |
| case glslang::EldGreater: mode = spv::ExecutionModeDepthGreater; break; |
| case glslang::EldLess: mode = spv::ExecutionModeDepthLess; 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; |
| |
| #ifndef GLSLANG_WEB |
| case EShLangCompute: |
| builder.addCapability(spv::CapabilityShader); |
| 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; |
| 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 EShLangRayGenNV: |
| case EShLangIntersectNV: |
| case EShLangAnyHitNV: |
| case EShLangClosestHitNV: |
| case EShLangMissNV: |
| case EShLangCallableNV: |
| builder.addCapability(spv::CapabilityRayTracingNV); |
| builder.addExtension("SPV_NV_ray_tracing"); |
| break; |
| case EShLangTaskNV: |
| case EShLangMeshNV: |
| builder.addCapability(spv::CapabilityMeshShadingNV); |
| builder.addExtension(spv::E_SPV_NV_mesh_shader); |
| builder.addExecutionMode(shaderEntry, spv::ExecutionModeLocalSize, glslangIntermediate->getLocalSize(0), |
| glslangIntermediate->getLocalSize(1), |
| glslangIntermediate->getLocalSize(2)); |
| if (glslangIntermediate->getStage() == EShLangMeshNV) { |
| 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; |
| } |
| } |
| |
| // 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); |
| |
| #ifndef GLSLANG_WEB |
| // Add capabilities, extensions, remove unneeded decorations, etc., |
| // based on the resulting SPIR-V. |
| builder.postProcess(); |
| #endif |
| } |
| |
| // 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().getQualifier().isSpecConstant()) |
| spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); |
| |
| // 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 (builder.isPointer(id)) { |
| // 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 && sc != spv::StorageClassFunction) || |
| (sc == spv::StorageClassInput || sc == spv::StorageClassOutput)) { |
| iOSet.insert(id); |
| } |
| } |
| |
| // If the SPIR-V type is required to be different than the AST type, |
| // 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()); |
| |
| 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 |
| OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()), |
| TranslateNoContractionDecoration(node->getType().getQualifier()), |
| TranslateNonUniformDecoration(node->getType().getQualifier()) }; |
| 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) { |
| // 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()), |
| TranslateCoherent(node->getLeft()->getType()), |
| 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. |
| std::vector<int>& remapper = memberRemapper[node->getLeft()->getType().getStruct()]; |
| assert(remapper.size() > 0); |
| spvIndex = remapper[glslangIndex]; |
| } |
| |
| // normal case for indexing array or structure or block |
| builder.accessChainPush(builder.makeIntConstant(spvIndex), TranslateCoherent(node->getLeft()->getType()), 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); |
| |
| if (! node->getLeft()->getType().isArray() && node->getLeft()->getType().isVector()) { |
| int dummySize; |
| builder.accessChainPushComponent(index, convertGlslangToSpvType(node->getLeft()->getType()), |
| TranslateCoherent(node->getLeft()->getType()), |
| glslangIntermediate->getBaseAlignmentScalar(node->getLeft()->getType(), dummySize)); |
| } else |
| builder.accessChainPush(index, TranslateCoherent(node->getLeft()->getType()), 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; |
| } |
| } |
| |
| // 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(spv::BuiltIn builtIn, |
| const glslang::TType& glslangType) |
| { |
| switch(builtIn) |
| { |
| case spv::BuiltInSubgroupEqMask: |
| case spv::BuiltInSubgroupGeMask: |
| case spv::BuiltInSubgroupGtMask: |
| case spv::BuiltInSubgroupLeMask: |
| case spv::BuiltInSubgroupLtMask: { |
| // these require changing a 64-bit scaler -> a vector of 32-bit components |
| if (glslangType.isVector()) |
| break; |
| std::pair<spv::Id, spv::Id> ret(builder.makeVectorType(builder.makeUintType(32), 4), |
| builder.makeUintType(64)); |
| 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, 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 { |
| 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; |
| } |
| |
| // 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) { |
| operand = builder.accessChainGetLValue(); // Special case l-value operands |
| lvalueCoherentFlags = builder.getAccessChain().coherentFlags; |
| lvalueCoherentFlags |= TranslateCoherent(operandNode->getAsTyped()->getType()); |
| } 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); |
| |
| if (result) { |
| if (invertedType) { |
| result = createInvertedSwizzle(decorations.precision, *node->getOperand(), result); |
| decorations.addNonUniform(builder, result); |
| } |
| |
| builder.clearAccessChain(); |
| builder.setAccessChainRValue(result); |
| |
| return false; // done with this node |
| } |
| |
| // it must be a special case, check... |
| switch (node->getOp()) { |
| case glslang::EOpPostIncrement: |
| case glslang::EOpPostDecrement: |
| case glslang::EOpPreIncrement: |
| case glslang::EOpPreDecrement: |
| { |
| // we need the integer value "1" or the floating point "1.0" to add/subtract |
| spv::Id one = 0; |
| if (node->getBasicType() == glslang::EbtFloat) |
| one = builder.makeFloatConstant(1.0F); |
| #ifndef GLSLANG_WEB |
| else if (node->getBasicType() == glslang::EbtDouble) |
| one = builder.makeDoubleConstant(1.0); |
| else if (node->getBasicType() == glslang::EbtFloat16) |
| one = builder.makeFloat16Constant(1.0F); |
| else if (node->getBasicType() == glslang::EbtInt8 || node->getBasicType() == glslang::EbtUint8) |
| one = builder.makeInt8Constant(1); |
| else if (node->getBasicType() == glslang::EbtInt16 || node->getBasicType() == glslang::EbtUint16) |
| one = builder.makeInt16Constant(1); |
| else if (node->getBasicType() == glslang::EbtInt64 || node->getBasicType() == glslang::EbtUint64) |
| one = builder.makeInt64Constant(1); |
| #endif |
| else |
| one = builder.makeIntConstant(1); |
| glslang::TOperator op; |
| if (node->getOp() == glslang::EOpPreIncrement || |
| node->getOp() == glslang::EOpPostIncrement) |
| op = glslang::EOpAdd; |
| else |
| op = glslang::EOpSub; |
| |
| spv::Id result = createBinaryOperation(op, decorations, |
| convertGlslangToSpvType(node->getType()), operand, one, |
| node->getType().getBasicType()); |
| assert(result != spv::NoResult); |
| |
| // The result of operation is always stored, but conditionally the |
| // consumed result. The consumed result is always an r-value. |
| builder.accessChainStore(result); |
| builder.clearAccessChain(); |
| if (node->getOp() == glslang::EOpPreIncrement || |
| node->getOp() == glslang::EOpPreDecrement) |
| builder.setAccessChainRValue(result); |
| else |
| builder.setAccessChainRValue(operand); |
| } |
| |
| return false; |
| |
| #ifndef GLSLANG_WEB |
| case glslang::EOpEmitStreamVertex: |
| builder.createNoResultOp(spv::OpEmitStreamVertex, operand); |
| return false; |
| case glslang::EOpEndStreamPrimitive: |
| builder.createNoResultOp(spv::OpEndStreamPrimitive, operand); |
| return false; |
| #endif |
| |
| default: |
| logger->missingFunctionality("unknown glslang unary"); |
| return true; // pick up operand as placeholder result |
| } |
| } |
| |
| // Construct a composite object, recursively copying members if their types don't match |
| spv::Id TGlslangToSpvTraverser::createCompositeConstruct(spv::Id resultTypeId, std::vector<spv::Id> constituents) |
| { |
| for (int c = 0; c < (int)constituents.size(); ++c) { |
| spv::Id& constituent = constituents[c]; |
| spv::Id lType = builder.getContainedTypeId(resultTypeId, c); |
| spv::Id rType = builder.getTypeId(constituent); |
| if (lType != rType) { |
| if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) { |
| constituent = builder.createUnaryOp(spv::OpCopyLogical, lType, constituent); |
| } else if (builder.isStructType(rType)) { |
| std::vector<spv::Id> rTypeConstituents; |
| int numrTypeConstituents = builder.getNumTypeConstituents(rType); |
| for (int i = 0; i < numrTypeConstituents; ++i) { |
| rTypeConstituents.push_back(builder.createCompositeExtract(constituent, builder.getContainedTypeId(rType, i), i)); |
| } |
| constituents[c] = createCompositeConstruct(lType, rTypeConstituents); |
| } else { |
| assert(builder.isArrayType(rType)); |
| std::vector<spv::Id> rTypeConstituents; |
| int numrTypeConstituents = builder.getNumTypeConstituents(rType); |
| |
| spv::Id elementRType = builder.getContainedTypeId(rType); |
| for (int i = 0; i < numrTypeConstituents; ++i) { |
| rTypeConstituents.push_back(builder.createCompositeExtract(constituent, elementRType, i)); |
| } |
| constituents[c] = createCompositeConstruct(lType, rTypeConstituents); |
| } |
| } |
| } |
| return builder.createCompositeConstruct(resultTypeId, constituents); |
| } |
| |
| bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TIntermAggregate* node) |
| { |
| SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); |
| if (node->getType().getQualifier().isSpecConstant()) |
| spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); |
| |
| spv::Id result = spv::NoResult; |
| spv::Id invertedType = spv::NoType; // to use to override the natural type of the node |
| auto resultType = [&invertedType, &node, this](){ return invertedType != spv::NoType ? invertedType : convertGlslangToSpvType(node->getType()); }; |
| |
| // try texturing |
| result = createImageTextureFunctionCall(node); |
| if (result != spv::NoResult) { |
| builder.clearAccessChain(); |
| builder.setAccessChainRValue(result); |
| |
| return false; |
| } |
| #ifndef GLSLANG_WEB |
| else if (node->getOp() == glslang::EOpImageStore || |
| node->getOp() == glslang::EOpImageStoreLod || |
| node->getOp() == glslang::EOpImageAtomicStore) { |
| // "imageStore" is a special case, which has no result |
| return false; |
| } |
| #endif |
| |
| glslang::TOperator binOp = glslang::EOpNull; |
| bool reduceComparison = true; |
| bool isMatrix = false; |
| bool noReturnValue = false; |
| bool atomic = false; |
| |
| spv::Builder::AccessChain::CoherentFlags lvalueCoherentFlags; |
| |
| assert(node->getOp()); |
| |
| spv::Decoration precision = TranslatePrecisionDecoration(node->getOperationPrecision()); |
| |
| switch (node->getOp()) { |
| case glslang::EOpSequence: |
| { |
| if (preVisit) |
| ++sequenceDepth; |
| else |
| --sequenceDepth; |
| |
| if (sequenceDepth == 1) { |
| // If this is the parent node of all the functions, we want to see them |
| // early, so all call points have actual SPIR-V functions to reference. |
| // In all cases, still let the traverser visit the children for us. |
| makeFunctions(node->getAsAggregate()->getSequence()); |
| |
| // Also, we want all globals initializers to go into the beginning of the entry point, before |
| // anything else gets there, so visit out of order, doing them all now. |
| makeGlobalInitializers(node->getAsAggregate()->getSequence()); |
| |
| // Initializers are done, don't want to visit again, but functions and link objects need to be processed, |
| // so do them manually. |
| visitFunctions(node->getAsAggregate()->getSequence()); |
| |
| return false; |
| } |
| |
| return true; |
| } |
| case glslang::EOpLinkerObjects: |
| { |
| if (visit == glslang::EvPreVisit) |
| linkageOnly = true; |
| else |
| linkageOnly = false; |
| |
| return true; |
| } |
| case glslang::EOpComma: |
| { |
| // processing from left to right naturally leaves the right-most |
| // lying around in the access chain |
| glslang::TIntermSequence& glslangOperands = node->getSequence(); |
| for (int i = 0; i < (int)glslangOperands.size(); ++i) |
| glslangOperands[i]->traverse(this); |
| |
| return false; |
| } |
| case glslang::EOpFunction: |
| if (visit == glslang::EvPreVisit) { |
| if (isShaderEntryPoint(node)) { |
| inEntryPoint = true; |
| builder.setBuildPoint(shaderEntry->getLastBlock()); |
| currentFunction = shaderEntry; |
| } else { |
| handleFunctionEntry(node); |
| } |
| } else { |
| if (inEntryPoint) |
| entryPointTerminated = true; |
| builder.leaveFunction(); |
| inEntryPoint = false; |
| } |
| |
| return true; |
| case glslang::EOpParameters: |
| // Parameters will have been consumed by EOpFunction processing, but not |
| // the body, so we still visited the function node's children, making this |
| // child redundant. |
| return false; |
| case glslang::EOpFunctionCall: |
| { |
| builder.setLine(node->getLoc().line, node->getLoc().getFilename()); |
| if (node->isUserDefined()) |
| result = handleUserFunctionCall(node); |
| // assert(result); // this can happen for bad shaders because the call graph completeness checking is not yet done |
| if (result) { |
| builder.clearAccessChain(); |
| builder.setAccessChainRValue(result); |
| } else |
| logger->missingFunctionality("missing user function; linker needs to catch that"); |
| |
| return false; |
| } |
| case glslang::EOpConstructMat2x2: |
| case glslang::EOpConstructMat2x3: |
| case glslang::EOpConstructMat2x4: |
| case glslang::EOpConstructMat3x2: |
| case glslang::EOpConstructMat3x3: |
| case glslang::EOpConstructMat3x4: |
| case glslang::EOpConstructMat4x2: |
| case glslang::EOpConstructMat4x3: |
| case glslang::EOpConstructMat4x4: |
| case glslang::EOpConstructDMat2x2: |
| case glslang::EOpConstructDMat2x3: |
| case glslang::EOpConstructDMat2x4: |
| case glslang::EOpConstructDMat3x2: |
| case glslang::EOpConstructDMat3x3: |
| case glslang::EOpConstructDMat3x4: |
| case glslang::EOpConstructDMat4x2: |
| case glslang::EOpConstructDMat4x3: |
| case glslang::EOpConstructDMat4x4: |
| case glslang::EOpConstructIMat2x2: |
| case glslang::EOpConstructIMat2x3: |
| case glslang::EOpConstructIMat2x4: |
| case glslang::EOpConstructIMat3x2: |
| case glslang::EOpConstructIMat3x3: |
| case glslang::EOpConstructIMat3x4: |
| case glslang::EOpConstructIMat4x2: |
| case glslang::EOpConstructIMat4x3: |
| case glslang::EOpConstructIMat4x4: |
| case glslang::EOpConstructUMat2x2: |
| case glslang::EOpConstructUMat2x3: |
| case glslang::EOpConstructUMat2x4: |
| case glslang::EOpConstructUMat3x2: |
| case glslang::EOpConstructUMat3x3: |
| case glslang::EOpConstructUMat3x4: |
| case glslang::EOpConstructUMat4x2: |
| case glslang::EOpConstructUMat4x3: |
| case glslang::EOpConstructUMat4x4: |
| case glslang::EOpConstructBMat2x2: |
| case glslang::EOpConstructBMat2x3: |
| case glslang::EOpConstructBMat2x4: |
| case glslang::EOpConstructBMat3x2: |
| case glslang::EOpConstructBMat3x3: |
| case glslang::EOpConstructBMat3x4: |
| case glslang::EOpConstructBMat4x2: |
| case glslang::EOpConstructBMat4x3: |
| case glslang::EOpConstructBMat4x4: |
| case glslang::EOpConstructF16Mat2x2: |
| case glslang::EOpConstructF16Mat2x3: |
| case glslang::EOpConstructF16Mat2x4: |
| case glslang::EOpConstructF16Mat3x2: |
| case glslang::EOpConstructF16Mat3x3: |
| case glslang::EOpConstructF16Mat3x4: |
| case glslang::EOpConstructF16Mat4x2: |
| case glslang::EOpConstructF16Mat4x3: |
| case glslang::EOpConstructF16Mat4x4: |
| isMatrix = true; |
| // fall through |
| case glslang::EOpConstructFloat: |
| case glslang::EOpConstructVec2: |
| case glslang::EOpConstructVec3: |
| case glslang::EOpConstructVec4: |
| case glslang::EOpConstructDouble: |
| case glslang::EOpConstructDVec2: |
| case glslang::EOpConstructDVec3: |
| case glslang::EOpConstructDVec4: |
| case glslang::EOpConstructFloat16: |
| case glslang::EOpConstructF16Vec2: |
| case glslang::EOpConstructF16Vec3: |
| case glslang::EOpConstructF16Vec4: |
| case glslang::EOpConstructBool: |
| case glslang::EOpConstructBVec2: |
| case glslang::EOpConstructBVec3: |
| case glslang::EOpConstructBVec4: |
| case glslang::EOpConstructInt8: |
| case glslang::EOpConstructI8Vec2: |
| case glslang::EOpConstructI8Vec3: |
| case glslang::EOpConstructI8Vec4: |
| case glslang::EOpConstructUint8: |
| case glslang::EOpConstructU8Vec2: |
| case glslang::EOpConstructU8Vec3: |
| case glslang::EOpConstructU8Vec4: |
| case glslang::EOpConstructInt16: |
| case glslang::EOpConstructI16Vec2: |
| case glslang::EOpConstructI16Vec3: |
| case glslang::EOpConstructI16Vec4: |
| case glslang::EOpConstructUint16: |
| case glslang::EOpConstructU16Vec2: |
| case glslang::EOpConstructU16Vec3: |
| case glslang::EOpConstructU16Vec4: |
| case glslang::EOpConstructInt: |
| case glslang::EOpConstructIVec2: |
| case glslang::EOpConstructIVec3: |
| case glslang::EOpConstructIVec4: |
| case glslang::EOpConstructUint: |
| case glslang::EOpConstructUVec2: |
| case glslang::EOpConstructUVec3: |
| case glslang::EOpConstructUVec4: |
| case glslang::EOpConstructInt64: |
| case glslang::EOpConstructI64Vec2: |
| case glslang::EOpConstructI64Vec3: |
| case glslang::EOpConstructI64Vec4: |
| case glslang::EOpConstructUint64: |
| case glslang::EOpConstructU64Vec2: |
| case glslang::EOpConstructU64Vec3: |
| case glslang::EOpConstructU64Vec4: |
| case glslang::EOpConstructStruct: |
| case glslang::EOpConstructTextureSampler: |
| case glslang::EOpConstructReference: |
| case glslang::EOpConstructCooperativeMatrix: |
| { |
| builder.setLine(node->getLoc().line, node->getLoc().getFilename()); |
| std::vector<spv::Id> arguments; |
| translateArguments(*node, arguments, lvalueCoherentFlags); |
| spv::Id constructed; |
| if (node->getOp() == glslang::EOpConstructTextureSampler) |
| constructed = builder.createOp(spv::OpSampledImage, resultType(), arguments); |
| else if (node->getOp() == glslang::EOpConstructStruct || |
| node->getOp() == glslang::EOpConstructCooperativeMatrix || |
| node->getType().isArray()) { |
| std::vector<spv::Id> constituents; |
| for (int c = 0; c < (int)arguments.size(); ++c) |
| constituents.push_back(arguments[c]); |
| constructed = createCompositeConstruct(resultType(), constituents); |
| } else if (isMatrix) |
| constructed = builder.createMatrixConstructor(precision, arguments, resultType()); |
| else |
| constructed = builder.createConstructor(precision, arguments, resultType()); |
| |
| builder.clearAccessChain(); |
| builder.setAccessChainRValue(constructed); |
| |
| return false; |
| } |
| |
| // These six are component-wise compares with component-wise results. |
| // Forward on to createBinaryOperation(), requesting a vector result. |
| case glslang::EOpLessThan: |
| case glslang::EOpGreaterThan: |
| case glslang::EOpLessThanEqual: |
| case glslang::EOpGreaterThanEqual: |
| case glslang::EOpVectorEqual: |
| case glslang::EOpVectorNotEqual: |
| { |
| // Map the operation to a binary |
| binOp = node->getOp(); |
| reduceComparison = false; |
| switch (node->getOp()) { |
| case glslang::EOpVectorEqual: binOp = glslang::EOpVectorEqual; break; |
| case glslang::EOpVectorNotEqual: binOp = glslang::EOpVectorNotEqual; break; |
| default: binOp = node->getOp(); break; |
| } |
| |
| break; |
| } |
| case glslang::EOpMul: |
| // component-wise matrix multiply |
| binOp = glslang::EOpMul; |
| break; |
| case glslang::EOpOuterProduct: |
| // two vectors multiplied to make a matrix |
| binOp = glslang::EOpOuterProduct; |
| break; |
| case glslang::EOpDot: |
| { |
| // for scalar dot product, use multiply |
| glslang::TIntermSequence& glslangOperands = node->getSequence(); |
| if (glslangOperands[0]->getAsTyped()->getVectorSize() == 1) |
| binOp = glslang::EOpMul; |
| break; |
| } |
| case glslang::EOpMod: |
| // when an aggregate, this is the floating-point mod built-in function, |
| // which can be emitted by the one in createBinaryOperation() |
| binOp = glslang::EOpMod; |
| break; |
| |
| #ifndef GLSLANG_WEB |
| case glslang::EOpEmitVertex: |
| case glslang::EOpEndPrimitive: |
| case glslang::EOpBarrier: |
| case glslang::EOpMemoryBarrier: |
| case glslang::EOpMemoryBarrierAtomicCounter: |
| case glslang::EOpMemoryBarrierBuffer: |
| case glslang::EOpMemoryBarrierImage: |
| case glslang::EOpMemoryBarrierShared: |
| case glslang::EOpGroupMemoryBarrier: |
| case glslang::EOpDeviceMemoryBarrier: |
| case glslang::EOpAllMemoryBarrierWithGroupSync: |
| case glslang::EOpDeviceMemoryBarrierWithGroupSync: |
| case glslang::EOpWorkgroupMemoryBarrier: |
| case glslang::EOpWorkgroupMemoryBarrierWithGroupSync: |
| case glslang::EOpSubgroupBarrier: |
| case glslang::EOpSubgroupMemoryBarrier: |
| case glslang::EOpSubgroupMemoryBarrierBuffer: |
| case glslang::EOpSubgroupMemoryBarrierImage: |
| case glslang::EOpSubgroupMemoryBarrierShared: |
| noReturnValue = true; |
| // These all have 0 operands and will naturally finish up in the code below for 0 operands |
| break; |
| |
| case glslang::EOpAtomicStore: |
| noReturnValue = true; |
| // fallthrough |
| case glslang::EOpAtomicLoad: |
| case glslang::EOpAtomicAdd: |
| case glslang::EOpAtomicMin: |
| case glslang::EOpAtomicMax: |
| case glslang::EOpAtomicAnd: |
| case glslang::EOpAtomicOr: |
| case glslang::EOpAtomicXor: |
| case glslang::EOpAtomicExchange: |
| case glslang::EOpAtomicCompSwap: |
| atomic = true; |
| break; |
| |
| case glslang::EOpAtomicCounterAdd: |
| case glslang::EOpAtomicCounterSubtract: |
| case glslang::EOpAtomicCounterMin: |
| case glslang::EOpAtomicCounterMax: |
| case glslang::EOpAtomicCounterAnd: |
| case glslang::EOpAtomicCounterOr: |
| case glslang::EOpAtomicCounterXor: |
| case glslang::EOpAtomicCounterExchange: |
| case glslang::EOpAtomicCounterCompSwap: |
| builder.addExtension("SPV_KHR_shader_atomic_counter_ops"); |
| builder.addCapability(spv::CapabilityAtomicStorageOps); |
| atomic = true; |
| break; |
| |
| case glslang::EOpIgnoreIntersectionNV: |
| case glslang::EOpTerminateRayNV: |
| case glslang::EOpTraceNV: |
| case glslang::EOpExecuteCallableNV: |
| case glslang::EOpWritePackedPrimitiveIndices4x8NV: |
| noReturnValue = true; |
| break; |
| case glslang::EOpCooperativeMatrixLoad: |
| case glslang::EOpCooperativeMatrixStore: |
| noReturnValue = true; |
| break; |
| case glslang::EOpBeginInvocationInterlock: |
| case glslang::EOpEndInvocationInterlock: |
| builder.addExtension(spv::E_SPV_EXT_fragment_shader_interlock); |
| noReturnValue = true; |
| break; |
| #endif |
| |
| default: |
| break; |
| } |
| |
| // |
| // See if it maps to a regular operation. |
| // |
| if (binOp != glslang::EOpNull) { |
| glslang::TIntermTyped* left = node->getSequence()[0]->getAsTyped(); |
| glslang::TIntermTyped* right = node->getSequence()[1]->getAsTyped(); |
| assert(left && right); |
| |
| builder.clearAccessChain(); |
| left->traverse(this); |
| spv::Id leftId = accessChainLoad(left->getType()); |
| |
| builder.clearAccessChain(); |
| right->traverse(this); |
| spv::Id rightId = accessChainLoad(right->getType()); |
| |
| builder.setLine(node->getLoc().line, node->getLoc().getFilename()); |
| OpDecorations decorations = { precision, |
| TranslateNoContractionDecoration(node->getType().getQualifier()), |
| TranslateNonUniformDecoration(node->getType().getQualifier()) }; |
| result = createBinaryOperation(binOp, decorations, |
| resultType(), leftId, rightId, |
| left->getType().getBasicType(), reduceComparison); |
| |
| // code above should only make binOp that exists in createBinaryOperation |
| assert(result != spv::NoResult); |
| builder.clearAccessChain(); |
| builder.setAccessChainRValue(result); |
| |
| return false; |
| } |
| |
| // |
| // Create the list of operands. |
| // |
| glslang::TIntermSequence& glslangOperands = node->getSequence(); |
| std::vector<spv::Id> operands; |
| std::vector<spv::IdImmediate> memoryAccessOperands; |
| for (int arg = 0; arg < (int)glslangOperands.size(); ++arg) { |
| |