Commit: 629a87481769d8886fcbe66ca4e4e4b60c767627
Author: Clément Foucault
Date:   Wed Feb 14 18:16:52 2018 +0100
Branches: blender2.8
https://developer.blender.org/rB629a87481769d8886fcbe66ca4e4e4b60c767627

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().

===================================================================

M       source/blender/draw/intern/DRW_render.h
M       source/blender/draw/intern/draw_instance_data.c
M       source/blender/draw/intern/draw_instance_data.h
M       source/blender/draw/intern/draw_manager.c

===================================================================

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.

_______________________________________________
Bf-blender-cvs mailing list
Bf-blender-cvs@blender.org
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to