Path: blob/main_old/src/compiler/translator/BuildSPIRV.cpp
1693 views
//1// Copyright 2021 The ANGLE Project Authors. All rights reserved.2// Use of this source code is governed by a BSD-style license that can be3// found in the LICENSE file.4//5// BuildSPIRV: Helper for OutputSPIRV to build SPIR-V.6//78#include "compiler/translator/BuildSPIRV.h"910#include "common/spirv/spirv_instruction_builder_autogen.h"11#include "compiler/translator/ValidateVaryingLocations.h"12#include "compiler/translator/blocklayout.h"13#include "compiler/translator/util.h"1415namespace sh16{17bool operator==(const SpirvType &a, const SpirvType &b)18{19if (a.block != b.block)20{21return false;22}2324if (a.arraySizes != b.arraySizes)25{26return false;27}2829// If structure or interface block, they should match by pointer (i.e. be the same block). The30// AST transformations are expected to keep the AST consistent by using the same structure and31// interface block pointer between declarations and usages. This is validated by32// ValidateASTOptions::validateVariableReferences.33if (a.block != nullptr)34{35return a.typeSpec.blockStorage == b.typeSpec.blockStorage &&36a.typeSpec.isInvariantBlock == b.typeSpec.isInvariantBlock &&37a.typeSpec.isRowMajorQualifiedBlock == b.typeSpec.isRowMajorQualifiedBlock &&38a.typeSpec.isPatchIOBlock == b.typeSpec.isPatchIOBlock &&39a.typeSpec.isOrHasBoolInInterfaceBlock == b.typeSpec.isOrHasBoolInInterfaceBlock;40}4142// Otherwise, match by the type contents. The AST transformations sometimes recreate types that43// are already defined, so we can't rely on pointers being unique.44return a.type == b.type && a.primarySize == b.primarySize &&45a.secondarySize == b.secondarySize && a.imageInternalFormat == b.imageInternalFormat &&46a.isSamplerBaseImage == b.isSamplerBaseImage &&47a.typeSpec.blockStorage == b.typeSpec.blockStorage &&48a.typeSpec.isRowMajorQualifiedArray == b.typeSpec.isRowMajorQualifiedArray &&49a.typeSpec.isOrHasBoolInInterfaceBlock == b.typeSpec.isOrHasBoolInInterfaceBlock;50}5152namespace53{54bool IsBlockFieldRowMajorQualified(const TType &fieldType, bool isParentBlockRowMajorQualified)55{56// If the field is specifically qualified as row-major, it will be row-major. Otherwise unless57// specifically qualified as column-major, its matrix packing is inherited from the parent58// block.59const TLayoutMatrixPacking fieldMatrixPacking = fieldType.getLayoutQualifier().matrixPacking;60return fieldMatrixPacking == EmpRowMajor ||61(fieldMatrixPacking == EmpUnspecified && isParentBlockRowMajorQualified);62}6364bool IsNonSquareRowMajorArrayInBlock(const TType &type, const SpirvTypeSpec &parentTypeSpec)65{66return parentTypeSpec.blockStorage != EbsUnspecified && type.isArray() && type.isMatrix() &&67type.getCols() != type.getRows() &&68IsBlockFieldRowMajorQualified(type, parentTypeSpec.isRowMajorQualifiedBlock);69}7071bool IsInvariant(const TType &type, TCompiler *compiler)72{73const bool invariantAll = compiler->getPragma().stdgl.invariantAll;7475// The Invariant decoration is applied to output variables if specified or if globally enabled.76return type.isInvariant() || (IsShaderOut(type.getQualifier()) && invariantAll);77}7879TLayoutBlockStorage GetBlockStorage(const TType &type)80{81// For interface blocks, the block storage is specified on the symbol itself.82if (type.getInterfaceBlock() != nullptr)83{84return type.getInterfaceBlock()->blockStorage();85}8687// I/O blocks must have been handled above.88ASSERT(!IsShaderIoBlock(type.getQualifier()));8990// Additionally, interface blocks are already handled, so it's not expected for the type to have91// a block storage specified.92ASSERT(type.getLayoutQualifier().blockStorage == EbsUnspecified);9394// Default to std140 for uniform and std430 for buffer blocks.95return type.getQualifier() == EvqBuffer ? EbsStd430 : EbsStd140;96}9798ShaderVariable ToShaderVariable(const TFieldListCollection *block,99GLenum type,100const TSpan<const unsigned int> arraySizes,101bool isRowMajor)102{103ShaderVariable var;104105var.type = type;106var.arraySizes = {arraySizes.begin(), arraySizes.end()};107var.isRowMajorLayout = isRowMajor;108109if (block != nullptr)110{111for (const TField *field : block->fields())112{113const TType &fieldType = *field->type();114115const TLayoutMatrixPacking fieldMatrixPacking =116fieldType.getLayoutQualifier().matrixPacking;117const bool isFieldRowMajor = fieldMatrixPacking == EmpRowMajor ||118(fieldMatrixPacking == EmpUnspecified && isRowMajor);119const GLenum glType =120fieldType.getStruct() != nullptr ? GL_NONE : GLVariableType(fieldType);121122var.fields.push_back(ToShaderVariable(fieldType.getStruct(), glType,123fieldType.getArraySizes(), isFieldRowMajor));124}125}126127return var;128}129130ShaderVariable SpirvTypeToShaderVariable(const SpirvType &type)131{132const bool isRowMajor =133type.typeSpec.isRowMajorQualifiedBlock || type.typeSpec.isRowMajorQualifiedArray;134const GLenum glType =135type.block != nullptr136? EbtStruct137: GLVariableType(TType(type.type, type.primarySize, type.secondarySize));138139return ToShaderVariable(type.block, glType, type.arraySizes, isRowMajor);140}141142// The following function encodes a variable in a std140 or std430 block. The variable could be:143//144// - An interface block: In this case, |decorationsBlob| is provided and SPIR-V decorations are145// output to this blob.146// - A struct: In this case, the return value is of interest as the size of the struct in the147// encoding.148//149// This function ignores arrayness in calculating the struct size.150//151uint32_t Encode(const ShaderVariable &var,152bool isStd140,153spirv::IdRef blockTypeId,154spirv::Blob *decorationsBlob)155{156Std140BlockEncoder std140;157Std430BlockEncoder std430;158BlockLayoutEncoder *encoder = isStd140 ? &std140 : &std430;159160ASSERT(var.isStruct());161encoder->enterAggregateType(var);162163uint32_t fieldIndex = 0;164165for (const ShaderVariable &fieldVar : var.fields)166{167BlockMemberInfo fieldInfo;168169// Encode the variable.170if (fieldVar.isStruct())171{172// For structs, recursively encode it.173const uint32_t structSize = Encode(fieldVar, isStd140, {}, nullptr);174175encoder->enterAggregateType(fieldVar);176fieldInfo = encoder->encodeArrayOfPreEncodedStructs(structSize, fieldVar.arraySizes);177encoder->exitAggregateType(fieldVar);178}179else180{181fieldInfo =182encoder->encodeType(fieldVar.type, fieldVar.arraySizes, fieldVar.isRowMajorLayout);183}184185if (decorationsBlob)186{187ASSERT(blockTypeId.valid());188189// Write the Offset decoration.190spirv::WriteMemberDecorate(decorationsBlob, blockTypeId,191spirv::LiteralInteger(fieldIndex), spv::DecorationOffset,192{spirv::LiteralInteger(fieldInfo.offset)});193194// For matrix types, write the MatrixStride decoration as well.195if (IsMatrixGLType(fieldVar.type))196{197ASSERT(fieldInfo.matrixStride > 0);198199// MatrixStride200spirv::WriteMemberDecorate(201decorationsBlob, blockTypeId, spirv::LiteralInteger(fieldIndex),202spv::DecorationMatrixStride, {spirv::LiteralInteger(fieldInfo.matrixStride)});203}204}205206++fieldIndex;207}208209encoder->exitAggregateType(var);210return static_cast<uint32_t>(encoder->getCurrentOffset());211}212213uint32_t GetArrayStrideInBlock(const ShaderVariable &var, bool isStd140)214{215Std140BlockEncoder std140;216Std430BlockEncoder std430;217BlockLayoutEncoder *encoder = isStd140 ? &std140 : &std430;218219ASSERT(var.isArray());220221// For structs, encode the struct to get the size, and calculate the stride based on that.222if (var.isStruct())223{224// Remove arrayness.225ShaderVariable element = var;226element.arraySizes.clear();227228const uint32_t structSize = Encode(element, isStd140, {}, nullptr);229230// Stride is struct size by inner array size231return structSize * var.getInnerArraySizeProduct();232}233234// Otherwise encode the basic type.235BlockMemberInfo memberInfo =236encoder->encodeType(var.type, var.arraySizes, var.isRowMajorLayout);237238// The encoder returns the array stride for the base element type (which is not an array!), so239// need to multiply by the inner array sizes to get the outermost array's stride.240return memberInfo.arrayStride * var.getInnerArraySizeProduct();241}242243spv::ExecutionMode GetGeometryInputExecutionMode(TLayoutPrimitiveType primitiveType)244{245// Default input primitive type for geometry shaders is points246if (primitiveType == EptUndefined)247{248primitiveType = EptPoints;249}250251switch (primitiveType)252{253case EptPoints:254return spv::ExecutionModeInputPoints;255case EptLines:256return spv::ExecutionModeInputLines;257case EptLinesAdjacency:258return spv::ExecutionModeInputLinesAdjacency;259case EptTriangles:260return spv::ExecutionModeTriangles;261case EptTrianglesAdjacency:262return spv::ExecutionModeInputTrianglesAdjacency;263case EptLineStrip:264case EptTriangleStrip:265default:266UNREACHABLE();267return {};268}269}270271spv::ExecutionMode GetGeometryOutputExecutionMode(TLayoutPrimitiveType primitiveType)272{273// Default output primitive type for geometry shaders is points274if (primitiveType == EptUndefined)275{276primitiveType = EptPoints;277}278279switch (primitiveType)280{281case EptPoints:282return spv::ExecutionModeOutputPoints;283case EptLineStrip:284return spv::ExecutionModeOutputLineStrip;285case EptTriangleStrip:286return spv::ExecutionModeOutputTriangleStrip;287case EptLines:288case EptLinesAdjacency:289case EptTriangles:290case EptTrianglesAdjacency:291default:292UNREACHABLE();293return {};294}295}296297spv::ExecutionMode GetTessEvalInputExecutionMode(TLayoutTessEvaluationType inputType)298{299// It's invalid for input type to not be specified, but that's a link-time error. Default to300// anything.301if (inputType == EtetUndefined)302{303inputType = EtetTriangles;304}305306switch (inputType)307{308case EtetTriangles:309return spv::ExecutionModeTriangles;310case EtetQuads:311return spv::ExecutionModeQuads;312case EtetIsolines:313return spv::ExecutionModeIsolines;314default:315UNREACHABLE();316return {};317}318}319320spv::ExecutionMode GetTessEvalSpacingExecutionMode(TLayoutTessEvaluationType spacing)321{322switch (spacing)323{324case EtetEqualSpacing:325case EtetUndefined:326return spv::ExecutionModeSpacingEqual;327case EtetFractionalEvenSpacing:328return spv::ExecutionModeSpacingFractionalEven;329case EtetFractionalOddSpacing:330return spv::ExecutionModeSpacingFractionalOdd;331default:332UNREACHABLE();333return {};334}335}336337spv::ExecutionMode GetTessEvalOrderingExecutionMode(TLayoutTessEvaluationType ordering)338{339switch (ordering)340{341case EtetCw:342return spv::ExecutionModeVertexOrderCw;343case EtetCcw:344case EtetUndefined:345return spv::ExecutionModeVertexOrderCcw;346default:347UNREACHABLE();348return {};349}350}351} // anonymous namespace352353void SpirvTypeSpec::inferDefaults(const TType &type, TCompiler *compiler)354{355// Infer some defaults based on type. If necessary, this overrides some fields (if not already356// specified). Otherwise, it leaves the pre-initialized values as-is.357358// Handle interface blocks and fields of nameless interface blocks.359if (type.getInterfaceBlock() != nullptr)360{361// Calculate the block storage from the interface block automatically. The fields inherit362// from this. Only blocks and arrays in blocks produce different SPIR-V types based on363// block storage.364const bool isBlock = type.isInterfaceBlock() || type.getStruct();365if (blockStorage == EbsUnspecified && (isBlock || type.isArray()))366{367blockStorage = GetBlockStorage(type);368}369370// row_major can only be specified on an interface block or one of its fields. The fields371// will inherit this from the interface block itself.372if (!isRowMajorQualifiedBlock && isBlock)373{374isRowMajorQualifiedBlock = type.getLayoutQualifier().matrixPacking == EmpRowMajor;375}376377// Arrays of matrices in a uniform/buffer block may generate a different stride based on378// whether they are row- or column-major. Square matrices are trivially known not to379// generate a different type.380if (!isRowMajorQualifiedArray)381{382isRowMajorQualifiedArray = IsNonSquareRowMajorArrayInBlock(type, *this);383}384385// Structs with bools, bool arrays, bool vectors and bools themselves are replaced with uint386// when used in an interface block.387if (!isOrHasBoolInInterfaceBlock)388{389isOrHasBoolInInterfaceBlock = type.isInterfaceBlockContainingType(EbtBool) ||390type.isStructureContainingType(EbtBool) ||391type.getBasicType() == EbtBool;392}393394if (!isPatchIOBlock && type.isInterfaceBlock())395{396isPatchIOBlock =397type.getQualifier() == EvqPatchIn || type.getQualifier() == EvqPatchOut;398}399}400401// |invariant| is significant for structs as the fields of the type are decorated with Invariant402// in SPIR-V. This is possible for outputs of struct type, or struct-typed fields of an403// interface block.404if (type.getStruct() != nullptr)405{406isInvariantBlock = isInvariantBlock || IsInvariant(type, compiler);407}408}409410void SpirvTypeSpec::onArrayElementSelection(bool isElementTypeBlock, bool isElementTypeArray)411{412// No difference in type for non-block non-array types in std140 and std430 block storage.413if (!isElementTypeBlock && !isElementTypeArray)414{415blockStorage = EbsUnspecified;416}417418// No difference in type for non-array types in std140 and std430 block storage.419if (!isElementTypeArray)420{421isRowMajorQualifiedArray = false;422}423}424425void SpirvTypeSpec::onBlockFieldSelection(const TType &fieldType)426{427// Patch is never recursively applied.428isPatchIOBlock = false;429430if (fieldType.getStruct() == nullptr)431{432// If the field is not a block, no difference if the parent block was invariant or433// row-major.434isRowMajorQualifiedArray = IsNonSquareRowMajorArrayInBlock(fieldType, *this);435isInvariantBlock = false;436isRowMajorQualifiedBlock = false;437438// If the field is not an array, no difference in storage block.439if (!fieldType.isArray())440{441blockStorage = EbsUnspecified;442}443444if (fieldType.getBasicType() != EbtBool)445{446isOrHasBoolInInterfaceBlock = false;447}448}449else450{451// Apply row-major only to structs that contain matrices.452isRowMajorQualifiedBlock =453IsBlockFieldRowMajorQualified(fieldType, isRowMajorQualifiedBlock) &&454fieldType.isStructureContainingMatrices();455456// Structs without bools aren't affected by |isOrHasBoolInInterfaceBlock|.457if (isOrHasBoolInInterfaceBlock)458{459isOrHasBoolInInterfaceBlock = fieldType.isStructureContainingType(EbtBool);460}461}462}463464void SpirvTypeSpec::onMatrixColumnSelection()465{466// The matrix types are never differentiated, so neither would be their columns.467ASSERT(!isInvariantBlock && !isRowMajorQualifiedBlock && !isRowMajorQualifiedArray &&468!isOrHasBoolInInterfaceBlock && blockStorage == EbsUnspecified);469}470471void SpirvTypeSpec::onVectorComponentSelection()472{473// The vector types are never differentiated, so neither would be their components. The only474// exception is bools in interface blocks, in which case the component and the vector are475// similarly differentiated.476ASSERT(!isInvariantBlock && !isRowMajorQualifiedBlock && !isRowMajorQualifiedArray &&477blockStorage == EbsUnspecified);478}479480SPIRVBuilder::SPIRVBuilder(TCompiler *compiler,481ShCompileOptions compileOptions,482ShHashFunction64 hashFunction,483NameMap &nameMap)484: mCompiler(compiler),485mCompileOptions(compileOptions),486mShaderType(gl::FromGLenum<gl::ShaderType>(compiler->getShaderType())),487mNextAvailableId(1),488mHashFunction(hashFunction),489mNameMap(nameMap),490mNextUnusedBinding(0),491mNextUnusedInputLocation(0),492mNextUnusedOutputLocation(0)493{494// The Shader capability is always defined.495addCapability(spv::CapabilityShader);496497// Add Geometry or Tessellation capabilities based on shader type.498if (mCompiler->getShaderType() == GL_GEOMETRY_SHADER)499{500addCapability(spv::CapabilityGeometry);501}502else if (mCompiler->getShaderType() == GL_TESS_CONTROL_SHADER_EXT ||503mCompiler->getShaderType() == GL_TESS_EVALUATION_SHADER_EXT)504{505addCapability(spv::CapabilityTessellation);506}507}508509spirv::IdRef SPIRVBuilder::getNewId(const SpirvDecorations &decorations)510{511spirv::IdRef newId = mNextAvailableId;512mNextAvailableId = spirv::IdRef(mNextAvailableId + 1);513514for (const spv::Decoration decoration : decorations)515{516spirv::WriteDecorate(&mSpirvDecorations, newId, decoration, {});517}518519return newId;520}521522SpirvType SPIRVBuilder::getSpirvType(const TType &type, const SpirvTypeSpec &typeSpec) const523{524SpirvType spirvType;525spirvType.type = type.getBasicType();526spirvType.primarySize = static_cast<uint8_t>(type.getNominalSize());527spirvType.secondarySize = static_cast<uint8_t>(type.getSecondarySize());528spirvType.arraySizes = type.getArraySizes();529spirvType.imageInternalFormat = type.getLayoutQualifier().imageInternalFormat;530531switch (spirvType.type)532{533// External textures are treated as 2D textures in the vulkan back-end.534case EbtSamplerExternalOES:535case EbtSamplerExternal2DY2YEXT:536// WEBGL video textures too.537case EbtSamplerVideoWEBGL:538spirvType.type = EbtSampler2D;539break;540default:541break;542}543544if (type.getStruct() != nullptr)545{546spirvType.block = type.getStruct();547}548else if (type.isInterfaceBlock())549{550spirvType.block = type.getInterfaceBlock();551}552553// Automatically inherit or infer the type-specializing properties.554spirvType.typeSpec = typeSpec;555spirvType.typeSpec.inferDefaults(type, mCompiler);556557return spirvType;558}559560const SpirvTypeData &SPIRVBuilder::getTypeData(const TType &type, const SpirvTypeSpec &typeSpec)561{562SpirvType spirvType = getSpirvType(type, typeSpec);563564const TSymbol *block = nullptr;565if (type.getStruct() != nullptr)566{567block = type.getStruct();568}569else if (type.isInterfaceBlock())570{571block = type.getInterfaceBlock();572}573574return getSpirvTypeData(spirvType, block);575}576577const SpirvTypeData &SPIRVBuilder::getTypeDataOverrideTypeSpec(const TType &type,578const SpirvTypeSpec &typeSpec)579{580// This is a variant of getTypeData() where type spec is not automatically derived. It's useful581// in cast operations that specifically need to override the spec.582SpirvType spirvType = getSpirvType(type, typeSpec);583spirvType.typeSpec = typeSpec;584585return getSpirvTypeData(spirvType, nullptr);586}587588const SpirvTypeData &SPIRVBuilder::getSpirvTypeData(const SpirvType &type, const TSymbol *block)589{590// Structs with bools generate a different type when used in an interface block (where the bool591// is replaced with a uint). The bool, bool vector and bool arrays too generate a different592// type when nested in an interface block, but that type is the same as the equivalent uint593// type. To avoid defining duplicate uint types, we switch the basic type here to uint. From594// the outside, therefore bool in an interface block and uint look like different types, but595// under the hood will be the same uint.596if (type.block == nullptr && type.typeSpec.isOrHasBoolInInterfaceBlock)597{598ASSERT(type.type == EbtBool);599600SpirvType uintType = type;601uintType.type = EbtUInt;602uintType.typeSpec.isOrHasBoolInInterfaceBlock = false;603return getSpirvTypeData(uintType, block);604}605606auto iter = mTypeMap.find(type);607if (iter == mTypeMap.end())608{609SpirvTypeData newTypeData = declareType(type, block);610611iter = mTypeMap.insert({type, newTypeData}).first;612}613614return iter->second;615}616617spirv::IdRef SPIRVBuilder::getBasicTypeId(TBasicType basicType, size_t size)618{619SpirvType type;620type.type = basicType;621type.primarySize = static_cast<uint8_t>(size);622return getSpirvTypeData(type, nullptr).id;623}624625spirv::IdRef SPIRVBuilder::getTypePointerId(spirv::IdRef typeId, spv::StorageClass storageClass)626{627SpirvIdAndStorageClass key{typeId, storageClass};628629auto iter = mTypePointerIdMap.find(key);630if (iter == mTypePointerIdMap.end())631{632const spirv::IdRef typePointerId = getNewId({});633634spirv::WriteTypePointer(&mSpirvTypePointerDecls, typePointerId, storageClass, typeId);635636iter = mTypePointerIdMap.insert({key, typePointerId}).first;637}638639return iter->second;640}641642spirv::IdRef SPIRVBuilder::getFunctionTypeId(spirv::IdRef returnTypeId,643const spirv::IdRefList ¶mTypeIds)644{645SpirvIdAndIdList key{returnTypeId, paramTypeIds};646647auto iter = mFunctionTypeIdMap.find(key);648if (iter == mFunctionTypeIdMap.end())649{650const spirv::IdRef functionTypeId = getNewId({});651652spirv::WriteTypeFunction(&mSpirvFunctionTypeDecls, functionTypeId, returnTypeId,653paramTypeIds);654655iter = mFunctionTypeIdMap.insert({key, functionTypeId}).first;656}657658return iter->second;659}660661SpirvDecorations SPIRVBuilder::getDecorations(const TType &type)662{663const bool enablePrecision = (mCompileOptions & SH_IGNORE_PRECISION_QUALIFIERS) == 0;664const TPrecision precision = type.getPrecision();665666SpirvDecorations decorations;667668// Handle precision.669if (enablePrecision && (precision == EbpMedium || precision == EbpLow))670{671decorations.push_back(spv::DecorationRelaxedPrecision);672}673674return decorations;675}676677SpirvDecorations SPIRVBuilder::getArithmeticDecorations(const TType &type, bool isPrecise)678{679SpirvDecorations decorations = getDecorations(type);680681// Handle |precise|.682if (isPrecise)683{684decorations.push_back(spv::DecorationNoContraction);685}686687return decorations;688}689690spirv::IdRef SPIRVBuilder::getExtInstImportIdStd()691{692if (!mExtInstImportIdStd.valid())693{694mExtInstImportIdStd = getNewId({});695}696return mExtInstImportIdStd;697}698699SpirvTypeData SPIRVBuilder::declareType(const SpirvType &type, const TSymbol *block)700{701// Recursively declare the type. Type id is allocated afterwards purely for better id order in702// output.703spirv::IdRef typeId;704705if (!type.arraySizes.empty())706{707// Declaring an array. First, declare the type without the outermost array size, then708// declare a new array type based on that.709710SpirvType subType = type;711subType.arraySizes = type.arraySizes.first(type.arraySizes.size() - 1);712subType.typeSpec.onArrayElementSelection(subType.block != nullptr,713!subType.arraySizes.empty());714715const spirv::IdRef subTypeId = getSpirvTypeData(subType, block).id;716717const unsigned int length = type.arraySizes.back();718719if (length == 0)720{721// Storage buffers may include a dynamically-sized array, which is identified by it722// having a length of 0.723typeId = getNewId({});724spirv::WriteTypeRuntimeArray(&mSpirvTypeAndConstantDecls, typeId, subTypeId);725}726else727{728const spirv::IdRef lengthId = getUintConstant(length);729typeId = getNewId({});730spirv::WriteTypeArray(&mSpirvTypeAndConstantDecls, typeId, subTypeId, lengthId);731}732}733else if (type.block != nullptr)734{735// Declaring a block. First, declare all the fields, then declare a struct based on the736// list of field types.737738spirv::IdRefList fieldTypeIds;739for (const TField *field : type.block->fields())740{741const TType &fieldType = *field->type();742743SpirvTypeSpec fieldTypeSpec = type.typeSpec;744fieldTypeSpec.onBlockFieldSelection(fieldType);745746const SpirvType fieldSpirvType = getSpirvType(fieldType, fieldTypeSpec);747const spirv::IdRef fieldTypeId =748getSpirvTypeData(fieldSpirvType, fieldType.getStruct()).id;749fieldTypeIds.push_back(fieldTypeId);750}751752typeId = getNewId({});753spirv::WriteTypeStruct(&mSpirvTypeAndConstantDecls, typeId, fieldTypeIds);754}755else if (IsSampler(type.type) && !type.isSamplerBaseImage)756{757// Declaring a sampler. First, declare the non-sampled image and then a combined758// image-sampler.759760SpirvType imageType = type;761imageType.isSamplerBaseImage = true;762763const spirv::IdRef nonSampledId = getSpirvTypeData(imageType, nullptr).id;764765typeId = getNewId({});766spirv::WriteTypeSampledImage(&mSpirvTypeAndConstantDecls, typeId, nonSampledId);767}768else if (IsImage(type.type) || IsSubpassInputType(type.type) || type.isSamplerBaseImage)769{770// Declaring an image.771772spirv::IdRef sampledType;773spv::Dim dim;774spirv::LiteralInteger depth;775spirv::LiteralInteger arrayed;776spirv::LiteralInteger multisampled;777spirv::LiteralInteger sampled;778779getImageTypeParameters(type.type, &sampledType, &dim, &depth, &arrayed, &multisampled,780&sampled);781const spv::ImageFormat imageFormat = getImageFormat(type.imageInternalFormat);782783typeId = getNewId({});784spirv::WriteTypeImage(&mSpirvTypeAndConstantDecls, typeId, sampledType, dim, depth, arrayed,785multisampled, sampled, imageFormat, nullptr);786}787else if (type.secondarySize > 1)788{789// Declaring a matrix. Declare the column type first, then create a matrix out of it.790791SpirvType columnType = type;792columnType.primarySize = columnType.secondarySize;793columnType.secondarySize = 1;794columnType.typeSpec.onMatrixColumnSelection();795796const spirv::IdRef columnTypeId = getSpirvTypeData(columnType, nullptr).id;797798typeId = getNewId({});799spirv::WriteTypeMatrix(&mSpirvTypeAndConstantDecls, typeId, columnTypeId,800spirv::LiteralInteger(type.primarySize));801}802else if (type.primarySize > 1)803{804// Declaring a vector. Declare the component type first, then create a vector out of it.805806SpirvType componentType = type;807componentType.primarySize = 1;808componentType.typeSpec.onVectorComponentSelection();809810const spirv::IdRef componentTypeId = getSpirvTypeData(componentType, nullptr).id;811812typeId = getNewId({});813spirv::WriteTypeVector(&mSpirvTypeAndConstantDecls, typeId, componentTypeId,814spirv::LiteralInteger(type.primarySize));815}816else817{818typeId = getNewId({});819820// Declaring a basic type. There's a different instruction for each.821switch (type.type)822{823case EbtVoid:824spirv::WriteTypeVoid(&mSpirvTypeAndConstantDecls, typeId);825break;826case EbtFloat:827spirv::WriteTypeFloat(&mSpirvTypeAndConstantDecls, typeId,828spirv::LiteralInteger(32));829break;830case EbtDouble:831// TODO: support desktop GLSL. http://anglebug.com/6197832UNIMPLEMENTED();833break;834case EbtInt:835spirv::WriteTypeInt(&mSpirvTypeAndConstantDecls, typeId, spirv::LiteralInteger(32),836spirv::LiteralInteger(1));837break;838case EbtUInt:839spirv::WriteTypeInt(&mSpirvTypeAndConstantDecls, typeId, spirv::LiteralInteger(32),840spirv::LiteralInteger(0));841break;842case EbtBool:843spirv::WriteTypeBool(&mSpirvTypeAndConstantDecls, typeId);844break;845default:846UNREACHABLE();847}848}849850// If this was a block declaration, add debug information for its type and field names.851//852// TODO: make this conditional to a compiler flag. Instead of outputting the debug info853// unconditionally and having the SPIR-V transformer remove them, it's better to avoid854// generating them in the first place. This both simplifies the transformer and reduces SPIR-V855// binary size that gets written to disk cache. http://anglebug.com/4889856if (block != nullptr && type.arraySizes.empty())857{858spirv::WriteName(&mSpirvDebug, typeId, hashName(block).data());859860uint32_t fieldIndex = 0;861for (const TField *field : type.block->fields())862{863spirv::WriteMemberName(&mSpirvDebug, typeId, spirv::LiteralInteger(fieldIndex++),864hashFieldName(field).data());865}866}867868// Write decorations for interface block fields.869if (type.typeSpec.blockStorage != EbsUnspecified)870{871// Cannot have opaque uniforms inside interface blocks.872ASSERT(!IsOpaqueType(type.type));873874const bool isInterfaceBlock = block != nullptr && block->isInterfaceBlock();875const bool isStd140 = type.typeSpec.blockStorage != EbsStd430;876877if (!type.arraySizes.empty() && !isInterfaceBlock)878{879// Write the ArrayStride decoration for arrays inside interface blocks. An array of880// interface blocks doesn't need a stride.881const ShaderVariable var = SpirvTypeToShaderVariable(type);882const uint32_t stride = GetArrayStrideInBlock(var, isStd140);883884spirv::WriteDecorate(&mSpirvDecorations, typeId, spv::DecorationArrayStride,885{spirv::LiteralInteger(stride)});886}887else if (type.arraySizes.empty() && type.block != nullptr)888{889// Write the Offset decoration for interface blocks and structs in them.890const ShaderVariable var = SpirvTypeToShaderVariable(type);891Encode(var, isStd140, typeId, &mSpirvDecorations);892}893}894895// Write other member decorations.896if (block != nullptr && type.arraySizes.empty())897{898writeMemberDecorations(type, typeId);899}900901return {typeId};902}903904void SPIRVBuilder::getImageTypeParameters(TBasicType type,905spirv::IdRef *sampledTypeOut,906spv::Dim *dimOut,907spirv::LiteralInteger *depthOut,908spirv::LiteralInteger *arrayedOut,909spirv::LiteralInteger *multisampledOut,910spirv::LiteralInteger *sampledOut)911{912TBasicType sampledType = EbtFloat;913*dimOut = IsSubpassInputType(type) ? spv::DimSubpassData : spv::Dim2D;914bool isDepth = false;915bool isArrayed = false;916bool isMultisampled = false;917918// Decompose the basic type into image properties919switch (type)920{921// Float 2D Images922case EbtSampler2D:923case EbtImage2D:924case EbtSubpassInput:925break;926case EbtSamplerExternalOES:927case EbtSamplerExternal2DY2YEXT:928case EbtSamplerVideoWEBGL:929// These must have already been converted to EbtSampler2D.930UNREACHABLE();931break;932case EbtSampler2DArray:933case EbtImage2DArray:934isArrayed = true;935break;936case EbtSampler2DMS:937case EbtImage2DMS:938case EbtSubpassInputMS:939isMultisampled = true;940break;941case EbtSampler2DMSArray:942case EbtImage2DMSArray:943isArrayed = true;944isMultisampled = true;945break;946case EbtSampler2DShadow:947isDepth = true;948break;949case EbtSampler2DArrayShadow:950isDepth = true;951isArrayed = true;952break;953954// Integer 2D images955case EbtISampler2D:956case EbtIImage2D:957case EbtISubpassInput:958sampledType = EbtInt;959break;960case EbtISampler2DArray:961case EbtIImage2DArray:962sampledType = EbtInt;963isArrayed = true;964break;965case EbtISampler2DMS:966case EbtIImage2DMS:967case EbtISubpassInputMS:968sampledType = EbtInt;969isMultisampled = true;970break;971case EbtISampler2DMSArray:972case EbtIImage2DMSArray:973sampledType = EbtInt;974isArrayed = true;975isMultisampled = true;976break;977978// Unsinged integer 2D images979case EbtUSampler2D:980case EbtUImage2D:981case EbtUSubpassInput:982sampledType = EbtUInt;983break;984case EbtUSampler2DArray:985case EbtUImage2DArray:986sampledType = EbtUInt;987isArrayed = true;988break;989case EbtUSampler2DMS:990case EbtUImage2DMS:991case EbtUSubpassInputMS:992sampledType = EbtUInt;993isMultisampled = true;994break;995case EbtUSampler2DMSArray:996case EbtUImage2DMSArray:997sampledType = EbtUInt;998isArrayed = true;999isMultisampled = true;1000break;10011002// 3D images1003case EbtSampler3D:1004case EbtImage3D:1005*dimOut = spv::Dim3D;1006break;1007case EbtISampler3D:1008case EbtIImage3D:1009sampledType = EbtInt;1010*dimOut = spv::Dim3D;1011break;1012case EbtUSampler3D:1013case EbtUImage3D:1014sampledType = EbtUInt;1015*dimOut = spv::Dim3D;1016break;10171018// Float cube images1019case EbtSamplerCube:1020case EbtImageCube:1021*dimOut = spv::DimCube;1022break;1023case EbtSamplerCubeArray:1024case EbtImageCubeArray:1025*dimOut = spv::DimCube;1026isArrayed = true;1027break;1028case EbtSamplerCubeArrayShadow:1029*dimOut = spv::DimCube;1030isDepth = true;1031isArrayed = true;1032break;1033case EbtSamplerCubeShadow:1034*dimOut = spv::DimCube;1035isDepth = true;1036break;10371038// Integer cube images1039case EbtISamplerCube:1040case EbtIImageCube:1041sampledType = EbtInt;1042*dimOut = spv::DimCube;1043break;1044case EbtISamplerCubeArray:1045case EbtIImageCubeArray:1046sampledType = EbtInt;1047*dimOut = spv::DimCube;1048isArrayed = true;1049break;10501051// Unsigned integer cube images1052case EbtUSamplerCube:1053case EbtUImageCube:1054sampledType = EbtUInt;1055*dimOut = spv::DimCube;1056break;1057case EbtUSamplerCubeArray:1058case EbtUImageCubeArray:1059sampledType = EbtUInt;1060*dimOut = spv::DimCube;1061isArrayed = true;1062break;10631064// Float 1D images1065case EbtSampler1D:1066case EbtImage1D:1067*dimOut = spv::Dim1D;1068break;1069case EbtSampler1DArray:1070case EbtImage1DArray:1071*dimOut = spv::Dim1D;1072isArrayed = true;1073break;1074case EbtSampler1DShadow:1075*dimOut = spv::Dim1D;1076isDepth = true;1077break;1078case EbtSampler1DArrayShadow:1079*dimOut = spv::Dim1D;1080isDepth = true;1081isArrayed = true;1082break;10831084// Integer 1D images1085case EbtISampler1D:1086case EbtIImage1D:1087sampledType = EbtInt;1088*dimOut = spv::Dim1D;1089break;1090case EbtISampler1DArray:1091case EbtIImage1DArray:1092sampledType = EbtInt;1093*dimOut = spv::Dim1D;1094isArrayed = true;1095break;10961097// Unsigned integer 1D images1098case EbtUSampler1D:1099case EbtUImage1D:1100sampledType = EbtUInt;1101*dimOut = spv::Dim1D;1102break;1103case EbtUSampler1DArray:1104case EbtUImage1DArray:1105sampledType = EbtUInt;1106*dimOut = spv::Dim1D;1107isArrayed = true;1108break;11091110// Rect images1111case EbtSampler2DRect:1112case EbtImageRect:1113*dimOut = spv::DimRect;1114break;1115case EbtSampler2DRectShadow:1116*dimOut = spv::DimRect;1117isDepth = true;1118break;1119case EbtISampler2DRect:1120case EbtIImageRect:1121sampledType = EbtInt;1122*dimOut = spv::DimRect;1123break;1124case EbtUSampler2DRect:1125case EbtUImageRect:1126sampledType = EbtUInt;1127*dimOut = spv::DimRect;1128break;11291130// Image buffers1131case EbtSamplerBuffer:1132case EbtImageBuffer:1133*dimOut = spv::DimBuffer;1134break;1135case EbtISamplerBuffer:1136case EbtIImageBuffer:1137sampledType = EbtInt;1138*dimOut = spv::DimBuffer;1139break;1140case EbtUSamplerBuffer:1141case EbtUImageBuffer:1142sampledType = EbtUInt;1143*dimOut = spv::DimBuffer;1144break;1145default:1146UNREACHABLE();1147}11481149// Get id of the component type of the image1150SpirvType sampledSpirvType;1151sampledSpirvType.type = sampledType;11521153*sampledTypeOut = getSpirvTypeData(sampledSpirvType, nullptr).id;11541155const bool isSampledImage = IsSampler(type);11561157// Set flags based on SPIR-V required values. See OpTypeImage:1158//1159// - For depth: 0 = non-depth, 1 = depth1160// - For arrayed: 0 = non-arrayed, 1 = arrayed1161// - For multisampled: 0 = single-sampled, 1 = multisampled1162// - For sampled: 1 = sampled, 2 = storage1163//1164*depthOut = spirv::LiteralInteger(isDepth ? 1 : 0);1165*arrayedOut = spirv::LiteralInteger(isArrayed ? 1 : 0);1166*multisampledOut = spirv::LiteralInteger(isMultisampled ? 1 : 0);1167*sampledOut = spirv::LiteralInteger(isSampledImage ? 1 : 2);11681169// Add the necessary capability based on parameters. The SPIR-V spec section 3.8 Dim specfies1170// the required capabilities:1171//1172// Dim Sampled Storage Storage Array1173// --------------------------------------------------------------1174// 1D Sampled1D Image1D1175// 2D Shader ImageMSArray1176// 3D1177// Cube Shader ImageCubeArray1178// Rect SampledRect ImageRect1179// Buffer SampledBuffer ImageBuffer1180//1181// Additionally, the SubpassData Dim requires the InputAttachment capability.1182//1183// Note that the Shader capability is always unconditionally added.1184//1185switch (*dimOut)1186{1187case spv::Dim1D:1188addCapability(isSampledImage ? spv::CapabilitySampled1D : spv::CapabilityImage1D);1189break;1190case spv::Dim2D:1191if (!isSampledImage && isArrayed && isMultisampled)1192{1193addCapability(spv::CapabilityImageMSArray);1194}1195break;1196case spv::Dim3D:1197break;1198case spv::DimCube:1199if (!isSampledImage && isArrayed)1200{1201addCapability(spv::CapabilityImageCubeArray);1202}1203break;1204case spv::DimRect:1205addCapability(isSampledImage ? spv::CapabilitySampledRect : spv::CapabilityImageRect);1206break;1207case spv::DimBuffer:1208addCapability(isSampledImage ? spv::CapabilitySampledBuffer1209: spv::CapabilityImageBuffer);1210break;1211case spv::DimSubpassData:1212addCapability(spv::CapabilityInputAttachment);1213break;1214default:1215UNREACHABLE();1216}1217}12181219spv::ImageFormat SPIRVBuilder::getImageFormat(TLayoutImageInternalFormat imageInternalFormat)1220{1221switch (imageInternalFormat)1222{1223case EiifUnspecified:1224return spv::ImageFormatUnknown;1225case EiifRGBA32F:1226return spv::ImageFormatRgba32f;1227case EiifRGBA16F:1228return spv::ImageFormatRgba16f;1229case EiifR32F:1230return spv::ImageFormatR32f;1231case EiifRGBA32UI:1232return spv::ImageFormatRgba32ui;1233case EiifRGBA16UI:1234return spv::ImageFormatRgba16ui;1235case EiifRGBA8UI:1236return spv::ImageFormatRgba8ui;1237case EiifR32UI:1238return spv::ImageFormatR32ui;1239case EiifRGBA32I:1240return spv::ImageFormatRgba32i;1241case EiifRGBA16I:1242return spv::ImageFormatRgba16i;1243case EiifRGBA8I:1244return spv::ImageFormatRgba8i;1245case EiifR32I:1246return spv::ImageFormatR32i;1247case EiifRGBA8:1248return spv::ImageFormatRgba8;1249case EiifRGBA8_SNORM:1250return spv::ImageFormatRgba8Snorm;1251default:1252UNREACHABLE();1253return spv::ImageFormatUnknown;1254}1255}12561257spirv::IdRef SPIRVBuilder::getBoolConstant(bool value)1258{1259uint32_t asInt = static_cast<uint32_t>(value);12601261spirv::IdRef constantId = mBoolConstants[asInt];12621263if (!constantId.valid())1264{1265SpirvType boolType;1266boolType.type = EbtBool;12671268const spirv::IdRef boolTypeId = getSpirvTypeData(boolType, nullptr).id;12691270mBoolConstants[asInt] = constantId = getNewId({});1271if (value)1272{1273spirv::WriteConstantTrue(&mSpirvTypeAndConstantDecls, boolTypeId, constantId);1274}1275else1276{1277spirv::WriteConstantFalse(&mSpirvTypeAndConstantDecls, boolTypeId, constantId);1278}1279}12801281return constantId;1282}12831284spirv::IdRef SPIRVBuilder::getBasicConstantHelper(uint32_t value,1285TBasicType type,1286angle::HashMap<uint32_t, spirv::IdRef> *constants)1287{1288auto iter = constants->find(value);1289if (iter != constants->end())1290{1291return iter->second;1292}12931294SpirvType spirvType;1295spirvType.type = type;12961297const spirv::IdRef typeId = getSpirvTypeData(spirvType, nullptr).id;1298const spirv::IdRef constantId = getNewId({});12991300spirv::WriteConstant(&mSpirvTypeAndConstantDecls, typeId, constantId,1301spirv::LiteralContextDependentNumber(value));13021303return constants->insert({value, constantId}).first->second;1304}13051306spirv::IdRef SPIRVBuilder::getUintConstant(uint32_t value)1307{1308return getBasicConstantHelper(value, EbtUInt, &mUintConstants);1309}13101311spirv::IdRef SPIRVBuilder::getIntConstant(int32_t value)1312{1313uint32_t asUint = static_cast<uint32_t>(value);1314return getBasicConstantHelper(asUint, EbtInt, &mIntConstants);1315}13161317spirv::IdRef SPIRVBuilder::getFloatConstant(float value)1318{1319union1320{1321float f;1322uint32_t u;1323} asUint;1324asUint.f = value;1325return getBasicConstantHelper(asUint.u, EbtFloat, &mFloatConstants);1326}13271328spirv::IdRef SPIRVBuilder::getNullConstant(spirv::IdRef typeId)1329{1330if (typeId >= mNullConstants.size())1331{1332mNullConstants.resize(typeId + 1);1333}13341335if (!mNullConstants[typeId].valid())1336{1337const spirv::IdRef constantId = getNewId({});1338mNullConstants[typeId] = constantId;13391340spirv::WriteConstantNull(&mSpirvTypeAndConstantDecls, typeId, constantId);1341}13421343return mNullConstants[typeId];1344}13451346spirv::IdRef SPIRVBuilder::getNullVectorConstantHelper(TBasicType type, int size)1347{1348SpirvType vecType;1349vecType.type = type;1350vecType.primarySize = static_cast<uint8_t>(size);13511352return getNullConstant(getSpirvTypeData(vecType, nullptr).id);1353}13541355spirv::IdRef SPIRVBuilder::getVectorConstantHelper(spirv::IdRef valueId, TBasicType type, int size)1356{1357if (size == 1)1358{1359return valueId;1360}13611362SpirvType vecType;1363vecType.type = type;1364vecType.primarySize = static_cast<uint8_t>(size);13651366const spirv::IdRef typeId = getSpirvTypeData(vecType, nullptr).id;1367const spirv::IdRefList valueIds(size, valueId);13681369return getCompositeConstant(typeId, valueIds);1370}13711372spirv::IdRef SPIRVBuilder::getUvecConstant(uint32_t value, int size)1373{1374if (value == 0)1375{1376return getNullVectorConstantHelper(EbtUInt, size);1377}13781379const spirv::IdRef valueId = getUintConstant(value);1380return getVectorConstantHelper(valueId, EbtUInt, size);1381}13821383spirv::IdRef SPIRVBuilder::getIvecConstant(int32_t value, int size)1384{1385if (value == 0)1386{1387return getNullVectorConstantHelper(EbtInt, size);1388}13891390const spirv::IdRef valueId = getIntConstant(value);1391return getVectorConstantHelper(valueId, EbtInt, size);1392}13931394spirv::IdRef SPIRVBuilder::getVecConstant(float value, int size)1395{1396if (value == 0)1397{1398return getNullVectorConstantHelper(EbtFloat, size);1399}14001401const spirv::IdRef valueId = getFloatConstant(value);1402return getVectorConstantHelper(valueId, EbtFloat, size);1403}14041405spirv::IdRef SPIRVBuilder::getCompositeConstant(spirv::IdRef typeId, const spirv::IdRefList &values)1406{1407SpirvIdAndIdList key{typeId, values};14081409auto iter = mCompositeConstants.find(key);1410if (iter == mCompositeConstants.end())1411{1412const spirv::IdRef constantId = getNewId({});14131414spirv::WriteConstantComposite(&mSpirvTypeAndConstantDecls, typeId, constantId, values);14151416iter = mCompositeConstants.insert({key, constantId}).first;1417}14181419return iter->second;1420}14211422void SPIRVBuilder::startNewFunction(spirv::IdRef functionId, const TFunction *func)1423{1424ASSERT(mSpirvCurrentFunctionBlocks.empty());14251426// Add the first block of the function.1427mSpirvCurrentFunctionBlocks.emplace_back();1428mSpirvCurrentFunctionBlocks.back().labelId = getNewId({});14291430// Output debug information.1431spirv::WriteName(&mSpirvDebug, functionId, hashFunctionName(func).data());1432}14331434void SPIRVBuilder::assembleSpirvFunctionBlocks()1435{1436// Take all the blocks and place them in the functions section of SPIR-V in sequence.1437for (const SpirvBlock &block : mSpirvCurrentFunctionBlocks)1438{1439// Every block must be properly terminated.1440ASSERT(block.isTerminated);14411442// Generate the OpLabel instruction for the block.1443spirv::WriteLabel(&mSpirvFunctions, block.labelId);14441445// Add the variable declarations if any.1446mSpirvFunctions.insert(mSpirvFunctions.end(), block.localVariables.begin(),1447block.localVariables.end());14481449// Add the body of the block.1450mSpirvFunctions.insert(mSpirvFunctions.end(), block.body.begin(), block.body.end());1451}14521453// Clean up.1454mSpirvCurrentFunctionBlocks.clear();1455}14561457spirv::IdRef SPIRVBuilder::declareVariable(spirv::IdRef typeId,1458spv::StorageClass storageClass,1459const SpirvDecorations &decorations,1460spirv::IdRef *initializerId,1461const char *name)1462{1463const bool isFunctionLocal = storageClass == spv::StorageClassFunction;14641465// Make sure storage class is consistent with where the variable is declared.1466ASSERT(!isFunctionLocal || !mSpirvCurrentFunctionBlocks.empty());14671468// Function-local variables go in the first block of the function, while the rest are in the1469// global variables section.1470spirv::Blob *spirvSection = isFunctionLocal1471? &mSpirvCurrentFunctionBlocks.front().localVariables1472: &mSpirvVariableDecls;14731474const spirv::IdRef typePointerId = getTypePointerId(typeId, storageClass);1475const spirv::IdRef variableId = getNewId(decorations);14761477spirv::WriteVariable(spirvSection, typePointerId, variableId, storageClass, initializerId);14781479// Output debug information.1480if (name)1481{1482spirv::WriteName(&mSpirvDebug, variableId, name);1483}14841485return variableId;1486}14871488spirv::IdRef SPIRVBuilder::declareSpecConst(TBasicType type, int id, const char *name)1489{1490SpirvType spirvType;1491spirvType.type = type;14921493const spirv::IdRef typeId = getSpirvTypeData(spirvType, nullptr).id;1494const spirv::IdRef specConstId = getNewId({});14951496// Note: all spec constants are 0 initialized by the translator.1497if (type == EbtBool)1498{1499spirv::WriteSpecConstantFalse(&mSpirvTypeAndConstantDecls, typeId, specConstId);1500}1501else1502{1503spirv::WriteSpecConstant(&mSpirvTypeAndConstantDecls, typeId, specConstId,1504spirv::LiteralContextDependentNumber(0));1505}15061507// Add the SpecId decoration1508spirv::WriteDecorate(&mSpirvDecorations, specConstId, spv::DecorationSpecId,1509{spirv::LiteralInteger(id)});15101511// Output debug information.1512if (name)1513{1514spirv::WriteName(&mSpirvDebug, specConstId, name);1515}15161517return specConstId;1518}15191520void SPIRVBuilder::startConditional(size_t blockCount, bool isContinuable, bool isBreakable)1521{1522mConditionalStack.emplace_back();1523SpirvConditional &conditional = mConditionalStack.back();15241525// Create the requested number of block ids.1526conditional.blockIds.resize(blockCount);1527for (spirv::IdRef &blockId : conditional.blockIds)1528{1529blockId = getNewId({});1530}15311532conditional.isContinuable = isContinuable;1533conditional.isBreakable = isBreakable;15341535// Don't automatically start the next block. The caller needs to generate instructions based on1536// the ids that were just generated above.1537}15381539void SPIRVBuilder::nextConditionalBlock()1540{1541ASSERT(!mConditionalStack.empty());1542SpirvConditional &conditional = mConditionalStack.back();15431544ASSERT(conditional.nextBlockToWrite < conditional.blockIds.size());1545const spirv::IdRef blockId = conditional.blockIds[conditional.nextBlockToWrite++];15461547// The previous block must have properly terminated.1548ASSERT(isCurrentFunctionBlockTerminated());15491550// Generate a new block.1551mSpirvCurrentFunctionBlocks.emplace_back();1552mSpirvCurrentFunctionBlocks.back().labelId = blockId;1553}15541555void SPIRVBuilder::endConditional()1556{1557ASSERT(!mConditionalStack.empty());15581559// No blocks should be left.1560ASSERT(mConditionalStack.back().nextBlockToWrite == mConditionalStack.back().blockIds.size());15611562mConditionalStack.pop_back();1563}15641565bool SPIRVBuilder::isInLoop() const1566{1567for (const SpirvConditional &conditional : mConditionalStack)1568{1569if (conditional.isContinuable)1570{1571return true;1572}1573}15741575return false;1576}15771578spirv::IdRef SPIRVBuilder::getBreakTargetId() const1579{1580for (size_t index = mConditionalStack.size(); index > 0; --index)1581{1582const SpirvConditional &conditional = mConditionalStack[index - 1];15831584if (conditional.isBreakable)1585{1586// The target of break; is always the merge block, and the merge block is always the1587// last block.1588return conditional.blockIds.back();1589}1590}15911592UNREACHABLE();1593return spirv::IdRef{};1594}15951596spirv::IdRef SPIRVBuilder::getContinueTargetId() const1597{1598for (size_t index = mConditionalStack.size(); index > 0; --index)1599{1600const SpirvConditional &conditional = mConditionalStack[index - 1];16011602if (conditional.isContinuable)1603{1604// The target of continue; is always the block before merge, so it's the one before1605// last.1606ASSERT(conditional.blockIds.size() > 2);1607return conditional.blockIds[conditional.blockIds.size() - 2];1608}1609}16101611UNREACHABLE();1612return spirv::IdRef{};1613}16141615uint32_t SPIRVBuilder::nextUnusedBinding()1616{1617return mNextUnusedBinding++;1618}16191620uint32_t SPIRVBuilder::nextUnusedInputLocation(uint32_t consumedCount)1621{1622uint32_t nextUnused = mNextUnusedInputLocation;1623mNextUnusedInputLocation += consumedCount;1624return nextUnused;1625}16261627uint32_t SPIRVBuilder::nextUnusedOutputLocation(uint32_t consumedCount)1628{1629uint32_t nextUnused = mNextUnusedOutputLocation;1630mNextUnusedOutputLocation += consumedCount;1631return nextUnused;1632}16331634bool SPIRVBuilder::isInvariantOutput(const TType &type) const1635{1636return IsInvariant(type, mCompiler);1637}16381639void SPIRVBuilder::addCapability(spv::Capability capability)1640{1641mCapabilities.insert(capability);1642}16431644void SPIRVBuilder::addExecutionMode(spv::ExecutionMode executionMode)1645{1646ASSERT(static_cast<size_t>(executionMode) < mExecutionModes.size());1647mExecutionModes.set(executionMode);1648}16491650void SPIRVBuilder::addExtension(SPIRVExtensions extension)1651{1652mExtensions.set(extension);1653}16541655void SPIRVBuilder::setEntryPointId(spirv::IdRef id)1656{1657ASSERT(!mEntryPointId.valid());1658mEntryPointId = id;1659}16601661void SPIRVBuilder::addEntryPointInterfaceVariableId(spirv::IdRef id)1662{1663mEntryPointInterfaceList.push_back(id);1664}16651666void SPIRVBuilder::writePerVertexBuiltIns(const TType &type, spirv::IdRef typeId)1667{1668ASSERT(type.isInterfaceBlock());1669const TInterfaceBlock *block = type.getInterfaceBlock();16701671uint32_t fieldIndex = 0;1672for (const TField *field : block->fields())1673{1674spv::BuiltIn decorationValue = spv::BuiltInPosition;1675switch (field->type()->getQualifier())1676{1677case EvqPosition:1678decorationValue = spv::BuiltInPosition;1679break;1680case EvqPointSize:1681decorationValue = spv::BuiltInPointSize;1682break;1683case EvqClipDistance:1684decorationValue = spv::BuiltInClipDistance;1685break;1686case EvqCullDistance:1687decorationValue = spv::BuiltInCullDistance;1688break;1689default:1690UNREACHABLE();1691}16921693spirv::WriteMemberDecorate(&mSpirvDecorations, typeId, spirv::LiteralInteger(fieldIndex++),1694spv::DecorationBuiltIn,1695{spirv::LiteralInteger(decorationValue)});1696}1697}16981699void SPIRVBuilder::writeInterfaceVariableDecorations(const TType &type, spirv::IdRef variableId)1700{1701const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier();17021703const bool isVarying = IsVarying(type.getQualifier());1704const bool needsSetBinding =1705IsSampler(type.getBasicType()) ||1706(type.isInterfaceBlock() &&1707(type.getQualifier() == EvqUniform || type.getQualifier() == EvqBuffer)) ||1708IsImage(type.getBasicType()) || IsSubpassInputType(type.getBasicType());1709const bool needsLocation = type.getQualifier() == EvqAttribute ||1710type.getQualifier() == EvqVertexIn ||1711type.getQualifier() == EvqFragmentOut || isVarying;1712const bool needsInputAttachmentIndex = IsSubpassInputType(type.getBasicType());1713const bool needsBlendIndex =1714type.getQualifier() == EvqFragmentOut && layoutQualifier.index >= 0;17151716// If the resource declaration requires set & binding, add the DescriptorSet and Binding1717// decorations.1718if (needsSetBinding)1719{1720spirv::WriteDecorate(&mSpirvDecorations, variableId, spv::DecorationDescriptorSet,1721{spirv::LiteralInteger(0)});1722spirv::WriteDecorate(&mSpirvDecorations, variableId, spv::DecorationBinding,1723{spirv::LiteralInteger(nextUnusedBinding())});1724}17251726if (needsLocation)1727{1728const unsigned int locationCount =1729CalculateVaryingLocationCount(type, gl::ToGLenum(mShaderType));1730const uint32_t location = IsShaderIn(type.getQualifier())1731? nextUnusedInputLocation(locationCount)1732: nextUnusedOutputLocation(locationCount);17331734spirv::WriteDecorate(&mSpirvDecorations, variableId, spv::DecorationLocation,1735{spirv::LiteralInteger(location)});1736}17371738// If the resource declaration is an input attachment, add the InputAttachmentIndex decoration.1739if (needsInputAttachmentIndex)1740{1741spirv::WriteDecorate(&mSpirvDecorations, variableId, spv::DecorationInputAttachmentIndex,1742{spirv::LiteralInteger(layoutQualifier.inputAttachmentIndex)});1743}17441745if (needsBlendIndex)1746{1747spirv::WriteDecorate(&mSpirvDecorations, variableId, spv::DecorationIndex,1748{spirv::LiteralInteger(layoutQualifier.index)});1749}17501751// Handle interpolation and auxiliary decorations on varyings1752if (isVarying)1753{1754writeInterpolationDecoration(type.getQualifier(), variableId,1755std::numeric_limits<uint32_t>::max());1756}1757}17581759void SPIRVBuilder::writeBranchConditional(spirv::IdRef conditionValue,1760spirv::IdRef trueBlock,1761spirv::IdRef falseBlock,1762spirv::IdRef mergeBlock)1763{1764// Generate the following:1765//1766// OpSelectionMerge %mergeBlock None1767// OpBranchConditional %conditionValue %trueBlock %falseBlock1768//1769spirv::WriteSelectionMerge(getSpirvCurrentFunctionBlock(), mergeBlock,1770spv::SelectionControlMaskNone);1771spirv::WriteBranchConditional(getSpirvCurrentFunctionBlock(), conditionValue, trueBlock,1772falseBlock, {});1773terminateCurrentFunctionBlock();17741775// Start the true or false block, whichever exists.1776nextConditionalBlock();1777}17781779void SPIRVBuilder::writeBranchConditionalBlockEnd()1780{1781if (!isCurrentFunctionBlockTerminated())1782{1783// Insert a branch to the merge block at the end of each if-else block, unless the block is1784// already terminated, such as with a return or discard.1785const spirv::IdRef mergeBlock = getCurrentConditional()->blockIds.back();17861787spirv::WriteBranch(getSpirvCurrentFunctionBlock(), mergeBlock);1788terminateCurrentFunctionBlock();1789}17901791// Move on to the next block.1792nextConditionalBlock();1793}17941795void SPIRVBuilder::writeLoopHeader(spirv::IdRef branchToBlock,1796spirv::IdRef continueBlock,1797spirv::IdRef mergeBlock)1798{1799// First, jump to the header block:1800//1801// OpBranch %header1802//1803const spirv::IdRef headerBlock = mConditionalStack.back().blockIds[0];1804spirv::WriteBranch(getSpirvCurrentFunctionBlock(), headerBlock);1805terminateCurrentFunctionBlock();18061807// Start the header block.1808nextConditionalBlock();18091810// Generate the following:1811//1812// OpLoopMerge %mergeBlock %continueBlock None1813// OpBranch %branchToBlock (%cond or if do-while, %body)1814//1815spirv::WriteLoopMerge(getSpirvCurrentFunctionBlock(), mergeBlock, continueBlock,1816spv::LoopControlMaskNone);1817spirv::WriteBranch(getSpirvCurrentFunctionBlock(), branchToBlock);1818terminateCurrentFunctionBlock();18191820// Start the next block, which is either %cond or %body.1821nextConditionalBlock();1822}18231824void SPIRVBuilder::writeLoopConditionEnd(spirv::IdRef conditionValue,1825spirv::IdRef branchToBlock,1826spirv::IdRef mergeBlock)1827{1828// Generate the following:1829//1830// OpBranchConditional %conditionValue %branchToBlock %mergeBlock1831//1832// %branchToBlock is either %body or if do-while, %header1833//1834spirv::WriteBranchConditional(getSpirvCurrentFunctionBlock(), conditionValue, branchToBlock,1835mergeBlock, {});1836terminateCurrentFunctionBlock();18371838// Start the next block, which is either %continue or %body.1839nextConditionalBlock();1840}18411842void SPIRVBuilder::writeLoopContinueEnd(spirv::IdRef headerBlock)1843{1844// Generate the following:1845//1846// OpBranch %headerBlock1847//1848spirv::WriteBranch(getSpirvCurrentFunctionBlock(), headerBlock);1849terminateCurrentFunctionBlock();18501851// Start the next block, which is %body.1852nextConditionalBlock();1853}18541855void SPIRVBuilder::writeLoopBodyEnd(spirv::IdRef continueBlock)1856{1857// Generate the following:1858//1859// OpBranch %continueBlock1860//1861// This is only done if the block isn't already terminated in another way, such as with an1862// unconditional continue/etc at the end of the loop.1863if (!isCurrentFunctionBlockTerminated())1864{1865spirv::WriteBranch(getSpirvCurrentFunctionBlock(), continueBlock);1866terminateCurrentFunctionBlock();1867}18681869// Start the next block, which is %merge or if while, %continue.1870nextConditionalBlock();1871}18721873void SPIRVBuilder::writeSwitch(spirv::IdRef conditionValue,1874spirv::IdRef defaultBlock,1875const spirv::PairLiteralIntegerIdRefList &targetPairList,1876spirv::IdRef mergeBlock)1877{1878// Generate the following:1879//1880// OpSelectionMerge %mergeBlock None1881// OpSwitch %conditionValue %defaultBlock A %ABlock B %BBlock ...1882//1883spirv::WriteSelectionMerge(getSpirvCurrentFunctionBlock(), mergeBlock,1884spv::SelectionControlMaskNone);1885spirv::WriteSwitch(getSpirvCurrentFunctionBlock(), conditionValue, defaultBlock,1886targetPairList);1887terminateCurrentFunctionBlock();18881889// Start the next case block.1890nextConditionalBlock();1891}18921893void SPIRVBuilder::writeSwitchCaseBlockEnd()1894{1895if (!isCurrentFunctionBlockTerminated())1896{1897// If a case does not end in branch, insert a branch to the next block, implementing1898// fallthrough. For the last block, the branch target would automatically be the merge1899// block.1900const SpirvConditional *conditional = getCurrentConditional();1901const spirv::IdRef nextBlock = conditional->blockIds[conditional->nextBlockToWrite];19021903spirv::WriteBranch(getSpirvCurrentFunctionBlock(), nextBlock);1904terminateCurrentFunctionBlock();1905}19061907// Move on to the next block.1908nextConditionalBlock();1909}19101911void SPIRVBuilder::writeMemberDecorations(const SpirvType &type, spirv::IdRef typeId)1912{1913ASSERT(type.block != nullptr);19141915uint32_t fieldIndex = 0;19161917for (const TField *field : type.block->fields())1918{1919const TType &fieldType = *field->type();19201921// Add invariant decoration if any.1922if (type.typeSpec.isInvariantBlock || fieldType.isInvariant())1923{1924spirv::WriteMemberDecorate(&mSpirvDecorations, typeId,1925spirv::LiteralInteger(fieldIndex), spv::DecorationInvariant,1926{});1927}19281929// Add matrix decorations if any.1930if (fieldType.isMatrix())1931{1932// ColMajor or RowMajor1933const bool isRowMajor =1934IsBlockFieldRowMajorQualified(fieldType, type.typeSpec.isRowMajorQualifiedBlock);1935spirv::WriteMemberDecorate(1936&mSpirvDecorations, typeId, spirv::LiteralInteger(fieldIndex),1937isRowMajor ? spv::DecorationRowMajor : spv::DecorationColMajor, {});1938}19391940// Add interpolation and auxiliary decorations1941writeInterpolationDecoration(fieldType.getQualifier(), typeId, fieldIndex);19421943// Add patch decoration if any.1944if (type.typeSpec.isPatchIOBlock)1945{1946spirv::WriteMemberDecorate(&mSpirvDecorations, typeId,1947spirv::LiteralInteger(fieldIndex), spv::DecorationPatch, {});1948}19491950// Add other decorations.1951SpirvDecorations decorations = getDecorations(fieldType);1952for (const spv::Decoration decoration : decorations)1953{1954spirv::WriteMemberDecorate(&mSpirvDecorations, typeId,1955spirv::LiteralInteger(fieldIndex), decoration, {});1956}19571958++fieldIndex;1959}1960}19611962void SPIRVBuilder::writeInterpolationDecoration(TQualifier qualifier,1963spirv::IdRef id,1964uint32_t fieldIndex)1965{1966spv::Decoration decoration = spv::DecorationMax;19671968switch (qualifier)1969{1970case EvqSmooth:1971case EvqSmoothOut:1972case EvqSmoothIn:1973// No decoration in SPIR-V for smooth, this is the default interpolation.1974return;19751976case EvqFlat:1977case EvqFlatOut:1978case EvqFlatIn:1979decoration = spv::DecorationFlat;1980break;19811982case EvqNoPerspective:1983case EvqNoPerspectiveOut:1984case EvqNoPerspectiveIn:1985decoration = spv::DecorationNoPerspective;1986break;19871988case EvqCentroid:1989case EvqCentroidOut:1990case EvqCentroidIn:1991decoration = spv::DecorationCentroid;1992break;19931994case EvqSample:1995case EvqSampleOut:1996case EvqSampleIn:1997decoration = spv::DecorationSample;1998addCapability(spv::CapabilitySampleRateShading);1999break;20002001default:2002return;2003}20042005if (fieldIndex != std::numeric_limits<uint32_t>::max())2006{2007spirv::WriteMemberDecorate(&mSpirvDecorations, id, spirv::LiteralInteger(fieldIndex),2008decoration, {});2009}2010else2011{2012spirv::WriteDecorate(&mSpirvDecorations, id, decoration, {});2013}2014}20152016ImmutableString SPIRVBuilder::hashName(const TSymbol *symbol)2017{2018return HashName(symbol, mHashFunction, &mNameMap);2019}20202021ImmutableString SPIRVBuilder::hashTypeName(const TType &type)2022{2023return GetTypeName(type, mHashFunction, &mNameMap);2024}20252026ImmutableString SPIRVBuilder::hashFieldName(const TField *field)2027{2028ASSERT(field->symbolType() != SymbolType::Empty);2029if (field->symbolType() == SymbolType::UserDefined)2030{2031return HashName(field->name(), mHashFunction, &mNameMap);2032}20332034return field->name();2035}20362037ImmutableString SPIRVBuilder::hashFunctionName(const TFunction *func)2038{2039if (func->isMain())2040{2041return func->name();2042}20432044return hashName(func);2045}20462047spirv::Blob SPIRVBuilder::getSpirv()2048{2049ASSERT(mConditionalStack.empty());20502051spirv::Blob result;20522053// Reserve a minimum amount of memory.2054//2055// 5 for header +2056// a number of capabilities +2057// size of already generated instructions.2058//2059// The actual size is larger due to other metadata instructions such as extensions,2060// OpExtInstImport, OpEntryPoint, OpExecutionMode etc.2061result.reserve(5 + mCapabilities.size() * 2 + mSpirvDebug.size() + mSpirvDecorations.size() +2062mSpirvTypeAndConstantDecls.size() + mSpirvTypePointerDecls.size() +2063mSpirvFunctionTypeDecls.size() + mSpirvVariableDecls.size() +2064mSpirvFunctions.size());20652066// Generate the SPIR-V header.2067spirv::WriteSpirvHeader(&result, mNextAvailableId);20682069// Generate metadata in the following order:2070//2071// - OpCapability instructions.2072for (spv::Capability capability : mCapabilities)2073{2074spirv::WriteCapability(&result, capability);2075}20762077// - OpExtension instructions2078writeExtensions(&result);20792080// - OpExtInstImport2081if (mExtInstImportIdStd.valid())2082{2083spirv::WriteExtInstImport(&result, mExtInstImportIdStd, "GLSL.std.450");2084}20852086// - OpMemoryModel2087spirv::WriteMemoryModel(&result, spv::AddressingModelLogical, spv::MemoryModelGLSL450);20882089// - OpEntryPoint2090constexpr gl::ShaderMap<spv::ExecutionModel> kExecutionModels = {2091{gl::ShaderType::Vertex, spv::ExecutionModelVertex},2092{gl::ShaderType::TessControl, spv::ExecutionModelTessellationControl},2093{gl::ShaderType::TessEvaluation, spv::ExecutionModelTessellationEvaluation},2094{gl::ShaderType::Geometry, spv::ExecutionModelGeometry},2095{gl::ShaderType::Fragment, spv::ExecutionModelFragment},2096{gl::ShaderType::Compute, spv::ExecutionModelGLCompute},2097};2098spirv::WriteEntryPoint(&result, kExecutionModels[mShaderType], mEntryPointId, "main",2099mEntryPointInterfaceList);21002101// - OpExecutionMode instructions2102writeExecutionModes(&result);21032104// - OpSource and OpSourceExtension instructions.2105//2106// This is to support debuggers and capture/replay tools and isn't strictly necessary.2107spirv::WriteSource(&result, spv::SourceLanguageGLSL, spirv::LiteralInteger(450), nullptr,2108nullptr);2109writeSourceExtensions(&result);21102111// Append the already generated sections in order2112result.insert(result.end(), mSpirvDebug.begin(), mSpirvDebug.end());2113result.insert(result.end(), mSpirvDecorations.begin(), mSpirvDecorations.end());2114result.insert(result.end(), mSpirvTypeAndConstantDecls.begin(),2115mSpirvTypeAndConstantDecls.end());2116result.insert(result.end(), mSpirvTypePointerDecls.begin(), mSpirvTypePointerDecls.end());2117result.insert(result.end(), mSpirvFunctionTypeDecls.begin(), mSpirvFunctionTypeDecls.end());2118result.insert(result.end(), mSpirvVariableDecls.begin(), mSpirvVariableDecls.end());2119result.insert(result.end(), mSpirvFunctions.begin(), mSpirvFunctions.end());21202121result.shrink_to_fit();2122return result;2123}21242125void SPIRVBuilder::writeExecutionModes(spirv::Blob *blob)2126{2127switch (mShaderType)2128{2129case gl::ShaderType::Fragment:2130spirv::WriteExecutionMode(blob, mEntryPointId, spv::ExecutionModeOriginUpperLeft, {});21312132if (mCompiler->isEarlyFragmentTestsSpecified() ||2133mCompiler->isEarlyFragmentTestsOptimized())2134{2135spirv::WriteExecutionMode(blob, mEntryPointId, spv::ExecutionModeEarlyFragmentTests,2136{});2137}21382139break;21402141case gl::ShaderType::TessControl:2142spirv::WriteExecutionMode(2143blob, mEntryPointId, spv::ExecutionModeOutputVertices,2144{spirv::LiteralInteger(mCompiler->getTessControlShaderOutputVertices())});2145break;21462147case gl::ShaderType::TessEvaluation:2148{2149const spv::ExecutionMode inputExecutionMode = GetTessEvalInputExecutionMode(2150mCompiler->getTessEvaluationShaderInputPrimitiveType());2151const spv::ExecutionMode spacingExecutionMode = GetTessEvalSpacingExecutionMode(2152mCompiler->getTessEvaluationShaderInputVertexSpacingType());2153const spv::ExecutionMode orderingExecutionMode = GetTessEvalOrderingExecutionMode(2154mCompiler->getTessEvaluationShaderInputOrderingType());21552156spirv::WriteExecutionMode(blob, mEntryPointId, inputExecutionMode, {});2157spirv::WriteExecutionMode(blob, mEntryPointId, spacingExecutionMode, {});2158spirv::WriteExecutionMode(blob, mEntryPointId, orderingExecutionMode, {});2159if (mCompiler->getTessEvaluationShaderInputPointType() == EtetPointMode)2160{2161spirv::WriteExecutionMode(blob, mEntryPointId, spv::ExecutionModePointMode, {});2162}2163break;2164}21652166case gl::ShaderType::Geometry:2167{2168const spv::ExecutionMode inputExecutionMode =2169GetGeometryInputExecutionMode(mCompiler->getGeometryShaderInputPrimitiveType());2170const spv::ExecutionMode outputExecutionMode =2171GetGeometryOutputExecutionMode(mCompiler->getGeometryShaderOutputPrimitiveType());21722173// max_vertices=0 is not valid in Vulkan2174const int maxVertices = std::max(1, mCompiler->getGeometryShaderMaxVertices());21752176spirv::WriteExecutionMode(blob, mEntryPointId, inputExecutionMode, {});2177spirv::WriteExecutionMode(blob, mEntryPointId, outputExecutionMode, {});2178spirv::WriteExecutionMode(blob, mEntryPointId, spv::ExecutionModeOutputVertices,2179{spirv::LiteralInteger(maxVertices)});2180spirv::WriteExecutionMode(2181blob, mEntryPointId, spv::ExecutionModeInvocations,2182{spirv::LiteralInteger(mCompiler->getGeometryShaderInvocations())});21832184break;2185}21862187case gl::ShaderType::Compute:2188{2189const sh::WorkGroupSize &localSize = mCompiler->getComputeShaderLocalSize();2190spirv::WriteExecutionMode(2191blob, mEntryPointId, spv::ExecutionModeLocalSize,2192{spirv::LiteralInteger(localSize[0]), spirv::LiteralInteger(localSize[1]),2193spirv::LiteralInteger(localSize[2])});2194break;2195}21962197default:2198break;2199}22002201// Add any execution modes that were added due to built-ins used in the shader.2202for (uint32_t executionMode : mExecutionModes)2203{2204spirv::WriteExecutionMode(blob, mEntryPointId,2205static_cast<spv::ExecutionMode>(executionMode), {});2206}2207}22082209void SPIRVBuilder::writeExtensions(spirv::Blob *blob)2210{2211for (SPIRVExtensions extension : mExtensions)2212{2213switch (extension)2214{2215case SPIRVExtensions::MultiviewOVR:2216spirv::WriteExtension(blob, "SPV_KHR_multiview");2217break;2218default:2219UNREACHABLE();2220}2221}2222}22232224void SPIRVBuilder::writeSourceExtensions(spirv::Blob *blob)2225{2226for (SPIRVExtensions extension : mExtensions)2227{2228switch (extension)2229{2230case SPIRVExtensions::MultiviewOVR:2231spirv::WriteSourceExtension(blob, "GL_OVR_multiview");2232break;2233default:2234UNREACHABLE();2235}2236}2237}22382239} // namespace sh224022412242