+
+ if (BIN_DEBUG) {
+ t = 0;
+ for (i = 0; i < gmem->nbins_y; i++) {
+ for (j = 0; j < gmem->nbins_x; j++) {
+ struct fd_tile *tile = &gmem->tile[t++];
+ printf("|p:%u n:%u|", tile->p, tile->n);
+ }
+ printf("\n");
+ }
+ }
+
+ return gmem;
+}
+
+void
+__fd_gmem_destroy(struct fd_gmem_stateobj *gmem)
+{
+ struct fd_gmem_cache *cache = &gmem->screen->gmem_cache;
+
+ fd_screen_assert_locked(gmem->screen);
+
+ _mesa_hash_table_remove_key(cache->ht, gmem->key);
+ list_del(&gmem->node);
+
+ ralloc_free(gmem->key);
+ ralloc_free(gmem);
+}
+
+static struct gmem_key *
+gmem_key_init(struct fd_batch *batch, bool assume_zs, bool no_scis_opt)
+{
+ struct fd_screen *screen = batch->ctx->screen;
+ struct pipe_framebuffer_state *pfb = &batch->framebuffer;
+ bool has_zs = pfb->zsbuf && !!(batch->gmem_reason & (FD_GMEM_DEPTH_ENABLED |
+ FD_GMEM_STENCIL_ENABLED | FD_GMEM_CLEARS_DEPTH_STENCIL));
+ struct gmem_key *key = rzalloc(screen->gmem_cache.ht, struct gmem_key);
+
+ if (has_zs || assume_zs) {
+ struct fd_resource *rsc = fd_resource(pfb->zsbuf->texture);
+ key->zsbuf_cpp[0] = rsc->layout.cpp;
+ if (rsc->stencil)
+ key->zsbuf_cpp[1] = rsc->stencil->layout.cpp;
+ } else {
+ /* we might have a zsbuf, but it isn't used */
+ batch->restore &= ~(FD_BUFFER_DEPTH | FD_BUFFER_STENCIL);
+ batch->resolve &= ~(FD_BUFFER_DEPTH | FD_BUFFER_STENCIL);
+ }
+
+ key->nr_cbufs = pfb->nr_cbufs;
+ for (unsigned i = 0; i < pfb->nr_cbufs; i++) {
+ if (pfb->cbufs[i])
+ key->cbuf_cpp[i] = util_format_get_blocksize(pfb->cbufs[i]->format);
+ else
+ key->cbuf_cpp[i] = 4;
+ /* if MSAA, color buffers are super-sampled in GMEM: */
+ key->cbuf_cpp[i] *= pfb->samples;
+ }
+
+ /* NOTE: on a6xx, the max-scissor-rect is handled in fd6_gmem, and
+ * we just rely on CP_COND_EXEC to skip bins with no geometry.
+ */
+ if (no_scis_opt || is_a6xx(screen)) {
+ key->minx = 0;
+ key->miny = 0;
+ key->width = pfb->width;
+ key->height = pfb->height;
+ } else {
+ struct pipe_scissor_state *scissor = &batch->max_scissor;
+
+ if (fd_mesa_debug & FD_DBG_NOSCIS) {
+ scissor->minx = 0;
+ scissor->miny = 0;
+ scissor->maxx = pfb->width;
+ scissor->maxy = pfb->height;
+ }
+
+ /* round down to multiple of alignment: */
+ key->minx = scissor->minx & ~(screen->gmem_alignw - 1);
+ key->miny = scissor->miny & ~(screen->gmem_alignh - 1);
+ key->width = scissor->maxx - key->minx;
+ key->height = scissor->maxy - key->miny;
+ }
+
+ if (is_a20x(screen) && batch->cleared) {
+ /* under normal circumstances the requirement would be 4K
+ * but the fast clear path requires an alignment of 32K
+ */
+ key->gmem_page_align = 8;
+ } else if (is_a6xx(screen)) {
+ key->gmem_page_align = is_a650(screen) ? 3 : 1;
+ } else {
+ // TODO re-check this across gens.. maybe it should only
+ // be a single page in some cases:
+ key->gmem_page_align = 4;
+ }
+
+ return key;
+}
+
+static struct fd_gmem_stateobj *
+lookup_gmem_state(struct fd_batch *batch, bool assume_zs, bool no_scis_opt)
+{
+ struct fd_screen *screen = batch->ctx->screen;
+ struct fd_gmem_cache *cache = &screen->gmem_cache;
+ struct fd_gmem_stateobj *gmem = NULL;
+ struct gmem_key *key = gmem_key_init(batch, assume_zs, no_scis_opt);
+ uint32_t hash = gmem_key_hash(key);
+
+ fd_screen_lock(screen);
+
+ struct hash_entry *entry =
+ _mesa_hash_table_search_pre_hashed(cache->ht, hash, key);
+ if (entry) {
+ ralloc_free(key);
+ goto found;
+ }
+
+ /* limit the # of cached gmem states, discarding the least
+ * recently used state if needed:
+ */
+ if (cache->ht->entries >= 20) {
+ struct fd_gmem_stateobj *last =
+ list_last_entry(&cache->lru, struct fd_gmem_stateobj, node);
+ fd_gmem_reference(&last, NULL);
+ }
+
+ entry = _mesa_hash_table_insert_pre_hashed(cache->ht,
+ hash, key, gmem_stateobj_init(screen, key));
+
+found:
+ fd_gmem_reference(&gmem, entry->data);
+ /* Move to the head of the LRU: */
+ list_delinit(&gmem->node);
+ list_add(&gmem->node, &cache->lru);
+
+ fd_screen_unlock(screen);
+
+ return gmem;
+}
+
+/*
+ * GMEM render pass
+ */
+
+static void
+render_tiles(struct fd_batch *batch, struct fd_gmem_stateobj *gmem)
+{
+ struct fd_context *ctx = batch->ctx;
+ int i;
+
+ mtx_lock(&ctx->gmem_lock);
+
+ ctx->emit_tile_init(batch);
+
+ if (batch->restore)
+ ctx->stats.batch_restore++;
+
+ for (i = 0; i < (gmem->nbins_x * gmem->nbins_y); i++) {
+ struct fd_tile *tile = &gmem->tile[i];
+
+ fd_log(batch, "bin_h=%d, yoff=%d, bin_w=%d, xoff=%d",
+ tile->bin_h, tile->yoff, tile->bin_w, tile->xoff);
+
+ 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: */
+ fd_log(batch, "TILE[%d]: START DRAW IB", i);
+ if (ctx->emit_tile) {
+ ctx->emit_tile(batch, tile);
+ } else {
+ ctx->screen->emit_ib(batch->gmem, batch->draw);
+ }
+
+ fd_log(batch, "TILE[%d]: END DRAW IB", i);
+ 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);
+
+ mtx_unlock(&ctx->gmem_lock);