anv: Implement VariableDescriptorCount

Reviewed-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/7180>
diff --git a/src/intel/vulkan/anv_cmd_buffer.c b/src/intel/vulkan/anv_cmd_buffer.c
index 86596b6..4734926 100644
--- a/src/intel/vulkan/anv_cmd_buffer.c
+++ b/src/intel/vulkan/anv_cmd_buffer.c
@@ -1239,9 +1239,9 @@
       anv_descriptor_set_layout_ref(layout);
       set->layout = layout;
    }
-   set->size = anv_descriptor_set_layout_size(layout);
+   set->size = anv_descriptor_set_layout_size(layout, 0);
    set->buffer_view_count = layout->buffer_view_count;
-   set->descriptor_count = layout->size;
+   set->descriptor_count = layout->descriptor_count;
    set->buffer_views = (*push_set)->buffer_views;
 
    if (layout->descriptor_buffer_size &&
diff --git a/src/intel/vulkan/anv_descriptor_set.c b/src/intel/vulkan/anv_descriptor_set.c
index 883c917..09d698e 100644
--- a/src/intel/vulkan/anv_descriptor_set.c
+++ b/src/intel/vulkan/anv_descriptor_set.c
@@ -248,17 +248,31 @@
    const struct anv_physical_device *pdevice = device->physical;
 
    uint32_t surface_count[MESA_SHADER_STAGES] = { 0, };
+   VkDescriptorType varying_desc_type = VK_DESCRIPTOR_TYPE_MAX_ENUM;
    bool needs_descriptor_buffer = false;
 
+   const VkDescriptorSetLayoutBindingFlagsCreateInfo *binding_flags_info =
+      vk_find_struct_const(pCreateInfo->pNext,
+                           DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO);
+
    for (uint32_t b = 0; b < pCreateInfo->bindingCount; b++) {
       const VkDescriptorSetLayoutBinding *binding = &pCreateInfo->pBindings[b];
 
+      VkDescriptorBindingFlags flags = 0;
+      if (binding_flags_info && binding_flags_info->bindingCount > 0) {
+         assert(binding_flags_info->bindingCount == pCreateInfo->bindingCount);
+         flags = binding_flags_info->pBindingFlags[b];
+      }
+
       enum anv_descriptor_data desc_data =
          anv_descriptor_data_for_type(pdevice, binding->descriptorType);
 
       if (anv_needs_descriptor_buffer(binding->descriptorType, desc_data))
          needs_descriptor_buffer = true;
 
+      if (flags & VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT)
+         varying_desc_type = binding->descriptorType;
+
       switch (binding->descriptorType) {
       case VK_DESCRIPTOR_TYPE_SAMPLER:
          /* There is no real limit on samplers */
@@ -300,6 +314,19 @@
          surface_count[s] += 1;
    }
 
+   VkDescriptorSetVariableDescriptorCountLayoutSupport *vdcls =
+      vk_find_struct(pSupport->pNext,
+                     DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT);
+   if (vdcls != NULL) {
+      if (varying_desc_type == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT) {
+         vdcls->maxVariableDescriptorCount = MAX_INLINE_UNIFORM_BLOCK_SIZE;
+      } else if (varying_desc_type != VK_DESCRIPTOR_TYPE_MAX_ENUM) {
+         vdcls->maxVariableDescriptorCount = UINT16_MAX;
+      } else {
+         vdcls->maxVariableDescriptorCount = 0;
+      }
+   }
+
    bool supported = true;
    for (unsigned s = 0; s < MESA_SHADER_STAGES; s++) {
       /* Our maximum binding table size is 240 and we need to reserve 8 for
@@ -417,22 +444,37 @@
       if (binding->descriptorCount == 0)
          continue;
 
-#ifndef NDEBUG
       set_layout->binding[b].type = binding->descriptorType;
-#endif
 
       if (binding_flags_info && binding_flags_info->bindingCount > 0) {
          assert(binding_flags_info->bindingCount == pCreateInfo->bindingCount);
          set_layout->binding[b].flags =
             binding_flags_info->pBindingFlags[info_idx];
+
+         /* From the Vulkan spec:
+          *
+          *    "If VkDescriptorSetLayoutCreateInfo::flags includes
+          *    VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR, then
+          *    all elements of pBindingFlags must not include
+          *    VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
+          *    VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT, or
+          *    VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT"
+          */
+         if (pCreateInfo->flags &
+             VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR) {
+            assert(!(set_layout->binding[b].flags &
+               (VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT |
+                VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT |
+                VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT)));
+         }
       }
 
       set_layout->binding[b].data =
          anv_descriptor_data_for_type(device->physical,
                                       binding->descriptorType);
       set_layout->binding[b].array_size = binding->descriptorCount;
-      set_layout->binding[b].descriptor_index = set_layout->size;
-      set_layout->size += binding->descriptorCount;
+      set_layout->binding[b].descriptor_index = set_layout->descriptor_count;
+      set_layout->descriptor_count += binding->descriptorCount;
 
       if (set_layout->binding[b].data & ANV_DESCRIPTOR_BUFFER_VIEW) {
          set_layout->binding[b].buffer_view_index = buffer_view_count;
@@ -514,6 +556,79 @@
    vk_free(&device->vk.alloc, layout);
 }
 
+static const struct anv_descriptor_set_binding_layout *
+set_layout_dynamic_binding(const struct anv_descriptor_set_layout *set_layout)
+{
+   if (set_layout->binding_count == 0)
+      return NULL;
+
+   const struct anv_descriptor_set_binding_layout *last_binding =
+      &set_layout->binding[set_layout->binding_count - 1];
+   if (!(last_binding->flags & VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT))
+      return NULL;
+
+   return last_binding;
+}
+
+static uint32_t
+set_layout_descriptor_count(const struct anv_descriptor_set_layout *set_layout,
+                            uint32_t var_desc_count)
+{
+   const struct anv_descriptor_set_binding_layout *dynamic_binding =
+      set_layout_dynamic_binding(set_layout);
+   if (dynamic_binding == NULL)
+      return set_layout->descriptor_count;
+
+   assert(var_desc_count <= dynamic_binding->array_size);
+   uint32_t shrink = dynamic_binding->array_size - var_desc_count;
+
+   if (dynamic_binding->type == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT)
+      return set_layout->descriptor_count;
+
+   return set_layout->descriptor_count - shrink;
+}
+
+static uint32_t
+set_layout_buffer_view_count(const struct anv_descriptor_set_layout *set_layout,
+                             uint32_t var_desc_count)
+{
+   const struct anv_descriptor_set_binding_layout *dynamic_binding =
+      set_layout_dynamic_binding(set_layout);
+   if (dynamic_binding == NULL)
+      return set_layout->buffer_view_count;
+
+   assert(var_desc_count <= dynamic_binding->array_size);
+   uint32_t shrink = dynamic_binding->array_size - var_desc_count;
+
+   if (!(dynamic_binding->data & ANV_DESCRIPTOR_BUFFER_VIEW))
+      return set_layout->buffer_view_count;
+
+   return set_layout->buffer_view_count - shrink;
+}
+
+static uint32_t
+set_layout_descriptor_buffer_size(const struct anv_descriptor_set_layout *set_layout,
+                                  uint32_t var_desc_count)
+{
+   const struct anv_descriptor_set_binding_layout *dynamic_binding =
+      set_layout_dynamic_binding(set_layout);
+   if (dynamic_binding == NULL)
+      return set_layout->descriptor_buffer_size;
+
+   assert(var_desc_count <= dynamic_binding->array_size);
+   uint32_t shrink = dynamic_binding->array_size - var_desc_count;
+
+   if (dynamic_binding->type == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT) {
+      /* Inline uniform blocks are specified to use the descriptor array
+       * size as the size in bytes of the block.
+       */
+      return set_layout->descriptor_buffer_size - shrink;
+   } else {
+      return set_layout->descriptor_buffer_size -
+             shrink * anv_descriptor_size(dynamic_binding);
+   }
+}
+
 void anv_DestroyDescriptorSetLayout(
     VkDevice                                    _device,
     VkDescriptorSetLayout                       _set_layout,
@@ -566,7 +681,7 @@
                                   const struct anv_descriptor_set_layout *layout)
 {
    SHA1_UPDATE_VALUE(ctx, layout->binding_count);
-   SHA1_UPDATE_VALUE(ctx, layout->size);
+   SHA1_UPDATE_VALUE(ctx, layout->descriptor_count);
    SHA1_UPDATE_VALUE(ctx, layout->shader_stages);
    SHA1_UPDATE_VALUE(ctx, layout->buffer_view_count);
    SHA1_UPDATE_VALUE(ctx, layout->dynamic_offset_count);
@@ -918,32 +1033,40 @@
 }
 
 size_t
-anv_descriptor_set_layout_size(const struct anv_descriptor_set_layout *layout)
+anv_descriptor_set_layout_size(const struct anv_descriptor_set_layout *layout,
+                               uint32_t var_desc_count)
 {
-   return
-      sizeof(struct anv_descriptor_set) +
-      layout->size * sizeof(struct anv_descriptor) +
-      layout->buffer_view_count * sizeof(struct anv_buffer_view);
+   const uint32_t descriptor_count =
+      set_layout_descriptor_count(layout, var_desc_count);
+   const uint32_t buffer_view_count =
+      set_layout_buffer_view_count(layout, var_desc_count);
+
+   return sizeof(struct anv_descriptor_set) +
+          descriptor_count * sizeof(struct anv_descriptor) +
+          buffer_view_count * sizeof(struct anv_buffer_view);
 }
 
 VkResult
 anv_descriptor_set_create(struct anv_device *device,
                           struct anv_descriptor_pool *pool,
                           struct anv_descriptor_set_layout *layout,
+                          uint32_t var_desc_count,
                           struct anv_descriptor_set **out_set)
 {
    struct anv_descriptor_set *set;
-   const size_t size = anv_descriptor_set_layout_size(layout);
+   const size_t size = anv_descriptor_set_layout_size(layout, var_desc_count);
 
    VkResult result = anv_descriptor_pool_alloc_set(pool, size, &set);
    if (result != VK_SUCCESS)
       return result;
 
-   if (layout->descriptor_buffer_size) {
+   uint32_t descriptor_buffer_size =
+      set_layout_descriptor_buffer_size(layout, var_desc_count);
+   if (descriptor_buffer_size) {
       /* Align the size to 32 so that alignment gaps don't cause extra holes
        * in the heap which can lead to bad performance.
        */
-      uint32_t set_buffer_size = ALIGN(layout->descriptor_buffer_size, 32);
+      uint32_t set_buffer_size = ALIGN(descriptor_buffer_size, 32);
       uint64_t pool_vma_offset =
          util_vma_heap_alloc(&pool->bo_heap, set_buffer_size, 32);
       if (pool_vma_offset == 0) {
@@ -966,7 +1089,7 @@
                                        .bo = pool->bo,
                                        .offset = set->desc_mem.offset,
                                     },
-                                    layout->descriptor_buffer_size, 1);
+                                    descriptor_buffer_size, 1);
    } else {
       set->desc_mem = ANV_STATE_NULL;
       set->desc_surface_state = ANV_STATE_NULL;
@@ -978,16 +1101,19 @@
    set->layout = layout;
    anv_descriptor_set_layout_ref(layout);
 
-   set->buffer_views =
-      (struct anv_buffer_view *) &set->descriptors[layout->size];
-   set->buffer_view_count = layout->buffer_view_count;
+   set->buffer_view_count =
+      set_layout_buffer_view_count(layout, var_desc_count);
+   set->descriptor_count =
+      set_layout_descriptor_count(layout, var_desc_count);
 
-   set->descriptor_count = layout->size;
+   set->buffer_views =
+      (struct anv_buffer_view *) &set->descriptors[set->descriptor_count];
 
    /* By defining the descriptors to be zero now, we can later verify that
     * a descriptor has not been populated with user data.
     */
-   memset(set->descriptors, 0, sizeof(struct anv_descriptor) * layout->size);
+   memset(set->descriptors, 0,
+          sizeof(struct anv_descriptor) * set->descriptor_count);
 
    /* Go through and fill out immutable samplers if we have any */
    struct anv_descriptor *desc = set->descriptors;
@@ -1013,7 +1139,7 @@
    }
 
    /* Allocate surface state for the buffer views. */
-   for (uint32_t b = 0; b < layout->buffer_view_count; b++) {
+   for (uint32_t b = 0; b < set->buffer_view_count; b++) {
       set->buffer_views[b].surface_state =
          anv_descriptor_pool_alloc_state(pool);
    }
@@ -1060,11 +1186,22 @@
    struct anv_descriptor_set *set;
    uint32_t i;
 
+   const VkDescriptorSetVariableDescriptorCountAllocateInfo *vdcai =
+      vk_find_struct_const(pAllocateInfo->pNext,
+                           DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO);
+
    for (i = 0; i < pAllocateInfo->descriptorSetCount; i++) {
       ANV_FROM_HANDLE(anv_descriptor_set_layout, layout,
                       pAllocateInfo->pSetLayouts[i]);
 
-      result = anv_descriptor_set_create(device, pool, layout, &set);
+      uint32_t var_desc_count = 0;
+      if (vdcai != NULL && vdcai->descriptorSetCount > 0) {
+         assert(vdcai->descriptorSetCount == pAllocateInfo->descriptorSetCount);
+         var_desc_count = vdcai->pDescriptorCounts[i];
+      }
+
+      result = anv_descriptor_set_create(device, pool, layout,
+                                         var_desc_count, &set);
       if (result != VK_SUCCESS)
          break;
 
diff --git a/src/intel/vulkan/anv_device.c b/src/intel/vulkan/anv_device.c
index 2b400be..ce172b4 100644
--- a/src/intel/vulkan/anv_device.c
+++ b/src/intel/vulkan/anv_device.c
@@ -1029,7 +1029,7 @@
    f->descriptorBindingStorageTexelBufferUpdateAfterBind = descIndexing;
    f->descriptorBindingUpdateUnusedWhilePending          = descIndexing;
    f->descriptorBindingPartiallyBound                    = descIndexing;
-   f->descriptorBindingVariableDescriptorCount           = false;
+   f->descriptorBindingVariableDescriptorCount           = descIndexing;
    f->runtimeDescriptorArray                             = descIndexing;
 
    f->samplerFilterMinmax                 = pdevice->info.gen >= 9;
diff --git a/src/intel/vulkan/anv_private.h b/src/intel/vulkan/anv_private.h
index f2046fb..06bc4d5 100644
--- a/src/intel/vulkan/anv_private.h
+++ b/src/intel/vulkan/anv_private.h
@@ -1949,10 +1949,8 @@
 };
 
 struct anv_descriptor_set_binding_layout {
-#ifndef NDEBUG
    /* The type of the descriptors in this binding */
    VkDescriptorType type;
-#endif
 
    /* Flags provided when this binding was created */
    VkDescriptorBindingFlagsEXT flags;
@@ -2006,8 +2004,8 @@
    /* Number of bindings in this descriptor set */
    uint16_t binding_count;
 
-   /* Total size of the descriptor set with room for all array entries */
-   uint16_t size;
+   /* Total number of descriptors */
+   uint16_t descriptor_count;
 
    /* Shader stages affected by this descriptor set */
    uint16_t shader_stages;
@@ -2188,7 +2186,8 @@
 };
 
 size_t
-anv_descriptor_set_layout_size(const struct anv_descriptor_set_layout *layout);
+anv_descriptor_set_layout_size(const struct anv_descriptor_set_layout *layout,
+                               uint32_t var_desc_count);
 
 void
 anv_descriptor_set_write_image_view(struct anv_device *device,
@@ -2235,6 +2234,7 @@
 anv_descriptor_set_create(struct anv_device *device,
                           struct anv_descriptor_pool *pool,
                           struct anv_descriptor_set_layout *layout,
+                          uint32_t var_desc_count,
                           struct anv_descriptor_set **out_set);
 
 void
diff --git a/src/intel/vulkan/genX_cmd_buffer.c b/src/intel/vulkan/genX_cmd_buffer.c
index 985e3b9..fceb65f 100644
--- a/src/intel/vulkan/genX_cmd_buffer.c
+++ b/src/intel/vulkan/genX_cmd_buffer.c
@@ -2602,7 +2602,22 @@
          assert(binding->set < MAX_SETS);
          const struct anv_descriptor_set *set =
             pipe_state->descriptors[binding->set];
-         assert(binding->index < set->descriptor_count);
+         if (binding->index >= set->descriptor_count) {
+            /* From the Vulkan spec section entitled "DescriptorSet and
+             * Binding Assignment":
+             *
+             *    "If the array is runtime-sized, then array elements greater
+             *    than or equal to the size of that binding in the bound
+             *    descriptor set must not be used."
+             *
+             * Unfortunately, the compiler isn't smart enough to figure out
+             * when a dynamic binding isn't used so it may grab the whole
+             * array and stick it in the binding table.  In this case, it's
+             * safe to just skip those bindings that are OOB.
+             */
+            assert(binding->index < set->layout->descriptor_count);
+            continue;
+         }
          const struct anv_descriptor *desc = &set->descriptors[binding->index];
 
          switch (desc->type) {