blob: f2a4600f1c055b341de529d22e191063c179cb38 [file] [log] [blame]
//
// Copyright 2020 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.
//
// RewriteSampleMaskVariable.cpp: Find any references to gl_SampleMask and gl_SampleMaskIn, and
// rewrite it with ANGLESampleMask or ANGLESampleMaskIn.
//
#include "compiler/translator/tree_util/RewriteSampleMaskVariable.h"
#include "common/bitset_utils.h"
#include "common/debug.h"
#include "common/utilities.h"
#include "compiler/translator/Compiler.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/tree_util/BuiltIn.h"
#include "compiler/translator/tree_util/IntermNode_util.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
#include "compiler/translator/tree_util/RunAtTheBeginningOfShader.h"
#include "compiler/translator/tree_util/RunAtTheEndOfShader.h"
namespace sh
{
namespace
{
constexpr int kMaxIndexForSampleMaskVar = 0;
constexpr int kFullSampleMask = 0xFFFFFFFF;
// Traverse the tree and collect the redeclaration and replace all non constant index references of
// gl_SampleMask or gl_SampleMaskIn with constant index references
class GLSampleMaskRelatedReferenceTraverser : public TIntermTraverser
{
public:
GLSampleMaskRelatedReferenceTraverser(const TIntermSymbol **redeclaredSymOut,
const ImmutableString &targetStr)
: TIntermTraverser(true, false, false),
mRedeclaredSym(redeclaredSymOut),
mTargetStr(targetStr)
{
*mRedeclaredSym = nullptr;
}
bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
{
// If gl_SampleMask is redeclared, we need to collect its information
const TIntermSequence &sequence = *(node->getSequence());
if (sequence.size() != 1)
{
return true;
}
TIntermTyped *variable = sequence.front()->getAsTyped();
TIntermSymbol *symbol = variable->getAsSymbolNode();
if (symbol == nullptr || symbol->getName() != mTargetStr)
{
return true;
}
*mRedeclaredSym = symbol;
return true;
}
bool visitBinary(Visit visit, TIntermBinary *node) override
{
TOperator op = node->getOp();
if (op != EOpIndexDirect && op != EOpIndexIndirect)
{
return true;
}
TIntermSymbol *left = node->getLeft()->getAsSymbolNode();
if (!left)
{
return true;
}
if (left->getName() != mTargetStr)
{
return true;
}
const TConstantUnion *constIdx = node->getRight()->getConstantValue();
if (!constIdx)
{
if (node->getRight()->hasSideEffects())
{
insertStatementInParentBlock(node->getRight());
}
queueReplacementWithParent(node, node->getRight(),
CreateIndexNode(kMaxIndexForSampleMaskVar),
OriginalNode::IS_DROPPED);
}
return true;
}
private:
const TIntermSymbol **mRedeclaredSym;
const ImmutableString mTargetStr;
};
} // anonymous namespace
ANGLE_NO_DISCARD bool RewriteSampleMask(TCompiler *compiler,
TIntermBlock *root,
TSymbolTable *symbolTable,
const TIntermTyped *numSamplesUniform)
{
const TIntermSymbol *redeclaredGLSampleMask = nullptr;
GLSampleMaskRelatedReferenceTraverser indexTraverser(&redeclaredGLSampleMask,
ImmutableString("gl_SampleMask"));
root->traverse(&indexTraverser);
if (!indexTraverser.updateTree(compiler, root))
{
return false;
}
// Retrieve gl_SampleMask variable reference
// Search user redeclared it first
const TVariable *glSampleMaskVar = nullptr;
if (redeclaredGLSampleMask)
{
glSampleMaskVar = &redeclaredGLSampleMask->variable();
}
else
{
// User defined not found, find in built-in table
glSampleMaskVar = static_cast<const TVariable *>(symbolTable->findBuiltIn(
ImmutableString("gl_SampleMask"), compiler->getShaderVersion()));
}
if (!glSampleMaskVar)
{
return false;
}
// Current ANGLE assumes that the maximum number of samples is less than or equal to
// VK_SAMPLE_COUNT_32_BIT. So, the size of gl_SampleMask array is always one.
const unsigned int arraySizeOfSampleMask = glSampleMaskVar->getType().getOutermostArraySize();
ASSERT(arraySizeOfSampleMask == 1);
TIntermSymbol *glSampleMaskSymbol = new TIntermSymbol(glSampleMaskVar);
// if (ANGLEUniforms.numSamples == 1)
// {
// gl_SampleMask[0] = int(0xFFFFFFFF);
// }
TIntermConstantUnion *singleSampleCount = CreateUIntNode(1);
TIntermBinary *equalTo =
new TIntermBinary(EOpEqual, numSamplesUniform->deepCopy(), singleSampleCount);
TIntermBlock *trueBlock = new TIntermBlock();
TIntermBinary *sampleMaskVar = new TIntermBinary(EOpIndexDirect, glSampleMaskSymbol->deepCopy(),
CreateIndexNode(kMaxIndexForSampleMaskVar));
TIntermConstantUnion *fullSampleMask = CreateIndexNode(kFullSampleMask);
TIntermBinary *assignment = new TIntermBinary(EOpAssign, sampleMaskVar, fullSampleMask);
trueBlock->appendStatement(assignment);
TIntermIfElse *multiSampleOrNot = new TIntermIfElse(equalTo, trueBlock, nullptr);
return RunAtTheEndOfShader(compiler, root, multiSampleOrNot, symbolTable);
}
ANGLE_NO_DISCARD bool RewriteSampleMaskIn(TCompiler *compiler,
TIntermBlock *root,
TSymbolTable *symbolTable)
{
const TIntermSymbol *redeclaredGLSampleMaskIn = nullptr;
GLSampleMaskRelatedReferenceTraverser indexTraverser(&redeclaredGLSampleMaskIn,
ImmutableString("gl_SampleMaskIn"));
root->traverse(&indexTraverser);
if (!indexTraverser.updateTree(compiler, root))
{
return false;
}
// Retrieve gl_SampleMaskIn variable reference
const TVariable *glSampleMaskInVar = nullptr;
glSampleMaskInVar = static_cast<const TVariable *>(
symbolTable->findBuiltIn(ImmutableString("gl_SampleMaskIn"), compiler->getShaderVersion()));
if (!glSampleMaskInVar)
{
return false;
}
// Current ANGLE assumes that the maximum number of samples is less than or equal to
// VK_SAMPLE_COUNT_32_BIT. So, the size of gl_SampleMask array is always one.
const unsigned int arraySizeOfSampleMaskIn =
glSampleMaskInVar->getType().getOutermostArraySize();
ASSERT(arraySizeOfSampleMaskIn == 1);
return true;
}
} // namespace sh