1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qopenglframebufferobject.h"
41#include "qopenglframebufferobject_p.h"
42
43#include <qdebug.h>
44#include <private/qopengl_p.h>
45#include <private/qopenglcontext_p.h>
46#include <private/qopenglextensions_p.h>
47#include <private/qfont_p.h>
48
49#include <qwindow.h>
50#include <qimage.h>
51#include <QtCore/qbytearray.h>
52
53QT_BEGIN_NAMESPACE
54
55#ifndef QT_NO_DEBUG
56#define QT_RESET_GLERROR() \
57{ \
58 while (true) {\
59 GLenum error = QOpenGLContext::currentContext()->functions()->glGetError(); \
60 if (error == GL_NO_ERROR || error == GL_CONTEXT_LOST) \
61 break; \
62 } \
63}
64#define QT_CHECK_GLERROR() \
65{ \
66 GLenum err = QOpenGLContext::currentContext()->functions()->glGetError(); \
67 if (err != GL_NO_ERROR && err != GL_CONTEXT_LOST) { \
68 qDebug("[%s line %d] OpenGL Error: %d", \
69 __FILE__, __LINE__, (int)err); \
70 } \
71}
72#else
73#define QT_RESET_GLERROR() {}
74#define QT_CHECK_GLERROR() {}
75#endif
76
77#ifndef GL_MAX_SAMPLES
78#define GL_MAX_SAMPLES 0x8D57
79#endif
80
81#ifndef GL_RENDERBUFFER_SAMPLES
82#define GL_RENDERBUFFER_SAMPLES 0x8CAB
83#endif
84
85#ifndef GL_DEPTH24_STENCIL8
86#define GL_DEPTH24_STENCIL8 0x88F0
87#endif
88
89#ifndef GL_DEPTH_COMPONENT24
90#define GL_DEPTH_COMPONENT24 0x81A6
91#endif
92
93#ifndef GL_DEPTH_COMPONENT24_OES
94#define GL_DEPTH_COMPONENT24_OES 0x81A6
95#endif
96
97#ifndef GL_READ_FRAMEBUFFER
98#define GL_READ_FRAMEBUFFER 0x8CA8
99#endif
100
101#ifndef GL_DRAW_FRAMEBUFFER
102#define GL_DRAW_FRAMEBUFFER 0x8CA9
103#endif
104
105#ifndef GL_RGB8
106#define GL_RGB8 0x8051
107#endif
108
109#ifndef GL_RGB10
110#define GL_RGB10 0x8052
111#endif
112
113#ifndef GL_RGB16
114#define GL_RGB16 0x8054
115#endif
116
117#ifndef GL_RGBA8
118#define GL_RGBA8 0x8058
119#endif
120
121#ifndef GL_RGB10_A2
122#define GL_RGB10_A2 0x8059
123#endif
124
125#ifndef GL_RGBA16
126#define GL_RGBA16 0x805B
127#endif
128
129#ifndef GL_BGRA
130#define GL_BGRA 0x80E1
131#endif
132
133#ifndef GL_UNSIGNED_INT_8_8_8_8_REV
134#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
135#endif
136
137#ifndef GL_UNSIGNED_INT_2_10_10_10_REV
138#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
139#endif
140
141#ifndef GL_CONTEXT_LOST
142#define GL_CONTEXT_LOST 0x0507
143#endif
144
145#ifndef GL_DEPTH_STENCIL_ATTACHMENT
146#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
147#endif
148
149#ifndef GL_DEPTH_STENCIL
150#define GL_DEPTH_STENCIL 0x84F9
151#endif
152
153
154
155/*!
156 \class QOpenGLFramebufferObjectFormat
157 \brief The QOpenGLFramebufferObjectFormat class specifies the format of an OpenGL
158 framebuffer object.
159 \inmodule QtGui
160
161 \since 5.0
162
163 \ingroup painting-3D
164
165 A framebuffer object has several characteristics:
166 \list
167 \li \l{setSamples()}{Number of samples per pixels.}
168 \li \l{setAttachment()}{Depth and/or stencil attachments.}
169 \li \l{setTextureTarget()}{Texture target.}
170 \li \l{setInternalTextureFormat()}{Internal texture format.}
171 \endlist
172
173 Note that the desired attachments or number of samples per pixels might not
174 be supported by the hardware driver. Call QOpenGLFramebufferObject::format()
175 after creating a QOpenGLFramebufferObject to find the exact format that was
176 used to create the frame buffer object.
177
178 \sa QOpenGLFramebufferObject
179*/
180
181/*!
182 \internal
183*/
184void QOpenGLFramebufferObjectFormat::detach()
185{
186 if (d->ref.loadRelaxed() != 1) {
187 QOpenGLFramebufferObjectFormatPrivate *newd
188 = new QOpenGLFramebufferObjectFormatPrivate(d);
189 if (!d->ref.deref())
190 delete d;
191 d = newd;
192 }
193}
194
195/*!
196 Creates a QOpenGLFramebufferObjectFormat object for specifying
197 the format of an OpenGL framebuffer object.
198
199 By default the format specifies a non-multisample framebuffer object with no
200 depth/stencil attachments, texture target \c GL_TEXTURE_2D, and internal format \c GL_RGBA8.
201 On OpenGL/ES systems, the default internal format is \c GL_RGBA.
202
203 \sa samples(), attachment(), internalTextureFormat()
204*/
205
206QOpenGLFramebufferObjectFormat::QOpenGLFramebufferObjectFormat()
207{
208 d = new QOpenGLFramebufferObjectFormatPrivate;
209}
210
211/*!
212 Constructs a copy of \a other.
213*/
214
215QOpenGLFramebufferObjectFormat::QOpenGLFramebufferObjectFormat(const QOpenGLFramebufferObjectFormat &other)
216{
217 d = other.d;
218 d->ref.ref();
219}
220
221/*!
222 Assigns \a other to this object.
223*/
224
225QOpenGLFramebufferObjectFormat &QOpenGLFramebufferObjectFormat::operator=(const QOpenGLFramebufferObjectFormat &other)
226{
227 if (d != other.d) {
228 other.d->ref.ref();
229 if (!d->ref.deref())
230 delete d;
231 d = other.d;
232 }
233 return *this;
234}
235
236/*!
237 Destroys the QOpenGLFramebufferObjectFormat.
238*/
239QOpenGLFramebufferObjectFormat::~QOpenGLFramebufferObjectFormat()
240{
241 if (!d->ref.deref())
242 delete d;
243}
244
245/*!
246 Sets the number of samples per pixel for a multisample framebuffer object
247 to \a samples. The default sample count of 0 represents a regular
248 non-multisample framebuffer object.
249
250 If the desired amount of samples per pixel is not supported by the hardware
251 then the maximum number of samples per pixel will be used. Note that
252 multisample framebuffer objects cannot be bound as textures. Also, the
253 \c{GL_EXT_framebuffer_multisample} extension is required to create a
254 framebuffer with more than one sample per pixel.
255
256 \sa samples()
257*/
258void QOpenGLFramebufferObjectFormat::setSamples(int samples)
259{
260 detach();
261 d->samples = samples;
262}
263
264/*!
265 Returns the number of samples per pixel if a framebuffer object
266 is a multisample framebuffer object. Otherwise, returns 0.
267 The default value is 0.
268
269 \sa setSamples()
270*/
271int QOpenGLFramebufferObjectFormat::samples() const
272{
273 return d->samples;
274}
275
276/*!
277 Enables mipmapping if \a enabled is true; otherwise disables it.
278
279 Mipmapping is disabled by default.
280
281 If mipmapping is enabled, additional memory will be allocated for
282 the mipmap levels. The mipmap levels can be updated by binding the
283 texture and calling glGenerateMipmap(). Mipmapping cannot be enabled
284 for multisampled framebuffer objects.
285
286 \sa mipmap(), QOpenGLFramebufferObject::texture()
287*/
288void QOpenGLFramebufferObjectFormat::setMipmap(bool enabled)
289{
290 detach();
291 d->mipmap = enabled;
292}
293
294/*!
295 Returns \c true if mipmapping is enabled.
296
297 \sa setMipmap()
298*/
299bool QOpenGLFramebufferObjectFormat::mipmap() const
300{
301 return d->mipmap;
302}
303
304/*!
305 Sets the attachment configuration of a framebuffer object to \a attachment.
306
307 \sa attachment()
308*/
309void QOpenGLFramebufferObjectFormat::setAttachment(QOpenGLFramebufferObject::Attachment attachment)
310{
311 detach();
312 d->attachment = attachment;
313}
314
315/*!
316 Returns the configuration of the depth and stencil buffers attached to
317 a framebuffer object. The default is QOpenGLFramebufferObject::NoAttachment.
318
319 \sa setAttachment()
320*/
321QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObjectFormat::attachment() const
322{
323 return d->attachment;
324}
325
326/*!
327 Sets the texture target of the texture attached to a framebuffer object to
328 \a target. Ignored for multisample framebuffer objects.
329
330 \sa textureTarget(), samples()
331*/
332void QOpenGLFramebufferObjectFormat::setTextureTarget(GLenum target)
333{
334 detach();
335 d->target = target;
336}
337
338/*!
339 Returns the texture target of the texture attached to a framebuffer object.
340 Ignored for multisample framebuffer objects. The default is
341 \c GL_TEXTURE_2D.
342
343 \sa setTextureTarget(), samples()
344*/
345GLenum QOpenGLFramebufferObjectFormat::textureTarget() const
346{
347 return d->target;
348}
349
350/*!
351 Sets the internal format of a framebuffer object's texture or
352 multisample framebuffer object's color buffer to
353 \a internalTextureFormat.
354
355 \sa internalTextureFormat()
356*/
357void QOpenGLFramebufferObjectFormat::setInternalTextureFormat(GLenum internalTextureFormat)
358{
359 detach();
360 d->internal_format = internalTextureFormat;
361}
362
363/*!
364 Returns the internal format of a framebuffer object's texture or
365 multisample framebuffer object's color buffer. The default is
366 \c GL_RGBA8 on desktop OpenGL systems, and \c GL_RGBA on
367 OpenGL/ES systems.
368
369 \sa setInternalTextureFormat()
370*/
371GLenum QOpenGLFramebufferObjectFormat::internalTextureFormat() const
372{
373 return d->internal_format;
374}
375
376/*!
377 Returns \c true if all the options of this framebuffer object format
378 are the same as \a other; otherwise returns \c false.
379*/
380bool QOpenGLFramebufferObjectFormat::operator==(const QOpenGLFramebufferObjectFormat& other) const
381{
382 if (d == other.d)
383 return true;
384 else
385 return d->equals(other.d);
386}
387
388/*!
389 Returns \c false if all the options of this framebuffer object format
390 are the same as \a other; otherwise returns \c true.
391*/
392bool QOpenGLFramebufferObjectFormat::operator!=(const QOpenGLFramebufferObjectFormat& other) const
393{
394 return !(*this == other);
395}
396
397bool QOpenGLFramebufferObjectPrivate::checkFramebufferStatus(QOpenGLContext *ctx) const
398{
399 if (!ctx)
400 return false; // Context no longer exists.
401 GLenum status = ctx->functions()->glCheckFramebufferStatus(GL_FRAMEBUFFER);
402 switch(status) {
403 case GL_NO_ERROR:
404 case GL_FRAMEBUFFER_COMPLETE:
405 return true;
406 case GL_FRAMEBUFFER_UNSUPPORTED:
407 qDebug("QOpenGLFramebufferObject: Unsupported framebuffer format.");
408 break;
409 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
410 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete attachment.");
411 break;
412 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
413 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing attachment.");
414 break;
415#ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT
416 case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT:
417 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, duplicate attachment.");
418 break;
419#endif
420#ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
421 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
422 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attached images must have same dimensions.");
423 break;
424#endif
425#ifdef GL_FRAMEBUFFER_INCOMPLETE_FORMATS
426 case GL_FRAMEBUFFER_INCOMPLETE_FORMATS:
427 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attached images must have same format.");
428 break;
429#endif
430#ifdef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER
431 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
432 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing draw buffer.");
433 break;
434#endif
435#ifdef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER
436 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
437 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing read buffer.");
438 break;
439#endif
440#ifdef GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE
441 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
442 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attachments must have same number of samples per pixel.");
443 break;
444#endif
445 default:
446 qDebug() <<"QOpenGLFramebufferObject: An undefined error has occurred: "<< status;
447 break;
448 }
449 return false;
450}
451
452namespace
453{
454 void freeFramebufferFunc(QOpenGLFunctions *funcs, GLuint id)
455 {
456 funcs->glDeleteFramebuffers(1, &id);
457 }
458
459 void freeRenderbufferFunc(QOpenGLFunctions *funcs, GLuint id)
460 {
461 funcs->glDeleteRenderbuffers(1, &id);
462 }
463
464 void freeTextureFunc(QOpenGLFunctions *funcs, GLuint id)
465 {
466 funcs->glDeleteTextures(1, &id);
467 }
468}
469
470void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSize &size,
471 QOpenGLFramebufferObject::Attachment attachment,
472 GLenum texture_target, GLenum internal_format,
473 GLint samples, bool mipmap)
474{
475 QOpenGLContext *ctx = QOpenGLContext::currentContext();
476
477 funcs.initializeOpenGLFunctions();
478
479 if (!funcs.hasOpenGLFeature(QOpenGLFunctions::Framebuffers))
480 return;
481
482 // Fall back to using a normal non-msaa FBO if we don't have support for MSAA
483 if (!funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)
484 || !funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit)) {
485 samples = 0;
486 } else if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
487 GLint maxSamples;
488 funcs.glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
489 samples = qBound(0, int(samples), int(maxSamples));
490 }
491
492 colorAttachments.append(ColorAttachment(size, internal_format));
493
494 dsSize = size;
495
496 samples = qMax(0, samples);
497 requestedSamples = samples;
498
499 target = texture_target;
500
501 QT_RESET_GLERROR(); // reset error state
502 GLuint fbo = 0;
503
504 funcs.glGenFramebuffers(1, &fbo);
505 funcs.glBindFramebuffer(GL_FRAMEBUFFER, fbo);
506
507 QOpenGLContextPrivate::get(ctx)->qgl_current_fbo_invalid = true;
508
509 QT_CHECK_GLERROR();
510
511 format.setTextureTarget(target);
512 format.setInternalTextureFormat(internal_format);
513 format.setMipmap(mipmap);
514
515 if (samples == 0)
516 initTexture(0);
517 else
518 initColorBuffer(0, &samples);
519
520 format.setSamples(int(samples));
521
522 initDepthStencilAttachments(ctx, attachment);
523
524 if (valid)
525 fbo_guard = new QOpenGLSharedResourceGuard(ctx, fbo, freeFramebufferFunc);
526 else
527 funcs.glDeleteFramebuffers(1, &fbo);
528
529 QT_CHECK_GLERROR();
530}
531
532void QOpenGLFramebufferObjectPrivate::initTexture(int idx)
533{
534 QOpenGLContext *ctx = QOpenGLContext::currentContext();
535 GLuint texture = 0;
536
537 funcs.glGenTextures(1, &texture);
538 funcs.glBindTexture(target, texture);
539
540 funcs.glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
541 funcs.glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
542 funcs.glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
543 funcs.glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
544
545 ColorAttachment &color(colorAttachments[idx]);
546
547 GLuint pixelType = GL_UNSIGNED_BYTE;
548 if (color.internalFormat == GL_RGB10_A2 || color.internalFormat == GL_RGB10)
549 pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
550 else if (color.internalFormat == GL_RGB16 || color.internalFormat == GL_RGBA16)
551 pixelType = GL_UNSIGNED_SHORT;
552
553 funcs.glTexImage2D(target, 0, color.internalFormat, color.size.width(), color.size.height(), 0,
554 GL_RGBA, pixelType, NULL);
555 if (format.mipmap()) {
556 int width = color.size.width();
557 int height = color.size.height();
558 int level = 0;
559 while (width > 1 || height > 1) {
560 width = qMax(1, width >> 1);
561 height = qMax(1, height >> 1);
562 ++level;
563 funcs.glTexImage2D(target, level, color.internalFormat, width, height, 0,
564 GL_RGBA, pixelType, NULL);
565 }
566 }
567 funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + idx,
568 target, texture, 0);
569
570 QT_CHECK_GLERROR();
571 funcs.glBindTexture(target, 0);
572 valid = checkFramebufferStatus(ctx);
573 if (valid) {
574 color.guard = new QOpenGLSharedResourceGuard(ctx, texture, freeTextureFunc);
575 } else {
576 funcs.glDeleteTextures(1, &texture);
577 }
578}
579
580void QOpenGLFramebufferObjectPrivate::initColorBuffer(int idx, GLint *samples)
581{
582 QOpenGLContext *ctx = QOpenGLContext::currentContext();
583 GLuint color_buffer = 0;
584
585 ColorAttachment &color(colorAttachments[idx]);
586
587 GLenum storageFormat = color.internalFormat;
588 // ES requires a sized format. The older desktop extension does not. Correct the format on ES.
589 if (ctx->isOpenGLES()) {
590 if (color.internalFormat == GL_RGBA) {
591 if (funcs.hasOpenGLExtension(QOpenGLExtensions::Sized8Formats))
592 storageFormat = GL_RGBA8;
593 else
594 storageFormat = GL_RGBA4;
595 } else if (color.internalFormat == GL_RGB10) {
596 // GL_RGB10 is not allowed in ES for glRenderbufferStorage.
597 storageFormat = GL_RGB10_A2;
598 }
599 }
600
601 funcs.glGenRenderbuffers(1, &color_buffer);
602 funcs.glBindRenderbuffer(GL_RENDERBUFFER, color_buffer);
603 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, *samples, storageFormat, color.size.width(), color.size.height());
604 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + idx,
605 GL_RENDERBUFFER, color_buffer);
606
607 QT_CHECK_GLERROR();
608 valid = checkFramebufferStatus(ctx);
609 if (valid) {
610 // Query the actual number of samples. This can be greater than the requested
611 // value since the typically supported values are 0, 4, 8, ..., and the
612 // requests are mapped to the next supported value.
613 funcs.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, samples);
614 color.guard = new QOpenGLSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc);
615 } else {
616 funcs.glDeleteRenderbuffers(1, &color_buffer);
617 }
618}
619
620void QOpenGLFramebufferObjectPrivate::initDepthStencilAttachments(QOpenGLContext *ctx,
621 QOpenGLFramebufferObject::Attachment attachment)
622{
623 // Use the same sample count for all attachments. format.samples() already contains
624 // the actual number of samples for the color attachment and is not suitable. Use
625 // requestedSamples instead.
626 const int samples = requestedSamples;
627
628 // free existing attachments
629 if (depth_buffer_guard) {
630#ifdef Q_OS_WASM
631 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
632#else
633 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
634#endif
635 depth_buffer_guard->free();
636 }
637 if (stencil_buffer_guard) {
638 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
639 if (stencil_buffer_guard != depth_buffer_guard)
640 stencil_buffer_guard->free();
641 }
642
643 depth_buffer_guard = 0;
644 stencil_buffer_guard = 0;
645
646 GLuint depth_buffer = 0;
647 GLuint stencil_buffer = 0;
648
649 // In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a
650 // separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer
651 // might not be supported while separate buffers are, according to QTBUG-12861.
652#ifdef Q_OS_WASM
653 // WebGL doesn't allow separately attach buffers to
654 // STENCIL_ATTACHMENT and DEPTH_ATTACHMENT
655 // QTBUG-69913
656 if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil) {
657 funcs.glGenRenderbuffers(1, &depth_buffer);
658 funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
659 Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
660
661 GLenum storageFormat = GL_DEPTH_STENCIL;
662
663 if (samples != 0 ) {
664 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
665 storageFormat, dsSize.width(), dsSize.height());
666 } else {
667 funcs.glRenderbufferStorage(GL_RENDERBUFFER, storageFormat,
668 dsSize.width(), dsSize.height());
669 }
670
671 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
672 GL_RENDERBUFFER, depth_buffer);
673
674 valid = checkFramebufferStatus(ctx);
675 if (!valid) {
676 funcs.glDeleteRenderbuffers(1, &depth_buffer);
677 depth_buffer = 0;
678 }
679 }
680#else
681 if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil
682 && funcs.hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil))
683 {
684 // depth and stencil buffer needs another extension
685 funcs.glGenRenderbuffers(1, &depth_buffer);
686 funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
687 Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
688 if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
689 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
690 GL_DEPTH24_STENCIL8, dsSize.width(), dsSize.height());
691 else
692 funcs.glRenderbufferStorage(GL_RENDERBUFFER,
693 GL_DEPTH24_STENCIL8, dsSize.width(), dsSize.height());
694
695 stencil_buffer = depth_buffer;
696 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
697 GL_RENDERBUFFER, depth_buffer);
698 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
699 GL_RENDERBUFFER, stencil_buffer);
700
701 valid = checkFramebufferStatus(ctx);
702 if (!valid) {
703 funcs.glDeleteRenderbuffers(1, &depth_buffer);
704 stencil_buffer = depth_buffer = 0;
705 }
706 }
707
708 if (depth_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil
709 || (attachment == QOpenGLFramebufferObject::Depth)))
710 {
711 funcs.glGenRenderbuffers(1, &depth_buffer);
712 funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
713 Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
714 if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) {
715 if (ctx->isOpenGLES()) {
716 if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24))
717 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
718 GL_DEPTH_COMPONENT24, dsSize.width(), dsSize.height());
719 else
720 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
721 GL_DEPTH_COMPONENT16, dsSize.width(), dsSize.height());
722 } else {
723 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
724 GL_DEPTH_COMPONENT, dsSize.width(), dsSize.height());
725 }
726 } else {
727 if (ctx->isOpenGLES()) {
728 if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) {
729 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24,
730 dsSize.width(), dsSize.height());
731 } else {
732 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
733 dsSize.width(), dsSize.height());
734 }
735 } else {
736 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, dsSize.width(), dsSize.height());
737 }
738 }
739 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
740 GL_RENDERBUFFER, depth_buffer);
741 valid = checkFramebufferStatus(ctx);
742 if (!valid) {
743 funcs.glDeleteRenderbuffers(1, &depth_buffer);
744 depth_buffer = 0;
745 }
746 }
747
748 if (stencil_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil)) {
749 funcs.glGenRenderbuffers(1, &stencil_buffer);
750 funcs.glBindRenderbuffer(GL_RENDERBUFFER, stencil_buffer);
751 Q_ASSERT(funcs.glIsRenderbuffer(stencil_buffer));
752
753#ifdef QT_OPENGL_ES
754 GLenum storage = GL_STENCIL_INDEX8;
755#else
756 GLenum storage = ctx->isOpenGLES() ? GL_STENCIL_INDEX8 : GL_STENCIL_INDEX;
757#endif
758
759 if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
760 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, storage, dsSize.width(), dsSize.height());
761 else
762 funcs.glRenderbufferStorage(GL_RENDERBUFFER, storage, dsSize.width(), dsSize.height());
763
764 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
765 GL_RENDERBUFFER, stencil_buffer);
766 valid = checkFramebufferStatus(ctx);
767 if (!valid) {
768 funcs.glDeleteRenderbuffers(1, &stencil_buffer);
769 stencil_buffer = 0;
770 }
771 }
772#endif //Q_OS_WASM
773
774 // The FBO might have become valid after removing the depth or stencil buffer.
775 valid = checkFramebufferStatus(ctx);
776
777#ifdef Q_OS_WASM
778 if (depth_buffer) {
779#else
780 if (depth_buffer && stencil_buffer) {
781#endif
782 fbo_attachment = QOpenGLFramebufferObject::CombinedDepthStencil;
783 } else if (depth_buffer) {
784 fbo_attachment = QOpenGLFramebufferObject::Depth;
785 } else {
786 fbo_attachment = QOpenGLFramebufferObject::NoAttachment;
787 }
788
789 if (valid) {
790 if (depth_buffer)
791 depth_buffer_guard = new QOpenGLSharedResourceGuard(ctx, depth_buffer, freeRenderbufferFunc);
792 if (stencil_buffer) {
793 if (stencil_buffer == depth_buffer)
794 stencil_buffer_guard = depth_buffer_guard;
795 else
796 stencil_buffer_guard = new QOpenGLSharedResourceGuard(ctx, stencil_buffer, freeRenderbufferFunc);
797 }
798 } else {
799 if (depth_buffer)
800 funcs.glDeleteRenderbuffers(1, &depth_buffer);
801 if (stencil_buffer && depth_buffer != stencil_buffer)
802 funcs.glDeleteRenderbuffers(1, &stencil_buffer);
803 }
804 QT_CHECK_GLERROR();
805
806 format.setAttachment(fbo_attachment);
807}
808
809/*!
810 \class QOpenGLFramebufferObject
811 \brief The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer object.
812 \since 5.0
813 \inmodule QtGui
814
815 \ingroup painting-3D
816
817 The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer
818 object, defined by the \c{GL_EXT_framebuffer_object} extension. It provides
819 a rendering surface that can be painted on with a QPainter with the help of
820 QOpenGLPaintDevice, or rendered to using native OpenGL calls. This surface
821 can be bound and used as a regular texture in your own OpenGL drawing code.
822 By default, the QOpenGLFramebufferObject class generates a 2D OpenGL
823 texture (using the \c{GL_TEXTURE_2D} target), which is used as the internal
824 rendering target.
825
826 \b{It is important to have a current OpenGL context when creating a
827 QOpenGLFramebufferObject, otherwise initialization will fail.}
828
829 Create the QOpenGLFrameBufferObject instance with the CombinedDepthStencil
830 attachment if you want QPainter to render correctly. Note that you need to
831 create a QOpenGLFramebufferObject with more than one sample per pixel for
832 primitives to be antialiased when drawing using a QPainter. To create a
833 multisample framebuffer object you should use one of the constructors that
834 take a QOpenGLFramebufferObjectFormat parameter, and set the
835 QOpenGLFramebufferObjectFormat::samples() property to a non-zero value.
836
837 For multisample framebuffer objects a color render buffer is created,
838 otherwise a texture with the specified texture target is created.
839 The color render buffer or texture will have the specified internal
840 format, and will be bound to the \c GL_COLOR_ATTACHMENT0
841 attachment in the framebuffer object.
842
843 Multiple render targets are also supported, in case the OpenGL
844 implementation supports this. Here there will be multiple textures (or, in
845 case of multisampling, renderbuffers) present and each of them will get
846 attached to \c GL_COLOR_ATTACHMENT0, \c 1, \c 2, ...
847
848 If you want to use a framebuffer object with multisampling enabled
849 as a texture, you first need to copy from it to a regular framebuffer
850 object using QOpenGLContext::blitFramebuffer().
851
852 It is possible to draw into a QOpenGLFramebufferObject using QPainter and
853 QOpenGLPaintDevice in a separate thread.
854*/
855
856
857/*!
858 \enum QOpenGLFramebufferObject::Attachment
859
860 This enum type is used to configure the depth and stencil buffers
861 attached to the framebuffer object when it is created.
862
863 \value NoAttachment No attachment is added to the framebuffer object. Note that the
864 OpenGL depth and stencil tests won't work when rendering to a
865 framebuffer object without any depth or stencil buffers.
866 This is the default value.
867
868 \value CombinedDepthStencil If the \c GL_EXT_packed_depth_stencil extension is present,
869 a combined depth and stencil buffer is attached.
870 If the extension is not present, only a depth buffer is attached.
871
872 \value Depth A depth buffer is attached to the framebuffer object.
873
874 \sa attachment()
875*/
876
877static inline GLenum effectiveInternalFormat(GLenum internalFormat)
878{
879 if (!internalFormat)
880#ifdef QT_OPENGL_ES_2
881 internalFormat = GL_RGBA;
882#else
883 internalFormat = QOpenGLContext::currentContext()->isOpenGLES() ? GL_RGBA : GL_RGBA8;
884#endif
885 return internalFormat;
886}
887
888/*!
889
890 Constructs an OpenGL framebuffer object and binds a 2D OpenGL texture
891 to the buffer of the size \a size. The texture is bound to the
892 \c GL_COLOR_ATTACHMENT0 target in the framebuffer object.
893
894 The \a target parameter is used to specify the OpenGL texture
895 target. The default target is \c GL_TEXTURE_2D. Keep in mind that
896 \c GL_TEXTURE_2D textures must have a power of 2 width and height
897 (e.g. 256x512), unless you are using OpenGL 2.0 or higher.
898
899 By default, no depth and stencil buffers are attached. This behavior
900 can be toggled using one of the overloaded constructors.
901
902 The default internal texture format is \c GL_RGBA8 for desktop
903 OpenGL, and \c GL_RGBA for OpenGL/ES.
904
905 It is important that you have a current OpenGL context set when
906 creating the QOpenGLFramebufferObject, otherwise the initialization
907 will fail.
908
909 \sa size(), texture(), attachment()
910*/
911
912QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, GLenum target)
913 : d_ptr(new QOpenGLFramebufferObjectPrivate)
914{
915 Q_D(QOpenGLFramebufferObject);
916 d->init(this, size, NoAttachment, target, effectiveInternalFormat(0));
917}
918
919/*!
920
921 Constructs an OpenGL framebuffer object and binds a 2D OpenGL texture
922 to the buffer of the given \a width and \a height.
923
924 \sa size(), texture()
925*/
926QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, GLenum target)
927 : QOpenGLFramebufferObject(QSize(width, height), target)
928{
929}
930
931/*!
932
933 Constructs an OpenGL framebuffer object of the given \a size based on the
934 supplied \a format.
935*/
936
937QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, const QOpenGLFramebufferObjectFormat &format)
938 : d_ptr(new QOpenGLFramebufferObjectPrivate)
939{
940 Q_D(QOpenGLFramebufferObject);
941 d->init(this, size, format.attachment(), format.textureTarget(), format.internalTextureFormat(),
942 format.samples(), format.mipmap());
943}
944
945/*!
946
947 Constructs an OpenGL framebuffer object of the given \a width and \a height
948 based on the supplied \a format.
949*/
950
951QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, const QOpenGLFramebufferObjectFormat &format)
952 : QOpenGLFramebufferObject(QSize(width, height), format)
953{
954}
955
956/*!
957
958 Constructs an OpenGL framebuffer object and binds a texture to the
959 buffer of the given \a width and \a height.
960
961 The \a attachment parameter describes the depth/stencil buffer
962 configuration, \a target the texture target and \a internalFormat
963 the internal texture format. The default texture target is \c
964 GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8
965 for desktop OpenGL and \c GL_RGBA for OpenGL/ES.
966
967 \sa size(), texture(), attachment()
968*/
969QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, Attachment attachment,
970 GLenum target, GLenum internalFormat)
971 : d_ptr(new QOpenGLFramebufferObjectPrivate)
972{
973 Q_D(QOpenGLFramebufferObject);
974 d->init(this, QSize(width, height), attachment, target, effectiveInternalFormat(internalFormat));
975}
976
977/*!
978
979 Constructs an OpenGL framebuffer object and binds a texture to the
980 buffer of the given \a size.
981
982 The \a attachment parameter describes the depth/stencil buffer
983 configuration, \a target the texture target and \a internalFormat
984 the internal texture format. The default texture target is \c
985 GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8
986 for desktop OpenGL and \c GL_RGBA for OpenGL/ES.
987
988 \sa size(), texture(), attachment()
989*/
990QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, Attachment attachment,
991 GLenum target, GLenum internalFormat)
992 : d_ptr(new QOpenGLFramebufferObjectPrivate)
993{
994 Q_D(QOpenGLFramebufferObject);
995 d->init(this, size, attachment, target, effectiveInternalFormat(internalFormat));
996}
997
998/*!
999
1000 Destroys the framebuffer object and frees any allocated resources.
1001*/
1002QOpenGLFramebufferObject::~QOpenGLFramebufferObject()
1003{
1004 Q_D(QOpenGLFramebufferObject);
1005 if (isBound())
1006 release();
1007
1008 for (const auto &color : qAsConst(d->colorAttachments)) {
1009 if (color.guard)
1010 color.guard->free();
1011 }
1012 d->colorAttachments.clear();
1013
1014 if (d->depth_buffer_guard)
1015 d->depth_buffer_guard->free();
1016 if (d->stencil_buffer_guard && d->stencil_buffer_guard != d->depth_buffer_guard)
1017 d->stencil_buffer_guard->free();
1018 if (d->fbo_guard)
1019 d->fbo_guard->free();
1020
1021 QOpenGLContextPrivate *contextPrv = QOpenGLContextPrivate::get(QOpenGLContext::currentContext());
1022 if (contextPrv && contextPrv->qgl_current_fbo == this) {
1023 contextPrv->qgl_current_fbo_invalid = true;
1024 contextPrv->qgl_current_fbo = nullptr;
1025 }
1026}
1027
1028/*!
1029 Creates and attaches an additional texture or renderbuffer of \a size width
1030 and height.
1031
1032 There is always an attachment at GL_COLOR_ATTACHMENT0. Call this function
1033 to set up additional attachments at GL_COLOR_ATTACHMENT1,
1034 GL_COLOR_ATTACHMENT2, ...
1035
1036 When \a internalFormat is not \c 0, it specifies the internal format of the
1037 texture or renderbuffer. Otherwise a default of GL_RGBA or GL_RGBA8 is
1038 used.
1039
1040 \note This is only functional when multiple render targets are supported by
1041 the OpenGL implementation. When that is not the case, the function will not
1042 add any additional color attachments. Call
1043 QOpenGLFunctions::hasOpenGLFeature() with
1044 QOpenGLFunctions::MultipleRenderTargets at runtime to check if MRT is
1045 supported.
1046
1047 \note The internal format of the color attachments may differ but there may
1048 be limitations on the supported combinations, depending on the drivers.
1049
1050 \note The size of the color attachments may differ but rendering is limited
1051 to the area that fits all the attachments, according to the OpenGL
1052 specification. Some drivers may not be fully conformant in this respect,
1053 however.
1054
1055 \since 5.6
1056 */
1057void QOpenGLFramebufferObject::addColorAttachment(const QSize &size, GLenum internalFormat)
1058{
1059 Q_D(QOpenGLFramebufferObject);
1060
1061 if (!QOpenGLContext::currentContext()->functions()->hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets)) {
1062 qWarning("Multiple render targets not supported, ignoring extra color attachment request");
1063 return;
1064 }
1065
1066 QOpenGLFramebufferObjectPrivate::ColorAttachment color(size, effectiveInternalFormat(internalFormat));
1067 d->colorAttachments.append(color);
1068 const int idx = d->colorAttachments.count() - 1;
1069
1070 if (d->requestedSamples == 0) {
1071 d->initTexture(idx);
1072 } else {
1073 GLint samples = d->requestedSamples;
1074 d->initColorBuffer(idx, &samples);
1075 }
1076}
1077
1078/*! \overload
1079
1080 Creates and attaches an additional texture or renderbuffer of size \a width and \a height.
1081
1082 When \a internalFormat is not \c 0, it specifies the internal format of the texture or
1083 renderbuffer. Otherwise a default of GL_RGBA or GL_RGBA8 is used.
1084
1085 \since 5.6
1086 */
1087void QOpenGLFramebufferObject::addColorAttachment(int width, int height, GLenum internalFormat)
1088{
1089 addColorAttachment(QSize(width, height), internalFormat);
1090}
1091
1092/*!
1093 \fn bool QOpenGLFramebufferObject::isValid() const
1094
1095 Returns \c true if the framebuffer object is valid.
1096
1097 The framebuffer can become invalid if the initialization process
1098 fails, the user attaches an invalid buffer to the framebuffer
1099 object, or a non-power of two width/height is specified as the
1100 texture size if the texture target is \c{GL_TEXTURE_2D}.
1101 The non-power of two limitation does not apply if the OpenGL version
1102 is 2.0 or higher, or if the GL_ARB_texture_non_power_of_two extension
1103 is present.
1104
1105 The framebuffer can also become invalid if the QOpenGLContext that
1106 the framebuffer was created within is destroyed and there are
1107 no other shared contexts that can take over ownership of the
1108 framebuffer.
1109*/
1110bool QOpenGLFramebufferObject::isValid() const
1111{
1112 Q_D(const QOpenGLFramebufferObject);
1113 return d->valid && d->fbo_guard && d->fbo_guard->id();
1114}
1115
1116/*!
1117 \fn bool QOpenGLFramebufferObject::bind()
1118
1119 Switches rendering from the default, windowing system provided
1120 framebuffer to this framebuffer object.
1121 Returns \c true upon success, false otherwise.
1122
1123 \note If takeTexture() was called, a new texture is created and associated
1124 with the framebuffer object. This is potentially expensive and changes the
1125 context state (the currently bound texture).
1126
1127 \sa release()
1128*/
1129bool QOpenGLFramebufferObject::bind()
1130{
1131 if (!isValid())
1132 return false;
1133 Q_D(QOpenGLFramebufferObject);
1134 QOpenGLContext *current = QOpenGLContext::currentContext();
1135 if (!current)
1136 return false;
1137#ifdef QT_DEBUG
1138 if (current->shareGroup() != d->fbo_guard->group())
1139 qWarning("QOpenGLFramebufferObject::bind() called from incompatible context");
1140#endif
1141
1142 d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo());
1143
1144 QOpenGLContextPrivate::get(current)->qgl_current_fbo_invalid = true;
1145 QOpenGLContextPrivate::get(current)->qgl_current_fbo = this;
1146
1147 if (d->format.samples() == 0) {
1148 // Create new textures to replace the ones stolen via takeTexture().
1149 for (int i = 0; i < d->colorAttachments.count(); ++i) {
1150 if (!d->colorAttachments.at(i).guard)
1151 d->initTexture(i);
1152 }
1153 }
1154
1155 return d->valid;
1156}
1157
1158/*!
1159 \fn bool QOpenGLFramebufferObject::release()
1160
1161 Switches rendering back to the default, windowing system provided
1162 framebuffer.
1163 Returns \c true upon success, false otherwise.
1164
1165 \sa bind()
1166*/
1167bool QOpenGLFramebufferObject::release()
1168{
1169 if (!isValid())
1170 return false;
1171
1172 QOpenGLContext *current = QOpenGLContext::currentContext();
1173 if (!current)
1174 return false;
1175
1176 Q_D(QOpenGLFramebufferObject);
1177#ifdef QT_DEBUG
1178 if (current->shareGroup() != d->fbo_guard->group())
1179 qWarning("QOpenGLFramebufferObject::release() called from incompatible context");
1180#endif
1181
1182 if (current) {
1183 d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->defaultFramebufferObject());
1184
1185 QOpenGLContextPrivate *contextPrv = QOpenGLContextPrivate::get(current);
1186 contextPrv->qgl_current_fbo_invalid = true;
1187 contextPrv->qgl_current_fbo = nullptr;
1188 }
1189
1190 return true;
1191}
1192
1193/*!
1194 \fn GLuint QOpenGLFramebufferObject::texture() const
1195
1196 Returns the texture id for the texture attached as the default
1197 rendering target in this framebuffer object. This texture id can
1198 be bound as a normal texture in your own OpenGL code.
1199
1200 If a multisample framebuffer object is used then the value returned
1201 from this function will be invalid.
1202
1203 When multiple textures are attached, the return value is the ID of
1204 the first one.
1205
1206 \sa takeTexture(), textures()
1207*/
1208GLuint QOpenGLFramebufferObject::texture() const
1209{
1210 Q_D(const QOpenGLFramebufferObject);
1211 return d->colorAttachments[0].guard ? d->colorAttachments[0].guard->id() : 0;
1212}
1213
1214/*!
1215 Returns the texture id for all attached textures.
1216
1217 If a multisample framebuffer object is used, then an empty vector is returned.
1218
1219 \since 5.6
1220
1221 \sa takeTexture(), texture()
1222*/
1223QVector<GLuint> QOpenGLFramebufferObject::textures() const
1224{
1225 Q_D(const QOpenGLFramebufferObject);
1226 QVector<GLuint> ids;
1227 if (d->format.samples() != 0)
1228 return ids;
1229 ids.reserve(d->colorAttachments.count());
1230 for (const auto &color : d->colorAttachments)
1231 ids.append(color.guard ? color.guard->id() : 0);
1232 return ids;
1233}
1234
1235/*!
1236 \fn GLuint QOpenGLFramebufferObject::takeTexture()
1237
1238 Returns the texture id for the texture attached to this framebuffer
1239 object. The ownership of the texture is transferred to the caller.
1240
1241 If the framebuffer object is currently bound, an implicit release()
1242 will be done. During the next call to bind() a new texture will be
1243 created.
1244
1245 If a multisample framebuffer object is used, then there is no
1246 texture and the return value from this function will be invalid.
1247 Similarly, incomplete framebuffer objects will also return 0.
1248
1249 \since 5.3
1250
1251 \sa texture(), bind(), release()
1252 */
1253GLuint QOpenGLFramebufferObject::takeTexture()
1254{
1255 return takeTexture(0);
1256}
1257
1258/*! \overload
1259
1260 Returns the texture id for the texture attached to the color attachment of
1261 index \a colorAttachmentIndex of this framebuffer object. The ownership of
1262 the texture is transferred to the caller.
1263
1264 When \a colorAttachmentIndex is \c 0, the behavior is identical to the
1265 parameter-less variant of this function.
1266
1267 If the framebuffer object is currently bound, an implicit release()
1268 will be done. During the next call to bind() a new texture will be
1269 created.
1270
1271 If a multisample framebuffer object is used, then there is no
1272 texture and the return value from this function will be invalid.
1273 Similarly, incomplete framebuffer objects will also return 0.
1274
1275 \since 5.6
1276 */
1277GLuint QOpenGLFramebufferObject::takeTexture(int colorAttachmentIndex)
1278{
1279 Q_D(QOpenGLFramebufferObject);
1280 GLuint id = 0;
1281 if (isValid() && d->format.samples() == 0 && d->colorAttachments.count() > colorAttachmentIndex) {
1282 QOpenGLContext *current = QOpenGLContext::currentContext();
1283 if (current && current->shareGroup() == d->fbo_guard->group() && isBound())
1284 release();
1285 auto &guard = d->colorAttachments[colorAttachmentIndex].guard;
1286 id = guard ? guard->id() : 0;
1287 // Do not call free() on texture_guard, just null it out.
1288 // This way the texture will not be deleted when the guard is destroyed.
1289 guard = 0;
1290 }
1291 return id;
1292}
1293
1294/*!
1295 \return the size of the color and depth/stencil attachments attached to
1296 this framebuffer object.
1297*/
1298QSize QOpenGLFramebufferObject::size() const
1299{
1300 Q_D(const QOpenGLFramebufferObject);
1301 return d->dsSize;
1302}
1303
1304/*!
1305 \return the sizes of all color attachments attached to this framebuffer
1306 object.
1307
1308 \since 5.6
1309*/
1310QVector<QSize> QOpenGLFramebufferObject::sizes() const
1311{
1312 Q_D(const QOpenGLFramebufferObject);
1313 QVector<QSize> sz;
1314 sz.reserve(d->colorAttachments.size());
1315 for (const auto &color : d->colorAttachments)
1316 sz.append(color.size);
1317 return sz;
1318}
1319
1320/*!
1321 \fn int QOpenGLFramebufferObject::width() const
1322
1323 Returns the width of the framebuffer object attachments.
1324*/
1325
1326/*!
1327 \fn int QOpenGLFramebufferObject::height() const
1328
1329 Returns the height of the framebuffer object attachments.
1330*/
1331
1332/*!
1333 Returns the format of this framebuffer object.
1334*/
1335QOpenGLFramebufferObjectFormat QOpenGLFramebufferObject::format() const
1336{
1337 Q_D(const QOpenGLFramebufferObject);
1338 return d->format;
1339}
1340
1341static inline QImage qt_gl_read_framebuffer_rgba8(const QSize &size, bool include_alpha, QOpenGLContext *context)
1342{
1343 QOpenGLFunctions *funcs = context->functions();
1344 const int w = size.width();
1345 const int h = size.height();
1346 bool isOpenGL12orBetter = !context->isOpenGLES() && (context->format().majorVersion() >= 2 || context->format().minorVersion() >= 2);
1347 if (isOpenGL12orBetter) {
1348 QImage img(size, include_alpha ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32);
1349 funcs->glReadPixels(0, 0, w, h, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, img.bits());
1350 return img;
1351 }
1352
1353 // For OpenGL ES stick with the byte ordered format / RGBA readback format
1354 // since that is the only spec mandated way. (also, skip the
1355 // GL_IMPLEMENTATION_COLOR_READ_FORMAT mess since there is nothing saying a
1356 // BGRA capable impl would return BGRA from there)
1357
1358 QImage rgbaImage(size, include_alpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGBX8888);
1359 funcs->glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, rgbaImage.bits());
1360 return rgbaImage;
1361}
1362
1363static inline QImage qt_gl_read_framebuffer_rgb10a2(const QSize &size, bool include_alpha, QOpenGLContext *context)
1364{
1365 // We assume OpenGL 1.2+ or ES 3.0+ here.
1366 QImage img(size, include_alpha ? QImage::Format_A2BGR30_Premultiplied : QImage::Format_BGR30);
1367 context->functions()->glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, img.bits());
1368 return img;
1369}
1370
1371static inline QImage qt_gl_read_framebuffer_rgba16(const QSize &size, bool include_alpha, QOpenGLContext *context)
1372{
1373 // We assume OpenGL 1.2+ or ES 3.0+ here.
1374 QImage img(size, include_alpha ? QImage::Format_RGBA64_Premultiplied : QImage::Format_RGBX64);
1375 context->functions()->glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_SHORT, img.bits());
1376 return img;
1377}
1378
1379static QImage qt_gl_read_framebuffer(const QSize &size, GLenum internal_format, bool include_alpha, bool flip)
1380{
1381 QOpenGLContext *ctx = QOpenGLContext::currentContext();
1382 QOpenGLFunctions *funcs = ctx->functions();
1383 while (true) {
1384 GLenum error = funcs->glGetError();
1385 if (error == GL_NO_ERROR || error == GL_CONTEXT_LOST)
1386 break;
1387 }
1388 switch (internal_format) {
1389 case GL_RGB:
1390 case GL_RGB8:
1391 return qt_gl_read_framebuffer_rgba8(size, false, ctx).mirrored(false, flip);
1392 case GL_RGB10:
1393 return qt_gl_read_framebuffer_rgb10a2(size, false, ctx).mirrored(false, flip);
1394 case GL_RGB10_A2:
1395 return qt_gl_read_framebuffer_rgb10a2(size, include_alpha, ctx).mirrored(false, flip);
1396 case GL_RGB16:
1397 return qt_gl_read_framebuffer_rgba16(size, false, ctx).mirrored(false, flip);
1398 case GL_RGBA16:
1399 return qt_gl_read_framebuffer_rgba16(size, include_alpha, ctx).mirrored(false, flip);
1400 case GL_RGBA:
1401 case GL_RGBA8:
1402 default:
1403 return qt_gl_read_framebuffer_rgba8(size, include_alpha, ctx).mirrored(false, flip);
1404 }
1405
1406 Q_UNREACHABLE();
1407 return QImage();
1408}
1409
1410Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha)
1411{
1412 return qt_gl_read_framebuffer(size, alpha_format ? GL_RGBA : GL_RGB, include_alpha, true);
1413}
1414
1415/*!
1416 \fn QImage QOpenGLFramebufferObject::toImage(bool flipped) const
1417
1418 Returns the contents of this framebuffer object as a QImage.
1419
1420 If \a flipped is true the image is flipped from OpenGL coordinates to raster coordinates.
1421 If used together with QOpenGLPaintDevice, \a flipped should be the opposite of the value
1422 of QOpenGLPaintDevice::paintFlipped().
1423
1424 The returned image has a format of premultiplied ARGB32 or RGB32. The latter
1425 is used only when internalTextureFormat() is set to \c GL_RGB. Since Qt 5.2
1426 the function will fall back to premultiplied RGBA8888 or RGBx8888 when
1427 reading to (A)RGB32 is not supported, and this includes OpenGL ES. Since Qt
1428 5.4 an A2BGR30 image is returned if the internal format is RGB10_A2, and since
1429 Qt 5.12 a RGBA64 image is return if the internal format is RGBA16.
1430
1431 If the rendering in the framebuffer was not done with premultiplied alpha in mind,
1432 create a wrapper QImage with a non-premultiplied format. This is necessary before
1433 performing operations like QImage::save() because otherwise the image data would get
1434 unpremultiplied, even though it was not premultiplied in the first place. To create
1435 such a wrapper without performing a copy of the pixel data, do the following:
1436
1437 \code
1438 QImage fboImage(fbo.toImage());
1439 QImage image(fboImage.constBits(), fboImage.width(), fboImage.height(), QImage::Format_ARGB32);
1440 \endcode
1441
1442 For multisampled framebuffer objects the samples are resolved using the
1443 \c{GL_EXT_framebuffer_blit} extension. If the extension is not available, the contents
1444 of the returned image is undefined.
1445
1446 For singlesampled framebuffers the contents is retrieved via \c glReadPixels. This is
1447 a potentially expensive and inefficient operation. Therefore it is recommended that
1448 this function is used as seldom as possible.
1449
1450 \sa QOpenGLPaintDevice::paintFlipped()
1451*/
1452
1453QImage QOpenGLFramebufferObject::toImage(bool flipped) const
1454{
1455 return toImage(flipped, 0);
1456}
1457
1458/*!
1459 \fn QImage QOpenGLFramebufferObject::toImage() const
1460 \overload
1461
1462 Returns the contents of this framebuffer object as a QImage. This method flips
1463 the image from OpenGL coordinates to raster coordinates.
1464*/
1465// ### Qt 6: Remove this method and make it a default argument instead.
1466QImage QOpenGLFramebufferObject::toImage() const
1467{
1468 return toImage(true, 0);
1469}
1470
1471/*! \overload
1472
1473 Returns the contents of the color attachment of index \a
1474 colorAttachmentIndex of this framebuffer object as a QImage. This method
1475 flips the image from OpenGL coordinates to raster coordinates when \a
1476 flipped is set to \c true.
1477
1478 \note This overload is only fully functional when multiple render targets are
1479 supported by the OpenGL implementation. When that is not the case, only one
1480 color attachment will be set up.
1481
1482 \since 5.6
1483*/
1484QImage QOpenGLFramebufferObject::toImage(bool flipped, int colorAttachmentIndex) const
1485{
1486 Q_D(const QOpenGLFramebufferObject);
1487 if (!d->valid)
1488 return QImage();
1489
1490 QOpenGLContext *ctx = QOpenGLContext::currentContext();
1491 if (!ctx) {
1492 qWarning("QOpenGLFramebufferObject::toImage() called without a current context");
1493 return QImage();
1494 }
1495
1496 if (d->colorAttachments.count() <= colorAttachmentIndex) {
1497 qWarning("QOpenGLFramebufferObject::toImage() called for missing color attachment");
1498 return QImage();
1499 }
1500
1501 GLuint prevFbo = 0;
1502 ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &prevFbo);
1503
1504 if (prevFbo != d->fbo())
1505 const_cast<QOpenGLFramebufferObject *>(this)->bind();
1506
1507 QImage image;
1508 QOpenGLExtraFunctions *extraFuncs = ctx->extraFunctions();
1509 // qt_gl_read_framebuffer doesn't work on a multisample FBO
1510 if (format().samples() != 0) {
1511 QRect rect(QPoint(0, 0), size());
1512 QOpenGLFramebufferObjectFormat fmt;
1513 if (extraFuncs->hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets)) {
1514 fmt.setInternalTextureFormat(d->colorAttachments[colorAttachmentIndex].internalFormat);
1515 QOpenGLFramebufferObject temp(d->colorAttachments[colorAttachmentIndex].size, fmt);
1516 blitFramebuffer(&temp, rect, const_cast<QOpenGLFramebufferObject *>(this), rect,
1517 GL_COLOR_BUFFER_BIT, GL_NEAREST,
1518 colorAttachmentIndex, 0);
1519 image = temp.toImage(flipped);
1520 } else {
1521 fmt.setInternalTextureFormat(d->colorAttachments[0].internalFormat);
1522 QOpenGLFramebufferObject temp(size(), fmt);
1523 blitFramebuffer(&temp, rect, const_cast<QOpenGLFramebufferObject *>(this), rect);
1524 image = temp.toImage(flipped);
1525 }
1526 } else {
1527 if (extraFuncs->hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets)) {
1528 extraFuncs->glReadBuffer(GL_COLOR_ATTACHMENT0 + colorAttachmentIndex);
1529 image = qt_gl_read_framebuffer(d->colorAttachments[colorAttachmentIndex].size,
1530 d->colorAttachments[colorAttachmentIndex].internalFormat,
1531 true, flipped);
1532 extraFuncs->glReadBuffer(GL_COLOR_ATTACHMENT0);
1533 } else {
1534 image = qt_gl_read_framebuffer(d->colorAttachments[0].size,
1535 d->colorAttachments[0].internalFormat,
1536 true, flipped);
1537 }
1538 }
1539
1540 if (prevFbo != d->fbo())
1541 ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, prevFbo);
1542
1543 return image;
1544}
1545
1546/*!
1547 \fn bool QOpenGLFramebufferObject::bindDefault()
1548
1549 Switches rendering back to the default, windowing system provided
1550 framebuffer.
1551 Returns \c true upon success, false otherwise.
1552
1553 \sa bind(), release()
1554*/
1555bool QOpenGLFramebufferObject::bindDefault()
1556{
1557 QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
1558
1559 if (ctx) {
1560 ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
1561 QOpenGLContextPrivate::get(ctx)->qgl_current_fbo_invalid = true;
1562 QOpenGLContextPrivate::get(ctx)->qgl_current_fbo = nullptr;
1563 }
1564#ifdef QT_DEBUG
1565 else
1566 qWarning("QOpenGLFramebufferObject::bindDefault() called without current context.");
1567#endif
1568
1569 return ctx != 0;
1570}
1571
1572/*!
1573 \fn bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()
1574
1575 Returns \c true if the OpenGL \c{GL_EXT_framebuffer_object} extension
1576 is present on this system; otherwise returns \c false.
1577*/
1578bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()
1579{
1580 return QOpenGLContext::currentContext()->functions()->hasOpenGLFeature(QOpenGLFunctions::Framebuffers);
1581}
1582
1583/*!
1584 \fn GLuint QOpenGLFramebufferObject::handle() const
1585
1586 Returns the OpenGL framebuffer object handle for this framebuffer
1587 object (returned by the \c{glGenFrameBuffersEXT()} function). This
1588 handle can be used to attach new images or buffers to the
1589 framebuffer. The user is responsible for cleaning up and
1590 destroying these objects.
1591*/
1592GLuint QOpenGLFramebufferObject::handle() const
1593{
1594 Q_D(const QOpenGLFramebufferObject);
1595 return d->fbo();
1596}
1597
1598/*!
1599 Returns the status of the depth and stencil buffers attached to
1600 this framebuffer object.
1601*/
1602
1603QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObject::attachment() const
1604{
1605 Q_D(const QOpenGLFramebufferObject);
1606 if (d->valid)
1607 return d->fbo_attachment;
1608 return NoAttachment;
1609}
1610
1611/*!
1612 Sets the attachments of the framebuffer object to \a attachment.
1613
1614 This can be used to free or reattach the depth and stencil buffer
1615 attachments as needed.
1616
1617 \note This function alters the current framebuffer binding.
1618 */
1619void QOpenGLFramebufferObject::setAttachment(QOpenGLFramebufferObject::Attachment attachment)
1620{
1621 Q_D(QOpenGLFramebufferObject);
1622 if (attachment == d->fbo_attachment || !isValid())
1623 return;
1624 QOpenGLContext *current = QOpenGLContext::currentContext();
1625 if (!current)
1626 return;
1627#ifdef QT_DEBUG
1628 if (current->shareGroup() != d->fbo_guard->group())
1629 qWarning("QOpenGLFramebufferObject::setAttachment() called from incompatible context");
1630#endif
1631 d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo());
1632 QOpenGLContextPrivate::get(current)->qgl_current_fbo_invalid = true;
1633 d->initDepthStencilAttachments(current, attachment);
1634}
1635
1636/*!
1637 Returns \c true if the framebuffer object is currently bound to the current context,
1638 otherwise false is returned.
1639*/
1640bool QOpenGLFramebufferObject::isBound() const
1641{
1642 Q_D(const QOpenGLFramebufferObject);
1643 QOpenGLContext *ctx = QOpenGLContext::currentContext();
1644 if (!ctx)
1645 return false;
1646 GLint fbo = 0;
1647 ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
1648 return GLuint(fbo) == d->fbo();
1649}
1650
1651/*!
1652 \fn bool QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()
1653
1654 Returns \c true if the OpenGL \c{GL_EXT_framebuffer_blit} extension
1655 is present on this system; otherwise returns \c false.
1656
1657 \sa blitFramebuffer()
1658*/
1659bool QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()
1660{
1661 return QOpenGLExtensions(QOpenGLContext::currentContext()).hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
1662}
1663
1664
1665/*!
1666 \overload
1667
1668 Convenience overload to blit between two framebuffer objects.
1669*/
1670void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target,
1671 QOpenGLFramebufferObject *source,
1672 GLbitfield buffers, GLenum filter)
1673{
1674 if (!target && !source)
1675 return;
1676
1677 QSize targetSize;
1678 QSize sourceSize;
1679
1680 if (target)
1681 targetSize = target->size();
1682 if (source)
1683 sourceSize = source->size();
1684
1685 if (targetSize.isEmpty())
1686 targetSize = sourceSize;
1687 else if (sourceSize.isEmpty())
1688 sourceSize = targetSize;
1689
1690 blitFramebuffer(target, QRect(QPoint(0, 0), targetSize),
1691 source, QRect(QPoint(0, 0), sourceSize),
1692 buffers, filter);
1693}
1694
1695/*! \overload
1696 *
1697 Convenience overload to blit between two framebuffer objects.
1698*/
1699void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect,
1700 QOpenGLFramebufferObject *source, const QRect &sourceRect,
1701 GLbitfield buffers,
1702 GLenum filter)
1703{
1704 blitFramebuffer(target, targetRect, source, sourceRect, buffers, filter, 0, 0);
1705}
1706
1707/*!
1708 \enum QOpenGLFramebufferObject::FramebufferRestorePolicy
1709 \since 5.7
1710
1711 This enum type is used to configure the behavior related to restoring
1712 framebuffer bindings when calling blitFramebuffer().
1713
1714 \value DontRestoreFramebufferBinding Do not restore the previous framebuffer binding.
1715 The caller is responsible for tracking and setting
1716 the framebuffer binding as needed.
1717
1718 \value RestoreFramebufferBindingToDefault After the blit operation, bind the default
1719 framebuffer.
1720
1721 \value RestoreFrameBufferBinding Restore the previously bound framebuffer. This is
1722 potentially expensive because of the need to
1723 query the currently bound framebuffer.
1724
1725 \sa blitFramebuffer()
1726*/
1727
1728/*!
1729 \since 5.7
1730
1731 Blits from the \a sourceRect rectangle in the \a source framebuffer
1732 object to the \a targetRect rectangle in the \a target framebuffer object.
1733
1734 If \a source or \a target is 0, the default framebuffer will be used
1735 instead of a framebuffer object as source or target respectively.
1736
1737 This function will have no effect unless hasOpenGLFramebufferBlit() returns
1738 true.
1739
1740 The \a buffers parameter should be a mask consisting of any combination of
1741 \c GL_COLOR_BUFFER_BIT, \c GL_DEPTH_BUFFER_BIT, and
1742 \c GL_STENCIL_BUFFER_BIT. Any buffer type that is not present both
1743 in the source and target buffers is ignored.
1744
1745 The \a sourceRect and \a targetRect rectangles may have different sizes;
1746 in this case \a buffers should not contain \c GL_DEPTH_BUFFER_BIT or
1747 \c GL_STENCIL_BUFFER_BIT. The \a filter parameter should be set to
1748 \c GL_LINEAR or \c GL_NEAREST, and specifies whether linear or nearest
1749 interpolation should be used when scaling is performed.
1750
1751 If \a source equals \a target a copy is performed within the same buffer.
1752 Results are undefined if the source and target rectangles overlap and
1753 have different sizes. The sizes must also be the same if any of the
1754 framebuffer objects are multisample framebuffers.
1755
1756 \note The scissor test will restrict the blit area if enabled.
1757
1758 When multiple render targets are in use, \a readColorAttachmentIndex and \a
1759 drawColorAttachmentIndex specify the index of the color attachments in the
1760 source and destination framebuffers.
1761
1762 The \a restorePolicy determines if the framebuffer that was bound prior to
1763 calling this function should be restored, or if the default framebuffer
1764 should be bound before returning, of if the caller is responsible for
1765 tracking and setting the bound framebuffer. Restoring the previous
1766 framebuffer can be relatively expensive due to the call to \c{glGetIntegerv}
1767 which on some OpenGL drivers may imply a pipeline stall.
1768
1769 \sa hasOpenGLFramebufferBlit()
1770*/
1771void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect,
1772 QOpenGLFramebufferObject *source, const QRect &sourceRect,
1773 GLbitfield buffers,
1774 GLenum filter,
1775 int readColorAttachmentIndex,
1776 int drawColorAttachmentIndex,
1777 QOpenGLFramebufferObject::FramebufferRestorePolicy restorePolicy)
1778{
1779 QOpenGLContext *ctx = QOpenGLContext::currentContext();
1780 if (!ctx)
1781 return;
1782
1783 QOpenGLExtensions extensions(ctx);
1784 if (!extensions.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit))
1785 return;
1786
1787 GLuint prevFbo = 0;
1788 if (restorePolicy == RestoreFrameBufferBinding)
1789 ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &prevFbo);
1790
1791 const int sx0 = sourceRect.left();
1792 const int sx1 = sourceRect.left() + sourceRect.width();
1793 const int sy0 = sourceRect.top();
1794 const int sy1 = sourceRect.top() + sourceRect.height();
1795
1796 const int tx0 = targetRect.left();
1797 const int tx1 = targetRect.left() + targetRect.width();
1798 const int ty0 = targetRect.top();
1799 const int ty1 = targetRect.top() + targetRect.height();
1800
1801 const GLuint defaultFboId = ctx->defaultFramebufferObject();
1802
1803 extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, source ? source->handle() : defaultFboId);
1804 extensions.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target ? target->handle() : defaultFboId);
1805
1806 const bool supportsMRT = extensions.hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets);
1807 if (supportsMRT) {
1808 extensions.glReadBuffer(GL_COLOR_ATTACHMENT0 + readColorAttachmentIndex);
1809 if (target) {
1810 GLenum drawBuf = GL_COLOR_ATTACHMENT0 + drawColorAttachmentIndex;
1811 extensions.glDrawBuffers(1, &drawBuf);
1812 }
1813 }
1814
1815 extensions.glBlitFramebuffer(sx0, sy0, sx1, sy1,
1816 tx0, ty0, tx1, ty1,
1817 buffers, filter);
1818
1819 if (supportsMRT)
1820 extensions.glReadBuffer(GL_COLOR_ATTACHMENT0);
1821
1822 switch (restorePolicy) {
1823 case RestoreFrameBufferBinding:
1824 ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, prevFbo); // sets both READ and DRAW
1825 break;
1826
1827 case RestoreFramebufferBindingToDefault:
1828 ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); // sets both READ and DRAW
1829 break;
1830
1831 case DontRestoreFramebufferBinding:
1832 break;
1833 }
1834}
1835
1836/*!
1837 \overload
1838
1839 Convenience overload to blit between two framebuffer objects and
1840 to restore the previous framebuffer binding. Equivalent to calling
1841 blitFramebuffer(target, targetRect, source, sourceRect, buffers, filter,
1842 readColorAttachmentIndex, drawColorAttachmentIndex,
1843 RestoreFrameBufferBinding).
1844*/
1845void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect,
1846 QOpenGLFramebufferObject *source, const QRect &sourceRect,
1847 GLbitfield buffers,
1848 GLenum filter,
1849 int readColorAttachmentIndex,
1850 int drawColorAttachmentIndex)
1851{
1852 blitFramebuffer(target, targetRect, source, sourceRect,
1853 buffers, filter,
1854 readColorAttachmentIndex,
1855 drawColorAttachmentIndex,
1856 RestoreFrameBufferBinding);
1857}
1858
1859QT_END_NAMESPACE
1860