diff options
-rw-r--r-- | source/blender/draw/intern/DRW_render.h | 1 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_instance_data.c | 131 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_instance_data.h | 14 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_manager.c | 5 |
4 files changed, 150 insertions, 1 deletions
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 64a907bf385..ab09586861d 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -336,6 +336,7 @@ void DRW_shgroup_call_dynamic_add_array(DRWShadingGroup *shgroup, const void *at } while (0) /* Use this to set a high number of instances. */ void DRW_shgroup_set_instance_count(DRWShadingGroup *shgroup, int count); +unsigned int DRW_shgroup_get_instance_count(const DRWShadingGroup *shgroup); void DRW_shgroup_state_enable(DRWShadingGroup *shgroup, DRWState state); void DRW_shgroup_state_disable(DRWShadingGroup *shgroup, DRWState state); diff --git a/source/blender/draw/intern/draw_instance_data.c b/source/blender/draw/intern/draw_instance_data.c index 7f3bd246818..c2aae8e33ae 100644 --- a/source/blender/draw/intern/draw_instance_data.c +++ b/source/blender/draw/intern/draw_instance_data.c @@ -34,10 +34,21 @@ #include "draw_instance_data.h" #include "DRW_engine.h" +#include "DRW_render.h" /* For DRW_shgroup_get_instance_count() */ #include "MEM_guardedalloc.h" #include "BLI_utildefines.h" +#define BUFFER_CHUNK_SIZE 32 +#define BUFFER_VERTS_CHUNK 32 + +typedef struct DRWInstanceBuffer { + struct DRWShadingGroup *shgroup; /* Link back to the owning shGroup. Also tells if it's used */ + Gwn_VertFormat *format; /* Identifier. */ + Gwn_VertBuf *vert; /* Gwn_VertBuf contained in the Gwn_Batch. */ + Gwn_Batch *batch; /* Gwn_Batch containing the Gwn_VertBuf. */ +} DRWInstanceBuffer; + struct DRWInstanceData { struct DRWInstanceData *next; bool used; /* If this data is used or not. */ @@ -54,10 +65,118 @@ struct DRWInstanceDataList { * This is done to minimize the reattribution misses. */ DRWInstanceData *idata_head[MAX_INSTANCE_DATA_SIZE]; DRWInstanceData *idata_tail[MAX_INSTANCE_DATA_SIZE]; + + struct { + size_t cursor; /* Offset to the next instance data. */ + size_t alloc_size; /* Number of DRWInstanceBuffer alloc'd in ibufs. */ + DRWInstanceBuffer *ibufs; + } ibuffers; }; /* -------------------------------------------------------------------- */ +/** \name Instance Buffer Management + * \{ */ + +/** + * This manager allows to distribute existing batches for instancing + * attributes. This reduce the number of batches creation. + * Querying a batch is done with a vertex format. This format should + * be static so that it's pointer never changes (because we are using + * this pointer as identifier [we don't want to check the full format + * that would be too slow]). + **/ + +void DRW_instance_buffer_request( + DRWInstanceDataList *idatalist, Gwn_VertFormat *format, struct DRWShadingGroup *shgroup, + Gwn_Batch **r_batch, Gwn_VertBuf **r_vert, Gwn_PrimType type) +{ + BLI_assert(format); + + DRWInstanceBuffer *ibuf = idatalist->ibuffers.ibufs; + int first_non_alloced = -1; + + /* Search for an unused batch. */ + for (int i = 0; i < idatalist->ibuffers.alloc_size; i++, ibuf++) { + if (ibuf->shgroup == NULL) { + if (ibuf->format == format) { + ibuf->shgroup = shgroup; + *r_batch = ibuf->batch; + *r_vert = ibuf->vert; + return; + } + else if (ibuf->format == NULL && first_non_alloced == -1) { + first_non_alloced = i; + } + } + } + + if (first_non_alloced == -1) { + /* There is no batch left. Allocate more. */ + first_non_alloced = idatalist->ibuffers.alloc_size; + idatalist->ibuffers.alloc_size += BUFFER_CHUNK_SIZE; + idatalist->ibuffers.ibufs = MEM_reallocN(idatalist->ibuffers.ibufs, + idatalist->ibuffers.alloc_size * sizeof(DRWInstanceBuffer)); + /* Clear new part of the memory. */ + memset(idatalist->ibuffers.ibufs + first_non_alloced, 0, sizeof(DRWInstanceBuffer) * BUFFER_CHUNK_SIZE); + } + + /* Create the batch. */ + ibuf = idatalist->ibuffers.ibufs + first_non_alloced; + ibuf->vert = *r_vert = GWN_vertbuf_create_dynamic_with_format(format); + ibuf->batch = *r_batch = GWN_batch_create_ex(type, ibuf->vert, NULL, GWN_BATCH_OWNS_VBO); + ibuf->format = format; + ibuf->shgroup = shgroup; + + GWN_vertbuf_data_alloc(*r_vert, BUFFER_VERTS_CHUNK); +} + +void DRW_instance_buffer_finish(DRWInstanceDataList *idatalist) +{ + DRWInstanceBuffer *ibuf = idatalist->ibuffers.ibufs; + size_t minimum_alloc_size = 1; /* Avoid 0 size realloc. */ + + /* Resize down buffers in use and send data to GPU & free unused buffers. */ + for (int i = 0; i < idatalist->ibuffers.alloc_size; i++, ibuf++) { + if (ibuf->shgroup != NULL) { + minimum_alloc_size = i + 1; + unsigned int vert_ct = DRW_shgroup_get_instance_count(ibuf->shgroup); + /* Do not realloc to 0 size buffer */ + vert_ct += (vert_ct == 0) ? 1 : 0; + /* Resize buffer to reclame space. */ + if (vert_ct + BUFFER_VERTS_CHUNK <= ibuf->vert->vertex_ct) { + unsigned int size = vert_ct + BUFFER_VERTS_CHUNK - 1; + size = size - size % BUFFER_VERTS_CHUNK; + GWN_vertbuf_data_resize(ibuf->vert, size); + } + /* Send data. */ + GWN_vertbuf_use(ibuf->vert); + /* Set as non used for the next round. */ + ibuf->shgroup = NULL; + } + else { + GWN_BATCH_DISCARD_SAFE(ibuf->batch); + /* Tag as non alloced. */ + ibuf->format = NULL; + } + } + + /* Resize down the handle buffer (ibuffers). */ + /* Rounding up to nearest chunk size. */ + minimum_alloc_size += BUFFER_CHUNK_SIZE - 1; + minimum_alloc_size -= minimum_alloc_size % BUFFER_CHUNK_SIZE; + /* Resize down if necessary. */ + if (minimum_alloc_size < idatalist->ibuffers.alloc_size) { + idatalist->ibuffers.alloc_size = minimum_alloc_size; + idatalist->ibuffers.ibufs = MEM_reallocN(idatalist->ibuffers.ibufs, + minimum_alloc_size * sizeof(DRWInstanceBuffer)); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ + /** \name Instance Data (DRWInstanceData) * \{ */ @@ -143,11 +262,16 @@ DRWInstanceData *DRW_instance_data_request( DRWInstanceDataList *DRW_instance_data_list_create(void) { - return MEM_callocN(sizeof(DRWInstanceDataList), "DRWInstanceDataList"); + DRWInstanceDataList *idatalist = MEM_callocN(sizeof(DRWInstanceDataList), "DRWInstanceDataList"); + idatalist->ibuffers.ibufs = MEM_callocN(sizeof(DRWInstanceBuffer) * BUFFER_CHUNK_SIZE, "DRWInstanceBuffers"); + idatalist->ibuffers.alloc_size = BUFFER_CHUNK_SIZE; + + return idatalist; } void DRW_instance_data_list_free(DRWInstanceDataList *idatalist) { + DRWInstanceBuffer *ibuf = idatalist->ibuffers.ibufs; DRWInstanceData *idata, *next_idata; for (int i = 0; i < MAX_INSTANCE_DATA_SIZE; ++i) { @@ -159,6 +283,11 @@ void DRW_instance_data_list_free(DRWInstanceDataList *idatalist) idatalist->idata_head[i] = NULL; idatalist->idata_tail[i] = NULL; } + + for (int i = 0; i < idatalist->ibuffers.alloc_size; i++, ibuf++) { + GWN_BATCH_DISCARD_SAFE(ibuf->batch); + } + MEM_freeN(idatalist->ibuffers.ibufs); } void DRW_instance_data_list_reset(DRWInstanceDataList *idatalist) diff --git a/source/blender/draw/intern/draw_instance_data.h b/source/blender/draw/intern/draw_instance_data.h index c8376189396..a7a66c9baff 100644 --- a/source/blender/draw/intern/draw_instance_data.h +++ b/source/blender/draw/intern/draw_instance_data.h @@ -26,16 +26,30 @@ #ifndef __DRAW_INSTANCE_DATA_H__ #define __DRAW_INSTANCE_DATA_H__ +#include "BLI_compiler_attrs.h" +#include "BLI_sys_types.h" + +#include "GPU_batch.h" + #define MAX_INSTANCE_DATA_SIZE 42 /* Can be adjusted for more */ typedef struct DRWInstanceData DRWInstanceData; typedef struct DRWInstanceDataList DRWInstanceDataList; +struct DRWShadingGroup; + void *DRW_instance_data_next(DRWInstanceData *idata); void *DRW_instance_data_get(DRWInstanceData *idata); DRWInstanceData *DRW_instance_data_request( DRWInstanceDataList *idatalist, unsigned int attrib_size, unsigned int instance_group); +void DRW_instance_buffer_request( + DRWInstanceDataList *idatalist, Gwn_VertFormat *format, struct DRWShadingGroup *shgroup, + Gwn_Batch **r_batch, Gwn_VertBuf **r_vert, Gwn_PrimType type); + +/* Upload all instance data to the GPU as soon as possible. */ +void DRW_instance_buffer_finish(DRWInstanceDataList *idatalist); + void DRW_instance_data_list_reset(DRWInstanceDataList *idatalist); void DRW_instance_data_list_free_unused(DRWInstanceDataList *idatalist); void DRW_instance_data_list_resize(DRWInstanceDataList *idatalist); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 6feab32f8e1..96fdc132842 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -1074,6 +1074,11 @@ void DRW_shgroup_set_instance_count(DRWShadingGroup *shgroup, int count) interface->instance_count = count; } +unsigned int DRW_shgroup_get_instance_count(const DRWShadingGroup *shgroup) +{ + return shgroup->interface.instance_count; +} + /** * State is added to #Pass.state while drawing. * Use to temporarily enable draw options. |