ee73a2ba2c63ba42e1fe2fd12603f61f9be18d1e
[blender.git] / source / blender / draw / intern / draw_instance_data.c
1 /*
2  * Copyright 2016, Blender Foundation.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): Blender Institute
19  *
20  */
21
22 /** \file blender/draw/intern/draw_instance_data.c
23  *  \ingroup draw
24  */
25
26 /**
27  * DRW Instance Data Manager
28  * This is a special memory manager that keeps memory blocks ready to send as vbo data in one continuous allocation.
29  * This way we avoid feeding gawain each instance data one by one and unecessary memcpy.
30  * Since we loose which memory block was used each DRWShadingGroup we need to redistribute them in the same order/size
31  * to avoid to realloc each frame.
32  * This is why DRWInstanceDatas are sorted in a list for each different data size.
33  **/
34
35 #include "draw_instance_data.h"
36 #include "DRW_engine.h"
37 #include "DRW_render.h" /* For DRW_shgroup_get_instance_count() */
38
39 #include "MEM_guardedalloc.h"
40 #include "BLI_utildefines.h"
41
42 #define BUFFER_CHUNK_SIZE 32
43 #define BUFFER_VERTS_CHUNK 32
44
45 typedef struct DRWBatchingBuffer {
46         struct DRWShadingGroup *shgroup;  /* Link back to the owning shGroup. Also tells if it's used */
47         Gwn_VertFormat *format;           /* Identifier. */
48         Gwn_VertBuf *vert;                /* Gwn_VertBuf contained in the Gwn_Batch. */
49         Gwn_Batch *batch;                 /* Gwn_Batch containing the Gwn_VertBuf. */
50 } DRWBatchingBuffer;
51
52 typedef struct DRWInstancingBuffer {
53         struct DRWShadingGroup *shgroup;  /* Link back to the owning shGroup. Also tells if it's used */
54         Gwn_VertFormat *format;           /* Identifier. */
55         Gwn_Batch *instance;              /* Identifier. */
56         Gwn_VertBuf *vert;                /* Gwn_VertBuf contained in the Gwn_Batch. */
57         Gwn_Batch *batch;                 /* Gwn_Batch containing the Gwn_VertBuf. */
58 } DRWInstancingBuffer;
59
60 typedef struct DRWInstanceChunk {
61         size_t cursor;             /* Offset to the next instance data. */
62         size_t alloc_size;         /* Number of DRWBatchingBuffer/Batches alloc'd in ibufs/btchs. */
63         union {
64                 DRWBatchingBuffer *bbufs;
65                 DRWInstancingBuffer *ibufs;
66         };
67 } DRWInstanceChunk;
68
69 struct DRWInstanceData {
70         struct DRWInstanceData *next;
71         bool used;                 /* If this data is used or not. */
72         size_t chunk_size;         /* Current size of the whole chunk. */
73         size_t data_size;          /* Size of one instance data. */
74         size_t instance_group;     /* How many instance to allocate at a time. */
75         size_t offset;             /* Offset to the next instance data. */
76         float *memchunk;           /* Should be float no matter what. */
77 };
78
79 struct DRWInstanceDataList {
80         struct DRWInstanceDataList *next, *prev;
81         /* Linked lists for all possible data pool size */
82         /* Not entirely sure if we should separate them in the first place.
83          * This is done to minimize the reattribution misses. */
84         DRWInstanceData *idata_head[MAX_INSTANCE_DATA_SIZE];
85         DRWInstanceData *idata_tail[MAX_INSTANCE_DATA_SIZE];
86
87         DRWInstanceChunk instancing;
88         DRWInstanceChunk batching;
89 };
90
91 static ListBase g_idatalists = {NULL, NULL};
92
93 /* -------------------------------------------------------------------- */
94
95 /** \name Instance Buffer Management
96  * \{ */
97
98 /**
99  * This manager allows to distribute existing batches for instancing
100  * attributes. This reduce the number of batches creation.
101  * Querying a batch is done with a vertex format. This format should
102  * be static so that it's pointer never changes (because we are using
103  * this pointer as identifier [we don't want to check the full format
104  * that would be too slow]).
105  **/
106
107 static void instance_batch_free(Gwn_Batch *batch, void *UNUSED(user_data))
108 {
109         /* Free all batches that have the same key before they are reused. */
110         /* TODO: Make it thread safe! Batch freeing can happen from another thread. */
111         /* XXX we need to iterate over all idatalists unless we make some smart
112          * data structure to store the locations to update. */
113         for (DRWInstanceDataList *idatalist = g_idatalists.first; idatalist; idatalist = idatalist->next) {
114                 DRWInstancingBuffer *ibuf = idatalist->instancing.ibufs;
115                 for (int i = 0; i < idatalist->instancing.alloc_size; i++, ibuf++) {
116                         if (ibuf->instance == batch) {
117                                 BLI_assert(ibuf->shgroup == NULL); /* Make sure it has no other users. */
118                                 GWN_VERTBUF_DISCARD_SAFE(ibuf->vert);
119                                 GWN_BATCH_DISCARD_SAFE(ibuf->batch);
120                                 /* Tag as non alloced. */
121                                 ibuf->format = NULL;
122                         }
123                 }
124         }
125 }
126
127 void DRW_batching_buffer_request(
128         DRWInstanceDataList *idatalist, Gwn_VertFormat *format, Gwn_PrimType type, struct DRWShadingGroup *shgroup,
129         Gwn_Batch **r_batch, Gwn_VertBuf **r_vert)
130 {
131         DRWInstanceChunk *chunk = &idatalist->batching;
132         DRWBatchingBuffer *bbuf = idatalist->batching.bbufs;
133         BLI_assert(format);
134         /* Search for an unused batch. */
135         for (int i = 0; i < idatalist->batching.alloc_size; i++, bbuf++) {
136                 if (bbuf->shgroup == NULL) {
137                         if (bbuf->format == format) {
138                                 bbuf->shgroup = shgroup;
139                                 *r_batch = bbuf->batch;
140                                 *r_vert = bbuf->vert;
141                                 return;
142                         }
143                 }
144         }
145         int new_id = 0; /* Find insertion point. */
146         for (; new_id < chunk->alloc_size; ++new_id) {
147                 if (chunk->bbufs[new_id].format == NULL)
148                         break;
149         }
150         /* If there is no batch left. Allocate more. */
151         if (new_id == chunk->alloc_size) {
152                 new_id = chunk->alloc_size;
153                 chunk->alloc_size += BUFFER_CHUNK_SIZE;
154                 chunk->bbufs = MEM_reallocN(chunk->bbufs, chunk->alloc_size * sizeof(DRWBatchingBuffer));
155                 memset(chunk->bbufs + new_id, 0, sizeof(DRWBatchingBuffer) * BUFFER_CHUNK_SIZE);
156         }
157         /* Create the batch. */
158         bbuf = chunk->bbufs + new_id;
159         bbuf->vert = *r_vert = GWN_vertbuf_create_with_format_ex(format, GWN_USAGE_DYNAMIC);
160         bbuf->batch = *r_batch = GWN_batch_create_ex(type, bbuf->vert, NULL, 0);
161         bbuf->format = format;
162         bbuf->shgroup = shgroup;
163         GWN_vertbuf_data_alloc(*r_vert, BUFFER_VERTS_CHUNK);
164 }
165
166 void DRW_instancing_buffer_request(
167         DRWInstanceDataList *idatalist, Gwn_VertFormat *format, Gwn_Batch *instance, struct DRWShadingGroup *shgroup,
168         Gwn_Batch **r_batch, Gwn_VertBuf **r_vert)
169 {
170         DRWInstanceChunk *chunk = &idatalist->instancing;
171         DRWInstancingBuffer *ibuf = idatalist->instancing.ibufs;
172         BLI_assert(format);
173         /* Search for an unused batch. */
174         for (int i = 0; i < idatalist->instancing.alloc_size; i++, ibuf++) {
175                 if (ibuf->shgroup == NULL) {
176                         if (ibuf->format == format) {
177                                 if (ibuf->instance == instance) {
178                                         ibuf->shgroup = shgroup;
179                                         *r_batch = ibuf->batch;
180                                         *r_vert = ibuf->vert;
181                                         return;
182                                 }
183                         }
184                 }
185         }
186         int new_id = 0; /* Find insertion point. */
187         for (; new_id < chunk->alloc_size; ++new_id) {
188                 if (chunk->ibufs[new_id].format == NULL)
189                         break;
190         }
191         /* If there is no batch left. Allocate more. */
192         if (new_id == chunk->alloc_size) {
193                 new_id = chunk->alloc_size;
194                 chunk->alloc_size += BUFFER_CHUNK_SIZE;
195                 chunk->ibufs = MEM_reallocN(chunk->ibufs, chunk->alloc_size * sizeof(DRWInstancingBuffer));
196                 memset(chunk->ibufs + new_id, 0, sizeof(DRWInstancingBuffer) * BUFFER_CHUNK_SIZE);
197         }
198         /* Create the batch. */
199         ibuf = chunk->ibufs + new_id;
200         ibuf->vert = *r_vert = GWN_vertbuf_create_with_format_ex(format, GWN_USAGE_DYNAMIC);
201         ibuf->batch = *r_batch = GWN_batch_duplicate(instance);
202         ibuf->format = format;
203         ibuf->shgroup = shgroup;
204         ibuf->instance = instance;
205         GWN_vertbuf_data_alloc(*r_vert, BUFFER_VERTS_CHUNK);
206         GWN_batch_instbuf_set(ibuf->batch, ibuf->vert, false);
207         /* Make sure to free this ibuf if the instance batch gets free. */
208         GWN_batch_callback_free_set(instance, &instance_batch_free, NULL);
209 }
210
211 void DRW_instance_buffer_finish(DRWInstanceDataList *idatalist)
212 {
213         size_t realloc_size = 1; /* Avoid 0 size realloc. */
214         /* Resize down buffers in use and send data to GPU & free unused buffers. */
215         DRWInstanceChunk *batching = &idatalist->batching;
216         DRWBatchingBuffer *bbuf = batching->bbufs;
217         for (int i = 0; i < batching->alloc_size; i++, bbuf++) {
218                 if (bbuf->shgroup != NULL) {
219                         realloc_size = i + 1;
220                         unsigned int vert_ct = DRW_shgroup_get_instance_count(bbuf->shgroup);
221                         vert_ct += (vert_ct == 0) ? 1 : 0; /* Do not realloc to 0 size buffer */
222                         if (vert_ct + BUFFER_VERTS_CHUNK <= bbuf->vert->vertex_ct) {
223                                 unsigned int size = vert_ct + BUFFER_VERTS_CHUNK - 1;
224                                 size = size - size % BUFFER_VERTS_CHUNK;
225                                 GWN_vertbuf_data_resize(bbuf->vert, size);
226                         }
227                         GWN_vertbuf_use(bbuf->vert); /* Send data. */
228                         bbuf->shgroup = NULL; /* Set as non used for the next round. */
229                 }
230                 else {
231                         GWN_VERTBUF_DISCARD_SAFE(bbuf->vert);
232                         GWN_BATCH_DISCARD_SAFE(bbuf->batch);
233                         bbuf->format = NULL; /* Tag as non alloced. */
234                 }
235         }
236         /* Rounding up to nearest chunk size. */
237         realloc_size += BUFFER_CHUNK_SIZE - 1;
238         realloc_size -= realloc_size % BUFFER_CHUNK_SIZE;
239         /* Resize down if necessary. */
240         if (realloc_size < batching->alloc_size) {
241                 batching->alloc_size = realloc_size;
242                 batching->ibufs = MEM_reallocN(batching->ibufs, realloc_size * sizeof(DRWBatchingBuffer));
243         }
244
245         realloc_size = 1;
246         /* Resize down buffers in use and send data to GPU & free unused buffers. */
247         DRWInstanceChunk *instancing = &idatalist->instancing;
248         DRWInstancingBuffer *ibuf = instancing->ibufs;
249         for (int i = 0; i < instancing->alloc_size; i++, ibuf++) {
250                 if (ibuf->shgroup != NULL) {
251                         realloc_size = i + 1;
252                         unsigned int vert_ct = DRW_shgroup_get_instance_count(ibuf->shgroup);
253                         vert_ct += (vert_ct == 0) ? 1 : 0; /* Do not realloc to 0 size buffer */
254                         if (vert_ct + BUFFER_VERTS_CHUNK <= ibuf->vert->vertex_ct) {
255                                 unsigned int size = vert_ct + BUFFER_VERTS_CHUNK - 1;
256                                 size = size - size % BUFFER_VERTS_CHUNK;
257                                 GWN_vertbuf_data_resize(ibuf->vert, size);
258                         }
259                         GWN_vertbuf_use(ibuf->vert); /* Send data. */
260                         ibuf->shgroup = NULL; /* Set as non used for the next round. */
261                 }
262                 else {
263                         GWN_VERTBUF_DISCARD_SAFE(ibuf->vert);
264                         GWN_BATCH_DISCARD_SAFE(ibuf->batch);
265                         ibuf->format = NULL; /* Tag as non alloced. */
266                 }
267         }
268         /* Rounding up to nearest chunk size. */
269         realloc_size += BUFFER_CHUNK_SIZE - 1;
270         realloc_size -= realloc_size % BUFFER_CHUNK_SIZE;
271         /* Resize down if necessary. */
272         if (realloc_size < instancing->alloc_size) {
273                 instancing->alloc_size = realloc_size;
274                 instancing->ibufs = MEM_reallocN(instancing->ibufs, realloc_size * sizeof(DRWInstancingBuffer));
275         }
276 }
277
278 /** \} */
279
280 /* -------------------------------------------------------------------- */
281
282 /** \name Instance Data (DRWInstanceData)
283  * \{ */
284
285 static DRWInstanceData *drw_instance_data_create(
286         DRWInstanceDataList *idatalist, unsigned int attrib_size, unsigned int instance_group)
287 {
288         DRWInstanceData *idata = MEM_callocN(sizeof(DRWInstanceData), "DRWInstanceData");
289         idata->next = NULL;
290         idata->used = true;
291         idata->data_size = attrib_size;
292         idata->instance_group = instance_group;
293         idata->chunk_size = idata->data_size * instance_group;
294         idata->offset = 0;
295         idata->memchunk = MEM_mallocN(idata->chunk_size * sizeof(float), "DRWInstanceData memchunk");
296
297         BLI_assert(attrib_size > 0);
298
299         /* Push to linked list. */
300         if (idatalist->idata_head[attrib_size-1] == NULL) {
301                 idatalist->idata_head[attrib_size-1] = idata;
302         }
303         else {
304                 idatalist->idata_tail[attrib_size-1]->next = idata;
305         }
306         idatalist->idata_tail[attrib_size-1] = idata;
307
308         return idata;
309 }
310
311 static void DRW_instance_data_free(DRWInstanceData *idata)
312 {
313         MEM_freeN(idata->memchunk);
314 }
315
316 /**
317  * Return a pointer to the next instance data space.
318  * DO NOT SAVE/REUSE THIS POINTER after the next call
319  * to this function since the chunk may have been
320  * reallocated.
321  **/
322 void *DRW_instance_data_next(DRWInstanceData *idata)
323 {
324         idata->offset += idata->data_size;
325
326         /* Check if chunk is large enough. realloc otherwise. */
327         if (idata->offset > idata->chunk_size) {
328                 idata->chunk_size += idata->data_size * idata->instance_group;
329                 idata->memchunk = MEM_reallocN(idata->memchunk, idata->chunk_size * sizeof(float));
330         }
331
332         return idata->memchunk + (idata->offset - idata->data_size);
333 }
334
335 void *DRW_instance_data_get(DRWInstanceData *idata)
336 {
337         return (void *)idata->memchunk;
338 }
339
340 DRWInstanceData *DRW_instance_data_request(
341         DRWInstanceDataList *idatalist, unsigned int attrib_size, unsigned int instance_group)
342 {
343         BLI_assert(attrib_size > 0 && attrib_size <= MAX_INSTANCE_DATA_SIZE);
344
345         DRWInstanceData *idata = idatalist->idata_head[attrib_size - 1];
346
347         /* Search for an unused data chunk. */
348         for (; idata; idata = idata->next) {
349                 if (idata->used == false) {
350                         idata->used = true;
351                         return idata;
352                 }
353         }
354
355         return drw_instance_data_create(idatalist, attrib_size, instance_group);
356 }
357
358 /** \} */
359
360 /* -------------------------------------------------------------------- */
361
362 /** \name Instance Data List (DRWInstanceDataList)
363  * \{ */
364
365 DRWInstanceDataList *DRW_instance_data_list_create(void)
366 {
367         DRWInstanceDataList *idatalist = MEM_callocN(sizeof(DRWInstanceDataList), "DRWInstanceDataList");
368         idatalist->batching.bbufs = MEM_callocN(sizeof(DRWBatchingBuffer) * BUFFER_CHUNK_SIZE, "DRWBatchingBuffers");
369         idatalist->batching.alloc_size = BUFFER_CHUNK_SIZE;
370         idatalist->instancing.ibufs = MEM_callocN(sizeof(DRWInstancingBuffer) * BUFFER_CHUNK_SIZE, "DRWInstancingBuffers");
371         idatalist->instancing.alloc_size = BUFFER_CHUNK_SIZE;
372
373         BLI_addtail(&g_idatalists, idatalist);
374
375         return idatalist;
376 }
377
378 void DRW_instance_data_list_free(DRWInstanceDataList *idatalist)
379 {
380         DRWInstanceData *idata, *next_idata;
381
382         for (int i = 0; i < MAX_INSTANCE_DATA_SIZE; ++i) {
383                 for (idata = idatalist->idata_head[i]; idata; idata = next_idata) {
384                         next_idata = idata->next;
385                         DRW_instance_data_free(idata);
386                         MEM_freeN(idata);
387                 }
388                 idatalist->idata_head[i] = NULL;
389                 idatalist->idata_tail[i] = NULL;
390         }
391
392         DRWBatchingBuffer *bbuf = idatalist->batching.bbufs;
393         for (int i = 0; i < idatalist->batching.alloc_size; i++, bbuf++) {
394                 GWN_VERTBUF_DISCARD_SAFE(bbuf->vert);
395                 GWN_BATCH_DISCARD_SAFE(bbuf->batch);
396         }
397         MEM_freeN(idatalist->batching.bbufs);
398
399         DRWInstancingBuffer *ibuf = idatalist->instancing.ibufs;
400         for (int i = 0; i < idatalist->instancing.alloc_size; i++, ibuf++) {
401                 GWN_VERTBUF_DISCARD_SAFE(ibuf->vert);
402                 GWN_BATCH_DISCARD_SAFE(ibuf->batch);
403         }
404         MEM_freeN(idatalist->instancing.ibufs);
405
406         BLI_remlink(&g_idatalists, idatalist);
407 }
408
409 void DRW_instance_data_list_reset(DRWInstanceDataList *idatalist)
410 {
411         DRWInstanceData *idata;
412
413         for (int i = 0; i < MAX_INSTANCE_DATA_SIZE; ++i) {
414                 for (idata = idatalist->idata_head[i]; idata; idata = idata->next) {
415                         idata->used = false;
416                         idata->offset = 0;
417                 }
418         }
419 }
420
421 void DRW_instance_data_list_free_unused(DRWInstanceDataList *idatalist)
422 {
423         DRWInstanceData *idata, *next_idata;
424
425         /* Remove unused data blocks and sanitize each list. */
426         for (int i = 0; i < MAX_INSTANCE_DATA_SIZE; ++i) {
427                 idatalist->idata_tail[i] = NULL;
428                 for (idata = idatalist->idata_head[i]; idata; idata = next_idata) {
429                         next_idata = idata->next;
430                         if (idata->used == false) {
431                                 if (idatalist->idata_head[i] == idata) {
432                                         idatalist->idata_head[i] = next_idata;
433                                 }
434                                 else {
435                                         /* idatalist->idata_tail[i] is garanteed not to be null in this case. */
436                                         idatalist->idata_tail[i]->next = next_idata;
437                                 }
438                                 DRW_instance_data_free(idata);
439                                 MEM_freeN(idata);
440                         }
441                         else {
442                                 if (idatalist->idata_tail[i] != NULL) {
443                                         idatalist->idata_tail[i]->next = idata;
444                                 }
445                                 idatalist->idata_tail[i] = idata;
446                         }
447                 }
448         }
449 }
450
451 void DRW_instance_data_list_resize(DRWInstanceDataList *idatalist)
452 {
453         DRWInstanceData *idata;
454
455         for (int i = 0; i < MAX_INSTANCE_DATA_SIZE; ++i) {
456                 for (idata = idatalist->idata_head[i]; idata; idata = idata->next) {
457                         /* Rounding up to nearest chunk size to compare. */
458                         size_t fac = idata->data_size * idata->instance_group;
459                         size_t tmp = idata->offset + fac - 1;
460                         size_t rounded_offset = tmp - tmp % fac;
461                         if (rounded_offset < idata->chunk_size) {
462                                 idata->chunk_size = rounded_offset;
463                                 idata->memchunk = MEM_reallocN(idata->memchunk, idata->chunk_size * sizeof(float));
464                         }
465                 }
466         }
467 }
468
469 /** \} */