BLI_memblock: Refactor for faster iteration and allocation
[blender.git] / source / blender / draw / intern / draw_instance_data.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * Copyright 2016, Blender Foundation.
17  */
18
19 /** \file
20  * \ingroup draw
21  */
22
23 /**
24  * DRW Instance Data Manager
25  * This is a special memory manager that keeps memory blocks ready to send as vbo data in one
26  * continuous allocation. This way we avoid feeding gawain each instance data one by one and
27  * unnecessary memcpy. Since we loose which memory block was used each #DRWShadingGroup we need to
28  * redistribute them in the same order/size to avoid to realloc each frame. This is why
29  * #DRWInstanceDatas are sorted in a list for each different data size.
30  */
31
32 #include "draw_instance_data.h"
33 #include "DRW_engine.h"
34 #include "DRW_render.h" /* For DRW_shgroup_get_instance_count() */
35
36 #include "MEM_guardedalloc.h"
37 #include "BLI_utildefines.h"
38 #include "BLI_mempool.h"
39 #include "BLI_memblock.h"
40
41 #include "intern/gpu_primitive_private.h"
42
43 struct DRWInstanceData {
44   struct DRWInstanceData *next;
45   bool used;        /* If this data is used or not. */
46   size_t data_size; /* Size of one instance data. */
47   BLI_mempool *mempool;
48 };
49
50 struct DRWInstanceDataList {
51   struct DRWInstanceDataList *next, *prev;
52   /* Linked lists for all possible data pool size */
53   DRWInstanceData *idata_head[MAX_INSTANCE_DATA_SIZE];
54   DRWInstanceData *idata_tail[MAX_INSTANCE_DATA_SIZE];
55
56   BLI_memblock *pool_instancing;
57   BLI_memblock *pool_batching;
58   BLI_memblock *pool_buffers;
59 };
60
61 typedef struct DRWTempBufferHandle {
62   /** Must be first for casting. */
63   GPUVertBuf buf;
64   /** Format pointer for reuse. */
65   GPUVertFormat *format;
66   /** Touched vertex length for resize. */
67   uint *vert_len;
68 } DRWTempBufferHandle;
69
70 static ListBase g_idatalists = {NULL, NULL};
71
72 /* -------------------------------------------------------------------- */
73 /** \name Instance Buffer Management
74  * \{ */
75
76 static void instance_batch_free(GPUBatch *geom, void *UNUSED(user_data))
77 {
78   if (geom->verts[0] == NULL) {
79     /** XXX This is a false positive case.
80      * The batch has been requested but not init yet
81      * and there is a chance that it might become init.
82      */
83     return;
84   }
85
86   /* Free all batches that use the same vbos before they are reused. */
87   /* TODO: Make it thread safe! Batch freeing can happen from another thread. */
88   /* FIXME: This is not really correct. The correct way would be to check based on
89    * the vertex buffers. We assume the batch containing the VBO is being when it should. */
90   /* PERF: This is doing a linear search. This can be very costly. */
91   LISTBASE_FOREACH (DRWInstanceDataList *, data_list, &g_idatalists) {
92     BLI_memblock *memblock = data_list->pool_instancing;
93     BLI_memblock_iter iter;
94     BLI_memblock_iternew(memblock, &iter);
95     GPUBatch *batch;
96     while ((batch = (GPUBatch *)BLI_memblock_iterstep(&iter))) {
97       /* Only check verts[0] that's enough. */
98       if (batch->verts[0] == geom->verts[0]) {
99         GPU_batch_clear(batch);
100       }
101     }
102   }
103 }
104
105 /**
106  * This manager allows to distribute existing batches for instancing
107  * attributes. This reduce the number of batches creation.
108  * Querying a batch is done with a vertex format. This format should
109  * be static so that it's pointer never changes (because we are using
110  * this pointer as identifier [we don't want to check the full format
111  * that would be too slow]).
112  */
113 GPUVertBuf *DRW_temp_buffer_request(DRWInstanceDataList *idatalist,
114                                     GPUVertFormat *format,
115                                     uint *vert_len)
116 {
117   BLI_assert(format != NULL);
118   BLI_assert(vert_len != NULL);
119
120   DRWTempBufferHandle *handle = BLI_memblock_alloc(idatalist->pool_buffers);
121   GPUVertBuf *vert = &handle->buf;
122   handle->vert_len = vert_len;
123
124   if (handle->format != format) {
125     handle->format = format;
126     /* TODO/PERF: Save the allocated data from freeing to avoid reallocation. */
127     GPU_vertbuf_clear(vert);
128     GPU_vertbuf_init_with_format_ex(vert, format, GPU_USAGE_DYNAMIC);
129     GPU_vertbuf_data_alloc(vert, DRW_BUFFER_VERTS_CHUNK);
130   }
131   return vert;
132 }
133
134 /* NOTE: Does not return a valid drawable batch until DRW_instance_buffer_finish has run. */
135 GPUBatch *DRW_temp_batch_instance_request(DRWInstanceDataList *idatalist,
136                                           GPUVertBuf *buf,
137                                           GPUBatch *geom)
138 {
139   /* Do not call this with a batch that is already an instancing batch. */
140   BLI_assert(geom->inst == NULL);
141
142   GPUBatch *batch = BLI_memblock_alloc(idatalist->pool_instancing);
143   bool is_compatible = (batch->gl_prim_type == geom->gl_prim_type) && (batch->inst == buf) &&
144                        (batch->phase == GPU_BATCH_READY_TO_DRAW);
145   for (int i = 0; i < GPU_BATCH_VBO_MAX_LEN && is_compatible; i++) {
146     if (batch->verts[i] != geom->verts[i]) {
147       is_compatible = false;
148     }
149   }
150
151   if (!is_compatible) {
152     GPU_batch_clear(batch);
153     /* Save args and init later */
154     batch->inst = buf;
155     batch->phase = GPU_BATCH_READY_TO_BUILD;
156     batch->verts[0] = (void *)geom; /* HACK to save the pointer without other alloc. */
157
158     /* Make sure to free this batch if the instance geom gets free. */
159     GPU_batch_callback_free_set(geom, &instance_batch_free, NULL);
160   }
161   return batch;
162 }
163
164 /* NOTE: Use only with buf allocated via DRW_temp_buffer_request. */
165 GPUBatch *DRW_temp_batch_request(DRWInstanceDataList *idatalist,
166                                  GPUVertBuf *buf,
167                                  GPUPrimType prim_type)
168 {
169   GPUBatch *batch = BLI_memblock_alloc(idatalist->pool_batching);
170   bool is_compatible = (batch->verts[0] == buf) &&
171                        (batch->gl_prim_type == convert_prim_type_to_gl(prim_type));
172   if (!is_compatible) {
173     GPU_batch_clear(batch);
174     GPU_batch_init(batch, prim_type, buf, NULL);
175   }
176   return batch;
177 }
178
179 static void temp_buffer_handle_free(DRWTempBufferHandle *handle)
180 {
181   handle->format = NULL;
182   GPU_vertbuf_clear(&handle->buf);
183 }
184
185 void DRW_instance_buffer_finish(DRWInstanceDataList *idatalist)
186 {
187   /* Resize down buffers in use and send data to GPU. */
188   BLI_memblock_iter iter;
189   DRWTempBufferHandle *handle;
190   BLI_memblock_iternew(idatalist->pool_buffers, &iter);
191   while ((handle = BLI_memblock_iterstep(&iter))) {
192     if (handle->vert_len != NULL) {
193       uint vert_len = *(handle->vert_len);
194       uint target_buf_size = ((vert_len / DRW_BUFFER_VERTS_CHUNK) + 1) * DRW_BUFFER_VERTS_CHUNK;
195       if (target_buf_size < handle->buf.vertex_alloc) {
196         GPU_vertbuf_data_resize(&handle->buf, target_buf_size);
197       }
198       GPU_vertbuf_data_len_set(&handle->buf, vert_len);
199       GPU_vertbuf_use(&handle->buf); /* Send data. */
200     }
201   }
202   /* Finish pending instancing batches. */
203   GPUBatch *batch;
204   BLI_memblock_iternew(idatalist->pool_instancing, &iter);
205   while ((batch = BLI_memblock_iterstep(&iter))) {
206     if (batch->phase == GPU_BATCH_READY_TO_BUILD) {
207       GPUVertBuf *inst = batch->inst;
208       GPUBatch *geom = (void *)batch->verts[0]; /* HACK see DRW_temp_batch_instance_request. */
209       GPU_batch_copy(batch, geom);
210       GPU_batch_instbuf_set(batch, inst, false);
211     }
212   }
213   /* Resize pools and free unused. */
214   BLI_memblock_clear(idatalist->pool_buffers, (MemblockValFreeFP)temp_buffer_handle_free);
215   BLI_memblock_clear(idatalist->pool_instancing, (MemblockValFreeFP)GPU_batch_clear);
216   BLI_memblock_clear(idatalist->pool_batching, (MemblockValFreeFP)GPU_batch_clear);
217 }
218
219 /** \} */
220
221 /* -------------------------------------------------------------------- */
222 /** \name Instance Data (DRWInstanceData)
223  * \{ */
224
225 static DRWInstanceData *drw_instance_data_create(DRWInstanceDataList *idatalist, uint attr_size)
226 {
227   DRWInstanceData *idata = MEM_callocN(sizeof(DRWInstanceData), "DRWInstanceData");
228   idata->next = NULL;
229   idata->used = true;
230   idata->data_size = attr_size;
231   idata->mempool = BLI_mempool_create(sizeof(float) * idata->data_size, 0, 16, 0);
232
233   BLI_assert(attr_size > 0);
234
235   /* Push to linked list. */
236   if (idatalist->idata_head[attr_size - 1] == NULL) {
237     idatalist->idata_head[attr_size - 1] = idata;
238   }
239   else {
240     idatalist->idata_tail[attr_size - 1]->next = idata;
241   }
242   idatalist->idata_tail[attr_size - 1] = idata;
243
244   return idata;
245 }
246
247 static void DRW_instance_data_free(DRWInstanceData *idata)
248 {
249   BLI_mempool_destroy(idata->mempool);
250 }
251
252 /**
253  * Return a pointer to the next instance data space.
254  */
255 void *DRW_instance_data_next(DRWInstanceData *idata)
256 {
257   return BLI_mempool_alloc(idata->mempool);
258 }
259
260 DRWInstanceData *DRW_instance_data_request(DRWInstanceDataList *idatalist, uint attr_size)
261 {
262   BLI_assert(attr_size > 0 && attr_size <= MAX_INSTANCE_DATA_SIZE);
263
264   DRWInstanceData *idata = idatalist->idata_head[attr_size - 1];
265
266   /* Search for an unused data chunk. */
267   for (; idata; idata = idata->next) {
268     if (idata->used == false) {
269       idata->used = true;
270       return idata;
271     }
272   }
273
274   return drw_instance_data_create(idatalist, attr_size);
275 }
276
277 /** \} */
278
279 /* -------------------------------------------------------------------- */
280 /** \name Instance Data List (DRWInstanceDataList)
281  * \{ */
282
283 DRWInstanceDataList *DRW_instance_data_list_create(void)
284 {
285   DRWInstanceDataList *idatalist = MEM_callocN(sizeof(DRWInstanceDataList), "DRWInstanceDataList");
286
287   idatalist->pool_batching = BLI_memblock_create(sizeof(GPUBatch));
288   idatalist->pool_instancing = BLI_memblock_create(sizeof(GPUBatch));
289   idatalist->pool_buffers = BLI_memblock_create(sizeof(DRWTempBufferHandle));
290
291   BLI_addtail(&g_idatalists, idatalist);
292
293   return idatalist;
294 }
295
296 void DRW_instance_data_list_free(DRWInstanceDataList *idatalist)
297 {
298   DRWInstanceData *idata, *next_idata;
299
300   for (int i = 0; i < MAX_INSTANCE_DATA_SIZE; ++i) {
301     for (idata = idatalist->idata_head[i]; idata; idata = next_idata) {
302       next_idata = idata->next;
303       DRW_instance_data_free(idata);
304       MEM_freeN(idata);
305     }
306     idatalist->idata_head[i] = NULL;
307     idatalist->idata_tail[i] = NULL;
308   }
309
310   BLI_memblock_destroy(idatalist->pool_buffers, (MemblockValFreeFP)temp_buffer_handle_free);
311   BLI_memblock_destroy(idatalist->pool_instancing, (MemblockValFreeFP)GPU_batch_clear);
312   BLI_memblock_destroy(idatalist->pool_batching, (MemblockValFreeFP)GPU_batch_clear);
313
314   BLI_remlink(&g_idatalists, idatalist);
315 }
316
317 void DRW_instance_data_list_reset(DRWInstanceDataList *idatalist)
318 {
319   DRWInstanceData *idata;
320
321   for (int i = 0; i < MAX_INSTANCE_DATA_SIZE; ++i) {
322     for (idata = idatalist->idata_head[i]; idata; idata = idata->next) {
323       idata->used = false;
324     }
325   }
326 }
327
328 void DRW_instance_data_list_free_unused(DRWInstanceDataList *idatalist)
329 {
330   DRWInstanceData *idata, *next_idata;
331
332   /* Remove unused data blocks and sanitize each list. */
333   for (int i = 0; i < MAX_INSTANCE_DATA_SIZE; ++i) {
334     idatalist->idata_tail[i] = NULL;
335     for (idata = idatalist->idata_head[i]; idata; idata = next_idata) {
336       next_idata = idata->next;
337       if (idata->used == false) {
338         if (idatalist->idata_head[i] == idata) {
339           idatalist->idata_head[i] = next_idata;
340         }
341         else {
342           /* idatalist->idata_tail[i] is guaranteed not to be null in this case. */
343           idatalist->idata_tail[i]->next = next_idata;
344         }
345         DRW_instance_data_free(idata);
346         MEM_freeN(idata);
347       }
348       else {
349         if (idatalist->idata_tail[i] != NULL) {
350           idatalist->idata_tail[i]->next = idata;
351         }
352         idatalist->idata_tail[i] = idata;
353       }
354     }
355   }
356 }
357
358 void DRW_instance_data_list_resize(DRWInstanceDataList *idatalist)
359 {
360   DRWInstanceData *idata;
361
362   for (int i = 0; i < MAX_INSTANCE_DATA_SIZE; ++i) {
363     for (idata = idatalist->idata_head[i]; idata; idata = idata->next) {
364       BLI_mempool_clear_ex(idata->mempool, BLI_mempool_len(idata->mempool));
365     }
366   }
367 }
368
369 /** \} */