xref: /aosp_15_r20/external/angle/src/compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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