From 629a87481769d8886fcbe66ca4e4e4b60c767627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Wed, 14 Feb 2018 18:16:52 +0100 Subject: DRW: Add instance buffer manager. 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]). This might make the original Instance Data manager useless but it's currently used by DRW_object_engine_data_ensure(). --- source/blender/draw/intern/draw_instance_data.c | 131 +++++++++++++++++++++++- 1 file changed, 130 insertions(+), 1 deletion(-) (limited to 'source/blender/draw/intern/draw_instance_data.c') 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) -- cgit v1.2.3