Mesa (master): freedreno: clear vs scissor

Rob Clark robclark at kemper.freedesktop.org
Wed Oct 22 00:09:13 UTC 2014


Module: Mesa
Branch: master
Commit: 01b757e2b0fb97a146b0ef278b449cecab0d15e8
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=01b757e2b0fb97a146b0ef278b449cecab0d15e8

Author: Rob Clark <robclark at freedesktop.org>
Date:   Tue Oct 21 10:30:49 2014 -0400

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 <robclark at freedesktop.org>

---

 src/gallium/drivers/freedreno/a2xx/fd2_gmem.c     |    4 +-
 src/gallium/drivers/freedreno/a3xx/fd3_gmem.c     |    4 +-
 src/gallium/drivers/freedreno/freedreno_context.c |    4 +-
 src/gallium/drivers/freedreno/freedreno_context.h |   14 +++++-
 src/gallium/drivers/freedreno/freedreno_draw.c    |   28 ++++++++++--
 src/gallium/drivers/freedreno/freedreno_gmem.c    |   48 ++++++++++++++++++++-
 src/gallium/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 274b614..e0aae1c 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 2eefa91..f454db2 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 3a8545f..f7e63fd 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 be2c263..22d950c 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 897f26a..525215e 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 7e43c2e..7f6c847 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 c7c6874..ff322df 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_ */




More information about the mesa-commit mailing list