nir/lower_gs_intrinsics: Make nir_lower_gs_intrinsics be idempotent

Calling this lower pass twice in a row would cause spurious
set_vertex_and_primitive_count(0, undef) intrinsics after the proper
set_vertex_and_primitive_count intrinsic.  This pretty much turns any
geometry shader into garbage.

Fix this by treating nir_intrinsic_emit_vertex_with_counter and
nir_intrinsic_end_primitive_with_counter just like the non-_with_counter
versions.  If no blocks would need set_vertex_and_primitive_count
intrinsics added, exit the pass before doing any work.  This prevents
the need for DCE to do extra clean up later.

Since this pass is potentially called multiple times via multiple
invocations of a finalize_nir callback, it is (hypothetically?) possible
that control flow could be changed to add new blocks that need this
intrinsic.  The check implemented in this commit should be robust
against that possibility.

v2: Add a_block_needs_set_vertex_and_primitive_count.  Suggested by
Timur.

Reviewed-by: Timur Kristóf <timur.kristof@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/12802>
This commit is contained in:
Ian Romanick
2021-09-09 17:31:24 -07:00
parent edf357b233
commit 7956a701d8

View File

@@ -246,9 +246,11 @@ rewrite_intrinsics(nir_block *block, struct state *state)
nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
switch (intrin->intrinsic) {
case nir_intrinsic_emit_vertex:
case nir_intrinsic_emit_vertex_with_counter:
rewrite_emit_vertex(intrin, state);
break;
case nir_intrinsic_end_primitive:
case nir_intrinsic_end_primitive_with_counter:
rewrite_end_primitive(intrin, state);
break;
default:
@@ -307,6 +309,48 @@ append_set_vertex_and_primitive_count(nir_block *end_block, struct state *state)
}
}
/**
* Check to see if there are any blocks that need set_vertex_and_primitive_count
*
* If every block that could need the set_vertex_and_primitive_count intrinsic
* already has one, there is nothing for this pass to do.
*/
static bool
a_block_needs_set_vertex_and_primitive_count(nir_block *end_block, bool per_stream)
{
set_foreach(end_block->predecessors, entry) {
nir_block *pred = (nir_block *) entry->key;
for (unsigned stream = 0; stream < NIR_MAX_XFB_STREAMS; ++stream) {
/* When it's not per-stream, we only need to write one variable. */
if (!per_stream && stream != 0)
continue;
bool found = false;
nir_foreach_instr_reverse(instr, pred) {
if (instr->type != nir_instr_type_intrinsic)
continue;
const nir_intrinsic_instr *const intrin =
nir_instr_as_intrinsic(instr);
if (intrin->intrinsic == nir_intrinsic_set_vertex_and_primitive_count &&
intrin->const_index[0] == stream) {
found = true;
break;
}
}
if (!found)
return true;
}
}
return false;
}
bool
nir_lower_gs_intrinsics(nir_shader *shader, nir_lower_gs_intrinsics_flags options)
{
@@ -327,6 +371,9 @@ nir_lower_gs_intrinsics(nir_shader *shader, nir_lower_gs_intrinsics_flags option
nir_function_impl *impl = nir_shader_get_entrypoint(shader);
assert(impl);
if (!a_block_needs_set_vertex_and_primitive_count(impl->end_block, per_stream))
return false;
nir_builder b;
nir_builder_init(&b, impl);
state.builder = &b;