diff options
-rw-r--r-- | examples/quick/quickwidgets/qquickviewcomparison/main.cpp | 3 | ||||
-rw-r--r-- | examples/quick/quickwidgets/qquickviewcomparison/mainwindow.cpp | 2 | ||||
-rw-r--r-- | examples/quick/quickwidgets/quickwidget/main.cpp | 3 | ||||
-rw-r--r-- | src/quickwidgets/qquickwidget.cpp | 120 | ||||
-rw-r--r-- | src/quickwidgets/qquickwidget_p.h | 1 | ||||
-rw-r--r-- | tests/auto/.prev_CMakeLists.txt | 3 | ||||
-rw-r--r-- | tests/auto/CMakeLists.txt | 3 | ||||
-rw-r--r-- | tests/auto/auto.pro | 4 | ||||
-rw-r--r-- | tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp | 9 |
9 files changed, 102 insertions, 46 deletions
diff --git a/examples/quick/quickwidgets/qquickviewcomparison/main.cpp b/examples/quick/quickwidgets/qquickviewcomparison/main.cpp index 73db97eddb..f1fe397462 100644 --- a/examples/quick/quickwidgets/qquickviewcomparison/main.cpp +++ b/examples/quick/quickwidgets/qquickviewcomparison/main.cpp @@ -60,6 +60,9 @@ int main(int argc, char **argv) QApplication app(argc, argv); + // this example and QQuickWidget are only functional when rendering with OpenGL + QQuickWindow::setSceneGraphBackend(QSGRendererInterface::OpenGLRhi); + QCoreApplication::setApplicationName("Qt QQuickView/QQuickWidget Comparison Example"); QCoreApplication::setOrganizationName("QtProject"); QCoreApplication::setApplicationVersion(QT_VERSION_STR); diff --git a/examples/quick/quickwidgets/qquickviewcomparison/mainwindow.cpp b/examples/quick/quickwidgets/qquickviewcomparison/mainwindow.cpp index 850ecd147b..2e45bc2df6 100644 --- a/examples/quick/quickwidgets/qquickviewcomparison/mainwindow.cpp +++ b/examples/quick/quickwidgets/qquickviewcomparison/mainwindow.cpp @@ -179,8 +179,8 @@ void MainWindow::updateView() connect(quickWidget, &QQuickWidget::statusChanged, this, &MainWindow::onStatusChangedWidget); connect(quickWidget, &QQuickWidget::sceneGraphError, this, &MainWindow::onSceneGraphError); quickWidget->setSource(source); - m_currentRootObject = quickWidget->rootObject(); switchTo(quickWidget); + m_currentRootObject = quickWidget->rootObject(); } if (m_currentRootObject) { diff --git a/examples/quick/quickwidgets/quickwidget/main.cpp b/examples/quick/quickwidgets/quickwidget/main.cpp index f28b2f1443..3bff5901f9 100644 --- a/examples/quick/quickwidgets/quickwidget/main.cpp +++ b/examples/quick/quickwidgets/quickwidget/main.cpp @@ -197,6 +197,9 @@ int main(int argc, char **argv) { QApplication app(argc, argv); + // this example and QQuickWidget are only functional when rendering with OpenGL + QQuickWindow::setSceneGraphBackend(QSGRendererInterface::OpenGLRhi); + QCoreApplication::setApplicationName("Qt QQuickWidget Example"); QCoreApplication::setOrganizationName("QtProject"); QCoreApplication::setApplicationVersion(QT_VERSION_STR); diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index f3d26e4a73..221bdfaee6 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -44,6 +44,7 @@ #include "private/qquickitem_p.h" #include "private/qquickitemchangelistener_p.h" #include "private/qquickrendercontrol_p.h" +#include "private/qsgrhisupport_p.h" #include "private/qsgsoftwarerenderer_p.h" @@ -75,6 +76,9 @@ # include <QtCore/qt_windows.h> #endif +#include <QtQuick/qquickgraphicsdevice.h> +#include <QtQuick/qquickrendertarget.h> + QT_BEGIN_NAMESPACE // override setVisble to prevent accidental offscreen window being created @@ -102,16 +106,25 @@ private: QQuickWidget *m_quickWidget; }; -void QQuickWidgetPrivate::init(QQmlEngine* e) +void QQuickWidgetPrivate::initOffscreenWindow() { Q_Q(QQuickWidget); - - renderControl = new QQuickWidgetRenderControl(q); - offscreenWindow = new QQuickWindow(*new QQuickOffcreenWindowPrivate(),renderControl); + offscreenWindow = new QQuickWindow(*new QQuickOffcreenWindowPrivate(), renderControl); offscreenWindow->setTitle(QString::fromLatin1("Offscreen")); offscreenWindow->setObjectName(QString::fromLatin1("QQuickOffScreenWindow")); // Do not call create() on offscreenWindow. + QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInitialized()), q, SLOT(createFramebufferObject())); + QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInvalidated()), q, SLOT(destroyFramebufferObject())); +} + +void QQuickWidgetPrivate::init(QQmlEngine* e) +{ + Q_Q(QQuickWidget); + + renderControl = new QQuickWidgetRenderControl(q); + initOffscreenWindow(); + // Check if the Software Adaptation is being used auto sgRendererInterface = offscreenWindow->rendererInterface(); if (sgRendererInterface && sgRendererInterface->graphicsApi() == QSGRendererInterface::Software) @@ -126,6 +139,9 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) qWarning("QQuickWidget is not supported on this platform."); } + if (QSGRhiSupport::instance()->rhiBackend() != QRhi::OpenGLES2) + qWarning("QQuickWidget is only supported on OpenGL. Use QQuickWindow::setSceneGraphBackend() to override the default."); + engine = e; if (!engine.isNull() && !engine.data()->incubationController()) @@ -135,8 +151,6 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) q->setAcceptDrops(true); #endif - QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInitialized()), q, SLOT(createFramebufferObject())); - QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInvalidated()), q, SLOT(destroyFramebufferObject())); QObject::connect(renderControl, SIGNAL(renderRequested()), q, SLOT(triggerUpdate())); QObject::connect(renderControl, SIGNAL(sceneChanged()), q, SLOT(triggerUpdate())); } @@ -181,18 +195,28 @@ void QQuickWidgetPrivate::invalidateRenderControl() void QQuickWidgetPrivate::handleWindowChange() { + Q_Q(QQuickWidget); + if (offscreenWindow->isPersistentSceneGraph() && qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts)) return; // In case of !isPersistentSceneGraph or when we need a new context due to // the need to share resources with the new window's context, we must both - // invalidate the scenegraph and destroy the context. With - // QQuickRenderControl destroying the context must be preceded by an - // invalidate to prevent being left with dangling context references in the - // rendercontrol. + // invalidate the scenegraph and destroy the context. QQuickRenderControl + // must be recreated because its RHI will contain a dangling pointer to + // the context. - invalidateRenderControl(); + delete offscreenWindow; + offscreenWindow = nullptr; + delete renderControl; + renderControl = new QQuickWidgetRenderControl(q); + initOffscreenWindow(); + + QObject::connect(renderControl, SIGNAL(renderRequested()), q, SLOT(triggerUpdate())); + QObject::connect(renderControl, SIGNAL(sceneChanged()), q, SLOT(triggerUpdate())); + + execute(); if (!useSoftwareRenderer) destroyContext(); } @@ -230,10 +254,10 @@ QQuickWidgetPrivate::~QQuickWidgetPrivate() #if QT_CONFIG(opengl) // context and offscreenSurface are current at this stage, if the context was created. Q_ASSERT(!context || (QOpenGLContext::currentContext() == context && context->surface() == offscreenSurface)); - delete renderControl; // always delete the rendercontrol first - delete offscreenWindow; delete resolvedFbo; delete fbo; + delete offscreenWindow; + delete renderControl; destroyContext(); #endif @@ -302,6 +326,8 @@ void QQuickWidgetPrivate::render(bool needsSync) QOpenGLContextPrivate::get(context)->defaultFboRedirect = fbo->handle(); + renderControl->beginFrame(); + if (needsSync) { renderControl->polishItems(); renderControl->sync(); @@ -309,6 +335,9 @@ void QQuickWidgetPrivate::render(bool needsSync) renderControl->render(); + renderControl->endFrame(); + + context->makeCurrent(offscreenSurface); if (resolvedFbo) { QRect rect(QPoint(0, 0), fbo->size()); QOpenGLFramebufferObject::blitFramebuffer(resolvedFbo, rect, fbo, rect); @@ -349,7 +378,6 @@ void QQuickWidgetPrivate::renderSceneGraph() return; if (!useSoftwareRenderer) { - QOpenGLContext *context = offscreenWindow->openglContext(); if (!context) { qWarning("QQuickWidget: Attempted to render scene with no context"); return; @@ -383,7 +411,13 @@ QImage QQuickWidgetPrivate::grabFramebuffer() context->makeCurrent(offscreenSurface); #endif } - return offscreenWindow->grabWindow(); + + + // grabWindow() does not work for the OpenGL + render control case, so we + // prefer the FBO's toImage() if available. When the software renderer + // is in use, however, there will be no FBO and we fall back to grabWindow() + // instead. + return fbo != nullptr ? fbo->toImage() : offscreenWindow->grabWindow(); } // Intentionally not overriding the QQuickWindow's focusObject. @@ -894,7 +928,7 @@ void QQuickWidgetPrivate::createContext() // On hide-show we may invalidate() (when !isPersistentSceneGraph) but our // context is kept. We may need to initialize() again, though. - const bool reinit = context && !offscreenWindow->openglContext(); + const bool reinit = context && !offscreenWindow->isSceneGraphInitialized(); if (!reinit) { if (context) @@ -929,8 +963,10 @@ void QQuickWidgetPrivate::createContext() } if (context->makeCurrent(offscreenSurface)) { - if (!offscreenWindow->openglContext()) - renderControl->initialize(context); + if (!offscreenWindow->isSceneGraphInitialized()) { + offscreenWindow->setGraphicsDevice(QQuickGraphicsDevice::fromOpenGLContext(context)); + renderControl->initialize(); + } } else #endif qWarning("QQuickWidget: Failed to make context current"); @@ -971,30 +1007,28 @@ void QQuickWidget::createFramebufferObject() } #if QT_CONFIG(opengl) - QOpenGLContext *context = d->offscreenWindow->openglContext(); - - if (!context) { + if (!d->context) { qWarning("QQuickWidget: Attempted to create FBO with no context"); return; } QOpenGLContext *shareWindowContext = QWidgetPrivate::get(window())->shareContext(); - if (shareWindowContext && context->shareContext() != shareWindowContext && !qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts)) { - context->setShareContext(shareWindowContext); - context->setScreen(shareWindowContext->screen()); - if (!context->create()) + if (shareWindowContext && d->context->shareContext() != shareWindowContext && !qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts)) { + d->context->setShareContext(shareWindowContext); + d->context->setScreen(shareWindowContext->screen()); + if (!d->context->create()) qWarning("QQuickWidget: Failed to recreate context"); // The screen may be different so we must recreate the offscreen surface too. // Unlike QOpenGLContext, QOffscreenSurface's create() does not recreate so have to destroy() first. d->offscreenSurface->destroy(); - d->offscreenSurface->setScreen(context->screen()); + d->offscreenSurface->setScreen(d->context->screen()); d->offscreenSurface->create(); } - context->makeCurrent(d->offscreenSurface); + d->context->makeCurrent(d->offscreenSurface); int samples = d->requestedSamples; - if (!QOpenGLExtensions(context).hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) + if (!QOpenGLExtensions(d->context).hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) samples = 0; QOpenGLFramebufferObjectFormat format; @@ -1004,11 +1038,12 @@ void QQuickWidget::createFramebufferObject() // The default framebuffer for normal windows have sRGB support on OS X which leads to the Qt Quick text item // utilizing sRGB blending. To get identical results with QQuickWidget we have to have our framebuffer backed // by an sRGB texture. -#ifdef Q_OS_OSX - if (context->hasExtension("GL_ARB_framebuffer_sRGB") - && context->hasExtension("GL_EXT_texture_sRGB") - && context->hasExtension("GL_EXT_texture_sRGB_decode")) +#ifdef Q_OS_MACOS + if (d->context->hasExtension("GL_ARB_framebuffer_sRGB") + && d->context->hasExtension("GL_EXT_texture_sRGB") + && d->context->hasExtension("GL_EXT_texture_sRGB_decode")) { format.setInternalTextureFormat(GL_SRGB8_ALPHA8_EXT); + } #endif const QSize fboSize = size() * devicePixelRatioF(); @@ -1022,15 +1057,18 @@ void QQuickWidget::createFramebufferObject() // When compositing in the backingstore, sampling the sRGB texture would perform an // sRGB-linear conversion which is not what we want when the final framebuffer (the window's) // is sRGB too. Disable the conversion. -#ifdef Q_OS_OSX +#ifdef Q_OS_MACOS if (format.internalTextureFormat() == GL_SRGB8_ALPHA8_EXT) { - QOpenGLFunctions *funcs = context->functions(); + QOpenGLFunctions *funcs = d->context->functions(); funcs->glBindTexture(GL_TEXTURE_2D, d->fbo->texture()); funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT); } #endif - d->offscreenWindow->setRenderTarget(d->fbo); + GLuint textureId = d->fbo->texture(); + d->offscreenWindow->setRenderTarget( QQuickRenderTarget::fromNativeTexture({ textureId, 0 }, fboSize, samples)); + + d->renderControl->setSamples(samples); if (samples > 0) d->resolvedFbo = new QOpenGLFramebufferObject(fboSize); @@ -1247,7 +1285,7 @@ void QQuickWidget::resizeEvent(QResizeEvent *e) if (d->context) { // Bail out when receiving a resize after scenegraph invalidation. This can happen // during hide - resize - show sequences and also during application exit. - if (!d->fbo && !d->offscreenWindow->openglContext()) + if (!d->fbo && !d->offscreenWindow->isSceneGraphInitialized()) return; if (!d->fbo || d->fbo->size() != size() * devicePixelRatioF()) { needsSync = true; @@ -1260,8 +1298,7 @@ void QQuickWidget::resizeEvent(QResizeEvent *e) d->createContext(); } - QOpenGLContext *context = d->offscreenWindow->openglContext(); - if (!context) { + if (!d->context) { qWarning("QQuickWidget::resizeEvent() no OpenGL context"); return; } @@ -1351,7 +1388,7 @@ void QQuickWidget::showEvent(QShowEvent *) if (!d->useSoftwareRenderer) { d->createContext(); - if (d->offscreenWindow->openglContext()) { + if (d->offscreenWindow->isSceneGraphInitialized()) { shouldTriggerUpdate = false; d->render(true); // render() may have led to a QQuickWindow::update() call (for @@ -1707,6 +1744,11 @@ void QQuickWidget::setClearColor(const QColor &color) \warning Use the return value of this function with caution. In particular, do not ever attempt to show the QQuickWindow, and be very careful when using other QWindow-only APIs. + + \warning The offscreen window may be deleted (and recreated) during + the life time of the QQuickWidget, particularly when the widget is + moved to another QQuickWindow. If you need to know when the window + has been replaced, connect to its destroyed() signal. */ QQuickWindow *QQuickWidget::quickWindow() const { diff --git a/src/quickwidgets/qquickwidget_p.h b/src/quickwidgets/qquickwidget_p.h index 881f7f9220..0517a772e1 100644 --- a/src/quickwidgets/qquickwidget_p.h +++ b/src/quickwidgets/qquickwidget_p.h @@ -108,6 +108,7 @@ public: #endif void init(QQmlEngine* e = 0); + void initOffscreenWindow(); void ensureEngine() const; void handleWindowChange(); void invalidateRenderControl(); diff --git a/tests/auto/.prev_CMakeLists.txt b/tests/auto/.prev_CMakeLists.txt index 9ec371359b..c52566a954 100644 --- a/tests/auto/.prev_CMakeLists.txt +++ b/tests/auto/.prev_CMakeLists.txt @@ -13,3 +13,6 @@ endif() if(TARGET Qt::Gui AND (QT_FEATURE_opengl OR QT_FEATURE_opengles2 OR QT_FEATURE_opengles3)) add_subdirectory(particles) endif() +if(TARGET Qt::Gui AND TARGET Qt::Widgets AND (QT_FEATURE_opengl OR QT_FEATURE_opengles2 OR QT_FEATURE_opengles3)) + add_subdirectory(quickwidgets) +endif() diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt index 222c735359..eb1c2d34df 100644 --- a/tests/auto/CMakeLists.txt +++ b/tests/auto/CMakeLists.txt @@ -15,3 +15,6 @@ endif() if(TARGET Qt::Gui AND (QT_FEATURE_opengl OR QT_FEATURE_opengles2 OR QT_FEATURE_opengles3)) add_subdirectory(particles) endif() +if(TARGET Qt::Gui AND TARGET Qt::Widgets AND (QT_FEATURE_opengl OR QT_FEATURE_opengles2 OR QT_FEATURE_opengles3)) + add_subdirectory(quickwidgets) +endif() diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 2a22868390..d967b01d61 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -11,9 +11,7 @@ SUBDIRS=\ qtHaveModule(gui):qtConfig(opengl(es1|es2)?) { SUBDIRS += particles -# Disabled for Qt 6 until a conclusion on QQuickWidget is reached -# qtHaveModule(widgets): SUBDIRS += quickwidgets - + qtHaveModule(widgets): SUBDIRS += quickwidgets } # console applications not supported diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp index 8211400262..b47837179a 100644 --- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp +++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp @@ -152,6 +152,7 @@ private: tst_qquickwidget::tst_qquickwidget() { + QQuickWindow::setSceneGraphBackend(QSGRendererInterface::OpenGLRhi); } void tst_qquickwidget::showHide() @@ -379,8 +380,8 @@ void tst_qquickwidget::readback() QImage img = view->grabFramebuffer(); QVERIFY(!img.isNull()); - QCOMPARE(img.width(), view->width()); - QCOMPARE(img.height(), view->height()); + QCOMPARE(img.width(), qCeil(view->width() * view->devicePixelRatioF())); + QCOMPARE(img.height(), qCeil(view->height() * view->devicePixelRatioF())); QRgb pix = img.pixel(5, 5); QCOMPARE(pix, qRgb(255, 0, 0)); @@ -447,13 +448,15 @@ void tst_qquickwidget::reparentToNewWindow() window2.show(); QVERIFY(QTest::qWaitForWindowExposed(&window2)); - QSignalSpy afterRenderingSpy(qqw->quickWindow(), &QQuickWindow::afterRendering); qqw->setParent(&window2); + + QSignalSpy afterRenderingSpy(qqw->quickWindow(), &QQuickWindow::afterRendering); qqw->show(); QTRY_VERIFY(afterRenderingSpy.size() > 0); QImage img = qqw->grabFramebuffer(); + QCOMPARE(img.pixel(5, 5), qRgb(255, 0, 0)); } |