1 /** @file gltarget.cpp  GL render target.
2  *
3  * Implementation does not use QGLFrameBufferObject because it does not allow
4  * attaching manually created textures.
5  *
6  * @authors Copyright (c) 2013-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
7  *
8  * @par License
9  * LGPL: http://www.gnu.org/licenses/lgpl.html
10  *
11  * <small>This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 3 of the License, or (at your
14  * option) any later version. This program is distributed in the hope that it
15  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
17  * General Public License for more details. You should have received a copy of
18  * the GNU Lesser General Public License along with this program; if not, see:
19  * http://www.gnu.org/licenses</small>
20  */
21 
22 #include "de/GLFramebuffer"
23 #include "de/GLTexture"
24 #include "de/GLState"
25 #include "de/GLInfo"
26 #include "de/GLWindow"
27 
28 #include <de/Asset>
29 #include <de/LogBuffer>
30 
31 namespace de {
32 
33 static Vector2ui const nullSize;
34 static GLuint defaultFramebuffer = 0;
35 
DENG2_PIMPL(GLFramebuffer)36 DENG2_PIMPL(GLFramebuffer),
37 DENG2_OBSERVES(Asset, Deletion)
38 {
39     enum AttachmentId {
40         ColorBuffer,
41         DepthBuffer,
42         StencilBuffer,
43         DepthStencilBuffer,
44         MAX_ATTACHMENTS
45     };
46 
47     static AttachmentId attachmentToId(GLenum atc)
48     {
49         switch (atc)
50         {
51         case GL_COLOR_ATTACHMENT0:
52             return ColorBuffer;
53 
54         case GL_DEPTH_ATTACHMENT:
55             return DepthBuffer;
56 
57         case GL_STENCIL_ATTACHMENT:
58             return StencilBuffer;
59 
60         case GL_DEPTH_STENCIL_ATTACHMENT:
61             return DepthStencilBuffer;
62 
63         default:
64             DENG2_ASSERT(false);
65             break;
66         }
67         return ColorBuffer; // should not be reached
68     }
69 
70     static GLenum flagsToGLAttachment(Flags const &flags)
71     {
72         DENG2_ASSERT(!flags.testFlag(ColorDepth));
73         DENG2_ASSERT(!flags.testFlag(ColorDepthStencil));
74 
75         return flags == Color?   GL_COLOR_ATTACHMENT0  :
76                flags == Depth?   GL_DEPTH_ATTACHMENT   :
77                flags == Stencil? GL_STENCIL_ATTACHMENT :
78                                  GL_DEPTH_STENCIL_ATTACHMENT;
79     }
80 
81     GLuint      fbo;
82     GLuint      renderBufs[MAX_ATTACHMENTS];
83     GLTexture * bufTextures[MAX_ATTACHMENTS];
84     Flags       flags;
85     Flags       textureAttachment; ///< Where to attach @a texture.
86     GLTexture * texture;
87     Vector2ui   size;
88     Vector4f    clearColor;
89     Rectangleui activeRect; ///< Initially null.
90     int         sampleCount;
91 
92     Impl(Public *i)
93         : Base(i)
94         , fbo(0)
95         , flags(DefaultFlags)
96         , textureAttachment(NoAttachments)
97         , texture(0)
98         , sampleCount(0)
99     {
100         zap(renderBufs);
101         zap(bufTextures);
102     }
103 
104     Impl(Public *i, Flags const &texAttachment, GLTexture &colorTexture, Flags const &otherAtm)
105         : Base(i)
106         , fbo(0)
107         , flags(texAttachment | otherAtm)
108         , textureAttachment(texAttachment)
109         , texture(&colorTexture)
110         , size(colorTexture.size())
111         , sampleCount(0)
112     {
113         zap(renderBufs);
114         zap(bufTextures);
115     }
116 
117     Impl(Public *i, Vector2ui const &targetSize, Flags const &fboFlags)
118         : Base(i)
119         , fbo(0)
120         , flags(fboFlags)
121         , textureAttachment(NoAttachments)
122         , texture(0)
123         , size(targetSize)
124         , sampleCount(0)
125     {
126         zap(renderBufs);
127         zap(bufTextures);
128     }
129 
130     ~Impl()
131     {
132         release();
133     }
134 
135     bool isDefault() const
136     {
137         return !texture && size == nullSize;
138     }
139 
140     static AttachmentId flagsToAttachmentId(Flags const &flags)
141     {
142         if (flags == Color)
143         {
144             return ColorBuffer;
145         }
146         if (flags == Depth)
147         {
148             return DepthBuffer;
149         }
150         if (flags == Stencil)
151         {
152             return StencilBuffer;
153         }
154         if (flags == DepthStencil)
155         {
156             return DepthStencilBuffer;
157         }
158         DENG2_ASSERT(0!="Invalid attachment flags");
159         return MAX_ATTACHMENTS;
160     }
161 
162     GLTexture *bufferTexture(Flags const &flags) const
163     {
164         auto attachId = flagsToAttachmentId(flags);
165         if (attachId != MAX_ATTACHMENTS)
166         {
167             return bufTextures[attachId];
168         }
169         return nullptr;
170     }
171 
172     GLuint renderBuffer(Flags const &flags) const
173     {
174         auto attachId = flagsToAttachmentId(flags);
175         if (attachId != MAX_ATTACHMENTS)
176         {
177             return renderBufs[attachId];
178         }
179         return 0;
180     }
181 
182     void allocFBO()
183     {
184         if (isDefault() || fbo) return;
185 
186         LIBGUI_GL.glGenFramebuffers(1, &fbo);
187         LIBGUI_GL.glBindFramebuffer(GL_FRAMEBUFFER, fbo);
188 
189         LIBGUI_ASSERT_GL_OK();
190         LOG_GL_XVERBOSE("Creating FBO %i", fbo);
191     }
192 
193     void attachTexture(GLTexture &tex, GLenum attachment, int level = 0)
194     {
195         DENG2_ASSERT(tex.isReady());
196 
197         LOG_GL_XVERBOSE("FBO %i: glTex %i (level %i) => attachment %i",
198                         fbo << tex.glName() << level << attachmentToId(attachment));
199 
200         LIBGUI_GL.glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex.glName(), level);
201         LIBGUI_ASSERT_GL_OK();
202 
203         bufTextures[attachmentToId(attachment)] = &tex;
204     }
205 
206     void attachRenderbuffer(AttachmentId id, GLenum type, GLenum attachment)
207     {
208         DENG2_ASSERT(size != Vector2ui(0, 0));
209 
210         LIBGUI_GL.glGenRenderbuffers(1, &renderBufs[id]);
211         LIBGUI_GL.glBindRenderbuffer(GL_RENDERBUFFER, renderBufs[id]);
212         LIBGUI_ASSERT_GL_OK();
213 
214 #if !defined(DENG_OPENGL_ES)
215         if (sampleCount > 1)
216         {
217             if (GLInfo::extensions().NV_framebuffer_multisample_coverage)
218             {
219                 LOG_GL_VERBOSE("FBO %i: renderbuffer %ix%i is multisampled with %i CSAA samples => attachment %i")
220                         << fbo << size.x << size.y << sampleCount
221                         << attachmentToId(attachment);
222 
223                 GLInfo::NV_framebuffer_multisample_coverage()->glRenderbufferStorageMultisampleCoverageNV(
224                         GL_RENDERBUFFER, 8, sampleCount, type, size.x, size.y);
225                 LIBGUI_ASSERT_GL_OK();
226             }
227             else
228             {
229                 LOG_GL_VERBOSE("FBO %i: renderbuffer %ix%i is multisampled with %i samples => attachment %i")
230                         << fbo << size.x << size.y << sampleCount
231                         << attachmentToId(attachment);
232 
233                 //DENG2_ASSERT(GLInfo::extensions().EXT_framebuffer_multisample);
234                 LIBGUI_GL.glRenderbufferStorageMultisample(
235                         GL_RENDERBUFFER, sampleCount, type, size.x, size.y);
236                 LIBGUI_ASSERT_GL_OK();
237             }
238         }
239         else
240 #endif
241         {
242             LIBGUI_GL.glRenderbufferStorage(GL_RENDERBUFFER, type, size.x, size.y);
243             LIBGUI_ASSERT_GL_OK();
244         }
245 
246         LIBGUI_GL.glFramebufferRenderbuffer(
247                     GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderBufs[id]);
248         LIBGUI_ASSERT_GL_OK();
249     }
250 
251     void alloc()
252     {
253         allocFBO();
254 
255         if (texture)
256         {
257             // The texture's attachment point must be unambiguously defined.
258             DENG2_ASSERT(textureAttachment == Color   ||
259                          textureAttachment == Depth   ||
260                          textureAttachment == Stencil ||
261                          textureAttachment == DepthStencil);
262 
263             attachTexture(*texture,
264                           textureAttachment == Color?   GL_COLOR_ATTACHMENT0  :
265                           textureAttachment == Depth?   GL_DEPTH_ATTACHMENT   :
266                           textureAttachment == Stencil? GL_STENCIL_ATTACHMENT :
267                                                         GL_DEPTH_STENCIL_ATTACHMENT);
268         }
269 
270         if (size != nullSize) // A non-default target: size must be specified.
271         {
272             allocRenderBuffers();
273         }
274 
275         validate();
276     }
277 
278     void allocRenderBuffers()
279     {
280         DENG2_ASSERT(size != nullSize);
281 
282         // Fill in all the other requested attachments.
283         if (flags.testFlag(Color) && !textureAttachment.testFlag(Color))
284         {
285             /// @todo Note that for GLES, GL_RGBA8 is not supported (without an extension).
286             LOG_GL_VERBOSE("FBO %i: color renderbuffer %s") << fbo << size.asText();
287             attachRenderbuffer(ColorBuffer, GL_RGBA8, GL_COLOR_ATTACHMENT0);
288         }
289 
290         allocDepthStencilRenderBuffers();
291 
292         LIBGUI_GL.glBindRenderbuffer(GL_RENDERBUFFER, 0);
293     }
294 
295     void allocDepthStencilRenderBuffers()
296     {
297         if (flags.testFlag(DepthStencil) && !flags.testFlag(SeparateDepthAndStencil) &&
298             (!texture || textureAttachment == Color))
299         {
300             // We can use a combined depth/stencil buffer.
301             LOG_GL_VERBOSE("FBO %i: depth+stencil renderbuffer %s") << fbo << size.asText();
302             attachRenderbuffer(DepthStencilBuffer, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL_ATTACHMENT);
303         }
304         else
305         {
306             // Separate depth and stencil, then.
307             if (flags.testFlag(Depth) && !textureAttachment.testFlag(Depth))
308             {
309                 LOG_GL_VERBOSE("FBO %i: depth renderbuffer %s") << fbo << size.asText();
310                 attachRenderbuffer(DepthBuffer, GL_DEPTH_COMPONENT, GL_DEPTH_ATTACHMENT);
311             }
312 #if defined (DENG_OPENGL)
313             if (flags.testFlag(Stencil) && !textureAttachment.testFlag(Stencil))
314             {
315                 LOG_GL_VERBOSE("FBO %i: stencil renderbuffer %s") << fbo << size.asText();
316                 attachRenderbuffer(StencilBuffer, GL_STENCIL_INDEX, GL_STENCIL_ATTACHMENT);
317             }
318 #endif
319         }
320     }
321 
322     void releaseRenderBuffers()
323     {
324         LIBGUI_GL.glDeleteRenderbuffers(MAX_ATTACHMENTS, renderBufs);
325         zap(renderBufs);
326         zap(bufTextures);
327     }
328 
329     void release()
330     {
331         self().setState(NotReady);
332         if (fbo)
333         {
334             releaseRenderBuffers();
335             LIBGUI_GL.glDeleteFramebuffers(1, &fbo);
336             fbo = 0;
337         }
338         zap(bufTextures);
339         texture = 0;
340         size = nullSize;
341     }
342 
343     void releaseAndReset()
344     {
345         release();
346 
347         textureAttachment = NoAttachments;
348         flags = NoAttachments;
349         sampleCount = 0;
350     }
351 
352     void releaseRenderBuffer(AttachmentId id)
353     {
354         if (renderBufs[id])
355         {
356             LIBGUI_GL.glDeleteRenderbuffers(1, &renderBufs[id]);
357             renderBufs[id] = 0;
358         }
359     }
360 
361     void resizeRenderBuffers(Size const &newSize)
362     {
363         size = newSize;
364 
365         releaseRenderBuffers();
366         allocRenderBuffers();
367     }
368 
369     void replace(GLenum attachment, GLTexture &newTexture)
370     {
371         DENG2_ASSERT(self().isReady()); // must already be inited
372         DENG2_ASSERT(bufTextures[attachmentToId(attachment)] != 0); // must have an attachment already
373 
374         LIBGUI_GL.glBindFramebuffer(GL_FRAMEBUFFER, fbo);
375         attachTexture(newTexture, attachment);
376 
377         validate();
378     }
379 
380     void replaceWithNewRenderBuffer(Flags const &attachment)
381     {
382         DENG2_ASSERT(self().isReady()); // must already be inited
383         if (attachment == DepthStencil) // this supported only
384         {
385             LIBGUI_GL.glBindFramebuffer(GL_FRAMEBUFFER, fbo);
386 
387             allocDepthStencilRenderBuffers();
388 
389             validate();
390         }
391     }
392 
393     void replaceWithExistingRenderBuffer(Flags const &attachment, GLuint renderBufId)
394     {
395         DENG2_ASSERT(self().isReady());       // must already be inited
396 
397         auto id = flagsToAttachmentId(attachment);
398 
399         renderBufs[id] = renderBufId;
400 
401         LIBGUI_GL.glBindFramebuffer(GL_FRAMEBUFFER, fbo);
402         LIBGUI_GL.glFramebufferRenderbuffer(
403                     GL_FRAMEBUFFER, flagsToGLAttachment(attachment),
404                     GL_RENDERBUFFER, renderBufs[id]);
405 
406         LIBGUI_ASSERT_GL_OK();
407 
408         // Restore previous target.
409         GLState::current().target().glBind();
410     }
411 
412     void validate()
413     {
414         if (isDefault())
415         {
416             self().setState(Ready);
417             return;
418         }
419 
420         DENG2_ASSERT(fbo != 0);
421 
422         LIBGUI_GL.glBindFramebuffer(GL_FRAMEBUFFER, fbo);
423         GLenum status = LIBGUI_GL.glCheckFramebufferStatus(GL_FRAMEBUFFER);
424         if (status != GL_FRAMEBUFFER_COMPLETE)
425         {
426             releaseAndReset();
427 
428             throw ConfigError("GLFramebuffer::validate",
429                 status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT? "Incomplete attachments" :
430                 status == GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS? "Mismatch with dimensions" :
431                 status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT? "No images attached" :
432                                                                         QString("Unsupported (0x%1)").arg(status, 0, 16));
433         }
434         self().setState(Ready);
435 
436         GLState::current().target().glBind();
437         LIBGUI_ASSERT_GL_OK();
438     }
439 
440     void assetBeingDeleted(Asset &asset)
441     {
442         if (texture == &asset)
443         {
444             release();
445         }
446     }
447 };
448 
setDefaultFramebuffer(GLuint defaultFBO)449 void GLFramebuffer::setDefaultFramebuffer(GLuint defaultFBO)
450 {
451     defaultFramebuffer = defaultFBO;
452 }
453 
GLFramebuffer()454 GLFramebuffer::GLFramebuffer() : d(new Impl(this))
455 {
456     setState(Ready);
457 }
458 
GLFramebuffer(GLTexture & colorTarget,Flags const & otherAttachments)459 GLFramebuffer::GLFramebuffer(GLTexture &colorTarget, Flags const &otherAttachments)
460     : d(new Impl(this, Color, colorTarget, otherAttachments))
461 {
462     LOG_AS("GLFramebuffer");
463     d->alloc();
464 }
465 
GLFramebuffer(Flags const & attachment,GLTexture & texture,Flags const & otherAttachments)466 GLFramebuffer::GLFramebuffer(Flags const &attachment, GLTexture &texture, Flags const &otherAttachments)
467     : d(new Impl(this, attachment, texture, otherAttachments))
468 {
469     LOG_AS("GLFramebuffer");
470     d->alloc();
471 }
472 
GLFramebuffer(Vector2ui const & size,Flags const & flags)473 GLFramebuffer::GLFramebuffer(Vector2ui const &size, Flags const &flags)
474     : d(new Impl(this, size, flags))
475 {
476     LOG_AS("GLFramebuffer");
477     d->alloc();
478 }
479 
flags() const480 GLFramebuffer::Flags GLFramebuffer::flags() const
481 {
482     return d->flags;
483 }
484 
markAsChanged()485 void GLFramebuffer::markAsChanged()
486 {
487     d->flags |= Changed;
488 }
489 
configure()490 void GLFramebuffer::configure()
491 {
492     LOG_AS("GLFramebuffer");
493 
494     d->releaseAndReset();
495     setState(Ready);
496 }
497 
configure(Vector2ui const & size,Flags const & flags,int sampleCount)498 void GLFramebuffer::configure(Vector2ui const &size, Flags const &flags, int sampleCount)
499 {
500     LOG_AS("GLFramebuffer");
501 
502     d->releaseAndReset();
503 
504     d->flags = flags;
505     d->size = size;
506 #if defined (DENG_OPENGL_ES)
507     DENG2_UNUSED(sampleCount);
508 #else
509     d->sampleCount = (sampleCount > 1? sampleCount : 0);
510 #endif
511 
512     d->allocFBO();
513     d->allocRenderBuffers();
514     d->validate();
515 
516     LIBGUI_ASSERT_GL_OK();
517 }
518 
configure(GLTexture * colorTex,GLTexture * depthStencilTex)519 void GLFramebuffer::configure(GLTexture *colorTex, GLTexture *depthStencilTex)
520 {
521     DENG2_ASSERT(colorTex || depthStencilTex);
522 
523     LOG_AS("GLFramebuffer");
524 
525     d->releaseAndReset();
526 
527     d->flags = ColorDepthStencil;
528     d->size = (colorTex? colorTex->size() : depthStencilTex->size());
529 
530     d->allocFBO();
531 
532     // The color attachment.
533     if (colorTex)
534     {
535         DENG2_ASSERT(colorTex->isReady());
536         DENG2_ASSERT(d->size == colorTex->size());
537         d->attachTexture(*colorTex, GL_COLOR_ATTACHMENT0);
538     }
539     else
540     {
541         d->attachRenderbuffer(Impl::ColorBuffer, GL_RGBA8, GL_COLOR_ATTACHMENT0);
542     }
543 
544     // The depth attachment.
545     if (depthStencilTex)
546     {
547         DENG2_ASSERT(depthStencilTex->isReady());
548         DENG2_ASSERT(d->size == depthStencilTex->size());
549         d->attachTexture(*depthStencilTex, GL_DEPTH_STENCIL_ATTACHMENT);
550     }
551     else
552     {
553         d->attachRenderbuffer(Impl::DepthStencilBuffer, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL_ATTACHMENT);
554     }
555 
556     LIBGUI_ASSERT_GL_OK();
557 
558     d->validate();
559 }
560 
configure(Flags const & attachment,GLTexture & texture,Flags const & otherAttachments)561 void GLFramebuffer::configure(Flags const &attachment, GLTexture &texture, Flags const &otherAttachments)
562 {
563     LOG_AS("GLFramebuffer");
564 
565     d->releaseAndReset();
566 
567     // Set new configuration.
568     d->texture = &texture;
569     d->textureAttachment = attachment;
570     d->flags = attachment | otherAttachments;
571     d->size = texture.size();
572 
573     d->alloc();
574 }
575 
deinit()576 void GLFramebuffer::deinit()
577 {
578     LOG_AS("GLFramebuffer");
579     d->releaseAndReset();
580 }
581 
glBind() const582 void GLFramebuffer::glBind() const
583 {
584     LIBGUI_ASSERT_GL_OK();
585     DENG2_ASSERT(isReady());
586     if (!isReady())
587     {
588         //qWarning() << "GLFramebuffer: Trying to bind a not-ready FBO";
589         return;
590     }
591 
592     GLuint const fbo = (d->fbo? d->fbo : defaultFramebuffer);
593 
594     LIBGUI_GL.glBindFramebuffer(GL_FRAMEBUFFER, fbo);
595     LIBGUI_ASSERT_GL_OK();
596 }
597 
glRelease() const598 void GLFramebuffer::glRelease() const
599 {
600     LIBGUI_ASSERT_GL_OK();
601 
602     LIBGUI_GL.glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer); // both read and write FBOs
603     LIBGUI_ASSERT_GL_OK();
604 }
605 
toImage() const606 QImage GLFramebuffer::toImage() const
607 {
608     if (!d->fbo)
609     {
610         return GLWindow::main().grabImage();
611     }
612     else if (d->flags & Color)
613     {
614         // Read the contents of the color attachment.
615         Size imgSize = size();
616         QImage img(QSize(imgSize.x, imgSize.y), QImage::Format_ARGB32);
617         LIBGUI_GL.glBindFramebuffer(GL_READ_FRAMEBUFFER, d->fbo);
618         LIBGUI_GL.glPixelStorei(GL_PACK_ALIGNMENT, 4);
619         LIBGUI_GL.glReadPixels(0, 0, imgSize.x, imgSize.y, GL_BGRA, GL_UNSIGNED_BYTE,
620                                (GLvoid *) img.constBits());
621         // Restore the stack's target.
622         GLState::current().target().glBind();
623         return img.mirrored(false, true);
624     }
625     return QImage();
626 }
627 
setClearColor(Vector4f const & color)628 void GLFramebuffer::setClearColor(Vector4f const &color)
629 {
630     d->clearColor = color;
631 }
632 
clear(Flags const & attachments)633 void GLFramebuffer::clear(Flags const &attachments)
634 {
635     DENG2_ASSERT(isReady());
636 
637     markAsChanged();
638 
639     GLState::current().apply();
640     glBind();
641 
642     // Only clear what we have.
643     Flags which = attachments & d->flags;
644 
645     LIBGUI_GL.glClearColor(d->clearColor.x, d->clearColor.y, d->clearColor.z, d->clearColor.w);
646     LIBGUI_GL.glClear((which & Color?   GL_COLOR_BUFFER_BIT   : 0) |
647                       (which & Depth?   GL_DEPTH_BUFFER_BIT   : 0) |
648                       (which & Stencil? GL_STENCIL_BUFFER_BIT : 0));
649 
650     GLState::current().target().glBind();
651 }
652 
resize(Size const & size)653 void GLFramebuffer::resize(Size const &size)
654 {
655     // The default target resizes itself automatically with the canvas.
656     if (d->size == size || d->isDefault()) return;
657 
658     LIBGUI_GL.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo);
659     if (d->texture)
660     {
661         d->texture->setUndefinedImage(size, d->texture->imageFormat());
662     }
663     d->resizeRenderBuffers(size);
664     GLState::current().target().glBind();
665 }
666 
attachedTexture(Flags const & attachment) const667 GLTexture *GLFramebuffer::attachedTexture(Flags const &attachment) const
668 {
669     return d->bufferTexture(attachment);
670 }
671 
attachedRenderBuffer(Flags const & attachment) const672 GLuint GLFramebuffer::attachedRenderBuffer(Flags const &attachment) const
673 {
674     return d->renderBuffer(attachment);
675 }
676 
replaceAttachment(Flags const & attachment,GLTexture & texture)677 void GLFramebuffer::replaceAttachment(Flags const &attachment, GLTexture &texture)
678 {
679     d->replace(d->flagsToGLAttachment(attachment), texture);
680 }
681 
replaceAttachment(Flags const & attachment,GLuint renderBufferId)682 void GLFramebuffer::replaceAttachment(Flags const &attachment, GLuint renderBufferId)
683 {
684     d->replaceWithExistingRenderBuffer(attachment, renderBufferId);
685 }
686 
replaceWithNewRenderBuffer(Flags const & attachment)687 void GLFramebuffer::replaceWithNewRenderBuffer(Flags const &attachment)
688 {
689     d->replaceWithNewRenderBuffer(attachment);
690 }
691 
releaseAttachment(Flags const & attachment)692 void GLFramebuffer::releaseAttachment(Flags const &attachment)
693 {
694     d->releaseRenderBuffer(d->flagsToAttachmentId(attachment));
695 }
696 
blit(GLFramebuffer & dest,Flags const & attachments,gl::Filter filtering) const697 void GLFramebuffer::blit(GLFramebuffer &dest, Flags const &attachments, gl::Filter filtering) const
698 {
699     LIBGUI_ASSERT_GL_OK();
700 
701     LIBGUI_GL.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dest.glName());
702     LIBGUI_ASSERT_GL_OK();
703 
704 #if defined (DENG_HAVE_BLIT_FRAMEBUFFER)
705 
706     LIBGUI_GL.glBindFramebuffer(GL_READ_FRAMEBUFFER, glName());
707     LIBGUI_ASSERT_GL_OK();
708 
709     Flags common = d->flags & dest.flags() & attachments;
710 
711     LIBGUI_GL.glBlitFramebuffer(
712                 0, 0, size().x, size().y,
713                 0, 0, dest.size().x, dest.size().y,
714                 (common.testFlag(Color)?   GL_COLOR_BUFFER_BIT   : 0) |
715                 (common.testFlag(Depth)?   GL_DEPTH_BUFFER_BIT   : 0) |
716                 (common.testFlag(Stencil)? GL_STENCIL_BUFFER_BIT : 0),
717                 filtering == gl::Nearest? GL_NEAREST : GL_LINEAR);
718     LIBGUI_ASSERT_GL_OK();
719 
720     //GLInfo::EXT_framebuffer_object()->glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
721     //LIBGUI_ASSERT_GL_OK();
722 
723 #else
724 
725     qDebug() << "need to implement glBlitFramebuffer:" << glName() << "->" << dest.glName();
726     qDebug() << "\t- from texture:" << attachedTexture(Color);
727     qDebug() << "\t- to texture:" << dest.attachedTexture(Color);
728 
729 #endif
730 
731     dest.markAsChanged();
732 
733     GLState::current().target().glBind();
734 }
735 
blit(gl::Filter filtering) const736 void GLFramebuffer::blit(gl::Filter filtering) const
737 {
738     LIBGUI_ASSERT_GL_OK();
739 
740     //qDebug() << "Blitting from" << glName() << "to" << defaultFramebuffer << size().asText();
741 
742 #if defined (DENG_HAVE_BLIT_FRAMEBUFFER)
743 
744     LIBGUI_GL.glBindFramebuffer(GL_READ_FRAMEBUFFER, glName());
745     LIBGUI_GL.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, defaultFramebuffer);
746 
747     LIBGUI_GL.glBlitFramebuffer(
748                 0, 0, size().x, size().y,
749                 0, 0, size().x, size().y,
750                 GL_COLOR_BUFFER_BIT,
751                 filtering == gl::Nearest? GL_NEAREST : GL_LINEAR);
752 
753     LIBGUI_ASSERT_GL_OK();
754 
755 #else
756 
757     qDebug() << "need to implement glBlitFramebuffer:" << glName() << "-> 0";
758     qDebug() << "\t- texture:" << attachedTexture(Color);
759 
760 #endif
761 
762     GLState::current().target().glBind();
763 }
764 
glName() const765 GLuint GLFramebuffer::glName() const
766 {
767     return d->fbo? d->fbo : defaultFramebuffer;
768 }
769 
size() const770 GLFramebuffer::Size GLFramebuffer::size() const
771 {
772     if (d->texture)
773     {
774         return d->texture->size();
775     }
776     else if (d->size != nullSize)
777     {
778         return d->size;
779     }
780     //qDebug() << "FBO" << d->fbo << "size" << GLWindow::main().canvas().size().asText();
781     return GLWindow::main().pixelSize();
782 }
783 
setActiveRect(Rectangleui const & rect,bool applyGLState)784 void GLFramebuffer::setActiveRect(Rectangleui const &rect, bool applyGLState)
785 {
786     d->activeRect = rect;
787     if (applyGLState)
788     {
789         // Forcibly update viewport and scissor (and other GL state).
790         GLState::considerNativeStateUndefined();
791         GLState::current().apply();
792     }
793 }
794 
unsetActiveRect(bool applyGLState)795 void GLFramebuffer::unsetActiveRect(bool applyGLState)
796 {
797     setActiveRect(Rectangleui(), applyGLState);
798 }
799 
activeRectScale() const800 Vector2f GLFramebuffer::activeRectScale() const
801 {
802     if (!hasActiveRect())
803     {
804         return Vector2f(1, 1);
805     }
806     return Vector2f(d->activeRect.size()) / size();
807 }
808 
activeRectNormalizedOffset() const809 Vector2f GLFramebuffer::activeRectNormalizedOffset() const
810 {
811     if (!hasActiveRect())
812     {
813         return Vector2f(0, 0);
814     }
815     return Vector2f(d->activeRect.topLeft) / size();
816 }
817 
scaleToActiveRect(Rectangleui const & rectInTarget) const818 Rectangleui GLFramebuffer::scaleToActiveRect(Rectangleui const &rectInTarget) const
819 {
820     // If no sub rectangle is defined, do nothing.
821     if (!hasActiveRect())
822     {
823         return rectInTarget;
824     }
825 
826     Vector2f const scaling = activeRectScale();
827 
828     return Rectangleui(d->activeRect.left()  + scaling.x * rectInTarget.left(),
829                        d->activeRect.top()   + scaling.y * rectInTarget.top(),
830                        rectInTarget.width()  * scaling.x,
831                        rectInTarget.height() * scaling.y);
832 }
833 
activeRect() const834 Rectangleui const &GLFramebuffer::activeRect() const
835 {
836     return d->activeRect;
837 }
838 
hasActiveRect() const839 bool GLFramebuffer::hasActiveRect() const
840 {
841     return !d->activeRect.isNull();
842 }
843 
rectInUse() const844 Rectangleui GLFramebuffer::rectInUse() const
845 {
846     if (hasActiveRect())
847     {
848         return activeRect();
849     }
850     return Rectangleui::fromSize(size());
851 }
852 
853 } // namespace de
854