1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** 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>
50 #include <private/qopenglpaintengine_p.h>
59 #define QT_RESET_GLERROR() \
61 while (glGetError() != GL_NO_ERROR) {} \
63 #define QT_CHECK_GLERROR() \
65 GLenum err = glGetError(); \
66 if (err != GL_NO_ERROR) { \
67 qDebug("[%s line %d] GL Error: %d", \
68 __FILE__, __LINE__, (int)err); \
72 #define QT_RESET_GLERROR() {}
73 #define QT_CHECK_GLERROR() {}
77 \class QOpenGLFramebufferObjectFormat
78 \brief The QOpenGLFramebufferObjectFormat class specifies the format of an OpenGL
85 A framebuffer object has several characteristics:
87 \i \link setSamples() Number of samples per pixels.\endlink
88 \i \link setAttachment() Depth and/or stencil attachments.\endlink
89 \i \link setTextureTarget() Texture target.\endlink
90 \i \link setInternalTextureFormat() Internal texture format.\endlink
93 Note that the desired attachments or number of samples per pixels might not
94 be supported by the hardware driver. Call QOpenGLFramebufferObject::format()
95 after creating a QOpenGLFramebufferObject to find the exact format that was
96 used to create the frame buffer object.
98 \sa QOpenGLFramebufferObject
104 void QOpenGLFramebufferObjectFormat::detach()
107 QOpenGLFramebufferObjectFormatPrivate *newd
108 = new QOpenGLFramebufferObjectFormatPrivate(d);
116 Creates a QOpenGLFramebufferObjectFormat object for specifying
117 the format of an OpenGL framebuffer object.
119 By default the format specifies a non-multisample framebuffer object with no
120 attachments, texture target \c GL_TEXTURE_2D, and internal format \c GL_RGBA8.
121 On OpenGL/ES systems, the default internal format is \c GL_RGBA.
123 \sa samples(), attachment(), internalTextureFormat()
126 QOpenGLFramebufferObjectFormat::QOpenGLFramebufferObjectFormat()
128 d = new QOpenGLFramebufferObjectFormatPrivate;
132 Constructs a copy of \a other.
135 QOpenGLFramebufferObjectFormat::QOpenGLFramebufferObjectFormat(const QOpenGLFramebufferObjectFormat &other)
142 Assigns \a other to this object.
145 QOpenGLFramebufferObjectFormat &QOpenGLFramebufferObjectFormat::operator=(const QOpenGLFramebufferObjectFormat &other)
157 Destroys the QOpenGLFramebufferObjectFormat.
159 QOpenGLFramebufferObjectFormat::~QOpenGLFramebufferObjectFormat()
166 Sets the number of samples per pixel for a multisample framebuffer object
167 to \a samples. The default sample count of 0 represents a regular
168 non-multisample framebuffer object.
170 If the desired amount of samples per pixel is not supported by the hardware
171 then the maximum number of samples per pixel will be used. Note that
172 multisample framebuffer objects can not be bound as textures. Also, the
173 \c{GL_EXT_framebuffer_multisample} extension is required to create a
174 framebuffer with more than one sample per pixel.
178 void QOpenGLFramebufferObjectFormat::setSamples(int samples)
181 d->samples = samples;
185 Returns the number of samples per pixel if a framebuffer object
186 is a multisample framebuffer object. Otherwise, returns 0.
187 The default value is 0.
191 int QOpenGLFramebufferObjectFormat::samples() const
199 Enables mipmapping if \a enabled is true; otherwise disables it.
201 Mipmapping is disabled by default.
203 If mipmapping is enabled, additional memory will be allocated for
204 the mipmap levels. The mipmap levels can be updated by binding the
205 texture and calling glGenerateMipmap(). Mipmapping cannot be enabled
206 for multisampled framebuffer objects.
208 \sa mipmap(), QOpenGLFramebufferObject::texture()
210 void QOpenGLFramebufferObjectFormat::setMipmap(bool enabled)
219 Returns true if mipmapping is enabled.
223 bool QOpenGLFramebufferObjectFormat::mipmap() const
229 Sets the attachment configuration of a framebuffer object to \a attachment.
233 void QOpenGLFramebufferObjectFormat::setAttachment(QOpenGLFramebufferObject::Attachment attachment)
236 d->attachment = attachment;
240 Returns the configuration of the depth and stencil buffers attached to
241 a framebuffer object. The default is QOpenGLFramebufferObject::NoAttachment.
245 QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObjectFormat::attachment() const
247 return d->attachment;
251 Sets the texture target of the texture attached to a framebuffer object to
252 \a target. Ignored for multisample framebuffer objects.
254 \sa textureTarget(), samples()
256 void QOpenGLFramebufferObjectFormat::setTextureTarget(GLenum target)
263 Returns the texture target of the texture attached to a framebuffer object.
264 Ignored for multisample framebuffer objects. The default is
267 \sa setTextureTarget(), samples()
269 GLenum QOpenGLFramebufferObjectFormat::textureTarget() const
275 Sets the internal format of a framebuffer object's texture or
276 multisample framebuffer object's color buffer to
277 \a internalTextureFormat.
279 \sa internalTextureFormat()
281 void QOpenGLFramebufferObjectFormat::setInternalTextureFormat(GLenum internalTextureFormat)
284 d->internal_format = internalTextureFormat;
288 Returns the internal format of a framebuffer object's texture or
289 multisample framebuffer object's color buffer. The default is
290 \c GL_RGBA8 on desktop OpenGL systems, and \c GL_RGBA on
293 \sa setInternalTextureFormat()
295 GLenum QOpenGLFramebufferObjectFormat::internalTextureFormat() const
297 return d->internal_format;
301 Returns true if all the options of this framebuffer object format
302 are the same as \a other; otherwise returns false.
304 bool QOpenGLFramebufferObjectFormat::operator==(const QOpenGLFramebufferObjectFormat& other) const
309 return d->equals(other.d);
313 Returns false if all the options of this framebuffer object format
314 are the same as \a other; otherwise returns true.
316 bool QOpenGLFramebufferObjectFormat::operator!=(const QOpenGLFramebufferObjectFormat& other) const
318 return !(*this == other);
321 void QOpenGLFBOGLPaintDevice::setFBO(QOpenGLFramebufferObject* f,
322 QOpenGLFramebufferObject::Attachment attachment)
325 m_thisFBO = fbo->d_func()->fbo(); // This shouldn't be needed
327 // The context that the fbo was created in may not have depth
328 // and stencil buffers, but the fbo itself might.
329 fboFormat = QOpenGLContext::currentContext()->format();
330 if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil) {
331 fboFormat.setDepthBufferSize(24);
332 fboFormat.setStencilBufferSize(8);
333 } else if (attachment == QOpenGLFramebufferObject::Depth) {
334 fboFormat.setDepthBufferSize(24);
335 fboFormat.setStencilBufferSize(0);
337 fboFormat.setDepthBufferSize(0);
338 fboFormat.setStencilBufferSize(0);
341 GLenum format = f->format().internalTextureFormat();
342 reqAlpha = (format != GL_RGB
344 && format != GL_RGB5 && format != GL_RGB8
349 QOpenGLContextGroup *QOpenGLFBOGLPaintDevice::group() const
351 QOpenGLSharedResourceGuard *fbo_guard =
352 fbo->d_func()->fbo_guard;
353 return fbo_guard ? fbo_guard->group() : 0;
356 bool QOpenGLFramebufferObjectPrivate::checkFramebufferStatus() const
358 QOpenGLContext *ctx = QOpenGLContext::currentContext();
360 return false; // Context no longer exists.
361 GLenum status = ctx->functions()->glCheckFramebufferStatus(GL_FRAMEBUFFER);
364 case GL_FRAMEBUFFER_COMPLETE:
367 case GL_FRAMEBUFFER_UNSUPPORTED:
368 qDebug("QOpenGLFramebufferObject: Unsupported framebuffer format.");
370 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
371 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete attachment.");
373 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
374 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing attachment.");
376 #ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT
377 case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT:
378 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, duplicate attachment.");
381 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
382 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attached images must have same dimensions.");
384 case GL_FRAMEBUFFER_INCOMPLETE_FORMATS:
385 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attached images must have same format.");
387 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
388 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing draw buffer.");
390 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
391 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing read buffer.");
393 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
394 qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attachments must have same number of samples per pixel.");
397 qDebug() <<"QOpenGLFramebufferObject: An undefined error has occurred: "<< status;
405 void freeFramebufferFunc(QOpenGLFunctions *funcs, GLuint id)
407 funcs->glDeleteFramebuffers(1, &id);
410 void freeRenderbufferFunc(QOpenGLFunctions *funcs, GLuint id)
412 funcs->glDeleteRenderbuffers(1, &id);
415 void freeTextureFunc(QOpenGLFunctions *, GLuint id)
417 glDeleteTextures(1, &id);
421 void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *q, const QSize &sz,
422 QOpenGLFramebufferObject::Attachment attachment,
423 GLenum texture_target, GLenum internal_format,
424 GLint samples, bool mipmap)
426 QOpenGLContext *ctx = QOpenGLContext::currentContext();
428 funcs.initializeGLFunctions();
430 if (!funcs.hasOpenGLFeature(QOpenGLFunctions::Framebuffers))
434 target = texture_target;
435 // texture dimensions
437 QT_RESET_GLERROR(); // reset error state
440 funcs.glGenFramebuffers(1, &fbo);
441 funcs.glBindFramebuffer(GL_FRAMEBUFFER, fbo);
444 GLuint color_buffer = 0;
445 GLuint depth_buffer = 0;
446 GLuint stencil_buffer = 0;
451 glGenTextures(1, &texture);
452 glBindTexture(target, texture);
453 glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0,
454 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
456 funcs.glGenerateMipmap(GL_TEXTURE_2D);
458 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
459 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
460 glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
461 glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
463 glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
464 glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
465 glTexParameterf(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
466 glTexParameterf(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
468 funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
472 valid = checkFramebufferStatus();
473 glBindTexture(target, 0);
479 glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
481 samples = qBound(0, int(samples), int(maxSamples));
483 funcs.glGenRenderbuffers(1, &color_buffer);
484 funcs.glBindRenderbuffer(GL_RENDERBUFFER, color_buffer);
485 if (funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample) && samples > 0) {
486 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
487 internal_format, size.width(), size.height());
490 funcs.glRenderbufferStorage(GL_RENDERBUFFER, internal_format,
491 size.width(), size.height());
494 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
495 GL_RENDERBUFFER, color_buffer);
498 valid = checkFramebufferStatus();
501 funcs.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
504 // In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a
505 // separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer
506 // might not be supported while separate buffers are, according to QTBUG-12861.
508 if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil
509 && funcs.hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil))
511 // depth and stencil buffer needs another extension
512 funcs.glGenRenderbuffers(1, &depth_buffer);
513 Q_ASSERT(!funcs.glIsRenderbuffer(depth_buffer));
514 funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
515 Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
516 if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
517 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
518 GL_DEPTH24_STENCIL8, size.width(), size.height());
520 funcs.glRenderbufferStorage(GL_RENDERBUFFER,
521 GL_DEPTH24_STENCIL8, size.width(), size.height());
523 stencil_buffer = depth_buffer;
524 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
525 GL_RENDERBUFFER, depth_buffer);
526 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
527 GL_RENDERBUFFER, stencil_buffer);
529 valid = checkFramebufferStatus();
531 funcs.glDeleteRenderbuffers(1, &depth_buffer);
532 stencil_buffer = depth_buffer = 0;
536 if (depth_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil
537 || (attachment == QOpenGLFramebufferObject::Depth)))
539 funcs.glGenRenderbuffers(1, &depth_buffer);
540 Q_ASSERT(!funcs.glIsRenderbuffer(depth_buffer));
541 funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
542 Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
543 if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) {
545 if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) {
546 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
547 GL_DEPTH_COMPONENT24, size.width(), size.height());
549 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
550 GL_DEPTH_COMPONENT16, size.width(), size.height());
553 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
554 GL_DEPTH_COMPONENT, size.width(), size.height());
558 if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) {
559 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24,
560 size.width(), size.height());
562 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
563 size.width(), size.height());
566 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height());
569 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
570 GL_RENDERBUFFER, depth_buffer);
571 valid = checkFramebufferStatus();
573 funcs.glDeleteRenderbuffers(1, &depth_buffer);
578 if (stencil_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil)) {
579 funcs.glGenRenderbuffers(1, &stencil_buffer);
580 Q_ASSERT(!funcs.glIsRenderbuffer(stencil_buffer));
581 funcs.glBindRenderbuffer(GL_RENDERBUFFER, stencil_buffer);
582 Q_ASSERT(funcs.glIsRenderbuffer(stencil_buffer));
583 if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) {
585 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
586 GL_STENCIL_INDEX8, size.width(), size.height());
588 funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
589 GL_STENCIL_INDEX, size.width(), size.height());
593 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8,
594 size.width(), size.height());
596 funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX,
597 size.width(), size.height());
600 funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
601 GL_RENDERBUFFER, stencil_buffer);
602 valid = checkFramebufferStatus();
604 funcs.glDeleteRenderbuffers(1, &stencil_buffer);
609 // The FBO might have become valid after removing the depth or stencil buffer.
610 valid = checkFramebufferStatus();
612 if (depth_buffer && stencil_buffer) {
613 fbo_attachment = QOpenGLFramebufferObject::CombinedDepthStencil;
614 } else if (depth_buffer) {
615 fbo_attachment = QOpenGLFramebufferObject::Depth;
617 fbo_attachment = QOpenGLFramebufferObject::NoAttachment;
620 funcs.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);
622 fbo_guard = new QOpenGLSharedResourceGuard(ctx, fbo, freeFramebufferFunc);
624 color_buffer_guard = new QOpenGLSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc);
626 texture_guard = new QOpenGLSharedResourceGuard(ctx, texture, freeTextureFunc);
628 depth_buffer_guard = new QOpenGLSharedResourceGuard(ctx, depth_buffer, freeRenderbufferFunc);
629 if (stencil_buffer) {
630 if (stencil_buffer == depth_buffer)
631 stencil_buffer_guard = depth_buffer_guard;
633 stencil_buffer_guard = new QOpenGLSharedResourceGuard(ctx, stencil_buffer, freeRenderbufferFunc);
637 funcs.glDeleteRenderbuffers(1, &color_buffer);
639 glDeleteTextures(1, &texture);
641 funcs.glDeleteRenderbuffers(1, &depth_buffer);
642 if (stencil_buffer && depth_buffer != stencil_buffer)
643 funcs.glDeleteRenderbuffers(1, &stencil_buffer);
644 funcs.glDeleteFramebuffers(1, &fbo);
648 format.setTextureTarget(target);
649 format.setSamples(int(samples));
650 format.setAttachment(fbo_attachment);
651 format.setInternalTextureFormat(internal_format);
652 format.setMipmap(mipmap);
654 glDevice.setFBO(q, attachment);
658 \class QOpenGLFramebufferObject
659 \brief The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer object.
664 The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer
665 object, defined by the \c{GL_EXT_framebuffer_object} extension. In
666 addition it provides a rendering surface that can be painted on
667 with a QPainter, rendered to using native GL calls, or both. This
668 surface can be bound and used as a regular texture in your own GL
669 drawing code. By default, the QOpenGLFramebufferObject class
670 generates a 2D GL texture (using the \c{GL_TEXTURE_2D} target),
671 which is used as the internal rendering target.
673 \bold{It is important to have a current GL context when creating a
674 QOpenGLFramebufferObject, otherwise initialization will fail.}
676 OpenGL framebuffer objects and pbuffers (see
677 \l{QOpenGLPixelBuffer}{QOpenGLPixelBuffer}) can both be used to render to
678 offscreen surfaces, but there are a number of advantages with
679 using framebuffer objects instead of pbuffers:
682 \o A framebuffer object does not require a separate rendering
683 context, so no context switching will occur when switching
684 rendering targets. There is an overhead involved in switching
685 targets, but in general it is cheaper than a context switch to a
688 \o Rendering to dynamic textures (i.e. render-to-texture
689 functionality) works on all platforms. No need to do explicit copy
690 calls from a render buffer into a texture, as was necessary on
691 systems that did not support the \c{render_texture} extension.
693 \o It is possible to attach several rendering buffers (or texture
694 objects) to the same framebuffer object, and render to all of them
695 without doing a context switch.
697 \o The OpenGL framebuffer extension is a pure GL extension with no
698 system dependant WGL, CGL, or GLX parts. This makes using
699 framebuffer objects more portable.
702 When using a QPainter to paint to a QOpenGLFramebufferObject you should take
703 care that the QOpenGLFramebufferObject is created with the CombinedDepthStencil
704 attachment for QPainter to be able to render correctly.
705 Note that you need to create a QOpenGLFramebufferObject with more than one
706 sample per pixel for primitives to be antialiased when drawing using a
707 QPainter. To create a multisample framebuffer object you should use one of
708 the constructors that take a QOpenGLFramebufferObject parameter, and set the
709 QOpenGLFramebufferObject::samples() property to a non-zero value.
711 When painting to a QOpenGLFramebufferObject using QPainter, the state of
712 the current GL context will be altered by the paint engine to reflect
713 its needs. Applications should not rely upon the GL state being reset
714 to its original conditions, particularly the current shader program,
715 GL viewport, texture units, and drawing modes.
717 For multisample framebuffer objects a color render buffer is created,
718 otherwise a texture with the specified texture target is created.
719 The color render buffer or texture will have the specified internal
720 format, and will be bound to the \c GL_COLOR_ATTACHMENT0
721 attachment in the framebuffer object.
723 If you want to use a framebuffer object with multisampling enabled
724 as a texture, you first need to copy from it to a regular framebuffer
725 object using QOpenGLContext::blitFramebuffer().
729 As of Qt 4.8, it's possible to draw into a QOpenGLFramebufferObject
730 using a QPainter in a separate thread. Note that OpenGL 2.0 or
731 OpenGL ES 2.0 is required for this to work.
733 \sa {Framebuffer Object Example}
738 \enum QOpenGLFramebufferObject::Attachment
741 This enum type is used to configure the depth and stencil buffers
742 attached to the framebuffer object when it is created.
744 \value NoAttachment No attachment is added to the framebuffer object. Note that the
745 OpenGL depth and stencil tests won't work when rendering to a
746 framebuffer object without any depth or stencil buffers.
747 This is the default value.
749 \value CombinedDepthStencil If the \c GL_EXT_packed_depth_stencil extension is present,
750 a combined depth and stencil buffer is attached.
751 If the extension is not present, only a depth buffer is attached.
753 \value Depth A depth buffer is attached to the framebuffer object.
759 /*! \fn QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, GLenum target)
761 Constructs an OpenGL framebuffer object and binds a 2D GL texture
762 to the buffer of the size \a size. The texture is bound to the
763 \c GL_COLOR_ATTACHMENT0 target in the framebuffer object.
765 The \a target parameter is used to specify the GL texture
766 target. The default target is \c GL_TEXTURE_2D. Keep in mind that
767 \c GL_TEXTURE_2D textures must have a power of 2 width and height
768 (e.g. 256x512), unless you are using OpenGL 2.0 or higher.
770 By default, no depth and stencil buffers are attached. This behavior
771 can be toggled using one of the overloaded constructors.
773 The default internal texture format is \c GL_RGBA8 for desktop
774 OpenGL, and \c GL_RGBA for OpenGL/ES.
776 It is important that you have a current GL context set when
777 creating the QOpenGLFramebufferObject, otherwise the initialization
780 \sa size(), texture(), attachment()
783 QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, GLenum target)
784 : d_ptr(new QOpenGLFramebufferObjectPrivate)
786 Q_D(QOpenGLFramebufferObject);
787 d->init(this, size, NoAttachment, target, DEFAULT_FORMAT);
792 Constructs an OpenGL framebuffer object and binds a 2D GL texture
793 to the buffer of the given \a width and \a height.
795 \sa size(), texture()
797 QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, GLenum target)
798 : d_ptr(new QOpenGLFramebufferObjectPrivate)
800 Q_D(QOpenGLFramebufferObject);
801 d->init(this, QSize(width, height), NoAttachment, target, DEFAULT_FORMAT);
806 Constructs an OpenGL framebuffer object of the given \a size based on the
810 QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, const QOpenGLFramebufferObjectFormat &format)
811 : d_ptr(new QOpenGLFramebufferObjectPrivate)
813 Q_D(QOpenGLFramebufferObject);
814 d->init(this, size, format.attachment(), format.textureTarget(), format.internalTextureFormat(),
815 format.samples(), format.mipmap());
820 Constructs an OpenGL framebuffer object of the given \a width and \a height
821 based on the supplied \a format.
824 QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, const QOpenGLFramebufferObjectFormat &format)
825 : d_ptr(new QOpenGLFramebufferObjectPrivate)
827 Q_D(QOpenGLFramebufferObject);
828 d->init(this, QSize(width, height), format.attachment(), format.textureTarget(),
829 format.internalTextureFormat(), format.samples(), format.mipmap());
834 Constructs an OpenGL framebuffer object and binds a texture to the
835 buffer of the given \a width and \a height.
837 The \a attachment parameter describes the depth/stencil buffer
838 configuration, \a target the texture target and \a internal_format
839 the internal texture format. The default texture target is \c
840 GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8
841 for desktop OpenGL and \c GL_RGBA for OpenGL/ES.
843 \sa size(), texture(), attachment()
845 QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, Attachment attachment,
846 GLenum target, GLenum internal_format)
847 : d_ptr(new QOpenGLFramebufferObjectPrivate)
849 Q_D(QOpenGLFramebufferObject);
850 d->init(this, QSize(width, height), attachment, target, internal_format);
855 Constructs an OpenGL framebuffer object and binds a texture to the
856 buffer of the given \a size.
858 The \a attachment parameter describes the depth/stencil buffer
859 configuration, \a target the texture target and \a internal_format
860 the internal texture format. The default texture target is \c
861 GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8
862 for desktop OpenGL and \c GL_RGBA for OpenGL/ES.
864 \sa size(), texture(), attachment()
866 QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, Attachment attachment,
867 GLenum target, GLenum internal_format)
868 : d_ptr(new QOpenGLFramebufferObjectPrivate)
870 Q_D(QOpenGLFramebufferObject);
871 d->init(this, size, attachment, target, internal_format);
875 \fn QOpenGLFramebufferObject::~QOpenGLFramebufferObject()
877 Destroys the framebuffer object and frees any allocated resources.
879 QOpenGLFramebufferObject::~QOpenGLFramebufferObject()
881 Q_D(QOpenGLFramebufferObject);
885 if (d->texture_guard)
886 d->texture_guard->free();
887 if (d->color_buffer_guard)
888 d->color_buffer_guard->free();
889 if (d->depth_buffer_guard)
890 d->depth_buffer_guard->free();
891 if (d->stencil_buffer_guard && d->stencil_buffer_guard != d->depth_buffer_guard)
892 d->stencil_buffer_guard->free();
894 d->fbo_guard->free();
898 \fn bool QOpenGLFramebufferObject::isValid() const
900 Returns true if the framebuffer object is valid.
902 The framebuffer can become invalid if the initialization process
903 fails, the user attaches an invalid buffer to the framebuffer
904 object, or a non-power of two width/height is specified as the
905 texture size if the texture target is \c{GL_TEXTURE_2D}.
906 The non-power of two limitation does not apply if the OpenGL version
907 is 2.0 or higher, or if the GL_ARB_texture_non_power_of_two extension
910 The framebuffer can also become invalid if the QOpenGLContext that
911 the framebuffer was created within is destroyed and there are
912 no other shared contexts that can take over ownership of the
915 bool QOpenGLFramebufferObject::isValid() const
917 Q_D(const QOpenGLFramebufferObject);
918 return d->valid && d->fbo_guard && d->fbo_guard->id();
922 \fn bool QOpenGLFramebufferObject::bind()
924 Switches rendering from the default, windowing system provided
925 framebuffer to this framebuffer object.
926 Returns true upon success, false otherwise.
930 bool QOpenGLFramebufferObject::bind()
934 Q_D(QOpenGLFramebufferObject);
935 QOpenGLContext *current = QOpenGLContext::currentContext();
939 if (current->shareGroup() != d->fbo_guard->group())
940 qWarning("QOpenGLFramebufferObject::bind() called from incompatible context");
942 d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo());
943 d->valid = d->checkFramebufferStatus();
944 if (d->valid && current)
945 current->d_func()->current_fbo = d->fbo();
950 \fn bool QOpenGLFramebufferObject::release()
952 Switches rendering back to the default, windowing system provided
954 Returns true upon success, false otherwise.
958 bool QOpenGLFramebufferObject::release()
963 QOpenGLContext *current = QOpenGLContext::currentContext();
967 Q_D(QOpenGLFramebufferObject);
969 if (current->shareGroup() != d->fbo_guard->group())
970 qWarning("QOpenGLFramebufferObject::release() called from incompatible context");
974 current->d_func()->current_fbo = current->d_func()->default_fbo;
975 d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->d_func()->default_fbo);
982 \fn GLuint QOpenGLFramebufferObject::texture() const
984 Returns the texture id for the texture attached as the default
985 rendering target in this framebuffer object. This texture id can
986 be bound as a normal texture in your own GL code.
988 If a multisample framebuffer object is used then the value returned
989 from this function will be invalid.
991 GLuint QOpenGLFramebufferObject::texture() const
993 Q_D(const QOpenGLFramebufferObject);
994 return d->texture_guard ? d->texture_guard->id() : 0;
998 \fn QSize QOpenGLFramebufferObject::size() const
1000 Returns the size of the texture attached to this framebuffer
1003 QSize QOpenGLFramebufferObject::size() const
1005 Q_D(const QOpenGLFramebufferObject);
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();
1111 Q_GLOBAL_STATIC(QOpenGLEngineThreadStorage<QOpenGL2PaintEngineEx>, qt_buffer_2_engine)
1114 QPaintEngine *QOpenGLFramebufferObject::paintEngine() const
1116 Q_D(const QOpenGLFramebufferObject);
1120 QPaintEngine *engine = qt_buffer_2_engine()->engine();
1121 if (engine->isActive() && engine->paintDevice() != this) {
1122 d->engine = new QOpenGL2PaintEngineEx;
1129 \fn bool QOpenGLFramebufferObject::bindDefault()
1132 Switches rendering back to the default, windowing system provided
1134 Returns true upon success, false otherwise.
1136 \sa bind(), release()
1138 bool QOpenGLFramebufferObject::bindDefault()
1140 QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
1141 QOpenGLFunctions functions(ctx);
1144 ctx->d_func()->current_fbo = ctx->d_func()->default_fbo;
1145 functions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->default_fbo);
1148 qWarning("QOpenGLFramebufferObject::bindDefault() called without current context.");
1156 \fn bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()
1158 Returns true if the OpenGL \c{GL_EXT_framebuffer_object} extension
1159 is present on this system; otherwise returns false.
1161 bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()
1163 return QOpenGLFunctions(QOpenGLContext::currentContext()).hasOpenGLFeature(QOpenGLFunctions::Framebuffers);
1167 int QOpenGLFramebufferObject::metric(PaintDeviceMetric metric) const
1169 Q_D(const QOpenGLFramebufferObject);
1171 float dpmx = qt_defaultDpiX()*100./2.54;
1172 float dpmy = qt_defaultDpiY()*100./2.54;
1173 int w = d->size.width();
1174 int h = d->size.height();
1183 return qRound(w * 1000 / dpmx);
1186 return qRound(h * 1000 / dpmy);
1192 return 32;//d->depth;
1195 return qRound(dpmx * 0.0254);
1198 return qRound(dpmy * 0.0254);
1200 case PdmPhysicalDpiX:
1201 return qRound(dpmx * 0.0254);
1203 case PdmPhysicalDpiY:
1204 return qRound(dpmy * 0.0254);
1207 qWarning("QOpenGLFramebufferObject::metric(), Unhandled metric type: %d.\n", metric);
1214 \fn GLuint QOpenGLFramebufferObject::handle() const
1216 Returns the GL framebuffer object handle for this framebuffer
1217 object (returned by the \c{glGenFrameBuffersEXT()} function). This
1218 handle can be used to attach new images or buffers to the
1219 framebuffer. The user is responsible for cleaning up and
1220 destroying these objects.
1222 GLuint QOpenGLFramebufferObject::handle() const
1224 Q_D(const QOpenGLFramebufferObject);
1228 /*! \fn int QOpenGLFramebufferObject::devType() const
1234 Returns the status of the depth and stencil buffers attached to
1235 this framebuffer object.
1238 QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObject::attachment() const
1240 Q_D(const QOpenGLFramebufferObject);
1242 return d->fbo_attachment;
1243 return NoAttachment;
1249 Returns true if the framebuffer object is currently bound to a context,
1250 otherwise false is returned.
1253 bool QOpenGLFramebufferObject::isBound() const
1255 Q_D(const QOpenGLFramebufferObject);
1256 QOpenGLContext *current = QOpenGLContext::currentContext();
1257 return current ? current->d_func()->current_fbo == d->fbo() : false;
1261 \fn bool QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()
1265 Returns true if the OpenGL \c{GL_EXT_framebuffer_blit} extension
1266 is present on this system; otherwise returns false.
1268 \sa blitFramebuffer()
1270 bool QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()
1272 return QOpenGLExtensions(QOpenGLContext::currentContext()).hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
1278 Blits from the \a sourceRect rectangle in the \a source framebuffer
1279 object to the \a targetRect rectangle in the \a target framebuffer object.
1281 If \a source or \a target is 0, the default framebuffer will be used
1282 instead of a framebuffer object as source or target respectively.
1284 The \a buffers parameter should be a mask consisting of any combination of
1285 \c GL_COLOR_BUFFER_BIT, \c GL_DEPTH_BUFFER_BIT, and
1286 \c GL_STENCIL_BUFFER_BIT. Any buffer type that is not present both
1287 in the source and target buffers is ignored.
1289 The \a sourceRect and \a targetRect rectangles may have different sizes;
1290 in this case \a buffers should not contain \c GL_DEPTH_BUFFER_BIT or
1291 \c GL_STENCIL_BUFFER_BIT. The \a filter parameter should be set to
1292 \c GL_LINEAR or \c GL_NEAREST, and specifies whether linear or nearest
1293 interpolation should be used when scaling is performed.
1295 If \a source equals \a target a copy is performed within the same buffer.
1296 Results are undefined if the source and target rectangles overlap and
1297 have different sizes. The sizes must also be the same if any of the
1298 framebuffer objects are multisample framebuffers.
1300 Note that the scissor test will restrict the blit area if enabled.
1302 This function will have no effect unless hasOpenGLFramebufferBlit() returns
1305 \sa hasOpenGLFramebufferBlit()
1307 void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect,
1308 QOpenGLFramebufferObject *source, const QRect &sourceRect,
1312 QOpenGLContext *ctx = QOpenGLContext::currentContext();
1316 QOpenGLExtensions extensions(ctx);
1317 if (!extensions.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit))
1320 QSurface *surface = ctx->surface();
1322 const int height = static_cast<QWindow *>(surface)->height();
1324 const int sh = source ? source->height() : height;
1325 const int th = target ? target->height() : height;
1327 const int sx0 = sourceRect.left();
1328 const int sx1 = sourceRect.left() + sourceRect.width();
1329 const int sy0 = sh - (sourceRect.top() + sourceRect.height());
1330 const int sy1 = sh - sourceRect.top();
1332 const int tx0 = targetRect.left();
1333 const int tx1 = targetRect.left() + targetRect.width();
1334 const int ty0 = th - (targetRect.top() + targetRect.height());
1335 const int ty1 = th - targetRect.top();
1337 extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, source ? source->handle() : 0);
1338 extensions.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target ? target->handle() : 0);
1340 extensions.glBlitFramebuffer(sx0, sy0, sx1, sy1,
1344 extensions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo);