Vulkan: Add PipelineLayout and DescriptorSetLayout caches.
This will be necessary to support more than one PipelineLayout per
instance of ANGLE. Sampler array handling requires different layouts
for different sampler uses.
Bug: angleproject:2462
Change-Id: I1d8b4919eed1a589002ad1898b05186f420061c7
Reviewed-on: https://chromium-review.googlesource.com/1089806
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Luc Ferron <lucferron@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp
index 00d09f1..8ef55dd 100644
--- a/src/libANGLE/renderer/vulkan/ContextVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp
@@ -125,9 +125,11 @@
// Ensure that the RenderPass description is updated.
mPipelineDesc->updateRenderPassDesc(framebufferVk->getRenderPassDesc());
+ const vk::PipelineLayout &pipelineLayout = mRenderer->getGraphicsPipelineLayout();
+
// TODO(jmadill): Validate with ASSERT against physical device limits/caps?
ANGLE_TRY(mRenderer->getAppPipeline(programVk, *mPipelineDesc, activeAttribLocationsMask,
- &mCurrentPipeline));
+ pipelineLayout, &mCurrentPipeline));
return gl::NoError();
}
diff --git a/src/libANGLE/renderer/vulkan/ProgramVk.cpp b/src/libANGLE/renderer/vulkan/ProgramVk.cpp
index 6334ae4..36a809e 100644
--- a/src/libANGLE/renderer/vulkan/ProgramVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ProgramVk.cpp
@@ -705,7 +705,6 @@
// Write out to a new a descriptor set.
vk::DynamicDescriptorPool *dynamicDescriptorPool = contextVk->getDynamicDescriptorPool();
- const auto &descriptorSetLayouts = renderer->getGraphicsDescriptorSetLayouts();
uint32_t potentialNewCount = descriptorSetIndex + 1;
if (potentialNewCount > mDescriptorSets.size())
@@ -713,10 +712,10 @@
mDescriptorSets.resize(potentialNewCount, VK_NULL_HANDLE);
}
- const VkDescriptorSetLayout *descriptorSetLayout =
- descriptorSetLayouts[descriptorSetIndex].ptr();
+ const vk::DescriptorSetLayout &descriptorSetLayout =
+ renderer->getGraphicsDescriptorSetLayout(descriptorSetIndex);
- ANGLE_TRY(dynamicDescriptorPool->allocateDescriptorSets(contextVk, descriptorSetLayout, 1,
+ ANGLE_TRY(dynamicDescriptorPool->allocateDescriptorSets(contextVk, descriptorSetLayout.ptr(), 1,
&mDescriptorSets[descriptorSetIndex]));
return vk::NoError();
}
@@ -772,7 +771,7 @@
{
// We need to reinitialize the descriptor sets if we newly allocated buffers since we can't
// modify the descriptor sets once initialized.
- ANGLE_TRY(allocateDescriptorSet(contextVk, vk::UniformBufferIndex));
+ ANGLE_TRY(allocateDescriptorSet(contextVk, kUniformsDescriptorSetIndex));
ANGLE_TRY(updateDefaultUniformsDescriptorSet(contextVk));
}
@@ -857,10 +856,10 @@
}
ContextVk *contextVk = GetImplAs<ContextVk>(context);
- ANGLE_TRY(allocateDescriptorSet(contextVk, vk::TextureIndex));
+ ANGLE_TRY(allocateDescriptorSet(contextVk, kTextureDescriptorSetIndex));
ASSERT(mUsedDescriptorSetRange.contains(1));
- VkDescriptorSet descriptorSet = mDescriptorSets[1];
+ VkDescriptorSet descriptorSet = mDescriptorSets[kTextureDescriptorSetIndex];
// TODO(jmadill): Don't hard-code the texture limit.
ShaderTextureArray<VkDescriptorImageInfo> descriptorImageInfo;
diff --git a/src/libANGLE/renderer/vulkan/RendererVk.cpp b/src/libANGLE/renderer/vulkan/RendererVk.cpp
index 8a2ed93..fd6855c 100644
--- a/src/libANGLE/renderer/vulkan/RendererVk.cpp
+++ b/src/libANGLE/renderer/vulkan/RendererVk.cpp
@@ -210,10 +210,13 @@
for (auto &descriptorSetLayout : mGraphicsDescriptorSetLayouts)
{
- descriptorSetLayout.destroy(mDevice);
+ descriptorSetLayout.reset();
}
- mGraphicsPipelineLayout.destroy(mDevice);
+ mGraphicsPipelineLayout.reset();
+ mPipelineLayoutCache.destroy(mDevice);
+ mDescriptorSetLayoutCache.destroy(mDevice);
+
mInternalPushConstantPipelineLayout.destroy(mDevice);
mRenderPassCache.destroy(mDevice);
@@ -842,12 +845,12 @@
const vk::PipelineLayout &RendererVk::getGraphicsPipelineLayout() const
{
- return mGraphicsPipelineLayout;
+ return mGraphicsPipelineLayout.get();
}
-const std::vector<vk::DescriptorSetLayout> &RendererVk::getGraphicsDescriptorSetLayouts() const
+const vk::DescriptorSetLayout &RendererVk::getGraphicsDescriptorSetLayout(uint32_t setIndex) const
{
- return mGraphicsDescriptorSetLayouts;
+ return mGraphicsDescriptorSetLayouts[setIndex].get();
}
vk::Error RendererVk::initGraphicsPipelineLayout()
@@ -855,84 +858,32 @@
ASSERT(!mGraphicsPipelineLayout.valid());
// Create two descriptor set layouts: one for default uniform info, and one for textures.
- // Skip one or both if there are no uniforms.
- VkDescriptorSetLayoutBinding uniformBindings[2];
- uint32_t blockCount = 0;
+ vk::DescriptorSetLayoutDesc uniformsSetDesc;
+ uniformsSetDesc.update(kVertexUniformsBindingIndex, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
+ 1);
+ uniformsSetDesc.update(kFragmentUniformsBindingIndex, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
+ 1);
+ ANGLE_TRY(mDescriptorSetLayoutCache.getDescriptorSetLayout(mDevice, uniformsSetDesc,
+ &mGraphicsDescriptorSetLayouts[0]));
+ const uint32_t maxTextures = getMaxActiveTextures();
+
+ vk::DescriptorSetLayoutDesc texturesSetDesc;
+ for (uint32_t textureIndex = 0; textureIndex < maxTextures; ++textureIndex)
{
- VkDescriptorSetLayoutBinding &layoutBinding = uniformBindings[blockCount];
-
- layoutBinding.binding = blockCount;
- layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
- layoutBinding.descriptorCount = 1;
- layoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
- layoutBinding.pImmutableSamplers = nullptr;
-
- blockCount++;
+ // TODO(jmadll): Sampler arrays. http://anglebug.com/2462
+ texturesSetDesc.update(textureIndex, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1);
}
- {
- VkDescriptorSetLayoutBinding &layoutBinding = uniformBindings[blockCount];
+ ANGLE_TRY(mDescriptorSetLayoutCache.getDescriptorSetLayout(mDevice, texturesSetDesc,
+ &mGraphicsDescriptorSetLayouts[1]));
- layoutBinding.binding = blockCount;
- layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
- layoutBinding.descriptorCount = 1;
- layoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
- layoutBinding.pImmutableSamplers = nullptr;
+ vk::PipelineLayoutDesc pipelineLayoutDesc;
+ pipelineLayoutDesc.updateDescriptorSetLayout(kUniformsDescriptorSetIndex, uniformsSetDesc);
+ pipelineLayoutDesc.updateDescriptorSetLayout(kTextureDescriptorSetIndex, texturesSetDesc);
- blockCount++;
- }
-
- {
- VkDescriptorSetLayoutCreateInfo uniformInfo;
- uniformInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
- uniformInfo.pNext = nullptr;
- uniformInfo.flags = 0;
- uniformInfo.bindingCount = blockCount;
- uniformInfo.pBindings = uniformBindings;
-
- vk::DescriptorSetLayout uniformLayout;
- ANGLE_TRY(uniformLayout.init(mDevice, uniformInfo));
- mGraphicsDescriptorSetLayouts.push_back(std::move(uniformLayout));
- }
-
- std::vector<VkDescriptorSetLayoutBinding> textureBindings(getMaxActiveTextures());
-
- // TODO(jmadill): This approach might not work well for texture arrays.
- for (uint32_t textureIndex = 0; textureIndex < textureBindings.size(); ++textureIndex)
- {
- VkDescriptorSetLayoutBinding &layoutBinding = textureBindings[textureIndex];
-
- layoutBinding.binding = textureIndex;
- layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
- layoutBinding.descriptorCount = 1;
- layoutBinding.stageFlags = (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
- layoutBinding.pImmutableSamplers = nullptr;
- }
-
- {
- VkDescriptorSetLayoutCreateInfo textureInfo;
- textureInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
- textureInfo.pNext = nullptr;
- textureInfo.flags = 0;
- textureInfo.bindingCount = static_cast<uint32_t>(textureBindings.size());
- textureInfo.pBindings = textureBindings.data();
-
- vk::DescriptorSetLayout textureLayout;
- ANGLE_TRY(textureLayout.init(mDevice, textureInfo));
- mGraphicsDescriptorSetLayouts.push_back(std::move(textureLayout));
- }
-
- VkPipelineLayoutCreateInfo createInfo;
- createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
- createInfo.pNext = nullptr;
- createInfo.flags = 0;
- createInfo.setLayoutCount = static_cast<uint32_t>(mGraphicsDescriptorSetLayouts.size());
- createInfo.pSetLayouts = mGraphicsDescriptorSetLayouts[0].ptr();
- createInfo.pushConstantRangeCount = 0;
- createInfo.pPushConstantRanges = nullptr;
-
- ANGLE_TRY(mGraphicsPipelineLayout.init(mDevice, createInfo));
+ ANGLE_TRY(mPipelineLayoutCache.getPipelineLayout(
+ mDevice, pipelineLayoutDesc, mGraphicsDescriptorSetLayouts, &mGraphicsPipelineLayout));
return vk::NoError();
}
@@ -973,6 +924,7 @@
vk::Error RendererVk::getAppPipeline(const ProgramVk *programVk,
const vk::PipelineDesc &desc,
const gl::AttributesMask &activeAttribLocationsMask,
+ const vk::PipelineLayout &pipelineLayout,
vk::PipelineAndSerial **pipelineOut)
{
ASSERT(programVk->getVertexModuleSerial() ==
@@ -984,7 +936,7 @@
vk::RenderPass *compatibleRenderPass = nullptr;
ANGLE_TRY(getCompatibleRenderPass(desc.getRenderPassDesc(), &compatibleRenderPass));
- return mPipelineCache.getPipeline(mDevice, *compatibleRenderPass, mGraphicsPipelineLayout,
+ return mPipelineCache.getPipeline(mDevice, *compatibleRenderPass, pipelineLayout,
activeAttribLocationsMask, programVk->getLinkedVertexModule(),
programVk->getLinkedFragmentModule(), desc, pipelineOut);
}
diff --git a/src/libANGLE/renderer/vulkan/RendererVk.h b/src/libANGLE/renderer/vulkan/RendererVk.h
index a580b99..5e9d67d 100644
--- a/src/libANGLE/renderer/vulkan/RendererVk.h
+++ b/src/libANGLE/renderer/vulkan/RendererVk.h
@@ -108,6 +108,7 @@
vk::Error getAppPipeline(const ProgramVk *programVk,
const vk::PipelineDesc &desc,
const gl::AttributesMask &activeAttribLocationsMask,
+ const vk::PipelineLayout &pipelineLayout,
vk::PipelineAndSerial **pipelineOut);
// For getting a vk::Pipeline for an internal draw call. Use an explicit RenderPass.
@@ -122,10 +123,12 @@
// TODO(jmadill): Keep in ContextVk to enable threaded rendering.
vk::CommandGraph *getCommandGraph();
+ // TODO(jmadill): Use PipelineLayout cache. http://anglebug.com/2462
const vk::PipelineLayout &getGraphicsPipelineLayout() const;
- const std::vector<vk::DescriptorSetLayout> &getGraphicsDescriptorSetLayouts() const;
+ const vk::DescriptorSetLayout &getGraphicsDescriptorSetLayout(uint32_t setIndex) const;
// Used in internal shaders.
+ // TODO(jmadill): Use PipelineLayout cache. http://anglebug.com/2462
vk::Error getInternalPushConstantPipelineLayout(const vk::PipelineLayout **pipelineLayoutOut);
// Issues a new serial for linked shader modules. Used in the pipeline cache.
@@ -187,12 +190,18 @@
// See CommandGraph.h for a desription of the Command Graph.
vk::CommandGraph mCommandGraph;
- // ANGLE uses a single pipeline layout for all GL programs. It is owned here in the Renderer.
- // See the design doc for an overview of the pipeline layout structure.
- vk::PipelineLayout mGraphicsPipelineLayout;
- std::vector<vk::DescriptorSetLayout> mGraphicsDescriptorSetLayouts;
+ // ANGLE uses a PipelineLayout cache to store compatible pipeline layouts.
+ PipelineLayoutCache mPipelineLayoutCache;
+
+ // DescriptorSetLayouts are also managed in a cache.
+ DescriptorSetLayoutCache mDescriptorSetLayoutCache;
+
+ // TODO(jmadill): Only use the pipeline layout cache. http://anglebug.com/2462
+ vk::BindingPointer<vk::PipelineLayout> mGraphicsPipelineLayout;
+ vk::DescriptorSetLayoutPointerArray mGraphicsDescriptorSetLayouts;
// Used for internal shaders.
+ // TODO(jmadill): Use PipelineLayout cache. http://anglebug.com/2462
vk::PipelineLayout mInternalPushConstantPipelineLayout;
// Internal shader library.
diff --git a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
index fd51a55..24ebc1d 100644
--- a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
@@ -860,6 +860,108 @@
{
return (memcmp(&lhs, &rhs, sizeof(AttachmentOpsArray)) == 0);
}
+
+// DescriptorSetLayoutDesc implementation.
+DescriptorSetLayoutDesc::DescriptorSetLayoutDesc() : mPackedDescriptorSetLayout{}
+{
+}
+
+DescriptorSetLayoutDesc::~DescriptorSetLayoutDesc() = default;
+
+DescriptorSetLayoutDesc::DescriptorSetLayoutDesc(const DescriptorSetLayoutDesc &other) = default;
+
+DescriptorSetLayoutDesc &DescriptorSetLayoutDesc::operator=(const DescriptorSetLayoutDesc &other) =
+ default;
+
+size_t DescriptorSetLayoutDesc::hash() const
+{
+ return angle::ComputeGenericHash(mPackedDescriptorSetLayout);
+}
+
+bool DescriptorSetLayoutDesc::operator==(const DescriptorSetLayoutDesc &other) const
+{
+ return (memcmp(&mPackedDescriptorSetLayout, &other.mPackedDescriptorSetLayout,
+ sizeof(mPackedDescriptorSetLayout)) == 0);
+}
+
+void DescriptorSetLayoutDesc::update(uint32_t bindingIndex, VkDescriptorType type, uint32_t count)
+{
+ ASSERT(static_cast<size_t>(type) < std::numeric_limits<uint16_t>::max());
+ ASSERT(count < std::numeric_limits<uint16_t>::max());
+
+ PackedDescriptorSetBinding &packedBinding = mPackedDescriptorSetLayout[bindingIndex];
+
+ packedBinding.type = static_cast<uint16_t>(type);
+ packedBinding.count = static_cast<uint16_t>(count);
+}
+
+void DescriptorSetLayoutDesc::unpackBindings(DescriptorSetLayoutBindingVector *bindings) const
+{
+ for (uint32_t bindingIndex = 0; bindingIndex < kMaxDescriptorSetLayoutBindings; ++bindingIndex)
+ {
+ const PackedDescriptorSetBinding &packedBinding = mPackedDescriptorSetLayout[bindingIndex];
+ if (packedBinding.count == 0)
+ continue;
+
+ VkDescriptorSetLayoutBinding binding;
+ 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);
+ binding.pImmutableSamplers = nullptr;
+
+ bindings->push_back(binding);
+ }
+}
+
+// PipelineLayoutDesc implementation.
+PipelineLayoutDesc::PipelineLayoutDesc() : mDescriptorSetLayouts{}, mPushConstantRanges{}
+{
+}
+
+PipelineLayoutDesc::~PipelineLayoutDesc() = default;
+
+PipelineLayoutDesc::PipelineLayoutDesc(const PipelineLayoutDesc &other) = default;
+
+PipelineLayoutDesc &PipelineLayoutDesc::operator=(const PipelineLayoutDesc &rhs)
+{
+ mDescriptorSetLayouts = rhs.mDescriptorSetLayouts;
+ mPushConstantRanges = rhs.mPushConstantRanges;
+ return *this;
+}
+
+size_t PipelineLayoutDesc::hash() const
+{
+ return angle::ComputeGenericHash(*this);
+}
+
+bool PipelineLayoutDesc::operator==(const PipelineLayoutDesc &other) const
+{
+ return memcmp(this, &other, sizeof(PipelineLayoutDesc)) == 0;
+}
+
+void PipelineLayoutDesc::updateDescriptorSetLayout(uint32_t setIndex,
+ const DescriptorSetLayoutDesc &desc)
+{
+ ASSERT(setIndex < mDescriptorSetLayouts.size());
+ mDescriptorSetLayouts[setIndex] = desc;
+}
+
+void PipelineLayoutDesc::updatePushConstantRange(gl::ShaderType shaderType,
+ uint32_t offset,
+ uint32_t size)
+{
+ ASSERT(shaderType == gl::ShaderType::Vertex || shaderType == gl::ShaderType::Fragment);
+ PackedPushConstantRange &packed = mPushConstantRanges[static_cast<size_t>(shaderType)];
+ packed.offset = offset;
+ packed.size = size;
+}
+
+const PushConstantRangeArray<PackedPushConstantRange> &PipelineLayoutDesc::getPushConstantRanges()
+ const
+{
+ return mPushConstantRanges;
+}
} // namespace vk
// RenderPassCache implementation.
@@ -1021,4 +1123,123 @@
mPayload.emplace(desc, vk::PipelineAndSerial(std::move(pipeline), Serial()));
}
+// DescriptorSetLayoutCache implementation.
+DescriptorSetLayoutCache::DescriptorSetLayoutCache() = default;
+
+DescriptorSetLayoutCache::~DescriptorSetLayoutCache()
+{
+ ASSERT(mPayload.empty());
+}
+
+void DescriptorSetLayoutCache::destroy(VkDevice device)
+{
+ for (auto &item : mPayload)
+ {
+ vk::SharedDescriptorSetLayout &layout = item.second;
+ ASSERT(!layout.isReferenced());
+ layout.get().destroy(device);
+ }
+
+ mPayload.clear();
+}
+
+vk::Error DescriptorSetLayoutCache::getDescriptorSetLayout(
+ VkDevice device,
+ const vk::DescriptorSetLayoutDesc &desc,
+ vk::BindingPointer<vk::DescriptorSetLayout> *descriptorSetLayoutOut)
+{
+ auto iter = mPayload.find(desc);
+ if (iter != mPayload.end())
+ {
+ vk::SharedDescriptorSetLayout &layout = iter->second;
+ descriptorSetLayoutOut->set(&layout);
+ return vk::NoError();
+ }
+
+ // We must unpack the descriptor set layout description.
+ vk::DescriptorSetLayoutBindingVector bindings;
+ desc.unpackBindings(&bindings);
+
+ VkDescriptorSetLayoutCreateInfo createInfo;
+ createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ createInfo.pNext = nullptr;
+ createInfo.flags = 0;
+ createInfo.bindingCount = static_cast<uint32_t>(bindings.size());
+ createInfo.pBindings = bindings.data();
+
+ vk::DescriptorSetLayout newLayout;
+ ANGLE_TRY(newLayout.init(device, createInfo));
+
+ auto insertedItem = mPayload.emplace(desc, vk::SharedDescriptorSetLayout(std::move(newLayout)));
+ vk::SharedDescriptorSetLayout &insertedLayout = insertedItem.first->second;
+ descriptorSetLayoutOut->set(&insertedLayout);
+
+ return vk::NoError();
+}
+
+// PipelineLayoutCache implementation.
+PipelineLayoutCache::PipelineLayoutCache() = default;
+
+PipelineLayoutCache::~PipelineLayoutCache()
+{
+ ASSERT(mPayload.empty());
+}
+
+void PipelineLayoutCache::destroy(VkDevice device)
+{
+ for (auto &item : mPayload)
+ {
+ vk::SharedPipelineLayout &layout = item.second;
+ layout.get().destroy(device);
+ }
+
+ mPayload.clear();
+}
+
+vk::Error PipelineLayoutCache::getPipelineLayout(
+ VkDevice device,
+ const vk::PipelineLayoutDesc &desc,
+ const vk::DescriptorSetLayoutPointerArray &descriptorSetLayouts,
+ vk::BindingPointer<vk::PipelineLayout> *pipelineLayoutOut)
+{
+ auto iter = mPayload.find(desc);
+ if (iter != mPayload.end())
+ {
+ vk::SharedPipelineLayout &layout = iter->second;
+ pipelineLayoutOut->set(&layout);
+ return vk::NoError();
+ }
+
+ // Note this does not handle gaps in descriptor set layouts gracefully.
+ angle::FixedVector<VkDescriptorSetLayout, vk::kMaxDescriptorSetLayouts> setLayoutHandles;
+ for (const vk::BindingPointer<vk::DescriptorSetLayout> &layoutPtr : descriptorSetLayouts)
+ {
+ VkDescriptorSetLayout setLayout = layoutPtr.get().getHandle();
+ if (setLayout != VK_NULL_HANDLE)
+ {
+ setLayoutHandles.push_back(setLayout);
+ }
+ }
+
+ // No pipeline layout found. We must create a new one.
+ VkPipelineLayoutCreateInfo createInfo;
+ createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ createInfo.pNext = nullptr;
+ createInfo.flags = 0;
+ createInfo.setLayoutCount = static_cast<uint32_t>(setLayoutHandles.size());
+ createInfo.pSetLayouts = setLayoutHandles.data();
+
+ // TODO(jmadill): Init push constant ranges. http://anglebug.com/2462
+ createInfo.pushConstantRangeCount = 0;
+ createInfo.pPushConstantRanges = nullptr;
+
+ vk::PipelineLayout newLayout;
+ ANGLE_TRY(newLayout.init(device, createInfo));
+
+ auto insertedItem = mPayload.emplace(desc, vk::SharedPipelineLayout(std::move(newLayout)));
+ vk::SharedPipelineLayout &insertedLayout = insertedItem.first->second;
+ pipelineLayoutOut->set(&insertedLayout);
+
+ return vk::NoError();
+}
} // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/vk_cache_utils.h b/src/libANGLE/renderer/vulkan/vk_cache_utils.h
index 77a49f5..a5c2aac 100644
--- a/src/libANGLE/renderer/vulkan/vk_cache_utils.h
+++ b/src/libANGLE/renderer/vulkan/vk_cache_utils.h
@@ -21,6 +21,88 @@
{
class ImageHelper;
+// This is a very simple RefCount class that has no autoreleasing. Used in the descriptor set and
+// pipeline layout caches.
+template <typename T>
+class RefCounted : angle::NonCopyable
+{
+ public:
+ RefCounted() : mRefCount(0) {}
+ explicit RefCounted(T &&newObject) : mRefCount(0), mObject(std::move(newObject)) {}
+ ~RefCounted() { ASSERT(mRefCount == 0 && !mObject.valid()); }
+
+ RefCounted(RefCounted &©) : mRefCount(copy.mRefCount), mObject(std::move(copy.mObject))
+ {
+ copy.mRefCount = 0;
+ }
+
+ RefCounted &operator=(RefCounted &&rhs)
+ {
+ std::swap(mRefCount, rhs.mRefCount);
+ mObject = std::move(rhs.mObject);
+ return *this;
+ }
+
+ void addRef()
+ {
+ ASSERT(mRefCount != std::numeric_limits<uint32_t>::max());
+ mRefCount++;
+ }
+ void releaseRef()
+ {
+ ASSERT(isReferenced());
+ mRefCount--;
+ }
+ bool isReferenced() const { return mRefCount != 0; }
+
+ T &get() { return mObject; }
+ const T &get() const { return mObject; }
+
+ private:
+ uint32_t mRefCount;
+ T mObject;
+};
+
+template <typename T>
+class BindingPointer final : angle::NonCopyable
+{
+ public:
+ BindingPointer() : mRefCounted(nullptr) {}
+
+ ~BindingPointer() { reset(); }
+
+ void set(RefCounted<T> *refCounted)
+ {
+ if (mRefCounted)
+ {
+ mRefCounted->releaseRef();
+ }
+
+ mRefCounted = refCounted;
+
+ if (mRefCounted)
+ {
+ mRefCounted->addRef();
+ }
+ }
+
+ void reset() { set(nullptr); }
+
+ T &get() { return mRefCounted->get(); }
+ const T &get() const { return mRefCounted->get(); }
+
+ bool valid() const { return mRefCounted != nullptr; }
+
+ private:
+ RefCounted<T> *mRefCounted;
+};
+
+using RenderPassAndSerial = ObjectAndSerial<RenderPass>;
+using PipelineAndSerial = ObjectAndSerial<Pipeline>;
+
+using SharedDescriptorSetLayout = RefCounted<DescriptorSetLayout>;
+using SharedPipelineLayout = RefCounted<PipelineLayout>;
+
// Packed Vk resource descriptions.
// Most Vk types use many more bits than required to represent the underlying data.
// Since ANGLE wants cache things like RenderPasses and Pipeline State Objects using
@@ -336,17 +418,105 @@
// This is not guaranteed by the spec, but is validated by a compile-time check.
// No gaps or padding at the end ensures that hashing and memcmp checks will not run
// into uninitialized memory regions.
-constexpr size_t PipelineDescSumOfSizes =
+constexpr size_t kPipelineDescSumOfSizes =
sizeof(ShaderStageInfo) + sizeof(VertexInputBindings) + sizeof(VertexInputAttributes) +
sizeof(PackedInputAssemblyInfo) + sizeof(VkViewport) + sizeof(VkRect2D) +
sizeof(PackedRasterizationStateInfo) + sizeof(PackedMultisampleStateInfo) +
sizeof(PackedDepthStencilStateInfo) + sizeof(PackedColorBlendStateInfo) +
sizeof(RenderPassDesc);
-static_assert(sizeof(PipelineDesc) == PipelineDescSumOfSizes, "Size mismatch");
+static_assert(sizeof(PipelineDesc) == kPipelineDescSumOfSizes, "Size mismatch");
-using RenderPassAndSerial = ObjectAndSerial<RenderPass>;
-using PipelineAndSerial = ObjectAndSerial<Pipeline>;
+constexpr uint32_t kMaxDescriptorSetLayoutBindings = gl::IMPLEMENTATION_MAX_ACTIVE_TEXTURES;
+
+using DescriptorSetLayoutBindingVector =
+ angle::FixedVector<VkDescriptorSetLayoutBinding, kMaxDescriptorSetLayoutBindings>;
+
+// A packed description of a descriptor set layout. Use similarly to RenderPassDesc and
+// PipelineDesc. Currently we only need to differentiate layouts based on sampler usage. In the
+// future we could generalize this.
+class DescriptorSetLayoutDesc final
+{
+ public:
+ DescriptorSetLayoutDesc();
+ ~DescriptorSetLayoutDesc();
+ DescriptorSetLayoutDesc(const DescriptorSetLayoutDesc &other);
+ DescriptorSetLayoutDesc &operator=(const DescriptorSetLayoutDesc &other);
+
+ size_t hash() const;
+ bool operator==(const DescriptorSetLayoutDesc &other) const;
+
+ void update(uint32_t bindingIndex, VkDescriptorType type, uint32_t count);
+
+ void unpackBindings(DescriptorSetLayoutBindingVector *bindings) const;
+
+ private:
+ struct PackedDescriptorSetBinding
+ {
+ 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");
+
+ // This is a compact representation of a descriptor set layout.
+ std::array<PackedDescriptorSetBinding, kMaxDescriptorSetLayoutBindings>
+ mPackedDescriptorSetLayout;
+};
+
+// The following are for caching descriptor set layouts. Limited to max two descriptor set layouts
+// and two push constants. One push constant per shader stage. This can be extended in the future.
+constexpr size_t kMaxDescriptorSetLayouts = 2;
+constexpr size_t kMaxPushConstantRanges = 2;
+
+struct PackedPushConstantRange
+{
+ uint32_t offset;
+ uint32_t size;
+};
+
+template <typename T>
+using DescriptorSetLayoutArray = std::array<T, kMaxDescriptorSetLayouts>;
+using DescriptorSetLayoutPointerArray =
+ DescriptorSetLayoutArray<BindingPointer<DescriptorSetLayout>>;
+template <typename T>
+using PushConstantRangeArray = std::array<T, kMaxPushConstantRanges>;
+
+class PipelineLayoutDesc final
+{
+ public:
+ PipelineLayoutDesc();
+ ~PipelineLayoutDesc();
+ PipelineLayoutDesc(const PipelineLayoutDesc &other);
+ PipelineLayoutDesc &operator=(const PipelineLayoutDesc &rhs);
+
+ size_t hash() const;
+ bool operator==(const PipelineLayoutDesc &other) const;
+
+ void updateDescriptorSetLayout(uint32_t setIndex, const DescriptorSetLayoutDesc &desc);
+ void updatePushConstantRange(gl::ShaderType shaderType, uint32_t offset, uint32_t size);
+
+ const PushConstantRangeArray<PackedPushConstantRange> &getPushConstantRanges() const;
+
+ private:
+ DescriptorSetLayoutArray<DescriptorSetLayoutDesc> mDescriptorSetLayouts;
+ PushConstantRangeArray<PackedPushConstantRange> mPushConstantRanges;
+
+ // Verify the arrays are properly packed.
+ static_assert(sizeof(decltype(mDescriptorSetLayouts)) ==
+ (sizeof(DescriptorSetLayoutDesc) * kMaxDescriptorSetLayouts),
+ "Unexpected size");
+ static_assert(sizeof(decltype(mPushConstantRanges)) ==
+ (sizeof(PackedPushConstantRange) * kMaxPushConstantRanges),
+ "Unexpected size");
+};
+
+// Verify the structure is properly packed.
+static_assert(sizeof(PipelineLayoutDesc) ==
+ (sizeof(DescriptorSetLayoutArray<DescriptorSetLayoutDesc>) +
+ sizeof(std::array<PackedPushConstantRange, kMaxPushConstantRanges>)),
+ "Unexpected Size");
} // namespace vk
} // namespace rx
@@ -371,6 +541,17 @@
size_t operator()(const rx::vk::PipelineDesc &key) const { return key.hash(); }
};
+template <>
+struct hash<rx::vk::DescriptorSetLayoutDesc>
+{
+ size_t operator()(const rx::vk::DescriptorSetLayoutDesc &key) const { return key.hash(); }
+};
+
+template <>
+struct hash<rx::vk::PipelineLayoutDesc>
+{
+ size_t operator()(const rx::vk::PipelineLayoutDesc &key) const { return key.hash(); }
+};
} // namespace std
namespace rx
@@ -426,6 +607,47 @@
std::unordered_map<vk::PipelineDesc, vk::PipelineAndSerial> mPayload;
};
+class DescriptorSetLayoutCache final : angle::NonCopyable
+{
+ public:
+ DescriptorSetLayoutCache();
+ ~DescriptorSetLayoutCache();
+
+ void destroy(VkDevice device);
+
+ vk::Error getDescriptorSetLayout(
+ VkDevice device,
+ const vk::DescriptorSetLayoutDesc &desc,
+ vk::BindingPointer<vk::DescriptorSetLayout> *descriptorSetLayoutOut);
+
+ private:
+ std::unordered_map<vk::DescriptorSetLayoutDesc, vk::SharedDescriptorSetLayout> mPayload;
+};
+
+class PipelineLayoutCache final : angle::NonCopyable
+{
+ public:
+ PipelineLayoutCache();
+ ~PipelineLayoutCache();
+
+ void destroy(VkDevice device);
+
+ vk::Error getPipelineLayout(VkDevice device,
+ const vk::PipelineLayoutDesc &desc,
+ const vk::DescriptorSetLayoutPointerArray &descriptorSetLayouts,
+ vk::BindingPointer<vk::PipelineLayout> *pipelineLayoutOut);
+
+ private:
+ std::unordered_map<vk::PipelineLayoutDesc, vk::SharedPipelineLayout> mPayload;
+};
+
+// 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 kDescriptorSetCount = 2;
+
} // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_VK_CACHE_UTILS_H_
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.cpp b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
index 6179b1f..f86beb2 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
@@ -331,12 +331,12 @@
Error DynamicDescriptorPool::allocateNewPool(const VkDevice &device)
{
- VkDescriptorPoolSize poolSizes[DescriptorPoolIndexCount];
- poolSizes[UniformBufferIndex].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
- poolSizes[UniformBufferIndex].descriptorCount =
+ VkDescriptorPoolSize poolSizes[kDescriptorSetCount];
+ poolSizes[kUniformsDescriptorSetIndex].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
+ poolSizes[kUniformsDescriptorSetIndex].descriptorCount =
mUniformBufferDescriptorsPerSet * mMaxSetsPerPool;
- poolSizes[TextureIndex].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
- poolSizes[TextureIndex].descriptorCount =
+ poolSizes[kTextureDescriptorSetIndex].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ poolSizes[kTextureDescriptorSetIndex].descriptorCount =
mCombinedImageSamplerDescriptorsPerSet * mMaxSetsPerPool;
VkDescriptorPoolCreateInfo descriptorPoolInfo;
@@ -346,7 +346,7 @@
descriptorPoolInfo.maxSets = mMaxSetsPerPool;
// Reserve pools for uniform blocks and textures.
- descriptorPoolInfo.poolSizeCount = DescriptorPoolIndexCount;
+ descriptorPoolInfo.poolSizeCount = kDescriptorSetCount;
descriptorPoolInfo.pPoolSizes = poolSizes;
mCurrentAllocatedDescriptorSetCount = 0;
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.h b/src/libANGLE/renderer/vulkan/vk_helpers.h
index 10c6dd8..7c77cbc 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.h
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.h
@@ -86,13 +86,6 @@
// Uses DescriptorPool to allocate descriptor sets as needed. If the descriptor pool
// is full, we simply allocate a new pool to keep allocating descriptor sets as needed and
// leave the renderer take care of the life time of the pools that become unused.
-enum DescriptorPoolIndex : uint8_t
-{
- UniformBufferIndex = 0,
- TextureIndex = 1,
- DescriptorPoolIndexCount = 2
-};
-
class DynamicDescriptorPool final : angle::NonCopyable
{
public: