summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.cpp')
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.cpp209
1 files changed, 209 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.cpp
new file mode 100644
index 0000000000..a5e20ebcb0
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.cpp
@@ -0,0 +1,209 @@
+//
+// 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<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);
+
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+
+ private:
+ const std::vector<sh::ShaderVariable> &mAttributes;
+ const std::vector<sh::ShaderVariable> &mInputVaryings;
+ const std::vector<sh::ShaderVariable> &mOutputVariables;
+ const std::vector<sh::ShaderVariable> &mUniforms;
+ const std::vector<sh::InterfaceBlock> &mInterfaceBlocks;
+ bool mRemoveFragmentOutputs;
+};
+
+RemoveInactiveInterfaceVariablesTraverser::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)
+ : TIntermTraverser(true, false, false, symbolTable),
+ mAttributes(attributes),
+ mInputVaryings(inputVaryings),
+ mOutputVariables(outputVariables),
+ mUniforms(uniforms),
+ mInterfaceBlocks(interfaceBlocks),
+ mRemoveFragmentOutputs(removeFragmentOutputs)
+{}
+
+template <typename Variable>
+bool IsVariableActive(const std::vector<Variable> &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<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)
+{
+ RemoveInactiveInterfaceVariablesTraverser traverser(symbolTable, attributes, inputVaryings,
+ outputVariables, uniforms, interfaceBlocks,
+ removeFragmentOutputs);
+ root->traverse(&traverser);
+ return traverser.updateTree(compiler, root);
+}
+
+} // namespace sh