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