Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qopenglframebufferobject.cpp
Go to the documentation of this file.
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
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
20
21Q_TRACE_PREFIX(qtopengl,
22 "#include <private/qopengl2pexvertexarray_p.h>"
23 "#include <private/qopengltextureuploader_p.h>"
24 "#include <qopenglframebufferobject.h>"
25);
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.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
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("QOpenGLFramebufferObject: Unsupported framebuffer format.");
402 break;
403 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
404 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete attachment.");
405 break;
406 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
407 qDebug("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("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("QOpenGLFramebufferObject: Framebuffer incomplete, missing read buffer.");
432 break;
433#endif
434#ifdef GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE
435 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
436 qDebug("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(1, &id);
451 }
452
453 void freeRenderbufferFunc(QOpenGLFunctions *funcs, GLuint id)
454 {
455 funcs->glDeleteRenderbuffers(1, &id);
456 }
457
458 void freeTextureFunc(QOpenGLFunctions *funcs, GLuint id)
459 {
460 funcs->glDeleteTextures(1, &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(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(QOpenGLExtensions::FramebufferMultisample)
482 || !funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit)) {
483 samples = 0;
484 } else if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
485 GLint maxSamples;
486 funcs.glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
487 samples = qBound(0, int(samples), int(maxSamples));
488 }
489
490 colorAttachments.append(ColorAttachment(size, internal_format));
491
492 dsSize = size;
493
494 samples = qMax(0, 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(1, &fbo);
503 funcs.glBindFramebuffer(GL_FRAMEBUFFER, fbo);
504
505 QOpenGLContextPrivate::get(ctx)->qgl_current_fbo_invalid = true;
506
508
509 format.setTextureTarget(target);
510 format.setInternalTextureFormat(internal_format);
511 format.setMipmap(mipmap);
512
513 if (samples == 0)
514 initTexture(0);
515 else
516 initColorBuffer(0, &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(1, &fbo);
526
528}
529
530void QOpenGLFramebufferObjectPrivate::initTexture(int idx)
531{
532 QOpenGLContext *ctx = QOpenGLContext::currentContext();
533 GLuint texture = 0;
534
535 funcs.glGenTextures(1, &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)
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 bool isOpaque = false;
554 switch (color.internalFormat) {
555 case GL_RGB8:
556 case GL_RGB16:
557 case GL_RGB16F:
558 case GL_RGB32F:
559 isOpaque = true;
560 break;
561 case GL_RGB10:
562 // opaque but the pixel type (INT_2_10_10_10) has alpha and so requires RGBA texture format
563 break;
564 }
565 const GLuint textureFormat = isOpaque ? GL_RGB : GL_RGBA;
566
567 funcs.glTexImage2D(target, 0, color.internalFormat, color.size.width(), color.size.height(), 0,
568 textureFormat, pixelType, nullptr);
569 if (format.mipmap()) {
570 int width = color.size.width();
571 int height = color.size.height();
572 int level = 0;
573 while (width > 1 || height > 1) {
574 width = qMax(1, width >> 1);
575 height = qMax(1, height >> 1);
576 ++level;
577 funcs.glTexImage2D(target, level, color.internalFormat, width, height, 0, textureFormat,
578 pixelType, nullptr);
579 }
580 }
581 funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + idx,
582 target, texture, 0);
583
585 funcs.glBindTexture(target, 0);
586 valid = checkFramebufferStatus(ctx);
587 if (valid) {
588 color.guard = new QOpenGLSharedResourceGuard(ctx, texture, freeTextureFunc);
589 } else {
590 funcs.glDeleteTextures(1, &texture);
591 }
592}
593
594void QOpenGLFramebufferObjectPrivate::initColorBuffer(int idx, GLint *samples)
595{
596 QOpenGLContext *ctx = QOpenGLContext::currentContext();
597 GLuint color_buffer = 0;
598
599 ColorAttachment &color(colorAttachments[idx]);
600
601 GLenum storageFormat = color.internalFormat;
602 // ES requires a sized format. The older desktop extension does not. Correct the format on ES.
603 if (ctx->isOpenGLES()) {
604 if (color.internalFormat == GL_RGBA) {
605 if (funcs.hasOpenGLExtension(QOpenGLExtensions::Sized8Formats))
606 storageFormat = GL_RGBA8;
607 else
608 storageFormat = GL_RGBA4;
609 } else if (color.internalFormat == GL_RGB10) {
610 // GL_RGB10 is not allowed in ES for glRenderbufferStorage.
611 storageFormat = GL_RGB10_A2;
612 }
613 }
614
615 funcs.glGenRenderbuffers(1, &color_buffer);
616 funcs.glBindRenderbuffer(GL_RENDERBUFFER, color_buffer);
617 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, *samples, storageFormat, color.size.width(), color.size.height());
618 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + idx,
619 GL_RENDERBUFFER, color_buffer);
620
622 valid = checkFramebufferStatus(ctx);
623 if (valid) {
624 // Query the actual number of samples. This can be greater than the requested
625 // value since the typically supported values are 0, 4, 8, ..., and the
626 // requests are mapped to the next supported value.
627 funcs.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, samples);
628 color.guard = new QOpenGLSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc);
629 } else {
630 funcs.glDeleteRenderbuffers(1, &color_buffer);
631 }
632}
633
634void QOpenGLFramebufferObjectPrivate::initDepthStencilAttachments(QOpenGLContext *ctx,
635 QOpenGLFramebufferObject::Attachment attachment)
636{
637 // Use the same sample count for all attachments. format.samples() already contains
638 // the actual number of samples for the color attachment and is not suitable. Use
639 // requestedSamples instead.
640 const int samples = requestedSamples;
641
642 // free existing attachments
643 if (depth_buffer_guard) {
644#ifdef Q_OS_WASM
645 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
646#else
647 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
648#endif
649 depth_buffer_guard->free();
650 }
651 if (stencil_buffer_guard) {
652 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
653 if (stencil_buffer_guard != depth_buffer_guard)
654 stencil_buffer_guard->free();
655 }
656
657 depth_buffer_guard = nullptr;
658 stencil_buffer_guard = nullptr;
659
660 GLuint depth_buffer = 0;
661 GLuint stencil_buffer = 0;
662
663 // In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a
664 // separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer
665 // might not be supported while separate buffers are, according to QTBUG-12861.
666#ifdef Q_OS_WASM
667 // WebGL doesn't allow separately attach buffers to
668 // STENCIL_ATTACHMENT and DEPTH_ATTACHMENT
669 // QTBUG-69913
670 if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil) {
671 funcs.glGenRenderbuffers(1, &depth_buffer);
672 funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
673 Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
674
675 if (samples != 0 ) {
676 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
677 GL_DEPTH24_STENCIL8, dsSize.width(), dsSize.height());
678 } else {
679 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL,
680 dsSize.width(), dsSize.height());
681 }
682
683 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
684 GL_RENDERBUFFER, depth_buffer);
685
686 valid = checkFramebufferStatus(ctx);
687 if (!valid) {
688 funcs.glDeleteRenderbuffers(1, &depth_buffer);
689 depth_buffer = 0;
690 }
691 }
692#else
693 if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil
694 && funcs.hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil))
695 {
696 // depth and stencil buffer needs another extension
697 funcs.glGenRenderbuffers(1, &depth_buffer);
698 funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
699 Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
700 if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
701 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
702 GL_DEPTH24_STENCIL8, dsSize.width(), dsSize.height());
703 else
704 funcs.glRenderbufferStorage(GL_RENDERBUFFER,
705 GL_DEPTH24_STENCIL8, dsSize.width(), dsSize.height());
706
707 stencil_buffer = depth_buffer;
708 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
709 GL_RENDERBUFFER, depth_buffer);
710 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
711 GL_RENDERBUFFER, stencil_buffer);
712
713 valid = checkFramebufferStatus(ctx);
714 if (!valid) {
715 funcs.glDeleteRenderbuffers(1, &depth_buffer);
716 stencil_buffer = depth_buffer = 0;
717 }
718 }
719
720 if (depth_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil
721 || (attachment == QOpenGLFramebufferObject::Depth)))
722 {
723 funcs.glGenRenderbuffers(1, &depth_buffer);
724 funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
725 Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
726 if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) {
727 if (ctx->isOpenGLES()) {
728 if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24))
729 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
730 GL_DEPTH_COMPONENT24, dsSize.width(), dsSize.height());
731 else
732 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
733 GL_DEPTH_COMPONENT16, dsSize.width(), dsSize.height());
734 } else {
735 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
736 GL_DEPTH_COMPONENT, dsSize.width(), dsSize.height());
737 }
738 } else {
739 if (ctx->isOpenGLES()) {
740 if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) {
741 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24,
742 dsSize.width(), dsSize.height());
743 } else {
744 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
745 dsSize.width(), dsSize.height());
746 }
747 } else {
748 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, dsSize.width(), dsSize.height());
749 }
750 }
751 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
752 GL_RENDERBUFFER, depth_buffer);
753 valid = checkFramebufferStatus(ctx);
754 if (!valid) {
755 funcs.glDeleteRenderbuffers(1, &depth_buffer);
756 depth_buffer = 0;
757 }
758 }
759
760 if (stencil_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil)) {
761 funcs.glGenRenderbuffers(1, &stencil_buffer);
762 funcs.glBindRenderbuffer(GL_RENDERBUFFER, stencil_buffer);
763 Q_ASSERT(funcs.glIsRenderbuffer(stencil_buffer));
764
765#if QT_CONFIG(opengles2)
766 GLenum storage = GL_STENCIL_INDEX8;
767#else
768 GLenum storage = ctx->isOpenGLES() ? GL_STENCIL_INDEX8 : GL_STENCIL_INDEX;
769#endif
770
771 if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
772 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, storage, dsSize.width(), dsSize.height());
773 else
774 funcs.glRenderbufferStorage(GL_RENDERBUFFER, storage, dsSize.width(), dsSize.height());
775
776 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
777 GL_RENDERBUFFER, stencil_buffer);
778 valid = checkFramebufferStatus(ctx);
779 if (!valid) {
780 funcs.glDeleteRenderbuffers(1, &stencil_buffer);
781 stencil_buffer = 0;
782 }
783 }
784#endif //Q_OS_WASM
785
786 // The FBO might have become valid after removing the depth or stencil buffer.
787 valid = checkFramebufferStatus(ctx);
788
789#ifdef Q_OS_WASM
790 if (depth_buffer) {
791#else
792 if (depth_buffer && stencil_buffer) {
793#endif
794 fbo_attachment = QOpenGLFramebufferObject::CombinedDepthStencil;
795 } else if (depth_buffer) {
796 fbo_attachment = QOpenGLFramebufferObject::Depth;
797 } else {
798 fbo_attachment = QOpenGLFramebufferObject::NoAttachment;
799 }
800
801 if (valid) {
802 if (depth_buffer)
803 depth_buffer_guard = new QOpenGLSharedResourceGuard(ctx, depth_buffer, freeRenderbufferFunc);
804 if (stencil_buffer) {
805 if (stencil_buffer == depth_buffer)
806 stencil_buffer_guard = depth_buffer_guard;
807 else
808 stencil_buffer_guard = new QOpenGLSharedResourceGuard(ctx, stencil_buffer, freeRenderbufferFunc);
809 }
810 } else {
811 if (depth_buffer)
812 funcs.glDeleteRenderbuffers(1, &depth_buffer);
813 if (stencil_buffer && depth_buffer != stencil_buffer)
814 funcs.glDeleteRenderbuffers(1, &stencil_buffer);
815 }
817
818 format.setAttachment(fbo_attachment);
819}
820
821/*!
822 \class QOpenGLFramebufferObject
823 \brief The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer object.
824 \since 5.0
825 \inmodule QtOpenGL
826
827 \ingroup painting-3D
828
829 The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer
830 object, defined by the \c{GL_EXT_framebuffer_object} extension. It provides
831 a rendering surface that can be painted on with a QPainter with the help of
832 QOpenGLPaintDevice, or rendered to using native OpenGL calls. This surface
833 can be bound and used as a regular texture in your own OpenGL drawing code.
834 By default, the QOpenGLFramebufferObject class generates a 2D OpenGL
835 texture (using the \c{GL_TEXTURE_2D} target), which is used as the internal
836 rendering target.
837
838 \b{It is important to have a current OpenGL context when creating a
839 QOpenGLFramebufferObject, otherwise initialization will fail.}
840
841 Create the QOpenGLFrameBufferObject instance with the CombinedDepthStencil
842 attachment if you want QPainter to render correctly. Note that you need to
843 create a QOpenGLFramebufferObject with more than one sample per pixel for
844 primitives to be antialiased when drawing using a QPainter. To create a
845 multisample framebuffer object you should use one of the constructors that
846 take a QOpenGLFramebufferObjectFormat parameter, and set the
847 QOpenGLFramebufferObjectFormat::samples() property to a non-zero value.
848
849 For multisample framebuffer objects a color render buffer is created,
850 otherwise a texture with the specified texture target is created.
851 The color render buffer or texture will have the specified internal
852 format, and will be bound to the \c GL_COLOR_ATTACHMENT0
853 attachment in the framebuffer object.
854
855 Multiple render targets are also supported, in case the OpenGL
856 implementation supports this. Here there will be multiple textures (or, in
857 case of multisampling, renderbuffers) present and each of them will get
858 attached to \c GL_COLOR_ATTACHMENT0, \c 1, \c 2, ...
859
860 If you want to use a framebuffer object with multisampling enabled
861 as a texture, you first need to copy from it to a regular framebuffer
862 object using QOpenGLContext::blitFramebuffer().
863
864 It is possible to draw into a QOpenGLFramebufferObject using QPainter and
865 QOpenGLPaintDevice in a separate thread.
866*/
867
868
869/*!
870 \enum QOpenGLFramebufferObject::Attachment
871
872 This enum type is used to configure the depth and stencil buffers
873 attached to the framebuffer object when it is created.
874
875 \value NoAttachment No attachment is added to the framebuffer object. Note that the
876 OpenGL depth and stencil tests won't work when rendering to a
877 framebuffer object without any depth or stencil buffers.
878 This is the default value.
879
880 \value CombinedDepthStencil If the \c GL_EXT_packed_depth_stencil extension is present,
881 a combined depth and stencil buffer is attached.
882 If the extension is not present, only a depth buffer is attached.
883
884 \value Depth A depth buffer is attached to the framebuffer object.
885
886 \sa attachment()
887*/
888
889static inline GLenum effectiveInternalFormat(GLenum internalFormat)
890{
891 if (!internalFormat)
892#if QT_CONFIG(opengles2)
893 internalFormat = GL_RGBA;
894#else
895 internalFormat = QOpenGLContext::currentContext()->isOpenGLES() ? GL_RGBA : GL_RGBA8;
896#endif
897 return internalFormat;
898}
899
900/*!
901
902 Constructs an OpenGL framebuffer object and binds a 2D OpenGL texture
903 to the buffer of the size \a size. The texture is bound to the
904 \c GL_COLOR_ATTACHMENT0 target in the framebuffer object.
905
906 The \a target parameter is used to specify the OpenGL texture
907 target. The default target is \c GL_TEXTURE_2D. Keep in mind that
908 \c GL_TEXTURE_2D textures must have a power of 2 width and height
909 (e.g. 256x512), unless you are using OpenGL 2.0 or higher.
910
911 By default, no depth and stencil buffers are attached. This behavior
912 can be toggled using one of the overloaded constructors.
913
914 The default internal texture format is \c GL_RGBA8 for desktop
915 OpenGL, and \c GL_RGBA for OpenGL/ES.
916
917 It is important that you have a current OpenGL context set when
918 creating the QOpenGLFramebufferObject, otherwise the initialization
919 will fail.
920
921 \sa size(), texture(), attachment()
922*/
923
924QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, GLenum target)
925 : d_ptr(new QOpenGLFramebufferObjectPrivate)
926{
927 Q_D(QOpenGLFramebufferObject);
928 d->init(this, size, NoAttachment, target, effectiveInternalFormat(0));
929}
930
931/*!
932
933 Constructs an OpenGL framebuffer object and binds a 2D OpenGL texture
934 to the buffer of the given \a width and \a height.
935
936 \sa size(), texture()
937*/
938QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, GLenum target)
939 : QOpenGLFramebufferObject(QSize(width, height), target)
940{
941}
942
943/*!
944
945 Constructs an OpenGL framebuffer object of the given \a size based on the
946 supplied \a format.
947*/
948
949QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, const QOpenGLFramebufferObjectFormat &format)
950 : d_ptr(new QOpenGLFramebufferObjectPrivate)
951{
952 Q_D(QOpenGLFramebufferObject);
953 d->init(this, size, format.attachment(), format.textureTarget(), format.internalTextureFormat(),
954 format.samples(), format.mipmap());
955}
956
957/*!
958
959 Constructs an OpenGL framebuffer object of the given \a width and \a height
960 based on the supplied \a format.
961*/
962
963QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, const QOpenGLFramebufferObjectFormat &format)
964 : QOpenGLFramebufferObject(QSize(width, height), format)
965{
966}
967
968/*!
969
970 Constructs an OpenGL framebuffer object and binds a texture to the
971 buffer of the given \a width and \a height.
972
973 The \a attachment parameter describes the depth/stencil buffer
974 configuration, \a target the texture target and \a internalFormat
975 the internal texture format. The default texture target is \c
976 GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8
977 for desktop OpenGL and \c GL_RGBA for OpenGL/ES.
978
979 \sa size(), texture(), attachment()
980*/
981QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, Attachment attachment,
982 GLenum target, GLenum internalFormat)
983 : d_ptr(new QOpenGLFramebufferObjectPrivate)
984{
985 Q_D(QOpenGLFramebufferObject);
986 d->init(this, QSize(width, height), attachment, target, effectiveInternalFormat(internalFormat));
987}
988
989/*!
990
991 Constructs an OpenGL framebuffer object and binds a texture to the
992 buffer of the given \a size.
993
994 The \a attachment parameter describes the depth/stencil buffer
995 configuration, \a target the texture target and \a internalFormat
996 the internal texture format. The default texture target is \c
997 GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8
998 for desktop OpenGL and \c GL_RGBA for OpenGL/ES.
999
1000 \sa size(), texture(), attachment()
1001*/
1002QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, Attachment attachment,
1003 GLenum target, GLenum internalFormat)
1004 : d_ptr(new QOpenGLFramebufferObjectPrivate)
1005{
1006 Q_D(QOpenGLFramebufferObject);
1007 d->init(this, size, attachment, target, effectiveInternalFormat(internalFormat));
1008}
1009
1010/*!
1011
1012 Destroys the framebuffer object and frees any allocated resources.
1013*/
1014QOpenGLFramebufferObject::~QOpenGLFramebufferObject()
1015{
1016 Q_D(QOpenGLFramebufferObject);
1017 if (isBound())
1018 release();
1019
1020 for (const auto &color : std::as_const(d->colorAttachments)) {
1021 if (color.guard)
1022 color.guard->free();
1023 }
1024 d->colorAttachments.clear();
1025
1026 if (d->depth_buffer_guard)
1027 d->depth_buffer_guard->free();
1028 if (d->stencil_buffer_guard && d->stencil_buffer_guard != d->depth_buffer_guard)
1029 d->stencil_buffer_guard->free();
1030 if (d->fbo_guard)
1031 d->fbo_guard->free();
1032
1033 QOpenGLContextPrivate *contextPrv = QOpenGLContextPrivate::get(QOpenGLContext::currentContext());
1034 if (contextPrv && contextPrv->qgl_current_fbo == this) {
1035 contextPrv->qgl_current_fbo_invalid = true;
1036 contextPrv->qgl_current_fbo = nullptr;
1037 }
1038}
1039
1040/*!
1041 Creates and attaches an additional texture or renderbuffer of \a size width
1042 and height.
1043
1044 There is always an attachment at GL_COLOR_ATTACHMENT0. Call this function
1045 to set up additional attachments at GL_COLOR_ATTACHMENT1,
1046 GL_COLOR_ATTACHMENT2, ...
1047
1048 When \a internalFormat is not \c 0, it specifies the internal format of the
1049 texture or renderbuffer. Otherwise a default of GL_RGBA or GL_RGBA8 is
1050 used.
1051
1052 \note This is only functional when multiple render targets are supported by
1053 the OpenGL implementation. When that is not the case, the function will not
1054 add any additional color attachments. Call
1055 QOpenGLFunctions::hasOpenGLFeature() with
1056 QOpenGLFunctions::MultipleRenderTargets at runtime to check if MRT is
1057 supported.
1058
1059 \note The internal format of the color attachments may differ but there may
1060 be limitations on the supported combinations, depending on the drivers.
1061
1062 \note The size of the color attachments may differ but rendering is limited
1063 to the area that fits all the attachments, according to the OpenGL
1064 specification. Some drivers may not be fully conformant in this respect,
1065 however.
1066
1067 \since 5.6
1068 */
1069void QOpenGLFramebufferObject::addColorAttachment(const QSize &size, GLenum internalFormat)
1070{
1071 Q_D(QOpenGLFramebufferObject);
1072
1073 if (!QOpenGLContext::currentContext()->functions()->hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets)) {
1074 qWarning("Multiple render targets not supported, ignoring extra color attachment request");
1075 return;
1076 }
1077
1078 QOpenGLFramebufferObjectPrivate::ColorAttachment color(size, effectiveInternalFormat(internalFormat));
1079 d->colorAttachments.append(color);
1080 const int idx = d->colorAttachments.size() - 1;
1081
1082 if (d->requestedSamples == 0) {
1083 d->initTexture(idx);
1084 } else {
1085 GLint samples = d->requestedSamples;
1086 d->initColorBuffer(idx, &samples);
1087 }
1088}
1089
1090/*! \overload
1091
1092 Creates and attaches an additional texture or renderbuffer of size \a width and \a height.
1093
1094 When \a internalFormat is not \c 0, it specifies the internal format of the texture or
1095 renderbuffer. Otherwise a default of GL_RGBA or GL_RGBA8 is used.
1096
1097 \since 5.6
1098 */
1099void QOpenGLFramebufferObject::addColorAttachment(int width, int height, GLenum internalFormat)
1100{
1101 addColorAttachment(QSize(width, height), internalFormat);
1102}
1103
1104/*!
1105 \fn bool QOpenGLFramebufferObject::isValid() const
1106
1107 Returns \c true if the framebuffer object is valid.
1108
1109 The framebuffer can become invalid if the initialization process
1110 fails, the user attaches an invalid buffer to the framebuffer
1111 object, or a non-power of two width/height is specified as the
1112 texture size if the texture target is \c{GL_TEXTURE_2D}.
1113 The non-power of two limitation does not apply if the OpenGL version
1114 is 2.0 or higher, or if the GL_ARB_texture_non_power_of_two extension
1115 is present.
1116
1117 The framebuffer can also become invalid if the QOpenGLContext that
1118 the framebuffer was created within is destroyed and there are
1119 no other shared contexts that can take over ownership of the
1120 framebuffer.
1121*/
1122bool QOpenGLFramebufferObject::isValid() const
1123{
1124 Q_D(const QOpenGLFramebufferObject);
1125 return d->valid && d->fbo_guard && d->fbo_guard->id();
1126}
1127
1128/*!
1129 \fn bool QOpenGLFramebufferObject::bind()
1130
1131 Switches rendering from the default, windowing system provided
1132 framebuffer to this framebuffer object.
1133 Returns \c true upon success, false otherwise.
1134
1135 \note If takeTexture() was called, a new texture is created and associated
1136 with the framebuffer object. This is potentially expensive and changes the
1137 context state (the currently bound texture).
1138
1139 \sa release()
1140*/
1141bool QOpenGLFramebufferObject::bind()
1142{
1143 if (!isValid())
1144 return false;
1145 Q_D(QOpenGLFramebufferObject);
1146 QOpenGLContext *current = QOpenGLContext::currentContext();
1147 if (!current)
1148 return false;
1149#ifdef QT_DEBUG
1150 if (current->shareGroup() != d->fbo_guard->group())
1151 qWarning("QOpenGLFramebufferObject::bind() called from incompatible context");
1152#endif
1153
1154 d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo());
1155
1156 QOpenGLContextPrivate::get(current)->qgl_current_fbo_invalid = true;
1157 QOpenGLContextPrivate::get(current)->qgl_current_fbo = this;
1158
1159 if (d->format.samples() == 0) {
1160 // Create new textures to replace the ones stolen via takeTexture().
1161 for (int i = 0; i < d->colorAttachments.size(); ++i) {
1162 if (!d->colorAttachments.at(i).guard)
1163 d->initTexture(i);
1164 }
1165 }
1166
1167 return d->valid;
1168}
1169
1170/*!
1171 \fn bool QOpenGLFramebufferObject::release()
1172
1173 Switches rendering back to the default, windowing system provided
1174 framebuffer.
1175 Returns \c true upon success, false otherwise.
1176
1177 \sa bind()
1178*/
1179bool QOpenGLFramebufferObject::release()
1180{
1181 if (!isValid())
1182 return false;
1183
1184 QOpenGLContext *current = QOpenGLContext::currentContext();
1185 if (!current)
1186 return false;
1187
1188 Q_D(QOpenGLFramebufferObject);
1189#ifdef QT_DEBUG
1190 if (current->shareGroup() != d->fbo_guard->group())
1191 qWarning("QOpenGLFramebufferObject::release() called from incompatible context");
1192#endif
1193
1194 if (current) {
1195 d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->defaultFramebufferObject());
1196
1197 QOpenGLContextPrivate *contextPrv = QOpenGLContextPrivate::get(current);
1198 contextPrv->qgl_current_fbo_invalid = true;
1199 contextPrv->qgl_current_fbo = nullptr;
1200 }
1201
1202 return true;
1203}
1204
1205/*!
1206 \fn GLuint QOpenGLFramebufferObject::texture() const
1207
1208 Returns the texture id for the texture attached as the default
1209 rendering target in this framebuffer object. This texture id can
1210 be bound as a normal texture in your own OpenGL code.
1211
1212 If a multisample framebuffer object is used then the value returned
1213 from this function will be invalid.
1214
1215 When multiple textures are attached, the return value is the ID of
1216 the first one.
1217
1218 \sa takeTexture(), textures()
1219*/
1220GLuint QOpenGLFramebufferObject::texture() const
1221{
1222 Q_D(const QOpenGLFramebufferObject);
1223 return d->colorAttachments[0].guard ? d->colorAttachments[0].guard->id() : 0;
1224}
1225
1226/*!
1227 Returns the texture id for all attached textures.
1228
1229 If a multisample framebuffer object is used, then an empty vector is returned.
1230
1231 \since 5.6
1232
1233 \sa takeTexture(), texture()
1234*/
1235QList<GLuint> QOpenGLFramebufferObject::textures() const
1236{
1237 Q_D(const QOpenGLFramebufferObject);
1238 QList<GLuint> ids;
1239 if (d->format.samples() != 0)
1240 return ids;
1241 ids.reserve(d->colorAttachments.size());
1242 for (const auto &color : d->colorAttachments)
1243 ids.append(color.guard ? color.guard->id() : 0);
1244 return ids;
1245}
1246
1247/*!
1248 \fn GLuint QOpenGLFramebufferObject::takeTexture()
1249
1250 Returns the texture id for the texture attached to this framebuffer
1251 object. The ownership of the texture is transferred to the caller.
1252
1253 If the framebuffer object is currently bound, an implicit release()
1254 will be done. During the next call to bind() a new texture will be
1255 created.
1256
1257 If a multisample framebuffer object is used, then there is no
1258 texture and the return value from this function will be invalid.
1259 Similarly, incomplete framebuffer objects will also return 0.
1260
1261 \since 5.3
1262
1263 \sa texture(), bind(), release()
1264 */
1265GLuint QOpenGLFramebufferObject::takeTexture()
1266{
1267 return takeTexture(0);
1268}
1269
1270/*! \overload
1271
1272 Returns the texture id for the texture attached to the color attachment of
1273 index \a colorAttachmentIndex of this framebuffer object. The ownership of
1274 the texture is transferred to the caller.
1275
1276 When \a colorAttachmentIndex is \c 0, the behavior is identical to the
1277 parameter-less variant of this function.
1278
1279 If the framebuffer object is currently bound, an implicit release()
1280 will be done. During the next call to bind() a new texture will be
1281 created.
1282
1283 If a multisample framebuffer object is used, then there is no
1284 texture and the return value from this function will be invalid.
1285 Similarly, incomplete framebuffer objects will also return 0.
1286
1287 \since 5.6
1288 */
1289GLuint QOpenGLFramebufferObject::takeTexture(int colorAttachmentIndex)
1290{
1291 Q_D(QOpenGLFramebufferObject);
1292 GLuint id = 0;
1293 if (isValid() && d->format.samples() == 0 && d->colorAttachments.size() > colorAttachmentIndex) {
1294 QOpenGLContext *current = QOpenGLContext::currentContext();
1295 if (current && current->shareGroup() == d->fbo_guard->group() && isBound())
1296 release();
1297 auto &guard = d->colorAttachments[colorAttachmentIndex].guard;
1298 id = guard ? guard->id() : 0;
1299 // Do not call free() on texture_guard, just null it out.
1300 // This way the texture will not be deleted when the guard is destroyed.
1301 guard = nullptr;
1302 }
1303 return id;
1304}
1305
1306/*!
1307 \return the size of the color and depth/stencil attachments attached to
1308 this framebuffer object.
1309*/
1310QSize QOpenGLFramebufferObject::size() const
1311{
1312 Q_D(const QOpenGLFramebufferObject);
1313 return d->dsSize;
1314}
1315
1316/*!
1317 \return the sizes of all color attachments attached to this framebuffer
1318 object.
1319
1320 \since 5.6
1321*/
1322QList<QSize> QOpenGLFramebufferObject::sizes() const
1323{
1324 Q_D(const QOpenGLFramebufferObject);
1325 QList<QSize> sz;
1326 sz.reserve(d->colorAttachments.size());
1327 for (const auto &color : d->colorAttachments)
1328 sz.append(color.size);
1329 return sz;
1330}
1331
1332/*!
1333 \fn int QOpenGLFramebufferObject::width() const
1334
1335 Returns the width of the framebuffer object attachments.
1336*/
1337
1338/*!
1339 \fn int QOpenGLFramebufferObject::height() const
1340
1341 Returns the height of the framebuffer object attachments.
1342*/
1343
1344/*!
1345 Returns the format of this framebuffer object.
1346*/
1347QOpenGLFramebufferObjectFormat QOpenGLFramebufferObject::format() const
1348{
1349 Q_D(const QOpenGLFramebufferObject);
1350 return d->format;
1351}
1352
1353static inline QImage qt_gl_read_framebuffer_rgba8(const QSize &size, bool include_alpha, QOpenGLContext *context)
1354{
1355 QOpenGLFunctions *funcs = context->functions();
1356 const int w = size.width();
1357 const int h = size.height();
1358 bool isOpenGL12orBetter = !context->isOpenGLES() && (context->format().majorVersion() >= 2 || context->format().minorVersion() >= 2);
1359 if (isOpenGL12orBetter) {
1360 QImage img(size, include_alpha ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32);
1361 if (!img.isNull())
1362 funcs->glReadPixels(0, 0, w, h, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, img.bits());
1363 return img;
1364 }
1365
1366 // For OpenGL ES stick with the byte ordered format / RGBA readback format
1367 // since that is the only spec mandated way. (also, skip the
1368 // GL_IMPLEMENTATION_COLOR_READ_FORMAT mess since there is nothing saying a
1369 // BGRA capable impl would return BGRA from there)
1370
1371 QImage rgbaImage(size, include_alpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGBX8888);
1372 if (!rgbaImage.isNull())
1373 funcs->glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, rgbaImage.bits());
1374 return rgbaImage;
1375}
1376
1377static inline QImage qt_gl_read_framebuffer_rgb10a2(const QSize &size, bool include_alpha, QOpenGLContext *context)
1378{
1379 // We assume OpenGL 1.2+ or ES 3.0+ here.
1380 QImage img(size, include_alpha ? QImage::Format_A2BGR30_Premultiplied : QImage::Format_BGR30);
1381 if (!img.isNull())
1382 context->functions()->glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, img.bits());
1383 return img;
1384}
1385
1386static inline QImage qt_gl_read_framebuffer_rgba16(const QSize &size, bool include_alpha, QOpenGLContext *context)
1387{
1388 // We assume OpenGL 1.2+ or ES 3.0+ here.
1389 QImage img(size, include_alpha ? QImage::Format_RGBA64_Premultiplied : QImage::Format_RGBX64);
1390 if (!img.isNull())
1391 context->functions()->glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_SHORT, img.bits());
1392 return img;
1393}
1394
1395static inline QImage qt_gl_read_framebuffer_rgba16f(const QSize &size, bool include_alpha, QOpenGLContext *context)
1396{
1397 // We assume OpenGL (ES) 3.0+ here.
1398 QImage img(size, include_alpha ? QImage::Format_RGBA16FPx4_Premultiplied : QImage::Format_RGBX16FPx4);
1399 if (!img.isNull())
1400 context->functions()->glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_HALF_FLOAT, img.bits());
1401 return img;
1402}
1403
1404static inline QImage qt_gl_read_framebuffer_rgba32f(const QSize &size, bool include_alpha, QOpenGLContext *context)
1405{
1406 QImage img(size, include_alpha ? QImage::Format_RGBA32FPx4_Premultiplied : QImage::Format_RGBX32FPx4);
1407 if (!img.isNull())
1408 context->functions()->glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_FLOAT, img.bits());
1409 return img;
1410}
1411
1412static QImage qt_gl_read_framebuffer(const QSize &size, GLenum internal_format, bool include_alpha, bool flip)
1413{
1414 QOpenGLContext *ctx = QOpenGLContext::currentContext();
1415 QOpenGLFunctions *funcs = ctx->functions();
1416 while (true) {
1417 GLenum error = funcs->glGetError();
1418 if (error == GL_NO_ERROR || error == GL_CONTEXT_LOST)
1419 break;
1420 }
1421 Qt::Orientations orient = flip ? Qt::Vertical : Qt::Orientations{};
1422 switch (internal_format) {
1423 case GL_RGB:
1424 case GL_RGB8:
1425 return qt_gl_read_framebuffer_rgba8(size, false, ctx).flipped(orient);
1426 case GL_RGB10:
1427 return qt_gl_read_framebuffer_rgb10a2(size, false, ctx).flipped(orient);
1428 case GL_RGB10_A2:
1429 return qt_gl_read_framebuffer_rgb10a2(size, include_alpha, ctx).flipped(orient);
1430 case GL_RGB16:
1431 return qt_gl_read_framebuffer_rgba16(size, false, ctx).flipped(orient);
1432 case GL_RGBA16:
1433 return qt_gl_read_framebuffer_rgba16(size, include_alpha, ctx).flipped(orient);
1434 case GL_RGB16F:
1435 return qt_gl_read_framebuffer_rgba16f(size, false, ctx).flipped(orient);
1436 case GL_RGBA16F:
1437 return qt_gl_read_framebuffer_rgba16f(size, include_alpha, ctx).flipped(orient);
1438 case GL_RGB32F:
1439 return qt_gl_read_framebuffer_rgba32f(size, false, ctx).flipped(orient);
1440 case GL_RGBA32F:
1441 return qt_gl_read_framebuffer_rgba32f(size, include_alpha, ctx).flipped(orient);
1442 case GL_RGBA:
1443 case GL_RGBA8:
1444 default:
1445 return qt_gl_read_framebuffer_rgba8(size, include_alpha, ctx).flipped(orient);
1446 }
1447
1448 Q_UNREACHABLE_RETURN(QImage());
1449}
1450
1451Q_OPENGL_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha)
1452{
1453 return qt_gl_read_framebuffer(size, alpha_format ? GL_RGBA : GL_RGB, include_alpha, true);
1454}
1455
1456/*!
1457 \fn QImage QOpenGLFramebufferObject::toImage(bool flipped) const
1458
1459 Returns the contents of this framebuffer object as a QImage.
1460
1461 If \a flipped is true the image is flipped from OpenGL coordinates to raster coordinates.
1462 If used together with QOpenGLPaintDevice, \a flipped should be the opposite of the value
1463 of QOpenGLPaintDevice::paintFlipped().
1464
1465 The returned image has a format of premultiplied ARGB32 or RGB32. The latter
1466 is used only when internalTextureFormat() is set to \c GL_RGB. Since Qt 5.2
1467 the function will fall back to premultiplied RGBA8888 or RGBx8888 when
1468 reading to (A)RGB32 is not supported, and this includes OpenGL ES. Since Qt
1469 5.4 an A2BGR30 image is returned if the internal format is RGB10_A2, and since
1470 Qt 5.12 a RGBA64 image is return if the internal format is RGBA16.
1471
1472 If the rendering in the framebuffer was not done with premultiplied alpha in mind,
1473 create a wrapper QImage with a non-premultiplied format. This is necessary before
1474 performing operations like QImage::save() because otherwise the image data would get
1475 unpremultiplied, even though it was not premultiplied in the first place. To create
1476 such a wrapper without performing a copy of the pixel data, do the following:
1477
1478 \code
1479 QImage fboImage(fbo.toImage());
1480 QImage image(fboImage.constBits(), fboImage.width(), fboImage.height(), QImage::Format_ARGB32);
1481 \endcode
1482
1483 For multisampled framebuffer objects the samples are resolved using the
1484 \c{GL_EXT_framebuffer_blit} extension. If the extension is not available, the contents
1485 of the returned image is undefined.
1486
1487 For singlesampled framebuffers the contents is retrieved via \c glReadPixels. This is
1488 a potentially expensive and inefficient operation. Therefore it is recommended that
1489 this function is used as seldom as possible.
1490
1491 \sa QOpenGLPaintDevice::paintFlipped()
1492*/
1493
1494QImage QOpenGLFramebufferObject::toImage(bool flipped) const
1495{
1496 return toImage(flipped, 0);
1497}
1498
1499/*! \overload
1500
1501 Returns the contents of the color attachment of index \a
1502 colorAttachmentIndex of this framebuffer object as a QImage. This method
1503 flips the image from OpenGL coordinates to raster coordinates when \a
1504 flipped is set to \c true.
1505
1506 \note This overload is only fully functional when multiple render targets are
1507 supported by the OpenGL implementation. When that is not the case, only one
1508 color attachment will be set up.
1509
1510 \since 5.6
1511*/
1512QImage QOpenGLFramebufferObject::toImage(bool flipped, int colorAttachmentIndex) const
1513{
1514 Q_D(const QOpenGLFramebufferObject);
1515 if (!d->valid)
1516 return QImage();
1517
1518 QOpenGLContext *ctx = QOpenGLContext::currentContext();
1519 if (!ctx) {
1520 qWarning("QOpenGLFramebufferObject::toImage() called without a current context");
1521 return QImage();
1522 }
1523
1524 if (d->colorAttachments.size() <= colorAttachmentIndex) {
1525 qWarning("QOpenGLFramebufferObject::toImage() called for missing color attachment");
1526 return QImage();
1527 }
1528
1529 GLuint prevFbo = 0;
1530 ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &prevFbo);
1531
1532 if (prevFbo != d->fbo())
1533 const_cast<QOpenGLFramebufferObject *>(this)->bind();
1534
1535 QImage image;
1536 QOpenGLExtraFunctions *extraFuncs = ctx->extraFunctions();
1537 // qt_gl_read_framebuffer doesn't work on a multisample FBO
1538 if (format().samples() != 0) {
1539 QRect rect(QPoint(0, 0), size());
1540 QOpenGLFramebufferObjectFormat fmt;
1541 if (extraFuncs->hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets)) {
1542 fmt.setInternalTextureFormat(d->colorAttachments[colorAttachmentIndex].internalFormat);
1543 QOpenGLFramebufferObject temp(d->colorAttachments[colorAttachmentIndex].size, fmt);
1544 blitFramebuffer(&temp, rect, const_cast<QOpenGLFramebufferObject *>(this), rect,
1545 GL_COLOR_BUFFER_BIT, GL_NEAREST,
1546 colorAttachmentIndex, 0);
1547 image = temp.toImage(flipped);
1548 } else {
1549 fmt.setInternalTextureFormat(d->colorAttachments[0].internalFormat);
1550 QOpenGLFramebufferObject temp(size(), fmt);
1551 blitFramebuffer(&temp, rect, const_cast<QOpenGLFramebufferObject *>(this), rect);
1552 image = temp.toImage(flipped);
1553 }
1554 } else {
1555 if (extraFuncs->hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets)) {
1556 extraFuncs->glReadBuffer(GL_COLOR_ATTACHMENT0 + colorAttachmentIndex);
1557 image = qt_gl_read_framebuffer(d->colorAttachments[colorAttachmentIndex].size,
1558 d->colorAttachments[colorAttachmentIndex].internalFormat,
1559 true, flipped);
1560 extraFuncs->glReadBuffer(GL_COLOR_ATTACHMENT0);
1561 } else {
1562 image = qt_gl_read_framebuffer(d->colorAttachments[0].size,
1563 d->colorAttachments[0].internalFormat,
1564 true, flipped);
1565 }
1566 }
1567
1568 if (prevFbo != d->fbo())
1569 ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, prevFbo);
1570
1571 return image;
1572}
1573
1574/*!
1575 \fn bool QOpenGLFramebufferObject::bindDefault()
1576
1577 Switches rendering back to the default, windowing system provided
1578 framebuffer.
1579 Returns \c true upon success, false otherwise.
1580
1581 \sa bind(), release()
1582*/
1583bool QOpenGLFramebufferObject::bindDefault()
1584{
1585 QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
1586
1587 if (ctx) {
1588 ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
1589 QOpenGLContextPrivate::get(ctx)->qgl_current_fbo_invalid = true;
1590 QOpenGLContextPrivate::get(ctx)->qgl_current_fbo = nullptr;
1591 }
1592#ifdef QT_DEBUG
1593 else
1594 qWarning("QOpenGLFramebufferObject::bindDefault() called without current context.");
1595#endif
1596
1597 return ctx != nullptr;
1598}
1599
1600/*!
1601 \fn bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()
1602
1603 Returns \c true if the OpenGL \c{GL_EXT_framebuffer_object} extension
1604 is present on this system; otherwise returns \c false.
1605*/
1606bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()
1607{
1608 return QOpenGLContext::currentContext()->functions()->hasOpenGLFeature(QOpenGLFunctions::Framebuffers);
1609}
1610
1611/*!
1612 \fn GLuint QOpenGLFramebufferObject::handle() const
1613
1614 Returns the OpenGL framebuffer object handle for this framebuffer
1615 object (returned by the \c{glGenFrameBuffersEXT()} function). This
1616 handle can be used to attach new images or buffers to the
1617 framebuffer. The user is responsible for cleaning up and
1618 destroying these objects.
1619*/
1620GLuint QOpenGLFramebufferObject::handle() const
1621{
1622 Q_D(const QOpenGLFramebufferObject);
1623 return d->fbo();
1624}
1625
1626/*!
1627 Returns the status of the depth and stencil buffers attached to
1628 this framebuffer object.
1629*/
1630
1631QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObject::attachment() const
1632{
1633 Q_D(const QOpenGLFramebufferObject);
1634 if (d->valid)
1635 return d->fbo_attachment;
1636 return NoAttachment;
1637}
1638
1639/*!
1640 Sets the attachments of the framebuffer object to \a attachment.
1641
1642 This can be used to free or reattach the depth and stencil buffer
1643 attachments as needed.
1644
1645 \note This function alters the current framebuffer binding.
1646 */
1647void QOpenGLFramebufferObject::setAttachment(QOpenGLFramebufferObject::Attachment attachment)
1648{
1649 Q_D(QOpenGLFramebufferObject);
1650 if (attachment == d->fbo_attachment || !isValid())
1651 return;
1652 QOpenGLContext *current = QOpenGLContext::currentContext();
1653 if (!current)
1654 return;
1655#ifdef QT_DEBUG
1656 if (current->shareGroup() != d->fbo_guard->group())
1657 qWarning("QOpenGLFramebufferObject::setAttachment() called from incompatible context");
1658#endif
1659 d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo());
1660 QOpenGLContextPrivate::get(current)->qgl_current_fbo_invalid = true;
1661 d->initDepthStencilAttachments(current, attachment);
1662}
1663
1664/*!
1665 Returns \c true if the framebuffer object is currently bound to the current context,
1666 otherwise false is returned.
1667*/
1668bool QOpenGLFramebufferObject::isBound() const
1669{
1670 Q_D(const QOpenGLFramebufferObject);
1671 QOpenGLContext *ctx = QOpenGLContext::currentContext();
1672 if (!ctx)
1673 return false;
1674 GLint fbo = 0;
1675 ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
1676 return GLuint(fbo) == d->fbo();
1677}
1678
1679/*!
1680 \fn bool QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()
1681
1682 Returns \c true if the OpenGL \c{GL_EXT_framebuffer_blit} extension
1683 is present on this system; otherwise returns \c false.
1684
1685 \sa blitFramebuffer()
1686*/
1687bool QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()
1688{
1689 return QOpenGLExtensions(QOpenGLContext::currentContext()).hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
1690}
1691
1692
1693/*!
1694 \overload
1695
1696 Convenience overload to blit between two framebuffer objects.
1697*/
1698void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target,
1699 QOpenGLFramebufferObject *source,
1700 GLbitfield buffers, GLenum filter)
1701{
1702 if (!target && !source)
1703 return;
1704
1705 QSize targetSize;
1706 QSize sourceSize;
1707
1708 if (target)
1709 targetSize = target->size();
1710 if (source)
1711 sourceSize = source->size();
1712
1713 if (targetSize.isEmpty())
1714 targetSize = sourceSize;
1715 else if (sourceSize.isEmpty())
1716 sourceSize = targetSize;
1717
1718 blitFramebuffer(target, QRect(QPoint(0, 0), targetSize),
1719 source, QRect(QPoint(0, 0), sourceSize),
1720 buffers, filter);
1721}
1722
1723/*! \overload
1724 *
1725 Convenience overload to blit between two framebuffer objects.
1726*/
1727void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect,
1728 QOpenGLFramebufferObject *source, const QRect &sourceRect,
1729 GLbitfield buffers,
1730 GLenum filter)
1731{
1732 blitFramebuffer(target, targetRect, source, sourceRect, buffers, filter, 0, 0);
1733}
1734
1735/*!
1736 \enum QOpenGLFramebufferObject::FramebufferRestorePolicy
1737 \since 5.7
1738
1739 This enum type is used to configure the behavior related to restoring
1740 framebuffer bindings when calling blitFramebuffer().
1741
1742 \value DontRestoreFramebufferBinding Do not restore the previous framebuffer binding.
1743 The caller is responsible for tracking and setting
1744 the framebuffer binding as needed.
1745
1746 \value RestoreFramebufferBindingToDefault After the blit operation, bind the default
1747 framebuffer.
1748
1749 \value RestoreFrameBufferBinding Restore the previously bound framebuffer. This is
1750 potentially expensive because of the need to
1751 query the currently bound framebuffer.
1752
1753 \sa blitFramebuffer()
1754*/
1755
1756/*!
1757 \since 5.7
1758
1759 Blits from the \a sourceRect rectangle in the \a source framebuffer
1760 object to the \a targetRect rectangle in the \a target framebuffer object.
1761
1762 If \a source or \a target is 0, the default framebuffer will be used
1763 instead of a framebuffer object as source or target respectively.
1764
1765 This function will have no effect unless hasOpenGLFramebufferBlit() returns
1766 true.
1767
1768 The \a buffers parameter should be a mask consisting of any combination of
1769 \c GL_COLOR_BUFFER_BIT, \c GL_DEPTH_BUFFER_BIT, and
1770 \c GL_STENCIL_BUFFER_BIT. Any buffer type that is not present both
1771 in the source and target buffers is ignored.
1772
1773 The \a sourceRect and \a targetRect rectangles may have different sizes;
1774 in this case \a buffers should not contain \c GL_DEPTH_BUFFER_BIT or
1775 \c GL_STENCIL_BUFFER_BIT. The \a filter parameter should be set to
1776 \c GL_LINEAR or \c GL_NEAREST, and specifies whether linear or nearest
1777 interpolation should be used when scaling is performed.
1778
1779 If \a source equals \a target a copy is performed within the same buffer.
1780 Results are undefined if the source and target rectangles overlap and
1781 have different sizes. The sizes must also be the same if any of the
1782 framebuffer objects are multisample framebuffers.
1783
1784 \note The scissor test will restrict the blit area if enabled.
1785
1786 When multiple render targets are in use, \a readColorAttachmentIndex and \a
1787 drawColorAttachmentIndex specify the index of the color attachments in the
1788 source and destination framebuffers.
1789
1790 The \a restorePolicy determines if the framebuffer that was bound prior to
1791 calling this function should be restored, or if the default framebuffer
1792 should be bound before returning, of if the caller is responsible for
1793 tracking and setting the bound framebuffer. Restoring the previous
1794 framebuffer can be relatively expensive due to the call to \c{glGetIntegerv}
1795 which on some OpenGL drivers may imply a pipeline stall.
1796
1797 \sa hasOpenGLFramebufferBlit()
1798*/
1799void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect,
1800 QOpenGLFramebufferObject *source, const QRect &sourceRect,
1801 GLbitfield buffers,
1802 GLenum filter,
1803 int readColorAttachmentIndex,
1804 int drawColorAttachmentIndex,
1805 QOpenGLFramebufferObject::FramebufferRestorePolicy restorePolicy)
1806{
1807 QOpenGLContext *ctx = QOpenGLContext::currentContext();
1808 if (!ctx)
1809 return;
1810
1811 QOpenGLExtensions extensions(ctx);
1812 if (!extensions.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit))
1813 return;
1814
1815 GLuint prevFbo = 0;
1816 if (restorePolicy == RestoreFrameBufferBinding)
1817 ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &prevFbo);
1818
1819 const int sx0 = sourceRect.left();
1820 const int sx1 = sourceRect.left() + sourceRect.width();
1821 const int sy0 = sourceRect.top();
1822 const int sy1 = sourceRect.top() + sourceRect.height();
1823
1824 const int tx0 = targetRect.left();
1825 const int tx1 = targetRect.left() + targetRect.width();
1826 const int ty0 = targetRect.top();
1827 const int ty1 = targetRect.top() + targetRect.height();
1828
1829 const GLuint defaultFboId = ctx->defaultFramebufferObject();
1830
1831 extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, source ? source->handle() : defaultFboId);
1832 extensions.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target ? target->handle() : defaultFboId);
1833
1834 const bool supportsMRT = extensions.hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets);
1835 if (supportsMRT) {
1836 extensions.glReadBuffer(GL_COLOR_ATTACHMENT0 + readColorAttachmentIndex);
1837 if (target) {
1838 GLenum drawBuf = GL_COLOR_ATTACHMENT0 + drawColorAttachmentIndex;
1839 extensions.glDrawBuffers(1, &drawBuf);
1840 }
1841 }
1842
1843 extensions.glBlitFramebuffer(sx0, sy0, sx1, sy1,
1844 tx0, ty0, tx1, ty1,
1845 buffers, filter);
1846
1847 if (supportsMRT)
1848 extensions.glReadBuffer(GL_COLOR_ATTACHMENT0);
1849
1850 switch (restorePolicy) {
1851 case RestoreFrameBufferBinding:
1852 ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, prevFbo); // sets both READ and DRAW
1853 break;
1854
1855 case RestoreFramebufferBindingToDefault:
1856 ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); // sets both READ and DRAW
1857 break;
1858
1859 case DontRestoreFramebufferBinding:
1860 break;
1861 }
1862}
1863
1864/*!
1865 \overload
1866
1867 Convenience overload to blit between two framebuffer objects and
1868 to restore the previous framebuffer binding. Equivalent to calling
1869 blitFramebuffer(target, targetRect, source, sourceRect, buffers, filter,
1870 readColorAttachmentIndex, drawColorAttachmentIndex,
1871 RestoreFrameBufferBinding).
1872*/
1873void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect,
1874 QOpenGLFramebufferObject *source, const QRect &sourceRect,
1875 GLbitfield buffers,
1876 GLenum filter,
1877 int readColorAttachmentIndex,
1878 int drawColorAttachmentIndex)
1879{
1880 blitFramebuffer(target, targetRect, source, sourceRect,
1881 buffers, filter,
1882 readColorAttachmentIndex,
1883 drawColorAttachmentIndex,
1884 RestoreFrameBufferBinding);
1885}
1886
1887QT_END_NAMESPACE
bool checkFramebufferStatus(QOpenGLContext *ctx) const
Combined button and popup list for selecting options.
Q_TRACE_METADATA(qtcore, "ENUM { AUTO, RANGE User ... MaxUser } QEvent::Type;")
#define GL_CONTEXT_LOST
Definition qopengl.cpp:30
#define GL_RENDERBUFFER_SAMPLES
#define GL_DEPTH24_STENCIL8
#define GL_RGBA16F
Definition qopenglext.h:913
#define GL_RGB16F
Definition qopenglext.h:914
#define GL_BGRA
Definition qopenglext.h:97
#define GL_DRAW_FRAMEBUFFER
#define GL_MAX_SAMPLES
#define GL_HALF_FLOAT
#define GL_READ_FRAMEBUFFER
#define GL_RGBA32F
Definition qopenglext.h:911
#define GL_DEPTH_COMPONENT24
Definition qopenglext.h:329
#define GL_RGB32F
Definition qopenglext.h:912
#define GL_UNSIGNED_INT_8_8_8_8_REV
Definition qopenglext.h:94
#define GL_RGB10
#define GL_RGB16
#define QT_RESET_GLERROR()
#define GL_RGB8
#define QT_CHECK_GLERROR()
Q_TRACE_PARAM_REPLACE(GLenum, int)
#define GL_RGBA16
#define GL_UNSIGNED_INT_2_10_10_10_REV
#define GL_RGBA8