summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/aspects/qabstractaspect.cpp2
-rw-r--r--src/extras/shaders/es2/phong.inc.frag1008
-rw-r--r--src/input/backend/mouseeventfilter.cpp1
-rw-r--r--src/input/frontend/qmousehandler.cpp7
-rw-r--r--src/quick3d/imports/scene3d/importsscene3d.pro2
-rw-r--r--src/quick3d/imports/scene3d/scene3dcleaner.cpp75
-rw-r--r--src/quick3d/imports/scene3d/scene3dcleaner_p.h82
-rw-r--r--src/quick3d/imports/scene3d/scene3ditem.cpp120
-rw-r--r--src/quick3d/imports/scene3d/scene3ditem_p.h10
-rw-r--r--src/quick3d/imports/scene3d/scene3drenderer.cpp43
-rw-r--r--src/quick3d/imports/scene3d/scene3drenderer_p.h15
-rw-r--r--src/render/materialsystem/shaderbuilder.cpp2
-rw-r--r--src/render/renderers/opengl/renderer/renderer.cpp8
-rw-r--r--src/render/renderers/opengl/renderer/renderview.cpp2
-rw-r--r--tests/manual/manual.pro2
-rw-r--r--tests/manual/quickwidget-switch/main.cpp125
-rw-r--r--tests/manual/quickwidget-switch/main.qml152
-rw-r--r--tests/manual/quickwidget-switch/quickwidget-switch.pro13
-rw-r--r--tests/manual/quickwidget-switch/quickwidget-switch.qrc5
-rw-r--r--tests/manual/quickwindow-switch/main.cpp68
-rw-r--r--tests/manual/quickwindow-switch/main.qml181
-rw-r--r--tests/manual/quickwindow-switch/quickwindow-switch.pro13
-rw-r--r--tests/manual/quickwindow-switch/quickwindow-switch.qrc5
23 files changed, 710 insertions, 231 deletions
diff --git a/src/core/aspects/qabstractaspect.cpp b/src/core/aspects/qabstractaspect.cpp
index 6e0d3bd02..f7248f180 100644
--- a/src/core/aspects/qabstractaspect.cpp
+++ b/src/core/aspects/qabstractaspect.cpp
@@ -247,6 +247,8 @@ void QAbstractAspectPrivate::syncDirtyFrontEndSubNodes(const QVector<NodeRelatio
for (const auto &nodeChange: qAsConst(nodes)) {
auto getBackend = [this](QNode *node) -> std::tuple<QBackendNode *, bool> {
const QMetaObject *metaObj = QNodePrivate::get(node)->m_typeInfo;
+ if (!metaObj)
+ return {};
const BackendNodeMapperAndInfo backendNodeMapperInfo = mapperForNode(metaObj);
const QBackendNodeMapperPtr backendNodeMapper = backendNodeMapperInfo.first;
diff --git a/src/extras/shaders/es2/phong.inc.frag100 b/src/extras/shaders/es2/phong.inc.frag100
index 0c326d0b6..c68c8e41a 100644
--- a/src/extras/shaders/es2/phong.inc.frag100
+++ b/src/extras/shaders/es2/phong.inc.frag100
@@ -66,8 +66,8 @@ void adsModel(const in FP vec3 vpos, const in FP vec3 vnormal, const in FP vec3
if ( lights[0].type != TYPE_DIRECTIONAL ) {
s = lights[0].position - vpos;
if (lights[0].constantAttenuation != 0.0
- || light[0].linearAttenuation != 0.0
- || light[0].quadraticAttenuation != 0.0) {
+ || lights[0].linearAttenuation != 0.0
+ || lights[0].quadraticAttenuation != 0.0) {
FP float dist = length(s);
att = 1.0 / (lights[0].constantAttenuation + lights[0].linearAttenuation * dist + lights[0].quadraticAttenuation * dist * dist);
}
@@ -99,8 +99,8 @@ void adsModel(const in FP vec3 vpos, const in FP vec3 vnormal, const in FP vec3
if ( lights[1].type != TYPE_DIRECTIONAL ) {
s = lights[1].position - vpos;
if (lights[1].constantAttenuation != 0.0
- || light[1].linearAttenuation != 0.0
- || light[1].quadraticAttenuation != 0.0) {
+ || lights[1].linearAttenuation != 0.0
+ || lights[1].quadraticAttenuation != 0.0) {
FP float dist = length(s);
att = 1.0 / (lights[1].constantAttenuation + lights[1].linearAttenuation * dist + lights[1].quadraticAttenuation * dist * dist);
}
diff --git a/src/input/backend/mouseeventfilter.cpp b/src/input/backend/mouseeventfilter.cpp
index 48b30725a..baa78ac25 100644
--- a/src/input/backend/mouseeventfilter.cpp
+++ b/src/input/backend/mouseeventfilter.cpp
@@ -77,6 +77,7 @@ bool MouseEventFilter::eventFilter(QObject *obj, QEvent *e)
}
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
// Creates copy and store event to be processed later on in an InputAspect job
m_inputHandler->appendMouseEvent(QMouseEvent(*static_cast<QMouseEvent *>(e)));
diff --git a/src/input/frontend/qmousehandler.cpp b/src/input/frontend/qmousehandler.cpp
index 89d76a057..068fe1964 100644
--- a/src/input/frontend/qmousehandler.cpp
+++ b/src/input/frontend/qmousehandler.cpp
@@ -58,7 +58,7 @@ QMouseHandlerPrivate::QMouseHandlerPrivate()
{
m_shareable = false;
m_pressAndHoldTimer->setSingleShot(true);
- m_pressAndHoldTimer->setInterval(500);
+ m_pressAndHoldTimer->setInterval(800);
QObject::connect(m_pressAndHoldTimer, &QTimer::timeout, [this] {
emit q_func()->pressAndHold(m_lastPressedEvent.data());
});
@@ -77,15 +77,15 @@ void QMouseHandlerPrivate::mouseEvent(const QMouseEventPtr &event)
{
Q_Q(QMouseHandler);
switch (event->type()) {
- case QEvent::MouseButtonPress: {
+ case QEvent::MouseButtonPress:
m_lastPressedEvent = event;
m_pressAndHoldTimer->start();
emit q->pressed(event.data());
break;
- }
case QEvent::MouseButtonRelease:
m_pressAndHoldTimer->stop();
emit q->released(event.data());
+ emit q->clicked(event.data());
break;
#if QT_CONFIG(gestures)
case QEvent::Gesture:
@@ -96,6 +96,7 @@ void QMouseHandlerPrivate::mouseEvent(const QMouseEventPtr &event)
emit q->doubleClicked(event.data());
break;
case QEvent::MouseMove:
+ m_pressAndHoldTimer->stop();
emit q->positionChanged(event.data());
break;
default:
diff --git a/src/quick3d/imports/scene3d/importsscene3d.pro b/src/quick3d/imports/scene3d/importsscene3d.pro
index e29f7b831..c4c1b7cc8 100644
--- a/src/quick3d/imports/scene3d/importsscene3d.pro
+++ b/src/quick3d/imports/scene3d/importsscene3d.pro
@@ -13,7 +13,6 @@ HEADERS += \
qtquickscene3dplugin.h \
scene3dlogging_p.h \
scene3ditem_p.h \
- scene3dcleaner_p.h \
scene3drenderer_p.h \
scene3dsgnode_p.h \
scene3dsgmaterialshader_p.h \
@@ -24,7 +23,6 @@ SOURCES += \
qtquickscene3dplugin.cpp \
scene3ditem.cpp \
scene3dlogging.cpp \
- scene3dcleaner.cpp \
scene3drenderer.cpp \
scene3dsgnode.cpp \
scene3dsgmaterialshader.cpp \
diff --git a/src/quick3d/imports/scene3d/scene3dcleaner.cpp b/src/quick3d/imports/scene3d/scene3dcleaner.cpp
deleted file mode 100644
index ec371410d..000000000
--- a/src/quick3d/imports/scene3d/scene3dcleaner.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt3D module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "scene3dcleaner_p.h"
-
-#include <Qt3DCore/qaspectengine.h>
-#include <QtCore/qthread.h>
-
-#include <scene3dlogging_p.h>
-#include <scene3drenderer_p.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace Qt3DRender {
-
-Scene3DCleaner::Scene3DCleaner(QObject *parent)
- : QObject(parent)
- , m_renderer(nullptr)
-{
-}
-
-Scene3DCleaner::~Scene3DCleaner()
-{
- qCDebug(Scene3D) << Q_FUNC_INFO << QThread::currentThread();
-}
-
-void Scene3DCleaner::cleanup()
-{
- Q_ASSERT(m_renderer);
- delete m_renderer->m_aspectEngine; // also deletes m_renderer->m_renderAspect
- m_renderer->m_aspectEngine = nullptr;
- m_renderer->m_renderAspect = nullptr;
- m_renderer->deleteLater();
- deleteLater();
-}
-
-} // namespace Qt3DRender
-
-QT_END_NAMESPACE
diff --git a/src/quick3d/imports/scene3d/scene3dcleaner_p.h b/src/quick3d/imports/scene3d/scene3dcleaner_p.h
deleted file mode 100644
index a246cbde7..000000000
--- a/src/quick3d/imports/scene3d/scene3dcleaner_p.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt3D 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$
-**
-****************************************************************************/
-
-#ifndef QT3DRENDER_SCENE3DCLEANER_P_H
-#define QT3DRENDER_SCENE3DCLEANER_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/QObject>
-
-QT_BEGIN_NAMESPACE
-
-namespace Qt3DRender {
-
-class Scene3DRenderer;
-
-class Scene3DCleaner : public QObject
-{
- Q_OBJECT
-public:
- explicit Scene3DCleaner(QObject *parent = 0);
- ~Scene3DCleaner();
-
- void setRenderer(Scene3DRenderer *renderer) { m_renderer = renderer; }
-
-public Q_SLOTS:
- void cleanup();
-
-private:
- Scene3DRenderer *m_renderer;
-};
-
-} // namespace Qt3DRender
-
-QT_END_NAMESPACE
-
-#endif // QT3DRENDER_SCENE3DCLEANER_H
diff --git a/src/quick3d/imports/scene3d/scene3ditem.cpp b/src/quick3d/imports/scene3d/scene3ditem.cpp
index f824d2c4e..6a1085a85 100644
--- a/src/quick3d/imports/scene3d/scene3ditem.cpp
+++ b/src/quick3d/imports/scene3d/scene3ditem.cpp
@@ -68,7 +68,6 @@
#include <Qt3DRender/private/qrendersurfaceselector_p.h>
#include <Qt3DRender/private/qrenderaspect_p.h>
#include <Qt3DRender/private/rendersettings_p.h>
-#include <scene3dcleaner_p.h>
#include <scene3dlogging_p.h>
#include <scene3drenderer_p.h>
#include <scene3dsgnode_p.h>
@@ -150,8 +149,8 @@ Scene3DItem::Scene3DItem(QQuickItem *parent)
, m_viewHolderFG(nullptr)
, m_aspectEngine(new Qt3DCore::QAspectEngine())
, m_renderAspect(nullptr)
+ , m_aspectToDelete(nullptr)
, m_renderer(nullptr)
- , m_rendererCleaner(new Scene3DCleaner())
, m_multisample(true)
, m_dirty(true)
, m_dirtyViews(false)
@@ -179,6 +178,8 @@ Scene3DItem::~Scene3DItem()
// When the window is closed, it first destroys all of its children. At
// this point, Scene3DItem is destroyed but the Renderer, AspectEngine and
// Scene3DSGNode still exist and will perform their cleanup on their own.
+ m_aspectEngine->deleteLater();
+ m_renderer->deleteLater();
}
/*!
@@ -204,18 +205,11 @@ QStringList Scene3DItem::aspects() const
*/
Qt3DCore::QEntity *Scene3DItem::entity() const
{
- return m_entity;
+ return m_entity.data();
}
-void Scene3DItem::setAspects(const QStringList &aspects)
+void Scene3DItem::applyAspects()
{
- if (!m_aspects.isEmpty()) {
- qCWarning(Scene3D) << "Aspects already set on the Scene3D, ignoring";
- return;
- }
-
- m_aspects = aspects;
-
// Aspects are owned by the aspect engine
for (const QString &aspect : qAsConst(m_aspects)) {
if (aspect == QLatin1String("render")) // This one is hardwired anyway
@@ -246,16 +240,27 @@ void Scene3DItem::setAspects(const QStringList &aspects)
}
m_aspectEngine->registerAspect(aspect);
}
+}
+
+void Scene3DItem::setAspects(const QStringList &aspects)
+{
+ if (!m_aspects.isEmpty()) {
+ qWarning() << "Aspects already set on the Scene3D, ignoring";
+ return;
+ }
+
+ m_aspects = aspects;
+ applyAspects();
emit aspectsChanged();
}
void Scene3DItem::setEntity(Qt3DCore::QEntity *entity)
{
- if (entity == m_entity)
+ if (entity == m_entity.data())
return;
- m_entity = entity;
+ m_entity.reset(entity);
emit entityChanged();
}
@@ -398,14 +403,21 @@ void Scene3DItem::removeView(Scene3DView *view)
void Scene3DItem::applyRootEntityChange()
{
- if (m_aspectEngine->rootEntity() != m_entity) {
- m_aspectEngine->setRootEntity(Qt3DCore::QEntityPtr(m_entity));
+ if (m_aspectEngine->rootEntity() != m_entity.data()) {
+ m_aspectEngine->setRootEntity(m_entity);
+
+ /* If we changed window, the old aspect engine must be deleted only after we have set
+ the root entity for the new one so that it doesn't delete the root node. */
+ if (m_aspectToDelete) {
+ delete m_aspectToDelete;
+ m_aspectToDelete = nullptr;
+ }
// Set the render surface
if (!m_entity)
return;
- setWindowSurface(m_entity);
+ setWindowSurface(entity());
if (m_cameraAspectRatioMode == AutomaticAspectRatio) {
// Set aspect ratio of first camera to match the window
@@ -481,6 +493,8 @@ void Scene3DItem::onBeforeSync()
// if the Scene3D item is not visible
if (!isVisible() && dontRenderWhenHidden)
return;
+ if (m_renderer->m_resetRequested)
+ return;
Q_ASSERT(QThread::currentThread() == thread());
@@ -496,7 +510,7 @@ void Scene3DItem::onBeforeSync()
// Make renderer aware of any Scene3DView we are dealing with
if (m_dirtyViews) {
// Scene3DViews checks
- if (m_entity != m_viewHolderEntity) {
+ if (entity() != m_viewHolderEntity) {
qCWarning(Scene3D) << "Scene3DView is not supported if the Scene3D entity property has been set";
}
if (!usesFBO) {
@@ -544,6 +558,20 @@ void Scene3DItem::requestUpdate()
}
}
+void Scene3DItem::updateWindowSurface()
+{
+ if (!m_entity || !m_dummySurface)
+ return;
+ Qt3DRender::QRenderSurfaceSelector *surfaceSelector =
+ Qt3DRender::QRenderSurfaceSelectorPrivate::find(entity());
+ if (surfaceSelector) {
+ if (QWindow *rw = QQuickRenderControl::renderWindowFor(this->window())) {
+ m_dummySurface->deleteLater();
+ createDummySurface(rw, surfaceSelector);
+ }
+ }
+}
+
void Scene3DItem::setWindowSurface(QObject *rootObject)
{
Qt3DRender::QRenderSurfaceSelector *surfaceSelector = Qt3DRender::QRenderSurfaceSelectorPrivate::find(rootObject);
@@ -554,20 +582,25 @@ void Scene3DItem::setWindowSurface(QObject *rootObject)
// We may not have a real, exposed QQuickWindow when the Quick rendering
// is redirected via QQuickRenderControl (f.ex. QQuickWidget).
if (QWindow *rw = QQuickRenderControl::renderWindowFor(this->window())) {
- // rw is the top-level window that is backed by a native window. Do
- // not use that though since we must not clash with e.g. the widget
- // backingstore compositor in the gui thread.
- m_dummySurface = new QOffscreenSurface;
- m_dummySurface->setParent(qGuiApp); // parent to something suitably long-living
- m_dummySurface->setFormat(rw->format());
- m_dummySurface->setScreen(rw->screen());
- m_dummySurface->create();
- surfaceSelector->setSurface(m_dummySurface);
+ createDummySurface(rw, surfaceSelector);
} else {
surfaceSelector->setSurface(this->window());
}
}
}
+
+void Scene3DItem::createDummySurface(QWindow *rw, Qt3DRender::QRenderSurfaceSelector *surfaceSelector)
+{
+ // rw is the top-level window that is backed by a native window. Do
+ // not use that though since we must not clash with e.g. the widget
+ // backingstore compositor in the gui thread.
+ m_dummySurface = new QOffscreenSurface;
+ m_dummySurface->setParent(qGuiApp); // parent to something suitably long-living
+ m_dummySurface->setFormat(rw->format());
+ m_dummySurface->setScreen(rw->screen());
+ m_dummySurface->create();
+ surfaceSelector->setSurface(m_dummySurface);
+}
/*!
\qmlmethod void Scene3D::setItemAreaAndDevicePixelRatio(size area, real devicePixelRatio)
@@ -575,7 +608,8 @@ void Scene3DItem::setWindowSurface(QObject *rootObject)
*/
void Scene3DItem::setItemAreaAndDevicePixelRatio(QSize area, qreal devicePixelRatio)
{
- Qt3DRender::QRenderSurfaceSelector *surfaceSelector = Qt3DRender::QRenderSurfaceSelectorPrivate::find(m_entity);
+ Qt3DRender::QRenderSurfaceSelector *surfaceSelector
+ = Qt3DRender::QRenderSurfaceSelectorPrivate::find(entity());
if (surfaceSelector) {
surfaceSelector->setExternalRenderTargetSize(area);
surfaceSelector->setSurfacePixelRatio(devicePixelRatio);
@@ -665,6 +699,22 @@ void Scene3DItem::setMultisample(bool enable)
QSGNode *Scene3DItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *)
{
+ // m_resetRequested is set to true by Scene3DRenderer::shutdown()
+ if (m_renderer && m_renderer->m_resetRequested) {
+ qCWarning(Scene3D) << "Renderer for Scene3DItem has requested a reset due to the item "
+ "moving to another window";
+ QObject::disconnect(m_windowConnection);
+ m_aspectEngine->unregisterAspect(m_renderAspect); // Deletes the renderAspect
+ m_renderAspect = nullptr;
+ m_aspectToDelete = m_aspectEngine;
+ m_aspectEngine = new Qt3DCore::QAspectEngine();
+ m_aspectEngine->setRunMode(Qt3DCore::QAspectEngine::Manual);
+ applyAspects();
+ // Needs to belong in the same thread as the item which is the same as the original
+ // QAspectEngine
+ m_aspectEngine->moveToThread(thread());
+ m_renderer->m_resetRequested = false;
+ }
// If the render aspect wasn't created yet, do so now
if (m_renderAspect == nullptr) {
m_renderAspect = new QRenderAspect(QRenderAspect::Synchronous);
@@ -675,16 +725,24 @@ QSGNode *Scene3DItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNode
// Before Synchronizing is in the SG Thread, we want beforeSync to be triggered
// in the context of the main thread
- QObject::connect(window(), &QQuickWindow::afterAnimating,
- this, &Scene3DItem::onBeforeSync, Qt::DirectConnection);
+ m_windowConnection = QObject::connect(window(), &QQuickWindow::afterAnimating,
+ this, &Scene3DItem::onBeforeSync, Qt::DirectConnection);
auto renderAspectPriv = static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect));
QObject::connect(renderAspectPriv->m_aspectManager->changeArbiter(), &Qt3DCore::QChangeArbiter::receivedChange,
this, [this] { m_dirty = true; }, Qt::DirectConnection);
}
if (m_renderer == nullptr) {
- m_renderer = new Scene3DRenderer(this, m_aspectEngine, m_renderAspect);
- m_renderer->setCleanerHelper(m_rendererCleaner);
+ m_renderer = new Scene3DRenderer();
+ m_renderer->init(this, m_aspectEngine, m_renderAspect);
+ } else if (m_renderer->renderAspect() != m_renderAspect) {
+ // If the renderer's renderAspect is not equal to the aspect used
+ // by the item, then it means that we have created a new one due to
+ // the fact that shutdown() was called on the renderer previously.
+ // This is a typical situation when the window the item is in has
+ // moved from one screen to another.
+ updateWindowSurface();
+ m_renderer->init(this, m_aspectEngine, m_renderAspect);
}
const bool usesFBO = m_compositingMode == FBO;
const bool hasScene3DViews = !m_views.empty();
diff --git a/src/quick3d/imports/scene3d/scene3ditem_p.h b/src/quick3d/imports/scene3d/scene3ditem_p.h
index e46bb20af..0beaf94c0 100644
--- a/src/quick3d/imports/scene3d/scene3ditem_p.h
+++ b/src/quick3d/imports/scene3d/scene3ditem_p.h
@@ -71,6 +71,7 @@ class Scene3DRenderer;
class Scene3DCleaner;
class Scene3DView;
class QFrameGraphNode;
+class QRenderSurfaceSelector;
class Scene3DItem : public QQuickItem
{
@@ -138,16 +139,20 @@ private:
void updateCameraAspectRatio();
void mousePressEvent(QMouseEvent *event) override;
bool needsRender();
+ void updateWindowSurface();
+ void createDummySurface(QWindow *window, QRenderSurfaceSelector *surfaceSelector);
+ void applyAspects();
QStringList m_aspects;
- Qt3DCore::QEntity *m_entity;
+ // Store as shared pointer so that aspect engine doesn't delete it.
+ QSharedPointer<Qt3DCore::QEntity> m_entity;
Qt3DCore::QEntity *m_viewHolderEntity;
Qt3DRender::QFrameGraphNode *m_viewHolderFG;
Qt3DCore::QAspectEngine *m_aspectEngine;
+ Qt3DCore::QAspectEngine *m_aspectToDelete;
QRenderAspect *m_renderAspect;
Scene3DRenderer *m_renderer;
- Scene3DCleaner *m_rendererCleaner;
bool m_multisample;
bool m_dirty;
@@ -160,6 +165,7 @@ private:
CompositingMode m_compositingMode;
QOffscreenSurface *m_dummySurface;
QVector<Scene3DView *> m_views;
+ QMetaObject::Connection m_windowConnection;
};
} // Qt3DRender
diff --git a/src/quick3d/imports/scene3d/scene3drenderer.cpp b/src/quick3d/imports/scene3d/scene3drenderer.cpp
index 312dc41cb..f28633c88 100644
--- a/src/quick3d/imports/scene3d/scene3drenderer.cpp
+++ b/src/quick3d/imports/scene3d/scene3drenderer.cpp
@@ -53,7 +53,6 @@
#include <Qt3DCore/private/qchangearbiter_p.h>
#include <Qt3DCore/private/qservicelocator_p.h>
-#include <scene3dcleaner_p.h>
#include <scene3ditem_p.h>
#include <scene3dlogging_p.h>
#include <scene3dsgnode_p.h>
@@ -144,16 +143,15 @@ private:
signal of the window is not called. Therefore the cleanup method is invoked
to properly destroy the aspect engine.
*/
-Scene3DRenderer::Scene3DRenderer(Scene3DItem *item, Qt3DCore::QAspectEngine *aspectEngine, QRenderAspect *renderAspect)
+Scene3DRenderer::Scene3DRenderer()
: QObject()
- , m_item(item)
- , m_aspectEngine(aspectEngine)
- , m_renderAspect(renderAspect)
+ , m_item(nullptr)
+ , m_aspectEngine(nullptr)
+ , m_renderAspect(nullptr)
, m_multisampledFBO(nullptr)
, m_finalFBO(nullptr)
, m_texture(nullptr)
, m_node(nullptr)
- , m_cleaner(nullptr)
, m_window(nullptr)
, m_multisample(false) // this value is not used, will be synced from the Scene3DItem instead
, m_lastMultisample(false)
@@ -165,6 +163,17 @@ Scene3DRenderer::Scene3DRenderer(Scene3DItem *item, Qt3DCore::QAspectEngine *asp
, m_allowRendering(0)
, m_compositingMode(Scene3DItem::FBO)
{
+}
+
+void Scene3DRenderer::init(Scene3DItem *item, Qt3DCore::QAspectEngine *aspectEngine,
+ QRenderAspect *renderAspect)
+{
+ m_item = item;
+ m_window = m_item->window();
+ m_aspectEngine = aspectEngine;
+ m_renderAspect = renderAspect;
+ m_needsShutdown = true;
+
Q_CHECK_PTR(m_item);
Q_CHECK_PTR(m_item->window());
@@ -215,21 +224,14 @@ void Scene3DRenderer::scheduleRootEntityChange()
QMetaObject::invokeMethod(m_item, "applyRootEntityChange", Qt::QueuedConnection);
}
-void Scene3DRenderer::setCleanerHelper(Scene3DCleaner *cleaner)
-{
- m_cleaner = cleaner;
- if (m_cleaner) {
- // Window closed case
- QObject::connect(m_item->window(), &QQuickWindow::destroyed, m_cleaner, &Scene3DCleaner::cleanup);
- m_cleaner->setRenderer(this);
- }
-}
-
// Executed in the QtQuick render thread (which may even be the gui/main with QQuickWidget / RenderControl).
void Scene3DRenderer::shutdown()
{
qCDebug(Scene3D) << Q_FUNC_INFO << QThread::currentThread();
+ // In case the same item is rendered on another window reset it
+ m_resetRequested = true;
+
// Set to null so that subsequent calls to render
// would return early
m_item = nullptr;
@@ -243,8 +245,13 @@ void Scene3DRenderer::shutdown()
// Shutdown the Renderer Aspect while the OpenGL context
// is still valid
- if (m_renderAspect)
+ if (m_renderAspect) {
static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->renderShutdown();
+ m_renderAspect = nullptr;
+ }
+ m_aspectEngine = nullptr;
+ m_finalFBO.reset();
+ m_multisampledFBO.reset();
}
// QtQuick render thread (which may also be the gui/main thread with QQuickWidget / RenderControl)
@@ -254,7 +261,6 @@ void Scene3DRenderer::onSceneGraphInvalidated()
if (m_needsShutdown) {
m_needsShutdown = false;
shutdown();
- QMetaObject::invokeMethod(m_cleaner, "cleanup");
}
}
@@ -265,7 +271,6 @@ void Scene3DRenderer::onWindowChanged(QQuickWindow *w)
if (m_needsShutdown) {
m_needsShutdown = false;
shutdown();
- QMetaObject::invokeMethod(m_cleaner, "cleanup");
}
}
}
diff --git a/src/quick3d/imports/scene3d/scene3drenderer_p.h b/src/quick3d/imports/scene3d/scene3drenderer_p.h
index 08a2c60a3..5674f21c2 100644
--- a/src/quick3d/imports/scene3d/scene3drenderer_p.h
+++ b/src/quick3d/imports/scene3d/scene3drenderer_p.h
@@ -78,9 +78,7 @@ class Scene3DRenderer : public QObject
{
Q_OBJECT
public:
- Scene3DRenderer(Scene3DItem *item,
- Qt3DCore::QAspectEngine *aspectEngine,
- QRenderAspect *renderAspect);
+ Scene3DRenderer();
~Scene3DRenderer();
void setSGNode(Scene3DSGNode *node);
@@ -89,7 +87,12 @@ public:
void setCompositingMode(Scene3DItem::CompositingMode mode);
void setSkipFrame(bool skip);
void setScene3DViews(const QVector<Scene3DView *> views);
+ void init(Scene3DItem *item, Qt3DCore::QAspectEngine *aspectEngine, QRenderAspect *renderAspect);
+ QRenderAspect *renderAspect() const
+ {
+ return m_renderAspect;
+ }
public Q_SLOTS:
void render();
void shutdown();
@@ -103,13 +106,12 @@ private:
void scheduleRootEntityChange();
Scene3DItem *m_item; // Will be released by the QQuickWindow/QML Engine
- Qt3DCore::QAspectEngine *m_aspectEngine; // Will be released by the Scene3DRendererCleaner
+ Qt3DCore::QAspectEngine *m_aspectEngine; // Will be released by the Scene3DItem
QRenderAspect *m_renderAspect; // Will be released by the aspectEngine
QScopedPointer<QOpenGLFramebufferObject> m_multisampledFBO;
QScopedPointer<QOpenGLFramebufferObject> m_finalFBO;
QScopedPointer<QSGTexture> m_texture;
Scene3DSGNode *m_node; // Will be released by the QtQuick SceneGraph
- Scene3DCleaner *m_cleaner;
QQuickWindow *m_window;
QMutex m_windowMutex;
QSize m_lastSize;
@@ -123,8 +125,9 @@ private:
QSemaphore m_allowRendering;
Scene3DItem::CompositingMode m_compositingMode;
QVector<Scene3DView *> m_views;
+ bool m_resetRequested = false;
- friend class Scene3DCleaner;
+ friend class Scene3DItem;
};
} // namespace Qt3DRender
diff --git a/src/render/materialsystem/shaderbuilder.cpp b/src/render/materialsystem/shaderbuilder.cpp
index 7434cd901..8efd8db8f 100644
--- a/src/render/materialsystem/shaderbuilder.cpp
+++ b/src/render/materialsystem/shaderbuilder.cpp
@@ -278,7 +278,7 @@ void ShaderBuilder::syncFromFrontEnd(const QNode *frontEnd, bool firstTime)
markDirty(AbstractRenderer::ShadersDirty);
}
- static const QVector<std::pair<QShaderProgram::ShaderType, QUrl (QShaderProgramBuilder::*)() const>> shaderTypesToGetters = {
+ static const QVarLengthArray<std::pair<QShaderProgram::ShaderType, QUrl (QShaderProgramBuilder::*)() const>, 6> shaderTypesToGetters {
{QShaderProgram::Vertex, &QShaderProgramBuilder::vertexShaderGraph},
{QShaderProgram::TessellationControl, &QShaderProgramBuilder::tessellationControlShaderGraph},
{QShaderProgram::TessellationEvaluation, &QShaderProgramBuilder::tessellationEvaluationShaderGraph},
diff --git a/src/render/renderers/opengl/renderer/renderer.cpp b/src/render/renderers/opengl/renderer/renderer.cpp
index aced13fc5..cbb4751f9 100644
--- a/src/render/renderers/opengl/renderer/renderer.cpp
+++ b/src/render/renderers/opengl/renderer/renderer.cpp
@@ -308,13 +308,13 @@ Renderer::Renderer(QRenderAspect::RenderType type)
// Create jobs to update transforms and bounding volumes
// We can only update bounding volumes once all world transforms are known
+ m_calculateBoundingVolumeJob->addDependency(m_updateTreeEnabledJob);
m_updateWorldBoundingVolumeJob->addDependency(m_worldTransformJob);
m_updateWorldBoundingVolumeJob->addDependency(m_calculateBoundingVolumeJob);
m_expandBoundingVolumeJob->addDependency(m_updateWorldBoundingVolumeJob);
m_updateShaderDataTransformJob->addDependency(m_worldTransformJob);
m_pickBoundingVolumeJob->addDependency(m_expandBoundingVolumeJob);
m_rayCastingJob->addDependency(m_expandBoundingVolumeJob);
- // m_calculateBoundingVolumeJob's dependency on m_updateTreeEnabledJob is set in renderBinJobs
// Ensures all skeletons are loaded before we try to update them
m_updateSkinningPaletteJob->addDependency(m_syncLoadingJobs);
@@ -1840,12 +1840,8 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
// Add jobs
const bool entitiesEnabledDirty = dirtyBitsForFrame & AbstractRenderer::EntityEnabledDirty;
- if (entitiesEnabledDirty) {
+ if (entitiesEnabledDirty)
renderBinJobs.push_back(m_updateTreeEnabledJob);
- // This dependency is added here because we clear all dependencies
- // at the start of this function.
- m_calculateBoundingVolumeJob->addDependency(m_updateTreeEnabledJob);
- }
if (dirtyBitsForFrame & AbstractRenderer::TransformDirty) {
renderBinJobs.push_back(m_worldTransformJob);
diff --git a/src/render/renderers/opengl/renderer/renderview.cpp b/src/render/renderers/opengl/renderer/renderview.cpp
index ef15d7fcd..65e65da3d 100644
--- a/src/render/renderers/opengl/renderer/renderview.cpp
+++ b/src/render/renderers/opengl/renderer/renderview.cpp
@@ -431,6 +431,7 @@ struct SubRangeSorter<QSortPolicy::Texture>
{
static void sortSubRange(CommandIt begin, const CommandIt end)
{
+#ifndef Q_OS_WIN
std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) {
QVector<ShaderParameterPack::NamedResource> texturesA = a.m_parameterPack.textures();
QVector<ShaderParameterPack::NamedResource> texturesB = b.m_parameterPack.textures();
@@ -449,6 +450,7 @@ struct SubRangeSorter<QSortPolicy::Texture>
return identicalTextureCount < originalTextureASize;
});
+#endif
}
};
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index 2f630e0d9..de9dd861b 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -88,3 +88,5 @@ qtHaveModule(widgets): {
rendercapture-cpp \
texture-updates-cpp
}
+
+qtHaveModule(quickwidgets): SUBDIRS += quickwidget-switch
diff --git a/tests/manual/quickwidget-switch/main.cpp b/tests/manual/quickwidget-switch/main.cpp
new file mode 100644
index 000000000..ebadc458d
--- /dev/null
+++ b/tests/manual/quickwidget-switch/main.cpp
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QDesktopWidget>
+#include <QScreen>
+#include <QApplication>
+#include <QMainWindow>
+#include <QMdiArea>
+#include <QMdiSubWindow>
+#include <QQuickWidget>
+#include <QVBoxLayout>
+#include <QPushButton>
+
+void configureMainWindow(QMainWindow *w, QMdiArea *mdiArea, QPushButton *button)
+{
+ auto widget = new QWidget;
+ auto layout = new QVBoxLayout;
+ layout->addWidget(mdiArea);
+ layout->addWidget(button);
+ widget->setLayout(layout);
+ w->setCentralWidget(widget);
+}
+
+int main(int argc, char* argv[])
+{
+ QApplication app(argc, argv);
+
+ QMainWindow w1;
+ w1.winId();
+ w1.windowHandle()->setScreen(QGuiApplication::screens().at(0));
+ auto mdiArea1 = new QMdiArea;
+ auto button1 = new QPushButton("Switch to this window");
+ configureMainWindow(&w1, mdiArea1, button1);
+ w1.setGeometry(0, 0, 800, 800);
+ w1.show();
+
+ QMainWindow w2;
+ w2.winId();
+ w2.windowHandle()->setScreen(QGuiApplication::screens().at(1));
+ auto mdiArea2 = new QMdiArea;
+ auto button2 = new QPushButton("Switch to this window");
+ configureMainWindow(&w2, mdiArea2, button2);
+ w2.setGeometry(0, 0, 800, 800);
+ w2.show();
+
+ QMdiSubWindow* subWindow = new QMdiSubWindow();
+
+ QQuickWidget *quickWidget = new QQuickWidget();
+ quickWidget->resize(QSize(400, 400));
+ quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ quickWidget->setSource(QUrl("qrc:/main.qml"));
+
+ subWindow->setWidget(quickWidget);
+
+ QObject::connect(button1, &QPushButton::clicked,
+ [mdiArea1, mdiArea2, subWindow, button1, button2]() {
+ mdiArea2->removeSubWindow(subWindow);
+ mdiArea1->addSubWindow(subWindow);
+ subWindow->show();
+ button1->setEnabled(false);
+ button2->setEnabled(true);
+ });
+
+ QObject::connect(button2, &QPushButton::clicked,
+ [mdiArea1, mdiArea2, subWindow, button1, button2]() {
+ mdiArea1->removeSubWindow(subWindow);
+ mdiArea2->addSubWindow(subWindow);
+ subWindow->show();
+ button1->setEnabled(true);
+ button2->setEnabled(false);
+ });
+
+ mdiArea2->addSubWindow(subWindow);
+ button2->setEnabled(false);
+ subWindow->show();
+
+ return app.exec();
+}
diff --git a/tests/manual/quickwidget-switch/main.qml b/tests/manual/quickwidget-switch/main.qml
new file mode 100644
index 000000000..d4bce020c
--- /dev/null
+++ b/tests/manual/quickwidget-switch/main.qml
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.2 as QQ2
+import Qt3D.Core 2.0
+import Qt3D.Render 2.0
+import Qt3D.Input 2.0
+import Qt3D.Extras 2.0
+
+import QtQuick.Scene3D 2.0
+
+QQ2.Item {
+ id: mioitem
+ width: 300
+ height: 300
+ Scene3D {
+ id: scene3d
+ anchors.fill: parent
+ Entity {
+ id: sceneRoot
+
+ Camera {
+ id: camera
+ projectionType: CameraLens.PerspectiveProjection
+ fieldOfView: 45
+ aspectRatio: 16/9
+ nearPlane : 0.1
+ farPlane : 1000.0
+ position: Qt.vector3d( 0.0, 0.0, -40.0 )
+ upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
+ viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
+ }
+
+ OrbitCameraController {
+ camera: camera
+ }
+
+ components: [
+ RenderSettings {
+ activeFrameGraph: ForwardRenderer {
+ clearColor: Qt.rgba(0, 0.5, 1, 1)
+ camera: camera
+ }
+ },
+ // Event Source will be set by the Qt3DQuickWindow
+ InputSettings { }
+ ]
+
+ PhongMaterial {
+ id: material
+ }
+
+ TorusMesh {
+ id: torusMesh
+ radius: 5
+ minorRadius: 1
+ rings: 100
+ slices: 20
+ }
+
+ Transform {
+ id: torusTransform
+ scale3D: Qt.vector3d(1.5, 1, 0.5)
+ rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), 45)
+ }
+
+ Entity {
+ id: torusEntity
+ components: [ torusMesh, material, torusTransform ]
+ }
+
+ SphereMesh {
+ id: sphereMesh
+ radius: 3
+ }
+
+ Transform {
+ id: sphereTransform
+ property real userAngle: 0.0
+ matrix: {
+ var m = Qt.matrix4x4();
+ m.rotate(userAngle, Qt.vector3d(0, 1, 0));
+ m.translate(Qt.vector3d(20, 0, 0));
+ return m;
+ }
+ }
+
+ QQ2.NumberAnimation {
+ target: sphereTransform
+ property: "userAngle"
+ duration: 10000
+ from: 0
+ to: 360
+
+ loops: QQ2.Animation.Infinite
+ running: true
+ }
+
+ Entity {
+ id: sphereEntity
+ components: [ sphereMesh, material, sphereTransform ]
+ }
+ }
+ }
+}
diff --git a/tests/manual/quickwidget-switch/quickwidget-switch.pro b/tests/manual/quickwidget-switch/quickwidget-switch.pro
new file mode 100644
index 000000000..2f1cb98f5
--- /dev/null
+++ b/tests/manual/quickwidget-switch/quickwidget-switch.pro
@@ -0,0 +1,13 @@
+TEMPLATE = app
+
+QT += 3dextras
+CONFIG += resources_big
+
+QT += 3dcore 3drender 3dinput 3dquick 3dlogic qml quick 3dquickextras widgets quickwidgets
+
+SOURCES += \
+ main.cpp
+
+RESOURCES += \
+ quickwidget-switch.qrc
+
diff --git a/tests/manual/quickwidget-switch/quickwidget-switch.qrc b/tests/manual/quickwidget-switch/quickwidget-switch.qrc
new file mode 100644
index 000000000..5f6483ac3
--- /dev/null
+++ b/tests/manual/quickwidget-switch/quickwidget-switch.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/quickwindow-switch/main.cpp b/tests/manual/quickwindow-switch/main.cpp
new file mode 100644
index 000000000..7bebe0c75
--- /dev/null
+++ b/tests/manual/quickwindow-switch/main.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char* argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ const QUrl url(QStringLiteral("qrc:/main.qml"));
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
+ &app, [url](QObject *obj, const QUrl &objUrl) {
+ if (!obj && url == objUrl)
+ QCoreApplication::exit(-1);
+ }, Qt::QueuedConnection);
+ engine.load(url);
+
+ return app.exec();
+}
diff --git a/tests/manual/quickwindow-switch/main.qml b/tests/manual/quickwindow-switch/main.qml
new file mode 100644
index 000000000..4b474bd11
--- /dev/null
+++ b/tests/manual/quickwindow-switch/main.qml
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.2
+import QtQuick.Window 2.12 as Win
+import Qt3D.Core 2.0
+import Qt3D.Render 2.0
+import Qt3D.Input 2.0
+import Qt3D.Extras 2.0
+
+import QtQuick.Scene3D 2.0
+
+Win.Window {
+ id: win
+ width: 300
+ height: 350
+ visible: true
+ x: Win.Screen.width / 2 - width / 2
+ y: Win.Screen.height / 2 - height / 2
+ Item {
+ id: mioitem
+ width: 300
+ height: 300
+ Scene3D {
+ id: scene3d
+ anchors.fill: parent
+ Entity {
+ id: sceneRoot
+
+ Camera {
+ id: camera
+ projectionType: CameraLens.PerspectiveProjection
+ fieldOfView: 45
+ aspectRatio: 16/9
+ nearPlane : 0.1
+ farPlane : 1000.0
+ position: Qt.vector3d( 0.0, 0.0, -40.0 )
+ upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
+ viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
+ }
+
+ OrbitCameraController {
+ camera: camera
+ }
+
+ components: [
+ RenderSettings {
+ activeFrameGraph: ForwardRenderer {
+ clearColor: Qt.rgba(0, 0.5, 1, 1)
+ camera: camera
+ }
+ },
+ // Event Source will be set by the Qt3DQuickWindow
+ InputSettings { }
+ ]
+
+ PhongMaterial {
+ id: material
+ }
+
+ TorusMesh {
+ id: torusMesh
+ radius: 5
+ minorRadius: 1
+ rings: 100
+ slices: 20
+ }
+
+ Transform {
+ id: torusTransform
+ scale3D: Qt.vector3d(1.5, 1, 0.5)
+ rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), 45)
+ }
+
+ Entity {
+ id: torusEntity
+ components: [ torusMesh, material, torusTransform ]
+ }
+
+ SphereMesh {
+ id: sphereMesh
+ radius: 3
+ }
+
+ Transform {
+ id: sphereTransform
+ property real userAngle: 0.0
+ matrix: {
+ var m = Qt.matrix4x4();
+ m.rotate(userAngle, Qt.vector3d(0, 1, 0));
+ m.translate(Qt.vector3d(20, 0, 0));
+ return m;
+ }
+ }
+
+ NumberAnimation {
+ target: sphereTransform
+ property: "userAngle"
+ duration: 10000
+ from: 0
+ to: 360
+
+ loops: Animation.Infinite
+ running: true
+ }
+
+ Entity {
+ id: sphereEntity
+ components: [ sphereMesh, material, sphereTransform ]
+ }
+ }
+ }
+ }
+ Rectangle {
+ height: 50
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ color: "yellow"
+ MouseArea {
+ anchors.fill: parent
+ z: 5
+ onClicked: {
+ win.screen = (win.screen === Qt.application.screens[0] ? Qt.application.screens[1] : Qt.application.screens[0])
+ }
+ }
+ Text {
+ anchors.centerIn: parent
+ minimumPointSize: 12
+ fontSizeMode: Text.Fit
+ text: "Move to the other screen"
+ }
+ }
+}
diff --git a/tests/manual/quickwindow-switch/quickwindow-switch.pro b/tests/manual/quickwindow-switch/quickwindow-switch.pro
new file mode 100644
index 000000000..2f338d36d
--- /dev/null
+++ b/tests/manual/quickwindow-switch/quickwindow-switch.pro
@@ -0,0 +1,13 @@
+TEMPLATE = app
+
+QT += 3dextras
+CONFIG += resources_big
+
+QT += 3dcore 3drender 3dinput 3dquick 3dlogic qml quick 3dquickextras
+
+SOURCES += \
+ main.cpp
+
+RESOURCES += \
+ quickwindow-switch.qrc
+
diff --git a/tests/manual/quickwindow-switch/quickwindow-switch.qrc b/tests/manual/quickwindow-switch/quickwindow-switch.qrc
new file mode 100644
index 000000000..5f6483ac3
--- /dev/null
+++ b/tests/manual/quickwindow-switch/quickwindow-switch.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ </qresource>
+</RCC>