From: Charmaine Lee Date: Wed, 16 Nov 2016 23:31:00 +0000 (-0800) Subject: svga: create buffer surfaces for incompatible bind flags X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=eea6223184e312bebbd751c892584e19301cf716;p=mesa.git svga: create buffer surfaces for incompatible bind flags 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 Reviewed-by: Jose Fonseca --- diff --git a/src/gallium/drivers/svga/svga_resource_buffer.c b/src/gallium/drivers/svga/svga_resource_buffer.c index 39aa530a672..e9d31de6166 100644 --- a/src/gallium/drivers/svga/svga_resource_buffer.c +++ b/src/gallium/drivers/svga/svga_resource_buffer.c @@ -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 diff --git a/src/gallium/drivers/svga/svga_resource_buffer.h b/src/gallium/drivers/svga/svga_resource_buffer.h index 2096a9821cc..db533416ec4 100644 --- a/src/gallium/drivers/svga/svga_resource_buffer.h +++ b/src/gallium/drivers/svga/svga_resource_buffer.h @@ -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. */ diff --git a/src/gallium/drivers/svga/svga_resource_buffer_upload.c b/src/gallium/drivers/svga/svga_resource_buffer_upload.c index d18d026bfa1..d467182f1ca 100644 --- a/src/gallium/drivers/svga/svga_resource_buffer_upload.c +++ b/src/gallium/drivers/svga/svga_resource_buffer_upload.c @@ -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; } diff --git a/src/gallium/drivers/svga/svga_resource_buffer_upload.h b/src/gallium/drivers/svga/svga_resource_buffer_upload.h index 877ae5499a1..c2d749b2091 100644 --- a/src/gallium/drivers/svga/svga_resource_buffer_upload.h +++ b/src/gallium/drivers/svga/svga_resource_buffer_upload.h @@ -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);