summaryrefslogtreecommitdiffstats
path: root/src/gui/opengl/qopenglframebufferobject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/opengl/qopenglframebufferobject.cpp')
-rw-r--r--src/gui/opengl/qopenglframebufferobject.cpp1857
1 files changed, 0 insertions, 1857 deletions
diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp
deleted file mode 100644
index d7a6d32218..0000000000
--- a/src/gui/opengl/qopenglframebufferobject.cpp
+++ /dev/null
@@ -1,1857 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtGui module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qopenglframebufferobject.h"
-#include "qopenglframebufferobject_p.h"
-
-#include <qdebug.h>
-#include <private/qopengl_p.h>
-#include <private/qopenglcontext_p.h>
-#include <private/qopenglextensions_p.h>
-#include <private/qfont_p.h>
-
-#include <qwindow.h>
-#include <qimage.h>
-#include <QtCore/qbytearray.h>
-
-QT_BEGIN_NAMESPACE
-
-#ifndef QT_NO_DEBUG
-#define QT_RESET_GLERROR() \
-{ \
- while (true) {\
- GLenum error = QOpenGLContext::currentContext()->functions()->glGetError(); \
- if (error == GL_NO_ERROR || error == GL_CONTEXT_LOST) \
- break; \
- } \
-}
-#define QT_CHECK_GLERROR() \
-{ \
- GLenum err = QOpenGLContext::currentContext()->functions()->glGetError(); \
- if (err != GL_NO_ERROR && err != GL_CONTEXT_LOST) { \
- qDebug("[%s line %d] OpenGL Error: %d", \
- __FILE__, __LINE__, (int)err); \
- } \
-}
-#else
-#define QT_RESET_GLERROR() {}
-#define QT_CHECK_GLERROR() {}
-#endif
-
-#ifndef GL_MAX_SAMPLES
-#define GL_MAX_SAMPLES 0x8D57
-#endif
-
-#ifndef GL_RENDERBUFFER_SAMPLES
-#define GL_RENDERBUFFER_SAMPLES 0x8CAB
-#endif
-
-#ifndef GL_DEPTH24_STENCIL8
-#define GL_DEPTH24_STENCIL8 0x88F0
-#endif
-
-#ifndef GL_DEPTH_COMPONENT24
-#define GL_DEPTH_COMPONENT24 0x81A6
-#endif
-
-#ifndef GL_DEPTH_COMPONENT24_OES
-#define GL_DEPTH_COMPONENT24_OES 0x81A6
-#endif
-
-#ifndef GL_READ_FRAMEBUFFER
-#define GL_READ_FRAMEBUFFER 0x8CA8
-#endif
-
-#ifndef GL_DRAW_FRAMEBUFFER
-#define GL_DRAW_FRAMEBUFFER 0x8CA9
-#endif
-
-#ifndef GL_RGB8
-#define GL_RGB8 0x8051
-#endif
-
-#ifndef GL_RGB10
-#define GL_RGB10 0x8052
-#endif
-
-#ifndef GL_RGB16
-#define GL_RGB16 0x8054
-#endif
-
-#ifndef GL_RGBA8
-#define GL_RGBA8 0x8058
-#endif
-
-#ifndef GL_RGB10_A2
-#define GL_RGB10_A2 0x8059
-#endif
-
-#ifndef GL_RGBA16
-#define GL_RGBA16 0x805B
-#endif
-
-#ifndef GL_BGRA
-#define GL_BGRA 0x80E1
-#endif
-
-#ifndef GL_UNSIGNED_INT_8_8_8_8_REV
-#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
-#endif
-
-#ifndef GL_UNSIGNED_INT_2_10_10_10_REV
-#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
-#endif
-
-#ifndef GL_CONTEXT_LOST
-#define GL_CONTEXT_LOST 0x0507
-#endif
-
-#ifndef GL_DEPTH_STENCIL_ATTACHMENT
-#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
-#endif
-
-#ifndef GL_DEPTH_STENCIL
-#define GL_DEPTH_STENCIL 0x84F9
-#endif
-
-
-
-/*!
- \class QOpenGLFramebufferObjectFormat
- \brief The QOpenGLFramebufferObjectFormat class specifies the format of an OpenGL
- framebuffer object.
- \inmodule QtGui
-
- \since 5.0
-
- \ingroup painting-3D
-
- A framebuffer object has several characteristics:
- \list
- \li \l{setSamples()}{Number of samples per pixels.}
- \li \l{setAttachment()}{Depth and/or stencil attachments.}
- \li \l{setTextureTarget()}{Texture target.}
- \li \l{setInternalTextureFormat()}{Internal texture format.}
- \endlist
-
- Note that the desired attachments or number of samples per pixels might not
- be supported by the hardware driver. Call QOpenGLFramebufferObject::format()
- after creating a QOpenGLFramebufferObject to find the exact format that was
- used to create the frame buffer object.
-
- \sa QOpenGLFramebufferObject
-*/
-
-/*!
- \internal
-*/
-void QOpenGLFramebufferObjectFormat::detach()
-{
- if (d->ref.loadRelaxed() != 1) {
- QOpenGLFramebufferObjectFormatPrivate *newd
- = new QOpenGLFramebufferObjectFormatPrivate(d);
- if (!d->ref.deref())
- delete d;
- d = newd;
- }
-}
-
-/*!
- Creates a QOpenGLFramebufferObjectFormat object for specifying
- the format of an OpenGL framebuffer object.
-
- By default the format specifies a non-multisample framebuffer object with no
- depth/stencil attachments, texture target \c GL_TEXTURE_2D, and internal format \c GL_RGBA8.
- On OpenGL/ES systems, the default internal format is \c GL_RGBA.
-
- \sa samples(), attachment(), internalTextureFormat()
-*/
-
-QOpenGLFramebufferObjectFormat::QOpenGLFramebufferObjectFormat()
-{
- d = new QOpenGLFramebufferObjectFormatPrivate;
-}
-
-/*!
- Constructs a copy of \a other.
-*/
-
-QOpenGLFramebufferObjectFormat::QOpenGLFramebufferObjectFormat(const QOpenGLFramebufferObjectFormat &other)
-{
- d = other.d;
- d->ref.ref();
-}
-
-/*!
- Assigns \a other to this object.
-*/
-
-QOpenGLFramebufferObjectFormat &QOpenGLFramebufferObjectFormat::operator=(const QOpenGLFramebufferObjectFormat &other)
-{
- if (d != other.d) {
- other.d->ref.ref();
- if (!d->ref.deref())
- delete d;
- d = other.d;
- }
- return *this;
-}
-
-/*!
- Destroys the QOpenGLFramebufferObjectFormat.
-*/
-QOpenGLFramebufferObjectFormat::~QOpenGLFramebufferObjectFormat()
-{
- if (!d->ref.deref())
- delete d;
-}
-
-/*!
- Sets the number of samples per pixel for a multisample framebuffer object
- to \a samples. The default sample count of 0 represents a regular
- non-multisample framebuffer object.
-
- If the desired amount of samples per pixel is not supported by the hardware
- then the maximum number of samples per pixel will be used. Note that
- multisample framebuffer objects cannot be bound as textures. Also, the
- \c{GL_EXT_framebuffer_multisample} extension is required to create a
- framebuffer with more than one sample per pixel.
-
- \sa samples()
-*/
-void QOpenGLFramebufferObjectFormat::setSamples(int samples)
-{
- detach();
- d->samples = samples;
-}
-
-/*!
- Returns the number of samples per pixel if a framebuffer object
- is a multisample framebuffer object. Otherwise, returns 0.
- The default value is 0.
-
- \sa setSamples()
-*/
-int QOpenGLFramebufferObjectFormat::samples() const
-{
- return d->samples;
-}
-
-/*!
- Enables mipmapping if \a enabled is true; otherwise disables it.
-
- Mipmapping is disabled by default.
-
- If mipmapping is enabled, additional memory will be allocated for
- the mipmap levels. The mipmap levels can be updated by binding the
- texture and calling glGenerateMipmap(). Mipmapping cannot be enabled
- for multisampled framebuffer objects.
-
- \sa mipmap(), QOpenGLFramebufferObject::texture()
-*/
-void QOpenGLFramebufferObjectFormat::setMipmap(bool enabled)
-{
- detach();
- d->mipmap = enabled;
-}
-
-/*!
- Returns \c true if mipmapping is enabled.
-
- \sa setMipmap()
-*/
-bool QOpenGLFramebufferObjectFormat::mipmap() const
-{
- return d->mipmap;
-}
-
-/*!
- Sets the attachment configuration of a framebuffer object to \a attachment.
-
- \sa attachment()
-*/
-void QOpenGLFramebufferObjectFormat::setAttachment(QOpenGLFramebufferObject::Attachment attachment)
-{
- detach();
- d->attachment = attachment;
-}
-
-/*!
- Returns the configuration of the depth and stencil buffers attached to
- a framebuffer object. The default is QOpenGLFramebufferObject::NoAttachment.
-
- \sa setAttachment()
-*/
-QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObjectFormat::attachment() const
-{
- return d->attachment;
-}
-
-/*!
- Sets the texture target of the texture attached to a framebuffer object to
- \a target. Ignored for multisample framebuffer objects.
-
- \sa textureTarget(), samples()
-*/
-void QOpenGLFramebufferObjectFormat::setTextureTarget(GLenum target)
-{
- detach();
- d->target = target;
-}
-
-/*!
- Returns the texture target of the texture attached to a framebuffer object.
- Ignored for multisample framebuffer objects. The default is
- \c GL_TEXTURE_2D.
-
- \sa setTextureTarget(), samples()
-*/
-GLenum QOpenGLFramebufferObjectFormat::textureTarget() const
-{
- return d->target;
-}
-
-/*!
- Sets the internal format of a framebuffer object's texture or
- multisample framebuffer object's color buffer to
- \a internalTextureFormat.
-
- \sa internalTextureFormat()
-*/
-void QOpenGLFramebufferObjectFormat::setInternalTextureFormat(GLenum internalTextureFormat)
-{
- detach();
- d->internal_format = internalTextureFormat;
-}
-
-/*!
- Returns the internal format of a framebuffer object's texture or
- multisample framebuffer object's color buffer. The default is
- \c GL_RGBA8 on desktop OpenGL systems, and \c GL_RGBA on
- OpenGL/ES systems.
-
- \sa setInternalTextureFormat()
-*/
-GLenum QOpenGLFramebufferObjectFormat::internalTextureFormat() const
-{
- return d->internal_format;
-}
-
-/*!
- Returns \c true if all the options of this framebuffer object format
- are the same as \a other; otherwise returns \c false.
-*/
-bool QOpenGLFramebufferObjectFormat::operator==(const QOpenGLFramebufferObjectFormat& other) const
-{
- if (d == other.d)
- return true;
- else
- return d->equals(other.d);
-}
-
-/*!
- Returns \c false if all the options of this framebuffer object format
- are the same as \a other; otherwise returns \c true.
-*/
-bool QOpenGLFramebufferObjectFormat::operator!=(const QOpenGLFramebufferObjectFormat& other) const
-{
- return !(*this == other);
-}
-
-bool QOpenGLFramebufferObjectPrivate::checkFramebufferStatus(QOpenGLContext *ctx) const
-{
- if (!ctx)
- return false; // Context no longer exists.
- GLenum status = ctx->functions()->glCheckFramebufferStatus(GL_FRAMEBUFFER);
- switch(status) {
- case GL_NO_ERROR:
- case GL_FRAMEBUFFER_COMPLETE:
- return true;
- case GL_FRAMEBUFFER_UNSUPPORTED:
- qDebug("QOpenGLFramebufferObject: Unsupported framebuffer format.");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
- qDebug("QOpenGLFramebufferObject: Framebuffer incomplete attachment.");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
- qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing attachment.");
- break;
-#ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT
- case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT:
- qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, duplicate attachment.");
- break;
-#endif
-#ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
- case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
- qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attached images must have same dimensions.");
- break;
-#endif
-#ifdef GL_FRAMEBUFFER_INCOMPLETE_FORMATS
- case GL_FRAMEBUFFER_INCOMPLETE_FORMATS:
- qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attached images must have same format.");
- break;
-#endif
-#ifdef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER
- case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
- qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing draw buffer.");
- break;
-#endif
-#ifdef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER
- case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
- qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing read buffer.");
- break;
-#endif
-#ifdef GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE
- case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
- qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attachments must have same number of samples per pixel.");
- break;
-#endif
- default:
- qDebug() <<"QOpenGLFramebufferObject: An undefined error has occurred: "<< status;
- break;
- }
- return false;
-}
-
-namespace
-{
- void freeFramebufferFunc(QOpenGLFunctions *funcs, GLuint id)
- {
- funcs->glDeleteFramebuffers(1, &id);
- }
-
- void freeRenderbufferFunc(QOpenGLFunctions *funcs, GLuint id)
- {
- funcs->glDeleteRenderbuffers(1, &id);
- }
-
- void freeTextureFunc(QOpenGLFunctions *funcs, GLuint id)
- {
- funcs->glDeleteTextures(1, &id);
- }
-}
-
-void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSize &size,
- QOpenGLFramebufferObject::Attachment attachment,
- GLenum texture_target, GLenum internal_format,
- GLint samples, bool mipmap)
-{
- QOpenGLContext *ctx = QOpenGLContext::currentContext();
-
- funcs.initializeOpenGLFunctions();
-
- if (!funcs.hasOpenGLFeature(QOpenGLFunctions::Framebuffers))
- return;
-
- // Fall back to using a normal non-msaa FBO if we don't have support for MSAA
- if (!funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)
- || !funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit)) {
- samples = 0;
- } else if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
- GLint maxSamples;
- funcs.glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
- samples = qBound(0, int(samples), int(maxSamples));
- }
-
- colorAttachments.append(ColorAttachment(size, internal_format));
-
- dsSize = size;
-
- samples = qMax(0, samples);
- requestedSamples = samples;
-
- target = texture_target;
-
- QT_RESET_GLERROR(); // reset error state
- GLuint fbo = 0;
-
- funcs.glGenFramebuffers(1, &fbo);
- funcs.glBindFramebuffer(GL_FRAMEBUFFER, fbo);
-
- QOpenGLContextPrivate::get(ctx)->qgl_current_fbo_invalid = true;
-
- QT_CHECK_GLERROR();
-
- format.setTextureTarget(target);
- format.setInternalTextureFormat(internal_format);
- format.setMipmap(mipmap);
-
- if (samples == 0)
- initTexture(0);
- else
- initColorBuffer(0, &samples);
-
- format.setSamples(int(samples));
-
- initDepthStencilAttachments(ctx, attachment);
-
- if (valid)
- fbo_guard = new QOpenGLSharedResourceGuard(ctx, fbo, freeFramebufferFunc);
- else
- funcs.glDeleteFramebuffers(1, &fbo);
-
- QT_CHECK_GLERROR();
-}
-
-void QOpenGLFramebufferObjectPrivate::initTexture(int idx)
-{
- QOpenGLContext *ctx = QOpenGLContext::currentContext();
- GLuint texture = 0;
-
- funcs.glGenTextures(1, &texture);
- funcs.glBindTexture(target, texture);
-
- funcs.glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- funcs.glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- funcs.glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- funcs.glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- ColorAttachment &color(colorAttachments[idx]);
-
- GLuint pixelType = GL_UNSIGNED_BYTE;
- if (color.internalFormat == GL_RGB10_A2 || color.internalFormat == GL_RGB10)
- pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
- else if (color.internalFormat == GL_RGB16 || color.internalFormat == GL_RGBA16)
- pixelType = GL_UNSIGNED_SHORT;
-
- funcs.glTexImage2D(target, 0, color.internalFormat, color.size.width(), color.size.height(), 0,
- GL_RGBA, pixelType, nullptr);
- if (format.mipmap()) {
- int width = color.size.width();
- int height = color.size.height();
- int level = 0;
- while (width > 1 || height > 1) {
- width = qMax(1, width >> 1);
- height = qMax(1, height >> 1);
- ++level;
- funcs.glTexImage2D(target, level, color.internalFormat, width, height, 0,
- GL_RGBA, pixelType, nullptr);
- }
- }
- funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + idx,
- target, texture, 0);
-
- QT_CHECK_GLERROR();
- funcs.glBindTexture(target, 0);
- valid = checkFramebufferStatus(ctx);
- if (valid) {
- color.guard = new QOpenGLSharedResourceGuard(ctx, texture, freeTextureFunc);
- } else {
- funcs.glDeleteTextures(1, &texture);
- }
-}
-
-void QOpenGLFramebufferObjectPrivate::initColorBuffer(int idx, GLint *samples)
-{
- QOpenGLContext *ctx = QOpenGLContext::currentContext();
- GLuint color_buffer = 0;
-
- ColorAttachment &color(colorAttachments[idx]);
-
- GLenum storageFormat = color.internalFormat;
- // ES requires a sized format. The older desktop extension does not. Correct the format on ES.
- if (ctx->isOpenGLES()) {
- if (color.internalFormat == GL_RGBA) {
- if (funcs.hasOpenGLExtension(QOpenGLExtensions::Sized8Formats))
- storageFormat = GL_RGBA8;
- else
- storageFormat = GL_RGBA4;
- } else if (color.internalFormat == GL_RGB10) {
- // GL_RGB10 is not allowed in ES for glRenderbufferStorage.
- storageFormat = GL_RGB10_A2;
- }
- }
-
- funcs.glGenRenderbuffers(1, &color_buffer);
- funcs.glBindRenderbuffer(GL_RENDERBUFFER, color_buffer);
- funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, *samples, storageFormat, color.size.width(), color.size.height());
- funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + idx,
- GL_RENDERBUFFER, color_buffer);
-
- QT_CHECK_GLERROR();
- valid = checkFramebufferStatus(ctx);
- if (valid) {
- // Query the actual number of samples. This can be greater than the requested
- // value since the typically supported values are 0, 4, 8, ..., and the
- // requests are mapped to the next supported value.
- funcs.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, samples);
- color.guard = new QOpenGLSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc);
- } else {
- funcs.glDeleteRenderbuffers(1, &color_buffer);
- }
-}
-
-void QOpenGLFramebufferObjectPrivate::initDepthStencilAttachments(QOpenGLContext *ctx,
- QOpenGLFramebufferObject::Attachment attachment)
-{
- // Use the same sample count for all attachments. format.samples() already contains
- // the actual number of samples for the color attachment and is not suitable. Use
- // requestedSamples instead.
- const int samples = requestedSamples;
-
- // free existing attachments
- if (depth_buffer_guard) {
-#ifdef Q_OS_WASM
- funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
-#else
- funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
-#endif
- depth_buffer_guard->free();
- }
- if (stencil_buffer_guard) {
- funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
- if (stencil_buffer_guard != depth_buffer_guard)
- stencil_buffer_guard->free();
- }
-
- depth_buffer_guard = nullptr;
- stencil_buffer_guard = nullptr;
-
- GLuint depth_buffer = 0;
- GLuint stencil_buffer = 0;
-
- // In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a
- // separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer
- // might not be supported while separate buffers are, according to QTBUG-12861.
-#ifdef Q_OS_WASM
- // WebGL doesn't allow separately attach buffers to
- // STENCIL_ATTACHMENT and DEPTH_ATTACHMENT
- // QTBUG-69913
- if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil) {
- funcs.glGenRenderbuffers(1, &depth_buffer);
- funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
- Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
-
- if (samples != 0 ) {
- funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
- GL_DEPTH24_STENCIL8, dsSize.width(), dsSize.height());
- } else {
- funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL,
- dsSize.width(), dsSize.height());
- }
-
- funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER, depth_buffer);
-
- valid = checkFramebufferStatus(ctx);
- if (!valid) {
- funcs.glDeleteRenderbuffers(1, &depth_buffer);
- depth_buffer = 0;
- }
- }
-#else
- if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil
- && funcs.hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil))
- {
- // depth and stencil buffer needs another extension
- funcs.glGenRenderbuffers(1, &depth_buffer);
- funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
- Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
- if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
- funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
- GL_DEPTH24_STENCIL8, dsSize.width(), dsSize.height());
- else
- funcs.glRenderbufferStorage(GL_RENDERBUFFER,
- GL_DEPTH24_STENCIL8, dsSize.width(), dsSize.height());
-
- stencil_buffer = depth_buffer;
- funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
- GL_RENDERBUFFER, depth_buffer);
- funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER, stencil_buffer);
-
- valid = checkFramebufferStatus(ctx);
- if (!valid) {
- funcs.glDeleteRenderbuffers(1, &depth_buffer);
- stencil_buffer = depth_buffer = 0;
- }
- }
-
- if (depth_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil
- || (attachment == QOpenGLFramebufferObject::Depth)))
- {
- funcs.glGenRenderbuffers(1, &depth_buffer);
- funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
- Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
- if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) {
- if (ctx->isOpenGLES()) {
- if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24))
- funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
- GL_DEPTH_COMPONENT24, dsSize.width(), dsSize.height());
- else
- funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
- GL_DEPTH_COMPONENT16, dsSize.width(), dsSize.height());
- } else {
- funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
- GL_DEPTH_COMPONENT, dsSize.width(), dsSize.height());
- }
- } else {
- if (ctx->isOpenGLES()) {
- if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) {
- funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24,
- dsSize.width(), dsSize.height());
- } else {
- funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
- dsSize.width(), dsSize.height());
- }
- } else {
- funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, dsSize.width(), dsSize.height());
- }
- }
- funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
- GL_RENDERBUFFER, depth_buffer);
- valid = checkFramebufferStatus(ctx);
- if (!valid) {
- funcs.glDeleteRenderbuffers(1, &depth_buffer);
- depth_buffer = 0;
- }
- }
-
- if (stencil_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil)) {
- funcs.glGenRenderbuffers(1, &stencil_buffer);
- funcs.glBindRenderbuffer(GL_RENDERBUFFER, stencil_buffer);
- Q_ASSERT(funcs.glIsRenderbuffer(stencil_buffer));
-
-#ifdef QT_OPENGL_ES
- GLenum storage = GL_STENCIL_INDEX8;
-#else
- GLenum storage = ctx->isOpenGLES() ? GL_STENCIL_INDEX8 : GL_STENCIL_INDEX;
-#endif
-
- if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
- funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, storage, dsSize.width(), dsSize.height());
- else
- funcs.glRenderbufferStorage(GL_RENDERBUFFER, storage, dsSize.width(), dsSize.height());
-
- funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER, stencil_buffer);
- valid = checkFramebufferStatus(ctx);
- if (!valid) {
- funcs.glDeleteRenderbuffers(1, &stencil_buffer);
- stencil_buffer = 0;
- }
- }
-#endif //Q_OS_WASM
-
- // The FBO might have become valid after removing the depth or stencil buffer.
- valid = checkFramebufferStatus(ctx);
-
-#ifdef Q_OS_WASM
- if (depth_buffer) {
-#else
- if (depth_buffer && stencil_buffer) {
-#endif
- fbo_attachment = QOpenGLFramebufferObject::CombinedDepthStencil;
- } else if (depth_buffer) {
- fbo_attachment = QOpenGLFramebufferObject::Depth;
- } else {
- fbo_attachment = QOpenGLFramebufferObject::NoAttachment;
- }
-
- if (valid) {
- if (depth_buffer)
- depth_buffer_guard = new QOpenGLSharedResourceGuard(ctx, depth_buffer, freeRenderbufferFunc);
- if (stencil_buffer) {
- if (stencil_buffer == depth_buffer)
- stencil_buffer_guard = depth_buffer_guard;
- else
- stencil_buffer_guard = new QOpenGLSharedResourceGuard(ctx, stencil_buffer, freeRenderbufferFunc);
- }
- } else {
- if (depth_buffer)
- funcs.glDeleteRenderbuffers(1, &depth_buffer);
- if (stencil_buffer && depth_buffer != stencil_buffer)
- funcs.glDeleteRenderbuffers(1, &stencil_buffer);
- }
- QT_CHECK_GLERROR();
-
- format.setAttachment(fbo_attachment);
-}
-
-/*!
- \class QOpenGLFramebufferObject
- \brief The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer object.
- \since 5.0
- \inmodule QtGui
-
- \ingroup painting-3D
-
- The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer
- object, defined by the \c{GL_EXT_framebuffer_object} extension. It provides
- a rendering surface that can be painted on with a QPainter with the help of
- QOpenGLPaintDevice, or rendered to using native OpenGL calls. This surface
- can be bound and used as a regular texture in your own OpenGL drawing code.
- By default, the QOpenGLFramebufferObject class generates a 2D OpenGL
- texture (using the \c{GL_TEXTURE_2D} target), which is used as the internal
- rendering target.
-
- \b{It is important to have a current OpenGL context when creating a
- QOpenGLFramebufferObject, otherwise initialization will fail.}
-
- Create the QOpenGLFrameBufferObject instance with the CombinedDepthStencil
- attachment if you want QPainter to render correctly. Note that you need to
- create a QOpenGLFramebufferObject with more than one sample per pixel for
- primitives to be antialiased when drawing using a QPainter. To create a
- multisample framebuffer object you should use one of the constructors that
- take a QOpenGLFramebufferObjectFormat parameter, and set the
- QOpenGLFramebufferObjectFormat::samples() property to a non-zero value.
-
- For multisample framebuffer objects a color render buffer is created,
- otherwise a texture with the specified texture target is created.
- The color render buffer or texture will have the specified internal
- format, and will be bound to the \c GL_COLOR_ATTACHMENT0
- attachment in the framebuffer object.
-
- Multiple render targets are also supported, in case the OpenGL
- implementation supports this. Here there will be multiple textures (or, in
- case of multisampling, renderbuffers) present and each of them will get
- attached to \c GL_COLOR_ATTACHMENT0, \c 1, \c 2, ...
-
- If you want to use a framebuffer object with multisampling enabled
- as a texture, you first need to copy from it to a regular framebuffer
- object using QOpenGLContext::blitFramebuffer().
-
- It is possible to draw into a QOpenGLFramebufferObject using QPainter and
- QOpenGLPaintDevice in a separate thread.
-*/
-
-
-/*!
- \enum QOpenGLFramebufferObject::Attachment
-
- This enum type is used to configure the depth and stencil buffers
- attached to the framebuffer object when it is created.
-
- \value NoAttachment No attachment is added to the framebuffer object. Note that the
- OpenGL depth and stencil tests won't work when rendering to a
- framebuffer object without any depth or stencil buffers.
- This is the default value.
-
- \value CombinedDepthStencil If the \c GL_EXT_packed_depth_stencil extension is present,
- a combined depth and stencil buffer is attached.
- If the extension is not present, only a depth buffer is attached.
-
- \value Depth A depth buffer is attached to the framebuffer object.
-
- \sa attachment()
-*/
-
-static inline GLenum effectiveInternalFormat(GLenum internalFormat)
-{
- if (!internalFormat)
-#ifdef QT_OPENGL_ES_2
- internalFormat = GL_RGBA;
-#else
- internalFormat = QOpenGLContext::currentContext()->isOpenGLES() ? GL_RGBA : GL_RGBA8;
-#endif
- return internalFormat;
-}
-
-/*!
-
- Constructs an OpenGL framebuffer object and binds a 2D OpenGL texture
- to the buffer of the size \a size. The texture is bound to the
- \c GL_COLOR_ATTACHMENT0 target in the framebuffer object.
-
- The \a target parameter is used to specify the OpenGL texture
- target. The default target is \c GL_TEXTURE_2D. Keep in mind that
- \c GL_TEXTURE_2D textures must have a power of 2 width and height
- (e.g. 256x512), unless you are using OpenGL 2.0 or higher.
-
- By default, no depth and stencil buffers are attached. This behavior
- can be toggled using one of the overloaded constructors.
-
- The default internal texture format is \c GL_RGBA8 for desktop
- OpenGL, and \c GL_RGBA for OpenGL/ES.
-
- It is important that you have a current OpenGL context set when
- creating the QOpenGLFramebufferObject, otherwise the initialization
- will fail.
-
- \sa size(), texture(), attachment()
-*/
-
-QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, GLenum target)
- : d_ptr(new QOpenGLFramebufferObjectPrivate)
-{
- Q_D(QOpenGLFramebufferObject);
- d->init(this, size, NoAttachment, target, effectiveInternalFormat(0));
-}
-
-/*!
-
- Constructs an OpenGL framebuffer object and binds a 2D OpenGL texture
- to the buffer of the given \a width and \a height.
-
- \sa size(), texture()
-*/
-QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, GLenum target)
- : QOpenGLFramebufferObject(QSize(width, height), target)
-{
-}
-
-/*!
-
- Constructs an OpenGL framebuffer object of the given \a size based on the
- supplied \a format.
-*/
-
-QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, const QOpenGLFramebufferObjectFormat &format)
- : d_ptr(new QOpenGLFramebufferObjectPrivate)
-{
- Q_D(QOpenGLFramebufferObject);
- d->init(this, size, format.attachment(), format.textureTarget(), format.internalTextureFormat(),
- format.samples(), format.mipmap());
-}
-
-/*!
-
- Constructs an OpenGL framebuffer object of the given \a width and \a height
- based on the supplied \a format.
-*/
-
-QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, const QOpenGLFramebufferObjectFormat &format)
- : QOpenGLFramebufferObject(QSize(width, height), format)
-{
-}
-
-/*!
-
- Constructs an OpenGL framebuffer object and binds a texture to the
- buffer of the given \a width and \a height.
-
- The \a attachment parameter describes the depth/stencil buffer
- configuration, \a target the texture target and \a internalFormat
- the internal texture format. The default texture target is \c
- GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8
- for desktop OpenGL and \c GL_RGBA for OpenGL/ES.
-
- \sa size(), texture(), attachment()
-*/
-QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, Attachment attachment,
- GLenum target, GLenum internalFormat)
- : d_ptr(new QOpenGLFramebufferObjectPrivate)
-{
- Q_D(QOpenGLFramebufferObject);
- d->init(this, QSize(width, height), attachment, target, effectiveInternalFormat(internalFormat));
-}
-
-/*!
-
- Constructs an OpenGL framebuffer object and binds a texture to the
- buffer of the given \a size.
-
- The \a attachment parameter describes the depth/stencil buffer
- configuration, \a target the texture target and \a internalFormat
- the internal texture format. The default texture target is \c
- GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8
- for desktop OpenGL and \c GL_RGBA for OpenGL/ES.
-
- \sa size(), texture(), attachment()
-*/
-QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, Attachment attachment,
- GLenum target, GLenum internalFormat)
- : d_ptr(new QOpenGLFramebufferObjectPrivate)
-{
- Q_D(QOpenGLFramebufferObject);
- d->init(this, size, attachment, target, effectiveInternalFormat(internalFormat));
-}
-
-/*!
-
- Destroys the framebuffer object and frees any allocated resources.
-*/
-QOpenGLFramebufferObject::~QOpenGLFramebufferObject()
-{
- Q_D(QOpenGLFramebufferObject);
- if (isBound())
- release();
-
- for (const auto &color : qAsConst(d->colorAttachments)) {
- if (color.guard)
- color.guard->free();
- }
- d->colorAttachments.clear();
-
- if (d->depth_buffer_guard)
- d->depth_buffer_guard->free();
- if (d->stencil_buffer_guard && d->stencil_buffer_guard != d->depth_buffer_guard)
- d->stencil_buffer_guard->free();
- if (d->fbo_guard)
- d->fbo_guard->free();
-
- QOpenGLContextPrivate *contextPrv = QOpenGLContextPrivate::get(QOpenGLContext::currentContext());
- if (contextPrv && contextPrv->qgl_current_fbo == this) {
- contextPrv->qgl_current_fbo_invalid = true;
- contextPrv->qgl_current_fbo = nullptr;
- }
-}
-
-/*!
- Creates and attaches an additional texture or renderbuffer of \a size width
- and height.
-
- There is always an attachment at GL_COLOR_ATTACHMENT0. Call this function
- to set up additional attachments at GL_COLOR_ATTACHMENT1,
- GL_COLOR_ATTACHMENT2, ...
-
- When \a internalFormat is not \c 0, it specifies the internal format of the
- texture or renderbuffer. Otherwise a default of GL_RGBA or GL_RGBA8 is
- used.
-
- \note This is only functional when multiple render targets are supported by
- the OpenGL implementation. When that is not the case, the function will not
- add any additional color attachments. Call
- QOpenGLFunctions::hasOpenGLFeature() with
- QOpenGLFunctions::MultipleRenderTargets at runtime to check if MRT is
- supported.
-
- \note The internal format of the color attachments may differ but there may
- be limitations on the supported combinations, depending on the drivers.
-
- \note The size of the color attachments may differ but rendering is limited
- to the area that fits all the attachments, according to the OpenGL
- specification. Some drivers may not be fully conformant in this respect,
- however.
-
- \since 5.6
- */
-void QOpenGLFramebufferObject::addColorAttachment(const QSize &size, GLenum internalFormat)
-{
- Q_D(QOpenGLFramebufferObject);
-
- if (!QOpenGLContext::currentContext()->functions()->hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets)) {
- qWarning("Multiple render targets not supported, ignoring extra color attachment request");
- return;
- }
-
- QOpenGLFramebufferObjectPrivate::ColorAttachment color(size, effectiveInternalFormat(internalFormat));
- d->colorAttachments.append(color);
- const int idx = d->colorAttachments.count() - 1;
-
- if (d->requestedSamples == 0) {
- d->initTexture(idx);
- } else {
- GLint samples = d->requestedSamples;
- d->initColorBuffer(idx, &samples);
- }
-}
-
-/*! \overload
-
- Creates and attaches an additional texture or renderbuffer of size \a width and \a height.
-
- When \a internalFormat is not \c 0, it specifies the internal format of the texture or
- renderbuffer. Otherwise a default of GL_RGBA or GL_RGBA8 is used.
-
- \since 5.6
- */
-void QOpenGLFramebufferObject::addColorAttachment(int width, int height, GLenum internalFormat)
-{
- addColorAttachment(QSize(width, height), internalFormat);
-}
-
-/*!
- \fn bool QOpenGLFramebufferObject::isValid() const
-
- Returns \c true if the framebuffer object is valid.
-
- The framebuffer can become invalid if the initialization process
- fails, the user attaches an invalid buffer to the framebuffer
- object, or a non-power of two width/height is specified as the
- texture size if the texture target is \c{GL_TEXTURE_2D}.
- The non-power of two limitation does not apply if the OpenGL version
- is 2.0 or higher, or if the GL_ARB_texture_non_power_of_two extension
- is present.
-
- The framebuffer can also become invalid if the QOpenGLContext that
- the framebuffer was created within is destroyed and there are
- no other shared contexts that can take over ownership of the
- framebuffer.
-*/
-bool QOpenGLFramebufferObject::isValid() const
-{
- Q_D(const QOpenGLFramebufferObject);
- return d->valid && d->fbo_guard && d->fbo_guard->id();
-}
-
-/*!
- \fn bool QOpenGLFramebufferObject::bind()
-
- Switches rendering from the default, windowing system provided
- framebuffer to this framebuffer object.
- Returns \c true upon success, false otherwise.
-
- \note If takeTexture() was called, a new texture is created and associated
- with the framebuffer object. This is potentially expensive and changes the
- context state (the currently bound texture).
-
- \sa release()
-*/
-bool QOpenGLFramebufferObject::bind()
-{
- if (!isValid())
- return false;
- Q_D(QOpenGLFramebufferObject);
- QOpenGLContext *current = QOpenGLContext::currentContext();
- if (!current)
- return false;
-#ifdef QT_DEBUG
- if (current->shareGroup() != d->fbo_guard->group())
- qWarning("QOpenGLFramebufferObject::bind() called from incompatible context");
-#endif
-
- d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo());
-
- QOpenGLContextPrivate::get(current)->qgl_current_fbo_invalid = true;
- QOpenGLContextPrivate::get(current)->qgl_current_fbo = this;
-
- if (d->format.samples() == 0) {
- // Create new textures to replace the ones stolen via takeTexture().
- for (int i = 0; i < d->colorAttachments.count(); ++i) {
- if (!d->colorAttachments.at(i).guard)
- d->initTexture(i);
- }
- }
-
- return d->valid;
-}
-
-/*!
- \fn bool QOpenGLFramebufferObject::release()
-
- Switches rendering back to the default, windowing system provided
- framebuffer.
- Returns \c true upon success, false otherwise.
-
- \sa bind()
-*/
-bool QOpenGLFramebufferObject::release()
-{
- if (!isValid())
- return false;
-
- QOpenGLContext *current = QOpenGLContext::currentContext();
- if (!current)
- return false;
-
- Q_D(QOpenGLFramebufferObject);
-#ifdef QT_DEBUG
- if (current->shareGroup() != d->fbo_guard->group())
- qWarning("QOpenGLFramebufferObject::release() called from incompatible context");
-#endif
-
- if (current) {
- d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->defaultFramebufferObject());
-
- QOpenGLContextPrivate *contextPrv = QOpenGLContextPrivate::get(current);
- contextPrv->qgl_current_fbo_invalid = true;
- contextPrv->qgl_current_fbo = nullptr;
- }
-
- return true;
-}
-
-/*!
- \fn GLuint QOpenGLFramebufferObject::texture() const
-
- Returns the texture id for the texture attached as the default
- rendering target in this framebuffer object. This texture id can
- be bound as a normal texture in your own OpenGL code.
-
- If a multisample framebuffer object is used then the value returned
- from this function will be invalid.
-
- When multiple textures are attached, the return value is the ID of
- the first one.
-
- \sa takeTexture(), textures()
-*/
-GLuint QOpenGLFramebufferObject::texture() const
-{
- Q_D(const QOpenGLFramebufferObject);
- return d->colorAttachments[0].guard ? d->colorAttachments[0].guard->id() : 0;
-}
-
-/*!
- Returns the texture id for all attached textures.
-
- If a multisample framebuffer object is used, then an empty vector is returned.
-
- \since 5.6
-
- \sa takeTexture(), texture()
-*/
-QVector<GLuint> QOpenGLFramebufferObject::textures() const
-{
- Q_D(const QOpenGLFramebufferObject);
- QVector<GLuint> ids;
- if (d->format.samples() != 0)
- return ids;
- ids.reserve(d->colorAttachments.count());
- for (const auto &color : d->colorAttachments)
- ids.append(color.guard ? color.guard->id() : 0);
- return ids;
-}
-
-/*!
- \fn GLuint QOpenGLFramebufferObject::takeTexture()
-
- Returns the texture id for the texture attached to this framebuffer
- object. The ownership of the texture is transferred to the caller.
-
- If the framebuffer object is currently bound, an implicit release()
- will be done. During the next call to bind() a new texture will be
- created.
-
- If a multisample framebuffer object is used, then there is no
- texture and the return value from this function will be invalid.
- Similarly, incomplete framebuffer objects will also return 0.
-
- \since 5.3
-
- \sa texture(), bind(), release()
- */
-GLuint QOpenGLFramebufferObject::takeTexture()
-{
- return takeTexture(0);
-}
-
-/*! \overload
-
- Returns the texture id for the texture attached to the color attachment of
- index \a colorAttachmentIndex of this framebuffer object. The ownership of
- the texture is transferred to the caller.
-
- When \a colorAttachmentIndex is \c 0, the behavior is identical to the
- parameter-less variant of this function.
-
- If the framebuffer object is currently bound, an implicit release()
- will be done. During the next call to bind() a new texture will be
- created.
-
- If a multisample framebuffer object is used, then there is no
- texture and the return value from this function will be invalid.
- Similarly, incomplete framebuffer objects will also return 0.
-
- \since 5.6
- */
-GLuint QOpenGLFramebufferObject::takeTexture(int colorAttachmentIndex)
-{
- Q_D(QOpenGLFramebufferObject);
- GLuint id = 0;
- if (isValid() && d->format.samples() == 0 && d->colorAttachments.count() > colorAttachmentIndex) {
- QOpenGLContext *current = QOpenGLContext::currentContext();
- if (current && current->shareGroup() == d->fbo_guard->group() && isBound())
- release();
- auto &guard = d->colorAttachments[colorAttachmentIndex].guard;
- id = guard ? guard->id() : 0;
- // Do not call free() on texture_guard, just null it out.
- // This way the texture will not be deleted when the guard is destroyed.
- guard = nullptr;
- }
- return id;
-}
-
-/*!
- \return the size of the color and depth/stencil attachments attached to
- this framebuffer object.
-*/
-QSize QOpenGLFramebufferObject::size() const
-{
- Q_D(const QOpenGLFramebufferObject);
- return d->dsSize;
-}
-
-/*!
- \return the sizes of all color attachments attached to this framebuffer
- object.
-
- \since 5.6
-*/
-QVector<QSize> QOpenGLFramebufferObject::sizes() const
-{
- Q_D(const QOpenGLFramebufferObject);
- QVector<QSize> sz;
- sz.reserve(d->colorAttachments.size());
- for (const auto &color : d->colorAttachments)
- sz.append(color.size);
- return sz;
-}
-
-/*!
- \fn int QOpenGLFramebufferObject::width() const
-
- Returns the width of the framebuffer object attachments.
-*/
-
-/*!
- \fn int QOpenGLFramebufferObject::height() const
-
- Returns the height of the framebuffer object attachments.
-*/
-
-/*!
- Returns the format of this framebuffer object.
-*/
-QOpenGLFramebufferObjectFormat QOpenGLFramebufferObject::format() const
-{
- Q_D(const QOpenGLFramebufferObject);
- return d->format;
-}
-
-static inline QImage qt_gl_read_framebuffer_rgba8(const QSize &size, bool include_alpha, QOpenGLContext *context)
-{
- QOpenGLFunctions *funcs = context->functions();
- const int w = size.width();
- const int h = size.height();
- bool isOpenGL12orBetter = !context->isOpenGLES() && (context->format().majorVersion() >= 2 || context->format().minorVersion() >= 2);
- if (isOpenGL12orBetter) {
- QImage img(size, include_alpha ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32);
- funcs->glReadPixels(0, 0, w, h, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, img.bits());
- return img;
- }
-
- // For OpenGL ES stick with the byte ordered format / RGBA readback format
- // since that is the only spec mandated way. (also, skip the
- // GL_IMPLEMENTATION_COLOR_READ_FORMAT mess since there is nothing saying a
- // BGRA capable impl would return BGRA from there)
-
- QImage rgbaImage(size, include_alpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGBX8888);
- funcs->glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, rgbaImage.bits());
- return rgbaImage;
-}
-
-static inline QImage qt_gl_read_framebuffer_rgb10a2(const QSize &size, bool include_alpha, QOpenGLContext *context)
-{
- // We assume OpenGL 1.2+ or ES 3.0+ here.
- QImage img(size, include_alpha ? QImage::Format_A2BGR30_Premultiplied : QImage::Format_BGR30);
- context->functions()->glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, img.bits());
- return img;
-}
-
-static inline QImage qt_gl_read_framebuffer_rgba16(const QSize &size, bool include_alpha, QOpenGLContext *context)
-{
- // We assume OpenGL 1.2+ or ES 3.0+ here.
- QImage img(size, include_alpha ? QImage::Format_RGBA64_Premultiplied : QImage::Format_RGBX64);
- context->functions()->glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_SHORT, img.bits());
- return img;
-}
-
-static QImage qt_gl_read_framebuffer(const QSize &size, GLenum internal_format, bool include_alpha, bool flip)
-{
- QOpenGLContext *ctx = QOpenGLContext::currentContext();
- QOpenGLFunctions *funcs = ctx->functions();
- while (true) {
- GLenum error = funcs->glGetError();
- if (error == GL_NO_ERROR || error == GL_CONTEXT_LOST)
- break;
- }
- switch (internal_format) {
- case GL_RGB:
- case GL_RGB8:
- return qt_gl_read_framebuffer_rgba8(size, false, ctx).mirrored(false, flip);
- case GL_RGB10:
- return qt_gl_read_framebuffer_rgb10a2(size, false, ctx).mirrored(false, flip);
- case GL_RGB10_A2:
- return qt_gl_read_framebuffer_rgb10a2(size, include_alpha, ctx).mirrored(false, flip);
- case GL_RGB16:
- return qt_gl_read_framebuffer_rgba16(size, false, ctx).mirrored(false, flip);
- case GL_RGBA16:
- return qt_gl_read_framebuffer_rgba16(size, include_alpha, ctx).mirrored(false, flip);
- case GL_RGBA:
- case GL_RGBA8:
- default:
- return qt_gl_read_framebuffer_rgba8(size, include_alpha, ctx).mirrored(false, flip);
- }
-
- Q_UNREACHABLE();
- return QImage();
-}
-
-Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha)
-{
- return qt_gl_read_framebuffer(size, alpha_format ? GL_RGBA : GL_RGB, include_alpha, true);
-}
-
-/*!
- \fn QImage QOpenGLFramebufferObject::toImage(bool flipped) const
-
- Returns the contents of this framebuffer object as a QImage.
-
- If \a flipped is true the image is flipped from OpenGL coordinates to raster coordinates.
- If used together with QOpenGLPaintDevice, \a flipped should be the opposite of the value
- of QOpenGLPaintDevice::paintFlipped().
-
- The returned image has a format of premultiplied ARGB32 or RGB32. The latter
- is used only when internalTextureFormat() is set to \c GL_RGB. Since Qt 5.2
- the function will fall back to premultiplied RGBA8888 or RGBx8888 when
- reading to (A)RGB32 is not supported, and this includes OpenGL ES. Since Qt
- 5.4 an A2BGR30 image is returned if the internal format is RGB10_A2, and since
- Qt 5.12 a RGBA64 image is return if the internal format is RGBA16.
-
- If the rendering in the framebuffer was not done with premultiplied alpha in mind,
- create a wrapper QImage with a non-premultiplied format. This is necessary before
- performing operations like QImage::save() because otherwise the image data would get
- unpremultiplied, even though it was not premultiplied in the first place. To create
- such a wrapper without performing a copy of the pixel data, do the following:
-
- \code
- QImage fboImage(fbo.toImage());
- QImage image(fboImage.constBits(), fboImage.width(), fboImage.height(), QImage::Format_ARGB32);
- \endcode
-
- For multisampled framebuffer objects the samples are resolved using the
- \c{GL_EXT_framebuffer_blit} extension. If the extension is not available, the contents
- of the returned image is undefined.
-
- For singlesampled framebuffers the contents is retrieved via \c glReadPixels. This is
- a potentially expensive and inefficient operation. Therefore it is recommended that
- this function is used as seldom as possible.
-
- \sa QOpenGLPaintDevice::paintFlipped()
-*/
-
-QImage QOpenGLFramebufferObject::toImage(bool flipped) const
-{
- return toImage(flipped, 0);
-}
-
-/*!
- \fn QImage QOpenGLFramebufferObject::toImage() const
- \overload
-
- Returns the contents of this framebuffer object as a QImage. This method flips
- the image from OpenGL coordinates to raster coordinates.
-*/
-// ### Qt 6: Remove this method and make it a default argument instead.
-QImage QOpenGLFramebufferObject::toImage() const
-{
- return toImage(true, 0);
-}
-
-/*! \overload
-
- Returns the contents of the color attachment of index \a
- colorAttachmentIndex of this framebuffer object as a QImage. This method
- flips the image from OpenGL coordinates to raster coordinates when \a
- flipped is set to \c true.
-
- \note This overload is only fully functional when multiple render targets are
- supported by the OpenGL implementation. When that is not the case, only one
- color attachment will be set up.
-
- \since 5.6
-*/
-QImage QOpenGLFramebufferObject::toImage(bool flipped, int colorAttachmentIndex) const
-{
- Q_D(const QOpenGLFramebufferObject);
- if (!d->valid)
- return QImage();
-
- QOpenGLContext *ctx = QOpenGLContext::currentContext();
- if (!ctx) {
- qWarning("QOpenGLFramebufferObject::toImage() called without a current context");
- return QImage();
- }
-
- if (d->colorAttachments.count() <= colorAttachmentIndex) {
- qWarning("QOpenGLFramebufferObject::toImage() called for missing color attachment");
- return QImage();
- }
-
- GLuint prevFbo = 0;
- ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &prevFbo);
-
- if (prevFbo != d->fbo())
- const_cast<QOpenGLFramebufferObject *>(this)->bind();
-
- QImage image;
- QOpenGLExtraFunctions *extraFuncs = ctx->extraFunctions();
- // qt_gl_read_framebuffer doesn't work on a multisample FBO
- if (format().samples() != 0) {
- QRect rect(QPoint(0, 0), size());
- QOpenGLFramebufferObjectFormat fmt;
- if (extraFuncs->hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets)) {
- fmt.setInternalTextureFormat(d->colorAttachments[colorAttachmentIndex].internalFormat);
- QOpenGLFramebufferObject temp(d->colorAttachments[colorAttachmentIndex].size, fmt);
- blitFramebuffer(&temp, rect, const_cast<QOpenGLFramebufferObject *>(this), rect,
- GL_COLOR_BUFFER_BIT, GL_NEAREST,
- colorAttachmentIndex, 0);
- image = temp.toImage(flipped);
- } else {
- fmt.setInternalTextureFormat(d->colorAttachments[0].internalFormat);
- QOpenGLFramebufferObject temp(size(), fmt);
- blitFramebuffer(&temp, rect, const_cast<QOpenGLFramebufferObject *>(this), rect);
- image = temp.toImage(flipped);
- }
- } else {
- if (extraFuncs->hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets)) {
- extraFuncs->glReadBuffer(GL_COLOR_ATTACHMENT0 + colorAttachmentIndex);
- image = qt_gl_read_framebuffer(d->colorAttachments[colorAttachmentIndex].size,
- d->colorAttachments[colorAttachmentIndex].internalFormat,
- true, flipped);
- extraFuncs->glReadBuffer(GL_COLOR_ATTACHMENT0);
- } else {
- image = qt_gl_read_framebuffer(d->colorAttachments[0].size,
- d->colorAttachments[0].internalFormat,
- true, flipped);
- }
- }
-
- if (prevFbo != d->fbo())
- ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, prevFbo);
-
- return image;
-}
-
-/*!
- \fn bool QOpenGLFramebufferObject::bindDefault()
-
- Switches rendering back to the default, windowing system provided
- framebuffer.
- Returns \c true upon success, false otherwise.
-
- \sa bind(), release()
-*/
-bool QOpenGLFramebufferObject::bindDefault()
-{
- QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
-
- if (ctx) {
- ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
- QOpenGLContextPrivate::get(ctx)->qgl_current_fbo_invalid = true;
- QOpenGLContextPrivate::get(ctx)->qgl_current_fbo = nullptr;
- }
-#ifdef QT_DEBUG
- else
- qWarning("QOpenGLFramebufferObject::bindDefault() called without current context.");
-#endif
-
- return ctx != nullptr;
-}
-
-/*!
- \fn bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()
-
- Returns \c true if the OpenGL \c{GL_EXT_framebuffer_object} extension
- is present on this system; otherwise returns \c false.
-*/
-bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()
-{
- return QOpenGLContext::currentContext()->functions()->hasOpenGLFeature(QOpenGLFunctions::Framebuffers);
-}
-
-/*!
- \fn GLuint QOpenGLFramebufferObject::handle() const
-
- Returns the OpenGL framebuffer object handle for this framebuffer
- object (returned by the \c{glGenFrameBuffersEXT()} function). This
- handle can be used to attach new images or buffers to the
- framebuffer. The user is responsible for cleaning up and
- destroying these objects.
-*/
-GLuint QOpenGLFramebufferObject::handle() const
-{
- Q_D(const QOpenGLFramebufferObject);
- return d->fbo();
-}
-
-/*!
- Returns the status of the depth and stencil buffers attached to
- this framebuffer object.
-*/
-
-QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObject::attachment() const
-{
- Q_D(const QOpenGLFramebufferObject);
- if (d->valid)
- return d->fbo_attachment;
- return NoAttachment;
-}
-
-/*!
- Sets the attachments of the framebuffer object to \a attachment.
-
- This can be used to free or reattach the depth and stencil buffer
- attachments as needed.
-
- \note This function alters the current framebuffer binding.
- */
-void QOpenGLFramebufferObject::setAttachment(QOpenGLFramebufferObject::Attachment attachment)
-{
- Q_D(QOpenGLFramebufferObject);
- if (attachment == d->fbo_attachment || !isValid())
- return;
- QOpenGLContext *current = QOpenGLContext::currentContext();
- if (!current)
- return;
-#ifdef QT_DEBUG
- if (current->shareGroup() != d->fbo_guard->group())
- qWarning("QOpenGLFramebufferObject::setAttachment() called from incompatible context");
-#endif
- d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo());
- QOpenGLContextPrivate::get(current)->qgl_current_fbo_invalid = true;
- d->initDepthStencilAttachments(current, attachment);
-}
-
-/*!
- Returns \c true if the framebuffer object is currently bound to the current context,
- otherwise false is returned.
-*/
-bool QOpenGLFramebufferObject::isBound() const
-{
- Q_D(const QOpenGLFramebufferObject);
- QOpenGLContext *ctx = QOpenGLContext::currentContext();
- if (!ctx)
- return false;
- GLint fbo = 0;
- ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
- return GLuint(fbo) == d->fbo();
-}
-
-/*!
- \fn bool QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()
-
- Returns \c true if the OpenGL \c{GL_EXT_framebuffer_blit} extension
- is present on this system; otherwise returns \c false.
-
- \sa blitFramebuffer()
-*/
-bool QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()
-{
- return QOpenGLExtensions(QOpenGLContext::currentContext()).hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
-}
-
-
-/*!
- \overload
-
- Convenience overload to blit between two framebuffer objects.
-*/
-void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target,
- QOpenGLFramebufferObject *source,
- GLbitfield buffers, GLenum filter)
-{
- if (!target && !source)
- return;
-
- QSize targetSize;
- QSize sourceSize;
-
- if (target)
- targetSize = target->size();
- if (source)
- sourceSize = source->size();
-
- if (targetSize.isEmpty())
- targetSize = sourceSize;
- else if (sourceSize.isEmpty())
- sourceSize = targetSize;
-
- blitFramebuffer(target, QRect(QPoint(0, 0), targetSize),
- source, QRect(QPoint(0, 0), sourceSize),
- buffers, filter);
-}
-
-/*! \overload
- *
- Convenience overload to blit between two framebuffer objects.
-*/
-void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect,
- QOpenGLFramebufferObject *source, const QRect &sourceRect,
- GLbitfield buffers,
- GLenum filter)
-{
- blitFramebuffer(target, targetRect, source, sourceRect, buffers, filter, 0, 0);
-}
-
-/*!
- \enum QOpenGLFramebufferObject::FramebufferRestorePolicy
- \since 5.7
-
- This enum type is used to configure the behavior related to restoring
- framebuffer bindings when calling blitFramebuffer().
-
- \value DontRestoreFramebufferBinding Do not restore the previous framebuffer binding.
- The caller is responsible for tracking and setting
- the framebuffer binding as needed.
-
- \value RestoreFramebufferBindingToDefault After the blit operation, bind the default
- framebuffer.
-
- \value RestoreFrameBufferBinding Restore the previously bound framebuffer. This is
- potentially expensive because of the need to
- query the currently bound framebuffer.
-
- \sa blitFramebuffer()
-*/
-
-/*!
- \since 5.7
-
- Blits from the \a sourceRect rectangle in the \a source framebuffer
- object to the \a targetRect rectangle in the \a target framebuffer object.
-
- If \a source or \a target is 0, the default framebuffer will be used
- instead of a framebuffer object as source or target respectively.
-
- This function will have no effect unless hasOpenGLFramebufferBlit() returns
- true.
-
- The \a buffers parameter should be a mask consisting of any combination of
- \c GL_COLOR_BUFFER_BIT, \c GL_DEPTH_BUFFER_BIT, and
- \c GL_STENCIL_BUFFER_BIT. Any buffer type that is not present both
- in the source and target buffers is ignored.
-
- The \a sourceRect and \a targetRect rectangles may have different sizes;
- in this case \a buffers should not contain \c GL_DEPTH_BUFFER_BIT or
- \c GL_STENCIL_BUFFER_BIT. The \a filter parameter should be set to
- \c GL_LINEAR or \c GL_NEAREST, and specifies whether linear or nearest
- interpolation should be used when scaling is performed.
-
- If \a source equals \a target a copy is performed within the same buffer.
- Results are undefined if the source and target rectangles overlap and
- have different sizes. The sizes must also be the same if any of the
- framebuffer objects are multisample framebuffers.
-
- \note The scissor test will restrict the blit area if enabled.
-
- When multiple render targets are in use, \a readColorAttachmentIndex and \a
- drawColorAttachmentIndex specify the index of the color attachments in the
- source and destination framebuffers.
-
- The \a restorePolicy determines if the framebuffer that was bound prior to
- calling this function should be restored, or if the default framebuffer
- should be bound before returning, of if the caller is responsible for
- tracking and setting the bound framebuffer. Restoring the previous
- framebuffer can be relatively expensive due to the call to \c{glGetIntegerv}
- which on some OpenGL drivers may imply a pipeline stall.
-
- \sa hasOpenGLFramebufferBlit()
-*/
-void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect,
- QOpenGLFramebufferObject *source, const QRect &sourceRect,
- GLbitfield buffers,
- GLenum filter,
- int readColorAttachmentIndex,
- int drawColorAttachmentIndex,
- QOpenGLFramebufferObject::FramebufferRestorePolicy restorePolicy)
-{
- QOpenGLContext *ctx = QOpenGLContext::currentContext();
- if (!ctx)
- return;
-
- QOpenGLExtensions extensions(ctx);
- if (!extensions.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit))
- return;
-
- GLuint prevFbo = 0;
- if (restorePolicy == RestoreFrameBufferBinding)
- ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &prevFbo);
-
- const int sx0 = sourceRect.left();
- const int sx1 = sourceRect.left() + sourceRect.width();
- const int sy0 = sourceRect.top();
- const int sy1 = sourceRect.top() + sourceRect.height();
-
- const int tx0 = targetRect.left();
- const int tx1 = targetRect.left() + targetRect.width();
- const int ty0 = targetRect.top();
- const int ty1 = targetRect.top() + targetRect.height();
-
- const GLuint defaultFboId = ctx->defaultFramebufferObject();
-
- extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, source ? source->handle() : defaultFboId);
- extensions.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target ? target->handle() : defaultFboId);
-
- const bool supportsMRT = extensions.hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets);
- if (supportsMRT) {
- extensions.glReadBuffer(GL_COLOR_ATTACHMENT0 + readColorAttachmentIndex);
- if (target) {
- GLenum drawBuf = GL_COLOR_ATTACHMENT0 + drawColorAttachmentIndex;
- extensions.glDrawBuffers(1, &drawBuf);
- }
- }
-
- extensions.glBlitFramebuffer(sx0, sy0, sx1, sy1,
- tx0, ty0, tx1, ty1,
- buffers, filter);
-
- if (supportsMRT)
- extensions.glReadBuffer(GL_COLOR_ATTACHMENT0);
-
- switch (restorePolicy) {
- case RestoreFrameBufferBinding:
- ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, prevFbo); // sets both READ and DRAW
- break;
-
- case RestoreFramebufferBindingToDefault:
- ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); // sets both READ and DRAW
- break;
-
- case DontRestoreFramebufferBinding:
- break;
- }
-}
-
-/*!
- \overload
-
- Convenience overload to blit between two framebuffer objects and
- to restore the previous framebuffer binding. Equivalent to calling
- blitFramebuffer(target, targetRect, source, sourceRect, buffers, filter,
- readColorAttachmentIndex, drawColorAttachmentIndex,
- RestoreFrameBufferBinding).
-*/
-void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect,
- QOpenGLFramebufferObject *source, const QRect &sourceRect,
- GLbitfield buffers,
- GLenum filter,
- int readColorAttachmentIndex,
- int drawColorAttachmentIndex)
-{
- blitFramebuffer(target, targetRect, source, sourceRect,
- buffers, filter,
- readColorAttachmentIndex,
- drawColorAttachmentIndex,
- RestoreFrameBufferBinding);
-}
-
-QT_END_NAMESPACE