nir/linker: support uniform when optimizing varying

Varying assigned from uniform won't change after interpolation,
so move uniform load to fragment shader to eliminate the varying.

Acked-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com>
Acked-by: Marek Olšák <marek.olsak@amd.com>
Signed-off-by: Qiang Yu <yuq825@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/12613>
This commit is contained in:
Qiang Yu
2021-08-30 15:51:50 +08:00
committed by Marge Bot
parent 3127282944
commit 2604625043

View File

@@ -1100,6 +1100,156 @@ replace_duplicate_input(nir_shader *shader, nir_variable *input_var,
return progress;
}
static bool
is_direct_uniform_load(nir_ssa_def *def, nir_ssa_scalar *s)
{
/* def is sure to be scalar as can_replace_varying() filter out vector case. */
assert(def->num_components == 1);
/* Uniform load may hide behind some move instruction for converting
* vector to scalar:
*
* vec1 32 ssa_1 = deref_var &color (uniform vec3)
* vec3 32 ssa_2 = intrinsic load_deref (ssa_1) (0)
* vec1 32 ssa_3 = mov ssa_2.x
* vec1 32 ssa_4 = deref_var &color_out (shader_out float)
* intrinsic store_deref (ssa_4, ssa_3) (1, 0)
*/
*s = nir_ssa_scalar_resolved(def, 0);
nir_ssa_def *ssa = s->def;
if (ssa->parent_instr->type != nir_instr_type_intrinsic)
return false;
nir_intrinsic_instr *intr = nir_instr_as_intrinsic(ssa->parent_instr);
if (intr->intrinsic != nir_intrinsic_load_deref)
return false;
nir_deref_instr *deref = nir_src_as_deref(intr->src[0]);
/* TODO: support nir_var_mem_ubo. */
if (!nir_deref_mode_is(deref, nir_var_uniform))
return false;
/* Does not support indirect uniform load. */
return !nir_deref_instr_has_indirect(deref);
}
static nir_variable *
get_uniform_var_in_consumer(nir_shader *consumer,
nir_variable *var_in_producer)
{
/* Find if uniform already exists in consumer. */
nir_variable *new_var = NULL;
nir_foreach_uniform_variable(v, consumer) {
if (!strcmp(var_in_producer->name, v->name)) {
new_var = v;
break;
}
}
/* Create a variable if not exist. */
if (!new_var) {
new_var = nir_variable_clone(var_in_producer, consumer);
nir_shader_add_variable(consumer, new_var);
}
return new_var;
}
static nir_deref_instr *
clone_deref_instr(nir_builder *b, nir_variable *var, nir_deref_instr *deref)
{
if (deref->deref_type == nir_deref_type_var)
return nir_build_deref_var(b, var);
nir_deref_instr *parent_deref = nir_deref_instr_parent(deref);
nir_deref_instr *parent = clone_deref_instr(b, var, parent_deref);
/* Build array and struct deref instruction.
* "deref" instr is sure to be direct (see is_direct_uniform_load()).
*/
switch (deref->deref_type) {
case nir_deref_type_array: {
nir_load_const_instr *index =
nir_instr_as_load_const(deref->arr.index.ssa->parent_instr);
return nir_build_deref_array_imm(b, parent, index->value->i64);
}
case nir_deref_type_ptr_as_array: {
nir_load_const_instr *index =
nir_instr_as_load_const(deref->arr.index.ssa->parent_instr);
nir_ssa_def *ssa = nir_imm_intN_t(b, index->value->i64,
parent->dest.ssa.bit_size);
return nir_build_deref_ptr_as_array(b, parent, ssa);
}
case nir_deref_type_struct:
return nir_build_deref_struct(b, parent, deref->strct.index);
default:
unreachable("invalid type");
return NULL;
}
}
static bool
replace_varying_input_by_uniform_load(nir_shader *shader,
nir_intrinsic_instr *store_intr,
nir_ssa_scalar *scalar)
{
nir_function_impl *impl = nir_shader_get_entrypoint(shader);
nir_builder b;
nir_builder_init(&b, impl);
nir_variable *out_var =
nir_deref_instr_get_variable(nir_src_as_deref(store_intr->src[0]));
nir_intrinsic_instr *load = nir_instr_as_intrinsic(scalar->def->parent_instr);
nir_deref_instr *deref = nir_src_as_deref(load->src[0]);
nir_variable *uni_var = nir_deref_instr_get_variable(deref);
uni_var = get_uniform_var_in_consumer(shader, uni_var);
bool progress = false;
nir_foreach_block(block, impl) {
nir_foreach_instr(instr, block) {
if (instr->type != nir_instr_type_intrinsic)
continue;
nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
if (intr->intrinsic != nir_intrinsic_load_deref)
continue;
nir_deref_instr *in_deref = nir_src_as_deref(intr->src[0]);
if (!nir_deref_mode_is(in_deref, nir_var_shader_in))
continue;
nir_variable *in_var = nir_deref_instr_get_variable(in_deref);
if (!does_varying_match(out_var, in_var))
continue;
b.cursor = nir_before_instr(instr);
/* Clone instructions start from deref load to variable deref. */
nir_deref_instr *uni_deref = clone_deref_instr(&b, uni_var, deref);
nir_ssa_def *uni_def = nir_load_deref(&b, uni_deref);
/* Add a vector to scalar move if uniform is a vector. */
if (uni_def->num_components > 1) {
nir_alu_src src = {0};
src.src = nir_src_for_ssa(uni_def);
src.swizzle[0] = scalar->comp;
uni_def = nir_mov_alu(&b, src, 1);
}
/* Replace load input with load uniform. */
nir_ssa_def_rewrite_uses(&intr->dest.ssa, uni_def);
progress = true;
}
}
return progress;
}
/* The GLSL ES 3.20 spec says:
*
* "The precision of a vertex output does not need to match the precision of
@@ -1201,11 +1351,16 @@ nir_link_opt_varyings(nir_shader *producer, nir_shader *consumer)
if (!can_replace_varying(out_var))
continue;
if (intr->src[1].ssa->parent_instr->type == nir_instr_type_load_const) {
nir_ssa_scalar uni_scalar;
nir_ssa_def *ssa = intr->src[1].ssa;
if (ssa->parent_instr->type == nir_instr_type_load_const) {
progress |= replace_constant_input(consumer, intr);
} else if (is_direct_uniform_load(ssa, &uni_scalar)) {
progress |= replace_varying_input_by_uniform_load(consumer, intr,
&uni_scalar);
} else {
struct hash_entry *entry =
_mesa_hash_table_search(varying_values, intr->src[1].ssa);
_mesa_hash_table_search(varying_values, ssa);
if (entry) {
progress |= replace_duplicate_input(consumer,
(nir_variable *) entry->data,
@@ -1213,8 +1368,7 @@ nir_link_opt_varyings(nir_shader *producer, nir_shader *consumer)
} else {
nir_variable *in_var = get_matching_input_var(consumer, out_var);
if (in_var) {
_mesa_hash_table_insert(varying_values, intr->src[1].ssa,
in_var);
_mesa_hash_table_insert(varying_values, ssa, in_var);
}
}
}