From 3d138f44601a480b422ecfa2983838af084bad47 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Sat, 11 Mar 2023 17:37:19 -0500 Subject: [PATCH] asahi: Copy resources if needed to shadow This lets us shadow textures updated in the middle of rendering in Quake3. They're big memcpys, but as long as the texture memory is cached it's ok. We use a heuristic to avoid too many memcpys from uncached memory, which would cause slideshow performance in quake. We need to be careful to avoid shadowing shared resources, though, that's invalid and would break WSI pretty hard. It would be better to blit on the GPU for large shadowing, but that's more involved and left for future work. Reduces stuttering in Quake3. Signed-off-by: Alyssa Rosenzweig Part-of: --- src/gallium/drivers/asahi/agx_pipe.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/gallium/drivers/asahi/agx_pipe.c b/src/gallium/drivers/asahi/agx_pipe.c index 3149d9d..dfbb405 100644 --- a/src/gallium/drivers/asahi/agx_pipe.c +++ b/src/gallium/drivers/asahi/agx_pipe.c @@ -642,16 +642,40 @@ agx_transfer_flush_region(struct pipe_context *pipe, /* Reallocate the backing buffer of a resource, returns true if successful */ static bool -agx_shadow(struct agx_context *ctx, struct agx_resource *rsrc) +agx_shadow(struct agx_context *ctx, struct agx_resource *rsrc, bool needs_copy) { struct agx_device *dev = agx_device(ctx->base.screen); struct agx_bo *old = rsrc->bo; - struct agx_bo *new_ = agx_bo_create(dev, old->size, old->flags, old->label); + unsigned flags = old->flags; + + /* If a resource is (or could be) shared, shadowing would desync across + * processes. (It's also not what this path is for.) + */ + if (flags & (AGX_BO_SHARED | AGX_BO_SHAREABLE)) + return false; + + /* If we need to copy, we reallocate the resource with cached-coherent + * memory. This is a heuristic: it assumes that if the app needs a shadows + * (with a copy) now, it will again need to shadow-and-copy the same resource + * in the future. This accelerates the later copies, since otherwise the copy + * involves reading uncached memory. + */ + if (needs_copy) + flags |= AGX_BO_WRITEBACK; + + struct agx_bo *new_ = agx_bo_create(dev, old->size, flags, old->label); /* If allocation failed, we can fallback on a flush gracefully*/ if (new_ == NULL) return false; + if (needs_copy) { + perf_debug_ctx(ctx, "Shadowing %zu bytes on the CPU (%s)", old->size, + (old->flags & AGX_BO_WRITEBACK) ? "cached" : "uncached"); + + memcpy(new_->ptr.cpu, old->ptr.cpu, old->size); + } + /* Swap the pointers, dropping a reference */ agx_bo_unreference(rsrc->bo); rsrc->bo = new_; @@ -717,7 +741,8 @@ agx_prepare_for_map(struct agx_context *ctx, struct agx_resource *rsrc, return; /* There are readers. Try to shadow the resource to avoid a sync */ - if ((usage & PIPE_MAP_DISCARD_WHOLE_RESOURCE) && agx_shadow(ctx, rsrc)) + if (!(rsrc->base.flags & PIPE_RESOURCE_FLAG_MAP_PERSISTENT) && + agx_shadow(ctx, rsrc, !(usage & PIPE_MAP_DISCARD_WHOLE_RESOURCE))) return; /* Otherwise, we need to sync */ -- 2.7.4