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 <robdcl...@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 <robcl...@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 <robcl...@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 <robcl...@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 <robcl...@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 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev