Mesa (main): nir/linker: support uniform when optimizing varying

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Wed Oct 13 05:12:05 UTC 2021


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

Author: Qiang Yu <yuq825 at gmail.com>
Date:   Mon Aug 30 15:51:50 2021 +0800

nir/linker: support uniform when optimizing varying

Varying assigned from uniform won't change after interpolation,
so move uniform load to fragment shader to eliminate the varying.

Acked-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer at amd.com>
Acked-by: Marek Olšák <marek.olsak at amd.com>
Signed-off-by: Qiang Yu <yuq825 at gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/12613>

---

 src/compiler/nir/nir_linking_helpers.c | 162 ++++++++++++++++++++++++++++++++-
 1 file changed, 158 insertions(+), 4 deletions(-)

diff --git a/src/compiler/nir/nir_linking_helpers.c b/src/compiler/nir/nir_linking_helpers.c
index 129ac48312c..b32fee2ced3 100644
--- a/src/compiler/nir/nir_linking_helpers.c
+++ b/src/compiler/nir/nir_linking_helpers.c
@@ -1100,6 +1100,156 @@ replace_duplicate_input(nir_shader *shader, nir_variable *input_var,
    return progress;
 }
 
+static bool
+is_direct_uniform_load(nir_ssa_def *def, nir_ssa_scalar *s)
+{
+   /* def is sure to be scalar as can_replace_varying() filter out vector case. */
+   assert(def->num_components == 1);
+
+   /* Uniform load may hide behind some move instruction for converting
+    * vector to scalar:
+    *
+    *     vec1 32 ssa_1 = deref_var &color (uniform vec3)
+    *     vec3 32 ssa_2 = intrinsic load_deref (ssa_1) (0)
+    *     vec1 32 ssa_3 = mov ssa_2.x
+    *     vec1 32 ssa_4 = deref_var &color_out (shader_out float)
+    *     intrinsic store_deref (ssa_4, ssa_3) (1, 0)
+    */
+   *s = nir_ssa_scalar_resolved(def, 0);
+
+   nir_ssa_def *ssa = s->def;
+   if (ssa->parent_instr->type != nir_instr_type_intrinsic)
+      return false;
+
+   nir_intrinsic_instr *intr = nir_instr_as_intrinsic(ssa->parent_instr);
+   if (intr->intrinsic != nir_intrinsic_load_deref)
+      return false;
+
+   nir_deref_instr *deref = nir_src_as_deref(intr->src[0]);
+   /* TODO: support nir_var_mem_ubo. */
+   if (!nir_deref_mode_is(deref, nir_var_uniform))
+      return false;
+
+   /* Does not support indirect uniform load. */
+   return !nir_deref_instr_has_indirect(deref);
+}
+
+static nir_variable *
+get_uniform_var_in_consumer(nir_shader *consumer,
+                            nir_variable *var_in_producer)
+{
+   /* Find if uniform already exists in consumer. */
+   nir_variable *new_var = NULL;
+   nir_foreach_uniform_variable(v, consumer) {
+      if (!strcmp(var_in_producer->name, v->name)) {
+         new_var = v;
+         break;
+      }
+   }
+
+   /* Create a variable if not exist. */
+   if (!new_var) {
+      new_var = nir_variable_clone(var_in_producer, consumer);
+      nir_shader_add_variable(consumer, new_var);
+   }
+
+   return new_var;
+}
+
+static nir_deref_instr *
+clone_deref_instr(nir_builder *b, nir_variable *var, nir_deref_instr *deref)
+{
+   if (deref->deref_type == nir_deref_type_var)
+       return nir_build_deref_var(b, var);
+
+   nir_deref_instr *parent_deref = nir_deref_instr_parent(deref);
+   nir_deref_instr *parent = clone_deref_instr(b, var, parent_deref);
+
+   /* Build array and struct deref instruction.
+    * "deref" instr is sure to be direct (see is_direct_uniform_load()).
+    */
+   switch (deref->deref_type) {
+   case nir_deref_type_array: {
+      nir_load_const_instr *index =
+         nir_instr_as_load_const(deref->arr.index.ssa->parent_instr);
+      return nir_build_deref_array_imm(b, parent, index->value->i64);
+   }
+   case nir_deref_type_ptr_as_array: {
+      nir_load_const_instr *index =
+         nir_instr_as_load_const(deref->arr.index.ssa->parent_instr);
+      nir_ssa_def *ssa = nir_imm_intN_t(b, index->value->i64,
+                                        parent->dest.ssa.bit_size);
+      return nir_build_deref_ptr_as_array(b, parent, ssa);
+   }
+   case nir_deref_type_struct:
+      return nir_build_deref_struct(b, parent, deref->strct.index);
+   default:
+      unreachable("invalid type");
+      return NULL;
+   }
+}
+
+static bool
+replace_varying_input_by_uniform_load(nir_shader *shader,
+                                      nir_intrinsic_instr *store_intr,
+                                      nir_ssa_scalar *scalar)
+{
+   nir_function_impl *impl = nir_shader_get_entrypoint(shader);
+
+   nir_builder b;
+   nir_builder_init(&b, impl);
+
+   nir_variable *out_var =
+      nir_deref_instr_get_variable(nir_src_as_deref(store_intr->src[0]));
+
+   nir_intrinsic_instr *load = nir_instr_as_intrinsic(scalar->def->parent_instr);
+   nir_deref_instr *deref = nir_src_as_deref(load->src[0]);
+   nir_variable *uni_var = nir_deref_instr_get_variable(deref);
+   uni_var = get_uniform_var_in_consumer(shader, uni_var);
+
+   bool progress = false;
+   nir_foreach_block(block, impl) {
+      nir_foreach_instr(instr, block) {
+         if (instr->type != nir_instr_type_intrinsic)
+            continue;
+
+         nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
+         if (intr->intrinsic != nir_intrinsic_load_deref)
+            continue;
+
+         nir_deref_instr *in_deref = nir_src_as_deref(intr->src[0]);
+         if (!nir_deref_mode_is(in_deref, nir_var_shader_in))
+            continue;
+
+         nir_variable *in_var = nir_deref_instr_get_variable(in_deref);
+
+         if (!does_varying_match(out_var, in_var))
+            continue;
+
+         b.cursor = nir_before_instr(instr);
+
+         /* Clone instructions start from deref load to variable deref. */
+         nir_deref_instr *uni_deref = clone_deref_instr(&b, uni_var, deref);
+         nir_ssa_def *uni_def = nir_load_deref(&b, uni_deref);
+
+         /* Add a vector to scalar move if uniform is a vector. */
+         if (uni_def->num_components > 1) {
+            nir_alu_src src = {0};
+            src.src = nir_src_for_ssa(uni_def);
+            src.swizzle[0] = scalar->comp;
+            uni_def = nir_mov_alu(&b, src, 1);
+         }
+
+         /* Replace load input with load uniform. */
+         nir_ssa_def_rewrite_uses(&intr->dest.ssa, uni_def);
+
+         progress = true;
+      }
+   }
+
+   return progress;
+}
+
 /* The GLSL ES 3.20 spec says:
  *
  * "The precision of a vertex output does not need to match the precision of
@@ -1201,11 +1351,16 @@ nir_link_opt_varyings(nir_shader *producer, nir_shader *consumer)
       if (!can_replace_varying(out_var))
          continue;
 
-      if (intr->src[1].ssa->parent_instr->type == nir_instr_type_load_const) {
+      nir_ssa_scalar uni_scalar;
+      nir_ssa_def *ssa = intr->src[1].ssa;
+      if (ssa->parent_instr->type == nir_instr_type_load_const) {
          progress |= replace_constant_input(consumer, intr);
+      } else if (is_direct_uniform_load(ssa, &uni_scalar)) {
+         progress |= replace_varying_input_by_uniform_load(consumer, intr,
+                                                           &uni_scalar);
       } else {
          struct hash_entry *entry =
-               _mesa_hash_table_search(varying_values, intr->src[1].ssa);
+               _mesa_hash_table_search(varying_values, ssa);
          if (entry) {
             progress |= replace_duplicate_input(consumer,
                                                 (nir_variable *) entry->data,
@@ -1213,8 +1368,7 @@ nir_link_opt_varyings(nir_shader *producer, nir_shader *consumer)
          } else {
             nir_variable *in_var = get_matching_input_var(consumer, out_var);
             if (in_var) {
-               _mesa_hash_table_insert(varying_values, intr->src[1].ssa,
-                                       in_var);
+               _mesa_hash_table_insert(varying_values, ssa, in_var);
             }
          }
       }



More information about the mesa-commit mailing list