+struct panfrost_batch *
+panfrost_get_fresh_batch_for_fbo(struct panfrost_context *ctx)
+{
+ struct panfrost_batch *batch;
+
+ batch = panfrost_get_batch(ctx, &ctx->pipe_framebuffer);
+
+ /* The batch has no draw/clear queued, let's return it directly.
+ * Note that it's perfectly fine to re-use a batch with an
+ * existing clear, we'll just update it with the new clear request.
+ */
+ if (!batch->scoreboard.first_job)
+ return batch;
+
+ /* Otherwise, we need to freeze the existing one and instantiate a new
+ * one.
+ */
+ panfrost_freeze_batch(batch);
+ return panfrost_get_batch(ctx, &ctx->pipe_framebuffer);
+}
+
+static void
+panfrost_bo_access_gc_fences(struct panfrost_context *ctx,
+ struct panfrost_bo_access *access,
+ const struct panfrost_bo *bo)
+{
+ if (access->writer) {
+ panfrost_batch_fence_unreference(access->writer);
+ access->writer = NULL;
+ }
+
+ struct panfrost_batch_fence **readers_array = util_dynarray_begin(&access->readers);
+ struct panfrost_batch_fence **new_readers = readers_array;
+
+ util_dynarray_foreach(&access->readers, struct panfrost_batch_fence *,
+ reader) {
+ if (!(*reader))
+ continue;
+
+ panfrost_batch_fence_unreference(*reader);
+ *reader = NULL;
+ }
+
+ if (!util_dynarray_resize(&access->readers, struct panfrost_batch_fence *,
+ new_readers - readers_array) &&
+ new_readers != readers_array)
+ unreachable("Invalid dynarray access->readers");
+}
+
+/* Collect signaled fences to keep the kernel-side syncobj-map small. The
+ * idea is to collect those signaled fences at the end of each flush_all
+ * call. This function is likely to collect only fences from previous
+ * batch flushes not the one that have just have just been submitted and
+ * are probably still in flight when we trigger the garbage collection.
+ * Anyway, we need to do this garbage collection at some point if we don't
+ * want the BO access map to keep invalid entries around and retain
+ * syncobjs forever.
+ */
+static void
+panfrost_gc_fences(struct panfrost_context *ctx)
+{
+ hash_table_foreach(ctx->accessed_bos, entry) {
+ struct panfrost_bo_access *access = entry->data;
+
+ assert(access);
+ panfrost_bo_access_gc_fences(ctx, access, entry->key);
+ if (!util_dynarray_num_elements(&access->readers,
+ struct panfrost_batch_fence *) &&
+ !access->writer) {
+ ralloc_free(access);
+ _mesa_hash_table_remove(ctx->accessed_bos, entry);
+ }
+ }
+}
+
+#ifdef PAN_BATCH_DEBUG
+static bool
+panfrost_batch_in_readers(struct panfrost_batch *batch,
+ struct panfrost_bo_access *access)
+{
+ util_dynarray_foreach(&access->readers, struct panfrost_batch_fence *,
+ reader) {
+ if (*reader && (*reader)->batch == batch)
+ return true;
+ }
+
+ return false;
+}
+#endif
+
+static void
+panfrost_batch_update_bo_access(struct panfrost_batch *batch,
+ struct panfrost_bo *bo, bool writes,
+ bool already_accessed)
+{
+ struct panfrost_context *ctx = batch->ctx;
+ struct panfrost_bo_access *access;
+ bool old_writes = false;
+ struct hash_entry *entry;
+
+ entry = _mesa_hash_table_search(ctx->accessed_bos, bo);
+ access = entry ? entry->data : NULL;
+ if (access) {
+ old_writes = access->writer != NULL;
+ } else {
+ access = rzalloc(ctx, struct panfrost_bo_access);
+ util_dynarray_init(&access->readers, access);
+ _mesa_hash_table_insert(ctx->accessed_bos, bo, access);
+ /* We are the first to access this BO, let's initialize
+ * old_writes to our own access type in that case.
+ */
+ old_writes = writes;
+ }
+
+ assert(access);
+
+ if (writes && !old_writes) {
+ /* Previous access was a read and we want to write this BO.
+ * We first need to add explicit deps between our batch and
+ * the previous readers.
+ */
+ util_dynarray_foreach(&access->readers,
+ struct panfrost_batch_fence *, reader) {
+ /* We were already reading the BO, no need to add a dep
+ * on ourself (the acyclic check would complain about
+ * that).
+ */
+ if (!(*reader) || (*reader)->batch == batch)
+ continue;
+
+ panfrost_batch_add_dep(batch, *reader);
+ }
+ panfrost_batch_fence_reference(batch->out_sync);
+
+ if (access->writer)
+ panfrost_batch_fence_unreference(access->writer);
+
+ /* We now are the new writer. */
+ access->writer = batch->out_sync;
+
+ /* Release the previous readers and reset the readers array. */
+ util_dynarray_foreach(&access->readers,
+ struct panfrost_batch_fence *,
+ reader) {
+ if (!*reader)
+ continue;
+ panfrost_batch_fence_unreference(*reader);
+ }
+
+ util_dynarray_clear(&access->readers);
+ } else if (writes && old_writes) {
+ /* First check if we were the previous writer, in that case
+ * there's nothing to do. Otherwise we need to add a
+ * dependency between the new writer and the old one.
+ */
+ if (access->writer != batch->out_sync) {
+ if (access->writer) {
+ panfrost_batch_add_dep(batch, access->writer);
+ panfrost_batch_fence_unreference(access->writer);
+ }
+ panfrost_batch_fence_reference(batch->out_sync);
+ access->writer = batch->out_sync;
+ }
+ } else if (!writes && old_writes) {
+ /* First check if we were the previous writer, in that case
+ * we want to keep the access type unchanged, as a write is
+ * more constraining than a read.
+ */
+ if (access->writer != batch->out_sync) {
+ /* Add a dependency on the previous writer. */
+ panfrost_batch_add_dep(batch, access->writer);
+
+ /* The previous access was a write, there's no reason
+ * to have entries in the readers array.
+ */
+ assert(!util_dynarray_num_elements(&access->readers,
+ struct panfrost_batch_fence *));
+
+ /* Add ourselves to the readers array. */
+ panfrost_batch_fence_reference(batch->out_sync);
+ util_dynarray_append(&access->readers,
+ struct panfrost_batch_fence *,
+ batch->out_sync);
+ access->writer = NULL;
+ }
+ } else {
+ /* We already accessed this BO before, so we should already be
+ * in the reader array.
+ */
+#ifdef PAN_BATCH_DEBUG
+ if (already_accessed) {
+ assert(panfrost_batch_in_readers(batch, access));
+ return;
+ }
+#endif
+
+ /* Previous access was a read and we want to read this BO.
+ * Add ourselves to the readers array and add a dependency on
+ * the previous writer if any.
+ */
+ panfrost_batch_fence_reference(batch->out_sync);
+ util_dynarray_append(&access->readers,
+ struct panfrost_batch_fence *,
+ batch->out_sync);
+
+ if (access->writer)
+ panfrost_batch_add_dep(batch, access->writer);
+ }
+}
+