summaryrefslogtreecommitdiffstats
path: root/src/openglwidgets
diff options
context:
space:
mode:
Diffstat (limited to 'src/openglwidgets')
-rw-r--r--src/openglwidgets/CMakeLists.txt7
-rw-r--r--src/openglwidgets/qopenglwidget.cpp721
-rw-r--r--src/openglwidgets/qopenglwidget.h52
-rw-r--r--src/openglwidgets/qtopenglwidgetsglobal.h55
4 files changed, 545 insertions, 290 deletions
diff --git a/src/openglwidgets/CMakeLists.txt b/src/openglwidgets/CMakeLists.txt
index 2a34171cca..af3efdf30f 100644
--- a/src/openglwidgets/CMakeLists.txt
+++ b/src/openglwidgets/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from openglwidgets.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## OpenGLWidgets Module:
@@ -7,13 +8,17 @@
qt_internal_add_module(OpenGLWidgets
SOURCES
qopenglwidget.cpp qopenglwidget.h
+ qtopenglwidgetsglobal.h
DEFINES
+ QT_NO_CONTEXTLESS_CONNECT
QT_NO_FOREACH
QT_NO_USING_NAMESPACE
+ QT_USE_NODISCARD_FILE_OPEN
LIBRARIES
Qt::OpenGLPrivate
Qt::WidgetsPrivate
PUBLIC_LIBRARIES
Qt::OpenGL
Qt::Widgets
+ GENERATE_CPP_EXPORTS
)
diff --git a/src/openglwidgets/qopenglwidget.cpp b/src/openglwidgets/qopenglwidget.cpp
index 2ae1a7f215..4a0bf7f492 100644
--- a/src/openglwidgets/qopenglwidget.cpp
+++ b/src/openglwidgets/qopenglwidget.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtOpenGLWidgets 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$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qopenglwidget.h"
#include <QtGui/QOpenGLContext>
@@ -55,9 +19,11 @@
#include <QtGui/private/qopenglcontext_p.h>
#include <QtOpenGL/private/qopenglframebufferobject_p.h>
#include <QtOpenGL/private/qopenglpaintdevice_p.h>
-#include <QtOpenGL/qpa/qplatformbackingstoreopenglsupport.h>
#include <QtWidgets/private/qwidget_p.h>
+#include <QtWidgets/private/qwidgetrepaintmanager_p.h>
+
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
@@ -90,7 +56,7 @@ QT_BEGIN_NAMESPACE
\endlist
If you need to trigger a repaint from places other than paintGL() (a
- typical example is when using \l{QTimer}{timers} to animate scenes),
+ typical example is when using \l{QChronoTimer}{timers} to animate scenes),
you should call the widget's update() function to schedule an update.
Your widget's OpenGL rendering context is made current when
@@ -178,7 +144,7 @@ QT_BEGIN_NAMESPACE
loading which means applications are not directly linking to an GL
implementation and thus direct function calls are not feasible.
- In paintGL() the current context is always accessible by caling
+ In paintGL() the current context is always accessible by calling
QOpenGLContext::currentContext(). From this context an already initialized,
ready-to-be-used QOpenGLFunctions instance is retrievable by calling
QOpenGLContext::functions(). An alternative to prefixing every GL call is to
@@ -197,7 +163,7 @@ QT_BEGIN_NAMESPACE
\section1 Code Examples
- To get started, the simplest QOpenGLWidget subclass could like like the following:
+ To get started, the simplest QOpenGLWidget subclass could look like the following:
\snippet code/doc_gui_widgets_qopenglwidget.cpp 0
@@ -211,6 +177,13 @@ QT_BEGIN_NAMESPACE
\snippet code/doc_gui_widgets_qopenglwidget.cpp 2
+ \note It is up to the application to ensure depth and stencil buffers are
+ requested from the underlying windowing system interface. Without requesting
+ a non-zero depth buffer size there is no guarantee that a depth buffer will
+ be available, and as a result depth testing related OpenGL operations may
+ fail to function as expected. Commonly used depth and stencil buffer size
+ requests are 24 and 8, respectively.
+
With OpenGL 3.0+ contexts, when portability is not important, the versioned
QOpenGLFunctions variants give easy access to all the modern OpenGL functions
available in a given version:
@@ -324,31 +297,34 @@ QT_BEGIN_NAMESPACE
\snippet code/doc_gui_widgets_qopenglwidget.cpp 4
- This is naturally not the only possible solution. One alternative is to use
- the \l{QOpenGLContext::aboutToBeDestroyed()}{aboutToBeDestroyed()} signal of
- QOpenGLContext. By connecting a slot, using direct connection, to this signal,
- it is possible to perform cleanup whenever the underlying native context
- handle, or the entire QOpenGLContext instance, is going to be released. The
- following snippet is in principle equivalent to the previous one:
+ This works for most cases, but not fully ideal as a generic solution. When
+ the widget is reparented so that it ends up in an entirely different
+ top-level window, something more is needed: by connecting to the
+ \l{QOpenGLContext::aboutToBeDestroyed()}{aboutToBeDestroyed()} signal of
+ QOpenGLContext, cleanup can be performed whenever the OpenGL context is about
+ to be released.
+
+ \note For widgets that change their associated top-level window multiple
+ times during their lifetime, a combined cleanup approach, as demonstrated in
+ the code snippet below, is essential. Whenever the widget or a parent of it
+ gets reparented so that the top-level window becomes different, the widget's
+ associated context is destroyed and a new one is created. This is then
+ followed by a call to initializeGL() where all OpenGL resources must get
+ reinitialized. Due to this the only option to perform proper cleanup is to
+ connect to the context's aboutToBeDestroyed() signal. Note that the context
+ in question may not be the current one when the signal gets emitted.
+ Therefore it is good practice to call makeCurrent() in the connected slot.
+ Additionally, the same cleanup steps must be performed from the derived
+ class' destructor, since the slot or lambda connected to the signal may not
+ invoked when the widget is being destroyed.
\snippet code/doc_gui_widgets_qopenglwidget.cpp 5
- \note For widgets that change their associated top-level window multiple times
- during their lifetime, a combined approach is essential. Whenever the widget
- or a parent of it gets reparented so that the top-level window becomes
- different, the widget's associated context is destroyed and a new one is
- created. This is then followed by a call to initializeGL() where all OpenGL
- resources must get reinitialized. Due to this the only option to perform
- proper cleanup is to connect to the context's aboutToBeDestroyed()
- signal. Note that the context in question may not be the current one when the
- signal gets emitted. Therefore it is good practice to call makeCurrent() in
- the connected slot. Additionally, the same cleanup steps must be performed
- from the derived class' destructor, since the slot connected to the signal
- will not get invoked when the widget is being destroyed.
-
\note When Qt::AA_ShareOpenGLContexts is set, the widget's context never
changes, not even when reparenting because the widget's associated texture is
- guaranteed to be accessible also from the new top-level's context.
+ going to be accessible also from the new top-level's context. Therefore,
+ acting on the aboutToBeDestroyed() signal of the context is not mandatory
+ with this flag set.
Proper cleanup is especially important due to context sharing. Even though
each QOpenGLWidget's associated context is destroyed together with the
@@ -361,7 +337,7 @@ QT_BEGIN_NAMESPACE
explicit cleanup for all resources and resource wrappers used in the
QOpenGLWidget.
- \section1 Limitations
+ \section1 Limitations and Other Considerations
Putting other widgets underneath and making the QOpenGLWidget transparent will
not lead to the expected results: The widgets underneath will not be
@@ -401,6 +377,20 @@ QT_BEGIN_NAMESPACE
each frame. To restore the preserved behavior, call setUpdateBehavior() with
\c PartialUpdate.
+ \note When dynamically adding a QOpenGLWidget into a widget hierarchy, e.g.
+ by parenting a new QOpenGLWidget to a widget where the corresponding
+ top-level widget is already shown on screen, the associated native window may
+ get implicitly destroyed and recreated if the QOpenGLWidget is the first of
+ its kind within its window. This is because the window type changes from
+ \l{QSurface::RasterSurface}{RasterSurface} to
+ \l{QSurface::OpenGLSurface}{OpenGLSurface} and that has platform-specific
+ implications. This behavior is new in Qt 6.4.
+
+ Once a QOpenGLWidget is added to a widget hierarchy, the contents of the
+ top-level window is flushed via OpenGL-based rendering. Widgets other than
+ the QOpenGLWidget continue to draw their content using a software-based
+ painter, but the final composition is done through the 3D API.
+
\note Displaying a QOpenGLWidget requires an alpha channel in the associated
top-level window's backing store due to the way composition with other
QWidget-based content works. If there is no alpha channel, the content
@@ -431,6 +421,26 @@ QT_BEGIN_NAMESPACE
certain desktop platforms (e.g. \macos) too. The stable,
cross-platform solution is always QOpenGLWidget.
+
+ \section1 Stereoscopic rendering
+
+ Starting from 6.5 QOpenGLWidget has support for stereoscopic rendering.
+ To enable it, set the QSurfaceFormat::StereoBuffers flag
+ globally before the window is created, using QSurfaceFormat::SetDefaultFormat().
+
+ \note Using setFormat() will not necessarily work because of how the flag is
+ handled internally.
+
+ This will trigger paintGL() to be called twice each frame,
+ once for each QOpenGLWidget::TargetBuffer. In paintGL(), call
+ currentTargetBuffer() to query which one is currently being drawn to.
+
+ \note For more control over the left and right color buffers, consider using
+ QOpenGLWindow + QWidget::createWindowContainer() instead.
+
+ \note This type of 3D rendering has certain hardware requirements,
+ like the graphics card needs to be setup with stereo support.
+
\e{OpenGL is a trademark of Silicon Graphics, Inc. in the United States and other
countries.}
@@ -467,6 +477,20 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \enum QOpenGLWidget::TargetBuffer
+ \since 6.5
+
+ Specifies the buffer to use when stereoscopic rendering is enabled, which is
+ toggled by setting \l QSurfaceFormat::StereoBuffers.
+
+ \note LeftBuffer is always the default and used as fallback value when
+ stereoscopic rendering is disabled or not supported by the graphics driver.
+
+ \value LeftBuffer
+ \value RightBuffer
+ */
+
+/*!
\enum QOpenGLWidget::UpdateBehavior
\since 5.5
@@ -480,9 +504,8 @@ QT_BEGIN_NAMESPACE
benefits on certain hardware architectures common in the mobile and
embedded space when a framebuffer object is used as the rendering target.
The framebuffer object is invalidated between frames with
- glDiscardFramebufferEXT if supported or a glClear. Please see the
- documentation of EXT_discard_framebuffer for more information:
- https://www.khronos.org/registry/gles/extensions/EXT/EXT_discard_framebuffer.txt
+ glInvalidateFramebuffer (if supported), or, as fallbacks,
+ glDiscardFramebufferEXT (if supported) or a call to glClear.
\value PartialUpdate The framebuffer objects color buffer and ancillary
buffers are not invalidated between frames.
@@ -518,29 +541,47 @@ public:
QOpenGLWidgetPrivate() = default;
void reset();
- void recreateFbo();
+ void resetRhiDependentResources();
+ void recreateFbos();
+ void ensureRhiDependentResources();
- GLuint textureId() const override;
+ QWidgetPrivate::TextureData texture() const override;
QPlatformTextureList::Flags textureListFlags() override;
+ QPlatformBackingStoreRhiConfig rhiConfig() const override { return { QPlatformBackingStoreRhiConfig::OpenGL }; }
+
void initialize();
- void invokeUserPaint();
void render();
- void invalidateFbo();
+ static constexpr GLenum gl_color_attachment0 = 0x8CE0; // GL_COLOR_ATTACHMENT0
+ static constexpr GLenum gl_depth_attachment = 0x8D00; // GL_DEPTH_ATTACHMENT
+ static constexpr GLenum gl_stencil_attachment = 0x8D20; // GL_STENCIL_ATTACHMENT
+ static constexpr GLenum gl_depth_stencil_attachment = 0x821A; // GL_DEPTH_STENCIL_ATTACHMENT
+
+ void invalidateFboBeforePainting();
+ void invalidateFboAfterPainting();
+
+ void destroyFbos();
+ bool setCurrentTargetBuffer(QOpenGLWidget::TargetBuffer targetBuffer);
+ QImage grabFramebuffer(QOpenGLWidget::TargetBuffer targetBuffer);
QImage grabFramebuffer() override;
void beginBackingStorePainting() override { inBackingStorePaint = true; }
void endBackingStorePainting() override { inBackingStorePaint = false; }
void beginCompose() override;
void endCompose() override;
void initializeViewportFramebuffer() override;
+ bool isStereoEnabled() override;
+ bool toggleStereoTargetBuffer() override;
void resizeViewportFramebuffer() override;
void resolveSamples() override;
+ void resolveSamplesForBuffer(QOpenGLWidget::TargetBuffer targetBuffer);
+
QOpenGLContext *context = nullptr;
- QOpenGLFramebufferObject *fbo = nullptr;
- QOpenGLFramebufferObject *resolvedFbo = nullptr;
+ QRhiTexture *wrapperTextures[2] = {};
+ QOpenGLFramebufferObject *fbos[2] = {};
+ QOpenGLFramebufferObject *resolvedFbos[2] = {};
QOffscreenSurface *surface = nullptr;
QOpenGLPaintDevice *paintDevice = nullptr;
int requestedSamples = 0;
@@ -553,6 +594,7 @@ public:
bool hasBeenComposed = false;
bool flushPending = false;
bool inPaintGL = false;
+ QOpenGLWidget::TargetBuffer currentTargetBuffer = QOpenGLWidget::LeftBuffer;
};
void QOpenGLWidgetPaintDevicePrivate::beginPaint()
@@ -594,10 +636,11 @@ void QOpenGLWidgetPaintDevice::ensureActiveTarget()
if (QOpenGLContext::currentContext() != wd->context)
d->w->makeCurrent();
else
- wd->fbo->bind();
+ wd->fbos[wd->currentTargetBuffer]->bind();
+
if (!wd->inPaintGL)
- QOpenGLContextPrivate::get(wd->context)->defaultFboRedirect = wd->fbo->handle();
+ QOpenGLContextPrivate::get(wd->context)->defaultFboRedirect = wd->fbos[wd->currentTargetBuffer]->handle();
// When used as a viewport, drawing is done via opening a QPainter on the widget
// without going through paintEvent(). We will have to make sure a glFlush() is done
@@ -605,9 +648,9 @@ void QOpenGLWidgetPaintDevice::ensureActiveTarget()
wd->flushPending = true;
}
-GLuint QOpenGLWidgetPrivate::textureId() const
+QWidgetPrivate::TextureData QOpenGLWidgetPrivate::texture() const
{
- return resolvedFbo ? resolvedFbo->texture() : (fbo ? fbo->texture() : 0);
+ return { wrapperTextures[QOpenGLWidget::LeftBuffer], wrapperTextures[QOpenGLWidget::RightBuffer] };
}
#ifndef GL_SRGB
@@ -649,10 +692,8 @@ void QOpenGLWidgetPrivate::reset()
delete paintDevice;
paintDevice = nullptr;
- delete fbo;
- fbo = nullptr;
- delete resolvedFbo;
- resolvedFbo = nullptr;
+
+ destroyFbos();
if (initialized)
q->doneCurrent();
@@ -667,7 +708,22 @@ void QOpenGLWidgetPrivate::reset()
initialized = fakeHidden = inBackingStorePaint = false;
}
-void QOpenGLWidgetPrivate::recreateFbo()
+void QOpenGLWidgetPrivate::resetRhiDependentResources()
+{
+ // QRhi resource created from the QRhi. These must be released whenever the
+ // widget gets associated with a different QRhi, even when all OpenGL
+ // contexts share resources.
+
+ delete wrapperTextures[0];
+ wrapperTextures[0] = nullptr;
+
+ if (isStereoEnabled()) {
+ delete wrapperTextures[1];
+ wrapperTextures[1] = nullptr;
+ }
+}
+
+void QOpenGLWidgetPrivate::recreateFbos()
{
Q_Q(QOpenGLWidget);
@@ -675,10 +731,7 @@ void QOpenGLWidgetPrivate::recreateFbo()
context->makeCurrent(surface);
- delete fbo;
- fbo = nullptr;
- delete resolvedFbo;
- resolvedFbo = nullptr;
+ destroyFbos();
int samples = requestedSamples;
QOpenGLExtensions *extfuncs = static_cast<QOpenGLExtensions *>(context->functions());
@@ -692,14 +745,33 @@ void QOpenGLWidgetPrivate::recreateFbo()
format.setInternalTextureFormat(textureFormat);
const QSize deviceSize = q->size() * q->devicePixelRatio();
- fbo = new QOpenGLFramebufferObject(deviceSize, format);
+ fbos[QOpenGLWidget::LeftBuffer] = new QOpenGLFramebufferObject(deviceSize, format);
if (samples > 0)
- resolvedFbo = new QOpenGLFramebufferObject(deviceSize);
+ resolvedFbos[QOpenGLWidget::LeftBuffer] = new QOpenGLFramebufferObject(deviceSize);
- textureFormat = fbo->format().internalTextureFormat();
+ const bool stereo = isStereoEnabled();
+
+ if (stereo) {
+ fbos[QOpenGLWidget::RightBuffer] = new QOpenGLFramebufferObject(deviceSize, format);
+ if (samples > 0)
+ resolvedFbos[QOpenGLWidget::RightBuffer] = new QOpenGLFramebufferObject(deviceSize);
+ }
- fbo->bind();
+ textureFormat = fbos[QOpenGLWidget::LeftBuffer]->format().internalTextureFormat();
+
+ currentTargetBuffer = QOpenGLWidget::LeftBuffer;
+ fbos[currentTargetBuffer]->bind();
context->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ ensureRhiDependentResources();
+
+ if (stereo) {
+ currentTargetBuffer = QOpenGLWidget::RightBuffer;
+ fbos[currentTargetBuffer]->bind();
+ context->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ ensureRhiDependentResources();
+ currentTargetBuffer = QOpenGLWidget::LeftBuffer;
+ }
+
flushPending = true; // Make sure the FBO is initialized before use
paintDevice->setSize(deviceSize);
@@ -708,6 +780,29 @@ void QOpenGLWidgetPrivate::recreateFbo()
emit q->resized();
}
+void QOpenGLWidgetPrivate::ensureRhiDependentResources()
+{
+ Q_Q(QOpenGLWidget);
+
+ QRhi *rhi = QWidgetPrivate::rhi();
+
+ // If there is no rhi, because we are completely offscreen, then there's no wrapperTexture either
+ if (rhi && rhi->backend() == QRhi::OpenGLES2) {
+ const QSize deviceSize = q->size() * q->devicePixelRatio();
+ if (!wrapperTextures[currentTargetBuffer] || wrapperTextures[currentTargetBuffer]->pixelSize() != deviceSize) {
+ const uint textureId = resolvedFbos[currentTargetBuffer] ?
+ resolvedFbos[currentTargetBuffer]->texture()
+ : (fbos[currentTargetBuffer] ? fbos[currentTargetBuffer]->texture() : 0);
+ if (!wrapperTextures[currentTargetBuffer])
+ wrapperTextures[currentTargetBuffer] = rhi->newTexture(QRhiTexture::RGBA8, deviceSize, 1, QRhiTexture::RenderTarget);
+ else
+ wrapperTextures[currentTargetBuffer]->setPixelSize(deviceSize);
+ if (!wrapperTextures[currentTargetBuffer]->createFrom({textureId, 0 }))
+ qWarning("QOpenGLWidget: Failed to create wrapper texture");
+ }
+ }
+}
+
void QOpenGLWidgetPrivate::beginCompose()
{
Q_Q(QOpenGLWidget);
@@ -735,11 +830,6 @@ void QOpenGLWidgetPrivate::initialize()
// If no global shared context get our toplevel's context with which we
// will share in order to make the texture usable by the underlying window's backingstore.
QWidget *tlw = q->window();
- QOpenGLContext *shareContext = qt_gl_global_share_context();
- if (!shareContext)
- shareContext = get(tlw)->shareContext();
- // If shareContext is null, showing content on-screen will not work.
- // However, offscreen rendering and grabFramebuffer() will stay fully functional.
// Do not include the sample count. Requesting a multisampled context is not necessary
// since we render into an FBO, never to an actual surface. What's more, attempting to
@@ -748,17 +838,44 @@ void QOpenGLWidgetPrivate::initialize()
requestedSamples = requestedFormat.samples();
requestedFormat.setSamples(0);
- QScopedPointer<QOpenGLContext> ctx(new QOpenGLContext);
- ctx->setFormat(requestedFormat);
+ QRhi *rhi = QWidgetPrivate::rhi();
+
+ // Could be that something else already initialized the window with some
+ // other graphics API for the QRhi, that's not good.
+ if (rhi && rhi->backend() != QRhi::OpenGLES2) {
+ qWarning("The top-level window is not using OpenGL for composition, '%s' is not compatible with QOpenGLWidget",
+ rhi->backendName());
+ return;
+ }
+
+ // If rhi or contextFromRhi is null, showing content on-screen will not work.
+ // However, offscreen rendering and grabFramebuffer() will stay fully functional.
+
+ QOpenGLContext *contextFromRhi = rhi ? static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles())->context : nullptr;
+
+ context = new QOpenGLContext;
+ context->setFormat(requestedFormat);
+
+ QOpenGLContext *shareContext = contextFromRhi ? contextFromRhi : qt_gl_global_share_context();
if (shareContext) {
- ctx->setShareContext(shareContext);
- ctx->setScreen(shareContext->screen());
+ context->setShareContext(shareContext);
+ context->setScreen(shareContext->screen());
}
- if (Q_UNLIKELY(!ctx->create())) {
+ if (Q_UNLIKELY(!context->create())) {
qWarning("QOpenGLWidget: Failed to create context");
return;
}
+ surface = new QOffscreenSurface;
+ surface->setFormat(context->format());
+ surface->setScreen(context->screen());
+ surface->create();
+
+ if (Q_UNLIKELY(!context->makeCurrent(surface))) {
+ qWarning("QOpenGLWidget: Failed to make context current");
+ return;
+ }
+
// Propagate settings that make sense only for the tlw. Note that this only
// makes sense for properties that get picked up even after the native
// window is created.
@@ -776,24 +893,10 @@ void QOpenGLWidgetPrivate::initialize()
}
}
- // The top-level window's surface is not good enough since it causes way too
- // much trouble with regards to the QSurfaceFormat for example. So just like
- // in QQuickWidget, use a dedicated QOffscreenSurface.
- surface = new QOffscreenSurface;
- surface->setFormat(ctx->format());
- surface->setScreen(ctx->screen());
- surface->create();
-
- if (Q_UNLIKELY(!ctx->makeCurrent(surface))) {
- qWarning("QOpenGLWidget: Failed to make context current");
- return;
- }
-
paintDevice = new QOpenGLWidgetPaintDevice(q);
paintDevice->setSize(q->size() * q->devicePixelRatio());
paintDevice->setDevicePixelRatio(q->devicePixelRatio());
- context = ctx.take();
initialized = true;
q->initializeGL();
@@ -801,96 +904,173 @@ void QOpenGLWidgetPrivate::initialize()
void QOpenGLWidgetPrivate::resolveSamples()
{
+ resolveSamplesForBuffer(QOpenGLWidget::LeftBuffer);
+ resolveSamplesForBuffer(QOpenGLWidget::RightBuffer);
+}
+
+void QOpenGLWidgetPrivate::resolveSamplesForBuffer(QOpenGLWidget::TargetBuffer targetBuffer)
+{
Q_Q(QOpenGLWidget);
- if (resolvedFbo) {
- q->makeCurrent();
- QRect rect(QPoint(0, 0), fbo->size());
- QOpenGLFramebufferObject::blitFramebuffer(resolvedFbo, rect, fbo, rect);
+ if (resolvedFbos[targetBuffer]) {
+ q->makeCurrent(targetBuffer);
+ QRect rect(QPoint(0, 0), fbos[targetBuffer]->size());
+ QOpenGLFramebufferObject::blitFramebuffer(resolvedFbos[targetBuffer], rect, fbos[targetBuffer], rect);
flushPending = true;
}
}
-void QOpenGLWidgetPrivate::invokeUserPaint()
+void QOpenGLWidgetPrivate::render()
{
Q_Q(QOpenGLWidget);
+ if (fakeHidden || !initialized)
+ return;
+
+ setCurrentTargetBuffer(QOpenGLWidget::LeftBuffer);
+
QOpenGLContext *ctx = QOpenGLContext::currentContext();
- Q_ASSERT(ctx && fbo);
+ if (!ctx) {
+ qWarning("QOpenGLWidget: No current context, cannot render");
+ return;
+ }
- QOpenGLFunctions *f = ctx->functions();
- QOpenGLContextPrivate::get(ctx)->defaultFboRedirect = fbo->handle();
+ if (!fbos[QOpenGLWidget::LeftBuffer]) {
+ qWarning("QOpenGLWidget: No fbo, cannot render");
+ return;
+ }
+
+ const bool stereo = isStereoEnabled();
+ if (stereo) {
+ static bool warningGiven = false;
+ if (!fbos[QOpenGLWidget::RightBuffer] && !warningGiven) {
+ qWarning("QOpenGLWidget: Stereo is enabled, but no right buffer. Using only left buffer");
+ warningGiven = true;
+ }
+ }
+
+ if (updateBehavior == QOpenGLWidget::NoPartialUpdate && hasBeenComposed) {
+ invalidateFboBeforePainting();
+ if (stereo && fbos[QOpenGLWidget::RightBuffer]) {
+ setCurrentTargetBuffer(QOpenGLWidget::RightBuffer);
+ invalidateFboBeforePainting();
+ setCurrentTargetBuffer(QOpenGLWidget::LeftBuffer);
+ }
+
+ hasBeenComposed = false;
+ }
+
+ QOpenGLFunctions *f = ctx->functions();
f->glViewport(0, 0, q->width() * q->devicePixelRatio(), q->height() * q->devicePixelRatio());
inPaintGL = true;
- q->paintGL();
- inPaintGL = false;
- flushPending = true;
- QOpenGLContextPrivate::get(ctx)->defaultFboRedirect = 0;
-}
-
-void QOpenGLWidgetPrivate::render()
-{
- Q_Q(QOpenGLWidget);
+#ifdef Q_OS_WASM
+ f->glDepthMask(GL_TRUE);
+#endif
- if (fakeHidden || !initialized)
- return;
+ QOpenGLContextPrivate::get(ctx)->defaultFboRedirect = fbos[currentTargetBuffer]->handle();
- q->makeCurrent();
+ f->glUseProgram(0);
+ f->glBindBuffer(GL_ARRAY_BUFFER, 0);
+ f->glEnable(GL_BLEND);
- if (updateBehavior == QOpenGLWidget::NoPartialUpdate && hasBeenComposed) {
- invalidateFbo();
- hasBeenComposed = false;
+ q->paintGL();
+ if (updateBehavior == QOpenGLWidget::NoPartialUpdate)
+ invalidateFboAfterPainting();
+
+ if (stereo && fbos[QOpenGLWidget::RightBuffer]) {
+ setCurrentTargetBuffer(QOpenGLWidget::RightBuffer);
+ QOpenGLContextPrivate::get(ctx)->defaultFboRedirect = fbos[currentTargetBuffer]->handle();
+ q->paintGL();
+ if (updateBehavior == QOpenGLWidget::NoPartialUpdate)
+ invalidateFboAfterPainting();
}
+ QOpenGLContextPrivate::get(ctx)->defaultFboRedirect = 0;
- invokeUserPaint();
+ inPaintGL = false;
+ flushPending = true;
}
-void QOpenGLWidgetPrivate::invalidateFbo()
+void QOpenGLWidgetPrivate::invalidateFboBeforePainting()
{
QOpenGLExtensions *f = static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions());
if (f->hasOpenGLExtension(QOpenGLExtensions::DiscardFramebuffer)) {
- const int gl_color_attachment0 = 0x8CE0; // GL_COLOR_ATTACHMENT0
- const int gl_depth_attachment = 0x8D00; // GL_DEPTH_ATTACHMENT
- const int gl_stencil_attachment = 0x8D20; // GL_STENCIL_ATTACHMENT
-#ifdef Q_OS_WASM
- // webgl does not allow separate depth and stencil attachments
- // QTBUG-69913
- const int gl_depth_stencil_attachment = 0x821A; // GL_DEPTH_STENCIL_ATTACHMENT
-
- const GLenum attachments[] = {
- gl_color_attachment0, gl_depth_attachment, gl_stencil_attachment, gl_depth_stencil_attachment
- };
-#else
const GLenum attachments[] = {
- gl_color_attachment0, gl_depth_attachment, gl_stencil_attachment
- };
+ gl_color_attachment0,
+ gl_depth_attachment,
+ gl_stencil_attachment,
+#ifdef Q_OS_WASM
+ // webgl does not allow separate depth and stencil attachments
+ // QTBUG-69913
+ gl_depth_stencil_attachment
#endif
- f->glDiscardFramebufferEXT(GL_FRAMEBUFFER, sizeof attachments / sizeof *attachments, attachments);
+ };
+ f->discardFramebuffer(GL_FRAMEBUFFER, GLsizei(std::size(attachments)), attachments);
} else {
f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
}
+void QOpenGLWidgetPrivate::invalidateFboAfterPainting()
+{
+ QOpenGLExtensions *f = static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions());
+ if (f->hasOpenGLExtension(QOpenGLExtensions::DiscardFramebuffer)) {
+ const GLenum attachments[] = {
+ gl_depth_attachment,
+ gl_stencil_attachment,
+#ifdef Q_OS_WASM
+ // webgl does not allow separate depth and stencil attachments
+ // QTBUG-69913
+ gl_depth_stencil_attachment
+#endif
+ };
+ f->discardFramebuffer(GL_FRAMEBUFFER, GLsizei(std::size(attachments)), attachments);
+ }
+}
+
+void QOpenGLWidgetPrivate::destroyFbos()
+{
+ delete fbos[QOpenGLWidget::LeftBuffer];
+ fbos[QOpenGLWidget::LeftBuffer] = nullptr;
+ delete resolvedFbos[QOpenGLWidget::LeftBuffer];
+ resolvedFbos[QOpenGLWidget::LeftBuffer] = nullptr;
+
+ delete fbos[QOpenGLWidget::RightBuffer];
+ fbos[QOpenGLWidget::RightBuffer] = nullptr;
+ delete resolvedFbos[QOpenGLWidget::RightBuffer];
+ resolvedFbos[QOpenGLWidget::RightBuffer] = nullptr;
+
+ resetRhiDependentResources();
+}
+
QImage QOpenGLWidgetPrivate::grabFramebuffer()
{
+ return grabFramebuffer(QOpenGLWidget::LeftBuffer);
+}
+
+QImage QOpenGLWidgetPrivate::grabFramebuffer(QOpenGLWidget::TargetBuffer targetBuffer)
+{
Q_Q(QOpenGLWidget);
initialize();
if (!initialized)
return QImage();
- if (!fbo) // could be completely offscreen, without ever getting a resize event
- recreateFbo();
+ // The second fbo is only created when stereoscopic rendering is enabled
+ // Just use the default one if not.
+ if (targetBuffer == QOpenGLWidget::RightBuffer && !isStereoEnabled())
+ targetBuffer = QOpenGLWidget::LeftBuffer;
+
+ if (!fbos[targetBuffer]) // could be completely offscreen, without ever getting a resize event
+ recreateFbos();
if (!inPaintGL)
render();
- if (resolvedFbo) {
- resolveSamples();
- resolvedFbo->bind();
- } else {
- q->makeCurrent();
+ setCurrentTargetBuffer(targetBuffer);
+ if (resolvedFbos[targetBuffer]) {
+ resolveSamplesForBuffer(targetBuffer);
+ resolvedFbos[targetBuffer]->bind();
}
const bool hasAlpha = q->format().hasAlpha();
@@ -900,8 +1080,9 @@ QImage QOpenGLWidgetPrivate::grabFramebuffer()
// While we give no guarantees of what is going to be left bound, prefer the
// multisample fbo instead of the resolved one. Clients may continue to
// render straight after calling this function.
- if (resolvedFbo)
- q->makeCurrent();
+ if (resolvedFbos[targetBuffer]) {
+ setCurrentTargetBuffer(targetBuffer);
+ }
return res;
}
@@ -914,14 +1095,30 @@ void QOpenGLWidgetPrivate::initializeViewportFramebuffer()
q->setAutoFillBackground(true);
}
+bool QOpenGLWidgetPrivate::isStereoEnabled()
+{
+ Q_Q(QOpenGLWidget);
+ // Note that because this internally might use the requested format,
+ // then this can return a false positive on hardware where
+ // steroscopic rendering is not supported.
+ return q->format().stereo();
+}
+
+bool QOpenGLWidgetPrivate::toggleStereoTargetBuffer()
+{
+ return setCurrentTargetBuffer(currentTargetBuffer == QOpenGLWidget::LeftBuffer ?
+ QOpenGLWidget::RightBuffer :
+ QOpenGLWidget::LeftBuffer);
+}
+
void QOpenGLWidgetPrivate::resizeViewportFramebuffer()
{
Q_Q(QOpenGLWidget);
if (!initialized)
return;
- if (!fbo || q->size() * q->devicePixelRatio() != fbo->size()) {
- recreateFbo();
+ if (!fbos[currentTargetBuffer] || q->size() * q->devicePixelRatio() != fbos[currentTargetBuffer]->size()) {
+ recreateFbos();
q->update();
}
}
@@ -933,7 +1130,8 @@ QOpenGLWidget::QOpenGLWidget(QWidget *parent, Qt::WindowFlags f)
: QWidget(*(new QOpenGLWidgetPrivate), parent, f)
{
Q_D(QOpenGLWidget);
- if (Q_UNLIKELY(!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface)))
+ if (Q_UNLIKELY(!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RhiBasedRendering)
+ || !QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)))
qWarning("QOpenGLWidget is not supported on this platform.");
else
d->setRenderToTexture();
@@ -958,6 +1156,12 @@ QOpenGLWidget::QOpenGLWidget(QWidget *parent, Qt::WindowFlags f)
*/
QOpenGLWidget::~QOpenGLWidget()
{
+ // NB! resetting graphics resources must be done from this destructor,
+ // *not* from the private class' destructor. This is due to how destruction
+ // works and due to the QWidget dtor (for toplevels) destroying the repaint
+ // manager and rhi before the (QObject) private gets destroyed. Hence must
+ // do it here early on.
+
Q_D(QOpenGLWidget);
d->reset();
}
@@ -1102,8 +1306,36 @@ void QOpenGLWidget::makeCurrent()
d->context->makeCurrent(d->surface);
- if (d->fbo) // there may not be one if we are in reset()
- d->fbo->bind();
+ if (d->fbos[d->currentTargetBuffer]) // there may not be one if we are in reset()
+ d->fbos[d->currentTargetBuffer]->bind();
+}
+
+/*!
+ Prepares for rendering OpenGL content for this widget by making the
+ context for the passed in buffer current and binding the framebuffer object in that
+ context.
+
+ \note This only makes sense to call when stereoscopic rendering is enabled.
+ Nothing will happen if the right buffer is requested when it's disabled.
+
+ It is not necessary to call this function in most cases, because it
+ is called automatically before invoking paintGL().
+
+ \since 6.5
+
+ \sa context(), paintGL(), doneCurrent()
+ */
+void QOpenGLWidget::makeCurrent(TargetBuffer targetBuffer)
+{
+ Q_D(QOpenGLWidget);
+ if (!d->initialized)
+ return;
+
+ // The FBO for the right buffer is only initialized when stereo is set
+ if (targetBuffer == TargetBuffer::RightBuffer && !format().stereo())
+ return;
+
+ d->setCurrentTargetBuffer(targetBuffer); // calls makeCurrent
}
/*!
@@ -1151,14 +1383,36 @@ QOpenGLContext *QOpenGLWidget::context() const
GLuint QOpenGLWidget::defaultFramebufferObject() const
{
Q_D(const QOpenGLWidget);
- return d->fbo ? d->fbo->handle() : 0;
+ return d->fbos[TargetBuffer::LeftBuffer] ? d->fbos[TargetBuffer::LeftBuffer]->handle() : 0;
+}
+
+/*!
+ \return The framebuffer object handle of the specified target buffer or
+ \c 0 if not yet initialized.
+
+ Calling this overload only makes sense if \l QSurfaceFormat::StereoBuffers is enabled
+ and supported by the hardware. If not, this method will return the default buffer.
+
+ \note The framebuffer object belongs to the context returned by context()
+ and may not be accessible from other contexts. The context and the framebuffer
+ object used by the widget changes when reparenting the widget via setParent().
+ In addition, the framebuffer object changes on each resize.
+
+ \since 6.5
+
+ \sa context()
+ */
+GLuint QOpenGLWidget::defaultFramebufferObject(TargetBuffer targetBuffer) const
+{
+ Q_D(const QOpenGLWidget);
+ return d->fbos[targetBuffer] ? d->fbos[targetBuffer]->handle() : 0;
}
/*!
This virtual function is called once before the first call to
paintGL() or resizeGL(). Reimplement it in a subclass.
- This function should set up any required OpenGL resources and state.
+ This function should set up any required OpenGL resources.
There is no need to call makeCurrent() because this has already been
done when this function is called. Note however that the framebuffer
@@ -1200,10 +1454,32 @@ void QOpenGLWidget::resizeGL(int w, int h)
other state is set and no clearing or drawing is performed by the
framework.
- \sa initializeGL(), resizeGL()
+ The default implementation performs a glClear(). Subclasses are not expected
+ to invoke the base class implementation and should perform clearing on their
+ own.
+
+ \note To ensure portability, do not expect that state set in initializeGL()
+ persists. Rather, set all necessary state, for example, by calling
+ glEnable(), in paintGL(). This is because some platforms, such as WebAssembly
+ with WebGL, may have limitations on OpenGL contexts in some situations, which
+ can lead to using the context used with the QOpenGLWidget for other purposes
+ as well.
+
+ When \l QSurfaceFormat::StereoBuffers is enabled, this function
+ will be called twice - once for each buffer. Query what buffer is
+ currently bound by calling currentTargetBuffer().
+
+ \note The framebuffer of each target will be drawn to even when
+ stereoscopic rendering is not supported by the hardware.
+ Only the left buffer will actually be visible in the window.
+
+ \sa initializeGL(), resizeGL(), currentTargetBuffer()
*/
void QOpenGLWidget::paintGL()
{
+ Q_D(QOpenGLWidget);
+ if (d->initialized)
+ d->context->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
/*!
@@ -1229,7 +1505,10 @@ void QOpenGLWidget::resizeEvent(QResizeEvent *e)
if (!d->initialized)
return;
- d->recreateFbo();
+ d->recreateFbos();
+ // Make sure our own context is current before invoking user overrides. If
+ // the fbo was recreated then there's a chance something else is current now.
+ makeCurrent();
resizeGL(width(), height());
d->sendPaintEvent(QRect(QPoint(0, 0), size()));
}
@@ -1249,11 +1528,13 @@ void QOpenGLWidget::paintEvent(QPaintEvent *e)
{
Q_UNUSED(e);
Q_D(QOpenGLWidget);
- if (!d->initialized)
- return;
- if (updatesEnabled())
- d->render();
+ d->initialize();
+ if (d->initialized) {
+ d->ensureRhiDependentResources();
+ if (updatesEnabled())
+ d->render();
+ }
}
/*!
@@ -1269,6 +1550,39 @@ QImage QOpenGLWidget::grabFramebuffer()
}
/*!
+ Renders and returns a 32-bit RGB image of the framebuffer of the specified target buffer.
+ This overload only makes sense to call when \l QSurfaceFormat::StereoBuffers is enabled.
+ Grabbing the framebuffer of the right target buffer will return the default image
+ if stereoscopic rendering is disabled or if not supported by the hardware.
+
+ \note This is a potentially expensive operation because it relies on glReadPixels()
+ to read back the pixels. This may be slow and can stall the GPU pipeline.
+
+ \since 6.5
+*/
+QImage QOpenGLWidget::grabFramebuffer(TargetBuffer targetBuffer)
+{
+ Q_D(QOpenGLWidget);
+ return d->grabFramebuffer(targetBuffer);
+}
+
+/*!
+ Returns the currently active target buffer. This will be the left buffer by default,
+ the right buffer is only used when \l QSurfaceFormat::StereoBuffers is enabled.
+ When stereoscopic rendering is enabled, this can be queried in paintGL() to know
+ what buffer is currently in use. paintGL() will be called twice, once for each target.
+
+ \since 6.5
+
+ \sa paintGL()
+*/
+QOpenGLWidget::TargetBuffer QOpenGLWidget::currentTargetBuffer() const
+{
+ Q_D(const QOpenGLWidget);
+ return d->currentTargetBuffer;
+}
+
+/*!
\reimp
*/
int QOpenGLWidget::metric(QPaintDevice::PaintDeviceMetric metric) const
@@ -1323,15 +1637,9 @@ int QOpenGLWidget::metric(QPaintDevice::PaintDeviceMetric metric) const
else
return qRound(dpmy * 0.0254);
case PdmDevicePixelRatio:
- if (window)
- return int(window->devicePixelRatio());
- else
- return 1.0;
+ return QWidget::metric(metric);
case PdmDevicePixelRatioScaled:
- if (window)
- return int(window->devicePixelRatio() * devicePixelRatioFScale());
- else
- return int(devicePixelRatioFScale());
+ return QWidget::metric(metric);
default:
qWarning("QOpenGLWidget::metric(): unknown metric %d", metric);
return 0;
@@ -1368,6 +1676,20 @@ QPaintEngine *QOpenGLWidget::paintEngine() const
return d->paintDevice->paintEngine();
}
+
+bool QOpenGLWidgetPrivate::setCurrentTargetBuffer(QOpenGLWidget::TargetBuffer targetBuffer)
+{
+ Q_Q(QOpenGLWidget);
+
+ if (targetBuffer == QOpenGLWidget::RightBuffer && !isStereoEnabled())
+ return false;
+
+ currentTargetBuffer = targetBuffer;
+ q->makeCurrent();
+
+ return true;
+}
+
/*!
\reimp
*/
@@ -1375,6 +1697,9 @@ bool QOpenGLWidget::event(QEvent *e)
{
Q_D(QOpenGLWidget);
switch (e->type()) {
+ case QEvent::WindowAboutToChangeInternal:
+ d->resetRhiDependentResources();
+ break;
case QEvent::WindowChangeInternal:
if (QCoreApplication::testAttribute(Qt::AA_ShareOpenGLContexts))
break;
@@ -1383,29 +1708,29 @@ bool QOpenGLWidget::event(QEvent *e)
if (isHidden())
break;
Q_FALLTHROUGH();
- case QEvent::Show: // reparenting may not lead to a resize so reinitalize on Show too
- if (d->initialized && window()->windowHandle()
- && d->context->shareContext() != QWidgetPrivate::get(window())->shareContext())
- {
+ case QEvent::Show: // reparenting may not lead to a resize so reinitialize on Show too
+ if (d->initialized && !d->wrapperTextures[d->currentTargetBuffer] && window()->windowHandle()) {
// Special case: did grabFramebuffer() for a hidden widget that then became visible.
// Recreate all resources since the context now needs to share with the TLW's.
if (!QCoreApplication::testAttribute(Qt::AA_ShareOpenGLContexts))
d->reset();
}
- if (!d->initialized && !size().isEmpty() && window()->windowHandle()) {
- d->initialize();
- if (d->initialized) {
- d->recreateFbo();
- // QTBUG-89812: generate a paint event, like resize would do,
- // otherwise a QOpenGLWidget in a QDockWidget may not show the
- // content upon (un)docking.
- d->sendPaintEvent(QRect(QPoint(0, 0), size()));
+ if (d->rhi()) {
+ if (!d->initialized && !size().isEmpty()) {
+ d->initialize();
+ if (d->initialized) {
+ d->recreateFbos();
+ // QTBUG-89812: generate a paint event, like resize would do,
+ // otherwise a QOpenGLWidget in a QDockWidget may not show the
+ // content upon (un)docking.
+ d->sendPaintEvent(QRect(QPoint(0, 0), size()));
+ }
}
}
break;
- case QEvent::ScreenChangeInternal:
+ case QEvent::DevicePixelRatioChange:
if (d->initialized && d->paintDevice->devicePixelRatio() != devicePixelRatio())
- d->recreateFbo();
+ d->recreateFbos();
break;
default:
break;
@@ -1413,8 +1738,6 @@ bool QOpenGLWidget::event(QEvent *e)
return QWidget::event(e);
}
-Q_CONSTRUCTOR_FUNCTION(qt_registerDefaultPlatformBackingStoreOpenGLSupport);
-
QT_END_NAMESPACE
#include "moc_qopenglwidget.cpp"
diff --git a/src/openglwidgets/qopenglwidget.h b/src/openglwidgets/qopenglwidget.h
index 0f2884f827..46cd39b4c1 100644
--- a/src/openglwidgets/qopenglwidget.h
+++ b/src/openglwidgets/qopenglwidget.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtOpenGLWidgets 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$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QOPENGLWIDGET_H
#define QOPENGLWIDGET_H
@@ -60,6 +24,13 @@ public:
NoPartialUpdate,
PartialUpdate
};
+ Q_ENUM(UpdateBehavior)
+
+ enum TargetBuffer : uint8_t {
+ LeftBuffer = 0, // Default
+ RightBuffer // Only used when QSurfaceFormat::StereoBuffers is enabled
+ };
+ Q_ENUM(TargetBuffer)
explicit QOpenGLWidget(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
~QOpenGLWidget();
@@ -76,12 +47,17 @@ public:
bool isValid() const;
void makeCurrent();
+ void makeCurrent(TargetBuffer targetBuffer);
void doneCurrent();
QOpenGLContext *context() const;
GLuint defaultFramebufferObject() const;
+ GLuint defaultFramebufferObject(TargetBuffer targetBuffer) const;
QImage grabFramebuffer();
+ QImage grabFramebuffer(TargetBuffer targetBuffer);
+
+ TargetBuffer currentTargetBuffer() const;
Q_SIGNALS:
void aboutToCompose();
diff --git a/src/openglwidgets/qtopenglwidgetsglobal.h b/src/openglwidgets/qtopenglwidgetsglobal.h
index 41c73fafda..b703ec27da 100644
--- a/src/openglwidgets/qtopenglwidgetsglobal.h
+++ b/src/openglwidgets/qtopenglwidgetsglobal.h
@@ -1,59 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtOpenGLWidgets 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$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTOPENGLWIDGETSGLOBAL_H
#define QTOPENGLWIDGETSGLOBAL_H
#include <QtCore/qglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-#ifndef QT_STATIC
-# if defined(QT_BUILD_OPENGLWIDGETS_LIB)
-# define Q_OPENGLWIDGETS_EXPORT Q_DECL_EXPORT
-# else
-# define Q_OPENGLWIDGETS_EXPORT Q_DECL_IMPORT
-# endif
-#else
-# define Q_OPENGLWIDGETS_EXPORT
-#endif
-
-QT_END_NAMESPACE
+#include <QtOpenGLWidgets/qtopenglwidgetsexports.h>
#endif // QTOPENGLWIDGETSGLOBAL_H