| // | |
| // 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; | |
| } | |
| } | |
| // TODO(jiawei.shao@intel.com): implement GL_EXT_shader_io_blocks. | |
| BlockType GetBlockType(TQualifier qualifier) | |
| { | |
| switch (qualifier) | |
| { | |
| case EvqUniform: | |
| return BlockType::BLOCK_UNIFORM; | |
| case EvqBuffer: | |
| return BlockType::BLOCK_BUFFER; | |
| case EvqPerVertexIn: | |
| return BlockType::BLOCK_IN; | |
| default: | |
| UNREACHABLE(); | |
| return BlockType::BLOCK_UNIFORM; | |
| } | |
| } | |
| template <class VarT> | |
| VarT *FindVariable(const ImmutableString &name, std::vector<VarT> *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<InterfaceBlock> *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); | |
| } | |
| // 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<ShaderVariable> *attribs, | |
| std::vector<ShaderVariable> *outputVariables, | |
| std::vector<ShaderVariable> *uniforms, | |
| std::vector<ShaderVariable> *inputVaryings, | |
| std::vector<ShaderVariable> *outputVaryings, | |
| std::vector<ShaderVariable> *sharedVariables, | |
| std::vector<InterfaceBlock> *uniformBlocks, | |
| std::vector<InterfaceBlock> *shaderStorageBlocks, | |
| std::vector<InterfaceBlock> *inBlocks, | |
| ShHashFunction64 hashFunction, | |
| TSymbolTable *symbolTable, | |
| GLenum shaderType, | |
| const TExtensionBehavior &extensionBehavior); | |
| 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, | |
| ShaderVariable *variableOut) const; | |
| void setFieldProperties(const TType &type, | |
| const ImmutableString &name, | |
| bool staticUse, | |
| 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<ShaderVariable> *varyings); | |
| void recordBuiltInFragmentOutputUsed(const TVariable &variable, bool *addedFlag); | |
| void recordBuiltInAttributeUsed(const TVariable &variable, bool *addedFlag); | |
| InterfaceBlock *recordGLInUsed(const TType &glInType); | |
| InterfaceBlock *findNamedInterfaceBlock(const ImmutableString &name) const; | |
| std::vector<ShaderVariable> *mAttribs; | |
| std::vector<ShaderVariable> *mOutputVariables; | |
| std::vector<ShaderVariable> *mUniforms; | |
| std::vector<ShaderVariable> *mInputVaryings; | |
| std::vector<ShaderVariable> *mOutputVaryings; | |
| std::vector<ShaderVariable> *mSharedVariables; | |
| std::vector<InterfaceBlock> *mUniformBlocks; | |
| std::vector<InterfaceBlock> *mShaderStorageBlocks; | |
| std::vector<InterfaceBlock> *mInBlocks; | |
| std::map<std::string, ShaderVariable *> mInterfaceBlockFields; | |
| // Shader uniforms | |
| bool mDepthRangeAdded; | |
| // Compute Shader builtins | |
| bool mNumWorkGroupsAdded; | |
| bool mWorkGroupIDAdded; | |
| bool mLocalInvocationIDAdded; | |
| bool mGlobalInvocationIDAdded; | |
| bool mLocalInvocationIndexAdded; | |
| // Vertex Shader builtins | |
| bool mInstanceIDAdded; | |
| bool mVertexIDAdded; | |
| bool mPointSizeAdded; | |
| bool mDrawIDAdded; | |
| bool mBaseVertexAdded; | |
| bool mBaseInstanceAdded; | |
| // Vertex Shader and Geometry Shader builtins | |
| bool mPositionAdded; | |
| // Fragment Shader builtins | |
| bool mPointCoordAdded; | |
| bool mFrontFacingAdded; | |
| bool mHelperInvocationAdded; | |
| bool mFragCoordAdded; | |
| bool mLastFragDataAdded; | |
| bool mFragColorAdded; | |
| bool mFragDataAdded; | |
| bool mFragDepthEXTAdded; | |
| bool mFragDepthAdded; | |
| bool mSecondaryFragColorEXTAdded; | |
| bool mSecondaryFragDataEXTAdded; | |
| // Geometry Shader builtins | |
| bool mPerVertexInAdded; | |
| bool mPrimitiveIDInAdded; | |
| bool mInvocationIDAdded; | |
| // Geometry Shader and Fragment Shader builtins | |
| bool mPrimitiveIDAdded; | |
| bool mLayerAdded; | |
| // Shared memory variables | |
| bool mSharedVariableAdded; | |
| ShHashFunction64 mHashFunction; | |
| GLenum mShaderType; | |
| const TExtensionBehavior &mExtensionBehavior; | |
| }; | |
| CollectVariablesTraverser::CollectVariablesTraverser( | |
| std::vector<sh::ShaderVariable> *attribs, | |
| std::vector<sh::ShaderVariable> *outputVariables, | |
| std::vector<sh::ShaderVariable> *uniforms, | |
| std::vector<sh::ShaderVariable> *inputVaryings, | |
| std::vector<sh::ShaderVariable> *outputVaryings, | |
| std::vector<sh::ShaderVariable> *sharedVariables, | |
| std::vector<sh::InterfaceBlock> *uniformBlocks, | |
| std::vector<sh::InterfaceBlock> *shaderStorageBlocks, | |
| std::vector<sh::InterfaceBlock> *inBlocks, | |
| ShHashFunction64 hashFunction, | |
| TSymbolTable *symbolTable, | |
| GLenum shaderType, | |
| const TExtensionBehavior &extensionBehavior) | |
| : TIntermTraverser(true, false, false, symbolTable), | |
| mAttribs(attribs), | |
| mOutputVariables(outputVariables), | |
| mUniforms(uniforms), | |
| mInputVaryings(inputVaryings), | |
| mOutputVaryings(outputVaryings), | |
| mSharedVariables(sharedVariables), | |
| mUniformBlocks(uniformBlocks), | |
| mShaderStorageBlocks(shaderStorageBlocks), | |
| mInBlocks(inBlocks), | |
| mDepthRangeAdded(false), | |
| mNumWorkGroupsAdded(false), | |
| mWorkGroupIDAdded(false), | |
| mLocalInvocationIDAdded(false), | |
| mGlobalInvocationIDAdded(false), | |
| mLocalInvocationIndexAdded(false), | |
| mInstanceIDAdded(false), | |
| mVertexIDAdded(false), | |
| mPointSizeAdded(false), | |
| mDrawIDAdded(false), | |
| mBaseVertexAdded(false), | |
| mBaseInstanceAdded(false), | |
| mPositionAdded(false), | |
| mPointCoordAdded(false), | |
| mFrontFacingAdded(false), | |
| mHelperInvocationAdded(false), | |
| mFragCoordAdded(false), | |
| mLastFragDataAdded(false), | |
| mFragColorAdded(false), | |
| mFragDataAdded(false), | |
| mFragDepthEXTAdded(false), | |
| mFragDepthAdded(false), | |
| mSecondaryFragColorEXTAdded(false), | |
| mSecondaryFragDataEXTAdded(false), | |
| mPerVertexInAdded(false), | |
| mPrimitiveIDInAdded(false), | |
| mInvocationIDAdded(false), | |
| mPrimitiveIDAdded(false), | |
| mLayerAdded(false), | |
| mSharedVariableAdded(false), | |
| mHashFunction(hashFunction), | |
| mShaderType(shaderType), | |
| mExtensionBehavior(extensionBehavior) | |
| {} | |
| 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(); | |
| setFieldOrVariableProperties(type, true, info); | |
| } | |
| void CollectVariablesTraverser::recordBuiltInVaryingUsed(const TVariable &variable, | |
| bool *addedFlag, | |
| std::vector<ShaderVariable> *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; | |
| } | |
| } | |
| InterfaceBlock *CollectVariablesTraverser::recordGLInUsed(const TType &glInType) | |
| { | |
| if (!mPerVertexInAdded) | |
| { | |
| ASSERT(glInType.getQualifier() == EvqPerVertexIn); | |
| InterfaceBlock info; | |
| recordInterfaceBlock("gl_in", glInType, &info); | |
| mPerVertexInAdded = true; | |
| mInBlocks->push_back(info); | |
| return &mInBlocks->back(); | |
| } | |
| else | |
| { | |
| return FindVariable(ImmutableString("gl_PerVertex"), mInBlocks); | |
| } | |
| } | |
| bool CollectVariablesTraverser::visitGlobalQualifierDeclaration( | |
| Visit visit, | |
| TIntermGlobalQualifierDeclaration *node) | |
| { | |
| // We should not mark variables as active just based on an invariant 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(); | |
| if (IsVaryingIn(qualifier)) | |
| { | |
| var = FindVariable(symbolName, mInputVaryings); | |
| } | |
| else if (IsVaryingOut(qualifier)) | |
| { | |
| 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 | |
| { | |
| switch (qualifier) | |
| { | |
| case EvqAttribute: | |
| case EvqVertexIn: | |
| var = FindVariable(symbolName, mAttribs); | |
| break; | |
| case EvqFragmentOut: | |
| var = FindVariable(symbolName, mOutputVariables); | |
| break; | |
| case EvqUniform: | |
| { | |
| const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock(); | |
| if (interfaceBlock) | |
| { | |
| var = FindVariableInInterfaceBlock(symbolName, interfaceBlock, mUniformBlocks); | |
| } | |
| else | |
| { | |
| var = FindVariable(symbolName, mUniforms); | |
| } | |
| // It's an internal error to reference an undefined user uniform | |
| ASSERT(!symbolName.beginsWith("gl_") || var); | |
| } | |
| break; | |
| case EvqBuffer: | |
| { | |
| const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock(); | |
| 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 SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW 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 EvqBaseVertex: | |
| recordBuiltInAttributeUsed(symbol->variable(), &mBaseVertexAdded); | |
| return; | |
| case EvqBaseInstance: | |
| recordBuiltInAttributeUsed(symbol->variable(), &mBaseInstanceAdded); | |
| return; | |
| case EvqLastFragData: | |
| recordBuiltInVaryingUsed(symbol->variable(), &mLastFragDataAdded, mInputVaryings); | |
| return; | |
| case EvqFragColor: | |
| recordBuiltInFragmentOutputUsed(symbol->variable(), &mFragColorAdded); | |
| return; | |
| case EvqFragData: | |
| if (!mFragDataAdded) | |
| { | |
| ShaderVariable info; | |
| setBuiltInInfoFromSymbol(symbol->variable(), &info); | |
| if (!IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_draw_buffers)) | |
| { | |
| ASSERT(info.arraySizes.size() == 1u); | |
| info.arraySizes.back() = 1u; | |
| } | |
| info.active = true; | |
| mOutputVariables->push_back(info); | |
| mFragDataAdded = true; | |
| } | |
| return; | |
| case EvqFragDepthEXT: | |
| recordBuiltInFragmentOutputUsed(symbol->variable(), &mFragDepthEXTAdded); | |
| 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); | |
| recordBuiltInVaryingUsed(symbol->variable(), &mPrimitiveIDAdded, | |
| mInputVaryings); | |
| } | |
| break; | |
| case EvqLayer: | |
| if (mShaderType == GL_GEOMETRY_SHADER_EXT) | |
| { | |
| recordBuiltInVaryingUsed(symbol->variable(), &mLayerAdded, mOutputVaryings); | |
| } | |
| else if (mShaderType == GL_FRAGMENT_SHADER) | |
| { | |
| recordBuiltInVaryingUsed(symbol->variable(), &mLayerAdded, mInputVaryings); | |
| } | |
| else | |
| { | |
| ASSERT(mShaderType == GL_VERTEX_SHADER && | |
| (IsExtensionEnabled(mExtensionBehavior, TExtension::OVR_multiview2) || | |
| IsExtensionEnabled(mExtensionBehavior, TExtension::OVR_multiview))); | |
| } | |
| break; | |
| case EvqShared: | |
| if (mShaderType == GL_COMPUTE_SHADER) | |
| { | |
| recordBuiltInVaryingUsed(symbol->variable(), &mSharedVariableAdded, | |
| mSharedVariables); | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| if (var) | |
| { | |
| MarkActive(var); | |
| } | |
| } | |
| void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type, | |
| bool staticUse, | |
| ShaderVariable *variableOut) const | |
| { | |
| ASSERT(variableOut); | |
| variableOut->staticUse = staticUse; | |
| const TStructure *structure = type.getStruct(); | |
| if (!structure) | |
| { | |
| variableOut->type = GLVariableType(type); | |
| variableOut->precision = GLVariablePrecision(type); | |
| } | |
| else | |
| { | |
| // Structures use a NONE type that isn't exposed outside ANGLE. | |
| variableOut->type = GL_NONE; | |
| if (structure->symbolType() != SymbolType::Empty) | |
| { | |
| variableOut->structName = 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, &fieldVariable); | |
| variableOut->fields.push_back(fieldVariable); | |
| } | |
| } | |
| if (auto *arraySizes = type.getArraySizes()) | |
| { | |
| variableOut->arraySizes.assign(arraySizes->begin(), arraySizes->end()); | |
| } | |
| } | |
| void CollectVariablesTraverser::setFieldProperties(const TType &type, | |
| const ImmutableString &name, | |
| bool staticUse, | |
| ShaderVariable *variableOut) const | |
| { | |
| ASSERT(variableOut); | |
| setFieldOrVariableProperties(type, staticUse, variableOut); | |
| variableOut->name.assign(name.data(), name.length()); | |
| variableOut->mappedName = HashName(name, mHashFunction, nullptr).data(); | |
| } | |
| void CollectVariablesTraverser::setCommonVariableProperties(const TType &type, | |
| const TVariable &variable, | |
| ShaderVariable *variableOut) const | |
| { | |
| ASSERT(variableOut); | |
| variableOut->staticUse = mSymbolTable->isStaticallyUsed(variable); | |
| setFieldOrVariableProperties(type, variableOut->staticUse, variableOut); | |
| ASSERT(variable.symbolType() != SymbolType::Empty); | |
| variableOut->name.assign(variable.name().data(), variable.name().length()); | |
| variableOut->mappedName = getMappedName(&variable); | |
| } | |
| 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; | |
| 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 EvqCentroidOut: | |
| case EvqGeometryOut: | |
| if (mSymbolTable->isVaryingInvariant(variable.variable()) || type.isInvariant()) | |
| { | |
| varying.isInvariant = true; | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| varying.interpolation = GetInterpolationType(type.getQualifier()); | |
| return varying; | |
| } | |
| // TODO(jiawei.shao@intel.com): implement GL_EXT_shader_io_blocks. | |
| 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); | |
| if (instanceName != nullptr) | |
| { | |
| interfaceBlock->instanceName = instanceName; | |
| const TSymbol *blockSymbol = nullptr; | |
| if (strncmp(instanceName, "gl_in", 5u) == 0) | |
| { | |
| blockSymbol = mSymbolTable->getGlInVariableWithArraySize(); | |
| } | |
| else | |
| { | |
| blockSymbol = mSymbolTable->findGlobal(ImmutableString(instanceName)); | |
| } | |
| ASSERT(blockSymbol && blockSymbol->isVariable()); | |
| interfaceBlock->staticUse = | |
| mSymbolTable->isStaticallyUsed(*static_cast<const TVariable *>(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<const TVariable *>(fieldSymbol)); | |
| if (staticUse) | |
| { | |
| anyFieldStaticallyUsed = true; | |
| } | |
| } | |
| ShaderVariable fieldVariable; | |
| setFieldProperties(fieldType, field->name(), staticUse, &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.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 == 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; | |
| } | |
| // TODO(jiawei.shao@intel.com): implement GL_EXT_shader_io_blocks. | |
| if (typedNode.getBasicType() == EbtInterfaceBlock) | |
| { | |
| InterfaceBlock interfaceBlock; | |
| recordInterfaceBlock(variable.variable().symbolType() != SymbolType::Empty | |
| ? variable.getName().data() | |
| : nullptr, | |
| variable.getType(), &interfaceBlock); | |
| 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); | |
| switch (qualifier) | |
| { | |
| case EvqAttribute: | |
| case EvqVertexIn: | |
| mAttribs->push_back(recordAttribute(variable)); | |
| break; | |
| case EvqFragmentOut: | |
| 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; | |
| } | |
| // TODO(jiawei.shao@intel.com): add search on mInBlocks and mOutBlocks when implementing | |
| // GL_EXT_shader_io_blocks. | |
| 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) | |
| { | |
| TIntermTyped *interfaceNode = interfaceIndexingNode->getLeft()->getAsTyped(); | |
| ASSERT(interfaceNode); | |
| const TType &interfaceType = interfaceNode->getType(); | |
| if (interfaceType.getQualifier() == EvqPerVertexIn) | |
| { | |
| namedBlock = recordGLInUsed(interfaceType); | |
| ASSERT(namedBlock); | |
| // We need to continue traversing to collect useful variables in the index | |
| // expression of gl_in. | |
| traverseIndexExpression = true; | |
| } | |
| } | |
| const TInterfaceBlock *interfaceBlock = blockNode->getType().getInterfaceBlock(); | |
| if (!namedBlock) | |
| { | |
| namedBlock = findNamedInterfaceBlock(interfaceBlock->name()); | |
| } | |
| ASSERT(namedBlock); | |
| ASSERT(namedBlock->staticUse); | |
| namedBlock->active = true; | |
| unsigned int fieldIndex = static_cast<unsigned int>(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<ShaderVariable> *attributes, | |
| std::vector<ShaderVariable> *outputVariables, | |
| std::vector<ShaderVariable> *uniforms, | |
| std::vector<ShaderVariable> *inputVaryings, | |
| std::vector<ShaderVariable> *outputVaryings, | |
| std::vector<ShaderVariable> *sharedVariables, | |
| std::vector<InterfaceBlock> *uniformBlocks, | |
| std::vector<InterfaceBlock> *shaderStorageBlocks, | |
| std::vector<InterfaceBlock> *inBlocks, | |
| ShHashFunction64 hashFunction, | |
| TSymbolTable *symbolTable, | |
| GLenum shaderType, | |
| const TExtensionBehavior &extensionBehavior) | |
| { | |
| CollectVariablesTraverser collect(attributes, outputVariables, uniforms, inputVaryings, | |
| outputVaryings, sharedVariables, uniformBlocks, | |
| shaderStorageBlocks, inBlocks, hashFunction, symbolTable, | |
| shaderType, extensionBehavior); | |
| root->traverse(&collect); | |
| } | |
| } // namespace sh |