+ util_range_set_empty(&rsc->valid_buffer_range);
+ fd_bc_invalidate_resource(rsc, true);
+}
+
+static void
+do_blit(struct fd_context *ctx, const struct pipe_blit_info *blit, bool fallback)
+{
+ /* TODO size threshold too?? */
+ if (!fallback) {
+ /* do blit on gpu: */
+ fd_blitter_pipe_begin(ctx, false, true, FD_STAGE_BLIT);
+ ctx->blit(ctx, blit);
+ fd_blitter_pipe_end(ctx);
+ } else {
+ /* do blit on cpu: */
+ util_resource_copy_region(&ctx->base,
+ blit->dst.resource, blit->dst.level, blit->dst.box.x,
+ blit->dst.box.y, blit->dst.box.z,
+ blit->src.resource, blit->src.level, &blit->src.box);
+ }
+}
+
+static bool
+fd_try_shadow_resource(struct fd_context *ctx, struct fd_resource *rsc,
+ unsigned level, const struct pipe_box *box)
+{
+ struct pipe_context *pctx = &ctx->base;
+ struct pipe_resource *prsc = &rsc->base;
+ bool fallback = false;
+
+ if (prsc->next)
+ return false;
+
+ /* TODO: somehow munge dimensions and format to copy unsupported
+ * render target format to something that is supported?
+ */
+ if (!pctx->screen->is_format_supported(pctx->screen,
+ prsc->format, prsc->target, prsc->nr_samples,
+ PIPE_BIND_RENDER_TARGET))
+ fallback = true;
+
+ /* do shadowing back-blits on the cpu for buffers: */
+ if (prsc->target == PIPE_BUFFER)
+ fallback = true;
+
+ bool whole_level = util_texrange_covers_whole_level(prsc, level,
+ box->x, box->y, box->z, box->width, box->height, box->depth);
+
+ /* TODO need to be more clever about current level */
+ if ((prsc->target >= PIPE_TEXTURE_2D) && !whole_level)
+ return false;
+
+ struct pipe_resource *pshadow =
+ pctx->screen->resource_create(pctx->screen, prsc);
+
+ if (!pshadow)
+ return false;
+
+ assert(!ctx->in_shadow);
+ ctx->in_shadow = true;
+
+ /* get rid of any references that batch-cache might have to us (which
+ * should empty/destroy rsc->batches hashset)
+ */
+ fd_bc_invalidate_resource(rsc, false);
+
+ mtx_lock(&ctx->screen->lock);
+
+ /* Swap the backing bo's, so shadow becomes the old buffer,
+ * blit from shadow to new buffer. From here on out, we
+ * cannot fail.
+ *
+ * Note that we need to do it in this order, otherwise if
+ * we go down cpu blit path, the recursive transfer_map()
+ * sees the wrong status..
+ */
+ struct fd_resource *shadow = fd_resource(pshadow);
+
+ DBG("shadow: %p (%d) -> %p (%d)\n", rsc, rsc->base.reference.count,
+ shadow, shadow->base.reference.count);
+
+ /* TODO valid_buffer_range?? */
+ swap(rsc->bo, shadow->bo);
+ swap(rsc->write_batch, shadow->write_batch);
+
+ /* at this point, the newly created shadow buffer is not referenced
+ * by any batches, but the existing rsc (probably) is. We need to
+ * transfer those references over:
+ */
+ debug_assert(shadow->batch_mask == 0);
+ struct fd_batch *batch;
+ foreach_batch(batch, &ctx->screen->batch_cache, rsc->batch_mask) {
+ struct set_entry *entry = _mesa_set_search(batch->resources, rsc);
+ _mesa_set_remove(batch->resources, entry);
+ _mesa_set_add(batch->resources, shadow);
+ }
+ swap(rsc->batch_mask, shadow->batch_mask);
+
+ mtx_unlock(&ctx->screen->lock);
+
+ struct pipe_blit_info blit = {0};
+ blit.dst.resource = prsc;
+ blit.dst.format = prsc->format;
+ blit.src.resource = pshadow;
+ blit.src.format = pshadow->format;
+ blit.mask = util_format_get_mask(prsc->format);
+ blit.filter = PIPE_TEX_FILTER_NEAREST;
+
+#define set_box(field, val) do { \
+ blit.dst.field = (val); \
+ blit.src.field = (val); \
+ } while (0)
+
+ /* blit the other levels in their entirety: */
+ for (unsigned l = 0; l <= prsc->last_level; l++) {
+ if (l == level)
+ continue;
+
+ /* just blit whole level: */
+ set_box(level, l);
+ set_box(box.width, u_minify(prsc->width0, l));
+ set_box(box.height, u_minify(prsc->height0, l));
+ set_box(box.depth, u_minify(prsc->depth0, l));
+
+ do_blit(ctx, &blit, fallback);
+ }
+
+ /* deal w/ current level specially, since we might need to split
+ * it up into a couple blits:
+ */
+ if (!whole_level) {
+ set_box(level, level);
+
+ switch (prsc->target) {
+ case PIPE_BUFFER:
+ case PIPE_TEXTURE_1D:
+ set_box(box.y, 0);
+ set_box(box.z, 0);
+ set_box(box.height, 1);
+ set_box(box.depth, 1);
+
+ if (box->x > 0) {
+ set_box(box.x, 0);
+ set_box(box.width, box->x);
+
+ do_blit(ctx, &blit, fallback);
+ }
+ if ((box->x + box->width) < u_minify(prsc->width0, level)) {
+ set_box(box.x, box->x + box->width);
+ set_box(box.width, u_minify(prsc->width0, level) - (box->x + box->width));
+
+ do_blit(ctx, &blit, fallback);
+ }
+ break;
+ case PIPE_TEXTURE_2D:
+ /* TODO */
+ default:
+ unreachable("TODO");
+ }
+ }
+
+ ctx->in_shadow = false;
+
+ pipe_resource_reference(&pshadow, NULL);
+
+ return true;
+}
+
+static struct fd_resource *
+fd_alloc_staging(struct fd_context *ctx, struct fd_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;
+ tmpl.depth0 = box->depth;
+ tmpl.array_size = 1;
+ 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 fd_resource(pstaging);
+}
+
+static void
+fd_blit_from_staging(struct fd_context *ctx, struct fd_transfer *trans)
+{
+ struct pipe_resource *dst = trans->base.resource;
+ struct pipe_blit_info blit = {0};
+
+ 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_prsc;
+ blit.src.format = trans->staging_prsc->format;
+ blit.src.level = 0;
+ blit.src.box = trans->staging_box;
+ blit.mask = util_format_get_mask(trans->staging_prsc->format);
+ blit.filter = PIPE_TEX_FILTER_NEAREST;
+
+ do_blit(ctx, &blit, false);
+}
+
+static void
+fd_blit_to_staging(struct fd_context *ctx, struct fd_transfer *trans)
+{
+ struct pipe_resource *src = trans->base.resource;
+ struct pipe_blit_info blit = {0};
+
+ 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_prsc;
+ blit.dst.format = trans->staging_prsc->format;
+ blit.dst.level = 0;
+ blit.dst.box = trans->staging_box;
+ blit.mask = util_format_get_mask(trans->staging_prsc->format);
+ blit.filter = PIPE_TEX_FILTER_NEAREST;
+
+ do_blit(ctx, &blit, false);
+}
+
+static unsigned
+fd_resource_layer_offset(struct fd_resource *rsc,
+ struct fd_resource_slice *slice,
+ unsigned layer)
+{
+ if (rsc->layer_first)
+ return layer * rsc->layer_size;
+ else
+ return layer * slice->size0;