1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qopenglframebufferobject.h"
43 #include "qopenglframebufferobject_p.h"
46 #include <private/qopengl_p.h>
47 #include <private/qopenglcontext_p.h>
48 #include <private/qopenglextensions_p.h>
49 #include <private/qfont_p.h>
58 #define QT_RESET_GLERROR() \
60 while (glGetError() != GL_NO_ERROR) {} \
62 #define QT_CHECK_GLERROR() \
64 GLenum err = glGetError(); \
65 if (err != GL_NO_ERROR) { \
66 qDebug("[%s line %d] OpenGL Error: %d", \
67 __FILE__, __LINE__, (int)err); \
71 #define QT_RESET_GLERROR() {}
72 #define QT_CHECK_GLERROR() {}
75 // ####TODO Properly #ifdef this class to use #define symbols actually defined
76 // by OpenGL/ES includes
77 #ifndef GL_MAX_SAMPLES
78 #define GL_MAX_SAMPLES 0x8D57
81 #ifndef GL_RENDERBUFFER_SAMPLES
82 #define GL_RENDERBUFFER_SAMPLES 0x8CAB
85 #ifndef GL_DEPTH24_STENCIL8
86 #define GL_DEPTH24_STENCIL8 0x88F0
89 #ifndef GL_DEPTH_COMPONENT24
90 #define GL_DEPTH_COMPONENT24 0x81A6
93 #ifndef GL_READ_FRAMEBUFFER
94 #define GL_READ_FRAMEBUFFER 0x8CA8
97 #ifndef GL_DRAW_FRAMEBUFFER
98 #define GL_DRAW_FRAMEBUFFER 0x8CA9
102 \class QOpenGLFramebufferObjectFormat
103 \brief The QOpenGLFramebufferObjectFormat class specifies the format of an OpenGL
111 A framebuffer object has several characteristics:
113 \li \l{setSamples()}{Number of samples per pixels.}
114 \li \l{setAttachment()}{Depth and/or stencil attachments.}
115 \li \l{setTextureTarget()}{Texture target.}
116 \li \l{setInternalTextureFormat()}{Internal texture format.}
119 Note that the desired attachments or number of samples per pixels might not
120 be supported by the hardware driver. Call QOpenGLFramebufferObject::format()
121 after creating a QOpenGLFramebufferObject to find the exact format that was
122 used to create the frame buffer object.
124 \sa QOpenGLFramebufferObject
130 void QOpenGLFramebufferObjectFormat::detach()
132 if (d->ref.load() != 1) {
133 QOpenGLFramebufferObjectFormatPrivate *newd
134 = new QOpenGLFramebufferObjectFormatPrivate(d);
142 Creates a QOpenGLFramebufferObjectFormat object for specifying
143 the format of an OpenGL framebuffer object.
145 By default the format specifies a non-multisample framebuffer object with no
146 attachments, texture target \c GL_TEXTURE_2D, and internal format \c GL_RGBA8.
147 On OpenGL/ES systems, the default internal format is \c GL_RGBA.
149 \sa samples(), attachment(), internalTextureFormat()
152 QOpenGLFramebufferObjectFormat::QOpenGLFramebufferObjectFormat()
154 d = new QOpenGLFramebufferObjectFormatPrivate;
158 Constructs a copy of \a other.
161 QOpenGLFramebufferObjectFormat::QOpenGLFramebufferObjectFormat(const QOpenGLFramebufferObjectFormat &other)
168 Assigns \a other to this object.
171 QOpenGLFramebufferObjectFormat &QOpenGLFramebufferObjectFormat::operator=(const QOpenGLFramebufferObjectFormat &other)
183 Destroys the QOpenGLFramebufferObjectFormat.
185 QOpenGLFramebufferObjectFormat::~QOpenGLFramebufferObjectFormat()
192 Sets the number of samples per pixel for a multisample framebuffer object
193 to \a samples. The default sample count of 0 represents a regular
194 non-multisample framebuffer object.
196 If the desired amount of samples per pixel is not supported by the hardware
197 then the maximum number of samples per pixel will be used. Note that
198 multisample framebuffer objects can not be bound as textures. Also, the
199 \c{GL_EXT_framebuffer_multisample} extension is required to create a
200 framebuffer with more than one sample per pixel.
204 void QOpenGLFramebufferObjectFormat::setSamples(int samples)
207 d->samples = samples;
211 Returns the number of samples per pixel if a framebuffer object
212 is a multisample framebuffer object. Otherwise, returns 0.
213 The default value is 0.
217 int QOpenGLFramebufferObjectFormat::samples() const
223 Enables mipmapping if \a enabled is true; otherwise disables it.
225 Mipmapping is disabled by default.
227 If mipmapping is enabled, additional memory will be allocated for
228 the mipmap levels. The mipmap levels can be updated by binding the
229 texture and calling glGenerateMipmap(). Mipmapping cannot be enabled
230 for multisampled framebuffer objects.
232 \sa mipmap(), QOpenGLFramebufferObject::texture()
234 void QOpenGLFramebufferObjectFormat::setMipmap(bool enabled)
241 Returns true if mipmapping is enabled.
245 bool QOpenGLFramebufferObjectFormat::mipmap() const
251 Sets the attachment configuration of a framebuffer object to \a attachment.
255 void QOpenGLFramebufferObjectFormat::setAttachment(QOpenGLFramebufferObject::Attachment attachment)
258 d->attachment = attachment;
262 Returns the configuration of the depth and stencil buffers attached to
263 a framebuffer object. The default is QOpenGLFramebufferObject::NoAttachment.
267 QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObjectFormat::attachment() const
269 return d->attachment;
273 Sets the texture target of the texture attached to a framebuffer object to
274 \a target. Ignored for multisample framebuffer objects.
276 \sa textureTarget(), samples()
278 void QOpenGLFramebufferObjectFormat::setTextureTarget(GLenum target)
285 Returns the texture target of the texture attached to a framebuffer object.
286 Ignored for multisample framebuffer objects. The default is
289 \sa setTextureTarget(), samples()
291 GLenum QOpenGLFramebufferObjectFormat::textureTarget() const
297 Sets the internal format of a framebuffer object's texture or
298 multisample framebuffer object's color buffer to
299 \a internalTextureFormat.
301 \sa internalTextureFormat()
303 void QOpenGLFramebufferObjectFormat::setInternalTextureFormat(GLenum internalTextureFormat)
306 d->internal_format = internalTextureFormat;
310 Returns the internal format of a framebuffer object's texture or
311 multisample framebuffer object's color buffer. The default is
312 \c GL_RGBA8 on desktop OpenGL systems, and \c GL_RGBA on
315 \sa setInternalTextureFormat()
317 GLenum QOpenGLFramebufferObjectFormat::internalTextureFormat() const
319 return d->internal_format;
323 Returns true if all the options of this framebuffer object format
324 are the same as \a other; otherwise returns false.
326 bool QOpenGLFramebufferObjectFormat::operator==(const QOpenGLFramebufferObjectFormat& other) const
331 return d->equals(other.d);
335 Returns false if all the options of this framebuffer object format
336 are the same as \a other; otherwise returns true.
338 bool QOpenGLFramebufferObjectFormat::operator!=(const QOpenGLFramebufferObjectFormat& other) const
340 return !(*this == other);
343 bool QOpenGLFramebufferObjectPrivate::checkFramebufferStatus(QOpenGLContext *ctx) const
346 return false; // Context no longer exists.
347 GLenum status = ctx->functions()->glCheckFramebufferStatus(GL_FRAMEBUFFER);
350 case GL_FRAMEBUFFER_COMPLETE:
353 case GL_FRAMEBUFFER_UNSUPPORTED:
354 qDebug("QOpenGLFramebufferObject: Unsupported framebuffer format.");
356 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
357 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete attachment.");
359 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
360 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing attachment.");
362 #ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT
363 case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT:
364 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, duplicate attachment.");
367 #ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
368 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
369 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attached images must have same dimensions.");
372 #ifdef GL_FRAMEBUFFER_INCOMPLETE_FORMATS
373 case GL_FRAMEBUFFER_INCOMPLETE_FORMATS:
374 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attached images must have same format.");
377 #ifdef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER
378 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
379 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing draw buffer.");
382 #ifdef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER
383 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
384 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing read buffer.");
387 #ifdef GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE
388 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
389 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attachments must have same number of samples per pixel.");
393 qDebug() <<"QOpenGLFramebufferObject: An undefined error has occurred: "<< status;
401 void freeFramebufferFunc(QOpenGLFunctions *funcs, GLuint id)
403 funcs->glDeleteFramebuffers(1, &id);
406 void freeRenderbufferFunc(QOpenGLFunctions *funcs, GLuint id)
408 funcs->glDeleteRenderbuffers(1, &id);
411 void freeTextureFunc(QOpenGLFunctions *, GLuint id)
413 glDeleteTextures(1, &id);
417 void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSize &sz,
418 QOpenGLFramebufferObject::Attachment attachment,
419 GLenum texture_target, GLenum internal_format,
420 GLint samples, bool mipmap)
422 QOpenGLContext *ctx = QOpenGLContext::currentContext();
424 funcs.initializeOpenGLFunctions();
426 if (!funcs.hasOpenGLFeature(QOpenGLFunctions::Framebuffers))
430 target = texture_target;
431 // texture dimensions
433 QT_RESET_GLERROR(); // reset error state
436 funcs.glGenFramebuffers(1, &fbo);
437 funcs.glBindFramebuffer(GL_FRAMEBUFFER, fbo);
440 GLuint color_buffer = 0;
445 glGenTextures(1, &texture);
446 glBindTexture(target, texture);
448 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
449 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
450 glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
451 glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
453 glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0,
454 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
456 int width = size.width();
457 int height = size.height();
459 while (width > 1 || height > 1) {
460 width = qMax(1, width >> 1);
461 height = qMax(1, height >> 1);
463 glTexImage2D(target, level, internal_format, width, height, 0,
464 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
467 funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
471 valid = checkFramebufferStatus(ctx);
472 glBindTexture(target, 0);
478 glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
480 samples = qBound(0, int(samples), int(maxSamples));
482 funcs.glGenRenderbuffers(1, &color_buffer);
483 funcs.glBindRenderbuffer(GL_RENDERBUFFER, color_buffer);
484 if (funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample) && samples > 0) {
485 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
486 internal_format, size.width(), size.height());
489 funcs.glRenderbufferStorage(GL_RENDERBUFFER, internal_format,
490 size.width(), size.height());
493 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
494 GL_RENDERBUFFER, color_buffer);
497 valid = checkFramebufferStatus(ctx);
500 funcs.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
503 format.setTextureTarget(target);
504 format.setSamples(int(samples));
505 format.setInternalTextureFormat(internal_format);
506 format.setMipmap(mipmap);
508 initAttachments(ctx, attachment);
510 funcs.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);
512 fbo_guard = new QOpenGLSharedResourceGuard(ctx, fbo, freeFramebufferFunc);
514 color_buffer_guard = new QOpenGLSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc);
516 texture_guard = new QOpenGLSharedResourceGuard(ctx, texture, freeTextureFunc);
519 funcs.glDeleteRenderbuffers(1, &color_buffer);
521 glDeleteTextures(1, &texture);
522 funcs.glDeleteFramebuffers(1, &fbo);
527 void QOpenGLFramebufferObjectPrivate::initAttachments(QOpenGLContext *ctx, QOpenGLFramebufferObject::Attachment attachment)
529 int samples = format.samples();
531 // free existing attachments
532 if (depth_buffer_guard) {
533 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
534 depth_buffer_guard->free();
536 if (stencil_buffer_guard) {
537 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
538 if (stencil_buffer_guard != depth_buffer_guard)
539 stencil_buffer_guard->free();
542 depth_buffer_guard = 0;
543 stencil_buffer_guard = 0;
545 GLuint depth_buffer = 0;
546 GLuint stencil_buffer = 0;
548 // In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a
549 // separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer
550 // might not be supported while separate buffers are, according to QTBUG-12861.
552 if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil
553 && funcs.hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil))
555 // depth and stencil buffer needs another extension
556 funcs.glGenRenderbuffers(1, &depth_buffer);
557 funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
558 Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
559 if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
560 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
561 GL_DEPTH24_STENCIL8, size.width(), size.height());
563 funcs.glRenderbufferStorage(GL_RENDERBUFFER,
564 GL_DEPTH24_STENCIL8, size.width(), size.height());
566 stencil_buffer = depth_buffer;
567 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
568 GL_RENDERBUFFER, depth_buffer);
569 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
570 GL_RENDERBUFFER, stencil_buffer);
572 valid = checkFramebufferStatus(ctx);
574 funcs.glDeleteRenderbuffers(1, &depth_buffer);
575 stencil_buffer = depth_buffer = 0;
579 if (depth_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil
580 || (attachment == QOpenGLFramebufferObject::Depth)))
582 funcs.glGenRenderbuffers(1, &depth_buffer);
583 funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
584 Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
585 if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) {
587 if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) {
588 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
589 GL_DEPTH_COMPONENT24, size.width(), size.height());
591 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
592 GL_DEPTH_COMPONENT16, size.width(), size.height());
595 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
596 GL_DEPTH_COMPONENT, size.width(), size.height());
600 if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) {
601 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24,
602 size.width(), size.height());
604 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
605 size.width(), size.height());
608 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height());
611 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
612 GL_RENDERBUFFER, depth_buffer);
613 valid = checkFramebufferStatus(ctx);
615 funcs.glDeleteRenderbuffers(1, &depth_buffer);
620 if (stencil_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil)) {
621 funcs.glGenRenderbuffers(1, &stencil_buffer);
622 funcs.glBindRenderbuffer(GL_RENDERBUFFER, stencil_buffer);
623 Q_ASSERT(funcs.glIsRenderbuffer(stencil_buffer));
624 if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) {
626 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
627 GL_STENCIL_INDEX8, size.width(), size.height());
629 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
630 GL_STENCIL_INDEX, size.width(), size.height());
634 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8,
635 size.width(), size.height());
637 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX,
638 size.width(), size.height());
641 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
642 GL_RENDERBUFFER, stencil_buffer);
643 valid = checkFramebufferStatus(ctx);
645 funcs.glDeleteRenderbuffers(1, &stencil_buffer);
650 // The FBO might have become valid after removing the depth or stencil buffer.
651 valid = checkFramebufferStatus(ctx);
653 if (depth_buffer && stencil_buffer) {
654 fbo_attachment = QOpenGLFramebufferObject::CombinedDepthStencil;
655 } else if (depth_buffer) {
656 fbo_attachment = QOpenGLFramebufferObject::Depth;
658 fbo_attachment = QOpenGLFramebufferObject::NoAttachment;
663 depth_buffer_guard = new QOpenGLSharedResourceGuard(ctx, depth_buffer, freeRenderbufferFunc);
664 if (stencil_buffer) {
665 if (stencil_buffer == depth_buffer)
666 stencil_buffer_guard = depth_buffer_guard;
668 stencil_buffer_guard = new QOpenGLSharedResourceGuard(ctx, stencil_buffer, freeRenderbufferFunc);
672 funcs.glDeleteRenderbuffers(1, &depth_buffer);
673 if (stencil_buffer && depth_buffer != stencil_buffer)
674 funcs.glDeleteRenderbuffers(1, &stencil_buffer);
678 format.setAttachment(fbo_attachment);
682 \class QOpenGLFramebufferObject
683 \brief The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer object.
689 The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer
690 object, defined by the \c{GL_EXT_framebuffer_object} extension. In
691 addition it provides a rendering surface that can be painted on
692 with a QPainter, rendered to using native OpenGL calls, or both. This
693 surface can be bound and used as a regular texture in your own OpenGL
694 drawing code. By default, the QOpenGLFramebufferObject class
695 generates a 2D OpenGL texture (using the \c{GL_TEXTURE_2D} target),
696 which is used as the internal rendering target.
698 \b{It is important to have a current OpenGL context when creating a
699 QOpenGLFramebufferObject, otherwise initialization will fail.}
701 When using a QPainter to paint to a QOpenGLFramebufferObject you should take
702 care that the QOpenGLFramebufferObject is created with the CombinedDepthStencil
703 attachment for QPainter to be able to render correctly.
704 Note that you need to create a QOpenGLFramebufferObject with more than one
705 sample per pixel for primitives to be antialiased when drawing using a
706 QPainter. To create a multisample framebuffer object you should use one of
707 the constructors that take a QOpenGLFramebufferObject parameter, and set the
708 QOpenGLFramebufferObject::samples() property to a non-zero value.
710 For multisample framebuffer objects a color render buffer is created,
711 otherwise a texture with the specified texture target is created.
712 The color render buffer or texture will have the specified internal
713 format, and will be bound to the \c GL_COLOR_ATTACHMENT0
714 attachment in the framebuffer object.
716 If you want to use a framebuffer object with multisampling enabled
717 as a texture, you first need to copy from it to a regular framebuffer
718 object using QOpenGLContext::blitFramebuffer().
722 As of Qt 4.8, it's possible to draw into a QOpenGLFramebufferObject
723 using a QPainter in a separate thread. Note that OpenGL 2.0 or
724 OpenGL ES 2.0 is required for this to work.
729 \enum QOpenGLFramebufferObject::Attachment
731 This enum type is used to configure the depth and stencil buffers
732 attached to the framebuffer object when it is created.
734 \value NoAttachment No attachment is added to the framebuffer object. Note that the
735 OpenGL depth and stencil tests won't work when rendering to a
736 framebuffer object without any depth or stencil buffers.
737 This is the default value.
739 \value CombinedDepthStencil If the \c GL_EXT_packed_depth_stencil extension is present,
740 a combined depth and stencil buffer is attached.
741 If the extension is not present, only a depth buffer is attached.
743 \value Depth A depth buffer is attached to the framebuffer object.
749 /*! \fn QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, GLenum target)
751 Constructs an OpenGL framebuffer object and binds a 2D OpenGL texture
752 to the buffer of the size \a size. The texture is bound to the
753 \c GL_COLOR_ATTACHMENT0 target in the framebuffer object.
755 The \a target parameter is used to specify the OpenGL texture
756 target. The default target is \c GL_TEXTURE_2D. Keep in mind that
757 \c GL_TEXTURE_2D textures must have a power of 2 width and height
758 (e.g. 256x512), unless you are using OpenGL 2.0 or higher.
760 By default, no depth and stencil buffers are attached. This behavior
761 can be toggled using one of the overloaded constructors.
763 The default internal texture format is \c GL_RGBA8 for desktop
764 OpenGL, and \c GL_RGBA for OpenGL/ES.
766 It is important that you have a current OpenGL context set when
767 creating the QOpenGLFramebufferObject, otherwise the initialization
770 \sa size(), texture(), attachment()
773 QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, GLenum target)
774 : d_ptr(new QOpenGLFramebufferObjectPrivate)
776 Q_D(QOpenGLFramebufferObject);
777 d->init(this, size, NoAttachment, target, DEFAULT_FORMAT);
782 Constructs an OpenGL framebuffer object and binds a 2D OpenGL texture
783 to the buffer of the given \a width and \a height.
785 \sa size(), texture()
787 QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, GLenum target)
788 : d_ptr(new QOpenGLFramebufferObjectPrivate)
790 Q_D(QOpenGLFramebufferObject);
791 d->init(this, QSize(width, height), NoAttachment, target, DEFAULT_FORMAT);
796 Constructs an OpenGL framebuffer object of the given \a size based on the
800 QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, const QOpenGLFramebufferObjectFormat &format)
801 : d_ptr(new QOpenGLFramebufferObjectPrivate)
803 Q_D(QOpenGLFramebufferObject);
804 d->init(this, size, format.attachment(), format.textureTarget(), format.internalTextureFormat(),
805 format.samples(), format.mipmap());
810 Constructs an OpenGL framebuffer object of the given \a width and \a height
811 based on the supplied \a format.
814 QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, const QOpenGLFramebufferObjectFormat &format)
815 : d_ptr(new QOpenGLFramebufferObjectPrivate)
817 Q_D(QOpenGLFramebufferObject);
818 d->init(this, QSize(width, height), format.attachment(), format.textureTarget(),
819 format.internalTextureFormat(), format.samples(), format.mipmap());
824 Constructs an OpenGL framebuffer object and binds a texture to the
825 buffer of the given \a width and \a height.
827 The \a attachment parameter describes the depth/stencil buffer
828 configuration, \a target the texture target and \a internal_format
829 the internal texture format. The default texture target is \c
830 GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8
831 for desktop OpenGL and \c GL_RGBA for OpenGL/ES.
833 \sa size(), texture(), attachment()
835 QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, Attachment attachment,
836 GLenum target, GLenum internal_format)
837 : d_ptr(new QOpenGLFramebufferObjectPrivate)
839 Q_D(QOpenGLFramebufferObject);
840 d->init(this, QSize(width, height), attachment, target, internal_format);
845 Constructs an OpenGL framebuffer object and binds a texture to the
846 buffer of the given \a size.
848 The \a attachment parameter describes the depth/stencil buffer
849 configuration, \a target the texture target and \a internal_format
850 the internal texture format. The default texture target is \c
851 GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8
852 for desktop OpenGL and \c GL_RGBA for OpenGL/ES.
854 \sa size(), texture(), attachment()
856 QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, Attachment attachment,
857 GLenum target, GLenum internal_format)
858 : d_ptr(new QOpenGLFramebufferObjectPrivate)
860 Q_D(QOpenGLFramebufferObject);
861 d->init(this, size, attachment, target, internal_format);
865 \fn QOpenGLFramebufferObject::~QOpenGLFramebufferObject()
867 Destroys the framebuffer object and frees any allocated resources.
869 QOpenGLFramebufferObject::~QOpenGLFramebufferObject()
871 Q_D(QOpenGLFramebufferObject);
873 if (d->texture_guard)
874 d->texture_guard->free();
875 if (d->color_buffer_guard)
876 d->color_buffer_guard->free();
877 if (d->depth_buffer_guard)
878 d->depth_buffer_guard->free();
879 if (d->stencil_buffer_guard && d->stencil_buffer_guard != d->depth_buffer_guard)
880 d->stencil_buffer_guard->free();
882 d->fbo_guard->free();
886 \fn bool QOpenGLFramebufferObject::isValid() const
888 Returns true if the framebuffer object is valid.
890 The framebuffer can become invalid if the initialization process
891 fails, the user attaches an invalid buffer to the framebuffer
892 object, or a non-power of two width/height is specified as the
893 texture size if the texture target is \c{GL_TEXTURE_2D}.
894 The non-power of two limitation does not apply if the OpenGL version
895 is 2.0 or higher, or if the GL_ARB_texture_non_power_of_two extension
898 The framebuffer can also become invalid if the QOpenGLContext that
899 the framebuffer was created within is destroyed and there are
900 no other shared contexts that can take over ownership of the
903 bool QOpenGLFramebufferObject::isValid() const
905 Q_D(const QOpenGLFramebufferObject);
906 return d->valid && d->fbo_guard && d->fbo_guard->id();
910 \fn bool QOpenGLFramebufferObject::bind()
912 Switches rendering from the default, windowing system provided
913 framebuffer to this framebuffer object.
914 Returns true upon success, false otherwise.
918 bool QOpenGLFramebufferObject::bind()
922 Q_D(QOpenGLFramebufferObject);
923 QOpenGLContext *current = QOpenGLContext::currentContext();
927 if (current->shareGroup() != d->fbo_guard->group())
928 qWarning("QOpenGLFramebufferObject::bind() called from incompatible context");
930 d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo());
931 d->valid = d->checkFramebufferStatus(current);
932 if (d->valid && current)
933 current->d_func()->current_fbo = d->fbo();
938 \fn bool QOpenGLFramebufferObject::release()
940 Switches rendering back to the default, windowing system provided
942 Returns true upon success, false otherwise.
946 bool QOpenGLFramebufferObject::release()
951 QOpenGLContext *current = QOpenGLContext::currentContext();
955 Q_D(QOpenGLFramebufferObject);
957 if (current->shareGroup() != d->fbo_guard->group())
958 qWarning("QOpenGLFramebufferObject::release() called from incompatible context");
962 current->d_func()->current_fbo = current->defaultFramebufferObject();
963 d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->d_func()->current_fbo);
970 \fn GLuint QOpenGLFramebufferObject::texture() const
972 Returns the texture id for the texture attached as the default
973 rendering target in this framebuffer object. This texture id can
974 be bound as a normal texture in your own OpenGL code.
976 If a multisample framebuffer object is used then the value returned
977 from this function will be invalid.
979 GLuint QOpenGLFramebufferObject::texture() const
981 Q_D(const QOpenGLFramebufferObject);
982 return d->texture_guard ? d->texture_guard->id() : 0;
986 \fn QSize QOpenGLFramebufferObject::size() const
988 Returns the size of the texture attached to this framebuffer
991 QSize QOpenGLFramebufferObject::size() const
993 Q_D(const QOpenGLFramebufferObject);
998 \fn int QOpenGLFramebufferObject::width() const
1000 Returns the width of the framebuffer object attachments.
1004 \fn int QOpenGLFramebufferObject::height() const
1006 Returns the height of the framebuffer object attachments.
1010 Returns the format of this framebuffer object.
1012 QOpenGLFramebufferObjectFormat QOpenGLFramebufferObject::format() const
1014 Q_D(const QOpenGLFramebufferObject);
1020 Read back the contents of the currently bound framebuffer, used in
1021 QGLWidget::grabFrameBuffer(), QGLPixelbuffer::toImage() and
1022 QGLFramebufferObject::toImage()
1025 void convertFromGLImage(QImage &img, int w, int h, bool alpha_format, bool include_alpha)
1027 if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
1028 // OpenGL gives RGBA; Qt wants ARGB
1029 uint *p = (uint*)img.bits();
1030 uint *end = p + w*h;
1031 if (alpha_format && include_alpha) {
1038 // This is an old legacy fix for PowerPC based Macs, which
1039 // we shouldn't remove
1041 *p = 0xff000000 | (*p>>8);
1046 // OpenGL gives ABGR (i.e. RGBA backwards); Qt wants ARGB
1047 for (int y = 0; y < h; y++) {
1048 uint *q = (uint*)img.scanLine(y);
1049 for (int x=0; x < w; ++x) {
1050 const uint pixel = *q;
1051 if (alpha_format && include_alpha) {
1052 *q = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff)
1053 | (pixel & 0xff00ff00);
1055 *q = 0xff000000 | ((pixel << 16) & 0xff0000)
1056 | ((pixel >> 16) & 0xff) | (pixel & 0x00ff00);
1064 img = img.mirrored();
1069 Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha)
1071 QImage img(size, (alpha_format && include_alpha) ? QImage::Format_ARGB32_Premultiplied
1072 : QImage::Format_RGB32);
1073 int w = size.width();
1074 int h = size.height();
1075 glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
1076 convertFromGLImage(img, w, h, alpha_format, include_alpha);
1081 \fn QImage QOpenGLFramebufferObject::toImage() const
1083 Returns the contents of this framebuffer object as a QImage.
1085 QImage QOpenGLFramebufferObject::toImage() const
1087 Q_D(const QOpenGLFramebufferObject);
1091 // qt_gl_read_framebuffer doesn't work on a multisample FBO
1092 if (format().samples() != 0) {
1093 QOpenGLFramebufferObject temp(size(), QOpenGLFramebufferObjectFormat());
1095 QRect rect(QPoint(0, 0), size());
1096 blitFramebuffer(&temp, rect, const_cast<QOpenGLFramebufferObject *>(this), rect);
1098 return temp.toImage();
1101 bool wasBound = isBound();
1103 const_cast<QOpenGLFramebufferObject *>(this)->bind();
1104 QImage image = qt_gl_read_framebuffer(d->size, format().internalTextureFormat() != GL_RGB, true);
1106 const_cast<QOpenGLFramebufferObject *>(this)->release();
1112 \fn bool QOpenGLFramebufferObject::bindDefault()
1115 Switches rendering back to the default, windowing system provided
1117 Returns true upon success, false otherwise.
1119 \sa bind(), release()
1121 bool QOpenGLFramebufferObject::bindDefault()
1123 QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
1124 QOpenGLFunctions functions(ctx);
1127 ctx->d_func()->current_fbo = ctx->defaultFramebufferObject();
1128 functions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);
1131 qWarning("QOpenGLFramebufferObject::bindDefault() called without current context.");
1139 \fn bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()
1141 Returns true if the OpenGL \c{GL_EXT_framebuffer_object} extension
1142 is present on this system; otherwise returns false.
1144 bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()
1146 return QOpenGLFunctions(QOpenGLContext::currentContext()).hasOpenGLFeature(QOpenGLFunctions::Framebuffers);
1150 \fn GLuint QOpenGLFramebufferObject::handle() const
1152 Returns the OpenGL framebuffer object handle for this framebuffer
1153 object (returned by the \c{glGenFrameBuffersEXT()} function). This
1154 handle can be used to attach new images or buffers to the
1155 framebuffer. The user is responsible for cleaning up and
1156 destroying these objects.
1158 GLuint QOpenGLFramebufferObject::handle() const
1160 Q_D(const QOpenGLFramebufferObject);
1165 Returns the status of the depth and stencil buffers attached to
1166 this framebuffer object.
1169 QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObject::attachment() const
1171 Q_D(const QOpenGLFramebufferObject);
1173 return d->fbo_attachment;
1174 return NoAttachment;
1178 Sets the attachments of the framebuffer object to \a attachment.
1180 This can be used to free or reattach the depth and stencil buffer
1181 attachments as needed.
1183 void QOpenGLFramebufferObject::setAttachment(QOpenGLFramebufferObject::Attachment attachment)
1185 Q_D(QOpenGLFramebufferObject);
1186 if (attachment == d->fbo_attachment || !isValid())
1188 QOpenGLContext *current = QOpenGLContext::currentContext();
1192 if (current->shareGroup() != d->fbo_guard->group())
1193 qWarning("QOpenGLFramebufferObject::setAttachment() called from incompatible context");
1195 d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo());
1196 d->initAttachments(current, attachment);
1197 if (current->d_func()->current_fbo != d->fbo())
1198 d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->d_func()->current_fbo);
1202 Returns true if the framebuffer object is currently bound to a context,
1203 otherwise false is returned.
1206 bool QOpenGLFramebufferObject::isBound() const
1208 Q_D(const QOpenGLFramebufferObject);
1209 QOpenGLContext *current = QOpenGLContext::currentContext();
1210 return current ? current->d_func()->current_fbo == d->fbo() : false;
1214 \fn bool QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()
1216 Returns true if the OpenGL \c{GL_EXT_framebuffer_blit} extension
1217 is present on this system; otherwise returns false.
1219 \sa blitFramebuffer()
1221 bool QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()
1223 return QOpenGLExtensions(QOpenGLContext::currentContext()).hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
1230 Convenience overload to blit between two framebuffer objects.
1232 void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target,
1233 QOpenGLFramebufferObject *source,
1234 GLbitfield buffers, GLenum filter)
1236 if (!target && !source)
1243 targetSize = target->size();
1245 sourceSize = source->size();
1247 if (targetSize.isEmpty())
1248 targetSize = sourceSize;
1249 else if (sourceSize.isEmpty())
1250 sourceSize = targetSize;
1252 blitFramebuffer(target, QRect(QPoint(0, 0), targetSize),
1253 source, QRect(QPoint(0, 0), sourceSize),
1258 Blits from the \a sourceRect rectangle in the \a source framebuffer
1259 object to the \a targetRect rectangle in the \a target framebuffer object.
1261 If \a source or \a target is 0, the default framebuffer will be used
1262 instead of a framebuffer object as source or target respectively.
1264 The \a buffers parameter should be a mask consisting of any combination of
1265 \c GL_COLOR_BUFFER_BIT, \c GL_DEPTH_BUFFER_BIT, and
1266 \c GL_STENCIL_BUFFER_BIT. Any buffer type that is not present both
1267 in the source and target buffers is ignored.
1269 The \a sourceRect and \a targetRect rectangles may have different sizes;
1270 in this case \a buffers should not contain \c GL_DEPTH_BUFFER_BIT or
1271 \c GL_STENCIL_BUFFER_BIT. The \a filter parameter should be set to
1272 \c GL_LINEAR or \c GL_NEAREST, and specifies whether linear or nearest
1273 interpolation should be used when scaling is performed.
1275 If \a source equals \a target a copy is performed within the same buffer.
1276 Results are undefined if the source and target rectangles overlap and
1277 have different sizes. The sizes must also be the same if any of the
1278 framebuffer objects are multisample framebuffers.
1280 Note that the scissor test will restrict the blit area if enabled.
1282 This function will have no effect unless hasOpenGLFramebufferBlit() returns
1285 \sa hasOpenGLFramebufferBlit()
1287 void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect,
1288 QOpenGLFramebufferObject *source, const QRect &sourceRect,
1292 QOpenGLContext *ctx = QOpenGLContext::currentContext();
1296 QOpenGLExtensions extensions(ctx);
1297 if (!extensions.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit))
1300 const int sx0 = sourceRect.left();
1301 const int sx1 = sourceRect.left() + sourceRect.width();
1302 const int sy0 = sourceRect.top();
1303 const int sy1 = sourceRect.top() + sourceRect.height();
1305 const int tx0 = targetRect.left();
1306 const int tx1 = targetRect.left() + targetRect.width();
1307 const int ty0 = targetRect.top();
1308 const int ty1 = targetRect.top() + targetRect.height();
1310 extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, source ? source->handle() : 0);
1311 extensions.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target ? target->handle() : 0);
1313 extensions.glBlitFramebuffer(sx0, sy0, sx1, sy1,
1317 extensions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);