From c54d45dd90a0606a9287bdcc03fd2463e47039a2 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Tue, 21 Jul 2020 12:57:25 -0400 Subject: [PATCH] panfrost: Import staging routines from freedreno For software access to AFBC textures. Signed-off-by: Alyssa Rosenzweig Tested-by: Icecream95 Part-of: --- src/gallium/drivers/panfrost/pan_resource.c | 151 ++++++++++++++++++++++++---- src/gallium/drivers/panfrost/pan_resource.h | 4 + 2 files changed, 136 insertions(+), 19 deletions(-) diff --git a/src/gallium/drivers/panfrost/pan_resource.c b/src/gallium/drivers/panfrost/pan_resource.c index 614c22f..8226b4b 100644 --- a/src/gallium/drivers/panfrost/pan_resource.c +++ b/src/gallium/drivers/panfrost/pan_resource.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2008 VMware, Inc. - * Copyright (C) 2014 Broadcom + * Copyright (C) 2012 Rob Clark + * 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, diff --git a/src/gallium/drivers/panfrost/pan_resource.h b/src/gallium/drivers/panfrost/pan_resource.h index ccf3af4..60970cb 100644 --- a/src/gallium/drivers/panfrost/pan_resource.h +++ b/src/gallium/drivers/panfrost/pan_resource.h @@ -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 * -- 2.7.4