zink: add line-stippling lowering passes

There's two notable limitations here:
- This will viewport-map to viewport #0 only. This is because we need
  the viewport-scale factors, which we'll be uploading using
  push-constants. And we don't want to waste too many of those...
- It's missing a "global" stipple-counter. It doesn't seem like there's
  a portable way of implementing this, so this is going to require a VK
  extension that can be implemented in a hardware-specific way in the
  long run. For now, let's just ignore the global stipple counter.

These two limitations don't seem viable to overcome for now, so but this
is better than nothing.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/19117>
This commit is contained in:
Erik Faye-Lund 2022-09-27 09:35:54 +02:00 committed by Marge Bot
parent 9f67e72e84
commit 4b17c099ca
2 changed files with 186 additions and 0 deletions

View file

@ -32,6 +32,7 @@
#include "nir.h"
#include "compiler/nir/nir_builder.h"
#include "compiler/nir/nir_builtin_builder.h"
#include "nir/tgsi_to_nir.h"
#include "tgsi/tgsi_dump.h"
@ -64,6 +65,8 @@ fields[member_idx].offset = offsetof(struct zink_gfx_push_constant, field);
PUSHCONST_MEMBER(ZINK_GFX_PUSHCONST_FRAMEBUFFER_IS_LAYERED, framebuffer_is_layered);
PUSHCONST_MEMBER(ZINK_GFX_PUSHCONST_DEFAULT_INNER_LEVEL, default_inner_level);
PUSHCONST_MEMBER(ZINK_GFX_PUSHCONST_DEFAULT_OUTER_LEVEL, default_outer_level);
PUSHCONST_MEMBER(ZINK_GFX_PUSHCONST_LINE_STIPPLE_PATTERN, line_stipple_pattern);
PUSHCONST_MEMBER(ZINK_GFX_PUSHCONST_VIEWPORT_SCALE, viewport_scale);
pushconst = nir_variable_create(nir, nir_var_mem_push_const,
glsl_struct_type(fields, ZINK_GFX_PUSHCONST_MAX, "struct", false),
@ -270,6 +273,185 @@ lower_drawid(nir_shader *shader)
return nir_shader_instructions_pass(shader, lower_drawid_instr, nir_metadata_dominance, NULL);
}
struct lower_line_stipple_state {
nir_variable *pos_out;
nir_variable *stipple_out;
nir_variable *prev_pos;
nir_variable *pos_counter;
nir_variable *stipple_counter;
};
static nir_ssa_def *
viewport_map(nir_builder *b, nir_ssa_def *vert,
nir_ssa_def *scale)
{
nir_ssa_def *w_recip = nir_frcp(b, nir_channel(b, vert, 3));
nir_ssa_def *ndc_point = nir_fmul(b, nir_channels(b, vert, 0x3),
w_recip);
return nir_fmul(b, ndc_point, scale);
}
static bool
lower_line_stipple_gs_instr(nir_builder *b, nir_instr *instr, void *data)
{
struct lower_line_stipple_state *state = data;
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;
b->cursor = nir_before_instr(instr);
nir_push_if(b, nir_ine_imm(b, nir_load_var(b, state->pos_counter), 0));
// viewport-map endpoints
nir_ssa_def *vp_scale = nir_load_push_constant(b, 2, 32,
nir_imm_int(b, ZINK_GFX_PUSHCONST_VIEWPORT_SCALE),
.base = 1,
.range = 2);
nir_ssa_def *prev = nir_load_var(b, state->prev_pos);
nir_ssa_def *curr = nir_load_var(b, state->pos_out);
prev = viewport_map(b, prev, vp_scale);
curr = viewport_map(b, curr, vp_scale);
// calculate length of line
nir_ssa_def *len = nir_fast_distance(b, prev, curr);
// update stipple_counter
nir_store_var(b, state->stipple_counter,
nir_fadd(b, nir_load_var(b, state->stipple_counter),
len), 1);
nir_pop_if(b, NULL);
// emit stipple out
nir_copy_var(b, state->stipple_out, state->stipple_counter);
nir_copy_var(b, state->prev_pos, state->pos_out);
// update prev_pos and pos_counter for next vertex
b->cursor = nir_after_instr(instr);
nir_store_var(b, state->pos_counter,
nir_iadd_imm(b, nir_load_var(b, state->pos_counter),
1), 1);
return true;
}
static bool
lower_line_stipple_gs(nir_shader *shader)
{
nir_builder b;
struct lower_line_stipple_state state;
state.pos_out =
nir_find_variable_with_location(shader, nir_var_shader_out,
VARYING_SLOT_POS);
// if position isn't written, we have nothing to do
if (!state.pos_out)
return false;
state.stipple_out = nir_variable_create(shader, nir_var_shader_out,
glsl_float_type(),
"__stipple");
state.stipple_out->data.interpolation = INTERP_MODE_NOPERSPECTIVE;
state.stipple_out->data.driver_location = shader->num_outputs++;
state.stipple_out->data.location = MIN2(util_last_bit64(shader->info.outputs_written) + 1, VARYING_SLOT_VAR0);
shader->info.outputs_written |= BITFIELD64_BIT(state.stipple_out->data.location);
// create temp variables
state.prev_pos = nir_variable_create(shader, nir_var_shader_temp,
glsl_vec4_type(),
"__prev_pos");
state.pos_counter = nir_variable_create(shader, nir_var_shader_temp,
glsl_uint_type(),
"__pos_counter");
state.stipple_counter = nir_variable_create(shader, nir_var_shader_temp,
glsl_float_type(),
"__stipple_counter");
// initialize pos_counter and stipple_counter
nir_function_impl *entry = nir_shader_get_entrypoint(shader);
nir_builder_init(&b, entry);
b.cursor = nir_before_cf_list(&entry->body);
nir_store_var(&b, state.pos_counter, nir_imm_int(&b, 0), 1);
nir_store_var(&b, state.stipple_counter, nir_imm_float(&b, 0), 1);
return nir_shader_instructions_pass(shader, lower_line_stipple_gs_instr,
nir_metadata_dominance, &state);
}
static bool
lower_line_stipple_fs(nir_shader *shader)
{
nir_builder b;
nir_function_impl *entry = nir_shader_get_entrypoint(shader);
nir_builder_init(&b, entry);
// create stipple counter
nir_variable *stipple = nir_variable_create(shader, nir_var_shader_in,
glsl_float_type(),
"__stipple");
stipple->data.interpolation = INTERP_MODE_NOPERSPECTIVE;
stipple->data.driver_location = shader->num_inputs++;
stipple->data.location = MIN2(util_last_bit64(shader->info.inputs_read) + 1, VARYING_SLOT_VAR0);
shader->info.inputs_read |= BITFIELD64_BIT(stipple->data.location);
nir_variable *sample_mask_out =
nir_find_variable_with_location(shader, nir_var_shader_out,
FRAG_RESULT_SAMPLE_MASK);
if (!sample_mask_out) {
sample_mask_out = nir_variable_create(shader, nir_var_shader_out,
glsl_uint_type(), "sample_mask");
sample_mask_out->data.driver_location = shader->num_outputs++;
sample_mask_out->data.location = FRAG_RESULT_SAMPLE_MASK;
}
b.cursor = nir_after_cf_list(&entry->body);
nir_ssa_def *pattern = nir_load_push_constant(&b, 1, 32,
nir_imm_int(&b, ZINK_GFX_PUSHCONST_LINE_STIPPLE_PATTERN),
.base = 1);
nir_ssa_def *factor = nir_i2f32(&b, nir_ishr_imm(&b, pattern, 16));
pattern = nir_iand_imm(&b, pattern, 0xffff);
nir_ssa_def *sample_mask_in = nir_load_sample_mask_in(&b);
nir_variable *v = nir_local_variable_create(entry, glsl_uint_type(), NULL);
nir_variable *sample_mask = nir_local_variable_create(entry, glsl_uint_type(), NULL);
nir_store_var(&b, v, sample_mask_in, 1);
nir_store_var(&b, sample_mask, sample_mask_in, 1);
nir_push_loop(&b);
{
nir_ssa_def *value = nir_load_var(&b, v);
nir_ssa_def *index = nir_ufind_msb(&b, value);
nir_ssa_def *index_mask = nir_ishl(&b, nir_imm_int(&b, 1), index);
nir_ssa_def *new_value = nir_ixor(&b, value, index_mask);
nir_store_var(&b, v, new_value, 1);
nir_push_if(&b, nir_ieq_imm(&b, value, 0));
nir_jump(&b, nir_jump_break);
nir_pop_if(&b, NULL);
nir_ssa_def *stipple_pos =
nir_interp_deref_at_sample(&b, 1, 32,
&nir_build_deref_var(&b, stipple)->dest.ssa, index);
stipple_pos = nir_fmod(&b, nir_fdiv(&b, stipple_pos, factor),
nir_imm_float(&b, 16.0));
stipple_pos = nir_f2i32(&b, stipple_pos);
nir_ssa_def *bit =
nir_iand_imm(&b, nir_ishr(&b, pattern, stipple_pos), 1);
nir_push_if(&b, nir_ieq_imm(&b, bit, 0));
{
nir_ssa_def *value = nir_load_var(&b, sample_mask);
value = nir_ixor(&b, value, index_mask);
nir_store_var(&b, sample_mask, value, 1);
}
nir_pop_if(&b, NULL);
}
nir_pop_loop(&b, NULL);
nir_store_var(&b, sample_mask_out, nir_load_var(&b, sample_mask), 1);
return true;
}
static bool
lower_dual_blend(nir_shader *shader)
{

View file

@ -804,6 +804,8 @@ struct zink_gfx_push_constant {
unsigned framebuffer_is_layered;
float default_inner_level[2];
float default_outer_level[4];
uint32_t line_stipple_pattern;
float viewport_scale[2];
};
/* The order of the enums MUST match the order of the zink_gfx_push_constant
@ -815,6 +817,8 @@ enum zink_gfx_push_constant_member {
ZINK_GFX_PUSHCONST_FRAMEBUFFER_IS_LAYERED,
ZINK_GFX_PUSHCONST_DEFAULT_INNER_LEVEL,
ZINK_GFX_PUSHCONST_DEFAULT_OUTER_LEVEL,
ZINK_GFX_PUSHCONST_LINE_STIPPLE_PATTERN,
ZINK_GFX_PUSHCONST_VIEWPORT_SCALE,
ZINK_GFX_PUSHCONST_MAX
};