// // Copyright 2019 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. // // RemoveInactiveInterfaceVariables.h: // Drop shader interface variable declarations for those that are inactive. // #include "compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.h" #include "compiler/translator/SymbolTable.h" #include "compiler/translator/tree_util/IntermTraverse.h" #include "compiler/translator/util.h" namespace sh { namespace { // Traverser that removes all declarations that correspond to inactive variables. class RemoveInactiveInterfaceVariablesTraverser : public TIntermTraverser { public: RemoveInactiveInterfaceVariablesTraverser( TSymbolTable *symbolTable, const std::vector &attributes, const std::vector &inputVaryings, const std::vector &outputVariables, const std::vector &uniforms, const std::vector &interfaceBlocks, bool removeFragmentOutputs); bool visitDeclaration(Visit visit, TIntermDeclaration *node) override; bool visitBinary(Visit visit, TIntermBinary *node) override; private: const std::vector &mAttributes; const std::vector &mInputVaryings; const std::vector &mOutputVariables; const std::vector &mUniforms; const std::vector &mInterfaceBlocks; bool mRemoveFragmentOutputs; }; RemoveInactiveInterfaceVariablesTraverser::RemoveInactiveInterfaceVariablesTraverser( TSymbolTable *symbolTable, const std::vector &attributes, const std::vector &inputVaryings, const std::vector &outputVariables, const std::vector &uniforms, const std::vector &interfaceBlocks, bool removeFragmentOutputs) : TIntermTraverser(true, false, false, symbolTable), mAttributes(attributes), mInputVaryings(inputVaryings), mOutputVariables(outputVariables), mUniforms(uniforms), mInterfaceBlocks(interfaceBlocks), mRemoveFragmentOutputs(removeFragmentOutputs) {} template bool IsVariableActive(const std::vector &mVars, const ImmutableString &name) { for (const Variable &var : mVars) { if (name == var.name) { return var.active; } } UNREACHABLE(); return true; } bool RemoveInactiveInterfaceVariablesTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node) { // SeparateDeclarations should have already been run. ASSERT(node->getSequence()->size() == 1u); TIntermTyped *declarator = node->getSequence()->front()->getAsTyped(); ASSERT(declarator); TIntermSymbol *asSymbol = declarator->getAsSymbolNode(); if (!asSymbol) { return false; } const TType &type = declarator->getType(); // Remove all shader interface variables except outputs, i.e. uniforms, interface blocks and // inputs. // // Imagine a situation where the VS doesn't write to a varying but the FS reads from it. This // is allowed, though the value of the varying is undefined. If the varying is removed here, // the situation is changed to VS not declaring the varying, but the FS reading from it, which // is not allowed. That's why inactive shader outputs are not removed. // // Inactive fragment shader outputs can be removed though, as there is no next stage. bool removeDeclaration = false; const TQualifier qualifier = type.getQualifier(); if (type.isInterfaceBlock()) { // When a member has an explicit location, interface block should not be removed. // If the member or interface would be removed, GetProgramResource could not return the // location. if (!IsShaderIoBlock(type.getQualifier()) && type.getQualifier() != EvqPatchIn && type.getQualifier() != EvqPatchOut) { removeDeclaration = !IsVariableActive(mInterfaceBlocks, type.getInterfaceBlock()->name()); } } else if (qualifier == EvqUniform) { removeDeclaration = !IsVariableActive(mUniforms, asSymbol->getName()); } else if (qualifier == EvqAttribute || qualifier == EvqVertexIn) { removeDeclaration = !IsVariableActive(mAttributes, asSymbol->getName()); } else if (IsShaderIn(qualifier)) { removeDeclaration = !IsVariableActive(mInputVaryings, asSymbol->getName()); } else if (qualifier == EvqFragmentOut) { removeDeclaration = !IsVariableActive(mOutputVariables, asSymbol->getName()) && mRemoveFragmentOutputs; } if (removeDeclaration) { TIntermSequence replacement; // If the declaration was of a struct, keep the struct declaration itself. if (type.isStructSpecifier()) { TType *structSpecifierType = new TType(type.getStruct(), true); TVariable *emptyVariable = new TVariable(mSymbolTable, kEmptyImmutableString, structSpecifierType, SymbolType::Empty); TIntermDeclaration *declaration = new TIntermDeclaration(); declaration->appendDeclarator(new TIntermSymbol(emptyVariable)); replacement.push_back(declaration); } mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node, std::move(replacement)); } return false; } bool RemoveInactiveInterfaceVariablesTraverser::visitBinary(Visit visit, TIntermBinary *node) { // Remove any code that initOutputVariables might have added corresponding to inactive // output variables. This code is always in the form of `variable = ...;`. if (node->getOp() != EOpAssign) { // Don't recurse, won't find the initialization nested in another expression. return false; } // Get the symbol being initialized, and check if it's an inactive output. If it is, this must // necessarily be initialization code that ANGLE has added (and wasn't there in the original // shader; if it was, the symbol wouldn't have been inactive). TIntermSymbol *symbol = node->getLeft()->getAsSymbolNode(); if (symbol == nullptr) { return false; } const TQualifier qualifier = symbol->getType().getQualifier(); if (qualifier != EvqFragmentOut || IsVariableActive(mOutputVariables, symbol->getName())) { return false; } // Drop the initialization code. TIntermSequence replacement; mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node, std::move(replacement)); return false; } } // namespace bool RemoveInactiveInterfaceVariables(TCompiler *compiler, TIntermBlock *root, TSymbolTable *symbolTable, const std::vector &attributes, const std::vector &inputVaryings, const std::vector &outputVariables, const std::vector &uniforms, const std::vector &interfaceBlocks, bool removeFragmentOutputs) { RemoveInactiveInterfaceVariablesTraverser traverser(symbolTable, attributes, inputVaryings, outputVariables, uniforms, interfaceBlocks, removeFragmentOutputs); root->traverse(&traverser); return traverser.updateTree(compiler, root); } } // namespace sh