Commit 186fe990 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Redo RewriteAtomicCounters

With MonomorphizeUnsupportedFunctionsInVulkanGLSL and RewriteArrayOfArrayOfOpaqueUniforms transformations run, it is no longer possible to encounter array of array of atomic counters, or have any passed to functions. As a result, RewriteAtomicCounters is greatly simplified. Additionally, it is no longer necessary to pass binding/offset information for atomic counters around and they can use constants. This change removes dependency on the shaderStorageBufferArrayDynamicIndexing Vulkan feature. Bug: angleproject:3726 Bug: angleproject:3881 Change-Id: Ia43092a668f60d009eccbbceeed5deaf105a5895 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2633687Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent 64c89120
......@@ -20,7 +20,6 @@ namespace sh
{
namespace
{
constexpr ImmutableString kAtomicCounterTypeName = ImmutableString("ANGLE_atomic_uint");
constexpr ImmutableString kAtomicCountersVarName = ImmutableString("atomicCounters");
constexpr ImmutableString kAtomicCounterFieldName = ImmutableString("counters");
......@@ -50,32 +49,32 @@ const TVariable *DeclareAtomicCountersBuffers(TIntermBlock *root, TSymbolTable *
ImmutableString(vk::kAtomicCountersBlockName), kAtomicCountersVarName);
}
TIntermConstantUnion *CreateUIntConstant(uint32_t value)
TIntermTyped *CreateUniformBufferOffset(const TIntermTyped *uniformBufferOffsets, int binding)
{
TType *constantType = new TType(*StaticType::GetBasic<EbtUInt, 1>());
constantType->setQualifier(EvqConst);
TConstantUnion *constantValue = new TConstantUnion;
constantValue->setUConst(value);
return new TIntermConstantUnion(constantValue, *constantType);
}
// Each uint in the |acbBufferOffsets| uniform contains offsets for 4 bindings. Therefore, the
// expression to get the uniform offset for the binding is:
//
// acbBufferOffsets[binding / 4] >> ((binding % 4) * 8) & 0xFF
TIntermTyped *CreateAtomicCounterConstant(TType *atomicCounterType,
uint32_t binding,
uint32_t offset)
{
ASSERT(atomicCounterType->getBasicType() == EbtStruct);
// acbBufferOffsets[binding / 4]
TIntermBinary *uniformBufferOffsetUint = new TIntermBinary(
EOpIndexDirect, uniformBufferOffsets->deepCopy(), CreateIndexNode(binding / 4));
TIntermSequence *arguments = new TIntermSequence();
arguments->push_back(CreateUIntConstant(binding));
arguments->push_back(CreateUIntConstant(offset));
// acbBufferOffsets[binding / 4] >> ((binding % 4) * 8)
TIntermBinary *uniformBufferOffsetShifted = uniformBufferOffsetUint;
if (binding % 4 != 0)
{
uniformBufferOffsetShifted = new TIntermBinary(EOpBitShiftRight, uniformBufferOffsetUint,
CreateUIntNode((binding % 4) * 8));
}
return TIntermAggregate::CreateConstructor(*atomicCounterType, arguments);
// acbBufferOffsets[binding / 4] >> ((binding % 4) * 8) & 0xFF
return new TIntermBinary(EOpBitwiseAnd, uniformBufferOffsetShifted, CreateUIntNode(0xFF));
}
TIntermBinary *CreateAtomicCounterRef(const TVariable *atomicCounters,
const TIntermTyped *bindingOffset,
const TIntermTyped *bufferOffsets)
TIntermBinary *CreateAtomicCounterRef(TIntermTyped *atomicCounterExpression,
const TVariable *atomicCounters,
const TIntermTyped *uniformBufferOffsets)
{
// The atomic counters storage buffer declaration looks as such:
//
......@@ -86,79 +85,109 @@ TIntermBinary *CreateAtomicCounterRef(const TVariable *atomicCounters,
//
// Where N is large enough to accommodate atomic counter buffer bindings used in the shader.
//
// Given an ANGLEAtomicCounter variable (which is a struct of {binding, offset}), we need to
// return:
// This function takes an expression that uses an atomic counter, which can either be:
//
// - ac
// - acArray[index]
//
// Note that RewriteArrayOfArrayOfOpaqueUniforms has already flattened array of array of atomic
// counters.
//
// For the first case (ac), the following code is generated:
//
// atomicCounters[binding].counters[offset]
//
// The offset itself is the provided one plus an offset given through uniforms.
// For the second case (acArray[index]), the following code is generated:
//
// atomicCounters[binding].counters[offset + index]
//
// In either case, an offset given through uniforms is also added to |offset|. The binding is
// necessarily a constant thanks to MonomorphizeUnsupportedFunctionsInVulkanGLSL.
TIntermSymbol *atomicCountersRef = new TIntermSymbol(atomicCounters);
// First determine if there's an index, and extract the atomic counter symbol out of the
// expression.
TIntermSymbol *atomicCounterSymbol = atomicCounterExpression->getAsSymbolNode();
TIntermTyped *atomicCounterIndex = nullptr;
int atomicCounterConstIndex = 0;
TIntermBinary *asBinary = atomicCounterExpression->getAsBinaryNode();
if (asBinary != nullptr)
{
atomicCounterSymbol = asBinary->getLeft()->getAsSymbolNode();
switch (asBinary->getOp())
{
case EOpIndexDirect:
atomicCounterConstIndex = asBinary->getRight()->getAsConstantUnion()->getIConst(0);
break;
case EOpIndexIndirect:
atomicCounterIndex = asBinary->getRight();
break;
default:
UNREACHABLE();
}
}
// Extract binding and offset information out of the atomic counter symbol.
ASSERT(atomicCounterSymbol);
const TVariable *atomicCounterVar = &atomicCounterSymbol->variable();
const TType &atomicCounterType = atomicCounterVar->getType();
const int binding = atomicCounterType.getLayoutQualifier().binding;
int offset = atomicCounterType.getLayoutQualifier().offset / 4;
// Create the expression:
//
// offset + arrayIndex + uniformOffset
//
// If arrayIndex is a constant, it's added with offset right here.
offset += atomicCounterConstIndex;
TIntermTyped *index = CreateUniformBufferOffset(uniformBufferOffsets, binding);
if (atomicCounterIndex != nullptr)
{
index = new TIntermBinary(EOpAdd, index, atomicCounterIndex);
}
if (offset != 0)
{
index = new TIntermBinary(EOpAdd, index, CreateIndexNode(offset));
}
TIntermConstantUnion *bindingFieldRef = CreateIndexNode(0);
TIntermConstantUnion *offsetFieldRef = CreateIndexNode(1);
TIntermConstantUnion *countersFieldRef = CreateIndexNode(0);
// Finally, create the complete expression:
//
// atomicCounters[binding].counters[index]
// Create references to bindingOffset.binding and bindingOffset.offset.
TIntermBinary *binding =
new TIntermBinary(EOpIndexDirectStruct, bindingOffset->deepCopy(), bindingFieldRef);
TIntermBinary *offset =
new TIntermBinary(EOpIndexDirectStruct, bindingOffset->deepCopy(), offsetFieldRef);
TIntermSymbol *atomicCountersRef = new TIntermSymbol(atomicCounters);
// Create reference to atomicCounters[bindingOffset.binding]
TIntermBinary *countersBlock = new TIntermBinary(EOpIndexDirect, atomicCountersRef, binding);
// atomicCounters[binding]
TIntermBinary *countersBlock =
new TIntermBinary(EOpIndexDirect, atomicCountersRef, CreateIndexNode(binding));
// Create reference to atomicCounters[bindingOffset.binding].counters
// atomicCounters[binding].counters
TIntermBinary *counters =
new TIntermBinary(EOpIndexDirectInterfaceBlock, countersBlock, countersFieldRef);
// Create bufferOffsets[binding / 4]. Each uint in bufferOffsets contains offsets for 4
// bindings.
TIntermBinary *bindingDivFour =
new TIntermBinary(EOpDiv, binding->deepCopy(), CreateUIntConstant(4));
TIntermBinary *bufferOffsetUint =
new TIntermBinary(EOpIndexDirect, bufferOffsets->deepCopy(), bindingDivFour);
// Create (binding % 4) * 8
TIntermBinary *bindingModFour =
new TIntermBinary(EOpIMod, binding->deepCopy(), CreateUIntConstant(4));
TIntermBinary *bufferOffsetShift =
new TIntermBinary(EOpMul, bindingModFour, CreateUIntConstant(8));
// Create bufferOffsets[binding / 4] >> ((binding % 4) * 8) & 0xFF
TIntermBinary *bufferOffsetShifted =
new TIntermBinary(EOpBitShiftRight, bufferOffsetUint, bufferOffsetShift);
TIntermBinary *bufferOffset =
new TIntermBinary(EOpBitwiseAnd, bufferOffsetShifted, CreateUIntConstant(0xFF));
// return atomicCounters[bindingOffset.binding].counters[bindingOffset.offset + bufferOffset]
offset = new TIntermBinary(EOpAdd, offset, bufferOffset);
return new TIntermBinary(EOpIndexDirect, counters, offset);
new TIntermBinary(EOpIndexDirectInterfaceBlock, countersBlock, CreateIndexNode(0));
return new TIntermBinary(EOpIndexIndirect, counters, index);
}
// Traverser that:
//
// 1. Converts the |atomic_uint| types to |{uint,uint}| for binding and offset.
// 2. Substitutes the |uniform atomic_uint| declarations with a global declaration that holds the
// binding and offset.
// 3. Substitutes |atomicVar[n]| with |buffer[binding].counters[offset + n]|.
// 1. Removes the |uniform atomic_uint| declarations and remembers the binding and offset.
// 2. Substitutes |atomicVar[n]| with |buffer[binding].counters[offset + n]|.
class RewriteAtomicCountersTraverser : public TIntermTraverser
{
public:
RewriteAtomicCountersTraverser(TSymbolTable *symbolTable,
const TVariable *atomicCounters,
const TIntermTyped *acbBufferOffsets)
: TIntermTraverser(true, true, true, symbolTable),
: TIntermTraverser(true, false, false, symbolTable),
mAtomicCounters(atomicCounters),
mAcbBufferOffsets(acbBufferOffsets),
mAtomicCounterType(nullptr),
mAtomicCounterTypeConst(nullptr),
mAtomicCounterTypeDeclaration(nullptr)
mAcbBufferOffsets(acbBufferOffsets)
{}
bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
{
if (visit != PreVisit)
if (!mInGlobalScope)
{
return true;
}
......@@ -167,411 +196,50 @@ class RewriteAtomicCountersTraverser : public TIntermTraverser
TIntermTyped *variable = sequence.front()->getAsTyped();
const TType &type = variable->getType();
bool isAtomicCounter = type.getQualifier() == EvqUniform && type.isAtomicCounter();
bool isAtomicCounter = type.isAtomicCounter();
if (isAtomicCounter)
{
// Atomic counters cannot have initializers, so the declaration must necessarily be a
// symbol.
TIntermSymbol *samplerVariable = variable->getAsSymbolNode();
ASSERT(samplerVariable != nullptr);
ASSERT(type.getQualifier() == EvqUniform);
TIntermSequence emptySequence;
mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node, emptySequence);
declareAtomicCounter(&samplerVariable->variable(), node);
return false;
}
return true;
}
void visitFunctionPrototype(TIntermFunctionPrototype *node) override
{
const TFunction *function = node->getFunction();
// Go over the parameters and replace the atomic arguments with a uint type.
mRetyper.visitFunctionPrototype();
for (size_t paramIndex = 0; paramIndex < function->getParamCount(); ++paramIndex)
{
const TVariable *param = function->getParam(paramIndex);
TVariable *replacement = convertFunctionParameter(node, param);
if (replacement)
{
mRetyper.replaceFunctionParam(param, replacement);
}
}
TIntermFunctionPrototype *replacementPrototype =
mRetyper.convertFunctionPrototype(mSymbolTable, function);
if (replacementPrototype)
{
queueReplacement(replacementPrototype, OriginalNode::IS_DROPPED);
}
}
bool visitAggregate(Visit visit, TIntermAggregate *node) override
{
if (visit == PreVisit)
{
mRetyper.preVisitAggregate();
}
if (visit != PostVisit)
{
return true;
}
if (node->getOp() == EOpCallBuiltInFunction)
{
convertBuiltinFunction(node);
}
else if (node->getOp() == EOpCallFunctionInAST)
{
TIntermAggregate *substituteCall = mRetyper.convertASTFunction(node);
if (substituteCall)
{
queueReplacement(substituteCall, OriginalNode::IS_DROPPED);
bool converted = convertBuiltinFunction(node);
return !converted;
}
}
mRetyper.postVisitAggregate();
// AST functions don't require modification as atomic counter function parameters are
// removed by MonomorphizeUnsupportedFunctionsInVulkanGLSL.
return true;
}
void visitSymbol(TIntermSymbol *symbol) override
{
const TVariable *symbolVariable = &symbol->variable();
if (!symbol->getType().isAtomicCounter())
{
return;
// Connot encounter the atomic counter symbol directly. It can only be used with functions,
// and therefore it's handled by visitAggregate.
ASSERT(!symbol->getType().isAtomicCounter());
}
// The symbol is either referencing a global atomic counter, or is a function parameter. In
// either case, it could be an array. The are the following possibilities:
//
// layout(..) uniform atomic_uint ac;
// layout(..) uniform atomic_uint acArray[N];
//
// void func(inout atomic_uint c)
// {
// otherFunc(c);
// }
//
// void funcArray(inout atomic_uint cArray[N])
// {
// otherFuncArray(cArray);
// otherFunc(cArray[n]);
// }
//
// void funcGlobal()
// {
// func(ac);
// func(acArray[n]);
// funcArray(acArray);
// atomicIncrement(ac);
// atomicIncrement(acArray[n]);
// }
//
// This should translate to:
//
// buffer ANGLEAtomicCounters
// {
// uint counters[];
// } atomicCounters;
//
// struct ANGLEAtomicCounter
// {
// uint binding;
// uint offset;
// };
// const ANGLEAtomicCounter ac = {<binding>, <offset>};
// const ANGLEAtomicCounter acArray = {<binding>, <offset>};
//
// void func(inout ANGLEAtomicCounter c)
// {
// otherFunc(c);
// }
//
// void funcArray(inout uint cArray)
// {
// otherFuncArray(cArray);
// otherFunc({cArray.binding, cArray.offset + n});
// }
//
// void funcGlobal()
// {
// func(ac);
// func(acArray+n);
// funcArray(acArray);
// atomicAdd(atomicCounters[ac.binding]counters[ac.offset]);
// atomicAdd(atomicCounters[ac.binding]counters[ac.offset+n]);
// }
//
// In all cases, the argument transformation is stored in mRetyper. In the function call's
// PostVisit, if it's a builtin, the look up in |atomicCounters.counters| is done as well as
// the builtin function change. Otherwise, the transformed argument is passed on as is.
//
TIntermTyped *bindingOffset =
new TIntermSymbol(mRetyper.getVariableReplacement(symbolVariable));
ASSERT(bindingOffset != nullptr);
TIntermNode *argument = convertFunctionArgument(symbol, &bindingOffset);
if (mRetyper.isInAggregate())
{
mRetyper.replaceFunctionCallArg(argument, bindingOffset);
}
else
{
// If there's a stray ac[i] lying around, just delete it. This can happen if the shader
// uses ac[i].length(), which in RemoveArrayLengthMethod() will result in an ineffective
// statement that's just ac[i]; (similarly for a stray ac;, it doesn't have to be
// subscripted). Note that the subscript could have side effects, but the
// convertFunctionArgument above has already generated code that includes the subscript
// (and therefore its side-effect).
TIntermBlock *block = nullptr;
for (uint32_t ancestorIndex = 0; block == nullptr; ++ancestorIndex)
bool visitBinary(Visit visit, TIntermBinary *node) override
{
block = getAncestorNode(ancestorIndex)->getAsBlock();
}
TIntermSequence emptySequence;
mMultiReplacements.emplace_back(block, argument, emptySequence);
}
// Connot encounter an atomic counter expression directly. It can only be used with
// functions, and therefore it's handled by visitAggregate.
ASSERT(!node->getType().isAtomicCounter());
return true;
}
TIntermDeclaration *getAtomicCounterTypeDeclaration() { return mAtomicCounterTypeDeclaration; }
private:
void declareAtomicCounter(const TVariable *atomicCounterVar, TIntermDeclaration *node)
{
// Create a global variable that contains the binding and offset of this atomic counter
// declaration.
if (mAtomicCounterType == nullptr)
{
declareAtomicCounterType();
}
ASSERT(mAtomicCounterTypeConst);
TVariable *bindingOffset = new TVariable(mSymbolTable, atomicCounterVar->name(),
mAtomicCounterTypeConst, SymbolType::UserDefined);
const TType &atomicCounterType = atomicCounterVar->getType();
uint32_t offset = atomicCounterType.getLayoutQualifier().offset;
uint32_t binding = atomicCounterType.getLayoutQualifier().binding;
ASSERT(offset % 4 == 0);
TIntermTyped *bindingOffsetInitValue =
CreateAtomicCounterConstant(mAtomicCounterTypeConst, binding, offset / 4);
TIntermSymbol *bindingOffsetSymbol = new TIntermSymbol(bindingOffset);
TIntermBinary *bindingOffsetInit =
new TIntermBinary(EOpInitialize, bindingOffsetSymbol, bindingOffsetInitValue);
TIntermDeclaration *bindingOffsetDeclaration = new TIntermDeclaration();
bindingOffsetDeclaration->appendDeclarator(bindingOffsetInit);
// Replace the atomic_uint declaration with the binding/offset declaration.
TIntermSequence replacement;
replacement.push_back(bindingOffsetDeclaration);
mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node, replacement);
// Remember the binding/offset variable.
mRetyper.replaceGlobalVariable(atomicCounterVar, bindingOffset);
}
void declareAtomicCounterType()
{
ASSERT(mAtomicCounterType == nullptr);
TFieldList *fields = new TFieldList();
fields->push_back(new TField(new TType(EbtUInt, EbpUndefined, EvqGlobal, 1, 1),
ImmutableString("binding"), TSourceLoc(),
SymbolType::AngleInternal));
fields->push_back(new TField(new TType(EbtUInt, EbpUndefined, EvqGlobal, 1, 1),
ImmutableString("arrayIndex"), TSourceLoc(),
SymbolType::AngleInternal));
TStructure *atomicCounterTypeStruct =
new TStructure(mSymbolTable, kAtomicCounterTypeName, fields, SymbolType::AngleInternal);
mAtomicCounterType = new TType(atomicCounterTypeStruct, false);
mAtomicCounterTypeDeclaration = new TIntermDeclaration;
TVariable *emptyVariable = new TVariable(mSymbolTable, kEmptyImmutableString,
mAtomicCounterType, SymbolType::Empty);
mAtomicCounterTypeDeclaration->appendDeclarator(new TIntermSymbol(emptyVariable));
// Keep a const variant around as well.
mAtomicCounterTypeConst = new TType(*mAtomicCounterType);
mAtomicCounterTypeConst->setQualifier(EvqConst);
}
TVariable *convertFunctionParameter(TIntermNode *parent, const TVariable *param)
{
if (!param->getType().isAtomicCounter())
{
return nullptr;
}
if (mAtomicCounterType == nullptr)
{
declareAtomicCounterType();
}
const TType *paramType = &param->getType();
TType *newType =
paramType->getQualifier() == EvqConst ? mAtomicCounterTypeConst : mAtomicCounterType;
TVariable *replacementVar =
new TVariable(mSymbolTable, param->name(), newType, SymbolType::UserDefined);
return replacementVar;
}
TIntermTyped *convertFunctionArgumentHelper(
const TVector<unsigned int> &runningArraySizeProducts,
TIntermTyped *flattenedSubscript,
uint32_t depth,
uint32_t *subscriptCountOut)
{
std::string prefix(depth, ' ');
TIntermNode *parent = getAncestorNode(depth);
ASSERT(parent);
TIntermBinary *arrayExpression = parent->getAsBinaryNode();
if (!arrayExpression)
{
// If the parent is not an array subscript operation, we have reached the end of the
// subscript chain. Note the depth that's traversed so the corresponding node can be
// taken as the function argument.
*subscriptCountOut = depth;
return flattenedSubscript;
}
ASSERT(arrayExpression->getOp() == EOpIndexDirect ||
arrayExpression->getOp() == EOpIndexIndirect);
// Assume i = n - depth. Get Pi. See comment in convertFunctionArgument.
ASSERT(depth < runningArraySizeProducts.size());
uint32_t thisDimensionSize =
runningArraySizeProducts[runningArraySizeProducts.size() - 1 - depth];
// Get Ii.
TIntermTyped *thisDimensionOffset = arrayExpression->getRight();
TIntermConstantUnion *subscriptAsConstant = thisDimensionOffset->getAsConstantUnion();
const bool subscriptIsZero = subscriptAsConstant && subscriptAsConstant->isZero(0);
// If Ii is zero, don't need to add Ii*Pi; that's zero.
if (!subscriptIsZero)
{
thisDimensionOffset = thisDimensionOffset->deepCopy();
// If Pi is 1, don't multiply. Just accumulate Ii.
if (thisDimensionSize != 1)
{
thisDimensionOffset = new TIntermBinary(EOpMul, thisDimensionOffset,
CreateUIntConstant(thisDimensionSize));
}
// Accumulate with the previous running offset, if any.
if (flattenedSubscript)
{
flattenedSubscript =
new TIntermBinary(EOpAdd, flattenedSubscript, thisDimensionOffset);
}
else
{
flattenedSubscript = thisDimensionOffset;
}
}
// Note: GLSL only allows 2 nested levels of arrays, so this recursion is bounded.
return convertFunctionArgumentHelper(runningArraySizeProducts, flattenedSubscript,
depth + 1, subscriptCountOut);
}
TIntermNode *convertFunctionArgument(TIntermNode *symbol, TIntermTyped **bindingOffset)
{
// Assume a general case of array declaration with N dimensions:
//
// atomic_uint ac[Dn]..[D2][D1];
//
// Let's define
//
// Pn = D(n-1)*...*D2*D1
//
// In that case, we have:
//
// ac[In] = ac + In*Pn
// ac[In][I(n-1)] = ac + In*Pn + I(n-1)*P(n-1)
// ac[In]...[Ii] = ac + In*Pn + ... + Ii*Pi
//
// We have just visited a symbol; ac. Walking the parent chain, we will visit the
// expressions in the above order (ac, ac[In], ac[In][I(n-1)], ...). We therefore can
// simply walk the parent chain and accumulate Ii*Pi to obtain the offset from the base of
// ac.
TIntermSymbol *argumentAsSymbol = symbol->getAsSymbolNode();
ASSERT(argumentAsSymbol);
const TSpan<const unsigned int> &arraySizes = argumentAsSymbol->getType().getArraySizes();
// Calculate Pi
TVector<unsigned int> runningArraySizeProducts;
if (!arraySizes.empty())
{
runningArraySizeProducts.resize(arraySizes.size());
uint32_t runningProduct = 1;
for (size_t dimension = 0; dimension < arraySizes.size(); ++dimension)
{
runningArraySizeProducts[dimension] = runningProduct;
runningProduct *= arraySizes[dimension];
}
}
// Walk the parent chain and accumulate Ii*Pi
uint32_t subscriptCount = 0;
TIntermTyped *flattenedSubscript =
convertFunctionArgumentHelper(runningArraySizeProducts, nullptr, 0, &subscriptCount);
// Find the function argument, which is either in the form of ac (i.e. there are no
// subscripts, in which case that's the function argument), or ac[In]...[Ii] (in which case
// the function argument is the (n-i)th ancestor of ac.
//
// Note that this is the case because no other operation is allowed on ac other than
// subscript.
TIntermNode *argument = subscriptCount == 0 ? symbol : getAncestorNode(subscriptCount - 1);
ASSERT(argument != nullptr);
// If not subscripted, keep the argument as-is.
if (flattenedSubscript == nullptr)
{
return argument;
}
// Copy the atomic counter binding/offset constant and modify it by adding the array
// subscript to its offset field.
TVariable *modified = CreateTempVariable(mSymbolTable, mAtomicCounterType);
TIntermDeclaration *modifiedDecl = CreateTempInitDeclarationNode(modified, *bindingOffset);
TIntermSymbol *modifiedSymbol = new TIntermSymbol(modified);
TConstantUnion *offsetFieldIndex = new TConstantUnion;
offsetFieldIndex->setIConst(1);
TIntermConstantUnion *offsetFieldRef =
new TIntermConstantUnion(offsetFieldIndex, *StaticType::GetBasic<EbtUInt>());
TIntermBinary *offsetField =
new TIntermBinary(EOpIndexDirectStruct, modifiedSymbol, offsetFieldRef);
TIntermBinary *modifiedOffset =
new TIntermBinary(EOpAddAssign, offsetField, flattenedSubscript);
TIntermSequence *modifySequence = new TIntermSequence({modifiedDecl, modifiedOffset});
insertStatementsInParentBlock(*modifySequence);
*bindingOffset = modifiedSymbol->deepCopy();
return argument;
}
void convertBuiltinFunction(TIntermAggregate *node)
bool convertBuiltinFunction(TIntermAggregate *node)
{
// If the function is |memoryBarrierAtomicCounter|, simply replace it with
// |memoryBarrierBuffer|.
......@@ -580,17 +248,16 @@ class RewriteAtomicCountersTraverser : public TIntermTraverser
TIntermTyped *substituteCall = CreateBuiltInFunctionCallNode(
"memoryBarrierBuffer", new TIntermSequence, *mSymbolTable, 310);
queueReplacement(substituteCall, OriginalNode::IS_DROPPED);
return;
return true;
}
// If it's an |atomicCounter*| function, replace the function with an |atomic*| equivalent.
if (!node->getFunction()->isAtomicCounterFunction())
{
return;
return false;
}
const ImmutableString &functionName = node->getFunction()->name();
TIntermSequence *arguments = node->getSequence();
// Note: atomicAdd(0) is used for atomic reads.
uint32_t valueChange = 0;
......@@ -615,14 +282,12 @@ class RewriteAtomicCountersTraverser : public TIntermTraverser
ASSERT(functionName == "atomicCounter");
}
const TIntermNode *param = (*arguments)[0];
TIntermTyped *bindingOffset = mRetyper.getFunctionCallArgReplacement(param);
TIntermTyped *param = (*node->getSequence())[0]->getAsTyped();
TIntermSequence *substituteArguments = new TIntermSequence;
substituteArguments->push_back(
CreateAtomicCounterRef(mAtomicCounters, bindingOffset, mAcbBufferOffsets));
substituteArguments->push_back(CreateUIntConstant(valueChange));
CreateAtomicCounterRef(param, mAtomicCounters, mAcbBufferOffsets));
substituteArguments->push_back(CreateUIntNode(valueChange));
TIntermTyped *substituteCall = CreateBuiltInFunctionCallNode(
kAtomicAddFunction, substituteArguments, *mSymbolTable, 310);
......@@ -631,22 +296,15 @@ class RewriteAtomicCountersTraverser : public TIntermTraverser
// unlike atomicAdd. So we need to do a -1 on the result as well.
if (isDecrement)
{
substituteCall = new TIntermBinary(EOpSub, substituteCall, CreateUIntConstant(1));
substituteCall = new TIntermBinary(EOpSub, substituteCall, CreateUIntNode(1));
}
queueReplacement(substituteCall, OriginalNode::IS_DROPPED);
return true;
}
const TVariable *mAtomicCounters;
const TIntermTyped *mAcbBufferOffsets;
RetypeOpaqueVariablesHelper mRetyper;
TType *mAtomicCounterType;
TType *mAtomicCounterTypeConst;
// Stored to be put at the top of the shader after the pass.
TIntermDeclaration *mAtomicCounterTypeDeclaration;
};
} // anonymous namespace
......@@ -660,17 +318,6 @@ bool RewriteAtomicCounters(TCompiler *compiler,
RewriteAtomicCountersTraverser traverser(symbolTable, atomicCounters, acbBufferOffsets);
root->traverse(&traverser);
if (!traverser.updateTree(compiler, root))
{
return false;
}
TIntermDeclaration *atomicCounterTypeDeclaration = traverser.getAtomicCounterTypeDeclaration();
if (atomicCounterTypeDeclaration)
{
root->getSequence()->insert(root->getSequence()->begin(), atomicCounterTypeDeclaration);
}
return compiler->validateAST(root);
return traverser.updateTree(compiler, root);
}
} // namespace sh
......@@ -1379,9 +1379,6 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF
mPhysicalDeviceFeatures.shaderUniformBufferArrayDynamicIndexing;
enabledFeatures.features.shaderSampledImageArrayDynamicIndexing =
mPhysicalDeviceFeatures.shaderSampledImageArrayDynamicIndexing;
// Used to support atomic counter emulation:
enabledFeatures.features.shaderStorageBufferArrayDynamicIndexing =
mPhysicalDeviceFeatures.shaderStorageBufferArrayDynamicIndexing;
// Used to support APPLE_clip_distance
enabledFeatures.features.shaderClipDistance = mPhysicalDeviceFeatures.shaderClipDistance;
// Used to support OES_sample_shading
......
......@@ -1016,12 +1016,8 @@ bool CanSupportGPUShader5EXT(const VkPhysicalDeviceFeatures &features)
// textureGatherOffsets family of functions.
// - shaderSampledImageArrayDynamicIndexing and shaderUniformBufferArrayDynamicIndexing:
// dynamically uniform indices for samplers and uniform buffers.
// - shaderStorageBufferArrayDynamicIndexing: While EXT_gpu_shader5 doesn't require dynamically
// uniform indices on storage buffers, we need it as we emulate atomic counter buffers with
// storage buffers (and atomic counter buffers *can* be indexed in that way).
return features.shaderImageGatherExtended && features.shaderSampledImageArrayDynamicIndexing &&
features.shaderUniformBufferArrayDynamicIndexing &&
features.shaderStorageBufferArrayDynamicIndexing;
features.shaderUniformBufferArrayDynamicIndexing;
}
} // namespace vk
......
......@@ -46,7 +46,7 @@ the name. Examples:
1442 OPENGL : dEQP-GLES31.functional.separate_shader.* = SKIP
1442 D3D11 : dEQP-GLES31.functional.separate_shader.* = SKIP
// Bug in older drivers:
// Unsupported feature:
3726 VULKAN ANDROID : dEQP-GLES31.functional.synchronization.inter_call.without_memory_barrier.*atomic_counter* = FAIL
// Failing test in Nvidia's OpenGL implementation on windows:
......
......@@ -217,26 +217,9 @@
4110 ANDROID VULKAN : dEQP-GLES31.functional.shaders.helper_invocation.value.points_8_samples = FAIL
4110 ANDROID VULKAN : dEQP-GLES31.functional.shaders.helper_invocation.value.triangles_max_samples = FAIL
// Passing on recent drivers:
3726 VULKAN ANDROID : dEQP-GLES31.functional.ssbo.layout.* = FAIL
3726 VULKAN ANDROID : dEQP-GLES31.functional.synchronization.inter_call.without_memory_barrier.*atomic_counter* = FAIL
3726 VULKAN ANDROID : dEQP-GLES31.functional.compute.basic.atomic_counter* = FAIL
// Seem to trigger LowMemoryKiller when run in a certain sequence
5185 VULKAN ANDROID : dEQP-GLES31.functional.atomic_counter.* = SKIP
// Arrays of atomic counters not supported on Android (lacking Vulkan feature)
3881 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.const_literal_vertex = FAIL
3881 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.const_literal_fragment = FAIL
3881 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.const_literal_compute = FAIL
3881 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.const_expression_vertex = FAIL
3881 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.const_expression_fragment = FAIL
3881 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.const_expression_compute = FAIL
3881 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.*uniform_vertex = FAIL
3881 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.*uniform_fragment = FAIL
3881 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.*uniform_compute = FAIL
// SSBO synchronization:
4097 VULKAN PIXEL2ORXL : dEQP-GLES31.functional.synchronization.in_invocation.ssbo_alias_overwrite = FAIL
......
......@@ -32,7 +32,6 @@
3573 VULKAN : KHR-GLES31.core.texture_buffer.texture_buffer_texture_buffer_range = SKIP
// Dispatch indirect:
3726 VULKAN PIXEL2ORXL : KHR-GLES31.core.compute_shader.pipeline-compute-chain = FAIL
4194 VULKAN PIXEL2ORXL : KHR-GLES31.core.compute_shader.resource-ubo = FAIL
4194 VULKAN PIXEL2ORXL : KHR-GLES31.core.compute_shader.built-in-variables = FAIL
......@@ -88,10 +87,6 @@
// Crashes on Android
4107 VULKAN ANDROID : KHR-GLES31.core.shader_storage_buffer_object.advanced-unsizedArrayLength* = SKIP
// Passing on recent drivers:
3726 VULKAN ANDROID : KHR-GLES31.core.shader_atomic_counters.* = FAIL
3726 VULKAN ANDROID : KHR-GLES31.core.shader_atomic_counters.advanced-usage-* = SKIP
// Explicit uniform locations:
4219 VULKAN PIXEL2ORXL : KHR-GLES31.core.explicit_uniform_location.uniform-loc-mix-with-implicit-max = FAIL
4219 VULKAN PIXEL2ORXL : KHR-GLES31.core.explicit_uniform_location.uniform-loc-mix-with-implicit-max-array = FAIL
......
......@@ -55,7 +55,6 @@
// Android failures
// Dynamic indexing features not supported on Qualcomm
5435 VULKAN ANDROID : KHR-GLES32.core.gpu_shader5.atomic_counters_array_indexing = FAIL
5435 VULKAN ANDROID : KHR-GLES32.core.gpu_shader5.images_array_indexing = FAIL
// Texture buffer failures
......
......@@ -115,10 +115,6 @@ TEST_P(AtomicCounterBufferTest31, OffsetNotAllSpecifiedWithSameValue)
// Tests atomic counter reads using compute shaders. Used as a confidence check for the translator.
TEST_P(AtomicCounterBufferTest31, AtomicCounterReadCompute)
{
// Skipping due to a bug on the Qualcomm Vulkan Android driver.
// http://anglebug.com/3726
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
// Skipping due to a bug on the Adreno OpenGLES Android driver.
// http://anglebug.com/2925
ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES());
......@@ -148,10 +144,6 @@ void main()
// Test atomic counter read.
TEST_P(AtomicCounterBufferTest31, AtomicCounterRead)
{
// Skipping due to a bug on the Qualcomm Vulkan Android driver.
// http://anglebug.com/3726
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
// Skipping test while we work on enabling atomic counter buffer support in th D3D renderer.
// http://anglebug.com/1729
ANGLE_SKIP_TEST_IF(IsD3D11());
......@@ -191,11 +183,7 @@ TEST_P(AtomicCounterBufferTest31, AtomicCounterBufferRangeRead)
{
// Skipping due to a bug on the Qualcomm driver.
// http://anglebug.com/3726
ANGLE_SKIP_TEST_IF(IsNexus5X());
// Skipping due to a bug on the Qualcomm Vulkan Android driver.
// http://anglebug.com/3726
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
ANGLE_SKIP_TEST_IF(IsNexus5X() && IsOpenGLES());
// Skipping test while we work on enabling atomic counter buffer support in th D3D renderer.
// http://anglebug.com/1729
......@@ -245,10 +233,6 @@ TEST_P(AtomicCounterBufferTest31, AtomicCounterBufferRangeRead)
// there are bugs in how we count valid bindings.
TEST_P(AtomicCounterBufferTest31, AtomicCounterBufferRepeatedBindUnbind)
{
// Skipping due to a bug on the Qualcomm Vulkan Android driver.
// http://anglebug.com/3726
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
// Skipping test while we work on enabling atomic counter buffer support in th D3D renderer.
// http://anglebug.com/1729
ANGLE_SKIP_TEST_IF(IsD3D11());
......@@ -319,10 +303,6 @@ TEST_P(AtomicCounterBufferTest31, AtomicCounterBufferRepeatedBindUnbind)
// Test atomic counter increment and decrement.
TEST_P(AtomicCounterBufferTest31, AtomicCounterIncrementAndDecrement)
{
// Skipping due to a bug on the Qualcomm Vulkan Android driver.
// http://anglebug.com/3726
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
constexpr char kCS[] =
"#version 310 es\n"
"layout(local_size_x=1, local_size_y=1, local_size_z=1) in;\n"
......@@ -364,10 +344,6 @@ TEST_P(AtomicCounterBufferTest31, AtomicCounterIncrementAndDecrement)
// Tests multiple atomic counter buffers.
TEST_P(AtomicCounterBufferTest31, AtomicCounterMultipleBuffers)
{
// Skipping due to a bug on the Qualcomm Vulkan Android driver.
// http://anglebug.com/3726
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
GLint maxAtomicCounterBuffers = 0;
glGetIntegerv(GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS, &maxAtomicCounterBuffers);
constexpr unsigned int kBufferCount = 3;
......@@ -430,10 +406,6 @@ TEST_P(AtomicCounterBufferTest31, AtomicCounterArrayOfArray)
// Intel's Windows OpenGL driver crashes in this test. http://anglebug.com/3791
ANGLE_SKIP_TEST_IF(IsOpenGL() && IsIntel() && IsWindows());
// Skipping due to a bug on the Qualcomm Vulkan Android driver.
// http://anglebug.com/3726
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
constexpr char kCS[] = R"(#version 310 es
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
layout(binding = 0) uniform atomic_uint ac[7][5][3];
......
......@@ -401,10 +401,6 @@ TEST_P(ComputeShaderTest, BufferImageBuffer)
// See http://anglebug.com/3536
ANGLE_SKIP_TEST_IF(IsOpenGL() && IsIntel() && IsWindows());
// Skipping due to a bug on the Qualcomm Vulkan Android driver.
// http://anglebug.com/3726
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
constexpr char kCS0[] = R"(#version 310 es
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
layout(binding = 0, offset = 4) uniform atomic_uint ac[2];
......@@ -476,10 +472,6 @@ TEST_P(ComputeShaderTest, ImageAtomicCounterBuffer)
// Flaky hang. http://anglebug.com/3636
ANGLE_SKIP_TEST_IF(IsWindows() && IsNVIDIA() && IsDesktopOpenGL());
// Skipping due to a bug on the Qualcomm Vulkan Android driver.
// http://anglebug.com/3726
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
constexpr char kCS0[] = R"(#version 310 es
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
layout(r32ui, binding = 0) writeonly uniform highp uimage2D uImage[2];
......@@ -3934,10 +3926,6 @@ TEST_P(ComputeShaderTest, DispatchConvertVertexDispatch)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_vertex_type_10_10_10_2"));
// Skipping due to a bug on the Qualcomm Vulkan Android driver.
// http://anglebug.com/3726
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
constexpr uint32_t kVertexCount = 6;
constexpr char kCS[] = R"(#version 310 es
......
......@@ -3265,10 +3265,6 @@ TEST_P(GLSLTest_ES31, AtomicCounterArrayLength)
// http://anglebug.com/3782
ANGLE_SKIP_TEST_IF(IsOpenGL());
// Skipping due to a bug on the Qualcomm Vulkan Android driver.
// http://anglebug.com/3726
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
constexpr char kCS[] = R"(#version 310 es
precision mediump float;
layout(local_size_x=1) in;
......
......@@ -890,8 +890,6 @@ TEST_P(ProgramBinaryES31Test, ProgramBinaryWithComputeShader)
// Tests that saving and loading a program attached with computer shader.
TEST_P(ProgramBinaryES31Test, ProgramBinaryWithAtomicCounterComputeShader)
{
// http://anglebug.com/4092
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
// We can't run the test if no program binary formats are supported.
GLint binaryFormatCount = 0;
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &binaryFormatCount);
......
......@@ -58,10 +58,6 @@ class VulkanDescriptorSetTest : public ANGLETest
// Test atomic counter read.
TEST_P(VulkanDescriptorSetTest, AtomicCounterReadLimitedDescriptorPool)
{
// Skipping due to a bug on the Qualcomm Vulkan Android driver.
// http://anglebug.com/3726
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
// Skipping test while we work on enabling atomic counter buffer support in th D3D renderer.
// http://anglebug.com/1729
ANGLE_SKIP_TEST_IF(IsD3D11());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment