зеркало из https://github.com/AvaloniaUI/angle.git
Vulkan: Redo RewriteStructSamplers
This transformation is split into two. The first transformation solely takes out the samplers out of structs, and potentially generates array of array of samplers. A second transformation is added that takes any array of array of opaque uniforms and flattens it. A follow up change will simplify RewriteAtomicCounters which also handles array of arrays (which is no longer possible), and removes dependency on shaderStorageBufferArrayDynamicIndexing. Bug: angleproject:2703 Bug: angleproject:3881 Bug: angleproject:4071 Bug: angleproject:4211 Change-Id: I352bb2bbe65ac49f4d7d753c0ba3160fa3cc925a Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2628138 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Tim Van Patten <timvp@google.com> Reviewed-by: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Родитель
9f09435bc1
Коммит
60015ff672
|
@ -305,9 +305,7 @@ const ShCompileOptions SH_TAKE_VIDEO_TEXTURE_AS_EXTERNAL_OES = UINT64_C(1) << 45
|
|||
// If requested, validates the AST after every transformation. Useful for debugging.
|
||||
const ShCompileOptions SH_VALIDATE_AST = UINT64_C(1) << 46;
|
||||
|
||||
// Use old version of RewriteStructSamplers, which doesn't produce as many
|
||||
// sampler arrays in parameters. This causes a few tests to pass on Android.
|
||||
const ShCompileOptions SH_USE_OLD_REWRITE_STRUCT_SAMPLERS = UINT64_C(1) << 47;
|
||||
// Bit 47 is available
|
||||
|
||||
// This flag works around a inconsistent behavior in Mac AMD driver where gl_VertexID doesn't
|
||||
// include base vertex value. It replaces gl_VertexID with (gl_VertexID + angle_BaseVertex)
|
||||
|
|
|
@ -255,14 +255,6 @@ struct FeaturesVk : FeatureSetBase
|
|||
"Seamful cube map emulation misbehaves on some drivers, so it's disallowed", &members,
|
||||
"http://anglebug.com/3243"};
|
||||
|
||||
// Qualcomm and SwiftShader shader compiler doesn't support sampler arrays as parameters, so
|
||||
// revert to old RewriteStructSamplers behavior, which produces fewer.
|
||||
Feature forceOldRewriteStructSamplers = {
|
||||
"forceOldRewriteStructSamplers", FeatureCategory::VulkanWorkarounds,
|
||||
"Some shader compilers don't support sampler arrays as parameters, so revert to old "
|
||||
"RewriteStructSamplers behavior, which produces fewer.",
|
||||
&members, "http://anglebug.com/2703"};
|
||||
|
||||
// Vulkan considers vertex attribute accesses to count up to the last multiple of the stride.
|
||||
// This additional access supports AMD's robust buffer access implementation.
|
||||
// AMDVLK in particular will return incorrect values when the vertex access extends into the
|
||||
|
|
|
@ -321,6 +321,8 @@ angle_translator_lib_vulkan_sources = [
|
|||
"src/compiler/translator/tree_ops/vulkan/RemoveAtomicCounterBuiltins.h",
|
||||
"src/compiler/translator/tree_ops/vulkan/RemoveInactiveInterfaceVariables.cpp",
|
||||
"src/compiler/translator/tree_ops/vulkan/RemoveInactiveInterfaceVariables.h",
|
||||
"src/compiler/translator/tree_ops/vulkan/RewriteArrayOfArrayOfOpaqueUniforms.cpp",
|
||||
"src/compiler/translator/tree_ops/vulkan/RewriteArrayOfArrayOfOpaqueUniforms.h",
|
||||
"src/compiler/translator/tree_ops/vulkan/RewriteAtomicCounters.cpp",
|
||||
"src/compiler/translator/tree_ops/vulkan/RewriteAtomicCounters.h",
|
||||
"src/compiler/translator/tree_ops/vulkan/RewriteCubeMapSamplersAs2DArray.cpp",
|
||||
|
@ -331,7 +333,6 @@ angle_translator_lib_vulkan_sources = [
|
|||
"src/compiler/translator/tree_ops/vulkan/RewriteInterpolateAtOffset.h",
|
||||
"src/compiler/translator/tree_ops/vulkan/RewriteStructSamplers.cpp",
|
||||
"src/compiler/translator/tree_ops/vulkan/RewriteStructSamplers.h",
|
||||
"src/compiler/translator/tree_ops/vulkan/RewriteStructSamplersOld.cpp",
|
||||
]
|
||||
if (is_android) {
|
||||
angle_translator_sources += [
|
||||
|
|
|
@ -56,10 +56,9 @@ constexpr ShCompileOptions kGLSLMacOnlyOptions =
|
|||
|
||||
// Options supported by Vulkan GLSL only
|
||||
constexpr ShCompileOptions kVulkanGLSLOnlyOptions =
|
||||
SH_ADD_PRE_ROTATION | SH_USE_OLD_REWRITE_STRUCT_SAMPLERS |
|
||||
SH_EMULATE_SEAMFUL_CUBE_MAP_SAMPLING | SH_ADD_BRESENHAM_LINE_RASTER_EMULATION |
|
||||
SH_EARLY_FRAGMENT_TESTS_OPTIMIZATION | SH_USE_SPECIALIZATION_CONSTANT |
|
||||
SH_ADD_VULKAN_XFB_EMULATION_SUPPORT_CODE;
|
||||
SH_ADD_PRE_ROTATION | SH_EMULATE_SEAMFUL_CUBE_MAP_SAMPLING |
|
||||
SH_ADD_BRESENHAM_LINE_RASTER_EMULATION | SH_EARLY_FRAGMENT_TESTS_OPTIMIZATION |
|
||||
SH_USE_SPECIALIZATION_CONSTANT | SH_ADD_VULKAN_XFB_EMULATION_SUPPORT_CODE;
|
||||
|
||||
// Options supported by HLSL output only
|
||||
constexpr ShCompileOptions kHLSLOnlyOptions = SH_EXPAND_SELECT_HLSL_INTEGER_POW_EXPRESSIONS |
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "compiler/translator/tree_ops/vulkan/NameEmbeddedUniformStructs.h"
|
||||
#include "compiler/translator/tree_ops/vulkan/RemoveAtomicCounterBuiltins.h"
|
||||
#include "compiler/translator/tree_ops/vulkan/RemoveInactiveInterfaceVariables.h"
|
||||
#include "compiler/translator/tree_ops/vulkan/RewriteArrayOfArrayOfOpaqueUniforms.h"
|
||||
#include "compiler/translator/tree_ops/vulkan/RewriteAtomicCounters.h"
|
||||
#include "compiler/translator/tree_ops/vulkan/RewriteCubeMapSamplersAs2DArray.h"
|
||||
#include "compiler/translator/tree_ops/vulkan/RewriteDfdy.h"
|
||||
|
@ -812,21 +813,9 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool rewriteStructSamplersResult;
|
||||
int removedUniformsCount;
|
||||
|
||||
if (compileOptions & SH_USE_OLD_REWRITE_STRUCT_SAMPLERS)
|
||||
{
|
||||
rewriteStructSamplersResult =
|
||||
RewriteStructSamplersOld(this, root, &getSymbolTable(), &removedUniformsCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
rewriteStructSamplersResult =
|
||||
RewriteStructSamplers(this, root, &getSymbolTable(), &removedUniformsCount);
|
||||
}
|
||||
|
||||
if (!rewriteStructSamplersResult)
|
||||
if (!RewriteStructSamplers(this, root, &getSymbolTable(), &removedUniformsCount))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -841,6 +830,15 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
|
|||
}
|
||||
}
|
||||
|
||||
// Replace array of array of opaque uniforms with a flattened array. This is run after
|
||||
// MonomorphizeUnsupportedFunctionsInVulkanGLSL and RewriteStructSamplers so that it's not
|
||||
// possible for an array of array of opaque type to be partially subscripted and passed to a
|
||||
// function.
|
||||
if (!RewriteArrayOfArrayOfOpaqueUniforms(this, root, &getSymbolTable()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rewrite samplerCubes as sampler2DArrays. This must be done after rewriting struct samplers
|
||||
// as it doesn't expect that.
|
||||
if (compileOptions & SH_EMULATE_SEAMFUL_CUBE_MAP_SAMPLING)
|
||||
|
|
|
@ -42,6 +42,8 @@ class RemoveArrayLengthTraverser : public TIntermTraverser
|
|||
bool foundArrayLength() const { return mFoundArrayLength; }
|
||||
|
||||
private:
|
||||
void insertSideEffectsInParentBlock(TIntermTyped *node);
|
||||
|
||||
bool mFoundArrayLength;
|
||||
};
|
||||
|
||||
|
@ -51,12 +53,7 @@ bool RemoveArrayLengthTraverser::visitUnary(Visit visit, TIntermUnary *node)
|
|||
if (node->getOp() == EOpArrayLength && !node->getOperand()->getType().isUnsizedArray())
|
||||
{
|
||||
mFoundArrayLength = true;
|
||||
if (!node->getOperand()->hasSideEffects())
|
||||
{
|
||||
queueReplacement(node->fold(nullptr), OriginalNode::IS_DROPPED);
|
||||
return false;
|
||||
}
|
||||
insertStatementInParentBlock(node->getOperand()->deepCopy());
|
||||
insertSideEffectsInParentBlock(node->getOperand());
|
||||
TConstantUnion *constArray = new TConstantUnion[1];
|
||||
constArray->setIConst(node->getOperand()->getOutermostArraySize());
|
||||
queueReplacement(new TIntermConstantUnion(constArray, node->getType()),
|
||||
|
@ -66,6 +63,28 @@ bool RemoveArrayLengthTraverser::visitUnary(Visit visit, TIntermUnary *node)
|
|||
return true;
|
||||
}
|
||||
|
||||
void RemoveArrayLengthTraverser::insertSideEffectsInParentBlock(TIntermTyped *node)
|
||||
{
|
||||
// If the node is an index type, traverse it and add the indices as side effects. If at the end
|
||||
// an expression without side effect is encountered, such as an opaque uniform or a lone symbol,
|
||||
// don't generate a statement for it.
|
||||
if (!node->hasSideEffects())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TIntermBinary *asBinary = node->getAsBinaryNode();
|
||||
if (asBinary && !asBinary->isAssignment())
|
||||
{
|
||||
insertSideEffectsInParentBlock(asBinary->getLeft());
|
||||
insertSideEffectsInParentBlock(asBinary->getRight());
|
||||
}
|
||||
else
|
||||
{
|
||||
insertStatementInParentBlock(node);
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
bool RemoveArrayLengthMethod(TCompiler *compiler, TIntermBlock *root)
|
||||
|
|
|
@ -0,0 +1,357 @@
|
|||
//
|
||||
// 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.
|
||||
//
|
||||
// RewriteAtomicCounters: Emulate atomic counter buffers with storage buffers.
|
||||
//
|
||||
|
||||
#include "compiler/translator/tree_ops/vulkan/RewriteAtomicCounters.h"
|
||||
|
||||
#include "compiler/translator/Compiler.h"
|
||||
#include "compiler/translator/ImmutableStringBuilder.h"
|
||||
#include "compiler/translator/StaticType.h"
|
||||
#include "compiler/translator/SymbolTable.h"
|
||||
#include "compiler/translator/tree_util/IntermNode_util.h"
|
||||
#include "compiler/translator/tree_util/IntermTraverse.h"
|
||||
#include "compiler/translator/tree_util/ReplaceVariable.h"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct UniformData
|
||||
{
|
||||
// Corresponding to an array of array of opaque uniform variable, this is the flattened variable
|
||||
// that is replacing it.
|
||||
const TVariable *flattened;
|
||||
// Assume a general case of array declaration with N dimensions:
|
||||
//
|
||||
// uniform type u[Dn]..[D2][D1];
|
||||
//
|
||||
// Let's define
|
||||
//
|
||||
// Pn = D(n-1)*...*D2*D1
|
||||
//
|
||||
// In that case, we have:
|
||||
//
|
||||
// u[In] = ac + In*Pn
|
||||
// u[In][I(n-1)] = ac + In*Pn + I(n-1)*P(n-1)
|
||||
// u[In]...[Ii] = ac + In*Pn + ... + Ii*Pi
|
||||
//
|
||||
// This array contains Pi. Note that the like TType::mArraySizes, the last element is the
|
||||
// outermost dimension. Element 0 is necessarily 1.
|
||||
TVector<unsigned int> mSubArraySizes;
|
||||
};
|
||||
|
||||
using UniformMap = angle::HashMap<const TVariable *, UniformData>;
|
||||
|
||||
TIntermTyped *RewriteArrayOfArraySubscriptExpression(TCompiler *compiler,
|
||||
TIntermBinary *node,
|
||||
const UniformMap &uniformMap);
|
||||
|
||||
// Given an expression, this traverser calculates a new expression where array of array of opaque
|
||||
// uniforms are replaced with their flattened ones. In particular, this is run on the right node of
|
||||
// EOpIndexIndirect binary nodes, so that the expression in the index gets a chance to go through
|
||||
// this transformation.
|
||||
class RewriteExpressionTraverser final : public TIntermTraverser
|
||||
{
|
||||
public:
|
||||
explicit RewriteExpressionTraverser(TCompiler *compiler, const UniformMap &uniformMap)
|
||||
: TIntermTraverser(true, false, false), mCompiler(compiler), mUniformMap(uniformMap)
|
||||
{}
|
||||
|
||||
bool visitBinary(Visit visit, TIntermBinary *node) override
|
||||
{
|
||||
// Only interested in opaque uniforms.
|
||||
if (!IsOpaqueType(node->getType().getBasicType()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
TIntermTyped *rewritten =
|
||||
RewriteArrayOfArraySubscriptExpression(mCompiler, node, mUniformMap);
|
||||
if (rewritten == nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
queueReplacement(rewritten, OriginalNode::IS_DROPPED);
|
||||
|
||||
// Don't iterate as the expression is rewritten.
|
||||
return false;
|
||||
}
|
||||
|
||||
void visitSymbol(TIntermSymbol *node) override
|
||||
{
|
||||
// We cannot reach here for an opaque uniform that is being replaced. visitBinary should
|
||||
// have taken care of it.
|
||||
ASSERT(!IsOpaqueType(node->getType().getBasicType()) ||
|
||||
mUniformMap.find(&node->variable()) == mUniformMap.end());
|
||||
}
|
||||
|
||||
private:
|
||||
TCompiler *mCompiler;
|
||||
|
||||
const UniformMap &mUniformMap;
|
||||
};
|
||||
|
||||
// Rewrite the index of an EOpIndexIndirect expression. The root can never need replacing, because
|
||||
// it cannot be an opaque uniform itself.
|
||||
void RewriteIndexExpression(TCompiler *compiler,
|
||||
TIntermTyped *expression,
|
||||
const UniformMap &uniformMap)
|
||||
{
|
||||
RewriteExpressionTraverser traverser(compiler, uniformMap);
|
||||
expression->traverse(&traverser);
|
||||
bool valid = traverser.updateTree(compiler, expression);
|
||||
ASSERT(valid);
|
||||
}
|
||||
|
||||
// Given an expression such as the following:
|
||||
//
|
||||
// EOpIndex(In)Direct (opaque uniform)
|
||||
// / \
|
||||
// EOpIndex(In)Direct I1
|
||||
// / \
|
||||
// ... I2
|
||||
// /
|
||||
// EOpIndex(In)Direct
|
||||
// / \
|
||||
// uniform In
|
||||
//
|
||||
// produces:
|
||||
//
|
||||
// EOpIndex(In)Direct
|
||||
// / \
|
||||
// uniform In*Pn + ... + I2*P2 + I1*P1
|
||||
//
|
||||
TIntermTyped *RewriteArrayOfArraySubscriptExpression(TCompiler *compiler,
|
||||
TIntermBinary *node,
|
||||
const UniformMap &uniformMap)
|
||||
{
|
||||
ASSERT(IsOpaqueType(node->getType().getBasicType()));
|
||||
|
||||
TIntermSymbol *opaqueUniform = nullptr;
|
||||
|
||||
// Iterate once and find the opaque uniform that's being indexed.
|
||||
TIntermBinary *iter = node;
|
||||
while (opaqueUniform == nullptr)
|
||||
{
|
||||
ASSERT(iter->getOp() == EOpIndexDirect || iter->getOp() == EOpIndexIndirect);
|
||||
|
||||
opaqueUniform = iter->getLeft()->getAsSymbolNode();
|
||||
iter = iter->getLeft()->getAsBinaryNode();
|
||||
}
|
||||
|
||||
// If not being replaced, there's nothing to do.
|
||||
auto flattenedIter = uniformMap.find(&opaqueUniform->variable());
|
||||
if (flattenedIter == uniformMap.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const UniformData &data = flattenedIter->second;
|
||||
|
||||
// Iterate again and build the index expression. The index expression constitutes the sum of
|
||||
// the variable indices plus a constant offset calculated from the constant indices. For
|
||||
// example, smplr[1][x][2][y] will have an index of x*P3 + y*P1 + c, where c = (1*P4 + 2*P2).
|
||||
unsigned int constantOffset = 0;
|
||||
TIntermTyped *variableIndex = nullptr;
|
||||
|
||||
// Since the opaque uniforms are fully subscripted, we know exactly how many EOpIndex* nodes
|
||||
// there should be.
|
||||
for (size_t dimIndex = 0; dimIndex < data.mSubArraySizes.size(); ++dimIndex)
|
||||
{
|
||||
ASSERT(node);
|
||||
|
||||
unsigned int subArraySize = data.mSubArraySizes[dimIndex];
|
||||
|
||||
switch (node->getOp())
|
||||
{
|
||||
case EOpIndexDirect:
|
||||
// Accumulate the constant index.
|
||||
constantOffset +=
|
||||
node->getRight()->getAsConstantUnion()->getIConst(0) * subArraySize;
|
||||
break;
|
||||
case EOpIndexIndirect:
|
||||
{
|
||||
// Run RewriteExpressionTraverser on the right node. It may itself be an expression
|
||||
// with an array of array of opaque uniform inside that needs to be rewritten.
|
||||
TIntermTyped *indexExpression = node->getRight();
|
||||
RewriteIndexExpression(compiler, indexExpression, uniformMap);
|
||||
|
||||
// Scale and accumulate.
|
||||
if (subArraySize != 1)
|
||||
{
|
||||
indexExpression =
|
||||
new TIntermBinary(EOpMul, indexExpression, CreateIndexNode(subArraySize));
|
||||
}
|
||||
|
||||
if (variableIndex == nullptr)
|
||||
{
|
||||
variableIndex = indexExpression;
|
||||
}
|
||||
else
|
||||
{
|
||||
variableIndex = new TIntermBinary(EOpAdd, variableIndex, indexExpression);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
|
||||
node = node->getLeft()->getAsBinaryNode();
|
||||
}
|
||||
|
||||
// Add the two accumulated indices together.
|
||||
TIntermTyped *index = nullptr;
|
||||
if (constantOffset == 0 && variableIndex != nullptr)
|
||||
{
|
||||
// No constant offset, but there's variable offset. Take that as offset.
|
||||
index = variableIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Either the constant offset is non zero, or there's no variable offset (so constant 0
|
||||
// should be used).
|
||||
index = CreateIndexNode(constantOffset);
|
||||
|
||||
if (variableIndex)
|
||||
{
|
||||
index = new TIntermBinary(EOpAdd, index, variableIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// Create an index into the flattened uniform.
|
||||
TOperator op = variableIndex ? EOpIndexIndirect : EOpIndexDirect;
|
||||
return new TIntermBinary(op, new TIntermSymbol(data.flattened), index);
|
||||
}
|
||||
|
||||
// Traverser that takes:
|
||||
//
|
||||
// uniform sampler/image/atomic_uint u[N][M]..
|
||||
//
|
||||
// and transforms it to:
|
||||
//
|
||||
// uniform sampler/image/atomic_uint u[N * M * ..]
|
||||
//
|
||||
// MonomorphizeUnsupportedFunctionsInVulkanGLSL makes it impossible for this array to be partially
|
||||
// subscripted, or passed as argument to a function unsubscripted. This means that every encounter
|
||||
// of this uniform can be expected to be fully subscripted.
|
||||
//
|
||||
class RewriteArrayOfArrayOfOpaqueUniformsTraverser : public TIntermTraverser
|
||||
{
|
||||
public:
|
||||
RewriteArrayOfArrayOfOpaqueUniformsTraverser(TCompiler *compiler, TSymbolTable *symbolTable)
|
||||
: TIntermTraverser(true, false, false, symbolTable), mCompiler(compiler)
|
||||
{}
|
||||
|
||||
bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
|
||||
{
|
||||
if (!mInGlobalScope)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const TIntermSequence &sequence = *(node->getSequence());
|
||||
|
||||
TIntermTyped *variable = sequence.front()->getAsTyped();
|
||||
const TType &type = variable->getType();
|
||||
bool isOpaqueUniform =
|
||||
type.getQualifier() == EvqUniform && IsOpaqueType(type.getBasicType());
|
||||
|
||||
// Only interested in array of array of opaque uniforms.
|
||||
if (!isOpaqueUniform || !type.isArrayOfArrays())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Opaque uniforms cannot have initializers, so the declaration must necessarily be a
|
||||
// symbol.
|
||||
TIntermSymbol *symbol = variable->getAsSymbolNode();
|
||||
ASSERT(symbol != nullptr);
|
||||
|
||||
const TVariable *uniformVariable = &symbol->variable();
|
||||
|
||||
// Create an entry in the map.
|
||||
ASSERT(mUniformMap.find(uniformVariable) == mUniformMap.end());
|
||||
UniformData &data = mUniformMap[uniformVariable];
|
||||
|
||||
// Calculate the accumulated dimension products. See UniformData::mSubArraySizes.
|
||||
const TSpan<const unsigned int> &arraySizes = type.getArraySizes();
|
||||
mUniformMap[uniformVariable].mSubArraySizes.resize(arraySizes.size());
|
||||
unsigned int runningProduct = 1;
|
||||
for (size_t dimension = 0; dimension < arraySizes.size(); ++dimension)
|
||||
{
|
||||
data.mSubArraySizes[dimension] = runningProduct;
|
||||
runningProduct *= arraySizes[dimension];
|
||||
}
|
||||
|
||||
// Create a replacement variable with the array flattened.
|
||||
TType *newType = new TType(type);
|
||||
newType->toArrayBaseType();
|
||||
newType->makeArray(runningProduct);
|
||||
|
||||
data.flattened = new TVariable(mSymbolTable, uniformVariable->name(), newType,
|
||||
uniformVariable->symbolType());
|
||||
|
||||
TIntermDeclaration *decl = new TIntermDeclaration;
|
||||
decl->appendDeclarator(new TIntermSymbol(data.flattened));
|
||||
|
||||
queueReplacement(decl, OriginalNode::IS_DROPPED);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
|
||||
{
|
||||
// As an optimization, don't bother inspecting functions if there aren't any opaque uniforms
|
||||
// to replace.
|
||||
return !mUniformMap.empty();
|
||||
}
|
||||
|
||||
// Same implementation as in RewriteExpressionTraverser. That traverser cannot replace root.
|
||||
bool visitBinary(Visit visit, TIntermBinary *node) override
|
||||
{
|
||||
// Only interested in opaque uniforms.
|
||||
if (!IsOpaqueType(node->getType().getBasicType()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
TIntermTyped *rewritten =
|
||||
RewriteArrayOfArraySubscriptExpression(mCompiler, node, mUniformMap);
|
||||
if (rewritten == nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
queueReplacement(rewritten, OriginalNode::IS_DROPPED);
|
||||
|
||||
// Don't iterate as the expression is rewritten.
|
||||
return false;
|
||||
}
|
||||
|
||||
void visitSymbol(TIntermSymbol *node) override
|
||||
{
|
||||
ASSERT(!IsOpaqueType(node->getType().getBasicType()) ||
|
||||
mUniformMap.find(&node->variable()) == mUniformMap.end());
|
||||
}
|
||||
|
||||
private:
|
||||
TCompiler *mCompiler;
|
||||
UniformMap mUniformMap;
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
bool RewriteArrayOfArrayOfOpaqueUniforms(TCompiler *compiler,
|
||||
TIntermBlock *root,
|
||||
TSymbolTable *symbolTable)
|
||||
{
|
||||
RewriteArrayOfArrayOfOpaqueUniformsTraverser traverser(compiler, symbolTable);
|
||||
root->traverse(&traverser);
|
||||
return traverser.updateTree(compiler, root);
|
||||
}
|
||||
} // namespace sh
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// Copyright 2021 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.
|
||||
//
|
||||
// RewriteArrayOfArrayOfOpaqueUniforms: Flatten array of array of opaque uniforms. Requires
|
||||
// MonomorphizeUnsupportedFunctionsInVulkanGLSL and RewriteStructSamplers to have been run.
|
||||
|
||||
#ifndef COMPILER_TRANSLATOR_TREEOPS_VULKAN_REWRITEARRAYOFARRAYOFOPAQUEUNIFORMS_H_
|
||||
#define COMPILER_TRANSLATOR_TREEOPS_VULKAN_REWRITEARRAYOFARRAYOFOPAQUEUNIFORMS_H_
|
||||
|
||||
#include "common/angleutils.h"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
class TCompiler;
|
||||
class TIntermBlock;
|
||||
class TSymbolTable;
|
||||
|
||||
ANGLE_NO_DISCARD bool RewriteArrayOfArrayOfOpaqueUniforms(TCompiler *compiler,
|
||||
TIntermBlock *root,
|
||||
TSymbolTable *symbolTable);
|
||||
} // namespace sh
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_TREEOPS_VULKAN_REWRITEARRAYOFARRAYOFOPAQUEUNIFORMS_H_
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -5,10 +5,10 @@
|
|||
//
|
||||
// RewriteStructSamplers: Extract structs from samplers.
|
||||
//
|
||||
// This traverser is designed to strip out samplers from structs. It moves them into
|
||||
// separate uniform sampler declarations. This allows the struct to be stored in the
|
||||
// default uniform block. It also requires that we rewrite any functions that take the
|
||||
// struct as an argument. The struct is split into two arguments.
|
||||
// This traverser is designed to strip out samplers from structs. It moves them into separate
|
||||
// uniform sampler declarations. This allows the struct to be stored in the default uniform block.
|
||||
// This transformation requires MonomorphizeUnsupportedFunctionsInVulkanGLSL to have been run so it
|
||||
// wouldn't need to deal with functions that are passed such structs.
|
||||
//
|
||||
// For example:
|
||||
// struct S { sampler2D samp; int i; };
|
||||
|
@ -33,10 +33,6 @@ ANGLE_NO_DISCARD bool RewriteStructSamplers(TCompiler *compiler,
|
|||
TIntermBlock *root,
|
||||
TSymbolTable *symbolTable,
|
||||
int *removedUniformsCountOut);
|
||||
ANGLE_NO_DISCARD bool RewriteStructSamplersOld(TCompiler *compier,
|
||||
TIntermBlock *root,
|
||||
TSymbolTable *symbolTable,
|
||||
int *removedUniformsCountOut);
|
||||
} // namespace sh
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_TREEOPS_VULKAN_REWRITESTRUCTSAMPLERS_H_
|
||||
|
|
|
@ -1,705 +0,0 @@
|
|||
//
|
||||
// Copyright 2018 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.
|
||||
//
|
||||
// RewriteStructSamplers: Extract structs from samplers.
|
||||
//
|
||||
|
||||
#include "compiler/translator/tree_ops/vulkan/RewriteStructSamplers.h"
|
||||
|
||||
#include "compiler/translator/ImmutableStringBuilder.h"
|
||||
#include "compiler/translator/SymbolTable.h"
|
||||
#include "compiler/translator/tree_util/IntermTraverse.h"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// Helper method to get the sampler extracted struct type of a parameter.
|
||||
TType *GetStructSamplerParameterType(TSymbolTable *symbolTable, const TVariable ¶m)
|
||||
{
|
||||
const TStructure *structure = param.getType().getStruct();
|
||||
const TSymbol *structSymbol = symbolTable->findUserDefined(structure->name());
|
||||
ASSERT(structSymbol && structSymbol->isStruct());
|
||||
const TStructure *structVar = static_cast<const TStructure *>(structSymbol);
|
||||
TType *structType = new TType(structVar, false);
|
||||
|
||||
if (param.getType().isArray())
|
||||
{
|
||||
structType->makeArrays(param.getType().getArraySizes());
|
||||
}
|
||||
|
||||
ASSERT(!structType->isStructureContainingSamplers());
|
||||
|
||||
return structType;
|
||||
}
|
||||
|
||||
TIntermSymbol *ReplaceTypeOfSymbolNode(TIntermSymbol *symbolNode, TSymbolTable *symbolTable)
|
||||
{
|
||||
const TVariable &oldVariable = symbolNode->variable();
|
||||
|
||||
TType *newType = GetStructSamplerParameterType(symbolTable, oldVariable);
|
||||
|
||||
TVariable *newVariable =
|
||||
new TVariable(oldVariable.uniqueId(), oldVariable.name(), oldVariable.symbolType(),
|
||||
oldVariable.extension(), newType);
|
||||
return new TIntermSymbol(newVariable);
|
||||
}
|
||||
|
||||
TIntermTyped *ReplaceTypeOfTypedStructNode(TIntermTyped *argument, TSymbolTable *symbolTable)
|
||||
{
|
||||
TIntermSymbol *asSymbol = argument->getAsSymbolNode();
|
||||
if (asSymbol)
|
||||
{
|
||||
ASSERT(asSymbol->getType().getStruct());
|
||||
return ReplaceTypeOfSymbolNode(asSymbol, symbolTable);
|
||||
}
|
||||
|
||||
TIntermTyped *replacement = argument->deepCopy();
|
||||
TIntermBinary *binary = replacement->getAsBinaryNode();
|
||||
ASSERT(binary);
|
||||
|
||||
while (binary)
|
||||
{
|
||||
ASSERT(binary->getOp() == EOpIndexDirectStruct || binary->getOp() == EOpIndexDirect);
|
||||
|
||||
asSymbol = binary->getLeft()->getAsSymbolNode();
|
||||
|
||||
if (asSymbol)
|
||||
{
|
||||
ASSERT(asSymbol->getType().getStruct());
|
||||
TIntermSymbol *newSymbol = ReplaceTypeOfSymbolNode(asSymbol, symbolTable);
|
||||
binary->replaceChildNode(binary->getLeft(), newSymbol);
|
||||
return replacement;
|
||||
}
|
||||
|
||||
binary = binary->getLeft()->getAsBinaryNode();
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Maximum string size of a hex unsigned int.
|
||||
constexpr size_t kHexSize = ImmutableStringBuilder::GetHexCharCount<unsigned int>();
|
||||
|
||||
class Traverser final : public TIntermTraverser
|
||||
{
|
||||
public:
|
||||
explicit Traverser(TSymbolTable *symbolTable)
|
||||
: TIntermTraverser(true, false, true, symbolTable), mRemovedUniformsCount(0)
|
||||
{
|
||||
mSymbolTable->push();
|
||||
}
|
||||
|
||||
~Traverser() override { mSymbolTable->pop(); }
|
||||
|
||||
int removedUniformsCount() const { return mRemovedUniformsCount; }
|
||||
|
||||
// Each struct sampler declaration is stripped of its samplers. New uniforms are added for each
|
||||
// stripped struct sampler.
|
||||
bool visitDeclaration(Visit visit, TIntermDeclaration *decl) override
|
||||
{
|
||||
if (visit != PreVisit)
|
||||
return true;
|
||||
|
||||
if (!mInGlobalScope)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const TIntermSequence &sequence = *(decl->getSequence());
|
||||
TIntermTyped *declarator = sequence.front()->getAsTyped();
|
||||
const TType &type = declarator->getType();
|
||||
|
||||
if (type.isStructureContainingSamplers())
|
||||
{
|
||||
TIntermSequence *newSequence = new TIntermSequence;
|
||||
|
||||
if (type.isStructSpecifier())
|
||||
{
|
||||
stripStructSpecifierSamplers(type.getStruct(), newSequence);
|
||||
}
|
||||
else
|
||||
{
|
||||
TIntermSymbol *asSymbol = declarator->getAsSymbolNode();
|
||||
ASSERT(asSymbol);
|
||||
const TVariable &variable = asSymbol->variable();
|
||||
ASSERT(variable.symbolType() != SymbolType::Empty);
|
||||
extractStructSamplerUniforms(decl, variable, type.getStruct(), newSequence);
|
||||
}
|
||||
|
||||
mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), decl, *newSequence);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Each struct sampler reference is replaced with a reference to the new extracted sampler.
|
||||
bool visitBinary(Visit visit, TIntermBinary *node) override
|
||||
{
|
||||
if (visit != PreVisit)
|
||||
return true;
|
||||
|
||||
if (node->getOp() == EOpIndexDirectStruct && node->getType().isSampler())
|
||||
{
|
||||
ImmutableString newName = GetStructSamplerNameFromTypedNode(node);
|
||||
const TVariable *samplerReplacement =
|
||||
static_cast<const TVariable *>(mSymbolTable->findUserDefined(newName));
|
||||
ASSERT(samplerReplacement);
|
||||
|
||||
TIntermSymbol *replacement = new TIntermSymbol(samplerReplacement);
|
||||
|
||||
queueReplacement(replacement, OriginalNode::IS_DROPPED);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// In we are passing references to structs containing samplers we must new additional
|
||||
// arguments. For each extracted struct sampler a new argument is added. This chains to nested
|
||||
// structs.
|
||||
void visitFunctionPrototype(TIntermFunctionPrototype *node) override
|
||||
{
|
||||
const TFunction *function = node->getFunction();
|
||||
|
||||
if (!function->hasSamplerInStructOrArrayOfArrayParams())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const TSymbol *foundFunction = mSymbolTable->findUserDefined(function->name());
|
||||
if (foundFunction)
|
||||
{
|
||||
ASSERT(foundFunction->isFunction());
|
||||
function = static_cast<const TFunction *>(foundFunction);
|
||||
}
|
||||
else
|
||||
{
|
||||
TFunction *newFunction = createStructSamplerFunction(function);
|
||||
mSymbolTable->declareUserDefinedFunction(newFunction, true);
|
||||
function = newFunction;
|
||||
}
|
||||
|
||||
ASSERT(!function->hasSamplerInStructOrArrayOfArrayParams());
|
||||
TIntermFunctionPrototype *newProto = new TIntermFunctionPrototype(function);
|
||||
queueReplacement(newProto, OriginalNode::IS_DROPPED);
|
||||
}
|
||||
|
||||
// We insert a new scope for each function definition so we can track the new parameters.
|
||||
bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
|
||||
{
|
||||
if (visit == PreVisit)
|
||||
{
|
||||
mSymbolTable->push();
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(visit == PostVisit);
|
||||
mSymbolTable->pop();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// For function call nodes we pass references to the extracted struct samplers in that scope.
|
||||
bool visitAggregate(Visit visit, TIntermAggregate *node) override
|
||||
{
|
||||
if (visit != PreVisit)
|
||||
return true;
|
||||
|
||||
if (!node->isFunctionCall())
|
||||
return true;
|
||||
|
||||
const TFunction *function = node->getFunction();
|
||||
if (!function->hasSamplerInStructOrArrayOfArrayParams())
|
||||
return true;
|
||||
|
||||
ASSERT(node->getOp() == EOpCallFunctionInAST);
|
||||
TFunction *newFunction = mSymbolTable->findUserDefinedFunction(function->name());
|
||||
TIntermSequence *newArguments = getStructSamplerArguments(function, node->getSequence());
|
||||
|
||||
TIntermAggregate *newCall =
|
||||
TIntermAggregate::CreateFunctionCall(*newFunction, newArguments);
|
||||
queueReplacement(newCall, OriginalNode::IS_DROPPED);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// This returns the name of a struct sampler reference. References are always TIntermBinary.
|
||||
static ImmutableString GetStructSamplerNameFromTypedNode(TIntermTyped *node)
|
||||
{
|
||||
std::string stringBuilder;
|
||||
|
||||
TIntermTyped *currentNode = node;
|
||||
while (currentNode->getAsBinaryNode())
|
||||
{
|
||||
TIntermBinary *asBinary = currentNode->getAsBinaryNode();
|
||||
|
||||
switch (asBinary->getOp())
|
||||
{
|
||||
case EOpIndexDirect:
|
||||
{
|
||||
const int index = asBinary->getRight()->getAsConstantUnion()->getIConst(0);
|
||||
const std::string strInt = Str(index);
|
||||
stringBuilder.insert(0, strInt);
|
||||
stringBuilder.insert(0, "_");
|
||||
break;
|
||||
}
|
||||
case EOpIndexDirectStruct:
|
||||
{
|
||||
stringBuilder.insert(0, asBinary->getIndexStructFieldName().data());
|
||||
stringBuilder.insert(0, "_");
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
|
||||
currentNode = asBinary->getLeft();
|
||||
}
|
||||
|
||||
const ImmutableString &variableName = currentNode->getAsSymbolNode()->variable().name();
|
||||
stringBuilder.insert(0, variableName.data());
|
||||
|
||||
return stringBuilder;
|
||||
}
|
||||
|
||||
// Removes all the struct samplers from a struct specifier.
|
||||
void stripStructSpecifierSamplers(const TStructure *structure, TIntermSequence *newSequence)
|
||||
{
|
||||
TFieldList *newFieldList = new TFieldList;
|
||||
ASSERT(structure->containsSamplers());
|
||||
|
||||
for (const TField *field : structure->fields())
|
||||
{
|
||||
const TType &fieldType = *field->type();
|
||||
if (!fieldType.isSampler() && !isRemovedStructType(fieldType))
|
||||
{
|
||||
TType *newType = nullptr;
|
||||
|
||||
if (fieldType.isStructureContainingSamplers())
|
||||
{
|
||||
const TSymbol *structSymbol =
|
||||
mSymbolTable->findUserDefined(fieldType.getStruct()->name());
|
||||
ASSERT(structSymbol && structSymbol->isStruct());
|
||||
const TStructure *fieldStruct = static_cast<const TStructure *>(structSymbol);
|
||||
newType = new TType(fieldStruct, true);
|
||||
if (fieldType.isArray())
|
||||
{
|
||||
newType->makeArrays(fieldType.getArraySizes());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newType = new TType(fieldType);
|
||||
}
|
||||
|
||||
TField *newField =
|
||||
new TField(newType, field->name(), field->line(), field->symbolType());
|
||||
newFieldList->push_back(newField);
|
||||
}
|
||||
}
|
||||
|
||||
// Prune empty structs.
|
||||
if (newFieldList->empty())
|
||||
{
|
||||
mRemovedStructs.insert(structure->name());
|
||||
return;
|
||||
}
|
||||
|
||||
TStructure *newStruct =
|
||||
new TStructure(mSymbolTable, structure->name(), newFieldList, structure->symbolType());
|
||||
TType *newStructType = new TType(newStruct, true);
|
||||
TVariable *newStructVar =
|
||||
new TVariable(mSymbolTable, kEmptyImmutableString, newStructType, SymbolType::Empty);
|
||||
TIntermSymbol *newStructRef = new TIntermSymbol(newStructVar);
|
||||
|
||||
TIntermDeclaration *structDecl = new TIntermDeclaration;
|
||||
structDecl->appendDeclarator(newStructRef);
|
||||
|
||||
newSequence->push_back(structDecl);
|
||||
|
||||
mSymbolTable->declare(newStruct);
|
||||
}
|
||||
|
||||
// Returns true if the type is a struct that was removed because we extracted all the members.
|
||||
bool isRemovedStructType(const TType &type) const
|
||||
{
|
||||
const TStructure *structure = type.getStruct();
|
||||
return (structure && (mRemovedStructs.count(structure->name()) > 0));
|
||||
}
|
||||
|
||||
// Removes samplers from struct uniforms. For each sampler removed also adds a new globally
|
||||
// defined sampler uniform.
|
||||
void extractStructSamplerUniforms(TIntermDeclaration *oldDeclaration,
|
||||
const TVariable &variable,
|
||||
const TStructure *structure,
|
||||
TIntermSequence *newSequence)
|
||||
{
|
||||
ASSERT(structure->containsSamplers());
|
||||
|
||||
size_t nonSamplerCount = 0;
|
||||
|
||||
for (const TField *field : structure->fields())
|
||||
{
|
||||
nonSamplerCount +=
|
||||
extractFieldSamplers(variable.name(), field, variable.getType(), newSequence);
|
||||
}
|
||||
|
||||
if (nonSamplerCount > 0)
|
||||
{
|
||||
// Keep the old declaration around if it has other members.
|
||||
newSequence->push_back(oldDeclaration);
|
||||
}
|
||||
else
|
||||
{
|
||||
mRemovedUniformsCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Extracts samplers from a field of a struct. Works with nested structs and arrays.
|
||||
size_t extractFieldSamplers(const ImmutableString &prefix,
|
||||
const TField *field,
|
||||
const TType &containingType,
|
||||
TIntermSequence *newSequence)
|
||||
{
|
||||
if (containingType.isArray())
|
||||
{
|
||||
size_t nonSamplerCount = 0;
|
||||
|
||||
// Name the samplers internally as varName_<index>_fieldName
|
||||
const TSpan<const unsigned int> &arraySizes = containingType.getArraySizes();
|
||||
for (unsigned int arrayElement = 0; arrayElement < arraySizes[0]; ++arrayElement)
|
||||
{
|
||||
ImmutableStringBuilder stringBuilder(prefix.length() + kHexSize + 1);
|
||||
stringBuilder << prefix << "_";
|
||||
stringBuilder.appendHex(arrayElement);
|
||||
nonSamplerCount = extractFieldSamplersImpl(stringBuilder, field, newSequence);
|
||||
}
|
||||
|
||||
return nonSamplerCount;
|
||||
}
|
||||
|
||||
return extractFieldSamplersImpl(prefix, field, newSequence);
|
||||
}
|
||||
|
||||
// Extracts samplers from a field of a struct. Works with nested structs and arrays.
|
||||
size_t extractFieldSamplersImpl(const ImmutableString &prefix,
|
||||
const TField *field,
|
||||
TIntermSequence *newSequence)
|
||||
{
|
||||
size_t nonSamplerCount = 0;
|
||||
|
||||
const TType &fieldType = *field->type();
|
||||
if (fieldType.isSampler() || fieldType.isStructureContainingSamplers())
|
||||
{
|
||||
ImmutableStringBuilder stringBuilder(prefix.length() + field->name().length() + 1);
|
||||
stringBuilder << prefix << "_" << field->name();
|
||||
ImmutableString newPrefix(stringBuilder);
|
||||
|
||||
if (fieldType.isSampler())
|
||||
{
|
||||
extractSampler(newPrefix, fieldType, newSequence);
|
||||
}
|
||||
else
|
||||
{
|
||||
const TStructure *structure = fieldType.getStruct();
|
||||
for (const TField *nestedField : structure->fields())
|
||||
{
|
||||
nonSamplerCount +=
|
||||
extractFieldSamplers(newPrefix, nestedField, fieldType, newSequence);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nonSamplerCount++;
|
||||
}
|
||||
|
||||
return nonSamplerCount;
|
||||
}
|
||||
|
||||
// Extracts a sampler from a struct. Declares the new extracted sampler.
|
||||
void extractSampler(const ImmutableString &newName,
|
||||
const TType &fieldType,
|
||||
TIntermSequence *newSequence) const
|
||||
{
|
||||
TType *newType = new TType(fieldType);
|
||||
newType->setQualifier(EvqUniform);
|
||||
TVariable *newVariable =
|
||||
new TVariable(mSymbolTable, newName, newType, SymbolType::AngleInternal);
|
||||
TIntermSymbol *newRef = new TIntermSymbol(newVariable);
|
||||
|
||||
TIntermDeclaration *samplerDecl = new TIntermDeclaration;
|
||||
samplerDecl->appendDeclarator(newRef);
|
||||
|
||||
newSequence->push_back(samplerDecl);
|
||||
|
||||
mSymbolTable->declareInternal(newVariable);
|
||||
}
|
||||
|
||||
// Returns the chained name of a sampler uniform field.
|
||||
static ImmutableString GetFieldName(const ImmutableString ¶mName,
|
||||
const TField *field,
|
||||
unsigned arrayIndex)
|
||||
{
|
||||
ImmutableStringBuilder nameBuilder(paramName.length() + kHexSize + 2 +
|
||||
field->name().length());
|
||||
nameBuilder << paramName << "_";
|
||||
|
||||
if (arrayIndex < std::numeric_limits<unsigned>::max())
|
||||
{
|
||||
nameBuilder.appendHex(arrayIndex);
|
||||
nameBuilder << "_";
|
||||
}
|
||||
nameBuilder << field->name();
|
||||
|
||||
return nameBuilder;
|
||||
}
|
||||
|
||||
// A pattern that visits every parameter of a function call. Uses different handlers for struct
|
||||
// parameters, struct sampler parameters, and non-struct parameters.
|
||||
class StructSamplerFunctionVisitor : angle::NonCopyable
|
||||
{
|
||||
public:
|
||||
StructSamplerFunctionVisitor() = default;
|
||||
virtual ~StructSamplerFunctionVisitor() = default;
|
||||
|
||||
virtual void traverse(const TFunction *function)
|
||||
{
|
||||
size_t paramCount = function->getParamCount();
|
||||
|
||||
for (size_t paramIndex = 0; paramIndex < paramCount; ++paramIndex)
|
||||
{
|
||||
const TVariable *param = function->getParam(paramIndex);
|
||||
const TType ¶mType = param->getType();
|
||||
|
||||
if (paramType.isStructureContainingSamplers())
|
||||
{
|
||||
const ImmutableString &baseName = getNameFromIndex(function, paramIndex);
|
||||
if (traverseStructContainingSamplers(baseName, paramType))
|
||||
{
|
||||
visitStructParam(function, paramIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
visitNonStructParam(function, paramIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual ImmutableString getNameFromIndex(const TFunction *function, size_t paramIndex) = 0;
|
||||
virtual void visitSamplerInStructParam(const ImmutableString &name,
|
||||
const TField *field) = 0;
|
||||
virtual void visitStructParam(const TFunction *function, size_t paramIndex) = 0;
|
||||
virtual void visitNonStructParam(const TFunction *function, size_t paramIndex) = 0;
|
||||
|
||||
private:
|
||||
bool traverseStructContainingSamplers(const ImmutableString &baseName,
|
||||
const TType &structType)
|
||||
{
|
||||
bool hasNonSamplerFields = false;
|
||||
const TStructure *structure = structType.getStruct();
|
||||
for (const TField *field : structure->fields())
|
||||
{
|
||||
if (field->type()->isStructureContainingSamplers() || field->type()->isSampler())
|
||||
{
|
||||
if (traverseSamplerInStruct(baseName, structType, field))
|
||||
{
|
||||
hasNonSamplerFields = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hasNonSamplerFields = true;
|
||||
}
|
||||
}
|
||||
return hasNonSamplerFields;
|
||||
}
|
||||
|
||||
bool traverseSamplerInStruct(const ImmutableString &baseName,
|
||||
const TType &baseType,
|
||||
const TField *field)
|
||||
{
|
||||
bool hasNonSamplerParams = false;
|
||||
|
||||
if (baseType.isArray())
|
||||
{
|
||||
const TSpan<const unsigned int> &arraySizes = baseType.getArraySizes();
|
||||
ASSERT(arraySizes.size() == 1);
|
||||
|
||||
for (unsigned int arrayIndex = 0; arrayIndex < arraySizes[0]; ++arrayIndex)
|
||||
{
|
||||
ImmutableString name = GetFieldName(baseName, field, arrayIndex);
|
||||
|
||||
if (field->type()->isStructureContainingSamplers())
|
||||
{
|
||||
if (traverseStructContainingSamplers(name, *field->type()))
|
||||
{
|
||||
hasNonSamplerParams = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(field->type()->isSampler());
|
||||
visitSamplerInStructParam(name, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (field->type()->isStructureContainingSamplers())
|
||||
{
|
||||
ImmutableString name =
|
||||
GetFieldName(baseName, field, std::numeric_limits<unsigned>::max());
|
||||
hasNonSamplerParams = traverseStructContainingSamplers(name, *field->type());
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(field->type()->isSampler());
|
||||
ImmutableString name =
|
||||
GetFieldName(baseName, field, std::numeric_limits<unsigned>::max());
|
||||
visitSamplerInStructParam(name, field);
|
||||
}
|
||||
|
||||
return hasNonSamplerParams;
|
||||
}
|
||||
};
|
||||
|
||||
// A visitor that replaces functions with struct sampler references. The struct sampler
|
||||
// references are expanded to include new fields for the structs.
|
||||
class CreateStructSamplerFunctionVisitor final : public StructSamplerFunctionVisitor
|
||||
{
|
||||
public:
|
||||
CreateStructSamplerFunctionVisitor(TSymbolTable *symbolTable)
|
||||
: mSymbolTable(symbolTable), mNewFunction(nullptr)
|
||||
{}
|
||||
|
||||
ImmutableString getNameFromIndex(const TFunction *function, size_t paramIndex) override
|
||||
{
|
||||
const TVariable *param = function->getParam(paramIndex);
|
||||
return param->name();
|
||||
}
|
||||
|
||||
void traverse(const TFunction *function) override
|
||||
{
|
||||
mNewFunction =
|
||||
new TFunction(mSymbolTable, function->name(), function->symbolType(),
|
||||
&function->getReturnType(), function->isKnownToNotHaveSideEffects());
|
||||
|
||||
StructSamplerFunctionVisitor::traverse(function);
|
||||
}
|
||||
|
||||
void visitSamplerInStructParam(const ImmutableString &name, const TField *field) override
|
||||
{
|
||||
TVariable *fieldSampler =
|
||||
new TVariable(mSymbolTable, name, field->type(), SymbolType::AngleInternal);
|
||||
mNewFunction->addParameter(fieldSampler);
|
||||
mSymbolTable->declareInternal(fieldSampler);
|
||||
}
|
||||
|
||||
void visitStructParam(const TFunction *function, size_t paramIndex) override
|
||||
{
|
||||
const TVariable *param = function->getParam(paramIndex);
|
||||
TType *structType = GetStructSamplerParameterType(mSymbolTable, *param);
|
||||
TVariable *newParam =
|
||||
new TVariable(mSymbolTable, param->name(), structType, param->symbolType());
|
||||
mNewFunction->addParameter(newParam);
|
||||
}
|
||||
|
||||
void visitNonStructParam(const TFunction *function, size_t paramIndex) override
|
||||
{
|
||||
const TVariable *param = function->getParam(paramIndex);
|
||||
mNewFunction->addParameter(param);
|
||||
}
|
||||
|
||||
TFunction *getNewFunction() const { return mNewFunction; }
|
||||
|
||||
private:
|
||||
TSymbolTable *mSymbolTable;
|
||||
TFunction *mNewFunction;
|
||||
};
|
||||
|
||||
TFunction *createStructSamplerFunction(const TFunction *function) const
|
||||
{
|
||||
CreateStructSamplerFunctionVisitor visitor(mSymbolTable);
|
||||
visitor.traverse(function);
|
||||
return visitor.getNewFunction();
|
||||
}
|
||||
|
||||
// A visitor that replaces function calls with expanded struct sampler parameters.
|
||||
class GetSamplerArgumentsVisitor final : public StructSamplerFunctionVisitor
|
||||
{
|
||||
public:
|
||||
GetSamplerArgumentsVisitor(TSymbolTable *symbolTable, const TIntermSequence *arguments)
|
||||
: mSymbolTable(symbolTable), mArguments(arguments), mNewArguments(new TIntermSequence)
|
||||
{}
|
||||
|
||||
ImmutableString getNameFromIndex(const TFunction *function, size_t paramIndex) override
|
||||
{
|
||||
TIntermTyped *argument = (*mArguments)[paramIndex]->getAsTyped();
|
||||
return GetStructSamplerNameFromTypedNode(argument);
|
||||
}
|
||||
|
||||
void visitSamplerInStructParam(const ImmutableString &name, const TField *field) override
|
||||
{
|
||||
TVariable *argSampler =
|
||||
new TVariable(mSymbolTable, name, field->type(), SymbolType::AngleInternal);
|
||||
TIntermSymbol *argSymbol = new TIntermSymbol(argSampler);
|
||||
mNewArguments->push_back(argSymbol);
|
||||
}
|
||||
|
||||
void visitStructParam(const TFunction *function, size_t paramIndex) override
|
||||
{
|
||||
// The tree structure of the parameter is modified to point to the new type. This leaves
|
||||
// the tree in a consistent state.
|
||||
TIntermTyped *argument = (*mArguments)[paramIndex]->getAsTyped();
|
||||
TIntermTyped *replacement = ReplaceTypeOfTypedStructNode(argument, mSymbolTable);
|
||||
mNewArguments->push_back(replacement);
|
||||
}
|
||||
|
||||
void visitNonStructParam(const TFunction *function, size_t paramIndex) override
|
||||
{
|
||||
TIntermTyped *argument = (*mArguments)[paramIndex]->getAsTyped();
|
||||
mNewArguments->push_back(argument);
|
||||
}
|
||||
|
||||
TIntermSequence *getNewArguments() const { return mNewArguments; }
|
||||
|
||||
private:
|
||||
TSymbolTable *mSymbolTable;
|
||||
const TIntermSequence *mArguments;
|
||||
TIntermSequence *mNewArguments;
|
||||
};
|
||||
|
||||
TIntermSequence *getStructSamplerArguments(const TFunction *function,
|
||||
const TIntermSequence *arguments) const
|
||||
{
|
||||
GetSamplerArgumentsVisitor visitor(mSymbolTable, arguments);
|
||||
visitor.traverse(function);
|
||||
return visitor.getNewArguments();
|
||||
}
|
||||
|
||||
int mRemovedUniformsCount;
|
||||
std::set<ImmutableString> mRemovedStructs;
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
bool RewriteStructSamplersOld(TCompiler *compiler,
|
||||
TIntermBlock *root,
|
||||
TSymbolTable *symbolTable,
|
||||
int *removedUniformsCountOut)
|
||||
{
|
||||
Traverser rewriteStructSamplers(symbolTable);
|
||||
root->traverse(&rewriteStructSamplers);
|
||||
if (!rewriteStructSamplers.updateTree(compiler, root))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*removedUniformsCountOut = rewriteStructSamplers.removedUniformsCount();
|
||||
return true;
|
||||
}
|
||||
} // namespace sh
|
|
@ -138,22 +138,10 @@ void GlslangWarmup()
|
|||
// array (except for the innermost dimension). When assigning decorations (set/binding/etc), only
|
||||
// the indices corresponding to the first element of the array should be specified. This function
|
||||
// is used to skip the other indices.
|
||||
//
|
||||
// If useOldRewriteStructSamplers, there are multiple samplers extracted out of struct arrays
|
||||
// though, so the above only applies to the sampler array defined in the struct.
|
||||
bool UniformNameIsIndexZero(const std::string &name, bool excludeCheckForOwningStructArrays)
|
||||
bool UniformNameIsIndexZero(const std::string &name)
|
||||
{
|
||||
size_t lastBracketClose = 0;
|
||||
|
||||
if (excludeCheckForOwningStructArrays)
|
||||
{
|
||||
size_t lastDot = name.find_last_of('.');
|
||||
if (lastDot != std::string::npos)
|
||||
{
|
||||
lastBracketClose = lastDot;
|
||||
}
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
size_t openBracket = name.find('[', lastBracketClose);
|
||||
|
@ -998,18 +986,15 @@ void AssignTextureBindings(const GlslangSourceOptions &options,
|
|||
{
|
||||
const gl::LinkedUniform &samplerUniform = uniforms[uniformIndex];
|
||||
|
||||
if (!options.useOldRewriteStructSamplers &&
|
||||
gl::SamplerNameContainsNonZeroArrayElement(samplerUniform.name))
|
||||
if (gl::SamplerNameContainsNonZeroArrayElement(samplerUniform.name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (UniformNameIsIndexZero(samplerUniform.name, options.useOldRewriteStructSamplers))
|
||||
if (UniformNameIsIndexZero(samplerUniform.name))
|
||||
{
|
||||
// Samplers in structs are extracted and renamed.
|
||||
const std::string samplerName = options.useOldRewriteStructSamplers
|
||||
? GetMappedSamplerNameOld(samplerUniform.name)
|
||||
: GlslangGetMappedSamplerName(samplerUniform.name);
|
||||
const std::string samplerName = GlslangGetMappedSamplerName(samplerUniform.name);
|
||||
|
||||
// TODO: http://anglebug.com/4523: All uniforms should be active
|
||||
if (programExecutable.hasLinkedShaderStage(shaderType) &&
|
||||
|
@ -3774,7 +3759,7 @@ bool GetImageNameWithoutIndices(std::string *name)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!UniformNameIsIndexZero(*name, false))
|
||||
if (!UniformNameIsIndexZero(*name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -3784,25 +3769,6 @@ bool GetImageNameWithoutIndices(std::string *name)
|
|||
return true;
|
||||
}
|
||||
|
||||
std::string GetMappedSamplerNameOld(const std::string &originalName)
|
||||
{
|
||||
std::string samplerName = gl::ParseResourceName(originalName, nullptr);
|
||||
|
||||
// Samplers in structs are extracted.
|
||||
std::replace(samplerName.begin(), samplerName.end(), '.', '_');
|
||||
|
||||
// Samplers in arrays of structs are also extracted.
|
||||
std::replace(samplerName.begin(), samplerName.end(), '[', '_');
|
||||
samplerName.erase(std::remove(samplerName.begin(), samplerName.end(), ']'), samplerName.end());
|
||||
|
||||
if (MappedSamplerNameNeedsUserDefinedPrefix(originalName))
|
||||
{
|
||||
samplerName = sh::kUserDefinedNamePrefix + samplerName;
|
||||
}
|
||||
|
||||
return samplerName;
|
||||
}
|
||||
|
||||
std::string GlslangGetMappedSamplerName(const std::string &originalName)
|
||||
{
|
||||
std::string samplerName = originalName;
|
||||
|
|
|
@ -47,7 +47,6 @@ struct GlslangProgramInterfaceInfo
|
|||
|
||||
struct GlslangSourceOptions
|
||||
{
|
||||
bool useOldRewriteStructSamplers = false;
|
||||
bool supportsTransformFeedbackExtension = false;
|
||||
bool emulateTransformFeedback = false;
|
||||
bool emulateBresenhamLines = false;
|
||||
|
@ -159,7 +158,6 @@ void GlslangRelease();
|
|||
bool GetImageNameWithoutIndices(std::string *name);
|
||||
|
||||
// Get the mapped sampler name after the source is transformed by GlslangGetShaderSource()
|
||||
std::string GetMappedSamplerNameOld(const std::string &originalName);
|
||||
std::string GlslangGetMappedSamplerName(const std::string &originalName);
|
||||
std::string GetXfbBufferName(const uint32_t bufferIndex);
|
||||
|
||||
|
|
|
@ -392,7 +392,6 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk
|
|||
mFlipYForCurrentSurface(false),
|
||||
mIsAnyHostVisibleBufferWritten(false),
|
||||
mEmulateSeamfulCubeMapSampling(false),
|
||||
mUseOldRewriteStructSamplers(false),
|
||||
mOutsideRenderPassCommands(nullptr),
|
||||
mRenderPassCommands(nullptr),
|
||||
mGpuEventsEnabled(false),
|
||||
|
@ -655,8 +654,6 @@ angle::Result ContextVk::initialize()
|
|||
|
||||
mEmulateSeamfulCubeMapSampling = shouldEmulateSeamfulCubeMapSampling();
|
||||
|
||||
mUseOldRewriteStructSamplers = shouldUseOldRewriteStructSamplers();
|
||||
|
||||
// Assign initial command buffers from queue
|
||||
mOutsideRenderPassCommands = mRenderer->getCommandBufferHelper(false);
|
||||
mRenderPassCommands = mRenderer->getCommandBufferHelper(true);
|
||||
|
@ -4580,11 +4577,6 @@ bool ContextVk::shouldEmulateSeamfulCubeMapSampling() const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ContextVk::shouldUseOldRewriteStructSamplers() const
|
||||
{
|
||||
return mRenderer->getFeatures().forceOldRewriteStructSamplers.enabled;
|
||||
}
|
||||
|
||||
angle::Result ContextVk::onBufferReleaseToExternal(const vk::BufferHelper &buffer)
|
||||
{
|
||||
if (mRenderPassCommands->usesBuffer(buffer))
|
||||
|
|
|
@ -421,8 +421,6 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
|
|||
|
||||
bool emulateSeamfulCubeMapSampling() const { return mEmulateSeamfulCubeMapSampling; }
|
||||
|
||||
bool useOldRewriteStructSamplers() const { return mUseOldRewriteStructSamplers; }
|
||||
|
||||
const gl::Debug &getDebug() const { return mState.getDebug(); }
|
||||
const gl::OverlayType *getOverlay() const { return mState.getOverlay(); }
|
||||
|
||||
|
@ -832,7 +830,6 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
|
|||
void flushGpuEvents(double nextSyncGpuTimestampS, double nextSyncCpuTimestampS);
|
||||
void handleDeviceLost();
|
||||
bool shouldEmulateSeamfulCubeMapSampling() const;
|
||||
bool shouldUseOldRewriteStructSamplers() const;
|
||||
void clearAllGarbage();
|
||||
bool hasRecordedCommands();
|
||||
void dumpCommandStreamDiagnostics();
|
||||
|
@ -952,10 +949,6 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
|
|||
// Whether this context should do seamful cube map sampling emulation.
|
||||
bool mEmulateSeamfulCubeMapSampling;
|
||||
|
||||
// Whether this context should use the old version of the
|
||||
// RewriteStructSamplers pass.
|
||||
bool mUseOldRewriteStructSamplers;
|
||||
|
||||
angle::PackedEnumMap<PipelineType, DriverUniformsDescriptorSet> mDriverUniforms;
|
||||
|
||||
// This cache should also probably include the texture index (shader location) and array
|
||||
|
|
|
@ -28,7 +28,6 @@ GlslangSourceOptions GlslangWrapperVk::CreateSourceOptions(const angle::Features
|
|||
{
|
||||
GlslangSourceOptions options;
|
||||
|
||||
options.useOldRewriteStructSamplers = features.forceOldRewriteStructSamplers.enabled;
|
||||
options.supportsTransformFeedbackExtension =
|
||||
features.supportsTransformFeedbackExtension.enabled;
|
||||
options.emulateTransformFeedback = features.emulateTransformFeedback.enabled;
|
||||
|
|
|
@ -486,7 +486,6 @@ void ProgramExecutableVk::addAtomicCounterBufferDescriptorSetDesc(
|
|||
}
|
||||
|
||||
void ProgramExecutableVk::addImageDescriptorSetDesc(const gl::ProgramExecutable &executable,
|
||||
bool useOldRewriteStructSamplers,
|
||||
vk::DescriptorSetLayoutDesc *descOut)
|
||||
{
|
||||
const std::vector<gl::ImageBinding> &imageBindings = executable.getImageBindings();
|
||||
|
@ -498,27 +497,22 @@ void ProgramExecutableVk::addImageDescriptorSetDesc(const gl::ProgramExecutable
|
|||
uint32_t uniformIndex = executable.getUniformIndexFromImageIndex(imageIndex);
|
||||
const gl::LinkedUniform &imageUniform = uniforms[uniformIndex];
|
||||
|
||||
std::string imageName = useOldRewriteStructSamplers
|
||||
? GetMappedSamplerNameOld(imageUniform.name)
|
||||
: GlslangGetMappedSamplerName(imageUniform.name);
|
||||
std::string imageName = GlslangGetMappedSamplerName(imageUniform.name);
|
||||
|
||||
// The front-end always binds array image units sequentially.
|
||||
uint32_t arraySize = static_cast<uint32_t>(imageBinding.boundImageUnits.size());
|
||||
|
||||
if (!useOldRewriteStructSamplers)
|
||||
// 2D arrays are split into multiple 1D arrays when generating LinkedUniforms. Since they
|
||||
// are flattened into one array, ignore the nonzero elements and expand the array to the
|
||||
// total array size.
|
||||
if (gl::SamplerNameContainsNonZeroArrayElement(imageUniform.name))
|
||||
{
|
||||
// 2D arrays are split into multiple 1D arrays when generating
|
||||
// LinkedUniforms. Since they are flattened into one array, ignore the
|
||||
// nonzero elements and expand the array to the total array size.
|
||||
if (gl::SamplerNameContainsNonZeroArrayElement(imageUniform.name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (unsigned int outerArraySize : imageUniform.outerArraySizes)
|
||||
{
|
||||
arraySize *= outerArraySize;
|
||||
}
|
||||
for (unsigned int outerArraySize : imageUniform.outerArraySizes)
|
||||
{
|
||||
arraySize *= outerArraySize;
|
||||
}
|
||||
|
||||
for (const gl::ShaderType shaderType : executable.getLinkedShaderStages())
|
||||
|
@ -542,7 +536,6 @@ void ProgramExecutableVk::addImageDescriptorSetDesc(const gl::ProgramExecutable
|
|||
|
||||
void ProgramExecutableVk::addTextureDescriptorSetDesc(
|
||||
const gl::ProgramState &programState,
|
||||
bool useOldRewriteStructSamplers,
|
||||
const gl::ActiveTextureArray<vk::TextureUnit> *activeTextures,
|
||||
vk::DescriptorSetLayoutDesc *descOut)
|
||||
{
|
||||
|
@ -556,27 +549,22 @@ void ProgramExecutableVk::addTextureDescriptorSetDesc(
|
|||
uint32_t uniformIndex = programState.getUniformIndexFromSamplerIndex(textureIndex);
|
||||
const gl::LinkedUniform &samplerUniform = uniforms[uniformIndex];
|
||||
|
||||
const std::string samplerName = useOldRewriteStructSamplers
|
||||
? GetMappedSamplerNameOld(samplerUniform.name)
|
||||
: GlslangGetMappedSamplerName(samplerUniform.name);
|
||||
const std::string samplerName = GlslangGetMappedSamplerName(samplerUniform.name);
|
||||
|
||||
// The front-end always binds array sampler units sequentially.
|
||||
uint32_t arraySize = static_cast<uint32_t>(samplerBinding.boundTextureUnits.size());
|
||||
|
||||
if (!useOldRewriteStructSamplers)
|
||||
// 2D arrays are split into multiple 1D arrays when generating LinkedUniforms. Since they
|
||||
// are flattened into one array, ignore the nonzero elements and expand the array to the
|
||||
// total array size.
|
||||
if (gl::SamplerNameContainsNonZeroArrayElement(samplerUniform.name))
|
||||
{
|
||||
// 2D arrays are split into multiple 1D arrays when generating
|
||||
// LinkedUniforms. Since they are flattened into one array, ignore the
|
||||
// nonzero elements and expand the array to the total array size.
|
||||
if (gl::SamplerNameContainsNonZeroArrayElement(samplerUniform.name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (unsigned int outerArraySize : samplerUniform.outerArraySizes)
|
||||
{
|
||||
arraySize *= outerArraySize;
|
||||
}
|
||||
for (unsigned int outerArraySize : samplerUniform.outerArraySizes)
|
||||
{
|
||||
arraySize *= outerArraySize;
|
||||
}
|
||||
|
||||
for (const gl::ShaderType shaderType : programState.getExecutable().getLinkedShaderStages())
|
||||
|
@ -857,8 +845,7 @@ angle::Result ProgramExecutableVk::createPipelineLayout(
|
|||
{
|
||||
const gl::ProgramState *programState = programStates[shaderType];
|
||||
ASSERT(programState);
|
||||
addImageDescriptorSetDesc(programState->getExecutable(),
|
||||
contextVk->useOldRewriteStructSamplers(), &resourcesSetDesc);
|
||||
addImageDescriptorSetDesc(programState->getExecutable(), &resourcesSetDesc);
|
||||
}
|
||||
|
||||
ANGLE_TRY(contextVk->getDescriptorSetLayoutCache().getDescriptorSetLayout(
|
||||
|
@ -872,8 +859,7 @@ angle::Result ProgramExecutableVk::createPipelineLayout(
|
|||
{
|
||||
const gl::ProgramState *programState = programStates[shaderType];
|
||||
ASSERT(programState);
|
||||
addTextureDescriptorSetDesc(*programState, contextVk->useOldRewriteStructSamplers(),
|
||||
activeTextures, &texturesSetDesc);
|
||||
addTextureDescriptorSetDesc(*programState, activeTextures, &texturesSetDesc);
|
||||
}
|
||||
|
||||
ANGLE_TRY(contextVk->getDescriptorSetLayoutCache().getDescriptorSetLayout(
|
||||
|
@ -1221,7 +1207,6 @@ angle::Result ProgramExecutableVk::updateImagesDescriptorSet(
|
|||
|
||||
const gl::ActiveTextureArray<TextureVk *> &activeImages = contextVk->getActiveImages();
|
||||
|
||||
bool useOldRewriteStructSamplers = contextVk->useOldRewriteStructSamplers();
|
||||
angle::HashMap<std::string, uint32_t> mappedImageNameToArrayOffset;
|
||||
|
||||
// Write images.
|
||||
|
@ -1245,28 +1230,17 @@ angle::Result ProgramExecutableVk::updateImagesDescriptorSet(
|
|||
}
|
||||
ASSERT(descriptorSet != VK_NULL_HANDLE);
|
||||
|
||||
std::string mappedImageName;
|
||||
if (!useOldRewriteStructSamplers)
|
||||
{
|
||||
mappedImageName = GlslangGetMappedSamplerName(imageUniform.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
mappedImageName = imageUniform.mappedName;
|
||||
}
|
||||
std::string mappedImageName = GlslangGetMappedSamplerName(imageUniform.name);
|
||||
|
||||
GetImageNameWithoutIndices(&mappedImageName);
|
||||
|
||||
uint32_t arrayOffset = 0;
|
||||
uint32_t arraySize = static_cast<uint32_t>(imageBinding.boundImageUnits.size());
|
||||
|
||||
if (!useOldRewriteStructSamplers)
|
||||
{
|
||||
arrayOffset = mappedImageNameToArrayOffset[mappedImageName];
|
||||
// Front-end generates array elements in order, so we can just increment
|
||||
// the offset each time we process a nested array.
|
||||
mappedImageNameToArrayOffset[mappedImageName] += arraySize;
|
||||
}
|
||||
arrayOffset = mappedImageNameToArrayOffset[mappedImageName];
|
||||
// Front-end generates array elements in order, so we can just increment the offset each
|
||||
// time we process a nested array.
|
||||
mappedImageNameToArrayOffset[mappedImageName] += arraySize;
|
||||
|
||||
VkWriteDescriptorSet *writeInfos = contextVk->allocWriteDescriptorSets(arraySize);
|
||||
|
||||
|
@ -1324,9 +1298,7 @@ angle::Result ProgramExecutableVk::updateImagesDescriptorSet(
|
|||
imageInfos[arrayElement].imageView = imageView->getHandle();
|
||||
imageInfos[arrayElement].imageLayout = image->getCurrentLayout();
|
||||
|
||||
const std::string imageName = useOldRewriteStructSamplers
|
||||
? GetMappedSamplerNameOld(imageUniform.name)
|
||||
: GlslangGetMappedSamplerName(imageUniform.name);
|
||||
const std::string imageName = GlslangGetMappedSamplerName(imageUniform.name);
|
||||
const ShaderInterfaceVariableInfo &info = mVariableInfoMap.get(shaderType, imageName);
|
||||
|
||||
writeInfos[arrayElement].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
|
@ -1463,7 +1435,6 @@ angle::Result ProgramExecutableVk::updateTexturesDescriptorSet(ContextVk *contex
|
|||
|
||||
const gl::ActiveTextureArray<vk::TextureUnit> &activeTextures = contextVk->getActiveTextures();
|
||||
bool emulateSeamfulCubeMapSampling = contextVk->emulateSeamfulCubeMapSampling();
|
||||
bool useOldRewriteStructSamplers = contextVk->useOldRewriteStructSamplers();
|
||||
|
||||
gl::ShaderMap<const gl::ProgramState *> programStates;
|
||||
fillProgramStateMap(contextVk, &programStates);
|
||||
|
@ -1509,13 +1480,10 @@ angle::Result ProgramExecutableVk::updateTexturesDescriptorSet(ContextVk *contex
|
|||
uint32_t arrayOffset = 0;
|
||||
uint32_t arraySize = static_cast<uint32_t>(samplerBinding.boundTextureUnits.size());
|
||||
|
||||
if (!useOldRewriteStructSamplers)
|
||||
{
|
||||
arrayOffset = mappedSamplerNameToArrayOffset[mappedSamplerName];
|
||||
// Front-end generates array elements in order, so we can just increment
|
||||
// the offset each time we process a nested array.
|
||||
mappedSamplerNameToArrayOffset[mappedSamplerName] += arraySize;
|
||||
}
|
||||
arrayOffset = mappedSamplerNameToArrayOffset[mappedSamplerName];
|
||||
// Front-end generates array elements in order, so we can just increment the offset each
|
||||
// time we process a nested array.
|
||||
mappedSamplerNameToArrayOffset[mappedSamplerName] += arraySize;
|
||||
|
||||
VkWriteDescriptorSet *writeInfos = contextVk->allocWriteDescriptorSets(arraySize);
|
||||
|
||||
|
@ -1582,10 +1550,7 @@ angle::Result ProgramExecutableVk::updateTexturesDescriptorSet(ContextVk *contex
|
|||
{
|
||||
imageInfos[arrayElement].sampler = textureVk->getSampler().get().getHandle();
|
||||
}
|
||||
const std::string samplerName =
|
||||
contextVk->getRenderer()->getFeatures().forceOldRewriteStructSamplers.enabled
|
||||
? GetMappedSamplerNameOld(samplerUniform.name)
|
||||
: GlslangGetMappedSamplerName(samplerUniform.name);
|
||||
const std::string samplerName = GlslangGetMappedSamplerName(samplerUniform.name);
|
||||
const ShaderInterfaceVariableInfo &info =
|
||||
mVariableInfoMap.get(shaderType, samplerName);
|
||||
|
||||
|
|
|
@ -199,10 +199,8 @@ class ProgramExecutableVk
|
|||
const gl::ShaderType shaderType,
|
||||
vk::DescriptorSetLayoutDesc *descOut);
|
||||
void addImageDescriptorSetDesc(const gl::ProgramExecutable &executable,
|
||||
bool useOldRewriteStructSamplers,
|
||||
vk::DescriptorSetLayoutDesc *descOut);
|
||||
void addTextureDescriptorSetDesc(const gl::ProgramState &programState,
|
||||
bool useOldRewriteStructSamplers,
|
||||
const gl::ActiveTextureArray<vk::TextureUnit> *activeTextures,
|
||||
vk::DescriptorSetLayoutDesc *descOut);
|
||||
|
||||
|
|
|
@ -1377,7 +1377,6 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF
|
|||
// Used to support EXT_gpu_shader5:
|
||||
enabledFeatures.features.shaderUniformBufferArrayDynamicIndexing =
|
||||
mPhysicalDeviceFeatures.shaderUniformBufferArrayDynamicIndexing;
|
||||
// Used to support EXT_gpu_shader5 and sampler array of array emulation:
|
||||
enabledFeatures.features.shaderSampledImageArrayDynamicIndexing =
|
||||
mPhysicalDeviceFeatures.shaderSampledImageArrayDynamicIndexing;
|
||||
// Used to support atomic counter emulation:
|
||||
|
@ -1946,10 +1945,6 @@ void RendererVk::initFeatures(DisplayVk *displayVk,
|
|||
ANGLE_FEATURE_CONDITION(&mFeatures, bindEmptyForUnusedDescriptorSets,
|
||||
IsAndroid() && isQualcomm);
|
||||
|
||||
ANGLE_FEATURE_CONDITION(
|
||||
&mFeatures, forceOldRewriteStructSamplers,
|
||||
!mPhysicalDeviceFeatures.shaderSampledImageArrayDynamicIndexing || isQualcomm);
|
||||
|
||||
ANGLE_FEATURE_CONDITION(&mFeatures, perFrameWindowSizeQuery,
|
||||
isIntel || (IsWindows() && isAMD) || IsFuchsia());
|
||||
|
||||
|
|
|
@ -58,11 +58,6 @@ std::shared_ptr<WaitableCompileEvent> ShaderVk::compile(const gl::Context *conte
|
|||
compileOptions |= SH_EMULATE_SEAMFUL_CUBE_MAP_SAMPLING;
|
||||
}
|
||||
|
||||
if (contextVk->useOldRewriteStructSamplers())
|
||||
{
|
||||
compileOptions |= SH_USE_OLD_REWRITE_STRUCT_SAMPLERS;
|
||||
}
|
||||
|
||||
if (!contextVk->getFeatures().enablePrecisionQualifiers.enabled)
|
||||
{
|
||||
compileOptions |= SH_IGNORE_PRECISION_QUALIFIERS;
|
||||
|
|
|
@ -169,10 +169,6 @@
|
|||
// Not failing in last official run, but failed recently:
|
||||
4110 SWIFTSHADER : dEQP-GLES31.functional.shaders.helper_invocation.* = FAIL
|
||||
|
||||
// Vulkan Android doesn't support array of array of opaque uniforms. Emulation is not yet done for images
|
||||
3881 VULKAN ANDROID : dEQP-GLES31.functional.program_interface_query.uniform.location.default_block.array.array.image_2d = SKIP
|
||||
3881 VULKAN ANDROID : dEQP-GLES31.functional.program_interface_query.uniform.location.default_block.array.array.iimage_2d_array = SKIP
|
||||
|
||||
////
|
||||
//// General Vulkan expectations
|
||||
////
|
||||
|
@ -228,16 +224,17 @@
|
|||
// Seem to trigger LowMemoryKiller when run in a certain sequence
|
||||
5185 VULKAN ANDROID : dEQP-GLES31.functional.atomic_counter.* = SKIP
|
||||
|
||||
// Arrays of opaque types as function parameters (possibly related to issue 3882)
|
||||
2703 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.const_literal_vertex = FAIL
|
||||
2703 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.const_literal_fragment = FAIL
|
||||
2703 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.const_literal_compute = FAIL
|
||||
2703 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.const_expression_vertex = FAIL
|
||||
2703 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.const_expression_fragment = FAIL
|
||||
2703 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.const_expression_compute = FAIL
|
||||
2703 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.*uniform_vertex = FAIL
|
||||
2703 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.*uniform_fragment = FAIL
|
||||
2703 VULKAN ANDROID : dEQP-GLES31.functional.shaders.opaque_type_indexing.atomic_counter.*uniform_compute = FAIL
|
||||
// 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
|
||||
|
@ -255,10 +252,6 @@
|
|||
// Need to support non-color when staging image updates
|
||||
4080 ANDROID VULKAN : dEQP-GLES31.functional.stencil_texturing.misc.base_level = SKIP
|
||||
|
||||
// Android uses "old" sampler rewrite that doesn't support array of arrays
|
||||
2703 VULKAN ANDROID : dEQP-GLES31.functional.program_interface_query.uniform.location.default_block.array.array.*sampler* = SKIP
|
||||
2703 VULKAN ANDROID : dEQP-GLES31.functional.uniform_location.nested_array.*sampler2D* = SKIP
|
||||
|
||||
// Fail on tests that use an unsupported extension!
|
||||
4371 VULKAN ANDROID : dEQP-GLES31.functional.shaders.implicit_conversions.es31.arithmetic.input_before_literal.add.int_to_vec3_vertex = FAIL
|
||||
4371 VULKAN ANDROID : dEQP-GLES31.functional.shaders.implicit_conversions.es31.arithmetic.input_before_literal.add.int_to_uvec3_vertex = FAIL
|
||||
|
|
|
@ -103,9 +103,6 @@
|
|||
// Fails to link the shader program on Pixel2 and Pixel2 XL
|
||||
3588 VULKAN PIXEL2ORXL : KHR-GLES31.core.sample_variables.mask.rgba8.samples_*.mask_* = FAIL
|
||||
|
||||
// Android uses "old" sampler rewrite that doesn't support array of arrays
|
||||
2703 VULKAN ANDROID : KHR-GLES31.core.arrays_of_arrays.* = SKIP
|
||||
|
||||
// Failing on Android with SwiftShader
|
||||
4300 SWIFTSHADER ANDROID : KHR-GLES31.core.shader_integer_mix.mix-bvec4 = FAIL
|
||||
4300 SWIFTSHADER ANDROID : KHR-GLES31.core.shader_integer_mix.mix-ivec4 = FAIL
|
||||
|
|
|
@ -55,8 +55,8 @@
|
|||
// Android failures
|
||||
|
||||
// Dynamic indexing features not supported on Qualcomm
|
||||
2703 VULKAN ANDROID : KHR-GLES32.core.gpu_shader5.atomic_counters_array_indexing = FAIL
|
||||
2703 VULKAN ANDROID : KHR-GLES32.core.gpu_shader5.images_array_indexing = FAIL
|
||||
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
|
||||
5392 VULKAN ANDROID : KHR-GLES32.core.texture_buffer.texture_buffer_active_uniform_validation_compute_shader = FAIL
|
||||
|
|
|
@ -3273,7 +3273,7 @@ TEST_P(GLSLTest_ES31, AtomicCounterArrayLength)
|
|||
precision mediump float;
|
||||
layout(local_size_x=1) in;
|
||||
|
||||
layout(binding = 0) uniform atomic_uint ac1[2][3];
|
||||
layout(binding = 0) uniform atomic_uint ac1[2][3][4];
|
||||
uniform uint testSideEffectValue;
|
||||
|
||||
layout(binding = 1, std140) buffer Result
|
||||
|
@ -3288,7 +3288,7 @@ void main() {
|
|||
passed = false;
|
||||
}
|
||||
uint value = 0u;
|
||||
if (ac1[(value = testSideEffectValue)].length() != 3)
|
||||
if (ac1[value = testSideEffectValue].length() != 3)
|
||||
{
|
||||
passed = false;
|
||||
}
|
||||
|
@ -3296,6 +3296,14 @@ void main() {
|
|||
{
|
||||
passed = false;
|
||||
}
|
||||
if (ac1[1][value = testSideEffectValue + 1u].length() != 4)
|
||||
{
|
||||
passed = false;
|
||||
}
|
||||
if (value != testSideEffectValue + 1u)
|
||||
{
|
||||
passed = false;
|
||||
}
|
||||
result.value = passed ? 255u : 127u;
|
||||
})";
|
||||
|
||||
|
@ -3329,9 +3337,10 @@ void main() {
|
|||
GL_STATIC_DRAW);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer);
|
||||
|
||||
GLint uniformLocation = glGetUniformLocation(program.get(), "testSideEffectValue");
|
||||
GLint uniformLocation = glGetUniformLocation(program, "testSideEffectValue");
|
||||
EXPECT_NE(uniformLocation, -1);
|
||||
glUniform1i(uniformLocation, kUniformTestValue);
|
||||
glUniform1ui(uniformLocation, kUniformTestValue);
|
||||
EXPECT_GL_NO_ERROR();
|
||||
|
||||
glDispatchCompute(1, 1, 1);
|
||||
|
||||
|
@ -3402,10 +3411,6 @@ void main()
|
|||
// Test that inactive samplers in structs don't cause any errors.
|
||||
TEST_P(GLSLTest_ES31, InactiveSamplersInStructCS)
|
||||
{
|
||||
// While the sampler is being extracted and declared outside of the struct, it's not removed
|
||||
// from the struct definition. http://anglebug.com/4211
|
||||
ANGLE_SKIP_TEST_IF(IsVulkan() || IsMetal());
|
||||
|
||||
constexpr char kCS[] = R"(#version 310 es
|
||||
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
|
||||
struct S
|
||||
|
@ -3532,10 +3537,6 @@ TEST_P(GLSLTest_ES31, ArraysOfArraysBlockBasicType)
|
|||
// Test that arrays of arrays of samplers work as expected.
|
||||
TEST_P(GLSLTest_ES31, ArraysOfArraysSampler)
|
||||
{
|
||||
// anglebug.com/2703 - QC doesn't support arrays of samplers as parameters,
|
||||
// so sampler array of array handling is disabled
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
|
||||
|
||||
constexpr char kFS[] =
|
||||
"#version 310 es\n"
|
||||
"precision mediump float;\n"
|
||||
|
@ -3588,10 +3589,6 @@ TEST_P(GLSLTest_ES31, ArraysOfArraysImage)
|
|||
// http://anglebug.com/5072
|
||||
ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux() && IsOpenGL());
|
||||
|
||||
// anglebug.com/2703 - QC doesn't support arrays of image as parameters,
|
||||
// so image array of array handling is disabled
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
|
||||
|
||||
// Fails on D3D due to mistranslation.
|
||||
ANGLE_SKIP_TEST_IF(IsD3D());
|
||||
|
||||
|
@ -3706,10 +3703,6 @@ TEST_P(GLSLTest_ES31, StructArraySampler)
|
|||
// Test that arrays of arrays of samplers inside structs work as expected.
|
||||
TEST_P(GLSLTest_ES31, StructArrayArraySampler)
|
||||
{
|
||||
// anglebug.com/2703 - QC doesn't support arrays of samplers as parameters,
|
||||
// so sampler array of array handling is disabled
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
|
||||
|
||||
constexpr char kFS[] =
|
||||
"#version 310 es\n"
|
||||
"precision mediump float;\n"
|
||||
|
@ -3760,10 +3753,6 @@ TEST_P(GLSLTest_ES31, StructArrayArraySampler)
|
|||
// Test that an array of structs with arrays of arrays of samplers works.
|
||||
TEST_P(GLSLTest_ES31, ArrayStructArrayArraySampler)
|
||||
{
|
||||
// anglebug.com/2703 - QC doesn't support arrays of samplers as parameters,
|
||||
// so sampler array of array handling is disabled
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
|
||||
|
||||
GLint numTextures;
|
||||
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &numTextures);
|
||||
ANGLE_SKIP_TEST_IF(numTextures < 2 * (2 * 2 + 2 * 2));
|
||||
|
@ -3832,10 +3821,6 @@ TEST_P(GLSLTest_ES31, ArrayStructArrayArraySampler)
|
|||
// Test that a complex chain of structs and arrays of samplers works as expected.
|
||||
TEST_P(GLSLTest_ES31, ComplexStructArraySampler)
|
||||
{
|
||||
// anglebug.com/2703 - QC doesn't support arrays of samplers as parameters,
|
||||
// so sampler array of array handling is disabled
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
|
||||
|
||||
GLint numTextures;
|
||||
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &numTextures);
|
||||
ANGLE_SKIP_TEST_IF(numTextures < 2 * 3 * (2 + 3));
|
||||
|
@ -3921,10 +3906,6 @@ TEST_P(GLSLTest_ES31, ComplexStructArraySampler)
|
|||
|
||||
TEST_P(GLSLTest_ES31, ArraysOfArraysStructDifferentTypesSampler)
|
||||
{
|
||||
// anglebug.com/2703 - QC doesn't support arrays of samplers as parameters,
|
||||
// so sampler array of array handling is disabled
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
|
||||
|
||||
GLint numTextures;
|
||||
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &numTextures);
|
||||
ANGLE_SKIP_TEST_IF(numTextures < 3 * (2 + 2));
|
||||
|
@ -4001,9 +3982,7 @@ TEST_P(GLSLTest_ES31, ParameterArraysOfArraysSampler)
|
|||
{
|
||||
// anglebug.com/3832 - no sampler array params on Android
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
|
||||
// anglebug.com/2703 - QC doesn't support arrays of samplers as parameters,
|
||||
// so sampler array of array handling is disabled
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
|
||||
|
||||
constexpr char kFS[] =
|
||||
"#version 310 es\n"
|
||||
"precision mediump float;\n"
|
||||
|
@ -4063,9 +4042,7 @@ TEST_P(GLSLTest_ES31, ParameterStructArrayArraySampler)
|
|||
{
|
||||
// anglebug.com/3832 - no sampler array params on Android
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
|
||||
// anglebug.com/2703 - QC doesn't support arrays of samplers as parameters,
|
||||
// so sampler array of array handling is disabled
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
|
||||
|
||||
constexpr char kFS[] =
|
||||
"#version 310 es\n"
|
||||
"precision mediump float;\n"
|
||||
|
@ -4126,9 +4103,7 @@ TEST_P(GLSLTest_ES31, ParameterArrayArrayStructArrayArraySampler)
|
|||
{
|
||||
// anglebug.com/3832 - no sampler array params on Android
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
|
||||
// anglebug.com/2703 - QC doesn't support arrays of samplers as parameters,
|
||||
// so sampler array of array handling is disabled
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
|
||||
|
||||
GLint numTextures;
|
||||
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &numTextures);
|
||||
ANGLE_SKIP_TEST_IF(numTextures < 3 * 2 * 2 * 2);
|
||||
|
@ -4202,10 +4177,6 @@ TEST_P(GLSLTest_ES31, ParameterArrayArrayStructArrayArraySampler)
|
|||
// Test that 3D arrays with sub-arrays passed as parameters works as expected.
|
||||
TEST_P(GLSLTest_ES31, ParameterArrayArrayArraySampler)
|
||||
{
|
||||
// anglebug.com/2703 - QC doesn't support arrays of samplers as parameters,
|
||||
// so sampler array of array handling is disabled
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
|
||||
|
||||
GLint numTextures;
|
||||
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &numTextures);
|
||||
ANGLE_SKIP_TEST_IF(numTextures < 2 * 3 * 4 + 4);
|
||||
|
@ -4343,10 +4314,6 @@ TEST_P(GLSLTest_ES31, ArraysOfArraysNameCollisionSampler)
|
|||
// Test that regular arrays are unmodified.
|
||||
TEST_P(GLSLTest_ES31, BasicTypeArrayAndArrayOfSampler)
|
||||
{
|
||||
// anglebug.com/2703 - QC doesn't support arrays of samplers as parameters,
|
||||
// so sampler array of array handling is disabled
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
|
||||
|
||||
constexpr char kFS[] =
|
||||
"#version 310 es\n"
|
||||
"precision mediump sampler2D;\n"
|
||||
|
@ -5476,9 +5443,6 @@ TEST_P(GLSLTest, StructWithSamplerArrayAsFunctionArg)
|
|||
// Shader failed to compile on Nexus devices. http://anglebug.com/2114
|
||||
ANGLE_SKIP_TEST_IF(IsNexus5X() && IsAdreno() && IsOpenGLES());
|
||||
|
||||
// TODO(jmadill): Fix on Android/vulkan if possible. http://anglebug.com/2703
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
|
||||
|
||||
constexpr char kFS[] =
|
||||
"precision mediump float;\n"
|
||||
"struct S\n"
|
||||
|
@ -7656,10 +7620,6 @@ TEST_P(GLSLTest, MemoryExhaustedTest)
|
|||
// Test that inactive samplers in structs don't cause any errors.
|
||||
TEST_P(GLSLTest, InactiveSamplersInStruct)
|
||||
{
|
||||
// While the sampler is being extracted and declared outside of the struct, it's not removed
|
||||
// from the struct definition. http://anglebug.com/4211
|
||||
ANGLE_SKIP_TEST_IF(IsVulkan() || IsMetal());
|
||||
|
||||
constexpr char kVS[] = R"(attribute vec4 a_position;
|
||||
void main() {
|
||||
gl_Position = a_position;
|
||||
|
@ -8533,10 +8493,6 @@ TEST_P(GLSLTest_ES31, MixOfResourcesAsFunctionArgs)
|
|||
// anglebug.com/3832 - no sampler array params on Android
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
|
||||
|
||||
// anglebug.com/2703 - QC doesn't support arrays of samplers as parameters,
|
||||
// so sampler array of array handling is disabled
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
|
||||
|
||||
constexpr char kComputeShader[] = R"(#version 310 es
|
||||
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
|
@ -8644,10 +8600,6 @@ TEST_P(GLSLTest_ES31, ArrayOfArrayOfSamplerAsFunctionParameterIndexedWithSideEff
|
|||
// anglebug.com/3832 - no sampler array params on Android
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
|
||||
|
||||
// anglebug.com/2703 - QC doesn't support arrays of samplers as parameters,
|
||||
// so sampler array of array handling is disabled
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
|
||||
|
||||
// Skip if EXT_gpu_shader5 is not enabled.
|
||||
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_gpu_shader5"));
|
||||
|
||||
|
@ -8747,6 +8699,446 @@ void main(void)
|
|||
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
|
||||
}
|
||||
|
||||
// Test that array of array of samplers can be indexed correctly with dynamic indices.
|
||||
TEST_P(GLSLTest_ES31, ArrayOfArrayOfSamplerDynamicIndex)
|
||||
{
|
||||
// Skip if EXT_gpu_shader5 is not enabled.
|
||||
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_gpu_shader5"));
|
||||
|
||||
int maxTextureImageUnits = 0;
|
||||
glGetIntegerv(GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits);
|
||||
ANGLE_SKIP_TEST_IF(maxTextureImageUnits < 24);
|
||||
|
||||
// anglebug.com/3832 - no sampler array params on Android
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
|
||||
|
||||
// http://anglebug.com/5546
|
||||
ANGLE_SKIP_TEST_IF(IsWindows() && IsIntel() && IsOpenGL());
|
||||
|
||||
constexpr char kComputeShader[] = R"(#version 310 es
|
||||
#extension GL_EXT_gpu_shader5 : require
|
||||
|
||||
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
layout(binding = 1, std430) buffer Output {
|
||||
uint success;
|
||||
} outbuf;
|
||||
|
||||
uniform sampler2D smplr[2][3][4];
|
||||
layout(binding=0) uniform atomic_uint ac;
|
||||
|
||||
bool sampler1DAndAtomicCounter(uvec4 sExpect, in sampler2D s[4], in atomic_uint a, uint aExpect)
|
||||
{
|
||||
uvec4 sResult = uvec4(uint(texture(s[0], vec2(0.5, 0.5)).x * 255.0),
|
||||
uint(texture(s[1], vec2(0.5, 0.5)).x * 255.0),
|
||||
uint(texture(s[2], vec2(0.5, 0.5)).x * 255.0),
|
||||
uint(texture(s[3], vec2(0.5, 0.5)).x * 255.0));
|
||||
uint aResult = atomicCounter(a);
|
||||
|
||||
return sExpect == sResult && aExpect == aResult;
|
||||
}
|
||||
|
||||
bool sampler3DAndAtomicCounter(in sampler2D s[2][3][4], uint aInitial, in atomic_uint a)
|
||||
{
|
||||
bool success = true;
|
||||
// [0][0]
|
||||
success = sampler1DAndAtomicCounter(uvec4(0, 8, 16, 24),
|
||||
s[atomicCounterIncrement(ac)][0], a, aInitial + 1u) && success;
|
||||
// [1][0]
|
||||
success = sampler1DAndAtomicCounter(uvec4(96, 104, 112, 120),
|
||||
s[atomicCounterIncrement(ac)][0], a, aInitial + 2u) && success;
|
||||
// [0][1]
|
||||
success = sampler1DAndAtomicCounter(uvec4(32, 40, 48, 56),
|
||||
s[0][atomicCounterIncrement(ac) - 1u], a, aInitial + 3u) && success;
|
||||
// [0][2]
|
||||
success = sampler1DAndAtomicCounter(uvec4(64, 72, 80, 88),
|
||||
s[0][atomicCounterIncrement(ac) - 1u], a, aInitial + 4u) && success;
|
||||
// [1][1]
|
||||
success = sampler1DAndAtomicCounter(uvec4(128, 136, 144, 152),
|
||||
s[1][atomicCounterIncrement(ac) - 3u], a, aInitial + 5u) && success;
|
||||
// [1][2]
|
||||
uint acValue = atomicCounterIncrement(ac); // Returns 5
|
||||
success = sampler1DAndAtomicCounter(uvec4(160, 168, 176, 184),
|
||||
s[acValue - 4u][atomicCounterIncrement(ac) - 4u], a, aInitial + 7u) && success;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
outbuf.success = uint(sampler3DAndAtomicCounter(smplr, 0u, ac));
|
||||
}
|
||||
)";
|
||||
ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShader);
|
||||
EXPECT_GL_NO_ERROR();
|
||||
|
||||
glUseProgram(program);
|
||||
|
||||
unsigned int outputInitData = 0x12345678u;
|
||||
GLBuffer outputBuffer;
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, outputBuffer);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(outputInitData), &outputInitData, GL_STATIC_DRAW);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, outputBuffer);
|
||||
EXPECT_GL_NO_ERROR();
|
||||
|
||||
unsigned int acData = 0u;
|
||||
GLBuffer atomicCounterBuffer;
|
||||
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicCounterBuffer);
|
||||
glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(acData), &acData, GL_STATIC_DRAW);
|
||||
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, atomicCounterBuffer);
|
||||
EXPECT_GL_NO_ERROR();
|
||||
|
||||
const std::array<GLColor, 24> kTextureData = {
|
||||
GLColor(0, 0, 0, 0), GLColor(8, 0, 0, 0), GLColor(16, 0, 0, 0), GLColor(24, 0, 0, 0),
|
||||
GLColor(32, 0, 0, 0), GLColor(40, 0, 0, 0), GLColor(48, 0, 0, 0), GLColor(56, 0, 0, 0),
|
||||
GLColor(64, 0, 0, 0), GLColor(72, 0, 0, 0), GLColor(80, 0, 0, 0), GLColor(88, 0, 0, 0),
|
||||
GLColor(96, 0, 0, 0), GLColor(104, 0, 0, 0), GLColor(112, 0, 0, 0), GLColor(120, 0, 0, 0),
|
||||
GLColor(128, 0, 0, 0), GLColor(136, 0, 0, 0), GLColor(144, 0, 0, 0), GLColor(152, 0, 0, 0),
|
||||
GLColor(160, 0, 0, 0), GLColor(168, 0, 0, 0), GLColor(176, 0, 0, 0), GLColor(184, 0, 0, 0),
|
||||
};
|
||||
GLTexture textures[2][3][4];
|
||||
|
||||
for (int dim1 = 0; dim1 < 2; ++dim1)
|
||||
{
|
||||
for (int dim2 = 0; dim2 < 3; ++dim2)
|
||||
{
|
||||
for (int dim3 = 0; dim3 < 4; ++dim3)
|
||||
{
|
||||
int textureUnit = (dim1 * 3 + dim2) * 4 + dim3;
|
||||
glActiveTexture(GL_TEXTURE0 + textureUnit);
|
||||
glBindTexture(GL_TEXTURE_2D, textures[dim1][dim2][dim3]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
&kTextureData[textureUnit]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
std::stringstream uniformName;
|
||||
uniformName << "smplr[" << dim1 << "][" << dim2 << "][" << dim3 << "]";
|
||||
GLint samplerLocation = glGetUniformLocation(program, uniformName.str().c_str());
|
||||
EXPECT_NE(samplerLocation, -1);
|
||||
glUniform1i(samplerLocation, textureUnit);
|
||||
}
|
||||
}
|
||||
}
|
||||
ASSERT_GL_NO_ERROR();
|
||||
|
||||
glDispatchCompute(1, 1, 1);
|
||||
EXPECT_GL_NO_ERROR();
|
||||
|
||||
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
|
||||
|
||||
// read back
|
||||
const GLuint *ptr = reinterpret_cast<const GLuint *>(
|
||||
glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(outputInitData), GL_MAP_READ_BIT));
|
||||
EXPECT_EQ(ptr[0], 1u);
|
||||
|
||||
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
|
||||
}
|
||||
|
||||
// Test that array of array of samplers can be indexed correctly with dynamic indices. Uses
|
||||
// samplers in structs.
|
||||
TEST_P(GLSLTest_ES31, ArrayOfArrayOfSamplerInStructDynamicIndex)
|
||||
{
|
||||
// Skip if EXT_gpu_shader5 is not enabled.
|
||||
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_gpu_shader5"));
|
||||
|
||||
int maxTextureImageUnits = 0;
|
||||
glGetIntegerv(GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits);
|
||||
ANGLE_SKIP_TEST_IF(maxTextureImageUnits < 24);
|
||||
|
||||
// http://anglebug.com/5072
|
||||
ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux() && IsOpenGL());
|
||||
|
||||
// anglebug.com/3832 - no sampler array params on Android
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
|
||||
|
||||
// http://anglebug.com/5546
|
||||
ANGLE_SKIP_TEST_IF(IsWindows() && IsIntel() && IsOpenGL());
|
||||
|
||||
constexpr char kComputeShader[] = R"(#version 310 es
|
||||
#extension GL_EXT_gpu_shader5 : require
|
||||
|
||||
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
layout(binding = 1, std430) buffer Output {
|
||||
uint success;
|
||||
} outbuf;
|
||||
|
||||
struct I
|
||||
{
|
||||
uint index;
|
||||
};
|
||||
|
||||
struct S
|
||||
{
|
||||
sampler2D smplr[4];
|
||||
I nested;
|
||||
};
|
||||
|
||||
struct T
|
||||
{
|
||||
S nested[3];
|
||||
uint tIndex;
|
||||
};
|
||||
|
||||
uniform T u[2];
|
||||
|
||||
uint getValue(in sampler2D s)
|
||||
{
|
||||
return uint(texture(s, vec2(0.5, 0.5)).x * 255.0);
|
||||
}
|
||||
|
||||
bool sampler1DTest(uvec4 sExpect, in sampler2D s[4])
|
||||
{
|
||||
uvec4 sResult = uvec4(getValue(s[0]), getValue(s[1]),
|
||||
getValue(s[2]), getValue(s[3]));
|
||||
|
||||
return sExpect == sResult;
|
||||
}
|
||||
|
||||
bool samplerTest(T t, uint N)
|
||||
{
|
||||
// u[N].tIndex == 0 + N*4
|
||||
// u[N].nested[0].nested.index == 1 + N*4
|
||||
// u[N].nested[1].nested.index == 2 + N*4
|
||||
// u[N].nested[2].nested.index == 3 + N*4
|
||||
|
||||
uvec4 colorOffset = N * 3u * 4u * uvec4(8);
|
||||
|
||||
bool success = true;
|
||||
// [N][0]
|
||||
success = sampler1DTest(uvec4(0, 8, 16, 24) + colorOffset,
|
||||
t.nested[t.nested[0].nested.index - t.tIndex - 1u].smplr) && success;
|
||||
// [N][1]
|
||||
success = sampler1DTest(uvec4(32, 40, 48, 56) + colorOffset,
|
||||
t.nested[t.nested[1].nested.index - t.tIndex - 1u].smplr) && success;
|
||||
// [N][2]
|
||||
success = sampler1DTest(uvec4(64, 72, 80, 88) + colorOffset,
|
||||
t.nested[t.nested[2].nested.index - t.tIndex - 1u].smplr) && success;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool uniformTest(T t, uint N)
|
||||
{
|
||||
// Also verify that expressions that involve structs-with-samplers are correct when not
|
||||
// referecing the sampler.
|
||||
|
||||
bool success = true;
|
||||
success = (t.nested[0].nested.index - t.tIndex == 1u) && success;
|
||||
success = (t.nested[1].nested.index - t.tIndex == 2u) && success;
|
||||
success = (t.nested[2].nested.index - t.tIndex == 3u) && success;
|
||||
|
||||
success = (t.nested[t.nested[0].nested.index - t.tIndex - 1u].nested.index - t.tIndex == 1u)
|
||||
&& success;
|
||||
success = (t.nested[t.nested[0].nested.index - t.tIndex ].nested.index - t.tIndex == 2u)
|
||||
&& success;
|
||||
success = (t.nested[t.nested[0].nested.index - t.tIndex + 1u].nested.index - t.tIndex == 3u)
|
||||
&& success;
|
||||
|
||||
success = (t.nested[
|
||||
t.nested[
|
||||
t.nested[2].nested.index - t.tIndex - 1u // 2
|
||||
].nested.index - t.tIndex - 2u // 1
|
||||
].nested.index - t.tIndex // 2
|
||||
== 2u) && success;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
bool success = samplerTest(u[0], 0u) && samplerTest(u[1], 1u)
|
||||
&& uniformTest(u[0], 0u) && uniformTest(u[1], 1u);
|
||||
outbuf.success = uint(success);
|
||||
}
|
||||
)";
|
||||
ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShader);
|
||||
EXPECT_GL_NO_ERROR();
|
||||
|
||||
glUseProgram(program);
|
||||
|
||||
unsigned int outputInitData = 0x12345678u;
|
||||
GLBuffer outputBuffer;
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, outputBuffer);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(outputInitData), &outputInitData, GL_STATIC_DRAW);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, outputBuffer);
|
||||
EXPECT_GL_NO_ERROR();
|
||||
|
||||
const std::array<GLColor, 24> kTextureData = {
|
||||
GLColor(0, 0, 0, 0), GLColor(8, 0, 0, 0), GLColor(16, 0, 0, 0), GLColor(24, 0, 0, 0),
|
||||
GLColor(32, 0, 0, 0), GLColor(40, 0, 0, 0), GLColor(48, 0, 0, 0), GLColor(56, 0, 0, 0),
|
||||
GLColor(64, 0, 0, 0), GLColor(72, 0, 0, 0), GLColor(80, 0, 0, 0), GLColor(88, 0, 0, 0),
|
||||
GLColor(96, 0, 0, 0), GLColor(104, 0, 0, 0), GLColor(112, 0, 0, 0), GLColor(120, 0, 0, 0),
|
||||
GLColor(128, 0, 0, 0), GLColor(136, 0, 0, 0), GLColor(144, 0, 0, 0), GLColor(152, 0, 0, 0),
|
||||
GLColor(160, 0, 0, 0), GLColor(168, 0, 0, 0), GLColor(176, 0, 0, 0), GLColor(184, 0, 0, 0),
|
||||
};
|
||||
GLTexture textures[2][3][4];
|
||||
|
||||
for (int dim1 = 0; dim1 < 2; ++dim1)
|
||||
{
|
||||
for (int dim2 = 0; dim2 < 3; ++dim2)
|
||||
{
|
||||
for (int dim3 = 0; dim3 < 4; ++dim3)
|
||||
{
|
||||
int textureUnit = (dim1 * 3 + dim2) * 4 + dim3;
|
||||
glActiveTexture(GL_TEXTURE0 + textureUnit);
|
||||
glBindTexture(GL_TEXTURE_2D, textures[dim1][dim2][dim3]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
&kTextureData[textureUnit]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
std::stringstream uniformName;
|
||||
uniformName << "u[" << dim1 << "].nested[" << dim2 << "].smplr[" << dim3 << "]";
|
||||
GLint samplerLocation = glGetUniformLocation(program, uniformName.str().c_str());
|
||||
EXPECT_NE(samplerLocation, -1);
|
||||
glUniform1i(samplerLocation, textureUnit);
|
||||
}
|
||||
|
||||
std::stringstream uniformName;
|
||||
uniformName << "u[" << dim1 << "].nested[" << dim2 << "].nested.index";
|
||||
GLint nestedIndexLocation = glGetUniformLocation(program, uniformName.str().c_str());
|
||||
EXPECT_NE(nestedIndexLocation, -1);
|
||||
glUniform1ui(nestedIndexLocation, dim1 * 4 + dim2 + 1);
|
||||
}
|
||||
|
||||
std::stringstream uniformName;
|
||||
uniformName << "u[" << dim1 << "].tIndex";
|
||||
GLint indexLocation = glGetUniformLocation(program, uniformName.str().c_str());
|
||||
EXPECT_NE(indexLocation, -1);
|
||||
glUniform1ui(indexLocation, dim1 * 4);
|
||||
}
|
||||
ASSERT_GL_NO_ERROR();
|
||||
|
||||
glDispatchCompute(1, 1, 1);
|
||||
EXPECT_GL_NO_ERROR();
|
||||
|
||||
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
|
||||
|
||||
// read back
|
||||
const GLuint *ptr = reinterpret_cast<const GLuint *>(
|
||||
glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(outputInitData), GL_MAP_READ_BIT));
|
||||
EXPECT_EQ(ptr[0], 1u);
|
||||
|
||||
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
|
||||
}
|
||||
|
||||
// Test that array of array of samplers work when indexed with an expression that's derived from an
|
||||
// array of array of samplers.
|
||||
TEST_P(GLSLTest_ES31, ArrayOfArrayOfSamplerIndexedWithArrayOfArrayOfSamplers)
|
||||
{
|
||||
// Skip if EXT_gpu_shader5 is not enabled.
|
||||
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_gpu_shader5"));
|
||||
|
||||
// anglebug.com/3832 - no sampler array params on Android
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
|
||||
|
||||
constexpr char kComputeShader[] = R"(#version 310 es
|
||||
#extension GL_EXT_gpu_shader5 : require
|
||||
|
||||
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
layout(binding = 1, std430) buffer Output {
|
||||
uint success;
|
||||
} outbuf;
|
||||
|
||||
uniform sampler2D smplr[2][3];
|
||||
|
||||
uint getValue(in sampler2D s)
|
||||
{
|
||||
return uint(texture(s, vec2(0.5, 0.5)).x * 255.0);
|
||||
}
|
||||
|
||||
bool runTest(in sampler2D s[2][3])
|
||||
{
|
||||
// s[0][0] should contain 2
|
||||
// s[0][1] should contain 0
|
||||
// s[0][2] should contain 1
|
||||
// s[1][0] should contain 1
|
||||
// s[1][1] should contain 2
|
||||
// s[1][2] should contain 0
|
||||
|
||||
uint result = getValue(
|
||||
s[
|
||||
getValue(
|
||||
s[
|
||||
getValue(s[0][1]) // 0
|
||||
][
|
||||
getValue(s[0][0]) // 2
|
||||
]
|
||||
) // s[0][2] -> 1
|
||||
][
|
||||
getValue(
|
||||
s[
|
||||
getValue(s[1][0]) // 1
|
||||
][
|
||||
getValue(s[1][1]) // 2
|
||||
]
|
||||
) // s[1][2] -> 0
|
||||
]
|
||||
); // s[1][0] -> 1
|
||||
|
||||
return result == 1u;
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
outbuf.success = uint(runTest(smplr));
|
||||
}
|
||||
)";
|
||||
ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShader);
|
||||
EXPECT_GL_NO_ERROR();
|
||||
|
||||
glUseProgram(program);
|
||||
|
||||
unsigned int outputInitData = 0x12345678u;
|
||||
GLBuffer outputBuffer;
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, outputBuffer);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(outputInitData), &outputInitData, GL_STATIC_DRAW);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, outputBuffer);
|
||||
EXPECT_GL_NO_ERROR();
|
||||
|
||||
const std::array<GLColor, 6> kTextureData = {
|
||||
GLColor(2, 0, 0, 0), GLColor(0, 0, 0, 0), GLColor(1, 0, 0, 0),
|
||||
GLColor(1, 0, 0, 0), GLColor(2, 0, 0, 0), GLColor(0, 0, 0, 0),
|
||||
};
|
||||
GLTexture textures[2][3];
|
||||
|
||||
for (int dim1 = 0; dim1 < 2; ++dim1)
|
||||
{
|
||||
for (int dim2 = 0; dim2 < 3; ++dim2)
|
||||
{
|
||||
int textureUnit = dim1 * 3 + dim2;
|
||||
glActiveTexture(GL_TEXTURE0 + textureUnit);
|
||||
glBindTexture(GL_TEXTURE_2D, textures[dim1][dim2]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
&kTextureData[textureUnit]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
std::stringstream uniformName;
|
||||
uniformName << "smplr[" << dim1 << "][" << dim2 << "]";
|
||||
GLint samplerLocation = glGetUniformLocation(program, uniformName.str().c_str());
|
||||
EXPECT_NE(samplerLocation, -1);
|
||||
glUniform1i(samplerLocation, textureUnit);
|
||||
}
|
||||
}
|
||||
ASSERT_GL_NO_ERROR();
|
||||
|
||||
glDispatchCompute(1, 1, 1);
|
||||
EXPECT_GL_NO_ERROR();
|
||||
|
||||
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
|
||||
|
||||
// read back
|
||||
const GLuint *ptr = reinterpret_cast<const GLuint *>(
|
||||
glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(outputInitData), GL_MAP_READ_BIT));
|
||||
EXPECT_EQ(ptr[0], 1u);
|
||||
|
||||
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
|
||||
}
|
||||
|
||||
// Test that multiple nested assignments are handled correctly.
|
||||
TEST_P(GLSLTest_ES31, MixedRowAndColumnMajorMatrices_WriteSideEffect)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче