From 2452921e5adb56a3d99e52fb2b963fcd2a0b75e9 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=A9=20Fonseca?= Date: Thu, 18 Feb 2010 16:40:14 +0000 Subject: [PATCH] svga: Break uploads of big buffers into smaller pieces. Fixes crash in Homeworld2 which tries to create a 14MB buffer, because we now avoid creating GMR buffers larger than 8MB to ensure progress given we have only a 16MB pool. --- src/gallium/drivers/svga/svga_screen_buffer.c | 159 ++++++++++++++++++++++---- src/gallium/drivers/svga/svga_screen_buffer.h | 10 ++ 2 files changed, 147 insertions(+), 22 deletions(-) diff --git a/src/gallium/drivers/svga/svga_screen_buffer.c b/src/gallium/drivers/svga/svga_screen_buffer.c index 14fa40d..ed8c33d 100644 --- a/src/gallium/drivers/svga/svga_screen_buffer.c +++ b/src/gallium/drivers/svga/svga_screen_buffer.c @@ -178,7 +178,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 +227,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; - swc->commit(swc); + SVGA_FIFOCommitAll(swc); + + sbuf->dma.flags.discard = FALSE; return PIPE_OK; } @@ -276,7 +277,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); @@ -627,7 +627,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; @@ -636,7 +635,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); @@ -652,10 +652,86 @@ 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 = sbuf->base.alignment; + const unsigned usage = 0; + unsigned size = sbuf->base.size; + unsigned offset = 0; + + SVGA_DBG(DEBUG_DMA, "dma to sid %p\n", sbuf->handle); + + /* + * TODO: upload only the modified ranges + */ + + offset = 0; + while (offset < sbuf->base.size) { + struct svga_winsys_buffer *hwbuf; + uint8_t *map; + enum pipe_error ret; + + if (offset + size > sbuf->base.size) + size = sbuf->base.size - 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, 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, 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; } @@ -680,29 +756,68 @@ svga_buffer_handle(struct svga_context *svga, 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, diff --git a/src/gallium/drivers/svga/svga_screen_buffer.h b/src/gallium/drivers/svga/svga_screen_buffer.h index 044fa0a..937cf30 100644 --- a/src/gallium/drivers/svga/svga_screen_buffer.h +++ b/src/gallium/drivers/svga/svga_screen_buffer.h @@ -201,6 +201,16 @@ svga_buffer_is_user_buffer( struct pipe_buffer *buffer ) void svga_screen_init_buffer_functions(struct pipe_screen *screen); + +/** + * Get the host surface handle for this buffer. + * + * This will ensure the host surface is updated, issuing DMAs as needed. + * + * NOTE: This may insert new commands in the context, so it *must* be called + * before reserving command buffer space. And, in order to insert commands + * it may need to call svga_context_flush(). + */ struct svga_winsys_surface * svga_buffer_handle(struct svga_context *svga, struct pipe_buffer *buf); -- 2.7.4