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 
34     bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
35 
36   private:
37     const std::vector<sh::ShaderVariable> &mAttributes;
38     const std::vector<sh::ShaderVariable> &mInputVaryings;
39     const std::vector<sh::ShaderVariable> &mOutputVariables;
40     const std::vector<sh::ShaderVariable> &mUniforms;
41     const std::vector<sh::InterfaceBlock> &mInterfaceBlocks;
42 };
43 
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)44 RemoveInactiveInterfaceVariablesTraverser::RemoveInactiveInterfaceVariablesTraverser(
45     TSymbolTable *symbolTable,
46     const std::vector<sh::ShaderVariable> &attributes,
47     const std::vector<sh::ShaderVariable> &inputVaryings,
48     const std::vector<sh::ShaderVariable> &outputVariables,
49     const std::vector<sh::ShaderVariable> &uniforms,
50     const std::vector<sh::InterfaceBlock> &interfaceBlocks)
51     : TIntermTraverser(true, false, false, symbolTable),
52       mAttributes(attributes),
53       mInputVaryings(inputVaryings),
54       mOutputVariables(outputVariables),
55       mUniforms(uniforms),
56       mInterfaceBlocks(interfaceBlocks)
57 {}
58 
59 template <typename Variable>
IsVariableActive(const std::vector<Variable> & mVars,const ImmutableString & name)60 bool IsVariableActive(const std::vector<Variable> &mVars, const ImmutableString &name)
61 {
62     for (const Variable &var : mVars)
63     {
64         if (name == var.name)
65         {
66             return var.active;
67         }
68     }
69     UNREACHABLE();
70     return true;
71 }
72 
visitDeclaration(Visit visit,TIntermDeclaration * node)73 bool RemoveInactiveInterfaceVariablesTraverser::visitDeclaration(Visit visit,
74                                                                  TIntermDeclaration *node)
75 {
76     // SeparateDeclarations should have already been run.
77     ASSERT(node->getSequence()->size() == 1u);
78 
79     TIntermTyped *declarator = node->getSequence()->front()->getAsTyped();
80     ASSERT(declarator);
81 
82     TIntermSymbol *asSymbol = declarator->getAsSymbolNode();
83     if (!asSymbol)
84     {
85         return false;
86     }
87 
88     const TType &type = declarator->getType();
89 
90     // Remove all shader interface variables except outputs, i.e. uniforms, interface blocks and
91     // inputs.
92     //
93     // Imagine a situation where the VS doesn't write to a varying but the FS reads from it.  This
94     // is allowed, though the value of the varying is undefined.  If the varying is removed here,
95     // the situation is changed to VS not declaring the varying, but the FS reading from it, which
96     // is not allowed.  That's why inactive shader outputs are not removed.
97     //
98     // Inactive fragment shader outputs can be removed though, as there is no next stage.
99     bool removeDeclaration     = false;
100     const TQualifier qualifier = type.getQualifier();
101 
102     if (type.isInterfaceBlock())
103     {
104         // When a member has an explicit location, interface block should not be removed.
105         // If the member or interface would be removed, GetProgramResource could not return the
106         // location.
107         if (!IsShaderIoBlock(type.getQualifier()) && type.getQualifier() != EvqPatchIn &&
108             type.getQualifier() != EvqPatchOut)
109         {
110             removeDeclaration =
111                 !IsVariableActive(mInterfaceBlocks, type.getInterfaceBlock()->name());
112         }
113     }
114     else if (qualifier == EvqUniform)
115     {
116         removeDeclaration = !IsVariableActive(mUniforms, asSymbol->getName());
117     }
118     else if (qualifier == EvqAttribute || qualifier == EvqVertexIn)
119     {
120         removeDeclaration = !IsVariableActive(mAttributes, asSymbol->getName());
121     }
122     else if (IsShaderIn(qualifier))
123     {
124         removeDeclaration = !IsVariableActive(mInputVaryings, asSymbol->getName());
125     }
126     else if (qualifier == EvqFragmentOut)
127     {
128         removeDeclaration = !IsVariableActive(mOutputVariables, asSymbol->getName());
129     }
130 
131     if (removeDeclaration)
132     {
133         TIntermSequence replacement;
134 
135         // If the declaration was of a struct, keep the struct declaration itself.
136         if (type.isStructSpecifier())
137         {
138             TType *structSpecifierType      = new TType(type.getStruct(), true);
139             TVariable *emptyVariable        = new TVariable(mSymbolTable, kEmptyImmutableString,
140                                                      structSpecifierType, SymbolType::Empty);
141             TIntermDeclaration *declaration = new TIntermDeclaration();
142             declaration->appendDeclarator(new TIntermSymbol(emptyVariable));
143             replacement.push_back(declaration);
144         }
145 
146         mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node,
147                                         std::move(replacement));
148     }
149 
150     return false;
151 }
152 
153 }  // namespace
154 
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)155 bool RemoveInactiveInterfaceVariables(TCompiler *compiler,
156                                       TIntermBlock *root,
157                                       TSymbolTable *symbolTable,
158                                       const std::vector<sh::ShaderVariable> &attributes,
159                                       const std::vector<sh::ShaderVariable> &inputVaryings,
160                                       const std::vector<sh::ShaderVariable> &outputVariables,
161                                       const std::vector<sh::ShaderVariable> &uniforms,
162                                       const std::vector<sh::InterfaceBlock> &interfaceBlocks)
163 {
164     RemoveInactiveInterfaceVariablesTraverser traverser(symbolTable, attributes, inputVaryings,
165                                                         outputVariables, uniforms, interfaceBlocks);
166     root->traverse(&traverser);
167     return traverser.updateTree(compiler, root);
168 }
169 
170 }  // namespace sh
171