Merge branch 'master' into blender2.8
[blender.git] / source / blender / gpu / intern / gpu_framebuffer.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) 2005 Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Brecht Van Lommel.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 #include "MEM_guardedalloc.h"
29
30 #include "BLI_blenlib.h"
31 #include "BLI_utildefines.h"
32
33 #include "BKE_global.h"
34
35 #include "GPU_batch.h"
36 #include "GPU_draw.h"
37 #include "GPU_framebuffer.h"
38 #include "GPU_matrix.h"
39 #include "GPU_shader.h"
40 #include "GPU_texture.h"
41
42 static struct GPUFrameBufferGlobal {
43         GLuint currentfb;
44 } GG = {0};
45
46 /* Number of maximum output slots.
47  * We support 4 outputs for now (usually we wouldn't need more to preserve fill rate) */
48 #define GPU_FB_MAX_SLOTS 4
49
50 struct GPUFrameBuffer {
51         GLuint object;
52         GPUTexture *colortex[GPU_FB_MAX_SLOTS];
53         GPUTexture *depthtex;
54 };
55
56 static void gpu_print_framebuffer_error(GLenum status, char err_out[256])
57 {
58         const char *format = "GPUFrameBuffer: framebuffer status %s\n";
59         const char *err = "unknown";
60
61 #define format_status(X) \
62         case GL_FRAMEBUFFER_##X: err = "GL_FRAMEBUFFER_"#X; \
63                 break;
64
65         switch (status) {
66                 /* success */
67                 format_status(COMPLETE)
68                 /* errors shared by OpenGL desktop & ES */
69                 format_status(INCOMPLETE_ATTACHMENT)
70                 format_status(INCOMPLETE_MISSING_ATTACHMENT)
71                 format_status(UNSUPPORTED)
72 #if 0 /* for OpenGL ES only */
73                 format_status(INCOMPLETE_DIMENSIONS)
74 #else /* for desktop GL only */
75                 format_status(INCOMPLETE_DRAW_BUFFER)
76                 format_status(INCOMPLETE_READ_BUFFER)
77                 format_status(INCOMPLETE_MULTISAMPLE)
78                 format_status(UNDEFINED)
79 #endif
80         }
81
82 #undef format_status
83
84         if (err_out) {
85                 BLI_snprintf(err_out, 256, format, err);
86         }
87         else {
88                 fprintf(stderr, format, err);
89         }
90 }
91
92 /* GPUFrameBuffer */
93
94 GPUFrameBuffer *GPU_framebuffer_create(void)
95 {
96         GPUFrameBuffer *fb;
97
98         fb = MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer");
99         glGenFramebuffers(1, &fb->object);
100
101         if (!fb->object) {
102                 fprintf(stderr, "GPUFFrameBuffer: framebuffer gen failed.\n");
103                 GPU_framebuffer_free(fb);
104                 return NULL;
105         }
106
107         /* make sure no read buffer is enabled, so completeness check will not fail. We set those at binding time */
108         glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
109         glReadBuffer(GL_NONE);
110         glDrawBuffer(GL_NONE);
111         glBindFramebuffer(GL_FRAMEBUFFER, 0);
112         
113         return fb;
114 }
115
116 bool GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip)
117 {
118         GLenum attachment;
119
120         if (slot >= GPU_FB_MAX_SLOTS) {
121                 fprintf(stderr,
122                         "Attaching to index %d framebuffer slot unsupported. "
123                         "Use at most %d\n", slot, GPU_FB_MAX_SLOTS);
124                 return false;
125         }
126
127         if ((G.debug & G_DEBUG)) {
128                 if (GPU_texture_bound_number(tex) != -1) {
129                         fprintf(stderr,
130                                 "Feedback loop warning!: "
131                                 "Attempting to attach texture to framebuffer while still bound to texture unit for drawing!\n");
132                 }
133         }
134
135         glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
136         GG.currentfb = fb->object;
137
138         if (GPU_texture_stencil(tex) && GPU_texture_depth(tex))
139                 attachment = GL_DEPTH_STENCIL_ATTACHMENT;
140         else if (GPU_texture_depth(tex))
141                 attachment = GL_DEPTH_ATTACHMENT;
142         else
143                 attachment = GL_COLOR_ATTACHMENT0 + slot;
144
145         glFramebufferTexture(GL_FRAMEBUFFER, attachment, GPU_texture_opengl_bindcode(tex), mip);
146
147         if (GPU_texture_depth(tex))
148                 fb->depthtex = tex;
149         else
150                 fb->colortex[slot] = tex;
151
152         GPU_texture_framebuffer_set(tex, fb, slot);
153
154         return true;
155 }
156
157 static bool gpu_framebuffer_texture_layer_attach_ex(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip, bool cubemap)
158 {
159         GLenum attachment;
160         GLenum facetarget;
161
162         if (slot >= GPU_FB_MAX_SLOTS) {
163                 fprintf(stderr,
164                         "Attaching to index %d framebuffer slot unsupported. "
165                         "Use at most %d\n", slot, GPU_FB_MAX_SLOTS);
166                 return false;
167         }
168
169         if ((G.debug & G_DEBUG)) {
170                 if (GPU_texture_bound_number(tex) != -1) {
171                         fprintf(stderr,
172                                 "Feedback loop warning!: "
173                                 "Attempting to attach texture to framebuffer while still bound to texture unit for drawing!\n");
174                 }
175         }
176
177         glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
178         GG.currentfb = fb->object;
179
180         if (GPU_texture_stencil(tex) && GPU_texture_depth(tex))
181                 attachment = GL_DEPTH_STENCIL_ATTACHMENT;
182         else if (GPU_texture_depth(tex))
183                 attachment = GL_DEPTH_ATTACHMENT;
184         else
185                 attachment = GL_COLOR_ATTACHMENT0 + slot;
186
187         if (cubemap) {
188                 facetarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer;
189                 glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, facetarget, GPU_texture_opengl_bindcode(tex), mip);
190         }
191         else {
192                 glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, GPU_texture_opengl_bindcode(tex), mip, layer);
193         }
194
195         if (GPU_texture_depth(tex))
196                 fb->depthtex = tex;
197         else
198                 fb->colortex[slot] = tex;
199
200         GPU_texture_framebuffer_set(tex, fb, slot);
201
202         return true;
203 }
204
205 bool GPU_framebuffer_texture_layer_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip)
206 {
207         return gpu_framebuffer_texture_layer_attach_ex(fb, tex, slot, layer, mip, false);
208 }
209
210 bool GPU_framebuffer_texture_cubeface_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip)
211 {
212         BLI_assert(GPU_texture_target(tex) == GL_TEXTURE_CUBE_MAP);
213         return gpu_framebuffer_texture_layer_attach_ex(fb, tex, slot, face, mip, true);
214 }
215
216 void GPU_framebuffer_texture_detach(GPUTexture *tex)
217 {
218         GLenum attachment;
219         GPUFrameBuffer *fb = GPU_texture_framebuffer(tex);
220         int fb_attachment = GPU_texture_framebuffer_attachment(tex);
221
222         if (!fb)
223                 return;
224
225         if (GG.currentfb != fb->object) {
226                 glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
227                 GG.currentfb = fb->object;
228         }
229
230         if (GPU_texture_stencil(tex) && GPU_texture_depth(tex)) {
231                 fb->depthtex = NULL;
232                 attachment = GL_DEPTH_STENCIL_ATTACHMENT;
233         }
234         else if (GPU_texture_depth(tex)) {
235                 fb->depthtex = NULL;
236                 attachment = GL_DEPTH_ATTACHMENT;
237         }
238         else {
239                 BLI_assert(fb->colortex[fb_attachment] == tex);
240                 fb->colortex[fb_attachment] = NULL;
241                 attachment = GL_COLOR_ATTACHMENT0 + fb_attachment;
242         }
243
244         glFramebufferTexture(GL_FRAMEBUFFER, attachment, 0, 0);
245
246         GPU_texture_framebuffer_set(tex, NULL, -1);
247 }
248
249 void GPU_texture_bind_as_framebuffer(GPUTexture *tex)
250 {
251         GPUFrameBuffer *fb = GPU_texture_framebuffer(tex);
252         int fb_attachment = GPU_texture_framebuffer_attachment(tex);
253
254         if (!fb) {
255                 fprintf(stderr, "Error, texture not bound to framebuffer!\n");
256                 return;
257         }
258
259         /* push attributes */
260         gpuPushAttrib(GPU_ENABLE_BIT | GPU_VIEWPORT_BIT);
261         glDisable(GL_SCISSOR_TEST);
262
263         /* bind framebuffer */
264         glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
265
266         if (GPU_texture_depth(tex)) {
267                 glDrawBuffer(GL_NONE);
268                 glReadBuffer(GL_NONE);
269         }
270         else {
271                 /* last bound prevails here, better allow explicit control here too */
272                 glDrawBuffer(GL_COLOR_ATTACHMENT0 + fb_attachment);
273                 glReadBuffer(GL_COLOR_ATTACHMENT0 + fb_attachment);
274         }
275         
276         if (GPU_texture_target(tex) == GL_TEXTURE_2D_MULTISAMPLE) {
277                 glEnable(GL_MULTISAMPLE);
278         }
279
280         /* set default viewport */
281         glViewport(0, 0, GPU_texture_width(tex), GPU_texture_height(tex));
282         GG.currentfb = fb->object;
283 }
284
285 void GPU_framebuffer_slots_bind(GPUFrameBuffer *fb, int slot)
286 {
287         int numslots = 0, i;
288         GLenum attachments[4];
289         
290         if (!fb->colortex[slot]) {
291                 fprintf(stderr, "Error, framebuffer slot empty!\n");
292                 return;
293         }
294         
295         for (i = 0; i < 4; i++) {
296                 if (fb->colortex[i]) {
297                         attachments[numslots] = GL_COLOR_ATTACHMENT0 + i;
298                         numslots++;
299                 }
300         }
301         
302         /* push attributes */
303         gpuPushAttrib(GPU_ENABLE_BIT | GPU_VIEWPORT_BIT);
304         glDisable(GL_SCISSOR_TEST);
305
306         /* bind framebuffer */
307         glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
308
309         /* last bound prevails here, better allow explicit control here too */
310         glDrawBuffers(numslots, attachments);
311         glReadBuffer(GL_COLOR_ATTACHMENT0 + slot);
312
313         /* set default viewport */
314         glViewport(0, 0, GPU_texture_width(fb->colortex[slot]), GPU_texture_height(fb->colortex[slot]));
315         GG.currentfb = fb->object;
316 }
317
318 void GPU_framebuffer_bind(GPUFrameBuffer *fb)
319 {
320         int numslots = 0, i;
321         GLenum attachments[4];
322         GLenum readattachement = 0;
323         GPUTexture *tex;
324
325         for (i = 0; i < 4; i++) {
326                 if (fb->colortex[i]) {
327                         attachments[numslots] = GL_COLOR_ATTACHMENT0 + i;
328                         tex = fb->colortex[i];
329
330                         if (!readattachement)
331                                 readattachement = GL_COLOR_ATTACHMENT0 + i;
332
333                         numslots++;
334                 }
335         }
336
337         /* bind framebuffer */
338         glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
339
340         if (numslots == 0) {
341                 glDrawBuffer(GL_NONE);
342                 glReadBuffer(GL_NONE);
343                 tex = fb->depthtex;
344         }
345         else {
346                 /* last bound prevails here, better allow explicit control here too */
347                 glDrawBuffers(numslots, attachments);
348                 glReadBuffer(readattachement);
349         }
350
351         glViewport(0, 0, GPU_texture_width(tex), GPU_texture_height(tex));
352         GG.currentfb = fb->object;
353 }
354
355 void GPU_framebuffer_texture_unbind(GPUFrameBuffer *UNUSED(fb), GPUTexture *UNUSED(tex))
356 {
357         /* Restore attributes. */
358         gpuPopAttrib();
359 }
360
361 void GPU_framebuffer_bind_no_save(GPUFrameBuffer *fb, int slot)
362 {
363         glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
364         /* last bound prevails here, better allow explicit control here too */
365         glDrawBuffer(GL_COLOR_ATTACHMENT0 + slot);
366         glReadBuffer(GL_COLOR_ATTACHMENT0 + slot);
367
368         /* push matrices and set default viewport and matrix */
369         glViewport(0, 0, GPU_texture_width(fb->colortex[slot]), GPU_texture_height(fb->colortex[slot]));
370         GG.currentfb = fb->object;
371 }
372
373 bool GPU_framebuffer_bound(GPUFrameBuffer *fb)
374 {
375         return fb->object == GG.currentfb;
376 }
377
378 bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256])
379 {
380         glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
381         GG.currentfb = fb->object;
382
383         GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
384
385         if (status != GL_FRAMEBUFFER_COMPLETE) {
386                 GPU_framebuffer_restore();
387                 gpu_print_framebuffer_error(status, err_out);
388                 return false;
389         }
390
391         return true;
392 }
393
394 void GPU_framebuffer_free(GPUFrameBuffer *fb)
395 {
396         int i;
397         if (fb->depthtex)
398                 GPU_framebuffer_texture_detach(fb->depthtex);
399
400         for (i = 0; i < GPU_FB_MAX_SLOTS; i++) {
401                 if (fb->colortex[i]) {
402                         GPU_framebuffer_texture_detach(fb->colortex[i]);
403                 }
404         }
405
406         if (fb->object) {
407                 glDeleteFramebuffers(1, &fb->object);
408
409                 if (GG.currentfb == fb->object) {
410                         glBindFramebuffer(GL_FRAMEBUFFER, 0);
411                         GG.currentfb = 0;
412                 }
413         }
414
415         MEM_freeN(fb);
416 }
417
418 void GPU_framebuffer_restore(void)
419 {
420         if (GG.currentfb != 0) {
421                 glBindFramebuffer(GL_FRAMEBUFFER, 0);
422                 GG.currentfb = 0;
423         }
424 }
425
426 void GPU_framebuffer_blur(
427         GPUFrameBuffer *fb, GPUTexture *tex,
428         GPUFrameBuffer *blurfb, GPUTexture *blurtex)
429 {
430         const float fullscreencos[4][2] = {{-1.0f, -1.0f}, {1.0f, -1.0f}, {-1.0f, 1.0f}, {1.0f, 1.0f}};
431         const float fullscreenuvs[4][2] = {{0.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}};
432
433         static Gwn_VertFormat format = {0};
434         static Gwn_VertBuf vbo = {{0}};
435         static Gwn_Batch batch = {{0}};
436
437         const float scaleh[2] = {1.0f / GPU_texture_width(blurtex), 0.0f};
438         const float scalev[2] = {0.0f, 1.0f / GPU_texture_height(tex)};
439
440         GPUShader *blur_shader = GPU_shader_get_builtin_shader(GPU_SHADER_SEP_GAUSSIAN_BLUR);
441
442         if (!blur_shader)
443                 return;
444
445         /* Preparing to draw quad */
446         if (format.attrib_ct == 0) {
447                 unsigned int i = 0;
448                 /* Vertex format */
449                 unsigned int pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
450                 unsigned int uvs = GWN_vertformat_attr_add(&format, "uvs", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
451
452                 /* Vertices */
453                 GWN_vertbuf_init_with_format(&vbo, &format);
454                 GWN_vertbuf_data_alloc(&vbo, 36);
455
456                 for (int j = 0; j < 3; ++j) {
457                         GWN_vertbuf_attr_set(&vbo, uvs, i, fullscreenuvs[j]);
458                         GWN_vertbuf_attr_set(&vbo, pos, i++, fullscreencos[j]);
459                 }
460                 for (int j = 1; j < 4; ++j) {
461                         GWN_vertbuf_attr_set(&vbo, uvs, i, fullscreenuvs[j]);
462                         GWN_vertbuf_attr_set(&vbo, pos, i++, fullscreencos[j]);
463                 }
464
465                 GWN_batch_init(&batch, GL_TRIANGLES, &vbo, NULL);
466         }
467                 
468         glDisable(GL_DEPTH_TEST);
469         
470         /* Blurring horizontally */
471         /* We do the bind ourselves rather than using GPU_framebuffer_texture_bind() to avoid
472          * pushing unnecessary matrices onto the OpenGL stack. */
473         glBindFramebuffer(GL_FRAMEBUFFER, blurfb->object);
474         glDrawBuffer(GL_COLOR_ATTACHMENT0);
475         
476         /* avoid warnings from texture binding */
477         GG.currentfb = blurfb->object;
478
479         glViewport(0, 0, GPU_texture_width(blurtex), GPU_texture_height(blurtex));
480
481         GPU_texture_bind(tex, 0);
482
483         Batch_set_builtin_program(&batch, GPU_SHADER_SEP_GAUSSIAN_BLUR);
484         GWN_batch_uniform_2f(&batch, "ScaleU", scaleh[0], scaleh[1]);
485         GWN_batch_uniform_1i(&batch, "textureSource", GL_TEXTURE0);
486         GWN_batch_draw(&batch);
487
488         /* Blurring vertically */
489         glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
490         glDrawBuffer(GL_COLOR_ATTACHMENT0);
491         
492         GG.currentfb = fb->object;
493         
494         glViewport(0, 0, GPU_texture_width(tex), GPU_texture_height(tex));
495
496         GPU_texture_bind(blurtex, 0);
497
498         /* Hack to make the following uniform stick */
499         Batch_set_builtin_program(&batch, GPU_SHADER_SEP_GAUSSIAN_BLUR);
500         GWN_batch_uniform_2f(&batch, "ScaleU", scalev[0], scalev[1]);
501         GWN_batch_uniform_1i(&batch, "textureSource", GL_TEXTURE0);
502         GWN_batch_draw(&batch);
503 }
504
505 void GPU_framebuffer_blit(GPUFrameBuffer *fb_read, int read_slot, GPUFrameBuffer *fb_write, int write_slot, bool use_depth)
506 {
507         GPUTexture *read_tex = (use_depth) ? fb_read->depthtex : fb_read->colortex[read_slot];
508         GPUTexture *write_tex = (use_depth) ? fb_write->depthtex : fb_write->colortex[write_slot];
509         int read_attach = (use_depth) ? GL_DEPTH_ATTACHMENT : GL_COLOR_ATTACHMENT0 + GPU_texture_framebuffer_attachment(read_tex);
510         int write_attach = (use_depth) ? GL_DEPTH_ATTACHMENT : GL_COLOR_ATTACHMENT0 + GPU_texture_framebuffer_attachment(write_tex);
511         int read_bind = GPU_texture_opengl_bindcode(read_tex);
512         int write_bind = GPU_texture_opengl_bindcode(write_tex);
513         const int read_w = GPU_texture_width(read_tex);
514         const int read_h = GPU_texture_height(read_tex);
515         const int write_w = GPU_texture_width(write_tex);
516         const int write_h = GPU_texture_height(write_tex);
517
518         /* read from multi-sample buffer */
519         glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_read->object);
520         glFramebufferTexture2D(
521                 GL_READ_FRAMEBUFFER, read_attach,
522                 GL_TEXTURE_2D, read_bind, 0);
523         BLI_assert(glCheckFramebufferStatus(GL_READ_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
524
525         /* write into new single-sample buffer */
526         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_write->object);
527         glFramebufferTexture2D(
528                 GL_DRAW_FRAMEBUFFER, write_attach,
529                 GL_TEXTURE_2D, write_bind, 0);
530         BLI_assert(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
531
532         glBlitFramebuffer(0, 0, read_w, read_h, 0, 0, write_w, write_h, (use_depth) ? GL_DEPTH_BUFFER_BIT : GL_COLOR_BUFFER_BIT, GL_NEAREST);
533
534         /* Restore previous framebuffer */
535         glBindFramebuffer(GL_FRAMEBUFFER, GG.currentfb);
536         glDrawBuffer(GL_COLOR_ATTACHMENT0);
537 }
538
539 /**
540  * Use this if you need to custom downsample your texture and use the previous mip level as input.
541  * This function only takes care of the correct texture handling. It execute the callback for each texture level.
542  **/
543 void GPU_framebuffer_recursive_downsample(
544         GPUFrameBuffer *fb, GPUTexture *tex, int num_iter, void (*callback)(void *userData, int level), void *userData)
545 {
546         int current_dim[2] = {GPU_texture_width(tex), GPU_texture_height(tex)};
547         GLenum attachment;
548
549         /* Manually setup framebuffer to not use GPU_texture_framebuffer_set() */
550         glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
551         GG.currentfb = fb->object;
552
553         if (GPU_texture_stencil(tex) && GPU_texture_depth(tex))
554                 attachment = GL_DEPTH_STENCIL_ATTACHMENT;
555         else if (GPU_texture_depth(tex))
556                 attachment = GL_DEPTH_ATTACHMENT;
557         else
558                 attachment = GL_COLOR_ATTACHMENT0;
559
560         /* last bound prevails here, better allow explicit control here too */
561         glDrawBuffer(GL_COLOR_ATTACHMENT0);
562         glReadBuffer(GL_COLOR_ATTACHMENT0);
563
564         for (int i=1; i < num_iter+1 && (current_dim[0] > 1 && current_dim[1] > 1); i++) {
565
566                 /* calculate next viewport size */
567                 current_dim[0] /= 2;
568                 current_dim[1] /= 2;
569
570                 /* ensure that the viewport size is always at least 1x1 */
571                 CLAMP_MIN(current_dim[0], 1);
572                 CLAMP_MIN(current_dim[1], 1);
573
574                 glViewport(0, 0, current_dim[0], current_dim[1]);
575
576                 /* bind next level for rendering but first restrict fetches only to previous level */
577                 GPU_texture_bind(tex, 0);
578                 glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, i-1);
579                 glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, i-1);
580                 GPU_texture_unbind(tex);
581
582                 glFramebufferTexture(GL_FRAMEBUFFER, attachment, GPU_texture_opengl_bindcode(tex), i);
583
584                 callback(userData, i);
585         }
586
587         glFramebufferTexture(GL_FRAMEBUFFER, attachment, 0, 0);
588
589         /* reset mipmap level range for the depth image */
590         GPU_texture_bind(tex, 0);
591         glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, 0);
592         glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, num_iter);
593         GPU_texture_unbind(tex);
594 }
595
596 /* GPUOffScreen */
597
598 struct GPUOffScreen {
599         GPUFrameBuffer *fb;
600         GPUTexture *color;
601         GPUTexture *depth;
602 };
603
604 GPUOffScreen *GPU_offscreen_create(int width, int height, int samples, char err_out[256])
605 {
606         GPUOffScreen *ofs;
607
608         ofs = MEM_callocN(sizeof(GPUOffScreen), "GPUOffScreen");
609
610         ofs->fb = GPU_framebuffer_create();
611         if (!ofs->fb) {
612                 GPU_offscreen_free(ofs);
613                 return NULL;
614         }
615
616         if (samples) {
617                 if (!GLEW_ARB_texture_multisample ||
618                     /* This is required when blitting from a multi-sampled buffers,
619                      * even though we're not scaling. */
620                     !GLEW_EXT_framebuffer_multisample_blit_scaled)
621                 {
622                         samples = 0;
623                 }
624         }
625
626         ofs->depth = GPU_texture_create_depth_multisample(width, height, samples, err_out);
627         if (!ofs->depth) {
628                 GPU_offscreen_free(ofs);
629                 return NULL;
630         }
631
632         if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->depth, 0, 0)) {
633                 GPU_offscreen_free(ofs);
634                 return NULL;
635         }
636
637         ofs->color = GPU_texture_create_2D_multisample(width, height, NULL, samples, err_out);
638         if (!ofs->color) {
639                 GPU_offscreen_free(ofs);
640                 return NULL;
641         }
642
643         if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->color, 0, 0)) {
644                 GPU_offscreen_free(ofs);
645                 return NULL;
646         }
647         
648         /* check validity at the very end! */
649         if (!GPU_framebuffer_check_valid(ofs->fb, err_out)) {
650                 GPU_offscreen_free(ofs);
651                 return NULL;            
652         }
653
654         GPU_framebuffer_restore();
655
656         return ofs;
657 }
658
659 void GPU_offscreen_free(GPUOffScreen *ofs)
660 {
661         if (ofs->fb)
662                 GPU_framebuffer_free(ofs->fb);
663         if (ofs->color)
664                 GPU_texture_free(ofs->color);
665         if (ofs->depth)
666                 GPU_texture_free(ofs->depth);
667         
668         MEM_freeN(ofs);
669 }
670
671 void GPU_offscreen_bind(GPUOffScreen *ofs, bool save)
672 {
673         glDisable(GL_SCISSOR_TEST);
674         if (save)
675                 GPU_texture_bind_as_framebuffer(ofs->color);
676         else {
677                 GPU_framebuffer_bind_no_save(ofs->fb, 0);
678         }
679 }
680
681 void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore)
682 {
683         if (restore)
684                 GPU_framebuffer_texture_unbind(ofs->fb, ofs->color);
685         GPU_framebuffer_restore();
686         glEnable(GL_SCISSOR_TEST);
687 }
688
689 void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels)
690 {
691         const int w = GPU_texture_width(ofs->color);
692         const int h = GPU_texture_height(ofs->color);
693
694         if (GPU_texture_target(ofs->color) == GL_TEXTURE_2D_MULTISAMPLE) {
695                 /* For a multi-sample texture,
696                  * we need to create an intermediate buffer to blit to,
697                  * before its copied using 'glReadPixels' */
698
699                 /* not needed since 'ofs' needs to be bound to the framebuffer already */
700 // #define USE_FBO_CTX_SWITCH
701
702                 GLuint fbo_blit = 0;
703                 GLuint tex_blit = 0;
704                 GLenum status;
705
706                 /* create texture for new 'fbo_blit' */
707                 glGenTextures(1, &tex_blit);
708                 if (!tex_blit) {
709                         goto finally;
710                 }
711
712                 glBindTexture(GL_TEXTURE_2D, tex_blit);
713                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, type, 0);
714
715 #ifdef USE_FBO_CTX_SWITCH
716                 /* read from multi-sample buffer */
717                 glBindFramebuffer(GL_READ_FRAMEBUFFER, ofs->color->fb->object);
718                 glFramebufferTexture2D(
719                         GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + ofs->color->fb_attachment,
720                         GL_TEXTURE_2D_MULTISAMPLE, ofs->color->bindcode, 0);
721                 status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);
722                 if (status != GL_FRAMEBUFFER_COMPLETE) {
723                         goto finally;
724                 }
725 #endif
726
727                 /* write into new single-sample buffer */
728                 glGenFramebuffers(1, &fbo_blit);
729                 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_blit);
730                 glFramebufferTexture2D(
731                         GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
732                         GL_TEXTURE_2D, tex_blit, 0);
733                 status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
734                 if (status != GL_FRAMEBUFFER_COMPLETE) {
735                         goto finally;
736                 }
737
738                 /* perform the copy */
739                 glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
740
741                 /* read the results */
742                 glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_blit);
743                 glReadPixels(0, 0, w, h, GL_RGBA, type, pixels);
744
745 #ifdef USE_FBO_CTX_SWITCH
746                 /* restore the original frame-bufer */
747                 glBindFramebuffer(GL_FRAMEBUFFER, ofs->color->fb->object);
748 #undef USE_FBO_CTX_SWITCH
749 #endif
750
751
752 finally:
753                 /* cleanup */
754                 if (tex_blit) {
755                         glDeleteTextures(1, &tex_blit);
756                 }
757                 if (fbo_blit) {
758                         glDeleteFramebuffers(1, &fbo_blit);
759                 }
760         }
761         else {
762                 glReadPixels(0, 0, w, h, GL_RGBA, type, pixels);
763         }
764 }
765
766 int GPU_offscreen_width(const GPUOffScreen *ofs)
767 {
768         return GPU_texture_width(ofs->color);
769 }
770
771 int GPU_offscreen_height(const GPUOffScreen *ofs)
772 {
773         return GPU_texture_height(ofs->color);
774 }
775
776 int GPU_offscreen_color_texture(const GPUOffScreen *ofs)
777 {
778         return GPU_texture_opengl_bindcode(ofs->color);
779 }
780
781 /* only to be used by viewport code! */
782 void GPU_offscreen_viewport_data_get(
783         GPUOffScreen *ofs,
784         GPUFrameBuffer **r_fb, GPUTexture **r_color, GPUTexture **r_depth)
785 {
786         *r_fb = ofs->fb;
787         *r_color = ofs->color;
788         *r_depth = ofs->depth;
789 }