blob: 432ea19a0ac25665ca958c6d92916f336036b624 [file]
//
// 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;
}
}
ASSERT(false);
return true;
}
bool IsIoBlockVariableActive(const std::vector<ShaderVariable> &mVars, const ImmutableString &name)
{
for (const ShaderVariable &var : mVars)
{
if (name == var.structOrBlockName)
{
return var.active;
}
}
ASSERT(false);
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() && !IsShaderIoBlock(type.getQualifier()))
{
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) && qualifier != EvqPerVertexIn && qualifier != EvqPerVertexOut)
{
// Match I/O blocks by the block name; the instance name may be empty. Also note that
// CollectVariables() does not collect gl_PerVertex at all if not active. Those are not
// removed for simplicity.
if (type.getInterfaceBlock() != nullptr)
{
removeDeclaration =
!IsIoBlockVariableActive(mInputVaryings, type.getInterfaceBlock()->name());
}
else
{
removeDeclaration = !IsVariableActive(mInputVaryings, asSymbol->getName());
}
}
else if (qualifier == EvqFragmentOut || qualifier == EvqFragmentInOut)
{
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