/*
 * Copyright © 2022 Imagination Technologies Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <assert.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <vulkan/vulkan.h>

#include "hwdef/rogue_hw_utils.h"
#include "pvr_bo.h"
#include "pvr_private.h"
#include "util/compiler.h"
#include "util/list.h"
#include "util/log.h"
#include "util/macros.h"
#include "vk_alloc.h"
#include "vk_log.h"
#include "vk_object.h"
#include "vk_util.h"

#if defined(DEBUG)
static const struct {
   const char *raw;
   const char *primary;
   const char *secondary;
   const char *primary_dynamic;
   const char *secondary_dynamic;
} stage_names[] = {
   { "Vertex",
     "Vertex Primary",
     "Vertex Secondary",
     "Vertex Dynamic Primary",
     "Vertex Dynamic Secondary" },
   { "Fragment",
     "Fragment Primary",
     "Fragment Secondary",
     "Fragment Dynamic Primary",
     "Fragment Dynamic Secondary" },
   { "Compute",
     "Compute Primary",
     "Compute Secondary",
     "Compute Dynamic Primary",
     "Compute Dynamic Secondary" },
};

static const char *descriptor_names[] = { "VK SAMPLER",
                                          "VK COMBINED_IMAGE_SAMPLER",
                                          "VK SAMPLED_IMAGE",
                                          "VK STORAGE_IMAGE",
                                          "VK UNIFORM_TEXEL_BUFFER",
                                          "VK STORAGE_TEXEL_BUFFER",
                                          "VK UNIFORM_BUFFER",
                                          "VK STORAGE_BUFFER",
                                          "VK UNIFORM_BUFFER_DYNAMIC",
                                          "VK STORAGE_BUFFER_DYNAMIC",
                                          "VK INPUT_ATTACHMENT" };
#endif

static void pvr_descriptor_size_info_init(
   const struct pvr_device *device,
   VkDescriptorType type,
   struct pvr_descriptor_size_info *const size_info_out)
{
   /* UINT_MAX is a place holder. These values will be filled by calling the
    * init function, and set appropriately based on device features.
    */
   static const struct pvr_descriptor_size_info template_size_infos[] = {
      /* VK_DESCRIPTOR_TYPE_SAMPLER */
      { 4, 0, 4 },
      /* VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER */
      { 8, UINT_MAX, 4 },
      /* VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE */
      { 4, UINT_MAX, 4 },
      /* VK_DESCRIPTOR_TYPE_STORAGE_IMAGE */
      { 4, UINT_MAX, 4 },
      /* VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER */
      { 4, UINT_MAX, 4 },
      /* VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER */
      { 4, UINT_MAX, 4 },
      /* VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER */
      { 2, UINT_MAX, 2 },
      /* VK_DESCRIPTOR_TYPE_STORAGE_BUFFER */
      { 2, 1, 2 },
      /* VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC */
      { 2, UINT_MAX, 2 },
      /* VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC */
      { 2, 1, 2 },
      /* VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT */
      { 8, UINT_MAX, 4 }
   };

   *size_info_out = template_size_infos[type];

   switch (type) {
   case VK_DESCRIPTOR_TYPE_SAMPLER:
   case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
   case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
      break;

   case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
   case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
   case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
   case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
   case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
   case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: {
      const uint32_t image_secondary_offset_arraybase = 0;
      const uint32_t image_secondary_size_arraybase = 2;
      const uint32_t image_secondary_size_arraystride = 1;

      const uint32_t image_secondary_offset_arraystride =
         image_secondary_offset_arraybase + image_secondary_size_arraybase;

      const uint32_t image_secondary_offset_arraymaxindex =
         (PVR_HAS_FEATURE(&device->pdevice->dev_info, tpu_array_textures))
            ? 0
            : image_secondary_offset_arraystride +
                 image_secondary_size_arraystride;

      const uint32_t image_secondary_size_arraymaxindex = 1;

      const uint32_t image_secondary_size_width = 1;
      const uint32_t image_secondary_size_height = 1;
      const uint32_t image_secondary_size_depth = 1;

      const uint32_t image_secondary_offset_width =
         image_secondary_offset_arraymaxindex +
         image_secondary_size_arraymaxindex;
      const uint32_t image_secondary_offset_height =
         image_secondary_offset_width + image_secondary_size_width;
      const uint32_t image_secondary_offset_depth =
         image_secondary_offset_height + image_secondary_size_height;
      const uint32_t image_secondary_total_size =
         image_secondary_offset_depth + image_secondary_size_depth;

      size_info_out->secondary = image_secondary_total_size;
      break;
   }
   case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
   case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
      size_info_out->secondary = (uint32_t)device->features.robustBufferAccess;
      break;

   default:
      unreachable("Unknown descriptor type");
   }
}

static bool pvr_stage_matches_vk_flags(enum pvr_stage_allocation pvr_stage,
                                       VkShaderStageFlags flags)
{
   VkShaderStageFlags flags_per_stage;

   switch (pvr_stage) {
   case PVR_STAGE_ALLOCATION_VERTEX_GEOMETRY:
      flags_per_stage = VK_SHADER_STAGE_VERTEX_BIT |
                        VK_SHADER_STAGE_GEOMETRY_BIT;
      break;
   case PVR_STAGE_ALLOCATION_FRAGMENT:
      flags_per_stage = VK_SHADER_STAGE_FRAGMENT_BIT;
      break;
   case PVR_STAGE_ALLOCATION_COMPUTE:
      flags_per_stage = VK_SHADER_STAGE_COMPUTE_BIT;
      break;
   default:
      unreachable("Unrecognized allocation stage.");
   }

   return !!(flags_per_stage & flags);
}

/* If allocator == NULL, the internal one will be used. */
static struct pvr_descriptor_set_layout *
pvr_descriptor_set_layout_allocate(struct pvr_device *device,
                                   const VkAllocationCallbacks *allocator,
                                   uint32_t binding_count,
                                   uint32_t immutable_sampler_count,
                                   uint32_t supported_descriptors_count)
{
   struct pvr_descriptor_set_layout_binding *bindings;
   struct pvr_descriptor_set_layout *layout;
   __typeof__(layout->per_stage_descriptor_count) counts;
   const struct pvr_sampler **immutable_samplers;

   VK_MULTIALLOC(ma);
   vk_multialloc_add(&ma, &layout, __typeof__(*layout), 1);
   vk_multialloc_add(&ma, &bindings, __typeof__(*bindings), binding_count);
   vk_multialloc_add(&ma,
                     &immutable_samplers,
                     __typeof__(*immutable_samplers),
                     immutable_sampler_count);

   for (uint32_t stage = 0; stage < ARRAY_SIZE(counts); stage++) {
      vk_multialloc_add(&ma,
                        &counts[stage],
                        __typeof__(*counts[0]),
                        supported_descriptors_count);
   }

   /* pvr_CreateDescriptorSetLayout() relies on this being zero allocated. */
   if (!vk_multialloc_zalloc2(&ma,
                              &device->vk.alloc,
                              allocator,
                              VK_SYSTEM_ALLOCATION_SCOPE_OBJECT)) {
      return NULL;
   }

   layout->bindings = bindings;
   layout->immutable_samplers = immutable_samplers;

   memcpy(&layout->per_stage_descriptor_count, &counts, sizeof(counts));

   vk_object_base_init(&device->vk,
                       &layout->base,
                       VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT);

   return layout;
}

/* If allocator == NULL, the internal one will be used. */
static void
pvr_descriptor_set_layout_free(struct pvr_device *device,
                               const VkAllocationCallbacks *allocator,
                               struct pvr_descriptor_set_layout *layout)
{
   vk_object_base_finish(&layout->base);
   vk_free2(&device->vk.alloc, allocator, layout);
}

static int pvr_binding_compare(const void *a, const void *b)
{
   uint32_t binding_a = ((VkDescriptorSetLayoutBinding *)a)->binding;
   uint32_t binding_b = ((VkDescriptorSetLayoutBinding *)b)->binding;

   if (binding_a < binding_b)
      return -1;

   if (binding_a > binding_b)
      return 1;

   return 0;
}

/* If allocator == NULL, the internal one will be used. */
static VkDescriptorSetLayoutBinding *
pvr_create_sorted_bindings(struct pvr_device *device,
                           const VkAllocationCallbacks *allocator,
                           const VkDescriptorSetLayoutBinding *bindings,
                           uint32_t binding_count)
{
   VkDescriptorSetLayoutBinding *sorted_bindings =
      vk_alloc2(&device->vk.alloc,
                allocator,
                binding_count * sizeof(*sorted_bindings),
                8,
                VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
   if (!sorted_bindings)
      return NULL;

   memcpy(sorted_bindings, bindings, binding_count * sizeof(*sorted_bindings));

   qsort(sorted_bindings,
         binding_count,
         sizeof(*sorted_bindings),
         pvr_binding_compare);

   return sorted_bindings;
}

struct pvr_register_usage {
   uint32_t primary;
   uint32_t primary_dynamic;
   uint32_t secondary;
   uint32_t secondary_dynamic;
};

static void pvr_setup_in_memory_layout_sizes(
   struct pvr_descriptor_set_layout *layout,
   const struct pvr_register_usage reg_usage[PVR_STAGE_ALLOCATION_COUNT])
{
   for (uint32_t stage = 0;
        stage < ARRAY_SIZE(layout->memory_layout_in_dwords_per_stage);
        stage++) {
      layout->total_size_in_dwords = ALIGN_POT(layout->total_size_in_dwords, 4);

      layout->memory_layout_in_dwords_per_stage[stage].primary_offset =
         layout->total_size_in_dwords;
      layout->memory_layout_in_dwords_per_stage[stage].primary_size =
         reg_usage[stage].primary;

      layout->total_size_in_dwords += reg_usage[stage].primary;
      layout->total_size_in_dwords = ALIGN_POT(layout->total_size_in_dwords, 4);

      layout->memory_layout_in_dwords_per_stage[stage].secondary_offset =
         layout->total_size_in_dwords;
      layout->memory_layout_in_dwords_per_stage[stage].secondary_size =
         reg_usage[stage].secondary;

      layout->total_size_in_dwords += reg_usage[stage].secondary;

      layout->memory_layout_in_dwords_per_stage[stage].primary_dynamic_size =
         reg_usage[stage].primary_dynamic;
      layout->memory_layout_in_dwords_per_stage[stage].secondary_dynamic_size =
         reg_usage[stage].secondary_dynamic;
   }
}

#if defined(DEBUG)
static void
pvr_dump_in_memory_layout_sizes(const struct pvr_descriptor_set_layout *layout)
{
   mesa_logd("=== SET LAYOUT ===");
   mesa_logd("----------------------------------------------");
   mesa_logd(" in memory:");
   mesa_logd("----------------------------------------------");

   for (uint32_t stage = 0;
        stage < ARRAY_SIZE(layout->memory_layout_in_dwords_per_stage);
        stage++) {
      mesa_logd(
         "| %-18s @   %04u                |",
         stage_names[stage].primary,
         layout->memory_layout_in_dwords_per_stage[stage].primary_offset);
      mesa_logd("----------------------------------------------");

      /* Print primaries. */
      for (uint32_t i = 0; i < layout->binding_count; i++) {
         const struct pvr_descriptor_set_layout_binding *const binding =
            &layout->bindings[i];

         if (binding->type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC ||
             binding->type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)
            continue;

         mesa_logd("|   %s %04u | %-26s[%3u] |",
                   (binding->shader_stage_mask & (1U << stage)) ? " " : "X",
                   binding->per_stage_offset_in_dwords[stage].primary,
                   descriptor_names[binding->type],
                   binding->descriptor_count);
      }

      /* Print dynamic primaries. */
      for (uint32_t i = 0; i < layout->binding_count; i++) {
         const struct pvr_descriptor_set_layout_binding *const binding =
            &layout->bindings[i];

         if (binding->type != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC &&
             binding->type != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)
            continue;

         mesa_logd("| * %s %04u | %-26s[%3u] |",
                   (binding->shader_stage_mask & (1U << stage)) ? " " : "X",
                   binding->per_stage_offset_in_dwords[stage].primary,
                   descriptor_names[binding->type],
                   binding->descriptor_count);
      }

      mesa_logd("----------------------------------------------");
      mesa_logd(
         "| %-18s @   %04u                |",
         stage_names[stage].secondary,
         layout->memory_layout_in_dwords_per_stage[stage].secondary_offset);
      mesa_logd("----------------------------------------------");

      /* Print secondaries. */
      for (uint32_t i = 0; i < layout->binding_count; i++) {
         const struct pvr_descriptor_set_layout_binding *const binding =
            &layout->bindings[i];

         if (binding->type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC ||
             binding->type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)
            continue;

         mesa_logd("|   %s %04u | %-26s[%3u] |",
                   (binding->shader_stage_mask & (1U << stage)) ? " " : "X",
                   binding->per_stage_offset_in_dwords[stage].secondary,
                   descriptor_names[binding->type],
                   binding->descriptor_count);
      }

      /* Print dynamic secondaries. */
      for (uint32_t i = 0; i < layout->binding_count; i++) {
         const struct pvr_descriptor_set_layout_binding *const binding =
            &layout->bindings[i];

         if (binding->type != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC &&
             binding->type != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)
            continue;

         mesa_logd("| * %s %04u | %-26s[%3u] |",
                   (binding->shader_stage_mask & (1U << stage)) ? " " : "X",
                   binding->per_stage_offset_in_dwords[stage].secondary,
                   descriptor_names[binding->type],
                   binding->descriptor_count);
      }

      mesa_logd("==============================================");
   }
}
#endif

VkResult pvr_CreateDescriptorSetLayout(
   VkDevice _device,
   const VkDescriptorSetLayoutCreateInfo *pCreateInfo,
   const VkAllocationCallbacks *pAllocator,
   VkDescriptorSetLayout *pSetLayout)
{
   /* Used to accumulate sizes and set each descriptor's offsets per stage. */
   struct pvr_register_usage reg_usage[PVR_STAGE_ALLOCATION_COUNT] = { 0 };
   PVR_FROM_HANDLE(pvr_device, device, _device);
   struct pvr_descriptor_set_layout *layout;
   VkDescriptorSetLayoutBinding *bindings;
   uint32_t immutable_sampler_count;

   assert(pCreateInfo->sType ==
          VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO);

   vk_foreach_struct (ext, pCreateInfo->pNext) {
      pvr_debug_ignored_stype(ext->sType);
   }

   /* TODO: Add support for push descriptors. */

   if (pCreateInfo->bindingCount == 0) {
      layout = pvr_descriptor_set_layout_allocate(device, pAllocator, 0, 0, 0);
      if (!layout)
         return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);

      *pSetLayout = pvr_descriptor_set_layout_to_handle(layout);
      return VK_SUCCESS;
   }

   /* TODO: Instead of sorting, maybe do what anvil does? */
   bindings = pvr_create_sorted_bindings(device,
                                         pAllocator,
                                         pCreateInfo->pBindings,
                                         pCreateInfo->bindingCount);
   if (!bindings)
      return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);

   immutable_sampler_count = 0;
   for (uint32_t i = 0; i < pCreateInfo->bindingCount; i++) {
      /* From the Vulkan 1.1.97 spec for VkDescriptorSetLayoutBinding:
       *
       *    "If descriptorType specifies a VK_DESCRIPTOR_TYPE_SAMPLER or
       *    VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER type descriptor, then
       *    pImmutableSamplers can be used to initialize a set of immutable
       *    samplers. [...]  If descriptorType is not one of these descriptor
       *    types, then pImmutableSamplers is ignored.
       *
       * We need to be careful here and only parse pImmutableSamplers if we
       * have one of the right descriptor types.
       */
      const VkDescriptorType descriptor_type = bindings[i].descriptorType;
      if ((descriptor_type == VK_DESCRIPTOR_TYPE_SAMPLER ||
           descriptor_type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) &&
          bindings[i].pImmutableSamplers)
         immutable_sampler_count += bindings[i].descriptorCount;
   }

   /* From the Vulkan 1.2.190 spec for VkDescriptorSetLayoutCreateInfo:
    *
    *     "The VkDescriptorSetLayoutBinding::binding members of the elements
    *     of the pBindings array must each have different values."
    *
    * So we don't worry about duplicates and just allocate for bindingCount
    * amount of bindings.
    */
   layout = pvr_descriptor_set_layout_allocate(
      device,
      pAllocator,
      pCreateInfo->bindingCount,
      immutable_sampler_count,
      PVR_PIPELINE_LAYOUT_SUPPORTED_DESCRIPTOR_TYPE_COUNT);
   if (!layout) {
      vk_free2(&device->vk.alloc, pAllocator, bindings);
      return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
   }

   layout->binding_count = pCreateInfo->bindingCount;

   for (uint32_t bind_num = 0; bind_num < layout->binding_count; bind_num++) {
      const VkDescriptorSetLayoutBinding *const binding = &bindings[bind_num];
      struct pvr_descriptor_set_layout_binding *const internal_binding =
         &layout->bindings[bind_num];
      VkShaderStageFlags shader_stages = 0;

      internal_binding->type = binding->descriptorType;
      /* The binding_numbers can be non-contiguous so we ignore the user
       * specified binding numbers and make them contiguous ourselves.
       */
      internal_binding->binding_number = bind_num;

      /* From Vulkan spec 1.2.189:
       *
       *    "If descriptorCount is zero this binding entry is reserved and the
       *    resource must not be accessed from any stage via this binding"
       *
       * So do not use bindings->stageFlags, use shader_stages instead.
       */
      if (binding->descriptorCount) {
         shader_stages = binding->stageFlags;

         internal_binding->descriptor_count = binding->descriptorCount;
         internal_binding->descriptor_index = layout->descriptor_count;
         layout->descriptor_count += binding->descriptorCount;
      }

      switch (binding->descriptorType) {
      case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
      case VK_DESCRIPTOR_TYPE_SAMPLER:
         if (binding->pImmutableSamplers && binding->descriptorCount > 0) {
            internal_binding->has_immutable_samplers = true;
            internal_binding->immutable_samplers_index =
               layout->immutable_sampler_count;

            for (uint32_t j = 0; j < binding->descriptorCount; j++) {
               PVR_FROM_HANDLE(pvr_sampler,
                               sampler,
                               bindings->pImmutableSamplers[j]);
               const uint32_t next = j + layout->immutable_sampler_count;

               layout->immutable_samplers[next] = sampler;
            }

            layout->immutable_sampler_count += binding->descriptorCount;
         }
         break;

      case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
      case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
         layout->dynamic_buffer_count += binding->descriptorCount;
         break;

      case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
      case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
      case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
      case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
      case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
      case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
      case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
         break;

      default:
         unreachable("Unknown descriptor type");
         break;
      }

      if (!shader_stages)
         continue;

      internal_binding->shader_stages = shader_stages;
      layout->shader_stages |= shader_stages;

      for (uint32_t stage = 0;
           stage < ARRAY_SIZE(layout->bindings[0].per_stage_offset_in_dwords);
           stage++) {
         const VkDescriptorType descriptor_type = binding->descriptorType;

         if (!pvr_stage_matches_vk_flags(stage, shader_stages))
            continue;

         internal_binding->shader_stage_mask |= (1U << stage);

         /* TODO: Do we have to allocate them at the end? We could speed it
          * by allocating them here if not. */
         /* We allocate dynamics primary and secondaries at the end. */
         if (descriptor_type != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC &&
             descriptor_type != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) {
            struct pvr_descriptor_size_info size_info;

            pvr_descriptor_size_info_init(device, descriptor_type, &size_info);

            STATIC_ASSERT(
               ARRAY_SIZE(reg_usage) ==
               ARRAY_SIZE(layout->bindings[0].per_stage_offset_in_dwords));

            reg_usage[stage].primary =
               ALIGN_POT(reg_usage[stage].primary, size_info.alignment);

            internal_binding->per_stage_offset_in_dwords[stage].primary =
               reg_usage[stage].primary;
            reg_usage[stage].primary +=
               size_info.primary * internal_binding->descriptor_count;

            internal_binding->per_stage_offset_in_dwords[stage].secondary =
               reg_usage[stage].secondary;
            reg_usage[stage].secondary +=
               size_info.secondary * internal_binding->descriptor_count;
         }

         STATIC_ASSERT(
            ARRAY_SIZE(layout->per_stage_descriptor_count) ==
            ARRAY_SIZE(layout->bindings[0].per_stage_offset_in_dwords));

         layout->per_stage_descriptor_count[stage][descriptor_type] +=
            internal_binding->descriptor_count;
      }
   }

   for (uint32_t bind_num = 0; bind_num < layout->binding_count; bind_num++) {
      struct pvr_descriptor_set_layout_binding *const internal_binding =
         &layout->bindings[bind_num];
      const VkDescriptorType descriptor_type = internal_binding->type;

      if (descriptor_type != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC &&
          descriptor_type != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)
         continue;

      for (uint32_t stage = 0;
           stage < ARRAY_SIZE(layout->bindings[0].per_stage_offset_in_dwords);
           stage++) {
         struct pvr_descriptor_size_info size_info;
         const VkShaderStageFlags shader_stages =
            internal_binding->shader_stages;

         if (!pvr_stage_matches_vk_flags(stage, shader_stages))
            continue;

         pvr_descriptor_size_info_init(device, descriptor_type, &size_info);

         /* TODO: align primary like we did with other descriptors? */
         internal_binding->per_stage_offset_in_dwords[stage].primary =
            reg_usage[stage].primary_dynamic;
         reg_usage[stage].primary_dynamic +=
            size_info.primary * internal_binding->descriptor_count;

         internal_binding->per_stage_offset_in_dwords[stage].secondary =
            reg_usage[stage].secondary_dynamic;
         reg_usage[stage].secondary_dynamic +=
            size_info.secondary * internal_binding->descriptor_count;
      }
   }

   pvr_setup_in_memory_layout_sizes(layout, reg_usage);

#if defined(DEBUG)
   pvr_dump_in_memory_layout_sizes(layout);
#endif

   vk_free2(&device->vk.alloc, pAllocator, bindings);

   *pSetLayout = pvr_descriptor_set_layout_to_handle(layout);

   return VK_SUCCESS;
}

void pvr_DestroyDescriptorSetLayout(VkDevice _device,
                                    VkDescriptorSetLayout _set_layout,
                                    const VkAllocationCallbacks *pAllocator)
{
   PVR_FROM_HANDLE(pvr_descriptor_set_layout, layout, _set_layout);
   PVR_FROM_HANDLE(pvr_device, device, _device);

   pvr_descriptor_set_layout_free(device, pAllocator, layout);
}

#if defined(DEBUG)
static void
pvr_dump_in_register_layout_sizes(const struct pvr_device *device,
                                  const struct pvr_pipeline_layout *layout)
{
   mesa_logd("=== SET LAYOUT ===");
   mesa_logd("----------------------------------------------------");
   mesa_logd(" in registers:");
   mesa_logd("----------------------------------------------------");

   for (uint32_t stage = 0;
        stage < ARRAY_SIZE(layout->register_layout_in_dwords_per_stage);
        stage++) {
      uint32_t dynamic_offset = 0;

      mesa_logd("| %-48s |", stage_names[stage].primary_dynamic);
      mesa_logd("----------------------------------------------------");

      /* Print dynamic primaries. */
      for (uint32_t set_num = 0; set_num < layout->set_count; set_num++) {
         const struct pvr_descriptor_set_layout *const set_layout =
            layout->set_layout[set_num];

         for (uint32_t i = 0; i < set_layout->binding_count; i++) {
            const struct pvr_descriptor_set_layout_binding *const binding =
               &set_layout->bindings[i];
            bool valid = !!(binding->shader_stage_mask & (1U << stage));

            if (binding->type != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC &&
                binding->type != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)
               continue;

            mesa_logd("| %s %04u | %u:%03u | %-26s[%3u] |",
                      (valid) ? " " : "X",
                      dynamic_offset,
                      set_num,
                      i,
                      descriptor_names[binding->type],
                      binding->descriptor_count);

            if (valid) {
               struct pvr_descriptor_size_info size_info;

               pvr_descriptor_size_info_init(device, binding->type, &size_info);

               dynamic_offset += size_info.primary;
            }
         }
      }

      mesa_logd("----------------------------------------------------");
      mesa_logd("| %-48s |", stage_names[stage].secondary_dynamic);
      mesa_logd("----------------------------------------------------");

      /* Print dynamic secondaries. */
      for (uint32_t set_num = 0; set_num < layout->set_count; set_num++) {
         const struct pvr_descriptor_set_layout *const set_layout =
            layout->set_layout[set_num];

         for (uint32_t i = 0; i < set_layout->binding_count; i++) {
            const struct pvr_descriptor_set_layout_binding *const binding =
               &set_layout->bindings[i];
            bool valid = !!(binding->shader_stage_mask & (1U << stage));

            if (binding->type != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC &&
                binding->type != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)
               continue;

            mesa_logd("| %s %04u | %u:%03u | %-26s[%3u] |",
                      (valid) ? " " : "X",
                      dynamic_offset,
                      set_num,
                      i,
                      descriptor_names[binding->type],
                      binding->descriptor_count);

            if (valid) {
               struct pvr_descriptor_size_info size_info;

               pvr_descriptor_size_info_init(device, binding->type, &size_info);

               dynamic_offset += size_info.secondary;
            }
         }
      }

      mesa_logd("----------------------------------------------------");
      mesa_logd("| %-48s |", stage_names[stage].primary);
      mesa_logd("----------------------------------------------------");

      /* Print primaries. */
      for (uint32_t set_num = 0; set_num < layout->set_count; set_num++) {
         const struct pvr_descriptor_set_layout *const set_layout =
            layout->set_layout[set_num];
         const uint32_t base =
            layout->register_layout_in_dwords_per_stage[stage][set_num]
               .primary_offset;

         for (uint32_t i = 0; i < set_layout->binding_count; i++) {
            const struct pvr_descriptor_set_layout_binding *const binding =
               &set_layout->bindings[i];

            if (binding->type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC ||
                binding->type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)
               continue;

            mesa_logd("| %s %04u | %u:%03u | %-26s[%3u] |",
                      (binding->shader_stage_mask & (1U << stage)) ? " " : "X",
                      base + binding->per_stage_offset_in_dwords[stage].primary,
                      set_num,
                      i,
                      descriptor_names[binding->type],
                      binding->descriptor_count);
         }
      }

      mesa_logd("----------------------------------------------------");
      mesa_logd("| %-48s |", stage_names[stage].secondary);
      mesa_logd("----------------------------------------------------");

      /* Print secondaries. */
      for (uint32_t set_num = 0; set_num < layout->set_count; set_num++) {
         const struct pvr_descriptor_set_layout *const set_layout =
            layout->set_layout[set_num];
         const uint32_t base =
            layout->register_layout_in_dwords_per_stage[stage][set_num]
               .secondary_offset;

         for (uint32_t i = 0; i < set_layout->binding_count; i++) {
            const struct pvr_descriptor_set_layout_binding *const binding =
               &set_layout->bindings[i];

            if (binding->type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC ||
                binding->type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)
               continue;

            mesa_logd("| %s %04u | %u:%03u | %-26s[%3u] |",
                      (binding->shader_stage_mask & (1U << stage)) ? " " : "X",
                      base +
                         binding->per_stage_offset_in_dwords[stage].secondary,
                      set_num,
                      i,
                      descriptor_names[binding->type],
                      binding->descriptor_count);
         }
      }

      mesa_logd("====================================================");
   }
}
#endif

/* Pipeline layouts. These have nothing to do with the pipeline. They are
 * just multiple descriptor set layouts pasted together.
 */
VkResult pvr_CreatePipelineLayout(VkDevice _device,
                                  const VkPipelineLayoutCreateInfo *pCreateInfo,
                                  const VkAllocationCallbacks *pAllocator,
                                  VkPipelineLayout *pPipelineLayout)
{
   uint32_t next_free_reg[PVR_STAGE_ALLOCATION_COUNT];
   PVR_FROM_HANDLE(pvr_device, device, _device);
   struct pvr_pipeline_layout *layout;

   assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO);
   assert(pCreateInfo->setLayoutCount <= PVR_MAX_DESCRIPTOR_SETS);

   layout = vk_object_alloc(&device->vk,
                            pAllocator,
                            sizeof(*layout),
                            VK_OBJECT_TYPE_PIPELINE_LAYOUT);
   if (!layout)
      return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);

   layout->set_count = pCreateInfo->setLayoutCount;
   layout->shader_stages = 0;
   for (uint32_t stage = 0; stage < PVR_STAGE_ALLOCATION_COUNT; stage++) {
      uint32_t descriptor_counts
         [PVR_PIPELINE_LAYOUT_SUPPORTED_DESCRIPTOR_TYPE_COUNT] = { 0 };
      struct pvr_pipeline_layout_reg_info *const reg_info =
         &layout->per_stage_reg_info[stage];

      *reg_info = (struct pvr_pipeline_layout_reg_info){ 0 };

      layout->per_stage_descriptor_masks[stage] = 0;

      for (uint32_t set_num = 0; set_num < layout->set_count; set_num++) {
         /* So we don't write these again and again. Just do it once. */
         if (stage == 0) {
            PVR_FROM_HANDLE(pvr_descriptor_set_layout,
                            set_layout,
                            pCreateInfo->pSetLayouts[set_num]);

            layout->set_layout[set_num] = set_layout;
            layout->shader_stages |= set_layout->shader_stages;
         }

         const struct pvr_descriptor_set_layout_mem_layout *const mem_layout =
            &layout->set_layout[set_num]
                ->memory_layout_in_dwords_per_stage[stage];

         /* Allocate registers counts for dynamic descriptors. */
         reg_info->primary_dynamic_size_in_dwords +=
            mem_layout->primary_dynamic_size;
         reg_info->secondary_dynamic_size_in_dwords +=
            mem_layout->secondary_dynamic_size;

         for (VkDescriptorType type = 0;
              type < PVR_PIPELINE_LAYOUT_SUPPORTED_DESCRIPTOR_TYPE_COUNT;
              type++) {
            uint32_t descriptor_count;

            layout->descriptor_offsets[set_num][stage][type] =
               descriptor_counts[type];

            descriptor_count = layout->set_layout[set_num]
                                  ->per_stage_descriptor_count[stage][type];

            if (!descriptor_count)
               continue;

            switch (type) {
            case VK_DESCRIPTOR_TYPE_SAMPLER:
            case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
            case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
            case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
            case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
               layout->per_stage_descriptor_masks[stage] |= 1U << set_num;
               descriptor_counts[type] += descriptor_count;
               break;

            /* We don't need to keep track of the counts or masks for other
             * descriptor types so there is no assert() here since other
             * types are not invalid or unsupported.
             */
            /* TODO: Improve the comment above to specify why, when we find
             * out.
             */
            default:
               break;
            }
         }
      }

      next_free_reg[stage] = reg_info->primary_dynamic_size_in_dwords +
                             reg_info->secondary_dynamic_size_in_dwords;
   }

   /* Allocate registers counts for primary and secondary descriptors. */
   for (uint32_t stage = 0; stage < PVR_STAGE_ALLOCATION_COUNT; stage++) {
      for (uint32_t set_num = 0; set_num < layout->set_count; set_num++) {
         const struct pvr_descriptor_set_layout_mem_layout *const mem_layout =
            &layout->set_layout[set_num]
                ->memory_layout_in_dwords_per_stage[stage];
         struct pvr_descriptor_set_layout_mem_layout *const reg_layout =
            &layout->register_layout_in_dwords_per_stage[stage][set_num];

         next_free_reg[stage] = ALIGN_POT(next_free_reg[stage], 4);

         reg_layout->primary_offset = next_free_reg[stage];
         reg_layout->primary_size = mem_layout->primary_size;

         next_free_reg[stage] += reg_layout->primary_size;
      }

      /* To optimize the total shared layout allocation used by the shader,
       * secondary descriptors come last since they're less likely to be used.
       */
      for (uint32_t set_num = 0; set_num < layout->set_count; set_num++) {
         const struct pvr_descriptor_set_layout_mem_layout *const mem_layout =
            &layout->set_layout[set_num]
                ->memory_layout_in_dwords_per_stage[stage];
         struct pvr_descriptor_set_layout_mem_layout *const reg_layout =
            &layout->register_layout_in_dwords_per_stage[stage][set_num];

         /* Should we be aligning next_free_reg like it's done with the
          * primary descriptors?
          */

         reg_layout->secondary_offset = next_free_reg[stage];
         reg_layout->secondary_size = mem_layout->secondary_size;

         next_free_reg[stage] += reg_layout->secondary_size;
      }
   }

   layout->push_constants_shader_stages = 0;
   for (uint32_t i = 0; i < pCreateInfo->pushConstantRangeCount; ++i) {
      const VkPushConstantRange *range = &pCreateInfo->pPushConstantRanges[i];

      layout->push_constants_shader_stages |= range->stageFlags;
   }

#if defined(DEBUG)
   pvr_dump_in_register_layout_sizes(device, layout);
#endif

   *pPipelineLayout = pvr_pipeline_layout_to_handle(layout);

   return VK_SUCCESS;
}

void pvr_DestroyPipelineLayout(VkDevice _device,
                               VkPipelineLayout _pipelineLayout,
                               const VkAllocationCallbacks *pAllocator)
{
   PVR_FROM_HANDLE(pvr_device, device, _device);
   PVR_FROM_HANDLE(pvr_pipeline_layout, layout, _pipelineLayout);

   vk_object_free(&device->vk, pAllocator, layout);
}

VkResult pvr_CreateDescriptorPool(VkDevice _device,
                                  const VkDescriptorPoolCreateInfo *pCreateInfo,
                                  const VkAllocationCallbacks *pAllocator,
                                  VkDescriptorPool *pDescriptorPool)
{
   PVR_FROM_HANDLE(pvr_device, device, _device);
   struct pvr_descriptor_pool *pool;

   assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO);

   pool = vk_object_alloc(&device->vk,
                          pAllocator,
                          sizeof(*pool),
                          VK_OBJECT_TYPE_DESCRIPTOR_POOL);
   if (!pool)
      return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);

   if (pAllocator)
      pool->alloc = *pAllocator;
   else
      pool->alloc = device->vk.alloc;

   pool->max_sets = pCreateInfo->maxSets;
   list_inithead(&pool->descriptor_sets);

   pool->total_size_in_dwords = 0;
   for (uint32_t i = 0; i < pCreateInfo->poolSizeCount; i++) {
      struct pvr_descriptor_size_info size_info;
      const uint32_t descriptor_count =
         pCreateInfo->pPoolSizes[i].descriptorCount;

      pvr_descriptor_size_info_init(device,
                                    pCreateInfo->pPoolSizes[i].type,
                                    &size_info);

      const uint32_t secondary = ALIGN_POT(size_info.secondary, 4);
      const uint32_t primary = ALIGN_POT(size_info.primary, 4);

      pool->total_size_in_dwords += descriptor_count * (primary + secondary);
   }
   pool->total_size_in_dwords *= PVR_STAGE_ALLOCATION_COUNT;
   pool->current_size_in_dwords = 0;

   pvr_finishme("Entry tracker for allocations?");

   *pDescriptorPool = pvr_descriptor_pool_to_handle(pool);

   return VK_SUCCESS;
}

static void pvr_free_descriptor_set(struct pvr_device *device,
                                    struct pvr_descriptor_pool *pool,
                                    struct pvr_descriptor_set *set)
{
   list_del(&set->link);
   pvr_bo_free(device, set->pvr_bo);
   vk_object_free(&device->vk, &pool->alloc, set);
}

void pvr_DestroyDescriptorPool(VkDevice _device,
                               VkDescriptorPool _pool,
                               const VkAllocationCallbacks *pAllocator)
{
   PVR_FROM_HANDLE(pvr_device, device, _device);
   PVR_FROM_HANDLE(pvr_descriptor_pool, pool, _pool);

   if (!pool)
      return;

   list_for_each_entry_safe (struct pvr_descriptor_set,
                             set,
                             &pool->descriptor_sets,
                             link) {
      pvr_free_descriptor_set(device, pool, set);
   }

   vk_object_free(&device->vk, pAllocator, pool);
}

VkResult pvr_ResetDescriptorPool(VkDevice _device,
                                 VkDescriptorPool descriptorPool,
                                 VkDescriptorPoolResetFlags flags)
{
   assert(!"Unimplemented");
   return VK_SUCCESS;
}

static uint16_t pvr_get_descriptor_primary_offset(
   const struct pvr_device *device,
   const struct pvr_descriptor_set_layout *layout,
   const struct pvr_descriptor_set_layout_binding *binding,
   const uint32_t stage,
   const uint32_t desc_idx)
{
   struct pvr_descriptor_size_info size_info;
   uint32_t offset;

   assert(stage < ARRAY_SIZE(layout->memory_layout_in_dwords_per_stage));
   assert(desc_idx < binding->descriptor_count);

   pvr_descriptor_size_info_init(device, binding->type, &size_info);

   offset = layout->memory_layout_in_dwords_per_stage[stage].primary_offset;
   offset += binding->per_stage_offset_in_dwords[stage].primary;
   offset += (desc_idx * size_info.primary);

   /* Offset must be less than 16bits. */
   assert(offset < UINT16_MAX);

   return (uint16_t)offset;
}

static uint16_t pvr_get_descriptor_secondary_offset(
   const struct pvr_device *device,
   const struct pvr_descriptor_set_layout *layout,
   const struct pvr_descriptor_set_layout_binding *binding,
   const uint32_t stage,
   const uint32_t desc_idx)
{
   struct pvr_descriptor_size_info size_info;
   uint32_t offset;

   assert(stage < ARRAY_SIZE(layout->memory_layout_in_dwords_per_stage));
   assert(desc_idx < binding->descriptor_count);

   pvr_descriptor_size_info_init(device, binding->type, &size_info);

   offset = layout->memory_layout_in_dwords_per_stage[stage].secondary_offset;
   offset += binding->per_stage_offset_in_dwords[stage].secondary;
   offset += (desc_idx * size_info.secondary);

   /* Offset must be less than 16bits. */
   assert(offset < UINT16_MAX);

   return (uint16_t)offset;
}

static void pvr_write_sampler_descriptor(uint32_t *primary,
                                         const struct pvr_sampler *sampler)
{
   /* TODO: Implement based on WriteSamplerDescriptor. */
   pvr_finishme("Implement after vkCreateSampler API.");
}

#define PVR_MAX_DESCRIPTOR_MEM_SIZE_IN_DWORDS (4 * 1024)

static VkResult
pvr_descriptor_set_create(struct pvr_device *device,
                          struct pvr_descriptor_pool *pool,
                          const struct pvr_descriptor_set_layout *layout,
                          struct pvr_descriptor_set **const descriptor_set_out)
{
   struct pvr_descriptor_set *set;
   VkResult result;
   size_t size;
   void *map;

   size = sizeof(*set) + sizeof(set->descriptors[0]) * layout->descriptor_count;

   /* TODO: Add support to allocate descriptors from descriptor pool, also
    * check the required descriptors must not exceed max allowed descriptors.
    */
   set = vk_object_zalloc(&device->vk,
                          &pool->alloc,
                          size,
                          VK_OBJECT_TYPE_DESCRIPTOR_SET);
   if (!set)
      return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);

   /* TODO: Add support to allocate device memory from a common pool. Look at
    * something like anv. Also we can allocate a whole chunk of device memory
    * for max descriptors supported by pool as done by v3dv. Also check the
    * possibility if this can be removed from here and done on need basis.
    */
   if (layout->binding_count > 0) {
      const uint32_t cache_line_size =
         rogue_get_slc_cache_line_size(&device->pdevice->dev_info);
      uint64_t bo_size = MIN2(pool->total_size_in_dwords,
                              PVR_MAX_DESCRIPTOR_MEM_SIZE_IN_DWORDS) *
                         sizeof(uint32_t);

      result = pvr_bo_alloc(device,
                            device->heaps.general_heap,
                            bo_size,
                            cache_line_size,
                            PVR_BO_ALLOC_FLAG_CPU_MAPPED,
                            &set->pvr_bo);
      if (result != VK_SUCCESS)
         goto err_free_descriptor_set;
   }

   set->layout = layout;
   set->pool = pool;

   map = set->pvr_bo->bo->map;
   for (uint32_t i = 0; i < layout->binding_count; i++) {
      const struct pvr_descriptor_set_layout_binding *binding =
         &layout->bindings[i];

      if (binding->descriptor_count == 0 || !binding->has_immutable_samplers)
         continue;

      for (uint32_t stage = 0;
           stage < ARRAY_SIZE(binding->per_stage_offset_in_dwords);
           stage++) {
         if (!(binding->shader_stage_mask & (1U << stage)))
            continue;

         for (uint32_t j = 0; j < binding->descriptor_count; j++) {
            uint32_t idx = binding->immutable_samplers_index + j;
            const struct pvr_sampler *sampler = layout->immutable_samplers[idx];
            unsigned int offset_in_dwords =
               pvr_get_descriptor_primary_offset(device,
                                                 layout,
                                                 binding,
                                                 stage,
                                                 j);

            if (binding->type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
               offset_in_dwords += 4;

            pvr_write_sampler_descriptor(map +
                                            offset_in_dwords * sizeof(uint32_t),
                                         sampler);
         }
      }
   }

   list_addtail(&set->link, &pool->descriptor_sets);

   *descriptor_set_out = set;

   return VK_SUCCESS;

err_free_descriptor_set:
   vk_object_free(&device->vk, &pool->alloc, set);

   return result;
}

VkResult
pvr_AllocateDescriptorSets(VkDevice _device,
                           const VkDescriptorSetAllocateInfo *pAllocateInfo,
                           VkDescriptorSet *pDescriptorSets)
{
   PVR_FROM_HANDLE(pvr_descriptor_pool, pool, pAllocateInfo->descriptorPool);
   PVR_FROM_HANDLE(pvr_device, device, _device);
   VkResult result;
   uint32_t i;

   vk_foreach_struct (ext, pAllocateInfo->pNext) {
      pvr_debug_ignored_stype(ext->sType);
   }

   for (i = 0; i < pAllocateInfo->descriptorSetCount; i++) {
      PVR_FROM_HANDLE(pvr_descriptor_set_layout,
                      layout,
                      pAllocateInfo->pSetLayouts[i]);
      struct pvr_descriptor_set *set = NULL;

      result = pvr_descriptor_set_create(device, pool, layout, &set);
      if (result != VK_SUCCESS)
         goto err_free_descriptor_sets;

      pDescriptorSets[i] = pvr_descriptor_set_to_handle(set);
   }

   return VK_SUCCESS;

err_free_descriptor_sets:
   pvr_FreeDescriptorSets(_device,
                          pAllocateInfo->descriptorPool,
                          i,
                          pDescriptorSets);

   for (i = 0; i < pAllocateInfo->descriptorSetCount; i++)
      pDescriptorSets[i] = VK_NULL_HANDLE;

   return result;
}

VkResult pvr_FreeDescriptorSets(VkDevice _device,
                                VkDescriptorPool descriptorPool,
                                uint32_t count,
                                const VkDescriptorSet *pDescriptorSets)
{
   PVR_FROM_HANDLE(pvr_descriptor_pool, pool, descriptorPool);
   PVR_FROM_HANDLE(pvr_device, device, _device);

   for (uint32_t i = 0; i < count; i++) {
      struct pvr_descriptor_set *set;

      if (!pDescriptorSets[i])
         continue;

      set = pvr_descriptor_set_from_handle(pDescriptorSets[i]);
      pvr_free_descriptor_set(device, pool, set);
   }

   return VK_SUCCESS;
}

static int pvr_compare_layout_binding(const void *a, const void *b)
{
   uint32_t binding_a;
   uint32_t binding_b;

   binding_a = ((struct pvr_descriptor_set_layout_binding *)a)->binding_number;
   binding_b = ((struct pvr_descriptor_set_layout_binding *)b)->binding_number;

   if (binding_a < binding_b)
      return -1;

   if (binding_a > binding_b)
      return 1;

   return 0;
}

/* This function does not assume that the binding will always exist for a
 * particular binding_num. Caller should check before using the return pointer.
 */
static struct pvr_descriptor_set_layout_binding *
pvr_get_descriptor_binding(const struct pvr_descriptor_set_layout *layout,
                           const uint32_t binding_num)
{
   struct pvr_descriptor_set_layout_binding binding;
   binding.binding_number = binding_num;

   return bsearch(&binding,
                  layout->bindings,
                  layout->binding_count,
                  sizeof(binding),
                  pvr_compare_layout_binding);
}

static void
pvr_descriptor_update_buffer_info(const struct pvr_device *device,
                                  const VkWriteDescriptorSet *write_set,
                                  struct pvr_descriptor_set *set,
                                  uint32_t *mem_ptr,
                                  uint32_t start_stage,
                                  uint32_t end_stage)
{
   const struct pvr_descriptor_set_layout_binding *binding;
   struct pvr_descriptor_size_info size_info;
   bool is_dynamic;

   binding = pvr_get_descriptor_binding(set->layout, write_set->dstBinding);
   /* Binding should not be NULL. */
   assert(binding);

   is_dynamic = (binding->type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) ||
                (binding->type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC);

   pvr_descriptor_size_info_init(device, binding->type, &size_info);

   /* Only need to update the buffer if it is actually being used. If it was
    * not present in any stage, then the shader_stage_mask would be 0 and we
    * can skip this update.
    */
   if (binding->shader_stage_mask == 0)
      return;

   for (uint32_t i = 0; i < write_set->descriptorCount; i++) {
      const VkDescriptorBufferInfo *buffer_info = &write_set->pBufferInfo[i];
      PVR_FROM_HANDLE(pvr_buffer, buffer, buffer_info->buffer);
      const uint32_t desc_idx =
         binding->descriptor_index + write_set->dstArrayElement + i;
      uint64_t addr = buffer->dev_addr.addr + buffer_info->offset;
      uint32_t range = (buffer_info->range == VK_WHOLE_SIZE)
                          ? (buffer->size - buffer_info->offset)
                          : (buffer_info->range);

      set->descriptors[desc_idx].type = write_set->descriptorType;
      set->descriptors[desc_idx].buffer_dev_addr.addr = addr;
      set->descriptors[desc_idx].buffer_create_info_size = buffer->size;
      set->descriptors[desc_idx].buffer_desc_range = range;

      if (is_dynamic)
         continue;

      /* Update the entries in the descriptor memory for static buffer. */
      for (uint32_t j = start_stage; j < end_stage; j++) {
         uint32_t primary_offset;
         uint32_t secondary_offset;

         if (!(binding->shader_stage_mask & (1U << j)))
            continue;

         /* Offset calculation functions expect descriptor_index to be
          * binding relative not layout relative, so we have used
          * write_set->dstArrayElement + i rather than desc_idx.
          */
         primary_offset =
            pvr_get_descriptor_primary_offset(device,
                                              set->layout,
                                              binding,
                                              j,
                                              write_set->dstArrayElement + i);
         secondary_offset =
            pvr_get_descriptor_secondary_offset(device,
                                                set->layout,
                                                binding,
                                                j,
                                                write_set->dstArrayElement + i);

         memcpy(mem_ptr + primary_offset, &addr, size_info.primary << 2);
         memcpy(mem_ptr + secondary_offset, &range, size_info.secondary << 2);
      }
   }
}

void pvr_UpdateDescriptorSets(VkDevice _device,
                              uint32_t descriptorWriteCount,
                              const VkWriteDescriptorSet *pDescriptorWrites,
                              uint32_t descriptorCopyCount,
                              const VkCopyDescriptorSet *pDescriptorCopies)
{
   PVR_FROM_HANDLE(pvr_device, device, _device);

   for (uint32_t i = 0; i < descriptorWriteCount; i++) {
      const VkWriteDescriptorSet *write_set = &pDescriptorWrites[i];
      PVR_FROM_HANDLE(pvr_descriptor_set, set, write_set->dstSet);
      uint32_t *map = set->pvr_bo->bo->map;

      vk_foreach_struct (ext, write_set->pNext) {
         pvr_debug_ignored_stype(ext->sType);
      }

      switch (write_set->descriptorType) {
      case VK_DESCRIPTOR_TYPE_SAMPLER:
      case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
      case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
      case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
      case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
      case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
      case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
         pvr_finishme("Update support missing for %d descriptor type\n",
                      write_set->descriptorType);
         break;

      case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
      case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
      case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
      case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
         pvr_descriptor_update_buffer_info(device,
                                           write_set,
                                           set,
                                           map,
                                           0,
                                           PVR_STAGE_ALLOCATION_COUNT);
         break;

      default:
         unreachable("Unknown descriptor type");
         break;
      }
   }

   if (descriptorCopyCount > 0)
      pvr_finishme("Descriptor copying support missing\n");
}
