zink: add gl_point lowering pass
authorIgor Torrente <igor.torrente@collabora.com>
Thu, 1 Dec 2022 12:41:48 +0000 (09:41 -0300)
committerMarge Bot <emma+marge@anholt.net>
Tue, 6 Dec 2022 12:37:13 +0000 (12:37 +0000)
This lowering pass is intended for hardwares/drivers that can't honor
the gl_PointSize when GL_PROGRAM_POINT_SIZE is enabled.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/20109>

src/gallium/drivers/zink/zink_compiler.c

index 81ed45f..852c127 100644 (file)
@@ -273,6 +273,114 @@ lower_drawid(nir_shader *shader)
    return nir_shader_instructions_pass(shader, lower_drawid_instr, nir_metadata_dominance, NULL);
 }
 
+struct lower_gl_point_state {
+   nir_variable *gl_pos_out;
+   nir_variable *gl_point_size;
+};
+
+static bool
+lower_gl_point_gs_instr(nir_builder *b, nir_instr *instr, void *data)
+{
+   struct lower_gl_point_state *state = data;
+   nir_ssa_def *vp_scale, *pos;
+
+   if (instr->type != nir_instr_type_intrinsic)
+      return false;
+
+   nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
+   if (intrin->intrinsic != nir_intrinsic_emit_vertex_with_counter &&
+       intrin->intrinsic != nir_intrinsic_emit_vertex)
+      return false;
+
+   if (nir_intrinsic_stream_id(intrin) != 0)
+      return false;
+
+   if (intrin->intrinsic == nir_intrinsic_end_primitive_with_counter ||
+         intrin->intrinsic == nir_intrinsic_end_primitive) {
+      nir_instr_remove(&intrin->instr);
+      return true;
+   }
+
+   b->cursor = nir_before_instr(instr);
+
+   // viewport-map endpoints
+   nir_ssa_def *vp_const_pos = nir_imm_int(b, ZINK_GFX_PUSHCONST_VIEWPORT_SCALE);
+   vp_scale = nir_load_push_constant(b, 2, 32, vp_const_pos, .base = 1, .range = 2);
+
+   // Load point info values
+   nir_ssa_def *point_size = nir_load_var(b, state->gl_point_size);
+   nir_ssa_def *point_pos = nir_load_var(b, state->gl_pos_out);
+
+   // w_delta = gl_point_size / width_viewport_size_scale * gl_Position.w
+   nir_ssa_def *w_delta = nir_fdiv(b, point_size, nir_channel(b, vp_scale, 0));
+   w_delta = nir_fmul(b, w_delta, nir_channel(b, point_pos, 3));
+   // halt_w_delta = w_delta / 2
+   nir_ssa_def *half_w_delta = nir_fmul(b, w_delta, nir_imm_float(b, 0.5));
+
+   // h_delta = gl_point_size / height_viewport_size_scale * gl_Position.w
+   nir_ssa_def *h_delta = nir_fdiv(b, point_size, nir_channel(b, vp_scale, 1));
+   h_delta = nir_fmul(b, h_delta, nir_channel(b, point_pos, 3));
+   // halt_h_delta = h_delta / 2
+   nir_ssa_def *half_h_delta = nir_fmul(b, h_delta, nir_imm_float(b, 0.5));
+
+   nir_ssa_def *point_dir[4][2] = {
+      { nir_imm_float(b, -1), nir_imm_float(b, -1) },
+      { nir_imm_float(b, -1), nir_imm_float(b, 1) },
+      { nir_imm_float(b, 1), nir_imm_float(b, -1) },
+      { nir_imm_float(b, 1), nir_imm_float(b, 1) }
+   };
+
+   nir_ssa_def *point_pos_x = nir_channel(b, point_pos, 0);
+   nir_ssa_def *point_pos_y = nir_channel(b, point_pos, 1);
+
+   for (size_t i = 0; i < 4; i++) {
+      pos = nir_vec4(b,
+                     nir_ffma(b, half_w_delta, point_dir[i][0], point_pos_x),
+                     nir_ffma(b, half_h_delta, point_dir[i][1], point_pos_y),
+                     nir_channel(b, point_pos, 2),
+                     nir_channel(b, point_pos, 3));
+
+      nir_store_var(b, state->gl_pos_out, pos, 0xf);
+
+      nir_emit_vertex(b);
+   }
+
+   nir_end_primitive(b);
+
+   nir_instr_remove(&intrin->instr);
+
+   return true;
+}
+
+static bool
+lower_gl_point_gs(nir_shader *shader)
+{
+   struct lower_gl_point_state state;
+   nir_builder b;
+
+   shader->info.gs.output_primitive = SHADER_PRIM_TRIANGLE_STRIP;
+   shader->info.gs.vertices_out *= 4;
+
+   // Gets the gl_Position in and out
+   state.gl_pos_out =
+      nir_find_variable_with_location(shader, nir_var_shader_out,
+                                      VARYING_SLOT_POS);
+   state.gl_point_size =
+      nir_find_variable_with_location(shader, nir_var_shader_out,
+                                      VARYING_SLOT_PSIZ);
+
+   // if position in or gl_PointSize aren't written, we have nothing to do
+   if (!state.gl_pos_out || !state.gl_point_size)
+      return false;
+
+   nir_function_impl *entry = nir_shader_get_entrypoint(shader);
+   nir_builder_init(&b, entry);
+   b.cursor = nir_before_cf_list(&entry->body);
+
+   return nir_shader_instructions_pass(shader, lower_gl_point_gs_instr,
+                                       nir_metadata_dominance, &state);
+}
+
 struct lower_line_stipple_state {
    nir_variable *pos_out;
    nir_variable *stipple_out;