Merge branch 'master' into 28
[blender.git] / source / blender / gpu / intern / gpu_viewport.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
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  * The Original Code is Copyright (C) 2006 Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s):
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/gpu/intern/gpu_viewport.c
29  *  \ingroup gpu
30  *
31  * System that manages viewport drawing.
32  */
33
34 #include <string.h>
35
36 #include "BLI_listbase.h"
37 #include "BLI_rect.h"
38 #include "BLI_string.h"
39
40 #include "DNA_vec_types.h"
41
42 #include "BKE_global.h"
43
44 #include "GPU_framebuffer.h"
45 #include "GPU_glew.h"
46 #include "GPU_immediate.h"
47 #include "GPU_texture.h"
48 #include "GPU_viewport.h"
49
50 #include "DRW_engine.h"
51
52 #include "MEM_guardedalloc.h"
53
54 static const int default_fbl_len = (sizeof(DefaultFramebufferList)) / sizeof(void *);
55 static const int default_txl_len = (sizeof(DefaultTextureList)) / sizeof(void *);
56
57 struct GPUViewport {
58         float pad[4];
59
60         /* debug */
61         GPUTexture *debug_depth;
62         int size[2];
63
64         ListBase data;  /* ViewportEngineData wrapped in LinkData */
65         unsigned int data_hash;  /* If hash mismatch we free all ViewportEngineData in this viewport */
66
67         DefaultFramebufferList *fbl;
68         DefaultTextureList *txl;
69 };
70
71 static void gpu_viewport_buffers_free(FramebufferList *fbl, int fbl_len, TextureList *txl, int txl_len);
72 static void gpu_viewport_storage_free(StorageList *stl, int stl_len);
73 static void gpu_viewport_passes_free(PassList *psl, int psl_len);
74
75 GPUViewport *GPU_viewport_create(void)
76 {
77         GPUViewport *viewport = MEM_callocN(sizeof(GPUViewport), "GPUViewport");
78         viewport->fbl = MEM_callocN(sizeof(DefaultFramebufferList), "FramebufferList");
79         viewport->txl = MEM_callocN(sizeof(DefaultTextureList), "TextureList");
80
81         viewport->size[0] = viewport->size[1] = -1;
82
83         return viewport;
84 }
85
86 void *GPU_viewport_engine_data_create(GPUViewport *viewport, void *engine_type)
87 {
88         LinkData *ld = MEM_callocN(sizeof(LinkData), "LinkData");
89         ViewportEngineData *data = MEM_callocN(sizeof(ViewportEngineData), "ViewportEngineData");
90         int fbl_len, txl_len, psl_len, stl_len;
91
92         DRW_engine_viewport_data_size_get(engine_type, &fbl_len, &txl_len, &psl_len, &stl_len);
93
94         data->engine_type = engine_type;
95
96         data->fbl = MEM_callocN((sizeof(void *) * fbl_len) + sizeof(FramebufferList), "FramebufferList");
97         data->txl = MEM_callocN((sizeof(void *) * txl_len) + sizeof(TextureList), "TextureList");
98         data->psl = MEM_callocN((sizeof(void *) * psl_len) + sizeof(PassList), "PassList");
99         data->stl = MEM_callocN((sizeof(void *) * stl_len) + sizeof(StorageList), "StorageList");
100
101         ld->data = data;
102         BLI_addtail(&viewport->data, ld);
103
104         return data;
105 }
106
107 static void gpu_viewport_engines_data_free(GPUViewport *viewport)
108 {
109         int fbl_len, txl_len, psl_len, stl_len;
110
111         LinkData *next;
112         for (LinkData *link = viewport->data.first; link; link = next) {
113                 next = link->next;
114                 ViewportEngineData *data = link->data;
115                 DRW_engine_viewport_data_size_get(data->engine_type, &fbl_len, &txl_len, &psl_len, &stl_len);
116
117                 gpu_viewport_buffers_free(data->fbl, fbl_len, data->txl, txl_len);
118                 gpu_viewport_passes_free(data->psl, psl_len);
119                 gpu_viewport_storage_free(data->stl, stl_len);
120
121                 MEM_freeN(data->fbl);
122                 MEM_freeN(data->txl);
123                 MEM_freeN(data->psl);
124                 MEM_freeN(data->stl);
125
126                 MEM_freeN(data);
127
128                 BLI_remlink(&viewport->data, link);
129                 MEM_freeN(link);
130         }
131 }
132
133 void *GPU_viewport_engine_data_get(GPUViewport *viewport, void *engine_type)
134 {
135         for (LinkData *link = viewport->data.first; link; link = link->next) {
136                 ViewportEngineData *vdata = link->data;
137                 if (vdata->engine_type == engine_type) {
138                         return vdata;
139                 }
140         }
141         return NULL;
142 }
143
144 void *GPU_viewport_framebuffer_list_get(GPUViewport *viewport)
145 {
146         return viewport->fbl;
147 }
148
149 void *GPU_viewport_texture_list_get(GPUViewport *viewport)
150 {
151         return viewport->txl;
152 }
153
154 void GPU_viewport_size_get(GPUViewport *viewport, int *size)
155 {
156         size[0] = viewport->size[0];
157         size[1] = viewport->size[1];
158 }
159
160 bool GPU_viewport_cache_validate(GPUViewport *viewport, unsigned int hash)
161 {
162         bool dirty = false;
163
164         /* TODO for testing only, we need proper cache invalidation */
165         if (G.debug_value != 666 && G.debug_value != 667) {
166                 for (LinkData *link = viewport->data.first; link; link = link->next) {
167                         ViewportEngineData *data = link->data;
168                         int psl_len;
169                         DRW_engine_viewport_data_size_get(data->engine_type, NULL, NULL, &psl_len, NULL);
170                         gpu_viewport_passes_free(data->psl, psl_len);
171                 }
172                 dirty = true;
173         }
174
175         if (viewport->data_hash != hash) {
176                 gpu_viewport_engines_data_free(viewport);
177                 dirty = true;
178         }
179
180         viewport->data_hash = hash;
181
182         return dirty;
183 }
184
185 void GPU_viewport_bind(GPUViewport *viewport, const rcti *rect)
186 {
187         DefaultFramebufferList *dfbl = viewport->fbl;
188         DefaultTextureList *dtxl = viewport->txl;
189         int fbl_len, txl_len;
190
191         /* add one pixel because of scissor test */
192         int rect_w = BLI_rcti_size_x(rect) + 1;
193         int rect_h = BLI_rcti_size_y(rect) + 1;
194
195         if (dfbl->default_fb) {
196                 if (rect_w != viewport->size[0] || rect_h != viewport->size[1]) {
197                         gpu_viewport_buffers_free(
198                                 (FramebufferList *)viewport->fbl, default_fbl_len,
199                                 (TextureList *)viewport->txl, default_txl_len);
200
201                         for (LinkData *link = viewport->data.first; link; link = link->next) {
202                                 ViewportEngineData *data = link->data;
203                                 DRW_engine_viewport_data_size_get(data->engine_type, &fbl_len, &txl_len, NULL, NULL);
204                                 gpu_viewport_buffers_free(data->fbl, fbl_len, data->txl, txl_len);
205                         }
206                 }
207         }
208
209         if (!dfbl->default_fb) {
210                 bool ok = true;
211                 viewport->size[0] = rect_w;
212                 viewport->size[1] = rect_h;
213
214                 dfbl->default_fb = GPU_framebuffer_create();
215                 if (!dfbl->default_fb) {
216                         ok = false;
217                         goto cleanup;
218                 }
219
220                 /* Color */
221                 /* No multi samples for now */
222                 dtxl->color = GPU_texture_create_2D(rect_w, rect_h, NULL, NULL);
223                 if (!dtxl->color) {
224                         ok = false;
225                         goto cleanup;
226                 }
227
228                 if (!GPU_framebuffer_texture_attach(dfbl->default_fb, dtxl->color, 0)) {
229                         ok = false;
230                         goto cleanup;
231                 }
232
233                 /* Depth */
234                 dtxl->depth = GPU_texture_create_depth(rect_w, rect_h, NULL);
235                 if (!dtxl->depth) {
236                         ok = false;
237                         goto cleanup;
238                 }
239                 else if (!GPU_framebuffer_texture_attach(dfbl->default_fb, dtxl->depth, 0)) {
240                         ok = false;
241                         goto cleanup;
242                 }
243                 else if (!GPU_framebuffer_check_valid(dfbl->default_fb, NULL)) {
244                         ok = false;
245                         goto cleanup;
246                 }
247
248 cleanup:
249                 if (!ok) {
250                         GPU_viewport_free(viewport);
251                         MEM_freeN(viewport);
252                         return;
253                 }
254
255                 GPU_framebuffer_restore();
256         }
257
258         GPU_framebuffer_slots_bind(dfbl->default_fb, 0);
259 }
260
261 static void draw_ofs_to_screen(GPUViewport *viewport)
262 {
263         DefaultTextureList *dtxl = viewport->txl;
264
265         GPUTexture *color = dtxl->color;
266
267         const float w = (float)GPU_texture_width(color);
268         const float h = (float)GPU_texture_height(color);
269
270         VertexFormat *format = immVertexFormat();
271         unsigned int texcoord = VertexFormat_add_attrib(format, "texCoord", COMP_F32, 2, KEEP_FLOAT);
272         unsigned int pos = VertexFormat_add_attrib(format, "pos", COMP_F32, 2, KEEP_FLOAT);
273
274         immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_MODULATE_ALPHA);
275         GPU_texture_bind(color, 0);
276
277         immUniform1i("image", 0); /* default GL_TEXTURE0 unit */
278
279         immBegin(PRIM_TRIANGLE_STRIP, 4);
280
281         immAttrib2f(texcoord, 0.0f, 0.0f);
282         immVertex2f(pos, 0.0f, 0.0f);
283
284         immAttrib2f(texcoord, 1.0f, 0.0f);
285         immVertex2f(pos, w, 0.0f);
286
287         immAttrib2f(texcoord, 0.0f, 1.0f);
288         immVertex2f(pos, 0.0f, h);
289
290         immAttrib2f(texcoord, 1.0f, 1.0f);
291         immVertex2f(pos, w, h);
292
293         immEnd();
294
295         GPU_texture_unbind(color);
296
297         immUnbindProgram();
298 }
299
300 void GPU_viewport_unbind(GPUViewport *viewport)
301 {
302         DefaultFramebufferList *dfbl = viewport->fbl;
303
304         if (dfbl->default_fb) {
305                 GPU_framebuffer_texture_unbind(dfbl->default_fb, NULL);
306                 GPU_framebuffer_restore();
307
308                 glEnable(GL_SCISSOR_TEST);
309                 glDisable(GL_DEPTH_TEST);
310
311                 /* This might be bandwidth limiting */
312                 draw_ofs_to_screen(viewport);
313         }
314 }
315
316 static void gpu_viewport_buffers_free(
317         FramebufferList *fbl, int fbl_len,
318         TextureList *txl, int txl_len)
319 {
320         for (int i = 0; i < fbl_len; i++) {
321                 GPUFrameBuffer *fb = fbl->framebuffers[i];
322                 if (fb) {
323                         GPU_framebuffer_free(fb);
324                         fbl->framebuffers[i] = NULL;
325                 }
326         }
327         for (int i = 0; i < txl_len; i++) {
328                 GPUTexture *tex = txl->textures[i];
329                 if (tex) {
330                         GPU_texture_free(tex);
331                         txl->textures[i] = NULL;
332                 }
333         }
334 }
335
336 static void gpu_viewport_storage_free(StorageList *stl, int stl_len)
337 {
338         for (int i = 0; i < stl_len; i++) {
339                 void *storage = stl->storage[i];
340                 if (storage) {
341                         MEM_freeN(storage);
342                         stl->storage[i] = NULL;
343                 }
344         }
345 }
346
347 static void gpu_viewport_passes_free(PassList *psl, int psl_len)
348 {
349         for (int i = 0; i < psl_len; i++) {
350                 struct DRWPass *pass = psl->passes[i];
351                 if (pass) {
352                         DRW_pass_free(pass);
353                         MEM_freeN(pass);
354                         psl->passes[i] = NULL;
355                 }
356         }
357 }
358
359 void GPU_viewport_free(GPUViewport *viewport)
360 {
361         gpu_viewport_engines_data_free(viewport);
362
363         gpu_viewport_buffers_free(
364                 (FramebufferList *)viewport->fbl, default_fbl_len,
365                 (TextureList *)viewport->txl, default_txl_len);
366
367         MEM_freeN(viewport->fbl);
368         MEM_freeN(viewport->txl);
369
370         GPU_viewport_debug_depth_free(viewport);
371 }
372
373 /****************** debug ********************/
374
375 bool GPU_viewport_debug_depth_create(GPUViewport *viewport, int width, int height, char err_out[256])
376 {
377         viewport->debug_depth = GPU_texture_create_2D_custom(width, height, 4, GPU_RGBA16F, NULL, err_out);
378         return (viewport->debug_depth != NULL);
379 }
380
381 void GPU_viewport_debug_depth_free(GPUViewport *viewport)
382 {
383         if (viewport->debug_depth != NULL) {
384                 MEM_freeN(viewport->debug_depth);
385                 viewport->debug_depth = NULL;
386         }
387 }
388
389 void GPU_viewport_debug_depth_store(GPUViewport *viewport, const int x, const int y)
390 {
391         const int w = GPU_texture_width(viewport->debug_depth);
392         const int h = GPU_texture_height(viewport->debug_depth);
393
394         GPU_texture_bind(viewport->debug_depth, 0);
395         glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, x, y, w, h, 0);
396         GPU_texture_unbind(viewport->debug_depth);
397 }
398
399 void GPU_viewport_debug_depth_draw(GPUViewport *viewport, const float znear, const float zfar)
400 {
401         const float w = (float)GPU_texture_width(viewport->debug_depth);
402         const float h = (float)GPU_texture_height(viewport->debug_depth);
403
404         VertexFormat *format = immVertexFormat();
405         unsigned int texcoord = VertexFormat_add_attrib(format, "texCoord", COMP_F32, 2, KEEP_FLOAT);
406         unsigned int pos = VertexFormat_add_attrib(format, "pos", COMP_F32, 2, KEEP_FLOAT);
407
408         immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_DEPTH);
409
410         GPU_texture_bind(viewport->debug_depth, 0);
411
412         immUniform1f("znear", znear);
413         immUniform1f("zfar", zfar);
414         immUniform1i("image", 0); /* default GL_TEXTURE0 unit */
415
416         immBegin(PRIM_TRIANGLE_STRIP, 4);
417
418         immAttrib2f(texcoord, 0.0f, 0.0f);
419         immVertex2f(pos, 0.0f, 0.0f);
420
421         immAttrib2f(texcoord, 1.0f, 0.0f);
422         immVertex2f(pos, w, 0.0f);
423
424         immAttrib2f(texcoord, 0.0f, 1.0f);
425         immVertex2f(pos, 0.0f, h);
426
427         immAttrib2f(texcoord, 1.0f, 1.0f);
428         immVertex2f(pos, w, h);
429
430         immEnd();
431
432         GPU_texture_unbind(viewport->debug_depth);
433
434         immUnbindProgram();
435 }
436
437 int GPU_viewport_debug_depth_width(const GPUViewport *viewport)
438 {
439         return GPU_texture_width(viewport->debug_depth);
440 }
441
442 int GPU_viewport_debug_depth_height(const GPUViewport *viewport)
443 {
444         return GPU_texture_height(viewport->debug_depth);
445 }
446
447 bool GPU_viewport_debug_depth_is_valid(GPUViewport *viewport)
448 {
449         return viewport->debug_depth != NULL;
450 }