From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- .../src/compiler/translator/CollectVariables.cpp | 1288 ++++++++++++++++++++ 1 file changed, 1288 insertions(+) create mode 100644 gfx/angle/checkout/src/compiler/translator/CollectVariables.cpp (limited to 'gfx/angle/checkout/src/compiler/translator/CollectVariables.cpp') diff --git a/gfx/angle/checkout/src/compiler/translator/CollectVariables.cpp b/gfx/angle/checkout/src/compiler/translator/CollectVariables.cpp new file mode 100644 index 0000000000..d6c58bebcb --- /dev/null +++ b/gfx/angle/checkout/src/compiler/translator/CollectVariables.cpp @@ -0,0 +1,1288 @@ +// +// Copyright 2002 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// CollectVariables.cpp: Collect lists of shader interface variables based on the AST. + +#include "compiler/translator/CollectVariables.h" + +#include "angle_gl.h" +#include "common/utilities.h" +#include "compiler/translator/HashNames.h" +#include "compiler/translator/SymbolTable.h" +#include "compiler/translator/tree_util/IntermTraverse.h" +#include "compiler/translator/util.h" + +namespace sh +{ + +namespace +{ + +BlockLayoutType GetBlockLayoutType(TLayoutBlockStorage blockStorage) +{ + switch (blockStorage) + { + case EbsPacked: + return BLOCKLAYOUT_PACKED; + case EbsShared: + return BLOCKLAYOUT_SHARED; + case EbsStd140: + return BLOCKLAYOUT_STD140; + case EbsStd430: + return BLOCKLAYOUT_STD430; + default: + UNREACHABLE(); + return BLOCKLAYOUT_SHARED; + } +} + +BlockType GetBlockType(TQualifier qualifier) +{ + switch (qualifier) + { + case EvqUniform: + return BlockType::BLOCK_UNIFORM; + case EvqBuffer: + return BlockType::BLOCK_BUFFER; + default: + UNREACHABLE(); + return BlockType::BLOCK_UNIFORM; + } +} + +template +VarT *FindVariable(const ImmutableString &name, std::vector *infoList) +{ + // TODO(zmo): optimize this function. + for (size_t ii = 0; ii < infoList->size(); ++ii) + { + if (name == (*infoList)[ii].name) + return &((*infoList)[ii]); + } + + return nullptr; +} + +void MarkActive(ShaderVariable *variable) +{ + if (!variable->active) + { + if (variable->isStruct()) + { + // Conservatively assume all fields are statically used as well. + for (auto &field : variable->fields) + { + MarkActive(&field); + } + } + variable->staticUse = true; + variable->active = true; + } +} + +ShaderVariable *FindVariableInInterfaceBlock(const ImmutableString &name, + const TInterfaceBlock *interfaceBlock, + std::vector *infoList) +{ + ASSERT(interfaceBlock); + InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), infoList); + ASSERT(namedBlock); + + // Set static use on the parent interface block here + namedBlock->staticUse = true; + namedBlock->active = true; + return FindVariable(name, &namedBlock->fields); +} + +ShaderVariable *FindShaderIOBlockVariable(const ImmutableString &blockName, + std::vector *infoList) +{ + for (size_t index = 0; index < infoList->size(); ++index) + { + if (blockName == (*infoList)[index].structOrBlockName) + return &(*infoList)[index]; + } + + return nullptr; +} + +// Traverses the intermediate tree to collect all attributes, uniforms, varyings, fragment outputs, +// shared data and interface blocks. +class CollectVariablesTraverser : public TIntermTraverser +{ + public: + CollectVariablesTraverser(std::vector *attribs, + std::vector *outputVariables, + std::vector *uniforms, + std::vector *inputVaryings, + std::vector *outputVaryings, + std::vector *sharedVariables, + std::vector *uniformBlocks, + std::vector *shaderStorageBlocks, + ShHashFunction64 hashFunction, + TSymbolTable *symbolTable, + GLenum shaderType, + const TExtensionBehavior &extensionBehavior, + const ShBuiltInResources &resources, + int tessControlShaderOutputVertices); + + bool visitGlobalQualifierDeclaration(Visit visit, + TIntermGlobalQualifierDeclaration *node) override; + void visitSymbol(TIntermSymbol *symbol) override; + bool visitDeclaration(Visit, TIntermDeclaration *node) override; + bool visitBinary(Visit visit, TIntermBinary *binaryNode) override; + + private: + std::string getMappedName(const TSymbol *symbol) const; + + void setFieldOrVariableProperties(const TType &type, + bool staticUse, + bool isShaderIOBlock, + bool isPatch, + ShaderVariable *variableOut) const; + void setFieldProperties(const TType &type, + const ImmutableString &name, + bool staticUse, + bool isShaderIOBlock, + bool isPatch, + SymbolType symbolType, + ShaderVariable *variableOut) const; + void setCommonVariableProperties(const TType &type, + const TVariable &variable, + ShaderVariable *variableOut) const; + + ShaderVariable recordAttribute(const TIntermSymbol &variable) const; + ShaderVariable recordOutputVariable(const TIntermSymbol &variable) const; + ShaderVariable recordVarying(const TIntermSymbol &variable) const; + void recordInterfaceBlock(const char *instanceName, + const TType &interfaceBlockType, + InterfaceBlock *interfaceBlock) const; + ShaderVariable recordUniform(const TIntermSymbol &variable) const; + + void setBuiltInInfoFromSymbol(const TVariable &variable, ShaderVariable *info); + + void recordBuiltInVaryingUsed(const TVariable &variable, + bool *addedFlag, + std::vector *varyings); + void recordBuiltInFragmentOutputUsed(const TVariable &variable, bool *addedFlag); + void recordBuiltInAttributeUsed(const TVariable &variable, bool *addedFlag); + InterfaceBlock *findNamedInterfaceBlock(const ImmutableString &name) const; + + std::vector *mAttribs; + std::vector *mOutputVariables; + std::vector *mUniforms; + std::vector *mInputVaryings; + std::vector *mOutputVaryings; + std::vector *mSharedVariables; + std::vector *mUniformBlocks; + std::vector *mShaderStorageBlocks; + + std::map mInterfaceBlockFields; + + // Shader uniforms + bool mDepthRangeAdded; + bool mNumSamplesAdded; + + // Compute Shader builtins + bool mNumWorkGroupsAdded; + bool mWorkGroupIDAdded; + bool mLocalInvocationIDAdded; + bool mGlobalInvocationIDAdded; + bool mLocalInvocationIndexAdded; + + // Vertex Shader builtins + bool mInstanceIDAdded; + bool mVertexIDAdded; + bool mPointSizeAdded; + bool mDrawIDAdded; + + // Vertex Shader and Geometry Shader builtins + bool mPositionAdded; + bool mClipDistanceAdded; + bool mCullDistanceAdded; + + // Fragment Shader builtins + bool mPointCoordAdded; + bool mFrontFacingAdded; + bool mHelperInvocationAdded; + bool mFragCoordAdded; + bool mLastFragDataAdded; + bool mFragColorAdded; + bool mFragDataAdded; + bool mFragDepthAdded; + bool mSecondaryFragColorEXTAdded; + bool mSecondaryFragDataEXTAdded; + bool mSampleIDAdded; + bool mSamplePositionAdded; + bool mSampleMaskAdded; + bool mSampleMaskInAdded; + + // Geometry and Tessellation Shader builtins + bool mPerVertexInAdded; + bool mPerVertexOutAdded; + + // Geometry Shader builtins + bool mPrimitiveIDInAdded; + bool mInvocationIDAdded; + + // Geometry Shader and Fragment Shader builtins + bool mPrimitiveIDAdded; + bool mLayerAdded; + + // Shared memory variables + bool mSharedVariableAdded; + + // Tessellation Shader builtins + bool mPatchVerticesInAdded; + bool mTessLevelOuterAdded; + bool mTessLevelInnerAdded; + bool mBoundingBoxAdded; + bool mTessCoordAdded; + const int mTessControlShaderOutputVertices; + + ShHashFunction64 mHashFunction; + + GLenum mShaderType; + const TExtensionBehavior &mExtensionBehavior; + const ShBuiltInResources &mResources; +}; + +CollectVariablesTraverser::CollectVariablesTraverser( + std::vector *attribs, + std::vector *outputVariables, + std::vector *uniforms, + std::vector *inputVaryings, + std::vector *outputVaryings, + std::vector *sharedVariables, + std::vector *uniformBlocks, + std::vector *shaderStorageBlocks, + ShHashFunction64 hashFunction, + TSymbolTable *symbolTable, + GLenum shaderType, + const TExtensionBehavior &extensionBehavior, + const ShBuiltInResources &resources, + int tessControlShaderOutputVertices) + : TIntermTraverser(true, false, false, symbolTable), + mAttribs(attribs), + mOutputVariables(outputVariables), + mUniforms(uniforms), + mInputVaryings(inputVaryings), + mOutputVaryings(outputVaryings), + mSharedVariables(sharedVariables), + mUniformBlocks(uniformBlocks), + mShaderStorageBlocks(shaderStorageBlocks), + mDepthRangeAdded(false), + mNumSamplesAdded(false), + mNumWorkGroupsAdded(false), + mWorkGroupIDAdded(false), + mLocalInvocationIDAdded(false), + mGlobalInvocationIDAdded(false), + mLocalInvocationIndexAdded(false), + mInstanceIDAdded(false), + mVertexIDAdded(false), + mPointSizeAdded(false), + mDrawIDAdded(false), + mPositionAdded(false), + mClipDistanceAdded(false), + mCullDistanceAdded(false), + mPointCoordAdded(false), + mFrontFacingAdded(false), + mHelperInvocationAdded(false), + mFragCoordAdded(false), + mLastFragDataAdded(false), + mFragColorAdded(false), + mFragDataAdded(false), + mFragDepthAdded(false), + mSecondaryFragColorEXTAdded(false), + mSecondaryFragDataEXTAdded(false), + mSampleIDAdded(false), + mSamplePositionAdded(false), + mSampleMaskAdded(false), + mSampleMaskInAdded(false), + mPerVertexInAdded(false), + mPerVertexOutAdded(false), + mPrimitiveIDInAdded(false), + mInvocationIDAdded(false), + mPrimitiveIDAdded(false), + mLayerAdded(false), + mSharedVariableAdded(false), + mPatchVerticesInAdded(false), + mTessLevelOuterAdded(false), + mTessLevelInnerAdded(false), + mBoundingBoxAdded(false), + mTessCoordAdded(false), + mTessControlShaderOutputVertices(tessControlShaderOutputVertices), + mHashFunction(hashFunction), + mShaderType(shaderType), + mExtensionBehavior(extensionBehavior), + mResources(resources) +{} + +std::string CollectVariablesTraverser::getMappedName(const TSymbol *symbol) const +{ + return HashName(symbol, mHashFunction, nullptr).data(); +} + +void CollectVariablesTraverser::setBuiltInInfoFromSymbol(const TVariable &variable, + ShaderVariable *info) +{ + const TType &type = variable.getType(); + + info->name = variable.name().data(); + info->mappedName = variable.name().data(); + + bool isShaderIOBlock = + IsShaderIoBlock(type.getQualifier()) && type.getInterfaceBlock() != nullptr; + bool isPatch = type.getQualifier() == EvqTessLevelInner || + type.getQualifier() == EvqTessLevelOuter || + type.getQualifier() == EvqBoundingBox; + + setFieldOrVariableProperties(type, true, isShaderIOBlock, isPatch, info); +} + +void CollectVariablesTraverser::recordBuiltInVaryingUsed(const TVariable &variable, + bool *addedFlag, + std::vector *varyings) +{ + ASSERT(varyings); + if (!(*addedFlag)) + { + ShaderVariable info; + setBuiltInInfoFromSymbol(variable, &info); + info.active = true; + info.isInvariant = mSymbolTable->isVaryingInvariant(variable); + + varyings->push_back(info); + (*addedFlag) = true; + } +} + +void CollectVariablesTraverser::recordBuiltInFragmentOutputUsed(const TVariable &variable, + bool *addedFlag) +{ + if (!(*addedFlag)) + { + ShaderVariable info; + setBuiltInInfoFromSymbol(variable, &info); + info.active = true; + mOutputVariables->push_back(info); + (*addedFlag) = true; + } +} + +void CollectVariablesTraverser::recordBuiltInAttributeUsed(const TVariable &variable, + bool *addedFlag) +{ + if (!(*addedFlag)) + { + ShaderVariable info; + setBuiltInInfoFromSymbol(variable, &info); + info.active = true; + info.location = -1; + mAttribs->push_back(info); + (*addedFlag) = true; + } +} + +bool CollectVariablesTraverser::visitGlobalQualifierDeclaration( + Visit visit, + TIntermGlobalQualifierDeclaration *node) +{ + // We should not mark variables as active just based on an invariant/precise declaration, so we + // don't traverse the symbols declared invariant. + return false; +} + +// We want to check whether a uniform/varying is active because we need to skip updating inactive +// ones. We also only count the active ones in packing computing. Also, gl_FragCoord, gl_PointCoord, +// and gl_FrontFacing count toward varying counting if they are active in a fragment shader. +void CollectVariablesTraverser::visitSymbol(TIntermSymbol *symbol) +{ + ASSERT(symbol != nullptr); + + if (symbol->variable().symbolType() == SymbolType::AngleInternal || + symbol->variable().symbolType() == SymbolType::Empty) + { + // Internal variables or nameless variables are not collected. + return; + } + + ShaderVariable *var = nullptr; + + const ImmutableString &symbolName = symbol->getName(); + + // Check the qualifier from the variable, not from the symbol node. The node may have a + // different qualifier if it's the result of a folded ternary node. + TQualifier qualifier = symbol->variable().getType().getQualifier(); + const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock(); + + if (IsVaryingIn(qualifier)) + { + if (interfaceBlock) + { + var = FindShaderIOBlockVariable(interfaceBlock->name(), mInputVaryings); + } + else + { + var = FindVariable(symbolName, mInputVaryings); + } + } + else if (IsVaryingOut(qualifier)) + { + if (interfaceBlock) + { + var = FindShaderIOBlockVariable(interfaceBlock->name(), mOutputVaryings); + } + else + { + var = FindVariable(symbolName, mOutputVaryings); + } + } + else if (symbol->getType().getBasicType() == EbtInterfaceBlock) + { + UNREACHABLE(); + } + else if (symbolName == "gl_DepthRange") + { + ASSERT(qualifier == EvqUniform); + + if (!mDepthRangeAdded) + { + ShaderVariable info; + const char kName[] = "gl_DepthRange"; + info.name = kName; + info.mappedName = kName; + info.type = GL_NONE; + info.precision = GL_NONE; + info.staticUse = true; + info.active = true; + + ShaderVariable nearInfo(GL_FLOAT); + const char kNearName[] = "near"; + nearInfo.name = kNearName; + nearInfo.mappedName = kNearName; + nearInfo.precision = GL_HIGH_FLOAT; + nearInfo.staticUse = true; + nearInfo.active = true; + + ShaderVariable farInfo(GL_FLOAT); + const char kFarName[] = "far"; + farInfo.name = kFarName; + farInfo.mappedName = kFarName; + farInfo.precision = GL_HIGH_FLOAT; + farInfo.staticUse = true; + farInfo.active = true; + + ShaderVariable diffInfo(GL_FLOAT); + const char kDiffName[] = "diff"; + diffInfo.name = kDiffName; + diffInfo.mappedName = kDiffName; + diffInfo.precision = GL_HIGH_FLOAT; + diffInfo.staticUse = true; + diffInfo.active = true; + + info.fields.push_back(nearInfo); + info.fields.push_back(farInfo); + info.fields.push_back(diffInfo); + + mUniforms->push_back(info); + mDepthRangeAdded = true; + } + } + else if (symbolName == "gl_NumSamples") + { + ASSERT(qualifier == EvqUniform); + + if (!mNumSamplesAdded) + { + ShaderVariable info; + const char kName[] = "gl_NumSamples"; + info.name = kName; + info.mappedName = kName; + info.type = GL_INT; + info.precision = GL_LOW_INT; + info.staticUse = true; + info.active = true; + + mUniforms->push_back(info); + mNumSamplesAdded = true; + } + } + else + { + switch (qualifier) + { + case EvqAttribute: + case EvqVertexIn: + var = FindVariable(symbolName, mAttribs); + break; + case EvqFragmentOut: + case EvqFragmentInOut: + var = FindVariable(symbolName, mOutputVariables); + var->isFragmentInOut = qualifier == EvqFragmentInOut; + break; + case EvqUniform: + { + if (interfaceBlock) + { + var = FindVariableInInterfaceBlock(symbolName, interfaceBlock, mUniformBlocks); + } + else + { + var = FindVariable(symbolName, mUniforms); + } + + // It's an internal error to reference an undefined user uniform + ASSERT(!gl::IsBuiltInName(symbolName.data()) || var); + } + break; + case EvqBuffer: + { + var = + FindVariableInInterfaceBlock(symbolName, interfaceBlock, mShaderStorageBlocks); + } + break; + case EvqFragCoord: + recordBuiltInVaryingUsed(symbol->variable(), &mFragCoordAdded, mInputVaryings); + return; + case EvqFrontFacing: + recordBuiltInVaryingUsed(symbol->variable(), &mFrontFacingAdded, mInputVaryings); + return; + case EvqHelperInvocation: + recordBuiltInVaryingUsed(symbol->variable(), &mHelperInvocationAdded, + mInputVaryings); + return; + case EvqPointCoord: + recordBuiltInVaryingUsed(symbol->variable(), &mPointCoordAdded, mInputVaryings); + return; + case EvqNumWorkGroups: + recordBuiltInAttributeUsed(symbol->variable(), &mNumWorkGroupsAdded); + return; + case EvqWorkGroupID: + recordBuiltInAttributeUsed(symbol->variable(), &mWorkGroupIDAdded); + return; + case EvqLocalInvocationID: + recordBuiltInAttributeUsed(symbol->variable(), &mLocalInvocationIDAdded); + return; + case EvqGlobalInvocationID: + recordBuiltInAttributeUsed(symbol->variable(), &mGlobalInvocationIDAdded); + return; + case EvqLocalInvocationIndex: + recordBuiltInAttributeUsed(symbol->variable(), &mLocalInvocationIndexAdded); + return; + case EvqInstanceID: + // Whenever the initializeBuiltinsForInstancedMultiview option is set, + // gl_InstanceID is added inside expressions to initialize ViewID_OVR and + // InstanceID. Note that gl_InstanceID is not added to the symbol table for ESSL1 + // shaders. + recordBuiltInAttributeUsed(symbol->variable(), &mInstanceIDAdded); + return; + case EvqVertexID: + recordBuiltInAttributeUsed(symbol->variable(), &mVertexIDAdded); + return; + case EvqPosition: + recordBuiltInVaryingUsed(symbol->variable(), &mPositionAdded, mOutputVaryings); + return; + case EvqPointSize: + recordBuiltInVaryingUsed(symbol->variable(), &mPointSizeAdded, mOutputVaryings); + return; + case EvqDrawID: + recordBuiltInAttributeUsed(symbol->variable(), &mDrawIDAdded); + return; + case EvqLastFragData: + recordBuiltInVaryingUsed(symbol->variable(), &mLastFragDataAdded, mInputVaryings); + return; + case EvqFragColor: + recordBuiltInFragmentOutputUsed(symbol->variable(), &mFragColorAdded); + return; + case EvqFragData: + recordBuiltInFragmentOutputUsed(symbol->variable(), &mFragDataAdded); + return; + case EvqFragDepth: + recordBuiltInFragmentOutputUsed(symbol->variable(), &mFragDepthAdded); + return; + case EvqSecondaryFragColorEXT: + recordBuiltInFragmentOutputUsed(symbol->variable(), &mSecondaryFragColorEXTAdded); + return; + case EvqSecondaryFragDataEXT: + recordBuiltInFragmentOutputUsed(symbol->variable(), &mSecondaryFragDataEXTAdded); + return; + case EvqInvocationID: + recordBuiltInVaryingUsed(symbol->variable(), &mInvocationIDAdded, mInputVaryings); + break; + case EvqPrimitiveIDIn: + recordBuiltInVaryingUsed(symbol->variable(), &mPrimitiveIDInAdded, mInputVaryings); + break; + case EvqPrimitiveID: + if (mShaderType == GL_GEOMETRY_SHADER_EXT) + { + recordBuiltInVaryingUsed(symbol->variable(), &mPrimitiveIDAdded, + mOutputVaryings); + } + else + { + ASSERT(mShaderType == GL_FRAGMENT_SHADER || + mShaderType == GL_TESS_CONTROL_SHADER || + mShaderType == GL_TESS_EVALUATION_SHADER); + recordBuiltInVaryingUsed(symbol->variable(), &mPrimitiveIDAdded, + mInputVaryings); + } + break; + case EvqLayerOut: + if (mShaderType == GL_GEOMETRY_SHADER_EXT) + { + recordBuiltInVaryingUsed(symbol->variable(), &mLayerAdded, mOutputVaryings); + } + else + { + ASSERT(mShaderType == GL_VERTEX_SHADER && + (IsExtensionEnabled(mExtensionBehavior, TExtension::OVR_multiview2) || + IsExtensionEnabled(mExtensionBehavior, TExtension::OVR_multiview))); + } + break; + case EvqLayerIn: + ASSERT(mShaderType == GL_FRAGMENT_SHADER); + recordBuiltInVaryingUsed(symbol->variable(), &mLayerAdded, mInputVaryings); + break; + case EvqShared: + if (mShaderType == GL_COMPUTE_SHADER) + { + recordBuiltInVaryingUsed(symbol->variable(), &mSharedVariableAdded, + mSharedVariables); + } + break; + case EvqClipDistance: + recordBuiltInVaryingUsed( + symbol->variable(), &mClipDistanceAdded, + mShaderType == GL_FRAGMENT_SHADER ? mInputVaryings : mOutputVaryings); + return; + case EvqCullDistance: + recordBuiltInVaryingUsed( + symbol->variable(), &mCullDistanceAdded, + mShaderType == GL_FRAGMENT_SHADER ? mInputVaryings : mOutputVaryings); + return; + case EvqSampleID: + recordBuiltInVaryingUsed(symbol->variable(), &mSampleIDAdded, mInputVaryings); + return; + case EvqSamplePosition: + recordBuiltInVaryingUsed(symbol->variable(), &mSamplePositionAdded, mInputVaryings); + return; + case EvqSampleMaskIn: + recordBuiltInVaryingUsed(symbol->variable(), &mSampleMaskInAdded, mInputVaryings); + return; + case EvqSampleMask: + recordBuiltInFragmentOutputUsed(symbol->variable(), &mSampleMaskAdded); + return; + case EvqPatchVerticesIn: + recordBuiltInVaryingUsed(symbol->variable(), &mPatchVerticesInAdded, + mInputVaryings); + break; + case EvqTessCoord: + recordBuiltInVaryingUsed(symbol->variable(), &mTessCoordAdded, mInputVaryings); + break; + case EvqTessLevelOuter: + if (mShaderType == GL_TESS_CONTROL_SHADER) + { + recordBuiltInVaryingUsed(symbol->variable(), &mTessLevelOuterAdded, + mOutputVaryings); + } + else + { + ASSERT(mShaderType == GL_TESS_EVALUATION_SHADER); + recordBuiltInVaryingUsed(symbol->variable(), &mTessLevelOuterAdded, + mInputVaryings); + } + break; + case EvqTessLevelInner: + if (mShaderType == GL_TESS_CONTROL_SHADER) + { + recordBuiltInVaryingUsed(symbol->variable(), &mTessLevelInnerAdded, + mOutputVaryings); + } + else + { + ASSERT(mShaderType == GL_TESS_EVALUATION_SHADER); + recordBuiltInVaryingUsed(symbol->variable(), &mTessLevelInnerAdded, + mInputVaryings); + } + break; + case EvqBoundingBox: + recordBuiltInVaryingUsed(symbol->variable(), &mBoundingBoxAdded, mOutputVaryings); + break; + default: + break; + } + } + if (var) + { + MarkActive(var); + } +} + +void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type, + bool staticUse, + bool isShaderIOBlock, + bool isPatch, + ShaderVariable *variableOut) const +{ + ASSERT(variableOut); + + variableOut->staticUse = staticUse; + variableOut->isShaderIOBlock = isShaderIOBlock; + variableOut->isPatch = isPatch; + + const TStructure *structure = type.getStruct(); + const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); + if (structure) + { + // Structures use a NONE type that isn't exposed outside ANGLE. + variableOut->type = GL_NONE; + if (structure->symbolType() != SymbolType::Empty) + { + variableOut->structOrBlockName = structure->name().data(); + } + + const TFieldList &fields = structure->fields(); + + for (const TField *field : fields) + { + // Regardless of the variable type (uniform, in/out etc.) its fields are always plain + // ShaderVariable objects. + ShaderVariable fieldVariable; + setFieldProperties(*field->type(), field->name(), staticUse, isShaderIOBlock, isPatch, + field->symbolType(), &fieldVariable); + variableOut->fields.push_back(fieldVariable); + } + } + else if (interfaceBlock && isShaderIOBlock) + { + const bool isPerVertex = (interfaceBlock->name() == "gl_PerVertex"); + variableOut->type = GL_NONE; + if (interfaceBlock->symbolType() != SymbolType::Empty) + { + variableOut->structOrBlockName = interfaceBlock->name().data(); + variableOut->mappedStructOrBlockName = + isPerVertex ? interfaceBlock->name().data() + : HashName(interfaceBlock->name(), mHashFunction, nullptr).data(); + } + const TFieldList &fields = interfaceBlock->fields(); + for (const TField *field : fields) + { + ShaderVariable fieldVariable; + + setFieldProperties(*field->type(), field->name(), staticUse, true, isPatch, + field->symbolType(), &fieldVariable); + fieldVariable.isShaderIOBlock = true; + variableOut->fields.push_back(fieldVariable); + } + } + else + { + variableOut->type = GLVariableType(type); + variableOut->precision = GLVariablePrecision(type); + } + + const TSpan &arraySizes = type.getArraySizes(); + if (!arraySizes.empty()) + { + variableOut->arraySizes.assign(arraySizes.begin(), arraySizes.end()); + + if (arraySizes[0] == 0) + { + // Tessellation Control & Evaluation shader inputs: + // Declaring an array size is optional. If no size is specified, it will be taken from + // the implementation-dependent maximum patch size (gl_MaxPatchVertices). + if (type.getQualifier() == EvqTessControlIn || + type.getQualifier() == EvqTessEvaluationIn) + { + variableOut->arraySizes[0] = mResources.MaxPatchVertices; + } + + // Tessellation Control shader outputs: + // Declaring an array size is optional. If no size is specified, it will be taken from + // output patch size declared in the shader. + if (type.getQualifier() == EvqTessControlOut) + { + ASSERT(mTessControlShaderOutputVertices > 0); + variableOut->arraySizes[0] = mTessControlShaderOutputVertices; + } + } + } +} + +void CollectVariablesTraverser::setFieldProperties(const TType &type, + const ImmutableString &name, + bool staticUse, + bool isShaderIOBlock, + bool isPatch, + SymbolType symbolType, + ShaderVariable *variableOut) const +{ + ASSERT(variableOut); + setFieldOrVariableProperties(type, staticUse, isShaderIOBlock, isPatch, variableOut); + variableOut->name.assign(name.data(), name.length()); + variableOut->mappedName = (symbolType == SymbolType::BuiltIn) + ? name.data() + : HashName(name, mHashFunction, nullptr).data(); +} + +void CollectVariablesTraverser::setCommonVariableProperties(const TType &type, + const TVariable &variable, + ShaderVariable *variableOut) const +{ + ASSERT(variableOut); + ASSERT(type.getInterfaceBlock() == nullptr || IsShaderIoBlock(type.getQualifier()) || + type.getQualifier() == EvqPatchIn || type.getQualifier() == EvqPatchOut); + + const bool staticUse = mSymbolTable->isStaticallyUsed(variable); + const bool isShaderIOBlock = type.getInterfaceBlock() != nullptr; + const bool isPatch = type.getQualifier() == EvqPatchIn || type.getQualifier() == EvqPatchOut; + + setFieldOrVariableProperties(type, staticUse, isShaderIOBlock, isPatch, variableOut); + + const bool isNamed = variable.symbolType() != SymbolType::Empty; + + ASSERT(isNamed || isShaderIOBlock); + if (isNamed) + { + variableOut->name.assign(variable.name().data(), variable.name().length()); + variableOut->mappedName = getMappedName(&variable); + } + + // For I/O blocks, additionally store the name of the block as blockName. If the variable is + // unnamed, this name will be used instead for the purpose of interface matching. + if (isShaderIOBlock) + { + const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); + ASSERT(interfaceBlock); + + variableOut->structOrBlockName.assign(interfaceBlock->name().data(), + interfaceBlock->name().length()); + variableOut->mappedStructOrBlockName = + HashName(interfaceBlock->name(), mHashFunction, nullptr).data(); + variableOut->isShaderIOBlock = true; + } +} + +ShaderVariable CollectVariablesTraverser::recordAttribute(const TIntermSymbol &variable) const +{ + const TType &type = variable.getType(); + ASSERT(!type.getStruct()); + + ShaderVariable attribute; + setCommonVariableProperties(type, variable.variable(), &attribute); + + attribute.location = type.getLayoutQualifier().location; + return attribute; +} + +ShaderVariable CollectVariablesTraverser::recordOutputVariable(const TIntermSymbol &variable) const +{ + const TType &type = variable.getType(); + ASSERT(!type.getStruct()); + + ShaderVariable outputVariable; + setCommonVariableProperties(type, variable.variable(), &outputVariable); + + outputVariable.location = type.getLayoutQualifier().location; + outputVariable.index = type.getLayoutQualifier().index; + outputVariable.yuv = type.getLayoutQualifier().yuv; + return outputVariable; +} + +ShaderVariable CollectVariablesTraverser::recordVarying(const TIntermSymbol &variable) const +{ + const TType &type = variable.getType(); + + ShaderVariable varying; + setCommonVariableProperties(type, variable.variable(), &varying); + varying.location = type.getLayoutQualifier().location; + + switch (type.getQualifier()) + { + case EvqVaryingIn: + case EvqVaryingOut: + case EvqVertexOut: + case EvqSmoothOut: + case EvqFlatOut: + case EvqNoPerspectiveOut: + case EvqCentroidOut: + case EvqGeometryOut: + case EvqSampleOut: + if (mSymbolTable->isVaryingInvariant(variable.variable()) || type.isInvariant()) + { + varying.isInvariant = true; + } + break; + case EvqPatchIn: + case EvqPatchOut: + varying.isPatch = true; + break; + default: + break; + } + + varying.interpolation = GetInterpolationType(type.getQualifier()); + + // Shader I/O block properties + if (type.getBasicType() == EbtInterfaceBlock) + { + bool isBlockImplicitLocation = false; + int location = type.getLayoutQualifier().location; + + // when a interface has not location in layout, assign to the zero. + if (location < 0) + { + location = 0; + isBlockImplicitLocation = true; + } + + const TInterfaceBlock *blockType = type.getInterfaceBlock(); + ASSERT(blockType->fields().size() == varying.fields.size()); + + for (size_t fieldIndex = 0; fieldIndex < varying.fields.size(); ++fieldIndex) + { + const TField *blockField = blockType->fields()[fieldIndex]; + ShaderVariable &fieldVariable = varying.fields[fieldIndex]; + const TType &fieldType = *blockField->type(); + + fieldVariable.hasImplicitLocation = isBlockImplicitLocation; + fieldVariable.isPatch = varying.isPatch; + + int fieldLocation = fieldType.getLayoutQualifier().location; + if (fieldLocation >= 0) + { + fieldVariable.hasImplicitLocation = false; + fieldVariable.location = fieldLocation; + location = fieldLocation; + } + else + { + fieldVariable.location = location; + location += fieldType.getLocationCount(); + } + + if (fieldType.getQualifier() != EvqGlobal) + { + fieldVariable.interpolation = GetFieldInterpolationType(fieldType.getQualifier()); + } + } + } + + return varying; +} + +void CollectVariablesTraverser::recordInterfaceBlock(const char *instanceName, + const TType &interfaceBlockType, + InterfaceBlock *interfaceBlock) const +{ + ASSERT(interfaceBlockType.getBasicType() == EbtInterfaceBlock); + ASSERT(interfaceBlock); + + const TInterfaceBlock *blockType = interfaceBlockType.getInterfaceBlock(); + ASSERT(blockType); + + interfaceBlock->name = blockType->name().data(); + interfaceBlock->mappedName = getMappedName(blockType); + + const bool isGLInBuiltin = (instanceName != nullptr) && strncmp(instanceName, "gl_in", 5u) == 0; + if (instanceName != nullptr) + { + interfaceBlock->instanceName = instanceName; + const TSymbol *blockSymbol = nullptr; + if (isGLInBuiltin) + { + blockSymbol = mSymbolTable->getGlInVariableWithArraySize(); + } + else + { + blockSymbol = mSymbolTable->findGlobal(ImmutableString(instanceName)); + } + ASSERT(blockSymbol && blockSymbol->isVariable()); + interfaceBlock->staticUse = + mSymbolTable->isStaticallyUsed(*static_cast(blockSymbol)); + } + + ASSERT(!interfaceBlockType.isArrayOfArrays()); // Disallowed by GLSL ES 3.10 section 4.3.9 + interfaceBlock->arraySize = + interfaceBlockType.isArray() ? interfaceBlockType.getOutermostArraySize() : 0; + + interfaceBlock->blockType = GetBlockType(interfaceBlockType.getQualifier()); + if (interfaceBlock->blockType == BlockType::BLOCK_UNIFORM || + interfaceBlock->blockType == BlockType::BLOCK_BUFFER) + { + // TODO(oetuaho): Remove setting isRowMajorLayout. + interfaceBlock->isRowMajorLayout = false; + interfaceBlock->binding = blockType->blockBinding(); + interfaceBlock->layout = GetBlockLayoutType(blockType->blockStorage()); + } + + // Gather field information + bool anyFieldStaticallyUsed = false; + + for (const TField *field : blockType->fields()) + { + const TType &fieldType = *field->type(); + + bool staticUse = false; + if (instanceName == nullptr) + { + // Static use of individual fields has been recorded, since they are present in the + // symbol table as variables. + const TSymbol *fieldSymbol = mSymbolTable->findGlobal(field->name()); + ASSERT(fieldSymbol && fieldSymbol->isVariable()); + staticUse = + mSymbolTable->isStaticallyUsed(*static_cast(fieldSymbol)); + if (staticUse) + { + anyFieldStaticallyUsed = true; + } + } + + ShaderVariable fieldVariable; + setFieldProperties(fieldType, field->name(), staticUse, false, false, field->symbolType(), + &fieldVariable); + fieldVariable.isRowMajorLayout = + (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor); + interfaceBlock->fields.push_back(fieldVariable); + } + if (anyFieldStaticallyUsed) + { + interfaceBlock->staticUse = true; + } +} + +ShaderVariable CollectVariablesTraverser::recordUniform(const TIntermSymbol &variable) const +{ + ShaderVariable uniform; + setCommonVariableProperties(variable.getType(), variable.variable(), &uniform); + uniform.binding = variable.getType().getLayoutQualifier().binding; + uniform.imageUnitFormat = + GetImageInternalFormatType(variable.getType().getLayoutQualifier().imageInternalFormat); + uniform.location = variable.getType().getLayoutQualifier().location; + uniform.offset = variable.getType().getLayoutQualifier().offset; + uniform.rasterOrdered = variable.getType().getLayoutQualifier().rasterOrdered; + uniform.readonly = variable.getType().getMemoryQualifier().readonly; + uniform.writeonly = variable.getType().getMemoryQualifier().writeonly; + return uniform; +} + +bool CollectVariablesTraverser::visitDeclaration(Visit, TIntermDeclaration *node) +{ + const TIntermSequence &sequence = *(node->getSequence()); + ASSERT(!sequence.empty()); + + const TIntermTyped &typedNode = *(sequence.front()->getAsTyped()); + TQualifier qualifier = typedNode.getQualifier(); + + bool isShaderVariable = qualifier == EvqAttribute || qualifier == EvqVertexIn || + qualifier == EvqFragmentOut || qualifier == EvqFragmentInOut || + qualifier == EvqUniform || IsVarying(qualifier); + + if (typedNode.getBasicType() != EbtInterfaceBlock && !isShaderVariable) + { + return true; + } + + for (TIntermNode *variableNode : sequence) + { + // The only case in which the sequence will not contain a TIntermSymbol node is + // initialization. It will contain a TInterBinary node in that case. Since attributes, + // uniforms, varyings, outputs and interface blocks cannot be initialized in a shader, we + // must have only TIntermSymbol nodes in the sequence in the cases we are interested in. + const TIntermSymbol &variable = *variableNode->getAsSymbolNode(); + if (variable.variable().symbolType() == SymbolType::AngleInternal) + { + // Internal variables are not collected. + continue; + } + + // SpirvTransformer::transform uses a map of ShaderVariables, it needs member variables and + // (named or unnamed) structure as ShaderVariable. at link between two shaders, validation + // between of named and unnamed, needs the same structure, its members, and members order + // except instance name. + if (typedNode.getBasicType() == EbtInterfaceBlock && !IsShaderIoBlock(qualifier) && + qualifier != EvqPatchIn && qualifier != EvqPatchOut) + { + InterfaceBlock interfaceBlock; + bool isUnnamed = variable.variable().symbolType() == SymbolType::Empty; + const TType &type = variable.getType(); + recordInterfaceBlock(isUnnamed ? nullptr : variable.getName().data(), type, + &interfaceBlock); + + // all fields in interface block will be added for updating interface variables because + // the temporal structure variable will be ignored. + switch (qualifier) + { + case EvqUniform: + mUniformBlocks->push_back(interfaceBlock); + break; + case EvqBuffer: + mShaderStorageBlocks->push_back(interfaceBlock); + break; + default: + UNREACHABLE(); + } + } + else + { + ASSERT(variable.variable().symbolType() != SymbolType::Empty || + IsShaderIoBlock(qualifier) || qualifier == EvqPatchIn || + qualifier == EvqPatchOut); + switch (qualifier) + { + case EvqAttribute: + case EvqVertexIn: + mAttribs->push_back(recordAttribute(variable)); + break; + case EvqFragmentOut: + case EvqFragmentInOut: + mOutputVariables->push_back(recordOutputVariable(variable)); + break; + case EvqUniform: + mUniforms->push_back(recordUniform(variable)); + break; + default: + if (IsVaryingIn(qualifier)) + { + mInputVaryings->push_back(recordVarying(variable)); + } + else + { + ASSERT(IsVaryingOut(qualifier)); + mOutputVaryings->push_back(recordVarying(variable)); + } + break; + } + } + } + + // None of the recorded variables can have initializers, so we don't need to traverse the + // declarators. + return false; +} + +InterfaceBlock *CollectVariablesTraverser::findNamedInterfaceBlock( + const ImmutableString &blockName) const +{ + InterfaceBlock *namedBlock = FindVariable(blockName, mUniformBlocks); + if (!namedBlock) + { + namedBlock = FindVariable(blockName, mShaderStorageBlocks); + } + return namedBlock; +} + +bool CollectVariablesTraverser::visitBinary(Visit, TIntermBinary *binaryNode) +{ + if (binaryNode->getOp() == EOpIndexDirectInterfaceBlock) + { + // NOTE: we do not determine static use / activeness for individual blocks of an array. + TIntermTyped *blockNode = binaryNode->getLeft()->getAsTyped(); + ASSERT(blockNode); + + TIntermConstantUnion *constantUnion = binaryNode->getRight()->getAsConstantUnion(); + ASSERT(constantUnion); + + InterfaceBlock *namedBlock = nullptr; + + bool traverseIndexExpression = false; + TIntermBinary *interfaceIndexingNode = blockNode->getAsBinaryNode(); + if (interfaceIndexingNode) + { + ASSERT(interfaceIndexingNode->getOp() == EOpIndexDirect || + interfaceIndexingNode->getOp() == EOpIndexIndirect); + traverseIndexExpression = true; + blockNode = interfaceIndexingNode->getLeft(); + } + + const TType &interfaceNodeType = blockNode->getType(); + const TInterfaceBlock *interfaceBlock = interfaceNodeType.getInterfaceBlock(); + const TQualifier qualifier = interfaceNodeType.getQualifier(); + + // If it's a shader I/O block, look in varyings + ShaderVariable *ioBlockVar = nullptr; + if (qualifier == EvqPerVertexIn) + { + TIntermSymbol *symbolNode = blockNode->getAsSymbolNode(); + ASSERT(symbolNode); + recordBuiltInVaryingUsed(symbolNode->variable(), &mPerVertexInAdded, mInputVaryings); + ioBlockVar = FindShaderIOBlockVariable(interfaceBlock->name(), mInputVaryings); + } + else if (IsVaryingIn(qualifier)) + { + ioBlockVar = FindShaderIOBlockVariable(interfaceBlock->name(), mInputVaryings); + } + else if (qualifier == EvqPerVertexOut) + { + TIntermSymbol *symbolNode = blockNode->getAsSymbolNode(); + ASSERT(symbolNode); + recordBuiltInVaryingUsed(symbolNode->variable(), &mPerVertexOutAdded, mOutputVaryings); + ioBlockVar = FindShaderIOBlockVariable(interfaceBlock->name(), mOutputVaryings); + } + else if (IsVaryingOut(qualifier)) + { + ioBlockVar = FindShaderIOBlockVariable(interfaceBlock->name(), mOutputVaryings); + } + + if (ioBlockVar) + { + MarkActive(ioBlockVar); + } + else + { + if (!namedBlock) + { + namedBlock = findNamedInterfaceBlock(interfaceBlock->name()); + } + ASSERT(namedBlock); + ASSERT(namedBlock->staticUse); + namedBlock->active = true; + unsigned int fieldIndex = static_cast(constantUnion->getIConst(0)); + ASSERT(fieldIndex < namedBlock->fields.size()); + // TODO(oetuaho): Would be nicer to record static use of fields of named interface + // blocks more accurately at parse time - now we only mark the fields statically used if + // they are active. http://anglebug.com/2440 We need to mark this field and all of its + // sub-fields, as static/active + MarkActive(&namedBlock->fields[fieldIndex]); + } + + if (traverseIndexExpression) + { + ASSERT(interfaceIndexingNode); + interfaceIndexingNode->getRight()->traverse(this); + } + return false; + } + + return true; +} + +} // anonymous namespace + +void CollectVariables(TIntermBlock *root, + std::vector *attributes, + std::vector *outputVariables, + std::vector *uniforms, + std::vector *inputVaryings, + std::vector *outputVaryings, + std::vector *sharedVariables, + std::vector *uniformBlocks, + std::vector *shaderStorageBlocks, + ShHashFunction64 hashFunction, + TSymbolTable *symbolTable, + GLenum shaderType, + const TExtensionBehavior &extensionBehavior, + const ShBuiltInResources &resources, + int tessControlShaderOutputVertices) +{ + CollectVariablesTraverser collect( + attributes, outputVariables, uniforms, inputVaryings, outputVaryings, sharedVariables, + uniformBlocks, shaderStorageBlocks, hashFunction, symbolTable, shaderType, + extensionBehavior, resources, tessControlShaderOutputVertices); + root->traverse(&collect); +} + +} // namespace sh -- cgit v1.2.3