panfrost: Import staging routines from freedreno
authorAlyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
Tue, 21 Jul 2020 16:57:25 +0000 (12:57 -0400)
committerAlyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
Wed, 12 Aug 2020 13:59:20 +0000 (09:59 -0400)
For software access to AFBC textures.

Signed-off-by: Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
Tested-by: Icecream95 <ixn@keemail.me>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6159>

src/gallium/drivers/panfrost/pan_resource.c
src/gallium/drivers/panfrost/pan_resource.h

index 614c22f67deab6b4918c1d877581661b8e37d7fd..8226b4b3c3e279b64d7d0d6292c0cdbfc547ac01 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2008 VMware, Inc.
- * Copyright (C) 2014 Broadcom
+ * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org>
+ * Copyright (C) 2014-2017 Broadcom
  * Copyright (C) 2018-2019 Alyssa Rosenzweig
  * Copyright (C) 2019 Collabora, Ltd.
  *
@@ -597,6 +598,81 @@ panfrost_resource_destroy(struct pipe_screen *screen,
         ralloc_free(rsrc);
 }
 
+/* Most of the time we can do CPU-side transfers, but sometimes we need to use
+ * the 3D pipe for this. Let's wrap u_blitter to blit to/from staging textures.
+ * Code adapted from freedreno */
+
+static struct panfrost_resource *
+pan_alloc_staging(struct panfrost_context *ctx, struct panfrost_resource *rsc,
+               unsigned level, const struct pipe_box *box)
+{
+        struct pipe_context *pctx = &ctx->base;
+        struct pipe_resource tmpl = rsc->base;
+
+        tmpl.width0  = box->width;
+        tmpl.height0 = box->height;
+        /* for array textures, box->depth is the array_size, otherwise
+         * for 3d textures, it is the depth:
+         */
+        if (tmpl.array_size > 1) {
+                if (tmpl.target == PIPE_TEXTURE_CUBE)
+                        tmpl.target = PIPE_TEXTURE_2D_ARRAY;
+                tmpl.array_size = box->depth;
+                tmpl.depth0 = 1;
+        } else {
+                tmpl.array_size = 1;
+                tmpl.depth0 = box->depth;
+        }
+        tmpl.last_level = 0;
+        tmpl.bind |= PIPE_BIND_LINEAR;
+
+        struct pipe_resource *pstaging =
+                pctx->screen->resource_create(pctx->screen, &tmpl);
+        if (!pstaging)
+                return NULL;
+
+        return pan_resource(pstaging);
+}
+
+static void
+pan_blit_from_staging(struct pipe_context *pctx, struct panfrost_gtransfer *trans)
+{
+        struct pipe_resource *dst = trans->base.resource;
+        struct pipe_blit_info blit = {};
+
+        blit.dst.resource = dst;
+        blit.dst.format   = dst->format;
+        blit.dst.level    = trans->base.level;
+        blit.dst.box      = trans->base.box;
+        blit.src.resource = trans->staging.rsrc;
+        blit.src.format   = trans->staging.rsrc->format;
+        blit.src.level    = 0;
+        blit.src.box      = trans->staging.box;
+        blit.mask = util_format_get_mask(trans->staging.rsrc->format);
+        blit.filter = PIPE_TEX_FILTER_NEAREST;
+
+        panfrost_blit(pctx, &blit);
+}
+
+static void
+pan_blit_to_staging(struct pipe_context *pctx, struct panfrost_gtransfer *trans)
+{
+        struct pipe_resource *src = trans->base.resource;
+        struct pipe_blit_info blit = {};
+
+        blit.src.resource = src;
+        blit.src.format   = src->format;
+        blit.src.level    = trans->base.level;
+        blit.src.box      = trans->base.box;
+        blit.dst.resource = trans->staging.rsrc;
+        blit.dst.format   = trans->staging.rsrc->format;
+        blit.dst.level    = 0;
+        blit.dst.box      = trans->staging.box;
+        blit.mask = util_format_get_mask(trans->staging.rsrc->format);
+        blit.filter = PIPE_TEX_FILTER_NEAREST;
+
+        panfrost_blit(pctx, &blit);
+}
 
 static void *
 panfrost_transfer_map(struct pipe_context *pctx,
@@ -612,15 +688,48 @@ panfrost_transfer_map(struct pipe_context *pctx,
         int bytes_per_pixel = util_format_get_blocksize(rsrc->internal_format);
         struct panfrost_bo *bo = rsrc->bo;
 
+        /* Can't map tiled/compressed directly */
+        if ((usage & PIPE_TRANSFER_MAP_DIRECTLY) && rsrc->modifier != DRM_FORMAT_MOD_LINEAR)
+                return NULL;
+
         struct panfrost_gtransfer *transfer = rzalloc(pctx, struct panfrost_gtransfer);
         transfer->base.level = level;
         transfer->base.usage = usage;
         transfer->base.box = *box;
 
         pipe_resource_reference(&transfer->base.resource, resource);
-
         *out_transfer = &transfer->base;
 
+        /* We don't have s/w routines for AFBC, so use a staging texture */
+        if (drm_is_afbc(rsrc->modifier)) {
+                struct panfrost_resource *staging = pan_alloc_staging(ctx, rsrc, level, box);
+                transfer->base.stride = staging->slices[0].stride;
+                transfer->base.layer_stride = transfer->base.stride * box->height;
+
+                transfer->staging.rsrc = &staging->base;
+
+                transfer->staging.box = *box;
+                transfer->staging.box.x = 0;
+                transfer->staging.box.y = 0;
+                transfer->staging.box.z = 0;
+
+                assert(transfer->staging.rsrc != NULL);
+
+                /* TODO: Eliminate this flush. It's only there to determine if
+                 * we're initialized or not, when the initialization could come
+                 * from a pending batch XXX */
+                panfrost_flush_batches_accessing_bo(ctx, rsrc->bo, true);
+
+                if ((usage & PIPE_TRANSFER_READ) && rsrc->slices[level].initialized) {
+                        pan_blit_to_staging(pctx, transfer);
+                        panfrost_flush_batches_accessing_bo(ctx, staging->bo, true);
+                        panfrost_bo_wait(staging->bo, INT64_MAX, false);
+                }
+
+                panfrost_bo_mmap(staging->bo);
+                return staging->bo->cpu;
+        }
+
         /* If we haven't already mmaped, now's the time */
         panfrost_bo_mmap(bo);
 
@@ -699,33 +808,26 @@ panfrost_transfer_map(struct pipe_context *pctx,
                 }
         }
 
-        if (rsrc->modifier != DRM_FORMAT_MOD_LINEAR) {
-                /* Non-linear resources need to be indirectly mapped */
-
-                if (usage & PIPE_TRANSFER_MAP_DIRECTLY)
-                        return NULL;
-
+        if (rsrc->modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED) {
                 transfer->base.stride = box->width * bytes_per_pixel;
                 transfer->base.layer_stride = transfer->base.stride * box->height;
                 transfer->map = ralloc_size(transfer, transfer->base.layer_stride * box->depth);
                 assert(box->depth == 1);
 
                 if ((usage & PIPE_TRANSFER_READ) && rsrc->slices[level].initialized) {
-                        if (drm_is_afbc(rsrc->modifier)) {
-                                unreachable("Unimplemented: reads from AFBC");
-                        } else if (rsrc->modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED) {
-                                panfrost_load_tiled_image(
+                        panfrost_load_tiled_image(
                                         transfer->map,
                                         bo->cpu + rsrc->slices[level].offset,
                                         box->x, box->y, box->width, box->height,
                                         transfer->base.stride,
                                         rsrc->slices[level].stride,
                                         rsrc->internal_format);
-                        }
                 }
 
                 return transfer->map;
         } else {
+                assert (rsrc->modifier == DRM_FORMAT_MOD_LINEAR);
+
                 /* Direct, persistent writes create holes in time for
                  * caching... I don't know if this is actually possible but we
                  * should still get it right */
@@ -765,17 +867,28 @@ panfrost_transfer_unmap(struct pipe_context *pctx,
         struct panfrost_gtransfer *trans = pan_transfer(transfer);
         struct panfrost_resource *prsrc = (struct panfrost_resource *) transfer->resource;
 
-        /* Mark whatever we wrote as written */
-        if (transfer->usage & PIPE_TRANSFER_WRITE)
-                prsrc->slices[transfer->level].initialized = true;
+        /* AFBC will use a staging resource. `initialized` will be set when the
+         * fragment job is created; this is deferred to prevent useless surface
+         * reloads that can cascade into DATA_INVALID_FAULTs due to reading
+         * malformed AFBC data if uninitialized */
 
+        if (trans->staging.rsrc) {
+               if (transfer->usage & PIPE_TRANSFER_WRITE) {
+                       pan_blit_from_staging(pctx, trans);
+                        panfrost_flush_batches_accessing_bo(pan_context(pctx), pan_resource(trans->staging.rsrc)->bo, true);
+                }
+
+               pipe_resource_reference(&trans->staging.rsrc, NULL);
+        }
+
+        /* Tiling will occur in software from a staging cpu buffer */
         if (trans->map) {
                 struct panfrost_bo *bo = prsrc->bo;
 
                 if (transfer->usage & PIPE_TRANSFER_WRITE) {
-                        if (drm_is_afbc(prsrc->modifier)) {
-                                unreachable("Unimplemented: writes to AFBC\n");
-                        } else if (prsrc->modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED) {
+                        prsrc->slices[transfer->level].initialized = true;
+
+                        if (prsrc->modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED) {
                                 assert(transfer->box.depth == 1);
 
                                 /* Do we overwrite the entire resource? If so,
index ccf3af45082313b4813449c14edc35db86e5f3db..60970cb44f921161ff1330265f99f57852cfda08 100644 (file)
@@ -85,6 +85,10 @@ pan_resource(struct pipe_resource *p)
 struct panfrost_gtransfer {
         struct pipe_transfer base;
         void *map;
+        struct {
+                struct pipe_resource *rsrc;
+                struct pipe_box box;
+        } staging;
 };
 
 static inline struct panfrost_gtransfer *