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