From fc6a8154018f4628c3524c28417ef2aaa9658958 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 16 Feb 2021 14:57:40 -0800 Subject: [PATCH] freedreno/a5xx: Fix stream-output binning handling. This makes it match (to the best I was able) a6xx's behavior, with the exception of the XXX note in fd5_gmem.c highlighting what I think is an issue on both a5xx and a6xx. Part-of: --- src/gallium/drivers/freedreno/a5xx/fd5_emit.h | 5 ++- src/gallium/drivers/freedreno/a5xx/fd5_gmem.c | 19 +++++++++++ .../drivers/freedreno/a5xx/fd5_program.c | 33 ++++++++++++------- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/gallium/drivers/freedreno/a5xx/fd5_emit.h b/src/gallium/drivers/freedreno/a5xx/fd5_emit.h index 44f32357414..43b079a6de7 100644 --- a/src/gallium/drivers/freedreno/a5xx/fd5_emit.h +++ b/src/gallium/drivers/freedreno/a5xx/fd5_emit.h @@ -79,9 +79,12 @@ static inline const struct ir3_shader_variant * fd5_emit_get_vp(struct fd5_emit *emit) { if (!emit->vs) { + /* We use nonbinning VS during binning when TFB is enabled because that + * is what has all the outputs that might be involved in TFB. + */ struct ir3_shader *shader = ir3_get_shader(emit->prog->vs); emit->vs = ir3_shader_variant(shader, emit->key, - emit->binning_pass, emit->debug); + emit->binning_pass && !shader->stream_output.num_outputs, emit->debug); } return emit->vs; } diff --git a/src/gallium/drivers/freedreno/a5xx/fd5_gmem.c b/src/gallium/drivers/freedreno/a5xx/fd5_gmem.c index da6196ac481..c3c33dfd776 100644 --- a/src/gallium/drivers/freedreno/a5xx/fd5_gmem.c +++ b/src/gallium/drivers/freedreno/a5xx/fd5_gmem.c @@ -391,8 +391,19 @@ fd5_emit_tile_init(struct fd_batch *batch) emit_zs(ring, pfb->zsbuf, batch->gmem_state); emit_mrt(ring, pfb->nr_cbufs, pfb->cbufs, batch->gmem_state); + /* Enable stream output for the first pass (likely the binning). */ + OUT_PKT4(ring, REG_A5XX_VPC_SO_OVERRIDE, 1); + OUT_RING(ring, 0); + if (use_hw_binning(batch)) { emit_binning_pass(batch); + + /* Disable stream output after binning, since each VS output should get + * streamed out once. + */ + OUT_PKT4(ring, REG_A5XX_VPC_SO_OVERRIDE, 1); + OUT_RING(ring, A5XX_VPC_SO_OVERRIDE_SO_DISABLE); + fd5_emit_lrz_flush(batch, ring); patch_draws(batch, USE_VISIBILITY); } else { @@ -400,6 +411,10 @@ fd5_emit_tile_init(struct fd_batch *batch) } fd5_set_render_mode(batch->ctx, ring, GMEM); + + /* XXX If we're in gmem mode but not doing HW binning, then after the first + * tile we should disable stream output (fd6_gmem.c doesn't do that either). + */ } /* before mem2gmem */ @@ -745,6 +760,10 @@ fd5_emit_sysmem_prep(struct fd_batch *batch) OUT_RING(ring, A5XX_RB_WINDOW_OFFSET_X(0) | A5XX_RB_WINDOW_OFFSET_Y(0)); + /* Enable stream output, since there's no binning pass to put it in. */ + OUT_PKT4(ring, REG_A5XX_VPC_SO_OVERRIDE, 1); + OUT_RING(ring, 0); + OUT_PKT7(ring, CP_SET_VISIBILITY_OVERRIDE, 1); OUT_RING(ring, 0x1); diff --git a/src/gallium/drivers/freedreno/a5xx/fd5_program.c b/src/gallium/drivers/freedreno/a5xx/fd5_program.c index bf2015599e9..474499e1d4a 100644 --- a/src/gallium/drivers/freedreno/a5xx/fd5_program.c +++ b/src/gallium/drivers/freedreno/a5xx/fd5_program.c @@ -247,6 +247,8 @@ fd5_program_emit(struct fd_context *ctx, struct fd_ringbuffer *ring, setup_stages(emit, s); + bool do_streamout = (s[VS].v->shader->stream_output.num_outputs > 0); + fssz = (s[FS].i->max_reg >= 24) ? TWO_QUADS : FOUR_QUADS; pos_regid = ir3_find_output_regid(s[VS].v, VARYING_SLOT_POS); @@ -364,8 +366,17 @@ fd5_program_emit(struct fd_context *ctx, struct fd_ringbuffer *ring, A5XX_SP_VS_CTRL_REG0_BRANCHSTACK(s[VS].v->branchstack) | COND(s[VS].v->need_pixlod, A5XX_SP_VS_CTRL_REG0_PIXLODENABLE)); + /* If we have streamout, link against the real FS in the binning program, + * rather than the dummy FS used for binning pass state, to ensure the + * OUTLOC's match. Depending on whether we end up doing sysmem or gmem, the + * actual streamout could happen with either the binning pass or draw pass + * program, but the same streamout stateobj is used in either case: + */ + const struct ir3_shader_variant *link_fs = s[FS].v; + if (do_streamout && emit->binning_pass) + link_fs = ir3_shader_variant(ir3_get_shader(emit->prog->fs), emit->key, false, emit->debug); struct ir3_shader_linkage l = {0}; - ir3_link_shaders(&l, s[VS].v, s[FS].v, true); + ir3_link_shaders(&l, s[VS].v, link_fs, true); OUT_PKT4(ring, REG_A5XX_VPC_VAR_DISABLE(0), 4); OUT_RING(ring, ~l.varmask[0]); /* VPC_VAR[0].DISABLE */ @@ -373,8 +384,8 @@ fd5_program_emit(struct fd_context *ctx, struct fd_ringbuffer *ring, OUT_RING(ring, ~l.varmask[2]); /* VPC_VAR[2].DISABLE */ OUT_RING(ring, ~l.varmask[3]); /* VPC_VAR[3].DISABLE */ - if (!emit->binning_pass) - ir3_link_stream_out(&l, s[VS].v); + /* Add stream out outputs after computing the VPC_VAR_DISABLE bitmask. */ + ir3_link_stream_out(&l, s[VS].v); /* a5xx appends pos/psize to end of the linkage map: */ if (pos_regid != regid(63,0)) @@ -385,17 +396,15 @@ fd5_program_emit(struct fd_context *ctx, struct fd_ringbuffer *ring, ir3_link_add(&l, psize_regid, 0x1, l.max_loc); } - if ((s[VS].v->shader->stream_output.num_outputs > 0) && - !emit->binning_pass) { + /* If we have stream-out, we use the full shader for binning + * pass, rather than the optimized binning pass one, so that we + * have all the varying outputs available for xfb. So streamout + * state should always be derived from the non-binning pass + * program: + */ + if (do_streamout && !emit->binning_pass) emit_stream_out(ring, s[VS].v, &l); - OUT_PKT4(ring, REG_A5XX_VPC_SO_OVERRIDE, 1); - OUT_RING(ring, 0x00000000); - } else { - OUT_PKT4(ring, REG_A5XX_VPC_SO_OVERRIDE, 1); - OUT_RING(ring, A5XX_VPC_SO_OVERRIDE_SO_DISABLE); - } - for (i = 0, j = 0; (i < 16) && (j < l.cnt); i++) { uint32_t reg = 0;