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 <alexandros.frantzis@collabora.com>
Reviewed-by: Chia-I Wu <olvaffe@gmail.com>
This commit is contained in:
Alexandros Frantzis 2019-05-24 14:03:28 +03:00 committed by Chia-I Wu
parent a22c5df079
commit 236c55f650
2 changed files with 87 additions and 9 deletions

View File

@ -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;

View File

@ -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 =