diff options
Diffstat (limited to 'source/blender/draw/intern/draw_instance_data.c')
-rw-r--r-- | source/blender/draw/intern/draw_instance_data.c | 153 |
1 files changed, 80 insertions, 73 deletions
diff --git a/source/blender/draw/intern/draw_instance_data.c b/source/blender/draw/intern/draw_instance_data.c index 5005f38c558..9d0727555b6 100644 --- a/source/blender/draw/intern/draw_instance_data.c +++ b/source/blender/draw/intern/draw_instance_data.c @@ -38,8 +38,6 @@ #include "BLI_utildefines.h" #include "MEM_guardedalloc.h" -#include "intern/gpu_primitive_private.h" - struct DRWInstanceData { struct DRWInstanceData *next; bool used; /* If this data is used or not. */ @@ -59,50 +57,50 @@ struct DRWInstanceDataList { }; typedef struct DRWTempBufferHandle { - /** Must be first for casting. */ - GPUVertBuf buf; + GPUVertBuf *buf; /** Format pointer for reuse. */ GPUVertFormat *format; /** Touched vertex length for resize. */ int *vert_len; } DRWTempBufferHandle; -static ListBase g_idatalists = {NULL, NULL}; +typedef struct DRWTempInstancingHandle { + /** Copy of geom but with the per-instance attributes. */ + GPUBatch *batch; + /** Batch containing instancing attributes. */ + GPUBatch *instancer; + /** Callbuffer to be used instead of instancer . */ + GPUVertBuf *buf; + /** Original non-instanced batch pointer. */ + GPUBatch *geom; +} DRWTempInstancingHandle; -/* -------------------------------------------------------------------- */ -/** \name Instance Buffer Management - * \{ */ +static ListBase g_idatalists = {NULL, NULL}; -static void instance_batch_free(GPUBatch *geom, void *UNUSED(user_data)) +static void instancing_batch_references_add(GPUBatch *batch) { - if (geom->verts[0] == NULL) { - /** XXX This is a false positive case. - * The batch has been requested but not init yet - * and there is a chance that it might become init. - */ - return; + for (int i = 0; i < GPU_BATCH_VBO_MAX_LEN && batch->verts[i]; i++) { + GPU_vertbuf_handle_ref_add(batch->verts[i]); + } + for (int i = 0; i < GPU_BATCH_INST_VBO_MAX_LEN && batch->inst[i]; i++) { + GPU_vertbuf_handle_ref_add(batch->inst[i]); } +} - /* Free all batches that use the same vbos before they are reused. */ - /* TODO: Make it thread safe! Batch freeing can happen from another thread. */ - /* FIXME: This is not really correct. The correct way would be to check based on - * the vertex buffers. We assume the batch containing the VBO is being when it should. */ - /* PERF: This is doing a linear search. This can be very costly. */ - LISTBASE_FOREACH (DRWInstanceDataList *, data_list, &g_idatalists) { - BLI_memblock *memblock = data_list->pool_instancing; - BLI_memblock_iter iter; - BLI_memblock_iternew(memblock, &iter); - GPUBatch **batch_ptr; - while ((batch_ptr = (GPUBatch **)BLI_memblock_iterstep(&iter))) { - GPUBatch *batch = *batch_ptr; - /* Only check verts[0] that's enough. */ - if (batch->verts[0] == geom->verts[0]) { - GPU_batch_clear(batch); - } - } +static void instancing_batch_references_remove(GPUBatch *batch) +{ + for (int i = 0; i < GPU_BATCH_VBO_MAX_LEN && batch->verts[i]; i++) { + GPU_vertbuf_handle_ref_remove(batch->verts[i]); + } + for (int i = 0; i < GPU_BATCH_INST_VBO_MAX_LEN && batch->inst[i]; i++) { + GPU_vertbuf_handle_ref_remove(batch->inst[i]); } } +/* -------------------------------------------------------------------- */ +/** \name Instance Buffer Management + * \{ */ + /** * This manager allows to distribute existing batches for instancing * attributes. This reduce the number of batches creation. @@ -119,20 +117,23 @@ GPUVertBuf *DRW_temp_buffer_request(DRWInstanceDataList *idatalist, BLI_assert(vert_len != NULL); DRWTempBufferHandle *handle = BLI_memblock_alloc(idatalist->pool_buffers); - GPUVertBuf *vert = &handle->buf; - handle->vert_len = vert_len; if (handle->format != format) { handle->format = format; - /* TODO/PERF: Save the allocated data from freeing to avoid reallocation. */ - GPU_vertbuf_clear(vert); + GPU_VERTBUF_DISCARD_SAFE(handle->buf); + + GPUVertBuf *vert = GPU_vertbuf_create(GPU_USAGE_DYNAMIC); GPU_vertbuf_init_with_format_ex(vert, format, GPU_USAGE_DYNAMIC); GPU_vertbuf_data_alloc(vert, DRW_BUFFER_VERTS_CHUNK); + + handle->buf = vert; } - return vert; + handle->vert_len = vert_len; + return handle->buf; } -/* NOTE: Does not return a valid drawable batch until DRW_instance_buffer_finish has run. */ +/* NOTE: Does not return a valid drawable batch until DRW_instance_buffer_finish has run. + * Initialization is delayed because instancer or geom could still not be initialized. */ GPUBatch *DRW_temp_batch_instance_request(DRWInstanceDataList *idatalist, GPUVertBuf *buf, GPUBatch *instancer, @@ -143,17 +144,17 @@ GPUBatch *DRW_temp_batch_instance_request(DRWInstanceDataList *idatalist, /* Only call with one of them. */ BLI_assert((instancer != NULL) != (buf != NULL)); - GPUBatch **batch_ptr = BLI_memblock_alloc(idatalist->pool_instancing); - if (*batch_ptr == NULL) { - *batch_ptr = GPU_batch_calloc(1); + DRWTempInstancingHandle *handle = BLI_memblock_alloc(idatalist->pool_instancing); + if (handle->batch == NULL) { + handle->batch = GPU_batch_calloc(); } - GPUBatch *batch = *batch_ptr; + GPUBatch *batch = handle->batch; bool instancer_compat = buf ? ((batch->inst[0] == buf) && (buf->vbo_id != 0)) : - ((batch->inst[0] == instancer->inst[0]) && - (batch->inst[1] == instancer->inst[1])); - bool is_compatible = (batch->gl_prim_type == geom->gl_prim_type) && instancer_compat && - (batch->phase == GPU_BATCH_READY_TO_DRAW) && (batch->elem == geom->elem); + ((batch->inst[0] == instancer->verts[0]) && + (batch->inst[1] == instancer->verts[1])); + bool is_compatible = (batch->prim_type == geom->prim_type) && instancer_compat && + (batch->flag & GPU_BATCH_BUILDING) == 0 && (batch->elem == geom->elem); for (int i = 0; i < GPU_BATCH_VBO_MAX_LEN && is_compatible; i++) { if (batch->verts[i] != geom->verts[i]) { is_compatible = false; @@ -161,15 +162,13 @@ GPUBatch *DRW_temp_batch_instance_request(DRWInstanceDataList *idatalist, } if (!is_compatible) { + instancing_batch_references_remove(batch); GPU_batch_clear(batch); - /* Save args and init later */ - batch->inst[0] = buf; - batch->inst[1] = (void *)instancer; /* HACK to save the pointer without other alloc. */ - batch->phase = GPU_BATCH_READY_TO_BUILD; - batch->verts[0] = (void *)geom; /* HACK to save the pointer without other alloc. */ - - /* Make sure to free this batch if the instance geom gets free. */ - GPU_batch_callback_free_set(geom, &instance_batch_free, NULL); + /* Save args and init later. */ + batch->flag = GPU_BATCH_BUILDING; + handle->buf = buf; + handle->instancer = instancer; + handle->geom = geom; } return batch; } @@ -179,14 +178,14 @@ GPUBatch *DRW_temp_batch_request(DRWInstanceDataList *idatalist, GPUVertBuf *buf, GPUPrimType prim_type) { - GPUBatch **batch_ptr = BLI_memblock_alloc(idatalist->pool_instancing); + GPUBatch **batch_ptr = BLI_memblock_alloc(idatalist->pool_batching); if (*batch_ptr == NULL) { - *batch_ptr = GPU_batch_calloc(1); + *batch_ptr = GPU_batch_calloc(); } GPUBatch *batch = *batch_ptr; bool is_compatible = (batch->verts[0] == buf) && (buf->vbo_id != 0) && - (batch->gl_prim_type == convert_prim_type_to_gl(prim_type)); + (batch->prim_type == prim_type); if (!is_compatible) { GPU_batch_clear(batch); GPU_batch_init(batch, prim_type, buf, NULL); @@ -197,7 +196,13 @@ GPUBatch *DRW_temp_batch_request(DRWInstanceDataList *idatalist, static void temp_buffer_handle_free(DRWTempBufferHandle *handle) { handle->format = NULL; - GPU_vertbuf_clear(&handle->buf); + GPU_VERTBUF_DISCARD_SAFE(handle->buf); +} + +static void temp_instancing_handle_free(DRWTempInstancingHandle *handle) +{ + instancing_batch_references_remove(handle->batch); + GPU_BATCH_DISCARD_SAFE(handle->batch); } static void temp_batch_free(GPUBatch **batch) @@ -215,23 +220,22 @@ void DRW_instance_buffer_finish(DRWInstanceDataList *idatalist) if (handle->vert_len != NULL) { uint vert_len = *(handle->vert_len); uint target_buf_size = ((vert_len / DRW_BUFFER_VERTS_CHUNK) + 1) * DRW_BUFFER_VERTS_CHUNK; - if (target_buf_size < handle->buf.vertex_alloc) { - GPU_vertbuf_data_resize(&handle->buf, target_buf_size); + if (target_buf_size < handle->buf->vertex_alloc) { + GPU_vertbuf_data_resize(handle->buf, target_buf_size); } - GPU_vertbuf_data_len_set(&handle->buf, vert_len); - GPU_vertbuf_use(&handle->buf); /* Send data. */ + GPU_vertbuf_data_len_set(handle->buf, vert_len); + GPU_vertbuf_use(handle->buf); /* Send data. */ } } /* Finish pending instancing batches. */ - GPUBatch **batch_ptr; + DRWTempInstancingHandle *handle_inst; BLI_memblock_iternew(idatalist->pool_instancing, &iter); - while ((batch_ptr = BLI_memblock_iterstep(&iter))) { - GPUBatch *batch = *batch_ptr; - if (batch && batch->phase == GPU_BATCH_READY_TO_BUILD) { - GPUVertBuf *inst_buf = batch->inst[0]; - /* HACK see DRW_temp_batch_instance_request. */ - GPUBatch *inst_batch = (void *)batch->inst[1]; - GPUBatch *geom = (void *)batch->verts[0]; + while ((handle_inst = BLI_memblock_iterstep(&iter))) { + GPUBatch *batch = handle_inst->batch; + if (batch && batch->flag == GPU_BATCH_BUILDING) { + GPUVertBuf *inst_buf = handle_inst->buf; + GPUBatch *inst_batch = handle_inst->instancer; + GPUBatch *geom = handle_inst->geom; GPU_batch_copy(batch, geom); if (inst_batch != NULL) { for (int i = 0; i < GPU_BATCH_INST_VBO_MAX_LEN && inst_batch->verts[i]; i++) { @@ -241,11 +245,14 @@ void DRW_instance_buffer_finish(DRWInstanceDataList *idatalist) else { GPU_batch_instbuf_add_ex(batch, inst_buf, false); } + /* Add reference to avoid comparing pointers (in DRW_temp_batch_request) that could + * potentially be the same. This will delay the freeing of the GPUVertBuf itself. */ + instancing_batch_references_add(batch); } } /* Resize pools and free unused. */ BLI_memblock_clear(idatalist->pool_buffers, (MemblockValFreeFP)temp_buffer_handle_free); - BLI_memblock_clear(idatalist->pool_instancing, (MemblockValFreeFP)temp_batch_free); + BLI_memblock_clear(idatalist->pool_instancing, (MemblockValFreeFP)temp_instancing_handle_free); BLI_memblock_clear(idatalist->pool_batching, (MemblockValFreeFP)temp_batch_free); } @@ -318,7 +325,7 @@ DRWInstanceDataList *DRW_instance_data_list_create(void) DRWInstanceDataList *idatalist = MEM_callocN(sizeof(DRWInstanceDataList), "DRWInstanceDataList"); idatalist->pool_batching = BLI_memblock_create(sizeof(GPUBatch *)); - idatalist->pool_instancing = BLI_memblock_create(sizeof(GPUBatch *)); + idatalist->pool_instancing = BLI_memblock_create(sizeof(DRWTempInstancingHandle)); idatalist->pool_buffers = BLI_memblock_create(sizeof(DRWTempBufferHandle)); BLI_addtail(&g_idatalists, idatalist); @@ -341,7 +348,7 @@ void DRW_instance_data_list_free(DRWInstanceDataList *idatalist) } BLI_memblock_destroy(idatalist->pool_buffers, (MemblockValFreeFP)temp_buffer_handle_free); - BLI_memblock_destroy(idatalist->pool_instancing, (MemblockValFreeFP)temp_batch_free); + BLI_memblock_destroy(idatalist->pool_instancing, (MemblockValFreeFP)temp_instancing_handle_free); BLI_memblock_destroy(idatalist->pool_batching, (MemblockValFreeFP)temp_batch_free); BLI_remlink(&g_idatalists, idatalist); |