Final round for line endings.
diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index 969c091..8cdc032 100644
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -1,2578 +1,2578 @@
-//
-//Copyright (C) 2014 LunarG, Inc.
-//
-//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.
-
-//
-// Author: John Kessenich, LunarG
-//
-// Visit the nodes in the glslang intermediate tree representation to
-// translate them to SPIR-V.
-//
-
-#include "spirv.h"
-#include "GlslangToSpv.h"
-#include "SpvBuilder.h"
-#include "GLSL450Lib.h"
-
-// Glslang includes
-#include "glslang/MachineIndependent/localintermediate.h"
-#include "glslang/MachineIndependent/SymbolTable.h"
-
-#include <string>
-#include <map>
-#include <list>
-#include <vector>
-#include <stack>
-#include <fstream>
-
-namespace {
-
-const int GlslangMagic = 0x51a;
-
-//
-// 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(const glslang::TIntermediate*);
- 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 dumpSpv(std::vector<unsigned int>& out) { builder.dump(out); }
-
-protected:
- spv::Id createSpvVariable(const glslang::TIntermSymbol*);
- spv::Id getSampledType(const glslang::TSampler&);
- spv::Id convertGlslangToSpvType(const glslang::TType& type);
-
- bool isShaderEntrypoint(const glslang::TIntermAggregate* node);
- 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::TIntermSequence& glslangArguments, std::vector<spv::Id>& arguments);
- spv::Id handleBuiltInFunctionCall(const glslang::TIntermAggregate*);
- spv::Id handleUserFunctionCall(const glslang::TIntermAggregate*);
-
- spv::Id createBinaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, spv::Id left, spv::Id right, glslang::TBasicType typeProxy, bool reduceComparison = true);
- spv::Id createUnaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, spv::Id operand, bool isFloat);
- spv::Id createConversion(glslang::TOperator op, spv::Decoration precision, spv::Id destTypeId, spv::Id operand);
- spv::Id makeSmearedConstant(spv::Id constant, int vectorSize);
- spv::Id createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& operands);
- spv::Id createNoArgOperation(glslang::TOperator op);
- spv::Id getSymbolId(const glslang::TIntermSymbol* node);
- void addDecoration(spv::Id id, spv::Decoration dec);
- void addMemberDecoration(spv::Id id, int member, spv::Decoration dec);
- spv::Id createSpvConstant(const glslang::TType& type, const glslang::TConstUnionArray&, int& nextConst);
-
- spv::Function* shaderEntry;
- int sequenceDepth;
-
- // There is a 1:1 mapping between a spv builder and a module; this is thread safe
- spv::Builder builder;
- bool inMain;
- bool mainTerminated;
- bool linkageOnly;
- const glslang::TIntermediate* glslangIntermediate;
- spv::Id stdBuiltins;
-
- std::map<int, spv::Id> symbolValues;
- std::set<int> constReadOnlyParameters; // set of formal function parameters that have glslang qualifier constReadOnly, so we know they are not local function "const" that are write-once
- std::map<std::string, spv::Function*> functionMap;
- std::map<const glslang::TTypeList*, spv::Id> structMap;
- std::map<const glslang::TTypeList*, std::vector<int> > memberRemapper; // for mapping glslang block indices to spv indices (e.g., due to hidden members)
- std::stack<bool> breakForLoop; // false means break for switch
- std::stack<glslang::TIntermTyped*> loopTerminal; // code from the last part of a for loop: for(...; ...; terminal), needed for e.g., continue };
-};
-
-//
-// Helper functions for translating glslang representations to SPIR-V enumerants.
-//
-
-// Translate glslang profile to SPIR-V source language.
-spv::SourceLanguage TranslateSourceLanguage(EProfile profile)
-{
- switch (profile) {
- case ENoProfile:
- case ECoreProfile:
- case ECompatibilityProfile:
- return spv::SourceLanguageGLSL;
- case EEsProfile:
- return spv::SourceLanguageESSL;
- 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 EShLangTessControl: return spv::ExecutionModelTessellationControl;
- case EShLangTessEvaluation: return spv::ExecutionModelTessellationEvaluation;
- case EShLangGeometry: return spv::ExecutionModelGeometry;
- case EShLangFragment: return spv::ExecutionModelFragment;
- case EShLangCompute: return spv::ExecutionModelGLCompute;
- default:
- spv::MissingFunctionality("GLSL stage");
- return spv::ExecutionModelFragment;
- }
-}
-
-// Translate glslang type to SPIR-V storage class.
-spv::StorageClass TranslateStorageClass(const glslang::TType& type)
-{
- if (type.getQualifier().isPipeInput())
- return spv::StorageClassInput;
- else if (type.getQualifier().isPipeOutput())
- return spv::StorageClassOutput;
- else if (type.getQualifier().isUniformOrBuffer()) {
- if (type.getBasicType() == glslang::EbtBlock)
- return spv::StorageClassUniform;
- else
- return spv::StorageClassUniformConstant;
- // TODO: how are we distuingishing between default and non-default non-writable uniforms? Do default uniforms even exist?
- } else {
- switch (type.getQualifier().storage) {
- case glslang::EvqShared: return spv::StorageClassWorkgroupLocal; break;
- case glslang::EvqGlobal: return spv::StorageClassPrivateGlobal;
- case glslang::EvqConstReadOnly: return spv::StorageClassFunction;
- case glslang::EvqTemporary: return spv::StorageClassFunction;
- default:
- spv::MissingFunctionality("unknown glslang storage class");
- return spv::StorageClassFunction;
- }
- }
-}
-
-// 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;
- default:
- spv::MissingFunctionality("unknown sampler dimension");
- return spv::Dim2D;
- }
-}
-
-// Translate glslang type to SPIR-V precision decorations.
-spv::Decoration TranslatePrecisionDecoration(const glslang::TType& type)
-{
- switch (type.getQualifier().precision) {
- case glslang::EpqLow: return spv::DecorationPrecisionLow;
- case glslang::EpqMedium: return spv::DecorationPrecisionMedium;
- case glslang::EpqHigh: return spv::DecorationPrecisionHigh;
- default:
- return spv::NoPrecision;
- }
-}
-
-// Translate glslang type to SPIR-V block decorations.
-spv::Decoration TranslateBlockDecoration(const glslang::TType& type)
-{
- if (type.getBasicType() == glslang::EbtBlock) {
- switch (type.getQualifier().storage) {
- case glslang::EvqUniform: return spv::DecorationBlock;
- case glslang::EvqBuffer: return spv::DecorationBufferBlock;
- case glslang::EvqVaryingIn: return spv::DecorationBlock;
- case glslang::EvqVaryingOut: return spv::DecorationBlock;
- default:
- spv::MissingFunctionality("kind of block");
- break;
- }
- }
-
- return (spv::Decoration)spv::BadValue;
-}
-
-// Translate glslang type to SPIR-V layout decorations.
-spv::Decoration TranslateLayoutDecoration(const glslang::TType& type)
-{
- if (type.isMatrix()) {
- switch (type.getQualifier().layoutMatrix) {
- case glslang::ElmRowMajor:
- return spv::DecorationRowMajor;
- default:
- return spv::DecorationColMajor;
- }
- } else {
- switch (type.getBasicType()) {
- default:
- return (spv::Decoration)spv::BadValue;
- 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::ElpStd140: return spv::DecorationGLSLStd140;
- case glslang::ElpStd430: return spv::DecorationGLSLStd430;
- case glslang::ElpPacked: return spv::DecorationGLSLPacked;
- default:
- spv::MissingFunctionality("uniform block layout");
- return spv::DecorationGLSLShared;
- }
- case glslang::EvqVaryingIn:
- case glslang::EvqVaryingOut:
- if (type.getQualifier().layoutPacking != glslang::ElpNone)
- spv::MissingFunctionality("in/out block layout");
- return (spv::Decoration)spv::BadValue;
- default:
- spv::MissingFunctionality("block storage qualification");
- return (spv::Decoration)spv::BadValue;
- }
- }
- }
-}
-
-// Translate glslang type to SPIR-V interpolation decorations.
-spv::Decoration TranslateInterpolationDecoration(const glslang::TType& type)
-{
- if (type.getQualifier().smooth)
- return spv::DecorationSmooth;
- if (type.getQualifier().nopersp)
- return spv::DecorationNoperspective;
- else if (type.getQualifier().patch)
- return spv::DecorationPatch;
- else if (type.getQualifier().flat)
- return spv::DecorationFlat;
- else if (type.getQualifier().centroid)
- return spv::DecorationCentroid;
- else if (type.getQualifier().sample)
- return spv::DecorationSample;
- else
- return (spv::Decoration)spv::BadValue;
-}
-
-// If glslang type is invaraiant, return SPIR-V invariant decoration.
-spv::Decoration TranslateInvariantDecoration(const glslang::TType& type)
-{
- if (type.getQualifier().invariant)
- return spv::DecorationInvariant;
- else
- return (spv::Decoration)spv::BadValue;
-}
-
-// Translate glslang built-in variable to SPIR-V built in decoration.
-spv::BuiltIn TranslateBuiltInDecoration(glslang::TBuiltInVariable builtIn)
-{
- switch (builtIn) {
- case glslang::EbvPosition: return spv::BuiltInPosition;
- case glslang::EbvPointSize: return spv::BuiltInPointSize;
- case glslang::EbvClipVertex: return spv::BuiltInClipVertex;
- case glslang::EbvClipDistance: return spv::BuiltInClipDistance;
- case glslang::EbvCullDistance: return spv::BuiltInCullDistance;
- case glslang::EbvVertexId: return spv::BuiltInVertexId;
- case glslang::EbvInstanceId: return spv::BuiltInInstanceId;
- case glslang::EbvPrimitiveId: return spv::BuiltInPrimitiveId;
- case glslang::EbvInvocationId: return spv::BuiltInInvocationId;
- case glslang::EbvLayer: return spv::BuiltInLayer;
- case glslang::EbvViewportIndex: return spv::BuiltInViewportIndex;
- 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::EbvFragCoord: return spv::BuiltInFragCoord;
- case glslang::EbvPointCoord: return spv::BuiltInPointCoord;
- case glslang::EbvFace: return spv::BuiltInFrontFacing;
- case glslang::EbvSampleId: return spv::BuiltInSampleId;
- case glslang::EbvSamplePosition: return spv::BuiltInSamplePosition;
- case glslang::EbvSampleMask: return spv::BuiltInSampleMask;
- case glslang::EbvFragColor: return spv::BuiltInFragColor;
- case glslang::EbvFragData: return spv::BuiltInFragColor;
- case glslang::EbvFragDepth: return spv::BuiltInFragDepth;
- 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;
- default: return (spv::BuiltIn)spv::BadValue;
- }
-}
-
-//
-// Implement the TGlslangToSpvTraverser class.
-//
-
-TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* glslangIntermediate)
- : TIntermTraverser(true, false, true), shaderEntry(0), sequenceDepth(0),
- builder(GlslangMagic),
- inMain(false), mainTerminated(false), linkageOnly(false),
- glslangIntermediate(glslangIntermediate)
-{
- spv::ExecutionModel executionModel = TranslateExecutionModel(glslangIntermediate->getStage());
-
- builder.clearAccessChain();
- builder.setSource(TranslateSourceLanguage(glslangIntermediate->getProfile()), glslangIntermediate->getVersion());
- stdBuiltins = builder.import("GLSL.std.450");
- builder.setMemoryModel(spv::AddressingModelLogical, spv::MemoryModelGLSL450);
- shaderEntry = builder.makeMain();
- builder.addEntryPoint(executionModel, shaderEntry);
-
- // Add the source extensions
- const std::set<std::string>& sourceExtensions = glslangIntermediate->getRequestedExtensions();
- for (std::set<std::string>::const_iterator it = sourceExtensions.begin(); it != sourceExtensions.end(); ++it)
- builder.addSourceExtension(it->c_str());
-
- // Add the top-level modes for this shader.
-
- if (glslangIntermediate->getXfbMode())
- builder.addExecutionMode(shaderEntry, spv::ExecutionModeXfb);
-
- unsigned int mode;
- switch (glslangIntermediate->getStage()) {
- case EShLangVertex:
- break;
-
- case EShLangTessControl:
- builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, glslangIntermediate->getVertices());
- break;
-
- case EShLangTessEvaluation:
- switch (glslangIntermediate->getInputPrimitive()) {
- case glslang::ElgTriangles: mode = spv::ExecutionModeInputTriangles; break;
- case glslang::ElgQuads: mode = spv::ExecutionModeInputQuads; break;
- case glslang::ElgIsolines: mode = spv::ExecutionModeInputIsolines; break;
- default: mode = spv::BadValue; break;
- }
- if (mode != spv::BadValue)
- builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
-
- // TODO
- //builder.addExecutionMode(spv::VertexSpacingMdName, glslangIntermediate->getVertexSpacing());
- //builder.addExecutionMode(spv::VertexOrderMdName, glslangIntermediate->getVertexOrder());
- //builder.addExecutionMode(spv::PointModeMdName, glslangIntermediate->getPointMode());
- break;
-
- case EShLangGeometry:
- 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::ExecutionModeInputTriangles; break;
- case glslang::ElgTrianglesAdjacency: mode = spv::ExecutionModeInputTrianglesAdjacency; break;
- default: mode = spv::BadValue; break;
- }
- if (mode != spv::BadValue)
- 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::BadValue; break;
- }
- if (mode != spv::BadValue)
- builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
- builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, glslangIntermediate->getVertices());
- break;
-
- case EShLangFragment:
- if (glslangIntermediate->getPixelCenterInteger())
- builder.addExecutionMode(shaderEntry, spv::ExecutionModePixelCenterInteger);
- if (glslangIntermediate->getOriginUpperLeft())
- builder.addExecutionMode(shaderEntry, spv::ExecutionModeOriginUpperLeft);
- break;
-
- case EShLangCompute:
- break;
-
- default:
- break;
- }
-
-}
-
-TGlslangToSpvTraverser::~TGlslangToSpvTraverser()
-{
- if (! mainTerminated) {
- spv::Block* lastMainBlock = shaderEntry->getLastBlock();
- builder.setBuildPoint(lastMainBlock);
- builder.leaveFunction(true);
- }
-}
-
-//
-// 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)
-{
- // 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 (! linkageOnly) {
- // 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 "const in" arguments to a function, which are an intermediate object.
- // See comments in handleUserFunctionCall().
- glslang::TStorageQualifier qualifier = symbol->getQualifier().storage;
- if (qualifier == glslang::EvqConstReadOnly && constReadOnlyParameters.find(symbol->getId()) != constReadOnlyParameters.end())
- builder.setAccessChainRValue(id);
- else
- builder.setAccessChainLValue(id);
- }
-}
-
-bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::TIntermBinary* node)
-{
- // 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 = builder.accessChainLoad(TranslatePrecisionDecoration(node->getRight()->getType()));
-
- if (node->getOp() != glslang::EOpAssign) {
- // the left is also an r-value
- builder.setAccessChain(lValue);
- spv::Id leftRValue = builder.accessChainLoad(TranslatePrecisionDecoration(node->getLeft()->getType()));
-
- // do the operation
- rValue = createBinaryOperation(node->getOp(), TranslatePrecisionDecoration(node->getType()),
- convertGlslangToSpvType(node->getType()), leftRValue, rValue,
- node->getType().getBasicType());
-
- // these all need their counterparts in createBinaryOperation()
- if (rValue == 0)
- spv::MissingFunctionality("createBinaryOperation");
- }
-
- // store the result
- builder.setAccessChain(lValue);
- builder.accessChainStore(rValue);
-
- // assignments are expressions having an rValue after they are evaluated...
- builder.clearAccessChain();
- builder.setAccessChainRValue(rValue);
- }
- return false;
- case glslang::EOpIndexDirect:
- case glslang::EOpIndexDirectStruct:
- {
- // Get the left part of the access chain.
- node->getLeft()->traverse(this);
-
- // Add the next element in the chain
-
- int index = 0;
- if (node->getRight()->getAsConstantUnion() == 0)
- spv::MissingFunctionality("direct index without a constant node");
- else
- index = node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();
-
- 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()];
- if (remapper.size() == 0)
- spv::MissingFunctionality("block without member remapping");
- else
- index = remapper[index];
- }
-
- 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(node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst());
- builder.accessChainPushSwizzle(swizzle);
- } else {
- // normal case for indexing array or structure or block
- builder.accessChainPush(builder.makeIntConstant(index), convertGlslangToSpvType(node->getType()));
- }
- }
- return false;
- case glslang::EOpIndexIndirect:
- {
- // Structure or array or vector indirection.
- // Will use native SPIR-V access-chain for struct 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 = builder.accessChainLoad(TranslatePrecisionDecoration(node->getRight()->getType()));
-
- // restore the saved access chain
- builder.setAccessChain(partial);
-
- if (! node->getLeft()->getType().isArray() && node->getLeft()->getType().isVector())
- builder.accessChainPushComponent(index);
- else
- builder.accessChainPush(index, convertGlslangToSpvType(node->getType()));
- }
- return false;
- case glslang::EOpVectorSwizzle:
- {
- node->getLeft()->traverse(this);
- glslang::TIntermSequence& swizzleSequence = node->getRight()->getAsAggregate()->getSequence();
- std::vector<unsigned> swizzle;
- for (int i = 0; i < (int)swizzleSequence.size(); ++i)
- swizzle.push_back(swizzleSequence[i]->getAsConstantUnion()->getConstArray()[0].getIConst());
- builder.accessChainPushSwizzle(swizzle);
- }
- return false;
- default:
- break;
- }
-
- // Assume generic binary op...
-
- // Get the operands
- builder.clearAccessChain();
- node->getLeft()->traverse(this);
- spv::Id left = builder.accessChainLoad(TranslatePrecisionDecoration(node->getLeft()->getType()));
-
- builder.clearAccessChain();
- node->getRight()->traverse(this);
- spv::Id right = builder.accessChainLoad(TranslatePrecisionDecoration(node->getRight()->getType()));
-
- spv::Id result;
- spv::Decoration precision = TranslatePrecisionDecoration(node->getType());
-
- result = createBinaryOperation(node->getOp(), precision,
- convertGlslangToSpvType(node->getType()), left, right,
- node->getLeft()->getType().getBasicType());
-
- if (! result) {
- spv::MissingFunctionality("glslang binary operation");
- } else {
- builder.clearAccessChain();
- builder.setAccessChainRValue(result);
-
- return false;
- }
-
- return true;
-}
-
-bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node)
-{
- builder.clearAccessChain();
- node->getOperand()->traverse(this);
- spv::Id operand = builder.accessChainLoad(TranslatePrecisionDecoration(node->getOperand()->getType()));
-
- spv::Decoration precision = TranslatePrecisionDecoration(node->getType());
-
- // it could be a conversion
- spv::Id result = createConversion(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operand);
-
- // if not, then possibly an operation
- if (! result)
- result = createUnaryOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operand, node->getBasicType() == glslang::EbtFloat || node->getBasicType() == glslang::EbtDouble);
-
- if (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 = node->getBasicType() == glslang::EbtFloat ?
- builder.makeFloatConstant(1.0F) :
- 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, TranslatePrecisionDecoration(node->getType()),
- convertGlslangToSpvType(node->getType()), operand, one,
- node->getType().getBasicType());
- if (result == 0)
- spv::MissingFunctionality("createBinaryOperation for unary");
-
- // 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;
-
- case glslang::EOpEmitStreamVertex:
- builder.createNoResultOp(spv::OpEmitStreamVertex, operand);
- return false;
- case glslang::EOpEndStreamPrimitive:
- builder.createNoResultOp(spv::OpEndStreamPrimitive, operand);
- return false;
-
- default:
- spv::MissingFunctionality("glslang unary");
- break;
- }
-
- return true;
-}
-
-bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TIntermAggregate* node)
-{
- spv::Id result;
- glslang::TOperator binOp = glslang::EOpNull;
- bool reduceComparison = true;
- bool isMatrix = false;
- bool noReturnValue = false;
-
- assert(node->getOp());
-
- spv::Decoration precision = TranslatePrecisionDecoration(node->getType());
-
- 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 entry of main(), 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 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)) {
- inMain = true;
- builder.setBuildPoint(shaderEntry->getLastBlock());
- } else {
- handleFunctionEntry(node);
- }
- } else {
- if (inMain)
- mainTerminated = true;
- builder.leaveFunction(inMain);
- inMain = 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:
- {
- if (node->isUserDefined())
- result = handleUserFunctionCall(node);
- else
- result = handleBuiltInFunctionCall(node);
-
- if (! result) {
- spv::MissingFunctionality("glslang function call");
- glslang::TConstUnionArray emptyConsts;
- int nextConst = 0;
- result = createSpvConstant(node->getType(), emptyConsts, nextConst);
- }
- builder.clearAccessChain();
- builder.setAccessChainRValue(result);
-
- 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:
- 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::EOpConstructBool:
- case glslang::EOpConstructBVec2:
- case glslang::EOpConstructBVec3:
- case glslang::EOpConstructBVec4:
- 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::EOpConstructStruct:
- {
- std::vector<spv::Id> arguments;
- translateArguments(node->getSequence(), arguments);
- spv::Id resultTypeId = convertGlslangToSpvType(node->getType());
- spv::Id constructed;
- if (node->getOp() == glslang::EOpConstructStruct || node->getType().isArray()) {
- std::vector<spv::Id> constituents;
- for (int c = 0; c < (int)arguments.size(); ++c)
- constituents.push_back(arguments[c]);
- constructed = builder.createCompositeConstruct(resultTypeId, constituents);
- } else {
- if (isMatrix)
- constructed = builder.createMatrixConstructor(precision, arguments, resultTypeId);
- else
- constructed = builder.createConstructor(precision, arguments, resultTypeId);
- }
-
- 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:
- // compontent-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()->isVector())
- 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;
- case glslang::EOpArrayLength:
- {
- glslang::TIntermTyped* typedNode = node->getSequence()[0]->getAsTyped();
- assert(typedNode);
- spv::Id length = builder.makeIntConstant(typedNode->getType().getArraySize());
-
- builder.clearAccessChain();
- builder.setAccessChainRValue(length);
-
- return false;
- }
- 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:
- noReturnValue = true;
- // These all have 0 operands and will naturally finish up in the code below for 0 operands
- break;
-
- 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 = builder.accessChainLoad(TranslatePrecisionDecoration(left->getType()));
-
- builder.clearAccessChain();
- right->traverse(this);
- spv::Id rightId = builder.accessChainLoad(TranslatePrecisionDecoration(right->getType()));
-
- result = createBinaryOperation(binOp, precision,
- convertGlslangToSpvType(node->getType()), leftId, rightId,
- left->getType().getBasicType(), reduceComparison);
-
- // code above should only make binOp that exists in createBinaryOperation
- if (result == 0)
- spv::MissingFunctionality("createBinaryOperation for aggregate");
-
- builder.clearAccessChain();
- builder.setAccessChainRValue(result);
-
- return false;
- }
-
- glslang::TIntermSequence& glslangOperands = node->getSequence();
- std::vector<spv::Id> operands;
- for (int arg = 0; arg < (int)glslangOperands.size(); ++arg) {
- builder.clearAccessChain();
- glslangOperands[arg]->traverse(this);
-
- // special case l-value operands; there are just a few
- bool lvalue = false;
- switch (node->getOp()) {
- //case glslang::EOpFrexp:
- case glslang::EOpModf:
- if (arg == 1)
- lvalue = true;
- break;
- //case glslang::EOpUAddCarry:
- //case glslang::EOpUSubBorrow:
- //case glslang::EOpUMulExtended:
- default:
- break;
- }
- if (lvalue)
- operands.push_back(builder.accessChainGetLValue());
- else
- operands.push_back(builder.accessChainLoad(TranslatePrecisionDecoration(glslangOperands[arg]->getAsTyped()->getType())));
- }
- switch (glslangOperands.size()) {
- case 0:
- result = createNoArgOperation(node->getOp());
- break;
- case 1:
- result = createUnaryOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands.front(), node->getType().getBasicType() == glslang::EbtFloat || node->getType().getBasicType() == glslang::EbtDouble);
- break;
- default:
- result = createMiscOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands);
- break;
- }
-
- if (noReturnValue)
- return false;
-
- if (! result) {
- spv::MissingFunctionality("glslang aggregate");
- return true;
- } else {
- builder.clearAccessChain();
- builder.setAccessChainRValue(result);
- return false;
- }
-}
-
-bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang::TIntermSelection* node)
-{
- // This path handles both if-then-else and ?:
- // The if-then-else has a node type of void, while
- // ?: has a non-void node type
- spv::Id result = 0;
- if (node->getBasicType() != glslang::EbtVoid) {
- // don't handle this as just on-the-fly temporaries, because there will be two names
- // and better to leave SSA to later passes
- result = builder.createVariable(spv::StorageClassFunction, convertGlslangToSpvType(node->getType()));
- }
-
- // emit the condition before doing anything with selection
- node->getCondition()->traverse(this);
-
- // make an "if" based on the value created by the condition
- spv::Builder::If ifBuilder(builder.accessChainLoad(spv::NoPrecision), builder);
-
- if (node->getTrueBlock()) {
- // emit the "then" statement
- node->getTrueBlock()->traverse(this);
- if (result)
- builder.createStore(builder.accessChainLoad(TranslatePrecisionDecoration(node->getTrueBlock()->getAsTyped()->getType())), result);
- }
-
- if (node->getFalseBlock()) {
- ifBuilder.makeBeginElse();
- // emit the "else" statement
- node->getFalseBlock()->traverse(this);
- if (result)
- builder.createStore(builder.accessChainLoad(TranslatePrecisionDecoration(node->getFalseBlock()->getAsTyped()->getType())), result);
- }
-
- ifBuilder.makeEndIf();
-
- if (result) {
- // GLSL only has r-values as the result of a :?, but
- // if we have an l-value, that can be more efficient if it will
- // become the base of a complex r-value expression, because the
- // next layer copies r-values into memory to use the access-chain mechanism
- builder.clearAccessChain();
- builder.setAccessChainLValue(result);
- }
-
- return false;
-}
-
-bool TGlslangToSpvTraverser::visitSwitch(glslang::TVisit /* visit */, glslang::TIntermSwitch* node)
-{
- // emit and get the condition before doing anything with switch
- node->getCondition()->traverse(this);
- spv::Id selector = builder.accessChainLoad(TranslatePrecisionDecoration(node->getCondition()->getAsTyped()->getType()));
-
- // browse the children to sort out code segments
- int defaultSegment = -1;
- std::vector<TIntermNode*> codeSegments;
- glslang::TIntermSequence& sequence = node->getBody()->getSequence();
- std::vector<int> caseValues;
- std::vector<int> valueIndexToSegment(sequence.size()); // note: probably not all are used, it is an overestimate
- for (glslang::TIntermSequence::iterator c = sequence.begin(); c != sequence.end(); ++c) {
- TIntermNode* child = *c;
- if (child->getAsBranchNode() && child->getAsBranchNode()->getFlowOp() == glslang::EOpDefault)
- defaultSegment = codeSegments.size();
- else if (child->getAsBranchNode() && child->getAsBranchNode()->getFlowOp() == glslang::EOpCase) {
- valueIndexToSegment[caseValues.size()] = codeSegments.size();
- caseValues.push_back(child->getAsBranchNode()->getExpression()->getAsConstantUnion()->getConstArray()[0].getIConst());
- } else
- codeSegments.push_back(child);
- }
-
- // handle the case where the last code segment is missing, due to no code
- // statements between the last case and the end of the switch statement
- if ((caseValues.size() && (int)codeSegments.size() == valueIndexToSegment[caseValues.size() - 1]) ||
- (int)codeSegments.size() == defaultSegment)
- codeSegments.push_back(nullptr);
-
- // make the switch statement
- std::vector<spv::Block*> segmentBlocks; // returned, as the blocks allocated in the call
- builder.makeSwitch(selector, codeSegments.size(), caseValues, valueIndexToSegment, defaultSegment, segmentBlocks);
-
- // emit all the code in the segments
- breakForLoop.push(false);
- for (unsigned int s = 0; s < codeSegments.size(); ++s) {
- builder.nextSwitchSegment(segmentBlocks, s);
- if (codeSegments[s])
- codeSegments[s]->traverse(this);
- else
- builder.addSwitchBreak();
- }
- breakForLoop.pop();
-
- builder.endSwitch(segmentBlocks);
-
- return false;
-}
-
-void TGlslangToSpvTraverser::visitConstantUnion(glslang::TIntermConstantUnion* node)
-{
- int nextConst = 0;
- spv::Id constant = createSpvConstant(node->getType(), node->getConstArray(), nextConst);
-
- builder.clearAccessChain();
- builder.setAccessChainRValue(constant);
-}
-
-bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIntermLoop* node)
-{
- // body emission needs to know what the for-loop terminal is when it sees a "continue"
- loopTerminal.push(node->getTerminal());
-
- builder.makeNewLoop();
-
- bool bodyOut = false;
- if (! node->testFirst()) {
- builder.endLoopHeaderWithoutTest();
- if (node->getBody()) {
- breakForLoop.push(true);
- node->getBody()->traverse(this);
- breakForLoop.pop();
- }
- bodyOut = true;
- builder.createBranchToLoopTest();
- }
-
- if (node->getTest()) {
- node->getTest()->traverse(this);
- // the AST only contained the test computation, not the branch, we have to add it
- spv::Id condition = builder.accessChainLoad(TranslatePrecisionDecoration(node->getTest()->getType()));
- builder.createLoopTestBranch(condition);
- }
-
- if (! bodyOut && node->getBody()) {
- breakForLoop.push(true);
- node->getBody()->traverse(this);
- breakForLoop.pop();
- }
-
- if (loopTerminal.top())
- loopTerminal.top()->traverse(this);
-
- builder.closeLoop();
-
- loopTerminal.pop();
-
- return false;
-}
-
-bool TGlslangToSpvTraverser::visitBranch(glslang::TVisit /* visit */, glslang::TIntermBranch* node)
-{
- if (node->getExpression())
- node->getExpression()->traverse(this);
-
- switch (node->getFlowOp()) {
- case glslang::EOpKill:
- builder.makeDiscard();
- break;
- case glslang::EOpBreak:
- if (breakForLoop.top())
- builder.createLoopExit();
- else
- builder.addSwitchBreak();
- break;
- case glslang::EOpContinue:
- if (loopTerminal.top())
- loopTerminal.top()->traverse(this);
- builder.createLoopContinue();
- break;
- case glslang::EOpReturn:
- if (inMain)
- builder.makeMainReturn();
- else if (node->getExpression())
- builder.makeReturn(false, builder.accessChainLoad(TranslatePrecisionDecoration(node->getExpression()->getType())));
- else
- builder.makeReturn();
-
- builder.clearAccessChain();
- break;
-
- default:
- spv::MissingFunctionality("branch type");
- break;
- }
-
- return false;
-}
-
-spv::Id TGlslangToSpvTraverser::createSpvVariable(const glslang::TIntermSymbol* node)
-{
- // First, steer off constants, which are not SPIR-V variables, but
- // can still have a mapping to a SPIR-V Id.
- if (node->getQualifier().storage == glslang::EvqConst) {
- int nextConst = 0;
- return createSpvConstant(node->getType(), node->getConstArray(), nextConst);
- }
-
- // Now, handle actual variables
- spv::StorageClass storageClass = TranslateStorageClass(node->getType());
- spv::Id spvType = convertGlslangToSpvType(node->getType());
-
- const char* name = node->getName().c_str();
- if (glslang::IsAnonymous(name))
- name = "";
-
- return builder.createVariable(storageClass, spvType, name);
-}
-
-// Return type Id of the sampled type.
-spv::Id TGlslangToSpvTraverser::getSampledType(const glslang::TSampler& sampler)
-{
- switch (sampler.type) {
- case glslang::EbtFloat: return builder.makeFloatType(32);
- case glslang::EbtInt: return builder.makeIntType(32);
- case glslang::EbtUint: return builder.makeUintType(32);
- default:
- spv::MissingFunctionality("sampled type");
- return builder.makeFloatType(32);
- }
-}
-
-// Do full recursive conversion of an arbitrary glslang type to a SPIR-V Id.
-spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& type)
-{
- spv::Id spvType = 0;
-
- switch (type.getBasicType()) {
- case glslang::EbtVoid:
- spvType = builder.makeVoidType();
- if (type.isArray())
- spv::MissingFunctionality("array of void");
- break;
- case glslang::EbtFloat:
- spvType = builder.makeFloatType(32);
- break;
- case glslang::EbtDouble:
- spvType = builder.makeFloatType(64);
- break;
- case glslang::EbtBool:
- spvType = builder.makeBoolType();
- break;
- case glslang::EbtInt:
- spvType = builder.makeIntType(32);
- break;
- case glslang::EbtUint:
- spvType = builder.makeUintType(32);
- break;
- case glslang::EbtSampler:
- {
- const glslang::TSampler& sampler = type.getSampler();
- spvType = builder.makeSampler(getSampledType(sampler), TranslateDimensionality(sampler),
- sampler.image ? spv::Builder::samplerContentImage : spv::Builder::samplerContentTextureFilter,
- sampler.arrayed, sampler.shadow, sampler.ms);
- }
- break;
- case glslang::EbtStruct:
- case glslang::EbtBlock:
- {
- // If we've seen this struct type, return it
- const glslang::TTypeList* glslangStruct = type.getStruct();
- std::vector<spv::Id> structFields;
- spvType = structMap[glslangStruct];
- if (spvType)
- break;
-
- // else, we haven't seen it...
-
- // Create a vector of struct types for SPIR-V to consume
- int memberDelta = 0; // how much the member's index changes from glslang to SPIR-V, normally 0, except sometimes for blocks
- if (type.getBasicType() == glslang::EbtBlock)
- memberRemapper[glslangStruct].resize(glslangStruct->size());
- for (int i = 0; i < (int)glslangStruct->size(); i++) {
- glslang::TType& glslangType = *(*glslangStruct)[i].type;
- if (glslangType.hiddenMember()) {
- ++memberDelta;
- if (type.getBasicType() == glslang::EbtBlock)
- memberRemapper[glslangStruct][i] = -1;
- } else {
- if (type.getBasicType() == glslang::EbtBlock)
- memberRemapper[glslangStruct][i] = i - memberDelta;
- structFields.push_back(convertGlslangToSpvType(glslangType));
- }
- }
-
- // Make the SPIR-V type
- spvType = builder.makeStructType(structFields, type.getTypeName().c_str());
- structMap[glslangStruct] = spvType;
-
- // Name and decorate the non-hidden members
- for (int i = 0; i < (int)glslangStruct->size(); i++) {
- glslang::TType& glslangType = *(*glslangStruct)[i].type;
- int member = i;
- if (type.getBasicType() == glslang::EbtBlock)
- member = memberRemapper[glslangStruct][i];
- // using -1 above to indicate a hidden member
- if (member >= 0) {
- builder.addMemberName(spvType, member, glslangType.getFieldName().c_str());
- addMemberDecoration(spvType, member, TranslateLayoutDecoration(glslangType));
- addMemberDecoration(spvType, member, TranslatePrecisionDecoration(glslangType));
- addMemberDecoration(spvType, member, TranslateInterpolationDecoration(glslangType));
- addMemberDecoration(spvType, member, TranslateInvariantDecoration(glslangType));
- if (glslangType.getQualifier().hasLocation())
- builder.addMemberDecoration(spvType, member, spv::DecorationLocation, glslangType.getQualifier().layoutLocation);
- if (glslangType.getQualifier().hasComponent())
- builder.addMemberDecoration(spvType, member, spv::DecorationComponent, glslangType.getQualifier().layoutComponent);
- if (glslangType.getQualifier().hasXfbOffset())
- builder.addMemberDecoration(spvType, member, spv::DecorationOffset, glslangType.getQualifier().layoutXfbOffset);
-
- // built-in variable decorations
- int builtIn = TranslateBuiltInDecoration(glslangType.getQualifier().builtIn);
- if (builtIn != spv::BadValue)
- builder.addMemberDecoration(spvType, member, spv::DecorationBuiltIn, builtIn);
- }
- }
-
- // Decorate the structure
- addDecoration(spvType, TranslateLayoutDecoration(type));
- addDecoration(spvType, TranslateBlockDecoration(type));
- if (type.getQualifier().hasStream())
- builder.addDecoration(spvType, spv::DecorationStream, type.getQualifier().layoutStream);
- if (glslangIntermediate->getXfbMode()) {
- if (type.getQualifier().hasXfbStride())
- builder.addDecoration(spvType, spv::DecorationStride, type.getQualifier().layoutXfbStride);
- if (type.getQualifier().hasXfbBuffer())
- builder.addDecoration(spvType, spv::DecorationXfbBuffer, type.getQualifier().layoutXfbBuffer);
- }
- }
- break;
- default:
- spv::MissingFunctionality("basic type");
- break;
- }
-
- if (type.isMatrix())
- spvType = builder.makeMatrixType(spvType, type.getMatrixCols(), type.getMatrixRows());
- else {
- // If this variable has a vector element count greater than 1, create a SPIR-V vector
- if (type.getVectorSize() > 1)
- spvType = builder.makeVectorType(spvType, type.getVectorSize());
- }
-
- if (type.isArray()) {
- unsigned arraySize;
- if (! type.isExplicitlySizedArray()) {
- spv::MissingFunctionality("Unsized array");
- arraySize = 8;
- } else
- arraySize = type.getArraySize();
- spvType = builder.makeArrayType(spvType, arraySize);
- }
-
- return spvType;
-}
-
-bool TGlslangToSpvTraverser::isShaderEntrypoint(const glslang::TIntermAggregate* node)
-{
- return node->getName() == "main(";
-}
-
-// Make all the functions, skeletally, without actually visiting their bodies.
-void TGlslangToSpvTraverser::makeFunctions(const glslang::TIntermSequence& glslFunctions)
-{
- for (int f = 0; f < (int)glslFunctions.size(); ++f) {
- glslang::TIntermAggregate* glslFunction = glslFunctions[f]->getAsAggregate();
- if (! glslFunction || glslFunction->getOp() != glslang::EOpFunction || isShaderEntrypoint(glslFunction))
- continue;
-
- // We're on a user function. Set up the basic interface for the function now,
- // so that it's available to call.
- // Translating the body will happen later.
- //
- // Typically (except for a "const in" parameter), an address will be passed to the
- // function. What it is an address of varies:
- //
- // - "in" parameters not marked as "const" can be written to without modifying the argument,
- // so that write needs to be to a copy, hence the address of a copy works.
- //
- // - "const in" parameters can just be the r-value, as no writes need occur.
- //
- // - "out" and "inout" arguments can't be done as direct pointers, because GLSL has
- // copy-in/copy-out semantics. They can be handled though with a pointer to a copy.
-
- std::vector<spv::Id> paramTypes;
- glslang::TIntermSequence& parameters = glslFunction->getSequence()[0]->getAsAggregate()->getSequence();
-
- for (int p = 0; p < (int)parameters.size(); ++p) {
- const glslang::TType& paramType = parameters[p]->getAsTyped()->getType();
- spv::Id typeId = convertGlslangToSpvType(paramType);
- if (paramType.getQualifier().storage != glslang::EvqConstReadOnly)
- typeId = builder.makePointer(spv::StorageClassFunction, typeId);
- else
- constReadOnlyParameters.insert(parameters[p]->getAsSymbolNode()->getId());
- paramTypes.push_back(typeId);
- }
-
- spv::Block* functionBlock;
- spv::Function *function = builder.makeFunctionEntry(convertGlslangToSpvType(glslFunction->getType()), glslFunction->getName().c_str(),
- paramTypes, &functionBlock);
-
- // Track function to emit/call later
- functionMap[glslFunction->getName().c_str()] = function;
-
- // Set the parameter id's
- for (int p = 0; p < (int)parameters.size(); ++p) {
- symbolValues[parameters[p]->getAsSymbolNode()->getId()] = function->getParamId(p);
- // give a name too
- builder.addName(function->getParamId(p), parameters[p]->getAsSymbolNode()->getName().c_str());
- }
- }
-}
-
-// Process all the initializers, while skipping the functions and link objects
-void TGlslangToSpvTraverser::makeGlobalInitializers(const glslang::TIntermSequence& initializers)
-{
- builder.setBuildPoint(shaderEntry->getLastBlock());
- for (int i = 0; i < (int)initializers.size(); ++i) {
- glslang::TIntermAggregate* initializer = initializers[i]->getAsAggregate();
- if (initializer && initializer->getOp() != glslang::EOpFunction && initializer->getOp() != glslang::EOpLinkerObjects) {
-
- // We're on a top-level node that's not a function. Treat as an initializer, whose
- // code goes into the beginning of main.
- initializer->traverse(this);
- }
- }
-}
-
-// Process all the functions, while skipping initializers.
-void TGlslangToSpvTraverser::visitFunctions(const glslang::TIntermSequence& glslFunctions)
-{
- for (int f = 0; f < (int)glslFunctions.size(); ++f) {
- glslang::TIntermAggregate* node = glslFunctions[f]->getAsAggregate();
- if (node && (node->getOp() == glslang::EOpFunction || node->getOp() == glslang ::EOpLinkerObjects))
- node->traverse(this);
- }
-}
-
-void TGlslangToSpvTraverser::handleFunctionEntry(const glslang::TIntermAggregate* node)
-{
- // SPIR-V functions should already be in the functionMap from the prepass
- // that called makeFunctions().
- spv::Function* function = functionMap[node->getName().c_str()];
- spv::Block* functionBlock = function->getEntryBlock();
- builder.setBuildPoint(functionBlock);
-}
-
-void TGlslangToSpvTraverser::translateArguments(const glslang::TIntermSequence& glslangArguments, std::vector<spv::Id>& arguments)
-{
- for (int i = 0; i < (int)glslangArguments.size(); ++i) {
- builder.clearAccessChain();
- glslangArguments[i]->traverse(this);
- arguments.push_back(builder.accessChainLoad(TranslatePrecisionDecoration(glslangArguments[i]->getAsTyped()->getType())));
- }
-}
-
-spv::Id TGlslangToSpvTraverser::handleBuiltInFunctionCall(const glslang::TIntermAggregate* node)
-{
- std::vector<spv::Id> arguments;
- translateArguments(node->getSequence(), arguments);
-
- std::vector<spv::Id> argTypes;
- for (int a = 0; a < (int)arguments.size(); ++a)
- argTypes.push_back(builder.getTypeId(arguments[a]));
-
- spv::Decoration precision = TranslatePrecisionDecoration(node->getType());
-
- if (node->getName() == "ftransform(") {
- spv::MissingFunctionality("ftransform()");
- //spv::Id vertex = builder.createVariable(spv::StorageShaderGlobal, spv::VectorType::get(spv::makeFloatType(), 4),
- // "gl_Vertex_sim");
- //spv::Id matrix = builder.createVariable(spv::StorageShaderGlobal, spv::VectorType::get(spv::makeFloatType(), 4),
- // "gl_ModelViewProjectionMatrix_sim");
- return 0;
- }
-
- if (node->getName().substr(0, 7) == "texture" || node->getName().substr(0, 5) == "texel" || node->getName().substr(0, 6) == "shadow") {
- const glslang::TSampler sampler = node->getSequence()[0]->getAsTyped()->getType().getSampler();
- spv::Builder::TextureParameters params = { };
- params.sampler = arguments[0];
-
- // special case size query
- if (node->getName().find("textureSize", 0) != std::string::npos) {
- if (arguments.size() > 1) {
- params.lod = arguments[1];
- return builder.createTextureQueryCall(spv::OpTextureQuerySizeLod, params);
- } else
- return builder.createTextureQueryCall(spv::OpTextureQuerySize, params);
- }
-
- // special case the number of samples query
- if (node->getName().find("textureSamples", 0) != std::string::npos)
- return builder.createTextureQueryCall(spv::OpTextureQuerySamples, params);
-
- // special case the other queries
- if (node->getName().find("Query", 0) != std::string::npos) {
- if (node->getName().find("Levels", 0) != std::string::npos)
- return builder.createTextureQueryCall(spv::OpTextureQueryLevels, params);
- else if (node->getName().find("Lod", 0) != std::string::npos) {
- params.coords = arguments[1];
- return builder.createTextureQueryCall(spv::OpTextureQueryLod, params);
- } else
- spv::MissingFunctionality("glslang texture query");
- }
-
- // This is no longer a query....
-
- bool lod = node->getName().find("Lod", 0) != std::string::npos;
- bool proj = node->getName().find("Proj", 0) != std::string::npos;
- bool offsets = node->getName().find("Offsets", 0) != std::string::npos;
- bool offset = ! offsets && node->getName().find("Offset", 0) != std::string::npos;
- bool fetch = node->getName().find("Fetch", 0) != std::string::npos;
- bool gather = node->getName().find("Gather", 0) != std::string::npos;
- bool grad = node->getName().find("Grad", 0) != std::string::npos;
-
- if (fetch)
- spv::MissingFunctionality("texel fetch");
- if (gather)
- spv::MissingFunctionality("texture gather");
-
- // check for bias argument
- bool bias = false;
- if (! lod && ! gather && ! grad && ! fetch) {
- int nonBiasArgCount = 2;
- if (offset)
- ++nonBiasArgCount;
- if (grad)
- nonBiasArgCount += 2;
-
- if ((int)arguments.size() > nonBiasArgCount)
- bias = true;
- }
-
- bool cubeCompare = sampler.dim == glslang::EsdCube && sampler.arrayed && sampler.shadow;
-
- // set the rest of the arguments
- params.coords = arguments[1];
- int extraArgs = 0;
- if (cubeCompare)
- params.Dref = arguments[2];
- if (lod) {
- params.lod = arguments[2];
- ++extraArgs;
- }
- if (grad) {
- params.gradX = arguments[2 + extraArgs];
- params.gradY = arguments[3 + extraArgs];
- extraArgs += 2;
- }
- //if (gather && compare) {
- // params.compare = arguments[2 + extraArgs];
- // ++extraArgs;
- //}
- if (offset | offsets) {
- params.offset = arguments[2 + extraArgs];
- ++extraArgs;
- }
- if (bias) {
- params.bias = arguments[2 + extraArgs];
- ++extraArgs;
- }
-
- return builder.createTextureCall(precision, convertGlslangToSpvType(node->getType()), proj, params);
- }
-
- spv::MissingFunctionality("built-in function call");
-
- return 0;
-}
-
-spv::Id TGlslangToSpvTraverser::handleUserFunctionCall(const glslang::TIntermAggregate* node)
-{
- // Grab the function's pointer from the previously created function
- spv::Function* function = functionMap[node->getName().c_str()];
- if (! function)
- return 0;
-
- const glslang::TIntermSequence& glslangArgs = node->getSequence();
- const glslang::TQualifierList& qualifiers = node->getQualifierList();
-
- // See comments in makeFunctions() for details about the semantics for parameter passing.
- //
- // These imply we need a four step process:
- // 1. Evaluate the arguments
- // 2. Allocate and make copies of in, out, and inout arguments
- // 3. Make the call
- // 4. Copy back the results
-
- // 1. Evaluate the arguments
- std::vector<spv::Builder::AccessChain> lValues;
- std::vector<spv::Id> rValues;
- for (int a = 0; a < (int)glslangArgs.size(); ++a) {
- // build l-value
- builder.clearAccessChain();
- glslangArgs[a]->traverse(this);
- // keep outputs as l-values, evaluate input-only as r-values
- if (qualifiers[a] != glslang::EvqConstReadOnly) {
- // save l-value
- lValues.push_back(builder.getAccessChain());
- } else {
- // process r-value
- rValues.push_back(builder.accessChainLoad(TranslatePrecisionDecoration(glslangArgs[a]->getAsTyped()->getType())));
- }
- }
-
- // 2. Allocate space for anything needing a copy, and if it's "in" or "inout"
- // copy the original into that space.
- //
- // Also, build up the list of actual arguments to pass in for the call
- int lValueCount = 0;
- int rValueCount = 0;
- std::vector<spv::Id> spvArgs;
- for (int a = 0; a < (int)glslangArgs.size(); ++a) {
- spv::Id arg;
- if (qualifiers[a] != glslang::EvqConstReadOnly) {
- // need space to hold the copy
- const glslang::TType& paramType = glslangArgs[a]->getAsTyped()->getType();
- arg = builder.createVariable(spv::StorageClassFunction, convertGlslangToSpvType(paramType), "param");
- if (qualifiers[a] == glslang::EvqIn || qualifiers[a] == glslang::EvqInOut) {
- // need to copy the input into output space
- builder.setAccessChain(lValues[lValueCount]);
- spv::Id copy = builder.accessChainLoad(spv::NoPrecision); // TODO: get precision
- builder.createStore(copy, arg);
- }
- ++lValueCount;
- } else {
- arg = rValues[rValueCount];
- ++rValueCount;
- }
- spvArgs.push_back(arg);
- }
-
- // 3. Make the call.
- spv::Id result = builder.createFunctionCall(function, spvArgs);
-
- // 4. Copy back out an "out" arguments.
- lValueCount = 0;
- for (int a = 0; a < (int)glslangArgs.size(); ++a) {
- if (qualifiers[a] != glslang::EvqConstReadOnly) {
- if (qualifiers[a] == glslang::EvqOut || qualifiers[a] == glslang::EvqInOut) {
- spv::Id copy = builder.createLoad(spvArgs[a]);
- builder.setAccessChain(lValues[lValueCount]);
- builder.accessChainStore(copy);
- }
- ++lValueCount;
- }
- }
-
- return result;
-}
-
-// Translate AST operation to SPV operation, already having SPV-based operands/types.
-spv::Id TGlslangToSpvTraverser::createBinaryOperation(glslang::TOperator op, spv::Decoration precision,
- spv::Id typeId, spv::Id left, spv::Id right,
- glslang::TBasicType typeProxy, bool reduceComparison)
-{
- bool isUnsigned = typeProxy == glslang::EbtUint;
- bool isFloat = typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble;
-
- spv::Op binOp = spv::OpNop;
- bool needsPromotion = true;
- bool comparison = false;
-
- switch (op) {
- case glslang::EOpAdd:
- case glslang::EOpAddAssign:
- if (isFloat)
- binOp = spv::OpFAdd;
- else
- binOp = spv::OpIAdd;
- break;
- case glslang::EOpSub:
- case glslang::EOpSubAssign:
- if (isFloat)
- binOp = spv::OpFSub;
- else
- binOp = spv::OpISub;
- break;
- case glslang::EOpMul:
- case glslang::EOpMulAssign:
- if (isFloat)
- binOp = spv::OpFMul;
- else
- binOp = spv::OpIMul;
- break;
- case glslang::EOpVectorTimesScalar:
- case glslang::EOpVectorTimesScalarAssign:
- if (builder.isVector(right))
- std::swap(left, right);
- assert(builder.isScalar(right));
- binOp = spv::OpVectorTimesScalar;
- needsPromotion = false;
- break;
- case glslang::EOpVectorTimesMatrix:
- case glslang::EOpVectorTimesMatrixAssign:
- assert(builder.isVector(left));
- assert(builder.isMatrix(right));
- binOp = spv::OpVectorTimesMatrix;
- break;
- case glslang::EOpMatrixTimesVector:
- assert(builder.isMatrix(left));
- assert(builder.isVector(right));
- binOp = spv::OpMatrixTimesVector;
- break;
- case glslang::EOpMatrixTimesScalar:
- case glslang::EOpMatrixTimesScalarAssign:
- if (builder.isMatrix(right))
- std::swap(left, right);
- assert(builder.isScalar(right));
- binOp = spv::OpMatrixTimesScalar;
- break;
- case glslang::EOpMatrixTimesMatrix:
- case glslang::EOpMatrixTimesMatrixAssign:
- assert(builder.isMatrix(left));
- assert(builder.isMatrix(right));
- binOp = spv::OpMatrixTimesMatrix;
- break;
- case glslang::EOpOuterProduct:
- binOp = spv::OpOuterProduct;
- needsPromotion = false;
- break;
-
- case glslang::EOpDiv:
- case glslang::EOpDivAssign:
- if (isFloat)
- binOp = spv::OpFDiv;
- else if (isUnsigned)
- binOp = spv::OpUDiv;
- else
- binOp = spv::OpSDiv;
- break;
- case glslang::EOpMod:
- case glslang::EOpModAssign:
- if (isFloat)
- binOp = spv::OpFMod;
- else if (isUnsigned)
- binOp = spv::OpUMod;
- else
- binOp = spv::OpSMod;
- break;
- case glslang::EOpRightShift:
- case glslang::EOpRightShiftAssign:
- if (isUnsigned)
- binOp = spv::OpShiftRightLogical;
- else
- binOp = spv::OpShiftRightArithmetic;
- break;
- case glslang::EOpLeftShift:
- case glslang::EOpLeftShiftAssign:
- binOp = spv::OpShiftLeftLogical;
- break;
- case glslang::EOpAnd:
- case glslang::EOpAndAssign:
- binOp = spv::OpBitwiseAnd;
- break;
- case glslang::EOpLogicalAnd:
- needsPromotion = false;
- binOp = spv::OpLogicalAnd;
- break;
- case glslang::EOpInclusiveOr:
- case glslang::EOpInclusiveOrAssign:
- binOp = spv::OpBitwiseOr;
- break;
- case glslang::EOpLogicalOr:
- needsPromotion = false;
- binOp = spv::OpLogicalOr;
- break;
- case glslang::EOpExclusiveOr:
- case glslang::EOpExclusiveOrAssign:
- binOp = spv::OpBitwiseXor;
- break;
- case glslang::EOpLogicalXor:
- needsPromotion = false;
- binOp = spv::OpLogicalXor;
- break;
-
- case glslang::EOpLessThan:
- case glslang::EOpGreaterThan:
- case glslang::EOpLessThanEqual:
- case glslang::EOpGreaterThanEqual:
- case glslang::EOpEqual:
- case glslang::EOpNotEqual:
- case glslang::EOpVectorEqual:
- case glslang::EOpVectorNotEqual:
- comparison = true;
- break;
- default:
- break;
- }
-
- if (binOp != spv::OpNop) {
- if (builder.isMatrix(left) || builder.isMatrix(right)) {
- switch (binOp) {
- case spv::OpMatrixTimesScalar:
- case spv::OpVectorTimesMatrix:
- case spv::OpMatrixTimesVector:
- case spv::OpMatrixTimesMatrix:
- break;
- case spv::OpFDiv:
- // turn it into a multiply...
- assert(builder.isMatrix(left) && builder.isScalar(right));
- right = builder.createBinOp(spv::OpFDiv, builder.getTypeId(right), builder.makeFloatConstant(1.0F), right);
- binOp = spv::OpFMul;
- break;
- default:
- spv::MissingFunctionality("binary operation on matrix");
- break;
- }
-
- spv::Id id = builder.createBinOp(binOp, typeId, left, right);
- builder.setPrecision(id, precision);
-
- return id;
- }
-
- // No matrix involved; make both operands be the same number of components, if needed
- if (needsPromotion)
- builder.promoteScalar(precision, left, right);
-
- spv::Id id = builder.createBinOp(binOp, typeId, left, right);
- builder.setPrecision(id, precision);
-
- return id;
- }
-
- if (! comparison)
- return 0;
-
- // Comparison instructions
-
- if (reduceComparison && (builder.isVector(left) || builder.isMatrix(left) || builder.isAggregate(left))) {
- assert(op == glslang::EOpEqual || op == glslang::EOpNotEqual);
-
- return builder.createCompare(precision, left, right, op == glslang::EOpEqual);
- }
-
- switch (op) {
- case glslang::EOpLessThan:
- if (isFloat)
- binOp = spv::OpFOrdLessThan;
- else if (isUnsigned)
- binOp = spv::OpULessThan;
- else
- binOp = spv::OpSLessThan;
- break;
- case glslang::EOpGreaterThan:
- if (isFloat)
- binOp = spv::OpFOrdGreaterThan;
- else if (isUnsigned)
- binOp = spv::OpUGreaterThan;
- else
- binOp = spv::OpSGreaterThan;
- break;
- case glslang::EOpLessThanEqual:
- if (isFloat)
- binOp = spv::OpFOrdLessThanEqual;
- else if (isUnsigned)
- binOp = spv::OpULessThanEqual;
- else
- binOp = spv::OpSLessThanEqual;
- break;
- case glslang::EOpGreaterThanEqual:
- if (isFloat)
- binOp = spv::OpFOrdGreaterThanEqual;
- else if (isUnsigned)
- binOp = spv::OpUGreaterThanEqual;
- else
- binOp = spv::OpSGreaterThanEqual;
- break;
- case glslang::EOpEqual:
- case glslang::EOpVectorEqual:
- if (isFloat)
- binOp = spv::OpFOrdEqual;
- else
- binOp = spv::OpIEqual;
- break;
- case glslang::EOpNotEqual:
- case glslang::EOpVectorNotEqual:
- if (isFloat)
- binOp = spv::OpFOrdNotEqual;
- else
- binOp = spv::OpINotEqual;
- break;
- default:
- break;
- }
-
- if (binOp != spv::OpNop) {
- spv::Id id = builder.createBinOp(binOp, typeId, left, right);
- builder.setPrecision(id, precision);
-
- return id;
- }
-
- return 0;
-}
-
-spv::Id TGlslangToSpvTraverser::createUnaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, spv::Id operand, bool isFloat)
-{
- spv::Op unaryOp = spv::OpNop;
- int libCall = -1;
-
- switch (op) {
- case glslang::EOpNegative:
- if (isFloat)
- unaryOp = spv::OpFNegate;
- else
- unaryOp = spv::OpSNegate;
- break;
-
- case glslang::EOpLogicalNot:
- case glslang::EOpVectorLogicalNot:
- case glslang::EOpBitwiseNot:
- unaryOp = spv::OpNot;
- break;
-
- case glslang::EOpDeterminant:
- libCall = GLSL_STD_450::Determinant;
- break;
- case glslang::EOpMatrixInverse:
- libCall = GLSL_STD_450::MatrixInverse;
- break;
- case glslang::EOpTranspose:
- unaryOp = spv::OpTranspose;
- break;
-
- case glslang::EOpRadians:
- libCall = GLSL_STD_450::Radians;
- break;
- case glslang::EOpDegrees:
- libCall = GLSL_STD_450::Degrees;
- break;
- case glslang::EOpSin:
- libCall = GLSL_STD_450::Sin;
- break;
- case glslang::EOpCos:
- libCall = GLSL_STD_450::Cos;
- break;
- case glslang::EOpTan:
- libCall = GLSL_STD_450::Tan;
- break;
- case glslang::EOpAcos:
- libCall = GLSL_STD_450::Acos;
- break;
- case glslang::EOpAsin:
- libCall = GLSL_STD_450::Asin;
- break;
- case glslang::EOpAtan:
- libCall = GLSL_STD_450::Atan;
- break;
-
- case glslang::EOpAcosh:
- libCall = GLSL_STD_450::Acosh;
- break;
- case glslang::EOpAsinh:
- libCall = GLSL_STD_450::Asinh;
- break;
- case glslang::EOpAtanh:
- libCall = GLSL_STD_450::Atanh;
- break;
- case glslang::EOpTanh:
- libCall = GLSL_STD_450::Tanh;
- break;
- case glslang::EOpCosh:
- libCall = GLSL_STD_450::Cosh;
- break;
- case glslang::EOpSinh:
- libCall = GLSL_STD_450::Sinh;
- break;
-
- case glslang::EOpLength:
- libCall = GLSL_STD_450::Length;
- break;
- case glslang::EOpNormalize:
- libCall = GLSL_STD_450::Normalize;
- break;
-
- case glslang::EOpExp:
- libCall = GLSL_STD_450::Exp;
- break;
- case glslang::EOpLog:
- libCall = GLSL_STD_450::Log;
- break;
- case glslang::EOpExp2:
- libCall = GLSL_STD_450::Exp2;
- break;
- case glslang::EOpLog2:
- libCall = GLSL_STD_450::Log2;
- break;
- case glslang::EOpSqrt:
- libCall = GLSL_STD_450::Sqrt;
- break;
- case glslang::EOpInverseSqrt:
- libCall = GLSL_STD_450::InverseSqrt;
- break;
-
- case glslang::EOpFloor:
- libCall = GLSL_STD_450::Floor;
- break;
- case glslang::EOpTrunc:
- libCall = GLSL_STD_450::Trunc;
- break;
- case glslang::EOpRound:
- libCall = GLSL_STD_450::Round;
- break;
- case glslang::EOpRoundEven:
- libCall = GLSL_STD_450::RoundEven;
- break;
- case glslang::EOpCeil:
- libCall = GLSL_STD_450::Ceil;
- break;
- case glslang::EOpFract:
- libCall = GLSL_STD_450::Fract;
- break;
-
- case glslang::EOpIsNan:
- unaryOp = spv::OpIsNan;
- break;
- case glslang::EOpIsInf:
- unaryOp = spv::OpIsInf;
- break;
-
- case glslang::EOpFloatBitsToInt:
- libCall = GLSL_STD_450::FloatBitsToInt;
- break;
- case glslang::EOpFloatBitsToUint:
- libCall = GLSL_STD_450::FloatBitsToUint;
- break;
- case glslang::EOpIntBitsToFloat:
- libCall = GLSL_STD_450::IntBitsToFloat;
- break;
- case glslang::EOpUintBitsToFloat:
- libCall = GLSL_STD_450::UintBitsToFloat;
- break;
- case glslang::EOpPackSnorm2x16:
- libCall = GLSL_STD_450::PackSnorm2x16;
- break;
- case glslang::EOpUnpackSnorm2x16:
- libCall = GLSL_STD_450::UnpackSnorm2x16;
- break;
- case glslang::EOpPackUnorm2x16:
- libCall = GLSL_STD_450::PackUnorm2x16;
- break;
- case glslang::EOpUnpackUnorm2x16:
- libCall = GLSL_STD_450::UnpackUnorm2x16;
- break;
- case glslang::EOpPackHalf2x16:
- libCall = GLSL_STD_450::PackHalf2x16;
- break;
- case glslang::EOpUnpackHalf2x16:
- libCall = GLSL_STD_450::UnpackHalf2x16;
- break;
-
- case glslang::EOpDPdx:
- unaryOp = spv::OpDPdx;
- break;
- case glslang::EOpDPdy:
- unaryOp = spv::OpDPdy;
- break;
- case glslang::EOpFwidth:
- unaryOp = spv::OpFwidth;
- break;
- case glslang::EOpDPdxFine:
- unaryOp = spv::OpDPdxFine;
- break;
- case glslang::EOpDPdyFine:
- unaryOp = spv::OpDPdyFine;
- break;
- case glslang::EOpFwidthFine:
- unaryOp = spv::OpFwidthFine;
- break;
- case glslang::EOpDPdxCoarse:
- unaryOp = spv::OpDPdxCoarse;
- break;
- case glslang::EOpDPdyCoarse:
- unaryOp = spv::OpDPdyCoarse;
- break;
- case glslang::EOpFwidthCoarse:
- unaryOp = spv::OpFwidthCoarse;
- break;
-
- case glslang::EOpAny:
- unaryOp = spv::OpAny;
- break;
- case glslang::EOpAll:
- unaryOp = spv::OpAll;
- break;
-
- case glslang::EOpAbs:
- libCall = GLSL_STD_450::Abs;
- break;
- case glslang::EOpSign:
- libCall = GLSL_STD_450::Sign;
- break;
-
- default:
- return 0;
- }
-
- spv::Id id;
- if (libCall >= 0) {
- std::vector<spv::Id> args;
- args.push_back(operand);
- id = builder.createBuiltinCall(precision, typeId, stdBuiltins, libCall, args);
- } else
- id = builder.createUnaryOp(unaryOp, typeId, operand);
-
- builder.setPrecision(id, precision);
-
- return id;
-}
-
-spv::Id TGlslangToSpvTraverser::createConversion(glslang::TOperator op, spv::Decoration precision, spv::Id destType, spv::Id operand)
-{
- spv::Op convOp = spv::OpNop;
- spv::Id zero = 0;
- spv::Id one = 0;
-
- int vectorSize = builder.isVectorType(destType) ? builder.getNumTypeComponents(destType) : 0;
-
- switch (op) {
- case glslang::EOpConvIntToBool:
- case glslang::EOpConvUintToBool:
- zero = builder.makeUintConstant(0);
- zero = makeSmearedConstant(zero, vectorSize);
- return builder.createBinOp(spv::OpINotEqual, destType, operand, zero);
-
- case glslang::EOpConvFloatToBool:
- zero = builder.makeFloatConstant(0.0F);
- zero = makeSmearedConstant(zero, vectorSize);
- return builder.createBinOp(spv::OpFOrdNotEqual, destType, operand, zero);
-
- case glslang::EOpConvDoubleToBool:
- zero = builder.makeDoubleConstant(0.0);
- zero = makeSmearedConstant(zero, vectorSize);
- return builder.createBinOp(spv::OpFOrdNotEqual, destType, operand, zero);
-
- case glslang::EOpConvBoolToFloat:
- convOp = spv::OpSelect;
- zero = builder.makeFloatConstant(0.0);
- one = builder.makeFloatConstant(1.0);
- break;
- case glslang::EOpConvBoolToDouble:
- convOp = spv::OpSelect;
- zero = builder.makeDoubleConstant(0.0);
- one = builder.makeDoubleConstant(1.0);
- break;
- case glslang::EOpConvBoolToInt:
- zero = builder.makeIntConstant(0);
- one = builder.makeIntConstant(1);
- convOp = spv::OpSelect;
- break;
- case glslang::EOpConvBoolToUint:
- zero = builder.makeUintConstant(0);
- one = builder.makeUintConstant(1);
- convOp = spv::OpSelect;
- break;
-
- case glslang::EOpConvIntToFloat:
- case glslang::EOpConvIntToDouble:
- convOp = spv::OpConvertSToF;
- break;
-
- case glslang::EOpConvUintToFloat:
- case glslang::EOpConvUintToDouble:
- convOp = spv::OpConvertUToF;
- break;
-
- case glslang::EOpConvDoubleToFloat:
- case glslang::EOpConvFloatToDouble:
- convOp = spv::OpFConvert;
- break;
-
- case glslang::EOpConvFloatToInt:
- case glslang::EOpConvDoubleToInt:
- convOp = spv::OpConvertFToS;
- break;
-
- case glslang::EOpConvUintToInt:
- case glslang::EOpConvIntToUint:
- convOp = spv::OpBitcast;
- break;
-
- case glslang::EOpConvFloatToUint:
- case glslang::EOpConvDoubleToUint:
- convOp = spv::OpConvertFToU;
- break;
- default:
- break;
- }
-
- spv::Id result = 0;
- if (convOp == spv::OpNop)
- return result;
-
- if (convOp == spv::OpSelect) {
- zero = makeSmearedConstant(zero, vectorSize);
- one = makeSmearedConstant(one, vectorSize);
- result = builder.createTriOp(convOp, destType, operand, one, zero);
- } else
- result = builder.createUnaryOp(convOp, destType, operand);
-
- builder.setPrecision(result, precision);
-
- return result;
-}
-
-spv::Id TGlslangToSpvTraverser::makeSmearedConstant(spv::Id constant, int vectorSize)
-{
- if (vectorSize == 0)
- return constant;
-
- spv::Id vectorTypeId = builder.makeVectorType(builder.getTypeId(constant), vectorSize);
- std::vector<spv::Id> components;
- for (int c = 0; c < vectorSize; ++c)
- components.push_back(constant);
- return builder.makeCompositeConstant(vectorTypeId, components);
-}
-
-spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& operands)
-{
- spv::Op opCode = spv::OpNop;
- int libCall = -1;
-
- switch (op) {
- case glslang::EOpMin:
- libCall = GLSL_STD_450::Min;
- break;
- case glslang::EOpModf:
- libCall = GLSL_STD_450::Modf;
- break;
- case glslang::EOpMax:
- libCall = GLSL_STD_450::Max;
- break;
- case glslang::EOpPow:
- libCall = GLSL_STD_450::Pow;
- break;
- case glslang::EOpDot:
- opCode = spv::OpDot;
- break;
- case glslang::EOpAtan:
- libCall = GLSL_STD_450::Atan2;
- break;
-
- case glslang::EOpClamp:
- libCall = GLSL_STD_450::Clamp;
- break;
- case glslang::EOpMix:
- libCall = GLSL_STD_450::Mix;
- break;
- case glslang::EOpStep:
- libCall = GLSL_STD_450::Step;
- break;
- case glslang::EOpSmoothStep:
- libCall = GLSL_STD_450::SmoothStep;
- break;
-
- case glslang::EOpDistance:
- libCall = GLSL_STD_450::Distance;
- break;
- case glslang::EOpCross:
- libCall = GLSL_STD_450::Cross;
- break;
- case glslang::EOpFaceForward:
- libCall = GLSL_STD_450::FaceForward;
- break;
- case glslang::EOpReflect:
- libCall = GLSL_STD_450::Reflect;
- break;
- case glslang::EOpRefract:
- libCall = GLSL_STD_450::Refract;
- break;
- default:
- return 0;
- }
-
- spv::Id id = 0;
- if (libCall >= 0)
- id = builder.createBuiltinCall(precision, typeId, stdBuiltins, libCall, operands);
- else {
- switch (operands.size()) {
- case 0:
- // should all be handled by visitAggregate and createNoArgOperation
- assert(0);
- return 0;
- case 1:
- // should all be handled by createUnaryOperation
- assert(0);
- return 0;
- case 2:
- id = builder.createBinOp(opCode, typeId, operands[0], operands[1]);
- break;
- case 3:
- id = builder.createTernaryOp(opCode, typeId, operands[0], operands[1], operands[2]);
- break;
- default:
- // These do not exist yet
- assert(0 && "operation with more than 3 operands");
- break;
- }
- }
-
- builder.setPrecision(id, precision);
-
- return id;
-}
-
-// Intrinsics with no arguments, no return value, and no precision.
-spv::Id TGlslangToSpvTraverser::createNoArgOperation(glslang::TOperator op)
-{
- // TODO: get the barrier operands correct
-
- switch (op) {
- case glslang::EOpEmitVertex:
- builder.createNoResultOp(spv::OpEmitVertex);
- return 0;
- case glslang::EOpEndPrimitive:
- builder.createNoResultOp(spv::OpEndPrimitive);
- return 0;
- case glslang::EOpBarrier:
- builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsAllMemory);
- builder.createControlBarrier(spv::ExecutionScopeDevice);
- return 0;
- case glslang::EOpMemoryBarrier:
- builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsAllMemory);
- return 0;
- case glslang::EOpMemoryBarrierAtomicCounter:
- builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsAtomicCounterMemoryMask);
- return 0;
- case glslang::EOpMemoryBarrierBuffer:
- builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsUniformMemoryMask);
- return 0;
- case glslang::EOpMemoryBarrierImage:
- builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsImageMemoryMask);
- return 0;
- case glslang::EOpMemoryBarrierShared:
- builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsWorkgroupLocalMemoryMask);
- return 0;
- case glslang::EOpGroupMemoryBarrier:
- builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsWorkgroupGlobalMemoryMask);
- return 0;
- default:
- spv::MissingFunctionality("operation with no arguments");
- return 0;
- }
-}
-
-spv::Id TGlslangToSpvTraverser::getSymbolId(const glslang::TIntermSymbol* symbol)
-{
- std::map<int, spv::Id>::iterator iter;
- iter = symbolValues.find(symbol->getId());
- spv::Id id;
- if (symbolValues.end() != iter) {
- id = iter->second;
- return id;
- }
-
- // it was not found, create it
- id = createSpvVariable(symbol);
- symbolValues[symbol->getId()] = id;
-
- if (! symbol->getType().isStruct()) {
- addDecoration(id, TranslatePrecisionDecoration(symbol->getType()));
- addDecoration(id, TranslateInterpolationDecoration(symbol->getType()));
- if (symbol->getQualifier().hasLocation())
- builder.addDecoration(id, spv::DecorationLocation, symbol->getQualifier().layoutLocation);
- if (symbol->getQualifier().hasIndex())
- builder.addDecoration(id, spv::DecorationIndex, symbol->getQualifier().layoutIndex);
- if (symbol->getQualifier().hasComponent())
- builder.addDecoration(id, spv::DecorationComponent, symbol->getQualifier().layoutComponent);
- if (glslangIntermediate->getXfbMode()) {
- if (symbol->getQualifier().hasXfbStride())
- builder.addDecoration(id, spv::DecorationStride, symbol->getQualifier().layoutXfbStride);
- if (symbol->getQualifier().hasXfbBuffer())
- builder.addDecoration(id, spv::DecorationXfbBuffer, symbol->getQualifier().layoutXfbBuffer);
- if (symbol->getQualifier().hasXfbOffset())
- builder.addDecoration(id, spv::DecorationOffset, symbol->getQualifier().layoutXfbOffset);
- }
- }
-
- addDecoration(id, TranslateInvariantDecoration(symbol->getType()));
- if (symbol->getQualifier().hasStream())
- builder.addDecoration(id, spv::DecorationStream, symbol->getQualifier().layoutStream);
- if (symbol->getQualifier().hasSet())
- builder.addDecoration(id, spv::DecorationDescriptorSet, symbol->getQualifier().layoutSet);
- if (symbol->getQualifier().hasBinding())
- builder.addDecoration(id, spv::DecorationBinding, symbol->getQualifier().layoutBinding);
- if (glslangIntermediate->getXfbMode()) {
- if (symbol->getQualifier().hasXfbStride())
- builder.addDecoration(id, spv::DecorationStride, symbol->getQualifier().layoutXfbStride);
- if (symbol->getQualifier().hasXfbBuffer())
- builder.addDecoration(id, spv::DecorationXfbBuffer, symbol->getQualifier().layoutXfbBuffer);
- }
-
- // built-in variable decorations
- int builtIn = TranslateBuiltInDecoration(symbol->getQualifier().builtIn);
- if (builtIn != spv::BadValue)
- builder.addDecoration(id, spv::DecorationBuiltIn, builtIn);
-
- if (linkageOnly)
- builder.addDecoration(id, spv::DecorationNoStaticUse);
-
- return id;
-}
-
-void TGlslangToSpvTraverser::addDecoration(spv::Id id, spv::Decoration dec)
-{
- if (dec != spv::BadValue)
- builder.addDecoration(id, dec);
-}
-
-void TGlslangToSpvTraverser::addMemberDecoration(spv::Id id, int member, spv::Decoration dec)
-{
- if (dec != spv::BadValue)
- builder.addMemberDecoration(id, (unsigned)member, dec);
-}
-
-// Use 'consts' as the flattened glslang source of scalar constants to recursively
-// build the aggregate SPIR-V constant.
-//
-// If there are not enough elements present in 'consts', 0 will be substituted;
-// an empty 'consts' can be used to create a fully zeroed SPIR-V constant.
-//
-spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TType& glslangType, const glslang::TConstUnionArray& consts, int& nextConst)
-{
- // vector of constants for SPIR-V
- std::vector<spv::Id> spvConsts;
-
- // Type is used for struct and array constants
- spv::Id typeId = convertGlslangToSpvType(glslangType);
-
- if (glslangType.isArray()) {
- glslang::TType elementType;
- elementType.shallowCopy(glslangType); // TODO: desktop arrays of arrays functionality will need a deeper copy to avoid modifying the original
- elementType.dereference();
- for (int i = 0; i < glslangType.getArraySize(); ++i)
- spvConsts.push_back(createSpvConstant(elementType, consts, nextConst));
- } else if (glslangType.isMatrix()) {
- glslang::TType vectorType;
- vectorType.shallowCopy(glslangType);
- vectorType.dereference();
- for (int col = 0; col < glslangType.getMatrixCols(); ++col)
- spvConsts.push_back(createSpvConstant(vectorType, consts, nextConst));
- } else if (glslangType.getStruct()) {
- glslang::TVector<glslang::TTypeLoc>::const_iterator iter;
- for (iter = glslangType.getStruct()->begin(); iter != glslangType.getStruct()->end(); ++iter)
- spvConsts.push_back(createSpvConstant(*iter->type, consts, nextConst));
- } else if (glslangType.isVector()) {
- for (unsigned int i = 0; i < (unsigned int)glslangType.getVectorSize(); ++i) {
- bool zero = nextConst >= consts.size();
- switch (glslangType.getBasicType()) {
- case glslang::EbtInt:
- spvConsts.push_back(builder.makeIntConstant(zero ? 0 : consts[nextConst].getIConst()));
- break;
- case glslang::EbtUint:
- spvConsts.push_back(builder.makeUintConstant(zero ? 0 : consts[nextConst].getUConst()));
- break;
- case glslang::EbtFloat:
- spvConsts.push_back(builder.makeFloatConstant(zero ? 0.0F : (float)consts[nextConst].getDConst()));
- break;
- case glslang::EbtDouble:
- spvConsts.push_back(builder.makeDoubleConstant(zero ? 0.0 : consts[nextConst].getDConst()));
- break;
- case glslang::EbtBool:
- spvConsts.push_back(builder.makeBoolConstant(zero ? false : consts[nextConst].getBConst()));
- break;
- default:
- spv::MissingFunctionality("constant vector type");
- break;
- }
- ++nextConst;
- }
- } else {
- // we have a non-aggregate (scalar) constant
- bool zero = nextConst >= consts.size();
- spv::Id scalar = 0;
- switch (glslangType.getBasicType()) {
- case glslang::EbtInt:
- scalar = builder.makeIntConstant(zero ? 0 : consts[nextConst].getIConst());
- break;
- case glslang::EbtUint:
- scalar = builder.makeUintConstant(zero ? 0 : consts[nextConst].getUConst());
- break;
- case glslang::EbtFloat:
- scalar = builder.makeFloatConstant(zero ? 0.0F : (float)consts[nextConst].getDConst());
- break;
- case glslang::EbtDouble:
- scalar = builder.makeDoubleConstant(zero ? 0.0 : consts[nextConst].getDConst());
- break;
- case glslang::EbtBool:
- scalar = builder.makeBoolConstant(zero ? false : consts[nextConst].getBConst());
- break;
- default:
- spv::MissingFunctionality("constant scalar type");
- break;
- }
- ++nextConst;
- return scalar;
- }
-
- return builder.makeCompositeConstant(typeId, spvConsts);
-}
-
-}; // end anonymous namespace
-
-namespace glslang {
-
-// Write SPIR-V out to a binary file
-void OutputSpv(const std::vector<unsigned int>& spirv, const char* baseName)
-{
- std::ofstream out;
- std::string fileName(baseName);
- fileName.append(".spv");
- out.open(fileName.c_str(), std::ios::binary | std::ios::out);
- for (int i = 0; i < (int)spirv.size(); ++i) {
- unsigned int word = spirv[i];
- out.write((const char*)&word, 4);
- }
- out.close();
-}
-
-//
-// Set up the glslang traversal
-//
-void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv)
-{
- TIntermNode* root = intermediate.getTreeRoot();
-
- if (root == 0)
- return;
-
- glslang::GetThreadPoolAllocator().push();
-
- TGlslangToSpvTraverser it(&intermediate);
-
- root->traverse(&it);
-
- it.dumpSpv(spirv);
-
- glslang::GetThreadPoolAllocator().pop();
-}
-
-}; // end namespace glslang
+//
+//Copyright (C) 2014 LunarG, Inc.
+//
+//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.
+
+//
+// Author: John Kessenich, LunarG
+//
+// Visit the nodes in the glslang intermediate tree representation to
+// translate them to SPIR-V.
+//
+
+#include "spirv.h"
+#include "GlslangToSpv.h"
+#include "SpvBuilder.h"
+#include "GLSL450Lib.h"
+
+// Glslang includes
+#include "glslang/MachineIndependent/localintermediate.h"
+#include "glslang/MachineIndependent/SymbolTable.h"
+
+#include <string>
+#include <map>
+#include <list>
+#include <vector>
+#include <stack>
+#include <fstream>
+
+namespace {
+
+const int GlslangMagic = 0x51a;
+
+//
+// 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(const glslang::TIntermediate*);
+ 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 dumpSpv(std::vector<unsigned int>& out) { builder.dump(out); }
+
+protected:
+ spv::Id createSpvVariable(const glslang::TIntermSymbol*);
+ spv::Id getSampledType(const glslang::TSampler&);
+ spv::Id convertGlslangToSpvType(const glslang::TType& type);
+
+ bool isShaderEntrypoint(const glslang::TIntermAggregate* node);
+ 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::TIntermSequence& glslangArguments, std::vector<spv::Id>& arguments);
+ spv::Id handleBuiltInFunctionCall(const glslang::TIntermAggregate*);
+ spv::Id handleUserFunctionCall(const glslang::TIntermAggregate*);
+
+ spv::Id createBinaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, spv::Id left, spv::Id right, glslang::TBasicType typeProxy, bool reduceComparison = true);
+ spv::Id createUnaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, spv::Id operand, bool isFloat);
+ spv::Id createConversion(glslang::TOperator op, spv::Decoration precision, spv::Id destTypeId, spv::Id operand);
+ spv::Id makeSmearedConstant(spv::Id constant, int vectorSize);
+ spv::Id createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& operands);
+ spv::Id createNoArgOperation(glslang::TOperator op);
+ spv::Id getSymbolId(const glslang::TIntermSymbol* node);
+ void addDecoration(spv::Id id, spv::Decoration dec);
+ void addMemberDecoration(spv::Id id, int member, spv::Decoration dec);
+ spv::Id createSpvConstant(const glslang::TType& type, const glslang::TConstUnionArray&, int& nextConst);
+
+ spv::Function* shaderEntry;
+ int sequenceDepth;
+
+ // There is a 1:1 mapping between a spv builder and a module; this is thread safe
+ spv::Builder builder;
+ bool inMain;
+ bool mainTerminated;
+ bool linkageOnly;
+ const glslang::TIntermediate* glslangIntermediate;
+ spv::Id stdBuiltins;
+
+ std::map<int, spv::Id> symbolValues;
+ std::set<int> constReadOnlyParameters; // set of formal function parameters that have glslang qualifier constReadOnly, so we know they are not local function "const" that are write-once
+ std::map<std::string, spv::Function*> functionMap;
+ std::map<const glslang::TTypeList*, spv::Id> structMap;
+ std::map<const glslang::TTypeList*, std::vector<int> > memberRemapper; // for mapping glslang block indices to spv indices (e.g., due to hidden members)
+ std::stack<bool> breakForLoop; // false means break for switch
+ std::stack<glslang::TIntermTyped*> loopTerminal; // code from the last part of a for loop: for(...; ...; terminal), needed for e.g., continue };
+};
+
+//
+// Helper functions for translating glslang representations to SPIR-V enumerants.
+//
+
+// Translate glslang profile to SPIR-V source language.
+spv::SourceLanguage TranslateSourceLanguage(EProfile profile)
+{
+ switch (profile) {
+ case ENoProfile:
+ case ECoreProfile:
+ case ECompatibilityProfile:
+ return spv::SourceLanguageGLSL;
+ case EEsProfile:
+ return spv::SourceLanguageESSL;
+ 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 EShLangTessControl: return spv::ExecutionModelTessellationControl;
+ case EShLangTessEvaluation: return spv::ExecutionModelTessellationEvaluation;
+ case EShLangGeometry: return spv::ExecutionModelGeometry;
+ case EShLangFragment: return spv::ExecutionModelFragment;
+ case EShLangCompute: return spv::ExecutionModelGLCompute;
+ default:
+ spv::MissingFunctionality("GLSL stage");
+ return spv::ExecutionModelFragment;
+ }
+}
+
+// Translate glslang type to SPIR-V storage class.
+spv::StorageClass TranslateStorageClass(const glslang::TType& type)
+{
+ if (type.getQualifier().isPipeInput())
+ return spv::StorageClassInput;
+ else if (type.getQualifier().isPipeOutput())
+ return spv::StorageClassOutput;
+ else if (type.getQualifier().isUniformOrBuffer()) {
+ if (type.getBasicType() == glslang::EbtBlock)
+ return spv::StorageClassUniform;
+ else
+ return spv::StorageClassUniformConstant;
+ // TODO: how are we distuingishing between default and non-default non-writable uniforms? Do default uniforms even exist?
+ } else {
+ switch (type.getQualifier().storage) {
+ case glslang::EvqShared: return spv::StorageClassWorkgroupLocal; break;
+ case glslang::EvqGlobal: return spv::StorageClassPrivateGlobal;
+ case glslang::EvqConstReadOnly: return spv::StorageClassFunction;
+ case glslang::EvqTemporary: return spv::StorageClassFunction;
+ default:
+ spv::MissingFunctionality("unknown glslang storage class");
+ return spv::StorageClassFunction;
+ }
+ }
+}
+
+// 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;
+ default:
+ spv::MissingFunctionality("unknown sampler dimension");
+ return spv::Dim2D;
+ }
+}
+
+// Translate glslang type to SPIR-V precision decorations.
+spv::Decoration TranslatePrecisionDecoration(const glslang::TType& type)
+{
+ switch (type.getQualifier().precision) {
+ case glslang::EpqLow: return spv::DecorationPrecisionLow;
+ case glslang::EpqMedium: return spv::DecorationPrecisionMedium;
+ case glslang::EpqHigh: return spv::DecorationPrecisionHigh;
+ default:
+ return spv::NoPrecision;
+ }
+}
+
+// Translate glslang type to SPIR-V block decorations.
+spv::Decoration TranslateBlockDecoration(const glslang::TType& type)
+{
+ if (type.getBasicType() == glslang::EbtBlock) {
+ switch (type.getQualifier().storage) {
+ case glslang::EvqUniform: return spv::DecorationBlock;
+ case glslang::EvqBuffer: return spv::DecorationBufferBlock;
+ case glslang::EvqVaryingIn: return spv::DecorationBlock;
+ case glslang::EvqVaryingOut: return spv::DecorationBlock;
+ default:
+ spv::MissingFunctionality("kind of block");
+ break;
+ }
+ }
+
+ return (spv::Decoration)spv::BadValue;
+}
+
+// Translate glslang type to SPIR-V layout decorations.
+spv::Decoration TranslateLayoutDecoration(const glslang::TType& type)
+{
+ if (type.isMatrix()) {
+ switch (type.getQualifier().layoutMatrix) {
+ case glslang::ElmRowMajor:
+ return spv::DecorationRowMajor;
+ default:
+ return spv::DecorationColMajor;
+ }
+ } else {
+ switch (type.getBasicType()) {
+ default:
+ return (spv::Decoration)spv::BadValue;
+ 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::ElpStd140: return spv::DecorationGLSLStd140;
+ case glslang::ElpStd430: return spv::DecorationGLSLStd430;
+ case glslang::ElpPacked: return spv::DecorationGLSLPacked;
+ default:
+ spv::MissingFunctionality("uniform block layout");
+ return spv::DecorationGLSLShared;
+ }
+ case glslang::EvqVaryingIn:
+ case glslang::EvqVaryingOut:
+ if (type.getQualifier().layoutPacking != glslang::ElpNone)
+ spv::MissingFunctionality("in/out block layout");
+ return (spv::Decoration)spv::BadValue;
+ default:
+ spv::MissingFunctionality("block storage qualification");
+ return (spv::Decoration)spv::BadValue;
+ }
+ }
+ }
+}
+
+// Translate glslang type to SPIR-V interpolation decorations.
+spv::Decoration TranslateInterpolationDecoration(const glslang::TType& type)
+{
+ if (type.getQualifier().smooth)
+ return spv::DecorationSmooth;
+ if (type.getQualifier().nopersp)
+ return spv::DecorationNoperspective;
+ else if (type.getQualifier().patch)
+ return spv::DecorationPatch;
+ else if (type.getQualifier().flat)
+ return spv::DecorationFlat;
+ else if (type.getQualifier().centroid)
+ return spv::DecorationCentroid;
+ else if (type.getQualifier().sample)
+ return spv::DecorationSample;
+ else
+ return (spv::Decoration)spv::BadValue;
+}
+
+// If glslang type is invaraiant, return SPIR-V invariant decoration.
+spv::Decoration TranslateInvariantDecoration(const glslang::TType& type)
+{
+ if (type.getQualifier().invariant)
+ return spv::DecorationInvariant;
+ else
+ return (spv::Decoration)spv::BadValue;
+}
+
+// Translate glslang built-in variable to SPIR-V built in decoration.
+spv::BuiltIn TranslateBuiltInDecoration(glslang::TBuiltInVariable builtIn)
+{
+ switch (builtIn) {
+ case glslang::EbvPosition: return spv::BuiltInPosition;
+ case glslang::EbvPointSize: return spv::BuiltInPointSize;
+ case glslang::EbvClipVertex: return spv::BuiltInClipVertex;
+ case glslang::EbvClipDistance: return spv::BuiltInClipDistance;
+ case glslang::EbvCullDistance: return spv::BuiltInCullDistance;
+ case glslang::EbvVertexId: return spv::BuiltInVertexId;
+ case glslang::EbvInstanceId: return spv::BuiltInInstanceId;
+ case glslang::EbvPrimitiveId: return spv::BuiltInPrimitiveId;
+ case glslang::EbvInvocationId: return spv::BuiltInInvocationId;
+ case glslang::EbvLayer: return spv::BuiltInLayer;
+ case glslang::EbvViewportIndex: return spv::BuiltInViewportIndex;
+ 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::EbvFragCoord: return spv::BuiltInFragCoord;
+ case glslang::EbvPointCoord: return spv::BuiltInPointCoord;
+ case glslang::EbvFace: return spv::BuiltInFrontFacing;
+ case glslang::EbvSampleId: return spv::BuiltInSampleId;
+ case glslang::EbvSamplePosition: return spv::BuiltInSamplePosition;
+ case glslang::EbvSampleMask: return spv::BuiltInSampleMask;
+ case glslang::EbvFragColor: return spv::BuiltInFragColor;
+ case glslang::EbvFragData: return spv::BuiltInFragColor;
+ case glslang::EbvFragDepth: return spv::BuiltInFragDepth;
+ 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;
+ default: return (spv::BuiltIn)spv::BadValue;
+ }
+}
+
+//
+// Implement the TGlslangToSpvTraverser class.
+//
+
+TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* glslangIntermediate)
+ : TIntermTraverser(true, false, true), shaderEntry(0), sequenceDepth(0),
+ builder(GlslangMagic),
+ inMain(false), mainTerminated(false), linkageOnly(false),
+ glslangIntermediate(glslangIntermediate)
+{
+ spv::ExecutionModel executionModel = TranslateExecutionModel(glslangIntermediate->getStage());
+
+ builder.clearAccessChain();
+ builder.setSource(TranslateSourceLanguage(glslangIntermediate->getProfile()), glslangIntermediate->getVersion());
+ stdBuiltins = builder.import("GLSL.std.450");
+ builder.setMemoryModel(spv::AddressingModelLogical, spv::MemoryModelGLSL450);
+ shaderEntry = builder.makeMain();
+ builder.addEntryPoint(executionModel, shaderEntry);
+
+ // Add the source extensions
+ const std::set<std::string>& sourceExtensions = glslangIntermediate->getRequestedExtensions();
+ for (std::set<std::string>::const_iterator it = sourceExtensions.begin(); it != sourceExtensions.end(); ++it)
+ builder.addSourceExtension(it->c_str());
+
+ // Add the top-level modes for this shader.
+
+ if (glslangIntermediate->getXfbMode())
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeXfb);
+
+ unsigned int mode;
+ switch (glslangIntermediate->getStage()) {
+ case EShLangVertex:
+ break;
+
+ case EShLangTessControl:
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, glslangIntermediate->getVertices());
+ break;
+
+ case EShLangTessEvaluation:
+ switch (glslangIntermediate->getInputPrimitive()) {
+ case glslang::ElgTriangles: mode = spv::ExecutionModeInputTriangles; break;
+ case glslang::ElgQuads: mode = spv::ExecutionModeInputQuads; break;
+ case glslang::ElgIsolines: mode = spv::ExecutionModeInputIsolines; break;
+ default: mode = spv::BadValue; break;
+ }
+ if (mode != spv::BadValue)
+ builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
+
+ // TODO
+ //builder.addExecutionMode(spv::VertexSpacingMdName, glslangIntermediate->getVertexSpacing());
+ //builder.addExecutionMode(spv::VertexOrderMdName, glslangIntermediate->getVertexOrder());
+ //builder.addExecutionMode(spv::PointModeMdName, glslangIntermediate->getPointMode());
+ break;
+
+ case EShLangGeometry:
+ 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::ExecutionModeInputTriangles; break;
+ case glslang::ElgTrianglesAdjacency: mode = spv::ExecutionModeInputTrianglesAdjacency; break;
+ default: mode = spv::BadValue; break;
+ }
+ if (mode != spv::BadValue)
+ 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::BadValue; break;
+ }
+ if (mode != spv::BadValue)
+ builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, glslangIntermediate->getVertices());
+ break;
+
+ case EShLangFragment:
+ if (glslangIntermediate->getPixelCenterInteger())
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModePixelCenterInteger);
+ if (glslangIntermediate->getOriginUpperLeft())
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeOriginUpperLeft);
+ break;
+
+ case EShLangCompute:
+ break;
+
+ default:
+ break;
+ }
+
+}
+
+TGlslangToSpvTraverser::~TGlslangToSpvTraverser()
+{
+ if (! mainTerminated) {
+ spv::Block* lastMainBlock = shaderEntry->getLastBlock();
+ builder.setBuildPoint(lastMainBlock);
+ builder.leaveFunction(true);
+ }
+}
+
+//
+// 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)
+{
+ // 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 (! linkageOnly) {
+ // 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 "const in" arguments to a function, which are an intermediate object.
+ // See comments in handleUserFunctionCall().
+ glslang::TStorageQualifier qualifier = symbol->getQualifier().storage;
+ if (qualifier == glslang::EvqConstReadOnly && constReadOnlyParameters.find(symbol->getId()) != constReadOnlyParameters.end())
+ builder.setAccessChainRValue(id);
+ else
+ builder.setAccessChainLValue(id);
+ }
+}
+
+bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::TIntermBinary* node)
+{
+ // 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 = builder.accessChainLoad(TranslatePrecisionDecoration(node->getRight()->getType()));
+
+ if (node->getOp() != glslang::EOpAssign) {
+ // the left is also an r-value
+ builder.setAccessChain(lValue);
+ spv::Id leftRValue = builder.accessChainLoad(TranslatePrecisionDecoration(node->getLeft()->getType()));
+
+ // do the operation
+ rValue = createBinaryOperation(node->getOp(), TranslatePrecisionDecoration(node->getType()),
+ convertGlslangToSpvType(node->getType()), leftRValue, rValue,
+ node->getType().getBasicType());
+
+ // these all need their counterparts in createBinaryOperation()
+ if (rValue == 0)
+ spv::MissingFunctionality("createBinaryOperation");
+ }
+
+ // store the result
+ builder.setAccessChain(lValue);
+ builder.accessChainStore(rValue);
+
+ // assignments are expressions having an rValue after they are evaluated...
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(rValue);
+ }
+ return false;
+ case glslang::EOpIndexDirect:
+ case glslang::EOpIndexDirectStruct:
+ {
+ // Get the left part of the access chain.
+ node->getLeft()->traverse(this);
+
+ // Add the next element in the chain
+
+ int index = 0;
+ if (node->getRight()->getAsConstantUnion() == 0)
+ spv::MissingFunctionality("direct index without a constant node");
+ else
+ index = node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();
+
+ 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()];
+ if (remapper.size() == 0)
+ spv::MissingFunctionality("block without member remapping");
+ else
+ index = remapper[index];
+ }
+
+ 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(node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst());
+ builder.accessChainPushSwizzle(swizzle);
+ } else {
+ // normal case for indexing array or structure or block
+ builder.accessChainPush(builder.makeIntConstant(index), convertGlslangToSpvType(node->getType()));
+ }
+ }
+ return false;
+ case glslang::EOpIndexIndirect:
+ {
+ // Structure or array or vector indirection.
+ // Will use native SPIR-V access-chain for struct 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 = builder.accessChainLoad(TranslatePrecisionDecoration(node->getRight()->getType()));
+
+ // restore the saved access chain
+ builder.setAccessChain(partial);
+
+ if (! node->getLeft()->getType().isArray() && node->getLeft()->getType().isVector())
+ builder.accessChainPushComponent(index);
+ else
+ builder.accessChainPush(index, convertGlslangToSpvType(node->getType()));
+ }
+ return false;
+ case glslang::EOpVectorSwizzle:
+ {
+ node->getLeft()->traverse(this);
+ glslang::TIntermSequence& swizzleSequence = node->getRight()->getAsAggregate()->getSequence();
+ std::vector<unsigned> swizzle;
+ for (int i = 0; i < (int)swizzleSequence.size(); ++i)
+ swizzle.push_back(swizzleSequence[i]->getAsConstantUnion()->getConstArray()[0].getIConst());
+ builder.accessChainPushSwizzle(swizzle);
+ }
+ return false;
+ default:
+ break;
+ }
+
+ // Assume generic binary op...
+
+ // Get the operands
+ builder.clearAccessChain();
+ node->getLeft()->traverse(this);
+ spv::Id left = builder.accessChainLoad(TranslatePrecisionDecoration(node->getLeft()->getType()));
+
+ builder.clearAccessChain();
+ node->getRight()->traverse(this);
+ spv::Id right = builder.accessChainLoad(TranslatePrecisionDecoration(node->getRight()->getType()));
+
+ spv::Id result;
+ spv::Decoration precision = TranslatePrecisionDecoration(node->getType());
+
+ result = createBinaryOperation(node->getOp(), precision,
+ convertGlslangToSpvType(node->getType()), left, right,
+ node->getLeft()->getType().getBasicType());
+
+ if (! result) {
+ spv::MissingFunctionality("glslang binary operation");
+ } else {
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(result);
+
+ return false;
+ }
+
+ return true;
+}
+
+bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node)
+{
+ builder.clearAccessChain();
+ node->getOperand()->traverse(this);
+ spv::Id operand = builder.accessChainLoad(TranslatePrecisionDecoration(node->getOperand()->getType()));
+
+ spv::Decoration precision = TranslatePrecisionDecoration(node->getType());
+
+ // it could be a conversion
+ spv::Id result = createConversion(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operand);
+
+ // if not, then possibly an operation
+ if (! result)
+ result = createUnaryOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operand, node->getBasicType() == glslang::EbtFloat || node->getBasicType() == glslang::EbtDouble);
+
+ if (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 = node->getBasicType() == glslang::EbtFloat ?
+ builder.makeFloatConstant(1.0F) :
+ 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, TranslatePrecisionDecoration(node->getType()),
+ convertGlslangToSpvType(node->getType()), operand, one,
+ node->getType().getBasicType());
+ if (result == 0)
+ spv::MissingFunctionality("createBinaryOperation for unary");
+
+ // 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;
+
+ case glslang::EOpEmitStreamVertex:
+ builder.createNoResultOp(spv::OpEmitStreamVertex, operand);
+ return false;
+ case glslang::EOpEndStreamPrimitive:
+ builder.createNoResultOp(spv::OpEndStreamPrimitive, operand);
+ return false;
+
+ default:
+ spv::MissingFunctionality("glslang unary");
+ break;
+ }
+
+ return true;
+}
+
+bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TIntermAggregate* node)
+{
+ spv::Id result;
+ glslang::TOperator binOp = glslang::EOpNull;
+ bool reduceComparison = true;
+ bool isMatrix = false;
+ bool noReturnValue = false;
+
+ assert(node->getOp());
+
+ spv::Decoration precision = TranslatePrecisionDecoration(node->getType());
+
+ 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 entry of main(), 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 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)) {
+ inMain = true;
+ builder.setBuildPoint(shaderEntry->getLastBlock());
+ } else {
+ handleFunctionEntry(node);
+ }
+ } else {
+ if (inMain)
+ mainTerminated = true;
+ builder.leaveFunction(inMain);
+ inMain = 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:
+ {
+ if (node->isUserDefined())
+ result = handleUserFunctionCall(node);
+ else
+ result = handleBuiltInFunctionCall(node);
+
+ if (! result) {
+ spv::MissingFunctionality("glslang function call");
+ glslang::TConstUnionArray emptyConsts;
+ int nextConst = 0;
+ result = createSpvConstant(node->getType(), emptyConsts, nextConst);
+ }
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(result);
+
+ 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:
+ 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::EOpConstructBool:
+ case glslang::EOpConstructBVec2:
+ case glslang::EOpConstructBVec3:
+ case glslang::EOpConstructBVec4:
+ 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::EOpConstructStruct:
+ {
+ std::vector<spv::Id> arguments;
+ translateArguments(node->getSequence(), arguments);
+ spv::Id resultTypeId = convertGlslangToSpvType(node->getType());
+ spv::Id constructed;
+ if (node->getOp() == glslang::EOpConstructStruct || node->getType().isArray()) {
+ std::vector<spv::Id> constituents;
+ for (int c = 0; c < (int)arguments.size(); ++c)
+ constituents.push_back(arguments[c]);
+ constructed = builder.createCompositeConstruct(resultTypeId, constituents);
+ } else {
+ if (isMatrix)
+ constructed = builder.createMatrixConstructor(precision, arguments, resultTypeId);
+ else
+ constructed = builder.createConstructor(precision, arguments, resultTypeId);
+ }
+
+ 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:
+ // compontent-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()->isVector())
+ 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;
+ case glslang::EOpArrayLength:
+ {
+ glslang::TIntermTyped* typedNode = node->getSequence()[0]->getAsTyped();
+ assert(typedNode);
+ spv::Id length = builder.makeIntConstant(typedNode->getType().getArraySize());
+
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(length);
+
+ return false;
+ }
+ 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:
+ noReturnValue = true;
+ // These all have 0 operands and will naturally finish up in the code below for 0 operands
+ break;
+
+ 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 = builder.accessChainLoad(TranslatePrecisionDecoration(left->getType()));
+
+ builder.clearAccessChain();
+ right->traverse(this);
+ spv::Id rightId = builder.accessChainLoad(TranslatePrecisionDecoration(right->getType()));
+
+ result = createBinaryOperation(binOp, precision,
+ convertGlslangToSpvType(node->getType()), leftId, rightId,
+ left->getType().getBasicType(), reduceComparison);
+
+ // code above should only make binOp that exists in createBinaryOperation
+ if (result == 0)
+ spv::MissingFunctionality("createBinaryOperation for aggregate");
+
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(result);
+
+ return false;
+ }
+
+ glslang::TIntermSequence& glslangOperands = node->getSequence();
+ std::vector<spv::Id> operands;
+ for (int arg = 0; arg < (int)glslangOperands.size(); ++arg) {
+ builder.clearAccessChain();
+ glslangOperands[arg]->traverse(this);
+
+ // special case l-value operands; there are just a few
+ bool lvalue = false;
+ switch (node->getOp()) {
+ //case glslang::EOpFrexp:
+ case glslang::EOpModf:
+ if (arg == 1)
+ lvalue = true;
+ break;
+ //case glslang::EOpUAddCarry:
+ //case glslang::EOpUSubBorrow:
+ //case glslang::EOpUMulExtended:
+ default:
+ break;
+ }
+ if (lvalue)
+ operands.push_back(builder.accessChainGetLValue());
+ else
+ operands.push_back(builder.accessChainLoad(TranslatePrecisionDecoration(glslangOperands[arg]->getAsTyped()->getType())));
+ }
+ switch (glslangOperands.size()) {
+ case 0:
+ result = createNoArgOperation(node->getOp());
+ break;
+ case 1:
+ result = createUnaryOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands.front(), node->getType().getBasicType() == glslang::EbtFloat || node->getType().getBasicType() == glslang::EbtDouble);
+ break;
+ default:
+ result = createMiscOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands);
+ break;
+ }
+
+ if (noReturnValue)
+ return false;
+
+ if (! result) {
+ spv::MissingFunctionality("glslang aggregate");
+ return true;
+ } else {
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(result);
+ return false;
+ }
+}
+
+bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang::TIntermSelection* node)
+{
+ // This path handles both if-then-else and ?:
+ // The if-then-else has a node type of void, while
+ // ?: has a non-void node type
+ spv::Id result = 0;
+ if (node->getBasicType() != glslang::EbtVoid) {
+ // don't handle this as just on-the-fly temporaries, because there will be two names
+ // and better to leave SSA to later passes
+ result = builder.createVariable(spv::StorageClassFunction, convertGlslangToSpvType(node->getType()));
+ }
+
+ // emit the condition before doing anything with selection
+ node->getCondition()->traverse(this);
+
+ // make an "if" based on the value created by the condition
+ spv::Builder::If ifBuilder(builder.accessChainLoad(spv::NoPrecision), builder);
+
+ if (node->getTrueBlock()) {
+ // emit the "then" statement
+ node->getTrueBlock()->traverse(this);
+ if (result)
+ builder.createStore(builder.accessChainLoad(TranslatePrecisionDecoration(node->getTrueBlock()->getAsTyped()->getType())), result);
+ }
+
+ if (node->getFalseBlock()) {
+ ifBuilder.makeBeginElse();
+ // emit the "else" statement
+ node->getFalseBlock()->traverse(this);
+ if (result)
+ builder.createStore(builder.accessChainLoad(TranslatePrecisionDecoration(node->getFalseBlock()->getAsTyped()->getType())), result);
+ }
+
+ ifBuilder.makeEndIf();
+
+ if (result) {
+ // GLSL only has r-values as the result of a :?, but
+ // if we have an l-value, that can be more efficient if it will
+ // become the base of a complex r-value expression, because the
+ // next layer copies r-values into memory to use the access-chain mechanism
+ builder.clearAccessChain();
+ builder.setAccessChainLValue(result);
+ }
+
+ return false;
+}
+
+bool TGlslangToSpvTraverser::visitSwitch(glslang::TVisit /* visit */, glslang::TIntermSwitch* node)
+{
+ // emit and get the condition before doing anything with switch
+ node->getCondition()->traverse(this);
+ spv::Id selector = builder.accessChainLoad(TranslatePrecisionDecoration(node->getCondition()->getAsTyped()->getType()));
+
+ // browse the children to sort out code segments
+ int defaultSegment = -1;
+ std::vector<TIntermNode*> codeSegments;
+ glslang::TIntermSequence& sequence = node->getBody()->getSequence();
+ std::vector<int> caseValues;
+ std::vector<int> valueIndexToSegment(sequence.size()); // note: probably not all are used, it is an overestimate
+ for (glslang::TIntermSequence::iterator c = sequence.begin(); c != sequence.end(); ++c) {
+ TIntermNode* child = *c;
+ if (child->getAsBranchNode() && child->getAsBranchNode()->getFlowOp() == glslang::EOpDefault)
+ defaultSegment = codeSegments.size();
+ else if (child->getAsBranchNode() && child->getAsBranchNode()->getFlowOp() == glslang::EOpCase) {
+ valueIndexToSegment[caseValues.size()] = codeSegments.size();
+ caseValues.push_back(child->getAsBranchNode()->getExpression()->getAsConstantUnion()->getConstArray()[0].getIConst());
+ } else
+ codeSegments.push_back(child);
+ }
+
+ // handle the case where the last code segment is missing, due to no code
+ // statements between the last case and the end of the switch statement
+ if ((caseValues.size() && (int)codeSegments.size() == valueIndexToSegment[caseValues.size() - 1]) ||
+ (int)codeSegments.size() == defaultSegment)
+ codeSegments.push_back(nullptr);
+
+ // make the switch statement
+ std::vector<spv::Block*> segmentBlocks; // returned, as the blocks allocated in the call
+ builder.makeSwitch(selector, codeSegments.size(), caseValues, valueIndexToSegment, defaultSegment, segmentBlocks);
+
+ // emit all the code in the segments
+ breakForLoop.push(false);
+ for (unsigned int s = 0; s < codeSegments.size(); ++s) {
+ builder.nextSwitchSegment(segmentBlocks, s);
+ if (codeSegments[s])
+ codeSegments[s]->traverse(this);
+ else
+ builder.addSwitchBreak();
+ }
+ breakForLoop.pop();
+
+ builder.endSwitch(segmentBlocks);
+
+ return false;
+}
+
+void TGlslangToSpvTraverser::visitConstantUnion(glslang::TIntermConstantUnion* node)
+{
+ int nextConst = 0;
+ spv::Id constant = createSpvConstant(node->getType(), node->getConstArray(), nextConst);
+
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(constant);
+}
+
+bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIntermLoop* node)
+{
+ // body emission needs to know what the for-loop terminal is when it sees a "continue"
+ loopTerminal.push(node->getTerminal());
+
+ builder.makeNewLoop();
+
+ bool bodyOut = false;
+ if (! node->testFirst()) {
+ builder.endLoopHeaderWithoutTest();
+ if (node->getBody()) {
+ breakForLoop.push(true);
+ node->getBody()->traverse(this);
+ breakForLoop.pop();
+ }
+ bodyOut = true;
+ builder.createBranchToLoopTest();
+ }
+
+ if (node->getTest()) {
+ node->getTest()->traverse(this);
+ // the AST only contained the test computation, not the branch, we have to add it
+ spv::Id condition = builder.accessChainLoad(TranslatePrecisionDecoration(node->getTest()->getType()));
+ builder.createLoopTestBranch(condition);
+ }
+
+ if (! bodyOut && node->getBody()) {
+ breakForLoop.push(true);
+ node->getBody()->traverse(this);
+ breakForLoop.pop();
+ }
+
+ if (loopTerminal.top())
+ loopTerminal.top()->traverse(this);
+
+ builder.closeLoop();
+
+ loopTerminal.pop();
+
+ return false;
+}
+
+bool TGlslangToSpvTraverser::visitBranch(glslang::TVisit /* visit */, glslang::TIntermBranch* node)
+{
+ if (node->getExpression())
+ node->getExpression()->traverse(this);
+
+ switch (node->getFlowOp()) {
+ case glslang::EOpKill:
+ builder.makeDiscard();
+ break;
+ case glslang::EOpBreak:
+ if (breakForLoop.top())
+ builder.createLoopExit();
+ else
+ builder.addSwitchBreak();
+ break;
+ case glslang::EOpContinue:
+ if (loopTerminal.top())
+ loopTerminal.top()->traverse(this);
+ builder.createLoopContinue();
+ break;
+ case glslang::EOpReturn:
+ if (inMain)
+ builder.makeMainReturn();
+ else if (node->getExpression())
+ builder.makeReturn(false, builder.accessChainLoad(TranslatePrecisionDecoration(node->getExpression()->getType())));
+ else
+ builder.makeReturn();
+
+ builder.clearAccessChain();
+ break;
+
+ default:
+ spv::MissingFunctionality("branch type");
+ break;
+ }
+
+ return false;
+}
+
+spv::Id TGlslangToSpvTraverser::createSpvVariable(const glslang::TIntermSymbol* node)
+{
+ // First, steer off constants, which are not SPIR-V variables, but
+ // can still have a mapping to a SPIR-V Id.
+ if (node->getQualifier().storage == glslang::EvqConst) {
+ int nextConst = 0;
+ return createSpvConstant(node->getType(), node->getConstArray(), nextConst);
+ }
+
+ // Now, handle actual variables
+ spv::StorageClass storageClass = TranslateStorageClass(node->getType());
+ spv::Id spvType = convertGlslangToSpvType(node->getType());
+
+ const char* name = node->getName().c_str();
+ if (glslang::IsAnonymous(name))
+ name = "";
+
+ return builder.createVariable(storageClass, spvType, name);
+}
+
+// Return type Id of the sampled type.
+spv::Id TGlslangToSpvTraverser::getSampledType(const glslang::TSampler& sampler)
+{
+ switch (sampler.type) {
+ case glslang::EbtFloat: return builder.makeFloatType(32);
+ case glslang::EbtInt: return builder.makeIntType(32);
+ case glslang::EbtUint: return builder.makeUintType(32);
+ default:
+ spv::MissingFunctionality("sampled type");
+ return builder.makeFloatType(32);
+ }
+}
+
+// Do full recursive conversion of an arbitrary glslang type to a SPIR-V Id.
+spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& type)
+{
+ spv::Id spvType = 0;
+
+ switch (type.getBasicType()) {
+ case glslang::EbtVoid:
+ spvType = builder.makeVoidType();
+ if (type.isArray())
+ spv::MissingFunctionality("array of void");
+ break;
+ case glslang::EbtFloat:
+ spvType = builder.makeFloatType(32);
+ break;
+ case glslang::EbtDouble:
+ spvType = builder.makeFloatType(64);
+ break;
+ case glslang::EbtBool:
+ spvType = builder.makeBoolType();
+ break;
+ case glslang::EbtInt:
+ spvType = builder.makeIntType(32);
+ break;
+ case glslang::EbtUint:
+ spvType = builder.makeUintType(32);
+ break;
+ case glslang::EbtSampler:
+ {
+ const glslang::TSampler& sampler = type.getSampler();
+ spvType = builder.makeSampler(getSampledType(sampler), TranslateDimensionality(sampler),
+ sampler.image ? spv::Builder::samplerContentImage : spv::Builder::samplerContentTextureFilter,
+ sampler.arrayed, sampler.shadow, sampler.ms);
+ }
+ break;
+ case glslang::EbtStruct:
+ case glslang::EbtBlock:
+ {
+ // If we've seen this struct type, return it
+ const glslang::TTypeList* glslangStruct = type.getStruct();
+ std::vector<spv::Id> structFields;
+ spvType = structMap[glslangStruct];
+ if (spvType)
+ break;
+
+ // else, we haven't seen it...
+
+ // Create a vector of struct types for SPIR-V to consume
+ int memberDelta = 0; // how much the member's index changes from glslang to SPIR-V, normally 0, except sometimes for blocks
+ if (type.getBasicType() == glslang::EbtBlock)
+ memberRemapper[glslangStruct].resize(glslangStruct->size());
+ for (int i = 0; i < (int)glslangStruct->size(); i++) {
+ glslang::TType& glslangType = *(*glslangStruct)[i].type;
+ if (glslangType.hiddenMember()) {
+ ++memberDelta;
+ if (type.getBasicType() == glslang::EbtBlock)
+ memberRemapper[glslangStruct][i] = -1;
+ } else {
+ if (type.getBasicType() == glslang::EbtBlock)
+ memberRemapper[glslangStruct][i] = i - memberDelta;
+ structFields.push_back(convertGlslangToSpvType(glslangType));
+ }
+ }
+
+ // Make the SPIR-V type
+ spvType = builder.makeStructType(structFields, type.getTypeName().c_str());
+ structMap[glslangStruct] = spvType;
+
+ // Name and decorate the non-hidden members
+ for (int i = 0; i < (int)glslangStruct->size(); i++) {
+ glslang::TType& glslangType = *(*glslangStruct)[i].type;
+ int member = i;
+ if (type.getBasicType() == glslang::EbtBlock)
+ member = memberRemapper[glslangStruct][i];
+ // using -1 above to indicate a hidden member
+ if (member >= 0) {
+ builder.addMemberName(spvType, member, glslangType.getFieldName().c_str());
+ addMemberDecoration(spvType, member, TranslateLayoutDecoration(glslangType));
+ addMemberDecoration(spvType, member, TranslatePrecisionDecoration(glslangType));
+ addMemberDecoration(spvType, member, TranslateInterpolationDecoration(glslangType));
+ addMemberDecoration(spvType, member, TranslateInvariantDecoration(glslangType));
+ if (glslangType.getQualifier().hasLocation())
+ builder.addMemberDecoration(spvType, member, spv::DecorationLocation, glslangType.getQualifier().layoutLocation);
+ if (glslangType.getQualifier().hasComponent())
+ builder.addMemberDecoration(spvType, member, spv::DecorationComponent, glslangType.getQualifier().layoutComponent);
+ if (glslangType.getQualifier().hasXfbOffset())
+ builder.addMemberDecoration(spvType, member, spv::DecorationOffset, glslangType.getQualifier().layoutXfbOffset);
+
+ // built-in variable decorations
+ int builtIn = TranslateBuiltInDecoration(glslangType.getQualifier().builtIn);
+ if (builtIn != spv::BadValue)
+ builder.addMemberDecoration(spvType, member, spv::DecorationBuiltIn, builtIn);
+ }
+ }
+
+ // Decorate the structure
+ addDecoration(spvType, TranslateLayoutDecoration(type));
+ addDecoration(spvType, TranslateBlockDecoration(type));
+ if (type.getQualifier().hasStream())
+ builder.addDecoration(spvType, spv::DecorationStream, type.getQualifier().layoutStream);
+ if (glslangIntermediate->getXfbMode()) {
+ if (type.getQualifier().hasXfbStride())
+ builder.addDecoration(spvType, spv::DecorationStride, type.getQualifier().layoutXfbStride);
+ if (type.getQualifier().hasXfbBuffer())
+ builder.addDecoration(spvType, spv::DecorationXfbBuffer, type.getQualifier().layoutXfbBuffer);
+ }
+ }
+ break;
+ default:
+ spv::MissingFunctionality("basic type");
+ break;
+ }
+
+ if (type.isMatrix())
+ spvType = builder.makeMatrixType(spvType, type.getMatrixCols(), type.getMatrixRows());
+ else {
+ // If this variable has a vector element count greater than 1, create a SPIR-V vector
+ if (type.getVectorSize() > 1)
+ spvType = builder.makeVectorType(spvType, type.getVectorSize());
+ }
+
+ if (type.isArray()) {
+ unsigned arraySize;
+ if (! type.isExplicitlySizedArray()) {
+ spv::MissingFunctionality("Unsized array");
+ arraySize = 8;
+ } else
+ arraySize = type.getArraySize();
+ spvType = builder.makeArrayType(spvType, arraySize);
+ }
+
+ return spvType;
+}
+
+bool TGlslangToSpvTraverser::isShaderEntrypoint(const glslang::TIntermAggregate* node)
+{
+ return node->getName() == "main(";
+}
+
+// Make all the functions, skeletally, without actually visiting their bodies.
+void TGlslangToSpvTraverser::makeFunctions(const glslang::TIntermSequence& glslFunctions)
+{
+ for (int f = 0; f < (int)glslFunctions.size(); ++f) {
+ glslang::TIntermAggregate* glslFunction = glslFunctions[f]->getAsAggregate();
+ if (! glslFunction || glslFunction->getOp() != glslang::EOpFunction || isShaderEntrypoint(glslFunction))
+ continue;
+
+ // We're on a user function. Set up the basic interface for the function now,
+ // so that it's available to call.
+ // Translating the body will happen later.
+ //
+ // Typically (except for a "const in" parameter), an address will be passed to the
+ // function. What it is an address of varies:
+ //
+ // - "in" parameters not marked as "const" can be written to without modifying the argument,
+ // so that write needs to be to a copy, hence the address of a copy works.
+ //
+ // - "const in" parameters can just be the r-value, as no writes need occur.
+ //
+ // - "out" and "inout" arguments can't be done as direct pointers, because GLSL has
+ // copy-in/copy-out semantics. They can be handled though with a pointer to a copy.
+
+ std::vector<spv::Id> paramTypes;
+ glslang::TIntermSequence& parameters = glslFunction->getSequence()[0]->getAsAggregate()->getSequence();
+
+ for (int p = 0; p < (int)parameters.size(); ++p) {
+ const glslang::TType& paramType = parameters[p]->getAsTyped()->getType();
+ spv::Id typeId = convertGlslangToSpvType(paramType);
+ if (paramType.getQualifier().storage != glslang::EvqConstReadOnly)
+ typeId = builder.makePointer(spv::StorageClassFunction, typeId);
+ else
+ constReadOnlyParameters.insert(parameters[p]->getAsSymbolNode()->getId());
+ paramTypes.push_back(typeId);
+ }
+
+ spv::Block* functionBlock;
+ spv::Function *function = builder.makeFunctionEntry(convertGlslangToSpvType(glslFunction->getType()), glslFunction->getName().c_str(),
+ paramTypes, &functionBlock);
+
+ // Track function to emit/call later
+ functionMap[glslFunction->getName().c_str()] = function;
+
+ // Set the parameter id's
+ for (int p = 0; p < (int)parameters.size(); ++p) {
+ symbolValues[parameters[p]->getAsSymbolNode()->getId()] = function->getParamId(p);
+ // give a name too
+ builder.addName(function->getParamId(p), parameters[p]->getAsSymbolNode()->getName().c_str());
+ }
+ }
+}
+
+// Process all the initializers, while skipping the functions and link objects
+void TGlslangToSpvTraverser::makeGlobalInitializers(const glslang::TIntermSequence& initializers)
+{
+ builder.setBuildPoint(shaderEntry->getLastBlock());
+ for (int i = 0; i < (int)initializers.size(); ++i) {
+ glslang::TIntermAggregate* initializer = initializers[i]->getAsAggregate();
+ if (initializer && initializer->getOp() != glslang::EOpFunction && initializer->getOp() != glslang::EOpLinkerObjects) {
+
+ // We're on a top-level node that's not a function. Treat as an initializer, whose
+ // code goes into the beginning of main.
+ initializer->traverse(this);
+ }
+ }
+}
+
+// Process all the functions, while skipping initializers.
+void TGlslangToSpvTraverser::visitFunctions(const glslang::TIntermSequence& glslFunctions)
+{
+ for (int f = 0; f < (int)glslFunctions.size(); ++f) {
+ glslang::TIntermAggregate* node = glslFunctions[f]->getAsAggregate();
+ if (node && (node->getOp() == glslang::EOpFunction || node->getOp() == glslang ::EOpLinkerObjects))
+ node->traverse(this);
+ }
+}
+
+void TGlslangToSpvTraverser::handleFunctionEntry(const glslang::TIntermAggregate* node)
+{
+ // SPIR-V functions should already be in the functionMap from the prepass
+ // that called makeFunctions().
+ spv::Function* function = functionMap[node->getName().c_str()];
+ spv::Block* functionBlock = function->getEntryBlock();
+ builder.setBuildPoint(functionBlock);
+}
+
+void TGlslangToSpvTraverser::translateArguments(const glslang::TIntermSequence& glslangArguments, std::vector<spv::Id>& arguments)
+{
+ for (int i = 0; i < (int)glslangArguments.size(); ++i) {
+ builder.clearAccessChain();
+ glslangArguments[i]->traverse(this);
+ arguments.push_back(builder.accessChainLoad(TranslatePrecisionDecoration(glslangArguments[i]->getAsTyped()->getType())));
+ }
+}
+
+spv::Id TGlslangToSpvTraverser::handleBuiltInFunctionCall(const glslang::TIntermAggregate* node)
+{
+ std::vector<spv::Id> arguments;
+ translateArguments(node->getSequence(), arguments);
+
+ std::vector<spv::Id> argTypes;
+ for (int a = 0; a < (int)arguments.size(); ++a)
+ argTypes.push_back(builder.getTypeId(arguments[a]));
+
+ spv::Decoration precision = TranslatePrecisionDecoration(node->getType());
+
+ if (node->getName() == "ftransform(") {
+ spv::MissingFunctionality("ftransform()");
+ //spv::Id vertex = builder.createVariable(spv::StorageShaderGlobal, spv::VectorType::get(spv::makeFloatType(), 4),
+ // "gl_Vertex_sim");
+ //spv::Id matrix = builder.createVariable(spv::StorageShaderGlobal, spv::VectorType::get(spv::makeFloatType(), 4),
+ // "gl_ModelViewProjectionMatrix_sim");
+ return 0;
+ }
+
+ if (node->getName().substr(0, 7) == "texture" || node->getName().substr(0, 5) == "texel" || node->getName().substr(0, 6) == "shadow") {
+ const glslang::TSampler sampler = node->getSequence()[0]->getAsTyped()->getType().getSampler();
+ spv::Builder::TextureParameters params = { };
+ params.sampler = arguments[0];
+
+ // special case size query
+ if (node->getName().find("textureSize", 0) != std::string::npos) {
+ if (arguments.size() > 1) {
+ params.lod = arguments[1];
+ return builder.createTextureQueryCall(spv::OpTextureQuerySizeLod, params);
+ } else
+ return builder.createTextureQueryCall(spv::OpTextureQuerySize, params);
+ }
+
+ // special case the number of samples query
+ if (node->getName().find("textureSamples", 0) != std::string::npos)
+ return builder.createTextureQueryCall(spv::OpTextureQuerySamples, params);
+
+ // special case the other queries
+ if (node->getName().find("Query", 0) != std::string::npos) {
+ if (node->getName().find("Levels", 0) != std::string::npos)
+ return builder.createTextureQueryCall(spv::OpTextureQueryLevels, params);
+ else if (node->getName().find("Lod", 0) != std::string::npos) {
+ params.coords = arguments[1];
+ return builder.createTextureQueryCall(spv::OpTextureQueryLod, params);
+ } else
+ spv::MissingFunctionality("glslang texture query");
+ }
+
+ // This is no longer a query....
+
+ bool lod = node->getName().find("Lod", 0) != std::string::npos;
+ bool proj = node->getName().find("Proj", 0) != std::string::npos;
+ bool offsets = node->getName().find("Offsets", 0) != std::string::npos;
+ bool offset = ! offsets && node->getName().find("Offset", 0) != std::string::npos;
+ bool fetch = node->getName().find("Fetch", 0) != std::string::npos;
+ bool gather = node->getName().find("Gather", 0) != std::string::npos;
+ bool grad = node->getName().find("Grad", 0) != std::string::npos;
+
+ if (fetch)
+ spv::MissingFunctionality("texel fetch");
+ if (gather)
+ spv::MissingFunctionality("texture gather");
+
+ // check for bias argument
+ bool bias = false;
+ if (! lod && ! gather && ! grad && ! fetch) {
+ int nonBiasArgCount = 2;
+ if (offset)
+ ++nonBiasArgCount;
+ if (grad)
+ nonBiasArgCount += 2;
+
+ if ((int)arguments.size() > nonBiasArgCount)
+ bias = true;
+ }
+
+ bool cubeCompare = sampler.dim == glslang::EsdCube && sampler.arrayed && sampler.shadow;
+
+ // set the rest of the arguments
+ params.coords = arguments[1];
+ int extraArgs = 0;
+ if (cubeCompare)
+ params.Dref = arguments[2];
+ if (lod) {
+ params.lod = arguments[2];
+ ++extraArgs;
+ }
+ if (grad) {
+ params.gradX = arguments[2 + extraArgs];
+ params.gradY = arguments[3 + extraArgs];
+ extraArgs += 2;
+ }
+ //if (gather && compare) {
+ // params.compare = arguments[2 + extraArgs];
+ // ++extraArgs;
+ //}
+ if (offset | offsets) {
+ params.offset = arguments[2 + extraArgs];
+ ++extraArgs;
+ }
+ if (bias) {
+ params.bias = arguments[2 + extraArgs];
+ ++extraArgs;
+ }
+
+ return builder.createTextureCall(precision, convertGlslangToSpvType(node->getType()), proj, params);
+ }
+
+ spv::MissingFunctionality("built-in function call");
+
+ return 0;
+}
+
+spv::Id TGlslangToSpvTraverser::handleUserFunctionCall(const glslang::TIntermAggregate* node)
+{
+ // Grab the function's pointer from the previously created function
+ spv::Function* function = functionMap[node->getName().c_str()];
+ if (! function)
+ return 0;
+
+ const glslang::TIntermSequence& glslangArgs = node->getSequence();
+ const glslang::TQualifierList& qualifiers = node->getQualifierList();
+
+ // See comments in makeFunctions() for details about the semantics for parameter passing.
+ //
+ // These imply we need a four step process:
+ // 1. Evaluate the arguments
+ // 2. Allocate and make copies of in, out, and inout arguments
+ // 3. Make the call
+ // 4. Copy back the results
+
+ // 1. Evaluate the arguments
+ std::vector<spv::Builder::AccessChain> lValues;
+ std::vector<spv::Id> rValues;
+ for (int a = 0; a < (int)glslangArgs.size(); ++a) {
+ // build l-value
+ builder.clearAccessChain();
+ glslangArgs[a]->traverse(this);
+ // keep outputs as l-values, evaluate input-only as r-values
+ if (qualifiers[a] != glslang::EvqConstReadOnly) {
+ // save l-value
+ lValues.push_back(builder.getAccessChain());
+ } else {
+ // process r-value
+ rValues.push_back(builder.accessChainLoad(TranslatePrecisionDecoration(glslangArgs[a]->getAsTyped()->getType())));
+ }
+ }
+
+ // 2. Allocate space for anything needing a copy, and if it's "in" or "inout"
+ // copy the original into that space.
+ //
+ // Also, build up the list of actual arguments to pass in for the call
+ int lValueCount = 0;
+ int rValueCount = 0;
+ std::vector<spv::Id> spvArgs;
+ for (int a = 0; a < (int)glslangArgs.size(); ++a) {
+ spv::Id arg;
+ if (qualifiers[a] != glslang::EvqConstReadOnly) {
+ // need space to hold the copy
+ const glslang::TType& paramType = glslangArgs[a]->getAsTyped()->getType();
+ arg = builder.createVariable(spv::StorageClassFunction, convertGlslangToSpvType(paramType), "param");
+ if (qualifiers[a] == glslang::EvqIn || qualifiers[a] == glslang::EvqInOut) {
+ // need to copy the input into output space
+ builder.setAccessChain(lValues[lValueCount]);
+ spv::Id copy = builder.accessChainLoad(spv::NoPrecision); // TODO: get precision
+ builder.createStore(copy, arg);
+ }
+ ++lValueCount;
+ } else {
+ arg = rValues[rValueCount];
+ ++rValueCount;
+ }
+ spvArgs.push_back(arg);
+ }
+
+ // 3. Make the call.
+ spv::Id result = builder.createFunctionCall(function, spvArgs);
+
+ // 4. Copy back out an "out" arguments.
+ lValueCount = 0;
+ for (int a = 0; a < (int)glslangArgs.size(); ++a) {
+ if (qualifiers[a] != glslang::EvqConstReadOnly) {
+ if (qualifiers[a] == glslang::EvqOut || qualifiers[a] == glslang::EvqInOut) {
+ spv::Id copy = builder.createLoad(spvArgs[a]);
+ builder.setAccessChain(lValues[lValueCount]);
+ builder.accessChainStore(copy);
+ }
+ ++lValueCount;
+ }
+ }
+
+ return result;
+}
+
+// Translate AST operation to SPV operation, already having SPV-based operands/types.
+spv::Id TGlslangToSpvTraverser::createBinaryOperation(glslang::TOperator op, spv::Decoration precision,
+ spv::Id typeId, spv::Id left, spv::Id right,
+ glslang::TBasicType typeProxy, bool reduceComparison)
+{
+ bool isUnsigned = typeProxy == glslang::EbtUint;
+ bool isFloat = typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble;
+
+ spv::Op binOp = spv::OpNop;
+ bool needsPromotion = true;
+ bool comparison = false;
+
+ switch (op) {
+ case glslang::EOpAdd:
+ case glslang::EOpAddAssign:
+ if (isFloat)
+ binOp = spv::OpFAdd;
+ else
+ binOp = spv::OpIAdd;
+ break;
+ case glslang::EOpSub:
+ case glslang::EOpSubAssign:
+ if (isFloat)
+ binOp = spv::OpFSub;
+ else
+ binOp = spv::OpISub;
+ break;
+ case glslang::EOpMul:
+ case glslang::EOpMulAssign:
+ if (isFloat)
+ binOp = spv::OpFMul;
+ else
+ binOp = spv::OpIMul;
+ break;
+ case glslang::EOpVectorTimesScalar:
+ case glslang::EOpVectorTimesScalarAssign:
+ if (builder.isVector(right))
+ std::swap(left, right);
+ assert(builder.isScalar(right));
+ binOp = spv::OpVectorTimesScalar;
+ needsPromotion = false;
+ break;
+ case glslang::EOpVectorTimesMatrix:
+ case glslang::EOpVectorTimesMatrixAssign:
+ assert(builder.isVector(left));
+ assert(builder.isMatrix(right));
+ binOp = spv::OpVectorTimesMatrix;
+ break;
+ case glslang::EOpMatrixTimesVector:
+ assert(builder.isMatrix(left));
+ assert(builder.isVector(right));
+ binOp = spv::OpMatrixTimesVector;
+ break;
+ case glslang::EOpMatrixTimesScalar:
+ case glslang::EOpMatrixTimesScalarAssign:
+ if (builder.isMatrix(right))
+ std::swap(left, right);
+ assert(builder.isScalar(right));
+ binOp = spv::OpMatrixTimesScalar;
+ break;
+ case glslang::EOpMatrixTimesMatrix:
+ case glslang::EOpMatrixTimesMatrixAssign:
+ assert(builder.isMatrix(left));
+ assert(builder.isMatrix(right));
+ binOp = spv::OpMatrixTimesMatrix;
+ break;
+ case glslang::EOpOuterProduct:
+ binOp = spv::OpOuterProduct;
+ needsPromotion = false;
+ break;
+
+ case glslang::EOpDiv:
+ case glslang::EOpDivAssign:
+ if (isFloat)
+ binOp = spv::OpFDiv;
+ else if (isUnsigned)
+ binOp = spv::OpUDiv;
+ else
+ binOp = spv::OpSDiv;
+ break;
+ case glslang::EOpMod:
+ case glslang::EOpModAssign:
+ if (isFloat)
+ binOp = spv::OpFMod;
+ else if (isUnsigned)
+ binOp = spv::OpUMod;
+ else
+ binOp = spv::OpSMod;
+ break;
+ case glslang::EOpRightShift:
+ case glslang::EOpRightShiftAssign:
+ if (isUnsigned)
+ binOp = spv::OpShiftRightLogical;
+ else
+ binOp = spv::OpShiftRightArithmetic;
+ break;
+ case glslang::EOpLeftShift:
+ case glslang::EOpLeftShiftAssign:
+ binOp = spv::OpShiftLeftLogical;
+ break;
+ case glslang::EOpAnd:
+ case glslang::EOpAndAssign:
+ binOp = spv::OpBitwiseAnd;
+ break;
+ case glslang::EOpLogicalAnd:
+ needsPromotion = false;
+ binOp = spv::OpLogicalAnd;
+ break;
+ case glslang::EOpInclusiveOr:
+ case glslang::EOpInclusiveOrAssign:
+ binOp = spv::OpBitwiseOr;
+ break;
+ case glslang::EOpLogicalOr:
+ needsPromotion = false;
+ binOp = spv::OpLogicalOr;
+ break;
+ case glslang::EOpExclusiveOr:
+ case glslang::EOpExclusiveOrAssign:
+ binOp = spv::OpBitwiseXor;
+ break;
+ case glslang::EOpLogicalXor:
+ needsPromotion = false;
+ binOp = spv::OpLogicalXor;
+ break;
+
+ case glslang::EOpLessThan:
+ case glslang::EOpGreaterThan:
+ case glslang::EOpLessThanEqual:
+ case glslang::EOpGreaterThanEqual:
+ case glslang::EOpEqual:
+ case glslang::EOpNotEqual:
+ case glslang::EOpVectorEqual:
+ case glslang::EOpVectorNotEqual:
+ comparison = true;
+ break;
+ default:
+ break;
+ }
+
+ if (binOp != spv::OpNop) {
+ if (builder.isMatrix(left) || builder.isMatrix(right)) {
+ switch (binOp) {
+ case spv::OpMatrixTimesScalar:
+ case spv::OpVectorTimesMatrix:
+ case spv::OpMatrixTimesVector:
+ case spv::OpMatrixTimesMatrix:
+ break;
+ case spv::OpFDiv:
+ // turn it into a multiply...
+ assert(builder.isMatrix(left) && builder.isScalar(right));
+ right = builder.createBinOp(spv::OpFDiv, builder.getTypeId(right), builder.makeFloatConstant(1.0F), right);
+ binOp = spv::OpFMul;
+ break;
+ default:
+ spv::MissingFunctionality("binary operation on matrix");
+ break;
+ }
+
+ spv::Id id = builder.createBinOp(binOp, typeId, left, right);
+ builder.setPrecision(id, precision);
+
+ return id;
+ }
+
+ // No matrix involved; make both operands be the same number of components, if needed
+ if (needsPromotion)
+ builder.promoteScalar(precision, left, right);
+
+ spv::Id id = builder.createBinOp(binOp, typeId, left, right);
+ builder.setPrecision(id, precision);
+
+ return id;
+ }
+
+ if (! comparison)
+ return 0;
+
+ // Comparison instructions
+
+ if (reduceComparison && (builder.isVector(left) || builder.isMatrix(left) || builder.isAggregate(left))) {
+ assert(op == glslang::EOpEqual || op == glslang::EOpNotEqual);
+
+ return builder.createCompare(precision, left, right, op == glslang::EOpEqual);
+ }
+
+ switch (op) {
+ case glslang::EOpLessThan:
+ if (isFloat)
+ binOp = spv::OpFOrdLessThan;
+ else if (isUnsigned)
+ binOp = spv::OpULessThan;
+ else
+ binOp = spv::OpSLessThan;
+ break;
+ case glslang::EOpGreaterThan:
+ if (isFloat)
+ binOp = spv::OpFOrdGreaterThan;
+ else if (isUnsigned)
+ binOp = spv::OpUGreaterThan;
+ else
+ binOp = spv::OpSGreaterThan;
+ break;
+ case glslang::EOpLessThanEqual:
+ if (isFloat)
+ binOp = spv::OpFOrdLessThanEqual;
+ else if (isUnsigned)
+ binOp = spv::OpULessThanEqual;
+ else
+ binOp = spv::OpSLessThanEqual;
+ break;
+ case glslang::EOpGreaterThanEqual:
+ if (isFloat)
+ binOp = spv::OpFOrdGreaterThanEqual;
+ else if (isUnsigned)
+ binOp = spv::OpUGreaterThanEqual;
+ else
+ binOp = spv::OpSGreaterThanEqual;
+ break;
+ case glslang::EOpEqual:
+ case glslang::EOpVectorEqual:
+ if (isFloat)
+ binOp = spv::OpFOrdEqual;
+ else
+ binOp = spv::OpIEqual;
+ break;
+ case glslang::EOpNotEqual:
+ case glslang::EOpVectorNotEqual:
+ if (isFloat)
+ binOp = spv::OpFOrdNotEqual;
+ else
+ binOp = spv::OpINotEqual;
+ break;
+ default:
+ break;
+ }
+
+ if (binOp != spv::OpNop) {
+ spv::Id id = builder.createBinOp(binOp, typeId, left, right);
+ builder.setPrecision(id, precision);
+
+ return id;
+ }
+
+ return 0;
+}
+
+spv::Id TGlslangToSpvTraverser::createUnaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, spv::Id operand, bool isFloat)
+{
+ spv::Op unaryOp = spv::OpNop;
+ int libCall = -1;
+
+ switch (op) {
+ case glslang::EOpNegative:
+ if (isFloat)
+ unaryOp = spv::OpFNegate;
+ else
+ unaryOp = spv::OpSNegate;
+ break;
+
+ case glslang::EOpLogicalNot:
+ case glslang::EOpVectorLogicalNot:
+ case glslang::EOpBitwiseNot:
+ unaryOp = spv::OpNot;
+ break;
+
+ case glslang::EOpDeterminant:
+ libCall = GLSL_STD_450::Determinant;
+ break;
+ case glslang::EOpMatrixInverse:
+ libCall = GLSL_STD_450::MatrixInverse;
+ break;
+ case glslang::EOpTranspose:
+ unaryOp = spv::OpTranspose;
+ break;
+
+ case glslang::EOpRadians:
+ libCall = GLSL_STD_450::Radians;
+ break;
+ case glslang::EOpDegrees:
+ libCall = GLSL_STD_450::Degrees;
+ break;
+ case glslang::EOpSin:
+ libCall = GLSL_STD_450::Sin;
+ break;
+ case glslang::EOpCos:
+ libCall = GLSL_STD_450::Cos;
+ break;
+ case glslang::EOpTan:
+ libCall = GLSL_STD_450::Tan;
+ break;
+ case glslang::EOpAcos:
+ libCall = GLSL_STD_450::Acos;
+ break;
+ case glslang::EOpAsin:
+ libCall = GLSL_STD_450::Asin;
+ break;
+ case glslang::EOpAtan:
+ libCall = GLSL_STD_450::Atan;
+ break;
+
+ case glslang::EOpAcosh:
+ libCall = GLSL_STD_450::Acosh;
+ break;
+ case glslang::EOpAsinh:
+ libCall = GLSL_STD_450::Asinh;
+ break;
+ case glslang::EOpAtanh:
+ libCall = GLSL_STD_450::Atanh;
+ break;
+ case glslang::EOpTanh:
+ libCall = GLSL_STD_450::Tanh;
+ break;
+ case glslang::EOpCosh:
+ libCall = GLSL_STD_450::Cosh;
+ break;
+ case glslang::EOpSinh:
+ libCall = GLSL_STD_450::Sinh;
+ break;
+
+ case glslang::EOpLength:
+ libCall = GLSL_STD_450::Length;
+ break;
+ case glslang::EOpNormalize:
+ libCall = GLSL_STD_450::Normalize;
+ break;
+
+ case glslang::EOpExp:
+ libCall = GLSL_STD_450::Exp;
+ break;
+ case glslang::EOpLog:
+ libCall = GLSL_STD_450::Log;
+ break;
+ case glslang::EOpExp2:
+ libCall = GLSL_STD_450::Exp2;
+ break;
+ case glslang::EOpLog2:
+ libCall = GLSL_STD_450::Log2;
+ break;
+ case glslang::EOpSqrt:
+ libCall = GLSL_STD_450::Sqrt;
+ break;
+ case glslang::EOpInverseSqrt:
+ libCall = GLSL_STD_450::InverseSqrt;
+ break;
+
+ case glslang::EOpFloor:
+ libCall = GLSL_STD_450::Floor;
+ break;
+ case glslang::EOpTrunc:
+ libCall = GLSL_STD_450::Trunc;
+ break;
+ case glslang::EOpRound:
+ libCall = GLSL_STD_450::Round;
+ break;
+ case glslang::EOpRoundEven:
+ libCall = GLSL_STD_450::RoundEven;
+ break;
+ case glslang::EOpCeil:
+ libCall = GLSL_STD_450::Ceil;
+ break;
+ case glslang::EOpFract:
+ libCall = GLSL_STD_450::Fract;
+ break;
+
+ case glslang::EOpIsNan:
+ unaryOp = spv::OpIsNan;
+ break;
+ case glslang::EOpIsInf:
+ unaryOp = spv::OpIsInf;
+ break;
+
+ case glslang::EOpFloatBitsToInt:
+ libCall = GLSL_STD_450::FloatBitsToInt;
+ break;
+ case glslang::EOpFloatBitsToUint:
+ libCall = GLSL_STD_450::FloatBitsToUint;
+ break;
+ case glslang::EOpIntBitsToFloat:
+ libCall = GLSL_STD_450::IntBitsToFloat;
+ break;
+ case glslang::EOpUintBitsToFloat:
+ libCall = GLSL_STD_450::UintBitsToFloat;
+ break;
+ case glslang::EOpPackSnorm2x16:
+ libCall = GLSL_STD_450::PackSnorm2x16;
+ break;
+ case glslang::EOpUnpackSnorm2x16:
+ libCall = GLSL_STD_450::UnpackSnorm2x16;
+ break;
+ case glslang::EOpPackUnorm2x16:
+ libCall = GLSL_STD_450::PackUnorm2x16;
+ break;
+ case glslang::EOpUnpackUnorm2x16:
+ libCall = GLSL_STD_450::UnpackUnorm2x16;
+ break;
+ case glslang::EOpPackHalf2x16:
+ libCall = GLSL_STD_450::PackHalf2x16;
+ break;
+ case glslang::EOpUnpackHalf2x16:
+ libCall = GLSL_STD_450::UnpackHalf2x16;
+ break;
+
+ case glslang::EOpDPdx:
+ unaryOp = spv::OpDPdx;
+ break;
+ case glslang::EOpDPdy:
+ unaryOp = spv::OpDPdy;
+ break;
+ case glslang::EOpFwidth:
+ unaryOp = spv::OpFwidth;
+ break;
+ case glslang::EOpDPdxFine:
+ unaryOp = spv::OpDPdxFine;
+ break;
+ case glslang::EOpDPdyFine:
+ unaryOp = spv::OpDPdyFine;
+ break;
+ case glslang::EOpFwidthFine:
+ unaryOp = spv::OpFwidthFine;
+ break;
+ case glslang::EOpDPdxCoarse:
+ unaryOp = spv::OpDPdxCoarse;
+ break;
+ case glslang::EOpDPdyCoarse:
+ unaryOp = spv::OpDPdyCoarse;
+ break;
+ case glslang::EOpFwidthCoarse:
+ unaryOp = spv::OpFwidthCoarse;
+ break;
+
+ case glslang::EOpAny:
+ unaryOp = spv::OpAny;
+ break;
+ case glslang::EOpAll:
+ unaryOp = spv::OpAll;
+ break;
+
+ case glslang::EOpAbs:
+ libCall = GLSL_STD_450::Abs;
+ break;
+ case glslang::EOpSign:
+ libCall = GLSL_STD_450::Sign;
+ break;
+
+ default:
+ return 0;
+ }
+
+ spv::Id id;
+ if (libCall >= 0) {
+ std::vector<spv::Id> args;
+ args.push_back(operand);
+ id = builder.createBuiltinCall(precision, typeId, stdBuiltins, libCall, args);
+ } else
+ id = builder.createUnaryOp(unaryOp, typeId, operand);
+
+ builder.setPrecision(id, precision);
+
+ return id;
+}
+
+spv::Id TGlslangToSpvTraverser::createConversion(glslang::TOperator op, spv::Decoration precision, spv::Id destType, spv::Id operand)
+{
+ spv::Op convOp = spv::OpNop;
+ spv::Id zero = 0;
+ spv::Id one = 0;
+
+ int vectorSize = builder.isVectorType(destType) ? builder.getNumTypeComponents(destType) : 0;
+
+ switch (op) {
+ case glslang::EOpConvIntToBool:
+ case glslang::EOpConvUintToBool:
+ zero = builder.makeUintConstant(0);
+ zero = makeSmearedConstant(zero, vectorSize);
+ return builder.createBinOp(spv::OpINotEqual, destType, operand, zero);
+
+ case glslang::EOpConvFloatToBool:
+ zero = builder.makeFloatConstant(0.0F);
+ zero = makeSmearedConstant(zero, vectorSize);
+ return builder.createBinOp(spv::OpFOrdNotEqual, destType, operand, zero);
+
+ case glslang::EOpConvDoubleToBool:
+ zero = builder.makeDoubleConstant(0.0);
+ zero = makeSmearedConstant(zero, vectorSize);
+ return builder.createBinOp(spv::OpFOrdNotEqual, destType, operand, zero);
+
+ case glslang::EOpConvBoolToFloat:
+ convOp = spv::OpSelect;
+ zero = builder.makeFloatConstant(0.0);
+ one = builder.makeFloatConstant(1.0);
+ break;
+ case glslang::EOpConvBoolToDouble:
+ convOp = spv::OpSelect;
+ zero = builder.makeDoubleConstant(0.0);
+ one = builder.makeDoubleConstant(1.0);
+ break;
+ case glslang::EOpConvBoolToInt:
+ zero = builder.makeIntConstant(0);
+ one = builder.makeIntConstant(1);
+ convOp = spv::OpSelect;
+ break;
+ case glslang::EOpConvBoolToUint:
+ zero = builder.makeUintConstant(0);
+ one = builder.makeUintConstant(1);
+ convOp = spv::OpSelect;
+ break;
+
+ case glslang::EOpConvIntToFloat:
+ case glslang::EOpConvIntToDouble:
+ convOp = spv::OpConvertSToF;
+ break;
+
+ case glslang::EOpConvUintToFloat:
+ case glslang::EOpConvUintToDouble:
+ convOp = spv::OpConvertUToF;
+ break;
+
+ case glslang::EOpConvDoubleToFloat:
+ case glslang::EOpConvFloatToDouble:
+ convOp = spv::OpFConvert;
+ break;
+
+ case glslang::EOpConvFloatToInt:
+ case glslang::EOpConvDoubleToInt:
+ convOp = spv::OpConvertFToS;
+ break;
+
+ case glslang::EOpConvUintToInt:
+ case glslang::EOpConvIntToUint:
+ convOp = spv::OpBitcast;
+ break;
+
+ case glslang::EOpConvFloatToUint:
+ case glslang::EOpConvDoubleToUint:
+ convOp = spv::OpConvertFToU;
+ break;
+ default:
+ break;
+ }
+
+ spv::Id result = 0;
+ if (convOp == spv::OpNop)
+ return result;
+
+ if (convOp == spv::OpSelect) {
+ zero = makeSmearedConstant(zero, vectorSize);
+ one = makeSmearedConstant(one, vectorSize);
+ result = builder.createTriOp(convOp, destType, operand, one, zero);
+ } else
+ result = builder.createUnaryOp(convOp, destType, operand);
+
+ builder.setPrecision(result, precision);
+
+ return result;
+}
+
+spv::Id TGlslangToSpvTraverser::makeSmearedConstant(spv::Id constant, int vectorSize)
+{
+ if (vectorSize == 0)
+ return constant;
+
+ spv::Id vectorTypeId = builder.makeVectorType(builder.getTypeId(constant), vectorSize);
+ std::vector<spv::Id> components;
+ for (int c = 0; c < vectorSize; ++c)
+ components.push_back(constant);
+ return builder.makeCompositeConstant(vectorTypeId, components);
+}
+
+spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& operands)
+{
+ spv::Op opCode = spv::OpNop;
+ int libCall = -1;
+
+ switch (op) {
+ case glslang::EOpMin:
+ libCall = GLSL_STD_450::Min;
+ break;
+ case glslang::EOpModf:
+ libCall = GLSL_STD_450::Modf;
+ break;
+ case glslang::EOpMax:
+ libCall = GLSL_STD_450::Max;
+ break;
+ case glslang::EOpPow:
+ libCall = GLSL_STD_450::Pow;
+ break;
+ case glslang::EOpDot:
+ opCode = spv::OpDot;
+ break;
+ case glslang::EOpAtan:
+ libCall = GLSL_STD_450::Atan2;
+ break;
+
+ case glslang::EOpClamp:
+ libCall = GLSL_STD_450::Clamp;
+ break;
+ case glslang::EOpMix:
+ libCall = GLSL_STD_450::Mix;
+ break;
+ case glslang::EOpStep:
+ libCall = GLSL_STD_450::Step;
+ break;
+ case glslang::EOpSmoothStep:
+ libCall = GLSL_STD_450::SmoothStep;
+ break;
+
+ case glslang::EOpDistance:
+ libCall = GLSL_STD_450::Distance;
+ break;
+ case glslang::EOpCross:
+ libCall = GLSL_STD_450::Cross;
+ break;
+ case glslang::EOpFaceForward:
+ libCall = GLSL_STD_450::FaceForward;
+ break;
+ case glslang::EOpReflect:
+ libCall = GLSL_STD_450::Reflect;
+ break;
+ case glslang::EOpRefract:
+ libCall = GLSL_STD_450::Refract;
+ break;
+ default:
+ return 0;
+ }
+
+ spv::Id id = 0;
+ if (libCall >= 0)
+ id = builder.createBuiltinCall(precision, typeId, stdBuiltins, libCall, operands);
+ else {
+ switch (operands.size()) {
+ case 0:
+ // should all be handled by visitAggregate and createNoArgOperation
+ assert(0);
+ return 0;
+ case 1:
+ // should all be handled by createUnaryOperation
+ assert(0);
+ return 0;
+ case 2:
+ id = builder.createBinOp(opCode, typeId, operands[0], operands[1]);
+ break;
+ case 3:
+ id = builder.createTernaryOp(opCode, typeId, operands[0], operands[1], operands[2]);
+ break;
+ default:
+ // These do not exist yet
+ assert(0 && "operation with more than 3 operands");
+ break;
+ }
+ }
+
+ builder.setPrecision(id, precision);
+
+ return id;
+}
+
+// Intrinsics with no arguments, no return value, and no precision.
+spv::Id TGlslangToSpvTraverser::createNoArgOperation(glslang::TOperator op)
+{
+ // TODO: get the barrier operands correct
+
+ switch (op) {
+ case glslang::EOpEmitVertex:
+ builder.createNoResultOp(spv::OpEmitVertex);
+ return 0;
+ case glslang::EOpEndPrimitive:
+ builder.createNoResultOp(spv::OpEndPrimitive);
+ return 0;
+ case glslang::EOpBarrier:
+ builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsAllMemory);
+ builder.createControlBarrier(spv::ExecutionScopeDevice);
+ return 0;
+ case glslang::EOpMemoryBarrier:
+ builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsAllMemory);
+ return 0;
+ case glslang::EOpMemoryBarrierAtomicCounter:
+ builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsAtomicCounterMemoryMask);
+ return 0;
+ case glslang::EOpMemoryBarrierBuffer:
+ builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsUniformMemoryMask);
+ return 0;
+ case glslang::EOpMemoryBarrierImage:
+ builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsImageMemoryMask);
+ return 0;
+ case glslang::EOpMemoryBarrierShared:
+ builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsWorkgroupLocalMemoryMask);
+ return 0;
+ case glslang::EOpGroupMemoryBarrier:
+ builder.createMemoryBarrier(spv::ExecutionScopeDevice, spv::MemorySemanticsWorkgroupGlobalMemoryMask);
+ return 0;
+ default:
+ spv::MissingFunctionality("operation with no arguments");
+ return 0;
+ }
+}
+
+spv::Id TGlslangToSpvTraverser::getSymbolId(const glslang::TIntermSymbol* symbol)
+{
+ std::map<int, spv::Id>::iterator iter;
+ iter = symbolValues.find(symbol->getId());
+ spv::Id id;
+ if (symbolValues.end() != iter) {
+ id = iter->second;
+ return id;
+ }
+
+ // it was not found, create it
+ id = createSpvVariable(symbol);
+ symbolValues[symbol->getId()] = id;
+
+ if (! symbol->getType().isStruct()) {
+ addDecoration(id, TranslatePrecisionDecoration(symbol->getType()));
+ addDecoration(id, TranslateInterpolationDecoration(symbol->getType()));
+ if (symbol->getQualifier().hasLocation())
+ builder.addDecoration(id, spv::DecorationLocation, symbol->getQualifier().layoutLocation);
+ if (symbol->getQualifier().hasIndex())
+ builder.addDecoration(id, spv::DecorationIndex, symbol->getQualifier().layoutIndex);
+ if (symbol->getQualifier().hasComponent())
+ builder.addDecoration(id, spv::DecorationComponent, symbol->getQualifier().layoutComponent);
+ if (glslangIntermediate->getXfbMode()) {
+ if (symbol->getQualifier().hasXfbStride())
+ builder.addDecoration(id, spv::DecorationStride, symbol->getQualifier().layoutXfbStride);
+ if (symbol->getQualifier().hasXfbBuffer())
+ builder.addDecoration(id, spv::DecorationXfbBuffer, symbol->getQualifier().layoutXfbBuffer);
+ if (symbol->getQualifier().hasXfbOffset())
+ builder.addDecoration(id, spv::DecorationOffset, symbol->getQualifier().layoutXfbOffset);
+ }
+ }
+
+ addDecoration(id, TranslateInvariantDecoration(symbol->getType()));
+ if (symbol->getQualifier().hasStream())
+ builder.addDecoration(id, spv::DecorationStream, symbol->getQualifier().layoutStream);
+ if (symbol->getQualifier().hasSet())
+ builder.addDecoration(id, spv::DecorationDescriptorSet, symbol->getQualifier().layoutSet);
+ if (symbol->getQualifier().hasBinding())
+ builder.addDecoration(id, spv::DecorationBinding, symbol->getQualifier().layoutBinding);
+ if (glslangIntermediate->getXfbMode()) {
+ if (symbol->getQualifier().hasXfbStride())
+ builder.addDecoration(id, spv::DecorationStride, symbol->getQualifier().layoutXfbStride);
+ if (symbol->getQualifier().hasXfbBuffer())
+ builder.addDecoration(id, spv::DecorationXfbBuffer, symbol->getQualifier().layoutXfbBuffer);
+ }
+
+ // built-in variable decorations
+ int builtIn = TranslateBuiltInDecoration(symbol->getQualifier().builtIn);
+ if (builtIn != spv::BadValue)
+ builder.addDecoration(id, spv::DecorationBuiltIn, builtIn);
+
+ if (linkageOnly)
+ builder.addDecoration(id, spv::DecorationNoStaticUse);
+
+ return id;
+}
+
+void TGlslangToSpvTraverser::addDecoration(spv::Id id, spv::Decoration dec)
+{
+ if (dec != spv::BadValue)
+ builder.addDecoration(id, dec);
+}
+
+void TGlslangToSpvTraverser::addMemberDecoration(spv::Id id, int member, spv::Decoration dec)
+{
+ if (dec != spv::BadValue)
+ builder.addMemberDecoration(id, (unsigned)member, dec);
+}
+
+// Use 'consts' as the flattened glslang source of scalar constants to recursively
+// build the aggregate SPIR-V constant.
+//
+// If there are not enough elements present in 'consts', 0 will be substituted;
+// an empty 'consts' can be used to create a fully zeroed SPIR-V constant.
+//
+spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TType& glslangType, const glslang::TConstUnionArray& consts, int& nextConst)
+{
+ // vector of constants for SPIR-V
+ std::vector<spv::Id> spvConsts;
+
+ // Type is used for struct and array constants
+ spv::Id typeId = convertGlslangToSpvType(glslangType);
+
+ if (glslangType.isArray()) {
+ glslang::TType elementType;
+ elementType.shallowCopy(glslangType); // TODO: desktop arrays of arrays functionality will need a deeper copy to avoid modifying the original
+ elementType.dereference();
+ for (int i = 0; i < glslangType.getArraySize(); ++i)
+ spvConsts.push_back(createSpvConstant(elementType, consts, nextConst));
+ } else if (glslangType.isMatrix()) {
+ glslang::TType vectorType;
+ vectorType.shallowCopy(glslangType);
+ vectorType.dereference();
+ for (int col = 0; col < glslangType.getMatrixCols(); ++col)
+ spvConsts.push_back(createSpvConstant(vectorType, consts, nextConst));
+ } else if (glslangType.getStruct()) {
+ glslang::TVector<glslang::TTypeLoc>::const_iterator iter;
+ for (iter = glslangType.getStruct()->begin(); iter != glslangType.getStruct()->end(); ++iter)
+ spvConsts.push_back(createSpvConstant(*iter->type, consts, nextConst));
+ } else if (glslangType.isVector()) {
+ for (unsigned int i = 0; i < (unsigned int)glslangType.getVectorSize(); ++i) {
+ bool zero = nextConst >= consts.size();
+ switch (glslangType.getBasicType()) {
+ case glslang::EbtInt:
+ spvConsts.push_back(builder.makeIntConstant(zero ? 0 : consts[nextConst].getIConst()));
+ break;
+ case glslang::EbtUint:
+ spvConsts.push_back(builder.makeUintConstant(zero ? 0 : consts[nextConst].getUConst()));
+ break;
+ case glslang::EbtFloat:
+ spvConsts.push_back(builder.makeFloatConstant(zero ? 0.0F : (float)consts[nextConst].getDConst()));
+ break;
+ case glslang::EbtDouble:
+ spvConsts.push_back(builder.makeDoubleConstant(zero ? 0.0 : consts[nextConst].getDConst()));
+ break;
+ case glslang::EbtBool:
+ spvConsts.push_back(builder.makeBoolConstant(zero ? false : consts[nextConst].getBConst()));
+ break;
+ default:
+ spv::MissingFunctionality("constant vector type");
+ break;
+ }
+ ++nextConst;
+ }
+ } else {
+ // we have a non-aggregate (scalar) constant
+ bool zero = nextConst >= consts.size();
+ spv::Id scalar = 0;
+ switch (glslangType.getBasicType()) {
+ case glslang::EbtInt:
+ scalar = builder.makeIntConstant(zero ? 0 : consts[nextConst].getIConst());
+ break;
+ case glslang::EbtUint:
+ scalar = builder.makeUintConstant(zero ? 0 : consts[nextConst].getUConst());
+ break;
+ case glslang::EbtFloat:
+ scalar = builder.makeFloatConstant(zero ? 0.0F : (float)consts[nextConst].getDConst());
+ break;
+ case glslang::EbtDouble:
+ scalar = builder.makeDoubleConstant(zero ? 0.0 : consts[nextConst].getDConst());
+ break;
+ case glslang::EbtBool:
+ scalar = builder.makeBoolConstant(zero ? false : consts[nextConst].getBConst());
+ break;
+ default:
+ spv::MissingFunctionality("constant scalar type");
+ break;
+ }
+ ++nextConst;
+ return scalar;
+ }
+
+ return builder.makeCompositeConstant(typeId, spvConsts);
+}
+
+}; // end anonymous namespace
+
+namespace glslang {
+
+// Write SPIR-V out to a binary file
+void OutputSpv(const std::vector<unsigned int>& spirv, const char* baseName)
+{
+ std::ofstream out;
+ std::string fileName(baseName);
+ fileName.append(".spv");
+ out.open(fileName.c_str(), std::ios::binary | std::ios::out);
+ for (int i = 0; i < (int)spirv.size(); ++i) {
+ unsigned int word = spirv[i];
+ out.write((const char*)&word, 4);
+ }
+ out.close();
+}
+
+//
+// Set up the glslang traversal
+//
+void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv)
+{
+ TIntermNode* root = intermediate.getTreeRoot();
+
+ if (root == 0)
+ return;
+
+ glslang::GetThreadPoolAllocator().push();
+
+ TGlslangToSpvTraverser it(&intermediate);
+
+ root->traverse(&it);
+
+ it.dumpSpv(spirv);
+
+ glslang::GetThreadPoolAllocator().pop();
+}
+
+}; // end namespace glslang