Mesa (main): radeonsi: implement shader culling in GS
GitLab Mirror
gitlab-mirror at kemper.freedesktop.org
Sat Nov 20 00:33:41 UTC 2021
Module: Mesa
Branch: main
Commit: 9d7ac70ffb3567dfafaa524aae0891d07c92aba0
URL: http://cgit.freedesktop.org/mesa/mesa/commit/?id=9d7ac70ffb3567dfafaa524aae0891d07c92aba0
Author: Marek Olšák <marek.olsak at amd.com>
Date: Tue Nov 16 21:56:05 2021 -0500
radeonsi: implement shader culling in GS
It already does compaction, so we just need to load vertex positions
and cull. This was easier than expected.
Reviewed-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer at amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/13829>
---
src/gallium/drivers/radeonsi/gfx10_shader_ngg.c | 78 ++++++++++++++++++++++-
src/gallium/drivers/radeonsi/si_shader.c | 15 +++--
src/gallium/drivers/radeonsi/si_shader_llvm.c | 2 +-
src/gallium/drivers/radeonsi/si_shader_llvm_gs.c | 3 +-
src/gallium/drivers/radeonsi/si_state_draw.cpp | 8 +--
src/gallium/drivers/radeonsi/si_state_shaders.cpp | 10 +--
6 files changed, 98 insertions(+), 18 deletions(-)
diff --git a/src/gallium/drivers/radeonsi/gfx10_shader_ngg.c b/src/gallium/drivers/radeonsi/gfx10_shader_ngg.c
index dee70769bfc..92f52c2bb16 100644
--- a/src/gallium/drivers/radeonsi/gfx10_shader_ngg.c
+++ b/src/gallium/drivers/radeonsi/gfx10_shader_ngg.c
@@ -78,7 +78,10 @@ static LLVMValueRef ngg_get_vertices_per_prim(struct si_shader_context *ctx, uns
{
const struct si_shader_info *info = &ctx->shader->selector->info;
- if (ctx->stage == MESA_SHADER_VERTEX) {
+ if (ctx->stage == MESA_SHADER_GEOMETRY) {
+ *num_vertices = u_vertices_per_prim(info->base.gs.output_primitive);
+ return LLVMConstInt(ctx->ac.i32, *num_vertices, false);
+ } else if (ctx->stage == MESA_SHADER_VERTEX) {
if (info->base.vs.blit_sgprs_amd) {
/* Blits always use axis-aligned rectangles with 3 vertices. */
*num_vertices = 3;
@@ -1954,6 +1957,79 @@ void gfx10_ngg_gs_emit_epilogue(struct si_shader_context *ctx)
ac_build_endif(&ctx->ac, 5109);
}
+ /* Cull primitives. */
+ if (ctx->shader->key.ge.opt.ngg_culling) {
+ assert(info->num_stream_output_components[0]);
+
+ LLVMValueRef gs_vtxptr = ngg_gs_vertex_ptr(ctx, tid);
+ LLVMValueRef live = LLVMBuildLoad(builder, ngg_gs_get_emit_primflag_ptr(ctx, gs_vtxptr, 0), "");
+ live = LLVMBuildTrunc(builder, live, ctx->ac.i1, "");
+ LLVMValueRef is_emit = LLVMBuildICmp(builder, LLVMIntULT, tid, num_emit_threads, "");
+ LLVMValueRef prim_enable = LLVMBuildAnd(builder, live, is_emit, "");
+
+ /* Wait for streamout to finish before we kill primitives. */
+ if (sel->so.num_outputs)
+ ac_build_s_barrier(&ctx->ac);
+
+ ac_build_ifcc(&ctx->ac, prim_enable, 0);
+ {
+ LLVMValueRef vtxptr[3] = {};
+ LLVMValueRef pos[3][4] = {};
+
+ for (unsigned i = 0; i < verts_per_prim; i++) {
+ tmp = LLVMBuildSub(builder, tid, LLVMConstInt(ctx->ac.i32, verts_per_prim - i - 1, false), "");
+ vtxptr[i] = ac_build_gep0(&ctx->ac, ngg_gs_vertex_ptr(ctx, tmp), ctx->ac.i32_0);
+ }
+
+ for (unsigned i = 0; i < info->num_outputs; i++) {
+ /* If the stream index is non-zero for all channels, skip the output. */
+ if (info->output_streams[i] & 0x3 &&
+ (info->output_streams[i] >> 2) & 0x3 &&
+ (info->output_streams[i] >> 4) & 0x3 &&
+ (info->output_streams[i] >> 6) & 0x3)
+ continue;
+
+ switch (info->output_semantic[i]) {
+ case VARYING_SLOT_POS:
+ /* Load the positions from LDS. */
+ for (unsigned vert = 0; vert < verts_per_prim; vert++) {
+ for (unsigned comp = 0; comp < 4; comp++) {
+ /* Z is not needed. */
+ if (comp == 2)
+ continue;
+
+ tmp = ac_build_gep0(&ctx->ac, vtxptr[vert],
+ LLVMConstInt(ctx->ac.i32, 4 * i + comp, false));
+ pos[vert][comp] = LLVMBuildLoad(builder, tmp, "");
+ pos[vert][comp] = ac_to_float(&ctx->ac, pos[vert][comp]);
+ }
+ }
+
+ /* Divide XY by W. */
+ for (unsigned vert = 0; vert < verts_per_prim; vert++) {
+ for (unsigned comp = 0; comp < 2; comp++)
+ pos[vert][comp] = ac_build_fdiv(&ctx->ac, pos[vert][comp], pos[vert][3]);
+ }
+ break;
+ }
+ }
+
+ LLVMValueRef clipdist_accepted = ctx->ac.i1true; /* TODO */
+ LLVMValueRef accepted = ac_build_alloca(&ctx->ac, ctx->ac.i32, "");
+
+ cull_primitive(ctx, pos, clipdist_accepted, accepted, NULL);
+
+ accepted = LLVMBuildLoad(builder, accepted, "");
+ LLVMValueRef rejected = LLVMBuildNot(builder, LLVMBuildTrunc(builder, accepted, ctx->ac.i1, ""), "");
+
+ ac_build_ifcc(&ctx->ac, rejected, 0);
+ LLVMBuildStore(builder, ctx->ac.i8_0, ngg_gs_get_emit_primflag_ptr(ctx, gs_vtxptr, 0));
+ ac_build_endif(&ctx->ac, 0);
+ }
+ ac_build_endif(&ctx->ac, 0);
+ ac_build_s_barrier(&ctx->ac);
+ }
+
/* Determine vertex liveness. */
LLVMValueRef vertliveptr = ac_build_alloca(&ctx->ac, ctx->ac.i1, "vertexlive");
diff --git a/src/gallium/drivers/radeonsi/si_shader.c b/src/gallium/drivers/radeonsi/si_shader.c
index 4c53477e92b..df108dca8cc 100644
--- a/src/gallium/drivers/radeonsi/si_shader.c
+++ b/src/gallium/drivers/radeonsi/si_shader.c
@@ -546,10 +546,14 @@ void si_init_shader_args(struct si_shader_context *ctx, bool ngg_cull_shader)
ac_add_arg(&ctx->args, AC_ARG_SGPR, 1, AC_ARG_INT, &ctx->tcs_offchip_layout);
ac_add_arg(&ctx->args, AC_ARG_SGPR, 1, AC_ARG_INT, &ctx->tes_offchip_addr);
ac_add_arg(&ctx->args, AC_ARG_SGPR, 1, AC_ARG_INT, NULL); /* unused */
+ } else {
+ /* GS */
+ ac_add_arg(&ctx->args, AC_ARG_SGPR, 1, AC_ARG_INT, NULL); /* unused */
+ ac_add_arg(&ctx->args, AC_ARG_SGPR, 1, AC_ARG_INT, NULL); /* unused */
+ ac_add_arg(&ctx->args, AC_ARG_SGPR, 1, AC_ARG_INT, NULL); /* unused */
}
- if (ctx->stage != MESA_SHADER_GEOMETRY)
- ac_add_arg(&ctx->args, AC_ARG_SGPR, 1, AC_ARG_CONST_DESC_PTR, &ctx->small_prim_cull_info);
+ ac_add_arg(&ctx->args, AC_ARG_SGPR, 1, AC_ARG_CONST_DESC_PTR, &ctx->small_prim_cull_info);
if (ctx->stage == MESA_SHADER_VERTEX)
declare_vb_descriptor_input_sgprs(ctx);
@@ -583,10 +587,8 @@ void si_init_shader_args(struct si_shader_context *ctx, bool ngg_cull_shader)
num_user_sgprs =
SI_SGPR_VS_VB_DESCRIPTOR_FIRST + shader->selector->num_vbos_in_user_sgprs * 4;
}
- } else if (ctx->stage == MESA_SHADER_TESS_EVAL && ngg_cull_shader) {
- num_user_sgprs = GFX9_GS_NUM_USER_SGPR;
} else {
- num_user_sgprs = SI_NUM_VS_STATE_RESOURCE_SGPRS;
+ num_user_sgprs = GFX9_GS_NUM_USER_SGPR;
}
/* The NGG cull shader has to return all 9 VGPRs.
@@ -1264,8 +1266,7 @@ static void si_dump_shader_key(const struct si_shader *shader, FILE *f)
fprintf(f, " opt.kill_outputs = 0x%" PRIx64 "\n", key->ge.opt.kill_outputs);
fprintf(f, " opt.kill_pointsize = 0x%x\n", key->ge.opt.kill_pointsize);
fprintf(f, " opt.kill_clip_distances = 0x%x\n", key->ge.opt.kill_clip_distances);
- if (stage != MESA_SHADER_GEOMETRY)
- fprintf(f, " opt.ngg_culling = 0x%x\n", key->ge.opt.ngg_culling);
+ fprintf(f, " opt.ngg_culling = 0x%x\n", key->ge.opt.ngg_culling);
}
if (stage <= MESA_SHADER_GEOMETRY) {
diff --git a/src/gallium/drivers/radeonsi/si_shader_llvm.c b/src/gallium/drivers/radeonsi/si_shader_llvm.c
index 80659a352ad..4e7a8a49431 100644
--- a/src/gallium/drivers/radeonsi/si_shader_llvm.c
+++ b/src/gallium/drivers/radeonsi/si_shader_llvm.c
@@ -1093,7 +1093,7 @@ bool si_llvm_compile_shader(struct si_screen *sscreen, struct ac_llvm_compiler *
si_llvm_context_init(&ctx, sscreen, compiler, si_get_shader_wave_size(shader));
LLVMValueRef ngg_cull_main_fn = NULL;
- if (sel->info.stage <= MESA_SHADER_GEOMETRY && shader->key.ge.opt.ngg_culling) {
+ if (sel->info.stage <= MESA_SHADER_TESS_EVAL && shader->key.ge.opt.ngg_culling) {
if (!si_llvm_translate_nir(&ctx, shader, nir, false, true)) {
si_llvm_dispose(&ctx);
return false;
diff --git a/src/gallium/drivers/radeonsi/si_shader_llvm_gs.c b/src/gallium/drivers/radeonsi/si_shader_llvm_gs.c
index 0a9f503ddb4..3ca42259116 100644
--- a/src/gallium/drivers/radeonsi/si_shader_llvm_gs.c
+++ b/src/gallium/drivers/radeonsi/si_shader_llvm_gs.c
@@ -116,9 +116,10 @@ static void si_set_es_return_value_for_gs(struct si_shader_context *ctx)
8 + SI_SGPR_BINDLESS_SAMPLERS_AND_IMAGES);
if (ctx->screen->use_ngg) {
ret = si_insert_input_ptr(ctx, ret, ctx->vs_state_bits, 8 + SI_SGPR_VS_STATE_BITS);
+ ret = si_insert_input_ptr(ctx, ret, ctx->small_prim_cull_info, 8 + GFX9_SGPR_SMALL_PRIM_CULL_INFO);
}
- unsigned vgpr = 8 + SI_NUM_VS_STATE_RESOURCE_SGPRS;
+ unsigned vgpr = 8 + GFX9_GS_NUM_USER_SGPR;
ret = si_insert_input_ret_float(ctx, ret, ctx->args.gs_vtx_offset[0], vgpr++);
ret = si_insert_input_ret_float(ctx, ret, ctx->args.gs_vtx_offset[1], vgpr++);
diff --git a/src/gallium/drivers/radeonsi/si_state_draw.cpp b/src/gallium/drivers/radeonsi/si_state_draw.cpp
index 2add72f5623..2a8a9f1b1f0 100644
--- a/src/gallium/drivers/radeonsi/si_state_draw.cpp
+++ b/src/gallium/drivers/radeonsi/si_state_draw.cpp
@@ -2267,10 +2267,10 @@ static void si_draw(struct pipe_context *ctx,
if (GFX_VERSION >= GFX10) {
struct si_shader_selector *hw_vs = si_get_vs_inline(sctx, HAS_TESS, HAS_GS)->cso;
- if (NGG && !HAS_GS &&
- /* Tessellation sets ngg_cull_vert_threshold to UINT_MAX if the prim type
- * is not points, so this check is only needed without tessellation. */
- (HAS_TESS || util_rast_prim_is_lines_or_triangles(sctx->current_rast_prim)) &&
+ if (NGG &&
+ /* Tessellation and GS set ngg_cull_vert_threshold to UINT_MAX if the prim type
+ * is not points, so this check is only needed for VS. */
+ (HAS_TESS || HAS_GS || util_rast_prim_is_lines_or_triangles(sctx->current_rast_prim)) &&
/* Only the first draw for a shader starts with culling disabled and it's disabled
* until we pass the total_direct_count check and then it stays enabled until
* the shader is changed. This eliminates most culling on/off state changes. */
diff --git a/src/gallium/drivers/radeonsi/si_state_shaders.cpp b/src/gallium/drivers/radeonsi/si_state_shaders.cpp
index d005a44a6e7..81be1e07bd6 100644
--- a/src/gallium/drivers/radeonsi/si_state_shaders.cpp
+++ b/src/gallium/drivers/radeonsi/si_state_shaders.cpp
@@ -3017,11 +3017,12 @@ static void *si_create_shader_selector(struct pipe_context *ctx,
bool ngg_culling_allowed =
sscreen->info.chip_class >= GFX10 &&
sscreen->use_ngg_culling &&
- (sel->info.stage == MESA_SHADER_VERTEX ||
- sel->info.stage == MESA_SHADER_TESS_EVAL) &&
sel->info.writes_position &&
!sel->info.writes_viewport_index && /* cull only against viewport 0 */
- !sel->info.base.writes_memory && !sel->so.num_outputs &&
+ !sel->info.base.writes_memory &&
+ /* NGG GS supports culling with streamout because it culls after streamout. */
+ (sel->info.stage == MESA_SHADER_GEOMETRY || !sel->so.num_outputs) &&
+ (sel->info.stage != MESA_SHADER_GEOMETRY || sel->info.num_stream_output_components[0]) &&
(sel->info.stage != MESA_SHADER_VERTEX ||
(!sel->info.base.vs.blit_sgprs_amd &&
!sel->info.base.vs.window_space_position));
@@ -3034,7 +3035,8 @@ static void *si_create_shader_selector(struct pipe_context *ctx,
sel->ngg_cull_vert_threshold = 0; /* always enabled */
else
sel->ngg_cull_vert_threshold = 128;
- } else if (sel->info.stage == MESA_SHADER_TESS_EVAL) {
+ } else if (sel->info.stage == MESA_SHADER_TESS_EVAL ||
+ sel->info.stage == MESA_SHADER_GEOMETRY) {
if (sel->rast_prim != PIPE_PRIM_POINTS)
sel->ngg_cull_vert_threshold = 0; /* always enabled */
}
More information about the mesa-commit
mailing list