svga: Ensure DMA commands are serialized with unsynchronized flag is unset.
authorJosé Fonseca <jfonseca@vmware.com>
Fri, 11 Mar 2011 14:00:25 +0000 (14:00 +0000)
committerJosé Fonseca <jfonseca@vmware.com>
Tue, 15 Mar 2011 15:38:18 +0000 (15:38 +0000)
src/gallium/drivers/svga/svga_resource_buffer.c
src/gallium/drivers/svga/svga_resource_buffer_upload.c
src/gallium/drivers/svga/svga_resource_buffer_upload.h

index b0e6d96b738c48a4e863e0e7a1ca080884162dc9..ae854a8054a7c6ee95d2e1ee789fde2d8202cb23 100644 (file)
@@ -51,18 +51,105 @@ svga_buffer_needs_hw_storage(unsigned usage)
 }
 
 
+/**
+ * Map a range of a buffer.
+ *
+ * Unlike texture DMAs (which are written immediately to the command buffer and
+ * therefore inherently serialized with other context operations), for buffers
+ * we try to coalesce multiple range mappings (i.e, multiple calls to this
+ * function) into a single DMA command, for better efficiency in command
+ * processing.  This means we need to exercise extra care here to ensure that
+ * the end result is exactly the same as if one DMA was used for every mapped
+ * range.
+ */
 static void *
-svga_buffer_map_range( struct pipe_screen *screen,
+svga_buffer_map_range( struct pipe_context *pipe,
                        struct pipe_resource *buf,
                        unsigned offset,
                       unsigned length,
                        unsigned usage )
 {
-   struct svga_screen *ss = svga_screen(screen); 
+   struct svga_context *svga = svga_context(pipe);
+   struct svga_screen *ss = svga_screen(pipe->screen);
    struct svga_winsys_screen *sws = ss->sws;
    struct svga_buffer *sbuf = svga_buffer( buf );
    void *map;
 
+   if (usage & PIPE_TRANSFER_WRITE) {
+      if (usage & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE) {
+         /*
+          * Finish writing any pending DMA commands, and tell the host to discard
+          * the buffer contents on the next DMA operation.
+          */
+
+         if (sbuf->dma.pending) {
+            svga_buffer_upload_flush(svga, sbuf);
+
+            /*
+             * Instead of flushing the context command buffer, simply discard
+             * the current hwbuf, and start a new one.
+             */
+
+            svga_buffer_destroy_hw_storage(ss, sbuf);
+         }
+
+         sbuf->map.num_ranges = 0;
+         sbuf->dma.flags.discard = TRUE;
+      }
+
+      if (usage & PIPE_TRANSFER_UNSYNCHRONIZED) {
+         if (!sbuf->map.num_ranges) {
+            /*
+             * No pending ranges to upload so far, so we can tell the host to
+             * not synchronize on the next DMA command.
+             */
+
+            sbuf->dma.flags.unsynchronized = TRUE;
+         }
+      } else {
+         /*
+          * Synchronizing, so finish writing any pending DMA command, and
+          * ensure the next DMA will be done in order.
+          */
+
+         if (sbuf->dma.pending) {
+            svga_buffer_upload_flush(svga, sbuf);
+
+            if (sbuf->hwbuf) {
+               /*
+                * We have a pending DMA upload from a hardware buffer, therefore
+                * we need to ensure that the host finishes processing that DMA
+                * command before the state tracker can start overwriting the
+                * hardware buffer.
+                *
+                * XXX: This could be avoided by tying the hardware buffer to
+                * the transfer (just as done with textures), which would allow
+                * overlapping DMAs commands to be queued on the same context
+                * buffer. However, due to the likelihood of software vertex
+                * processing, it is more convenient to hold on to the hardware
+                * buffer, allowing to quickly access the contents from the CPU
+                * without having to do a DMA download from the host.
+                */
+
+               if (usage & PIPE_TRANSFER_DONTBLOCK) {
+                  /*
+                   * Flushing the command buffer here will most likely cause
+                   * the map of the hwbuf below to block, so preemptively
+                   * return NULL here if DONTBLOCK is set to prevent unnecessary
+                   * command buffer flushes.
+                   */
+
+                  return NULL;
+               }
+
+               svga_context_flush(svga, NULL);
+            }
+         }
+
+         sbuf->dma.flags.unsynchronized = FALSE;
+      }
+   }
+
    if (!sbuf->swbuf && !sbuf->hwbuf) {
       if (svga_buffer_create_hw_storage(ss, sbuf) != PIPE_OK) {
          /*
@@ -108,12 +195,12 @@ svga_buffer_map_range( struct pipe_screen *screen,
 
 
 static void 
-svga_buffer_flush_mapped_range( struct pipe_screen *screen,
+svga_buffer_flush_mapped_range( struct pipe_context *pipe,
                                 struct pipe_resource *buf,
                                 unsigned offset, unsigned length)
 {
    struct svga_buffer *sbuf = svga_buffer( buf );
-   struct svga_screen *ss = svga_screen(screen);
+   struct svga_screen *ss = svga_screen(pipe->screen);
    
    pipe_mutex_lock(ss->swc_mutex);
    assert(sbuf->map.writing);
@@ -125,10 +212,10 @@ svga_buffer_flush_mapped_range( struct pipe_screen *screen,
 }
 
 static void 
-svga_buffer_unmap( struct pipe_screen *screen,
+svga_buffer_unmap( struct pipe_context *pipe,
                    struct pipe_resource *buf)
 {
-   struct svga_screen *ss = svga_screen(screen); 
+   struct svga_screen *ss = svga_screen(pipe->screen);
    struct svga_winsys_screen *sws = ss->sws;
    struct svga_buffer *sbuf = svga_buffer( buf );
    
@@ -192,7 +279,7 @@ static void *
 svga_buffer_transfer_map( struct pipe_context *pipe,
                          struct pipe_transfer *transfer )
 {
-   uint8_t *map = svga_buffer_map_range( pipe->screen,
+   uint8_t *map = svga_buffer_map_range( pipe,
                                         transfer->resource,
                                         transfer->box.x,
                                         transfer->box.width,
@@ -215,7 +302,7 @@ static void svga_buffer_transfer_flush_region( struct pipe_context *pipe,
 {
    assert(box->x + box->width <= transfer->box.width);
 
-   svga_buffer_flush_mapped_range(pipe->screen,
+   svga_buffer_flush_mapped_range(pipe,
                                  transfer->resource,
                                  transfer->box.x + box->x,
                                  box->width);
@@ -224,7 +311,7 @@ static void svga_buffer_transfer_flush_region( struct pipe_context *pipe,
 static void svga_buffer_transfer_unmap( struct pipe_context *pipe,
                            struct pipe_transfer *transfer )
 {
-   svga_buffer_unmap(pipe->screen,
+   svga_buffer_unmap(pipe,
                     transfer->resource);
 }
 
index b7d54605e665538e10bf4694c23a30071d2ca6c2..0bfa8a14a6fcfa57a359f4c6681a597076e93c86 100644 (file)
@@ -252,7 +252,7 @@ svga_buffer_upload_command(struct svga_context *svga,
  * Patch up the upload DMA command reserved by svga_buffer_upload_command
  * with the final ranges.
  */
-static void
+void
 svga_buffer_upload_flush(struct svga_context *svga,
                          struct svga_buffer *sbuf)
 {
@@ -260,6 +260,10 @@ svga_buffer_upload_flush(struct svga_context *svga,
    unsigned i;
    struct pipe_resource *dummy;
 
+   if (!sbuf->dma.pending) {
+      return;
+   }
+
    assert(sbuf->handle); 
    assert(sbuf->hwbuf);
    assert(sbuf->map.num_ranges);
@@ -296,6 +300,8 @@ svga_buffer_upload_flush(struct svga_context *svga,
    sbuf->head.next = sbuf->head.prev = NULL; 
 #endif
    sbuf->dma.pending = FALSE;
+   sbuf->dma.flags.discard = FALSE;
+   sbuf->dma.flags.unsynchronized = FALSE;
 
    sbuf->dma.svga = NULL;
    sbuf->dma.boxes = NULL;
@@ -306,7 +312,6 @@ svga_buffer_upload_flush(struct svga_context *svga,
 }
 
 
-
 /**
  * Note a dirty range.
  *
@@ -337,12 +342,6 @@ svga_buffer_add_range(struct svga_buffer *sbuf,
 
    /*
     * Try to grow one of the ranges.
-    *
-    * Note that it is not this function task to care about overlapping ranges,
-    * as the GMR was already given so it is too late to do anything. Situations
-    * where overlapping ranges may pose a problem should be detected via
-    * pipe_context::is_resource_referenced and the context that refers to the
-    * buffer should be flushed.
     */
 
    for(i = 0; i < sbuf->map.num_ranges; ++i) {
@@ -357,6 +356,11 @@ svga_buffer_add_range(struct svga_buffer *sbuf,
       if (dist <= 0) {
          /*
           * Ranges are contiguous or overlapping -- extend this one and return.
+          *
+          * Note that it is not this function's task to prevent overlapping
+          * ranges, as the GMR was already given so it is too late to do
+          * anything.  If the ranges overlap here it must surely be because
+          * PIPE_TRANSFER_UNSYNCHRONIZED was set.
           */
 
          sbuf->map.ranges[i].start = MIN2(sbuf->map.ranges[i].start, start);
@@ -380,8 +384,7 @@ svga_buffer_add_range(struct svga_buffer *sbuf,
     * pending DMA upload and start clean.
     */
 
-   if(sbuf->dma.pending)
-      svga_buffer_upload_flush(sbuf->dma.svga, sbuf);
+   svga_buffer_upload_flush(sbuf->dma.svga, sbuf);
 
    assert(!sbuf->dma.pending);
    assert(!sbuf->dma.svga);
index 11df3065263158d134176b967b3091828baa857a..13d8f3e299b7786a58809ce23427aa914ba9ca5c 100644 (file)
 #define SVGA_BUFFER_UPLOAD_H
 
 
+void
+svga_buffer_upload_flush(struct svga_context *svga,
+                         struct svga_buffer *sbuf);
+
 void
 svga_buffer_add_range(struct svga_buffer *sbuf,
                       unsigned start,