From 236c55f650d096e63a5de36e66a47a2f46bab5d3 Mon Sep 17 00:00:00 2001 From: Alexandros Frantzis Date: Fri, 24 May 2019 14:03:28 +0300 Subject: [PATCH] virgl: Use copy transfers for textures Extend copy transfers to also be used for busy textures. Performance results: Unigine Valley, qemu before: 22.7 FPS after: 23.1 FPS Signed-off-by: Alexandros Frantzis Reviewed-by: Chia-I Wu --- src/gallium/drivers/virgl/virgl_resource.c | 56 ++++++++++++++++++++-- src/gallium/drivers/virgl/virgl_texture.c | 40 ++++++++++++++-- 2 files changed, 87 insertions(+), 9 deletions(-) diff --git a/src/gallium/drivers/virgl/virgl_resource.c b/src/gallium/drivers/virgl/virgl_resource.c index d9d79b9c0df..897e0c16c7e 100644 --- a/src/gallium/drivers/virgl/virgl_resource.c +++ b/src/gallium/drivers/virgl/virgl_resource.c @@ -106,8 +106,7 @@ virgl_resource_transfer_prepare(struct virgl_context *vctx, xfer->base.level); /* Check if we should perform a copy transfer through the transfer_uploader. */ - copy_transfer = res->u.b.target == PIPE_BUFFER && - discard && + copy_transfer = discard && !readback && !unsynchronized && vctx->transfer_uploader && @@ -490,18 +489,58 @@ void virgl_resource_dirty(struct virgl_resource *res, uint32_t level) } } +/* Calculate the minimum size of the memory required to service a resource + * transfer map. Also return the stride and layer_stride for the corresponding + * layout. + */ +static unsigned virgl_transfer_map_size(struct virgl_transfer *vtransfer, + unsigned *out_stride, + unsigned *out_layer_stride) +{ + struct pipe_resource *pres = vtransfer->base.resource; + struct pipe_box *box = &vtransfer->base.box; + unsigned stride; + unsigned layer_stride; + unsigned size; + + assert(out_stride); + assert(out_layer_stride); + + stride = util_format_get_stride(pres->format, box->width); + layer_stride = util_format_get_2d_size(pres->format, stride, box->height); + + if (pres->target == PIPE_TEXTURE_CUBE || + pres->target == PIPE_TEXTURE_CUBE_ARRAY || + pres->target == PIPE_TEXTURE_3D || + pres->target == PIPE_TEXTURE_2D_ARRAY) { + size = box->depth * layer_stride; + } else if (pres->target == PIPE_TEXTURE_1D_ARRAY) { + size = box->depth * stride; + } else { + size = layer_stride; + } + + *out_stride = stride; + *out_layer_stride = layer_stride; + + return size; +} + +/* Maps a region from the transfer uploader to service the transfer. */ void *virgl_transfer_uploader_map(struct virgl_context *vctx, struct virgl_transfer *vtransfer) { struct virgl_resource *vres = virgl_resource(vtransfer->base.resource); unsigned size; unsigned align_offset; + unsigned stride; + unsigned layer_stride; void *map_addr; assert(vctx->transfer_uploader); assert(!vctx->transfer_uploader_in_use); - size = vtransfer->base.box.width; + size = virgl_transfer_map_size(vtransfer, &stride, &layer_stride); /* For buffers we need to ensure that the start of the buffer would be * aligned to VIRGL_MAP_BUFFER_ALIGNMENT, even if our transfer doesn't @@ -516,7 +555,9 @@ void *virgl_transfer_uploader_map(struct virgl_context *vctx, * |---| ==> align_offset * |------------| ==> allocation of size + align_offset */ - align_offset = vtransfer->base.box.x % VIRGL_MAP_BUFFER_ALIGNMENT; + align_offset = vres->u.b.target == PIPE_BUFFER ? + vtransfer->base.box.x % VIRGL_MAP_BUFFER_ALIGNMENT : + 0; u_upload_alloc(vctx->transfer_uploader, 0, size + align_offset, VIRGL_MAP_BUFFER_ALIGNMENT, @@ -537,6 +578,13 @@ void *virgl_transfer_uploader_map(struct virgl_context *vctx, /* The pointer returned by u_upload_alloc already has +offset * applied. */ vctx->transfer_uploader_in_use = true; + + /* We are using the minimum required size to hold the contents, + * possibly using a layout different from the layout of the resource, + * so update the transfer strides accordingly. + */ + vtransfer->base.stride = stride; + vtransfer->base.layer_stride = layer_stride; } return map_addr; diff --git a/src/gallium/drivers/virgl/virgl_texture.c b/src/gallium/drivers/virgl/virgl_texture.c index 7eca56bd927..0d9c6a03e35 100644 --- a/src/gallium/drivers/virgl/virgl_texture.c +++ b/src/gallium/drivers/virgl/virgl_texture.c @@ -25,6 +25,7 @@ #include "util/u_memory.h" #include "virgl_context.h" +#include "virgl_encode.h" #include "virgl_resource.h" #include "virgl_screen.h" @@ -123,6 +124,7 @@ static void *texture_transfer_map_plain(struct pipe_context *ctx, struct virgl_resource *vtex = virgl_resource(resource); struct virgl_transfer *trans; enum virgl_transfer_map_type map_type; + void *map_addr; trans = virgl_resource_create_transfer(&vctx->transfer_pool, resource, &vtex->metadata, level, usage, box); @@ -134,20 +136,30 @@ static void *texture_transfer_map_plain(struct pipe_context *ctx, switch (map_type) { case VIRGL_TRANSFER_MAP_HW_RES: trans->hw_res_map = vws->resource_map(vws, vtex->hw_res); + if (trans->hw_res_map) + map_addr = trans->hw_res_map + trans->offset; + else + map_addr = NULL; + break; + case VIRGL_TRANSFER_MAP_STAGING: + map_addr = virgl_transfer_uploader_map(vctx, trans); + /* Copy transfers don't make use of hw_res_map at the moment. */ + trans->hw_res_map = NULL; break; case VIRGL_TRANSFER_MAP_ERROR: default: trans->hw_res_map = NULL; + map_addr = NULL; break; } - if (!trans->hw_res_map) { + if (!map_addr) { virgl_resource_destroy_transfer(&vctx->transfer_pool, trans); return NULL; } *transfer = &trans->base; - return trans->hw_res_map + trans->offset; + return map_addr; } static void *texture_transfer_map_resolve(struct pipe_context *ctx, @@ -294,8 +306,17 @@ static void virgl_texture_transfer_unmap(struct pipe_context *ctx, { struct virgl_context *vctx = virgl_context(ctx); struct virgl_transfer *trans = virgl_transfer(transfer); + struct virgl_screen *vs = virgl_screen(ctx->screen); + struct pipe_resource *res = transfer->resource; bool queue_unmap = false; + /* We don't need to transfer the contents of staging buffers, since they + * don't have any host-side storage. */ + if (pipe_to_virgl_bind(vs, res->bind, res->flags) == VIRGL_BIND_STAGING) { + virgl_resource_destroy_transfer(&vctx->transfer_pool, trans); + return; + } + if (transfer->usage & PIPE_TRANSFER_WRITE && (transfer->usage & PIPE_TRANSFER_FLUSH_EXPLICIT) == 0) { @@ -326,10 +347,19 @@ static void virgl_texture_transfer_unmap(struct pipe_context *ctx, virgl_transfer(trans->resolve_transfer)); } - if (queue_unmap) - virgl_transfer_queue_unmap(&vctx->queue, trans); - else + if (queue_unmap) { + if (trans->copy_src_res) { + virgl_encode_copy_transfer(vctx, trans); + /* It's now safe for other mappings to use the transfer_uploader. */ + vctx->transfer_uploader_in_use = false; + virgl_resource_destroy_transfer(&vctx->transfer_pool, trans); + } else { + virgl_transfer_queue_unmap(&vctx->queue, trans); + } + } else { virgl_resource_destroy_transfer(&vctx->transfer_pool, trans); + } + } static const struct u_resource_vtbl virgl_texture_vtbl = -- 2.30.2