svga: create buffer surfaces for incompatible bind flags
authorCharmaine Lee <charmainel@vmware.com>
Wed, 16 Nov 2016 23:31:00 +0000 (15:31 -0800)
committerBrian Paul <brianp@vmware.com>
Tue, 27 Jun 2017 13:49:02 +0000 (07:49 -0600)
In cases where certain bind flags cannot be enabled together,
such as CONSTANT_BUFFER cannot be combined with any other flags,
a separate host surface will be created.
For example, if a stream output buffer is reused as a constant buffer,
two host surfaces will be created, one for stream output,
and another one for constant buffer. Data will be copied from the
stream output surface to the constant buffer surface.

Fixes piglit test ext_transform_feedback-immediate-reuse-index-buffer,
                  ext_transform_feedback-immediate-reuse-uniform-buffer

Tested with MTT piglit, MTT glretrace, Nature, NobelClinician Viewer, Tropics.

v2: Fix bind flags compatibility check as suggested by Brian.
v3: Use the list utility to maintain the buffer surface list.
v4: Use the SAFE rev of LIST_FOR_EACH_ENTRY

Reviewed-by: Brian Paul <brianp@vmware.com>
Reviewed-by: Jose Fonseca <jfonseca@vmware.com>
src/gallium/drivers/svga/svga_resource_buffer.c
src/gallium/drivers/svga/svga_resource_buffer.h
src/gallium/drivers/svga/svga_resource_buffer_upload.c
src/gallium/drivers/svga/svga_resource_buffer_upload.h

index 39aa530a6727e04f365e8624608f81f459a186d1..e9d31de6166919398f4097b93e89d26bfe26ec78 100644 (file)
@@ -413,21 +413,26 @@ svga_buffer_create(struct pipe_screen *screen,
    sbuf->b.b.screen = screen;
    bind_flags = template->bind;
 
+   LIST_INITHEAD(&sbuf->surfaces);
+
    if (bind_flags & PIPE_BIND_CONSTANT_BUFFER) {
       /* Constant buffers can only have the PIPE_BIND_CONSTANT_BUFFER
        * flag set.
        */
       if (ss->sws->have_vgpu10) {
          bind_flags = PIPE_BIND_CONSTANT_BUFFER;
-
-         /* Constant buffer size needs to be in multiples of 16. */
-         sbuf->b.b.width0 = align(sbuf->b.b.width0, 16);
       }
    }
 
+   /* Although svga device only requires constant buffer size to be
+    * in multiples of 16, in order to allow bind_flags promotion,
+    * we are mandating all buffer size to be in multiples of 16.
+    */
+   sbuf->b.b.width0 = align(sbuf->b.b.width0, 16);
+
    if (svga_buffer_needs_hw_storage(bind_flags)) {
 
-      /* If the buffer will be used for vertex/index/stream data, set
+      /* If the buffer is not used for constant buffer, set
        * the vertex/index bind flags as well so that the buffer will be
        * accepted for those uses.
        * Note that the PIPE_BIND_ flags we get from the state tracker are
index 2096a9821cc574a97206136355ebfed2b380d9ab..db533416ec4cef9d0591d1cfd7ab48c9eb0ea601 100644 (file)
@@ -58,6 +58,18 @@ struct svga_buffer_range
 
 struct svga_3d_update_gb_image;
 
+/**
+ * This structure describes the bind flags and cache key associated
+ * with the host surface.
+ */
+struct svga_buffer_surface
+{
+   struct list_head list;
+   unsigned bind_flags;
+   struct svga_host_surface_cache_key key;
+   struct svga_winsys_surface *handle;
+};
+
 /**
  * SVGA pipe buffer.
  */
@@ -100,6 +112,12 @@ struct svga_buffer
     */
    struct svga_winsys_surface *handle;
 
+   /**
+    * List of surfaces created for this buffer resource to support
+    * incompatible bind flags.
+    */
+   struct list_head surfaces;
+
    /**
     * Information about ongoing and past map operations.
     */
index d18d026bfa1284646363678453974a64560d7a38..d467182f1ca86f34597c2784f8648658cc52e4f6 100644 (file)
@@ -148,6 +148,8 @@ svga_buffer_create_host_surface(struct svga_screen *ss,
                                 struct svga_buffer *sbuf,
                                 unsigned bind_flags)
 {
+   enum pipe_error ret = PIPE_OK;
+
    assert(!sbuf->user);
 
    if (!sbuf->handle) {
@@ -207,9 +209,13 @@ svga_buffer_create_host_surface(struct svga_screen *ss,
 
       SVGA_DBG(DEBUG_DMA, "   --> got sid %p sz %d (buffer)\n",
                sbuf->handle, sbuf->b.b.width0);
+
+      /* Add the new surface to the buffer surface list */
+      ret = svga_buffer_add_host_surface(sbuf, sbuf->handle, &sbuf->key,
+                                         bind_flags);
    }
 
-   return PIPE_OK;
+   return ret;
 }
 
 
@@ -221,23 +227,17 @@ svga_buffer_recreate_host_surface(struct svga_context *svga,
                                   struct svga_buffer *sbuf,
                                   unsigned bind_flags)
 {
-   struct svga_screen *ss = svga_screen(sbuf->b.b.screen);
-   struct svga_winsys_surface *old_handle;
-   struct svga_host_surface_cache_key old_key;
    enum pipe_error ret = PIPE_OK;
+   struct svga_winsys_surface *old_handle = sbuf->handle;
 
    assert(sbuf->bind_flags != bind_flags);
+   assert(old_handle);
 
-   /* Flush any pending upload first */
-   svga_buffer_upload_flush(svga, sbuf);
-
-   /* Save the old resource handle and key */
-   old_handle = sbuf->handle;
-   old_key = sbuf->key;
    sbuf->handle = NULL;
  
-   /* Create a new resource with the required bind_flags */
-   ret = svga_buffer_create_host_surface(ss, sbuf, bind_flags);
+   /* Create a new resource with the requested bind_flags */
+   ret = svga_buffer_create_host_surface(svga_screen(svga->pipe.screen),
+                                         sbuf, bind_flags);
    if (ret == PIPE_OK) {
       /* Copy the surface data */
       assert(sbuf->handle);
@@ -254,20 +254,175 @@ svga_buffer_recreate_host_surface(struct svga_context *svga,
    /* Set the new bind flags for this buffer resource */
    sbuf->bind_flags = bind_flags;
 
-   /* Destroy the old resource handle */
-   svga_screen_surface_destroy(ss, &old_key, &old_handle);
+   return ret;
+}
+
+
+/**
+ * Returns TRUE if the surface bind flags is compatible with the new bind flags.
+ */
+static boolean
+compatible_bind_flags(unsigned bind_flags,
+                      unsigned tobind_flags)
+{
+   if ((bind_flags & tobind_flags) == tobind_flags)
+      return TRUE;
+   else if ((bind_flags|tobind_flags) & PIPE_BIND_CONSTANT_BUFFER)
+      return FALSE;
+   else
+      return TRUE;
+}
+
+
+/**
+ * Returns a buffer surface from the surface list
+ * that has the requested bind flags or its existing bind flags
+ * can be promoted to include the new bind flags.
+ */
+static struct svga_buffer_surface *
+svga_buffer_get_host_surface(struct svga_buffer *sbuf,
+                             unsigned bind_flags)
+{
+   struct svga_buffer_surface *bufsurf;
+
+   LIST_FOR_EACH_ENTRY(bufsurf, &sbuf->surfaces, list) {
+      if (compatible_bind_flags(bufsurf->bind_flags, bind_flags))
+         return bufsurf;
+   }
+   return NULL;
+}
+
+
+/**
+ * Adds the host surface to the buffer surface list.
+ */
+enum pipe_error
+svga_buffer_add_host_surface(struct svga_buffer *sbuf,
+                             struct svga_winsys_surface *handle,
+                             struct svga_host_surface_cache_key *key,
+                             unsigned bind_flags)
+{
+   struct svga_buffer_surface *bufsurf;
+
+   bufsurf = CALLOC_STRUCT(svga_buffer_surface);
+   if (!bufsurf)
+      return PIPE_ERROR_OUT_OF_MEMORY;
+
+   bufsurf->bind_flags = bind_flags;
+   bufsurf->handle = handle;
+   bufsurf->key = *key;
+
+   /* add the surface to the surface list */
+   LIST_ADD(&bufsurf->list, &sbuf->surfaces);
+
+   return PIPE_OK;
+}
+
+
+/**
+ * Start using the specified surface for this buffer resource.
+ */
+void
+svga_buffer_bind_host_surface(struct svga_context *svga,
+                              struct svga_buffer *sbuf,
+                              struct svga_buffer_surface *bufsurf)
+{
+   enum pipe_error ret;
+
+   /* Update the to-bind surface */
+   assert(bufsurf->handle);
+   assert(sbuf->handle);
 
+   /* If we are switching from stream output to other buffer,
+    * make sure to copy the buffer content.
+    */
+   if (sbuf->bind_flags & PIPE_BIND_STREAM_OUTPUT) {
+      ret = SVGA3D_vgpu10_BufferCopy(svga->swc, sbuf->handle, bufsurf->handle,
+                                     0, 0, sbuf->b.b.width0);
+      if (ret != PIPE_OK) {
+         svga_context_flush(svga, NULL);
+         ret = SVGA3D_vgpu10_BufferCopy(svga->swc, sbuf->handle, bufsurf->handle,
+                                        0, 0, sbuf->b.b.width0);
+         assert(ret == PIPE_OK);
+      }
+   }
+
+   /* Set this surface as the current one */
+   sbuf->handle = bufsurf->handle;
+   sbuf->key = bufsurf->key;
+   sbuf->bind_flags = bufsurf->bind_flags;
+}
+
+
+/**
+ * Prepare a host surface that can be used as indicated in the
+ * tobind_flags. If the existing host surface is not created
+ * with the necessary binding flags and if the new bind flags can be
+ * combined with the existing bind flags, then we will recreate a
+ * new surface with the combined bind flags. Otherwise, we will create
+ * a surface for that incompatible bind flags.
+ * For example, if a stream output buffer is reused as a constant buffer,
+ * since constant buffer surface cannot be bound as a stream output surface,
+ * two surfaces will be created, one for stream output,
+ * and another one for constant buffer.
+ */
+enum pipe_error
+svga_buffer_validate_host_surface(struct svga_context *svga,
+                                  struct svga_buffer *sbuf,
+                                  unsigned tobind_flags)
+{
+   struct svga_buffer_surface *bufsurf;
+   enum pipe_error ret = PIPE_OK;
+
+   /* Flush any pending upload first */
+   svga_buffer_upload_flush(svga, sbuf);
+
+   /* First check from the cached buffer surface list to see if there is
+    * already a buffer surface that has the requested bind flags, or
+    * surface with compatible bind flags that can be promoted.
+    */
+   bufsurf = svga_buffer_get_host_surface(sbuf, tobind_flags);
+   
+   if (bufsurf) {
+      if ((bufsurf->bind_flags & tobind_flags) == tobind_flags) {
+         /* there is a surface with the requested bind flags */
+         svga_buffer_bind_host_surface(svga, sbuf, bufsurf);
+      } else {
+
+         /* Recreate a host surface with the combined bind flags */
+         ret = svga_buffer_recreate_host_surface(svga, sbuf,
+                                                 bufsurf->bind_flags |
+                                                 tobind_flags);
+
+         /* Destroy the old surface */
+         svga_screen_surface_destroy(svga_screen(sbuf->b.b.screen),
+                                     &bufsurf->key, &bufsurf->handle);
+         
+         LIST_DEL(&bufsurf->list);
+         FREE(bufsurf);
+      }
+   } else {
+      /* Need to create a new surface if the bind flags are incompatible,
+       * such as constant buffer surface & stream output surface.
+       */
+      ret = svga_buffer_recreate_host_surface(svga, sbuf,
+                                              tobind_flags);
+   }
    return ret;
 }
 
+
 void
 svga_buffer_destroy_host_surface(struct svga_screen *ss,
                                  struct svga_buffer *sbuf)
 {
-   if (sbuf->handle) {
+   struct svga_buffer_surface *bufsurf, *next;
+
+   LIST_FOR_EACH_ENTRY_SAFE(bufsurf, next, &sbuf->surfaces, list) {
       SVGA_DBG(DEBUG_DMA, " ungrab sid %p sz %d\n",
-               sbuf->handle, sbuf->b.b.width0);
-      svga_screen_surface_destroy(ss, &sbuf->key, &sbuf->handle);
+               bufsurf->handle, sbuf->b.b.width0);
+      svga_screen_surface_destroy(ss, &bufsurf->key, &bufsurf->handle);
+      FREE(bufsurf);
    }
 }
 
@@ -841,13 +996,9 @@ svga_buffer_handle(struct svga_context *svga, struct pipe_resource *buf,
    if (sbuf->handle) {
       if ((sbuf->bind_flags & tobind_flags) != tobind_flags) {
          /* If the allocated resource's bind flags do not include the
-          * requested bind flags, create a new resource to include the
-          * new bind flags, and do a BufferCopy from the old resource to
-          * the new one.
+          * requested bind flags, validate the host surface.
           */
-         assert(svga_have_vgpu10(svga));
-         ret = svga_buffer_recreate_host_surface(svga, sbuf,
-                                                 sbuf->bind_flags|tobind_flags);
+         ret = svga_buffer_validate_host_surface(svga, sbuf, tobind_flags);
          if (ret != PIPE_OK)
             return NULL;
       }
index 877ae5499a1485a3971f0be865b101c28521d697..c2d749b20911fb6c20ec76dad2b3ea5def0ab1dc 100644 (file)
@@ -55,6 +55,22 @@ svga_buffer_recreate_host_surface(struct svga_context *svga,
                                   struct svga_buffer *sbuf,
                                   unsigned bind_flags);
 
+enum pipe_error
+svga_buffer_add_host_surface(struct svga_buffer *sbuf,
+                             struct svga_winsys_surface *handle,
+                             struct svga_host_surface_cache_key *key,
+                             unsigned bind_flags);
+
+void
+svga_buffer_bind_host_surface(struct svga_context *svga,
+                             struct svga_buffer *sbuf,
+                             struct svga_buffer_surface *bufsurf);
+
+enum pipe_error
+svga_buffer_validate_host_surface(struct svga_context *svga,
+                                  struct svga_buffer *sbuf,
+                                  unsigned bind_flags);
+
 void
 svga_buffer_destroy_host_surface(struct svga_screen *ss,
                                  struct svga_buffer *sbuf);