blob: a32281f07dd8496b302bf16cfc7c5fe6b51bc69c [file] [log] [blame]
//
// 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.
//
// RedefineInterfaceBlockLayoutQualifiersWithStd: Ensure layout qualifiers are either std140 or
// std430 to comply with Vulkan GLSL.
//
#include "compiler/translator/tree_ops/RedefineInterfaceBlockLayoutQualifiersWithStd.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
namespace sh
{
namespace
{
// Helper to replace the type of a symbol
TIntermSymbol *RedefineLayoutQualifierOfSymbolNode(TIntermSymbol *symbolNode,
const TLayoutQualifier &newLayoutQualifier,
TSymbolTable *symbolTable)
{
const TVariable &oldVariable = symbolNode->variable();
ASSERT(symbolNode->getType().isInterfaceBlock());
const TType &oldType = symbolNode->getType();
const TInterfaceBlock *oldInterfaceBlock = oldType.getInterfaceBlock();
// Create a new type based on the old type, but the memory layout qualifier changed.
TType *newType = new TType(oldType);
newType->setLayoutQualifier(newLayoutQualifier);
// Create a new interface block based on the old one, with the new memory layout qualifier as
// well.
TInterfaceBlock *newInterfaceBlock =
new TInterfaceBlock(symbolTable, oldInterfaceBlock->name(), &oldInterfaceBlock->fields(),
newLayoutQualifier, oldInterfaceBlock->symbolType());
newType->setInterfaceBlock(newInterfaceBlock);
// Create a new variable with the modified type, to substitute the old variable.
TVariable *newVariable =
new TVariable(oldVariable.uniqueId(), oldVariable.name(), oldVariable.symbolType(),
oldVariable.extension(), newType);
return new TIntermSymbol(newVariable);
}
class Traverser : public TIntermTraverser
{
public:
explicit Traverser(TSymbolTable *symbolTable)
: TIntermTraverser(true, false, false, symbolTable)
{
symbolTable->push();
}
~Traverser() override { mSymbolTable->pop(); }
bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
{
ASSERT(visit == PreVisit);
if (!mInGlobalScope)
{
return false;
}
const TIntermSequence &sequence = *(node->getSequence());
TIntermTyped *declarator = sequence.front()->getAsTyped();
const TType &type = declarator->getType();
if (type.isInterfaceBlock())
{
ASSERT(declarator->getAsSymbolNode());
TLayoutQualifier layoutQualifier = type.getLayoutQualifier();
// If the layout qualifier is not explicitly std140 or std430, change it to std140 for
// uniforms and std430 otherwise. See the comment in the header for more information.
if (layoutQualifier.blockStorage != EbsStd140 &&
layoutQualifier.blockStorage != EbsStd430)
{
layoutQualifier.blockStorage =
type.getQualifier() == EvqUniform ? EbsStd140 : EbsStd430;
TIntermSymbol *replacement = RedefineLayoutQualifierOfSymbolNode(
declarator->getAsSymbolNode(), layoutQualifier, mSymbolTable);
queueReplacementWithParent(node, declarator, replacement, OriginalNode::IS_DROPPED);
}
}
return false;
}
};
} // anonymous namespace
void RedefineInterfaceBlockLayoutQualifiersWithStd(TIntermBlock *root, TSymbolTable *symbolTable)
{
Traverser redefineInterfaceBlockLayoutQualifiersWithStd(symbolTable);
root->traverse(&redefineInterfaceBlockLayoutQualifiersWithStd);
redefineInterfaceBlockLayoutQualifiersWithStd.updateTree();
}
} // namespace sh