[graphite] Create & cache descriptor sets
* Move static ds_type_enum_to_vk_ds function to be a static member of VulkanDescriptorSet.h for better accessibility
* Flesh out VulkanDescriptorSet class
* Add VulkanResourceProvider::findOrCreateDescriptorSet which takes in a span of descriptor types & counts
Change-Id: Ide6d1954e3932948a81a1d7ea49ebeebbe5259c1
Bug: b/274762860
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/691516
Commit-Queue: Nicolette Prevost <nicolettep@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
diff --git a/src/gpu/graphite/vk/VulkanDescriptorPool.cpp b/src/gpu/graphite/vk/VulkanDescriptorPool.cpp
index 3462387..d2ec165 100644
--- a/src/gpu/graphite/vk/VulkanDescriptorPool.cpp
+++ b/src/gpu/graphite/vk/VulkanDescriptorPool.cpp
@@ -13,26 +13,6 @@
namespace skgpu::graphite {
-namespace { // anonymous namespace
-static VkDescriptorType ds_type_enum_to_vk_ds(DescriptorType type) {
- switch (type) {
- case DescriptorType::kUniformBuffer:
- return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
- case DescriptorType::kTextureSampler:
- return VK_DESCRIPTOR_TYPE_SAMPLER;
- case DescriptorType::kTexture:
- return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
- case DescriptorType::kCombinedTextureSampler:
- return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
- case DescriptorType::kStorageBuffer:
- return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
- case DescriptorType::kInputAttachment:
- return VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
- }
- SkUNREACHABLE;
-}
-} // anonymous namespace
-
sk_sp<VulkanDescriptorPool> VulkanDescriptorPool::Make(
const VulkanSharedContext* context,
SkSpan<DescTypeAndCount> requestedDescCounts) {
@@ -42,7 +22,13 @@
}
// For each requested descriptor type and count, create a VkDescriptorPoolSize struct which
- // specifies the descriptor type and quantity for pool creation.
+ // specifies the descriptor type and quantity for pool creation. Multiple pool size structures
+ // may contain the same descriptor type - the pool will be created with enough storage for the
+ // total number of descriptors of each type. Source:
+ // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VkDescriptorPoolSize
+ // Note: The kMaxNumDescriptors limit could be evaded since we do not currently track and check
+ // the cumulative quantities of each type of descriptor, but this is an internal call and it is
+ // highly unexpected for us to exceed this limit in practice.
skia_private::STArray<kDescriptorTypeCount, VkDescriptorPoolSize> poolSizes;
for (size_t i = 0; i < requestedDescCounts.size(); i++) {
SkASSERT(requestedDescCounts[i].count > 0);
@@ -55,7 +41,7 @@
VkDescriptorPoolSize* poolSize = &poolSizes.at(i);
memset(poolSize, 0, sizeof(VkDescriptorPoolSize));
// Map each DescriptorSetType to the appropriate backend VkDescriptorType
- poolSize->type = ds_type_enum_to_vk_ds(requestedDescCounts[i].type);
+ poolSize->type = VulkanDescriptorSet::DsTypeEnumToVkDs(requestedDescCounts[i].type);
// Create a pool large enough to accommodate the maximum possible number of descriptor sets
poolSize->descriptorCount = requestedDescCounts[i].count * kMaxNumSets;
}
diff --git a/src/gpu/graphite/vk/VulkanDescriptorSet.cpp b/src/gpu/graphite/vk/VulkanDescriptorSet.cpp
index 45b2489..62ee831 100644
--- a/src/gpu/graphite/vk/VulkanDescriptorSet.cpp
+++ b/src/gpu/graphite/vk/VulkanDescriptorSet.cpp
@@ -12,12 +12,60 @@
namespace skgpu::graphite {
+sk_sp<VulkanDescriptorSet> VulkanDescriptorSet::Make(const VulkanSharedContext* ctxt,
+ sk_sp<VulkanDescriptorPool> pool,
+ const VkDescriptorSetLayout* layout) {
+ VkDescriptorSet descSet;
+
+ VkDescriptorSetAllocateInfo dsAllocateInfo;
+ memset(&dsAllocateInfo, 0, sizeof(VkDescriptorSetAllocateInfo));
+ dsAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ dsAllocateInfo.pNext = nullptr;
+ dsAllocateInfo.descriptorPool = *(pool->descPool());
+ dsAllocateInfo.descriptorSetCount = VulkanDescriptorPool::kMaxNumSets;
+ dsAllocateInfo.pSetLayouts = layout;
+
+ VkResult result;
+ VULKAN_CALL_RESULT(ctxt->interface(),
+ result,
+ AllocateDescriptorSets(ctxt->device(),
+ &dsAllocateInfo,
+ &descSet));
+ if (result != VK_SUCCESS) {
+ return nullptr;
+ }
+ return sk_sp<VulkanDescriptorSet>(new VulkanDescriptorSet(ctxt, descSet, pool));
+}
+
+VkDescriptorType VulkanDescriptorSet::DsTypeEnumToVkDs(DescriptorType type) {
+ switch (type) {
+ case DescriptorType::kUniformBuffer:
+ return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ case DescriptorType::kTextureSampler:
+ return VK_DESCRIPTOR_TYPE_SAMPLER;
+ case DescriptorType::kTexture:
+ return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
+ case DescriptorType::kCombinedTextureSampler:
+ return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ case DescriptorType::kStorageBuffer:
+ return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ case DescriptorType::kInputAttachment:
+ return VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
+ }
+ SkUNREACHABLE;
+}
+
VulkanDescriptorSet::VulkanDescriptorSet(const VulkanSharedContext* ctxt,
+ VkDescriptorSet set,
sk_sp<VulkanDescriptorPool> pool)
: Resource(ctxt, Ownership::kOwned, skgpu::Budgeted::kNo, /*gpuMemorySize=*/0)
- , fPool (pool)
- , fIsAvailable (true) {
+ , fDescSet (set)
+ , fPool (pool) {
fPool->ref();
}
+void VulkanDescriptorSet::freeGpuData() {
+ fPool->unref();
+}
+
} // namespace skgpu::graphite
diff --git a/src/gpu/graphite/vk/VulkanDescriptorSet.h b/src/gpu/graphite/vk/VulkanDescriptorSet.h
index bc4b89b..23bb895 100644
--- a/src/gpu/graphite/vk/VulkanDescriptorSet.h
+++ b/src/gpu/graphite/vk/VulkanDescriptorSet.h
@@ -10,6 +10,7 @@
#include "src/gpu/graphite/Resource.h"
+#include "src/gpu/graphite/Descriptors.h"
#include "src/gpu/graphite/vk/VulkanGraphiteUtilsPriv.h"
namespace skgpu::graphite {
@@ -20,33 +21,31 @@
/**
* Wrapper around VkDescriptorSet which maintains a reference to its descriptor pool. Once the ref
* count on that pool is 0, it will be destroyed.
+ *
+ * TODO: Track whether a descriptor set is available for use or if it is already in use elsewhere.
*/
class VulkanDescriptorSet : public Resource {
public:
- VulkanDescriptorSet(const VulkanSharedContext*, sk_sp<VulkanDescriptorPool>);
+ static sk_sp<VulkanDescriptorSet> Make(const VulkanSharedContext*,
+ sk_sp<VulkanDescriptorPool>,
+ const VkDescriptorSetLayout*);
+
+ static VkDescriptorType DsTypeEnumToVkDs(DescriptorType type);
+
+ VulkanDescriptorSet(const VulkanSharedContext*, VkDescriptorSet, sk_sp<VulkanDescriptorPool>);
VkDescriptorSetLayout layout() const { return fDescLayout; }
- bool isAvailable() const { return fIsAvailable; }
-
- VkDescriptorSet descriptorSet() {
- SkASSERT(fIsAvailable);
- fIsAvailable = false;
- return fDescSet;
- }
-
- void setAvailability(bool isAvailable) {
- fIsAvailable = isAvailable;
- }
+ VkDescriptorSet descriptorSet() { return fDescSet; }
private:
- void freeGpuData() override {
- //TODO: Implement.
- }
+ void freeGpuData() override;
VkDescriptorSet fDescSet;
+ // Have this class hold on to a reference of the descriptor pool. When a pool's reference count
+ // is 0, that means all the descriptor sets that came from that pool are no longer needed, so
+ // the pool can safely be destroyed.
sk_sp<VulkanDescriptorPool> fPool;
- bool fIsAvailable;
VkDescriptorSetLayout fDescLayout;
};
} // namespace skgpu::graphite
diff --git a/src/gpu/graphite/vk/VulkanResourceProvider.cpp b/src/gpu/graphite/vk/VulkanResourceProvider.cpp
index 97eaa6e..f2ee7f4 100644
--- a/src/gpu/graphite/vk/VulkanResourceProvider.cpp
+++ b/src/gpu/graphite/vk/VulkanResourceProvider.cpp
@@ -7,6 +7,7 @@
#include "src/gpu/graphite/vk/VulkanResourceProvider.h"
+#include "include/core/SkSpan.h"
#include "include/gpu/graphite/BackendTexture.h"
#include "src/gpu/graphite/Buffer.h"
#include "src/gpu/graphite/ComputePipeline.h"
@@ -15,6 +16,8 @@
#include "src/gpu/graphite/Texture.h"
#include "src/gpu/graphite/vk/VulkanBuffer.h"
#include "src/gpu/graphite/vk/VulkanCommandBuffer.h"
+#include "src/gpu/graphite/vk/VulkanDescriptorPool.h"
+#include "src/gpu/graphite/vk/VulkanDescriptorSet.h"
#include "src/gpu/graphite/vk/VulkanGraphicsPipeline.h"
#include "src/gpu/graphite/vk/VulkanSampler.h"
#include "src/gpu/graphite/vk/VulkanSharedContext.h"
@@ -22,6 +25,54 @@
namespace skgpu::graphite {
+namespace { // Anonymous namespace
+
+VkDescriptorSetLayout desc_type_count_to_desc_set_layout(
+ const VulkanSharedContext* ctxt,
+ SkSpan<DescTypeAndCount> requestedDescriptors) {
+
+ VkDescriptorSetLayout layout;
+ skia_private::STArray<kDescriptorTypeCount, VkDescriptorSetLayoutBinding> bindingLayouts;
+
+ for (size_t i = 0, j = 0; i < requestedDescriptors.size(); i++) {
+ if (requestedDescriptors[i].count != 0) {
+ VkDescriptorSetLayoutBinding* layoutBinding = &bindingLayouts.at(j++);
+ memset(layoutBinding, 0, sizeof(VkDescriptorSetLayoutBinding));
+ layoutBinding->binding = 0;
+ layoutBinding->descriptorType =
+ VulkanDescriptorSet::DsTypeEnumToVkDs(requestedDescriptors[i].type);
+ layoutBinding->descriptorCount = requestedDescriptors[i].count;
+ // TODO: Obtain layout binding stage flags from visibility (vertex or shader)
+ layoutBinding->stageFlags = 0;
+ // TODO: Optionally set immutableSamplers here.
+ layoutBinding->pImmutableSamplers = nullptr;
+ }
+ }
+
+ VkDescriptorSetLayoutCreateInfo layoutCreateInfo;
+ memset(&layoutCreateInfo, 0, sizeof(VkDescriptorSetLayoutCreateInfo));
+ layoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ layoutCreateInfo.pNext = nullptr;
+ layoutCreateInfo.flags = 0;
+ layoutCreateInfo.bindingCount = bindingLayouts.size();
+ layoutCreateInfo.pBindings = &bindingLayouts.front();
+
+ VkResult result;
+ VULKAN_CALL_RESULT(ctxt->interface(),
+ result,
+ CreateDescriptorSetLayout(ctxt->device(),
+ &layoutCreateInfo,
+ nullptr,
+ &layout));
+ if (result != VK_SUCCESS) {
+ SkDebugf("Failed to create VkDescriptorSetLayout\n");
+ layout = VK_NULL_HANDLE;
+ }
+ return layout;
+}
+
+} // Anonymous namespace
+
VulkanResourceProvider::VulkanResourceProvider(SharedContext* sharedContext,
SingleOwner* singleOwner,
uint32_t recorderID)
@@ -72,4 +123,68 @@
return {};
}
+VulkanDescriptorSet* VulkanResourceProvider::findOrCreateDescriptorSet(
+ SkSpan<DescTypeAndCount> requestedDescriptors) {
+ GraphiteResourceKey key;
+ // TODO(nicolettep): Optimize key structure. It is horrendously inefficient but functional.
+ // Fow now, have each descriptor type and quantity take up an entire uint32_t, with an
+ // additional uint32_t added to include a unique identifier for different descriptor sets that
+ // have the same set layout.
+ static const int kNum32DataCnt = (kDescriptorTypeCount * 2) + 1;
+ static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
+
+ Resource* descSet = nullptr;
+ // Search for available descriptor sets by assembling the last part of the key with a unique set
+ // ID (which ranges from 0 to kMaxNumSets - 1). Start the search at 0 and continue until an
+ // available set is found.
+ // TODO(nicolettep): Explore ways to optimize this traversal.
+ for (uint32_t i = 0; i < VulkanDescriptorPool::kMaxNumSets; i++) {
+ GraphiteResourceKey::Builder builder(&key, kType, kNum32DataCnt, Shareable::kNo);
+ // Assemble the base component of a descriptor set key which is determined by the type and
+ // quantity of requested descriptors.
+ for (size_t j = 0, k = 0; k < requestedDescriptors.size(); j = j + 2, k++) {
+ builder[j+1] = static_cast<uint32_t>(requestedDescriptors[k].type);
+ builder[j] = requestedDescriptors[k].count;
+ }
+ builder[kNum32DataCnt - 1] = i;
+ builder.finish();
+
+ if ((descSet = fResourceCache->findAndRefResource(key, skgpu::Budgeted::kNo))) {
+ // A non-null resource pointer indicates we have found an available descriptor set.
+ return static_cast<VulkanDescriptorSet*>(descSet);
+ }
+ key.reset();
+ }
+
+ // If we did not find an existing avilable desc set, allocate sets with the appropriate layout
+ // and add them to the cache.
+ auto pool = VulkanDescriptorPool::Make(this->vulkanSharedContext(), requestedDescriptors);
+ VkDescriptorSetLayout layout = desc_type_count_to_desc_set_layout(
+ this->vulkanSharedContext(),
+ requestedDescriptors);
+
+ // Store the key of the first descriptor set so it can be easily accessed later.
+ GraphiteResourceKey firstSetKey;
+ // Allocate the maximum number of sets so they can be easily accessed as needed from the cache.
+ for (int i = 0; i < VulkanDescriptorPool::kMaxNumSets ; i++) {
+ GraphiteResourceKey::Builder builder(&key, kType, kNum32DataCnt, Shareable::kNo);
+ descSet = VulkanDescriptorSet::Make(this->vulkanSharedContext(), pool, &layout).get();
+ // Assemble the base component of a descriptor set key which is determined by the type and
+ // quantity of requested descriptors.
+ for (size_t j = 0, k = 0; k < requestedDescriptors.size(); j = j + 2, k++) {
+ builder[j+1] = static_cast<uint32_t>(requestedDescriptors[k].type);
+ builder[j] = requestedDescriptors[k].count;
+ }
+ builder[kNum32DataCnt - 1] = i;
+ builder.finish();
+ descSet->setKey(key);
+ fResourceCache->insertResource(descSet);
+ if (i == 0) {
+ firstSetKey = key;
+ }
+ key.reset();
+ }
+ descSet = fResourceCache->findAndRefResource(firstSetKey, skgpu::Budgeted::kNo);
+ return descSet ? static_cast<VulkanDescriptorSet*>(descSet) : nullptr;
+}
} // namespace skgpu::graphite
diff --git a/src/gpu/graphite/vk/VulkanResourceProvider.h b/src/gpu/graphite/vk/VulkanResourceProvider.h
index c5815d4..b2b3265 100644
--- a/src/gpu/graphite/vk/VulkanResourceProvider.h
+++ b/src/gpu/graphite/vk/VulkanResourceProvider.h
@@ -11,10 +11,12 @@
#include "src/gpu/graphite/ResourceProvider.h"
#include "include/gpu/vk/VulkanTypes.h"
+#include "src/gpu/graphite/Descriptors.h"
namespace skgpu::graphite {
class VulkanCommandBuffer;
+class VulkanDescriptorSet;
class VulkanSharedContext;
class VulkanResourceProvider final : public ResourceProvider {
@@ -41,6 +43,8 @@
BackendTexture onCreateBackendTexture(SkISize dimensions, const TextureInfo&) override;
void onDeleteBackendTexture(BackendTexture&) override {}
+
+ VulkanDescriptorSet* findOrCreateDescriptorSet(SkSpan<DescTypeAndCount>);
};
} // namespace skgpu::graphite