v3dv/descriptor_set: added support for samplers

This include SAMPLER, COMBINED_IMAGE_SAMPLER and SAMPLED_IMAGE
descriptors.

In order to support them we do the pre-packing of TEXTURE_SHADER_STATE
and SAMPLER_STATE when Images and Samplers (respectively) are
created. Those packets doesn't need to be tweaked later, so we upload
them to an bo.

A possible improvement of this would be that the descriptor pool
manages a bo for all descriptors, that suballocate for each descriptor
allocated. This is what other drivers do (and as far as I understand,
one of the reasons of having a descriptor pool).

Immutable samplers are not supported, will be handled on a following
patch.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6766>
diff --git a/src/broadcom/vulkan/v3dv_descriptor_set.c b/src/broadcom/vulkan/v3dv_descriptor_set.c
index e20de48..2c8d2c9 100644
--- a/src/broadcom/vulkan/v3dv_descriptor_set.c
+++ b/src/broadcom/vulkan/v3dv_descriptor_set.c
@@ -108,12 +108,15 @@
       if (pCreateInfo->pPoolSizes[i].type != VK_DESCRIPTOR_TYPE_SAMPLER)
          descriptor_count += pCreateInfo->pPoolSizes[i].descriptorCount;
 
+      /* Verify supported descriptor type */
       switch(pCreateInfo->pPoolSizes[i].type) {
       case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
       case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
-         break;
       case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
       case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
+      case VK_DESCRIPTOR_TYPE_SAMPLER:
+      case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
+      case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
          break;
       default:
          unreachable("Unimplemented descriptor type");
@@ -255,10 +258,19 @@
    assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO);
 
    uint32_t max_binding = 0;
+   uint32_t immutable_sampler_count = 0;
    for (uint32_t j = 0; j < pCreateInfo->bindingCount; j++) {
       max_binding = MAX2(max_binding, pCreateInfo->pBindings[j].binding);
+      if ((pCreateInfo->pBindings[j].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ||
+           pCreateInfo->pBindings[j].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER) &&
+           pCreateInfo->pBindings[j].pImmutableSamplers) {
+         immutable_sampler_count += pCreateInfo->pBindings[j].descriptorCount;
+      }
    }
 
+   /* FIXME: immutable samplers not supported yet */
+   assert(immutable_sampler_count == 0);
+
    uint32_t size = sizeof(struct v3dv_descriptor_set_layout) +
       (max_binding + 1) * sizeof(set_layout->binding[0]);
 
@@ -283,6 +295,7 @@
    set_layout->binding_count = max_binding + 1;
    set_layout->flags = pCreateInfo->flags;
    set_layout->shader_stages = 0;
+   set_layout->has_immutable_samplers = false;
 
    uint32_t descriptor_count = 0;
    uint32_t dynamic_offset_count = 0;
@@ -299,6 +312,11 @@
       case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
          set_layout->binding[binding_number].dynamic_offset_count = 1;
          break;
+      case VK_DESCRIPTOR_TYPE_SAMPLER:
+      case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
+      case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+         /* Nothing here, just to keep the descriptor type filtering below */
+         break;
       default:
          unreachable("Unknown descriptor type\n");
          break;
@@ -375,6 +393,9 @@
 
    set->layout = layout;
 
+   /* FIXME: if we have immutable samplers those are tightly included here */
+   assert(layout->has_immutable_samplers == false);
+
    if (!pool->host_memory_base && pool->entry_count == pool->max_entry_count) {
       vk_free2(&device->alloc, NULL, set);
       return vk_error(device->instance, VK_ERROR_OUT_OF_POOL_MEMORY);
@@ -461,7 +482,9 @@
 
       descriptor += binding_layout->descriptor_index;
       descriptor += writeset->dstArrayElement;
+
       for (uint32_t j = 0; j < writeset->descriptorCount; ++j) {
+         descriptor->type = writeset->descriptorType;
 
          switch(writeset->descriptorType) {
 
@@ -472,10 +495,34 @@
             const VkDescriptorBufferInfo *buffer_info = writeset->pBufferInfo + j;
             V3DV_FROM_HANDLE(v3dv_buffer, buffer, buffer_info->buffer);
 
-            descriptor->bo = buffer->mem->bo;
+            descriptor->buffer = buffer;
             descriptor->offset = buffer_info->offset;
             break;
          }
+         case VK_DESCRIPTOR_TYPE_SAMPLER: {
+            const VkDescriptorImageInfo *image_info = writeset->pImageInfo + j;
+            V3DV_FROM_HANDLE(v3dv_sampler, sampler, image_info->sampler);
+
+            descriptor->sampler = sampler;
+            break;
+         }
+         case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: {
+            const VkDescriptorImageInfo *image_info = writeset->pImageInfo + j;
+            V3DV_FROM_HANDLE(v3dv_image_view, iview, image_info->imageView);
+
+            descriptor->image_view = iview;
+            break;
+         }
+         case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: {
+            const VkDescriptorImageInfo *image_info = writeset->pImageInfo + j;
+            V3DV_FROM_HANDLE(v3dv_image_view, iview, image_info->imageView);
+            V3DV_FROM_HANDLE(v3dv_sampler, sampler, image_info->sampler);
+
+            descriptor->image_view = iview;
+            descriptor->sampler = sampler;
+
+            break;
+         }
          default:
             unreachable("unimplemented descriptor type");
             break;
diff --git a/src/broadcom/vulkan/v3dv_device.c b/src/broadcom/vulkan/v3dv_device.c
index 52519ca..0374f70 100644
--- a/src/broadcom/vulkan/v3dv_device.c
+++ b/src/broadcom/vulkan/v3dv_device.c
@@ -555,7 +555,7 @@
       .largePoints = false,
       .alphaToOne = false,
       .multiViewport = false,
-      .samplerAnisotropy = false,
+      .samplerAnisotropy = true,
       .textureCompressionETC2 = true,
       .textureCompressionASTC_LDR = false,
       .textureCompressionBC = false,
@@ -1877,3 +1877,138 @@
    *((uint32_t *) event->bo->map) = 0;
    return VK_SUCCESS;
 }
+
+static const enum V3DX(Wrap_Mode) vk_to_v3d_wrap_mode[] = {
+   [VK_SAMPLER_ADDRESS_MODE_REPEAT]          = V3D_WRAP_MODE_REPEAT,
+   [VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT] = V3D_WRAP_MODE_MIRROR,
+   [VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE]   = V3D_WRAP_MODE_CLAMP,
+   [VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE] = V3D_WRAP_MODE_MIRROR_ONCE,
+   [VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER] = V3D_WRAP_MODE_BORDER,
+};
+
+static const enum V3DX(Compare_Function)
+vk_to_v3d_compare_func[] = {
+   [VK_COMPARE_OP_NEVER]                        = V3D_COMPARE_FUNC_NEVER,
+   [VK_COMPARE_OP_LESS]                         = V3D_COMPARE_FUNC_LESS,
+   [VK_COMPARE_OP_EQUAL]                        = V3D_COMPARE_FUNC_EQUAL,
+   [VK_COMPARE_OP_LESS_OR_EQUAL]                = V3D_COMPARE_FUNC_LEQUAL,
+   [VK_COMPARE_OP_GREATER]                      = V3D_COMPARE_FUNC_GREATER,
+   [VK_COMPARE_OP_NOT_EQUAL]                    = V3D_COMPARE_FUNC_NOTEQUAL,
+   [VK_COMPARE_OP_GREATER_OR_EQUAL]             = V3D_COMPARE_FUNC_GEQUAL,
+   [VK_COMPARE_OP_ALWAYS]                       = V3D_COMPARE_FUNC_ALWAYS,
+};
+
+static void
+pack_sampler_state(struct v3dv_sampler *sampler,
+                   const VkSamplerCreateInfo *pCreateInfo)
+{
+   enum V3DX(Border_Color_Mode) border_color_mode;
+
+   /* FIXME: direct border_color_mode mapping would work with some specific
+    * formats, but some others it would be needed to use
+    * V3D_BORDER_COLOR_FOLLOWS, and fill up
+    * SAMPLER_STATE.border_color_word_[0/1/2/3]
+    */
+   switch (pCreateInfo->borderColor) {
+   case VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK:
+   case VK_BORDER_COLOR_INT_TRANSPARENT_BLACK:
+      border_color_mode = V3D_BORDER_COLOR_0000;
+      break;
+   case VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK:
+   case VK_BORDER_COLOR_INT_OPAQUE_BLACK:
+      border_color_mode = V3D_BORDER_COLOR_0001;
+      break;
+   case VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE:
+   case VK_BORDER_COLOR_INT_OPAQUE_WHITE:
+      border_color_mode = V3D_BORDER_COLOR_1111;
+      break;
+   default:
+      unreachable("Unknown border color");
+      break;
+   }
+
+   v3dv_pack(sampler->state->map, SAMPLER_STATE, s) {
+      if (pCreateInfo->anisotropyEnable) {
+         s.anisotropy_enable = true;
+         if (pCreateInfo->maxAnisotropy > 8)
+            s.maximum_anisotropy = 3;
+         else if (pCreateInfo->maxAnisotropy > 4)
+            s.maximum_anisotropy = 2;
+         else if (pCreateInfo->maxAnisotropy > 2)
+            s.maximum_anisotropy = 1;
+      }
+
+      s.border_color_mode = border_color_mode;
+
+      s.wrap_i_border = false; /* Also hardcoded on v3d */
+      s.wrap_s = vk_to_v3d_wrap_mode[pCreateInfo->addressModeU];
+      s.wrap_t = vk_to_v3d_wrap_mode[pCreateInfo->addressModeV];
+      s.wrap_r = vk_to_v3d_wrap_mode[pCreateInfo->addressModeW];
+      s.fixed_bias = pCreateInfo->mipLodBias;
+      s.max_level_of_detail = MIN2(MAX2(0, pCreateInfo->maxLod), 15);
+      s.min_level_of_detail = MIN2(MAX2(0, pCreateInfo->minLod), 15);
+      s.srgb_disable = 0; /* Not even set by v3d */
+      s.depth_compare_function =
+         vk_to_v3d_compare_func[pCreateInfo->compareEnable ?
+                                pCreateInfo->compareOp : VK_COMPARE_OP_NEVER];
+      s.mip_filter_nearest = pCreateInfo->mipmapMode == VK_SAMPLER_MIPMAP_MODE_NEAREST;
+      s.min_filter_nearest = pCreateInfo->minFilter == VK_FILTER_NEAREST;
+      s.mag_filter_nearest = pCreateInfo->magFilter == VK_FILTER_NEAREST;
+   }
+}
+
+VkResult
+v3dv_CreateSampler(VkDevice _device,
+                 const VkSamplerCreateInfo *pCreateInfo,
+                 const VkAllocationCallbacks *pAllocator,
+                 VkSampler *pSampler)
+{
+   V3DV_FROM_HANDLE(v3dv_device, device, _device);
+   struct v3dv_sampler *sampler;
+
+   assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO);
+
+   sampler = vk_zalloc2(&device->alloc, pAllocator, sizeof(*sampler), 8,
+                        VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+   if (!sampler)
+      return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
+
+   if (sampler->state == NULL) {
+      sampler->state = v3dv_bo_alloc(device, cl_packet_length(SAMPLER_STATE),
+                                     "sampler_state");
+
+      if (!sampler->state) {
+         fprintf(stderr, "Failed to allocate memory for sampler state\n");
+         abort();
+      }
+
+      bool ok = v3dv_bo_map(device, sampler->state,
+                            cl_packet_length(SAMPLER_STATE));
+      if (!ok) {
+         fprintf(stderr, "failed to map sampler state buffer\n");
+         abort();
+      }
+   }
+
+   pack_sampler_state(sampler, pCreateInfo);
+
+   *pSampler = v3dv_sampler_to_handle(sampler);
+
+   return VK_SUCCESS;
+}
+
+void
+v3dv_DestroySampler(VkDevice _device,
+                  VkSampler _sampler,
+                  const VkAllocationCallbacks *pAllocator)
+{
+   V3DV_FROM_HANDLE(v3dv_device, device, _device);
+   V3DV_FROM_HANDLE(v3dv_sampler, sampler, _sampler);
+
+   if (!sampler)
+      return;
+
+   vk_free2(&device->alloc, pAllocator, sampler);
+}
+
+
diff --git a/src/broadcom/vulkan/v3dv_image.c b/src/broadcom/vulkan/v3dv_image.c
index e76452e..7a53b2a 100644
--- a/src/broadcom/vulkan/v3dv_image.c
+++ b/src/broadcom/vulkan/v3dv_image.c
@@ -395,6 +395,144 @@
    vk_free2(&device->alloc, pAllocator, image);
 }
 
+/*
+ * This method translates pipe_swizzle to the swizzle values used at the
+ * packet TEXTURE_SHADER_STATE
+ *
+ * FIXME: C&P from v3d, common place?
+ */
+static uint32_t
+translate_swizzle(unsigned char pipe_swizzle)
+{
+   switch (pipe_swizzle) {
+   case PIPE_SWIZZLE_0:
+      return 0;
+   case PIPE_SWIZZLE_1:
+      return 1;
+   case PIPE_SWIZZLE_X:
+   case PIPE_SWIZZLE_Y:
+   case PIPE_SWIZZLE_Z:
+   case PIPE_SWIZZLE_W:
+      return 2 + pipe_swizzle;
+   default:
+      unreachable("unknown swizzle");
+   }
+}
+
+static void
+pack_texture_shader_state(struct v3dv_device *device,
+                          struct v3dv_image_view *image_view)
+{
+   assert(image_view->image);
+   const struct v3dv_image *image = image_view->image;
+
+   if (image_view->texture_shader_state == NULL) {
+      image_view->texture_shader_state =
+         v3dv_bo_alloc(device, cl_packet_length(TEXTURE_SHADER_STATE),
+                       "texture_shader_state");
+
+      if (!image_view->texture_shader_state) {
+         fprintf(stderr, "Failed to allocate memory for texture shader state\n");
+         abort();
+      }
+
+      bool ok = v3dv_bo_map(device, image_view->texture_shader_state,
+                            cl_packet_length(TEXTURE_SHADER_STATE));
+      if (!ok) {
+         fprintf(stderr, "failed to map texture shader state\n");
+         abort();
+      }
+   }
+
+   int msaa_scale = 1; /* FIXME: hardcoded. Revisit when msaa get supported */
+   v3dv_pack(image_view->texture_shader_state->map, TEXTURE_SHADER_STATE, tex) {
+
+      tex.level_0_is_strictly_uif =
+         (image->slices[0].tiling == VC5_TILING_UIF_XOR ||
+          image->slices[0].tiling == VC5_TILING_UIF_NO_XOR);
+
+      tex.level_0_xor_enable = (image->slices[0].tiling == VC5_TILING_UIF_XOR);
+
+      if (tex.level_0_is_strictly_uif)
+         tex.level_0_ub_pad = image->slices[0].ub_pad;
+
+      /* FIXME: v3d never sets uif_xor_disable, but uses it on the following
+       * check so let's set the default value
+       */
+      tex.uif_xor_disable = false;
+      if (tex.uif_xor_disable ||
+          tex.level_0_is_strictly_uif) {
+         tex.extended = true;
+      }
+
+      tex.base_level = image_view->base_level;
+      tex.max_level = image_view->max_level;
+
+      tex.swizzle_r = translate_swizzle(image_view->swizzle[0]);
+      tex.swizzle_g = translate_swizzle(image_view->swizzle[1]);
+      tex.swizzle_b = translate_swizzle(image_view->swizzle[2]);
+      tex.swizzle_a = translate_swizzle(image_view->swizzle[3]);
+
+      tex.texture_type = image_view->format->tex_type;
+
+      if (image->type == VK_IMAGE_TYPE_3D) {
+         tex.image_depth = image->extent.depth;
+      } else {
+         tex.image_depth = (image_view->last_layer - image_view->first_layer) + 1;
+      }
+      tex.image_height = image->extent.height * msaa_scale;
+      tex.image_width = image->extent.width * msaa_scale;
+
+      /* On 4.x, the height of a 1D texture is redefined to be the
+       * upper 14 bits of the width (which is only usable with txf).
+       */
+      if (image->type == VK_IMAGE_TYPE_1D) {
+         tex.image_height = tex.image_width >> 14;
+      }
+      tex.image_width &= (1 << 14) - 1;
+      tex.image_height &= (1 << 14) - 1;
+
+      tex.array_stride_64_byte_aligned = image->cube_map_stride / 64;
+
+      tex.srgb = vk_format_is_srgb(image_view->vk_format);
+
+      /* At this point we don't have the job. That's the reason the first
+       * parameter is NULL, to avoid a crash when cl_pack_emit_reloc tries to
+       * add the bo to the job. This also means that we need to add manually
+       * the image bo to the job using the texture.
+       */
+      const uint32_t base_offset =
+         image->mem->bo->offset +
+         v3dv_layer_offset(image, 0, image_view->first_layer);
+      tex.texture_base_pointer = v3dv_cl_address(NULL, base_offset);
+   }
+}
+
+static enum pipe_swizzle
+vk_component_mapping_to_pipe_swizzle(VkComponentSwizzle comp,
+                                     VkComponentSwizzle swz)
+{
+   if (swz == VK_COMPONENT_SWIZZLE_IDENTITY)
+      swz = comp;
+
+   switch (swz) {
+   case VK_COMPONENT_SWIZZLE_ZERO:
+      return PIPE_SWIZZLE_0;
+   case VK_COMPONENT_SWIZZLE_ONE:
+      return PIPE_SWIZZLE_1;
+   case VK_COMPONENT_SWIZZLE_R:
+      return PIPE_SWIZZLE_X;
+   case VK_COMPONENT_SWIZZLE_G:
+      return PIPE_SWIZZLE_Y;
+   case VK_COMPONENT_SWIZZLE_B:
+      return PIPE_SWIZZLE_Z;
+   case VK_COMPONENT_SWIZZLE_A:
+      return PIPE_SWIZZLE_W;
+   default:
+      unreachable("Unknown VkComponentSwizzle");
+   };
+}
+
 VkResult
 v3dv_CreateImageView(VkDevice _device,
                      const VkImageViewCreateInfo *pCreateInfo,
@@ -435,6 +573,7 @@
    iview->aspects = range->aspectMask;
 
    iview->base_level = range->baseMipLevel;
+   iview->max_level = iview->base_level + v3dv_level_count(image, range) - 1;
    iview->extent = (VkExtent3D) {
       .width  = u_minify(image->extent.width , iview->base_level),
       .height = u_minify(image->extent.height, iview->base_level),
@@ -476,6 +615,28 @@
                                                    &iview->internal_type,
                                                    &iview->internal_bpp);
    }
+
+   /* FIXME: we are doing this vk to pipe swizzle mapping just to call
+    * util_format_compose_swizzles. Would be good to check if it would be
+    * better to reimplement the latter using vk component
+    */
+   uint8_t image_view_swizzle[4] = {
+      vk_component_mapping_to_pipe_swizzle(VK_COMPONENT_SWIZZLE_R,
+                                           pCreateInfo->components.r),
+      vk_component_mapping_to_pipe_swizzle(VK_COMPONENT_SWIZZLE_G,
+                                           pCreateInfo->components.g),
+      vk_component_mapping_to_pipe_swizzle(VK_COMPONENT_SWIZZLE_B,
+                                           pCreateInfo->components.b),
+      vk_component_mapping_to_pipe_swizzle(VK_COMPONENT_SWIZZLE_A,
+                                           pCreateInfo->components.a),
+   };
+   const uint8_t *format_swizzle =
+      v3dv_get_format_swizzle(iview->vk_format);
+
+   util_format_compose_swizzles(format_swizzle, image_view_swizzle, iview->swizzle);
+
+   pack_texture_shader_state(device, iview);
+
    *pView = v3dv_image_view_to_handle(iview);
 
    return VK_SUCCESS;
diff --git a/src/broadcom/vulkan/v3dv_private.h b/src/broadcom/vulkan/v3dv_private.h
index c3d3d52..61254d5 100644
--- a/src/broadcom/vulkan/v3dv_private.h
+++ b/src/broadcom/vulkan/v3dv_private.h
@@ -349,9 +349,27 @@
    uint32_t internal_type;
 
    uint32_t base_level;
+   uint32_t max_level;
    uint32_t first_layer;
    uint32_t last_layer;
    uint32_t offset;
+
+   /* Precomputed (composed from createinfo->components and formar swizzle)
+    * swizzles to pass in to the shader key.
+    *
+    * FIXME: this is also a candidate to be included on the descriptor info.
+    */
+   uint8_t swizzle[4];
+
+   /* FIXME: here we store the packet TEXTURE_SHADER_STATE, that is referenced
+    * as part of the tmu configuration, and the content is set per sampler. A
+    * possible perf improvement, to avoid bo fragmentation, would be to save
+    * the state as static, have the bo as part of the descriptor (booked from
+    * the descriptor pools), and then copy this content to the descriptor bo
+    * on UpdateDescriptor. This also makes sense because not all the images
+    * are used as textures.
+    */
+   struct v3dv_bo *texture_shader_state;
 };
 
 uint32_t v3dv_layer_offset(const struct v3dv_image *image, uint32_t level, uint32_t layer);
@@ -668,8 +686,19 @@
 };
 
 struct v3dv_descriptor {
-   struct v3dv_bo *bo;
-   uint32_t offset;
+   VkDescriptorType type;
+
+   union {
+      struct {
+         struct v3dv_image_view *image_view;
+         struct v3dv_sampler *sampler;
+      };
+
+      struct {
+         struct v3dv_buffer *buffer;
+         uint32_t offset;
+      };
+   };
 };
 
 /* Aux struct as it is really common to have a pair bo/address. Called
@@ -858,6 +887,9 @@
    /* Number of bindings in this descriptor set */
    uint32_t binding_count;
 
+   /* Total size of the descriptor set with room for all array entries */
+   uint32_t size;
+
    /* Shader stages affected by this descriptor set */
    uint16_t shader_stages;
 
@@ -867,6 +899,8 @@
    /* Number of dynamic offsets used by this descriptor set */
    uint16_t dynamic_offset_count;
 
+   bool has_immutable_samplers;
+
    /* Bindings in this descriptor set */
    struct v3dv_descriptor_set_binding_layout binding[0];
 };
@@ -892,6 +926,17 @@
    int array_size[64];
 };
 
+struct v3dv_sampler {
+   /* FIXME: here we store the packet SAMPLER_STATE, that is referenced as part
+    * of the tmu configuration, and the content is set per sampler. A possible
+    * perf improvement, to avoid bo fragmentation, would be to save the state
+    * as static, have the bo as part of the descriptor (booked from the
+    * descriptor pools), and then copy this content to the descriptor bo on
+    * UpdateDescriptor
+    */
+   struct v3dv_bo *state;
+};
+
 struct v3dv_pipeline {
    struct v3dv_device *device;
 
@@ -1137,6 +1182,7 @@
 V3DV_DEFINE_NONDISP_HANDLE_CASTS(v3dv_pipeline, VkPipeline)
 V3DV_DEFINE_NONDISP_HANDLE_CASTS(v3dv_pipeline_layout, VkPipelineLayout)
 V3DV_DEFINE_NONDISP_HANDLE_CASTS(v3dv_render_pass, VkRenderPass)
+V3DV_DEFINE_NONDISP_HANDLE_CASTS(v3dv_sampler, VkSampler)
 V3DV_DEFINE_NONDISP_HANDLE_CASTS(v3dv_semaphore, VkSemaphore)
 V3DV_DEFINE_NONDISP_HANDLE_CASTS(v3dv_shader_module, VkShaderModule)
 
@@ -1147,6 +1193,10 @@
    ((_range)->layerCount == VK_REMAINING_ARRAY_LAYERS ? \
     (_image)->array_size - (_range)->baseArrayLayer : (_range)->layerCount)
 
+#define v3dv_level_count(_image, _range) \
+   ((_range)->levelCount == VK_REMAINING_MIP_LEVELS ? \
+    (_image)->levels - (_range)->baseMipLevel : (_range)->levelCount)
+
 static inline int
 v3dv_ioctl(int fd, unsigned long request, void *arg)
 {
diff --git a/src/broadcom/vulkan/v3dv_uniforms.c b/src/broadcom/vulkan/v3dv_uniforms.c
index 5372476..c3bf969 100644
--- a/src/broadcom/vulkan/v3dv_uniforms.c
+++ b/src/broadcom/vulkan/v3dv_uniforms.c
@@ -186,10 +186,11 @@
                         pipeline->layout,
                         index, &dynamic_offset);
       assert(descriptor);
-      assert(descriptor->bo);
+      assert(descriptor->buffer);
 
       cl_aligned_reloc(&job->indirect, uniforms,
-                       descriptor->bo,
+                       descriptor->buffer->mem->bo,
+                       descriptor->buffer->mem_offset +
                        descriptor->offset + offset + dynamic_offset);
    }
 }