Mesa (main): v3dv: inject a custom passthrough geometry shader for multiview pipelines

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Tue Jul 27 07:45:58 UTC 2021


Module: Mesa
Branch: main
Commit: 5872c7ca7ba79f5e5bf8824ee474b30fa9c12948
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=5872c7ca7ba79f5e5bf8824ee474b30fa9c12948

Author: Iago Toral Quiroga <itoral at igalia.com>
Date:   Fri Jul 23 12:42:59 2021 +0200

v3dv: inject a custom passthrough geometry shader for multiview pipelines

This allows us to use layered rendering to broadcast draw calls to the
appropriate views (layers).

Reviewed-by: Alejandro Piñeiro <apinheiro at igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/12034>

---

 src/broadcom/vulkan/v3dv_pipeline.c | 178 +++++++++++++++++++++++++++++++++++-
 src/broadcom/vulkan/v3dv_private.h  |   1 +
 2 files changed, 177 insertions(+), 2 deletions(-)

diff --git a/src/broadcom/vulkan/v3dv_pipeline.c b/src/broadcom/vulkan/v3dv_pipeline.c
index 15f87fbcc59..370a4f9ccd6 100644
--- a/src/broadcom/vulkan/v3dv_pipeline.c
+++ b/src/broadcom/vulkan/v3dv_pipeline.c
@@ -34,6 +34,7 @@
 #include "nir/nir_serialize.h"
 
 #include "util/u_atomic.h"
+#include "util/u_prim.h"
 
 #include "vulkan/util/vk_format.h"
 
@@ -2060,6 +2061,8 @@ pipeline_populate_graphics_key(struct v3dv_pipeline *pipeline,
          key->va_swap_rb_mask |= 1 << (VERT_ATTRIB_GENERIC0 + desc->location);
    }
 
+   assert(pipeline->subpass);
+   key->has_multiview = pipeline->subpass->view_mask != 0;
 }
 
 static void
@@ -2107,8 +2110,11 @@ v3dv_pipeline_shared_data_new_empty(const unsigned char sha1_key[20],
          continue;
       }
 
-      if (stage == BROADCOM_SHADER_GEOMETRY && !pipeline->gs)
-         continue;
+      if (stage == BROADCOM_SHADER_GEOMETRY && !pipeline->gs) {
+         /* We always inject a custom GS if we have multiview */
+         if (!pipeline->subpass->view_mask)
+            continue;
+      }
 
       struct v3dv_descriptor_maps *new_maps =
          vk_zalloc2(&pipeline->device->vk.alloc, NULL,
@@ -2145,6 +2151,165 @@ fail:
    return NULL;
 }
 
+static uint32_t
+multiview_gs_input_primitive_from_pipeline(struct v3dv_pipeline *pipeline)
+{
+   switch (pipeline->topology) {
+   case PIPE_PRIM_POINTS:
+      return GL_POINTS;
+   case PIPE_PRIM_LINES:
+   case PIPE_PRIM_LINE_STRIP:
+      return GL_LINES;
+   case PIPE_PRIM_TRIANGLES:
+   case PIPE_PRIM_TRIANGLE_STRIP:
+   case PIPE_PRIM_TRIANGLE_FAN:
+      return GL_TRIANGLES;
+   default:
+      /* Since we don't allow GS with multiview, we can only see non-adjacency
+       * primitives.
+       */
+      unreachable("Unexpected pipeline primitive type");
+   }
+}
+
+static uint32_t
+multiview_gs_output_primitive_from_pipeline(struct v3dv_pipeline *pipeline)
+{
+   switch (pipeline->topology) {
+   case PIPE_PRIM_POINTS:
+      return GL_POINTS;
+   case PIPE_PRIM_LINES:
+   case PIPE_PRIM_LINE_STRIP:
+      return GL_LINE_STRIP;
+   case PIPE_PRIM_TRIANGLES:
+   case PIPE_PRIM_TRIANGLE_STRIP:
+   case PIPE_PRIM_TRIANGLE_FAN:
+      return GL_TRIANGLE_STRIP;
+   default:
+      /* Since we don't allow GS with multiview, we can only see non-adjacency
+       * primitives.
+       */
+      unreachable("Unexpected pipeline primitive type");
+   }
+}
+
+static bool
+pipeline_add_multiview_gs(struct v3dv_pipeline *pipeline,
+                          struct v3dv_pipeline_cache *cache,
+                          const VkAllocationCallbacks *pAllocator)
+{
+   /* Create the passthrough GS from the VS output interface */
+   pipeline->vs->nir = pipeline_stage_get_nir(pipeline->vs, pipeline, cache);
+   nir_shader *vs_nir = pipeline->vs->nir;
+
+   const nir_shader_compiler_options *options = v3dv_pipeline_get_nir_options();
+   nir_builder b = nir_builder_init_simple_shader(MESA_SHADER_GEOMETRY, options,
+                                                  "multiview broadcast gs");
+   nir_shader *nir = b.shader;
+   nir->info.inputs_read = vs_nir->info.outputs_written;
+   nir->info.outputs_written = vs_nir->info.outputs_written |
+                               (1ull << VARYING_SLOT_LAYER);
+
+   uint32_t vertex_count = u_vertices_per_prim(pipeline->topology);
+   nir->info.gs.input_primitive =
+      multiview_gs_input_primitive_from_pipeline(pipeline);
+   nir->info.gs.output_primitive =
+      multiview_gs_output_primitive_from_pipeline(pipeline);
+   nir->info.gs.vertices_in = vertex_count;
+   nir->info.gs.vertices_out = nir->info.gs.vertices_in;
+   nir->info.gs.invocations = 1;
+   nir->info.gs.active_stream_mask = 0x1;
+
+   /* Make a list of GS input/output variables from the VS outputs */
+   nir_variable *in_vars[100];
+   nir_variable *out_vars[100];
+   uint32_t var_count = 0;
+   nir_foreach_shader_out_variable(out_vs_var, vs_nir) {
+      char name[8];
+      snprintf(name, ARRAY_SIZE(name), "in_%d", var_count);
+
+      in_vars[var_count] =
+         nir_variable_create(nir, nir_var_shader_in,
+                             glsl_array_type(out_vs_var->type, vertex_count, 0),
+                             name);
+      in_vars[var_count]->data.location = out_vs_var->data.location;
+      in_vars[var_count]->data.location_frac = out_vs_var->data.location_frac;
+      in_vars[var_count]->data.interpolation = out_vs_var->data.interpolation;
+
+      snprintf(name, ARRAY_SIZE(name), "out_%d", var_count);
+      out_vars[var_count] =
+         nir_variable_create(nir, nir_var_shader_out, out_vs_var->type, name);
+      out_vars[var_count]->data.location = out_vs_var->data.location;
+      out_vars[var_count]->data.interpolation = out_vs_var->data.interpolation;
+
+      var_count++;
+   }
+
+   /* Add the gl_Layer output variable */
+   nir_variable *out_layer =
+      nir_variable_create(nir, nir_var_shader_out, glsl_int_type(),
+                          "out_Layer");
+   out_layer->data.location = VARYING_SLOT_LAYER;
+
+   /* Get the view index value that we will write to gl_Layer */
+   nir_ssa_def *layer =
+      nir_load_system_value(&b, nir_intrinsic_load_view_index, 0, 1, 32);
+
+   /* Emit all output vertices */
+   for (uint32_t vi = 0; vi < vertex_count; vi++) {
+      /* Emit all output varyings */
+      for (uint32_t i = 0; i < var_count; i++) {
+         nir_deref_instr *in_value =
+            nir_build_deref_array_imm(&b, nir_build_deref_var(&b, in_vars[i]), vi);
+         nir_copy_deref(&b, nir_build_deref_var(&b, out_vars[i]), in_value);
+      }
+
+      /* Emit gl_Layer write */
+      nir_store_var(&b, out_layer, layer, 0x1);
+
+      nir_emit_vertex(&b, 0);
+   }
+   nir_end_primitive(&b, 0);
+
+   /* Make sure we run our pre-process NIR passes so we produce NIR compatible
+    * with what we expect from SPIR-V modules.
+    */
+   preprocess_nir(nir);
+
+   /* Attach the geometry shader to the  pipeline */
+   struct v3dv_device *device = pipeline->device;
+   struct v3dv_physical_device *physical_device =
+      &device->instance->physicalDevice;
+
+   struct v3dv_pipeline_stage *p_stage =
+      vk_zalloc2(&device->vk.alloc, pAllocator, sizeof(*p_stage), 8,
+                 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+
+   if (p_stage == NULL) {
+      ralloc_free(nir);
+      return false;
+   }
+
+   p_stage->pipeline = pipeline;
+   p_stage->stage = BROADCOM_SHADER_GEOMETRY;
+   p_stage->entrypoint = "main";
+   p_stage->module = 0;
+   p_stage->nir = nir;
+   pipeline_compute_sha1_from_nir(p_stage->nir, p_stage->shader_sha1);
+   p_stage->program_id = p_atomic_inc_return(&physical_device->next_program_id);
+
+   pipeline->has_gs = true;
+   pipeline->gs = p_stage;
+   pipeline->active_stages |= MESA_SHADER_GEOMETRY;
+
+   pipeline->gs_bin =
+      pipeline_stage_create_binning(pipeline->gs, pAllocator);
+      if (pipeline->gs_bin == NULL)
+         return false;
+
+   return true;
+}
+
 /*
  * It compiles a pipeline. Note that it also allocate internal object, but if
  * some allocations success, but other fails, the method is not freeing the
@@ -2260,6 +2425,15 @@ pipeline_compile_graphics(struct v3dv_pipeline *pipeline,
       pipeline->active_stages |= MESA_SHADER_FRAGMENT;
    }
 
+   /* If multiview is enabled, we inject a custom passthrough geometry shader
+    * to broadcast draw calls to the appropriate views.
+    */
+   assert(!pipeline->subpass->view_mask || (!pipeline->has_gs && !pipeline->gs));
+   if (pipeline->subpass->view_mask) {
+      if (!pipeline_add_multiview_gs(pipeline, cache, pAllocator))
+         return VK_ERROR_OUT_OF_HOST_MEMORY;
+   }
+
    /* First we try to get the variants from the pipeline cache */
    struct v3dv_pipeline_key pipeline_key;
    pipeline_populate_graphics_key(pipeline, &pipeline_key, pCreateInfo);
diff --git a/src/broadcom/vulkan/v3dv_private.h b/src/broadcom/vulkan/v3dv_private.h
index c6fa66b134b..5d2549a6319 100644
--- a/src/broadcom/vulkan/v3dv_private.h
+++ b/src/broadcom/vulkan/v3dv_private.h
@@ -274,6 +274,7 @@ struct v3dv_pipeline_key {
    } color_fmt[V3D_MAX_DRAW_BUFFERS];
    uint8_t f32_color_rb;
    uint32_t va_swap_rb_mask;
+   bool has_multiview;
 };
 
 struct v3dv_pipeline_cache_stats {



More information about the mesa-commit mailing list