v3dv/pipeline: lower_image_deref

This is really similar to the existing lower_tex_src_to_offset, but
for now we prefer to keep them independent, just in case we start to
found specific image use-cases as we advance fixing CTS tests.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6766>
diff --git a/src/broadcom/vulkan/v3dv_pipeline.c b/src/broadcom/vulkan/v3dv_pipeline.c
index 704759a..aeac572 100644
--- a/src/broadcom/vulkan/v3dv_pipeline.c
+++ b/src/broadcom/vulkan/v3dv_pipeline.c
@@ -728,6 +728,76 @@
    return true;
 }
 
+/* FIXME: really similar to lower_tex_src_to_offset, perhaps refactor? */
+static void
+lower_image_deref(nir_builder *b,
+                  nir_intrinsic_instr *instr,
+                  struct v3dv_pipeline *pipeline,
+                  const struct v3dv_pipeline_layout *layout)
+{
+   nir_deref_instr *deref = nir_src_as_deref(instr->src[0]);
+   nir_ssa_def *index = NULL;
+   unsigned array_elements = 1;
+   unsigned base_index = 0;
+
+   while (deref->deref_type != nir_deref_type_var) {
+      assert(deref->parent.is_ssa);
+      nir_deref_instr *parent =
+         nir_instr_as_deref(deref->parent.ssa->parent_instr);
+
+      assert(deref->deref_type == nir_deref_type_array);
+
+      if (nir_src_is_const(deref->arr.index) && index == NULL) {
+         /* We're still building a direct index */
+         base_index += nir_src_as_uint(deref->arr.index) * array_elements;
+      } else {
+         if (index == NULL) {
+            /* We used to be direct but not anymore */
+            index = nir_imm_int(b, base_index);
+            base_index = 0;
+         }
+
+         index = nir_iadd(b, index,
+                          nir_imul(b, nir_imm_int(b, array_elements),
+                                   nir_ssa_for_src(b, deref->arr.index, 1)));
+      }
+
+      array_elements *= glsl_get_length(parent->type);
+
+      deref = parent;
+   }
+
+   if (index)
+      index = nir_umin(b, index, nir_imm_int(b, array_elements - 1));
+
+   uint32_t set = deref->var->data.descriptor_set;
+   uint32_t binding = deref->var->data.binding;
+   struct v3dv_descriptor_set_layout *set_layout = layout->set[set].layout;
+   struct v3dv_descriptor_set_binding_layout *binding_layout =
+      &set_layout->binding[binding];
+
+   uint32_t array_index = deref->var->data.index + base_index;
+
+   assert(binding_layout->type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
+
+   int desc_index =
+      descriptor_map_add(&pipeline->texture_map,
+                         deref->var->data.descriptor_set,
+                         deref->var->data.binding,
+                         array_index,
+                         binding_layout->array_size);
+
+   /* We still need to get a combined_index, as we are integrating images with
+    * the rest of the texture/sampler support
+    */
+   int combined_index =
+      get_combined_index(pipeline, desc_index, V3DV_NO_SAMPLER_IDX);
+
+   index = nir_imm_int(b, combined_index);
+
+   nir_rewrite_image_intrinsic(instr, index, false);
+}
+
 static bool
 lower_intrinsic(nir_builder *b, nir_intrinsic_instr *instr,
                 struct v3dv_pipeline *pipeline,
@@ -752,6 +822,23 @@
       lower_vulkan_resource_index(b, instr, pipeline, layout);
       return true;
 
+   case nir_intrinsic_image_deref_load:
+   case nir_intrinsic_image_deref_store:
+   case nir_intrinsic_image_deref_atomic_add:
+   case nir_intrinsic_image_deref_atomic_imin:
+   case nir_intrinsic_image_deref_atomic_umin:
+   case nir_intrinsic_image_deref_atomic_imax:
+   case nir_intrinsic_image_deref_atomic_umax:
+   case nir_intrinsic_image_deref_atomic_and:
+   case nir_intrinsic_image_deref_atomic_or:
+   case nir_intrinsic_image_deref_atomic_xor:
+   case nir_intrinsic_image_deref_atomic_exchange:
+   case nir_intrinsic_image_deref_atomic_comp_swap:
+   case nir_intrinsic_image_deref_size:
+   case nir_intrinsic_image_deref_samples:
+      lower_image_deref(b, instr, pipeline, layout);
+      return true;
+
    default:
       return false;
    }