summaryrefslogtreecommitdiffstats
path: root/src/gallium
diff options
context:
space:
mode:
authorChia-I Wu <[email protected]>2019-05-09 21:44:33 -0700
committerChia-I Wu <[email protected]>2019-06-17 09:36:31 -0700
commit1fece5fa5f8284076d923be6937d9115055a533e (patch)
tree298fa1f76de034c3b1971c80355a6fae56919bb9 /src/gallium
parent9975a0a84ca2ee18a0f051df44932bd7b2284a02 (diff)
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 <[email protected]> Reviewed-by: Alexandros Frantzis <[email protected]>
Diffstat (limited to 'src/gallium')
-rw-r--r--src/gallium/drivers/virgl/virgl_buffer.c7
-rw-r--r--src/gallium/drivers/virgl/virgl_resource.c95
-rw-r--r--src/gallium/drivers/virgl/virgl_resource.h8
-rw-r--r--src/gallium/drivers/virgl/virgl_texture.c7
4 files changed, 92 insertions, 25 deletions
diff --git a/src/gallium/drivers/virgl/virgl_buffer.c b/src/gallium/drivers/virgl/virgl_buffer.c
index ddb632db483..ed001722ab4 100644
--- a/src/gallium/drivers/virgl/virgl_buffer.c
+++ b/src/gallium/drivers/virgl/virgl_buffer.c
@@ -47,6 +47,13 @@ static void *virgl_buffer_transfer_map(struct pipe_context *ctx,
map_type = virgl_resource_transfer_prepare(vctx, trans);
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:
trans->hw_res_map = vs->vws->resource_map(vs->vws, vbuf->hw_res);
if (trans->hw_res_map)
diff --git a/src/gallium/drivers/virgl/virgl_resource.c b/src/gallium/drivers/virgl/virgl_resource.c
index 59afcca968e..9b908afd852 100644
--- a/src/gallium/drivers/virgl/virgl_resource.c
+++ b/src/gallium/drivers/virgl/virgl_resource.c
@@ -87,13 +87,9 @@ virgl_resource_transfer_prepare(struct virgl_context *vctx,
struct virgl_winsys *vws = vs->vws;
struct virgl_resource *res = virgl_resource(xfer->base.resource);
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 readback;
bool wait;
- bool copy_transfer;
/* there is no way to map the host storage currently */
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);
readback = virgl_res_needs_readback(vctx, res, xfer->base.usage,
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
* 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
* 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;
readback = false;
wait = false;
- copy_transfer = false;
}
- /* When performing a copy transfer there is no need wait for the target
- * resource. 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.
+ /* When the resource is busy but its content can be discarded, we can
+ * replace its HW resource or use a staging buffer to avoid waiting.
*/
- if (copy_transfer) {
- flush = (vctx->queued_staging_res_size > VIRGL_QUEUED_STAGING_RES_SIZE_LIMIT);
- wait = false;
+ if (wait && (xfer->base.usage & (PIPE_TRANSFER_DISCARD_RANGE |
+ PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE))) {
+ 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 */
@@ -185,9 +197,6 @@ virgl_resource_transfer_prepare(struct virgl_context *vctx,
if (wait)
vws->resource_wait(vws, res->hw_res);
- if (copy_transfer)
- map_type = VIRGL_TRANSFER_MAP_STAGING;
-
return map_type;
}
@@ -598,3 +607,39 @@ void *virgl_transfer_uploader_map(struct virgl_context *vctx,
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;
+}
diff --git a/src/gallium/drivers/virgl/virgl_resource.h b/src/gallium/drivers/virgl/virgl_resource.h
index 86eb2322513..c3374fbef70 100644
--- a/src/gallium/drivers/virgl/virgl_resource.h
+++ b/src/gallium/drivers/virgl/virgl_resource.h
@@ -73,10 +73,14 @@ struct virgl_resource {
enum virgl_transfer_map_type {
VIRGL_TRANSFER_MAP_ERROR = -1,
VIRGL_TRANSFER_MAP_HW_RES,
+
/* Map a range of a staging buffer. The updated contents should be transferred
* with a copy transfer.
*/
VIRGL_TRANSFER_MAP_STAGING,
+
+ /* Reallocate the underlying virgl_hw_res. */
+ VIRGL_TRANSFER_MAP_REALLOC,
};
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,
struct virgl_transfer *vtransfer);
+bool
+virgl_resource_realloc(struct virgl_context *vctx,
+ struct virgl_resource *res);
+
#endif
diff --git a/src/gallium/drivers/virgl/virgl_texture.c b/src/gallium/drivers/virgl/virgl_texture.c
index 1de6d4f17db..3c5737c5d17 100644
--- a/src/gallium/drivers/virgl/virgl_texture.c
+++ b/src/gallium/drivers/virgl/virgl_texture.c
@@ -134,6 +134,13 @@ static void *texture_transfer_map_plain(struct pipe_context *ctx,
map_type = virgl_resource_transfer_prepare(vctx, trans);
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:
trans->hw_res_map = vws->resource_map(vws, vtex->hw_res);
if (trans->hw_res_map)