blob: eec393b44523934bcd459f08321d77954b9cf1c2 [file] [log] [blame]
//
// Copyright 2025 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.
//
#include "compiler/translator/tree_ops/GatherDefaultUniforms.h"
#include "GLSLANG/ShaderVars.h"
#include "angle_gl.h"
#include "common/debug.h"
#include "compiler/translator/Compiler.h"
#include "compiler/translator/ImmutableString.h"
#include "compiler/translator/SymbolUniqueId.h"
#include "compiler/translator/tree_util/IntermNode_util.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
#include "compiler/translator/tree_util/ReplaceVariable.h"
#include "compiler/translator/util.h"
namespace sh
{
namespace
{
class ReplaceDefaultUniformsTraverser : public TIntermTraverser
{
public:
ReplaceDefaultUniformsTraverser(const VariableReplacementMap &variableMap)
: TIntermTraverser(true, false, false), mVariableMap(variableMap)
{}
bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
{
const TIntermSequence &sequence = *(node->getSequence());
TIntermTyped *variable = sequence.front()->getAsTyped();
const TType &type = variable->getType();
if (IsDefaultUniform(type))
{
// Remove the uniform declaration.
TIntermSequence emptyReplacement;
mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node,
std::move(emptyReplacement));
return false;
}
return true;
}
void visitSymbol(TIntermSymbol *symbol) override
{
const TVariable &variable = symbol->variable();
const TType &type = variable.getType();
if (!IsDefaultUniform(type) || gl::IsBuiltInName(variable.name().data()))
{
return;
}
ASSERT(mVariableMap.count(variable.uniqueId()) > 0);
queueReplacement(mVariableMap.at(variable.uniqueId())->deepCopy(),
OriginalNode::IS_DROPPED);
}
private:
const VariableReplacementMap &mVariableMap;
};
// Fields of nameless interface blocks are TVariables. So when creating a new interface blocks we
// need to create all the TVariables representing its fields.
TIntermSymbol *CreateVariableForFieldOfNamelessInterfaceBlock(const TVariable *object,
int index,
TSymbolTable *symbolTable)
{
ASSERT(object->getType().getInterfaceBlock());
const TFieldList &fieldList = object->getType().getInterfaceBlock()->fields();
ASSERT(0 <= index);
ASSERT(static_cast<size_t>(index) < fieldList.size());
ASSERT(object->symbolType() == SymbolType::Empty);
TType *replacementType = new TType(*fieldList[index]->type());
replacementType->setInterfaceBlockField(object->getType().getInterfaceBlock(), index);
TVariable *replacementVariable = new TVariable(symbolTable, fieldList[index]->name(),
replacementType, fieldList[index]->symbolType());
return new TIntermSymbol(replacementVariable);
}
} // namespace
bool IsDefaultUniform(const TType &type)
{
return type.getQualifier() == EvqUniform && type.getInterfaceBlock() == nullptr &&
!IsOpaqueType(type.getBasicType());
}
TSet<ImmutableString> *GetActiveUniforms(const std::vector<ShaderVariable> &defaultUniforms)
{
auto activeUniforms = new TSet<ImmutableString>();
for (const ShaderVariable &uniform : defaultUniforms)
{
if (uniform.active)
{
activeUniforms->insert(uniform.name);
}
}
return activeUniforms;
}
bool GatherDefaultUniforms(TCompiler *compiler,
TIntermBlock *root,
TSymbolTable *symbolTable,
gl::ShaderType shaderType,
const ImmutableString &uniformBlockType,
const ImmutableString &uniformBlockVarName,
const TVariable **outUniformBlock)
{
// First, collect all default uniforms and declare a uniform block.
TFieldList *uniformList = new TFieldList;
TVector<const TVariable *> uniformVars;
TSet<ImmutableString> *activeUniforms = GetActiveUniforms(compiler->getUniforms());
for (TIntermNode *node : *root->getSequence())
{
TIntermDeclaration *decl = node->getAsDeclarationNode();
if (decl == nullptr)
{
continue;
}
const TIntermSequence &sequence = *(decl->getSequence());
TIntermSymbol *symbol = sequence.front()->getAsSymbolNode();
if (symbol == nullptr)
{
continue;
}
const TType &type = symbol->getType();
// Only gather active default uniforms.
if (IsDefaultUniform(type) && activeUniforms->count(symbol->getName()) != 0)
{
TType *fieldType = new TType(type);
uniformList->push_back(new TField(fieldType, symbol->getName(), symbol->getLine(),
symbol->variable().symbolType()));
uniformVars.push_back(&symbol->variable());
}
}
VariableReplacementMap variableMap;
if (uniformList->empty())
{
*outUniformBlock = nullptr;
}
else
{
TLayoutQualifier layoutQualifier = TLayoutQualifier::Create();
layoutQualifier.blockStorage = EbsStd140;
TInterfaceBlock *interfaceBlock =
DeclareInterfaceBlock(symbolTable, uniformList, layoutQualifier, uniformBlockType);
// Set the mIsDefaultUniformBlock bit because the interfaceBlock represents default uniform
// interfaceBlock.
// Later when traversing the AST and output SPIRV, we will rely on this bit to decide if we
// want to transform FP32 to FP16 for float based on if the float vars are inside the
// default uniform block.
interfaceBlock->setDefaultUniformBlock();
*outUniformBlock = DeclareInterfaceBlockVariable(
root, symbolTable, EvqUniform, interfaceBlock, layoutQualifier,
TMemoryQualifier::Create(), 0, uniformBlockVarName);
// Create a map from the uniform variables to new variables that reference the fields of the
// block.
for (size_t fieldIndex = 0; fieldIndex < uniformVars.size(); ++fieldIndex)
{
const TVariable *variable = uniformVars[fieldIndex];
if ((*outUniformBlock)->symbolType() == SymbolType::Empty)
{
variableMap[variable->uniqueId()] = CreateVariableForFieldOfNamelessInterfaceBlock(
*outUniformBlock, static_cast<int>(fieldIndex), symbolTable);
}
else
{
variableMap[variable->uniqueId()] = AccessFieldOfNamedInterfaceBlock(
*outUniformBlock, static_cast<int>(fieldIndex));
}
}
}
// Finally transform the AST and make sure references to the uniforms are replaced with the new
// variables.
ReplaceDefaultUniformsTraverser defaultTraverser(variableMap);
root->traverse(&defaultTraverser);
return defaultTraverser.updateTree(compiler, root);
}
} // namespace sh