blob: afb405e40b73b44bc38a314cd711ec872cf0b78b [file] [log] [blame] [edit]
//
// Copyright (C) 2014-2016 LunarG, Inc.
// Copyright (C) 2015-2018 Google, Inc.
// Copyright (C) 2017 ARM Limited.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Visit the nodes in the glslang intermediate tree representation to
// translate them to SPIR-V.
//
#include "spirv.hpp"
#include "GlslangToSpv.h"
#include "SpvBuilder.h"
namespace spv {
#include "GLSL.std.450.h"
#include "GLSL.ext.KHR.h"
#include "GLSL.ext.EXT.h"
#include "GLSL.ext.AMD.h"
#include "GLSL.ext.NV.h"
}
// Glslang includes
#include "../glslang/MachineIndependent/localintermediate.h"
#include "../glslang/MachineIndependent/SymbolTable.h"
#include "../glslang/Include/Common.h"
#include "../glslang/Include/revision.h"
#include <fstream>
#include <iomanip>
#include <list>
#include <map>
#include <stack>
#include <string>
#include <vector>
namespace {
namespace {
class SpecConstantOpModeGuard {
public:
SpecConstantOpModeGuard(spv::Builder* builder)
: builder_(builder) {
previous_flag_ = builder->isInSpecConstCodeGenMode();
}
~SpecConstantOpModeGuard() {
previous_flag_ ? builder_->setToSpecConstCodeGenMode()
: builder_->setToNormalCodeGenMode();
}
void turnOnSpecConstantOpMode() {
builder_->setToSpecConstCodeGenMode();
}
private:
spv::Builder* builder_;
bool previous_flag_;
};
struct OpDecorations {
public:
OpDecorations(spv::Decoration precision, spv::Decoration noContraction, spv::Decoration nonUniform) :
precision(precision)
#ifndef GLSLANG_WEB
,
noContraction(noContraction),
nonUniform(nonUniform)
#endif
{ }
spv::Decoration precision;
#ifdef GLSLANG_WEB
void addNoContraction(spv::Builder&, spv::Id) const { };
void addNonUniform(spv::Builder&, spv::Id) const { };
#else
void addNoContraction(spv::Builder& builder, spv::Id t) { builder.addDecoration(t, noContraction); };
void addNonUniform(spv::Builder& builder, spv::Id t) { builder.addDecoration(t, nonUniform); };
protected:
spv::Decoration noContraction;
spv::Decoration nonUniform;
#endif
};
} // namespace
//
// The main holder of information for translating glslang to SPIR-V.
//
// Derives from the AST walking base class.
//
class TGlslangToSpvTraverser : public glslang::TIntermTraverser {
public:
TGlslangToSpvTraverser(unsigned int spvVersion, const glslang::TIntermediate*, spv::SpvBuildLogger* logger,
glslang::SpvOptions& options);
virtual ~TGlslangToSpvTraverser() { }
bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*);
bool visitBinary(glslang::TVisit, glslang::TIntermBinary*);
void visitConstantUnion(glslang::TIntermConstantUnion*);
bool visitSelection(glslang::TVisit, glslang::TIntermSelection*);
bool visitSwitch(glslang::TVisit, glslang::TIntermSwitch*);
void visitSymbol(glslang::TIntermSymbol* symbol);
bool visitUnary(glslang::TVisit, glslang::TIntermUnary*);
bool visitLoop(glslang::TVisit, glslang::TIntermLoop*);
bool visitBranch(glslang::TVisit visit, glslang::TIntermBranch*);
void finishSpv();
void dumpSpv(std::vector<unsigned int>& out);
protected:
TGlslangToSpvTraverser(TGlslangToSpvTraverser&);
TGlslangToSpvTraverser& operator=(TGlslangToSpvTraverser&);
spv::Decoration TranslateInterpolationDecoration(const glslang::TQualifier& qualifier);
spv::Decoration TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier);
spv::Decoration TranslateNonUniformDecoration(const glslang::TQualifier& qualifier);
spv::Builder::AccessChain::CoherentFlags TranslateCoherent(const glslang::TType& type);
spv::MemoryAccessMask TranslateMemoryAccess(const spv::Builder::AccessChain::CoherentFlags &coherentFlags);
spv::ImageOperandsMask TranslateImageOperands(const spv::Builder::AccessChain::CoherentFlags &coherentFlags);
spv::Scope TranslateMemoryScope(const spv::Builder::AccessChain::CoherentFlags &coherentFlags);
spv::BuiltIn TranslateBuiltInDecoration(glslang::TBuiltInVariable, bool memberDeclaration);
spv::ImageFormat TranslateImageFormat(const glslang::TType& type);
spv::SelectionControlMask TranslateSelectionControl(const glslang::TIntermSelection&) const;
spv::SelectionControlMask TranslateSwitchControl(const glslang::TIntermSwitch&) const;
spv::LoopControlMask TranslateLoopControl(const glslang::TIntermLoop&, std::vector<unsigned int>& operands) const;
spv::StorageClass TranslateStorageClass(const glslang::TType&);
void addIndirectionIndexCapabilities(const glslang::TType& baseType, const glslang::TType& indexType);
spv::Id createSpvVariable(const glslang::TIntermSymbol*, spv::Id forcedType);
spv::Id getSampledType(const glslang::TSampler&);
spv::Id getInvertedSwizzleType(const glslang::TIntermTyped&);
spv::Id createInvertedSwizzle(spv::Decoration precision, const glslang::TIntermTyped&, spv::Id parentResult);
void convertSwizzle(const glslang::TIntermAggregate&, std::vector<unsigned>& swizzle);
spv::Id convertGlslangToSpvType(const glslang::TType& type, bool forwardReferenceOnly = false);
spv::Id convertGlslangToSpvType(const glslang::TType& type, glslang::TLayoutPacking, const glslang::TQualifier&,
bool lastBufferBlockMember, bool forwardReferenceOnly = false);
bool filterMember(const glslang::TType& member);
spv::Id convertGlslangStructToSpvType(const glslang::TType&, const glslang::TTypeList* glslangStruct,
glslang::TLayoutPacking, const glslang::TQualifier&);
void decorateStructType(const glslang::TType&, const glslang::TTypeList* glslangStruct, glslang::TLayoutPacking,
const glslang::TQualifier&, spv::Id);
spv::Id makeArraySizeId(const glslang::TArraySizes&, int dim);
spv::Id accessChainLoad(const glslang::TType& type);
void accessChainStore(const glslang::TType& type, spv::Id rvalue);
void multiTypeStore(const glslang::TType&, spv::Id rValue);
glslang::TLayoutPacking getExplicitLayout(const glslang::TType& type) const;
int getArrayStride(const glslang::TType& arrayType, glslang::TLayoutPacking, glslang::TLayoutMatrix);
int getMatrixStride(const glslang::TType& matrixType, glslang::TLayoutPacking, glslang::TLayoutMatrix);
void updateMemberOffset(const glslang::TType& structType, const glslang::TType& memberType, int& currentOffset,
int& nextOffset, glslang::TLayoutPacking, glslang::TLayoutMatrix);
void declareUseOfStructMember(const glslang::TTypeList& members, int glslangMember);
bool isShaderEntryPoint(const glslang::TIntermAggregate* node);
bool writableParam(glslang::TStorageQualifier) const;
bool originalParam(glslang::TStorageQualifier, const glslang::TType&, bool implicitThisParam);
void makeFunctions(const glslang::TIntermSequence&);
void makeGlobalInitializers(const glslang::TIntermSequence&);
void visitFunctions(const glslang::TIntermSequence&);
void handleFunctionEntry(const glslang::TIntermAggregate* node);
void translateArguments(const glslang::TIntermAggregate& node, std::vector<spv::Id>& arguments, spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags);
void translateArguments(glslang::TIntermUnary& node, std::vector<spv::Id>& arguments);
spv::Id createImageTextureFunctionCall(glslang::TIntermOperator* node);
spv::Id handleUserFunctionCall(const glslang::TIntermAggregate*);
spv::Id createBinaryOperation(glslang::TOperator op, OpDecorations&, spv::Id typeId, spv::Id left, spv::Id right,
glslang::TBasicType typeProxy, bool reduceComparison = true);
spv::Id createBinaryMatrixOperation(spv::Op, OpDecorations&, spv::Id typeId, spv::Id left, spv::Id right);
spv::Id createUnaryOperation(glslang::TOperator op, OpDecorations&, spv::Id typeId, spv::Id operand,
glslang::TBasicType typeProxy, const spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags);
spv::Id createUnaryMatrixOperation(spv::Op op, OpDecorations&, spv::Id typeId, spv::Id operand,
glslang::TBasicType typeProxy);
spv::Id createConversion(glslang::TOperator op, OpDecorations&, spv::Id destTypeId, spv::Id operand,
glslang::TBasicType typeProxy);
spv::Id createIntWidthConversion(glslang::TOperator op, spv::Id operand, int vectorSize);
spv::Id makeSmearedConstant(spv::Id constant, int vectorSize);
spv::Id createAtomicOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& operands, glslang::TBasicType typeProxy, const spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags);
spv::Id createInvocationsOperation(glslang::TOperator op, spv::Id typeId, std::vector<spv::Id>& operands, glslang::TBasicType typeProxy);
spv::Id CreateInvocationsVectorOperation(spv::Op op, spv::GroupOperation groupOperation, spv::Id typeId, std::vector<spv::Id>& operands);
spv::Id createSubgroupOperation(glslang::TOperator op, spv::Id typeId, std::vector<spv::Id>& operands, glslang::TBasicType typeProxy);
spv::Id createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& operands, glslang::TBasicType typeProxy);
spv::Id createNoArgOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId);
spv::Id getSymbolId(const glslang::TIntermSymbol* node);
void addMeshNVDecoration(spv::Id id, int member, const glslang::TQualifier & qualifier);
spv::Id createSpvConstant(const glslang::TIntermTyped&);
spv::Id createSpvConstantFromConstUnionArray(const glslang::TType& type, const glslang::TConstUnionArray&, int& nextConst, bool specConstant);
bool isTrivialLeaf(const glslang::TIntermTyped* node);
bool isTrivial(const glslang::TIntermTyped* node);
spv::Id createShortCircuit(glslang::TOperator, glslang::TIntermTyped& left, glslang::TIntermTyped& right);
spv::Id getExtBuiltins(const char* name);
std::pair<spv::Id, spv::Id> getForcedType(spv::BuiltIn, const glslang::TType&);
spv::Id translateForcedType(spv::Id object);
spv::Id createCompositeConstruct(spv::Id typeId, std::vector<spv::Id> constituents);
glslang::SpvOptions& options;
spv::Function* shaderEntry;
spv::Function* currentFunction;
spv::Instruction* entryPoint;
int sequenceDepth;
spv::SpvBuildLogger* logger;
// There is a 1:1 mapping between a spv builder and a module; this is thread safe
spv::Builder builder;
bool inEntryPoint;
bool entryPointTerminated;
bool linkageOnly; // true when visiting the set of objects in the AST present only for establishing interface, whether or not they were statically used
std::set<spv::Id> iOSet; // all input/output variables from either static use or declaration of interface
const glslang::TIntermediate* glslangIntermediate;
bool nanMinMaxClamp; // true if use NMin/NMax/NClamp instead of FMin/FMax/FClamp
spv::Id stdBuiltins;
std::unordered_map<const char*, spv::Id> extBuiltinMap;
std::unordered_map<int, spv::Id> symbolValues;
std::unordered_set<int> rValueParameters; // set of formal function parameters passed as rValues, rather than a pointer
std::unordered_map<std::string, spv::Function*> functionMap;
std::unordered_map<const glslang::TTypeList*, spv::Id> structMap[glslang::ElpCount][glslang::ElmCount];
// for mapping glslang block indices to spv indices (e.g., due to hidden members):
std::unordered_map<const glslang::TTypeList*, std::vector<int> > memberRemapper;
std::stack<bool> breakForLoop; // false means break for switch
std::unordered_map<std::string, const glslang::TIntermSymbol*> counterOriginator;
// Map pointee types for EbtReference to their forward pointers
std::map<const glslang::TType *, spv::Id> forwardPointers;
// Type forcing, for when SPIR-V wants a different type than the AST,
// requiring local translation to and from SPIR-V type on every access.
// Maps <builtin-variable-id -> AST-required-type-id>
std::unordered_map<spv::Id, spv::Id> forceType;
};
//
// Helper functions for translating glslang representations to SPIR-V enumerants.
//
// Translate glslang profile to SPIR-V source language.
spv::SourceLanguage TranslateSourceLanguage(glslang::EShSource source, EProfile profile)
{
#ifdef GLSLANG_WEB
return spv::SourceLanguageESSL;
#endif
switch (source) {
case glslang::EShSourceGlsl:
switch (profile) {
case ENoProfile:
case ECoreProfile:
case ECompatibilityProfile:
return spv::SourceLanguageGLSL;
case EEsProfile:
return spv::SourceLanguageESSL;
default:
return spv::SourceLanguageUnknown;
}
case glslang::EShSourceHlsl:
return spv::SourceLanguageHLSL;
default:
return spv::SourceLanguageUnknown;
}
}
// Translate glslang language (stage) to SPIR-V execution model.
spv::ExecutionModel TranslateExecutionModel(EShLanguage stage)
{
switch (stage) {
case EShLangVertex: return spv::ExecutionModelVertex;
case EShLangFragment: return spv::ExecutionModelFragment;
#ifndef GLSLANG_WEB
case EShLangCompute: return spv::ExecutionModelGLCompute;
case EShLangTessControl: return spv::ExecutionModelTessellationControl;
case EShLangTessEvaluation: return spv::ExecutionModelTessellationEvaluation;
case EShLangGeometry: return spv::ExecutionModelGeometry;
case EShLangRayGenNV: return spv::ExecutionModelRayGenerationNV;
case EShLangIntersectNV: return spv::ExecutionModelIntersectionNV;
case EShLangAnyHitNV: return spv::ExecutionModelAnyHitNV;
case EShLangClosestHitNV: return spv::ExecutionModelClosestHitNV;
case EShLangMissNV: return spv::ExecutionModelMissNV;
case EShLangCallableNV: return spv::ExecutionModelCallableNV;
case EShLangTaskNV: return spv::ExecutionModelTaskNV;
case EShLangMeshNV: return spv::ExecutionModelMeshNV;
#endif
default:
assert(0);
return spv::ExecutionModelFragment;
}
}
// Translate glslang sampler type to SPIR-V dimensionality.
spv::Dim TranslateDimensionality(const glslang::TSampler& sampler)
{
switch (sampler.dim) {
case glslang::Esd1D: return spv::Dim1D;
case glslang::Esd2D: return spv::Dim2D;
case glslang::Esd3D: return spv::Dim3D;
case glslang::EsdCube: return spv::DimCube;
case glslang::EsdRect: return spv::DimRect;
case glslang::EsdBuffer: return spv::DimBuffer;
case glslang::EsdSubpass: return spv::DimSubpassData;
default:
assert(0);
return spv::Dim2D;
}
}
// Translate glslang precision to SPIR-V precision decorations.
spv::Decoration TranslatePrecisionDecoration(glslang::TPrecisionQualifier glslangPrecision)
{
switch (glslangPrecision) {
case glslang::EpqLow: return spv::DecorationRelaxedPrecision;
case glslang::EpqMedium: return spv::DecorationRelaxedPrecision;
default:
return spv::NoPrecision;
}
}
// Translate glslang type to SPIR-V precision decorations.
spv::Decoration TranslatePrecisionDecoration(const glslang::TType& type)
{
return TranslatePrecisionDecoration(type.getQualifier().precision);
}
// Translate glslang type to SPIR-V block decorations.
spv::Decoration TranslateBlockDecoration(const glslang::TType& type, bool useStorageBuffer)
{
if (type.getBasicType() == glslang::EbtBlock) {
switch (type.getQualifier().storage) {
case glslang::EvqUniform: return spv::DecorationBlock;
case glslang::EvqBuffer: return useStorageBuffer ? spv::DecorationBlock : spv::DecorationBufferBlock;
case glslang::EvqVaryingIn: return spv::DecorationBlock;
case glslang::EvqVaryingOut: return spv::DecorationBlock;
#ifndef GLSLANG_WEB
case glslang::EvqPayloadNV: return spv::DecorationBlock;
case glslang::EvqPayloadInNV: return spv::DecorationBlock;
case glslang::EvqHitAttrNV: return spv::DecorationBlock;
case glslang::EvqCallableDataNV: return spv::DecorationBlock;
case glslang::EvqCallableDataInNV: return spv::DecorationBlock;
#endif
default:
assert(0);
break;
}
}
return spv::DecorationMax;
}
// Translate glslang type to SPIR-V memory decorations.
void TranslateMemoryDecoration(const glslang::TQualifier& qualifier, std::vector<spv::Decoration>& memory, bool useVulkanMemoryModel)
{
#ifndef GLSLANG_WEB
if (!useVulkanMemoryModel) {
if (qualifier.coherent)
memory.push_back(spv::DecorationCoherent);
if (qualifier.volatil) {
memory.push_back(spv::DecorationVolatile);
memory.push_back(spv::DecorationCoherent);
}
}
if (qualifier.restrict)
memory.push_back(spv::DecorationRestrict);
if (qualifier.isReadOnly())
memory.push_back(spv::DecorationNonWritable);
if (qualifier.isWriteOnly())
memory.push_back(spv::DecorationNonReadable);
#endif
}
// Translate glslang type to SPIR-V layout decorations.
spv::Decoration TranslateLayoutDecoration(const glslang::TType& type, glslang::TLayoutMatrix matrixLayout)
{
if (type.isMatrix()) {
switch (matrixLayout) {
case glslang::ElmRowMajor:
return spv::DecorationRowMajor;
case glslang::ElmColumnMajor:
return spv::DecorationColMajor;
default:
// opaque layouts don't need a majorness
return spv::DecorationMax;
}
} else {
switch (type.getBasicType()) {
default:
return spv::DecorationMax;
break;
case glslang::EbtBlock:
switch (type.getQualifier().storage) {
case glslang::EvqUniform:
case glslang::EvqBuffer:
switch (type.getQualifier().layoutPacking) {
case glslang::ElpShared: return spv::DecorationGLSLShared;
case glslang::ElpPacked: return spv::DecorationGLSLPacked;
default:
return spv::DecorationMax;
}
case glslang::EvqVaryingIn:
case glslang::EvqVaryingOut:
if (type.getQualifier().isTaskMemory()) {
switch (type.getQualifier().layoutPacking) {
case glslang::ElpShared: return spv::DecorationGLSLShared;
case glslang::ElpPacked: return spv::DecorationGLSLPacked;
default: break;
}
} else {
assert(type.getQualifier().layoutPacking == glslang::ElpNone);
}
return spv::DecorationMax;
#ifndef GLSLANG_WEB
case glslang::EvqPayloadNV:
case glslang::EvqPayloadInNV:
case glslang::EvqHitAttrNV:
case glslang::EvqCallableDataNV:
case glslang::EvqCallableDataInNV:
return spv::DecorationMax;
#endif
default:
assert(0);
return spv::DecorationMax;
}
}
}
}
// Translate glslang type to SPIR-V interpolation decorations.
// Returns spv::DecorationMax when no decoration
// should be applied.
spv::Decoration TGlslangToSpvTraverser::TranslateInterpolationDecoration(const glslang::TQualifier& qualifier)
{
if (qualifier.smooth)
// Smooth decoration doesn't exist in SPIR-V 1.0
return spv::DecorationMax;
else if (qualifier.isNonPerspective())
return spv::DecorationNoPerspective;
else if (qualifier.flat)
return spv::DecorationFlat;
else if (qualifier.isExplicitInterpolation()) {
builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
return spv::DecorationExplicitInterpAMD;
}
else
return spv::DecorationMax;
}
// Translate glslang type to SPIR-V auxiliary storage decorations.
// Returns spv::DecorationMax when no decoration
// should be applied.
spv::Decoration TGlslangToSpvTraverser::TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier)
{
if (qualifier.centroid)
return spv::DecorationCentroid;
#ifndef GLSLANG_WEB
else if (qualifier.patch)
return spv::DecorationPatch;
else if (qualifier.sample) {
builder.addCapability(spv::CapabilitySampleRateShading);
return spv::DecorationSample;
}
#endif
return spv::DecorationMax;
}
// If glslang type is invariant, return SPIR-V invariant decoration.
spv::Decoration TranslateInvariantDecoration(const glslang::TQualifier& qualifier)
{
if (qualifier.invariant)
return spv::DecorationInvariant;
else
return spv::DecorationMax;
}
// If glslang type is noContraction, return SPIR-V NoContraction decoration.
spv::Decoration TranslateNoContractionDecoration(const glslang::TQualifier& qualifier)
{
#ifndef GLSLANG_WEB
if (qualifier.isNoContraction())
return spv::DecorationNoContraction;
else
#endif
return spv::DecorationMax;
}
// If glslang type is nonUniform, return SPIR-V NonUniform decoration.
spv::Decoration TGlslangToSpvTraverser::TranslateNonUniformDecoration(const glslang::TQualifier& qualifier)
{
#ifndef GLSLANG_WEB
if (qualifier.isNonUniform()) {
builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5);
builder.addCapability(spv::CapabilityShaderNonUniformEXT);
return spv::DecorationNonUniformEXT;
} else
#endif
return spv::DecorationMax;
}
spv::MemoryAccessMask TGlslangToSpvTraverser::TranslateMemoryAccess(
const spv::Builder::AccessChain::CoherentFlags &coherentFlags)
{
spv::MemoryAccessMask mask = spv::MemoryAccessMaskNone;
#ifndef GLSLANG_WEB
if (!glslangIntermediate->usingVulkanMemoryModel() || coherentFlags.isImage)
return mask;
if (coherentFlags.volatil ||
coherentFlags.coherent ||
coherentFlags.devicecoherent ||
coherentFlags.queuefamilycoherent ||
coherentFlags.workgroupcoherent ||
coherentFlags.subgroupcoherent) {
mask = mask | spv::MemoryAccessMakePointerAvailableKHRMask |
spv::MemoryAccessMakePointerVisibleKHRMask;
}
if (coherentFlags.nonprivate) {
mask = mask | spv::MemoryAccessNonPrivatePointerKHRMask;
}
if (coherentFlags.volatil) {
mask = mask | spv::MemoryAccessVolatileMask;
}
if (mask != spv::MemoryAccessMaskNone) {
builder.addCapability(spv::CapabilityVulkanMemoryModelKHR);
}
#endif
return mask;
}
spv::ImageOperandsMask TGlslangToSpvTraverser::TranslateImageOperands(
const spv::Builder::AccessChain::CoherentFlags &coherentFlags)
{
spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone;
#ifndef GLSLANG_WEB
if (!glslangIntermediate->usingVulkanMemoryModel())
return mask;
if (coherentFlags.volatil ||
coherentFlags.coherent ||
coherentFlags.devicecoherent ||
coherentFlags.queuefamilycoherent ||
coherentFlags.workgroupcoherent ||
coherentFlags.subgroupcoherent) {
mask = mask | spv::ImageOperandsMakeTexelAvailableKHRMask |
spv::ImageOperandsMakeTexelVisibleKHRMask;
}
if (coherentFlags.nonprivate) {
mask = mask | spv::ImageOperandsNonPrivateTexelKHRMask;
}
if (coherentFlags.volatil) {
mask = mask | spv::ImageOperandsVolatileTexelKHRMask;
}
if (mask != spv::ImageOperandsMaskNone) {
builder.addCapability(spv::CapabilityVulkanMemoryModelKHR);
}
#endif
return mask;
}
spv::Builder::AccessChain::CoherentFlags TGlslangToSpvTraverser::TranslateCoherent(const glslang::TType& type)
{
spv::Builder::AccessChain::CoherentFlags flags = {};
#ifndef GLSLANG_WEB
flags.coherent = type.getQualifier().coherent;
flags.devicecoherent = type.getQualifier().devicecoherent;
flags.queuefamilycoherent = type.getQualifier().queuefamilycoherent;
// shared variables are implicitly workgroupcoherent in GLSL.
flags.workgroupcoherent = type.getQualifier().workgroupcoherent ||
type.getQualifier().storage == glslang::EvqShared;
flags.subgroupcoherent = type.getQualifier().subgroupcoherent;
flags.volatil = type.getQualifier().volatil;
// *coherent variables are implicitly nonprivate in GLSL
flags.nonprivate = type.getQualifier().nonprivate ||
flags.subgroupcoherent ||
flags.workgroupcoherent ||
flags.queuefamilycoherent ||
flags.devicecoherent ||
flags.coherent ||
flags.volatil;
flags.isImage = type.getBasicType() == glslang::EbtSampler;
#endif
return flags;
}
spv::Scope TGlslangToSpvTraverser::TranslateMemoryScope(
const spv::Builder::AccessChain::CoherentFlags &coherentFlags)
{
spv::Scope scope = spv::ScopeMax;
#ifndef GLSLANG_WEB
if (coherentFlags.volatil || coherentFlags.coherent) {
// coherent defaults to Device scope in the old model, QueueFamilyKHR scope in the new model
scope = glslangIntermediate->usingVulkanMemoryModel() ? spv::ScopeQueueFamilyKHR : spv::ScopeDevice;
} else if (coherentFlags.devicecoherent) {
scope = spv::ScopeDevice;
} else if (coherentFlags.queuefamilycoherent) {
scope = spv::ScopeQueueFamilyKHR;
} else if (coherentFlags.workgroupcoherent) {
scope = spv::ScopeWorkgroup;
} else if (coherentFlags.subgroupcoherent) {
scope = spv::ScopeSubgroup;
}
if (glslangIntermediate->usingVulkanMemoryModel() && scope == spv::ScopeDevice) {
builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR);
}
#endif
return scope;
}
// Translate a glslang built-in variable to a SPIR-V built in decoration. Also generate
// associated capabilities when required. For some built-in variables, a capability
// is generated only when using the variable in an executable instruction, but not when
// just declaring a struct member variable with it. This is true for PointSize,
// ClipDistance, and CullDistance.
spv::BuiltIn TGlslangToSpvTraverser::TranslateBuiltInDecoration(glslang::TBuiltInVariable builtIn, bool memberDeclaration)
{
switch (builtIn) {
case glslang::EbvPointSize:
#ifndef GLSLANG_WEB
// Defer adding the capability until the built-in is actually used.
if (! memberDeclaration) {
switch (glslangIntermediate->getStage()) {
case EShLangGeometry:
builder.addCapability(spv::CapabilityGeometryPointSize);
break;
case EShLangTessControl:
case EShLangTessEvaluation:
builder.addCapability(spv::CapabilityTessellationPointSize);
break;
default:
break;
}
}
#endif
return spv::BuiltInPointSize;
case glslang::EbvPosition: return spv::BuiltInPosition;
case glslang::EbvVertexId: return spv::BuiltInVertexId;
case glslang::EbvInstanceId: return spv::BuiltInInstanceId;
case glslang::EbvVertexIndex: return spv::BuiltInVertexIndex;
case glslang::EbvInstanceIndex: return spv::BuiltInInstanceIndex;
case glslang::EbvFragCoord: return spv::BuiltInFragCoord;
case glslang::EbvPointCoord: return spv::BuiltInPointCoord;
case glslang::EbvFace: return spv::BuiltInFrontFacing;
case glslang::EbvFragDepth: return spv::BuiltInFragDepth;
#ifndef GLSLANG_WEB
// These *Distance capabilities logically belong here, but if the member is declared and
// then never used, consumers of SPIR-V prefer the capability not be declared.
// They are now generated when used, rather than here when declared.
// Potentially, the specification should be more clear what the minimum
// use needed is to trigger the capability.
//
case glslang::EbvClipDistance:
if (!memberDeclaration)
builder.addCapability(spv::CapabilityClipDistance);
return spv::BuiltInClipDistance;
case glslang::EbvCullDistance:
if (!memberDeclaration)
builder.addCapability(spv::CapabilityCullDistance);
return spv::BuiltInCullDistance;
case glslang::EbvViewportIndex:
builder.addCapability(spv::CapabilityMultiViewport);
if (glslangIntermediate->getStage() == EShLangVertex ||
glslangIntermediate->getStage() == EShLangTessControl ||
glslangIntermediate->getStage() == EShLangTessEvaluation) {
builder.addIncorporatedExtension(spv::E_SPV_EXT_shader_viewport_index_layer, spv::Spv_1_5);
builder.addCapability(spv::CapabilityShaderViewportIndexLayerEXT);
}
return spv::BuiltInViewportIndex;
case glslang::EbvSampleId:
builder.addCapability(spv::CapabilitySampleRateShading);
return spv::BuiltInSampleId;
case glslang::EbvSamplePosition:
builder.addCapability(spv::CapabilitySampleRateShading);
return spv::BuiltInSamplePosition;
case glslang::EbvSampleMask:
return spv::BuiltInSampleMask;
case glslang::EbvLayer:
if (glslangIntermediate->getStage() == EShLangMeshNV) {
return spv::BuiltInLayer;
}
builder.addCapability(spv::CapabilityGeometry);
if (glslangIntermediate->getStage() == EShLangVertex ||
glslangIntermediate->getStage() == EShLangTessControl ||
glslangIntermediate->getStage() == EShLangTessEvaluation) {
builder.addIncorporatedExtension(spv::E_SPV_EXT_shader_viewport_index_layer, spv::Spv_1_5);
builder.addCapability(spv::CapabilityShaderViewportIndexLayerEXT);
}
return spv::BuiltInLayer;
case glslang::EbvBaseVertex:
builder.addIncorporatedExtension(spv::E_SPV_KHR_shader_draw_parameters, spv::Spv_1_3);
builder.addCapability(spv::CapabilityDrawParameters);
return spv::BuiltInBaseVertex;
case glslang::EbvBaseInstance:
builder.addIncorporatedExtension(spv::E_SPV_KHR_shader_draw_parameters, spv::Spv_1_3);
builder.addCapability(spv::CapabilityDrawParameters);
return spv::BuiltInBaseInstance;
case glslang::EbvDrawId:
builder.addIncorporatedExtension(spv::E_SPV_KHR_shader_draw_parameters, spv::Spv_1_3);
builder.addCapability(spv::CapabilityDrawParameters);
return spv::BuiltInDrawIndex;
case glslang::EbvPrimitiveId:
if (glslangIntermediate->getStage() == EShLangFragment)
builder.addCapability(spv::CapabilityGeometry);
return spv::BuiltInPrimitiveId;
case glslang::EbvFragStencilRef:
builder.addExtension(spv::E_SPV_EXT_shader_stencil_export);
builder.addCapability(spv::CapabilityStencilExportEXT);
return spv::BuiltInFragStencilRefEXT;
case glslang::EbvInvocationId: return spv::BuiltInInvocationId;
case glslang::EbvTessLevelInner: return spv::BuiltInTessLevelInner;
case glslang::EbvTessLevelOuter: return spv::BuiltInTessLevelOuter;
case glslang::EbvTessCoord: return spv::BuiltInTessCoord;
case glslang::EbvPatchVertices: return spv::BuiltInPatchVertices;
case glslang::EbvHelperInvocation: return spv::BuiltInHelperInvocation;
case glslang::EbvNumWorkGroups: return spv::BuiltInNumWorkgroups;
case glslang::EbvWorkGroupSize: return spv::BuiltInWorkgroupSize;
case glslang::EbvWorkGroupId: return spv::BuiltInWorkgroupId;
case glslang::EbvLocalInvocationId: return spv::BuiltInLocalInvocationId;
case glslang::EbvLocalInvocationIndex: return spv::BuiltInLocalInvocationIndex;
case glslang::EbvGlobalInvocationId: return spv::BuiltInGlobalInvocationId;
case glslang::EbvSubGroupSize:
builder.addExtension(spv::E_SPV_KHR_shader_ballot);
builder.addCapability(spv::CapabilitySubgroupBallotKHR);
return spv::BuiltInSubgroupSize;
case glslang::EbvSubGroupInvocation:
builder.addExtension(spv::E_SPV_KHR_shader_ballot);
builder.addCapability(spv::CapabilitySubgroupBallotKHR);
return spv::BuiltInSubgroupLocalInvocationId;
case glslang::EbvSubGroupEqMask:
builder.addExtension(spv::E_SPV_KHR_shader_ballot);
builder.addCapability(spv::CapabilitySubgroupBallotKHR);
return spv::BuiltInSubgroupEqMask;
case glslang::EbvSubGroupGeMask:
builder.addExtension(spv::E_SPV_KHR_shader_ballot);
builder.addCapability(spv::CapabilitySubgroupBallotKHR);
return spv::BuiltInSubgroupGeMask;
case glslang::EbvSubGroupGtMask:
builder.addExtension(spv::E_SPV_KHR_shader_ballot);
builder.addCapability(spv::CapabilitySubgroupBallotKHR);
return spv::BuiltInSubgroupGtMask;
case glslang::EbvSubGroupLeMask:
builder.addExtension(spv::E_SPV_KHR_shader_ballot);
builder.addCapability(spv::CapabilitySubgroupBallotKHR);
return spv::BuiltInSubgroupLeMask;
case glslang::EbvSubGroupLtMask:
builder.addExtension(spv::E_SPV_KHR_shader_ballot);
builder.addCapability(spv::CapabilitySubgroupBallotKHR);
return spv::BuiltInSubgroupLtMask;
case glslang::EbvNumSubgroups:
builder.addCapability(spv::CapabilityGroupNonUniform);
return spv::BuiltInNumSubgroups;
case glslang::EbvSubgroupID:
builder.addCapability(spv::CapabilityGroupNonUniform);
return spv::BuiltInSubgroupId;
case glslang::EbvSubgroupSize2:
builder.addCapability(spv::CapabilityGroupNonUniform);
return spv::BuiltInSubgroupSize;
case glslang::EbvSubgroupInvocation2:
builder.addCapability(spv::CapabilityGroupNonUniform);
return spv::BuiltInSubgroupLocalInvocationId;
case glslang::EbvSubgroupEqMask2:
builder.addCapability(spv::CapabilityGroupNonUniform);
builder.addCapability(spv::CapabilityGroupNonUniformBallot);
return spv::BuiltInSubgroupEqMask;
case glslang::EbvSubgroupGeMask2:
builder.addCapability(spv::CapabilityGroupNonUniform);
builder.addCapability(spv::CapabilityGroupNonUniformBallot);
return spv::BuiltInSubgroupGeMask;
case glslang::EbvSubgroupGtMask2:
builder.addCapability(spv::CapabilityGroupNonUniform);
builder.addCapability(spv::CapabilityGroupNonUniformBallot);
return spv::BuiltInSubgroupGtMask;
case glslang::EbvSubgroupLeMask2:
builder.addCapability(spv::CapabilityGroupNonUniform);
builder.addCapability(spv::CapabilityGroupNonUniformBallot);
return spv::BuiltInSubgroupLeMask;
case glslang::EbvSubgroupLtMask2:
builder.addCapability(spv::CapabilityGroupNonUniform);
builder.addCapability(spv::CapabilityGroupNonUniformBallot);
return spv::BuiltInSubgroupLtMask;
case glslang::EbvBaryCoordNoPersp:
builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
return spv::BuiltInBaryCoordNoPerspAMD;
case glslang::EbvBaryCoordNoPerspCentroid:
builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
return spv::BuiltInBaryCoordNoPerspCentroidAMD;
case glslang::EbvBaryCoordNoPerspSample:
builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
return spv::BuiltInBaryCoordNoPerspSampleAMD;
case glslang::EbvBaryCoordSmooth:
builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
return spv::BuiltInBaryCoordSmoothAMD;
case glslang::EbvBaryCoordSmoothCentroid:
builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
return spv::BuiltInBaryCoordSmoothCentroidAMD;
case glslang::EbvBaryCoordSmoothSample:
builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
return spv::BuiltInBaryCoordSmoothSampleAMD;
case glslang::EbvBaryCoordPullModel:
builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
return spv::BuiltInBaryCoordPullModelAMD;
case glslang::EbvDeviceIndex:
builder.addIncorporatedExtension(spv::E_SPV_KHR_device_group, spv::Spv_1_3);
builder.addCapability(spv::CapabilityDeviceGroup);
return spv::BuiltInDeviceIndex;
case glslang::EbvViewIndex:
builder.addIncorporatedExtension(spv::E_SPV_KHR_multiview, spv::Spv_1_3);
builder.addCapability(spv::CapabilityMultiView);
return spv::BuiltInViewIndex;
case glslang::EbvFragSizeEXT:
builder.addExtension(spv::E_SPV_EXT_fragment_invocation_density);
builder.addCapability(spv::CapabilityFragmentDensityEXT);
return spv::BuiltInFragSizeEXT;
case glslang::EbvFragInvocationCountEXT:
builder.addExtension(spv::E_SPV_EXT_fragment_invocation_density);
builder.addCapability(spv::CapabilityFragmentDensityEXT);
return spv::BuiltInFragInvocationCountEXT;
case glslang::EbvViewportMaskNV:
if (!memberDeclaration) {
builder.addExtension(spv::E_SPV_NV_viewport_array2);
builder.addCapability(spv::CapabilityShaderViewportMaskNV);
}
return spv::BuiltInViewportMaskNV;
case glslang::EbvSecondaryPositionNV:
if (!memberDeclaration) {
builder.addExtension(spv::E_SPV_NV_stereo_view_rendering);
builder.addCapability(spv::CapabilityShaderStereoViewNV);
}
return spv::BuiltInSecondaryPositionNV;
case glslang::EbvSecondaryViewportMaskNV:
if (!memberDeclaration) {
builder.addExtension(spv::E_SPV_NV_stereo_view_rendering);
builder.addCapability(spv::CapabilityShaderStereoViewNV);
}
return spv::BuiltInSecondaryViewportMaskNV;
case glslang::EbvPositionPerViewNV:
if (!memberDeclaration) {
builder.addExtension(spv::E_SPV_NVX_multiview_per_view_attributes);
builder.addCapability(spv::CapabilityPerViewAttributesNV);
}
return spv::BuiltInPositionPerViewNV;
case glslang::EbvViewportMaskPerViewNV:
if (!memberDeclaration) {
builder.addExtension(spv::E_SPV_NVX_multiview_per_view_attributes);
builder.addCapability(spv::CapabilityPerViewAttributesNV);
}
return spv::BuiltInViewportMaskPerViewNV;
case glslang::EbvFragFullyCoveredNV:
builder.addExtension(spv::E_SPV_EXT_fragment_fully_covered);
builder.addCapability(spv::CapabilityFragmentFullyCoveredEXT);
return spv::BuiltInFullyCoveredEXT;
case glslang::EbvFragmentSizeNV:
builder.addExtension(spv::E_SPV_NV_shading_rate);
builder.addCapability(spv::CapabilityShadingRateNV);
return spv::BuiltInFragmentSizeNV;
case glslang::EbvInvocationsPerPixelNV:
builder.addExtension(spv::E_SPV_NV_shading_rate);
builder.addCapability(spv::CapabilityShadingRateNV);
return spv::BuiltInInvocationsPerPixelNV;
// ray tracing
case glslang::EbvLaunchIdNV:
return spv::BuiltInLaunchIdNV;
case glslang::EbvLaunchSizeNV:
return spv::BuiltInLaunchSizeNV;
case glslang::EbvWorldRayOriginNV:
return spv::BuiltInWorldRayOriginNV;
case glslang::EbvWorldRayDirectionNV:
return spv::BuiltInWorldRayDirectionNV;
case glslang::EbvObjectRayOriginNV:
return spv::BuiltInObjectRayOriginNV;
case glslang::EbvObjectRayDirectionNV:
return spv::BuiltInObjectRayDirectionNV;
case glslang::EbvRayTminNV:
return spv::BuiltInRayTminNV;
case glslang::EbvRayTmaxNV:
return spv::BuiltInRayTmaxNV;
case glslang::EbvInstanceCustomIndexNV:
return spv::BuiltInInstanceCustomIndexNV;
case glslang::EbvHitTNV:
return spv::BuiltInHitTNV;
case glslang::EbvHitKindNV:
return spv::BuiltInHitKindNV;
case glslang::EbvObjectToWorldNV:
return spv::BuiltInObjectToWorldNV;
case glslang::EbvWorldToObjectNV:
return spv::BuiltInWorldToObjectNV;
case glslang::EbvIncomingRayFlagsNV:
return spv::BuiltInIncomingRayFlagsNV;
// barycentrics
case glslang::EbvBaryCoordNV:
builder.addExtension(spv::E_SPV_NV_fragment_shader_barycentric);
builder.addCapability(spv::CapabilityFragmentBarycentricNV);
return spv::BuiltInBaryCoordNV;
case glslang::EbvBaryCoordNoPerspNV:
builder.addExtension(spv::E_SPV_NV_fragment_shader_barycentric);
builder.addCapability(spv::CapabilityFragmentBarycentricNV);
return spv::BuiltInBaryCoordNoPerspNV;
// mesh shaders
case glslang::EbvTaskCountNV:
return spv::BuiltInTaskCountNV;
case glslang::EbvPrimitiveCountNV:
return spv::BuiltInPrimitiveCountNV;
case glslang::EbvPrimitiveIndicesNV:
return spv::BuiltInPrimitiveIndicesNV;
case glslang::EbvClipDistancePerViewNV:
return spv::BuiltInClipDistancePerViewNV;
case glslang::EbvCullDistancePerViewNV:
return spv::BuiltInCullDistancePerViewNV;
case glslang::EbvLayerPerViewNV:
return spv::BuiltInLayerPerViewNV;
case glslang::EbvMeshViewCountNV:
return spv::BuiltInMeshViewCountNV;
case glslang::EbvMeshViewIndicesNV:
return spv::BuiltInMeshViewIndicesNV;
// sm builtins
case glslang::EbvWarpsPerSM:
builder.addExtension(spv::E_SPV_NV_shader_sm_builtins);
builder.addCapability(spv::CapabilityShaderSMBuiltinsNV);
return spv::BuiltInWarpsPerSMNV;
case glslang::EbvSMCount:
builder.addExtension(spv::E_SPV_NV_shader_sm_builtins);
builder.addCapability(spv::CapabilityShaderSMBuiltinsNV);
return spv::BuiltInSMCountNV;
case glslang::EbvWarpID:
builder.addExtension(spv::E_SPV_NV_shader_sm_builtins);
builder.addCapability(spv::CapabilityShaderSMBuiltinsNV);
return spv::BuiltInWarpIDNV;
case glslang::EbvSMID:
builder.addExtension(spv::E_SPV_NV_shader_sm_builtins);
builder.addCapability(spv::CapabilityShaderSMBuiltinsNV);
return spv::BuiltInSMIDNV;
#endif
default:
return spv::BuiltInMax;
}
}
// Translate glslang image layout format to SPIR-V image format.
spv::ImageFormat TGlslangToSpvTraverser::TranslateImageFormat(const glslang::TType& type)
{
assert(type.getBasicType() == glslang::EbtSampler);
#ifdef GLSLANG_WEB
return spv::ImageFormatUnknown;
#endif
// Check for capabilities
switch (type.getQualifier().getFormat()) {
case glslang::ElfRg32f:
case glslang::ElfRg16f:
case glslang::ElfR11fG11fB10f:
case glslang::ElfR16f:
case glslang::ElfRgba16:
case glslang::ElfRgb10A2:
case glslang::ElfRg16:
case glslang::ElfRg8:
case glslang::ElfR16:
case glslang::ElfR8:
case glslang::ElfRgba16Snorm:
case glslang::ElfRg16Snorm:
case glslang::ElfRg8Snorm:
case glslang::ElfR16Snorm:
case glslang::ElfR8Snorm:
case glslang::ElfRg32i:
case glslang::ElfRg16i:
case glslang::ElfRg8i:
case glslang::ElfR16i:
case glslang::ElfR8i:
case glslang::ElfRgb10a2ui:
case glslang::ElfRg32ui:
case glslang::ElfRg16ui:
case glslang::ElfRg8ui:
case glslang::ElfR16ui:
case glslang::ElfR8ui:
builder.addCapability(spv::CapabilityStorageImageExtendedFormats);
break;
default:
break;
}
// do the translation
switch (type.getQualifier().getFormat()) {
case glslang::ElfNone: return spv::ImageFormatUnknown;
case glslang::ElfRgba32f: return spv::ImageFormatRgba32f;
case glslang::ElfRgba16f: return spv::ImageFormatRgba16f;
case glslang::ElfR32f: return spv::ImageFormatR32f;
case glslang::ElfRgba8: return spv::ImageFormatRgba8;
case glslang::ElfRgba8Snorm: return spv::ImageFormatRgba8Snorm;
case glslang::ElfRg32f: return spv::ImageFormatRg32f;
case glslang::ElfRg16f: return spv::ImageFormatRg16f;
case glslang::ElfR11fG11fB10f: return spv::ImageFormatR11fG11fB10f;
case glslang::ElfR16f: return spv::ImageFormatR16f;
case glslang::ElfRgba16: return spv::ImageFormatRgba16;
case glslang::ElfRgb10A2: return spv::ImageFormatRgb10A2;
case glslang::ElfRg16: return spv::ImageFormatRg16;
case glslang::ElfRg8: return spv::ImageFormatRg8;
case glslang::ElfR16: return spv::ImageFormatR16;
case glslang::ElfR8: return spv::ImageFormatR8;
case glslang::ElfRgba16Snorm: return spv::ImageFormatRgba16Snorm;
case glslang::ElfRg16Snorm: return spv::ImageFormatRg16Snorm;
case glslang::ElfRg8Snorm: return spv::ImageFormatRg8Snorm;
case glslang::ElfR16Snorm: return spv::ImageFormatR16Snorm;
case glslang::ElfR8Snorm: return spv::ImageFormatR8Snorm;
case glslang::ElfRgba32i: return spv::ImageFormatRgba32i;
case glslang::ElfRgba16i: return spv::ImageFormatRgba16i;
case glslang::ElfRgba8i: return spv::ImageFormatRgba8i;
case glslang::ElfR32i: return spv::ImageFormatR32i;
case glslang::ElfRg32i: return spv::ImageFormatRg32i;
case glslang::ElfRg16i: return spv::ImageFormatRg16i;
case glslang::ElfRg8i: return spv::ImageFormatRg8i;
case glslang::ElfR16i: return spv::ImageFormatR16i;
case glslang::ElfR8i: return spv::ImageFormatR8i;
case glslang::ElfRgba32ui: return spv::ImageFormatRgba32ui;
case glslang::ElfRgba16ui: return spv::ImageFormatRgba16ui;
case glslang::ElfRgba8ui: return spv::ImageFormatRgba8ui;
case glslang::ElfR32ui: return spv::ImageFormatR32ui;
case glslang::ElfRg32ui: return spv::ImageFormatRg32ui;
case glslang::ElfRg16ui: return spv::ImageFormatRg16ui;
case glslang::ElfRgb10a2ui: return spv::ImageFormatRgb10a2ui;
case glslang::ElfRg8ui: return spv::ImageFormatRg8ui;
case glslang::ElfR16ui: return spv::ImageFormatR16ui;
case glslang::ElfR8ui: return spv::ImageFormatR8ui;
default: return spv::ImageFormatMax;
}
}
spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSelectionControl(const glslang::TIntermSelection& selectionNode) const
{
if (selectionNode.getFlatten())
return spv::SelectionControlFlattenMask;
if (selectionNode.getDontFlatten())
return spv::SelectionControlDontFlattenMask;
return spv::SelectionControlMaskNone;
}
spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSwitchControl(const glslang::TIntermSwitch& switchNode) const
{
if (switchNode.getFlatten())
return spv::SelectionControlFlattenMask;
if (switchNode.getDontFlatten())
return spv::SelectionControlDontFlattenMask;
return spv::SelectionControlMaskNone;
}
// return a non-0 dependency if the dependency argument must be set
spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(const glslang::TIntermLoop& loopNode,
std::vector<unsigned int>& operands) const
{
spv::LoopControlMask control = spv::LoopControlMaskNone;
if (loopNode.getDontUnroll())
control = control | spv::LoopControlDontUnrollMask;
if (loopNode.getUnroll())
control = control | spv::LoopControlUnrollMask;
if (unsigned(loopNode.getLoopDependency()) == glslang::TIntermLoop::dependencyInfinite)
control = control | spv::LoopControlDependencyInfiniteMask;
else if (loopNode.getLoopDependency() > 0) {
control = control | spv::LoopControlDependencyLengthMask;
operands.push_back((unsigned int)loopNode.getLoopDependency());
}
if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) {
if (loopNode.getMinIterations() > 0) {
control = control | spv::LoopControlMinIterationsMask;
operands.push_back(loopNode.getMinIterations());
}
if (loopNode.getMaxIterations() < glslang::TIntermLoop::iterationsInfinite) {
control = control | spv::LoopControlMaxIterationsMask;
operands.push_back(loopNode.getMaxIterations());
}
if (loopNode.getIterationMultiple() > 1) {
control = control | spv::LoopControlIterationMultipleMask;
operands.push_back(loopNode.getIterationMultiple());
}
if (loopNode.getPeelCount() > 0) {
control = control | spv::LoopControlPeelCountMask;
operands.push_back(loopNode.getPeelCount());
}
if (loopNode.getPartialCount() > 0) {
control = control | spv::LoopControlPartialCountMask;
operands.push_back(loopNode.getPartialCount());
}
}
return control;
}
// Translate glslang type to SPIR-V storage class.
spv::StorageClass TGlslangToSpvTraverser::TranslateStorageClass(const glslang::TType& type)
{
if (type.getQualifier().isPipeInput())
return spv::StorageClassInput;
if (type.getQualifier().isPipeOutput())
return spv::StorageClassOutput;
if (glslangIntermediate->getSource() != glslang::EShSourceHlsl ||
type.getQualifier().storage == glslang::EvqUniform) {
if (type.isAtomic())
return spv::StorageClassAtomicCounter;
if (type.containsOpaque())
return spv::StorageClassUniformConstant;
}
if (type.getQualifier().isUniformOrBuffer() &&
type.getQualifier().isShaderRecordNV()) {
return spv::StorageClassShaderRecordBufferNV;
}
if (glslangIntermediate->usingStorageBuffer() && type.getQualifier().storage == glslang::EvqBuffer) {
builder.addIncorporatedExtension(spv::E_SPV_KHR_storage_buffer_storage_class, spv::Spv_1_3);
return spv::StorageClassStorageBuffer;
}
if (type.getQualifier().isUniformOrBuffer()) {
if (type.getQualifier().isPushConstant())
return spv::StorageClassPushConstant;
if (type.getBasicType() == glslang::EbtBlock)
return spv::StorageClassUniform;
return spv::StorageClassUniformConstant;
}
switch (type.getQualifier().storage) {
case glslang::EvqGlobal: return spv::StorageClassPrivate;
case glslang::EvqConstReadOnly: return spv::StorageClassFunction;
case glslang::EvqTemporary: return spv::StorageClassFunction;
#ifndef GLSLANG_WEB
case glslang::EvqShared: return spv::StorageClassWorkgroup;
case glslang::EvqPayloadNV: return spv::StorageClassRayPayloadNV;
case glslang::EvqPayloadInNV: return spv::StorageClassIncomingRayPayloadNV;
case glslang::EvqHitAttrNV: return spv::StorageClassHitAttributeNV;
case glslang::EvqCallableDataNV: return spv::StorageClassCallableDataNV;
case glslang::EvqCallableDataInNV: return spv::StorageClassIncomingCallableDataNV;
#endif
default:
assert(0);
break;
}
return spv::StorageClassFunction;
}
// Add capabilities pertaining to how an array is indexed.
void TGlslangToSpvTraverser::addIndirectionIndexCapabilities(const glslang::TType& baseType,
const glslang::TType& indexType)
{
#ifndef GLSLANG_WEB
if (indexType.getQualifier().isNonUniform()) {
// deal with an asserted non-uniform index
// SPV_EXT_descriptor_indexing already added in TranslateNonUniformDecoration
if (baseType.getBasicType() == glslang::EbtSampler) {
if (baseType.getQualifier().hasAttachment())
builder.addCapability(spv::CapabilityInputAttachmentArrayNonUniformIndexingEXT);
else if (baseType.isImage() && baseType.getSampler().isBuffer())
builder.addCapability(spv::CapabilityStorageTexelBufferArrayNonUniformIndexingEXT);
else if (baseType.isTexture() && baseType.getSampler().isBuffer())
builder.addCapability(spv::CapabilityUniformTexelBufferArrayNonUniformIndexingEXT);
else if (baseType.isImage())
builder.addCapability(spv::CapabilityStorageImageArrayNonUniformIndexingEXT);
else if (baseType.isTexture())
builder.addCapability(spv::CapabilitySampledImageArrayNonUniformIndexingEXT);
} else if (baseType.getBasicType() == glslang::EbtBlock) {
if (baseType.getQualifier().storage == glslang::EvqBuffer)
builder.addCapability(spv::CapabilityStorageBufferArrayNonUniformIndexingEXT);
else if (baseType.getQualifier().storage == glslang::EvqUniform)
builder.addCapability(spv::CapabilityUniformBufferArrayNonUniformIndexingEXT);
}
} else {
// assume a dynamically uniform index
if (baseType.getBasicType() == glslang::EbtSampler) {
if (baseType.getQualifier().hasAttachment()) {
builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5);
builder.addCapability(spv::CapabilityInputAttachmentArrayDynamicIndexingEXT);
} else if (baseType.isImage() && baseType.getSampler().isBuffer()) {
builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5);
builder.addCapability(spv::CapabilityStorageTexelBufferArrayDynamicIndexingEXT);
} else if (baseType.isTexture() && baseType.getSampler().isBuffer()) {
builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5);
builder.addCapability(spv::CapabilityUniformTexelBufferArrayDynamicIndexingEXT);
}
}
}
#endif
}
// Return whether or not the given type is something that should be tied to a
// descriptor set.
bool IsDescriptorResource(const glslang::TType& type)
{
// uniform and buffer blocks are included, unless it is a push_constant
if (type.getBasicType() == glslang::EbtBlock)
return type.getQualifier().isUniformOrBuffer() &&
! type.getQualifier().isShaderRecordNV() &&
! type.getQualifier().isPushConstant();
// non block...
// basically samplerXXX/subpass/sampler/texture are all included
// if they are the global-scope-class, not the function parameter
// (or local, if they ever exist) class.
if (type.getBasicType() == glslang::EbtSampler)
return type.getQualifier().isUniformOrBuffer();
// None of the above.
return false;
}
void InheritQualifiers(glslang::TQualifier& child, const glslang::TQualifier& parent)
{
if (child.layoutMatrix == glslang::ElmNone)
child.layoutMatrix = parent.layoutMatrix;
if (parent.invariant)
child.invariant = true;
if (parent.flat)
child.flat = true;
if (parent.centroid)
child.centroid = true;
#ifndef GLSLANG_WEB
if (parent.nopersp)
child.nopersp = true;
if (parent.explicitInterp)
child.explicitInterp = true;
if (parent.perPrimitiveNV)
child.perPrimitiveNV = true;
if (parent.perViewNV)
child.perViewNV = true;
if (parent.perTaskNV)
child.perTaskNV = true;
if (parent.patch)
child.patch = true;
if (parent.sample)
child.sample = true;
if (parent.coherent)
child.coherent = true;
if (parent.devicecoherent)
child.devicecoherent = true;
if (parent.queuefamilycoherent)
child.queuefamilycoherent = true;
if (parent.workgroupcoherent)
child.workgroupcoherent = true;
if (parent.subgroupcoherent)
child.subgroupcoherent = true;
if (parent.nonprivate)
child.nonprivate = true;
if (parent.volatil)
child.volatil = true;
if (parent.restrict)
child.restrict = true;
if (parent.readonly)
child.readonly = true;
if (parent.writeonly)
child.writeonly = true;
#endif
}
bool HasNonLayoutQualifiers(const glslang::TType& type, const glslang::TQualifier& qualifier)
{
// This should list qualifiers that simultaneous satisfy:
// - struct members might inherit from a struct declaration
// (note that non-block structs don't explicitly inherit,
// only implicitly, meaning no decoration involved)
// - affect decorations on the struct members
// (note smooth does not, and expecting something like volatile
// to effect the whole object)
// - are not part of the offset/st430/etc or row/column-major layout
return qualifier.invariant || (qualifier.hasLocation() && type.getBasicType() == glslang::EbtBlock);
}
//
// Implement the TGlslangToSpvTraverser class.
//
TGlslangToSpvTraverser::TGlslangToSpvTraverser(unsigned int spvVersion, const glslang::TIntermediate* glslangIntermediate,
spv::SpvBuildLogger* buildLogger, glslang::SpvOptions& options)
: TIntermTraverser(true, false, true),
options(options),
shaderEntry(nullptr), currentFunction(nullptr),
sequenceDepth(0), logger(buildLogger),
builder(spvVersion, (glslang::GetKhronosToolId() << 16) | glslang::GetSpirvGeneratorVersion(), logger),
inEntryPoint(false), entryPointTerminated(false), linkageOnly(false),
glslangIntermediate(glslangIntermediate),
nanMinMaxClamp(glslangIntermediate->getNanMinMaxClamp())
{
spv::ExecutionModel executionModel = TranslateExecutionModel(glslangIntermediate->getStage());
builder.clearAccessChain();
builder.setSource(TranslateSourceLanguage(glslangIntermediate->getSource(), glslangIntermediate->getProfile()),
glslangIntermediate->getVersion());
if (options.generateDebugInfo) {
builder.setEmitOpLines();
builder.setSourceFile(glslangIntermediate->getSourceFile());
// Set the source shader's text. If for SPV version 1.0, include
// a preamble in comments stating the OpModuleProcessed instructions.
// Otherwise, emit those as actual instructions.
std::string text;
const std::vector<std::string>& processes = glslangIntermediate->getProcesses();
for (int p = 0; p < (int)processes.size(); ++p) {
if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_1) {
text.append("// OpModuleProcessed ");
text.append(processes[p]);
text.append("\n");
} else
builder.addModuleProcessed(processes[p]);
}
if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_1 && (int)processes.size() > 0)
text.append("#line 1\n");
text.append(glslangIntermediate->getSourceText());
builder.setSourceText(text);
// Pass name and text for all included files
const std::map<std::string, std::string>& include_txt = glslangIntermediate->getIncludeText();
for (auto iItr = include_txt.begin(); iItr != include_txt.end(); ++iItr)
builder.addInclude(iItr->first, iItr->second);
}
stdBuiltins = builder.import("GLSL.std.450");
spv::AddressingModel addressingModel = spv::AddressingModelLogical;
spv::MemoryModel memoryModel = spv::MemoryModelGLSL450;
if (glslangIntermediate->usingPhysicalStorageBuffer()) {
addressingModel = spv::AddressingModelPhysicalStorageBuffer64EXT;
builder.addIncorporatedExtension(spv::E_SPV_KHR_physical_storage_buffer, spv::Spv_1_5);
builder.addCapability(spv::CapabilityPhysicalStorageBufferAddressesEXT);
};
if (glslangIntermediate->usingVulkanMemoryModel()) {
memoryModel = spv::MemoryModelVulkanKHR;
builder.addCapability(spv::CapabilityVulkanMemoryModelKHR);
builder.addIncorporatedExtension(spv::E_SPV_KHR_vulkan_memory_model, spv::Spv_1_5);
}
builder.setMemoryModel(addressingModel, memoryModel);
if (glslangIntermediate->usingVariablePointers()) {
builder.addCapability(spv::CapabilityVariablePointers);
}
shaderEntry = builder.makeEntryPoint(glslangIntermediate->getEntryPointName().c_str());
entryPoint = builder.addEntryPoint(executionModel, shaderEntry, glslangIntermediate->getEntryPointName().c_str());
// Add the source extensions
const auto& sourceExtensions = glslangIntermediate->getRequestedExtensions();
for (auto it = sourceExtensions.begin(); it != sourceExtensions.end(); ++it)
builder.addSourceExtension(it->c_str());
// Add the top-level modes for this shader.
if (glslangIntermediate->getXfbMode()) {
builder.addCapability(spv::CapabilityTransformFeedback);
builder.addExecutionMode(shaderEntry, spv::ExecutionModeXfb);
}
unsigned int mode;
switch (glslangIntermediate->getStage()) {
case EShLangVertex:
builder.addCapability(spv::CapabilityShader);
break;
case EShLangFragment:
builder.addCapability(spv::CapabilityShader);
if (glslangIntermediate->getPixelCenterInteger())
builder.addExecutionMode(shaderEntry, spv::ExecutionModePixelCenterInteger);
if (glslangIntermediate->getOriginUpperLeft())
builder.addExecutionMode(shaderEntry, spv::ExecutionModeOriginUpperLeft);
else
builder.addExecutionMode(shaderEntry, spv::ExecutionModeOriginLowerLeft);
if (glslangIntermediate->getEarlyFragmentTests())
builder.addExecutionMode(shaderEntry, spv::ExecutionModeEarlyFragmentTests);
if (glslangIntermediate->getPostDepthCoverage()) {
builder.addCapability(spv::CapabilitySampleMaskPostDepthCoverage);
builder.addExecutionMode(shaderEntry, spv::ExecutionModePostDepthCoverage);
builder.addExtension(spv::E_SPV_KHR_post_depth_coverage);
}
if (glslangIntermediate->getDepth() != glslang::EldUnchanged && glslangIntermediate->isDepthReplacing())
builder.addExecutionMode(shaderEntry, spv::ExecutionModeDepthReplacing);
#ifndef GLSLANG_WEB
switch(glslangIntermediate->getDepth()) {
case glslang::EldGreater: mode = spv::ExecutionModeDepthGreater; break;
case glslang::EldLess: mode = spv::ExecutionModeDepthLess; break;
default: mode = spv::ExecutionModeMax; break;
}
if (mode != spv::ExecutionModeMax)
builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
switch (glslangIntermediate->getInterlockOrdering()) {
case glslang::EioPixelInterlockOrdered: mode = spv::ExecutionModePixelInterlockOrderedEXT; break;
case glslang::EioPixelInterlockUnordered: mode = spv::ExecutionModePixelInterlockUnorderedEXT; break;
case glslang::EioSampleInterlockOrdered: mode = spv::ExecutionModeSampleInterlockOrderedEXT; break;
case glslang::EioSampleInterlockUnordered: mode = spv::ExecutionModeSampleInterlockUnorderedEXT; break;
case glslang::EioShadingRateInterlockOrdered: mode = spv::ExecutionModeShadingRateInterlockOrderedEXT; break;
case glslang::EioShadingRateInterlockUnordered: mode = spv::ExecutionModeShadingRateInterlockUnorderedEXT; break;
default: mode = spv::ExecutionModeMax; break;
}
if (mode != spv::ExecutionModeMax) {
builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
if (mode == spv::ExecutionModeShadingRateInterlockOrderedEXT ||
mode == spv::ExecutionModeShadingRateInterlockUnorderedEXT) {
builder.addCapability(spv::CapabilityFragmentShaderShadingRateInterlockEXT);
} else if (mode == spv::ExecutionModePixelInterlockOrderedEXT ||
mode == spv::ExecutionModePixelInterlockUnorderedEXT) {
builder.addCapability(spv::CapabilityFragmentShaderPixelInterlockEXT);
} else {
builder.addCapability(spv::CapabilityFragmentShaderSampleInterlockEXT);
}
builder.addExtension(spv::E_SPV_EXT_fragment_shader_interlock);
}
#endif
break;
#ifndef GLSLANG_WEB
case EShLangCompute:
builder.addCapability(spv::CapabilityShader);
builder.addExecutionMode(shaderEntry, spv::ExecutionModeLocalSize, glslangIntermediate->getLocalSize(0),
glslangIntermediate->getLocalSize(1),
glslangIntermediate->getLocalSize(2));
if (glslangIntermediate->getLayoutDerivativeModeNone() == glslang::LayoutDerivativeGroupQuads) {
builder.addCapability(spv::CapabilityComputeDerivativeGroupQuadsNV);
builder.addExecutionMode(shaderEntry, spv::ExecutionModeDerivativeGroupQuadsNV);
builder.addExtension(spv::E_SPV_NV_compute_shader_derivatives);
} else if (glslangIntermediate->getLayoutDerivativeModeNone() == glslang::LayoutDerivativeGroupLinear) {
builder.addCapability(spv::CapabilityComputeDerivativeGroupLinearNV);
builder.addExecutionMode(shaderEntry, spv::ExecutionModeDerivativeGroupLinearNV);
builder.addExtension(spv::E_SPV_NV_compute_shader_derivatives);
}
break;
case EShLangTessEvaluation:
case EShLangTessControl:
builder.addCapability(spv::CapabilityTessellation);
glslang::TLayoutGeometry primitive;
if (glslangIntermediate->getStage() == EShLangTessControl) {
builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, glslangIntermediate->getVertices());
primitive = glslangIntermediate->getOutputPrimitive();
} else {
primitive = glslangIntermediate->getInputPrimitive();
}
switch (primitive) {
case glslang::ElgTriangles: mode = spv::ExecutionModeTriangles; break;
case glslang::ElgQuads: mode = spv::ExecutionModeQuads; break;
case glslang::ElgIsolines: mode = spv::ExecutionModeIsolines; break;
default: mode = spv::ExecutionModeMax; break;
}
if (mode != spv::ExecutionModeMax)
builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
switch (glslangIntermediate->getVertexSpacing()) {
case glslang::EvsEqual: mode = spv::ExecutionModeSpacingEqual; break;
case glslang::EvsFractionalEven: mode = spv::ExecutionModeSpacingFractionalEven; break;
case glslang::EvsFractionalOdd: mode = spv::ExecutionModeSpacingFractionalOdd; break;
default: mode = spv::ExecutionModeMax; break;
}
if (mode != spv::ExecutionModeMax)
builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
switch (glslangIntermediate->getVertexOrder()) {
case glslang::EvoCw: mode = spv::ExecutionModeVertexOrderCw; break;
case glslang::EvoCcw: mode = spv::ExecutionModeVertexOrderCcw; break;
default: mode = spv::ExecutionModeMax; break;
}
if (mode != spv::ExecutionModeMax)
builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
if (glslangIntermediate->getPointMode())
builder.addExecutionMode(shaderEntry, spv::ExecutionModePointMode);
break;
case EShLangGeometry:
builder.addCapability(spv::CapabilityGeometry);
switch (glslangIntermediate->getInputPrimitive()) {
case glslang::ElgPoints: mode = spv::ExecutionModeInputPoints; break;
case glslang::ElgLines: mode = spv::ExecutionModeInputLines; break;
case glslang::ElgLinesAdjacency: mode = spv::ExecutionModeInputLinesAdjacency; break;
case glslang::ElgTriangles: mode = spv::ExecutionModeTriangles; break;
case glslang::ElgTrianglesAdjacency: mode = spv::ExecutionModeInputTrianglesAdjacency; break;
default: mode = spv::ExecutionModeMax; break;
}
if (mode != spv::ExecutionModeMax)
builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
builder.addExecutionMode(shaderEntry, spv::ExecutionModeInvocations, glslangIntermediate->getInvocations());
switch (glslangIntermediate->getOutputPrimitive()) {
case glslang::ElgPoints: mode = spv::ExecutionModeOutputPoints; break;
case glslang::ElgLineStrip: mode = spv::ExecutionModeOutputLineStrip; break;
case glslang::ElgTriangleStrip: mode = spv::ExecutionModeOutputTriangleStrip; break;
default: mode = spv::ExecutionModeMax; break;
}
if (mode != spv::ExecutionModeMax)
builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, glslangIntermediate->getVertices());
break;
case EShLangRayGenNV:
case EShLangIntersectNV:
case EShLangAnyHitNV:
case EShLangClosestHitNV:
case EShLangMissNV:
case EShLangCallableNV:
builder.addCapability(spv::CapabilityRayTracingNV);
builder.addExtension("SPV_NV_ray_tracing");
break;
case EShLangTaskNV:
case EShLangMeshNV:
builder.addCapability(spv::CapabilityMeshShadingNV);
builder.addExtension(spv::E_SPV_NV_mesh_shader);
builder.addExecutionMode(shaderEntry, spv::ExecutionModeLocalSize, glslangIntermediate->getLocalSize(0),
glslangIntermediate->getLocalSize(1),
glslangIntermediate->getLocalSize(2));
if (glslangIntermediate->getStage() == EShLangMeshNV) {
builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, glslangIntermediate->getVertices());
builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputPrimitivesNV, glslangIntermediate->getPrimitives());
switch (glslangIntermediate->getOutputPrimitive()) {
case glslang::ElgPoints: mode = spv::ExecutionModeOutputPoints; break;
case glslang::ElgLines: mode = spv::ExecutionModeOutputLinesNV; break;
case glslang::ElgTriangles: mode = spv::ExecutionModeOutputTrianglesNV; break;
default: mode = spv::ExecutionModeMax; break;
}
if (mode != spv::ExecutionModeMax)
builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
}
break;
#endif
default:
break;
}
}
// Finish creating SPV, after the traversal is complete.
void TGlslangToSpvTraverser::finishSpv()
{
// Finish the entry point function
if (! entryPointTerminated) {
builder.setBuildPoint(shaderEntry->getLastBlock());
builder.leaveFunction();
}
// finish off the entry-point SPV instruction by adding the Input/Output <id>
for (auto it = iOSet.cbegin(); it != iOSet.cend(); ++it)
entryPoint->addIdOperand(*it);
#ifndef GLSLANG_WEB
// Add capabilities, extensions, remove unneeded decorations, etc.,
// based on the resulting SPIR-V.
builder.postProcess();
#endif
}
// Write the SPV into 'out'.
void TGlslangToSpvTraverser::dumpSpv(std::vector<unsigned int>& out)
{
builder.dump(out);
}
//
// Implement the traversal functions.
//
// Return true from interior nodes to have the external traversal
// continue on to children. Return false if children were
// already processed.
//
//
// Symbols can turn into
// - uniform/input reads
// - output writes
// - complex lvalue base setups: foo.bar[3].... , where we see foo and start up an access chain
// - something simple that degenerates into the last bullet
//
void TGlslangToSpvTraverser::visitSymbol(glslang::TIntermSymbol* symbol)
{
SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder);
if (symbol->getType().getQualifier().isSpecConstant())
spec_constant_op_mode_setter.turnOnSpecConstantOpMode();
// getSymbolId() will set up all the IO decorations on the first call.
// Formal function parameters were mapped during makeFunctions().
spv::Id id = getSymbolId(symbol);
if (builder.isPointer(id)) {
// Include all "static use" and "linkage only" interface variables on the OpEntryPoint instruction
// Consider adding to the OpEntryPoint interface list.
// Only looking at structures if they have at least one member.
if (!symbol->getType().isStruct() || symbol->getType().getStruct()->size() > 0) {
spv::StorageClass sc = builder.getStorageClass(id);
// Before SPIR-V 1.4, we only want to include Input and Output.
// Starting with SPIR-V 1.4, we want all globals.
if ((glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4 && sc != spv::StorageClassFunction) ||
(sc == spv::StorageClassInput || sc == spv::StorageClassOutput)) {
iOSet.insert(id);
}
}
// If the SPIR-V type is required to be different than the AST type,
// translate now from the SPIR-V type to the AST type, for the consuming
// operation.
// Note this turns it from an l-value to an r-value.
// Currently, all symbols needing this are inputs; avoid the map lookup when non-input.
if (symbol->getType().getQualifier().storage == glslang::EvqVaryingIn)
id = translateForcedType(id);
}
// Only process non-linkage-only nodes for generating actual static uses
if (! linkageOnly || symbol->getQualifier().isSpecConstant()) {
// Prepare to generate code for the access
// L-value chains will be computed left to right. We're on the symbol now,
// which is the left-most part of the access chain, so now is "clear" time,
// followed by setting the base.
builder.clearAccessChain();
// For now, we consider all user variables as being in memory, so they are pointers,
// except for
// A) R-Value arguments to a function, which are an intermediate object.
// See comments in handleUserFunctionCall().
// B) Specialization constants (normal constants don't even come in as a variable),
// These are also pure R-values.
// C) R-Values from type translation, see above call to translateForcedType()
glslang::TQualifier qualifier = symbol->getQualifier();
if (qualifier.isSpecConstant() || rValueParameters.find(symbol->getId()) != rValueParameters.end() ||
!builder.isPointerType(builder.getTypeId(id)))
builder.setAccessChainRValue(id);
else
builder.setAccessChainLValue(id);
}
#ifdef ENABLE_HLSL
// Process linkage-only nodes for any special additional interface work.
if (linkageOnly) {
if (glslangIntermediate->getHlslFunctionality1()) {
// Map implicit counter buffers to their originating buffers, which should have been
// seen by now, given earlier pruning of unused counters, and preservation of order
// of declaration.
if (symbol->getType().getQualifier().isUniformOrBuffer()) {
if (!glslangIntermediate->hasCounterBufferName(symbol->getName())) {
// Save possible originating buffers for counter buffers, keyed by
// making the potential counter-buffer name.
std::string keyName = symbol->getName().c_str();
keyName = glslangIntermediate->addCounterBufferName(keyName);
counterOriginator[keyName] = symbol;
} else {
// Handle a counter buffer, by finding the saved originating buffer.
std::string keyName = symbol->getName().c_str();
auto it = counterOriginator.find(keyName);
if (it != counterOriginator.end()) {
id = getSymbolId(it->second);
if (id != spv::NoResult) {
spv::Id counterId = getSymbolId(symbol);
if (counterId != spv::NoResult) {
builder.addExtension("SPV_GOOGLE_hlsl_functionality1");
builder.addDecorationId(id, spv::DecorationHlslCounterBufferGOOGLE, counterId);
}
}
}
}
}
}
}
#endif
}
bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::TIntermBinary* node)
{
builder.setLine(node->getLoc().line, node->getLoc().getFilename());
SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder);
if (node->getType().getQualifier().isSpecConstant())
spec_constant_op_mode_setter.turnOnSpecConstantOpMode();
// First, handle special cases
switch (node->getOp()) {
case glslang::EOpAssign:
case glslang::EOpAddAssign:
case glslang::EOpSubAssign:
case glslang::EOpMulAssign:
case glslang::EOpVectorTimesMatrixAssign:
case glslang::EOpVectorTimesScalarAssign:
case glslang::EOpMatrixTimesScalarAssign:
case glslang::EOpMatrixTimesMatrixAssign:
case glslang::EOpDivAssign:
case glslang::EOpModAssign:
case glslang::EOpAndAssign:
case glslang::EOpInclusiveOrAssign:
case glslang::EOpExclusiveOrAssign:
case glslang::EOpLeftShiftAssign:
case glslang::EOpRightShiftAssign:
// A bin-op assign "a += b" means the same thing as "a = a + b"
// where a is evaluated before b. For a simple assignment, GLSL
// says to evaluate the left before the right. So, always, left
// node then right node.
{
// get the left l-value, save it away
builder.clearAccessChain();
node->getLeft()->traverse(this);
spv::Builder::AccessChain lValue = builder.getAccessChain();
// evaluate the right
builder.clearAccessChain();
node->getRight()->traverse(this);
spv::Id rValue = accessChainLoad(node->getRight()->getType());
if (node->getOp() != glslang::EOpAssign) {
// the left is also an r-value
builder.setAccessChain(lValue);
spv::Id leftRValue = accessChainLoad(node->getLeft()->getType());
// do the operation
OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()),
TranslateNoContractionDecoration(node->getType().getQualifier()),
TranslateNonUniformDecoration(node->getType().getQualifier()) };
rValue = createBinaryOperation(node->getOp(), decorations,
convertGlslangToSpvType(node->getType()), leftRValue, rValue,
node->getType().getBasicType());
// these all need their counterparts in createBinaryOperation()
assert(rValue != spv::NoResult);
}
// store the result
builder.setAccessChain(lValue);
multiTypeStore(node->getLeft()->getType(), rValue);
// assignments are expressions having an rValue after they are evaluated...
builder.clearAccessChain();
builder.setAccessChainRValue(rValue);
}
return false;
case glslang::EOpIndexDirect:
case glslang::EOpIndexDirectStruct:
{
// Structure, array, matrix, or vector indirection with statically known index.
// Get the left part of the access chain.
node->getLeft()->traverse(this);
// Add the next element in the chain
const int glslangIndex = node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();
if (! node->getLeft()->getType().isArray() &&
node->getLeft()->getType().isVector() &&
node->getOp() == glslang::EOpIndexDirect) {
// This is essentially a hard-coded vector swizzle of size 1,
// so short circuit the access-chain stuff with a swizzle.
std::vector<unsigned> swizzle;
swizzle.push_back(glslangIndex);
int dummySize;
builder.accessChainPushSwizzle(swizzle, convertGlslangToSpvType(node->getLeft()->getType()),
TranslateCoherent(node->getLeft()->getType()),
glslangIntermediate->getBaseAlignmentScalar(node->getLeft()->getType(), dummySize));
} else {
// Load through a block reference is performed with a dot operator that
// is mapped to EOpIndexDirectStruct. When we get to the actual reference,
// do a load and reset the access chain.
if (node->getLeft()->isReference() &&
!node->getLeft()->getType().isArray() &&
node->getOp() == glslang::EOpIndexDirectStruct)
{
spv::Id left = accessChainLoad(node->getLeft()->getType());
builder.clearAccessChain();
builder.setAccessChainLValue(left);
}
int spvIndex = glslangIndex;
if (node->getLeft()->getBasicType() == glslang::EbtBlock &&
node->getOp() == glslang::EOpIndexDirectStruct)
{
// This may be, e.g., an anonymous block-member selection, which generally need
// index remapping due to hidden members in anonymous blocks.
std::vector<int>& remapper = memberRemapper[node->getLeft()->getType().getStruct()];
assert(remapper.size() > 0);
spvIndex = remapper[glslangIndex];
}
// normal case for indexing array or structure or block
builder.accessChainPush(builder.makeIntConstant(spvIndex), TranslateCoherent(node->getLeft()->getType()), node->getLeft()->getType().getBufferReferenceAlignment());
// Add capabilities here for accessing PointSize and clip/cull distance.
// We have deferred generation of associated capabilities until now.
if (node->getLeft()->getType().isStruct() && ! node->getLeft()->getType().isArray())
declareUseOfStructMember(*(node->getLeft()->getType().getStruct()), glslangIndex);
}
}
return false;
case glslang::EOpIndexIndirect:
{
// Array, matrix, or vector indirection with variable index.
// Will use native SPIR-V access-chain for and array indirection;
// matrices are arrays of vectors, so will also work for a matrix.
// Will use the access chain's 'component' for variable index into a vector.
// This adapter is building access chains left to right.
// Set up the access chain to the left.
node->getLeft()->traverse(this);
// save it so that computing the right side doesn't trash it
spv::Builder::AccessChain partial = builder.getAccessChain();
// compute the next index in the chain
builder.clearAccessChain();
node->getRight()->traverse(this);
spv::Id index = accessChainLoad(node->getRight()->getType());
addIndirectionIndexCapabilities(node->getLeft()->getType(), node->getRight()->getType());
// restore the saved access chain
builder.setAccessChain(partial);
if (! node->getLeft()->getType().isArray() && node->getLeft()->getType().isVector()) {
int dummySize;
builder.accessChainPushComponent(index, convertGlslangToSpvType(node->getLeft()->getType()),
TranslateCoherent(node->getLeft()->getType()),
glslangIntermediate->getBaseAlignmentScalar(node->getLeft()->getType(), dummySize));
} else
builder.accessChainPush(index, TranslateCoherent(node->getLeft()->getType()), node->getLeft()->getType().getBufferReferenceAlignment());
}
return false;
case glslang::EOpVectorSwizzle:
{
node->getLeft()->traverse(this);
std::vector<unsigned> swizzle;
convertSwizzle(*node->getRight()->getAsAggregate(), swizzle);
int dummySize;
builder.accessChainPushSwizzle(swizzle, convertGlslangToSpvType(node->getLeft()->getType()),
TranslateCoherent(node->getLeft()->getType()),
glslangIntermediate->getBaseAlignmentScalar(node->getLeft()->getType(), dummySize));
}
return false;
case glslang::EOpMatrixSwizzle:
logger->missingFunctionality("matrix swizzle");
return true;
case glslang::EOpLogicalOr:
case glslang::EOpLogicalAnd:
{
// These may require short circuiting, but can sometimes be done as straight
// binary operations. The right operand must be short circuited if it has
// side effects, and should probably be if it is complex.
if (isTrivial(node->getRight()->getAsTyped()))
break; // handle below as a normal binary operation
// otherwise, we need to do dynamic short circuiting on the right operand
spv::Id result = createShortCircuit(node->getOp(), *node->getLeft()->getAsTyped(), *node->getRight()->getAsTyped());
builder.clearAccessChain();
builder.setAccessChainRValue(result);
}
return false;
default:
break;
}
// Assume generic binary op...
// get right operand
builder.clearAccessChain();
node->getLeft()->traverse(this);
spv::Id left = accessChainLoad(node->getLeft()->getType());
// get left operand
builder.clearAccessChain();
node->getRight()->traverse(this);
spv::Id right = accessChainLoad(node->getRight()->getType());
// get result
OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()),
TranslateNoContractionDecoration(node->getType().getQualifier()),
TranslateNonUniformDecoration(node->getType().getQualifier()) };
spv::Id result = createBinaryOperation(node->getOp(), decorations,
convertGlslangToSpvType(node->getType()), left, right,
node->getLeft()->getType().getBasicType());
builder.clearAccessChain();
if (! result) {
logger->missingFunctionality("unknown glslang binary operation");
return true; // pick up a child as the place-holder result
} else {
builder.setAccessChainRValue(result);
return false;
}
}
// Figure out what, if any, type changes are needed when accessing a specific built-in.
// Returns <the type SPIR-V requires for declarion, the type to translate to on use>.
// Also see comment for 'forceType', regarding tracking SPIR-V-required types.
std::pair<spv::Id, spv::Id> TGlslangToSpvTraverser::getForcedType(spv::BuiltIn builtIn,
const glslang::TType& glslangType)
{
switch(builtIn)
{
case spv::BuiltInSubgroupEqMask:
case spv::BuiltInSubgroupGeMask:
case spv::BuiltInSubgroupGtMask:
case spv::BuiltInSubgroupLeMask:
case spv::BuiltInSubgroupLtMask: {
// these require changing a 64-bit scaler -> a vector of 32-bit components
if (glslangType.isVector())
break;
std::pair<spv::Id, spv::Id> ret(builder.makeVectorType(builder.makeUintType(32), 4),
builder.makeUintType(64));
return ret;
}
default:
break;
}
std::pair<spv::Id, spv::Id> ret(spv::NoType, spv::NoType);
return ret;
}
// For an object previously identified (see getForcedType() and forceType)
// as needing type translations, do the translation needed for a load, turning
// an L-value into in R-value.
spv::Id TGlslangToSpvTraverser::translateForcedType(spv::Id object)
{
const auto forceIt = forceType.find(object);
if (forceIt == forceType.end())
return object;
spv::Id desiredTypeId = forceIt->second;
spv::Id objectTypeId = builder.getTypeId(object);
assert(builder.isPointerType(objectTypeId));
objectTypeId = builder.getContainedTypeId(objectTypeId);
if (builder.isVectorType(objectTypeId) &&
builder.getScalarTypeWidth(builder.getContainedTypeId(objectTypeId)) == 32) {
if (builder.getScalarTypeWidth(desiredTypeId) == 64) {
// handle 32-bit v.xy* -> 64-bit
builder.clearAccessChain();
builder.setAccessChainLValue(object);
object = builder.accessChainLoad(spv::NoPrecision, spv::DecorationMax, objectTypeId);
std::vector<spv::Id> components;
components.push_back(builder.createCompositeExtract(object, builder.getContainedTypeId(objectTypeId), 0));
components.push_back(builder.createCompositeExtract(object, builder.getContainedTypeId(objectTypeId), 1));
spv::Id vecType = builder.makeVectorType(builder.getContainedTypeId(objectTypeId), 2);
return builder.createUnaryOp(spv::OpBitcast, desiredTypeId,
builder.createCompositeConstruct(vecType, components));
} else {
logger->missingFunctionality("forcing 32-bit vector type to non 64-bit scalar");
}
} else {
logger->missingFunctionality("forcing non 32-bit vector type");
}
return object;
}
bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node)
{
builder.setLine(node->getLoc().line, node->getLoc().getFilename());
SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder);
if (node->getType().getQualifier().isSpecConstant())
spec_constant_op_mode_setter.turnOnSpecConstantOpMode();
spv::Id result = spv::NoResult;
// try texturing first
result = createImageTextureFunctionCall(node);
if (result != spv::NoResult) {
builder.clearAccessChain();
builder.setAccessChainRValue(result);
return false; // done with this node
}
// Non-texturing.
if (node->getOp() == glslang::EOpArrayLength) {
// Quite special; won't want to evaluate the operand.
// Currently, the front-end does not allow .length() on an array until it is sized,
// except for the last block membeor of an SSBO.
// TODO: If this changes, link-time sized arrays might show up here, and need their
// size extracted.
// Normal .length() would have been constant folded by the front-end.
// So, this has to be block.lastMember.length().
// SPV wants "block" and member number as the operands, go get them.
spv::Id length;
if (node->getOperand()->getType().isCoopMat()) {
spec_constant_op_mode_setter.turnOnSpecConstantOpMode();
spv::Id typeId = convertGlslangToSpvType(node->getOperand()->getType());
assert(builder.isCooperativeMatrixType(typeId));
length = builder.createCooperativeMatrixLength(typeId);
} else {
glslang::TIntermTyped* block = node->getOperand()->getAsBinaryNode()->getLeft();
block->traverse(this);
unsigned int member = node->getOperand()->getAsBinaryNode()->getRight()->getAsConstantUnion()->getConstArray()[0].getUConst();
length = builder.createArrayLength(builder.accessChainGetLValue(), member);
}
// GLSL semantics say the result of .length() is an int, while SPIR-V says
// signedness must be 0. So, convert from SPIR-V unsigned back to GLSL's
// AST expectation of a signed result.
if (glslangIntermediate->getSource() == glslang::EShSourceGlsl) {
if (builder.isInSpecConstCodeGenMode()) {
length = builder.createBinOp(spv::OpIAdd, builder.makeIntType(32), length, builder.makeIntConstant(0));
} else {
length = builder.createUnaryOp(spv::OpBitcast, builder.makeIntType(32), length);
}
}
builder.clearAccessChain();
builder.setAccessChainRValue(length);
return false;
}
// Start by evaluating the operand
// Does it need a swizzle inversion? If so, evaluation is inverted;
// operate first on the swizzle base, then apply the swizzle.
spv::Id invertedType = spv::NoType;
auto resultType = [&invertedType, &node, this](){ return invertedType != spv::NoType ? invertedType : convertGlslangToSpvType(node->getType()); };
if (node->getOp() == glslang::EOpInterpolateAtCentroid)
invertedType = getInvertedSwizzleType(*node->getOperand());
builder.clearAccessChain();
TIntermNode *operandNode;
if (invertedType != spv::NoType)
operandNode = node->getOperand()->getAsBinaryNode()->getLeft();
else
operandNode = node->getOperand();
operandNode->traverse(this);
spv::Id operand = spv::NoResult;
spv::Builder::AccessChain::CoherentFlags lvalueCoherentFlags;
#ifndef GLSLANG_WEB
if (node->getOp() == glslang::EOpAtomicCounterIncrement ||
node->getOp() == glslang::EOpAtomicCounterDecrement ||
node->getOp() == glslang::EOpAtomicCounter ||
node->getOp() == glslang::EOpInterpolateAtCentroid) {
operand = builder.accessChainGetLValue(); // Special case l-value operands
lvalueCoherentFlags = builder.getAccessChain().coherentFlags;
lvalueCoherentFlags |= TranslateCoherent(operandNode->getAsTyped()->getType());
} else
#endif
{
operand = accessChainLoad(node->getOperand()->getType());
}
OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()),
TranslateNoContractionDecoration(node->getType().getQualifier()),
TranslateNonUniformDecoration(node->getType().getQualifier()) };
// it could be a conversion
if (! result)
result = createConversion(node->getOp(), decorations, resultType(), operand, node->getOperand()->getBasicType());
// if not, then possibly an operation
if (! result)
result = createUnaryOperation(node->getOp(), decorations, resultType(), operand, node->getOperand()->getBasicType(), lvalueCoherentFlags);
if (result) {
if (invertedType) {
result = createInvertedSwizzle(decorations.precision, *node->getOperand(), result);
decorations.addNonUniform(builder, result);
}
builder.clearAccessChain();
builder.setAccessChainRValue(result);
return false; // done with this node
}
// it must be a special case, check...
switch (node->getOp()) {
case glslang::EOpPostIncrement:
case glslang::EOpPostDecrement:
case glslang::EOpPreIncrement:
case glslang::EOpPreDecrement:
{
// we need the integer value "1" or the floating point "1.0" to add/subtract
spv::Id one = 0;
if (node->getBasicType() == glslang::EbtFloat)
one = builder.makeFloatConstant(1.0F);
#ifndef GLSLANG_WEB
else if (node->getBasicType() == glslang::EbtDouble)
one = builder.makeDoubleConstant(1.0);
else if (node->getBasicType() == glslang::EbtFloat16)
one = builder.makeFloat16Constant(1.0F);
else if (node->getBasicType() == glslang::EbtInt8 || node->getBasicType() == glslang::EbtUint8)
one = builder.makeInt8Constant(1);
else if (node->getBasicType() == glslang::EbtInt16 || node->getBasicType() == glslang::EbtUint16)
one = builder.makeInt16Constant(1);
else if (node->getBasicType() == glslang::EbtInt64 || node->getBasicType() == glslang::EbtUint64)
one = builder.makeInt64Constant(1);
#endif
else
one = builder.makeIntConstant(1);
glslang::TOperator op;
if (node->getOp() == glslang::EOpPreIncrement ||
node->getOp() == glslang::EOpPostIncrement)
op = glslang::EOpAdd;
else
op = glslang::EOpSub;
spv::Id result = createBinaryOperation(op, decorations,
convertGlslangToSpvType(node->getType()), operand, one,
node->getType().getBasicType());
assert(result != spv::NoResult);
// The result of operation is always stored, but conditionally the
// consumed result. The consumed result is always an r-value.
builder.accessChainStore(result);
builder.clearAccessChain();
if (node->getOp() == glslang::EOpPreIncrement ||
node->getOp() == glslang::EOpPreDecrement)
builder.setAccessChainRValue(result);
else
builder.setAccessChainRValue(operand);
}
return false;
#ifndef GLSLANG_WEB
case glslang::EOpEmitStreamVertex:
builder.createNoResultOp(spv::OpEmitStreamVertex, operand);
return false;
case glslang::EOpEndStreamPrimitive:
builder.createNoResultOp(spv::OpEndStreamPrimitive, operand);
return false;
#endif
default:
logger->missingFunctionality("unknown glslang unary");
return true; // pick up operand as placeholder result
}
}
// Construct a composite object, recursively copying members if their types don't match
spv::Id TGlslangToSpvTraverser::createCompositeConstruct(spv::Id resultTypeId, std::vector<spv::Id> constituents)
{
for (int c = 0; c < (int)constituents.size(); ++c) {
spv::Id& constituent = constituents[c];
spv::Id lType = builder.getContainedTypeId(resultTypeId, c);
spv::Id rType = builder.getTypeId(constituent);
if (lType != rType) {
if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) {
constituent = builder.createUnaryOp(spv::OpCopyLogical, lType, constituent);
} else if (builder.isStructType(rType)) {
std::vector<spv::Id> rTypeConstituents;
int numrTypeConstituents = builder.getNumTypeConstituents(rType);
for (int i = 0; i < numrTypeConstituents; ++i) {
rTypeConstituents.push_back(builder.createCompositeExtract(constituent, builder.getContainedTypeId(rType, i), i));
}
constituents[c] = createCompositeConstruct(lType, rTypeConstituents);
} else {
assert(builder.isArrayType(rType));
std::vector<spv::Id> rTypeConstituents;
int numrTypeConstituents = builder.getNumTypeConstituents(rType);
spv::Id elementRType = builder.getContainedTypeId(rType);
for (int i = 0; i < numrTypeConstituents; ++i) {
rTypeConstituents.push_back(builder.createCompositeExtract(constituent, elementRType, i));
}
constituents[c] = createCompositeConstruct(lType, rTypeConstituents);
}
}
}
return builder.createCompositeConstruct(resultTypeId, constituents);
}
bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TIntermAggregate* node)
{
SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder);
if (node->getType().getQualifier().isSpecConstant())
spec_constant_op_mode_setter.turnOnSpecConstantOpMode();
spv::Id result = spv::NoResult;
spv::Id invertedType = spv::NoType; // to use to override the natural type of the node
auto resultType = [&invertedType, &node, this](){ return invertedType != spv::NoType ? invertedType : convertGlslangToSpvType(node->getType()); };
// try texturing
result = createImageTextureFunctionCall(node);
if (result != spv::NoResult) {
builder.clearAccessChain();
builder.setAccessChainRValue(result);
return false;
}
#ifndef GLSLANG_WEB
else if (node->getOp() == glslang::EOpImageStore ||
node->getOp() == glslang::EOpImageStoreLod ||
node->getOp() == glslang::EOpImageAtomicStore) {
// "imageStore" is a special case, which has no result
return false;
}
#endif
glslang::TOperator binOp = glslang::EOpNull;
bool reduceComparison = true;
bool isMatrix = false;
bool noReturnValue = false;
bool atomic = false;
spv::Builder::AccessChain::CoherentFlags lvalueCoherentFlags;
assert(node->getOp());
spv::Decoration precision = TranslatePrecisionDecoration(node->getOperationPrecision());
switch (node->getOp()) {
case glslang::EOpSequence:
{
if (preVisit)
++sequenceDepth;
else
--sequenceDepth;
if (sequenceDepth == 1) {
// If this is the parent node of all the functions, we want to see them
// early, so all call points have actual SPIR-V functions to reference.
// In all cases, still let the traverser visit the children for us.
makeFunctions(node->getAsAggregate()->getSequence());
// Also, we want all globals initializers to go into the beginning of the entry point, before
// anything else gets there, so visit out of order, doing them all now.
makeGlobalInitializers(node->getAsAggregate()->getSequence());
// Initializers are done, don't want to visit again, but functions and link objects need to be processed,
// so do them manually.
visitFunctions(node->getAsAggregate()->getSequence());
return false;
}
return true;
}
case glslang::EOpLinkerObjects:
{
if (visit == glslang::EvPreVisit)
linkageOnly = true;
else
linkageOnly = false;
return true;
}
case glslang::EOpComma:
{
// processing from left to right naturally leaves the right-most
// lying around in the access chain
glslang::TIntermSequence& glslangOperands = node->getSequence();
for (int i = 0; i < (int)glslangOperands.size(); ++i)
glslangOperands[i]->traverse(this);
return false;
}
case glslang::EOpFunction:
if (visit == glslang::EvPreVisit) {
if (isShaderEntryPoint(node)) {
inEntryPoint = true;
builder.setBuildPoint(shaderEntry->getLastBlock());
currentFunction = shaderEntry;
} else {
handleFunctionEntry(node);
}
} else {
if (inEntryPoint)
entryPointTerminated = true;
builder.leaveFunction();
inEntryPoint = false;
}
return true;
case glslang::EOpParameters:
// Parameters will have been consumed by EOpFunction processing, but not
// the body, so we still visited the function node's children, making this
// child redundant.
return false;
case glslang::EOpFunctionCall:
{
builder.setLine(node->getLoc().line, node->getLoc().getFilename());
if (node->isUserDefined())
result = handleUserFunctionCall(node);
// assert(result); // this can happen for bad shaders because the call graph completeness checking is not yet done
if (result) {
builder.clearAccessChain();
builder.setAccessChainRValue(result);
} else
logger->missingFunctionality("missing user function; linker needs to catch that");
return false;
}
case glslang::EOpConstructMat2x2:
case glslang::EOpConstructMat2x3:
case glslang::EOpConstructMat2x4:
case glslang::EOpConstructMat3x2:
case glslang::EOpConstructMat3x3:
case glslang::EOpConstructMat3x4:
case glslang::EOpConstructMat4x2:
case glslang::EOpConstructMat4x3:
case glslang::EOpConstructMat4x4:
case glslang::EOpConstructDMat2x2:
case glslang::EOpConstructDMat2x3:
case glslang::EOpConstructDMat2x4:
case glslang::EOpConstructDMat3x2:
case glslang::EOpConstructDMat3x3:
case glslang::EOpConstructDMat3x4:
case glslang::EOpConstructDMat4x2:
case glslang::EOpConstructDMat4x3:
case glslang::EOpConstructDMat4x4:
case glslang::EOpConstructIMat2x2:
case glslang::EOpConstructIMat2x3:
case glslang::EOpConstructIMat2x4:
case glslang::EOpConstructIMat3x2:
case glslang::EOpConstructIMat3x3:
case glslang::EOpConstructIMat3x4:
case glslang::EOpConstructIMat4x2:
case glslang::EOpConstructIMat4x3:
case glslang::EOpConstructIMat4x4:
case glslang::EOpConstructUMat2x2:
case glslang::EOpConstructUMat2x3:
case glslang::EOpConstructUMat2x4:
case glslang::EOpConstructUMat3x2:
case glslang::EOpConstructUMat3x3:
case glslang::EOpConstructUMat3x4:
case glslang::EOpConstructUMat4x2:
case glslang::EOpConstructUMat4x3:
case glslang::EOpConstructUMat4x4:
case glslang::EOpConstructBMat2x2:
case glslang::EOpConstructBMat2x3:
case glslang::EOpConstructBMat2x4:
case glslang::EOpConstructBMat3x2:
case glslang::EOpConstructBMat3x3:
case glslang::EOpConstructBMat3x4:
case glslang::EOpConstructBMat4x2:
case glslang::EOpConstructBMat4x3:
case glslang::EOpConstructBMat4x4:
case glslang::EOpConstructF16Mat2x2:
case glslang::EOpConstructF16Mat2x3:
case glslang::EOpConstructF16Mat2x4:
case glslang::EOpConstructF16Mat3x2:
case glslang::EOpConstructF16Mat3x3:
case glslang::EOpConstructF16Mat3x4:
case glslang::EOpConstructF16Mat4x2:
case glslang::EOpConstructF16Mat4x3:
case glslang::EOpConstructF16Mat4x4:
isMatrix = true;
// fall through
case glslang::EOpConstructFloat:
case glslang::EOpConstructVec2:
case glslang::EOpConstructVec3:
case glslang::EOpConstructVec4:
case glslang::EOpConstructDouble:
case glslang::EOpConstructDVec2:
case glslang::EOpConstructDVec3:
case glslang::EOpConstructDVec4:
case glslang::EOpConstructFloat16:
case glslang::EOpConstructF16Vec2:
case glslang::EOpConstructF16Vec3:
case glslang::EOpConstructF16Vec4:
case glslang::EOpConstructBool:
case glslang::EOpConstructBVec2:
case glslang::EOpConstructBVec3:
case glslang::EOpConstructBVec4:
case glslang::EOpConstructInt8:
case glslang::EOpConstructI8Vec2:
case glslang::EOpConstructI8Vec3:
case glslang::EOpConstructI8Vec4:
case glslang::EOpConstructUint8:
case glslang::EOpConstructU8Vec2:
case glslang::EOpConstructU8Vec3:
case glslang::EOpConstructU8Vec4:
case glslang::EOpConstructInt16:
case glslang::EOpConstructI16Vec2:
case glslang::EOpConstructI16Vec3:
case glslang::EOpConstructI16Vec4:
case glslang::EOpConstructUint16:
case glslang::EOpConstructU16Vec2:
case glslang::EOpConstructU16Vec3:
case glslang::EOpConstructU16Vec4:
case glslang::EOpConstructInt:
case glslang::EOpConstructIVec2:
case glslang::EOpConstructIVec3:
case glslang::EOpConstructIVec4:
case glslang::EOpConstructUint:
case glslang::EOpConstructUVec2:
case glslang::EOpConstructUVec3:
case glslang::EOpConstructUVec4:
case glslang::EOpConstructInt64:
case glslang::EOpConstructI64Vec2:
case glslang::EOpConstructI64Vec3:
case glslang::EOpConstructI64Vec4:
case glslang::EOpConstructUint64:
case glslang::EOpConstructU64Vec2:
case glslang::EOpConstructU64Vec3:
case glslang::EOpConstructU64Vec4:
case glslang::EOpConstructStruct:
case glslang::EOpConstructTextureSampler:
case glslang::EOpConstructReference:
case glslang::EOpConstructCooperativeMatrix:
{
builder.setLine(node->getLoc().line, node->getLoc().getFilename());
std::vector<spv::Id> arguments;
translateArguments(*node, arguments, lvalueCoherentFlags);
spv::Id constructed;
if (node->getOp() == glslang::EOpConstructTextureSampler)
constructed = builder.createOp(spv::OpSampledImage, resultType(), arguments);
else if (node->getOp() == glslang::EOpConstructStruct ||
node->getOp() == glslang::EOpConstructCooperativeMatrix ||
node->getType().isArray()) {
std::vector<spv::Id> constituents;
for (int c = 0; c < (int)arguments.size(); ++c)
constituents.push_back(arguments[c]);
constructed = createCompositeConstruct(resultType(), constituents);
} else if (isMatrix)
constructed = builder.createMatrixConstructor(precision, arguments, resultType());
else
constructed = builder.createConstructor(precision, arguments, resultType());
builder.clearAccessChain();
builder.setAccessChainRValue(constructed);
return false;
}
// These six are component-wise compares with component-wise results.
// Forward on to createBinaryOperation(), requesting a vector result.
case glslang::EOpLessThan:
case glslang::EOpGreaterThan:
case glslang::EOpLessThanEqual:
case glslang::EOpGreaterThanEqual:
case glslang::EOpVectorEqual:
case glslang::EOpVectorNotEqual:
{
// Map the operation to a binary
binOp = node->getOp();
reduceComparison = false;
switch (node->getOp()) {
case glslang::EOpVectorEqual: binOp = glslang::EOpVectorEqual; break;
case glslang::EOpVectorNotEqual: binOp = glslang::EOpVectorNotEqual; break;
default: binOp = node->getOp(); break;
}
break;
}
case glslang::EOpMul:
// component-wise matrix multiply
binOp = glslang::EOpMul;
break;
case glslang::EOpOuterProduct:
// two vectors multiplied to make a matrix
binOp = glslang::EOpOuterProduct;
break;
case glslang::EOpDot:
{
// for scalar dot product, use multiply
glslang::TIntermSequence& glslangOperands = node->getSequence();
if (glslangOperands[0]->getAsTyped()->getVectorSize() == 1)
binOp = glslang::EOpMul;
break;
}
case glslang::EOpMod:
// when an aggregate, this is the floating-point mod built-in function,
// which can be emitted by the one in createBinaryOperation()
binOp = glslang::EOpMod;
break;
#ifndef GLSLANG_WEB
case glslang::EOpEmitVertex:
case glslang::EOpEndPrimitive:
case glslang::EOpBarrier:
case glslang::EOpMemoryBarrier:
case glslang::EOpMemoryBarrierAtomicCounter:
case glslang::EOpMemoryBarrierBuffer:
case glslang::EOpMemoryBarrierImage:
case glslang::EOpMemoryBarrierShared:
case glslang::EOpGroupMemoryBarrier:
case glslang::EOpDeviceMemoryBarrier:
case glslang::EOpAllMemoryBarrierWithGroupSync:
case glslang::EOpDeviceMemoryBarrierWithGroupSync:
case glslang::EOpWorkgroupMemoryBarrier:
case glslang::EOpWorkgroupMemoryBarrierWithGroupSync:
case glslang::EOpSubgroupBarrier:
case glslang::EOpSubgroupMemoryBarrier:
case glslang::EOpSubgroupMemoryBarrierBuffer:
case glslang::EOpSubgroupMemoryBarrierImage:
case glslang::EOpSubgroupMemoryBarrierShared:
noReturnValue = true;
// These all have 0 operands and will naturally finish up in the code below for 0 operands
break;
case glslang::EOpAtomicStore:
noReturnValue = true;
// fallthrough
case glslang::EOpAtomicLoad:
case glslang::EOpAtomicAdd:
case glslang::EOpAtomicMin:
case glslang::EOpAtomicMax:
case glslang::EOpAtomicAnd:
case glslang::EOpAtomicOr:
case glslang::EOpAtomicXor:
case glslang::EOpAtomicExchange:
case glslang::EOpAtomicCompSwap:
atomic = true;
break;
case glslang::EOpAtomicCounterAdd:
case glslang::EOpAtomicCounterSubtract:
case glslang::EOpAtomicCounterMin:
case glslang::EOpAtomicCounterMax:
case glslang::EOpAtomicCounterAnd:
case glslang::EOpAtomicCounterOr:
case glslang::EOpAtomicCounterXor:
case glslang::EOpAtomicCounterExchange:
case glslang::EOpAtomicCounterCompSwap:
builder.addExtension("SPV_KHR_shader_atomic_counter_ops");
builder.addCapability(spv::CapabilityAtomicStorageOps);
atomic = true;
break;
case glslang::EOpIgnoreIntersectionNV:
case glslang::EOpTerminateRayNV:
case glslang::EOpTraceNV:
case glslang::EOpExecuteCallableNV:
case glslang::EOpWritePackedPrimitiveIndices4x8NV:
noReturnValue = true;
break;
case glslang::EOpCooperativeMatrixLoad:
case glslang::EOpCooperativeMatrixStore:
noReturnValue = true;
break;
case glslang::EOpBeginInvocationInterlock:
case glslang::EOpEndInvocationInterlock:
builder.addExtension(spv::E_SPV_EXT_fragment_shader_interlock);
noReturnValue = true;
break;
#endif
default:
break;
}
//
// See if it maps to a regular operation.
//
if (binOp != glslang::EOpNull) {
glslang::TIntermTyped* left = node->getSequence()[0]->getAsTyped();
glslang::TIntermTyped* right = node->getSequence()[1]->getAsTyped();
assert(left && right);
builder.clearAccessChain();
left->traverse(this);
spv::Id leftId = accessChainLoad(left->getType());
builder.clearAccessChain();
right->traverse(this);
spv::Id rightId = accessChainLoad(right->getType());
builder.setLine(node->getLoc().line, node->getLoc().getFilename());
OpDecorations decorations = { precision,
TranslateNoContractionDecoration(node->getType().getQualifier()),
TranslateNonUniformDecoration(node->getType().getQualifier()) };
result = createBinaryOperation(binOp, decorations,
resultType(), leftId, rightId,
left->getType().getBasicType(), reduceComparison);
// code above should only make binOp that exists in createBinaryOperation
assert(result != spv::NoResult);
builder.clearAccessChain();
builder.setAccessChainRValue(result);
return false;
}
//
// Create the list of operands.
//
glslang::TIntermSequence& glslangOperands = node->getSequence();
std::vector<spv::Id> operands;
std::vector<spv::IdImmediate> memoryAccessOperands;
for (int arg = 0; arg < (int)glslangOperands.size(); ++