diff options
Diffstat (limited to 'src/quickwidgets/qquickwidget.cpp')
-rw-r--r-- | src/quickwidgets/qquickwidget.cpp | 867 |
1 files changed, 497 insertions, 370 deletions
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 8ddd2d76c7..c782dc74b1 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -1,44 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick 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) 2021 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 "qquickwidget.h" #include "qquickwidget_p.h" +#include "qaccessiblequickwidgetfactory_p.h" +#include <QtWidgets/private/qwidgetrepaintmanager_p.h> #include "private/qquickwindow_p.h" #include "private/qquickitem_p.h" @@ -59,14 +25,6 @@ #include <QtGui/private/qguiapplication_p.h> #include <QtGui/qpa/qplatformintegration.h> -#if QT_CONFIG(opengl) -#include <private/qopenglcontext_p.h> -#include <private/qopenglextensions_p.h> -#include <QOpenGLFramebufferObject> -#include <QOpenGLContext> -#include <QOpenGLFunctions> -#include <QtOpenGL/qpa/qplatformbackingstoreopenglsupport.h> -#endif #include <QtGui/QPainter> #include <QtQuick/QSGRendererInterface> @@ -82,11 +40,29 @@ #include <QtQuick/qquickgraphicsdevice.h> #include <QtQuick/qquickrendertarget.h> +#include "private/qwidget_p.h" + +#if QT_CONFIG(graphicsview) +#include <QtWidgets/qgraphicsscene.h> +#include <QtWidgets/qgraphicsview.h> +#endif + +#if QT_CONFIG(vulkan) +#include <QtGui/private/qvulkandefaultinstance_p.h> +#endif + QT_BEGIN_NAMESPACE +QQuickWidgetOffscreenWindow::QQuickWidgetOffscreenWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control) +:QQuickWindow(dd, control) +{ + setTitle(QString::fromLatin1("Offscreen")); + setObjectName(QString::fromLatin1("QQuickWidgetOffscreenWindow")); +} + // override setVisble to prevent accidental offscreen window being created // by base class. -class QQuickOffcreenWindowPrivate: public QQuickWindowPrivate { +class QQuickWidgetOffscreenWindowPrivate: public QQuickWindowPrivate { public: void setVisible(bool visible) override { Q_Q(QWindow); @@ -96,61 +72,155 @@ public: } }; +class QQuickWidgetRenderControlPrivate; + class QQuickWidgetRenderControl : public QQuickRenderControl { + Q_DECLARE_PRIVATE(QQuickWidgetRenderControl) public: - QQuickWidgetRenderControl(QQuickWidget *quickwidget) : m_quickWidget(quickwidget) {} - QWindow *renderWindow(QPoint *offset) override { - if (offset) - *offset = m_quickWidget->mapTo(m_quickWidget->window(), QPoint()); - return m_quickWidget->window()->windowHandle(); + QQuickWidgetRenderControl(QQuickWidget *quickwidget); + QWindow *renderWindow(QPoint *offset) override; + +}; + +class QQuickWidgetRenderControlPrivate : public QQuickRenderControlPrivate +{ +public: + Q_DECLARE_PUBLIC(QQuickWidgetRenderControl) + QQuickWidgetRenderControlPrivate(QQuickWidgetRenderControl *renderControl, QQuickWidget *qqw) + : QQuickRenderControlPrivate(renderControl) + , m_quickWidget(qqw) + { + } + + bool isRenderWindow(const QWindow *w) override { +#if QT_CONFIG(graphicsview) + QWidgetPrivate *widgetd = QWidgetPrivate::get(m_quickWidget); + auto *proxy = (widgetd && widgetd->extra) ? widgetd->extra->proxyWidget : nullptr; + auto *scene = proxy ? proxy->scene() : nullptr; + if (scene) { + for (const auto &view : scene->views()) { + if (view->window()->windowHandle() == w) + return true; + } + } + + return m_quickWidget->window()->windowHandle() == w; +#endif } -private: QQuickWidget *m_quickWidget; }; +QQuickWidgetRenderControl::QQuickWidgetRenderControl(QQuickWidget *quickWidget) + : QQuickRenderControl(*(new QQuickWidgetRenderControlPrivate(this, quickWidget)), nullptr) +{ +} + +QWindow *QQuickWidgetRenderControl::renderWindow(QPoint *offset) +{ + Q_D(QQuickWidgetRenderControl); + if (offset) + *offset = d->m_quickWidget->mapTo(d->m_quickWidget->window(), QPoint()); + + QWindow *result = nullptr; +#if QT_CONFIG(graphicsview) + QWidgetPrivate *widgetd = QWidgetPrivate::get(d->m_quickWidget); + if (widgetd->extra) { + if (auto proxy = widgetd->extra->proxyWidget) { + auto scene = proxy->scene(); + if (scene) { + const auto views = scene->views(); + if (!views.isEmpty()) { + // Get the first QGV containing the proxy. Not ideal, but the callers + // of this function aren't prepared to handle more than one render window. + auto candidateView = views.first(); + result = candidateView->window()->windowHandle(); + } + } + } + } +#endif + if (!result) + result = d->m_quickWidget->window()->windowHandle(); + + return result; +} + void QQuickWidgetPrivate::initOffscreenWindow() { Q_Q(QQuickWidget); - offscreenWindow = new QQuickWindow(*new QQuickOffcreenWindowPrivate(), renderControl); - offscreenWindow->setTitle(QString::fromLatin1("Offscreen")); - offscreenWindow->setObjectName(QString::fromLatin1("QQuickOffScreenWindow")); + + ensureBackingScene(); + offscreenWindow->setScreen(q->screen()); // Do not call create() on offscreenWindow. QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInitialized()), q, SLOT(createFramebufferObject())); QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInvalidated()), q, SLOT(destroyFramebufferObject())); QWidget::connect(offscreenWindow, &QQuickWindow::focusObjectChanged, q, &QQuickWidget::propagateFocusObjectChanged); + +#if QT_CONFIG(accessibility) + QAccessible::installFactory(&qAccessibleQuickWidgetFactory); +#endif } -void QQuickWidgetPrivate::init(QQmlEngine* e) +void QQuickWidgetPrivate::ensureBackingScene() { - Q_Q(QQuickWidget); + // This should initialize, if not already done, the absolute minimum set of + // mandatory backing resources, meaning the QQuickWindow and its + // QQuickRenderControl. This function may be called very early on upon + // construction, including before init() even. - renderControl = new QQuickWidgetRenderControl(q); - initOffscreenWindow(); + Q_Q(QQuickWidget); + if (!renderControl) + renderControl = new QQuickWidgetRenderControl(q); + if (!offscreenWindow) + offscreenWindow = new QQuickWidgetOffscreenWindow(*new QQuickWidgetOffscreenWindowPrivate(), renderControl); // Check if the Software Adaptation is being used auto sgRendererInterface = offscreenWindow->rendererInterface(); if (sgRendererInterface && sgRendererInterface->graphicsApi() == QSGRendererInterface::Software) useSoftwareRenderer = true; +} + +void QQuickWidgetPrivate::init(QQmlEngine* e) +{ + Q_Q(QQuickWidget); + + initOffscreenWindow(); if (!useSoftwareRenderer) { -#if QT_CONFIG(opengl) - if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface)) + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RhiBasedRendering)) setRenderToTexture(); else -#endif qWarning("QQuickWidget is not supported on this platform."); } - if (QSGRhiSupport::instance()->rhiBackend() != QRhi::OpenGLES2) - qWarning("QQuickWidget is only supported on OpenGL. Use QQuickWindow::setGraphicsApi() to override the default."); - engine = e; if (!engine.isNull() && !engine.data()->incubationController()) engine.data()->setIncubationController(offscreenWindow->incubationController()); + q->setMouseTracking(true); + q->setFocusPolicy(Qt::StrongFocus); +#ifndef Q_OS_MACOS + /* + Usually, a QTouchEvent comes from a touchscreen, and we want those + touch events in Qt Quick. But on macOS, there are no touchscreens, and + WA_AcceptTouchEvents has a different meaning: QApplication::notify() + calls the native-integration function registertouchwindow() to change + NSView::allowedTouchTypes to include NSTouchTypeMaskIndirect when the + trackpad cursor enters the window, and removes that mask when the + cursor exits. In other words, WA_AcceptTouchEvents enables getting + discrete touchpoints from the trackpad. We rather prefer to get mouse, + wheel and native gesture events from the trackpad (because those + provide more of a "native feel"). The only exception is for + MultiPointTouchArea, and it takes care of that for itself. So don't + automatically set WA_AcceptTouchEvents on macOS. The user can still do + it, but we don't recommend it. + */ + q->setAttribute(Qt::WA_AcceptTouchEvents); +#endif + #if QT_CONFIG(quick_draganddrop) q->setAcceptDrops(true); #endif @@ -171,38 +241,24 @@ void QQuickWidgetPrivate::ensureEngine() const void QQuickWidgetPrivate::invalidateRenderControl() { -#if QT_CONFIG(opengl) - if (!useSoftwareRenderer) { - if (!context) // this is not an error, could be called before creating the context, or multiple times - return; - - bool success = context->makeCurrent(offscreenSurface); - if (!success) { - qWarning("QQuickWidget::invalidateRenderControl could not make context current"); - return; - } + if (!useSoftwareRenderer && rhi) { + // For the user's own OpenGL code connected to some QQuickWindow signals. + rhi->makeThreadLocalNativeContextCurrent(); } -#endif renderControl->invalidate(); - - // Many things can happen inside the above invalidate() call, including a - // change of current context. Restore if needed since some code will rely - // on the fact that this function makes and leaves the context current. -#if QT_CONFIG(opengl) - if (!useSoftwareRenderer && context) { - if (QOpenGLContext::currentContext() != context) - context->makeCurrent(offscreenSurface); - } -#endif } void QQuickWidgetPrivate::handleWindowChange() { Q_Q(QQuickWidget); - if (offscreenWindow->isPersistentSceneGraph() && qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts)) + if (offscreenWindow->isPersistentSceneGraph() + && qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts) + && rhiConfig().api() == QPlatformBackingStoreRhiConfig::OpenGL) + { 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 @@ -210,7 +266,7 @@ void QQuickWidgetPrivate::handleWindowChange() // must be recreated because its RHI will contain a dangling pointer to // the context. - delete offscreenWindow; + QScopedPointer<QQuickWindow> oldOffScreenWindow(offscreenWindow); // Do not delete before reparenting sgItem offscreenWindow = nullptr; delete renderControl; @@ -220,22 +276,23 @@ void QQuickWidgetPrivate::handleWindowChange() QObject::connect(renderControl, SIGNAL(renderRequested()), q, SLOT(triggerUpdate())); QObject::connect(renderControl, SIGNAL(sceneChanged()), q, SLOT(triggerUpdate())); - execute(); - if (!useSoftwareRenderer) - destroyContext(); + if (!source.isEmpty()) + execute(); + else if (QQuickItem *sgItem = qobject_cast<QQuickItem *>(root)) + sgItem->setParentItem(offscreenWindow->contentItem()); } QQuickWidgetPrivate::QQuickWidgetPrivate() : root(nullptr) , component(nullptr) , offscreenWindow(nullptr) - , offscreenSurface(nullptr) , renderControl(nullptr) -#if QT_CONFIG(opengl) - , fbo(nullptr) - , resolvedFbo(nullptr) - , context(nullptr) -#endif + , rhi(nullptr) + , outputTexture(nullptr) + , depthStencil(nullptr) + , msaaBuffer(nullptr) + , rt(nullptr) + , rtRp(nullptr) , resizeMode(QQuickWidget::SizeViewToRootObject) , initialSize(0,0) , eventPending(false) @@ -244,28 +301,18 @@ QQuickWidgetPrivate::QQuickWidgetPrivate() , requestedSamples(0) , useSoftwareRenderer(false) , forceFullUpdate(false) + , deviceLost(false) { } -QQuickWidgetPrivate::~QQuickWidgetPrivate() +void QQuickWidgetPrivate::destroy() { + Q_Q(QQuickWidget); invalidateRenderControl(); - - if (useSoftwareRenderer) { - delete renderControl; - delete offscreenWindow; - } else { -#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 resolvedFbo; - delete fbo; - delete offscreenWindow; - delete renderControl; - - destroyContext(); -#endif - } + q->destroyFramebufferObject(); + delete offscreenWindow; + delete renderControl; + offscreenRenderer.reset(); } void QQuickWidgetPrivate::execute() @@ -305,34 +352,37 @@ void QQuickWidgetPrivate::itemGeometryChanged(QQuickItem *resizeItem, QQuickGeom void QQuickWidgetPrivate::render(bool needsSync) { + Q_Q(QQuickWidget); if (!useSoftwareRenderer) { -#if QT_CONFIG(opengl) - // createFramebufferObject() bails out when the size is empty. In this case - // we cannot render either. - if (!fbo) - return; - - Q_ASSERT(context); - - bool current = context->makeCurrent(offscreenSurface); - - if (!current && !context->isValid()) { - renderControl->invalidate(); - current = context->create() && context->makeCurrent(offscreenSurface); - if (current) { - offscreenWindow->setGraphicsDevice(QQuickGraphicsDevice::fromOpenGLContext(context)); - renderControl->initialize(); - } + if (deviceLost) { + deviceLost = false; + initializeWithRhi(); + q->createFramebufferObject(); } - if (!current) { - qWarning("QQuickWidget: Cannot render due to failing makeCurrent()"); + if (!rhi) { + qWarning("QQuickWidget: Attempted to render scene with no rhi"); return; } - QOpenGLContextPrivate::get(context)->defaultFboRedirect = fbo->handle(); + // createFramebufferObject() bails out when the size is empty. In this case + // we cannot render either. + if (!outputTexture) + return; renderControl->beginFrame(); + QQuickRenderControlPrivate::FrameStatus frameStatus = QQuickRenderControlPrivate::get(renderControl)->frameStatus; + if (frameStatus == QQuickRenderControlPrivate::DeviceLostInBeginFrame) { + // graphics resources controlled by us must be released + invalidateRenderControl(); + // skip this round and hope that the tlw's repaint manager will manage to reinitialize + deviceLost = true; + return; + } + if (frameStatus != QQuickRenderControlPrivate::RecordingFrame) { + qWarning("QQuickWidget: Failed to begin recording a frame"); + return; + } if (needsSync) { renderControl->polishItems(); @@ -342,24 +392,14 @@ 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); - } - - static_cast<QOpenGLExtensions *>(context->functions())->flushShared(); - - QOpenGLContextPrivate::get(context)->defaultFboRedirect = 0; -#endif } else { //Software Renderer if (needsSync) { renderControl->polishItems(); renderControl->sync(); } - + if (!offscreenWindow) + return; QQuickWindowPrivate *cd = QQuickWindowPrivate::get(offscreenWindow); auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(cd->renderer); if (softwareRenderer && !softwareImage.isNull()) { @@ -383,16 +423,6 @@ void QQuickWidgetPrivate::renderSceneGraph() if (!q->isVisible() || fakeHidden) return; - if (!useSoftwareRenderer) { -#if QT_CONFIG(opengl) - if (!context) { - qWarning("QQuickWidget: Attempted to render scene with no context"); - return; - } -#endif - Q_ASSERT(offscreenSurface); - } - render(true); #if QT_CONFIG(graphicsview) @@ -410,25 +440,35 @@ void QQuickWidgetPrivate::renderSceneGraph() QImage QQuickWidgetPrivate::grabFramebuffer() { - if (!useSoftwareRenderer) { -#if QT_CONFIG(opengl) - if (!context) - return QImage(); - - context->makeCurrent(offscreenSurface); -#endif + if (!useSoftwareRenderer && !rhi) + return QImage(); + + // grabWindow() does not work for the rhi case, we are in control of the + // render target, and so it is up to us to read it back. When the software + // renderer is in use, just call grabWindow(). + + if (outputTexture) { + render(true); + QRhiCommandBuffer *cb = nullptr; + rhi->beginOffscreenFrame(&cb); + QRhiResourceUpdateBatch *resUpd = rhi->nextResourceUpdateBatch(); + QRhiReadbackResult readResult; + resUpd->readBackTexture(QRhiReadbackDescription(outputTexture), &readResult); + cb->resourceUpdate(resUpd); + rhi->endOffscreenFrame(); + if (!readResult.data.isEmpty()) { + QImage wrapperImage(reinterpret_cast<const uchar *>(readResult.data.constData()), + readResult.pixelSize.width(), readResult.pixelSize.height(), + QImage::Format_RGBA8888_Premultiplied); + if (rhi->isYUpInFramebuffer()) + return wrapperImage.mirrored(); + else + return wrapperImage.copy(); + } + return QImage(); } - - // 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 -#if QT_CONFIG(opengl) - fbo != nullptr ? fbo->toImage() : -#endif - offscreenWindow->grabWindow(); + return offscreenWindow->grabWindow(); } // Intentionally not overriding the QQuickWindow's focusObject. @@ -481,25 +521,48 @@ QImage QQuickWidgetPrivate::grabFramebuffer() size of the view. Alternatively the resizeMode may be set to SizeRootObjectToView which will resize the view to the size of the root object. - \note QQuickWidget is an alternative to using QQuickView and QWidget::createWindowContainer(). + \section1 Performance Considerations + + QQuickWidget is an alternative to using QQuickView and QWidget::createWindowContainer(). The restrictions on stacking order do not apply, making QQuickWidget the more flexible alternative, behaving more like an ordinary widget. - \note However, the above mentioned advantages come at the expense of performance. - Unlike QQuickWindow and QQuickView, QQuickWidget requires rendering into OpenGL - framebuffer objects, which needs to be enforced by calling - QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi) at startup. - This will naturally carry a minor performance hit. + However, the above mentioned advantages come at the expense of performance: + \list + + \li Unlike QQuickWindow and QQuickView, QQuickWidget involves at least one + additional render pass targeting an offscreen color buffer, typically a 2D + texture, followed by drawing a texture quad. This means increased load + especially for the fragment processing of the GPU. - \note Using QQuickWidget disables the threaded render loop on all platforms. This means that - some of the benefits of threaded rendering, for example \l Animator classes and vsync driven - animations, will not be available. + \li Using QQuickWidget disables the \l{threaded_render_loop}{threaded render loop} on all + platforms. This means that some of the benefits of threaded rendering, for example + \l Animator classes and vsync driven animations, will not be available. + \endlist \note Avoid calling winId() on a QQuickWidget. This function triggers the creation of a native window, resulting in reduced performance and possibly rendering glitches. The entire purpose of QQuickWidget is to render Quick scenes without a separate native window, hence making it a native widget should always be avoided. + \section1 Graphics API Support + + QQuickWidget is functional with all the 3D graphics APIs supported by Qt + Quick, as well as the \c software backend. Other backends, for example + OpenVG, are not compatible however and attempting to construct a + QQuickWidget will lead to problems. + + Overriding the platform's default graphics API is done the same way as with + QQuickWindow and QQuickView: either by calling + QQuickWindow::setGraphicsApi() early on before constructing the first + QQuickWidget, or by setting the \c{QSG_RHI_BACKEND} environment variable. + + \note One top-level window can only use one single graphics API for + rendering. For example, attempting to place a QQuickWidget using Vulkan and + a QOpenGLWidget in the widget hierarchy of the same top-level window, + problems will occur and one of the widgets will not be rendering as + expected. + \section1 Scene Graph and Context Persistency QQuickWidget honors QQuickWindow::isPersistentSceneGraph(), meaning that @@ -509,20 +572,19 @@ QImage QQuickWidgetPrivate::grabFramebuffer() related resources be released whenever the widget becomes hidden. By default persistency is enabled, just like with QQuickWindow. - When running with the OpenGL backend of the scene graph, QQuickWindow - offers the possibility to disable persistent OpenGL contexts as well. This - setting is currently ignored by QQuickWidget and the context is always - persistent. The OpenGL context is thus not destroyed when hiding the - widget. The context is destroyed only when the widget is destroyed or when - the widget gets reparented into another top-level widget's child hierarchy. - However, some applications, in particular those that have their own - graphics resources due to performing custom OpenGL rendering in the Qt - Quick scene, may wish to disable the latter since they may not be prepared - to handle the loss of the context when moving a QQuickWidget into another - window. Such applications can set the - QCoreApplication::AA_ShareOpenGLContexts attribute. For a discussion on the - details of resource initialization and cleanup, refer to the QOpenGLWidget - documentation. + When running with the OpenGL, QQuickWindow offers the possibility to + disable persistent OpenGL contexts as well. This setting is currently + ignored by QQuickWidget and the context is always persistent. The OpenGL + context is thus not destroyed when hiding the widget. The context is + destroyed only when the widget is destroyed or when the widget gets + reparented into another top-level widget's child hierarchy. However, some + applications, in particular those that have their own graphics resources + due to performing custom OpenGL rendering in the Qt Quick scene, may wish + to disable the latter since they may not be prepared to handle the loss of + the context when moving a QQuickWidget into another window. Such + applications can set the QCoreApplication::AA_ShareOpenGLContexts + attribute. For a discussion on the details of resource initialization and + cleanup, refer to the QOpenGLWidget documentation. \note QQuickWidget offers less fine-grained control over its internal OpenGL context than QOpenGLWidget, and there are subtle differences, most @@ -550,13 +612,6 @@ QImage QQuickWidgetPrivate::grabFramebuffer() Qt::WA_TranslucentBackground on the top-level window, request an alpha channel, and change the Qt Quick Scenegraph's clear color to Qt::transparent via setClearColor(). - \section1 Support when not using OpenGL - - In addition to OpenGL, the \c software backend of Qt Quick also supports - QQuickWidget. Other backends, for example OpenVG, are not - compatible however and attempting to construct a QQuickWidget will lead to - problems. - \section1 Tab Key Handling On press of the \c[TAB] key, the item inside the QQuickWidget gets focus. If @@ -573,23 +628,22 @@ QImage QQuickWidgetPrivate::grabFramebuffer() */ /*! - Constructs a QQuickWidget with the given \a parent. - The default value of \a parent is 0. + Constructs a QQuickWidget with a default QML engine as a child of \a parent. + The default value of \a parent is \c nullptr. */ QQuickWidget::QQuickWidget(QWidget *parent) : QWidget(*(new QQuickWidgetPrivate), parent, {}) { - setMouseTracking(true); - setFocusPolicy(Qt::StrongFocus); d_func()->init(); } /*! - Constructs a QQuickWidget with the given QML \a source and \a parent. - The default value of \a parent is 0. + Constructs a QQuickWidget with a default QML engine and the given QML \a source + as a child of \a parent. -*/ + The default value of \a parent is \c nullptr. + */ QQuickWidget::QQuickWidget(const QUrl &source, QWidget *parent) : QQuickWidget(parent) { @@ -597,19 +651,15 @@ QQuickWidget::QQuickWidget(const QUrl &source, QWidget *parent) } /*! - Constructs a QQuickWidget with the given QML \a engine and \a parent. + Constructs a QQuickWidget with the given QML \a engine as a child of \a parent. - Note: In this case, the QQuickWidget does not own the given \a engine object; + \note The QQuickWidget does not take ownership of the given \a engine object; it is the caller's responsibility to destroy the engine. If the \a engine is deleted - before the view, status() will return QQuickWidget::Error. - - \sa Status, status(), errors() + before the view, \l status() will return \l QQuickWidget::Error. */ QQuickWidget::QQuickWidget(QQmlEngine* engine, QWidget *parent) : QWidget(*(new QQuickWidgetPrivate), parent, {}) { - setMouseTracking(true); - setFocusPolicy(Qt::StrongFocus); d_func()->init(engine); } @@ -623,6 +673,16 @@ QQuickWidget::~QQuickWidget() Q_D(QQuickWidget); delete d->root; d->root = nullptr; + + if (d->rhi) + d->rhi->removeCleanupCallback(this); + + // 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. + d->destroy(); } /*! @@ -927,7 +987,7 @@ void QQuickWidgetPrivate::handleContextCreationFailure(const QSurfaceFormat &) QString translatedMessage; QString untranslatedMessage; - QQuickWindowPrivate::rhiCreationFailureMessage(QLatin1String("OpenGL"), &translatedMessage, &untranslatedMessage); + QQuickWindowPrivate::rhiCreationFailureMessage(QLatin1String("QRhi"), &translatedMessage, &untranslatedMessage); static const QMetaMethod errorSignal = QMetaMethod::fromSignal(&QQuickWidget::sceneGraphError); const bool signalConnected = q->isSignalConnected(errorSignal); @@ -942,74 +1002,100 @@ void QQuickWidgetPrivate::handleContextCreationFailure(const QSurfaceFormat &) qFatal("%s", qPrintable(untranslatedMessage)); } +static inline QPlatformBackingStoreRhiConfig::Api graphicsApiToBackingStoreRhiApi(QSGRendererInterface::GraphicsApi api) +{ + switch (api) { + case QSGRendererInterface::OpenGL: + return QPlatformBackingStoreRhiConfig::OpenGL; + case QSGRendererInterface::Vulkan: + return QPlatformBackingStoreRhiConfig::Vulkan; + case QSGRendererInterface::Direct3D11: + return QPlatformBackingStoreRhiConfig::D3D11; + case QSGRendererInterface::Direct3D12: + return QPlatformBackingStoreRhiConfig::D3D12; + case QSGRendererInterface::Metal: + return QPlatformBackingStoreRhiConfig::Metal; + default: + return QPlatformBackingStoreRhiConfig::Null; + } +} + // Never called by Software Rendering backend -void QQuickWidgetPrivate::createContext() +void QQuickWidgetPrivate::initializeWithRhi() { -#if QT_CONFIG(opengl) Q_Q(QQuickWidget); + // when reparenting, the rhi may suddenly be different + if (rhi) { + QRhi *backingStoreRhi = QWidgetPrivate::rhi(); + if (backingStoreRhi && rhi != backingStoreRhi) + rhi = nullptr; + } + // 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->isSceneGraphInitialized(); + const bool onlyNeedsSgInit = rhi && !offscreenWindow->isSceneGraphInitialized(); - if (!reinit) { - if (context) + if (!onlyNeedsSgInit) { + if (rhi) return; - context = new QOpenGLContext; - context->setFormat(offscreenWindow->requestedFormat()); - const QWindow *win = q->window()->windowHandle(); - if (win && win->screen()) - context->setScreen(win->screen()); - QOpenGLContext *shareContext = qt_gl_global_share_context(); - if (!shareContext) - shareContext = QWidgetPrivate::get(q->window())->shareContext(); - if (shareContext) { - context->setShareContext(shareContext); - context->setScreen(shareContext->screen()); + if (QRhi *backingStoreRhi = QWidgetPrivate::rhi()) { + rhi = backingStoreRhi; + // We don't own the RHI, so make sure we clean up if it goes away + rhi->addCleanupCallback(q, [this](QRhi *rhi) { + if (this->rhi == rhi) { + invalidateRenderControl(); + deviceLost = true; + this->rhi = nullptr; + } + }); } - if (!context->create()) { - delete context; - context = nullptr; - handleContextCreationFailure(offscreenWindow->requestedFormat()); - return; + + if (!rhi) { + // The widget (and its parent chain, if any) may not be shown at + // all, yet one may still want to use it for grabs. This is + // ridiculous of course because the rendering infrastructure is + // tied to the top-level widget that initializes upon expose, but + // it has to be supported. + offscreenRenderer.setConfig(rhiConfig()); + offscreenRenderer.setFormat(q->format()); + // no window passed in, so no swapchain, but we get a functional QRhi which we own + if (offscreenRenderer.create()) + rhi = offscreenRenderer.rhi(); } - offscreenSurface = new QOffscreenSurface; - // Pass the context's format(), which, now that the underlying platform context is created, - // contains a QSurfaceFormat representing the _actual_ format of the underlying - // configuration. This is essential to get a surface that is compatible with the context. - offscreenSurface->setFormat(context->format()); - offscreenSurface->setScreen(context->screen()); - offscreenSurface->create(); + // 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() != QBackingStoreRhiSupport::apiToRhiBackend(graphicsApiToBackingStoreRhiApi(QQuickWindow::graphicsApi()))) { + qWarning("The top-level window is not using the expected graphics API for composition, " + "'%s' is not compatible with this QQuickWidget", + rhi->backendName()); + rhi = nullptr; + } } - if (context->makeCurrent(offscreenSurface)) { + if (rhi) { if (!offscreenWindow->isSceneGraphInitialized()) { - offscreenWindow->setGraphicsDevice(QQuickGraphicsDevice::fromOpenGLContext(context)); + offscreenWindow->setGraphicsDevice(QQuickGraphicsDevice::fromRhi(rhi)); +#if QT_CONFIG(vulkan) + if (QWindow *w = q->window()->windowHandle()) + offscreenWindow->setVulkanInstance(w->vulkanInstance()); + else if (rhi == offscreenRenderer.rhi()) + offscreenWindow->setVulkanInstance(QVulkanDefaultInstance::instance()); +#endif renderControl->initialize(); } - } else -#endif - qWarning("QQuickWidget: Failed to make context current"); -} - -// Never called by Software Rendering backend -void QQuickWidgetPrivate::destroyContext() -{ -#if QT_CONFIG(opengl) - delete context; - context = nullptr; -#endif - delete offscreenSurface; - offscreenSurface = nullptr; + } else { + qWarning("QQuickWidget: Failed to get a QRhi from the top-level widget's window"); + } } void QQuickWidget::createFramebufferObject() { Q_D(QQuickWidget); - // Could come from Show -> createContext -> sceneGraphInitialized in which case the size may + // Could come from Show -> initializeWithRhi -> sceneGraphInitialized in which case the size may // still be invalid on some platforms. Bail out. A resize will come later on. if (size().isEmpty()) return; @@ -1028,82 +1114,85 @@ void QQuickWidget::createFramebufferObject() return; } -#if QT_CONFIG(opengl) - if (!d->context) { - qWarning("QQuickWidget: Attempted to create FBO with no context"); - return; - } - - QOpenGLContext *shareWindowContext = QWidgetPrivate::get(window())->shareContext(); - 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(d->context->screen()); - d->offscreenSurface->create(); - } - - bool current = d->context->makeCurrent(d->offscreenSurface); - if (!current) { - qWarning("QQuickWidget: Failed to make context current when creating FBO"); + if (!d->rhi) { + qWarning("QQuickWidget: Attempted to create output texture with no QRhi"); return; } int samples = d->requestedSamples; - if (!QOpenGLExtensions(d->context).hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) + if (d->rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer)) + samples = QSGRhiSupport::chooseSampleCount(samples, d->rhi); + else samples = 0; - QOpenGLFramebufferObjectFormat format; - format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); - format.setSamples(samples); - - // 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_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() * devicePixelRatio(); - // Could be a simple hide - show, in which case the previous fbo is just fine. - if (!d->fbo || d->fbo->size() != fboSize) { - delete d->fbo; - d->fbo = new QOpenGLFramebufferObject(fboSize, format); + // Could be a simple hide - show, in which case the previous texture is just fine. + if (!d->outputTexture) { + d->outputTexture = d->rhi->newTexture(QRhiTexture::RGBA8, fboSize, 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource); + if (!d->outputTexture->create()) { + qWarning("QQuickWidget: failed to create output texture of size %dx%d", + fboSize.width(), fboSize.height()); + } } - - // 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_MACOS - if (format.internalTextureFormat() == GL_SRGB8_ALPHA8_EXT) { - 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); + if (!d->depthStencil) { + d->depthStencil = d->rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, fboSize, samples); + if (!d->depthStencil->create()) { + qWarning("QQuickWidget: failed to create depth/stencil buffer of size %dx%d and sample count %d", + fboSize.width(), fboSize.height(), samples); + } + } + if (samples > 1 && !d->msaaBuffer) { + d->msaaBuffer = d->rhi->newRenderBuffer(QRhiRenderBuffer::Color, fboSize, samples); + if (!d->msaaBuffer->create()) { + qWarning("QQuickWidget: failed to create multisample renderbuffer of size %dx%d and sample count %d", + fboSize.width(), fboSize.height(), samples); + } + } + if (!d->rt) { + QRhiTextureRenderTargetDescription rtDesc; + QRhiColorAttachment colorAtt; + if (samples <= 1) { + colorAtt.setTexture(d->outputTexture); + } else { + colorAtt.setRenderBuffer(d->msaaBuffer); + colorAtt.setResolveTexture(d->outputTexture); + } + rtDesc.setColorAttachments({ colorAtt }); + rtDesc.setDepthStencilBuffer(d->depthStencil); + d->rt = d->rhi->newTextureRenderTarget(rtDesc); + d->rtRp = d->rt->newCompatibleRenderPassDescriptor(); + d->rt->setRenderPassDescriptor(d->rtRp); + d->rt->create(); + } + if (d->outputTexture->pixelSize() != fboSize) { + d->outputTexture->setPixelSize(fboSize); + if (!d->outputTexture->create()) { + qWarning("QQuickWidget: failed to create resized output texture of size %dx%d", + fboSize.width(), fboSize.height()); + } + d->depthStencil->setPixelSize(fboSize); + if (!d->depthStencil->create()) { + qWarning("QQuickWidget: failed to create resized depth/stencil buffer of size %dx%d", + fboSize.width(), fboSize.height()); + } + if (d->msaaBuffer) { + d->msaaBuffer->setPixelSize(fboSize); + if (!d->msaaBuffer->create()) { + qWarning("QQuickWidget: failed to create resized multisample renderbuffer of size %dx%d", + fboSize.width(), fboSize.height()); + } + } } -#endif - GLuint textureId = d->fbo->texture(); - d->offscreenWindow->setRenderTarget( QQuickRenderTarget::fromOpenGLTexture(textureId, fboSize, samples)); + d->offscreenWindow->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(d->rt)); d->renderControl->setSamples(samples); - if (samples > 0) - d->resolvedFbo = new QOpenGLFramebufferObject(fboSize); - // Sanity check: The window must not have an underlying platform window. // Having one would mean create() was called and platforms that only support // a single native window were in trouble. Q_ASSERT(!d->offscreenWindow->handle()); -#endif } void QQuickWidget::destroyFramebufferObject() @@ -1115,12 +1204,16 @@ void QQuickWidget::destroyFramebufferObject() return; } -#if QT_CONFIG(opengl) - delete d->fbo; - d->fbo = nullptr; - delete d->resolvedFbo; - d->resolvedFbo = nullptr; -#endif + delete d->rt; + d->rt = nullptr; + delete d->rtRp; + d->rtRp = nullptr; + delete d->depthStencil; + d->depthStencil = nullptr; + delete d->msaaBuffer; + d->msaaBuffer = nullptr; + delete d->outputTexture; + d->outputTexture = nullptr; } QQuickWidget::ResizeMode QQuickWidget::resizeMode() const @@ -1198,17 +1291,35 @@ void QQuickWidgetPrivate::setRootObject(QObject *obj) } } -#if QT_CONFIG(opengl) -GLuint QQuickWidgetPrivate::textureId() const +QPlatformBackingStoreRhiConfig QQuickWidgetPrivate::rhiConfig() const +{ + const_cast<QQuickWidgetPrivate *>(this)->ensureBackingScene(); + if (useSoftwareRenderer) + return {}; + + QPlatformBackingStoreRhiConfig config(graphicsApiToBackingStoreRhiApi(QQuickWindow::graphicsApi())); + + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(offscreenWindow); + // This is only here to support some of the env.vars. (such as + // QSG_RHI_DEBUG_LAYER). There is currently no way to set a + // QQuickGraphicsConfiguration for a QQuickWidget, which means things like + // the pipeline cache are just not available. That is something to support + // on the widget/backingstore level since that's where the QRhi is + // controlled in this case. + const bool debugLayerRequested = wd->graphicsConfig.isDebugLayerEnabled(); + config.setDebugLayer(debugLayerRequested); + return config; +} + +QWidgetPrivate::TextureData QQuickWidgetPrivate::texture() const { Q_Q(const QQuickWidget); if (!q->isWindow() && q->internalWinId()) { qWarning() << "QQuickWidget cannot be used as a native child widget." << "Consider setting Qt::AA_DontCreateNativeWidgetSiblings"; - return 0; + return {}; } - return resolvedFbo ? resolvedFbo->texture() - : (fbo ? fbo->texture() : 0); + return { outputTexture, nullptr }; } QPlatformTextureList::Flags QQuickWidgetPrivate::textureListFlags() @@ -1217,7 +1328,6 @@ QPlatformTextureList::Flags QQuickWidgetPrivate::textureListFlags() flags |= QPlatformTextureList::NeedsPremultipliedAlphaBlending; return flags; } -#endif /*! \internal @@ -1307,13 +1417,12 @@ void QQuickWidget::resizeEvent(QResizeEvent *e) createFramebufferObject(); } } else { -#if QT_CONFIG(opengl) - if (d->context) { + if (d->rhi) { // 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->isSceneGraphInitialized()) + if (!d->outputTexture && !d->offscreenWindow->isSceneGraphInitialized()) return; - if (!d->fbo || d->fbo->size() != size() * devicePixelRatio()) { + if (!d->outputTexture || d->outputTexture->pixelSize() != size() * devicePixelRatio()) { needsSync = true; createFramebufferObject(); } @@ -1321,14 +1430,13 @@ void QQuickWidget::resizeEvent(QResizeEvent *e) // This will result in a scenegraphInitialized() signal which // is connected to createFramebufferObject(). needsSync = true; - d->createContext(); + d->initializeWithRhi(); } - if (!d->context) { - qWarning("QQuickWidget::resizeEvent() no OpenGL context"); + if (!d->rhi) { + qWarning("QQuickWidget::resizeEvent() no QRhi"); return; } -#endif } d->render(needsSync); @@ -1383,6 +1491,8 @@ void QQuickWidget::mouseMoveEvent(QMouseEvent *e) // top-level window always. QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(), e->button(), e->buttons(), e->modifiers(), e->source()); + // It's not just the timestamp but also the globalPressPosition, velocity etc. + mappedEvent.setTimestamp(e->timestamp()); QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent); e->setAccepted(mappedEvent.isAccepted()); } @@ -1398,10 +1508,12 @@ void QQuickWidget::mouseDoubleClickEvent(QMouseEvent *e) // See QTBUG-25831 QMouseEvent pressEvent(QEvent::MouseButtonPress, e->position(), e->position(), e->globalPosition(), e->button(), e->buttons(), e->modifiers(), e->source()); + pressEvent.setTimestamp(e->timestamp()); QCoreApplication::sendEvent(d->offscreenWindow, &pressEvent); e->setAccepted(pressEvent.isAccepted()); QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(), e->button(), e->buttons(), e->modifiers(), e->source()); + mappedEvent.setTimestamp(e->timestamp()); QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent); } @@ -1412,7 +1524,7 @@ void QQuickWidget::showEvent(QShowEvent *) bool shouldTriggerUpdate = true; if (!d->useSoftwareRenderer) { - d->createContext(); + d->initializeWithRhi(); if (d->offscreenWindow->isSceneGraphInitialized()) { shouldTriggerUpdate = false; @@ -1434,7 +1546,7 @@ void QQuickWidget::showEvent(QShowEvent *) if (shouldTriggerUpdate) triggerUpdate(); - // note offscreenWindow is "QQuickOffScreenWindow" instance + // note offscreenWindow is "QQuickWidgetOffscreenWindow" instance d->offscreenWindow->setVisible(true); if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>()) service->setParentWindow(d->offscreenWindow, window()->windowHandle()); @@ -1446,7 +1558,7 @@ void QQuickWidget::hideEvent(QHideEvent *) Q_D(QQuickWidget); if (!d->offscreenWindow->isPersistentSceneGraph()) d->invalidateRenderControl(); - // note offscreenWindow is "QQuickOffScreenWindow" instance + // note offscreenWindow is "QQuickWidgetOffscreenWindow" instance d->offscreenWindow->setVisible(false); if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>()) service->setParentWindow(d->offscreenWindow, d->offscreenWindow); @@ -1461,6 +1573,7 @@ void QQuickWidget::mousePressEvent(QMouseEvent *e) QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(), e->button(), e->buttons(), e->modifiers(), e->source()); + mappedEvent.setTimestamp(e->timestamp()); QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent); e->setAccepted(mappedEvent.isAccepted()); } @@ -1474,6 +1587,7 @@ void QQuickWidget::mouseReleaseEvent(QMouseEvent *e) QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(), e->button(), e->buttons(), e->modifiers(), e->source()); + mappedEvent.setTimestamp(e->timestamp()); QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent); e->setAccepted(mappedEvent.isAccepted()); } @@ -1556,9 +1670,23 @@ bool QQuickWidget::event(QEvent *e) case QEvent::TouchBegin: case QEvent::TouchEnd: case QEvent::TouchUpdate: - case QEvent::TouchCancel: + case QEvent::TouchCancel: { // Touch events only have local and global positions, no need to map. - return QCoreApplication::sendEvent(d->offscreenWindow, e); + bool res = QCoreApplication::sendEvent(d->offscreenWindow, e); + if (e->isAccepted() && e->type() == QEvent::TouchBegin) { + // If the TouchBegin got accepted, then make sure all points that have + // an exclusive grabber are also accepted so that the widget code for + // delivering touch events make this widget an implicit grabber of those + // points. + QPointerEvent *pointerEvent = static_cast<QPointerEvent *>(e); + auto deliveredPoints = pointerEvent->points(); + for (auto &point : deliveredPoints) { + if (pointerEvent->exclusiveGrabber(point) || !pointerEvent->passiveGrabbers(point).isEmpty()) + point.setAccepted(true); + } + } + return res; + } case QEvent::FocusAboutToChange: return QCoreApplication::sendEvent(d->offscreenWindow, e); @@ -1576,36 +1704,37 @@ bool QQuickWidget::event(QEvent *e) return eventResult; } + case QEvent::WindowAboutToChangeInternal: + if (d->rhi) + d->rhi->removeCleanupCallback(this); + d->invalidateRenderControl(); + d->deviceLost = true; + d->rhi = nullptr; + break; + case QEvent::WindowChangeInternal: d->handleWindowChange(); break; case QEvent::ScreenChangeInternal: - if (QWindow *window = this->window()->windowHandle()) { - QScreen *newScreen = window->screen(); - - if (d->offscreenWindow) - d->offscreenWindow->setScreen(newScreen); - if (d->offscreenSurface) - d->offscreenSurface->setScreen(newScreen); -#if QT_CONFIG(opengl) - if (d->context) - d->context->setScreen(newScreen); -#endif - } - - if (d->useSoftwareRenderer -#if QT_CONFIG(opengl) - || d->fbo -#endif - ) { + { + QScreen *newScreen = screen(); + if (d->offscreenWindow) + d->offscreenWindow->setScreen(newScreen); + break; + } + case QEvent::DevicePixelRatioChange: + if (d->useSoftwareRenderer || d->outputTexture) { // This will check the size taking the devicePixelRatio into account // and recreate if needed. createFramebufferObject(); d->render(true); } + if (d->offscreenWindow) { + QEvent dprChangeEvent(QEvent::DevicePixelRatioChange); + QGuiApplication::sendEvent(d->offscreenWindow, &dprChangeEvent); + } break; - case QEvent::Show: case QEvent::Move: d->updatePosition(); @@ -1820,10 +1949,8 @@ void QQuickWidget::propagateFocusObjectChanged(QObject *focusObject) emit window->focusObjectChanged(focusObject); } -#if QT_CONFIG(opengl) -Q_CONSTRUCTOR_FUNCTION(qt_registerDefaultPlatformBackingStoreOpenGLSupport); -#endif - QT_END_NAMESPACE +#include "moc_qquickwidget_p.cpp" + #include "moc_qquickwidget.cpp" |