[Mesa-dev] [PATCH 08/12] freedreno: add batch-cache

Rob Clark robdclark at gmail.com
Sat Jul 2 16:52:11 UTC 2016


Note that I originally also had a entry-point that would construct a key
and do lookup from a pipe_surface.  I ended up not needing that (yet?)
but it is easy-enough to re-introduce later if we need it for the blit
path.

Signed-off-by: Rob Clark <robdclark at gmail.com>
---
 src/gallium/drivers/freedreno/Makefile.sources     |   2 +
 src/gallium/drivers/freedreno/freedreno_batch.c    |   5 +
 src/gallium/drivers/freedreno/freedreno_batch.h    |   6 +
 .../drivers/freedreno/freedreno_batch_cache.c      | 246 +++++++++++++++++++++
 .../drivers/freedreno/freedreno_batch_cache.h      |  51 +++++
 src/gallium/drivers/freedreno/freedreno_context.c  |   4 +
 src/gallium/drivers/freedreno/freedreno_context.h  |   3 +
 src/gallium/drivers/freedreno/freedreno_resource.c |   2 +
 src/gallium/drivers/freedreno/freedreno_resource.h |   6 +
 9 files changed, 325 insertions(+)
 create mode 100644 src/gallium/drivers/freedreno/freedreno_batch_cache.c
 create mode 100644 src/gallium/drivers/freedreno/freedreno_batch_cache.h

diff --git a/src/gallium/drivers/freedreno/Makefile.sources b/src/gallium/drivers/freedreno/Makefile.sources
index 4ba8c9d..92d9186 100644
--- a/src/gallium/drivers/freedreno/Makefile.sources
+++ b/src/gallium/drivers/freedreno/Makefile.sources
@@ -4,6 +4,8 @@ C_SOURCES := \
 	disasm.h \
 	freedreno_batch.c \
 	freedreno_batch.h \
+	freedreno_batch_cache.c \
+	freedreno_batch_cache.h \
 	freedreno_context.c \
 	freedreno_context.h \
 	freedreno_draw.c \
diff --git a/src/gallium/drivers/freedreno/freedreno_batch.c b/src/gallium/drivers/freedreno/freedreno_batch.c
index 1fbce43..5c6ae76 100644
--- a/src/gallium/drivers/freedreno/freedreno_batch.c
+++ b/src/gallium/drivers/freedreno/freedreno_batch.c
@@ -79,7 +79,10 @@ fd_batch_create(struct fd_context *ctx)
 void
 __fd_batch_destroy(struct fd_batch *batch)
 {
+	fd_bc_invalidate_batch(batch);
+
 	util_copy_framebuffer_state(&batch->framebuffer, NULL);
+
 	fd_ringbuffer_del(batch->draw);
 	fd_ringbuffer_del(batch->binning);
 	fd_ringbuffer_del(batch->gmem);
@@ -120,6 +123,8 @@ fd_batch_flush(struct fd_batch *batch)
 	}
 
 	assert(LIST_IS_EMPTY(&batch->used_resources));
+	batch->needs_flush = false;
+	fd_bc_invalidate_batch(batch);
 }
 
 void
diff --git a/src/gallium/drivers/freedreno/freedreno_batch.h b/src/gallium/drivers/freedreno/freedreno_batch.h
index 4607250..d500f95 100644
--- a/src/gallium/drivers/freedreno/freedreno_batch.h
+++ b/src/gallium/drivers/freedreno/freedreno_batch.h
@@ -119,6 +119,12 @@ struct fd_batch {
 
 	/** list of resources used by currently-unsubmitted batch */
 	struct list_head used_resources;
+
+	/** key in batch-cache (if not null): */
+	const void *key;
+
+	/** set of dependent batches.. holds refs to dependent batches: */
+	struct set *dependencies;
 };
 
 struct fd_batch * fd_batch_create(struct fd_context *ctx);
diff --git a/src/gallium/drivers/freedreno/freedreno_batch_cache.c b/src/gallium/drivers/freedreno/freedreno_batch_cache.c
new file mode 100644
index 0000000..bd47251
--- /dev/null
+++ b/src/gallium/drivers/freedreno/freedreno_batch_cache.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2016 Rob Clark <robclark at freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *    Rob Clark <robclark at freedesktop.org>
+ */
+
+#include "util/hash_table.h"
+#include "util/set.h"
+#include "util/list.h"
+#include "util/u_string.h"
+
+#include "freedreno_batch.h"
+#include "freedreno_batch_cache.h"
+#include "freedreno_context.h"
+#include "freedreno_resource.h"
+
+/* Overview:
+ *
+ *   The batch cache provides lookup for mapping pipe_framebuffer_state
+ *   to a batch.
+ *
+ *   It does this via hashtable, with key that roughly matches the
+ *   pipe_framebuffer_state, as described below.
+ *
+ * Batch Cache hashtable key:
+ *
+ *   To serialize the key, and to avoid dealing with holding a reference to
+ *   pipe_surface's (which hold a reference to pipe_resource and complicate
+ *   the whole refcnting thing), the key is variable length and inline's the
+ *   pertinent details of the pipe_surface.
+ *
+ * Batch:
+ *
+ *   Each batch needs to hold a reference to each resource it depends on (ie.
+ *   anything that needs a mem2gmem).  And a weak reference to resources it
+ *   renders to.  (If both src[n] and dst[n] are not NULL then they are the
+ *   same.)
+ *
+ *   When a resource is destroyed, we need to remove entries in the batch
+ *   cache that reference the resource, to avoid dangling pointer issues.
+ *   So each resource holds a hashset of batches which have reference them
+ *   in their hashtable key.
+ *
+ *   When a batch has weak reference to no more resources (ie. all the
+ *   surfaces it rendered to are destroyed) the batch can be destroyed.
+ *   Could happen in an app that renders and never uses the result.  More
+ *   common scenario, I think, will be that some, but not all, of the
+ *   surfaces are destroyed before the batch is submitted.
+ *
+ *   If (for example), batch writes to zsbuf but that surface is destroyed
+ *   before batch is submitted, we can skip gmem2mem (but still need to
+ *   alloc gmem space as before.  If the batch depended on previous contents
+ *   of that surface, it would be holding a reference so the surface would
+ *   not have been destroyed.
+ */
+
+struct key {
+	uint32_t width, height, layers;
+	uint16_t samples, num_surfs;
+	struct {
+		struct pipe_resource *texture;
+		union pipe_surface_desc u;
+		uint16_t pos, format;
+	} surf[0];
+};
+
+static struct key *
+key_alloc(unsigned num_surfs)
+{
+	struct key *key =
+		CALLOC_VARIANT_LENGTH_STRUCT(key, sizeof(key->surf[0]) * num_surfs);
+	key->num_surfs = num_surfs;
+	return key;
+}
+
+static uint32_t
+key_hash(const void *_key)
+{
+	const struct key *key = _key;
+	uint32_t hash = _mesa_fnv32_1a_offset_bias;
+	hash = _mesa_fnv32_1a_accumulate_block(hash, key, offsetof(struct key, surf[0]));
+	hash = _mesa_fnv32_1a_accumulate_block(hash, key->surf, sizeof(key->surf[0]) * key->num_surfs);
+	return hash;
+}
+
+static bool
+key_equals(const void *_a, const void *_b)
+{
+	const struct key *a = _a;
+	const struct key *b = _b;
+	return (memcmp(a, b, offsetof(struct key, surf[0])) == 0) &&
+		(memcmp(a->surf, b->surf, sizeof(a->surf[0]) * a->num_surfs) == 0);
+}
+
+void
+fd_bc_init(struct fd_batch_cache *cache)
+{
+	cache->ht = _mesa_hash_table_create(NULL, key_hash, key_equals);
+}
+
+void
+fd_bc_fini(struct fd_batch_cache *cache)
+{
+	_mesa_hash_table_destroy(cache->ht, NULL);
+}
+
+uint32_t
+fd_bc_flush(struct fd_batch_cache *cache)
+{
+	struct hash_entry *entry;
+	uint32_t timestamp = 0;
+
+	hash_table_foreach(cache->ht, entry) {
+		struct fd_batch *batch = NULL;
+		fd_batch_reference(&batch, (struct fd_batch *)entry->data);
+		fd_batch_flush(batch);
+		timestamp = MAX2(timestamp, fd_ringbuffer_timestamp(batch->gmem));
+		fd_batch_reference(&batch, NULL);
+	}
+
+	return timestamp;
+}
+
+void
+fd_bc_invalidate_batch(struct fd_batch *batch)
+{
+	struct fd_batch_cache *cache = &batch->ctx->batch_cache;
+	struct key *key;
+
+	if (!batch->key)
+		return;
+
+	key = (struct key *)batch->key;
+
+	for (unsigned idx = 0; idx < key->num_surfs; idx++) {
+		struct fd_resource *rsc = fd_resource(key->surf[idx].texture);
+		struct set_entry *entry = _mesa_set_search(rsc->batches, batch);
+		_mesa_set_remove(rsc->batches, entry);
+	}
+
+	struct hash_entry *entry = _mesa_hash_table_search(cache->ht, key);
+	_mesa_hash_table_remove(cache->ht, entry);
+
+	batch->key = NULL;
+	free(key);
+}
+
+void
+fd_bc_invalidate_resource(struct fd_resource *rsc)
+{
+	struct set_entry *entry;
+
+	if (!rsc->batches)
+		return;
+
+	set_foreach(rsc->batches, entry)
+		fd_bc_invalidate_batch((struct fd_batch *)entry->key);
+
+	_mesa_set_destroy(rsc->batches, NULL);
+	rsc->batches = NULL;
+}
+
+static struct fd_batch *
+batch_from_key(struct fd_batch_cache *cache, struct key *key,
+		struct fd_context *ctx)
+{
+	struct fd_batch *batch = NULL;
+	uint32_t hash = key_hash(key);
+	struct hash_entry *entry =
+		_mesa_hash_table_search_pre_hashed(cache->ht, hash, key);
+
+	if (entry) {
+		free(key);
+		fd_batch_reference(&batch, (struct fd_batch *)entry->data);
+		return batch;
+	}
+
+	batch = fd_batch_create(ctx);
+	if (!batch)
+		return NULL;
+
+	_mesa_hash_table_insert_pre_hashed(cache->ht, hash, key, batch);
+	batch->key = key;
+
+	for (unsigned idx = 0; idx < key->num_surfs; idx++) {
+		struct fd_resource *rsc = fd_resource(key->surf[idx].texture);
+		if (!rsc->batches) {
+			rsc->batches = _mesa_set_create(NULL, _mesa_hash_pointer,
+					_mesa_key_pointer_equal);
+		}
+
+		_mesa_set_add(rsc->batches, batch);
+	}
+
+	return batch;
+}
+
+static void
+key_surf(struct key *key, unsigned idx, unsigned pos, struct pipe_surface *psurf)
+{
+	key->surf[idx].texture = psurf->texture;
+	key->surf[idx].u = psurf->u;
+	key->surf[idx].pos = pos;
+	key->surf[idx].format = psurf->format;
+}
+
+struct fd_batch *
+fd_batch_from_fb(struct fd_batch_cache *cache, struct fd_context *ctx,
+		const struct pipe_framebuffer_state *pfb)
+{
+	unsigned idx = 0, n = pfb->nr_cbufs + (pfb->zsbuf ? 1 : 0);
+	struct key *key = key_alloc(n);
+
+	key->width = pfb->width;
+	key->height = pfb->height;
+	key->layers = pfb->layers;
+	key->samples = pfb->samples;
+
+	if (pfb->zsbuf)
+		key_surf(key, idx++, 0, pfb->zsbuf);
+
+	for (unsigned i = 0; i < pfb->nr_cbufs; i++)
+		key_surf(key, idx++, i + 1, pfb->cbufs[i]);
+
+	return batch_from_key(cache, key, ctx);
+}
diff --git a/src/gallium/drivers/freedreno/freedreno_batch_cache.h b/src/gallium/drivers/freedreno/freedreno_batch_cache.h
new file mode 100644
index 0000000..2daab6c
--- /dev/null
+++ b/src/gallium/drivers/freedreno/freedreno_batch_cache.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 Rob Clark <robclark at freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *    Rob Clark <robclark at freedesktop.org>
+ */
+
+#ifndef FREEDRENO_BATCH_CACHE_H_
+#define FREEDRENO_BATCH_CACHE_H_
+
+#include "pipe/p_state.h"
+
+#include "freedreno_batch.h"
+
+struct hash_table;
+
+struct fd_batch_cache {
+	struct hash_table *ht;
+};
+
+void fd_bc_init(struct fd_batch_cache *cache);
+void fd_bc_fini(struct fd_batch_cache *cache);
+
+uint32_t fd_bc_flush(struct fd_batch_cache *cache);
+
+void fd_bc_invalidate_resource(struct fd_resource *rsc);
+void fd_bc_invalidate_batch(struct fd_batch *batch);
+
+struct fd_batch * fd_batch_from_fb(struct fd_batch_cache *cache,
+		struct fd_context *ctx, const struct pipe_framebuffer_state *pfb);
+
+#endif /* FREEDRENO_BATCH_CACHE_H_ */
diff --git a/src/gallium/drivers/freedreno/freedreno_context.c b/src/gallium/drivers/freedreno/freedreno_context.c
index ce30833..4359fb2 100644
--- a/src/gallium/drivers/freedreno/freedreno_context.c
+++ b/src/gallium/drivers/freedreno/freedreno_context.c
@@ -110,6 +110,8 @@ fd_context_destroy(struct pipe_context *pctx)
 
 	DBG("");
 
+	fd_bc_fini(&ctx->batch_cache);
+
 	fd_prog_fini(pctx);
 	fd_hw_query_fini(pctx);
 
@@ -197,6 +199,8 @@ fd_context_init(struct fd_context *ctx, struct pipe_screen *pscreen,
 	if (!ctx->primconvert)
 		goto fail;
 
+	fd_bc_init(&ctx->batch_cache);
+
 	return pctx;
 
 fail:
diff --git a/src/gallium/drivers/freedreno/freedreno_context.h b/src/gallium/drivers/freedreno/freedreno_context.h
index 92d14bb..6be7437 100644
--- a/src/gallium/drivers/freedreno/freedreno_context.h
+++ b/src/gallium/drivers/freedreno/freedreno_context.h
@@ -37,6 +37,7 @@
 #include "util/u_string.h"
 
 #include "freedreno_batch.h"
+#include "freedreno_batch_cache.h"
 #include "freedreno_screen.h"
 #include "freedreno_gmem.h"
 #include "freedreno_util.h"
@@ -141,6 +142,8 @@ struct fd_context {
 	struct fd_device *dev;
 	struct fd_screen *screen;
 
+	struct fd_batch_cache batch_cache;
+
 	struct blitter_context *blitter;
 	struct primconvert_context *primconvert;
 
diff --git a/src/gallium/drivers/freedreno/freedreno_resource.c b/src/gallium/drivers/freedreno/freedreno_resource.c
index a35dc90..4fd8559 100644
--- a/src/gallium/drivers/freedreno/freedreno_resource.c
+++ b/src/gallium/drivers/freedreno/freedreno_resource.c
@@ -35,6 +35,7 @@
 #include "util/u_surface.h"
 
 #include "freedreno_resource.h"
+#include "freedreno_batch_cache.h"
 #include "freedreno_screen.h"
 #include "freedreno_surface.h"
 #include "freedreno_context.h"
@@ -451,6 +452,7 @@ fd_resource_destroy(struct pipe_screen *pscreen,
 		struct pipe_resource *prsc)
 {
 	struct fd_resource *rsc = fd_resource(prsc);
+	fd_bc_invalidate_resource(rsc);
 	if (rsc->bo)
 		fd_bo_del(rsc->bo);
 	fd_batch_reference(&rsc->pending_batch, NULL);
diff --git a/src/gallium/drivers/freedreno/freedreno_resource.h b/src/gallium/drivers/freedreno/freedreno_resource.h
index f8131c7..3b990a9 100644
--- a/src/gallium/drivers/freedreno/freedreno_resource.h
+++ b/src/gallium/drivers/freedreno/freedreno_resource.h
@@ -70,6 +70,8 @@ enum fd_resource_status {
 	FD_PENDING_READ  = 0x02,
 };
 
+struct set;
+
 struct fd_resource {
 	struct u_resource base;
 	struct fd_bo *bo;
@@ -88,11 +90,15 @@ struct fd_resource {
 
 	/* pending read/write state: */
 	enum fd_resource_status status;
+
 	/* resources accessed by queued but not flushed draws are tracked
 	 * in the used_resources list.
 	 */
 	struct list_head list;
 	struct fd_batch *pending_batch;
+
+	/* set of batches whose batch-cache key references this resource: */
+	struct set *batches;
 };
 
 static inline struct fd_resource *
-- 
2.7.4



More information about the mesa-dev mailing list