Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2018-02-14 20:16:52 +0300
committerClément Foucault <foucault.clem@gmail.com>2018-02-14 20:59:42 +0300
commit629a87481769d8886fcbe66ca4e4e4b60c767627 (patch)
treee7265a43ce343da558d431ee4362d774ef222713 /source/blender/draw/intern/draw_instance_data.c
parentab7e7a005bde711d14493d51ee26ea297fa99e47 (diff)
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().
Diffstat (limited to 'source/blender/draw/intern/draw_instance_data.c')
-rw-r--r--source/blender/draw/intern/draw_instance_data.c131
1 files changed, 130 insertions, 1 deletions
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)