mesa/st: add implicit zeroing of clipdistance array
authorMike Blumenkrantz <michael.blumenkrantz@gmail.com>
Tue, 12 Jul 2022 17:57:31 +0000 (13:57 -0400)
committerMarge Bot <emma+marge@anholt.net>
Mon, 18 Jul 2022 20:33:11 +0000 (20:33 +0000)
GL drivers have an implicit default of "in bounds" for unwritten clipdistance
values, but some (layered) drivers have to deal with api mismatch which
prevents that implicit value from being used after the shader
gets mangled by various compiler passes

to avoid issues here, write out all members of the clipdistance array
with zeroes at the very start of the shader such that any values the
shader actually writes will naturally overwrite the implicit zero and
any unwritten values will now be written as zero

fixes #6845

Reviewed-by: Jesse Natalie <jenatali@microsoft.com>
Reviewed-by: Marek Olšák <marek.olsak@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/17498>

src/mesa/state_tracker/st_glsl_to_nir.cpp

index 179a646..dcd1b8d 100644 (file)
@@ -290,6 +290,40 @@ st_can_remove_varying_before_linking(nir_variable *var, void *data)
       return true;
 }
 
+static void
+zero_array_members(nir_builder *b, nir_variable *var)
+{
+   nir_deref_instr *deref = nir_build_deref_var(b, var);
+   nir_ssa_def *zero = nir_imm_zero(b, 4, 32);
+   for (int i = 0; i < glsl_array_size(var->type); i++) {
+      nir_deref_instr *arr = nir_build_deref_array_imm(b, deref, i);
+      uint32_t mask = BITFIELD_MASK(glsl_get_vector_elements(arr->type));
+      nir_store_deref(b, arr, nir_channels(b, zero, mask), mask);
+   }
+}
+
+/* GL has an implicit default of 0 for unwritten gl_ClipDistance members;
+ * to achieve this, write 0 to all members at the start of the shader and
+ * let them be naturally overwritten later
+ */
+static bool
+st_nir_zero_initialize_clip_distance(nir_shader *nir)
+{
+   nir_variable *clip_dist0 = nir_find_variable_with_location(nir, nir_var_shader_out, VARYING_SLOT_CLIP_DIST0);
+   nir_variable *clip_dist1 = nir_find_variable_with_location(nir, nir_var_shader_out, VARYING_SLOT_CLIP_DIST1);
+   if (!clip_dist0 && !clip_dist1)
+      return false;
+   nir_builder b;
+   nir_function_impl *impl = nir_shader_get_entrypoint(nir);
+   nir_builder_init(&b, impl);
+   b.cursor = nir_before_block(nir_start_block(impl));
+   if (clip_dist0)
+      zero_array_members(&b, clip_dist0);
+   if (clip_dist1)
+      zero_array_members(&b, clip_dist1);
+   return true;
+}
+
 /* First third of converting glsl_to_nir.. this leaves things in a pre-
  * nir_lower_io state, so that shader variants can more easily insert/
  * replace variables, etc.
@@ -339,6 +373,10 @@ st_nir_preprocess(struct st_context *st, struct gl_program *prog,
       NIR_PASS_V(nir, st_nir_add_point_size);
    }
 
+   if (stage < MESA_SHADER_FRAGMENT && stage != MESA_SHADER_TESS_CTRL &&
+       (nir->info.outputs_written & (VARYING_BIT_CLIP_DIST0 | VARYING_BIT_CLIP_DIST1)))
+      NIR_PASS_V(nir, st_nir_zero_initialize_clip_distance);
+
    struct nir_remove_dead_variables_options opts;
    bool is_sso = nir->info.separate_shader;
    opts.can_remove_var_data = &is_sso;