r300g: align the height of NPOT textures to POT
[mesa.git] / src / gallium / drivers / svga / svga_screen_buffer.c
index bf637f2aa93b7a950c6f83008bfbc001db8e16b8..1ff6a3a5b317172dce122055b466ea8e52ce5eb8 100644 (file)
@@ -151,6 +151,8 @@ static INLINE enum pipe_error
 svga_buffer_create_hw_storage(struct svga_screen *ss,
                               struct svga_buffer *sbuf)
 {
+   assert(!sbuf->user);
+
    if(!sbuf->hwbuf) {
       unsigned alignment = sbuf->base.alignment;
       unsigned usage = 0;
@@ -178,7 +180,6 @@ svga_buffer_upload_command(struct svga_context *svga,
    struct svga_winsys_buffer *guest = sbuf->hwbuf;
    struct svga_winsys_surface *host = sbuf->handle;
    SVGA3dTransferType transfer = SVGA3D_WRITE_HOST_VRAM;
-   SVGA3dSurfaceDMAFlags flags = sbuf->dma.flags;
    SVGA3dCmdSurfaceDMA *cmd;
    uint32 numBoxes = sbuf->map.num_ranges;
    SVGA3dCopyBox *boxes;
@@ -228,9 +229,11 @@ svga_buffer_upload_command(struct svga_context *svga,
    pSuffix = (SVGA3dCmdSurfaceDMASuffix *)((uint8_t*)cmd + sizeof *cmd + numBoxes * sizeof *boxes);
    pSuffix->suffixSize = sizeof *pSuffix;
    pSuffix->maximumOffset = sbuf->base.size;
-   pSuffix->flags = flags;
+   pSuffix->flags = sbuf->dma.flags;
+
+   SVGA_FIFOCommitAll(swc);
 
-   swc->commit(swc);
+   sbuf->dma.flags.discard = FALSE;
 
    return PIPE_OK;
 }
@@ -276,7 +279,6 @@ svga_buffer_upload_flush(struct svga_context *svga,
    }
 
    sbuf->map.num_ranges = 0;
-   memset(&sbuf->dma.flags, 0, sizeof sbuf->dma.flags);
 
    assert(sbuf->head.prev && sbuf->head.next);
    LIST_DEL(&sbuf->head);
@@ -312,7 +314,6 @@ svga_buffer_add_range(struct svga_buffer *sbuf,
    unsigned nearest_range;
    unsigned nearest_dist;
 
-   assert(sbuf->hwbuf);
    assert(end > start);
    
    if (sbuf->map.num_ranges < SVGA_BUFFER_MAX_RANGES) {
@@ -466,8 +467,7 @@ svga_buffer_flush_mapped_range( struct pipe_screen *screen,
    assert(sbuf->map.writing);
    if(sbuf->map.writing) {
       assert(sbuf->map.flush_explicit);
-      if(sbuf->hwbuf)
-         svga_buffer_add_range(sbuf, offset, offset + length);
+      svga_buffer_add_range(sbuf, offset, offset + length);
    }
    pipe_mutex_unlock(ss->swc_mutex);
 }
@@ -494,8 +494,7 @@ svga_buffer_unmap( struct pipe_screen *screen,
          /* No mapped range was flushed -- flush the whole buffer */
          SVGA_DBG(DEBUG_DMA, "flushing the whole buffer\n");
    
-         if(sbuf->hwbuf)
-            svga_buffer_add_range(sbuf, 0, sbuf->base.size);
+         svga_buffer_add_range(sbuf, 0, sbuf->base.size);
       }
       
       sbuf->map.writing = FALSE;
@@ -518,6 +517,9 @@ svga_buffer_destroy( struct pipe_buffer *buf )
    if(sbuf->handle)
       svga_buffer_destroy_host_surface(ss, sbuf);
    
+   if(sbuf->uploaded.buffer)
+      pipe_buffer_reference(&sbuf->uploaded.buffer, NULL);
+
    if(sbuf->hwbuf)
       svga_buffer_destroy_hw_storage(ss, sbuf);
    
@@ -615,12 +617,13 @@ svga_screen_init_buffer_functions(struct pipe_screen *screen)
 }
 
 
-/** 
- * Copy the contents of the user buffer / malloc buffer to a hardware buffer.
+/**
+ * Copy the contents of the malloc buffer to a hardware buffer.
  */
 static INLINE enum pipe_error
 svga_buffer_update_hw(struct svga_screen *ss, struct svga_buffer *sbuf)
 {
+   assert(!sbuf->user);
    if(!sbuf->hwbuf) {
       enum pipe_error ret;
       void *map;
@@ -630,7 +633,6 @@ svga_buffer_update_hw(struct svga_screen *ss, struct svga_buffer *sbuf)
          return PIPE_ERROR;
       
       ret = svga_buffer_create_hw_storage(ss, sbuf);
-      assert(ret == PIPE_OK);
       if(ret != PIPE_OK)
          return ret;
 
@@ -639,7 +641,8 @@ svga_buffer_update_hw(struct svga_screen *ss, struct svga_buffer *sbuf)
       assert(map);
       if(!map) {
         pipe_mutex_unlock(ss->swc_mutex);
-         return PIPE_ERROR_OUT_OF_MEMORY;
+         svga_buffer_destroy_hw_storage(ss, sbuf);
+         return PIPE_ERROR;
       }
 
       memcpy(map, sbuf->swbuf, sbuf->base.size);
@@ -655,10 +658,89 @@ svga_buffer_update_hw(struct svga_screen *ss, struct svga_buffer *sbuf)
          sbuf->swbuf = NULL;
       }
       
-      svga_buffer_add_range(sbuf, 0, sbuf->base.size);
+      pipe_mutex_unlock(ss->swc_mutex);
    }
    
-   pipe_mutex_unlock(ss->swc_mutex);
+   return PIPE_OK;
+}
+
+
+/**
+ * Upload the buffer to the host in a piecewise fashion.
+ *
+ * Used when the buffer is too big to fit in the GMR aperture.
+ */
+static INLINE enum pipe_error
+svga_buffer_upload_piecewise(struct svga_screen *ss,
+                             struct svga_context *svga,
+                             struct svga_buffer *sbuf)
+{
+   struct svga_winsys_screen *sws = ss->sws;
+   const unsigned alignment = sizeof(void *);
+   const unsigned usage = 0;
+   unsigned i;
+
+   assert(sbuf->map.num_ranges);
+   assert(!sbuf->dma.pending);
+
+   SVGA_DBG(DEBUG_DMA, "dma to sid %p\n", sbuf->handle);
+
+   for (i = 0; i < sbuf->map.num_ranges; ++i) {
+      struct svga_buffer_range *range = &sbuf->map.ranges[i];
+      unsigned offset = range->start;
+      unsigned size = range->end - range->start;
+
+      while (offset < range->end) {
+         struct svga_winsys_buffer *hwbuf;
+         uint8_t *map;
+         enum pipe_error ret;
+
+         if (offset + size > range->end)
+            size = range->end - offset;
+
+         hwbuf = svga_winsys_buffer_create(ss, alignment, usage, size);
+         while (!hwbuf) {
+            size /= 2;
+            if (!size)
+               return PIPE_ERROR_OUT_OF_MEMORY;
+            hwbuf = svga_winsys_buffer_create(ss, alignment, usage, size);
+         }
+
+         SVGA_DBG(DEBUG_DMA, "  bytes %u - %u\n",
+                  offset, offset + size);
+
+         map = sws->buffer_map(sws, hwbuf,
+                               PIPE_BUFFER_USAGE_CPU_WRITE |
+                               PIPE_BUFFER_USAGE_DISCARD);
+         assert(map);
+         if (map) {
+            memcpy(map, sbuf->swbuf, size);
+            sws->buffer_unmap(sws, hwbuf);
+         }
+
+         ret = SVGA3D_BufferDMA(svga->swc,
+                                hwbuf, sbuf->handle,
+                                SVGA3D_WRITE_HOST_VRAM,
+                                size, 0, offset, sbuf->dma.flags);
+         if(ret != PIPE_OK) {
+            svga_context_flush(svga, NULL);
+            ret =  SVGA3D_BufferDMA(svga->swc,
+                                    hwbuf, sbuf->handle,
+                                    SVGA3D_WRITE_HOST_VRAM,
+                                    size, 0, offset, sbuf->dma.flags);
+            assert(ret == PIPE_OK);
+         }
+
+         sbuf->dma.flags.discard = FALSE;
+
+         sws->buffer_destroy(sws, hwbuf);
+
+         offset += size;
+      }
+   }
+
+   sbuf->map.num_ranges = 0;
+
    return PIPE_OK;
 }
 
@@ -678,34 +760,74 @@ svga_buffer_handle(struct svga_context *svga,
    sbuf = svga_buffer(buf);
    
    assert(!sbuf->map.count);
+   assert(!sbuf->user);
    
    if(!sbuf->handle) {
       ret = svga_buffer_create_host_surface(ss, sbuf);
       if(ret != PIPE_OK)
         return NULL;
-
-      ret = svga_buffer_update_hw(ss, sbuf);
-      if(ret != PIPE_OK)
-        return NULL;
    }
 
-   if(!sbuf->dma.pending && sbuf->map.num_ranges) {
-      /* Queue the buffer for flushing */
-      ret = svga_buffer_upload_command(svga, sbuf);
-      if(ret != PIPE_OK)
-         /* XXX: Should probably have a richer return value */
-         return NULL;
+   assert(sbuf->handle);
 
-      assert(sbuf->dma.svga == svga);
+   if (sbuf->map.num_ranges) {
+      if (!sbuf->dma.pending) {
+         /*
+          * No pending DMA upload yet, so insert a DMA upload command now.
+          */
 
-      sbuf->dma.pending = TRUE;
-      assert(!sbuf->head.prev && !sbuf->head.next);
-      LIST_ADDTAIL(&sbuf->head, &svga->dirty_buffers);
+         /*
+          * Migrate the data from swbuf -> hwbuf if necessary.
+          */
+         ret = svga_buffer_update_hw(ss, sbuf);
+         if (ret == PIPE_OK) {
+            /*
+             * Queue a dma command.
+             */
+
+            ret = svga_buffer_upload_command(svga, sbuf);
+            if (ret == PIPE_ERROR_OUT_OF_MEMORY) {
+               svga_context_flush(svga, NULL);
+               ret = svga_buffer_upload_command(svga, sbuf);
+               assert(ret == PIPE_OK);
+            }
+            if (ret == PIPE_OK) {
+               sbuf->dma.pending = TRUE;
+               assert(!sbuf->head.prev && !sbuf->head.next);
+               LIST_ADDTAIL(&sbuf->head, &svga->dirty_buffers);
+            }
+         }
+         else if (ret == PIPE_ERROR_OUT_OF_MEMORY) {
+            /*
+             * The buffer is too big to fit in the GMR aperture, so break it in
+             * smaller pieces.
+             */
+            ret = svga_buffer_upload_piecewise(ss, svga, sbuf);
+         }
+
+         if (ret != PIPE_OK) {
+            /*
+             * Something unexpected happened above. There is very little that
+             * we can do other than proceeding while ignoring the dirty ranges.
+             */
+            assert(0);
+            sbuf->map.num_ranges = 0;
+         }
+      }
+      else {
+         /*
+          * There a pending dma already. Make sure it is from this context.
+          */
+         assert(sbuf->dma.svga == svga);
+      }
    }
 
+   assert(!sbuf->map.num_ranges || sbuf->dma.pending);
+
    return sbuf->handle;
 }
 
+
 struct pipe_buffer *
 svga_screen_buffer_wrap_surface(struct pipe_screen *screen,
                                enum SVGA3dSurfaceFormat format,