Vulkan: add uniform buffer object support
Support for layout qualifiers in interface blocks are added. All
interface blocks are adjusted to either be in std140 or std430.
In the Vulkan backend, a new descriptor set is added for UBOs. A dirty
bit is added for UBO updating and pipeline layouts and descriptor
bindings are updated.
Bug: angleproject:3199, angleproject:3220
Change-Id: I271fc34ac2e1e8b76dee75e54a7cff0fe15fe4ee
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1565061
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/include/GLSLANG/ShaderLang.h b/include/GLSLANG/ShaderLang.h
index c804715..516b7e0 100644
--- a/include/GLSLANG/ShaderLang.h
+++ b/include/GLSLANG/ShaderLang.h
@@ -26,7 +26,7 @@
// Version number for shader translation API.
// It is incremented every time the API changes.
-#define ANGLE_SH_VERSION 206
+#define ANGLE_SH_VERSION 207
enum ShShaderSpec
{
@@ -281,6 +281,12 @@
// http://anglebug.com/3246
const ShCompileOptions SH_FORCE_ATOMIC_VALUE_RESOLUTION = UINT64_C(1) << 42;
+// Change interface block layout qualifiers to std140 for any layout that is not explicitly set to
+// std430. This is to comply with GL_KHR_vulkan_glsl where shared and packed are not allowed (and
+// std140 could be used instead) and unspecified layouts can assume either std140 or std430 (and we
+// choose std140 as std430 is not yet universally supported).
+const ShCompileOptions SH_REDEFINE_INTERFACE_LAYOUT_QUALIFIERS_WITH_STD = UINT64_C(1) << 43;
+
// Defines alternate strategies for implementing array index clamping.
enum ShArrayIndexClampingStrategy
{
diff --git a/include/platform/FeaturesVk.h b/include/platform/FeaturesVk.h
index 92698ea..183f114 100644
--- a/include/platform/FeaturesVk.h
+++ b/include/platform/FeaturesVk.h
@@ -83,6 +83,12 @@
// On Qualcomm, a bug is preventing us from using loadOp=Clear with inline commands in the
// render pass. http://anglebug.com/2361
bool restartRenderPassAfterLoadOpClear = false;
+
+ // On Qualcomm, gaps in bound descriptor set indices causes the post-gap sets to misbehave.
+ // For example, binding only descriptor set 3 results in zero being read from a uniform buffer
+ // object within that set. This flag results in empty descriptor sets being bound for any
+ // unused descriptor set to work around this issue. http://anglebug.com/2727
+ bool bindEmptyForUnusedDescriptorSets = false;
};
inline FeaturesVk::FeaturesVk() = default;
diff --git a/src/compiler.gni b/src/compiler.gni
index 9fced4d..5f8a2d1 100644
--- a/src/compiler.gni
+++ b/src/compiler.gni
@@ -160,6 +160,8 @@
"src/compiler/translator/tree_ops/RewriteDoWhile.h",
"src/compiler/translator/tree_ops/RewriteExpressionsWithShaderStorageBlock.cpp",
"src/compiler/translator/tree_ops/RewriteExpressionsWithShaderStorageBlock.h",
+ "src/compiler/translator/tree_ops/RedefineInterfaceBlockLayoutQualifiersWithStd.cpp",
+ "src/compiler/translator/tree_ops/RedefineInterfaceBlockLayoutQualifiersWithStd.h",
"src/compiler/translator/tree_ops/RewriteStructSamplers.cpp",
"src/compiler/translator/tree_ops/RewriteStructSamplers.h",
"src/compiler/translator/tree_ops/RewriteRepeatedAssignToSwizzled.cpp",
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index a31eda7..bdfb7f3 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -33,6 +33,7 @@
#include "compiler/translator/tree_ops/InitializeVariables.h"
#include "compiler/translator/tree_ops/PruneEmptyCases.h"
#include "compiler/translator/tree_ops/PruneNoOps.h"
+#include "compiler/translator/tree_ops/RedefineInterfaceBlockLayoutQualifiersWithStd.h"
#include "compiler/translator/tree_ops/RegenerateStructNames.h"
#include "compiler/translator/tree_ops/RemoveArrayLengthMethod.h"
#include "compiler/translator/tree_ops/RemoveInvariantDeclaration.h"
@@ -723,6 +724,13 @@
}
}
+ if (compileOptions & SH_REDEFINE_INTERFACE_LAYOUT_QUALIFIERS_WITH_STD)
+ {
+ // Change the interface block layouts based on GL_KHR_vulkan_glsl. Only std140 and std430
+ // are allowed in Vulkan GLSL.
+ RedefineInterfaceBlockLayoutQualifiersWithStd(root, &mSymbolTable);
+ }
+
if (shouldCollectVariables(compileOptions))
{
ASSERT(!mVariablesCollected);
diff --git a/src/compiler/translator/OutputGLSLBase.cpp b/src/compiler/translator/OutputGLSLBase.cpp
index 21396ea..254177c 100644
--- a/src/compiler/translator/OutputGLSLBase.cpp
+++ b/src/compiler/translator/OutputGLSLBase.cpp
@@ -61,11 +61,12 @@
private:
bool mFirst;
- friend TInfoSinkBase &operator<<(TInfoSinkBase &out,
- CommaSeparatedListItemPrefixGenerator &gen);
+ template <typename Stream>
+ friend Stream &operator<<(Stream &out, CommaSeparatedListItemPrefixGenerator &gen);
};
-TInfoSinkBase &operator<<(TInfoSinkBase &out, CommaSeparatedListItemPrefixGenerator &gen)
+template <typename Stream>
+Stream &operator<<(Stream &out, CommaSeparatedListItemPrefixGenerator &gen)
{
if (gen.mFirst)
{
@@ -160,6 +161,51 @@
}
}
+// Outputs what goes inside layout(), except for location and binding qualifiers, as they are
+// handled differently between GL GLSL and Vulkan GLSL.
+std::string TOutputGLSLBase::getCommonLayoutQualifiers(TIntermTyped *variable)
+{
+ std::ostringstream out;
+ CommaSeparatedListItemPrefixGenerator listItemPrefix;
+
+ const TType &type = variable->getType();
+ const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier();
+
+ if (type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn ||
+ IsVarying(type.getQualifier()))
+ {
+ if (type.getQualifier() == EvqFragmentOut && layoutQualifier.index >= 0)
+ {
+ out << listItemPrefix << "index = " << layoutQualifier.index;
+ }
+ }
+
+ if (type.getQualifier() == EvqFragmentOut)
+ {
+ if (layoutQualifier.yuv == true)
+ {
+ out << listItemPrefix << "yuv";
+ }
+ }
+
+ if (IsImage(type.getBasicType()))
+ {
+ if (layoutQualifier.imageInternalFormat != EiifUnspecified)
+ {
+ ASSERT(type.getQualifier() == EvqTemporary || type.getQualifier() == EvqUniform);
+ out << listItemPrefix
+ << getImageInternalFormatString(layoutQualifier.imageInternalFormat);
+ }
+ }
+
+ if (IsAtomicCounter(type.getBasicType()))
+ {
+ out << listItemPrefix << "offset = " << layoutQualifier.offset;
+ }
+
+ return out.str();
+}
+
void TOutputGLSLBase::writeLayoutQualifier(TIntermTyped *variable)
{
const TType &type = variable->getType();
@@ -189,18 +235,6 @@
{
out << listItemPrefix << "location = " << layoutQualifier.location;
}
- if (type.getQualifier() == EvqFragmentOut && layoutQualifier.index >= 0)
- {
- out << listItemPrefix << "index = " << layoutQualifier.index;
- }
- }
-
- if (type.getQualifier() == EvqFragmentOut)
- {
- if (layoutQualifier.yuv == true)
- {
- out << listItemPrefix << "yuv";
- }
}
if (IsOpaqueType(type.getBasicType()))
@@ -211,25 +245,16 @@
}
}
- if (IsImage(type.getBasicType()))
+ std::string otherQualifiers = getCommonLayoutQualifiers(variable);
+ if (!otherQualifiers.empty())
{
- if (layoutQualifier.imageInternalFormat != EiifUnspecified)
- {
- ASSERT(type.getQualifier() == EvqTemporary || type.getQualifier() == EvqUniform);
- out << listItemPrefix
- << getImageInternalFormatString(layoutQualifier.imageInternalFormat);
- }
- }
-
- if (IsAtomicCounter(type.getBasicType()))
- {
- out << listItemPrefix << "offset = " << layoutQualifier.offset;
+ out << listItemPrefix << otherQualifiers;
}
out << ") ";
}
-void TOutputGLSLBase::writeQualifier(TQualifier qualifier, const TSymbol *symbol)
+void TOutputGLSLBase::writeQualifier(TQualifier qualifier, const TType &type, const TSymbol *symbol)
{
const char *result = mapQualifierToString(qualifier);
if (result && result[0] != '\0')
@@ -284,7 +309,7 @@
}
if (qualifier != EvqTemporary && qualifier != EvqGlobal)
{
- writeQualifier(qualifier, symbol);
+ writeQualifier(qualifier, type, symbol);
}
const TMemoryQualifier &memoryQualifier = type.getMemoryQualifier();
diff --git a/src/compiler/translator/OutputGLSLBase.h b/src/compiler/translator/OutputGLSLBase.h
index 96f767e..feeffee 100644
--- a/src/compiler/translator/OutputGLSLBase.h
+++ b/src/compiler/translator/OutputGLSLBase.h
@@ -40,6 +40,7 @@
TInfoSinkBase &objSink() { return mObjSink; }
void writeFloat(TInfoSinkBase &out, float f);
void writeTriplet(Visit visit, const char *preStr, const char *inStr, const char *postStr);
+ std::string getCommonLayoutQualifiers(TIntermTyped *variable);
virtual void writeLayoutQualifier(TIntermTyped *variable);
void writeInvariantQualifier(const TType &type);
virtual void writeVariableType(const TType &type, const TSymbol *symbol);
@@ -77,7 +78,7 @@
virtual ImmutableString translateTextureFunction(const ImmutableString &name) { return name; }
void declareStruct(const TStructure *structure);
- virtual void writeQualifier(TQualifier qualifier, const TSymbol *symbol);
+ virtual void writeQualifier(TQualifier qualifier, const TType &type, const TSymbol *symbol);
bool structDeclared(const TStructure *structure) const;
private:
diff --git a/src/compiler/translator/OutputVulkanGLSL.cpp b/src/compiler/translator/OutputVulkanGLSL.cpp
index 2075cfe..73b76de 100644
--- a/src/compiler/translator/OutputVulkanGLSL.cpp
+++ b/src/compiler/translator/OutputVulkanGLSL.cpp
@@ -61,32 +61,73 @@
TIntermSymbol *symbol = variable->getAsSymbolNode();
ASSERT(symbol);
+ ImmutableString name = symbol->getName();
+ const char *blockStorage = nullptr;
+ const char *matrixPacking = nullptr;
+
+ // For interface blocks, use the block name instead. When the layout qualifier is being
+ // replaced in the backend, that would be the name that's available.
+ if (type.isInterfaceBlock())
+ {
+ const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
+ name = interfaceBlock->name();
+
+ // Make sure block storage format is specified.
+ if (interfaceBlock->blockStorage() != EbsUnspecified)
+ {
+ blockStorage = getBlockStorageString(interfaceBlock->blockStorage());
+ }
+ }
+
+ // Specify matrix packing if necessary.
+ if (layoutQualifier.matrixPacking != EmpUnspecified)
+ {
+ matrixPacking = getMatrixPackingString(layoutQualifier.matrixPacking);
+ }
+
if (needsCustomLayout)
{
- out << "@@ LAYOUT-" << symbol->getName() << "() @@";
+ out << "@@ LAYOUT-" << name << "(";
}
else
{
out << "layout(";
}
- if (IsImage(type.getBasicType()) && layoutQualifier.imageInternalFormat != EiifUnspecified)
+ // Output the list of qualifiers already known at this stage, i.e. everything other than
+ // `location` and `set`/`binding`.
+ std::string otherQualifiers = getCommonLayoutQualifiers(variable);
+
+ const char *separator = "";
+ if (blockStorage)
{
- ASSERT(type.getQualifier() == EvqTemporary || type.getQualifier() == EvqUniform);
- out << getImageInternalFormatString(layoutQualifier.imageInternalFormat);
+ out << separator << blockStorage;
+ separator = ", ";
+ }
+ if (matrixPacking)
+ {
+ out << separator << matrixPacking;
+ separator = ", ";
+ }
+ if (!otherQualifiers.empty())
+ {
+ out << separator << otherQualifiers;
}
- if (!needsCustomLayout)
+ out << ") ";
+ if (needsCustomLayout)
{
- out << ") ";
+ out << "@@";
}
}
-void TOutputVulkanGLSL::writeQualifier(TQualifier qualifier, const TSymbol *symbol)
+void TOutputVulkanGLSL::writeQualifier(TQualifier qualifier,
+ const TType &type,
+ const TSymbol *symbol)
{
if (qualifier != EvqUniform && qualifier != EvqAttribute && !sh::IsVarying(qualifier))
{
- TOutputGLSLBase::writeQualifier(qualifier, symbol);
+ TOutputGLSLBase::writeQualifier(qualifier, type, symbol);
return;
}
@@ -95,8 +136,17 @@
return;
}
+ ImmutableString name = symbol->name();
+
+ // For interface blocks, use the block name instead. When the qualifier is being replaced in
+ // the backend, that would be the name that's available.
+ if (type.isInterfaceBlock())
+ {
+ name = type.getInterfaceBlock()->name();
+ }
+
TInfoSinkBase &out = objSink();
- out << "@@ QUALIFIER-" << symbol->name().data() << " @@ ";
+ out << "@@ QUALIFIER-" << name.data() << " @@ ";
}
void TOutputVulkanGLSL::writeVariableType(const TType &type, const TSymbol *symbol)
diff --git a/src/compiler/translator/OutputVulkanGLSL.h b/src/compiler/translator/OutputVulkanGLSL.h
index ef49c97..717801d 100644
--- a/src/compiler/translator/OutputVulkanGLSL.h
+++ b/src/compiler/translator/OutputVulkanGLSL.h
@@ -31,7 +31,7 @@
protected:
void writeLayoutQualifier(TIntermTyped *variable) override;
- void writeQualifier(TQualifier qualifier, const TSymbol *symbol) override;
+ void writeQualifier(TQualifier qualifier, const TType &type, const TSymbol *symbol) override;
void writeVariableType(const TType &type, const TSymbol *symbol) override;
};
diff --git a/src/compiler/translator/tree_ops/RedefineInterfaceBlockLayoutQualifiersWithStd.cpp b/src/compiler/translator/tree_ops/RedefineInterfaceBlockLayoutQualifiersWithStd.cpp
new file mode 100644
index 0000000..a32281f
--- /dev/null
+++ b/src/compiler/translator/tree_ops/RedefineInterfaceBlockLayoutQualifiersWithStd.cpp
@@ -0,0 +1,107 @@
+//
+// Copyright 2019 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RedefineInterfaceBlockLayoutQualifiersWithStd: Ensure layout qualifiers are either std140 or
+// std430 to comply with Vulkan GLSL.
+//
+
+#include "compiler/translator/tree_ops/RedefineInterfaceBlockLayoutQualifiersWithStd.h"
+
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/tree_util/IntermTraverse.h"
+
+namespace sh
+{
+namespace
+{
+// Helper to replace the type of a symbol
+TIntermSymbol *RedefineLayoutQualifierOfSymbolNode(TIntermSymbol *symbolNode,
+ const TLayoutQualifier &newLayoutQualifier,
+ TSymbolTable *symbolTable)
+{
+ const TVariable &oldVariable = symbolNode->variable();
+
+ ASSERT(symbolNode->getType().isInterfaceBlock());
+
+ const TType &oldType = symbolNode->getType();
+ const TInterfaceBlock *oldInterfaceBlock = oldType.getInterfaceBlock();
+
+ // Create a new type based on the old type, but the memory layout qualifier changed.
+ TType *newType = new TType(oldType);
+ newType->setLayoutQualifier(newLayoutQualifier);
+
+ // Create a new interface block based on the old one, with the new memory layout qualifier as
+ // well.
+ TInterfaceBlock *newInterfaceBlock =
+ new TInterfaceBlock(symbolTable, oldInterfaceBlock->name(), &oldInterfaceBlock->fields(),
+ newLayoutQualifier, oldInterfaceBlock->symbolType());
+
+ newType->setInterfaceBlock(newInterfaceBlock);
+
+ // Create a new variable with the modified type, to substitute the old variable.
+ TVariable *newVariable =
+ new TVariable(oldVariable.uniqueId(), oldVariable.name(), oldVariable.symbolType(),
+ oldVariable.extension(), newType);
+
+ return new TIntermSymbol(newVariable);
+}
+
+class Traverser : public TIntermTraverser
+{
+ public:
+ explicit Traverser(TSymbolTable *symbolTable)
+ : TIntermTraverser(true, false, false, symbolTable)
+ {
+ symbolTable->push();
+ }
+
+ ~Traverser() override { mSymbolTable->pop(); }
+
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
+ {
+ ASSERT(visit == PreVisit);
+
+ if (!mInGlobalScope)
+ {
+ return false;
+ }
+
+ const TIntermSequence &sequence = *(node->getSequence());
+ TIntermTyped *declarator = sequence.front()->getAsTyped();
+ const TType &type = declarator->getType();
+
+ if (type.isInterfaceBlock())
+ {
+ ASSERT(declarator->getAsSymbolNode());
+
+ TLayoutQualifier layoutQualifier = type.getLayoutQualifier();
+
+ // If the layout qualifier is not explicitly std140 or std430, change it to std140 for
+ // uniforms and std430 otherwise. See the comment in the header for more information.
+ if (layoutQualifier.blockStorage != EbsStd140 &&
+ layoutQualifier.blockStorage != EbsStd430)
+ {
+ layoutQualifier.blockStorage =
+ type.getQualifier() == EvqUniform ? EbsStd140 : EbsStd430;
+
+ TIntermSymbol *replacement = RedefineLayoutQualifierOfSymbolNode(
+ declarator->getAsSymbolNode(), layoutQualifier, mSymbolTable);
+
+ queueReplacementWithParent(node, declarator, replacement, OriginalNode::IS_DROPPED);
+ }
+ }
+
+ return false;
+ }
+};
+} // anonymous namespace
+
+void RedefineInterfaceBlockLayoutQualifiersWithStd(TIntermBlock *root, TSymbolTable *symbolTable)
+{
+ Traverser redefineInterfaceBlockLayoutQualifiersWithStd(symbolTable);
+ root->traverse(&redefineInterfaceBlockLayoutQualifiersWithStd);
+ redefineInterfaceBlockLayoutQualifiersWithStd.updateTree();
+}
+} // namespace sh
diff --git a/src/compiler/translator/tree_ops/RedefineInterfaceBlockLayoutQualifiersWithStd.h b/src/compiler/translator/tree_ops/RedefineInterfaceBlockLayoutQualifiersWithStd.h
new file mode 100644
index 0000000..87b0c39
--- /dev/null
+++ b/src/compiler/translator/tree_ops/RedefineInterfaceBlockLayoutQualifiersWithStd.h
@@ -0,0 +1,25 @@
+//
+// Copyright 2019 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RedefineInterfaceBlockLayoutQualifiersWithStd: Change the memory layout qualifier of interface
+// blocks if not specifically requested to be std140 or std430, i.e. the memory layout qualifier is
+// changed if it's unspecified, shared or packed. This makes the layout qualifiers conformant with
+// Vulkan GLSL (GL_KHR_vulkan_glsl).
+//
+// - For uniform buffers, std140 is used. It would have been more efficient to default to std430,
+// but that would require GL_EXT_scalar_block_layout.
+// - For storage buffers, std430 is used.
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_REDEFINEINTERFACEBLOCKLAYOUTQUALIFIERSWITHSTD_H_
+#define COMPILER_TRANSLATOR_TREEOPS_REDEFINEINTERFACEBLOCKLAYOUTQUALIFIERSWITHSTD_H_
+
+namespace sh
+{
+class TIntermBlock;
+class TSymbolTable;
+void RedefineInterfaceBlockLayoutQualifiersWithStd(TIntermBlock *root, TSymbolTable *symbolTable);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_REDEFINEINTERFACEBLOCKLAYOUTQUALIFIERSWITHSTD_H_
diff --git a/src/libANGLE/ProgramLinkedResources.cpp b/src/libANGLE/ProgramLinkedResources.cpp
index 787687e..eaea974 100644
--- a/src/libANGLE/ProgramLinkedResources.cpp
+++ b/src/libANGLE/ProgramLinkedResources.cpp
@@ -535,6 +535,116 @@
ShaderUniformCount mUniformCount;
unsigned int mStructStackSize = 0;
};
+
+class InterfaceBlockInfo final : angle::NonCopyable
+{
+ public:
+ InterfaceBlockInfo(CustomBlockLayoutEncoderFactory *customEncoderFactory)
+ : mCustomEncoderFactory(customEncoderFactory)
+ {}
+
+ void getShaderBlockInfo(const std::vector<sh::InterfaceBlock> &interfaceBlocks);
+
+ bool getBlockSize(const std::string &name, const std::string &mappedName, size_t *sizeOut);
+ bool getBlockMemberInfo(const std::string &name,
+ const std::string &mappedName,
+ sh::BlockMemberInfo *infoOut);
+
+ private:
+ size_t getBlockInfo(const sh::InterfaceBlock &interfaceBlock);
+
+ std::map<std::string, size_t> mBlockSizes;
+ sh::BlockLayoutMap mBlockLayout;
+ // Based on the interface block layout, the std140 or std430 encoders are used. On some
+ // platforms (currently only D3D), there could be another non-standard encoder used.
+ CustomBlockLayoutEncoderFactory *mCustomEncoderFactory;
+};
+
+void InterfaceBlockInfo::getShaderBlockInfo(const std::vector<sh::InterfaceBlock> &interfaceBlocks)
+{
+ for (const sh::InterfaceBlock &interfaceBlock : interfaceBlocks)
+ {
+ if (!interfaceBlock.active && interfaceBlock.layout == sh::BLOCKLAYOUT_PACKED)
+ continue;
+
+ if (mBlockSizes.count(interfaceBlock.name) > 0)
+ continue;
+
+ size_t dataSize = getBlockInfo(interfaceBlock);
+ mBlockSizes[interfaceBlock.name] = dataSize;
+ }
+}
+
+size_t InterfaceBlockInfo::getBlockInfo(const sh::InterfaceBlock &interfaceBlock)
+{
+ ASSERT(interfaceBlock.active || interfaceBlock.layout != sh::BLOCKLAYOUT_PACKED);
+
+ // define member uniforms
+ sh::Std140BlockEncoder std140Encoder;
+ sh::Std430BlockEncoder std430Encoder;
+ sh::BlockLayoutEncoder *customEncoder = nullptr;
+ sh::BlockLayoutEncoder *encoder = nullptr;
+
+ if (interfaceBlock.layout == sh::BLOCKLAYOUT_STD140)
+ {
+ encoder = &std140Encoder;
+ }
+ else if (interfaceBlock.layout == sh::BLOCKLAYOUT_STD430)
+ {
+ encoder = &std430Encoder;
+ }
+ else if (mCustomEncoderFactory)
+ {
+ encoder = customEncoder = mCustomEncoderFactory->makeEncoder();
+ }
+ else
+ {
+ UNREACHABLE();
+ return 0;
+ }
+
+ sh::GetInterfaceBlockInfo(interfaceBlock.fields, interfaceBlock.fieldPrefix(), encoder,
+ &mBlockLayout);
+
+ size_t offset = encoder->getCurrentOffset();
+
+ SafeDelete(customEncoder);
+
+ return offset;
+}
+
+bool InterfaceBlockInfo::getBlockSize(const std::string &name,
+ const std::string &mappedName,
+ size_t *sizeOut)
+{
+ size_t nameLengthWithoutArrayIndex;
+ gl::ParseArrayIndex(name, &nameLengthWithoutArrayIndex);
+ std::string baseName = name.substr(0u, nameLengthWithoutArrayIndex);
+ auto sizeIter = mBlockSizes.find(baseName);
+ if (sizeIter == mBlockSizes.end())
+ {
+ *sizeOut = 0;
+ return false;
+ }
+
+ *sizeOut = sizeIter->second;
+ return true;
+}
+
+bool InterfaceBlockInfo::getBlockMemberInfo(const std::string &name,
+ const std::string &mappedName,
+ sh::BlockMemberInfo *infoOut)
+{
+ auto infoIter = mBlockLayout.find(name);
+ if (infoIter == mBlockLayout.end())
+ {
+ *infoOut = sh::kDefaultBlockMemberInfo;
+ return false;
+ }
+
+ *infoOut = infoIter->second;
+ return true;
+}
} // anonymous namespace
UniformLinker::UniformLinker(const ProgramState &state) : mState(state) {}
@@ -1121,4 +1231,87 @@
ProgramLinkedResources::~ProgramLinkedResources() = default;
+void ProgramLinkedResourcesLinker::linkResources(const gl::ProgramState &programState,
+ const gl::ProgramLinkedResources &resources) const
+{
+ // Gather uniform interface block info.
+ InterfaceBlockInfo uniformBlockInfo(mCustomEncoderFactory);
+ for (gl::ShaderType shaderType : gl::AllShaderTypes())
+ {
+ gl::Shader *shader = programState.getAttachedShader(shaderType);
+ if (shader)
+ {
+ uniformBlockInfo.getShaderBlockInfo(shader->getUniformBlocks());
+ }
+ }
+
+ auto getUniformBlockSize = [&uniformBlockInfo](const std::string &name,
+ const std::string &mappedName, size_t *sizeOut) {
+ return uniformBlockInfo.getBlockSize(name, mappedName, sizeOut);
+ };
+
+ auto getUniformBlockMemberInfo = [&uniformBlockInfo](const std::string &name,
+ const std::string &mappedName,
+ sh::BlockMemberInfo *infoOut) {
+ return uniformBlockInfo.getBlockMemberInfo(name, mappedName, infoOut);
+ };
+
+ // Link uniform interface blocks.
+ resources.uniformBlockLinker.linkBlocks(getUniformBlockSize, getUniformBlockMemberInfo);
+
+ // Gather storage bufer interface block info.
+ InterfaceBlockInfo shaderStorageBlockInfo(mCustomEncoderFactory);
+ for (gl::ShaderType shaderType : gl::AllShaderTypes())
+ {
+ gl::Shader *shader = programState.getAttachedShader(shaderType);
+ if (shader)
+ {
+ shaderStorageBlockInfo.getShaderBlockInfo(shader->getShaderStorageBlocks());
+ }
+ }
+ auto getShaderStorageBlockSize = [&shaderStorageBlockInfo](const std::string &name,
+ const std::string &mappedName,
+ size_t *sizeOut) {
+ return shaderStorageBlockInfo.getBlockSize(name, mappedName, sizeOut);
+ };
+
+ auto getShaderStorageBlockMemberInfo = [&shaderStorageBlockInfo](const std::string &name,
+ const std::string &mappedName,
+ sh::BlockMemberInfo *infoOut) {
+ return shaderStorageBlockInfo.getBlockMemberInfo(name, mappedName, infoOut);
+ };
+
+ // Link storage buffer interface blocks.
+ resources.shaderStorageBlockLinker.linkBlocks(getShaderStorageBlockSize,
+ getShaderStorageBlockMemberInfo);
+
+ // Gather and link atomic counter buffer interface blocks.
+ std::map<int, unsigned int> sizeMap;
+ getAtomicCounterBufferSizeMap(programState, sizeMap);
+ resources.atomicCounterBufferLinker.link(sizeMap);
+}
+
+void ProgramLinkedResourcesLinker::getAtomicCounterBufferSizeMap(
+ const gl::ProgramState &programState,
+ std::map<int, unsigned int> &sizeMapOut) const
+{
+ for (unsigned int index : programState.getAtomicCounterUniformRange())
+ {
+ const gl::LinkedUniform &glUniform = programState.getUniforms()[index];
+
+ auto &bufferDataSize = sizeMapOut[glUniform.binding];
+
+ // Calculate the size of the buffer by finding the end of the last uniform with the same
+ // binding. The end of the uniform is calculated by finding the initial offset of the
+ // uniform and adding size of the uniform. For arrays, the size is the number of elements
+ // times the element size (should always by 4 for atomic_units).
+ unsigned dataOffset =
+ glUniform.offset + (glUniform.getBasicTypeElementCount() * glUniform.getElementSize());
+ if (dataOffset > bufferDataSize)
+ {
+ bufferDataSize = dataOffset;
+ }
+ }
+}
+
} // namespace gl
diff --git a/src/libANGLE/ProgramLinkedResources.h b/src/libANGLE/ProgramLinkedResources.h
index a4588f9..e91102b 100644
--- a/src/libANGLE/ProgramLinkedResources.h
+++ b/src/libANGLE/ProgramLinkedResources.h
@@ -20,6 +20,7 @@
namespace sh
{
+class BlockLayoutEncoder;
struct BlockMemberInfo;
struct InterfaceBlock;
struct ShaderVariable;
@@ -209,6 +210,33 @@
std::vector<UnusedUniform> unusedUniforms;
};
+class CustomBlockLayoutEncoderFactory : angle::NonCopyable
+{
+ public:
+ virtual ~CustomBlockLayoutEncoderFactory() {}
+
+ virtual sh::BlockLayoutEncoder *makeEncoder() = 0;
+};
+
+// Used by the backends in Program*::linkResources to parse interface blocks and provide
+// information to ProgramLinkedResources' linkers.
+class ProgramLinkedResourcesLinker final : angle::NonCopyable
+{
+ public:
+ ProgramLinkedResourcesLinker(CustomBlockLayoutEncoderFactory *customEncoderFactory)
+ : mCustomEncoderFactory(customEncoderFactory)
+ {}
+
+ void linkResources(const gl::ProgramState &programState,
+ const gl::ProgramLinkedResources &resources) const;
+
+ private:
+ void getAtomicCounterBufferSizeMap(const gl::ProgramState &programState,
+ std::map<int, unsigned int> &sizeMapOut) const;
+
+ CustomBlockLayoutEncoderFactory *mCustomEncoderFactory;
+};
+
} // namespace gl
#endif // LIBANGLE_UNIFORMLINKER_H_
diff --git a/src/libANGLE/angletypes.h b/src/libANGLE/angletypes.h
index e7b5bf0..a5f94a5 100644
--- a/src/libANGLE/angletypes.h
+++ b/src/libANGLE/angletypes.h
@@ -461,6 +461,9 @@
using ActiveTexturePointerArray = ActiveTextureArray<Texture *>;
using ActiveTextureTypeArray = ActiveTextureArray<TextureType>;
+template <typename T>
+using UniformBuffersArray = std::array<T, IMPLEMENTATION_MAX_UNIFORM_BUFFER_BINDINGS>;
+
using ImageUnitMask = angle::BitSet<IMPLEMENTATION_MAX_IMAGE_UNITS>;
// OffsetBindingPointer.getSize() returns the size specified by the user, which may be larger than
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
index 20139d1..7508723 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -201,102 +201,6 @@
return false;
}
-class InterfaceBlockInfo final : angle::NonCopyable
-{
- public:
- InterfaceBlockInfo() {}
-
- void getShaderBlockInfo(const std::vector<sh::InterfaceBlock> &interfaceBlocks);
-
- bool getBlockSize(const std::string &name, const std::string &mappedName, size_t *sizeOut);
- bool getBlockMemberInfo(const std::string &name,
- const std::string &mappedName,
- sh::BlockMemberInfo *infoOut);
-
- private:
- size_t getBlockInfo(const sh::InterfaceBlock &interfaceBlock);
-
- std::map<std::string, size_t> mBlockSizes;
- sh::BlockLayoutMap mBlockLayout;
-};
-
-void InterfaceBlockInfo::getShaderBlockInfo(const std::vector<sh::InterfaceBlock> &interfaceBlocks)
-{
- for (const sh::InterfaceBlock &interfaceBlock : interfaceBlocks)
- {
- if (!interfaceBlock.active && interfaceBlock.layout == sh::BLOCKLAYOUT_PACKED)
- continue;
-
- if (mBlockSizes.count(interfaceBlock.name) > 0)
- continue;
-
- size_t dataSize = getBlockInfo(interfaceBlock);
- mBlockSizes[interfaceBlock.name] = dataSize;
- }
-}
-
-size_t InterfaceBlockInfo::getBlockInfo(const sh::InterfaceBlock &interfaceBlock)
-{
- ASSERT(interfaceBlock.active || interfaceBlock.layout != sh::BLOCKLAYOUT_PACKED);
-
- // define member uniforms
- sh::Std140BlockEncoder std140Encoder;
- sh::Std430BlockEncoder std430Encoder;
- sh::HLSLBlockEncoder hlslEncoder(sh::HLSLBlockEncoder::ENCODE_PACKED, false);
- sh::BlockLayoutEncoder *encoder = nullptr;
-
- if (interfaceBlock.layout == sh::BLOCKLAYOUT_STD140)
- {
- encoder = &std140Encoder;
- }
- else if (interfaceBlock.layout == sh::BLOCKLAYOUT_STD430)
- {
- encoder = &std430Encoder;
- }
- else
- {
- encoder = &hlslEncoder;
- }
-
- sh::GetInterfaceBlockInfo(interfaceBlock.fields, interfaceBlock.fieldPrefix(), encoder,
- &mBlockLayout);
-
- return encoder->getCurrentOffset();
-}
-
-bool InterfaceBlockInfo::getBlockSize(const std::string &name,
- const std::string &mappedName,
- size_t *sizeOut)
-{
- size_t nameLengthWithoutArrayIndex;
- gl::ParseArrayIndex(name, &nameLengthWithoutArrayIndex);
- std::string baseName = name.substr(0u, nameLengthWithoutArrayIndex);
- auto sizeIter = mBlockSizes.find(baseName);
- if (sizeIter == mBlockSizes.end())
- {
- *sizeOut = 0;
- return false;
- }
-
- *sizeOut = sizeIter->second;
- return true;
-}
-
-bool InterfaceBlockInfo::getBlockMemberInfo(const std::string &name,
- const std::string &mappedName,
- sh::BlockMemberInfo *infoOut)
-{
- auto infoIter = mBlockLayout.find(name);
- if (infoIter == mBlockLayout.end())
- {
- *infoOut = sh::kDefaultBlockMemberInfo;
- return false;
- }
-
- *infoOut = infoIter->second;
- return true;
-}
-
// Helper class that gathers uniform info from the default uniform block.
class UniformEncodingVisitorD3D : public sh::BlockEncoderVisitor
{
@@ -356,6 +260,15 @@
HLSLRegisterType mRegisterType;
D3DUniformMap *mUniformMapOut;
};
+
+class HLSLBlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFactory
+{
+ public:
+ sh::BlockLayoutEncoder *makeEncoder() override
+ {
+ return new sh::HLSLBlockEncoder(sh::HLSLBlockEncoder::ENCODE_PACKED, false);
+ }
+};
} // anonymous namespace
// D3DUniform Implementation
@@ -3288,80 +3201,13 @@
void ProgramD3D::linkResources(const gl::ProgramLinkedResources &resources)
{
- InterfaceBlockInfo uniformBlockInfo;
- for (gl::ShaderType shaderType : gl::AllShaderTypes())
- {
- gl::Shader *shader = mState.getAttachedShader(shaderType);
- if (shader)
- {
- uniformBlockInfo.getShaderBlockInfo(shader->getUniformBlocks());
- }
- }
+ HLSLBlockLayoutEncoderFactory hlslEncoderFactory;
+ gl::ProgramLinkedResourcesLinker linker(&hlslEncoderFactory);
- // Gather interface block info.
- auto getUniformBlockSize = [&uniformBlockInfo](const std::string &name,
- const std::string &mappedName, size_t *sizeOut) {
- return uniformBlockInfo.getBlockSize(name, mappedName, sizeOut);
- };
+ linker.linkResources(mState, resources);
- auto getUniformBlockMemberInfo = [&uniformBlockInfo](const std::string &name,
- const std::string &mappedName,
- sh::BlockMemberInfo *infoOut) {
- return uniformBlockInfo.getBlockMemberInfo(name, mappedName, infoOut);
- };
-
- resources.uniformBlockLinker.linkBlocks(getUniformBlockSize, getUniformBlockMemberInfo);
initializeUniformBlocks();
-
- InterfaceBlockInfo shaderStorageBlockInfo;
- for (gl::ShaderType shaderType : gl::AllShaderTypes())
- {
- gl::Shader *shader = mState.getAttachedShader(shaderType);
- if (shader)
- {
- shaderStorageBlockInfo.getShaderBlockInfo(shader->getShaderStorageBlocks());
- }
- }
- auto getShaderStorageBlockSize = [&shaderStorageBlockInfo](const std::string &name,
- const std::string &mappedName,
- size_t *sizeOut) {
- return shaderStorageBlockInfo.getBlockSize(name, mappedName, sizeOut);
- };
-
- auto getShaderStorageBlockMemberInfo = [&shaderStorageBlockInfo](const std::string &name,
- const std::string &mappedName,
- sh::BlockMemberInfo *infoOut) {
- return shaderStorageBlockInfo.getBlockMemberInfo(name, mappedName, infoOut);
- };
-
- resources.shaderStorageBlockLinker.linkBlocks(getShaderStorageBlockSize,
- getShaderStorageBlockMemberInfo);
initializeShaderStorageBlocks();
-
- std::map<int, unsigned int> sizeMap;
- getAtomicCounterBufferSizeMap(sizeMap);
- resources.atomicCounterBufferLinker.link(sizeMap);
-}
-
-void ProgramD3D::getAtomicCounterBufferSizeMap(std::map<int, unsigned int> &sizeMapOut) const
-{
- for (unsigned int index : mState.getAtomicCounterUniformRange())
- {
- const gl::LinkedUniform &glUniform = mState.getUniforms()[index];
-
- auto &bufferDataSize = sizeMapOut[glUniform.binding];
-
- // Calculate the size of the buffer by finding the end of the last uniform with the same
- // binding. The end of the uniform is calculated by finding the initial offset of the
- // uniform and adding size of the uniform. For arrays, the size is the number of elements
- // times the element size (should always by 4 for atomic_units).
- unsigned dataOffset =
- glUniform.offset + (glUniform.getBasicTypeElementCount() * glUniform.getElementSize());
- if (dataOffset > bufferDataSize)
- {
- bufferDataSize = dataOffset;
- }
- }
}
} // namespace rx
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.h b/src/libANGLE/renderer/d3d/ProgramD3D.h
index e06e7b5..e8b54f9 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.h
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.h
@@ -455,8 +455,6 @@
std::vector<Image> &outImages,
gl::RangeUI *outUsedRange);
- void getAtomicCounterBufferSizeMap(std::map<int, unsigned int> &sizeMapOut) const;
-
template <typename DestT>
void getUniformInternal(GLint location, DestT *dataOut) const;
diff --git a/src/libANGLE/renderer/vulkan/BufferVk.cpp b/src/libANGLE/renderer/vulkan/BufferVk.cpp
index bd0385e..8b9aa96 100644
--- a/src/libANGLE/renderer/vulkan/BufferVk.cpp
+++ b/src/libANGLE/renderer/vulkan/BufferVk.cpp
@@ -28,7 +28,7 @@
constexpr size_t kBufferSizeGranularity = 4;
} // namespace
-BufferVk::BufferVk(const gl::BufferState &state) : BufferImpl(state) {}
+BufferVk::BufferVk(const gl::BufferState &state) : BufferImpl(state), mDataWriteAccessFlags(0) {}
BufferVk::~BufferVk() {}
@@ -63,7 +63,8 @@
const VkImageUsageFlags usageFlags =
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
- VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
+ VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
+ VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
VkBufferCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
@@ -154,6 +155,27 @@
ASSERT(mBuffer.valid());
mBuffer.getDeviceMemory().unmap(contextVk->getDevice());
+ mDataWriteAccessFlags = VK_ACCESS_HOST_WRITE_BIT;
+
+ return angle::Result::Continue;
+}
+
+angle::Result BufferVk::onRead(ContextVk *contextVk,
+ vk::CommandGraphResource *reader,
+ VkAccessFlagBits readAccessType)
+{
+ // Now that the buffer helper is being used (and will be part of the command graph), make sure
+ // its data write barrier is executed.
+ if (mDataWriteAccessFlags != 0)
+ {
+ vk::CommandBuffer *commandBuffer;
+ ANGLE_TRY(mBuffer.recordCommands(contextVk, &commandBuffer));
+
+ mBuffer.onWrite(mDataWriteAccessFlags);
+ mDataWriteAccessFlags = 0;
+ }
+
+ mBuffer.onRead(reader, readAccessType);
return angle::Result::Continue;
}
@@ -221,7 +243,8 @@
// Enqueue a copy command on the GPU.
VkBufferCopy copyRegion = {0, offset, size};
- ANGLE_TRY(mBuffer.copyFromBuffer(contextVk, stagingBuffer.getBuffer(), copyRegion));
+ ANGLE_TRY(mBuffer.copyFromBuffer(contextVk, stagingBuffer.getBuffer(),
+ VK_ACCESS_HOST_WRITE_BIT, copyRegion));
// Immediately release staging buffer. We should probably be using a DynamicBuffer here.
renderer->releaseObject(renderer->getCurrentQueueSerial(), &stagingBuffer);
@@ -236,6 +259,7 @@
memcpy(mapPointer, data, size);
mBuffer.getDeviceMemory().unmap(device);
+ mDataWriteAccessFlags = VK_ACCESS_HOST_WRITE_BIT;
}
return angle::Result::Continue;
diff --git a/src/libANGLE/renderer/vulkan/BufferVk.h b/src/libANGLE/renderer/vulkan/BufferVk.h
index 0484698..6a258eb 100644
--- a/src/libANGLE/renderer/vulkan/BufferVk.h
+++ b/src/libANGLE/renderer/vulkan/BufferVk.h
@@ -73,6 +73,10 @@
angle::Result mapImpl(ContextVk *contextVk, void **mapPtr);
angle::Result unmapImpl(ContextVk *contextVk);
+ angle::Result onRead(ContextVk *contextVk,
+ vk::CommandGraphResource *reader,
+ VkAccessFlagBits readAccessType);
+
// Calls copyBuffer internally.
angle::Result copyToBuffer(ContextVk *contextVk,
vk::BufferHelper *destBuffer,
@@ -87,6 +91,7 @@
void release(RendererVk *renderer);
vk::BufferHelper mBuffer;
+ VkAccessFlags mDataWriteAccessFlags;
};
} // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp
index 52d8456..3e9503f 100644
--- a/src/libANGLE/renderer/vulkan/ContextVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp
@@ -100,6 +100,7 @@
mNewCommandBufferDirtyBits.set(DIRTY_BIT_TEXTURES);
mNewCommandBufferDirtyBits.set(DIRTY_BIT_VERTEX_BUFFERS);
mNewCommandBufferDirtyBits.set(DIRTY_BIT_INDEX_BUFFER);
+ mNewCommandBufferDirtyBits.set(DIRTY_BIT_UNIFORM_BUFFERS);
mNewCommandBufferDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
mDirtyBitHandlers[DIRTY_BIT_DEFAULT_ATTRIBS] = &ContextVk::handleDirtyDefaultAttribs;
@@ -108,6 +109,7 @@
mDirtyBitHandlers[DIRTY_BIT_VERTEX_BUFFERS] = &ContextVk::handleDirtyVertexBuffers;
mDirtyBitHandlers[DIRTY_BIT_INDEX_BUFFER] = &ContextVk::handleDirtyIndexBuffer;
mDirtyBitHandlers[DIRTY_BIT_DRIVER_UNIFORMS] = &ContextVk::handleDirtyDriverUniforms;
+ mDirtyBitHandlers[DIRTY_BIT_UNIFORM_BUFFERS] = &ContextVk::handleDirtyUniformBuffers;
mDirtyBitHandlers[DIRTY_BIT_DESCRIPTOR_SETS] = &ContextVk::handleDirtyDescriptorSets;
mDirtyBits = mNewCommandBufferDirtyBits;
@@ -158,10 +160,14 @@
// Note that this may reserve more sets than strictly necessary for a particular layout.
VkDescriptorPoolSize uniformSetSize = {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
GetUniformBufferDescriptorCount()};
+ VkDescriptorPoolSize uniformBlockSetSize = {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ mRenderer->getMaxUniformBlocks()};
VkDescriptorPoolSize textureSetSize = {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
mRenderer->getMaxActiveTextures()};
VkDescriptorPoolSize driverSetSize = {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1};
ANGLE_TRY(mDynamicDescriptorPools[kUniformsDescriptorSetIndex].init(this, &uniformSetSize, 1));
+ ANGLE_TRY(mDynamicDescriptorPools[kUniformBlockDescriptorSetIndex].init(
+ this, &uniformBlockSetSize, 1));
ANGLE_TRY(mDynamicDescriptorPools[kTextureDescriptorSetIndex].init(this, &textureSetSize, 1));
ANGLE_TRY(
mDynamicDescriptorPools[kDriverUniformsDescriptorSetIndex].init(this, &driverSetSize, 1));
@@ -462,6 +468,17 @@
return angle::Result::Continue;
}
+angle::Result ContextVk::handleDirtyUniformBuffers(const gl::Context *context,
+ vk::CommandBuffer *commandBuffer)
+{
+ if (mProgram->hasUniformBuffers())
+ {
+ ANGLE_TRY(
+ mProgram->updateUniformBuffersDescriptorSet(this, mDrawFramebuffer->getFramebuffer()));
+ }
+ return angle::Result::Continue;
+}
+
angle::Result ContextVk::handleDirtyDescriptorSets(const gl::Context *context,
vk::CommandBuffer *commandBuffer)
{
@@ -914,6 +931,7 @@
case gl::State::DIRTY_BIT_PROGRAM_EXECUTABLE:
{
invalidateCurrentTextures();
+ invalidateCurrentUniformBuffers();
// No additional work is needed here. We will update the pipeline desc later.
invalidateDefaultAttributes(context->getStateCache().getActiveDefaultAttribsMask());
bool useVertexBuffer = (mProgram->getState().getMaxActiveAttribLocation());
@@ -934,6 +952,7 @@
case gl::State::DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING:
break;
case gl::State::DIRTY_BIT_UNIFORM_BUFFER_BINDINGS:
+ invalidateCurrentUniformBuffers();
break;
case gl::State::DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING:
break;
@@ -1137,6 +1156,16 @@
}
}
+void ContextVk::invalidateCurrentUniformBuffers()
+{
+ ASSERT(mProgram);
+ if (mProgram->hasUniformBuffers())
+ {
+ mDirtyBits.set(DIRTY_BIT_UNIFORM_BUFFERS);
+ mDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
+ }
+}
+
void ContextVk::invalidateDriverUniforms()
{
mDirtyBits.set(DIRTY_BIT_DRIVER_UNIFORMS);
diff --git a/src/libANGLE/renderer/vulkan/ContextVk.h b/src/libANGLE/renderer/vulkan/ContextVk.h
index 84c5d5e..a336eda 100644
--- a/src/libANGLE/renderer/vulkan/ContextVk.h
+++ b/src/libANGLE/renderer/vulkan/ContextVk.h
@@ -226,6 +226,7 @@
DIRTY_BIT_VERTEX_BUFFERS,
DIRTY_BIT_INDEX_BUFFER,
DIRTY_BIT_DRIVER_UNIFORMS,
+ DIRTY_BIT_UNIFORM_BUFFERS,
DIRTY_BIT_DESCRIPTOR_SETS,
DIRTY_BIT_MAX,
};
@@ -277,6 +278,7 @@
ANGLE_INLINE void invalidateCurrentPipeline() { mDirtyBits.set(DIRTY_BIT_PIPELINE); }
void invalidateCurrentTextures();
+ void invalidateCurrentUniformBuffers();
void invalidateDriverUniforms();
angle::Result handleDirtyDefaultAttribs(const gl::Context *context,
@@ -289,6 +291,8 @@
vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyDriverUniforms(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
+ angle::Result handleDirtyUniformBuffers(const gl::Context *context,
+ vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyDescriptorSets(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
diff --git a/src/libANGLE/renderer/vulkan/GlslangWrapper.cpp b/src/libANGLE/renderer/vulkan/GlslangWrapper.cpp
index 8d0e4eb..240671a 100644
--- a/src/libANGLE/renderer/vulkan/GlslangWrapper.cpp
+++ b/src/libANGLE/renderer/vulkan/GlslangWrapper.cpp
@@ -27,6 +27,7 @@
#include "common/utilities.h"
#include "libANGLE/Caps.h"
#include "libANGLE/ProgramLinkedResources.h"
+#include "libANGLE/renderer/vulkan/vk_cache_utils.h"
namespace rx
{
@@ -227,7 +228,7 @@
{
if (block.type == TokenType::Layout && block.text == name)
{
- const char *separator = specifier.empty() || block.args.empty() ? "" : ",";
+ const char *separator = specifier.empty() || block.args.empty() ? "" : ", ";
block.type = TokenType::Text;
block.text = "layout(" + block.args + separator + specifier + ")";
@@ -422,19 +423,52 @@
fragmentSource.eraseLayoutAndQualifierSpecifiers(varyingName);
}
+ // Assign uniform locations
+
// Bind the default uniforms for vertex and fragment shaders.
// See corresponding code in OutputVulkanGLSL.cpp.
+ const std::string uniformsSearchString("@@ DEFAULT-UNIFORMS-SET-BINDING @@");
+
+ const std::string driverUniformsDescriptorSet =
+ "set = " + Str(kDriverUniformsDescriptorSetIndex);
+ const std::string uniformsDescriptorSet = "set = " + Str(kUniformsDescriptorSetIndex);
+ const std::string uniformBlocksDescriptorSet = "set = " + Str(kUniformBlockDescriptorSetIndex);
+ const std::string texturesDescriptorSet = "set = " + Str(kTextureDescriptorSetIndex);
+
+ std::string vertexDefaultUniformsBinding =
+ uniformsDescriptorSet + ", binding = " + Str(kVertexUniformsBindingIndex);
+ std::string fragmentDefaultUniformsBinding =
+ uniformsDescriptorSet + ", binding = " + Str(kFragmentUniformsBindingIndex);
+
constexpr char kDefaultUniformsBlockName[] = "defaultUniforms";
- vertexSource.insertLayoutSpecifier(kDefaultUniformsBlockName, "set = 0, binding = 0");
- fragmentSource.insertLayoutSpecifier(kDefaultUniformsBlockName, "set = 0, binding = 1");
+ vertexSource.insertLayoutSpecifier(kDefaultUniformsBlockName, vertexDefaultUniformsBinding);
+ fragmentSource.insertLayoutSpecifier(kDefaultUniformsBlockName, fragmentDefaultUniformsBinding);
+
+ // Assign uniform blocks to a descriptor set and binding.
+ const auto &uniformBlocks = programState.getUniformBlocks();
+ uint32_t uniformBlockBinding = 0;
+ for (const gl::InterfaceBlock &uniformBlock : uniformBlocks)
+ {
+ const std::string setBindingString =
+ uniformBlocksDescriptorSet + ", binding = " + Str(uniformBlockBinding);
+
+ vertexSource.insertLayoutSpecifier(uniformBlock.name, setBindingString);
+ fragmentSource.insertLayoutSpecifier(uniformBlock.name, setBindingString);
+
+ vertexSource.insertQualifierSpecifier(uniformBlock.name, kUniformQualifier);
+ fragmentSource.insertQualifierSpecifier(uniformBlock.name, kUniformQualifier);
+
+ ++uniformBlockBinding;
+ }
// Assign textures to a descriptor set and binding.
- int textureCount = 0;
+ uint32_t textureBinding = 0;
const auto &uniforms = programState.getUniforms();
for (unsigned int uniformIndex : programState.getSamplerUniformRange())
{
const gl::LinkedUniform &samplerUniform = uniforms[uniformIndex];
- std::string setBindingString = "set = 1, binding = " + Str(textureCount);
+ const std::string setBindingString =
+ texturesDescriptorSet + ", binding = " + Str(textureBinding);
// Samplers in structs are extracted and renamed.
const std::string samplerName = GetMappedSamplerName(samplerUniform.name);
@@ -453,11 +487,13 @@
}
fragmentSource.insertQualifierSpecifier(samplerName, kUniformQualifier);
- textureCount++;
+ textureBinding++;
}
- // Start the unused sampler bindings at something ridiculously high.
- constexpr int kBaseUnusedSamplerBinding = 100;
+ // Place the unused uniforms in the driver uniforms descriptor set, which has a fixed number of
+ // bindings. This avoids any possible index collision between uniform bindings set in the
+ // shader and the ones assigned here to the unused ones.
+ constexpr int kBaseUnusedSamplerBinding = kReservedDriverUniformBindingCount;
int unusedSamplerBinding = kBaseUnusedSamplerBinding;
for (const gl::UnusedUniform &unusedUniform : resources.unusedUniforms)
@@ -469,7 +505,8 @@
std::stringstream layoutStringStream;
- layoutStringStream << "set = 0, binding = " << unusedSamplerBinding++;
+ layoutStringStream << driverUniformsDescriptorSet + ", binding = "
+ << unusedSamplerBinding++;
std::string layoutString = layoutStringStream.str();
@@ -487,10 +524,10 @@
}
// Substitute layout and qualifier strings for the driver uniforms block.
- constexpr char kDriverBlockLayoutString[] = "set = 2, binding = 0";
- constexpr char kDriverBlockName[] = "ANGLEUniforms";
- vertexSource.insertLayoutSpecifier(kDriverBlockName, kDriverBlockLayoutString);
- fragmentSource.insertLayoutSpecifier(kDriverBlockName, kDriverBlockLayoutString);
+ const std::string driverBlockLayoutString = driverUniformsDescriptorSet + ", binding = 0";
+ constexpr char kDriverBlockName[] = "ANGLEUniformBlock";
+ vertexSource.insertLayoutSpecifier(kDriverBlockName, driverBlockLayoutString);
+ fragmentSource.insertLayoutSpecifier(kDriverBlockName, driverBlockLayoutString);
vertexSource.insertQualifierSpecifier(kDriverBlockName, kUniformQualifier);
fragmentSource.insertQualifierSpecifier(kDriverBlockName, kUniformQualifier);
diff --git a/src/libANGLE/renderer/vulkan/ProgramVk.cpp b/src/libANGLE/renderer/vulkan/ProgramVk.cpp
index f9b6e9b..9440136 100644
--- a/src/libANGLE/renderer/vulkan/ProgramVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ProgramVk.cpp
@@ -11,7 +11,9 @@
#include "common/debug.h"
#include "libANGLE/Context.h"
+#include "libANGLE/ProgramLinkedResources.h"
#include "libANGLE/renderer/renderer_utils.h"
+#include "libANGLE/renderer/vulkan/BufferVk.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/GlslangWrapper.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
@@ -131,6 +133,38 @@
ANGLE_TRY(dynamicBuffer->flush(contextVk));
return angle::Result::Continue;
}
+
+uint32_t GetUniformBlockArraySize(const std::vector<gl::InterfaceBlock> &uniformBlocks,
+ uint32_t bufferIndex)
+{
+ const gl::InterfaceBlock &uniformBlock = uniformBlocks[bufferIndex];
+
+ if (!uniformBlock.isArray)
+ {
+ return 1;
+ }
+
+ ASSERT(uniformBlock.arrayElement == 0);
+
+ // Search consecutively until all array indices of this block are visited.
+ uint32_t arraySize;
+ for (arraySize = 1; bufferIndex + arraySize < uniformBlocks.size(); ++arraySize)
+ {
+ const gl::InterfaceBlock &nextBlock = uniformBlocks[bufferIndex + arraySize];
+
+ if (nextBlock.arrayElement != arraySize)
+ {
+ break;
+ }
+
+ // It's unexpected for an array to start at a non-zero array size, so we can always rely on
+ // the sequential `arrayElement`s to belong to the same block.
+ ASSERT(nextBlock.name == uniformBlock.name);
+ ASSERT(nextBlock.isArray);
+ }
+
+ return arraySize;
+}
} // anonymous namespace
// ProgramVk::ShaderInfo implementation.
@@ -183,7 +217,6 @@
ProgramVk::ProgramVk(const gl::ProgramState &state) : ProgramImpl(state), mUniformBlocksOffsets{}
{
- mUsedDescriptorSetRange.invalidate();
}
ProgramVk::~ProgramVk() = default;
@@ -213,7 +246,7 @@
mEmptyUniformBlockStorage.release(renderer);
mDescriptorSets.clear();
- mUsedDescriptorSetRange.invalidate();
+ mEmptyDescriptorSets.fill(VK_NULL_HANDLE);
for (vk::RefCountedDescriptorPoolBinding &binding : mDescriptorPoolBindings)
{
@@ -262,16 +295,14 @@
reset(renderer);
+ // Link resources before calling GetShaderSource to make sure they are ready for the set/binding
+ // assignment done in that function.
+ linkResources(resources);
+
GlslangWrapper::GetShaderSource(mState, resources, &mVertexSource, &mFragmentSource);
ANGLE_TRY(initDefaultUniformBlocks(glContext));
- if (!mState.getSamplerUniformRange().empty())
- {
- // Ensure the descriptor set range includes the textures at position 1.
- mUsedDescriptorSetRange.extend(kTextureDescriptorSetIndex);
- }
-
// Store a reference to the pipeline and descriptor set layouts. This will create them if they
// don't already exist in the cache.
vk::DescriptorSetLayoutDesc uniformsSetDesc;
@@ -283,6 +314,21 @@
ANGLE_TRY(renderer->getDescriptorSetLayout(
contextVk, uniformsSetDesc, &mDescriptorSetLayouts[kUniformsDescriptorSetIndex]));
+ vk::DescriptorSetLayoutDesc uniformBlocksSetDesc;
+
+ const std::vector<gl::InterfaceBlock> &uniformBlocks = mState.getUniformBlocks();
+ for (uint32_t bufferIndex = 0; bufferIndex < uniformBlocks.size();)
+ {
+ const uint32_t arraySize = GetUniformBlockArraySize(uniformBlocks, bufferIndex);
+
+ uniformBlocksSetDesc.update(bufferIndex, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, arraySize);
+
+ bufferIndex += arraySize;
+ }
+
+ ANGLE_TRY(renderer->getDescriptorSetLayout(
+ contextVk, uniformBlocksSetDesc, &mDescriptorSetLayouts[kUniformBlockDescriptorSetIndex]));
+
vk::DescriptorSetLayoutDesc texturesSetDesc;
for (uint32_t textureIndex = 0; textureIndex < mState.getSamplerBindings().size();
@@ -306,6 +352,8 @@
vk::PipelineLayoutDesc pipelineLayoutDesc;
pipelineLayoutDesc.updateDescriptorSetLayout(kUniformsDescriptorSetIndex, uniformsSetDesc);
+ pipelineLayoutDesc.updateDescriptorSetLayout(kUniformBlockDescriptorSetIndex,
+ uniformBlocksSetDesc);
pipelineLayoutDesc.updateDescriptorSetLayout(kTextureDescriptorSetIndex, texturesSetDesc);
pipelineLayoutDesc.updateDescriptorSetLayout(kDriverUniformsDescriptorSetIndex,
driverUniformsSetDesc);
@@ -313,26 +361,16 @@
ANGLE_TRY(renderer->getPipelineLayout(contextVk, pipelineLayoutDesc, mDescriptorSetLayouts,
&mPipelineLayout));
- if (!mState.getUniforms().empty())
- {
- const gl::RangeUI &samplerRange = mState.getSamplerUniformRange();
-
- if (mState.getUniforms().size() > samplerRange.length())
- {
- // Ensure the descriptor set range includes the uniform buffers at position 0.
- mUsedDescriptorSetRange.extend(kUniformsDescriptorSetIndex);
- }
-
- if (!samplerRange.empty())
- {
- // Ensure the descriptor set range includes the textures at position 1.
- mUsedDescriptorSetRange.extend(kTextureDescriptorSetIndex);
- }
- }
-
return angle::Result::Continue;
}
+void ProgramVk::linkResources(const gl::ProgramLinkedResources &resources)
+{
+ gl::ProgramLinkedResourcesLinker linker(nullptr);
+
+ linker.linkResources(mState, resources);
+}
+
angle::Result ProgramVk::initDefaultUniformBlocks(const gl::Context *glContext)
{
ContextVk *contextVk = vk::GetImpl(glContext);
@@ -360,7 +398,7 @@
if (location.used() && !location.ignored)
{
const auto &uniform = uniforms[location.index];
- if (!uniform.isSampler())
+ if (uniform.isInDefaultBlock() && !uniform.isSampler())
{
std::string uniformName = uniform.name;
if (uniform.isArray())
@@ -425,10 +463,8 @@
uniformBufferInfo.queueFamilyIndexCount = 0;
uniformBufferInfo.pQueueFamilyIndices = nullptr;
- // Assume host visible/coherent memory available.
- VkMemoryPropertyFlags flags =
- (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
- ANGLE_TRY(mEmptyUniformBlockStorage.init(contextVk, uniformBufferInfo, flags));
+ constexpr VkMemoryPropertyFlags kMemoryType = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+ ANGLE_TRY(mEmptyUniformBlockStorage.init(contextVk, uniformBufferInfo, kMemoryType));
}
}
@@ -731,6 +767,8 @@
ANGLE_TRY(dynamicDescriptorPool->allocateSets(contextVk, descriptorSetLayout.ptr(), 1,
&mDescriptorPoolBindings[descriptorSetIndex],
&mDescriptorSets[descriptorSetIndex]));
+ mEmptyDescriptorSets[descriptorSetIndex] = VK_NULL_HANDLE;
+
return angle::Result::Continue;
}
@@ -790,6 +828,7 @@
gl::ShaderMap<VkDescriptorBufferInfo> descriptorBufferInfo;
gl::ShaderMap<VkWriteDescriptorSet> writeDescriptorInfo;
+ // Write default uniforms for each shader type.
for (gl::ShaderType shaderType : gl::AllGLES2ShaderTypes())
{
DefaultUniformBlock &uniformBlock = mDefaultUniformBlocks[shaderType];
@@ -798,7 +837,8 @@
if (!uniformBlock.uniformData.empty())
{
- bufferInfo.buffer = uniformBlock.storage.getCurrentBuffer()->getBuffer().getHandle();
+ const vk::BufferHelper *bufferHelper = uniformBlock.storage.getCurrentBuffer();
+ bufferInfo.buffer = bufferHelper->getBuffer().getHandle();
}
else
{
@@ -810,7 +850,7 @@
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
- writeInfo.dstSet = mDescriptorSets[0];
+ writeInfo.dstSet = mDescriptorSets[kUniformsDescriptorSetIndex];
writeInfo.dstBinding = static_cast<uint32_t>(shaderType);
writeInfo.dstArrayElement = 0;
writeInfo.descriptorCount = 1;
@@ -822,7 +862,94 @@
VkDevice device = contextVk->getDevice();
- vkUpdateDescriptorSets(device, 2, writeDescriptorInfo.data(), 0, nullptr);
+ constexpr uint32_t kShaderTypeMin = static_cast<uint32_t>(gl::kGLES2ShaderTypeMin);
+ constexpr uint32_t kShaderTypeMax = static_cast<uint32_t>(gl::kGLES2ShaderTypeMax);
+ constexpr uint32_t kGLES2ShaderCount = kShaderTypeMax - kShaderTypeMin + 1;
+ vkUpdateDescriptorSets(device, kGLES2ShaderCount, writeDescriptorInfo.data(), 0, nullptr);
+
+ return angle::Result::Continue;
+}
+
+angle::Result ProgramVk::updateUniformBuffersDescriptorSet(ContextVk *contextVk,
+ vk::FramebufferHelper *framebuffer)
+{
+ ASSERT(hasUniformBuffers());
+ ANGLE_TRY(allocateDescriptorSet(contextVk, kUniformBlockDescriptorSetIndex));
+
+ VkDescriptorSet descriptorSet = mDescriptorSets[kUniformBlockDescriptorSetIndex];
+
+ gl::UniformBuffersArray<VkDescriptorBufferInfo> descriptorBufferInfo;
+ gl::UniformBuffersArray<VkWriteDescriptorSet> writeDescriptorInfo;
+ uint32_t writeCount = 0;
+ uint32_t currentBinding = 0;
+
+ // Write uniform buffers.
+ const gl::State &glState = contextVk->getState();
+ const std::vector<gl::InterfaceBlock> &uniformBlocks = mState.getUniformBlocks();
+ for (uint32_t bufferIndex = 0; bufferIndex < uniformBlocks.size(); ++bufferIndex)
+ {
+ if (glState.getIndexedUniformBuffer(uniformBlocks[bufferIndex].binding).get() == nullptr)
+ {
+ continue;
+ }
+
+ VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[writeCount];
+ VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[writeCount];
+
+ const gl::InterfaceBlock &uniformBlock = uniformBlocks[bufferIndex];
+ const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
+ glState.getIndexedUniformBuffer(uniformBlock.binding);
+ gl::Buffer *buffer = bufferBinding.get();
+ ASSERT(buffer != nullptr);
+
+ // Make sure there's no possible under/overflow with binding size.
+ static_assert(sizeof(VkDeviceSize) >= sizeof(bufferBinding.getSize()),
+ "VkDeviceSize too small");
+ ASSERT(bufferBinding.getSize() >= 0);
+
+ BufferVk *bufferVk = vk::GetImpl(buffer);
+ GLintptr offset = bufferBinding.getOffset();
+ VkDeviceSize size = bufferBinding.getSize();
+ VkDeviceSize blockSize = uniformBlock.dataSize;
+ vk::BufferHelper &bufferHelper = bufferVk->getBuffer();
+
+ ANGLE_TRY(bufferVk->onRead(contextVk, framebuffer, VK_ACCESS_UNIFORM_READ_BIT));
+
+ // If size is 0, we can't always use VK_WHOLE_SIZE (or bufferHelper.getSize()), as the
+ // backing buffer may be larger than maxUniformBufferRange. In that case, we use the
+ // minimum of the backing buffer size (what's left after offset) and the uniform buffer
+ // size as defined by the shader.
+ size = std::min(size > 0 ? size : (bufferHelper.getSize() - offset), blockSize);
+
+ bufferInfo.buffer = bufferHelper.getBuffer().getHandle();
+ bufferInfo.offset = offset;
+ bufferInfo.range = size;
+
+ if (!uniformBlock.isArray || uniformBlock.arrayElement == 0)
+ {
+ // Array indices of the same buffer binding are placed sequentially in `uniformBlocks`.
+ // Thus, the uniform block binding is updated only when array index 0 is encountered.
+ currentBinding = bufferIndex;
+ }
+
+ writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ writeInfo.pNext = nullptr;
+ writeInfo.dstSet = descriptorSet;
+ writeInfo.dstBinding = currentBinding;
+ writeInfo.dstArrayElement = uniformBlock.isArray ? uniformBlock.arrayElement : 0;
+ writeInfo.descriptorCount = 1;
+ writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ writeInfo.pImageInfo = nullptr;
+ writeInfo.pBufferInfo = &bufferInfo;
+ writeInfo.pTexelBufferView = nullptr;
+ ASSERT(writeInfo.pBufferInfo[0].buffer != VK_NULL_HANDLE);
+
+ ++writeCount;
+ }
+
+ VkDevice device = contextVk->getDevice();
+
+ vkUpdateDescriptorSets(device, writeCount, writeDescriptorInfo.data(), 0, nullptr);
return angle::Result::Continue;
}
@@ -833,7 +960,6 @@
ASSERT(hasTextures());
ANGLE_TRY(allocateDescriptorSet(contextVk, kTextureDescriptorSetIndex));
- ASSERT(mUsedDescriptorSetRange.contains(1));
VkDescriptorSet descriptorSet = mDescriptorSets[kTextureDescriptorSetIndex];
gl::ActiveTextureArray<VkDescriptorImageInfo> descriptorImageInfo;
@@ -915,27 +1041,63 @@
{
// Can probably use better dirty bits here.
- if (mUsedDescriptorSetRange.empty())
+ if (mDescriptorSets.empty())
return angle::Result::Continue;
- ASSERT(!mDescriptorSets.empty());
-
- unsigned int low = mUsedDescriptorSetRange.low();
-
- // No uniforms descriptor set means no need to specify dynamic buffer offsets.
- if (mUsedDescriptorSetRange.contains(kUniformsDescriptorSetIndex))
+ // Find the maximum non-null descriptor set. This is used in conjunction with a driver
+ // workaround to bind empty descriptor sets only for gaps in between 0 and max and avoid
+ // binding unnecessary empty descriptor sets for the sets beyond max.
+ size_t descriptorSetRange = 0;
+ for (size_t descriptorSetIndex = 0; descriptorSetIndex < mDescriptorSets.size();
+ ++descriptorSetIndex)
{
+ if (mDescriptorSets[descriptorSetIndex] != VK_NULL_HANDLE)
+ {
+ descriptorSetRange = descriptorSetIndex + 1;
+ }
+ }
+
+ for (size_t descriptorSetIndex = 0; descriptorSetIndex < descriptorSetRange;
+ ++descriptorSetIndex)
+ {
+ VkDescriptorSet descSet = mDescriptorSets[descriptorSetIndex];
+ if (descSet == VK_NULL_HANDLE)
+ {
+ if (!contextVk->getRenderer()->getFeatures().bindEmptyForUnusedDescriptorSets)
+ {
+ continue;
+ }
+
+ // Workaround a driver bug where missing (though unused) descriptor sets indices cause
+ // later sets to misbehave.
+ if (mEmptyDescriptorSets[descriptorSetIndex] == VK_NULL_HANDLE)
+ {
+ vk::DynamicDescriptorPool *dynamicDescriptorPool =
+ contextVk->getDynamicDescriptorPool(descriptorSetIndex);
+ const vk::DescriptorSetLayout &descriptorSetLayout =
+ mDescriptorSetLayouts[descriptorSetIndex].get();
+
+ ANGLE_TRY(dynamicDescriptorPool->allocateSets(
+ contextVk, descriptorSetLayout.ptr(), 1,
+ &mDescriptorPoolBindings[descriptorSetIndex],
+ &mEmptyDescriptorSets[descriptorSetIndex]));
+ }
+ descSet = mEmptyDescriptorSets[descriptorSetIndex];
+ }
+
constexpr uint32_t kShaderTypeMin = static_cast<uint32_t>(gl::kGLES2ShaderTypeMin);
constexpr uint32_t kShaderTypeMax = static_cast<uint32_t>(gl::kGLES2ShaderTypeMax);
- commandBuffer->bindGraphicsDescriptorSets(
- mPipelineLayout.get(), low, mUsedDescriptorSetRange.length(), &mDescriptorSets[low],
- kShaderTypeMax - kShaderTypeMin + 1, mUniformBlocksOffsets.data() + kShaderTypeMin);
- }
- else
- {
- commandBuffer->bindGraphicsDescriptorSets(mPipelineLayout.get(), low,
- mUsedDescriptorSetRange.length(),
- &mDescriptorSets[low], 0, nullptr);
+ constexpr uint32_t kShaderTypeCount = kShaderTypeMax - kShaderTypeMin + 1;
+
+ // Default uniforms are encompassed in a block per shader stage, and they are assigned
+ // through dynamic uniform buffers (requiring dynamic offsets). No other descriptor
+ // requires a dynamic offset.
+ const uint32_t uniformBlockOffsetCount =
+ descriptorSetIndex == kUniformsDescriptorSetIndex ? kShaderTypeCount : 0;
+
+ commandBuffer->bindGraphicsDescriptorSets(mPipelineLayout.get(), descriptorSetIndex, 1,
+ &descSet, uniformBlockOffsetCount,
+ mUniformBlocksOffsets.data() + kShaderTypeMin);
}
return angle::Result::Continue;
diff --git a/src/libANGLE/renderer/vulkan/ProgramVk.h b/src/libANGLE/renderer/vulkan/ProgramVk.h
index e59feef..93dec39 100644
--- a/src/libANGLE/renderer/vulkan/ProgramVk.h
+++ b/src/libANGLE/renderer/vulkan/ProgramVk.h
@@ -107,6 +107,8 @@
angle::Result updateUniforms(ContextVk *contextVk);
angle::Result updateTexturesDescriptorSet(ContextVk *contextVk,
vk::FramebufferHelper *framebuffer);
+ angle::Result updateUniformBuffersDescriptorSet(ContextVk *contextVk,
+ vk::FramebufferHelper *framebuffer);
angle::Result updateDescriptorSets(ContextVk *contextVk, vk::CommandBuffer *commandBuffer);
@@ -116,6 +118,7 @@
const vk::PipelineLayout &getPipelineLayout() const { return mPipelineLayout.get(); }
bool hasTextures() const { return !mState.getSamplerBindings().empty(); }
+ bool hasUniformBuffers() const { return !mState.getUniformBlocks().empty(); }
bool dirtyUniforms() const { return mDefaultUniformBlocksDirty.any(); }
@@ -157,6 +160,7 @@
angle::Result linkImpl(const gl::Context *glContext,
const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog);
+ void linkResources(const gl::ProgramLinkedResources &resources);
ANGLE_INLINE angle::Result initShaders(ContextVk *contextVk,
gl::PrimitiveMode mode,
@@ -215,7 +219,7 @@
// Descriptor sets for uniform blocks and textures for this program.
std::vector<VkDescriptorSet> mDescriptorSets;
- gl::RangeUI mUsedDescriptorSetRange;
+ vk::DescriptorSetLayoutArray<VkDescriptorSet> mEmptyDescriptorSets;
// We keep a reference to the pipeline and descriptor set layouts. This ensures they don't get
// deleted while this program is in use.
diff --git a/src/libANGLE/renderer/vulkan/RendererVk.cpp b/src/libANGLE/renderer/vulkan/RendererVk.cpp
index 502e5bb..12a5427 100644
--- a/src/libANGLE/renderer/vulkan/RendererVk.cpp
+++ b/src/libANGLE/renderer/vulkan/RendererVk.cpp
@@ -1305,12 +1305,14 @@
mFeatures.disableFifoPresentMode = true;
}
- if (vk::CommandBuffer::ExecutesInline())
+ if (IsAndroid() && IsQualcomm(mPhysicalDeviceProperties.vendorID))
{
- if (IsAndroid() && IsQualcomm(mPhysicalDeviceProperties.vendorID))
+ if (vk::CommandBuffer::ExecutesInline())
{
mFeatures.restartRenderPassAfterLoadOpClear = true;
}
+
+ mFeatures.bindEmptyForUnusedDescriptorSets = true;
}
}
@@ -1376,10 +1378,16 @@
return mNativeLimitations;
}
-uint32_t RendererVk::getMaxActiveTextures()
+uint32_t RendererVk::getMaxUniformBlocks() const
+{
+ return std::min<uint32_t>(mPhysicalDeviceProperties.limits.maxDescriptorSetUniformBuffers,
+ gl::IMPLEMENTATION_MAX_UNIFORM_BUFFER_BINDINGS);
+}
+
+uint32_t RendererVk::getMaxActiveTextures() const
{
// TODO(lucferron): expose this limitation to GL in Context Caps
- return std::min<uint32_t>(mPhysicalDeviceProperties.limits.maxPerStageDescriptorSamplers,
+ return std::min<uint32_t>(mPhysicalDeviceProperties.limits.maxDescriptorSetSamplers,
gl::IMPLEMENTATION_MAX_ACTIVE_TEXTURES);
}
diff --git a/src/libANGLE/renderer/vulkan/RendererVk.h b/src/libANGLE/renderer/vulkan/RendererVk.h
index 2df53c7..75baa8b 100644
--- a/src/libANGLE/renderer/vulkan/RendererVk.h
+++ b/src/libANGLE/renderer/vulkan/RendererVk.h
@@ -90,7 +90,8 @@
const gl::TextureCapsMap &getNativeTextureCaps() const;
const gl::Extensions &getNativeExtensions() const;
const gl::Limitations &getNativeLimitations() const;
- uint32_t getMaxActiveTextures();
+ uint32_t getMaxUniformBlocks() const;
+ uint32_t getMaxActiveTextures() const;
Serial getCurrentQueueSerial() const { return mCurrentQueueSerial; }
Serial getLastSubmittedQueueSerial() const { return mLastSubmittedQueueSerial; }
diff --git a/src/libANGLE/renderer/vulkan/ShaderVk.cpp b/src/libANGLE/renderer/vulkan/ShaderVk.cpp
index 479571c..1f6ff62 100644
--- a/src/libANGLE/renderer/vulkan/ShaderVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ShaderVk.cpp
@@ -25,7 +25,8 @@
gl::ShCompilerInstance *compilerInstance,
ShCompileOptions options)
{
- ShCompileOptions compileOptions = SH_INITIALIZE_UNINITIALIZED_LOCALS;
+ ShCompileOptions compileOptions =
+ SH_INITIALIZE_UNINITIALIZED_LOCALS | SH_REDEFINE_INTERFACE_LAYOUT_QUALIFIERS_WITH_STD;
ContextVk *contextVk = vk::GetImpl(context);
diff --git a/src/libANGLE/renderer/vulkan/UtilsVk.cpp b/src/libANGLE/renderer/vulkan/UtilsVk.cpp
index 18e1754..22f1111 100644
--- a/src/libANGLE/renderer/vulkan/UtilsVk.cpp
+++ b/src/libANGLE/renderer/vulkan/UtilsVk.cpp
@@ -220,6 +220,7 @@
RendererVk *renderer = context->getRenderer();
vk::DescriptorSetLayoutDesc descriptorSetDesc;
+ bool isCompute = function >= Function::ComputeStartIndex;
uint32_t currentBinding = 0;
for (size_t i = 0; i < setSizesCount; ++i)
@@ -231,9 +232,8 @@
ANGLE_TRY(renderer->getDescriptorSetLayout(context, descriptorSetDesc,
&mDescriptorSetLayouts[function][kSetIndex]));
- gl::ShaderType pushConstantsShaderStage = function >= Function::ComputeStartIndex
- ? gl::ShaderType::Compute
- : gl::ShaderType::Fragment;
+ gl::ShaderType pushConstantsShaderStage =
+ isCompute ? gl::ShaderType::Compute : gl::ShaderType::Fragment;
// Corresponding pipeline layouts:
vk::PipelineLayoutDesc pipelineLayoutDesc;
diff --git a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
index 92bc6ec..d6de558 100644
--- a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
@@ -1221,8 +1221,8 @@
PackedDescriptorSetBinding &packedBinding = mPackedDescriptorSetLayout[bindingIndex];
- packedBinding.type = static_cast<uint16_t>(type);
- packedBinding.count = static_cast<uint16_t>(count);
+ SetBitField(packedBinding.type, type);
+ SetBitField(packedBinding.count, count);
}
void DescriptorSetLayoutDesc::unpackBindings(DescriptorSetLayoutBindingVector *bindings) const
@@ -1237,8 +1237,7 @@
binding.binding = bindingIndex;
binding.descriptorCount = packedBinding.count;
binding.descriptorType = static_cast<VkDescriptorType>(packedBinding.type);
- binding.stageFlags =
- VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_COMPUTE_BIT;
+ binding.stageFlags = VK_SHADER_STAGE_ALL;
binding.pImmutableSamplers = nullptr;
bindings->push_back(binding);
diff --git a/src/libANGLE/renderer/vulkan/vk_cache_utils.h b/src/libANGLE/renderer/vulkan/vk_cache_utils.h
index ad04674..ca00568 100644
--- a/src/libANGLE/renderer/vulkan/vk_cache_utils.h
+++ b/src/libANGLE/renderer/vulkan/vk_cache_utils.h
@@ -442,14 +442,16 @@
constexpr size_t kGraphicsPipelineDescSize = sizeof(GraphicsPipelineDesc);
static_assert(kGraphicsPipelineDescSize == kGraphicsPipelineDescSumOfSizes, "Size mismatch");
-constexpr uint32_t kMaxDescriptorSetLayoutBindings = gl::IMPLEMENTATION_MAX_ACTIVE_TEXTURES;
+constexpr uint32_t kMaxDescriptorSetLayoutBindings =
+ std::max(gl::IMPLEMENTATION_MAX_ACTIVE_TEXTURES,
+ gl::IMPLEMENTATION_MAX_UNIFORM_BUFFER_BINDINGS);
using DescriptorSetLayoutBindingVector =
angle::FixedVector<VkDescriptorSetLayoutBinding, kMaxDescriptorSetLayoutBindings>;
// A packed description of a descriptor set layout. Use similarly to RenderPassDesc and
-// GraphicsPipelineDesc. Currently we only need to differentiate layouts based on sampler usage. In
-// the future we could generalize this.
+// GraphicsPipelineDesc. Currently we only need to differentiate layouts based on sampler and ubo
+// usage. In the future we could generalize this.
class DescriptorSetLayoutDesc final
{
public:
@@ -470,7 +472,6 @@
{
uint16_t type; // Stores a packed VkDescriptorType descriptorType.
uint16_t count; // Stores a packed uint32_t descriptorCount.
- // We currently make all descriptors available in the VS and FS shaders.
};
static_assert(sizeof(PackedDescriptorSetBinding) == sizeof(uint32_t), "Unexpected size");
@@ -480,9 +481,9 @@
mPackedDescriptorSetLayout;
};
-// The following are for caching descriptor set layouts. Limited to max two descriptor set layouts
+// The following are for caching descriptor set layouts. Limited to max four descriptor set layouts
// and one push constant per shader stage. This can be extended in the future.
-constexpr size_t kMaxDescriptorSetLayouts = 3;
+constexpr size_t kMaxDescriptorSetLayouts = 4;
constexpr size_t kMaxPushConstantRanges = angle::EnumSize<gl::ShaderType>();
struct PackedPushConstantRange
@@ -808,11 +809,32 @@
};
// Some descriptor set and pipeline layout constants.
-constexpr uint32_t kVertexUniformsBindingIndex = 0;
-constexpr uint32_t kFragmentUniformsBindingIndex = 1;
-constexpr uint32_t kUniformsDescriptorSetIndex = 0;
-constexpr uint32_t kTextureDescriptorSetIndex = 1;
-constexpr uint32_t kDriverUniformsDescriptorSetIndex = 2;
+//
+// The set/binding assignment is done as following:
+//
+// - Set 0 contains the ANGLE driver uniforms at binding 0. Note that driver uniforms are updated
+// only under rare circumstances, such as viewport or depth range change. However, there is only
+// one binding in this set.
+// - Set 1 contains uniform blocks created to encompass default uniforms. Bindings 0 and 1
+// correspond to default uniforms in the vertex and fragment shaders respectively.
+// - Set 2 contains all textures.
+// - Set 3 contains all uniform blocks.
+
+// ANGLE driver uniforms set index (binding is always 0):
+constexpr uint32_t kDriverUniformsDescriptorSetIndex = 0;
+// Uniforms set index:
+constexpr uint32_t kUniformsDescriptorSetIndex = 1;
+// Textures set index:
+constexpr uint32_t kTextureDescriptorSetIndex = 2;
+// Uniform blocks set index:
+constexpr uint32_t kUniformBlockDescriptorSetIndex = 3;
+
+// Only 1 driver uniform binding is used.
+constexpr uint32_t kReservedDriverUniformBindingCount = 1;
+// Binding index for default uniforms in the vertex shader:
+constexpr uint32_t kVertexUniformsBindingIndex = 0;
+// Binding index for default uniforms in the fragment shader:
+constexpr uint32_t kFragmentUniformsBindingIndex = 1;
} // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp b/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
index 2f1e908..6b8537d 100644
--- a/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
@@ -178,11 +178,30 @@
mNativeCaps.maxFragmentUniformVectors = maxUniformVectors;
mNativeCaps.maxShaderUniformComponents[gl::ShaderType::Fragment] = maxUniformComponents;
- // TODO(jmadill): this is an ES 3.0 property and we can skip implementing it for now.
- // This is maxDescriptorSetUniformBuffers minus the number of uniform buffers we
- // reserve for internal variables. We reserve one per shader stage for default uniforms
- // and likely one per shader stage for ANGLE internal variables.
- // mNativeCaps.maxShaderUniformBlocks[gl::ShaderType::Vertex] = ...
+ // A number of uniform buffers are reserved for internal use. There's one dynamic uniform
+ // buffer used per stage for default uniforms, and a single uniform buffer object used for
+ // ANGLE internal variables. ANGLE implements UBOs as uniform buffers, so the maximum number
+ // of uniform blocks is maxDescriptorSetUniformBuffers - 1:
+ const uint32_t maxUniformBuffers =
+ mPhysicalDeviceProperties.limits.maxDescriptorSetUniformBuffers -
+ kReservedDriverUniformBindingCount;
+
+ mNativeCaps.maxShaderUniformBlocks[gl::ShaderType::Vertex] = maxUniformBuffers;
+ mNativeCaps.maxShaderUniformBlocks[gl::ShaderType::Fragment] = maxUniformBuffers;
+ mNativeCaps.maxCombinedUniformBlocks = maxUniformBuffers;
+
+ mNativeCaps.maxUniformBufferBindings = maxUniformBuffers;
+ mNativeCaps.maxUniformBlockSize = mPhysicalDeviceProperties.limits.maxUniformBufferRange;
+ mNativeCaps.uniformBufferOffsetAlignment =
+ static_cast<GLuint>(mPhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment);
+
+ // There is no additional limit to the combined number of components. We can have up to a
+ // maximum number of uniform buffers, each having the maximum number of components.
+ const uint32_t maxCombinedUniformComponents = maxUniformBuffers * maxUniformComponents;
+ for (gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes)
+ {
+ mNativeCaps.maxCombinedShaderUniformComponents[shaderType] = maxCombinedUniformComponents;
+ }
// we use the same bindings on each stage, so the limitation is the same combined or not.
mNativeCaps.maxCombinedTextureImageUnits =
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.cpp b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
index 34e076d..7530649 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
@@ -1159,7 +1159,7 @@
mDeviceMemory.dumpResources(garbageQueue);
}
-void BufferHelper::onWrite(VkAccessFlagBits writeAccessType)
+void BufferHelper::onWrite(VkAccessFlags writeAccessType)
{
if (mCurrentReadAccess != 0 || mCurrentWriteAccess != 0)
{
@@ -1172,19 +1172,20 @@
angle::Result BufferHelper::copyFromBuffer(Context *context,
const Buffer &buffer,
+ VkAccessFlags bufferAccessType,
const VkBufferCopy ©Region)
{
// 'recordCommands' will implicitly stop any reads from using the old buffer data.
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(recordCommands(context, &commandBuffer));
- if (mCurrentReadAccess != 0 || mCurrentWriteAccess != 0)
+ if (mCurrentReadAccess != 0 || mCurrentWriteAccess != 0 || bufferAccessType != 0)
{
// Insert a barrier to ensure reads/writes are complete.
// Use a global memory barrier to keep things simple.
VkMemoryBarrier memoryBarrier = {};
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
- memoryBarrier.srcAccessMask = mCurrentReadAccess | mCurrentWriteAccess;
+ memoryBarrier.srcAccessMask = mCurrentReadAccess | mCurrentWriteAccess | bufferAccessType;
memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
commandBuffer->pipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.h b/src/libANGLE/renderer/vulkan/vk_helpers.h
index d1b82e1..9672faa 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.h
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.h
@@ -406,24 +406,26 @@
bool valid() const { return mBuffer.valid(); }
const Buffer &getBuffer() const { return mBuffer; }
const DeviceMemory &getDeviceMemory() const { return mDeviceMemory; }
+ VkDeviceSize getSize() const { return mSize; }
// Helpers for setting the graph dependencies *and* setting the appropriate barrier.
- ANGLE_INLINE void onRead(CommandGraphResource *reader, VkAccessFlagBits readAccessType)
+ ANGLE_INLINE void onRead(CommandGraphResource *reader, VkAccessFlags readAccessType)
{
addReadDependency(reader);
- if (mCurrentWriteAccess != 0 && (mCurrentReadAccess & readAccessType) == 0)
+ if (mCurrentWriteAccess != 0 && (mCurrentReadAccess & readAccessType) != readAccessType)
{
reader->addGlobalMemoryBarrier(mCurrentWriteAccess, readAccessType);
mCurrentReadAccess |= readAccessType;
}
}
- void onWrite(VkAccessFlagBits writeAccessType);
+ void onWrite(VkAccessFlags writeAccessType);
// Also implicitly sets up the correct barriers.
angle::Result copyFromBuffer(Context *context,
const Buffer &buffer,
+ VkAccessFlags bufferAccessType,
const VkBufferCopy ©Region);
// Note: currently only one view is allowed. If needs be, multiple views can be created
diff --git a/src/tests/deqp_support/deqp_gles3_test_expectations.txt b/src/tests/deqp_support/deqp_gles3_test_expectations.txt
index 7233432..92f360c 100644
--- a/src/tests/deqp_support/deqp_gles3_test_expectations.txt
+++ b/src/tests/deqp_support/deqp_gles3_test_expectations.txt
@@ -521,19 +521,12 @@
// General Vulkan failures
// Limits:
-2950 VULKAN : dEQP-GLES3.functional.implementation_limits.max_elements* = SKIP
+2950 VULKAN : dEQP-GLES3.functional.implementation_limits.max_elements_indices = SKIP
+2950 VULKAN : dEQP-GLES3.functional.implementation_limits.max_elements_vertices = SKIP
2950 VULKAN : dEQP-GLES3.functional.implementation_limits.compressed_texture_formats = SKIP
-2950 VULKAN : dEQP-GLES3.functional.implementation_limits.num_compressed_texture_formats = SKIP
-2950 VULKAN : dEQP-GLES3.functional.implementation_limits.max_vertex_uniform_blocks = SKIP
-2950 VULKAN : dEQP-GLES3.functional.implementation_limits.max_fragment_uniform_blocks = SKIP
2950 VULKAN : dEQP-GLES3.functional.implementation_limits.max_fragment_input_components = SKIP
2950 VULKAN : dEQP-GLES3.functional.implementation_limits.max_program_texel_offset = SKIP
2950 VULKAN : dEQP-GLES3.functional.implementation_limits.min_program_texel_offset = SKIP
-2950 VULKAN : dEQP-GLES3.functional.implementation_limits.max_uniform_buffer_bindings = SKIP
-2950 VULKAN : dEQP-GLES3.functional.implementation_limits.max_uniform_block_size = SKIP
-2950 VULKAN : dEQP-GLES3.functional.implementation_limits.max_combined_vertex_uniform_components = SKIP
-2950 VULKAN : dEQP-GLES3.functional.implementation_limits.max_combined_fragment_uniform_components = SKIP
-2950 VULKAN : dEQP-GLES3.functional.implementation_limits.max_combined_uniform_blocks = SKIP
2950 VULKAN : dEQP-GLES3.functional.implementation_limits.max_transform_feedback_* = SKIP
// 3D texture (anglebug.com/3188), 2D array (anglebug.com/3189):
@@ -656,10 +649,6 @@
2950 VULKAN : dEQP-GLES3.functional.polygon_offset.float32_result_depth_clamp = FAIL
2950 VULKAN : dEQP-GLES3.functional.polygon_offset.float32_factor_1_slope = FAIL
-// UBO:
-2950 VULKAN : dEQP-GLES3.functional.ubo.* = SKIP
-3219 VULKAN : dEQP-GLES3.functional.shaders.uniform_block.* = SKIP
-
// PBO:
2950 VULKAN : dEQP-GLES3.functional.pbo.* = SKIP
@@ -704,18 +693,10 @@
// - ES3 State queries:
2950 VULKAN : dEQP-GLES3.functional.state_query.integers.max_elements_indices_getfloat = FAIL
2950 VULKAN : dEQP-GLES3.functional.state_query.integers.max_elements_vertices_getfloat = FAIL
-2950 VULKAN : dEQP-GLES3.functional.state_query.integers.max_vertex_uniform_blocks_get* = FAIL
-2950 VULKAN : dEQP-GLES3.functional.state_query.integers.max_fragment_uniform_blocks_get* = FAIL
-2950 VULKAN : dEQP-GLES3.functional.state_query.integers.max_combined_uniform_blocks_get* = FAIL
2950 VULKAN : dEQP-GLES3.functional.state_query.integers.max_fragment_input_components_get* = FAIL
2950 VULKAN : dEQP-GLES3.functional.state_query.integers.max_program_texel_offset_get* = FAIL
-2950 VULKAN : dEQP-GLES3.functional.state_query.integers.max_uniform_buffer_bindings_get* = FAIL
2950 VULKAN : dEQP-GLES3.functional.state_query.integers.max_samples_get* = FAIL
2950 VULKAN : dEQP-GLES3.functional.state_query.integers.min_program_texel_offset_get* = FAIL
-2950 VULKAN : dEQP-GLES3.functional.state_query.integers.compressed_texture_formats_get* = FAIL
-2950 VULKAN : dEQP-GLES3.functional.state_query.indexed.uniform_buffer* = FAIL
-2950 VULKAN : dEQP-GLES3.functional.state_query.integers64.max_uniform_block_size_get* = FAIL
-2950 VULKAN : dEQP-GLES3.functional.state_query.integers64.max_combined_vertex_uniform_components_get* = FAIL
2950 VULKAN : dEQP-GLES3.functional.state_query.integers64.max_combined_fragment_uniform_components_get* = FAIL
// Misc failures:
@@ -723,7 +704,6 @@
2950 VULKAN : dEQP-GLES3.functional.state_query.fbo.framebuffer_attachment_color_encoding = FAIL
2950 VULKAN : dEQP-GLES3.functional.state_query.rbo.renderbuffer_internal_format = FAIL
2950 VULKAN : dEQP-GLES3.functional.state_query.rbo.renderbuffer_component_size_color = FAIL
-2950 VULKAN : dEQP-GLES3.functional.state_query.shader.program_active_uniform_blocks = FAIL
2950 VULKAN : dEQP-GLES3.functional.state_query.shader.uniform_value_uint = FAIL
2950 VULKAN : dEQP-GLES3.functional.lifetime.* = SKIP
2950 VULKAN : dEQP-GLES3.functional.clipping.line.line_clip_viewport_center = FAIL
diff --git a/src/tests/gl_tests/GLSLTest.cpp b/src/tests/gl_tests/GLSLTest.cpp
index 0c1a59d..9f0cf9b 100644
--- a/src/tests/gl_tests/GLSLTest.cpp
+++ b/src/tests/gl_tests/GLSLTest.cpp
@@ -4741,10 +4741,6 @@
// field.
TEST_P(GLSLTest_ES3, ErrorMessageOfLinkInterfaceBlockFieldMismatch)
{
- // TODO(syoussefi): missing ES3 shader feature support.
- // http://anglebug.com/3199
- ANGLE_SKIP_TEST_IF(IsVulkan());
-
constexpr char kVS[] = R"(#version 300 es
uniform S {
vec2 val1;
@@ -4780,10 +4776,6 @@
// struct field.
TEST_P(GLSLTest_ES3, ErrorMessageOfLinkInterfaceBlockStructFieldMismatch)
{
- // TODO(syoussefi): missing ES3 shader feature support.
- // http://anglebug.com/3199
- ANGLE_SKIP_TEST_IF(IsVulkan());
-
constexpr char kVS[] = R"(#version 300 es
struct T
{
diff --git a/src/tests/gl_tests/UniformBufferTest.cpp b/src/tests/gl_tests/UniformBufferTest.cpp
index abede84..01a981f 100644
--- a/src/tests/gl_tests/UniformBufferTest.cpp
+++ b/src/tests/gl_tests/UniformBufferTest.cpp
@@ -1518,7 +1518,7 @@
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
-ANGLE_INSTANTIATE_TEST(UniformBufferTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
+ANGLE_INSTANTIATE_TEST(UniformBufferTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES(), ES3_VULKAN());
ANGLE_INSTANTIATE_TEST(UniformBufferTest31, ES31_D3D11(), ES31_OPENGL(), ES31_OPENGLES());
} // namespace
diff --git a/src/tests/gl_tests/UniformTest.cpp b/src/tests/gl_tests/UniformTest.cpp
index f960595..0d98653 100644
--- a/src/tests/gl_tests/UniformTest.cpp
+++ b/src/tests/gl_tests/UniformTest.cpp
@@ -694,6 +694,9 @@
// Test queries for transposed arrays of non-square matrix uniforms.
TEST_P(UniformTestES3, TransposedMatrixArrayUniformStateQuery)
{
+ // TODO(syoussefi): Bug in glGetUniformfv. http://anglebug.com/3387
+ ANGLE_SKIP_TEST_IF(IsVulkan());
+
constexpr char kFS[] =
"#version 300 es\n"
"precision mediump float;\n"
@@ -1279,19 +1282,20 @@
ANGLE_INSTANTIATE_TEST(SimpleUniformTest,
ES2_D3D9(),
ES2_D3D11(),
- ES2_OPENGL(),
ES3_D3D11(),
+ ES2_OPENGL(),
ES3_OPENGL(),
- ES3_OPENGLES(),
ES2_OPENGLES(),
- ES2_VULKAN());
+ ES3_OPENGLES(),
+ ES2_VULKAN(),
+ ES3_VULKAN());
ANGLE_INSTANTIATE_TEST(UniformTest,
ES2_D3D9(),
ES2_D3D11(),
ES2_OPENGL(),
ES2_OPENGLES(),
ES2_VULKAN());
-ANGLE_INSTANTIATE_TEST(UniformTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
+ANGLE_INSTANTIATE_TEST(UniformTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES(), ES3_VULKAN());
ANGLE_INSTANTIATE_TEST(UniformTestES31, ES31_D3D11(), ES31_OPENGL(), ES31_OPENGLES());
} // namespace
diff --git a/src/tests/gl_tests/VulkanUniformUpdatesTest.cpp b/src/tests/gl_tests/VulkanUniformUpdatesTest.cpp
index 30be1d2..4f35cbd 100644
--- a/src/tests/gl_tests/VulkanUniformUpdatesTest.cpp
+++ b/src/tests/gl_tests/VulkanUniformUpdatesTest.cpp
@@ -313,6 +313,6 @@
ASSERT_GL_NO_ERROR();
}
-ANGLE_INSTANTIATE_TEST(VulkanUniformUpdatesTest, ES2_VULKAN());
+ANGLE_INSTANTIATE_TEST(VulkanUniformUpdatesTest, ES2_VULKAN(), ES3_VULKAN());
} // anonymous namespace