nir/opt_deref: Remove certain sampler type casts
authorJason Ekstrand <jason@jlekstrand.net>
Wed, 22 Apr 2020 20:47:28 +0000 (15:47 -0500)
committerMarge Bot <eric+marge@anholt.net>
Fri, 24 Apr 2020 09:43:21 +0000 (09:43 +0000)
The SPIR-V parser sometimes generates casts from specific sampler types
like sampler2D to the bare sampler type.  This results in a cast which
causes heartburn for drivers but is harmless to remove.

cc: mesa-stable@lists.freedesktop.org

Reviewed-by: Dave Airlie <airlied@redhat.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/4684>

src/compiler/nir/nir_deref.c

index aa841223a3be9b80646a070d5580c16f16b05936..e06fd07aeb223ea9c6d68dc67155a0ff1bd0f94d 100644 (file)
@@ -753,6 +753,42 @@ nir_rematerialize_derefs_in_use_blocks_impl(nir_function_impl *impl)
    return state.progress;
 }
 
+static void
+nir_deref_instr_fixup_child_types(nir_deref_instr *parent)
+{
+   nir_foreach_use(use, &parent->dest.ssa) {
+      if (use->parent_instr->type != nir_instr_type_deref)
+         continue;
+
+      nir_deref_instr *child = nir_instr_as_deref(use->parent_instr);
+      switch (child->deref_type) {
+      case nir_deref_type_var:
+         unreachable("nir_deref_type_var cannot be a child");
+
+      case nir_deref_type_array:
+      case nir_deref_type_array_wildcard:
+         child->type = glsl_get_array_element(parent->type);
+         break;
+
+      case nir_deref_type_ptr_as_array:
+         child->type = parent->type;
+         break;
+
+      case nir_deref_type_struct:
+         child->type = glsl_get_struct_field(parent->type,
+                                             child->strct.index);
+         break;
+
+      case nir_deref_type_cast:
+         /* We stop the recursion here */
+         continue;
+      }
+
+      /* Recurse into children */
+      nir_deref_instr_fixup_child_types(child);
+   }
+}
+
 static bool
 is_trivial_array_deref_cast(nir_deref_instr *cast)
 {
@@ -800,6 +836,44 @@ opt_remove_cast_cast(nir_deref_instr *cast)
    return true;
 }
 
+static bool
+opt_remove_sampler_cast(nir_deref_instr *cast)
+{
+   assert(cast->deref_type == nir_deref_type_cast);
+   nir_deref_instr *parent = nir_src_as_deref(cast->parent);
+   if (parent == NULL)
+      return false;
+
+   /* Strip both types down to their non-array type and bail if there are any
+    * discrepancies in array lengths.
+    */
+   const struct glsl_type *parent_type = parent->type;
+   const struct glsl_type *cast_type = cast->type;
+   while (glsl_type_is_array(parent_type) && glsl_type_is_array(cast_type)) {
+      if (glsl_get_length(parent_type) != glsl_get_length(cast_type))
+         return false;
+      parent_type = glsl_get_array_element(parent_type);
+      cast_type = glsl_get_array_element(cast_type);
+   }
+
+   if (glsl_type_is_array(parent_type) || glsl_type_is_array(cast_type))
+      return false;
+
+   if (!glsl_type_is_sampler(parent_type) ||
+       cast_type != glsl_bare_sampler_type())
+      return false;
+
+   /* We're a cast from a more detailed sampler type to a bare sampler */
+   nir_ssa_def_rewrite_uses(&cast->dest.ssa,
+                            nir_src_for_ssa(&parent->dest.ssa));
+   nir_instr_remove(&cast->instr);
+
+   /* Recursively crawl the deref tree and clean up types */
+   nir_deref_instr_fixup_child_types(parent);
+
+   return true;
+}
+
 /**
  * Is this casting a struct to a contained struct.
  * struct a { struct b field0 };
@@ -839,6 +913,9 @@ opt_deref_cast(nir_builder *b, nir_deref_instr *cast)
    if (opt_replace_struct_wrapper_cast(b, cast))
       return true;
 
+   if (opt_remove_sampler_cast(cast))
+      return true;
+
    progress = opt_remove_cast_cast(cast);
    if (!is_trivial_deref_cast(cast))
       return progress;