v3dv: add v3dv_write_uniforms

Initial port of the equivalent v3d_write_uniforms, to be used by the
cmd_buffer when emitting the drawing packets.

Initially doesn't include all the quniform types, only those needed by
the initial basic vulkan tests.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6766>
diff --git a/src/broadcom/vulkan/meson.build b/src/broadcom/vulkan/meson.build
index 3aab825..39de45c 100644
--- a/src/broadcom/vulkan/meson.build
+++ b/src/broadcom/vulkan/meson.build
@@ -66,6 +66,7 @@
   'v3dv_pipeline.c',
   'v3dv_private.h',
   'v3dv_queue.c',
+  'v3dv_uniforms.c',
   'v3dv_util.c',
   'v3d_tiling.c',
 )
diff --git a/src/broadcom/vulkan/v3dv_cl.h b/src/broadcom/vulkan/v3dv_cl.h
index d2fcc09..f58b2d5 100644
--- a/src/broadcom/vulkan/v3dv_cl.h
+++ b/src/broadcom/vulkan/v3dv_cl.h
@@ -106,6 +106,19 @@
    (*cl) = (struct v3dv_cl_out *)((char *)(*cl) + n);
 }
 
+static inline void
+cl_aligned_u32(struct v3dv_cl_out **cl, uint32_t n)
+{
+   *(uint32_t *)(*cl) = n;
+   cl_advance(cl, 4);
+}
+
+static inline void
+cl_aligned_f(struct v3dv_cl_out **cl, float f)
+{
+   cl_aligned_u32(cl, fui(f));
+}
+
 uint32_t v3dv_cl_ensure_space(struct v3dv_cl *cl, uint32_t space, uint32_t alignment);
 void v3dv_cl_ensure_space_with_branch(struct v3dv_cl *cl, uint32_t space);
 
diff --git a/src/broadcom/vulkan/v3dv_cmd_buffer.c b/src/broadcom/vulkan/v3dv_cmd_buffer.c
index 8930d28..ca302db 100644
--- a/src/broadcom/vulkan/v3dv_cmd_buffer.c
+++ b/src/broadcom/vulkan/v3dv_cmd_buffer.c
@@ -1239,12 +1239,14 @@
    assert(pipeline);
 
    /* Upload the uniforms to the indirect CL first */
+   struct v3dv_cl_reloc fs_uniforms =
+      v3dv_write_uniforms(cmd_buffer, pipeline->fs);
 
-   /* FIXME: uniforms not supported yet */
+   struct v3dv_cl_reloc vs_uniforms =
+      v3dv_write_uniforms(cmd_buffer, pipeline->vs);
 
-   struct v3dv_cl_reloc vs_uniforms = { NULL, 0 };
-   struct v3dv_cl_reloc vs_bin_uniforms = { NULL, 0 };
-   struct v3dv_cl_reloc fs_uniforms = { NULL, 0 };
+   struct v3dv_cl_reloc vs_bin_uniforms =
+      v3dv_write_uniforms(cmd_buffer, pipeline->vs_bin);
 
    /* Update the cache dirty flag based on the shader progs data */
    state->tmu_dirty_rcl |= pipeline->vs_bin->prog_data.vs->base.tmu_dirty_rcl;
diff --git a/src/broadcom/vulkan/v3dv_private.h b/src/broadcom/vulkan/v3dv_private.h
index d9a199a..d9aeac3 100644
--- a/src/broadcom/vulkan/v3dv_private.h
+++ b/src/broadcom/vulkan/v3dv_private.h
@@ -629,6 +629,9 @@
                            int cpp, uint32_t image_h,
                            const struct pipe_box *box);
 
+struct v3dv_cl_reloc v3dv_write_uniforms(struct v3dv_cmd_buffer *cmd_buffer,
+                                         struct v3dv_pipeline_stage *p_stage);
+
 #define V3DV_DEFINE_HANDLE_CASTS(__v3dv_type, __VkType)   \
                                                         \
    static inline struct __v3dv_type *                    \
diff --git a/src/broadcom/vulkan/v3dv_uniforms.c b/src/broadcom/vulkan/v3dv_uniforms.c
new file mode 100644
index 0000000..109dbe4
--- /dev/null
+++ b/src/broadcom/vulkan/v3dv_uniforms.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2019 Raspberry Pi
+ *
+ * Based in part on v3d driver which is:
+ *
+ * Copyright © 2014-2017 Broadcom
+ *
+ * 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 "v3dv_private.h"
+
+struct v3dv_cl_reloc
+v3dv_write_uniforms(struct v3dv_cmd_buffer *cmd_buffer,
+                    struct v3dv_pipeline_stage *p_stage)
+{
+   struct v3d_uniform_list *uinfo = &p_stage->prog_data.base->uniforms;
+   struct v3dv_dynamic_state *dynamic = &cmd_buffer->state.dynamic;
+
+   /* The hardware always pre-fetches the next uniform (also when there
+    * aren't any), so we always allocate space for an extra slot. This
+    * fixes MMU exceptions reported since Linux kernel 5.4 when the
+    * uniforms fill up the tail bytes of a page in the indirect
+    * BO. In that scenario, when the hardware pre-fetches after reading
+    * the last uniform it will read beyond the end of the page and trigger
+    * the MMU exception.
+    */
+   v3dv_cl_ensure_space(&cmd_buffer->indirect, (uinfo->count + 1) * 4, 4);
+
+   struct v3dv_cl_reloc uniform_stream =
+      v3dv_cl_get_address(&cmd_buffer->indirect);
+
+   struct v3dv_cl_out *uniforms =
+      cl_start(&cmd_buffer->indirect);
+
+   for (int i = 0; i < uinfo->count; i++) {
+      uint32_t data = uinfo->data[i];
+
+      switch (uinfo->contents[i]) {
+      case QUNIFORM_CONSTANT:
+         cl_aligned_u32(&uniforms, data);
+         break;
+
+      case QUNIFORM_VIEWPORT_X_SCALE:
+         cl_aligned_f(&uniforms, dynamic->viewport.scale[0][0] * 256.0f);
+         break;
+
+      case QUNIFORM_VIEWPORT_Y_SCALE:
+         cl_aligned_f(&uniforms, dynamic->viewport.scale[0][1] * 256.0f);
+         break;
+
+      case QUNIFORM_VIEWPORT_Z_OFFSET:
+         cl_aligned_f(&uniforms, dynamic->viewport.translate[0][2]);
+         break;
+
+      case QUNIFORM_VIEWPORT_Z_SCALE:
+         cl_aligned_f(&uniforms, dynamic->viewport.scale[0][2]);
+         break;
+
+      default:
+         unreachable("unsupported quniform_contents uniform type\n");
+      }
+   }
+
+   cl_end(&cmd_buffer->indirect, uniforms);
+
+   return uniform_stream;
+}