From 01b757e2b0fb97a146b0ef278b449cecab0d15e8 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 21 Oct 2014 10:30:49 -0400 Subject: [PATCH] freedreno: clear vs scissor The optimization of avoiding restore (mem2gmem) if there was a clear falls down a bit if you don't have a fullscreen scissor. We need to make the decision logic a bit more clever to keep track of *what* was cleared, so that we can (a) completely skip mem2gmem if entire buffer was cleared, or (b) skip mem2gmem on a per-tile basis for tiles that were completely cleared. Signed-off-by: Rob Clark --- src/gallium/drivers/freedreno/a2xx/fd2_gmem.c | 4 +- src/gallium/drivers/freedreno/a3xx/fd3_gmem.c | 4 +- .../drivers/freedreno/freedreno_context.c | 4 +- .../drivers/freedreno/freedreno_context.h | 14 +++++- .../drivers/freedreno/freedreno_draw.c | 28 +++++++++-- .../drivers/freedreno/freedreno_gmem.c | 48 ++++++++++++++++++- .../drivers/freedreno/freedreno_gmem.h | 7 ++- 7 files changed, 96 insertions(+), 13 deletions(-) diff --git a/src/gallium/drivers/freedreno/a2xx/fd2_gmem.c b/src/gallium/drivers/freedreno/a2xx/fd2_gmem.c index 274b6145fde..e0aae1c7865 100644 --- a/src/gallium/drivers/freedreno/a2xx/fd2_gmem.c +++ b/src/gallium/drivers/freedreno/a2xx/fd2_gmem.c @@ -317,10 +317,10 @@ fd2_emit_tile_mem2gmem(struct fd_context *ctx, struct fd_tile *tile) OUT_RING(ring, CP_REG(REG_A2XX_PA_CL_CLIP_CNTL)); OUT_RING(ring, 0x00000000); - if (ctx->restore & (FD_BUFFER_DEPTH | FD_BUFFER_STENCIL)) + if (fd_gmem_needs_restore(ctx, tile, FD_BUFFER_DEPTH | FD_BUFFER_STENCIL)) emit_mem2gmem_surf(ctx, bin_w * bin_h, pfb->zsbuf); - if (ctx->restore & FD_BUFFER_COLOR) + if (fd_gmem_needs_restore(ctx, tile, FD_BUFFER_COLOR)) emit_mem2gmem_surf(ctx, 0, pfb->cbufs[0]); /* TODO blob driver seems to toss in a CACHE_FLUSH after each DRAW_INDX.. */ diff --git a/src/gallium/drivers/freedreno/a3xx/fd3_gmem.c b/src/gallium/drivers/freedreno/a3xx/fd3_gmem.c index 2eefa9119dc..f454db28a5e 100644 --- a/src/gallium/drivers/freedreno/a3xx/fd3_gmem.c +++ b/src/gallium/drivers/freedreno/a3xx/fd3_gmem.c @@ -566,10 +566,10 @@ fd3_emit_tile_mem2gmem(struct fd_context *ctx, struct fd_tile *tile) bin_w = gmem->bin_w; bin_h = gmem->bin_h; - if (ctx->restore & (FD_BUFFER_DEPTH | FD_BUFFER_STENCIL)) + if (fd_gmem_needs_restore(ctx, tile, FD_BUFFER_DEPTH | FD_BUFFER_STENCIL)) emit_mem2gmem_surf(ctx, depth_base(ctx), pfb->zsbuf, bin_w); - if (ctx->restore & FD_BUFFER_COLOR) + if (fd_gmem_needs_restore(ctx, tile, FD_BUFFER_COLOR)) emit_mem2gmem_surf(ctx, 0, pfb->cbufs[0], bin_w); OUT_PKT0(ring, REG_A3XX_GRAS_SC_CONTROL, 1); diff --git a/src/gallium/drivers/freedreno/freedreno_context.c b/src/gallium/drivers/freedreno/freedreno_context.c index 3a8545f6716..f7e63fdb2d4 100644 --- a/src/gallium/drivers/freedreno/freedreno_context.c +++ b/src/gallium/drivers/freedreno/freedreno_context.c @@ -100,7 +100,7 @@ fd_context_render(struct pipe_context *pctx) if (!ctx->needs_flush) return; - fd_gmem_render_tiles(pctx); + fd_gmem_render_tiles(ctx); DBG("%p/%p/%p", ctx->ring->start, ctx->ring->cur, ctx->ring->end); @@ -111,7 +111,7 @@ fd_context_render(struct pipe_context *pctx) fd_context_next_rb(pctx); ctx->needs_flush = false; - ctx->cleared = ctx->restore = ctx->resolve = 0; + ctx->cleared = ctx->partial_cleared = ctx->restore = ctx->resolve = 0; ctx->gmem_reason = 0; ctx->num_draws = 0; diff --git a/src/gallium/drivers/freedreno/freedreno_context.h b/src/gallium/drivers/freedreno/freedreno_context.h index be2c2638a17..22d950c090c 100644 --- a/src/gallium/drivers/freedreno/freedreno_context.h +++ b/src/gallium/drivers/freedreno/freedreno_context.h @@ -182,6 +182,10 @@ struct fd_context { * there was a glClear() that invalidated the entire previous buffer * contents. Keep track of which buffer(s) are cleared, or needs * restore. Masks of PIPE_CLEAR_* + * + * The 'cleared' bits will be set for buffers which are *entirely* + * cleared, and 'partial_cleared' bits will be set if you must + * check cleared_scissor. */ enum { /* align bitmask values w/ PIPE_CLEAR_*.. since that is convenient.. */ @@ -189,7 +193,7 @@ struct fd_context { FD_BUFFER_DEPTH = PIPE_CLEAR_DEPTH, FD_BUFFER_STENCIL = PIPE_CLEAR_STENCIL, FD_BUFFER_ALL = FD_BUFFER_COLOR | FD_BUFFER_DEPTH | FD_BUFFER_STENCIL, - } cleared, restore, resolve; + } cleared, partial_cleared, restore, resolve; bool needs_flush; @@ -276,6 +280,14 @@ struct fd_context { */ struct pipe_scissor_state max_scissor; + /* Track the cleared scissor for color/depth/stencil, so we know + * which, if any, tiles need to be restored (mem2gmem). Only valid + * if the corresponding bit in ctx->cleared is set. + */ + struct { + struct pipe_scissor_state color, depth, stencil; + } cleared_scissor; + /* Current gmem/tiling configuration.. gets updated on render_tiles() * if out of date with current maximal-scissor/cpp: */ diff --git a/src/gallium/drivers/freedreno/freedreno_draw.c b/src/gallium/drivers/freedreno/freedreno_draw.c index 897f26a6534..525215e4cbb 100644 --- a/src/gallium/drivers/freedreno/freedreno_draw.c +++ b/src/gallium/drivers/freedreno/freedreno_draw.c @@ -107,7 +107,7 @@ fd_draw_vbo(struct pipe_context *pctx, const struct pipe_draw_info *info) ctx->stats.prims_emitted += u_reduced_prims_for_vertices(info->mode, info->count); - /* any buffers that haven't been cleared, we need to restore: */ + /* any buffers that haven't been cleared yet, we need to restore: */ ctx->restore |= buffers & (FD_BUFFER_ALL & ~ctx->cleared); /* and any buffers used, need to be resolved: */ ctx->resolve |= buffers; @@ -145,8 +145,30 @@ fd_clear(struct pipe_context *pctx, unsigned buffers, { struct fd_context *ctx = fd_context(pctx); struct pipe_framebuffer_state *pfb = &ctx->framebuffer; - - ctx->cleared |= buffers; + struct pipe_scissor_state *scissor = fd_context_get_scissor(ctx); + unsigned cleared_buffers; + + /* for bookkeeping about which buffers have been cleared (and thus + * can fully or partially skip mem2gmem) we need to ignore buffers + * that have already had a draw, in case apps do silly things like + * clear after draw (ie. if you only clear the color buffer, but + * something like alpha-test causes side effects from the draw in + * the depth buffer, etc) + */ + cleared_buffers = buffers & (FD_BUFFER_ALL & ~ctx->restore); + + /* do we have full-screen scissor? */ + if (!memcmp(scissor, &ctx->disabled_scissor, sizeof(*scissor))) { + ctx->cleared |= cleared_buffers; + } else { + ctx->partial_cleared |= cleared_buffers; + if (cleared_buffers & PIPE_CLEAR_COLOR) + ctx->cleared_scissor.color = *scissor; + if (cleared_buffers & PIPE_CLEAR_DEPTH) + ctx->cleared_scissor.depth = *scissor; + if (cleared_buffers & PIPE_CLEAR_STENCIL) + ctx->cleared_scissor.stencil = *scissor; + } ctx->resolve |= buffers; ctx->needs_flush = true; diff --git a/src/gallium/drivers/freedreno/freedreno_gmem.c b/src/gallium/drivers/freedreno/freedreno_gmem.c index 7e43c2e74ce..7f6c8476cdb 100644 --- a/src/gallium/drivers/freedreno/freedreno_gmem.c +++ b/src/gallium/drivers/freedreno/freedreno_gmem.c @@ -314,9 +314,8 @@ render_sysmem(struct fd_context *ctx) } void -fd_gmem_render_tiles(struct pipe_context *pctx) +fd_gmem_render_tiles(struct fd_context *ctx) { - struct fd_context *ctx = fd_context(pctx); struct pipe_framebuffer_state *pfb = &ctx->framebuffer; uint32_t timestamp = 0; bool sysmem = false; @@ -383,3 +382,48 @@ fd_gmem_render_tiles(struct pipe_context *pctx) ctx->dirty = ~0; } + +/* tile needs restore if it isn't completely contained within the + * cleared scissor: + */ +static bool +skip_restore(struct pipe_scissor_state *scissor, struct fd_tile *tile) +{ + unsigned minx = tile->xoff; + unsigned maxx = tile->xoff + tile->bin_w; + unsigned miny = tile->yoff; + unsigned maxy = tile->yoff + tile->bin_h; + return (minx >= scissor->minx) && (maxx <= scissor->maxx) && + (miny >= scissor->miny) && (maxy <= scissor->maxy); +} + +/* When deciding whether a tile needs mem2gmem, we need to take into + * account the scissor rect(s) that were cleared. To simplify we only + * consider the last scissor rect for each buffer, since the common + * case would be a single clear. + */ +bool +fd_gmem_needs_restore(struct fd_context *ctx, struct fd_tile *tile, + uint32_t buffers) +{ + if (!(ctx->restore & buffers)) + return false; + + /* if buffers partially cleared, then slow-path to figure out + * if this particular tile needs restoring: + */ + if ((buffers & FD_BUFFER_COLOR) && + (ctx->partial_cleared & FD_BUFFER_COLOR) && + skip_restore(&ctx->cleared_scissor.color, tile)) + return false; + if ((buffers & FD_BUFFER_DEPTH) && + (ctx->partial_cleared & FD_BUFFER_DEPTH) && + skip_restore(&ctx->cleared_scissor.depth, tile)) + return false; + if ((buffers & FD_BUFFER_STENCIL) && + (ctx->partial_cleared & FD_BUFFER_STENCIL) && + skip_restore(&ctx->cleared_scissor.stencil, tile)) + return false; + + return true; +} diff --git a/src/gallium/drivers/freedreno/freedreno_gmem.h b/src/gallium/drivers/freedreno/freedreno_gmem.h index c7c687419b4..ff322df3c2f 100644 --- a/src/gallium/drivers/freedreno/freedreno_gmem.h +++ b/src/gallium/drivers/freedreno/freedreno_gmem.h @@ -55,6 +55,11 @@ struct fd_gmem_stateobj { bool has_zs; /* gmem config using depth/stencil? */ }; -void fd_gmem_render_tiles(struct pipe_context *pctx); +struct fd_context; + +void fd_gmem_render_tiles(struct fd_context *ctx); + +bool fd_gmem_needs_restore(struct fd_context *ctx, struct fd_tile *tile, + uint32_t buffers); #endif /* FREEDRENO_GMEM_H_ */ -- 2.30.2