From f1b3374d28577ca0b84ce1fbab171c39a637d3df Mon Sep 17 00:00:00 2001 From: Charmaine Lee Date: Tue, 6 Sep 2016 11:29:41 -0700 Subject: [PATCH] svga: use upload buffer for upload texture. With this patch, when running with vgpu10, instead of mapping directly to the guest backed memory for texture update, we'll use the texture upload buffer and use the transfer from buffer command to update the host side texture memory. This optimization yields about 20% performance improvement with Lightsmark2008 and about 40% with Tropics. Tested with Lightsmark2008, Tropics, Heaven, MTT piglit, glretrace, conform. Reviewed-by: Brian Paul --- src/gallium/drivers/svga/svga_context.c | 5 + src/gallium/drivers/svga/svga_context.h | 1 + .../svga/svga_resource_buffer_upload.c | 8 + .../drivers/svga/svga_resource_texture.c | 258 ++++++++++++++++-- .../drivers/svga/svga_resource_texture.h | 32 +++ src/gallium/drivers/svga/svga_winsys.h | 1 + 6 files changed, 279 insertions(+), 26 deletions(-) diff --git a/src/gallium/drivers/svga/svga_context.c b/src/gallium/drivers/svga/svga_context.c index 1ed0f3d1b78..10085102110 100644 --- a/src/gallium/drivers/svga/svga_context.c +++ b/src/gallium/drivers/svga/svga_context.c @@ -102,6 +102,7 @@ static void svga_destroy( struct pipe_context *pipe ) util_bitmask_destroy(svga->stream_output_id_bm); util_bitmask_destroy(svga->query_id_bm); u_upload_destroy(svga->const0_upload); + svga_texture_transfer_map_upload_destroy(svga); /* free user's constant buffers */ for (shader = 0; shader < PIPE_SHADER_TYPES; ++shader) { @@ -214,6 +215,9 @@ struct pipe_context *svga_context_create(struct pipe_screen *screen, if (!svga->const0_upload) goto cleanup; + if (!svga_texture_transfer_map_upload_create(svga)) + goto cleanup; + /* Avoid shortcircuiting state with initial value of zero. */ memset(&svga->state.hw_clear, 0xcd, sizeof(svga->state.hw_clear)); @@ -279,6 +283,7 @@ cleanup: if (svga->const0_upload) u_upload_destroy(svga->const0_upload); + svga_texture_transfer_map_upload_destroy(svga); if (svga->hwtnl) svga_hwtnl_destroy(svga->hwtnl); if (svga->swc) diff --git a/src/gallium/drivers/svga/svga_context.h b/src/gallium/drivers/svga/svga_context.h index afb04120ef5..cbc4a9c86e9 100644 --- a/src/gallium/drivers/svga/svga_context.h +++ b/src/gallium/drivers/svga/svga_context.h @@ -431,6 +431,7 @@ struct svga_context struct svga_winsys_context *swc; struct blitter_context *blitter; struct u_upload_mgr *const0_upload; + struct u_upload_mgr *tex_upload; struct { boolean no_swtnl; diff --git a/src/gallium/drivers/svga/svga_resource_buffer_upload.c b/src/gallium/drivers/svga/svga_resource_buffer_upload.c index 7ecf95c1af7..ac8cedfd3b7 100644 --- a/src/gallium/drivers/svga/svga_resource_buffer_upload.c +++ b/src/gallium/drivers/svga/svga_resource_buffer_upload.c @@ -166,6 +166,14 @@ svga_buffer_create_host_surface(struct svga_screen *ss, if (sbuf->bind_flags & PIPE_BIND_SAMPLER_VIEW) sbuf->key.flags |= SVGA3D_SURFACE_BIND_SHADER_RESOURCE; + if (!sbuf->bind_flags && sbuf->b.b.usage == PIPE_USAGE_STAGING) { + /* This surface is to be used with the + * SVGA3D_CMD_DX_TRANSFER_FROM_BUFFER command, and no other + * bind flags are allowed to be set for this surface. + */ + sbuf->key.flags = SVGA3D_SURFACE_TRANSFER_FROM_BUFFER; + } + sbuf->key.size.width = sbuf->b.b.width0; sbuf->key.size.height = 1; sbuf->key.size.depth = 1; diff --git a/src/gallium/drivers/svga/svga_resource_texture.c b/src/gallium/drivers/svga/svga_resource_texture.c index 85e1805c546..a79f43d9fe8 100644 --- a/src/gallium/drivers/svga/svga_resource_texture.c +++ b/src/gallium/drivers/svga/svga_resource_texture.c @@ -34,6 +34,7 @@ #include "util/u_math.h" #include "util/u_memory.h" #include "util/u_resource.h" +#include "util/u_upload_mgr.h" #include "svga_cmd.h" #include "svga_format.h" @@ -403,11 +404,6 @@ svga_texture_transfer_map_direct(struct svga_context *svga, unsigned w, h, nblocksx, nblocksy; unsigned usage = st->base.usage; - if (!surf) { - FREE(st); - return NULL; - } - /* we'll directly access the guest-backed surface */ w = u_minify(texture->width0, level); h = u_minify(texture->height0, level); @@ -417,13 +413,6 @@ svga_texture_transfer_map_direct(struct svga_context *svga, st->base.stride = nblocksx*util_format_get_blocksize(texture->format); st->base.layer_stride = st->base.stride * nblocksy; - /* If this is the first time mapping to the surface in this - * command buffer, clear the dirty masks of this surface. - */ - if (sws->surface_is_flushed(sws, surf)) { - svga_clear_texture_dirty(tex); - } - if (need_tex_readback(transfer)) { enum pipe_error ret; @@ -525,11 +514,6 @@ svga_texture_transfer_map_direct(struct svga_context *svga, st->base.box.y, st->base.box.z); - if (usage & PIPE_TRANSFER_WRITE) { - /* mark this texture level as dirty */ - svga_set_texture_dirty(tex, st->slice, level); - } - return (void *) (map + offset); } } @@ -550,13 +534,17 @@ svga_texture_transfer_map(struct pipe_context *pipe, struct svga_winsys_screen *sws = svga_screen(pipe->screen)->sws; struct svga_texture *tex = svga_texture(texture); struct svga_transfer *st; + struct svga_winsys_surface *surf = tex->handle; boolean use_direct_map = svga_have_gb_objects(svga) && !svga_have_gb_dma(svga); - void *returnVal = NULL; + void *map = NULL; int64_t begin = svga_get_time(svga); SVGA_STATS_TIME_PUSH(sws, SVGA_STATS_TIME_TEXTRANSFERMAP); + if (!surf) + goto done; + /* We can't map texture storage directly unless we have GB objects */ if (usage & PIPE_TRANSFER_MAP_DIRECTLY) { if (svga_have_gb_objects(svga)) @@ -596,14 +584,30 @@ svga_texture_transfer_map(struct pipe_context *pipe, st->use_direct_map = use_direct_map; pipe_resource_reference(&st->base.resource, texture); - if (use_direct_map) { - returnVal = svga_texture_transfer_map_direct(svga, st); + /* If this is the first time mapping to the surface in this + * command buffer, clear the dirty masks of this surface. + */ + if (sws->surface_is_flushed(sws, surf)) { + svga_clear_texture_dirty(tex); + } + + if (!use_direct_map) { + /* upload to the DMA buffer */ + map = svga_texture_transfer_map_dma(svga, st); } else { - returnVal = svga_texture_transfer_map_dma(svga, st); + if (svga_texture_transfer_map_can_upload(svga, st)) { + /* upload to the texture upload buffer */ + map = svga_texture_transfer_map_upload(svga, st); + } + + if (!map) { + /* map directly to the GBS surface */ + map = svga_texture_transfer_map_direct(svga, st); + } } - if (!returnVal) { + if (!map) { FREE(st); } else { @@ -613,13 +617,18 @@ svga_texture_transfer_map(struct pipe_context *pipe, /* record texture upload for HUD */ svga->hud.num_bytes_uploaded += st->base.layer_stride * st->base.box.depth; + + /* mark this texture level as dirty */ + svga_set_texture_dirty(tex, st->slice, level); } } done: svga->hud.map_buffer_time += (svga_get_time(svga) - begin); SVGA_STATS_TIME_POP(sws); - return returnVal; + (void) sws; + + return map; } /** @@ -732,6 +741,7 @@ svga_texture_transfer_unmap_direct(struct svga_context *svga, svga_texture_surface_unmap(svga, transfer); + /* Now send an update command to update the content in the backend. */ if (st->base.usage & PIPE_TRANSFER_WRITE) { struct svga_winsys_surface *surf = tex->handle; SVGA3dBox box; @@ -785,6 +795,7 @@ svga_texture_transfer_unmap_direct(struct svga_context *svga, ret = update_image_vgpu9(svga, surf, &box, st->slice, transfer->level); assert(ret == PIPE_OK); } + (void) ret; } } @@ -800,16 +811,20 @@ svga_texture_transfer_unmap(struct pipe_context *pipe, SVGA_STATS_TIME_PUSH(sws, SVGA_STATS_TIME_TEXTRANSFERUNMAP); - if (st->use_direct_map) { - svga_texture_transfer_unmap_direct(svga, st); + if (!st->use_direct_map) { + svga_texture_transfer_unmap_dma(svga, st); + } + else if (st->upload.buf) { + svga_texture_transfer_unmap_upload(svga, st); } else { - svga_texture_transfer_unmap_dma(svga, st); + svga_texture_transfer_unmap_direct(svga, st); } if (st->base.usage & PIPE_TRANSFER_WRITE) { svga->hud.num_resource_updates++; + /* Mark the texture level as dirty */ ss->texture_timestamp++; svga_age_texture_view(tex, transfer->level); if (transfer->resource->target == PIPE_TEXTURE_CUBE) @@ -821,6 +836,7 @@ svga_texture_transfer_unmap(struct pipe_context *pipe, pipe_resource_reference(&st->base.resource, NULL); FREE(st); SVGA_STATS_TIME_POP(sws); + (void) sws; } @@ -1237,3 +1253,193 @@ svga_texture_generate_mipmap(struct pipe_context *pipe, return TRUE; } + + +/* texture upload buffer default size in bytes */ +#define TEX_UPLOAD_DEFAULT_SIZE (1024 * 1024) + +/** + * Create a texture upload buffer + */ +boolean +svga_texture_transfer_map_upload_create(struct svga_context *svga) +{ + svga->tex_upload = u_upload_create(&svga->pipe, TEX_UPLOAD_DEFAULT_SIZE, + 0, PIPE_USAGE_STAGING); + return svga->tex_upload != NULL; +} + + +/** + * Destroy the texture upload buffer + */ +void +svga_texture_transfer_map_upload_destroy(struct svga_context *svga) +{ + u_upload_destroy(svga->tex_upload); +} + + +/** + * Returns true if this transfer map request can use the upload buffer. + */ +boolean +svga_texture_transfer_map_can_upload(struct svga_context *svga, + struct svga_transfer *st) +{ + struct pipe_resource *texture = st->base.resource; + + if (!svga_have_vgpu10(svga)) + return FALSE; + + if (svga_sws(svga)->have_transfer_from_buffer_cmd == FALSE) + return FALSE; + + if (st->base.usage & PIPE_TRANSFER_READ) + return FALSE; + + /* TransferFromBuffer command is not well supported with multi-samples surface */ + if (texture->nr_samples > 1) + return FALSE; + + /* Do not use the upload path with compressed format or rgb9_e5 */ + if (util_format_is_compressed(texture->format) || + texture->format == PIPE_FORMAT_R9G9B9E5_FLOAT) + return FALSE; + + return TRUE; +} + + +/** + * Use upload buffer for the transfer map request. + */ +void * +svga_texture_transfer_map_upload(struct svga_context *svga, + struct svga_transfer *st) +{ + struct pipe_resource *texture = st->base.resource; + struct pipe_resource *tex_buffer = NULL; + void *tex_map; + unsigned nblocksx, nblocksy; + unsigned offset; + unsigned upload_size; + + assert(svga->tex_upload); + + st->upload.box.x = st->base.box.x; + st->upload.box.y = st->base.box.y; + st->upload.box.z = st->base.box.z; + st->upload.box.w = st->base.box.width; + st->upload.box.h = st->base.box.height; + st->upload.box.d = st->base.box.depth; + st->upload.nlayers = 1; + + switch (texture->target) { + case PIPE_TEXTURE_CUBE: + st->upload.box.z = 0; + break; + case PIPE_TEXTURE_2D_ARRAY: + st->upload.nlayers = st->base.box.depth; + st->upload.box.z = 0; + st->upload.box.d = 1; + break; + case PIPE_TEXTURE_1D_ARRAY: + st->upload.nlayers = st->base.box.depth; + st->upload.box.y = st->upload.box.z = 0; + st->upload.box.d = 1; + break; + default: + break; + } + + nblocksx = util_format_get_nblocksx(texture->format, st->base.box.width); + nblocksy = util_format_get_nblocksy(texture->format, st->base.box.height); + + st->base.stride = nblocksx * util_format_get_blocksize(texture->format); + st->base.layer_stride = st->base.stride * nblocksy; + + /* In order to use the TransferFromBuffer command to update the + * texture content from the buffer, the layer stride for a multi-layers + * surface needs to be in multiples of 16 bytes. + */ + if (st->upload.nlayers > 1 && st->base.layer_stride & 15) + return NULL; + + upload_size = st->base.layer_stride * st->base.box.depth; + upload_size = align(upload_size, 16); + + /* If the upload size exceeds the default buffer size, the + * upload buffer manager code will try to allocate a new buffer + * with the new buffer size. + */ + u_upload_alloc(svga->tex_upload, 0, upload_size, 16, + &offset, &tex_buffer, &tex_map); + + if (!tex_map) { + return NULL; + } + + st->upload.buf = tex_buffer; + st->upload.map = tex_map; + st->upload.offset = offset; + + return tex_map; +} + + +/** + * Unmap upload map transfer request + */ +void +svga_texture_transfer_unmap_upload(struct svga_context *svga, + struct svga_transfer *st) +{ + struct svga_winsys_surface *srcsurf; + struct svga_winsys_surface *dstsurf; + struct pipe_resource *texture = st->base.resource; + enum pipe_error ret; + unsigned subResource; + unsigned numMipLevels; + unsigned i, layer; + unsigned offset = st->upload.offset; + + assert(svga->tex_upload); + assert(st->upload.buf); + + /* unmap the texture upload buffer */ + u_upload_unmap(svga->tex_upload); + + srcsurf = svga_buffer_handle(svga, st->upload.buf); + dstsurf = svga_texture(texture)->handle; + assert(dstsurf); + + numMipLevels = texture->last_level + 1; + + for (i = 0, layer = st->slice; i < st->upload.nlayers; i++, layer++) { + subResource = layer * numMipLevels + st->base.level; + + /* send a transferFromBuffer command to update the host texture surface */ + assert((offset & 15) == 0); + + ret = SVGA3D_vgpu10_TransferFromBuffer(svga->swc, srcsurf, + offset, + st->base.stride, + st->base.layer_stride, + dstsurf, subResource, + &st->upload.box); + if (ret != PIPE_OK) { + svga_context_flush(svga, NULL); + ret = SVGA3D_vgpu10_TransferFromBuffer(svga->swc, srcsurf, + offset, + st->base.stride, + st->base.layer_stride, + dstsurf, subResource, + &st->upload.box); + assert(ret == PIPE_OK); + } + offset += st->base.layer_stride; + } + + pipe_resource_reference(&st->upload.buf, NULL); +} diff --git a/src/gallium/drivers/svga/svga_resource_texture.h b/src/gallium/drivers/svga/svga_resource_texture.h index ffd5feac9a3..ff46ed55f03 100644 --- a/src/gallium/drivers/svga/svga_resource_texture.h +++ b/src/gallium/drivers/svga/svga_resource_texture.h @@ -113,7 +113,22 @@ struct svga_transfer * big enough */ void *swbuf; + /* True if guest backed surface is supported and we can directly map + * to the surface for this transfer. + */ boolean use_direct_map; + + struct { + struct pipe_resource *buf; /* points to the upload buffer if this + * transfer is done via the upload buffer + * instead of directly mapping to the + * resource's surface. + */ + void *map; + unsigned offset; + SVGA3dBox box; + unsigned nlayers; + } upload; }; @@ -256,5 +271,22 @@ svga_texture_generate_mipmap(struct pipe_context *pipe, unsigned first_layer, unsigned last_layer); +boolean +svga_texture_transfer_map_upload_create(struct svga_context *svga); + +void +svga_texture_transfer_map_upload_destroy(struct svga_context *svga); + +boolean +svga_texture_transfer_map_can_upload(struct svga_context *svga, + struct svga_transfer *st); + +void * +svga_texture_transfer_map_upload(struct svga_context *svga, + struct svga_transfer *st); + +void +svga_texture_transfer_unmap_upload(struct svga_context *svga, + struct svga_transfer *st); #endif /* SVGA_TEXTURE_H */ diff --git a/src/gallium/drivers/svga/svga_winsys.h b/src/gallium/drivers/svga/svga_winsys.h index 901a73eba0a..f226581774e 100644 --- a/src/gallium/drivers/svga/svga_winsys.h +++ b/src/gallium/drivers/svga/svga_winsys.h @@ -711,6 +711,7 @@ struct svga_winsys_screen boolean have_generate_mipmap_cmd; boolean have_set_predication_cmd; + boolean have_transfer_from_buffer_cmd; }; -- 2.30.2