Cleanup: comments (long lines) in gpu
[blender.git] / source / blender / gpu / intern / gpu_viewport.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  * The Original Code is Copyright (C) 2006 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup gpu
22  *
23  * System that manages viewport drawing.
24  */
25
26 #include <string.h>
27
28 #include "BLI_listbase.h"
29 #include "BLI_rect.h"
30 #include "BLI_mempool.h"
31
32 #include "BIF_gl.h"
33
34 #include "DNA_vec_types.h"
35 #include "DNA_userdef_types.h"
36
37 #include "GPU_framebuffer.h"
38 #include "GPU_glew.h"
39 #include "GPU_immediate.h"
40 #include "GPU_texture.h"
41 #include "GPU_viewport.h"
42 #include "GPU_draw.h"
43
44 #include "DRW_engine.h"
45
46 #include "MEM_guardedalloc.h"
47
48 static const int default_fbl_len = (sizeof(DefaultFramebufferList)) / sizeof(void *);
49 static const int default_txl_len = (sizeof(DefaultTextureList)) / sizeof(void *);
50
51 /* Maximum number of simultaneous engine enabled at the same time.
52  * Setting it lower than the real number will do lead to
53  * higher VRAM usage due to sub-efficient buffer reuse. */
54 #define MAX_ENGINE_BUFFER_SHARING 5
55
56 typedef struct ViewportTempTexture {
57   struct ViewportTempTexture *next, *prev;
58   void *user[MAX_ENGINE_BUFFER_SHARING];
59   GPUTexture *texture;
60 } ViewportTempTexture;
61
62 struct GPUViewport {
63   int size[2];
64   int samples;
65   int flag;
66
67   ListBase data;  /* ViewportEngineData wrapped in LinkData */
68   uint data_hash; /* If hash mismatch we free all ViewportEngineData in this viewport */
69
70   DefaultFramebufferList *fbl;
71   DefaultTextureList *txl;
72
73   ViewportMemoryPool vmempool;           /* Used for rendering data structure. */
74   struct DRWInstanceDataList *idatalist; /* Used for rendering data structure. */
75
76   ListBase tex_pool; /* ViewportTempTexture list : Temporary textures shared across draw engines */
77
78   /* Profiling data */
79   double cache_time;
80 };
81
82 enum {
83   DO_UPDATE = (1 << 0),
84 };
85
86 static void gpu_viewport_buffers_free(FramebufferList *fbl,
87                                       int fbl_len,
88                                       TextureList *txl,
89                                       int txl_len);
90 static void gpu_viewport_storage_free(StorageList *stl, int stl_len);
91 static void gpu_viewport_passes_free(PassList *psl, int psl_len);
92 static void gpu_viewport_texture_pool_free(GPUViewport *viewport);
93 static void gpu_viewport_default_fb_create(GPUViewport *viewport);
94
95 void GPU_viewport_tag_update(GPUViewport *viewport)
96 {
97   viewport->flag |= DO_UPDATE;
98 }
99
100 bool GPU_viewport_do_update(GPUViewport *viewport)
101 {
102   bool ret = (viewport->flag & DO_UPDATE);
103   viewport->flag &= ~DO_UPDATE;
104   return ret;
105 }
106
107 GPUViewport *GPU_viewport_create(void)
108 {
109   GPUViewport *viewport = MEM_callocN(sizeof(GPUViewport), "GPUViewport");
110   viewport->fbl = MEM_callocN(sizeof(DefaultFramebufferList), "FramebufferList");
111   viewport->txl = MEM_callocN(sizeof(DefaultTextureList), "TextureList");
112   viewport->idatalist = DRW_instance_data_list_create();
113
114   viewport->size[0] = viewport->size[1] = -1;
115
116   return viewport;
117 }
118
119 GPUViewport *GPU_viewport_create_from_offscreen(struct GPUOffScreen *ofs)
120 {
121   GPUViewport *viewport = GPU_viewport_create();
122   GPUTexture *color, *depth;
123   GPUFrameBuffer *fb;
124   viewport->size[0] = GPU_offscreen_width(ofs);
125   viewport->size[1] = GPU_offscreen_height(ofs);
126
127   GPU_offscreen_viewport_data_get(ofs, &fb, &color, &depth);
128
129   if (GPU_texture_samples(color)) {
130     viewport->txl->multisample_color = color;
131     viewport->txl->multisample_depth = depth;
132     viewport->fbl->multisample_fb = fb;
133     gpu_viewport_default_fb_create(viewport);
134   }
135   else {
136     viewport->fbl->default_fb = fb;
137     viewport->txl->color = color;
138     viewport->txl->depth = depth;
139     GPU_framebuffer_ensure_config(
140         &viewport->fbl->color_only_fb,
141         {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(viewport->txl->color)});
142     GPU_framebuffer_ensure_config(
143         &viewport->fbl->depth_only_fb,
144         {GPU_ATTACHMENT_TEXTURE(viewport->txl->depth), GPU_ATTACHMENT_NONE});
145   }
146
147   return viewport;
148 }
149 /**
150  * Clear vars assigned from offscreen, so we don't free data owned by `GPUOffScreen`.
151  */
152 void GPU_viewport_clear_from_offscreen(GPUViewport *viewport)
153 {
154   DefaultFramebufferList *dfbl = viewport->fbl;
155   DefaultTextureList *dtxl = viewport->txl;
156
157   if (dfbl->multisample_fb) {
158     /* GPUViewport expect the final result to be in default_fb but
159      * GPUOffscreen wants it in its multisample_fb, so we sync it back. */
160     GPU_framebuffer_blit(
161         dfbl->default_fb, 0, dfbl->multisample_fb, 0, GPU_COLOR_BIT | GPU_DEPTH_BIT);
162     dfbl->multisample_fb = NULL;
163     dtxl->multisample_color = NULL;
164     dtxl->multisample_depth = NULL;
165   }
166   else {
167     viewport->fbl->default_fb = NULL;
168     dtxl->color = NULL;
169     dtxl->depth = NULL;
170   }
171 }
172
173 void *GPU_viewport_engine_data_create(GPUViewport *viewport, void *engine_type)
174 {
175   LinkData *ld = MEM_callocN(sizeof(LinkData), "LinkData");
176   ViewportEngineData *data = MEM_callocN(sizeof(ViewportEngineData), "ViewportEngineData");
177   int fbl_len, txl_len, psl_len, stl_len;
178
179   DRW_engine_viewport_data_size_get(engine_type, &fbl_len, &txl_len, &psl_len, &stl_len);
180
181   data->engine_type = engine_type;
182
183   data->fbl = MEM_callocN((sizeof(void *) * fbl_len) + sizeof(FramebufferList), "FramebufferList");
184   data->txl = MEM_callocN((sizeof(void *) * txl_len) + sizeof(TextureList), "TextureList");
185   data->psl = MEM_callocN((sizeof(void *) * psl_len) + sizeof(PassList), "PassList");
186   data->stl = MEM_callocN((sizeof(void *) * stl_len) + sizeof(StorageList), "StorageList");
187
188   ld->data = data;
189   BLI_addtail(&viewport->data, ld);
190
191   return data;
192 }
193
194 static void gpu_viewport_engines_data_free(GPUViewport *viewport)
195 {
196   int fbl_len, txl_len, psl_len, stl_len;
197
198   LinkData *next;
199   for (LinkData *link = viewport->data.first; link; link = next) {
200     next = link->next;
201     ViewportEngineData *data = link->data;
202     DRW_engine_viewport_data_size_get(data->engine_type, &fbl_len, &txl_len, &psl_len, &stl_len);
203
204     gpu_viewport_buffers_free(data->fbl, fbl_len, data->txl, txl_len);
205     gpu_viewport_passes_free(data->psl, psl_len);
206     gpu_viewport_storage_free(data->stl, stl_len);
207
208     MEM_freeN(data->fbl);
209     MEM_freeN(data->txl);
210     MEM_freeN(data->psl);
211     MEM_freeN(data->stl);
212
213     /* We could handle this in the DRW module */
214     if (data->text_draw_cache) {
215       extern void DRW_text_cache_destroy(struct DRWTextStore * dt);
216       DRW_text_cache_destroy(data->text_draw_cache);
217       data->text_draw_cache = NULL;
218     }
219
220     MEM_freeN(data);
221
222     BLI_remlink(&viewport->data, link);
223     MEM_freeN(link);
224   }
225
226   gpu_viewport_texture_pool_free(viewport);
227 }
228
229 void *GPU_viewport_engine_data_get(GPUViewport *viewport, void *engine_type)
230 {
231   for (LinkData *link = viewport->data.first; link; link = link->next) {
232     ViewportEngineData *vdata = link->data;
233     if (vdata->engine_type == engine_type) {
234       return vdata;
235     }
236   }
237   return NULL;
238 }
239
240 ViewportMemoryPool *GPU_viewport_mempool_get(GPUViewport *viewport)
241 {
242   return &viewport->vmempool;
243 }
244
245 struct DRWInstanceDataList *GPU_viewport_instance_data_list_get(GPUViewport *viewport)
246 {
247   return viewport->idatalist;
248 }
249
250 void *GPU_viewport_framebuffer_list_get(GPUViewport *viewport)
251 {
252   return viewport->fbl;
253 }
254
255 void *GPU_viewport_texture_list_get(GPUViewport *viewport)
256 {
257   return viewport->txl;
258 }
259
260 void GPU_viewport_size_get(const GPUViewport *viewport, int size[2])
261 {
262   size[0] = viewport->size[0];
263   size[1] = viewport->size[1];
264 }
265
266 /**
267  * Special case, this is needed for when we have a viewport without a frame-buffer output
268  * (occlusion queries for eg)
269  * but still need to set the size since it may be used for other calculations.
270  */
271 void GPU_viewport_size_set(GPUViewport *viewport, const int size[2])
272 {
273   viewport->size[0] = size[0];
274   viewport->size[1] = size[1];
275 }
276
277 double *GPU_viewport_cache_time_get(GPUViewport *viewport)
278 {
279   return &viewport->cache_time;
280 }
281
282 /**
283  * Try to find a texture corresponding to params into the texture pool.
284  * If no texture was found, create one and add it to the pool.
285  */
286 GPUTexture *GPU_viewport_texture_pool_query(
287     GPUViewport *viewport, void *engine, int width, int height, int format)
288 {
289   GPUTexture *tex;
290
291   for (ViewportTempTexture *tmp_tex = viewport->tex_pool.first; tmp_tex; tmp_tex = tmp_tex->next) {
292     if ((GPU_texture_format(tmp_tex->texture) == format) &&
293         (GPU_texture_width(tmp_tex->texture) == width) &&
294         (GPU_texture_height(tmp_tex->texture) == height)) {
295       /* Search if the engine is not already using this texture */
296       for (int i = 0; i < MAX_ENGINE_BUFFER_SHARING; ++i) {
297         if (tmp_tex->user[i] == engine) {
298           break;
299         }
300
301         if (tmp_tex->user[i] == NULL) {
302           tmp_tex->user[i] = engine;
303           return tmp_tex->texture;
304         }
305       }
306     }
307   }
308
309   tex = GPU_texture_create_2d(width, height, format, NULL, NULL);
310   GPU_texture_bind(tex, 0);
311   /* Doing filtering for depth does not make sense when not doing shadow mapping,
312    * and enabling texture filtering on integer texture make them unreadable. */
313   bool do_filter = !GPU_texture_depth(tex) && !GPU_texture_integer(tex);
314   GPU_texture_filter_mode(tex, do_filter);
315   GPU_texture_unbind(tex);
316
317   ViewportTempTexture *tmp_tex = MEM_callocN(sizeof(ViewportTempTexture), "ViewportTempTexture");
318   tmp_tex->texture = tex;
319   tmp_tex->user[0] = engine;
320   BLI_addtail(&viewport->tex_pool, tmp_tex);
321
322   return tex;
323 }
324
325 static void gpu_viewport_texture_pool_clear_users(GPUViewport *viewport)
326 {
327   ViewportTempTexture *tmp_tex_next;
328
329   for (ViewportTempTexture *tmp_tex = viewport->tex_pool.first; tmp_tex; tmp_tex = tmp_tex_next) {
330     tmp_tex_next = tmp_tex->next;
331     bool no_user = true;
332     for (int i = 0; i < MAX_ENGINE_BUFFER_SHARING; ++i) {
333       if (tmp_tex->user[i] != NULL) {
334         tmp_tex->user[i] = NULL;
335         no_user = false;
336       }
337     }
338
339     if (no_user) {
340       GPU_texture_free(tmp_tex->texture);
341       BLI_freelinkN(&viewport->tex_pool, tmp_tex);
342     }
343   }
344 }
345
346 static void gpu_viewport_texture_pool_free(GPUViewport *viewport)
347 {
348   for (ViewportTempTexture *tmp_tex = viewport->tex_pool.first; tmp_tex; tmp_tex = tmp_tex->next) {
349     GPU_texture_free(tmp_tex->texture);
350   }
351
352   BLI_freelistN(&viewport->tex_pool);
353 }
354
355 bool GPU_viewport_engines_data_validate(GPUViewport *viewport, uint hash)
356 {
357   bool dirty = false;
358
359   if (viewport->data_hash != hash) {
360     gpu_viewport_engines_data_free(viewport);
361     dirty = true;
362   }
363
364   viewport->data_hash = hash;
365
366   return dirty;
367 }
368
369 void GPU_viewport_cache_release(GPUViewport *viewport)
370 {
371   for (LinkData *link = viewport->data.first; link; link = link->next) {
372     ViewportEngineData *data = link->data;
373     int psl_len;
374     DRW_engine_viewport_data_size_get(data->engine_type, NULL, NULL, &psl_len, NULL);
375     gpu_viewport_passes_free(data->psl, psl_len);
376   }
377 }
378
379 static void gpu_viewport_default_fb_create(GPUViewport *viewport)
380 {
381   DefaultFramebufferList *dfbl = viewport->fbl;
382   DefaultTextureList *dtxl = viewport->txl;
383   int *size = viewport->size;
384   bool ok = true;
385
386   dtxl->color = GPU_texture_create_2d(size[0], size[1], GPU_RGBA8, NULL, NULL);
387   dtxl->depth = GPU_texture_create_2d(size[0], size[1], GPU_DEPTH24_STENCIL8, NULL, NULL);
388
389   if (!(dtxl->depth && dtxl->color)) {
390     ok = false;
391     goto cleanup;
392   }
393
394   GPU_framebuffer_ensure_config(
395       &dfbl->default_fb,
396       {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(dtxl->color)});
397
398   GPU_framebuffer_ensure_config(&dfbl->depth_only_fb,
399                                 {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_NONE});
400
401   GPU_framebuffer_ensure_config(&dfbl->color_only_fb,
402                                 {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(dtxl->color)});
403
404   ok = ok && GPU_framebuffer_check_valid(dfbl->default_fb, NULL);
405   ok = ok && GPU_framebuffer_check_valid(dfbl->color_only_fb, NULL);
406   ok = ok && GPU_framebuffer_check_valid(dfbl->depth_only_fb, NULL);
407
408 cleanup:
409   if (!ok) {
410     GPU_viewport_free(viewport);
411     DRW_opengl_context_disable();
412     return;
413   }
414
415   GPU_framebuffer_restore();
416 }
417
418 static void gpu_viewport_default_multisample_fb_create(GPUViewport *viewport)
419 {
420   DefaultFramebufferList *dfbl = viewport->fbl;
421   DefaultTextureList *dtxl = viewport->txl;
422   int *size = viewport->size;
423   int samples = viewport->samples;
424   bool ok = true;
425
426   dtxl->multisample_color = GPU_texture_create_2d_multisample(
427       size[0], size[1], GPU_RGBA8, NULL, samples, NULL);
428   dtxl->multisample_depth = GPU_texture_create_2d_multisample(
429       size[0], size[1], GPU_DEPTH24_STENCIL8, NULL, samples, NULL);
430
431   if (!(dtxl->multisample_depth && dtxl->multisample_color)) {
432     ok = false;
433     goto cleanup;
434   }
435
436   GPU_framebuffer_ensure_config(&dfbl->multisample_fb,
437                                 {GPU_ATTACHMENT_TEXTURE(dtxl->multisample_depth),
438                                  GPU_ATTACHMENT_TEXTURE(dtxl->multisample_color)});
439
440   ok = ok && GPU_framebuffer_check_valid(dfbl->multisample_fb, NULL);
441
442 cleanup:
443   if (!ok) {
444     GPU_viewport_free(viewport);
445     DRW_opengl_context_disable();
446     return;
447   }
448
449   GPU_framebuffer_restore();
450 }
451
452 void GPU_viewport_bind(GPUViewport *viewport, const rcti *rect)
453 {
454   DefaultFramebufferList *dfbl = viewport->fbl;
455   int fbl_len, txl_len;
456
457   /* add one pixel because of scissor test */
458   int rect_w = BLI_rcti_size_x(rect) + 1;
459   int rect_h = BLI_rcti_size_y(rect) + 1;
460
461   DRW_opengl_context_enable();
462
463   if (dfbl->default_fb) {
464     if (rect_w != viewport->size[0] || rect_h != viewport->size[1] ||
465         U.ogl_multisamples != viewport->samples) {
466       gpu_viewport_buffers_free((FramebufferList *)viewport->fbl,
467                                 default_fbl_len,
468                                 (TextureList *)viewport->txl,
469                                 default_txl_len);
470
471       for (LinkData *link = viewport->data.first; link; link = link->next) {
472         ViewportEngineData *data = link->data;
473         DRW_engine_viewport_data_size_get(data->engine_type, &fbl_len, &txl_len, NULL, NULL);
474         gpu_viewport_buffers_free(data->fbl, fbl_len, data->txl, txl_len);
475       }
476
477       gpu_viewport_texture_pool_free(viewport);
478     }
479   }
480
481   viewport->size[0] = rect_w;
482   viewport->size[1] = rect_h;
483   viewport->samples = U.ogl_multisamples;
484
485   gpu_viewport_texture_pool_clear_users(viewport);
486
487   /* Multisample Buffer */
488   if (viewport->samples > 0) {
489     if (!dfbl->default_fb) {
490       gpu_viewport_default_multisample_fb_create(viewport);
491     }
492   }
493
494   if (!dfbl->default_fb) {
495     gpu_viewport_default_fb_create(viewport);
496   }
497 }
498
499 void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect)
500 {
501   DefaultFramebufferList *dfbl = viewport->fbl;
502
503   if (dfbl->default_fb == NULL)
504     return;
505
506   DefaultTextureList *dtxl = viewport->txl;
507
508   GPUTexture *color = dtxl->color;
509
510   const float w = (float)GPU_texture_width(color);
511   const float h = (float)GPU_texture_height(color);
512
513   BLI_assert(w == BLI_rcti_size_x(rect) + 1);
514   BLI_assert(h == BLI_rcti_size_y(rect) + 1);
515
516   /* wmOrtho for the screen has this same offset */
517   const float halfx = GLA_PIXEL_OFS / w;
518   const float halfy = GLA_PIXEL_OFS / h;
519
520   float x1 = rect->xmin;
521   float x2 = rect->xmin + w;
522   float y1 = rect->ymin;
523   float y2 = rect->ymin + h;
524
525   GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR);
526   GPU_shader_bind(shader);
527
528   GPU_texture_bind(color, 0);
529   glUniform1i(GPU_shader_get_uniform_ensure(shader, "image"), 0);
530   glUniform4f(GPU_shader_get_uniform_ensure(shader, "rect_icon"),
531               halfx,
532               halfy,
533               1.0f + halfx,
534               1.0f + halfy);
535   glUniform4f(GPU_shader_get_uniform_ensure(shader, "rect_geom"), x1, y1, x2, y2);
536   glUniform4f(GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_COLOR), 1.0f, 1.0f, 1.0f, 1.0f);
537
538   GPU_draw_primitive(GPU_PRIM_TRI_STRIP, 4);
539
540   GPU_texture_unbind(color);
541 }
542
543 void GPU_viewport_unbind(GPUViewport *UNUSED(viewport))
544 {
545   GPU_framebuffer_restore();
546   DRW_opengl_context_disable();
547 }
548
549 GPUTexture *GPU_viewport_color_texture(GPUViewport *viewport)
550 {
551   DefaultFramebufferList *dfbl = viewport->fbl;
552
553   if (dfbl->default_fb) {
554     DefaultTextureList *dtxl = viewport->txl;
555     return dtxl->color;
556   }
557
558   return NULL;
559 }
560
561 static void gpu_viewport_buffers_free(FramebufferList *fbl,
562                                       int fbl_len,
563                                       TextureList *txl,
564                                       int txl_len)
565 {
566   for (int i = 0; i < fbl_len; i++) {
567     GPUFrameBuffer *fb = fbl->framebuffers[i];
568     if (fb) {
569       GPU_framebuffer_free(fb);
570       fbl->framebuffers[i] = NULL;
571     }
572   }
573   for (int i = 0; i < txl_len; i++) {
574     GPUTexture *tex = txl->textures[i];
575     if (tex) {
576       GPU_texture_free(tex);
577       txl->textures[i] = NULL;
578     }
579   }
580 }
581
582 static void gpu_viewport_storage_free(StorageList *stl, int stl_len)
583 {
584   for (int i = 0; i < stl_len; i++) {
585     void *storage = stl->storage[i];
586     if (storage) {
587       MEM_freeN(storage);
588       stl->storage[i] = NULL;
589     }
590   }
591 }
592
593 static void gpu_viewport_passes_free(PassList *psl, int psl_len)
594 {
595   for (int i = 0; i < psl_len; i++) {
596     struct DRWPass *pass = psl->passes[i];
597     if (pass) {
598       DRW_pass_free(pass);
599       psl->passes[i] = NULL;
600     }
601   }
602 }
603
604 /* Must be executed inside Drawmanager Opengl Context. */
605 void GPU_viewport_free(GPUViewport *viewport)
606 {
607   gpu_viewport_engines_data_free(viewport);
608
609   gpu_viewport_buffers_free((FramebufferList *)viewport->fbl,
610                             default_fbl_len,
611                             (TextureList *)viewport->txl,
612                             default_txl_len);
613
614   gpu_viewport_texture_pool_free(viewport);
615
616   MEM_freeN(viewport->fbl);
617   MEM_freeN(viewport->txl);
618
619   if (viewport->vmempool.calls != NULL) {
620     BLI_mempool_destroy(viewport->vmempool.calls);
621   }
622   if (viewport->vmempool.states != NULL) {
623     BLI_mempool_destroy(viewport->vmempool.states);
624   }
625   if (viewport->vmempool.shgroups != NULL) {
626     BLI_mempool_destroy(viewport->vmempool.shgroups);
627   }
628   if (viewport->vmempool.uniforms != NULL) {
629     BLI_mempool_destroy(viewport->vmempool.uniforms);
630   }
631   if (viewport->vmempool.passes != NULL) {
632     BLI_mempool_destroy(viewport->vmempool.passes);
633   }
634
635   DRW_instance_data_list_free(viewport->idatalist);
636   MEM_freeN(viewport->idatalist);
637
638   MEM_freeN(viewport);
639 }