RemoveInactiveInterfaceVariables.cpp (8143B)
1 // 2 // Copyright 2019 The ANGLE Project Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE file. 5 // 6 // RemoveInactiveInterfaceVariables.h: 7 // Drop shader interface variable declarations for those that are inactive. 8 // 9 10 #include "compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.h" 11 12 #include "compiler/translator/SymbolTable.h" 13 #include "compiler/translator/tree_util/IntermTraverse.h" 14 #include "compiler/translator/util.h" 15 16 namespace sh 17 { 18 19 namespace 20 { 21 22 // Traverser that removes all declarations that correspond to inactive variables. 23 class RemoveInactiveInterfaceVariablesTraverser : public TIntermTraverser 24 { 25 public: 26 RemoveInactiveInterfaceVariablesTraverser( 27 TSymbolTable *symbolTable, 28 const std::vector<sh::ShaderVariable> &attributes, 29 const std::vector<sh::ShaderVariable> &inputVaryings, 30 const std::vector<sh::ShaderVariable> &outputVariables, 31 const std::vector<sh::ShaderVariable> &uniforms, 32 const std::vector<sh::InterfaceBlock> &interfaceBlocks, 33 bool removeFragmentOutputs); 34 35 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override; 36 bool visitBinary(Visit visit, TIntermBinary *node) override; 37 38 private: 39 const std::vector<sh::ShaderVariable> &mAttributes; 40 const std::vector<sh::ShaderVariable> &mInputVaryings; 41 const std::vector<sh::ShaderVariable> &mOutputVariables; 42 const std::vector<sh::ShaderVariable> &mUniforms; 43 const std::vector<sh::InterfaceBlock> &mInterfaceBlocks; 44 bool mRemoveFragmentOutputs; 45 }; 46 47 RemoveInactiveInterfaceVariablesTraverser::RemoveInactiveInterfaceVariablesTraverser( 48 TSymbolTable *symbolTable, 49 const std::vector<sh::ShaderVariable> &attributes, 50 const std::vector<sh::ShaderVariable> &inputVaryings, 51 const std::vector<sh::ShaderVariable> &outputVariables, 52 const std::vector<sh::ShaderVariable> &uniforms, 53 const std::vector<sh::InterfaceBlock> &interfaceBlocks, 54 bool removeFragmentOutputs) 55 : TIntermTraverser(true, false, false, symbolTable), 56 mAttributes(attributes), 57 mInputVaryings(inputVaryings), 58 mOutputVariables(outputVariables), 59 mUniforms(uniforms), 60 mInterfaceBlocks(interfaceBlocks), 61 mRemoveFragmentOutputs(removeFragmentOutputs) 62 {} 63 64 template <typename Variable> 65 bool IsVariableActive(const std::vector<Variable> &mVars, const ImmutableString &name) 66 { 67 for (const Variable &var : mVars) 68 { 69 if (name == var.name) 70 { 71 return var.active; 72 } 73 } 74 UNREACHABLE(); 75 return true; 76 } 77 78 bool RemoveInactiveInterfaceVariablesTraverser::visitDeclaration(Visit visit, 79 TIntermDeclaration *node) 80 { 81 // SeparateDeclarations should have already been run. 82 ASSERT(node->getSequence()->size() == 1u); 83 84 TIntermTyped *declarator = node->getSequence()->front()->getAsTyped(); 85 ASSERT(declarator); 86 87 TIntermSymbol *asSymbol = declarator->getAsSymbolNode(); 88 if (!asSymbol) 89 { 90 return false; 91 } 92 93 const TType &type = declarator->getType(); 94 95 // Remove all shader interface variables except outputs, i.e. uniforms, interface blocks and 96 // inputs. 97 // 98 // Imagine a situation where the VS doesn't write to a varying but the FS reads from it. This 99 // is allowed, though the value of the varying is undefined. If the varying is removed here, 100 // the situation is changed to VS not declaring the varying, but the FS reading from it, which 101 // is not allowed. That's why inactive shader outputs are not removed. 102 // 103 // Inactive fragment shader outputs can be removed though, as there is no next stage. 104 bool removeDeclaration = false; 105 const TQualifier qualifier = type.getQualifier(); 106 107 if (type.isInterfaceBlock()) 108 { 109 // When a member has an explicit location, interface block should not be removed. 110 // If the member or interface would be removed, GetProgramResource could not return the 111 // location. 112 if (!IsShaderIoBlock(type.getQualifier()) && type.getQualifier() != EvqPatchIn && 113 type.getQualifier() != EvqPatchOut) 114 { 115 removeDeclaration = 116 !IsVariableActive(mInterfaceBlocks, type.getInterfaceBlock()->name()); 117 } 118 } 119 else if (qualifier == EvqUniform) 120 { 121 removeDeclaration = !IsVariableActive(mUniforms, asSymbol->getName()); 122 } 123 else if (qualifier == EvqAttribute || qualifier == EvqVertexIn) 124 { 125 removeDeclaration = !IsVariableActive(mAttributes, asSymbol->getName()); 126 } 127 else if (IsShaderIn(qualifier)) 128 { 129 removeDeclaration = !IsVariableActive(mInputVaryings, asSymbol->getName()); 130 } 131 else if (qualifier == EvqFragmentOut) 132 { 133 removeDeclaration = 134 !IsVariableActive(mOutputVariables, asSymbol->getName()) && mRemoveFragmentOutputs; 135 } 136 137 if (removeDeclaration) 138 { 139 TIntermSequence replacement; 140 141 // If the declaration was of a struct, keep the struct declaration itself. 142 if (type.isStructSpecifier()) 143 { 144 TType *structSpecifierType = new TType(type.getStruct(), true); 145 TVariable *emptyVariable = new TVariable(mSymbolTable, kEmptyImmutableString, 146 structSpecifierType, SymbolType::Empty); 147 TIntermDeclaration *declaration = new TIntermDeclaration(); 148 declaration->appendDeclarator(new TIntermSymbol(emptyVariable)); 149 replacement.push_back(declaration); 150 } 151 152 mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node, 153 std::move(replacement)); 154 } 155 156 return false; 157 } 158 159 bool RemoveInactiveInterfaceVariablesTraverser::visitBinary(Visit visit, TIntermBinary *node) 160 { 161 // Remove any code that initOutputVariables might have added corresponding to inactive 162 // output variables. This code is always in the form of `variable = ...;`. 163 if (node->getOp() != EOpAssign) 164 { 165 // Don't recurse, won't find the initialization nested in another expression. 166 return false; 167 } 168 169 // Get the symbol being initialized, and check if it's an inactive output. If it is, this must 170 // necessarily be initialization code that ANGLE has added (and wasn't there in the original 171 // shader; if it was, the symbol wouldn't have been inactive). 172 TIntermSymbol *symbol = node->getLeft()->getAsSymbolNode(); 173 if (symbol == nullptr) 174 { 175 return false; 176 } 177 178 const TQualifier qualifier = symbol->getType().getQualifier(); 179 if (qualifier != EvqFragmentOut || IsVariableActive(mOutputVariables, symbol->getName())) 180 { 181 return false; 182 } 183 184 // Drop the initialization code. 185 TIntermSequence replacement; 186 mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node, std::move(replacement)); 187 return false; 188 } 189 190 } // namespace 191 192 bool RemoveInactiveInterfaceVariables(TCompiler *compiler, 193 TIntermBlock *root, 194 TSymbolTable *symbolTable, 195 const std::vector<sh::ShaderVariable> &attributes, 196 const std::vector<sh::ShaderVariable> &inputVaryings, 197 const std::vector<sh::ShaderVariable> &outputVariables, 198 const std::vector<sh::ShaderVariable> &uniforms, 199 const std::vector<sh::InterfaceBlock> &interfaceBlocks, 200 bool removeFragmentOutputs) 201 { 202 RemoveInactiveInterfaceVariablesTraverser traverser(symbolTable, attributes, inputVaryings, 203 outputVariables, uniforms, interfaceBlocks, 204 removeFragmentOutputs); 205 root->traverse(&traverser); 206 return traverser.updateTree(compiler, root); 207 } 208 209 } // namespace sh