Cleanup: remove redundant doxygen \file argument
[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 \ingroup draw
20  */
21
22 /**
23  * DRW Instance Data Manager
24  * This is a special memory manager that keeps memory blocks ready to send as vbo data in one continuous allocation.
25  * This way we avoid feeding gawain each instance data one by one and unnecessary memcpy.
26  * Since we loose which memory block was used each DRWShadingGroup we need to redistribute them in the same order/size
27  * to avoid to realloc each frame.
28  * This is why DRWInstanceDatas are sorted in a list for each different data size.
29  **/
30
31 #include "draw_instance_data.h"
32 #include "DRW_engine.h"
33 #include "DRW_render.h" /* For DRW_shgroup_get_instance_count() */
34
35 #include "MEM_guardedalloc.h"
36 #include "BLI_utildefines.h"
37 #include "BLI_mempool.h"
38
39 #define BUFFER_CHUNK_SIZE 32
40 #define BUFFER_VERTS_CHUNK 32
41
42 typedef struct DRWBatchingBuffer {
43         struct DRWShadingGroup *shgroup;  /* Link back to the owning shGroup. Also tells if it's used */
44         GPUVertFormat *format;           /* Identifier. */
45         GPUVertBuf *vert;                /* GPUVertBuf contained in the GPUBatch. */
46         GPUBatch *batch;                 /* GPUBatch containing the GPUVertBuf. */
47 } DRWBatchingBuffer;
48
49 typedef struct DRWInstancingBuffer {
50         struct DRWShadingGroup *shgroup;  /* Link back to the owning shGroup. Also tells if it's used */
51         GPUVertFormat *format;           /* Identifier. */
52         GPUBatch *instance;              /* Identifier. */
53         GPUVertBuf *vert;                /* GPUVertBuf contained in the GPUBatch. */
54         GPUBatch *batch;                 /* GPUBatch containing the GPUVertBuf. */
55 } DRWInstancingBuffer;
56
57 typedef struct DRWInstanceChunk {
58         size_t cursor;             /* Offset to the next instance data. */
59         size_t alloc_size;         /* Number of DRWBatchingBuffer/Batches alloc'd in ibufs/btchs. */
60         union {
61                 DRWBatchingBuffer *bbufs;
62                 DRWInstancingBuffer *ibufs;
63         };
64 } DRWInstanceChunk;
65
66 struct DRWInstanceData {
67         struct DRWInstanceData *next;
68         bool used;                 /* If this data is used or not. */
69         size_t data_size;          /* Size of one instance data. */
70         BLI_mempool *mempool;
71 };
72
73 struct DRWInstanceDataList {
74         struct DRWInstanceDataList *next, *prev;
75         /* Linked lists for all possible data pool size */
76         DRWInstanceData *idata_head[MAX_INSTANCE_DATA_SIZE];
77         DRWInstanceData *idata_tail[MAX_INSTANCE_DATA_SIZE];
78
79         DRWInstanceChunk instancing;
80         DRWInstanceChunk batching;
81 };
82
83 static ListBase g_idatalists = {NULL, NULL};
84
85 /* -------------------------------------------------------------------- */
86 /** \name Instance Buffer Management
87  * \{ */
88
89 /**
90  * This manager allows to distribute existing batches for instancing
91  * attributes. This reduce the number of batches creation.
92  * Querying a batch is done with a vertex format. This format should
93  * be static so that it's pointer never changes (because we are using
94  * this pointer as identifier [we don't want to check the full format
95  * that would be too slow]).
96  **/
97
98 static void instance_batch_free(GPUBatch *batch, void *UNUSED(user_data))
99 {
100         if (batch->verts[0] == NULL) {
101                 /** XXX This is a false positive case.
102                  * The batch has been requested but not init yet
103                  * and there is a chance that it might become init.
104                  **/
105                 return;
106         }
107         /* Free all batches that have the same key before they are reused. */
108         /* TODO: Make it thread safe! Batch freeing can happen from another thread. */
109         /* XXX we need to iterate over all idatalists unless we make some smart
110          * data structure to store the locations to update. */
111         for (DRWInstanceDataList *idatalist = g_idatalists.first; idatalist; idatalist = idatalist->next) {
112                 DRWInstancingBuffer *ibuf = idatalist->instancing.ibufs;
113                 for (int i = 0; i < idatalist->instancing.alloc_size; i++, ibuf++) {
114                         if (ibuf->instance == batch) {
115                                 BLI_assert(ibuf->shgroup == NULL); /* Make sure it has no other users. */
116                                 GPU_VERTBUF_DISCARD_SAFE(ibuf->vert);
117                                 GPU_BATCH_DISCARD_SAFE(ibuf->batch);
118                                 /* Tag as non alloced. */
119                                 ibuf->format = NULL;
120                         }
121                 }
122         }
123 }
124
125 void DRW_batching_buffer_request(
126         DRWInstanceDataList *idatalist, GPUVertFormat *format, GPUPrimType type, struct DRWShadingGroup *shgroup,
127         GPUBatch **r_batch, GPUVertBuf **r_vert)
128 {
129         DRWInstanceChunk *chunk = &idatalist->batching;
130         DRWBatchingBuffer *bbuf = idatalist->batching.bbufs;
131         BLI_assert(format);
132         /* Search for an unused batch. */
133         for (int i = 0; i < idatalist->batching.alloc_size; i++, bbuf++) {
134                 if (bbuf->shgroup == NULL) {
135                         if (bbuf->format == format) {
136                                 bbuf->shgroup = shgroup;
137                                 *r_batch = bbuf->batch;
138                                 *r_vert = bbuf->vert;
139                                 return;
140                         }
141                 }
142         }
143         int new_id = 0; /* Find insertion point. */
144         for (; new_id < chunk->alloc_size; ++new_id) {
145                 if (chunk->bbufs[new_id].format == NULL) {
146                         break;
147                 }
148         }
149         /* If there is no batch left. Allocate more. */
150         if (new_id == chunk->alloc_size) {
151                 new_id = chunk->alloc_size;
152                 chunk->alloc_size += BUFFER_CHUNK_SIZE;
153                 chunk->bbufs = MEM_reallocN(chunk->bbufs, chunk->alloc_size * sizeof(DRWBatchingBuffer));
154                 memset(chunk->bbufs + new_id, 0, sizeof(DRWBatchingBuffer) * BUFFER_CHUNK_SIZE);
155         }
156         /* Create the batch. */
157         bbuf = chunk->bbufs + new_id;
158         bbuf->vert = *r_vert = GPU_vertbuf_create_with_format_ex(format, GPU_USAGE_DYNAMIC);
159         bbuf->batch = *r_batch = GPU_batch_create_ex(type, bbuf->vert, NULL, 0);
160         bbuf->format = format;
161         bbuf->shgroup = shgroup;
162         GPU_vertbuf_data_alloc(*r_vert, BUFFER_VERTS_CHUNK);
163 }
164
165 void DRW_instancing_buffer_request(
166         DRWInstanceDataList *idatalist, GPUVertFormat *format, GPUBatch *instance, struct DRWShadingGroup *shgroup,
167         GPUBatch **r_batch, GPUVertBuf **r_vert)
168 {
169         DRWInstanceChunk *chunk = &idatalist->instancing;
170         DRWInstancingBuffer *ibuf = idatalist->instancing.ibufs;
171         BLI_assert(format);
172         /* Search for an unused batch. */
173         for (int i = 0; i < idatalist->instancing.alloc_size; i++, ibuf++) {
174                 if (ibuf->shgroup == NULL) {
175                         if (ibuf->format == format) {
176                                 if (ibuf->instance == instance) {
177                                         ibuf->shgroup = shgroup;
178                                         *r_batch = ibuf->batch;
179                                         *r_vert = ibuf->vert;
180                                         return;
181                                 }
182                         }
183                 }
184         }
185         int new_id = 0; /* Find insertion point. */
186         for (; new_id < chunk->alloc_size; ++new_id) {
187                 if (chunk->ibufs[new_id].format == NULL) {
188                         break;
189                 }
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 = GPU_vertbuf_create_with_format_ex(format, GPU_USAGE_DYNAMIC);
201         ibuf->batch = *r_batch = MEM_callocN(sizeof(GPUBatch), "GPUBatch");
202         ibuf->format = format;
203         ibuf->shgroup = shgroup;
204         ibuf->instance = instance;
205         GPU_vertbuf_data_alloc(*r_vert, BUFFER_VERTS_CHUNK);
206         /* Make sure to free this ibuf if the instance batch gets free. */
207         GPU_batch_callback_free_set(instance, &instance_batch_free, NULL);
208 }
209
210 void DRW_instance_buffer_finish(DRWInstanceDataList *idatalist)
211 {
212         size_t realloc_size = 1; /* Avoid 0 size realloc. */
213         /* Resize down buffers in use and send data to GPU & free unused buffers. */
214         DRWInstanceChunk *batching = &idatalist->batching;
215         DRWBatchingBuffer *bbuf = batching->bbufs;
216         for (int i = 0; i < batching->alloc_size; i++, bbuf++) {
217                 if (bbuf->shgroup != NULL) {
218                         realloc_size = i + 1;
219                         uint vert_len = DRW_shgroup_get_instance_count(bbuf->shgroup);
220                         vert_len += (vert_len == 0) ? 1 : 0; /* Do not realloc to 0 size buffer */
221                         if (vert_len + BUFFER_VERTS_CHUNK <= bbuf->vert->vertex_len) {
222                                 uint size = vert_len + BUFFER_VERTS_CHUNK - 1;
223                                 size = size - size % BUFFER_VERTS_CHUNK;
224                                 GPU_vertbuf_data_resize(bbuf->vert, size);
225                         }
226                         GPU_vertbuf_use(bbuf->vert); /* Send data. */
227                         bbuf->shgroup = NULL; /* Set as non used for the next round. */
228                 }
229                 else {
230                         GPU_VERTBUF_DISCARD_SAFE(bbuf->vert);
231                         GPU_BATCH_DISCARD_SAFE(bbuf->batch);
232                         bbuf->format = NULL; /* Tag as non alloced. */
233                 }
234         }
235         /* Rounding up to nearest chunk size. */
236         realloc_size += BUFFER_CHUNK_SIZE - 1;
237         realloc_size -= realloc_size % BUFFER_CHUNK_SIZE;
238         /* Resize down if necessary. */
239         if (realloc_size < batching->alloc_size) {
240                 batching->alloc_size = realloc_size;
241                 batching->ibufs = MEM_reallocN(batching->ibufs, realloc_size * sizeof(DRWBatchingBuffer));
242         }
243
244         realloc_size = 1;
245         /* Resize down buffers in use and send data to GPU & free unused buffers. */
246         DRWInstanceChunk *instancing = &idatalist->instancing;
247         DRWInstancingBuffer *ibuf = instancing->ibufs;
248         for (int i = 0; i < instancing->alloc_size; i++, ibuf++) {
249                 if (ibuf->shgroup != NULL) {
250                         realloc_size = i + 1;
251                         uint vert_len = DRW_shgroup_get_instance_count(ibuf->shgroup);
252                         vert_len += (vert_len == 0) ? 1 : 0; /* Do not realloc to 0 size buffer */
253                         if (vert_len + BUFFER_VERTS_CHUNK <= ibuf->vert->vertex_len) {
254                                 uint size = vert_len + BUFFER_VERTS_CHUNK - 1;
255                                 size = size - size % BUFFER_VERTS_CHUNK;
256                                 GPU_vertbuf_data_resize(ibuf->vert, size);
257                         }
258                         GPU_vertbuf_use(ibuf->vert); /* Send data. */
259                         /* Setup batch now that we are sure ibuf->instance is setup. */
260                         GPU_batch_copy(ibuf->batch, ibuf->instance);
261                         GPU_batch_instbuf_set(ibuf->batch, ibuf->vert, false);
262                         ibuf->shgroup = NULL; /* Set as non used for the next round. */
263                 }
264                 else {
265                         GPU_VERTBUF_DISCARD_SAFE(ibuf->vert);
266                         GPU_BATCH_DISCARD_SAFE(ibuf->batch);
267                         ibuf->format = NULL; /* Tag as non alloced. */
268                 }
269         }
270         /* Rounding up to nearest chunk size. */
271         realloc_size += BUFFER_CHUNK_SIZE - 1;
272         realloc_size -= realloc_size % BUFFER_CHUNK_SIZE;
273         /* Resize down if necessary. */
274         if (realloc_size < instancing->alloc_size) {
275                 instancing->alloc_size = realloc_size;
276                 instancing->ibufs = MEM_reallocN(instancing->ibufs, realloc_size * sizeof(DRWInstancingBuffer));
277         }
278 }
279
280 /** \} */
281
282 /* -------------------------------------------------------------------- */
283 /** \name Instance Data (DRWInstanceData)
284  * \{ */
285
286 static DRWInstanceData *drw_instance_data_create(DRWInstanceDataList *idatalist, uint attr_size)
287 {
288         DRWInstanceData *idata = MEM_callocN(sizeof(DRWInstanceData), "DRWInstanceData");
289         idata->next = NULL;
290         idata->used = true;
291         idata->data_size = attr_size;
292         idata->mempool = BLI_mempool_create(sizeof(float) * idata->data_size, 0, 16, 0);
293
294         BLI_assert(attr_size > 0);
295
296         /* Push to linked list. */
297         if (idatalist->idata_head[attr_size - 1] == NULL) {
298                 idatalist->idata_head[attr_size - 1] = idata;
299         }
300         else {
301                 idatalist->idata_tail[attr_size - 1]->next = idata;
302         }
303         idatalist->idata_tail[attr_size - 1] = idata;
304
305         return idata;
306 }
307
308 static void DRW_instance_data_free(DRWInstanceData *idata)
309 {
310         BLI_mempool_destroy(idata->mempool);
311 }
312
313 /**
314  * Return a pointer to the next instance data space.
315  **/
316 void *DRW_instance_data_next(DRWInstanceData *idata)
317 {
318         return BLI_mempool_alloc(idata->mempool);
319 }
320
321 DRWInstanceData *DRW_instance_data_request(DRWInstanceDataList *idatalist, uint attr_size)
322 {
323         BLI_assert(attr_size > 0 && attr_size <= MAX_INSTANCE_DATA_SIZE);
324
325         DRWInstanceData *idata = idatalist->idata_head[attr_size - 1];
326
327         /* Search for an unused data chunk. */
328         for (; idata; idata = idata->next) {
329                 if (idata->used == false) {
330                         idata->used = true;
331                         return idata;
332                 }
333         }
334
335         return drw_instance_data_create(idatalist, attr_size);
336 }
337
338 /** \} */
339
340 /* -------------------------------------------------------------------- */
341 /** \name Instance Data List (DRWInstanceDataList)
342  * \{ */
343
344 DRWInstanceDataList *DRW_instance_data_list_create(void)
345 {
346         DRWInstanceDataList *idatalist = MEM_callocN(sizeof(DRWInstanceDataList), "DRWInstanceDataList");
347         idatalist->batching.bbufs = MEM_callocN(sizeof(DRWBatchingBuffer) * BUFFER_CHUNK_SIZE, "DRWBatchingBuffers");
348         idatalist->batching.alloc_size = BUFFER_CHUNK_SIZE;
349         idatalist->instancing.ibufs = MEM_callocN(sizeof(DRWInstancingBuffer) * BUFFER_CHUNK_SIZE, "DRWInstancingBuffers");
350         idatalist->instancing.alloc_size = BUFFER_CHUNK_SIZE;
351
352         BLI_addtail(&g_idatalists, idatalist);
353
354         return idatalist;
355 }
356
357 void DRW_instance_data_list_free(DRWInstanceDataList *idatalist)
358 {
359         DRWInstanceData *idata, *next_idata;
360
361         for (int i = 0; i < MAX_INSTANCE_DATA_SIZE; ++i) {
362                 for (idata = idatalist->idata_head[i]; idata; idata = next_idata) {
363                         next_idata = idata->next;
364                         DRW_instance_data_free(idata);
365                         MEM_freeN(idata);
366                 }
367                 idatalist->idata_head[i] = NULL;
368                 idatalist->idata_tail[i] = NULL;
369         }
370
371         DRWBatchingBuffer *bbuf = idatalist->batching.bbufs;
372         for (int i = 0; i < idatalist->batching.alloc_size; i++, bbuf++) {
373                 GPU_VERTBUF_DISCARD_SAFE(bbuf->vert);
374                 GPU_BATCH_DISCARD_SAFE(bbuf->batch);
375         }
376         MEM_freeN(idatalist->batching.bbufs);
377
378         DRWInstancingBuffer *ibuf = idatalist->instancing.ibufs;
379         for (int i = 0; i < idatalist->instancing.alloc_size; i++, ibuf++) {
380                 GPU_VERTBUF_DISCARD_SAFE(ibuf->vert);
381                 GPU_BATCH_DISCARD_SAFE(ibuf->batch);
382         }
383         MEM_freeN(idatalist->instancing.ibufs);
384
385         BLI_remlink(&g_idatalists, idatalist);
386 }
387
388 void DRW_instance_data_list_reset(DRWInstanceDataList *idatalist)
389 {
390         DRWInstanceData *idata;
391
392         for (int i = 0; i < MAX_INSTANCE_DATA_SIZE; ++i) {
393                 for (idata = idatalist->idata_head[i]; idata; idata = idata->next) {
394                         idata->used = false;
395                 }
396         }
397 }
398
399 void DRW_instance_data_list_free_unused(DRWInstanceDataList *idatalist)
400 {
401         DRWInstanceData *idata, *next_idata;
402
403         /* Remove unused data blocks and sanitize each list. */
404         for (int i = 0; i < MAX_INSTANCE_DATA_SIZE; ++i) {
405                 idatalist->idata_tail[i] = NULL;
406                 for (idata = idatalist->idata_head[i]; idata; idata = next_idata) {
407                         next_idata = idata->next;
408                         if (idata->used == false) {
409                                 if (idatalist->idata_head[i] == idata) {
410                                         idatalist->idata_head[i] = next_idata;
411                                 }
412                                 else {
413                                         /* idatalist->idata_tail[i] is guaranteed not to be null in this case. */
414                                         idatalist->idata_tail[i]->next = next_idata;
415                                 }
416                                 DRW_instance_data_free(idata);
417                                 MEM_freeN(idata);
418                         }
419                         else {
420                                 if (idatalist->idata_tail[i] != NULL) {
421                                         idatalist->idata_tail[i]->next = idata;
422                                 }
423                                 idatalist->idata_tail[i] = idata;
424                         }
425                 }
426         }
427 }
428
429 void DRW_instance_data_list_resize(DRWInstanceDataList *idatalist)
430 {
431         DRWInstanceData *idata;
432
433         for (int i = 0; i < MAX_INSTANCE_DATA_SIZE; ++i) {
434                 for (idata = idatalist->idata_head[i]; idata; idata = idata->next) {
435                         BLI_mempool_clear_ex(idata->mempool, BLI_mempool_len(idata->mempool));
436                 }
437         }
438 }
439
440 /** \} */