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

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