diff options
author | Kristoffer Skau <kristoffer.skau@qt.io> | 2022-11-24 14:31:09 +0100 |
---|---|---|
committer | Kristoffer Skau <kristoffer.skau@qt.io> | 2022-12-01 13:42:07 +0100 |
commit | c450f6d21c5153e05bd10afdd54767623cfbe7e8 (patch) | |
tree | e7ce6d73cda2f0f83673cad8b528bb67463e9cdd /src/openglwidgets/qopenglwidget.cpp | |
parent | 9bc74d14f379ad58d7a80d5514a3db5be5012de0 (diff) |
Support stereoscopic rendering with QGraphicsView
This patch adds a manual test and the required work in graphicsview and
qwidget private apis to support stereoscopic rendeing. Basically it
works by doing the drawing in QGraphicsView::paintEvent twice, once for
each buffer. This way the scene items are rendered to both buffers.
There's also an update to resolvement in QOpenGLWidgetPrivate
so that multisampling works correctly.
[ChangeLog][Widgets][QGraphicsView] Added support for
stereoscopic rendering.
Task-number: QTBUG-64587
Change-Id: I20650682daa805b64fe7f0d2ba086917d3f12229
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
Diffstat (limited to 'src/openglwidgets/qopenglwidget.cpp')
-rw-r--r-- | src/openglwidgets/qopenglwidget.cpp | 96 |
1 files changed, 78 insertions, 18 deletions
diff --git a/src/openglwidgets/qopenglwidget.cpp b/src/openglwidgets/qopenglwidget.cpp index 7a0ac05cee..4d8c181c22 100644 --- a/src/openglwidgets/qopenglwidget.cpp +++ b/src/openglwidgets/qopenglwidget.cpp @@ -552,7 +552,7 @@ public: void destroyFbos(); - void setCurrentTargetBuffer(QOpenGLWidget::TargetBuffer targetBuffer); + bool setCurrentTargetBuffer(QOpenGLWidget::TargetBuffer targetBuffer); QImage grabFramebuffer(QOpenGLWidget::TargetBuffer targetBuffer); QImage grabFramebuffer() override; void beginBackingStorePainting() override { inBackingStorePaint = true; } @@ -560,9 +560,13 @@ public: 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; QRhiTexture *wrapperTextures[2] = {}; QOpenGLFramebufferObject *fbos[2] = {}; @@ -697,8 +701,6 @@ void QOpenGLWidgetPrivate::reset() void QOpenGLWidgetPrivate::resetRhiDependentResources() { - Q_Q(QOpenGLWidget); - // 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. @@ -706,7 +708,7 @@ void QOpenGLWidgetPrivate::resetRhiDependentResources() delete wrapperTextures[0]; wrapperTextures[0] = nullptr; - if (q->format().stereo()) { + if (isStereoEnabled()) { delete wrapperTextures[1]; wrapperTextures[1] = nullptr; } @@ -738,9 +740,9 @@ void QOpenGLWidgetPrivate::recreateFbos() if (samples > 0) resolvedFbos[QOpenGLWidget::LeftBuffer] = new QOpenGLFramebufferObject(deviceSize); - const bool stereoEnabled = q->format().stereo(); + const bool stereo = isStereoEnabled(); - if (stereoEnabled) { + if (stereo) { fbos[QOpenGLWidget::RightBuffer] = new QOpenGLFramebufferObject(deviceSize, format); if (samples > 0) resolvedFbos[QOpenGLWidget::RightBuffer] = new QOpenGLFramebufferObject(deviceSize); @@ -753,11 +755,12 @@ void QOpenGLWidgetPrivate::recreateFbos() context->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); ensureRhiDependentResources(); - if (stereoEnabled) { + 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 @@ -895,11 +898,17 @@ void QOpenGLWidgetPrivate::initialize() void QOpenGLWidgetPrivate::resolveSamples() { + resolveSamplesForBuffer(QOpenGLWidget::LeftBuffer); + resolveSamplesForBuffer(QOpenGLWidget::RightBuffer); +} + +void QOpenGLWidgetPrivate::resolveSamplesForBuffer(QOpenGLWidget::TargetBuffer targetBuffer) +{ Q_Q(QOpenGLWidget); - if (resolvedFbos[currentTargetBuffer]) { - q->makeCurrent(); - QRect rect(QPoint(0, 0), fbos[currentTargetBuffer]->size()); - QOpenGLFramebufferObject::blitFramebuffer(resolvedFbos[currentTargetBuffer], rect, fbos[currentTargetBuffer], 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; } } @@ -924,8 +933,8 @@ void QOpenGLWidgetPrivate::render() return; } - const bool stereoEnabled = q->format().stereo(); - if (stereoEnabled) { + 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"); @@ -936,7 +945,7 @@ void QOpenGLWidgetPrivate::render() if (updateBehavior == QOpenGLWidget::NoPartialUpdate && hasBeenComposed) { invalidateFbo(); - if (stereoEnabled && fbos[QOpenGLWidget::RightBuffer]) { + if (stereo && fbos[QOpenGLWidget::RightBuffer]) { setCurrentTargetBuffer(QOpenGLWidget::RightBuffer); invalidateFbo(); setCurrentTargetBuffer(QOpenGLWidget::LeftBuffer); @@ -952,7 +961,7 @@ void QOpenGLWidgetPrivate::render() QOpenGLContextPrivate::get(ctx)->defaultFboRedirect = fbos[currentTargetBuffer]->handle(); q->paintGL(); - if (stereoEnabled && fbos[QOpenGLWidget::RightBuffer]) { + if (stereo && fbos[QOpenGLWidget::RightBuffer]) { setCurrentTargetBuffer(QOpenGLWidget::RightBuffer); QOpenGLContextPrivate::get(ctx)->defaultFboRedirect = fbos[currentTargetBuffer]->handle(); q->paintGL(); @@ -1017,7 +1026,7 @@ QImage QOpenGLWidgetPrivate::grabFramebuffer(QOpenGLWidget::TargetBuffer targetB // The second fbo is only created when stereoscopic rendering is enabled // Just use the default one if not. - if (targetBuffer == QOpenGLWidget::RightBuffer && !q->format().stereo()) + if (targetBuffer == QOpenGLWidget::RightBuffer && !isStereoEnabled()) targetBuffer = QOpenGLWidget::LeftBuffer; if (!fbos[targetBuffer]) // could be completely offscreen, without ever getting a resize event @@ -1028,7 +1037,7 @@ QImage QOpenGLWidgetPrivate::grabFramebuffer(QOpenGLWidget::TargetBuffer targetB setCurrentTargetBuffer(targetBuffer); if (resolvedFbos[targetBuffer]) { - resolveSamples(); + resolveSamplesForBuffer(targetBuffer); resolvedFbos[targetBuffer]->bind(); } @@ -1054,6 +1063,22 @@ 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); @@ -1254,6 +1279,34 @@ void QOpenGLWidget::makeCurrent() } /*! + 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 +} + +/*! Releases the context. It is not necessary to call this function in most cases, since the @@ -1585,11 +1638,18 @@ QPaintEngine *QOpenGLWidget::paintEngine() const return d->paintDevice->paintEngine(); } -void QOpenGLWidgetPrivate::setCurrentTargetBuffer(QOpenGLWidget::TargetBuffer targetBuffer) + +bool QOpenGLWidgetPrivate::setCurrentTargetBuffer(QOpenGLWidget::TargetBuffer targetBuffer) { Q_Q(QOpenGLWidget); + + if (targetBuffer == QOpenGLWidget::RightBuffer && !isStereoEnabled()) + return false; + currentTargetBuffer = targetBuffer; q->makeCurrent(); + + return true; } /*! |