From 4b17c099ca47ffd8831fa9f835ca4eb4c3ab93ad Mon Sep 17 00:00:00 2001 From: Erik Faye-Lund Date: Tue, 27 Sep 2022 09:35:54 +0200 Subject: [PATCH] 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: --- src/gallium/drivers/zink/zink_compiler.c | 182 +++++++++++++++++++++++ src/gallium/drivers/zink/zink_types.h | 4 + 2 files changed, 186 insertions(+) diff --git a/src/gallium/drivers/zink/zink_compiler.c b/src/gallium/drivers/zink/zink_compiler.c index 8e1299692da..9d5234af1fe 100644 --- a/src/gallium/drivers/zink/zink_compiler.c +++ b/src/gallium/drivers/zink/zink_compiler.c @@ -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) { diff --git a/src/gallium/drivers/zink/zink_types.h b/src/gallium/drivers/zink/zink_types.h index 0f9c7100e99..0819a9801f6 100644 --- a/src/gallium/drivers/zink/zink_types.h +++ b/src/gallium/drivers/zink/zink_types.h @@ -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 };