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