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
RemoveInactiveInterfaceVariablesTraverser(TSymbolTable * symbolTable,const std::vector<sh::ShaderVariable> & attributes,const std::vector<sh::ShaderVariable> & inputVaryings,const std::vector<sh::ShaderVariable> & outputVariables,const std::vector<sh::ShaderVariable> & uniforms,const std::vector<sh::InterfaceBlock> & interfaceBlocks,bool removeFragmentOutputs)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>
IsVariableActive(const std::vector<Variable> & mVars,const ImmutableString & name)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
visitDeclaration(Visit visit,TIntermDeclaration * node)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
visitBinary(Visit visit,TIntermBinary * node)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
RemoveInactiveInterfaceVariables(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,const std::vector<sh::ShaderVariable> & attributes,const std::vector<sh::ShaderVariable> & inputVaryings,const std::vector<sh::ShaderVariable> & outputVariables,const std::vector<sh::ShaderVariable> & uniforms,const std::vector<sh::InterfaceBlock> & interfaceBlocks,bool removeFragmentOutputs)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
210