aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc6
-rw-r--r--examples/quick/scenegraph/metaltextureimport/doc/images/metaltextureimport-example.jpgbin0 -> 44545 bytes
-rw-r--r--examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc101
-rw-r--r--examples/quick/scenegraph/metaltextureimport/main.cpp70
-rw-r--r--examples/quick/scenegraph/metaltextureimport/main.qml123
-rw-r--r--examples/quick/scenegraph/metaltextureimport/metaltextureimport.h88
-rw-r--r--examples/quick/scenegraph/metaltextureimport/metaltextureimport.mm424
-rw-r--r--examples/quick/scenegraph/metaltextureimport/metaltextureimport.pro10
-rw-r--r--examples/quick/scenegraph/metaltextureimport/metaltextureimport.qrc7
-rw-r--r--examples/quick/scenegraph/metaltextureimport/squircle.frag29
-rw-r--r--examples/quick/scenegraph/metaltextureimport/squircle.vert23
-rw-r--r--examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc7
-rw-r--r--examples/quick/scenegraph/metalunderqml/metalsquircle.mm5
-rw-r--r--examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc7
-rw-r--r--examples/quick/scenegraph/rendernode/customrenderitem.cpp58
-rw-r--r--examples/quick/scenegraph/rendernode/d3d12renderer.cpp28
-rw-r--r--examples/quick/scenegraph/rendernode/d3d12renderer.h11
-rw-r--r--examples/quick/scenegraph/rendernode/metalrenderer.h13
-rw-r--r--examples/quick/scenegraph/rendernode/metalrenderer.mm27
-rw-r--r--examples/quick/scenegraph/rendernode/openglrenderer.cpp17
-rw-r--r--examples/quick/scenegraph/rendernode/openglrenderer.h8
-rw-r--r--examples/quick/scenegraph/rendernode/softwarerenderer.cpp26
-rw-r--r--examples/quick/scenegraph/rendernode/softwarerenderer.h7
-rw-r--r--examples/quick/scenegraph/scenegraph.pro8
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/doc/images/vulkanunderqml-example.jpgbin0 -> 39962 bytes
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/doc/src/vulkanunderqml.qdoc68
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/main.cpp70
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/main.qml89
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/squircle.frag.spvbin0 -> 1432 bytes
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/squircle.vert.spvbin0 -> 644 bytes
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp623
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/vulkansquircle.h86
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.pro8
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.qrc7
-rw-r--r--src/imports/testlib/main.cpp2
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h13
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp31
-rw-r--r--src/qml/jsruntime/qv4qmlcontext_p.h1
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp2
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp2
-rw-r--r--src/qml/qml/qqml.h4
-rw-r--r--src/qml/qml/qqmlglobal_p.h8
-rw-r--r--src/qml/qml/qqmlmetatype.cpp13
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h13
-rw-r--r--src/qml/qml/qqmltype.cpp65
-rw-r--r--src/qml/qml/qqmltype_p.h4
-rw-r--r--src/qml/qml/qqmltype_p_p.h22
-rw-r--r--src/qml/qml/qqmltypecompiler.cpp14
-rw-r--r--src/qml/qml/qqmltypeloader.cpp4
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp56
-rw-r--r--src/qml/qml/qqmltypewrapper_p.h2
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp48
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc13
-rw-r--r--src/quick/doc/src/examples.qdoc7
-rw-r--r--src/quick/items/qquickwindow.cpp21
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendererinterface.cpp4
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendererinterface.h3
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext.cpp3
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext_p.h6
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp4
-rw-r--r--src/quick/scenegraph/qsgrhisupport.cpp18
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp4
-rw-r--r--tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt1
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/data/multiSingleton.qml6
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton.qml16
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton2.qml6
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/internal/InternalType.qml6
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/moduleWithQmlSingleton.pro18
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp50
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/qmldir2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro3
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp15
-rw-r--r--tests/auto/qml/qqmltranslation/data/mylibrary.js5
-rw-r--r--tests/auto/qml/qqmltranslation/data/nested_js_translation.js3
-rw-r--r--tests/auto/qml/qqmltranslation/data/preferjs.qml8
-rw-r--r--tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp23
76 files changed, 2394 insertions, 209 deletions
diff --git a/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc b/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc
index d7b60d3b81..1f0c15e1c6 100644
--- a/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc
+++ b/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc
@@ -51,8 +51,8 @@
program that draws the squircles.
The example is equivalent in most ways to the \l{Scene Graph - OpenGL Under
- QML}{OpenGL Under QML} and \l{Scene Graph - Metal Under QML}{Metal Under
- QML} examples, they all render the same custom content, just via different
- native APIs.
+ QML}{OpenGL Under QML}, \l{Scene Graph - Metal Under QML}{Metal Under QML},
+ and \l{Scene Graph - Vulkan Under QML}{Vulkan Under QML} examples, they all
+ render the same custom content, just via different native APIs.
*/
diff --git a/examples/quick/scenegraph/metaltextureimport/doc/images/metaltextureimport-example.jpg b/examples/quick/scenegraph/metaltextureimport/doc/images/metaltextureimport-example.jpg
new file mode 100644
index 0000000000..19ad40cd85
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/doc/images/metaltextureimport-example.jpg
Binary files differ
diff --git a/examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc b/examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc
new file mode 100644
index 0000000000..2a584c26cc
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example scenegraph/metaltextureimport
+ \title Scene Graph - Metal Texture Import
+ \ingroup qtquickexamples
+ \brief Shows how to use a texture created directly with Metal
+
+ \image metaltextureimport-example.jpg
+
+ The Metal Texture Import example shows how an application can import and
+ use a
+ \l{https://developer.apple.com/documentation/metal/mtltexture}{MTLTexture}
+ in the Qt Quick scene. This provides an alternative to the \l{Scene Graph -
+ Metal Under QML}{underlay}, overlay, or \l{Scene Graph - Custom Rendering
+ with QSGRenderNode}{render node} approaches when it comes to integrating
+ native Metal rendering. In many cases going through a texture, and
+ therefore "flattening" the 3D contents first, is the best option to
+ integrate and mix custom 3D contents with the 2D UI elements provided by Qt
+ Quick.
+
+ \snippet scenegraph/metaltextureimport/main.qml 1
+ \snippet scenegraph/metaltextureimport/main.qml 2
+
+ The application exposes a custom QQuickItem subclass under ther name of
+ CustomTextureItem. This is instantiated in QML. The value of the \c t
+ property is animated as well.
+
+ \snippet scenegraph/metaltextureimport/metaltextureimport.h 1
+
+ The implementation of our custom item involves overriding
+ QQuickItem::updatePaintNode(), as well as functions and slots related to
+ geometry changes and cleanup.
+
+ \snippet scenegraph/metaltextureimport/metaltextureimport.mm 1
+
+ We also need a scenegraph node. Instead of deriving directly from QSGNode,
+ we can use QSGSimpleTextureNode which gives us some of the functionality
+ pre-implemented as a convenience.
+
+ \snippet scenegraph/metaltextureimport/metaltextureimport.mm 2
+
+ The updatePaintNode() function of the item is called on the render thread
+ (if there is one), with the main (gui) thread blocked. Here we create a new
+ node if there has not yet been one, and update it. Accessing Qt objects
+ living on the main thread is safe here, so sync() will calculate and copy
+ the values it needs from QQuickItem or QQuickWindow.
+
+ \snippet scenegraph/metaltextureimport/metaltextureimport.mm 3
+
+ The node does not merely rely on the typical QQuickItem - QSGNode
+ update sequence, it connects to QQuickWindow::beforeRendering() as
+ well. That is where the contents of the Metal texture will be updated by
+ encoding a full render pass, targeting the texture, on the Qt Quicks
+ scenegraph's command buffer. beforeRendering() is the right place for this,
+ because the signal is emitted before Qt Quick starts to encode its own
+ rendering commands. Choosing QQuickWindow::beforeRenderPassRecording()
+ instead would be an error in this exanple.
+
+ \snippet scenegraph/metaltextureimport/metaltextureimport.mm 4
+ \snippet scenegraph/metaltextureimport/metaltextureimport.mm 5
+
+ After copying the values we need, sync() also performs some graphics
+ resource initialization. The MTLDevice is queried from the scenegraph. Once
+ a MTLTexture is available, a QSGTexture wrapping (not owning) it is created
+ via QQuickWindow::createTextureFromNativeObject(). This function is a
+ modern equivalent to QQuickWindow::createTextureFromId() that is not tied
+ to OpenGL. Finally, the QSGTexture is associated with the underlying
+ materials by calling the base class' setTexture() function.
+
+ \snippet scenegraph/metaltextureimport/metaltextureimport.mm 6
+
+ render(), the slot connected to beforeRendering(), encodes the rendering
+ commands using the buffers and pipeline state objects created in sync().
+
+ */
diff --git a/examples/quick/scenegraph/metaltextureimport/main.cpp b/examples/quick/scenegraph/metaltextureimport/main.cpp
new file mode 100644
index 0000000000..c969817e8f
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/main.cpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications 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 <QtQuick/QQuickView>
+#include "metaltextureimport.h"
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+ qmlRegisterType<CustomTextureItem>("MetalTextureImport", 1, 0, "CustomTextureItem");
+
+ QQuickWindow::setSceneGraphBackend(QSGRendererInterface::MetalRhi);
+
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl("qrc:///scenegraph/metaltextureimport/main.qml"));
+ view.resize(400, 400);
+ view.show();
+
+ return app.exec();
+}
diff --git a/examples/quick/scenegraph/metaltextureimport/main.qml b/examples/quick/scenegraph/metaltextureimport/main.qml
new file mode 100644
index 0000000000..facab4e440
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/main.qml
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications 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.0
+//! [1]
+import MetalTextureImport 1.0
+//! [1]
+
+Rectangle {
+ gradient: Gradient {
+ GradientStop { position: 0; color: "steelblue" }
+ GradientStop { position: 1; color: "black" }
+ }
+
+ //! [2]
+ CustomTextureItem {
+ id: renderer
+ anchors.fill: parent
+ anchors.margins: 10
+
+ SequentialAnimation on t {
+ NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad }
+ NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad }
+ loops: Animation.Infinite
+ running: true
+ }
+ //! [2]
+
+ transform: [
+ Rotation { id: rotation; axis.x: 0; axis.z: 0; axis.y: 1; angle: 0; origin.x: renderer.width / 2; origin.y: renderer.height / 2; },
+ Translate { id: txOut; x: -renderer.width / 2; y: -renderer.height / 2 },
+ Scale { id: scale; },
+ Translate { id: txIn; x: renderer.width / 2; y: renderer.height / 2 }
+ ]
+ }
+
+ SequentialAnimation {
+ PauseAnimation { duration: 5000 }
+ ParallelAnimation {
+ NumberAnimation { target: scale; property: "xScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack }
+ NumberAnimation { target: scale; property: "yScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack }
+ }
+ NumberAnimation { target: rotation; property: "angle"; to: 80; duration: 1000; easing.type: Easing.InOutCubic }
+ NumberAnimation { target: rotation; property: "angle"; to: -80; duration: 1000; easing.type: Easing.InOutCubic }
+ NumberAnimation { target: rotation; property: "angle"; to: 0; duration: 1000; easing.type: Easing.InOutCubic }
+ NumberAnimation { target: renderer; property: "opacity"; to: 0.1; duration: 1000; easing.type: Easing.InOutCubic }
+ PauseAnimation { duration: 1000 }
+ NumberAnimation { target: renderer; property: "opacity"; to: 1.0; duration: 1000; easing.type: Easing.InOutCubic }
+ ParallelAnimation {
+ NumberAnimation { target: scale; property: "xScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack }
+ NumberAnimation { target: scale; property: "yScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack }
+ }
+ running: true
+ loops: Animation.Infinite
+ }
+
+ Rectangle {
+ id: labelFrame
+ anchors.margins: -10
+ radius: 5
+ color: "white"
+ border.color: "black"
+ opacity: 0.5
+ anchors.fill: label
+ }
+
+ Text {
+ id: label
+ anchors.bottom: renderer.bottom
+ anchors.left: renderer.left
+ anchors.right: renderer.right
+ anchors.margins: 20
+ wrapMode: Text.WordWrap
+ text: "The squircle, using rendering code borrowed from the metalunderqml example, is rendered into a texture directly with Metal. The MTLTexture is then imported and used in a custom Qt Quick item."
+ }
+}
diff --git a/examples/quick/scenegraph/metaltextureimport/metaltextureimport.h b/examples/quick/scenegraph/metaltextureimport/metaltextureimport.h
new file mode 100644
index 0000000000..afc5aced97
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/metaltextureimport.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications 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$
+**
+****************************************************************************/
+
+#ifndef METALTEXTUREIMPORT_H
+#define METALTEXTUREIMPORT_H
+
+#include <QtQuick/QQuickItem>
+
+class CustomTextureNode;
+
+//! [1]
+class CustomTextureItem : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged)
+
+public:
+ CustomTextureItem();
+
+ qreal t() const { return m_t; }
+ void setT(qreal t);
+
+signals:
+ void tChanged();
+
+protected:
+ QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override;
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+
+private slots:
+ void invalidateSceneGraph();
+
+private:
+ void releaseResources() override;
+
+ CustomTextureNode *m_node = nullptr;
+ qreal m_t = 0;
+};
+//! [1]
+
+#endif // METALTEXTUREIMPORT_H
diff --git a/examples/quick/scenegraph/metaltextureimport/metaltextureimport.mm b/examples/quick/scenegraph/metaltextureimport/metaltextureimport.mm
new file mode 100644
index 0000000000..6bb68dac44
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/metaltextureimport.mm
@@ -0,0 +1,424 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications 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 "metaltextureimport.h"
+#include <QtGui/QScreen>
+#include <QtQuick/QQuickWindow>
+#include <QtQuick/QSGTextureProvider>
+#include <QtQuick/QSGSimpleTextureNode>
+
+#include <Metal/Metal.h>
+
+//! [1]
+class CustomTextureNode : public QSGTextureProvider, public QSGSimpleTextureNode
+{
+ Q_OBJECT
+
+public:
+ CustomTextureNode(QQuickItem *item);
+ ~CustomTextureNode();
+
+ QSGTexture *texture() const override;
+
+ void sync();
+//! [1]
+private slots:
+ void render();
+
+private:
+ enum Stage {
+ VertexStage,
+ FragmentStage
+ };
+ void prepareShader(Stage stage);
+ using FuncAndLib = QPair<id<MTLFunction>, id<MTLLibrary> >;
+ FuncAndLib compileShaderFromSource(const QByteArray &src, const QByteArray &entryPoint);
+
+ QQuickItem *m_item;
+ QQuickWindow *m_window;
+ QSize m_size;
+ qreal m_dpr;
+ id<MTLDevice> m_device = nil;
+ id<MTLTexture> m_texture = nil;
+
+ bool m_initialized = false;
+ QByteArray m_vert;
+ QByteArray m_vertEntryPoint;
+ QByteArray m_frag;
+ QByteArray m_fragEntryPoint;
+ FuncAndLib m_vs;
+ FuncAndLib m_fs;
+ id<MTLBuffer> m_vbuf;
+ id<MTLBuffer> m_ubuf[3];
+ id<MTLRenderPipelineState> m_pipeline;
+
+ float m_t;
+};
+
+CustomTextureItem::CustomTextureItem()
+{
+ setFlag(ItemHasContents, true);
+}
+
+// The beauty of using a true QSGNode: no need for complicated cleanup
+// arrangements, unlike in other examples like metalunderqml, because the
+// scenegraph will handle destroying the node at the appropriate time.
+
+void CustomTextureItem::invalidateSceneGraph() // called on the render thread when the scenegraph is invalidated
+{
+ m_node = nullptr;
+}
+
+void CustomTextureItem::releaseResources() // called on the gui thread if the item is removed from scene
+{
+ m_node = nullptr;
+}
+
+//! [2]
+QSGNode *CustomTextureItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
+{
+ CustomTextureNode *n = static_cast<CustomTextureNode *>(node);
+
+ if (!n && (width() <= 0 || height() <= 0))
+ return nullptr;
+
+ if (!n) {
+ m_node = new CustomTextureNode(this);
+ n = m_node;
+ }
+
+ m_node->sync();
+
+ n->setTextureCoordinatesTransform(QSGSimpleTextureNode::NoTransform);
+ n->setFiltering(QSGTexture::Linear);
+ n->setRect(0, 0, width(), height());
+
+ window()->update(); // ensure getting to beforeRendering() at some point
+
+ return n;
+}
+//! [2]
+
+void CustomTextureItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ QQuickItem::geometryChanged(newGeometry, oldGeometry);
+
+ if (newGeometry.size() != oldGeometry.size())
+ update();
+}
+
+void CustomTextureItem::setT(qreal t)
+{
+ if (t == m_t)
+ return;
+
+ m_t = t;
+ emit tChanged();
+
+ update();
+}
+
+//! [3]
+CustomTextureNode::CustomTextureNode(QQuickItem *item)
+ : m_item(item)
+{
+ m_window = m_item->window();
+ connect(m_window, &QQuickWindow::beforeRendering, this, &CustomTextureNode::render);
+ connect(m_window, &QQuickWindow::screenChanged, this, [this]() {
+ if (m_window->effectiveDevicePixelRatio() != m_dpr)
+ m_item->update();
+ });
+//! [3]
+ m_vs.first = nil;
+ m_vs.second = nil;
+
+ m_fs.first = nil;
+ m_fs.second = nil;
+
+ m_vbuf = nil;
+
+ for (int i = 0; i < 3; ++i)
+ m_ubuf[i] = nil;
+
+ m_pipeline = nil;
+
+ qDebug("renderer created");
+}
+
+CustomTextureNode::~CustomTextureNode()
+{
+ [m_pipeline release];
+
+ [m_vbuf release];
+
+ for (int i = 0; i < 3; ++i)
+ [m_ubuf[i] release];
+
+ [m_vs.first release];
+ [m_vs.second release];
+
+ [m_fs.first release];
+ [m_fs.second release];
+
+ delete texture();
+ [m_texture release];
+
+ qDebug("renderer destroyed");
+}
+
+QSGTexture *CustomTextureNode::texture() const
+{
+ return QSGSimpleTextureNode::texture();
+}
+
+static const float vertices[] = {
+ -1, -1,
+ 1, -1,
+ -1, 1,
+ 1, 1
+};
+
+const int UBUF_SIZE = 4;
+
+//! [4]
+void CustomTextureNode::sync()
+{
+ m_dpr = m_window->effectiveDevicePixelRatio();
+ const QSize newSize = m_window->size() * m_dpr;
+ bool needsNew = false;
+
+ if (!texture())
+ needsNew = true;
+
+ if (newSize != m_size) {
+ needsNew = true;
+ m_size = newSize;
+ }
+
+ if (needsNew) {
+ delete texture();
+ [m_texture release];
+
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ m_device = (id<MTLDevice>) rif->getResource(m_window, QSGRendererInterface::DeviceResource);
+ Q_ASSERT(m_device);
+
+ MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
+ desc.textureType = MTLTextureType2D;
+ desc.pixelFormat = MTLPixelFormatRGBA8Unorm;
+ desc.width = m_size.width();
+ desc.height = m_size.height();
+ desc.mipmapLevelCount = 1;
+ desc.resourceOptions = MTLResourceStorageModePrivate;
+ desc.storageMode = MTLStorageModePrivate;
+ desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
+ m_texture = [m_device newTextureWithDescriptor: desc];
+ [desc release];
+
+ QSGTexture *wrapper = m_window->createTextureFromNativeObject(QQuickWindow::NativeObjectTexture,
+ &m_texture,
+ 0,
+ m_size);
+
+ qDebug() << "Got QSGTexture wrapper" << wrapper << "for an MTLTexture of size" << m_size;
+
+ setTexture(wrapper);
+ }
+//! [4]
+ if (!m_initialized && texture()) {
+ m_initialized = true;
+
+ prepareShader(VertexStage);
+ prepareShader(FragmentStage);
+
+ m_vs = compileShaderFromSource(m_vert, m_vertEntryPoint);
+ m_fs = compileShaderFromSource(m_frag, m_fragEntryPoint);
+
+ const int framesInFlight = m_window->graphicsStateInfo()->framesInFlight;
+
+ m_vbuf = [m_device newBufferWithLength: sizeof(vertices) options: MTLResourceStorageModeShared];
+ void *p = [m_vbuf contents];
+ memcpy(p, vertices, sizeof(vertices));
+
+ for (int i = 0; i < framesInFlight; ++i)
+ m_ubuf[i] = [m_device newBufferWithLength: UBUF_SIZE options: MTLResourceStorageModeShared];
+
+ MTLVertexDescriptor *inputLayout = [MTLVertexDescriptor vertexDescriptor];
+ inputLayout.attributes[0].format = MTLVertexFormatFloat2;
+ inputLayout.attributes[0].offset = 0;
+ inputLayout.attributes[0].bufferIndex = 1; // ubuf is 0, vbuf is 1
+ inputLayout.layouts[1].stride = 2 * sizeof(float);
+
+ MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
+ rpDesc.vertexDescriptor = inputLayout;
+
+ rpDesc.vertexFunction = m_vs.first;
+ rpDesc.fragmentFunction = m_fs.first;
+
+ rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormatRGBA8Unorm;
+ rpDesc.colorAttachments[0].blendingEnabled = true;
+ rpDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
+ rpDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
+ rpDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOne;
+ rpDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOne;
+
+ NSError *err = nil;
+ m_pipeline = [m_device newRenderPipelineStateWithDescriptor: rpDesc error: &err];
+ if (!m_pipeline) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qFatal("Failed to create render pipeline state: %s", qPrintable(msg));
+ }
+ [rpDesc release];
+
+ qDebug("resources initialized");
+ }
+
+//! [5]
+ m_t = float(static_cast<CustomTextureItem *>(m_item)->t());
+//! [5]
+}
+
+// This is hooked up to beforeRendering() so we can start our own render
+// command encoder. If we instead wanted to use the scenegraph's render command
+// encoder (targeting the window), it should be connected to
+// beforeRenderPassRecording() instead.
+//! [6]
+void CustomTextureNode::render()
+{
+ if (!m_initialized)
+ return;
+
+ // Render to m_texture.
+ MTLRenderPassDescriptor *renderpassdesc = [MTLRenderPassDescriptor renderPassDescriptor];
+ MTLClearColor c = MTLClearColorMake(0, 0, 0, 1);
+ renderpassdesc.colorAttachments[0].loadAction = MTLLoadActionClear;
+ renderpassdesc.colorAttachments[0].storeAction = MTLStoreActionStore;
+ renderpassdesc.colorAttachments[0].clearColor = c;
+ renderpassdesc.colorAttachments[0].texture = m_texture;
+
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ id<MTLCommandBuffer> cb = (id<MTLCommandBuffer>) rif->getResource(m_window, QSGRendererInterface::CommandListResource);
+ Q_ASSERT(cb);
+ id<MTLRenderCommandEncoder> encoder = [cb renderCommandEncoderWithDescriptor: renderpassdesc];
+
+ const QQuickWindow::GraphicsStateInfo *stateInfo = m_window->graphicsStateInfo();
+ void *p = [m_ubuf[stateInfo->currentFrameSlot] contents];
+ memcpy(p, &m_t, 4);
+
+ MTLViewport vp;
+ vp.originX = 0;
+ vp.originY = 0;
+ vp.width = m_size.width();
+ vp.height = m_size.height();
+ vp.znear = 0;
+ vp.zfar = 1;
+ [encoder setViewport: vp];
+
+ [encoder setFragmentBuffer: m_ubuf[stateInfo->currentFrameSlot] offset: 0 atIndex: 0];
+ [encoder setVertexBuffer: m_vbuf offset: 0 atIndex: 1];
+ [encoder setRenderPipelineState: m_pipeline];
+ [encoder drawPrimitives: MTLPrimitiveTypeTriangleStrip vertexStart: 0 vertexCount: 4 instanceCount: 1 baseInstance: 0];
+
+ [encoder endEncoding];
+}
+//! [6]
+
+void CustomTextureNode::prepareShader(Stage stage)
+{
+ QString filename;
+ if (stage == VertexStage) {
+ filename = QLatin1String(":/scenegraph/metaltextureimport/squircle.vert");
+ } else {
+ Q_ASSERT(stage == FragmentStage);
+ filename = QLatin1String(":/scenegraph/metaltextureimport/squircle.frag");
+ }
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly))
+ qFatal("Failed to read shader %s", qPrintable(filename));
+
+ const QByteArray contents = f.readAll();
+
+ if (stage == VertexStage) {
+ m_vert = contents;
+ Q_ASSERT(!m_vert.isEmpty());
+ m_vertEntryPoint = QByteArrayLiteral("main0");
+ } else {
+ m_frag = contents;
+ Q_ASSERT(!m_frag.isEmpty());
+ m_fragEntryPoint = QByteArrayLiteral("main0");
+ }
+}
+
+CustomTextureNode::FuncAndLib CustomTextureNode::compileShaderFromSource(const QByteArray &src, const QByteArray &entryPoint)
+{
+ FuncAndLib fl;
+
+ NSString *srcstr = [NSString stringWithUTF8String: src.constData()];
+ MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
+ opts.languageVersion = MTLLanguageVersion1_2;
+ NSError *err = nil;
+ fl.second = [m_device newLibraryWithSource: srcstr options: opts error: &err];
+ [opts release];
+ // srcstr is autoreleased
+
+ if (err) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qFatal("%s", qPrintable(msg));
+ return fl;
+ }
+
+ NSString *name = [NSString stringWithUTF8String: entryPoint.constData()];
+ fl.first = [fl.second newFunctionWithName: name];
+ [name release];
+
+ return fl;
+}
+
+#include "metaltextureimport.moc"
diff --git a/examples/quick/scenegraph/metaltextureimport/metaltextureimport.pro b/examples/quick/scenegraph/metaltextureimport/metaltextureimport.pro
new file mode 100644
index 0000000000..c8ea7ce478
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/metaltextureimport.pro
@@ -0,0 +1,10 @@
+QT += qml quick
+
+HEADERS += metaltextureimport.h
+SOURCES += metaltextureimport.mm main.cpp
+RESOURCES += metaltextureimport.qrc
+
+LIBS += -framework Metal -framework AppKit
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/metaltextureimport
+INSTALLS += target
diff --git a/examples/quick/scenegraph/metaltextureimport/metaltextureimport.qrc b/examples/quick/scenegraph/metaltextureimport/metaltextureimport.qrc
new file mode 100644
index 0000000000..7a8dd7a860
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/metaltextureimport.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/scenegraph/metaltextureimport">
+ <file>main.qml</file>
+ <file>squircle.vert</file>
+ <file>squircle.frag</file>
+ </qresource>
+</RCC>
diff --git a/examples/quick/scenegraph/metaltextureimport/squircle.frag b/examples/quick/scenegraph/metaltextureimport/squircle.frag
new file mode 100644
index 0000000000..15f34624fe
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/squircle.frag
@@ -0,0 +1,29 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct buf
+{
+ float t;
+};
+
+struct main0_out
+{
+ float4 fragColor [[color(0)]];
+};
+
+struct main0_in
+{
+ float2 coords [[user(locn0)]];
+};
+
+fragment main0_out main0(main0_in in [[stage_in]], constant buf& ubuf [[buffer(0)]])
+{
+ main0_out out = {};
+ float i = 1.0 - (pow(abs(in.coords.x), 4.0) + pow(abs(in.coords.y), 4.0));
+ i = smoothstep(ubuf.t - 0.800000011920928955078125, ubuf.t + 0.800000011920928955078125, i);
+ i = floor(i * 20.0) / 20.0;
+ out.fragColor = float4((in.coords * 0.5) + float2(0.5), i, i);
+ return out;
+}
diff --git a/examples/quick/scenegraph/metaltextureimport/squircle.vert b/examples/quick/scenegraph/metaltextureimport/squircle.vert
new file mode 100644
index 0000000000..a88c59f541
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/squircle.vert
@@ -0,0 +1,23 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct main0_out
+{
+ float2 coords [[user(locn0)]];
+ float4 gl_Position [[position]];
+};
+
+struct main0_in
+{
+ float4 vertices [[attribute(0)]];
+};
+
+vertex main0_out main0(main0_in in [[stage_in]])
+{
+ main0_out out = {};
+ out.gl_Position = in.vertices;
+ out.coords = in.vertices.xy;
+ return out;
+}
diff --git a/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc b/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc
index d499f47de3..fb28270315 100644
--- a/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc
+++ b/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc
@@ -61,8 +61,9 @@
program that draws the squircles.
The example is equivalent in most ways to the \l{Scene Graph - OpenGL Under
- QML}{OpenGL Under QML} and \l{Scene Graph - Direct3D 11 Under QML}{Direct3D
- 11 Under QML} examples, they all render the same custom content, just via
- different native APIs.
+ QML}{OpenGL Under QML}, \l{Scene Graph - Direct3D 11 Under QML}{Direct3D 11
+ Under QML}, and \l{Scene Graph - Vulkan Under QML}{Vulkan Under QML}
+ examples, they all render the same custom content, just via different
+ native APIs.
*/
diff --git a/examples/quick/scenegraph/metalunderqml/metalsquircle.mm b/examples/quick/scenegraph/metalunderqml/metalsquircle.mm
index 92aceeb433..8a1a03dc68 100644
--- a/examples/quick/scenegraph/metalunderqml/metalsquircle.mm
+++ b/examples/quick/scenegraph/metalunderqml/metalsquircle.mm
@@ -186,7 +186,12 @@ void MetalSquircle::sync()
{
if (!m_renderer) {
m_renderer = new SquircleRenderer;
+ // Initializing resources is done before starting to encode render
+ // commands, regardless of wanting an underlay or overlay.
connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::frameStart, Qt::DirectConnection);
+ // Here we want an underlay and therefore connect to
+ // beforeRenderPassRecording. Changing to afterRenderPassRecording
+ // would render the squircle on top (overlay).
connect(window(), &QQuickWindow::beforeRenderPassRecording, m_renderer, &SquircleRenderer::mainPassRecordingStart, Qt::DirectConnection);
}
m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio());
diff --git a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc
index ed46b40420..9676815c44 100644
--- a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc
+++ b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc
@@ -50,9 +50,10 @@
in the QML file and this value is used by the OpenGL shader
program that draws the squircles.
- The example is equivalent in most ways to the \l{Scene Graph - Direct3D 11 Under
- QML}{Direct3D 11 Under QML} and \l{Scene Graph - Metal Under QML}{Metal Under
- QML} examples, they all render the same custom content, just via different
+ The example is equivalent in most ways to the \l{Scene Graph - Direct3D 11
+ Under QML}{Direct3D 11 Under QML}, \l{Scene Graph - Metal Under QML}{Metal
+ Under QML}, and \l{Scene Graph - Vulkan Under QML}{Vulkan Under QML}
+ examples, they all render the same custom content, just via different
native APIs.
\snippet scenegraph/openglunderqml/squircle.h 2
diff --git a/examples/quick/scenegraph/rendernode/customrenderitem.cpp b/examples/quick/scenegraph/rendernode/customrenderitem.cpp
index 67a9cccfc6..8aaa56c359 100644
--- a/examples/quick/scenegraph/rendernode/customrenderitem.cpp
+++ b/examples/quick/scenegraph/rendernode/customrenderitem.cpp
@@ -70,48 +70,56 @@ CustomRenderItem::CustomRenderItem(QQuickItem *parent)
QSGNode *CustomRenderItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
{
QSGRenderNode *n = static_cast<QSGRenderNode *>(node);
- if (!n) {
- QSGRendererInterface *ri = window()->rendererInterface();
- if (!ri)
- return nullptr;
- switch (ri->graphicsApi()) {
- case QSGRendererInterface::OpenGL:
- Q_FALLTHROUGH();
- case QSGRendererInterface::OpenGLRhi:
+
+ QSGRendererInterface *ri = window()->rendererInterface();
+ if (!ri)
+ return nullptr;
+
+ switch (ri->graphicsApi()) {
+ case QSGRendererInterface::OpenGL:
+ Q_FALLTHROUGH();
+ case QSGRendererInterface::OpenGLRhi:
#if QT_CONFIG(opengl)
- n = new OpenGLRenderNode(this);
+ if (!n)
+ n = new OpenGLRenderNode;
+ static_cast<OpenGLRenderNode *>(n)->sync(this);
#endif
- break;
+ break;
- case QSGRendererInterface::MetalRhi:
+ case QSGRendererInterface::MetalRhi:
#ifdef Q_OS_DARWIN
- {
- MetalRenderNode *metalNode = new MetalRenderNode(this);
+ if (!n) {
+ MetalRenderNode *metalNode = new MetalRenderNode;
n = metalNode;
metalNode->resourceBuilder()->setWindow(window());
QObject::connect(window(), &QQuickWindow::beforeRendering,
metalNode->resourceBuilder(), &MetalRenderNodeResourceBuilder::build);
}
+ static_cast<MetalRenderNode *>(n)->sync(this);
#endif
- break;
+ break;
- case QSGRendererInterface::Direct3D12: // ### Qt 6: remove
+ case QSGRendererInterface::Direct3D12: // ### Qt 6: remove
#if QT_CONFIG(d3d12)
- n = new D3D12RenderNode(this);
+ if (!n)
+ n = new D3D12RenderNode;
+ static_cast<D3D12RenderNode *>(n)->sync(this);
#endif
- break;
+ break;
- case QSGRendererInterface::Software:
- n = new SoftwareRenderNode(this);
- break;
-
- default:
- break;
- }
+ case QSGRendererInterface::Software:
if (!n)
- qWarning("QSGRendererInterface reports unknown graphics API %d", ri->graphicsApi());
+ n = new SoftwareRenderNode;
+ static_cast<SoftwareRenderNode *>(n)->sync(this);
+ break;
+
+ default:
+ break;
}
+ if (!n)
+ qWarning("QSGRendererInterface reports unknown graphics API %d", ri->graphicsApi());
+
return n;
}
//! [2]
diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp
index 878b022950..e85811c089 100644
--- a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp
+++ b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp
@@ -58,11 +58,6 @@
#if QT_CONFIG(d3d12)
-D3D12RenderNode::D3D12RenderNode(QQuickItem *item)
- : m_item(item)
-{
-}
-
D3D12RenderNode::~D3D12RenderNode()
{
releaseResources();
@@ -87,8 +82,8 @@ void D3D12RenderNode::releaseResources()
void D3D12RenderNode::init()
{
- QSGRendererInterface *rif = m_item->window()->rendererInterface();
- m_device = static_cast<ID3D12Device *>(rif->getResource(m_item->window(), QSGRendererInterface::DeviceResource));
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ m_device = static_cast<ID3D12Device *>(rif->getResource(m_window, QSGRendererInterface::DeviceResource));
Q_ASSERT(m_device);
D3D12_ROOT_PARAMETER rootParameter;
@@ -178,7 +173,7 @@ void D3D12RenderNode::init()
psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; // not in use due to !DepthEnable, but this would be the correct format otherwise
// We are rendering on the default render target so if the QuickWindow/View
// has requested samples > 0 then we have to follow suit.
- const uint samples = qMax(1, m_item->window()->format().samples());
+ const uint samples = qMax(1, m_window->format().samples());
psoDesc.SampleDesc.Count = samples;
if (samples > 1) {
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msaaInfo = {};
@@ -257,9 +252,9 @@ void D3D12RenderNode::render(const RenderState *state)
if (!m_device)
init();
- QSGRendererInterface *rif = m_item->window()->rendererInterface();
+ QSGRendererInterface *rif = m_window->rendererInterface();
ID3D12GraphicsCommandList *commandList = static_cast<ID3D12GraphicsCommandList *>(
- rif->getResource(m_item->window(), QSGRendererInterface::CommandListResource));
+ rif->getResource(m_window, QSGRendererInterface::CommandListResource));
Q_ASSERT(commandList);
const int msize = 16 * sizeof(float);
@@ -268,9 +263,9 @@ void D3D12RenderNode::render(const RenderState *state)
const float opacity = inheritedOpacity();
memcpy(cbPtr + 2 * msize, &opacity, sizeof(float));
- const QPointF p0(m_item->width() - 1, m_item->height() - 1);
+ const QPointF p0(m_width - 1, m_height - 1);
const QPointF p1(0, 0);
- const QPointF p2(0, m_item->height() - 1);
+ const QPointF p2(0, m_height - 1);
float *vp = reinterpret_cast<float *>(vbPtr);
*vp++ = p0.x();
@@ -301,7 +296,14 @@ QSGRenderNode::RenderingFlags D3D12RenderNode::flags() const
QRectF D3D12RenderNode::rect() const
{
- return QRect(0, 0, m_item->width(), m_item->height());
+ return QRect(0, 0, m_width, m_height);
+}
+
+void D3D12RenderNode::sync(QQuickItem *item)
+{
+ m_window = item->window();
+ m_width = item->width();
+ m_height = item->height();
}
#endif // d3d12
diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.h b/examples/quick/scenegraph/rendernode/d3d12renderer.h
index ec4b5f85e8..7186b72c04 100644
--- a/examples/quick/scenegraph/rendernode/d3d12renderer.h
+++ b/examples/quick/scenegraph/rendernode/d3d12renderer.h
@@ -52,11 +52,10 @@
#define D3D12RENDERER_H
#include <qsgrendernode.h>
+#include <QQuickItem>
#if QT_CONFIG(d3d12)
-QT_FORWARD_DECLARE_CLASS(QQuickItem)
-
#include <d3d12.h>
#include <wrl/client.h>
@@ -65,7 +64,6 @@ using namespace Microsoft::WRL;
class D3D12RenderNode : public QSGRenderNode
{
public:
- D3D12RenderNode(QQuickItem *item);
~D3D12RenderNode();
void render(const RenderState *state) override;
@@ -73,10 +71,15 @@ public:
RenderingFlags flags() const override;
QRectF rect() const override;
+ void sync(QQuickItem *item);
+
private:
void init();
- QQuickItem *m_item;
+ QQuickWindow *m_window = nullptr;
+ int m_width = 0;
+ int m_height = 0;
+
ID3D12Device *m_device = nullptr;
ComPtr<ID3D12PipelineState> pipelineState;
ComPtr<ID3D12RootSignature> rootSignature;
diff --git a/examples/quick/scenegraph/rendernode/metalrenderer.h b/examples/quick/scenegraph/rendernode/metalrenderer.h
index 77c9892313..31387a0e24 100644
--- a/examples/quick/scenegraph/rendernode/metalrenderer.h
+++ b/examples/quick/scenegraph/rendernode/metalrenderer.h
@@ -52,14 +52,12 @@
#define METALRENDERER_H
#include <qsgrendernode.h>
+#include <QQuickItem>
#ifdef Q_OS_DARWIN
QT_BEGIN_NAMESPACE
-class QQuickItem;
-class QQuickWindow;
-
QT_END_NAMESPACE
class MetalRenderNodeResourceBuilder : public QObject
@@ -79,7 +77,7 @@ private:
class MetalRenderNode : public QSGRenderNode
{
public:
- MetalRenderNode(QQuickItem *item);
+ MetalRenderNode();
~MetalRenderNode();
void render(const RenderState *state) override;
@@ -90,9 +88,14 @@ public:
MetalRenderNodeResourceBuilder *resourceBuilder() { return &m_resourceBuilder; }
+ void sync(QQuickItem *item);
+
private:
- QQuickItem *m_item;
MetalRenderNodeResourceBuilder m_resourceBuilder;
+ QQuickWindow *m_window = nullptr;
+ int m_width = 0;
+ int m_height = 0;
+ int m_outputHeight = 0;
};
#endif // Q_OS_DARWIN
diff --git a/examples/quick/scenegraph/rendernode/metalrenderer.mm b/examples/quick/scenegraph/rendernode/metalrenderer.mm
index 4cb973abee..11db276f37 100644
--- a/examples/quick/scenegraph/rendernode/metalrenderer.mm
+++ b/examples/quick/scenegraph/rendernode/metalrenderer.mm
@@ -214,8 +214,7 @@ void MetalRenderNodeResourceBuilder::build()
}
}
-MetalRenderNode::MetalRenderNode(QQuickItem *item)
- : m_item(item)
+MetalRenderNode::MetalRenderNode()
{
g.vs.first = g.fs.first = nil;
g.vs.second = g.fs.second = nil;
@@ -258,14 +257,14 @@ void MetalRenderNode::releaseResources()
void MetalRenderNode::render(const RenderState *state)
{
- QQuickWindow *window = m_item->window();
- const QQuickWindow::GraphicsStateInfo *stateInfo = window->graphicsStateInfo();
+ Q_ASSERT(m_window);
+ const QQuickWindow::GraphicsStateInfo *stateInfo = m_window->graphicsStateInfo();
id<MTLBuffer> vbuf = g.vbuf[stateInfo->currentFrameSlot];
id<MTLBuffer> ubuf = g.ubuf[stateInfo->currentFrameSlot];
- QPointF p0(m_item->width() - 1, m_item->height() - 1);
+ QPointF p0(m_width - 1, m_height - 1);
QPointF p1(0, 0);
- QPointF p2(0, m_item->height() - 1);
+ QPointF p2(0, m_height - 1);
float vertices[6] = { float(p0.x()), float(p0.y()),
float(p1.x()), float(p1.y()),
@@ -280,9 +279,9 @@ void MetalRenderNode::render(const RenderState *state)
memcpy(p, mvp.constData(), 64);
memcpy(p + 64, &opacity, 4);
- QSGRendererInterface *rif = window->rendererInterface();
+ QSGRendererInterface *rif = m_window->rendererInterface();
id<MTLRenderCommandEncoder> encoder = (id<MTLRenderCommandEncoder>) rif->getResource(
- window, QSGRendererInterface::CommandEncoderResource);
+ m_window, QSGRendererInterface::CommandEncoderResource);
Q_ASSERT(encoder);
[encoder setVertexBuffer: vbuf offset: 0 atIndex: 1];
@@ -296,7 +295,7 @@ void MetalRenderNode::render(const RenderState *state)
const QRect r = state->scissorRect(); // bottom-up
MTLScissorRect s;
s.x = r.x();
- s.y = (window->height() * window->effectiveDevicePixelRatio()) - (r.y() + r.height());
+ s.y = m_outputHeight - (r.y() + r.height());
s.width = r.width();
s.height = r.height();
[encoder setScissorRect: s];
@@ -322,5 +321,13 @@ QSGRenderNode::RenderingFlags MetalRenderNode::flags() const
QRectF MetalRenderNode::rect() const
{
- return QRect(0, 0, m_item->width(), m_item->height());
+ return QRect(0, 0, m_width, m_height);
+}
+
+void MetalRenderNode::sync(QQuickItem *item)
+{
+ m_window = item->window();
+ m_width = item->width();
+ m_height = item->height();
+ m_outputHeight = m_window->height() * m_window->effectiveDevicePixelRatio();
}
diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.cpp b/examples/quick/scenegraph/rendernode/openglrenderer.cpp
index 0633731617..a4e619bea9 100644
--- a/examples/quick/scenegraph/rendernode/openglrenderer.cpp
+++ b/examples/quick/scenegraph/rendernode/openglrenderer.cpp
@@ -58,11 +58,6 @@
#include <QOpenGLFunctions>
//! [1]
-OpenGLRenderNode::OpenGLRenderNode(QQuickItem *item)
- : m_item(item)
-{
-}
-
OpenGLRenderNode::~OpenGLRenderNode()
{
releaseResources();
@@ -138,9 +133,9 @@ void OpenGLRenderNode::render(const RenderState *state)
m_vbo->bind();
//! [5]
- QPointF p0(m_item->width() - 1, m_item->height() - 1);
+ QPointF p0(m_width - 1, m_height - 1);
QPointF p1(0, 0);
- QPointF p2(0, m_item->height() - 1);
+ QPointF p2(0, m_height - 1);
GLfloat vertices[6] = { GLfloat(p0.x()), GLfloat(p0.y()),
GLfloat(p1.x()), GLfloat(p1.y()),
@@ -192,8 +187,14 @@ QSGRenderNode::RenderingFlags OpenGLRenderNode::flags() const
QRectF OpenGLRenderNode::rect() const
{
- return QRect(0, 0, m_item->width(), m_item->height());
+ return QRect(0, 0, m_width, m_height);
}
//! [4]
+void OpenGLRenderNode::sync(QQuickItem *item)
+{
+ m_width = item->width();
+ m_height = item->height();
+}
+
#endif // opengl
diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.h b/examples/quick/scenegraph/rendernode/openglrenderer.h
index 8d2d3caad1..1e7977481a 100644
--- a/examples/quick/scenegraph/rendernode/openglrenderer.h
+++ b/examples/quick/scenegraph/rendernode/openglrenderer.h
@@ -52,12 +52,12 @@
#define OPENGLRENDERER_H
#include <qsgrendernode.h>
+#include <QQuickItem>
#if QT_CONFIG(opengl)
QT_BEGIN_NAMESPACE
-class QQuickItem;
class QOpenGLShaderProgram;
class QOpenGLBuffer;
@@ -67,7 +67,6 @@ QT_END_NAMESPACE
class OpenGLRenderNode : public QSGRenderNode
{
public:
- OpenGLRenderNode(QQuickItem *item);
~OpenGLRenderNode();
void render(const RenderState *state) override;
@@ -77,10 +76,13 @@ public:
QRectF rect() const override;
//! [1]
+ void sync(QQuickItem *item);
+
private:
void init();
- QQuickItem *m_item;
+ int m_width = 0;
+ int m_height = 0;
QOpenGLShaderProgram *m_program = nullptr;
int m_matrixUniform;
int m_opacityUniform;
diff --git a/examples/quick/scenegraph/rendernode/softwarerenderer.cpp b/examples/quick/scenegraph/rendernode/softwarerenderer.cpp
index 0a0ec4b485..bba364ac97 100644
--- a/examples/quick/scenegraph/rendernode/softwarerenderer.cpp
+++ b/examples/quick/scenegraph/rendernode/softwarerenderer.cpp
@@ -54,11 +54,6 @@
#include <QSGRendererInterface>
#include <QPainter>
-SoftwareRenderNode::SoftwareRenderNode(QQuickItem *item)
- : m_item(item)
-{
-}
-
SoftwareRenderNode::~SoftwareRenderNode()
{
releaseResources();
@@ -70,8 +65,10 @@ void SoftwareRenderNode::releaseResources()
void SoftwareRenderNode::render(const RenderState *renderState)
{
- QSGRendererInterface *rif = m_item->window()->rendererInterface();
- QPainter *p = static_cast<QPainter *>(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource));
+ Q_ASSERT(m_window);
+
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ QPainter *p = static_cast<QPainter *>(rif->getResource(m_window, QSGRendererInterface::PainterResource));
Q_ASSERT(p);
const QRegion *clipRegion = renderState->clipRegion();
@@ -81,15 +78,15 @@ void SoftwareRenderNode::render(const RenderState *renderState)
p->setTransform(matrix()->toTransform());
p->setOpacity(inheritedOpacity());
- const QPointF p0(m_item->width() - 1, m_item->height() - 1);
+ const QPointF p0(m_width - 1, m_height - 1);
const QPointF p1(0, 0);
- const QPointF p2(0, m_item->height() - 1);
+ const QPointF p2(0, m_height - 1);
QPainterPath path(p0);
path.lineTo(p1);
path.lineTo(p2);
path.closeSubpath();
- QLinearGradient gradient(QPointF(0, 0), QPointF(m_item->width(), m_item->height()));
+ QLinearGradient gradient(QPointF(0, 0), QPointF(m_width, m_height));
gradient.setColorAt(0, Qt::green);
gradient.setColorAt(1, Qt::red);
@@ -108,5 +105,12 @@ QSGRenderNode::RenderingFlags SoftwareRenderNode::flags() const
QRectF SoftwareRenderNode::rect() const
{
- return QRect(0, 0, m_item->width(), m_item->height());
+ return QRect(0, 0, m_width, m_height);
+}
+
+void SoftwareRenderNode::sync(QQuickItem *item)
+{
+ m_window = item->window();
+ m_width = item->width();
+ m_height = item->height();
}
diff --git a/examples/quick/scenegraph/rendernode/softwarerenderer.h b/examples/quick/scenegraph/rendernode/softwarerenderer.h
index cc2aaf7ed3..6091a13ca3 100644
--- a/examples/quick/scenegraph/rendernode/softwarerenderer.h
+++ b/examples/quick/scenegraph/rendernode/softwarerenderer.h
@@ -57,7 +57,6 @@
class SoftwareRenderNode : public QSGRenderNode
{
public:
- SoftwareRenderNode(QQuickItem *item);
~SoftwareRenderNode();
void render(const RenderState *state) override;
@@ -66,8 +65,12 @@ public:
RenderingFlags flags() const override;
QRectF rect() const override;
+ void sync(QQuickItem *item);
+
private:
- QQuickItem *m_item;
+ QQuickWindow *m_window = nullptr;
+ int m_width = 0;
+ int m_height = 0;
};
#endif
diff --git a/examples/quick/scenegraph/scenegraph.pro b/examples/quick/scenegraph/scenegraph.pro
index ac368c79a5..e05e1ddb44 100644
--- a/examples/quick/scenegraph/scenegraph.pro
+++ b/examples/quick/scenegraph/scenegraph.pro
@@ -17,12 +17,18 @@ SUBDIRS += \
threadedanimation
macos {
- SUBDIRS += metalunderqml
+ SUBDIRS += \
+ metalunderqml \
+ metaltextureimport
}
win32 {
SUBDIRS += d3d11underqml
}
+qtConfig(vulkan) {
+ SUBDIRS += vulkanunderqml
+}
+
EXAMPLE_FILES += \
shared
diff --git a/examples/quick/scenegraph/vulkanunderqml/doc/images/vulkanunderqml-example.jpg b/examples/quick/scenegraph/vulkanunderqml/doc/images/vulkanunderqml-example.jpg
new file mode 100644
index 0000000000..c3f51b6194
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/doc/images/vulkanunderqml-example.jpg
Binary files differ
diff --git a/examples/quick/scenegraph/vulkanunderqml/doc/src/vulkanunderqml.qdoc b/examples/quick/scenegraph/vulkanunderqml/doc/src/vulkanunderqml.qdoc
new file mode 100644
index 0000000000..d11982c074
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/doc/src/vulkanunderqml.qdoc
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example scenegraph/vulkanunderqml
+ \title Scene Graph - Vulkan Under QML
+ \ingroup qtquickexamples
+ \brief Shows how to render directly with vulkan under a Qt Quick scene.
+
+ \image vulkanunderqml-example.jpg
+
+ The Vulkan Under QML example shows how an application can make use of the
+ \l QQuickWindow::beforeRendering() and \l
+ QQuickWindow::beforeRenderPassRecording() signals to draw custom Vulkan
+ content under a Qt Quick scene. This signal is emitted at the start of
+ every frame, before the scene graph starts its rendering, thus any Vulkan
+ draw calls that are made as a response to this signal, will stack under the
+ Qt Quick items. There are two signals, because the custom Vulkan commands
+ are recorded onto the same command buffer that is used by the scene graph.
+ beforeRendering() on its own is not sufficient for this because it gets
+ emitted at the start of the frame, before recording the start of a
+ renderpass instance via
+ \l{https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCmdBeginRenderPass.html}{vkCmdBeginRenderPass}.
+ By also connecting to beforeRenderPassRecording(), the application's own
+ commands and the scene graph's scaffolding will end up in the right order.
+
+ As an alternative, applications that wish to render Vulkan content
+ on top of the Qt Quick scene, can do so by connecting to the \l
+ QQuickWindow::afterRendering() and \l
+ QQuickWindow::afterRenderPassRecording() signals.
+
+ In this example, we will also see how it is possible to have
+ values that are exposed to QML which affect the Vulkan
+ rendering. We animate the threshold value using a NumberAnimation
+ in the QML file and this value is used by the SPIR-V shader
+ program that draws the squircles.
+
+ The example is equivalent in most ways to the \l{Scene Graph - OpenGL Under
+ QML}{OpenGL Under QML}, \l{Scene Graph - Direct3D 11 Under QML}{Direct3D 11
+ Under QML}, and \l{Scene Graph - Metal Under QML}{Metal Under QML}
+ examples, they all render the same custom content, just via different
+ native APIs.
+
+ */
diff --git a/examples/quick/scenegraph/vulkanunderqml/main.cpp b/examples/quick/scenegraph/vulkanunderqml/main.cpp
new file mode 100644
index 0000000000..a04497b1d6
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/main.cpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications 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 <QtQuick/QQuickView>
+#include "vulkansquircle.h"
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+ qmlRegisterType<VulkanSquircle>("VulkanUnderQML", 1, 0, "VulkanSquircle");
+
+ // This example needs Vulkan. It will not run otherwise.
+ QQuickWindow::setSceneGraphBackend(QSGRendererInterface::VulkanRhi);
+
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl("qrc:///scenegraph/vulkanunderqml/main.qml"));
+ view.show();
+
+ return app.exec();
+}
diff --git a/examples/quick/scenegraph/vulkanunderqml/main.qml b/examples/quick/scenegraph/vulkanunderqml/main.qml
new file mode 100644
index 0000000000..c364ca7636
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/main.qml
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications 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$
+**
+****************************************************************************/
+
+//! [1]
+import QtQuick 2.0
+import VulkanUnderQML 1.0
+
+Item {
+
+ width: 320
+ height: 480
+
+ VulkanSquircle {
+ SequentialAnimation on t {
+ NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad }
+ NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad }
+ loops: Animation.Infinite
+ running: true
+ }
+ }
+//! [1] //! [2]
+ Rectangle {
+ color: Qt.rgba(1, 1, 1, 0.7)
+ radius: 10
+ border.width: 1
+ border.color: "white"
+ anchors.fill: label
+ anchors.margins: -10
+ }
+
+ Text {
+ id: label
+ color: "black"
+ wrapMode: Text.WordWrap
+ text: "The background here is a squircle rendered with raw Vulkan using the beforeRendering() and beforeRenderPassRecording() signals in QQuickWindow. This text label and its border is rendered using QML"
+ anchors.right: parent.right
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ anchors.margins: 20
+ }
+}
+//! [2]
diff --git a/examples/quick/scenegraph/vulkanunderqml/squircle.frag.spv b/examples/quick/scenegraph/vulkanunderqml/squircle.frag.spv
new file mode 100644
index 0000000000..e4d13a871d
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/squircle.frag.spv
Binary files differ
diff --git a/examples/quick/scenegraph/vulkanunderqml/squircle.vert.spv b/examples/quick/scenegraph/vulkanunderqml/squircle.vert.spv
new file mode 100644
index 0000000000..5df94a47e4
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/squircle.vert.spv
Binary files differ
diff --git a/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp b/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp
new file mode 100644
index 0000000000..59e9606e7b
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp
@@ -0,0 +1,623 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications 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 "vulkansquircle.h"
+#include <QtCore/QRunnable>
+#include <QtQuick/QQuickWindow>
+
+#include <QVulkanInstance>
+#include <QVulkanFunctions>
+
+class SquircleRenderer : public QObject
+{
+ Q_OBJECT
+public:
+ ~SquircleRenderer();
+
+ void setT(qreal t) { m_t = t; }
+ void setViewportSize(const QSize &size) { m_viewportSize = size; }
+ void setWindow(QQuickWindow *window) { m_window = window; }
+
+public slots:
+ void frameStart();
+ void mainPassRecordingStart();
+
+private:
+ enum Stage {
+ VertexStage,
+ FragmentStage
+ };
+ void prepareShader(Stage stage);
+ void init(int framesInFlight);
+
+ QSize m_viewportSize;
+ qreal m_t = 0;
+ QQuickWindow *m_window;
+
+ QByteArray m_vert;
+ QByteArray m_frag;
+
+ bool m_initialized = false;
+ VkPhysicalDevice m_physDev = VK_NULL_HANDLE;
+ VkDevice m_dev = VK_NULL_HANDLE;
+ QVulkanDeviceFunctions *m_devFuncs = nullptr;
+ QVulkanFunctions *m_funcs = nullptr;
+
+ VkBuffer m_vbuf = VK_NULL_HANDLE;
+ VkDeviceMemory m_vbufMem = VK_NULL_HANDLE;
+ VkBuffer m_ubuf = VK_NULL_HANDLE;
+ VkDeviceMemory m_ubufMem = VK_NULL_HANDLE;
+ VkDeviceSize m_allocPerUbuf = 0;
+
+ VkPipelineCache m_pipelineCache = VK_NULL_HANDLE;
+
+ VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
+ VkDescriptorSetLayout m_resLayout = VK_NULL_HANDLE;
+ VkPipeline m_pipeline = VK_NULL_HANDLE;
+
+ VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE;
+ VkDescriptorSet m_ubufDescriptor = VK_NULL_HANDLE;
+};
+
+VulkanSquircle::VulkanSquircle()
+{
+ connect(this, &QQuickItem::windowChanged, this, &VulkanSquircle::handleWindowChanged);
+}
+
+void VulkanSquircle::setT(qreal t)
+{
+ if (t == m_t)
+ return;
+ m_t = t;
+ emit tChanged();
+ if (window())
+ window()->update();
+}
+
+void VulkanSquircle::handleWindowChanged(QQuickWindow *win)
+{
+ if (win) {
+ connect(win, &QQuickWindow::beforeSynchronizing, this, &VulkanSquircle::sync, Qt::DirectConnection);
+ connect(win, &QQuickWindow::sceneGraphInvalidated, this, &VulkanSquircle::cleanup, Qt::DirectConnection);
+
+ // Ensure we start with cleared to black. The squircle's blend mode relies on this.
+ win->setColor(Qt::black);
+ }
+}
+
+// The safe way to release custom graphics resources is to both connect to
+// sceneGraphInvalidated() and implement releaseResources(). To support
+// threaded render loops the latter performs the SquircleRenderer destruction
+// via scheduleRenderJob(). Note that the VulkanSquircle may be gone by the time
+// the QRunnable is invoked.
+
+void VulkanSquircle::cleanup()
+{
+ delete m_renderer;
+ m_renderer = nullptr;
+}
+
+class CleanupJob : public QRunnable
+{
+public:
+ CleanupJob(SquircleRenderer *renderer) : m_renderer(renderer) { }
+ void run() override { delete m_renderer; }
+private:
+ SquircleRenderer *m_renderer;
+};
+
+void VulkanSquircle::releaseResources()
+{
+ window()->scheduleRenderJob(new CleanupJob(m_renderer), QQuickWindow::BeforeSynchronizingStage);
+ m_renderer = nullptr;
+}
+
+SquircleRenderer::~SquircleRenderer()
+{
+ qDebug("cleanup");
+ if (!m_devFuncs)
+ return;
+
+ m_devFuncs->vkDestroyPipeline(m_dev, m_pipeline, nullptr);
+ m_devFuncs->vkDestroyPipelineLayout(m_dev, m_pipelineLayout, nullptr);
+ m_devFuncs->vkDestroyDescriptorSetLayout(m_dev, m_resLayout, nullptr);
+
+ m_devFuncs->vkDestroyDescriptorPool(m_dev, m_descriptorPool, nullptr);
+
+ m_devFuncs->vkDestroyPipelineCache(m_dev, m_pipelineCache, nullptr);
+
+ m_devFuncs->vkDestroyBuffer(m_dev, m_vbuf, nullptr);
+ m_devFuncs->vkFreeMemory(m_dev, m_vbufMem, nullptr);
+
+ m_devFuncs->vkDestroyBuffer(m_dev, m_ubuf, nullptr);
+ m_devFuncs->vkFreeMemory(m_dev, m_ubufMem, nullptr);
+
+ qDebug("released");
+}
+
+void VulkanSquircle::sync()
+{
+ if (!m_renderer) {
+ m_renderer = new SquircleRenderer;
+ // Initializing resources is done before starting to record the
+ // renderpass, regardless of wanting an underlay or overlay.
+ connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::frameStart, Qt::DirectConnection);
+ // Here we want an underlay and therefore connect to
+ // beforeRenderPassRecording. Changing to afterRenderPassRecording
+ // would render the squircle on top (overlay).
+ connect(window(), &QQuickWindow::beforeRenderPassRecording, m_renderer, &SquircleRenderer::mainPassRecordingStart, Qt::DirectConnection);
+ }
+ m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio());
+ m_renderer->setT(m_t);
+ m_renderer->setWindow(window());
+}
+
+void SquircleRenderer::frameStart()
+{
+ QSGRendererInterface *rif = m_window->rendererInterface();
+
+ // We are not prepared for anything other than running with the RHI and its Vulkan backend.
+ Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::VulkanRhi);
+
+ if (m_vert.isEmpty())
+ prepareShader(VertexStage);
+ if (m_frag.isEmpty())
+ prepareShader(FragmentStage);
+
+ if (!m_initialized)
+ init(m_window->graphicsStateInfo()->framesInFlight);
+}
+
+static const float vertices[] = {
+ -1, -1,
+ 1, -1,
+ -1, 1,
+ 1, 1
+};
+
+const int UBUF_SIZE = 4;
+
+void SquircleRenderer::mainPassRecordingStart()
+{
+ // This example demonstrates the simple case: prepending some commands to
+ // the scenegraph's main renderpass. It does not create its own passes,
+ // rendertargets, etc. so no synchronization is needed.
+
+ const QQuickWindow::GraphicsStateInfo *stateInfo = m_window->graphicsStateInfo();
+ QSGRendererInterface *rif = m_window->rendererInterface();
+
+ VkDeviceSize ubufOffset = stateInfo->currentFrameSlot * m_allocPerUbuf;
+ void *p = nullptr;
+ VkResult err = m_devFuncs->vkMapMemory(m_dev, m_ubufMem, ubufOffset, m_allocPerUbuf, 0, &p);
+ if (err != VK_SUCCESS || !p)
+ qFatal("Failed to map uniform buffer memory: %d", err);
+ float t = m_t;
+ memcpy(p, &t, 4);
+ m_devFuncs->vkUnmapMemory(m_dev, m_ubufMem);
+
+ m_window->beginExternalCommands();
+
+ // Must query the command buffer _after_ beginExternalCommands(), this is
+ // actually important when running on Vulkan because what we get here is a
+ // new secondary command buffer, not the primary one.
+ VkCommandBuffer cb = *reinterpret_cast<VkCommandBuffer *>(
+ rif->getResource(m_window, QSGRendererInterface::CommandListResource));
+ Q_ASSERT(cb);
+
+ // Do not assume any state persists on the command buffer. (it may be a
+ // brand new one that just started recording)
+
+ m_devFuncs->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline);
+
+ VkDeviceSize vbufOffset = 0;
+ m_devFuncs->vkCmdBindVertexBuffers(cb, 0, 1, &m_vbuf, &vbufOffset);
+
+ uint32_t dynamicOffset = m_allocPerUbuf * stateInfo->currentFrameSlot;
+ m_devFuncs->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1,
+ &m_ubufDescriptor, 1, &dynamicOffset);
+
+ VkViewport vp = { 0, 0, float(m_viewportSize.width()), float(m_viewportSize.height()), 0.0f, 1.0f };
+ m_devFuncs->vkCmdSetViewport(cb, 0, 1, &vp);
+ VkRect2D scissor = { { 0, 0 }, { uint32_t(m_viewportSize.width()), uint32_t(m_viewportSize.height()) } };
+ m_devFuncs->vkCmdSetScissor(cb, 0, 1, &scissor);
+
+ m_devFuncs->vkCmdDraw(cb, 4, 1, 0, 0);
+
+ m_window->endExternalCommands();
+}
+
+void SquircleRenderer::prepareShader(Stage stage)
+{
+ QString filename;
+ if (stage == VertexStage) {
+ filename = QLatin1String(":/scenegraph/vulkanunderqml/squircle.vert.spv");
+ } else {
+ Q_ASSERT(stage == FragmentStage);
+ filename = QLatin1String(":/scenegraph/vulkanunderqml/squircle.frag.spv");
+ }
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly))
+ qFatal("Failed to read shader %s", qPrintable(filename));
+
+ const QByteArray contents = f.readAll();
+
+ if (stage == VertexStage) {
+ m_vert = contents;
+ Q_ASSERT(!m_vert.isEmpty());
+ } else {
+ m_frag = contents;
+ Q_ASSERT(!m_frag.isEmpty());
+ }
+}
+
+static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
+{
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+void SquircleRenderer::init(int framesInFlight)
+{
+ qDebug("init");
+
+ Q_ASSERT(framesInFlight <= 3);
+ m_initialized = true;
+
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ QVulkanInstance *inst = reinterpret_cast<QVulkanInstance *>(
+ rif->getResource(m_window, QSGRendererInterface::VulkanInstanceResource));
+ Q_ASSERT(inst && inst->isValid());
+
+ m_physDev = *reinterpret_cast<VkPhysicalDevice *>(rif->getResource(m_window, QSGRendererInterface::PhysicalDeviceResource));
+ m_dev = *reinterpret_cast<VkDevice *>(rif->getResource(m_window, QSGRendererInterface::DeviceResource));
+ Q_ASSERT(m_physDev && m_dev);
+
+ m_devFuncs = inst->deviceFunctions(m_dev);
+ m_funcs = inst->functions();
+ Q_ASSERT(m_devFuncs && m_funcs);
+
+ VkRenderPass rp = *reinterpret_cast<VkRenderPass *>(
+ rif->getResource(m_window, QSGRendererInterface::RenderPassResource));
+ Q_ASSERT(rp);
+
+ // For simplicity we just use host visible buffers instead of device local + staging.
+
+ VkPhysicalDeviceProperties physDevProps;
+ m_funcs->vkGetPhysicalDeviceProperties(m_physDev, &physDevProps);
+
+ VkPhysicalDeviceMemoryProperties physDevMemProps;
+ m_funcs->vkGetPhysicalDeviceMemoryProperties(m_physDev, &physDevMemProps);
+
+ VkBufferCreateInfo bufferInfo;
+ memset(&bufferInfo, 0, sizeof(bufferInfo));
+ bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufferInfo.size = sizeof(vertices);
+ bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+ VkResult err = m_devFuncs->vkCreateBuffer(m_dev, &bufferInfo, nullptr, &m_vbuf);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create vertex buffer: %d", err);
+
+ VkMemoryRequirements memReq;
+ m_devFuncs->vkGetBufferMemoryRequirements(m_dev, m_vbuf, &memReq);
+ VkMemoryAllocateInfo allocInfo;
+ memset(&allocInfo, 0, sizeof(allocInfo));
+ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ allocInfo.allocationSize = memReq.size;
+
+ uint32_t memTypeIndex = uint32_t(-1);
+ const VkMemoryType *memType = physDevMemProps.memoryTypes;
+ for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) {
+ if (memReq.memoryTypeBits & (1 << i)) {
+ if ((memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
+ && (memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
+ {
+ memTypeIndex = i;
+ break;
+ }
+ }
+ }
+ if (memTypeIndex < 0)
+ qFatal("Failed to find host visible and coherent memory type");
+
+ allocInfo.memoryTypeIndex = memTypeIndex;
+ err = m_devFuncs->vkAllocateMemory(m_dev, &allocInfo, nullptr, &m_vbufMem);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to allocate vertex buffer memory of size %llu: %d", allocInfo.allocationSize, err);
+
+ void *p = nullptr;
+ err = m_devFuncs->vkMapMemory(m_dev, m_vbufMem, 0, allocInfo.allocationSize, 0, &p);
+ if (err != VK_SUCCESS || !p)
+ qFatal("Failed to map vertex buffer memory: %d", err);
+ memcpy(p, vertices, sizeof(vertices));
+ m_devFuncs->vkUnmapMemory(m_dev, m_vbufMem);
+ err = m_devFuncs->vkBindBufferMemory(m_dev, m_vbuf, m_vbufMem, 0);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to bind vertex buffer memory: %d", err);
+
+ // Now have a uniform buffer with enough space for the buffer data for each
+ // (potentially) in-flight frame. (as we will write the contents every
+ // frame, and so would need to wait for command buffer completion if there
+ // was only one, and that would not be nice)
+
+ // Could have three buffers and three descriptor sets, or one buffer and
+ // one descriptor set and dynamic offset. We chose the latter in this
+ // example.
+
+ // We use one memory allocation for all uniform buffers, but then have to
+ // watch out for the buffer offset aligment requirement, which may be as
+ // large as 256 bytes.
+
+ m_allocPerUbuf = aligned(UBUF_SIZE, physDevProps.limits.minUniformBufferOffsetAlignment);
+
+ bufferInfo.size = framesInFlight * m_allocPerUbuf;
+ bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
+ err = m_devFuncs->vkCreateBuffer(m_dev, &bufferInfo, nullptr, &m_ubuf);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create uniform buffer: %d", err);
+ m_devFuncs->vkGetBufferMemoryRequirements(m_dev, m_ubuf, &memReq);
+ memTypeIndex = -1;
+ for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) {
+ if (memReq.memoryTypeBits & (1 << i)) {
+ if ((memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
+ && (memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
+ {
+ memTypeIndex = i;
+ break;
+ }
+ }
+ }
+ if (memTypeIndex < 0)
+ qFatal("Failed to find host visible and coherent memory type");
+
+ allocInfo.allocationSize = framesInFlight * m_allocPerUbuf;
+ allocInfo.memoryTypeIndex = memTypeIndex;
+ err = m_devFuncs->vkAllocateMemory(m_dev, &allocInfo, nullptr, &m_ubufMem);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to allocate uniform buffer memory of size %llu: %d", allocInfo.allocationSize, err);
+
+ err = m_devFuncs->vkBindBufferMemory(m_dev, m_ubuf, m_ubufMem, 0);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to bind uniform buffer memory: %d", err);
+
+ // Now onto the pipeline.
+
+ VkPipelineCacheCreateInfo pipelineCacheInfo;
+ memset(&pipelineCacheInfo, 0, sizeof(pipelineCacheInfo));
+ pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
+ err = m_devFuncs->vkCreatePipelineCache(m_dev, &pipelineCacheInfo, nullptr, &m_pipelineCache);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create pipeline cache: %d", err);
+
+ VkDescriptorSetLayoutBinding descLayoutBinding;
+ memset(&descLayoutBinding, 0, sizeof(descLayoutBinding));
+ descLayoutBinding.binding = 0;
+ descLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
+ descLayoutBinding.descriptorCount = 1;
+ descLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
+ VkDescriptorSetLayoutCreateInfo layoutInfo;
+ memset(&layoutInfo, 0, sizeof(layoutInfo));
+ layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ layoutInfo.bindingCount = 1;
+ layoutInfo.pBindings = &descLayoutBinding;
+ err = m_devFuncs->vkCreateDescriptorSetLayout(m_dev, &layoutInfo, nullptr, &m_resLayout);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create descriptor set layout: %d", err);
+
+ VkPipelineLayoutCreateInfo pipelineLayoutInfo;
+ memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo));
+ pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipelineLayoutInfo.setLayoutCount = 1;
+ pipelineLayoutInfo.pSetLayouts = &m_resLayout;
+ err = m_devFuncs->vkCreatePipelineLayout(m_dev, &pipelineLayoutInfo, nullptr, &m_pipelineLayout);
+ if (err != VK_SUCCESS)
+ qWarning("Failed to create pipeline layout: %d", err);
+
+ VkGraphicsPipelineCreateInfo pipelineInfo;
+ memset(&pipelineInfo, 0, sizeof(pipelineInfo));
+ pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+
+ VkShaderModuleCreateInfo shaderInfo;
+ memset(&shaderInfo, 0, sizeof(shaderInfo));
+ shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+ shaderInfo.codeSize = m_vert.size();
+ shaderInfo.pCode = reinterpret_cast<const quint32 *>(m_vert.constData());
+ VkShaderModule vertShaderModule;
+ err = m_devFuncs->vkCreateShaderModule(m_dev, &shaderInfo, nullptr, &vertShaderModule);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create vertex shader module: %d", err);
+
+ shaderInfo.codeSize = m_frag.size();
+ shaderInfo.pCode = reinterpret_cast<const quint32 *>(m_frag.constData());
+ VkShaderModule fragShaderModule;
+ err = m_devFuncs->vkCreateShaderModule(m_dev, &shaderInfo, nullptr, &fragShaderModule);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create fragment shader module: %d", err);
+
+ VkPipelineShaderStageCreateInfo stageInfo[2];
+ memset(&stageInfo, 0, sizeof(stageInfo));
+ stageInfo[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ stageInfo[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
+ stageInfo[0].module = vertShaderModule;
+ stageInfo[0].pName = "main";
+ stageInfo[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ stageInfo[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
+ stageInfo[1].module = fragShaderModule;
+ stageInfo[1].pName = "main";
+ pipelineInfo.stageCount = 2;
+ pipelineInfo.pStages = stageInfo;
+
+ VkVertexInputBindingDescription vertexBinding = {
+ 0, // binding
+ 2 * sizeof(float), // stride
+ VK_VERTEX_INPUT_RATE_VERTEX
+ };
+ VkVertexInputAttributeDescription vertexAttr = {
+ 0, // location
+ 0, // binding
+ VK_FORMAT_R32G32_SFLOAT, // 'vertices' only has 2 floats per vertex
+ 0 // offset
+ };
+ VkPipelineVertexInputStateCreateInfo vertexInputInfo;
+ memset(&vertexInputInfo, 0, sizeof(vertexInputInfo));
+ vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ vertexInputInfo.vertexBindingDescriptionCount = 1;
+ vertexInputInfo.pVertexBindingDescriptions = &vertexBinding;
+ vertexInputInfo.vertexAttributeDescriptionCount = 1;
+ vertexInputInfo.pVertexAttributeDescriptions = &vertexAttr;
+ pipelineInfo.pVertexInputState = &vertexInputInfo;
+
+ VkDynamicState dynStates[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
+ VkPipelineDynamicStateCreateInfo dynamicInfo;
+ memset(&dynamicInfo, 0, sizeof(dynamicInfo));
+ dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+ dynamicInfo.dynamicStateCount = 2;
+ dynamicInfo.pDynamicStates = dynStates;
+ pipelineInfo.pDynamicState = &dynamicInfo;
+
+ VkPipelineViewportStateCreateInfo viewportInfo;
+ memset(&viewportInfo, 0, sizeof(viewportInfo));
+ viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ viewportInfo.viewportCount = viewportInfo.scissorCount = 1;
+ pipelineInfo.pViewportState = &viewportInfo;
+
+ VkPipelineInputAssemblyStateCreateInfo iaInfo;
+ memset(&iaInfo, 0, sizeof(iaInfo));
+ iaInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ iaInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
+ pipelineInfo.pInputAssemblyState = &iaInfo;
+
+ VkPipelineRasterizationStateCreateInfo rsInfo;
+ memset(&rsInfo, 0, sizeof(rsInfo));
+ rsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+ rsInfo.lineWidth = 1.0f;
+ pipelineInfo.pRasterizationState = &rsInfo;
+
+ VkPipelineMultisampleStateCreateInfo msInfo;
+ memset(&msInfo, 0, sizeof(msInfo));
+ msInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ msInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+ pipelineInfo.pMultisampleState = &msInfo;
+
+ VkPipelineDepthStencilStateCreateInfo dsInfo;
+ memset(&dsInfo, 0, sizeof(dsInfo));
+ dsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+ pipelineInfo.pDepthStencilState = &dsInfo;
+
+ // SrcAlpha, One
+ VkPipelineColorBlendStateCreateInfo blendInfo;
+ memset(&blendInfo, 0, sizeof(blendInfo));
+ blendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+ VkPipelineColorBlendAttachmentState blend;
+ memset(&blend, 0, sizeof(blend));
+ blend.blendEnable = true;
+ blend.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
+ blend.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
+ blend.colorBlendOp = VK_BLEND_OP_ADD;
+ blend.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
+ blend.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
+ blend.alphaBlendOp = VK_BLEND_OP_ADD;
+ blend.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT
+ | VK_COLOR_COMPONENT_A_BIT;
+ blendInfo.attachmentCount = 1;
+ blendInfo.pAttachments = &blend;
+ pipelineInfo.pColorBlendState = &blendInfo;
+
+ pipelineInfo.layout = m_pipelineLayout;
+
+ pipelineInfo.renderPass = rp;
+
+ err = m_devFuncs->vkCreateGraphicsPipelines(m_dev, m_pipelineCache, 1, &pipelineInfo, nullptr, &m_pipeline);
+
+ m_devFuncs->vkDestroyShaderModule(m_dev, vertShaderModule, nullptr);
+ m_devFuncs->vkDestroyShaderModule(m_dev, fragShaderModule, nullptr);
+
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create graphics pipeline: %d", err);
+
+ // Now just need some descriptors.
+ VkDescriptorPoolSize descPoolSizes[] = {
+ { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1 }
+ };
+ VkDescriptorPoolCreateInfo descPoolInfo;
+ memset(&descPoolInfo, 0, sizeof(descPoolInfo));
+ descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+ descPoolInfo.flags = 0; // won't use vkFreeDescriptorSets
+ descPoolInfo.maxSets = 1;
+ descPoolInfo.poolSizeCount = sizeof(descPoolSizes) / sizeof(descPoolSizes[0]);
+ descPoolInfo.pPoolSizes = descPoolSizes;
+ err = m_devFuncs->vkCreateDescriptorPool(m_dev, &descPoolInfo, nullptr, &m_descriptorPool);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create descriptor pool: %d", err);
+
+ VkDescriptorSetAllocateInfo descAllocInfo;
+ memset(&descAllocInfo, 0, sizeof(descAllocInfo));
+ descAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ descAllocInfo.descriptorPool = m_descriptorPool;
+ descAllocInfo.descriptorSetCount = 1;
+ descAllocInfo.pSetLayouts = &m_resLayout;
+ err = m_devFuncs->vkAllocateDescriptorSets(m_dev, &descAllocInfo, &m_ubufDescriptor);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to allocate descriptor set");
+
+ VkWriteDescriptorSet writeInfo;
+ memset(&writeInfo, 0, sizeof(writeInfo));
+ writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ writeInfo.dstSet = m_ubufDescriptor;
+ writeInfo.dstBinding = 0;
+ writeInfo.descriptorCount = 1;
+ writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
+ VkDescriptorBufferInfo bufInfo;
+ bufInfo.buffer = m_ubuf;
+ bufInfo.offset = 0; // dynamic offset is used so this is ignored
+ bufInfo.range = UBUF_SIZE;
+ writeInfo.pBufferInfo = &bufInfo;
+ m_devFuncs->vkUpdateDescriptorSets(m_dev, 1, &writeInfo, 0, nullptr);
+}
+
+#include "vulkansquircle.moc"
diff --git a/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.h b/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.h
new file mode 100644
index 0000000000..7e65d01a15
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications 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$
+**
+****************************************************************************/
+
+#ifndef VULKANSQUIRCLE_H
+#define VULKANSQUIRCLE_H
+
+#include <QtQuick/QQuickItem>
+
+class SquircleRenderer;
+
+class VulkanSquircle : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged)
+
+public:
+ VulkanSquircle();
+
+ qreal t() const { return m_t; }
+ void setT(qreal t);
+
+signals:
+ void tChanged();
+
+public slots:
+ void sync();
+ void cleanup();
+
+private slots:
+ void handleWindowChanged(QQuickWindow *win);
+
+private:
+ void releaseResources() override;
+
+ qreal m_t = 0;
+ SquircleRenderer *m_renderer = nullptr;
+};
+
+#endif
diff --git a/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.pro b/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.pro
new file mode 100644
index 0000000000..9a0a87c9f0
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.pro
@@ -0,0 +1,8 @@
+QT += qml quick
+
+HEADERS += vulkansquircle.h
+SOURCES += vulkansquircle.cpp main.cpp
+RESOURCES += vulkanunderqml.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/vulkanunderqml
+INSTALLS += target
diff --git a/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.qrc b/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.qrc
new file mode 100644
index 0000000000..c85be0f238
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/scenegraph/vulkanunderqml">
+ <file>main.qml</file>
+ <file>squircle.vert.spv</file>
+ <file>squircle.frag.spv</file>
+ </qresource>
+</RCC>
diff --git a/src/imports/testlib/main.cpp b/src/imports/testlib/main.cpp
index 33fe912692..c52cf417a6 100644
--- a/src/imports/testlib/main.cpp
+++ b/src/imports/testlib/main.cpp
@@ -150,7 +150,7 @@ public:
qmlRegisterType<QuickTestEvent>(uri,1,0,"TestEvent");
qmlRegisterType<QuickTestEvent>(uri,1,2,"TestEvent");
qmlRegisterType<QuickTestUtil>(uri,1,0,"TestUtil");
- qmlRegisterAnonymousType<QQuickTouchEventSequence>(uri);
+ qmlRegisterAnonymousType<QQuickTouchEventSequence>(uri, 1);
// Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward
qmlRegisterModule(uri, 1, QT_VERSION_MINOR);
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index 7578de4d14..31c90b31f6 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -151,6 +151,19 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
quintptr reserved3;
ReturnedValue (*getterTrampoline)(Lookup *l, ExecutionEngine *engine);
} qmlContextGlobalLookup;
+ struct {
+ Heap::Object *qmlTypeWrapper;
+ quintptr unused2;
+ } qmlTypeLookup;
+ struct {
+ Heap::InternalClass *ic;
+ quintptr unused;
+ ReturnedValue encodedEnumValue;
+ } qmlEnumValueLookup;
+ struct {
+ Heap::InternalClass *ic;
+ Heap::Object *qmlScopedEnumWrapper;
+ } qmlScopedEnumWrapperLookup;
};
uint nameIndex;
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index c832bff051..e2d3b98ff6 100644
--- a/src/qml/jsruntime/qv4qmlcontext.cpp
+++ b/src/qml/jsruntime/qv4qmlcontext.cpp
@@ -248,11 +248,15 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r
return lookup->qmlContextPropertyGetter(lookup, v4, base);
}
}
- return QQmlTypeWrapper::create(v4, scopeObject, r.type);
+ result = QQmlTypeWrapper::create(v4, scopeObject, r.type);
} else if (r.importNamespace) {
- return QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace);
+ result = QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace);
}
- Q_ASSERT(!"Unreachable");
+ if (lookup) {
+ lookup->qmlTypeLookup.qmlTypeWrapper = static_cast<Heap::Object*>(result->heapObject());
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupType;
+ }
+ return result->asReturnedValue();
}
// Fall through
@@ -659,6 +663,27 @@ ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, Exec
return Encode::undefined();
}
+ReturnedValue QQmlContextWrapper::lookupType(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Scope scope(engine);
+ Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
+ if (!qmlContext)
+ return QV4::Encode::undefined();
+
+ QObject *scopeObject = qmlContext->qmlScope();
+ if (scopeObject && QQmlData::wasDeleted(scopeObject))
+ return QV4::Encode::undefined();
+
+ Heap::Object *heapObject = l->qmlTypeLookup.qmlTypeWrapper;
+ if (static_cast<Heap::QQmlTypeWrapper *>(heapObject)->object != scopeObject) {
+ l->qmlTypeLookup.qmlTypeWrapper = nullptr;
+ l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
+ return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
+ }
+
+ return Value::fromHeapObject(heapObject).asReturnedValue();
+}
+
void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml)
{
Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext);
diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h
index 4c8287ef2f..e3e7239fe5 100644
--- a/src/qml/jsruntime/qv4qmlcontext_p.h
+++ b/src/qml/jsruntime/qv4qmlcontext_p.h
@@ -112,6 +112,7 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object
static ReturnedValue lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupType(Lookup *l, ExecutionEngine *engine, Value *base);
};
struct Q_QML_EXPORT QmlContext : public ExecutionContext
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 8a7cbdfb2a..aaa198c62a 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -2050,7 +2050,7 @@ ReturnedValue Runtime::Exp::call(const Value &base, const Value &exp)
double b = base.toNumber();
double e = exp.toNumber();
if (qt_is_inf(e) && (b == 1 || b == -1))
- return Encode(qt_snan());
+ return Encode(qt_qnan());
return Encode(pow(b,e));
}
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index b4c34d60fa..4d099d2e0f 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -1247,7 +1247,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
double base = left.toNumber();
double exp = ACC.toNumber();
if (qIsInf(exp) && (base == 1 || base == -1))
- acc = Encode(qSNaN());
+ acc = Encode(qQNaN());
else
acc = Encode(pow(base,exp));
MOTH_END_INSTR(Exp)
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h
index a93b012c70..6295345fa4 100644
--- a/src/qml/qml/qqml.h
+++ b/src/qml/qml/qqml.h
@@ -102,7 +102,7 @@ class QQmlPropertyValueInterceptor;
void Q_QML_EXPORT qmlClearTypeRegistrations();
template<typename T>
-int qmlRegisterAnonymousType(const char *uri, int versionMajor=1)
+int qmlRegisterAnonymousType(const char *uri, int versionMajor)
{
QML_GETTYPENAMES
@@ -136,7 +136,7 @@ int qmlRegisterAnonymousType(const char *uri, int versionMajor=1)
template<typename T>
QT_DEPRECATED_VERSION_X_5_14("Use qmlRegisterAnonymousType instead") int qmlRegisterType()
{
- return qmlRegisterAnonymousType<T>("");
+ return qmlRegisterAnonymousType<T>("", 1);
}
int Q_QML_EXPORT qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message);
diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h
index 136159993a..96891af416 100644
--- a/src/qml/qml/qqmlglobal_p.h
+++ b/src/qml/qml/qqmlglobal_p.h
@@ -98,11 +98,11 @@ QT_BEGIN_NAMESPACE
static int signalIdx = -1; \
static int methodIdx = -1; \
if (signalIdx < 0) { \
- Q_ASSERT(((int)(*signal) - '0') == QSIGNAL_CODE); \
+ Q_ASSERT((int(*signal) - '0') == QSIGNAL_CODE); \
signalIdx = SenderType::staticMetaObject.indexOfSignal(signal+1); \
} \
if (methodIdx < 0) { \
- int code = ((int)(*method) - '0'); \
+ int code = (int(*method) - '0'); \
Q_ASSERT(code == QSLOT_CODE || code == QSIGNAL_CODE); \
if (code == QSLOT_CODE) \
methodIdx = ReceiverType::staticMetaObject.indexOfSlot(method+1); \
@@ -137,11 +137,11 @@ QT_BEGIN_NAMESPACE
static int signalIdx = -1; \
static int methodIdx = -1; \
if (signalIdx < 0) { \
- Q_ASSERT(((int)(*signal) - '0') == QSIGNAL_CODE); \
+ Q_ASSERT((int(*signal) - '0') == QSIGNAL_CODE); \
signalIdx = SenderType::staticMetaObject.indexOfSignal(signal+1); \
} \
if (methodIdx < 0) { \
- int code = ((int)(*method) - '0'); \
+ int code = (int(*method) - '0'); \
Q_ASSERT(code == QSLOT_CODE || code == QSIGNAL_CODE); \
if (code == QSLOT_CODE) \
methodIdx = ReceiverType::staticMetaObject.indexOfSlot(method+1); \
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index 2c641d3845..c2674b402a 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -739,9 +739,16 @@ QQmlType QQmlMetaType::typeForUrl(const QString &urlString,
const QUrl url = QQmlTypeLoader::normalize(QUrl(urlString));
QQmlMetaTypeDataPtr data;
- QQmlType ret(data->urlToType.value(url));
- if (ret.isValid() && ret.sourceUrl() == url)
- return ret;
+ {
+ QQmlType ret(data->urlToType.value(url));
+ if (ret.isValid() && ret.sourceUrl() == url)
+ return ret;
+ }
+ {
+ QQmlType ret(data->urlToNonFileImportType.value(url));
+ if (ret.isValid() && ret.sourceUrl() == url)
+ return ret;
+ }
const int dot = qualifiedType.indexOf(QLatin1Char('.'));
const QString typeName = dot < 0
diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h
index 9c7a69d571..def4480198 100644
--- a/src/qml/qml/qqmlpropertycachecreator_p.h
+++ b/src/qml/qml/qqmlpropertycachecreator_p.h
@@ -256,17 +256,8 @@ inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContaine
Q_ASSERT(typeRef);
QQmlType qmltype = typeRef->type;
if (!qmltype.isValid()) {
- QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex);
- if (imports->resolveType(propertyName, &qmltype, nullptr, nullptr, nullptr)) {
- if (qmltype.isComposite()) {
- QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
- Q_ASSERT(tdata);
- Q_ASSERT(tdata->isComplete());
-
- auto compilationUnit = tdata->compilationUnit();
- qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
- }
- }
+ imports->resolveType(stringAt(context.instantiatingBinding->propertyNameIndex),
+ &qmltype, nullptr, nullptr, nullptr);
}
const QMetaObject *attachedMo = qmltype.attachedPropertiesType(enginePrivate);
diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp
index 252ff26a64..3615749da1 100644
--- a/src/qml/qml/qqmltype.cpp
+++ b/src/qml/qml/qqmltype.cpp
@@ -154,10 +154,10 @@ bool QQmlType::availableInVersion(const QHashedStringRef &module, int vmajor, in
return module == d->module && vmajor == d->version_maj && vminor >= d->version_min;
}
-QQmlType QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const
+QQmlType QQmlTypePrivate::resolveCompositeBaseType(QQmlEnginePrivate *engine) const
{
Q_ASSERT(isComposite());
- if (!engine || !d)
+ if (!engine)
return QQmlType();
QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()));
if (td.isNull() || !td->isComplete())
@@ -167,7 +167,7 @@ QQmlType QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const
return QQmlMetaType::qmlType(mo);
}
-QQmlPropertyCache *QQmlType::compositePropertyCache(QQmlEnginePrivate *engine) const
+QQmlPropertyCache *QQmlTypePrivate::compositePropertyCache(QQmlEnginePrivate *engine) const
{
// similar logic to resolveCompositeBaseType
Q_ASSERT(isComposite());
@@ -262,24 +262,30 @@ void QQmlTypePrivate::init() const
lock.unlock();
}
-void QQmlTypePrivate::initEnums(const QQmlPropertyCache *cache) const
+void QQmlTypePrivate::initEnums(QQmlEnginePrivate *engine) const
{
- if ((isEnumFromBaseSetup || !baseMetaObject)
- && (isEnumFromCacheSetup || !cache)) {
+ const QQmlPropertyCache *cache = (!isEnumFromCacheSetup && isComposite())
+ ? compositePropertyCache(engine)
+ : nullptr;
+
+ const QMetaObject *metaObject = !isEnumFromCacheSetup
+ ? baseMetaObject // beware: It could be a singleton type without metaobject
+ : nullptr;
+
+ if (!cache && !metaObject)
return;
- }
init();
QMutexLocker lock(QQmlMetaType::typeRegistrationLock());
- if (!isEnumFromCacheSetup && cache) {
+ if (cache) {
insertEnumsFromPropertyCache(cache);
isEnumFromCacheSetup = true;
}
- if (!isEnumFromBaseSetup && baseMetaObject) { // could be singleton type without metaobject
- insertEnums(baseMetaObject);
+ if (metaObject) {
+ insertEnums(metaObject);
isEnumFromBaseSetup = true;
}
}
@@ -548,7 +554,7 @@ bool QQmlType::isInterface() const
bool QQmlType::isComposite() const
{
- return d && (d->regType == CompositeType || d->regType == CompositeSingletonType);
+ return d && d->isComposite();
}
bool QQmlType::isCompositeSingleton() const
@@ -617,7 +623,7 @@ QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction(QQmlEnginePrivat
QQmlType base;
if (d->regType == CompositeType)
- base = resolveCompositeBaseType(engine);
+ base = d->resolveCompositeBaseType(engine);
return base.attachedPropertiesFunction(engine);
}
@@ -630,7 +636,7 @@ const QMetaObject *QQmlType::attachedPropertiesType(QQmlEnginePrivate *engine) c
QQmlType base;
if (d->regType == CompositeType)
- base = resolveCompositeBaseType(engine);
+ base = d->resolveCompositeBaseType(engine);
return base.attachedPropertiesType(engine);
}
@@ -649,7 +655,7 @@ int QQmlType::attachedPropertiesId(QQmlEnginePrivate *engine) const
QQmlType base;
if (d->regType == CompositeType)
- base = resolveCompositeBaseType(engine);
+ base = d->resolveCompositeBaseType(engine);
return base.attachedPropertiesId(engine);
}
#endif
@@ -689,24 +695,16 @@ int QQmlType::index() const
QUrl QQmlType::sourceUrl() const
{
- if (d) {
- if (d->regType == CompositeType)
- return d->extraData.fd->url;
- else if (d->regType == CompositeSingletonType)
- return d->extraData.sd->singletonInstanceInfo->url;
- }
- return QUrl();
+ return d ? d->sourceUrl() : QUrl();
}
int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, bool *ok) const
{
Q_ASSERT(ok);
if (d) {
- const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr;
-
*ok = true;
- d->initEnums(cache);
+ d->initEnums(engine);
int *rv = d->enums.value(name);
if (rv)
@@ -721,11 +719,9 @@ int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &name
{
Q_ASSERT(ok);
if (d) {
- const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr;
-
*ok = true;
- d->initEnums(cache);
+ d->initEnums(engine);
int *rv = d->enums.value(name);
if (rv)
@@ -740,10 +736,9 @@ int QQmlType::enumValue(QQmlEnginePrivate *engine, const QV4::String *name, bool
{
Q_ASSERT(ok);
if (d) {
- const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr;
*ok = true;
- d->initEnums(cache);
+ d->initEnums(engine);
int *rv = d->enums.value(name);
if (rv)
@@ -758,10 +753,9 @@ int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *name
{
Q_ASSERT(ok);
if (d) {
- const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr;
*ok = true;
- d->initEnums(cache);
+ d->initEnums(engine);
int *rv = d->scopedEnumIndex.value(name);
if (rv)
@@ -776,10 +770,9 @@ int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QString &name, bo
{
Q_ASSERT(ok);
if (d) {
- const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr;
*ok = true;
- d->initEnums(cache);
+ d->initEnums(engine);
int *rv = d->scopedEnumIndex.value(name);
if (rv)
@@ -828,10 +821,9 @@ int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scope
{
Q_ASSERT(ok);
if (d) {
- const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr;
*ok = true;
- d->initEnums(cache);
+ d->initEnums(engine);
int *rv = d->scopedEnumIndex.value(QHashedCStringRef(scopedEnumName.constData(), scopedEnumName.length()));
if (rv) {
@@ -851,10 +843,9 @@ int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scope
{
Q_ASSERT(ok);
if (d) {
- const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr;
*ok = true;
- d->initEnums(cache);
+ d->initEnums(engine);
int *rv = d->scopedEnumIndex.value(QHashedStringRef(scopedEnumName));
if (rv) {
diff --git a/src/qml/qml/qqmltype_p.h b/src/qml/qml/qqmltype_p.h
index 4dec20600b..ec27b38a73 100644
--- a/src/qml/qml/qqmltype_p.h
+++ b/src/qml/qml/qqmltype_p.h
@@ -182,11 +182,7 @@ public:
};
private:
- QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const;
- int resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const;
- QQmlPropertyCache *compositePropertyCache(QQmlEnginePrivate *engine) const;
friend uint qHash(const QQmlType &t, uint seed);
-
QQmlRefPointer<const QQmlTypePrivate> d;
};
diff --git a/src/qml/qml/qqmltype_p_p.h b/src/qml/qml/qqmltype_p_p.h
index d381e11df4..6a2d961de8 100644
--- a/src/qml/qml/qqmltype_p_p.h
+++ b/src/qml/qml/qqmltype_p_p.h
@@ -66,10 +66,30 @@ public:
QQmlTypePrivate(QQmlType::RegistrationType type);
void init() const;
- void initEnums(const QQmlPropertyCache *cache = nullptr) const;
+ void initEnums(QQmlEnginePrivate *engine) const;
void insertEnums(const QMetaObject *metaObject) const;
void insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const;
+ QUrl sourceUrl() const
+ {
+ switch (regType) {
+ case QQmlType::CompositeType:
+ return extraData.fd->url;
+ case QQmlType::CompositeSingletonType:
+ return extraData.sd->singletonInstanceInfo->url;
+ default:
+ return QUrl();
+ }
+ }
+
+ bool isComposite() const
+ {
+ return regType == QQmlType::CompositeType || regType == QQmlType::CompositeSingletonType;
+ }
+
+ QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const;
+ QQmlPropertyCache *compositePropertyCache(QQmlEnginePrivate *engine) const;
+
QQmlType::RegistrationType regType;
struct QQmlCppTypeData
diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp
index e7633a1bba..9a6bd73326 100644
--- a/src/qml/qml/qqmltypecompiler.cpp
+++ b/src/qml/qml/qqmltypecompiler.cpp
@@ -328,18 +328,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex);
auto *typeRef = resolvedType(binding->propertyNameIndex);
QQmlType type = typeRef ? typeRef->type : QQmlType();
- if (!type.isValid()) {
- if (imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr)) {
- if (type.isComposite()) {
- QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(type.sourceUrl());
- Q_ASSERT(tdata);
- Q_ASSERT(tdata->isComplete());
-
- auto compilationUnit = tdata->compilationUnit();
- type = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
- }
- }
- }
+ if (!type.isValid())
+ imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr);
const QMetaObject *attachedType = type.attachedPropertiesType(enginePrivate);
if (!attachedType)
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 3a18bbf7c9..42e7d2c4b4 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -964,8 +964,10 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path)
bool QQmlTypeLoader::fileExists(const QString &path, const QString &file)
{
- if (path.isEmpty())
+ const QChar nullChar(QChar::Null);
+ if (path.isEmpty() || path.contains(nullChar) || file.isEmpty() || file.contains(nullChar))
return false;
+
Q_ASSERT(path.endsWith(QLatin1Char('/')));
if (path.at(0) == QLatin1Char(':')) {
// qrc resource
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp
index 931f37b35a..ef4a628a04 100644
--- a/src/qml/qml/qqmltypewrapper.cpp
+++ b/src/qml/qml/qqmltypewrapper.cpp
@@ -476,6 +476,34 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object,
}
// Fall through to base implementation
}
+
+ if (name->startsWithUpper()) {
+ bool ok = false;
+ int value = type.enumValue(QQmlEnginePrivate::get(engine->qmlEngine()), name, &ok);
+ if (ok) {
+ lookup->qmlEnumValueLookup.ic = This->internalClass();
+ lookup->qmlEnumValueLookup.encodedEnumValue
+ = QV4::Value::fromInt32(value).asReturnedValue();
+ lookup->getter = QQmlTypeWrapper::lookupEnumValue;
+ return lookup->getter(lookup, engine, *object);
+ }
+
+ value = type.scopedEnumIndex(QQmlEnginePrivate::get(engine->qmlEngine()), name, &ok);
+ if (ok) {
+ Scoped<QQmlScopedEnumWrapper> enumWrapper(
+ scope, engine->memoryManager->allocate<QQmlScopedEnumWrapper>());
+ enumWrapper->d()->typePrivate = type.priv();
+ QQmlType::refHandle(enumWrapper->d()->typePrivate);
+ enumWrapper->d()->scopeEnumIndex = value;
+
+ lookup->qmlScopedEnumWrapperLookup.ic = This->internalClass();
+ lookup->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper
+ = static_cast<Heap::Object*>(enumWrapper->heapObject());
+ lookup->getter = QQmlTypeWrapper::lookupScopedEnum;
+ return enumWrapper.asReturnedValue();
+ }
+ // Fall through to base implementation
+ }
// Fall through to base implementation
}
return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
@@ -519,6 +547,34 @@ ReturnedValue QQmlTypeWrapper::lookupSingletonProperty(Lookup *l, ExecutionEngin
return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup);
}
+ReturnedValue QQmlTypeWrapper::lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base)
+{
+ auto *o = static_cast<Heap::Object *>(base.heapObject());
+ if (!o || o->internalClass != l->qmlEnumValueLookup.ic) {
+ l->getter = Lookup::getterGeneric;
+ return Lookup::getterGeneric(l, engine, base);
+ }
+
+ return l->qmlEnumValueLookup.encodedEnumValue;
+}
+
+ReturnedValue QQmlTypeWrapper::lookupScopedEnum(Lookup *l, ExecutionEngine *engine, const Value &base)
+{
+ Scope scope(engine);
+ Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, static_cast<Heap::QQmlScopedEnumWrapper *>(
+ l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper));
+
+ auto *o = static_cast<Heap::Object *>(base.heapObject());
+ if (!o || o->internalClass != l->qmlScopedEnumWrapperLookup.ic) {
+ QQmlType::derefHandle(enumWrapper->d()->typePrivate);
+ l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper = nullptr;
+ l->getter = Lookup::getterGeneric;
+ return Lookup::getterGeneric(l, engine, base);
+ }
+
+ return enumWrapper.asReturnedValue();
+}
+
void Heap::QQmlScopedEnumWrapper::destroy()
{
QQmlType::derefHandle(typePrivate);
diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h
index 6b51f421b3..7dc3f55310 100644
--- a/src/qml/qml/qqmltypewrapper_p.h
+++ b/src/qml/qml/qqmltypewrapper_p.h
@@ -115,6 +115,8 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object
static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
static ReturnedValue lookupSingletonProperty(Lookup *l, ExecutionEngine *engine, const Value &base);
+ static ReturnedValue lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base);
+ static ReturnedValue lookupScopedEnum(Lookup *l, ExecutionEngine *engine, const Value &base);
protected:
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
index 2e213e7dc3..355150b786 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -1941,30 +1941,32 @@ ReturnedValue GlobalExtensions::method_qsTr(const FunctionObject *b, const Value
THROW_GENERIC_ERROR("qsTr(): third argument (n) must be a number");
QString context;
- if (QQmlContextData *ctxt = scope.engine->callingQmlContext()) {
- QString path = ctxt->urlString();
- int lastSlash = path.lastIndexOf(QLatin1Char('/'));
- int lastDot = path.lastIndexOf(QLatin1Char('.'));
- int length = lastDot - (lastSlash + 1);
- context = (lastSlash > -1) ? path.mid(lastSlash + 1, (length > -1) ? length : -1) : QString();
- } else {
- CppStackFrame *frame = scope.engine->currentStackFrame;
- // The first non-empty source URL in the call stack determines the translation context.
- while (frame && context.isEmpty()) {
- if (CompiledData::CompilationUnitBase *baseUnit = frame->v4Function->compilationUnit) {
- const auto *unit = static_cast<const CompiledData::CompilationUnit *>(baseUnit);
- QString fileName = unit->fileName();
- QUrl url(unit->fileName());
- if (url.isValid() && url.isRelative()) {
- context = url.fileName();
- } else {
- context = QQmlFile::urlToLocalFileOrQrc(fileName);
- if (context.isEmpty() && fileName.startsWith(QLatin1String(":/")))
- context = fileName;
- }
- context = QFileInfo(context).baseName();
+ CppStackFrame *frame = scope.engine->currentStackFrame;
+ // The first non-empty source URL in the call stack determines the translation context.
+ while (frame && context.isEmpty()) {
+ if (CompiledData::CompilationUnitBase *baseUnit = frame->v4Function->compilationUnit) {
+ const auto *unit = static_cast<const CompiledData::CompilationUnit *>(baseUnit);
+ QString fileName = unit->fileName();
+ QUrl url(unit->fileName());
+ if (url.isValid() && url.isRelative()) {
+ context = url.fileName();
+ } else {
+ context = QQmlFile::urlToLocalFileOrQrc(fileName);
+ if (context.isEmpty() && fileName.startsWith(QLatin1String(":/")))
+ context = fileName;
}
- frame = frame->parent;
+ context = QFileInfo(context).baseName();
+ }
+ frame = frame->parent;
+ }
+
+ if (context.isEmpty()) {
+ if (QQmlContextData *ctxt = scope.engine->callingQmlContext()) {
+ QString path = ctxt->urlString();
+ int lastSlash = path.lastIndexOf(QLatin1Char('/'));
+ int lastDot = path.lastIndexOf(QLatin1Char('.'));
+ int length = lastDot - (lastSlash + 1);
+ context = (lastSlash > -1) ? path.mid(lastSlash + 1, (length > -1) ? length : -1) : QString();
}
}
diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
index b96d09996d..864d90274c 100644
--- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
@@ -341,9 +341,15 @@ OpenGL application is allowed to draw.
The \l {Scene Graph - OpenGL Under QML} example gives an example on
how to use these signals using OpenGL.
+The \l {Scene Graph - Direct3D 11 Under QML} example gives an example on
+how to use these signals using Direct3D.
+
The \l {Scene Graph - Metal Under QML} example gives an example on
how to use these signals using Metal.
+The \l {Scene Graph - Vulkan Under QML} example gives an example on
+how to use these signals using Vulkan.
+
The other alternative, only available for OpenGL currently, is to create a
QQuickFramebufferObject, render into it, and let it be displayed in the scene
graph as a texture. The \l {Scene Graph - Rendering FBOs} example shows how
@@ -352,6 +358,13 @@ and multiple threads to create content to be displayed in the scene graph. The
\l {Scene Graph - Rendering FBOs in a thread} examples show how this can be
done.
+Graphics APIs other than OpenGL can also follow this approach, even though
+QQuickFramebufferObject does not currently support them. Creating and rendering
+to a texture directly with the underlying API, followed by wrapping and using
+this resource in a Qt Quick scene in a custom QQuickItem, is demonstrated in
+the \l {Scene Graph - Metal Texture Import} example. That example uses Metal,
+the concepts however apply to all other graphics APIs as well.
+
\warning When mixing OpenGL content with scene graph rendering, it is
important the application does not leave the OpenGL context in a state
with buffers bound, attributes enabled, special values in the z-buffer
diff --git a/src/quick/doc/src/examples.qdoc b/src/quick/doc/src/examples.qdoc
index 9034c90eb8..8c31e13a2d 100644
--- a/src/quick/doc/src/examples.qdoc
+++ b/src/quick/doc/src/examples.qdoc
@@ -171,16 +171,17 @@ Creator.
\div {class="doc-column"}
\b{Scene Graph}
\list
- \li \l{Scene Graph - OpenGL Under QML}{OpenGL Under QML}
+ \li \l{Scene Graph - Custom Geometry}{Custom Geometry}
\li \l{Scene Graph - Metal Under QML}{Metal Under QML}
+ \li \l{Scene Graph - Metal Texture Import}{Metal Texture Import}
+ \li \l{Scene Graph - OpenGL Under QML}{OpenGL Under QML}
\li \l{Scene Graph - Direct3D 11 Under QML}{Direct3D 11 Under QML}
+ \li \l{Scene Graph - Custom Rendering with QSGRenderNode}{Render Node}
\li \l{Scene Graph - Painted Item}{Painted Item}
- \li \l{Scene Graph - Custom Geometry}{Custom Geometry}
\li \l{Scene Graph - Graph}{Graph}
\li \l{Scene Graph - Simple Material}{Simple Material}
\li \l{Scene Graph - Rendering FBOs}{Rendering FBOs}
\li \l{Scene Graph - Rendering FBOs in a thread}{Rendering FBOs in a thread}
- \li \l{Scene Graph - Custom Rendering with QSGRenderNode}{Render Node}
\endlist
\enddiv
\enddiv
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 27e2a4572e..6fe2891242 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -4193,7 +4193,11 @@ QQmlIncubationController *QQuickWindow::incubationController() const
command buffer, before via QSGRendererInterface. Note however that the
render pass (or passes) are already recorded at this point and it is not
possible to add more commands within the scenegraph's pass. Instead, use
- afterRenderPassRecording() for that.
+ afterRenderPassRecording() for that. This signal has therefore limited use
+ and is rarely needed in an RHI-based setup. Rather, it is the combination
+ of beforeRendering() + beforeRenderPassRecording() or beforeRendering() +
+ afterRenderPassRecording() that is typically used to achieve under- or
+ overlaying of the custom rendering.
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
@@ -4229,7 +4233,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
\note Resource updates (uploads, copies) typically cannot be enqueued from
within a render pass. Therefore, more complex user rendering will need to
- connect to both the beforeRendering() and this signals.
+ connect to both beforeRendering() and this signal.
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
@@ -4260,7 +4264,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
\note Resource updates (uploads, copies) typically cannot be enqueued from
within a render pass. Therefore, more complex user rendering will need to
- connect to both the beforeRendering() and this signals.
+ connect to both beforeRendering() and this signal.
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
@@ -4773,6 +4777,17 @@ const QQuickWindow::GraphicsStateInfo *QQuickWindow::graphicsStateInfo()
because the scene graph performs the necessary steps implicitly for render
nodes.
+ Native graphics objects (such as, graphics device, command buffer or
+ encoder) are accessible via QSGRendererInterface::getResource().
+
+ \warning Watch out for the fact that
+ QSGRendererInterface::CommandListResource may return a different object
+ between beginExternalCommands() - endExternalCommands(). This can happen
+ when the underlying implementation provides a dedicated secondary command
+ buffer for recording external graphics commands within a render pass.
+ Therefore, always query CommandListResource after calling this function. Do
+ not attempt to reuse an object from an earlier query.
+
\note This function has no effect when the scene graph is using OpenGL
directly and the RHI graphics abstraction layer is not in use. Refer to
resetOpenGLState() in that case.
diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
index e504fe1c62..bc68199e08 100644
--- a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
@@ -134,6 +134,10 @@ QT_BEGIN_NAMESPACE
\value VulkanInstanceResource The resource is a pointer to the
QVulkanInstance used by the scenegraph, when applicable.
+
+ \value RenderPassResource The resource is a pointer to the render pass used
+ by the scenegraph, describing the color and depth/stecil attachments and
+ how they are used. For example, a \c{VkRenderPass *}.
*/
/*!
diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.h b/src/quick/scenegraph/coreapi/qsgrendererinterface.h
index 3052c81f6c..7aa7d0e769 100644
--- a/src/quick/scenegraph/coreapi/qsgrendererinterface.h
+++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.h
@@ -72,7 +72,8 @@ public:
OpenGLContextResource,
DeviceContextResource,
CommandEncoderResource,
- VulkanInstanceResource
+ VulkanInstanceResource,
+ RenderPassResource
};
enum ShaderType {
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
index b6f35b8580..4ccaa91cce 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp
+++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
@@ -68,6 +68,7 @@ QSGDefaultRenderContext::QSGDefaultRenderContext(QSGContext *context)
, m_glAtlasManager(nullptr)
, m_rhiAtlasManager(nullptr)
, m_currentFrameCommandBuffer(nullptr)
+ , m_currentFrameRenderPass(nullptr)
{
}
@@ -239,6 +240,7 @@ void QSGDefaultRenderContext::beginNextRhiFrame(QSGRenderer *renderer, QRhiRende
renderer->setRenderPassRecordingCallbacks(mainPassRecordingStart, mainPassRecordingEnd, callbackUserData);
m_currentFrameCommandBuffer = cb;
+ m_currentFrameRenderPass = rp;
}
void QSGDefaultRenderContext::renderNextRhiFrame(QSGRenderer *renderer)
@@ -250,6 +252,7 @@ void QSGDefaultRenderContext::endNextRhiFrame(QSGRenderer *renderer)
{
Q_UNUSED(renderer);
m_currentFrameCommandBuffer = nullptr;
+ m_currentFrameRenderPass = nullptr;
}
/*!
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
index 79bc9dd76d..6a3462ae2b 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext_p.h
+++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
@@ -62,6 +62,7 @@ QT_BEGIN_NAMESPACE
class QRhi;
class QRhiCommandBuffer;
+class QRhiRenderPassDescriptor;
class QOpenGLContext;
class QSGMaterialShader;
class QSGMaterialRhiShader;
@@ -145,6 +146,10 @@ public:
// may be null if not in an active frame, but returning null is valid then
return m_currentFrameCommandBuffer;
}
+ QRhiRenderPassDescriptor *currentFrameRenderPass() const {
+ // may be null if not in an active frame, but returning null is valid then
+ return m_currentFrameRenderPass;
+ }
protected:
static QString fontKey(const QRawFont &font);
@@ -160,6 +165,7 @@ protected:
QSGOpenGLAtlasTexture::Manager *m_glAtlasManager;
QSGRhiAtlasTexture::Manager *m_rhiAtlasManager;
QRhiCommandBuffer *m_currentFrameCommandBuffer;
+ QRhiRenderPassDescriptor *m_currentFrameRenderPass;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index 08d1c726ab..f15105168e 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -648,7 +648,9 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
}
Q_ASSERT(rhi == cd->rhi);
- QRhi::FrameOpResult frameResult = rhi->beginFrame(cd->swapchain);
+ // ### the flag should only be set when the app requests it, but there's no way to do that right now
+ QRhi::BeginFrameFlags frameFlags = QRhi::ExternalContentsInPass;
+ QRhi::FrameOpResult frameResult = rhi->beginFrame(cd->swapchain, frameFlags);
if (frameResult != QRhi::FrameOpSuccess) {
if (frameResult == QRhi::FrameOpDeviceLost)
qWarning("Device lost");
diff --git a/src/quick/scenegraph/qsgrhisupport.cpp b/src/quick/scenegraph/qsgrhisupport.cpp
index a92b6b0c84..e050938cc9 100644
--- a/src/quick/scenegraph/qsgrhisupport.cpp
+++ b/src/quick/scenegraph/qsgrhisupport.cpp
@@ -279,12 +279,16 @@ QSurface::SurfaceType QSGRhiSupport::windowSurfaceType() const
}
#if QT_CONFIG(vulkan)
-static const void *qsgrhi_vk_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat,
- const QRhiNativeHandles *cbNat)
+static const void *qsgrhi_vk_rifResource(QSGRendererInterface::Resource res,
+ const QRhiNativeHandles *nat,
+ const QRhiNativeHandles *cbNat,
+ const QRhiNativeHandles *rpNat)
{
const QRhiVulkanNativeHandles *vknat = static_cast<const QRhiVulkanNativeHandles *>(nat);
const QRhiVulkanCommandBufferNativeHandles *maybeVkCbNat =
static_cast<const QRhiVulkanCommandBufferNativeHandles *>(cbNat);
+ const QRhiVulkanRenderPassNativeHandles *maybeVkRpNat =
+ static_cast<const QRhiVulkanRenderPassNativeHandles *>(rpNat);
switch (res) {
case QSGRendererInterface::DeviceResource:
@@ -298,6 +302,11 @@ static const void *qsgrhi_vk_rifResource(QSGRendererInterface::Resource res, con
return nullptr;
case QSGRendererInterface::PhysicalDeviceResource:
return &vknat->physDev;
+ case QSGRendererInterface::RenderPassResource:
+ if (maybeVkRpNat)
+ return &maybeVkRpNat->renderPass;
+ else
+ return nullptr;
default:
return nullptr;
}
@@ -376,7 +385,10 @@ const void *QSGRhiSupport::rifResource(QSGRendererInterface::Resource res, const
case QRhi::Vulkan:
{
QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer();
- return qsgrhi_vk_rifResource(res, nat, cb ? cb->nativeHandles() : nullptr);
+ QRhiRenderPassDescriptor *rp = rc->currentFrameRenderPass();
+ return qsgrhi_vk_rifResource(res, nat,
+ cb ? cb->nativeHandles() : nullptr,
+ rp ? rp->nativeHandles() : nullptr);
}
#endif
#if QT_CONFIG(opengl)
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index 9e34a2b201..9bcb96a65a 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -713,7 +713,9 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
}
Q_ASSERT(rhi == cd->rhi);
- QRhi::FrameOpResult frameResult = rhi->beginFrame(cd->swapchain);
+ // ### the flag should only be set when the app requests it, but there's no way to do that right now
+ QRhi::BeginFrameFlags frameFlags = QRhi::ExternalContentsInPass;
+ QRhi::FrameOpResult frameResult = rhi->beginFrame(cd->swapchain, frameFlags);
if (frameResult != QRhi::FrameOpSuccess) {
if (frameResult == QRhi::FrameOpDeviceLost)
qWarning("Device lost");
diff --git a/tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt b/tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt
index 92ce4c649f..8dca84b34e 100644
--- a/tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt
+++ b/tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt
@@ -1,2 +1 @@
5:1:TetZ$ is not a type
--1:-1:Invalid QML type name "TetZ$"
diff --git a/tests/auto/qml/qqmlmoduleplugin/data/multiSingleton.qml b/tests/auto/qml/qqmlmoduleplugin/data/multiSingleton.qml
new file mode 100644
index 0000000000..2fc2e9f076
--- /dev/null
+++ b/tests/auto/qml/qqmlmoduleplugin/data/multiSingleton.qml
@@ -0,0 +1,6 @@
+import org.qtproject.ModuleWithQmlSingleton 1.0
+import QtQuick 2.0
+
+Item {
+ Component.onCompleted: MySingleton
+}
diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton.qml b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton.qml
new file mode 100644
index 0000000000..9789be8191
--- /dev/null
+++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton.qml
@@ -0,0 +1,16 @@
+pragma Singleton
+import QtQuick 2.0
+
+QtObject {
+ property Loader _loader: Loader {
+ source: "internal/InternalType.qml"
+ }
+
+ Component.onCompleted: {
+ if (tracker.objectName === "first")
+ tracker.objectName = "second"
+ else
+ tracker.objectName = "first"
+ //console.log("created singleton", this)
+ }
+}
diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton2.qml b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton2.qml
new file mode 100644
index 0000000000..9be34eb061
--- /dev/null
+++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton2.qml
@@ -0,0 +1,6 @@
+pragma Singleton
+import QtQuick 2.0
+import org.qtproject.ModuleWithQmlSingleton 1.0
+import "."
+
+QtObject {}
diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/internal/InternalType.qml b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/internal/InternalType.qml
new file mode 100644
index 0000000000..4a8badefd2
--- /dev/null
+++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/internal/InternalType.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+import ".."
+
+QtObject {
+ Component.onCompleted: MySingleton
+}
diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/moduleWithQmlSingleton.pro b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/moduleWithQmlSingleton.pro
new file mode 100644
index 0000000000..b16e0743c8
--- /dev/null
+++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/moduleWithQmlSingleton.pro
@@ -0,0 +1,18 @@
+TEMPLATE = lib
+CONFIG += plugin
+SOURCES = plugin.cpp
+QT = core qml
+DESTDIR = ../imports/org/qtproject/ModuleWithQmlSingleton
+
+QT += core-private gui-private qml-private
+
+IMPORT_FILES = \
+ qmldir \
+ MySingleton.qml \
+ MySingleton2.qml
+
+include (../../../shared/imports.pri)
+
+subfiles.files = internal/InternalType.qml
+subfiles.path = $$DESTDIR/internal
+COPIES += subfiles
diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp
new file mode 100644
index 0000000000..6329927c34
--- /dev/null
+++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqml.h>
+#include <QDir>
+#include <QDebug>
+
+class MyPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+
+public:
+ MyPlugin() {}
+
+ void registerTypes(const char *uri)
+ {
+ Q_ASSERT(QLatin1String(uri) == "org.qtproject.ModuleWithQmlSingleton");
+ qmlRegisterSingletonType(baseUrl().resolved(QUrl("ModuleWithQmlSingleton/MySingleton.qml")), uri, 1, 0, "MySingleton");
+ qmlRegisterSingletonType(baseUrl().resolved(QUrl("ModuleWithQmlSingleton/MySingleton2.qml")), uri, 1, 0, "MySingleton2");
+ }
+};
+
+#include "plugin.moc"
diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/qmldir b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/qmldir
new file mode 100644
index 0000000000..3483f80ab1
--- /dev/null
+++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/qmldir
@@ -0,0 +1,2 @@
+module org.qtproject.ModuleWithQmlSingleton
+plugin moduleWithQmlSingleton
diff --git a/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro b/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro
index ae13a041cc..44b3ab14e6 100644
--- a/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro
+++ b/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro
@@ -20,7 +20,8 @@ SUBDIRS =\
plugin/childplugin\
plugin.2/childplugin\
plugin.2.1/childplugin\
- plugin.2.2
+ plugin.2.2\
+ moduleWithQmlSingleton
tst_qqmlmoduleplugin_pro.depends += plugin
SUBDIRS += tst_qqmlmoduleplugin.pro
diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
index eed0ade98d..f89cc9f24a 100644
--- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
@@ -78,6 +78,7 @@ private slots:
void importsChildPlugin2();
void importsChildPlugin21();
void parallelPluginImport();
+ void multiSingleton();
private:
QString m_importsDirectory;
@@ -772,6 +773,20 @@ void tst_qqmlmoduleplugin::parallelPluginImport()
worker.wait();
}
+void tst_qqmlmoduleplugin::multiSingleton()
+{
+ QQmlEngine engine;
+ QObject obj;
+ engine.rootContext()->setContextProperty("tracker", &obj);
+ engine.addImportPath(m_importsDirectory);
+ QQmlComponent component(&engine, testFileUrl("multiSingleton.qml"));
+ QObject *object = component.create();
+ QVERIFY(object != nullptr);
+ QCOMPARE(obj.objectName(), QLatin1String("first"));
+ delete object;
+}
+
+
QTEST_MAIN(tst_qqmlmoduleplugin)
#include "tst_qqmlmoduleplugin.moc"
diff --git a/tests/auto/qml/qqmltranslation/data/mylibrary.js b/tests/auto/qml/qqmltranslation/data/mylibrary.js
new file mode 100644
index 0000000000..5903db3b4b
--- /dev/null
+++ b/tests/auto/qml/qqmltranslation/data/mylibrary.js
@@ -0,0 +1,5 @@
+Qt.include("nested_js_translation.js")
+
+function translation_success() {
+ return qsTr("English in mylibrary");
+}
diff --git a/tests/auto/qml/qqmltranslation/data/nested_js_translation.js b/tests/auto/qml/qqmltranslation/data/nested_js_translation.js
new file mode 100644
index 0000000000..336cdedfea
--- /dev/null
+++ b/tests/auto/qml/qqmltranslation/data/nested_js_translation.js
@@ -0,0 +1,3 @@
+function translation_fail() {
+ return qsTr("English in translation")
+}
diff --git a/tests/auto/qml/qqmltranslation/data/preferjs.qml b/tests/auto/qml/qqmltranslation/data/preferjs.qml
new file mode 100644
index 0000000000..040fa12e4e
--- /dev/null
+++ b/tests/auto/qml/qqmltranslation/data/preferjs.qml
@@ -0,0 +1,8 @@
+import QtQml 2.12
+
+import "mylibrary.js" as Lib
+
+QtObject {
+ property string german1: Lib.translation_fail()
+ property string german2: Lib.translation_success()
+}
diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
index dcfe914af6..a75a00bd01 100644
--- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
+++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
@@ -46,6 +46,7 @@ private slots:
void translation();
void idTranslation();
void translationChange();
+ void preferJSContext();
};
void tst_qqmltranslation::translation_data()
@@ -175,6 +176,10 @@ class DummyTranslator : public QTranslator
Q_UNUSED(n);
if (!qstrcmp(sourceText, "translate me"))
return QString::fromUtf8("xxx");
+ if (!qstrcmp(sourceText, "English in mylibrary") && !qstrcmp(context, "mylibrary"))
+ return QString::fromUtf8("Deutsch in mylibrary");
+ if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "nested_js_translation"))
+ return QString::fromUtf8("Deutsch in Setzung");
return QString();
}
@@ -213,6 +218,24 @@ void tst_qqmltranslation::translationChange()
QCoreApplication::removeTranslator(&translator);
}
+void tst_qqmltranslation::preferJSContext()
+{
+ DummyTranslator translator;
+ QCoreApplication::installTranslator(&translator);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("preferjs.qml"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QCOMPARE(object->property("german1").toString(),
+ QStringLiteral("Deutsch in Setzung"));
+ QCOMPARE(object->property("german2").toString(),
+ QStringLiteral("Deutsch in mylibrary"));
+
+ QCoreApplication::removeTranslator(&translator);
+}
+
QTEST_MAIN(tst_qqmltranslation)
#include "tst_qqmltranslation.moc"