[Freedreno] [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 Freedreno
mailing list