svga: use upload buffer for upload texture.
authorCharmaine Lee <charmainel@vmware.com>
Tue, 6 Sep 2016 18:29:41 +0000 (11:29 -0700)
committerBrian Paul <brianp@vmware.com>
Sat, 17 Sep 2016 16:24:13 +0000 (10:24 -0600)
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 <brianp@vmware.com>
src/gallium/drivers/svga/svga_context.c
src/gallium/drivers/svga/svga_context.h
src/gallium/drivers/svga/svga_resource_buffer_upload.c
src/gallium/drivers/svga/svga_resource_texture.c
src/gallium/drivers/svga/svga_resource_texture.h
src/gallium/drivers/svga/svga_winsys.h

index 1ed0f3d1b78226497884fc84258d440138ee7b73..10085102110a34881e1f680d14d3c66ea79b645d 100644 (file)
@@ -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)
index afb04120ef57f20f5eb044bbd983325eee27604e..cbc4a9c86e95bb041fe06d637b4b6d96c3612cd3 100644 (file)
@@ -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;
index 7ecf95c1af7f2157e11bfdfd4f92f1acf1f197c0..ac8cedfd3b791256c75ca0fbc1894f430efb4268 100644 (file)
@@ -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;
index 85e1805c546ffb0d9027fb8cbc9a93836200c811..a79f43d9fe8bb800bb5e4bd4540d569dfe16aa40 100644 (file)
@@ -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);
+}
index ffd5feac9a326e22a8242049ff1aa938ad5b7479..ff46ed55f03d28583d952cbc68f723b9308d8819 100644 (file)
@@ -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 */
index 901a73eba0ae785be8423657ecca07bb194f4f0f..f226581774e0307b1eaed473978c618ad14dc6c9 100644 (file)
@@ -711,6 +711,7 @@ struct svga_winsys_screen
 
    boolean have_generate_mipmap_cmd;
    boolean have_set_predication_cmd;
+   boolean have_transfer_from_buffer_cmd;
 };