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

source code of qtbase/src/gui/opengl/qopenglframebufferobject.cpp