virgl: better support for PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE
When the resource to be mapped is busy and the backing storage can be discarded, reallocate the backing storage to avoid waiting. In this new path, we allocate a new buffer, emit a state change, write, and add the transfer to the queue . In the PIPE_TRANSFER_DISCARD_RANGE path, we suballocate a staging buffer, write, and emit a copy_transfer (which may allocate, memcpy, and blit internally). The win might not always be clear. But another win comes from that the new path clears res->valid_buffer_range and does not clear res->clean_mask. This makes it much more preferable in scenarios such as access = enough_space ? GL_MAP_UNSYNCHRONIZED_BIT : GL_MAP_INVALIDATE_BUFFER_BIT; glMapBufferRange(..., GL_MAP_WRITE_BIT | access); memcpy(...); // append new data glUnmapBuffer(...); Signed-off-by: Chia-I Wu <olvaffe@gmail.com> Reviewed-by: Alexandros Frantzis <alexandros.frantzis@collabora.com>
This commit is contained in:
parent
9975a0a84c
commit
1fece5fa5f
|
@ -47,6 +47,13 @@ static void *virgl_buffer_transfer_map(struct pipe_context *ctx,
|
||||||
|
|
||||||
map_type = virgl_resource_transfer_prepare(vctx, trans);
|
map_type = virgl_resource_transfer_prepare(vctx, trans);
|
||||||
switch (map_type) {
|
switch (map_type) {
|
||||||
|
case VIRGL_TRANSFER_MAP_REALLOC:
|
||||||
|
if (!virgl_resource_realloc(vctx, vbuf)) {
|
||||||
|
map_addr = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vs->vws->resource_reference(vs->vws, &trans->hw_res, vbuf->hw_res);
|
||||||
|
/* fall through */
|
||||||
case VIRGL_TRANSFER_MAP_HW_RES:
|
case VIRGL_TRANSFER_MAP_HW_RES:
|
||||||
trans->hw_res_map = vs->vws->resource_map(vs->vws, vbuf->hw_res);
|
trans->hw_res_map = vs->vws->resource_map(vs->vws, vbuf->hw_res);
|
||||||
if (trans->hw_res_map)
|
if (trans->hw_res_map)
|
||||||
|
|
|
@ -87,13 +87,9 @@ virgl_resource_transfer_prepare(struct virgl_context *vctx,
|
||||||
struct virgl_winsys *vws = vs->vws;
|
struct virgl_winsys *vws = vs->vws;
|
||||||
struct virgl_resource *res = virgl_resource(xfer->base.resource);
|
struct virgl_resource *res = virgl_resource(xfer->base.resource);
|
||||||
enum virgl_transfer_map_type map_type = VIRGL_TRANSFER_MAP_HW_RES;
|
enum virgl_transfer_map_type map_type = VIRGL_TRANSFER_MAP_HW_RES;
|
||||||
bool unsynchronized = xfer->base.usage & PIPE_TRANSFER_UNSYNCHRONIZED;
|
|
||||||
bool discard = xfer->base.usage & (PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE |
|
|
||||||
PIPE_TRANSFER_DISCARD_RANGE);
|
|
||||||
bool flush;
|
bool flush;
|
||||||
bool readback;
|
bool readback;
|
||||||
bool wait;
|
bool wait;
|
||||||
bool copy_transfer;
|
|
||||||
|
|
||||||
/* there is no way to map the host storage currently */
|
/* there is no way to map the host storage currently */
|
||||||
if (xfer->base.usage & PIPE_TRANSFER_MAP_DIRECTLY)
|
if (xfer->base.usage & PIPE_TRANSFER_MAP_DIRECTLY)
|
||||||
|
@ -110,19 +106,10 @@ virgl_resource_transfer_prepare(struct virgl_context *vctx,
|
||||||
flush = virgl_res_needs_flush(vctx, xfer);
|
flush = virgl_res_needs_flush(vctx, xfer);
|
||||||
readback = virgl_res_needs_readback(vctx, res, xfer->base.usage,
|
readback = virgl_res_needs_readback(vctx, res, xfer->base.usage,
|
||||||
xfer->base.level);
|
xfer->base.level);
|
||||||
|
|
||||||
/* Check if we should perform a copy transfer through the transfer_uploader. */
|
|
||||||
copy_transfer = discard &&
|
|
||||||
!readback &&
|
|
||||||
!unsynchronized &&
|
|
||||||
vctx->transfer_uploader &&
|
|
||||||
!vctx->transfer_uploader_in_use &&
|
|
||||||
(flush || vws->resource_is_busy(vws, res->hw_res));
|
|
||||||
|
|
||||||
/* We need to wait for all cmdbufs, current or previous, that access the
|
/* We need to wait for all cmdbufs, current or previous, that access the
|
||||||
* resource to finish unless synchronization is disabled.
|
* resource to finish unless synchronization is disabled.
|
||||||
*/
|
*/
|
||||||
wait = !unsynchronized;
|
wait = !(xfer->base.usage & PIPE_TRANSFER_UNSYNCHRONIZED);
|
||||||
|
|
||||||
/* When the transfer range consists of only uninitialized data, we can
|
/* When the transfer range consists of only uninitialized data, we can
|
||||||
* assume the GPU is not accessing the range and readback is unnecessary.
|
* assume the GPU is not accessing the range and readback is unnecessary.
|
||||||
|
@ -135,17 +122,42 @@ virgl_resource_transfer_prepare(struct virgl_context *vctx,
|
||||||
flush = false;
|
flush = false;
|
||||||
readback = false;
|
readback = false;
|
||||||
wait = false;
|
wait = false;
|
||||||
copy_transfer = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* When performing a copy transfer there is no need wait for the target
|
/* When the resource is busy but its content can be discarded, we can
|
||||||
* resource. There is normally no need to flush either, unless the amount of
|
* replace its HW resource or use a staging buffer to avoid waiting.
|
||||||
* memory we are using for staging resources starts growing, in which case
|
|
||||||
* we want to flush to keep our memory consumption in check.
|
|
||||||
*/
|
*/
|
||||||
if (copy_transfer) {
|
if (wait && (xfer->base.usage & (PIPE_TRANSFER_DISCARD_RANGE |
|
||||||
flush = (vctx->queued_staging_res_size > VIRGL_QUEUED_STAGING_RES_SIZE_LIMIT);
|
PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE))) {
|
||||||
wait = false;
|
const bool can_realloc =
|
||||||
|
(xfer->base.usage & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE) &&
|
||||||
|
virgl_can_rebind_resource(vctx, &res->u.b);
|
||||||
|
const bool can_staging = vctx->transfer_uploader &&
|
||||||
|
!vctx->transfer_uploader_in_use;
|
||||||
|
|
||||||
|
/* discard implies no readback */
|
||||||
|
assert(!readback);
|
||||||
|
|
||||||
|
if (can_realloc || can_staging) {
|
||||||
|
/* Both map types have some costs. Do them only when the resource is
|
||||||
|
* (or will be) busy for real. Otherwise, set wait to false.
|
||||||
|
*/
|
||||||
|
wait = (flush || vws->resource_is_busy(vws, res->hw_res));
|
||||||
|
if (wait) {
|
||||||
|
map_type = (can_realloc) ?
|
||||||
|
VIRGL_TRANSFER_MAP_REALLOC :
|
||||||
|
VIRGL_TRANSFER_MAP_STAGING;
|
||||||
|
wait = false;
|
||||||
|
|
||||||
|
/* There is normally no need to flush either, unless the amount of
|
||||||
|
* memory we are using for staging resources starts growing, in
|
||||||
|
* which case we want to flush to keep our memory consumption in
|
||||||
|
* check.
|
||||||
|
*/
|
||||||
|
flush = (vctx->queued_staging_res_size >
|
||||||
|
VIRGL_QUEUED_STAGING_RES_SIZE_LIMIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* readback has some implications */
|
/* readback has some implications */
|
||||||
|
@ -185,9 +197,6 @@ virgl_resource_transfer_prepare(struct virgl_context *vctx,
|
||||||
if (wait)
|
if (wait)
|
||||||
vws->resource_wait(vws, res->hw_res);
|
vws->resource_wait(vws, res->hw_res);
|
||||||
|
|
||||||
if (copy_transfer)
|
|
||||||
map_type = VIRGL_TRANSFER_MAP_STAGING;
|
|
||||||
|
|
||||||
return map_type;
|
return map_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -598,3 +607,39 @@ void *virgl_transfer_uploader_map(struct virgl_context *vctx,
|
||||||
|
|
||||||
return map_addr;
|
return map_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
virgl_resource_realloc(struct virgl_context *vctx, struct virgl_resource *res)
|
||||||
|
{
|
||||||
|
struct virgl_screen *vs = virgl_screen(vctx->base.screen);
|
||||||
|
const struct pipe_resource *templ = &res->u.b;
|
||||||
|
unsigned vbind;
|
||||||
|
struct virgl_hw_res *hw_res;
|
||||||
|
|
||||||
|
vbind = pipe_to_virgl_bind(vs, templ->bind, templ->flags);
|
||||||
|
hw_res = vs->vws->resource_create(vs->vws,
|
||||||
|
templ->target,
|
||||||
|
templ->format,
|
||||||
|
vbind,
|
||||||
|
templ->width0,
|
||||||
|
templ->height0,
|
||||||
|
templ->depth0,
|
||||||
|
templ->array_size,
|
||||||
|
templ->last_level,
|
||||||
|
templ->nr_samples,
|
||||||
|
res->metadata.total_size);
|
||||||
|
if (!hw_res)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
vs->vws->resource_reference(vs->vws, &res->hw_res, NULL);
|
||||||
|
res->hw_res = hw_res;
|
||||||
|
|
||||||
|
util_range_set_empty(&res->valid_buffer_range);
|
||||||
|
|
||||||
|
/* count toward the staging resource size limit */
|
||||||
|
vctx->queued_staging_res_size += res->metadata.total_size;
|
||||||
|
|
||||||
|
virgl_rebind_resource(vctx, &res->u.b);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -73,10 +73,14 @@ struct virgl_resource {
|
||||||
enum virgl_transfer_map_type {
|
enum virgl_transfer_map_type {
|
||||||
VIRGL_TRANSFER_MAP_ERROR = -1,
|
VIRGL_TRANSFER_MAP_ERROR = -1,
|
||||||
VIRGL_TRANSFER_MAP_HW_RES,
|
VIRGL_TRANSFER_MAP_HW_RES,
|
||||||
|
|
||||||
/* Map a range of a staging buffer. The updated contents should be transferred
|
/* Map a range of a staging buffer. The updated contents should be transferred
|
||||||
* with a copy transfer.
|
* with a copy transfer.
|
||||||
*/
|
*/
|
||||||
VIRGL_TRANSFER_MAP_STAGING,
|
VIRGL_TRANSFER_MAP_STAGING,
|
||||||
|
|
||||||
|
/* Reallocate the underlying virgl_hw_res. */
|
||||||
|
VIRGL_TRANSFER_MAP_REALLOC,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct virgl_transfer {
|
struct virgl_transfer {
|
||||||
|
@ -187,4 +191,8 @@ void virgl_resource_dirty(struct virgl_resource *res, uint32_t level);
|
||||||
void *virgl_transfer_uploader_map(struct virgl_context *vctx,
|
void *virgl_transfer_uploader_map(struct virgl_context *vctx,
|
||||||
struct virgl_transfer *vtransfer);
|
struct virgl_transfer *vtransfer);
|
||||||
|
|
||||||
|
bool
|
||||||
|
virgl_resource_realloc(struct virgl_context *vctx,
|
||||||
|
struct virgl_resource *res);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -134,6 +134,13 @@ static void *texture_transfer_map_plain(struct pipe_context *ctx,
|
||||||
|
|
||||||
map_type = virgl_resource_transfer_prepare(vctx, trans);
|
map_type = virgl_resource_transfer_prepare(vctx, trans);
|
||||||
switch (map_type) {
|
switch (map_type) {
|
||||||
|
case VIRGL_TRANSFER_MAP_REALLOC:
|
||||||
|
if (!virgl_resource_realloc(vctx, vtex)) {
|
||||||
|
map_addr = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vws->resource_reference(vws, &trans->hw_res, vtex->hw_res);
|
||||||
|
/* fall through */
|
||||||
case VIRGL_TRANSFER_MAP_HW_RES:
|
case VIRGL_TRANSFER_MAP_HW_RES:
|
||||||
trans->hw_res_map = vws->resource_map(vws, vtex->hw_res);
|
trans->hw_res_map = vws->resource_map(vws, vtex->hw_res);
|
||||||
if (trans->hw_res_map)
|
if (trans->hw_res_map)
|
||||||
|
|
Loading…
Reference in New Issue