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] GL Error: %d", \
67 __FILE__, __LINE__, (int)err); \
71 #define QT_RESET_GLERROR() {}
72 #define QT_CHECK_GLERROR() {}
76 \class QOpenGLFramebufferObjectFormat
77 \brief The QOpenGLFramebufferObjectFormat class specifies the format of an OpenGL
84 A framebuffer object has several characteristics:
86 \i \link setSamples() Number of samples per pixels.\endlink
87 \i \link setAttachment() Depth and/or stencil attachments.\endlink
88 \i \link setTextureTarget() Texture target.\endlink
89 \i \link setInternalTextureFormat() Internal texture format.\endlink
92 Note that the desired attachments or number of samples per pixels might not
93 be supported by the hardware driver. Call QOpenGLFramebufferObject::format()
94 after creating a QOpenGLFramebufferObject to find the exact format that was
95 used to create the frame buffer object.
97 \sa QOpenGLFramebufferObject
103 void QOpenGLFramebufferObjectFormat::detach()
105 if (d->ref.load() != 1) {
106 QOpenGLFramebufferObjectFormatPrivate *newd
107 = new QOpenGLFramebufferObjectFormatPrivate(d);
115 Creates a QOpenGLFramebufferObjectFormat object for specifying
116 the format of an OpenGL framebuffer object.
118 By default the format specifies a non-multisample framebuffer object with no
119 attachments, texture target \c GL_TEXTURE_2D, and internal format \c GL_RGBA8.
120 On OpenGL/ES systems, the default internal format is \c GL_RGBA.
122 \sa samples(), attachment(), internalTextureFormat()
125 QOpenGLFramebufferObjectFormat::QOpenGLFramebufferObjectFormat()
127 d = new QOpenGLFramebufferObjectFormatPrivate;
131 Constructs a copy of \a other.
134 QOpenGLFramebufferObjectFormat::QOpenGLFramebufferObjectFormat(const QOpenGLFramebufferObjectFormat &other)
141 Assigns \a other to this object.
144 QOpenGLFramebufferObjectFormat &QOpenGLFramebufferObjectFormat::operator=(const QOpenGLFramebufferObjectFormat &other)
156 Destroys the QOpenGLFramebufferObjectFormat.
158 QOpenGLFramebufferObjectFormat::~QOpenGLFramebufferObjectFormat()
165 Sets the number of samples per pixel for a multisample framebuffer object
166 to \a samples. The default sample count of 0 represents a regular
167 non-multisample framebuffer object.
169 If the desired amount of samples per pixel is not supported by the hardware
170 then the maximum number of samples per pixel will be used. Note that
171 multisample framebuffer objects can not be bound as textures. Also, the
172 \c{GL_EXT_framebuffer_multisample} extension is required to create a
173 framebuffer with more than one sample per pixel.
177 void QOpenGLFramebufferObjectFormat::setSamples(int samples)
180 d->samples = samples;
184 Returns the number of samples per pixel if a framebuffer object
185 is a multisample framebuffer object. Otherwise, returns 0.
186 The default value is 0.
190 int QOpenGLFramebufferObjectFormat::samples() const
196 Enables mipmapping if \a enabled is true; otherwise disables it.
198 Mipmapping is disabled by default.
200 If mipmapping is enabled, additional memory will be allocated for
201 the mipmap levels. The mipmap levels can be updated by binding the
202 texture and calling glGenerateMipmap(). Mipmapping cannot be enabled
203 for multisampled framebuffer objects.
205 \sa mipmap(), QOpenGLFramebufferObject::texture()
207 void QOpenGLFramebufferObjectFormat::setMipmap(bool enabled)
214 Returns true if mipmapping is enabled.
218 bool QOpenGLFramebufferObjectFormat::mipmap() const
224 Sets the attachment configuration of a framebuffer object to \a attachment.
228 void QOpenGLFramebufferObjectFormat::setAttachment(QOpenGLFramebufferObject::Attachment attachment)
231 d->attachment = attachment;
235 Returns the configuration of the depth and stencil buffers attached to
236 a framebuffer object. The default is QOpenGLFramebufferObject::NoAttachment.
240 QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObjectFormat::attachment() const
242 return d->attachment;
246 Sets the texture target of the texture attached to a framebuffer object to
247 \a target. Ignored for multisample framebuffer objects.
249 \sa textureTarget(), samples()
251 void QOpenGLFramebufferObjectFormat::setTextureTarget(GLenum target)
258 Returns the texture target of the texture attached to a framebuffer object.
259 Ignored for multisample framebuffer objects. The default is
262 \sa setTextureTarget(), samples()
264 GLenum QOpenGLFramebufferObjectFormat::textureTarget() const
270 Sets the internal format of a framebuffer object's texture or
271 multisample framebuffer object's color buffer to
272 \a internalTextureFormat.
274 \sa internalTextureFormat()
276 void QOpenGLFramebufferObjectFormat::setInternalTextureFormat(GLenum internalTextureFormat)
279 d->internal_format = internalTextureFormat;
283 Returns the internal format of a framebuffer object's texture or
284 multisample framebuffer object's color buffer. The default is
285 \c GL_RGBA8 on desktop OpenGL systems, and \c GL_RGBA on
288 \sa setInternalTextureFormat()
290 GLenum QOpenGLFramebufferObjectFormat::internalTextureFormat() const
292 return d->internal_format;
296 Returns true if all the options of this framebuffer object format
297 are the same as \a other; otherwise returns false.
299 bool QOpenGLFramebufferObjectFormat::operator==(const QOpenGLFramebufferObjectFormat& other) const
304 return d->equals(other.d);
308 Returns false if all the options of this framebuffer object format
309 are the same as \a other; otherwise returns true.
311 bool QOpenGLFramebufferObjectFormat::operator!=(const QOpenGLFramebufferObjectFormat& other) const
313 return !(*this == other);
316 bool QOpenGLFramebufferObjectPrivate::checkFramebufferStatus(QOpenGLContext *ctx) const
319 return false; // Context no longer exists.
320 GLenum status = ctx->functions()->glCheckFramebufferStatus(GL_FRAMEBUFFER);
323 case GL_FRAMEBUFFER_COMPLETE:
326 case GL_FRAMEBUFFER_UNSUPPORTED:
327 qDebug("QOpenGLFramebufferObject: Unsupported framebuffer format.");
329 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
330 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete attachment.");
332 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
333 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing attachment.");
335 #ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT
336 case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT:
337 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, duplicate attachment.");
340 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
341 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attached images must have same dimensions.");
343 case GL_FRAMEBUFFER_INCOMPLETE_FORMATS:
344 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attached images must have same format.");
346 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
347 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing draw buffer.");
349 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
350 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing read buffer.");
352 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
353 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attachments must have same number of samples per pixel.");
356 qDebug() <<"QOpenGLFramebufferObject: An undefined error has occurred: "<< status;
364 void freeFramebufferFunc(QOpenGLFunctions *funcs, GLuint id)
366 funcs->glDeleteFramebuffers(1, &id);
369 void freeRenderbufferFunc(QOpenGLFunctions *funcs, GLuint id)
371 funcs->glDeleteRenderbuffers(1, &id);
374 void freeTextureFunc(QOpenGLFunctions *, GLuint id)
376 glDeleteTextures(1, &id);
380 void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSize &sz,
381 QOpenGLFramebufferObject::Attachment attachment,
382 GLenum texture_target, GLenum internal_format,
383 GLint samples, bool mipmap)
385 QOpenGLContext *ctx = QOpenGLContext::currentContext();
387 funcs.initializeGLFunctions();
389 if (!funcs.hasOpenGLFeature(QOpenGLFunctions::Framebuffers))
393 target = texture_target;
394 // texture dimensions
396 QT_RESET_GLERROR(); // reset error state
399 funcs.glGenFramebuffers(1, &fbo);
400 funcs.glBindFramebuffer(GL_FRAMEBUFFER, fbo);
403 GLuint color_buffer = 0;
408 glGenTextures(1, &texture);
409 glBindTexture(target, texture);
410 glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0,
411 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
413 int width = size.width();
414 int height = size.height();
416 while (width > 1 || height > 1) {
417 width = (width + 1) >> 1;
418 height = (height + 1) >> 1;
420 glTexImage2D(target, level, internal_format, width, height, 0,
421 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
424 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
425 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
426 glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
427 glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
428 funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
432 valid = checkFramebufferStatus(ctx);
433 glBindTexture(target, 0);
439 glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
441 samples = qBound(0, int(samples), int(maxSamples));
443 funcs.glGenRenderbuffers(1, &color_buffer);
444 funcs.glBindRenderbuffer(GL_RENDERBUFFER, color_buffer);
445 if (funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample) && samples > 0) {
446 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
447 internal_format, size.width(), size.height());
450 funcs.glRenderbufferStorage(GL_RENDERBUFFER, internal_format,
451 size.width(), size.height());
454 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
455 GL_RENDERBUFFER, color_buffer);
458 valid = checkFramebufferStatus(ctx);
461 funcs.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
464 format.setTextureTarget(target);
465 format.setSamples(int(samples));
466 format.setInternalTextureFormat(internal_format);
467 format.setMipmap(mipmap);
469 initAttachments(ctx, attachment);
471 funcs.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);
473 fbo_guard = new QOpenGLSharedResourceGuard(ctx, fbo, freeFramebufferFunc);
475 color_buffer_guard = new QOpenGLSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc);
477 texture_guard = new QOpenGLSharedResourceGuard(ctx, texture, freeTextureFunc);
480 funcs.glDeleteRenderbuffers(1, &color_buffer);
482 glDeleteTextures(1, &texture);
483 funcs.glDeleteFramebuffers(1, &fbo);
488 void QOpenGLFramebufferObjectPrivate::initAttachments(QOpenGLContext *ctx, QOpenGLFramebufferObject::Attachment attachment)
490 int samples = format.samples();
492 // free existing attachments
493 if (depth_buffer_guard) {
494 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
495 depth_buffer_guard->free();
497 if (stencil_buffer_guard) {
498 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
499 if (stencil_buffer_guard != depth_buffer_guard)
500 stencil_buffer_guard->free();
503 depth_buffer_guard = 0;
504 stencil_buffer_guard = 0;
506 GLuint depth_buffer = 0;
507 GLuint stencil_buffer = 0;
509 // In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a
510 // separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer
511 // might not be supported while separate buffers are, according to QTBUG-12861.
513 if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil
514 && funcs.hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil))
516 // depth and stencil buffer needs another extension
517 funcs.glGenRenderbuffers(1, &depth_buffer);
518 funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
519 Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
520 if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
521 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
522 GL_DEPTH24_STENCIL8, size.width(), size.height());
524 funcs.glRenderbufferStorage(GL_RENDERBUFFER,
525 GL_DEPTH24_STENCIL8, size.width(), size.height());
527 stencil_buffer = depth_buffer;
528 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
529 GL_RENDERBUFFER, depth_buffer);
530 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
531 GL_RENDERBUFFER, stencil_buffer);
533 valid = checkFramebufferStatus(ctx);
535 funcs.glDeleteRenderbuffers(1, &depth_buffer);
536 stencil_buffer = depth_buffer = 0;
540 if (depth_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil
541 || (attachment == QOpenGLFramebufferObject::Depth)))
543 funcs.glGenRenderbuffers(1, &depth_buffer);
544 funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
545 Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
546 if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) {
548 if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) {
549 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
550 GL_DEPTH_COMPONENT24, size.width(), size.height());
552 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
553 GL_DEPTH_COMPONENT16, size.width(), size.height());
556 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
557 GL_DEPTH_COMPONENT, size.width(), size.height());
561 if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) {
562 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24,
563 size.width(), size.height());
565 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
566 size.width(), size.height());
569 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height());
572 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
573 GL_RENDERBUFFER, depth_buffer);
574 valid = checkFramebufferStatus(ctx);
576 funcs.glDeleteRenderbuffers(1, &depth_buffer);
581 if (stencil_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil)) {
582 funcs.glGenRenderbuffers(1, &stencil_buffer);
583 funcs.glBindRenderbuffer(GL_RENDERBUFFER, stencil_buffer);
584 Q_ASSERT(funcs.glIsRenderbuffer(stencil_buffer));
585 if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) {
587 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
588 GL_STENCIL_INDEX8, size.width(), size.height());
590 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
591 GL_STENCIL_INDEX, size.width(), size.height());
595 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8,
596 size.width(), size.height());
598 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX,
599 size.width(), size.height());
602 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
603 GL_RENDERBUFFER, stencil_buffer);
604 valid = checkFramebufferStatus(ctx);
606 funcs.glDeleteRenderbuffers(1, &stencil_buffer);
611 // The FBO might have become valid after removing the depth or stencil buffer.
612 valid = checkFramebufferStatus(ctx);
614 if (depth_buffer && stencil_buffer) {
615 fbo_attachment = QOpenGLFramebufferObject::CombinedDepthStencil;
616 } else if (depth_buffer) {
617 fbo_attachment = QOpenGLFramebufferObject::Depth;
619 fbo_attachment = QOpenGLFramebufferObject::NoAttachment;
624 depth_buffer_guard = new QOpenGLSharedResourceGuard(ctx, depth_buffer, freeRenderbufferFunc);
625 if (stencil_buffer) {
626 if (stencil_buffer == depth_buffer)
627 stencil_buffer_guard = depth_buffer_guard;
629 stencil_buffer_guard = new QOpenGLSharedResourceGuard(ctx, stencil_buffer, freeRenderbufferFunc);
633 funcs.glDeleteRenderbuffers(1, &depth_buffer);
634 if (stencil_buffer && depth_buffer != stencil_buffer)
635 funcs.glDeleteRenderbuffers(1, &stencil_buffer);
639 format.setAttachment(fbo_attachment);
643 \class QOpenGLFramebufferObject
644 \brief The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer object.
649 The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer
650 object, defined by the \c{GL_EXT_framebuffer_object} extension. In
651 addition it provides a rendering surface that can be painted on
652 with a QPainter, rendered to using native GL calls, or both. This
653 surface can be bound and used as a regular texture in your own GL
654 drawing code. By default, the QOpenGLFramebufferObject class
655 generates a 2D GL texture (using the \c{GL_TEXTURE_2D} target),
656 which is used as the internal rendering target.
658 \bold{It is important to have a current GL context when creating a
659 QOpenGLFramebufferObject, otherwise initialization will fail.}
661 When using a QPainter to paint to a QOpenGLFramebufferObject you should take
662 care that the QOpenGLFramebufferObject is created with the CombinedDepthStencil
663 attachment for QPainter to be able to render correctly.
664 Note that you need to create a QOpenGLFramebufferObject with more than one
665 sample per pixel for primitives to be antialiased when drawing using a
666 QPainter. To create a multisample framebuffer object you should use one of
667 the constructors that take a QOpenGLFramebufferObject parameter, and set the
668 QOpenGLFramebufferObject::samples() property to a non-zero value.
670 For multisample framebuffer objects a color render buffer is created,
671 otherwise a texture with the specified texture target is created.
672 The color render buffer or texture will have the specified internal
673 format, and will be bound to the \c GL_COLOR_ATTACHMENT0
674 attachment in the framebuffer object.
676 If you want to use a framebuffer object with multisampling enabled
677 as a texture, you first need to copy from it to a regular framebuffer
678 object using QOpenGLContext::blitFramebuffer().
682 As of Qt 4.8, it's possible to draw into a QOpenGLFramebufferObject
683 using a QPainter in a separate thread. Note that OpenGL 2.0 or
684 OpenGL ES 2.0 is required for this to work.
689 \enum QOpenGLFramebufferObject::Attachment
691 This enum type is used to configure the depth and stencil buffers
692 attached to the framebuffer object when it is created.
694 \value NoAttachment No attachment is added to the framebuffer object. Note that the
695 OpenGL depth and stencil tests won't work when rendering to a
696 framebuffer object without any depth or stencil buffers.
697 This is the default value.
699 \value CombinedDepthStencil If the \c GL_EXT_packed_depth_stencil extension is present,
700 a combined depth and stencil buffer is attached.
701 If the extension is not present, only a depth buffer is attached.
703 \value Depth A depth buffer is attached to the framebuffer object.
709 /*! \fn QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, GLenum target)
711 Constructs an OpenGL framebuffer object and binds a 2D GL texture
712 to the buffer of the size \a size. The texture is bound to the
713 \c GL_COLOR_ATTACHMENT0 target in the framebuffer object.
715 The \a target parameter is used to specify the GL texture
716 target. The default target is \c GL_TEXTURE_2D. Keep in mind that
717 \c GL_TEXTURE_2D textures must have a power of 2 width and height
718 (e.g. 256x512), unless you are using OpenGL 2.0 or higher.
720 By default, no depth and stencil buffers are attached. This behavior
721 can be toggled using one of the overloaded constructors.
723 The default internal texture format is \c GL_RGBA8 for desktop
724 OpenGL, and \c GL_RGBA for OpenGL/ES.
726 It is important that you have a current GL context set when
727 creating the QOpenGLFramebufferObject, otherwise the initialization
730 \sa size(), texture(), attachment()
733 QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, GLenum target)
734 : d_ptr(new QOpenGLFramebufferObjectPrivate)
736 Q_D(QOpenGLFramebufferObject);
737 d->init(this, size, NoAttachment, target, DEFAULT_FORMAT);
742 Constructs an OpenGL framebuffer object and binds a 2D GL texture
743 to the buffer of the given \a width and \a height.
745 \sa size(), texture()
747 QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, GLenum target)
748 : d_ptr(new QOpenGLFramebufferObjectPrivate)
750 Q_D(QOpenGLFramebufferObject);
751 d->init(this, QSize(width, height), NoAttachment, target, DEFAULT_FORMAT);
756 Constructs an OpenGL framebuffer object of the given \a size based on the
760 QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, const QOpenGLFramebufferObjectFormat &format)
761 : d_ptr(new QOpenGLFramebufferObjectPrivate)
763 Q_D(QOpenGLFramebufferObject);
764 d->init(this, size, format.attachment(), format.textureTarget(), format.internalTextureFormat(),
765 format.samples(), format.mipmap());
770 Constructs an OpenGL framebuffer object of the given \a width and \a height
771 based on the supplied \a format.
774 QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, const QOpenGLFramebufferObjectFormat &format)
775 : d_ptr(new QOpenGLFramebufferObjectPrivate)
777 Q_D(QOpenGLFramebufferObject);
778 d->init(this, QSize(width, height), format.attachment(), format.textureTarget(),
779 format.internalTextureFormat(), format.samples(), format.mipmap());
784 Constructs an OpenGL framebuffer object and binds a texture to the
785 buffer of the given \a width and \a height.
787 The \a attachment parameter describes the depth/stencil buffer
788 configuration, \a target the texture target and \a internal_format
789 the internal texture format. The default texture target is \c
790 GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8
791 for desktop OpenGL and \c GL_RGBA for OpenGL/ES.
793 \sa size(), texture(), attachment()
795 QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, Attachment attachment,
796 GLenum target, GLenum internal_format)
797 : d_ptr(new QOpenGLFramebufferObjectPrivate)
799 Q_D(QOpenGLFramebufferObject);
800 d->init(this, QSize(width, height), attachment, target, internal_format);
805 Constructs an OpenGL framebuffer object and binds a texture to the
806 buffer of the given \a size.
808 The \a attachment parameter describes the depth/stencil buffer
809 configuration, \a target the texture target and \a internal_format
810 the internal texture format. The default texture target is \c
811 GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8
812 for desktop OpenGL and \c GL_RGBA for OpenGL/ES.
814 \sa size(), texture(), attachment()
816 QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, Attachment attachment,
817 GLenum target, GLenum internal_format)
818 : d_ptr(new QOpenGLFramebufferObjectPrivate)
820 Q_D(QOpenGLFramebufferObject);
821 d->init(this, size, attachment, target, internal_format);
825 \fn QOpenGLFramebufferObject::~QOpenGLFramebufferObject()
827 Destroys the framebuffer object and frees any allocated resources.
829 QOpenGLFramebufferObject::~QOpenGLFramebufferObject()
831 Q_D(QOpenGLFramebufferObject);
833 if (d->texture_guard)
834 d->texture_guard->free();
835 if (d->color_buffer_guard)
836 d->color_buffer_guard->free();
837 if (d->depth_buffer_guard)
838 d->depth_buffer_guard->free();
839 if (d->stencil_buffer_guard && d->stencil_buffer_guard != d->depth_buffer_guard)
840 d->stencil_buffer_guard->free();
842 d->fbo_guard->free();
846 \fn bool QOpenGLFramebufferObject::isValid() const
848 Returns true if the framebuffer object is valid.
850 The framebuffer can become invalid if the initialization process
851 fails, the user attaches an invalid buffer to the framebuffer
852 object, or a non-power of two width/height is specified as the
853 texture size if the texture target is \c{GL_TEXTURE_2D}.
854 The non-power of two limitation does not apply if the OpenGL version
855 is 2.0 or higher, or if the GL_ARB_texture_non_power_of_two extension
858 The framebuffer can also become invalid if the QOpenGLContext that
859 the framebuffer was created within is destroyed and there are
860 no other shared contexts that can take over ownership of the
863 bool QOpenGLFramebufferObject::isValid() const
865 Q_D(const QOpenGLFramebufferObject);
866 return d->valid && d->fbo_guard && d->fbo_guard->id();
870 \fn bool QOpenGLFramebufferObject::bind()
872 Switches rendering from the default, windowing system provided
873 framebuffer to this framebuffer object.
874 Returns true upon success, false otherwise.
878 bool QOpenGLFramebufferObject::bind()
882 Q_D(QOpenGLFramebufferObject);
883 QOpenGLContext *current = QOpenGLContext::currentContext();
887 if (current->shareGroup() != d->fbo_guard->group())
888 qWarning("QOpenGLFramebufferObject::bind() called from incompatible context");
890 d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo());
891 d->valid = d->checkFramebufferStatus(current);
892 if (d->valid && current)
893 current->d_func()->current_fbo = d->fbo();
898 \fn bool QOpenGLFramebufferObject::release()
900 Switches rendering back to the default, windowing system provided
902 Returns true upon success, false otherwise.
906 bool QOpenGLFramebufferObject::release()
911 QOpenGLContext *current = QOpenGLContext::currentContext();
915 Q_D(QOpenGLFramebufferObject);
917 if (current->shareGroup() != d->fbo_guard->group())
918 qWarning("QOpenGLFramebufferObject::release() called from incompatible context");
922 current->d_func()->current_fbo = current->defaultFramebufferObject();
923 d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->d_func()->current_fbo);
930 \fn GLuint QOpenGLFramebufferObject::texture() const
932 Returns the texture id for the texture attached as the default
933 rendering target in this framebuffer object. This texture id can
934 be bound as a normal texture in your own GL code.
936 If a multisample framebuffer object is used then the value returned
937 from this function will be invalid.
939 GLuint QOpenGLFramebufferObject::texture() const
941 Q_D(const QOpenGLFramebufferObject);
942 return d->texture_guard ? d->texture_guard->id() : 0;
946 \fn QSize QOpenGLFramebufferObject::size() const
948 Returns the size of the texture attached to this framebuffer
951 QSize QOpenGLFramebufferObject::size() const
953 Q_D(const QOpenGLFramebufferObject);
958 \fn int QOpenGLFramebufferObject::width() const
960 Returns the width of the framebuffer object attachments.
964 \fn int QOpenGLFramebufferObject::height() const
966 Returns the height of the framebuffer object attachments.
970 Returns the format of this framebuffer object.
972 QOpenGLFramebufferObjectFormat QOpenGLFramebufferObject::format() const
974 Q_D(const QOpenGLFramebufferObject);
980 Read back the contents of the currently bound framebuffer, used in
981 QGLWidget::grabFrameBuffer(), QGLPixelbuffer::toImage() and
982 QGLFramebufferObject::toImage()
985 void convertFromGLImage(QImage &img, int w, int h, bool alpha_format, bool include_alpha)
987 if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
988 // OpenGL gives RGBA; Qt wants ARGB
989 uint *p = (uint*)img.bits();
991 if (alpha_format && include_alpha) {
998 // This is an old legacy fix for PowerPC based Macs, which
999 // we shouldn't remove
1001 *p = 0xff000000 | (*p>>8);
1006 // OpenGL gives ABGR (i.e. RGBA backwards); Qt wants ARGB
1007 for (int y = 0; y < h; y++) {
1008 uint *q = (uint*)img.scanLine(y);
1009 for (int x=0; x < w; ++x) {
1010 const uint pixel = *q;
1011 if (alpha_format && include_alpha) {
1012 *q = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff)
1013 | (pixel & 0xff00ff00);
1015 *q = 0xff000000 | ((pixel << 16) & 0xff0000)
1016 | ((pixel >> 16) & 0xff) | (pixel & 0x00ff00);
1024 img = img.mirrored();
1029 Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha)
1031 QImage img(size, (alpha_format && include_alpha) ? QImage::Format_ARGB32_Premultiplied
1032 : QImage::Format_RGB32);
1033 int w = size.width();
1034 int h = size.height();
1035 glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
1036 convertFromGLImage(img, w, h, alpha_format, include_alpha);
1041 \fn QImage QOpenGLFramebufferObject::toImage() const
1043 Returns the contents of this framebuffer object as a QImage.
1045 QImage QOpenGLFramebufferObject::toImage() const
1047 Q_D(const QOpenGLFramebufferObject);
1051 // qt_gl_read_framebuffer doesn't work on a multisample FBO
1052 if (format().samples() != 0) {
1053 QOpenGLFramebufferObject temp(size(), QOpenGLFramebufferObjectFormat());
1055 QRect rect(QPoint(0, 0), size());
1056 blitFramebuffer(&temp, rect, const_cast<QOpenGLFramebufferObject *>(this), rect);
1058 return temp.toImage();
1061 bool wasBound = isBound();
1063 const_cast<QOpenGLFramebufferObject *>(this)->bind();
1064 QImage image = qt_gl_read_framebuffer(d->size, format().internalTextureFormat() != GL_RGB, true);
1066 const_cast<QOpenGLFramebufferObject *>(this)->release();
1072 \fn bool QOpenGLFramebufferObject::bindDefault()
1075 Switches rendering back to the default, windowing system provided
1077 Returns true upon success, false otherwise.
1079 \sa bind(), release()
1081 bool QOpenGLFramebufferObject::bindDefault()
1083 QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
1084 QOpenGLFunctions functions(ctx);
1087 ctx->d_func()->current_fbo = ctx->defaultFramebufferObject();
1088 functions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);
1091 qWarning("QOpenGLFramebufferObject::bindDefault() called without current context.");
1099 \fn bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()
1101 Returns true if the OpenGL \c{GL_EXT_framebuffer_object} extension
1102 is present on this system; otherwise returns false.
1104 bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()
1106 return QOpenGLFunctions(QOpenGLContext::currentContext()).hasOpenGLFeature(QOpenGLFunctions::Framebuffers);
1110 \fn GLuint QOpenGLFramebufferObject::handle() const
1112 Returns the GL framebuffer object handle for this framebuffer
1113 object (returned by the \c{glGenFrameBuffersEXT()} function). This
1114 handle can be used to attach new images or buffers to the
1115 framebuffer. The user is responsible for cleaning up and
1116 destroying these objects.
1118 GLuint QOpenGLFramebufferObject::handle() const
1120 Q_D(const QOpenGLFramebufferObject);
1125 Returns the status of the depth and stencil buffers attached to
1126 this framebuffer object.
1129 QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObject::attachment() const
1131 Q_D(const QOpenGLFramebufferObject);
1133 return d->fbo_attachment;
1134 return NoAttachment;
1138 Sets the attachments of the framebuffer object.
1140 This can be used to free or reattach the depth and stencil buffer
1141 attachments as needed.
1143 void QOpenGLFramebufferObject::setAttachment(QOpenGLFramebufferObject::Attachment attachment)
1145 Q_D(QOpenGLFramebufferObject);
1146 if (attachment == d->fbo_attachment || !isValid())
1148 QOpenGLContext *current = QOpenGLContext::currentContext();
1152 if (current->shareGroup() != d->fbo_guard->group())
1153 qWarning("QOpenGLFramebufferObject::setAttachment() called from incompatible context");
1155 d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo());
1156 d->initAttachments(current, attachment);
1157 if (current->d_func()->current_fbo != d->fbo())
1158 d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->d_func()->current_fbo);
1162 Returns true if the framebuffer object is currently bound to a context,
1163 otherwise false is returned.
1166 bool QOpenGLFramebufferObject::isBound() const
1168 Q_D(const QOpenGLFramebufferObject);
1169 QOpenGLContext *current = QOpenGLContext::currentContext();
1170 return current ? current->d_func()->current_fbo == d->fbo() : false;
1174 \fn bool QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()
1176 Returns true if the OpenGL \c{GL_EXT_framebuffer_blit} extension
1177 is present on this system; otherwise returns false.
1179 \sa blitFramebuffer()
1181 bool QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()
1183 return QOpenGLExtensions(QOpenGLContext::currentContext()).hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
1190 Convenience overload to blit between two framebuffer objects.
1192 void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target,
1193 QOpenGLFramebufferObject *source,
1194 GLbitfield buffers, GLenum filter)
1196 if (!target && !source)
1203 targetSize = target->size();
1205 sourceSize = source->size();
1207 if (targetSize.isEmpty())
1208 targetSize = sourceSize;
1209 else if (sourceSize.isEmpty())
1210 sourceSize = targetSize;
1212 blitFramebuffer(target, QRect(QPoint(0, 0), targetSize),
1213 source, QRect(QPoint(0, 0), sourceSize),
1218 Blits from the \a sourceRect rectangle in the \a source framebuffer
1219 object to the \a targetRect rectangle in the \a target framebuffer object.
1221 If \a source or \a target is 0, the default framebuffer will be used
1222 instead of a framebuffer object as source or target respectively.
1224 The \a buffers parameter should be a mask consisting of any combination of
1225 \c GL_COLOR_BUFFER_BIT, \c GL_DEPTH_BUFFER_BIT, and
1226 \c GL_STENCIL_BUFFER_BIT. Any buffer type that is not present both
1227 in the source and target buffers is ignored.
1229 The \a sourceRect and \a targetRect rectangles may have different sizes;
1230 in this case \a buffers should not contain \c GL_DEPTH_BUFFER_BIT or
1231 \c GL_STENCIL_BUFFER_BIT. The \a filter parameter should be set to
1232 \c GL_LINEAR or \c GL_NEAREST, and specifies whether linear or nearest
1233 interpolation should be used when scaling is performed.
1235 If \a source equals \a target a copy is performed within the same buffer.
1236 Results are undefined if the source and target rectangles overlap and
1237 have different sizes. The sizes must also be the same if any of the
1238 framebuffer objects are multisample framebuffers.
1240 Note that the scissor test will restrict the blit area if enabled.
1242 This function will have no effect unless hasOpenGLFramebufferBlit() returns
1245 \sa hasOpenGLFramebufferBlit()
1247 void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect,
1248 QOpenGLFramebufferObject *source, const QRect &sourceRect,
1252 QOpenGLContext *ctx = QOpenGLContext::currentContext();
1256 QOpenGLExtensions extensions(ctx);
1257 if (!extensions.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit))
1260 const int sx0 = sourceRect.left();
1261 const int sx1 = sourceRect.left() + sourceRect.width();
1262 const int sy0 = sourceRect.top();
1263 const int sy1 = sourceRect.top() + sourceRect.height();
1265 const int tx0 = targetRect.left();
1266 const int tx1 = targetRect.left() + targetRect.width();
1267 const int ty0 = targetRect.top();
1268 const int ty1 = targetRect.top() + targetRect.height();
1270 extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, source ? source->handle() : 0);
1271 extensions.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target ? target->handle() : 0);
1273 extensions.glBlitFramebuffer(sx0, sy0, sx1, sy1,
1277 extensions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);