+ ctx->emit_tile_prep(batch, tile);
+
+ if (batch->restore) {
+ ctx->emit_tile_mem2gmem(batch, tile);
+ }
+
+ ctx->emit_tile_renderprep(batch, tile);
+
+ if (ctx->query_prepare_tile)
+ ctx->query_prepare_tile(batch, i, batch->gmem);
+
+ /* emit IB to drawcmds: */
+ ctx->emit_ib(batch->gmem, batch->draw);
+ fd_reset_wfi(batch);
+
+ /* emit gmem2mem to transfer tile back to system memory: */
+ ctx->emit_tile_gmem2mem(batch, tile);
+ }
+
+ if (ctx->emit_tile_fini)
+ ctx->emit_tile_fini(batch);
+}
+
+static void
+render_sysmem(struct fd_batch *batch)
+{
+ struct fd_context *ctx = batch->ctx;
+
+ ctx->emit_sysmem_prep(batch);
+
+ if (ctx->query_prepare_tile)
+ ctx->query_prepare_tile(batch, 0, batch->gmem);
+
+ /* emit IB to drawcmds: */
+ ctx->emit_ib(batch->gmem, batch->draw);
+ fd_reset_wfi(batch);
+
+ if (ctx->emit_sysmem_fini)
+ ctx->emit_sysmem_fini(batch);
+}
+
+static void
+flush_ring(struct fd_batch *batch)
+{
+ uint32_t timestamp;
+ int out_fence_fd = -1;
+
+ fd_submit_flush(batch->submit, batch->in_fence_fd,
+ batch->needs_out_fence_fd ? &out_fence_fd : NULL,
+ ×tamp);
+
+ fd_fence_populate(batch->fence, timestamp, out_fence_fd);
+}
+
+void
+fd_gmem_render_tiles(struct fd_batch *batch)
+{
+ struct fd_context *ctx = batch->ctx;
+ struct pipe_framebuffer_state *pfb = &batch->framebuffer;
+ bool sysmem = false;
+
+ if (ctx->emit_sysmem_prep && !batch->nondraw) {
+ if (batch->cleared || batch->gmem_reason ||
+ ((batch->num_draws > 5) && !batch->blit) ||
+ (pfb->samples > 1)) {
+ DBG("GMEM: cleared=%x, gmem_reason=%x, num_draws=%u, samples=%u",
+ batch->cleared, batch->gmem_reason, batch->num_draws,
+ pfb->samples);
+ } else if (!(fd_mesa_debug & FD_DBG_NOBYPASS)) {
+ sysmem = true;
+ }
+
+ /* For ARB_framebuffer_no_attachments: */
+ if ((pfb->nr_cbufs == 0) && !pfb->zsbuf) {
+ sysmem = true;
+ }
+ }
+
+ fd_reset_wfi(batch);
+
+ ctx->stats.batch_total++;
+
+ if (batch->nondraw) {
+ DBG("%p: rendering non-draw", batch);
+ ctx->stats.batch_nondraw++;
+ } else if (sysmem) {
+ DBG("%p: rendering sysmem %ux%u (%s/%s), num_draws=%u",
+ batch, pfb->width, pfb->height,
+ util_format_short_name(pipe_surface_format(pfb->cbufs[0])),
+ util_format_short_name(pipe_surface_format(pfb->zsbuf)),
+ batch->num_draws);
+ if (ctx->query_prepare)
+ ctx->query_prepare(batch, 1);
+ render_sysmem(batch);
+ ctx->stats.batch_sysmem++;
+ } else {
+ struct fd_gmem_stateobj *gmem = &ctx->gmem;
+ calculate_tiles(batch);
+ DBG("%p: rendering %dx%d tiles %ux%u (%s/%s)",
+ batch, pfb->width, pfb->height, gmem->nbins_x, gmem->nbins_y,
+ util_format_short_name(pipe_surface_format(pfb->cbufs[0])),
+ util_format_short_name(pipe_surface_format(pfb->zsbuf)));
+ if (ctx->query_prepare)
+ ctx->query_prepare(batch, gmem->nbins_x * gmem->nbins_y);
+ render_tiles(batch);
+ ctx->stats.batch_gmem++;
+ }
+
+ flush_ring(batch);
+}
+
+/* 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_batch *batch, struct fd_tile *tile,
+ uint32_t buffers)
+{
+ if (!(batch->restore & buffers))
+ return false;
+
+ return true;