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