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/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_