aboutsummaryrefslogtreecommitdiffstats
path: root/examples/quick
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2019-10-11 16:21:42 +0200
committerAlexandru Croitor <alexandru.croitor@qt.io>2019-10-11 16:21:42 +0200
commit6a0bb9a7d6a6473920da74bb48c52a4eaed990b0 (patch)
tree3a32c56b1fe21c96d5982511e0b4815c1c07dfbb /examples/quick
parentf0d23d3ff9da26a71295a6abd356c8d366d17439 (diff)
parent2746518c76e02c642ff29faf568de4de90216e58 (diff)
Merge remote-tracking branch 'origin/wip/qt6' into wip/cmake
Diffstat (limited to 'examples/quick')
-rw-r--r--examples/quick/imageelements/content/multi.icobin0 -> 27110 bytes
-rw-r--r--examples/quick/imageelements/framestepping.qml82
-rw-r--r--examples/quick/imageelements/imageelements.qml2
-rw-r--r--examples/quick/imageelements/imageelements.qrc3
-rw-r--r--examples/quick/imageelements/multiframeborderimage.qml83
-rw-r--r--examples/quick/scenegraph/d3d11underqml/doc/images/d3d11underqml-example.jpgbin0 -> 79343 bytes
-rw-r--r--examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc58
-rw-r--r--examples/quick/scenegraph/metalunderqml/doc/images/metalunderqml-example.jpgbin0 -> 55194 bytes
-rw-r--r--examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc68
-rw-r--r--examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc5
-rw-r--r--examples/quick/scenegraph/rendernode/customrenderitem.cpp48
-rw-r--r--examples/quick/scenegraph/rendernode/d3d12renderer.cpp6
-rw-r--r--examples/quick/scenegraph/rendernode/doc/images/rendernode-example.jpgbin0 -> 77210 bytes
-rw-r--r--examples/quick/scenegraph/rendernode/doc/src/rendernode.qdoc165
-rw-r--r--examples/quick/scenegraph/rendernode/main.cpp2
-rw-r--r--examples/quick/scenegraph/rendernode/main.qml90
-rw-r--r--examples/quick/scenegraph/rendernode/metalrenderer.h100
-rw-r--r--examples/quick/scenegraph/rendernode/metalrenderer.mm326
-rw-r--r--examples/quick/scenegraph/rendernode/metalshader.frag28
-rw-r--r--examples/quick/scenegraph/rendernode/metalshader.vert31
-rw-r--r--examples/quick/scenegraph/rendernode/openglrenderer.cpp31
-rw-r--r--examples/quick/scenegraph/rendernode/openglrenderer.h2
-rw-r--r--examples/quick/scenegraph/rendernode/rendernode.pro6
-rw-r--r--examples/quick/scenegraph/rendernode/rendernode.qrc2
-rw-r--r--examples/quick/shared/CheckBox.qml2
-rw-r--r--examples/quick/shared/LauncherList.qml11
-rw-r--r--examples/quick/shared/Slider.qml2
27 files changed, 1114 insertions, 39 deletions
diff --git a/examples/quick/imageelements/content/multi.ico b/examples/quick/imageelements/content/multi.ico
new file mode 100644
index 0000000000..b748ceaa29
--- /dev/null
+++ b/examples/quick/imageelements/content/multi.ico
Binary files differ
diff --git a/examples/quick/imageelements/framestepping.qml b/examples/quick/imageelements/framestepping.qml
new file mode 100644
index 0000000000..f5bad46e7b
--- /dev/null
+++ b/examples/quick/imageelements/framestepping.qml
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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.14
+
+Rectangle {
+ width: 480
+ height: 320
+ Image {
+ id: img
+ anchors.centerIn: parent
+ cache: true
+ source: "content/multi.ico"
+
+ Shortcut {
+ sequence: StandardKey.MoveToNextPage
+ enabled: img.currentFrame < img.frameCount - 1
+ onActivated: img.currentFrame++
+ }
+ Shortcut {
+ sequence: StandardKey.MoveToPreviousPage
+ enabled: img.currentFrame > 0
+ onActivated: img.currentFrame--
+ }
+ }
+
+ Text {
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ anchors.margins: 6
+ horizontalAlignment: Text.AlignHCenter
+ text: "frame " + (img.currentFrame + 1) + " of " + img.frameCount +
+ "\nPress PgUp/PgDn to switch frames"
+ }
+}
diff --git a/examples/quick/imageelements/imageelements.qml b/examples/quick/imageelements/imageelements.qml
index dfb4d24ea6..91f2e034f7 100644
--- a/examples/quick/imageelements/imageelements.qml
+++ b/examples/quick/imageelements/imageelements.qml
@@ -64,6 +64,8 @@ Item {
addExample("AnimatedImage", "An image which plays animated formats", Qt.resolvedUrl("animatedimage.qml"));
addExample("AnimatedSprite", "A simple sprite-based animation", Qt.resolvedUrl("animatedsprite.qml"));
addExample("SpriteSequence", "A sprite-based animation with complex transitions", Qt.resolvedUrl("spritesequence.qml"));
+ addExample("FrameStepping", "A multi-frame non-animated image", Qt.resolvedUrl("framestepping.qml"));
+ addExample("MultiBorderImage", "A multi-frame image with scaled borders", Qt.resolvedUrl("multiframeborderimage.qml"));
}
}
}
diff --git a/examples/quick/imageelements/imageelements.qrc b/examples/quick/imageelements/imageelements.qrc
index 2488fb083b..cedef2204c 100644
--- a/examples/quick/imageelements/imageelements.qrc
+++ b/examples/quick/imageelements/imageelements.qrc
@@ -8,6 +8,7 @@
<file>content/colors-stretch.sci</file>
<file>content/colors.png</file>
<file>content/ImageCell.qml</file>
+ <file>content/multi.ico</file>
<file>content/MyBorderImage.qml</file>
<file>content/qt-logo.png</file>
<file>content/shadow.png</file>
@@ -18,6 +19,8 @@
<file>animatedimage.qml</file>
<file>animatedsprite.qml</file>
<file>borderimage.qml</file>
+ <file>framestepping.qml</file>
+ <file>multiframeborderimage.qml</file>
<file>image.qml</file>
<file>shadows.qml</file>
<file>spritesequence.qml</file>
diff --git a/examples/quick/imageelements/multiframeborderimage.qml b/examples/quick/imageelements/multiframeborderimage.qml
new file mode 100644
index 0000000000..0805ea4243
--- /dev/null
+++ b/examples/quick/imageelements/multiframeborderimage.qml
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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.14
+
+Rectangle {
+ width: 480
+ height: 320
+ BorderImage {
+ id: img
+ anchors.fill: parent
+ anchors.margins: 6
+ cache: true
+ source: "content/multi.ico"
+ border { left: 19; top: 19; right: 19; bottom: 19 }
+ horizontalTileMode: BorderImage.Stretch
+
+ Shortcut {
+ sequence: StandardKey.MoveToNextPage
+ enabled: img.currentFrame < img.frameCount - 1
+ onActivated: img.currentFrame++
+ }
+ Shortcut {
+ sequence: StandardKey.MoveToPreviousPage
+ enabled: img.currentFrame > 0
+ onActivated: img.currentFrame--
+ }
+ }
+
+ Text {
+ anchors.centerIn: parent
+ horizontalAlignment: Text.AlignHCenter
+ text: "frame " + (img.currentFrame + 1) + " of " + img.frameCount +
+ "\nPress PgUp/PgDn to switch frames"
+ }
+}
diff --git a/examples/quick/scenegraph/d3d11underqml/doc/images/d3d11underqml-example.jpg b/examples/quick/scenegraph/d3d11underqml/doc/images/d3d11underqml-example.jpg
new file mode 100644
index 0000000000..9f1e53ad61
--- /dev/null
+++ b/examples/quick/scenegraph/d3d11underqml/doc/images/d3d11underqml-example.jpg
Binary files differ
diff --git a/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc b/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc
new file mode 100644
index 0000000000..d7b60d3b81
--- /dev/null
+++ b/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** 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/d3d11underqml
+ \title Scene Graph - Direct3D 11 Under QML
+ \ingroup qtquickexamples
+ \brief Shows how to render directly with Direct3D 11 under a Qt Quick scene.
+
+ \image d3d11underqml-example.jpg
+
+ The Direct3D 11 Under QML example shows how an application can make use
+ of the \l QQuickWindow::beforeRendering() signal to draw custom
+ D3D11 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 D3D11 draw calls that are made as a response
+ to this signal, will stack under the Qt Quick items.
+
+ As an alternative, applications that wish to render D3D11 content
+ on top of the Qt Quick scene, can do so by connecting to the \l
+ QQuickWindow::afterRendering() signal.
+
+ In this example, we will also see how it is possible to have
+ values that are exposed to QML which affect the D3D11
+ rendering. We animate the threshold value using a NumberAnimation
+ in the QML file and this value is used by the HLSL shader
+ 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.
+
+ */
diff --git a/examples/quick/scenegraph/metalunderqml/doc/images/metalunderqml-example.jpg b/examples/quick/scenegraph/metalunderqml/doc/images/metalunderqml-example.jpg
new file mode 100644
index 0000000000..98085773de
--- /dev/null
+++ b/examples/quick/scenegraph/metalunderqml/doc/images/metalunderqml-example.jpg
Binary files differ
diff --git a/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc b/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc
new file mode 100644
index 0000000000..d499f47de3
--- /dev/null
+++ b/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.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/metalunderqml
+ \title Scene Graph - Metal Under QML
+ \ingroup qtquickexamples
+ \brief Shows how to render directly with Metal under a Qt Quick scene.
+
+ \image metalunderqml-example.jpg
+
+ The Metal Under QML example shows how an application can make use
+ of the \l QQuickWindow::beforeRendering() and \l
+ QQuickWindow::beforeRenderPassRecording() signals to draw custom
+ Metal 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 Metal 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 Metal commands are recorded onto the
+ same command buffer with the same render command encoder that the
+ scene graph uses. beforeRendering() on its own is not sufficient
+ for this because it gets emitted at the start of the frame, before
+ having an
+ \l{https://developer.apple.com/documentation/metal/mtlrendercommandencoder}{MTLRenderCommandEncoder}
+ available. By also connecting to beforeRenderPassRecording(), the
+ application can gain access to the necessary native objects.
+
+ As an alternative, applications that wish to render Metal 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 Metal
+ rendering. We animate the threshold value using a NumberAnimation
+ in the QML file and this value is used by the Metal shader
+ 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.
+
+ */
diff --git a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc
index 69a9d2ce4b..ed46b40420 100644
--- a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc
+++ b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc
@@ -50,6 +50,11 @@
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
+ native APIs.
+
\snippet scenegraph/openglunderqml/squircle.h 2
First of all, we need an object we can expose to QML. This is a
diff --git a/examples/quick/scenegraph/rendernode/customrenderitem.cpp b/examples/quick/scenegraph/rendernode/customrenderitem.cpp
index 8f248e2ecb..67a9cccfc6 100644
--- a/examples/quick/scenegraph/rendernode/customrenderitem.cpp
+++ b/examples/quick/scenegraph/rendernode/customrenderitem.cpp
@@ -53,16 +53,20 @@
#include <QSGRendererInterface>
#include "openglrenderer.h"
+#include "metalrenderer.h"
#include "d3d12renderer.h"
#include "softwarerenderer.h"
+//! [1]
CustomRenderItem::CustomRenderItem(QQuickItem *parent)
: QQuickItem(parent)
{
// Our item shows something so set the flag.
setFlag(ItemHasContents);
}
+//! [1]
+//! [2]
QSGNode *CustomRenderItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
{
QSGRenderNode *n = static_cast<QSGRenderNode *>(node);
@@ -71,24 +75,46 @@ QSGNode *CustomRenderItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
if (!ri)
return nullptr;
switch (ri->graphicsApi()) {
- case QSGRendererInterface::OpenGL:
+ case QSGRendererInterface::OpenGL:
+ Q_FALLTHROUGH();
+ case QSGRendererInterface::OpenGLRhi:
#if QT_CONFIG(opengl)
- n = new OpenGLRenderNode(this);
- break;
+ n = new OpenGLRenderNode(this);
#endif
- case QSGRendererInterface::Direct3D12:
+ break;
+
+ case QSGRendererInterface::MetalRhi:
+#ifdef Q_OS_DARWIN
+ {
+ MetalRenderNode *metalNode = new MetalRenderNode(this);
+ n = metalNode;
+ metalNode->resourceBuilder()->setWindow(window());
+ QObject::connect(window(), &QQuickWindow::beforeRendering,
+ metalNode->resourceBuilder(), &MetalRenderNodeResourceBuilder::build);
+ }
+#endif
+ break;
+
+ case QSGRendererInterface::Direct3D12: // ### Qt 6: remove
#if QT_CONFIG(d3d12)
- n = new D3D12RenderNode(this);
- break;
+ n = new D3D12RenderNode(this);
#endif
- case QSGRendererInterface::Software:
- n = new SoftwareRenderNode(this);
- break;
+ break;
+
+ case QSGRendererInterface::Software:
+ n = new SoftwareRenderNode(this);
+ break;
- default:
- return nullptr;
+ default:
+ break;
}
+ if (!n)
+ qWarning("QSGRendererInterface reports unknown graphics API %d", ri->graphicsApi());
}
return n;
}
+//! [2]
+
+// This item does not support being moved between windows. If that is desired,
+// itemChange() should be reimplemented as well.
diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp
index df0e29eb67..878b022950 100644
--- a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp
+++ b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp
@@ -54,6 +54,8 @@
#include <QSGRendererInterface>
#include <QFile>
+// ### Qt 6: remove
+
#if QT_CONFIG(d3d12)
D3D12RenderNode::D3D12RenderNode(QQuickItem *item)
@@ -166,8 +168,8 @@ void D3D12RenderNode::init()
psoDesc.RasterizerState = rastDesc;
psoDesc.BlendState = blendDesc;
// No depth. The correct stacking of the item is ensured by the projection matrix.
- // Do not bother with stencil since we do not apply clipping in the
- // example. If clipping is desired, render() needs to set a different PSO
+ // Note that this does not support clipping.
+ // If clipping is desired, render() needs to set a different PSO
// with stencil enabled whenever the RenderState indicates so.
psoDesc.SampleMask = UINT_MAX;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
diff --git a/examples/quick/scenegraph/rendernode/doc/images/rendernode-example.jpg b/examples/quick/scenegraph/rendernode/doc/images/rendernode-example.jpg
new file mode 100644
index 0000000000..cbb59b950d
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/doc/images/rendernode-example.jpg
Binary files differ
diff --git a/examples/quick/scenegraph/rendernode/doc/src/rendernode.qdoc b/examples/quick/scenegraph/rendernode/doc/src/rendernode.qdoc
new file mode 100644
index 0000000000..ba6551fddf
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/doc/src/rendernode.qdoc
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** 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/rendernode
+ \title Scene Graph - Custom Rendering with QSGRenderNode
+ \ingroup qtquickexamples
+ \brief Shows how to integrate drawing via the native graphics API with the Qt Quick scene graph.
+
+ \image rendernode-example.jpg
+
+ \l QSGRenderNode allows integrating draw and other calls made directly via
+ the Qt Quick scene graph's underlying native graphics API (such as, Vulkan,
+ Metal, Direct 3D, or OpenGL). This example demonstrates implementing a
+ custom QQuickItem backed by a QSGRenderNode implementation, where the node
+ renders a triangle directly via the graphics API. The rest of the scene
+ (background, text, rectangles) are standard Qt Quick items. The example has
+ full support for OpenGL and Metal, as well as the software backend of Qt
+ Quick.
+
+ The custom item behaves like any other Qt Quick item, meaning it
+ participates and stacking and clipping as usual, which is a big difference
+ to the alternative approaches like having the custom rendering as an
+ overlay (connecting to \l QQuickWindow::afterRendering()) and underlay
+ (connecting to \l QQuickWindow::beforeRendering()) because those do not
+ offer the possibility of proper mixing of the custom content with the Qt
+ Quick scene.
+
+ Another important feature is that QSGRenderNode can be helpful to preserve
+ performance, when compared to some of the alternatives. Going through \l
+ QQuickFramebufferObject allows creating a custom item similarly to what
+ this example does, but it does it by rendering the custom content in a
+ texture, and then drawing a textured quad with that texture. This can be
+ expensive on some systems due to the cost of texturing and blending.
+ QSGRenderNode avoids this since the native graphics calls are issued in
+ line with the draw calls for the scene graph's batches.
+
+ All this comes at the cost of being more complex, and not necessarily being
+ suitable for all types of 3D content, in particular where vertices and
+ different depth would clash with the 2D content in the Qt Quick scene
+ graph's batches (those are better served by "flattening" into a 2D texture
+ via approaches like QQuickFramebufferObject). Therefore QSGRenderNode is
+ not always the right choice. It can however a good and powerful choice in
+ many cases. This is what the example demonstrates.
+
+ Let's go through the most important parts of the code:
+
+ \snippet scenegraph/rendernode/main.cpp 1
+
+ Our custom QML type is implemented in the class CustomRenderItem.
+
+ \snippet scenegraph/rendernode/main.qml 2
+
+ The corresponding import in the QML document.
+
+ \snippet scenegraph/rendernode/main.qml 3
+
+ The CustomRenderItem object. It is positioned to fill a big part of the
+ scene, covering its parent (the yellow rectangle; this will be used to
+ demonstrate clipping). The item will have its scale and rotation animated.
+
+ \snippet scenegraph/rendernode/main.qml 4
+
+ Text items are used to show some helpful information, such as, the
+ active graphics API Qt Quick uses.
+
+ \snippet scenegraph/rendernode/main.qml 5
+
+ Clicking the left mouse button is used to toggle clipping on the custom
+ item's parent item. By default this is done using scissoring (GL_SCISSOR_TEST
+ with OpenGL). A well-written QSGRenderNode implementation is expected to be
+ able to take this into account and enable scissor testing when the scene graph
+ indicates that it is necessary.
+
+ The right mouse button is used to toggle an animation on the rotation of
+ the parent item. With clipping enabled, this demonstrates clipping via the
+ stencil buffer since a rectangular scissor is not appropriate when we need
+ to clip to a rotated rectangle shape. The scene graph fills up the stencil
+ buffer as necessary, the QSGRenderNode implementation just has to enable
+ stencil testing using the provided reference value.
+
+ \snippet scenegraph/rendernode/customrenderitem.cpp 1
+
+ Moving on to the CustomRenderItem implementation. This is a visual item.
+
+ \snippet scenegraph/rendernode/customrenderitem.cpp 2
+
+ The implementation of \l QQuickItem::updatePaintNode() creates (if not yet
+ done) and returns an instance of a suitable QSGRenderNode subclass. The
+ example supports multiple graphics APIs, and also the \c software backend.
+
+ Let's look at the the render node for OpenGL (supporting both the
+ traditional, direct OpenGL-based scene graph, and also the modern,
+ abstracted variant using the RHI). For other graphics APIs, the concepts
+ and the outline of a QSGRenderNode implementation are the same. It is worth
+ noting that in some cases it will also be necessary to connect to a signal
+ like \l QQuickWindow::beforeRendering() to perform copy type of operations
+ (such as, vertex buffer uploads). This is not necessary for OpenGL, but it
+ is essential for Vulkan or Metal since there such operations cannot be
+ issued in render() as there is a renderpass being recorded when render() is
+ called.
+
+ \snippet scenegraph/rendernode/openglrenderer.h 1
+
+ The main job is to provide implementations of the virtual QSGRenderNode functions.
+
+ \snippet scenegraph/rendernode/openglrenderer.cpp 1
+
+ The pattern for safe graphics resource management is to do any cleanup in
+ \l{QSGRenderNode::releaseResources()}{releaseResources()}, while also
+ calling this from the destructor.
+
+ \snippet scenegraph/rendernode/openglrenderer.cpp 2
+
+ The render() function initializes graphics resources (in this case, an
+ OpenGL shader program and a vertex buffer), if not yet done. It then
+ makes sure the necessary resources are bound and updates uniforms.
+ The transformation matrix and the opacity are provided by the scene graph
+ either via the \c state argument or base class functions.
+
+ \snippet scenegraph/rendernode/openglrenderer.cpp 5
+
+ This render node is well-behaving since it basically renders in 2D,
+ respecting the item's geometry. This is not mandatory, but then flags() has
+ to return (or not return) the appropriate flags.
+
+ \snippet scenegraph/rendernode/openglrenderer.cpp 3
+
+ After setting up vertex inputs, but before recording a draw call for our
+ triangle, it is important to set some state in order to integrate with the
+ rest of the scene correctly. Setting scissor and stencil as instructed by
+ \c state allows our item to render correctly even when there are one or
+ more clips in the parent chain.
+
+ \snippet scenegraph/rendernode/openglrenderer.cpp 4
+
+ As shown above, we only really render in 2D (no depth), within the item's
+ geometry. changedStates() returns the flags corresponding to the OpenGL
+ states render() touches.
+
+*/
diff --git a/examples/quick/scenegraph/rendernode/main.cpp b/examples/quick/scenegraph/rendernode/main.cpp
index 21419abfc9..146d787e50 100644
--- a/examples/quick/scenegraph/rendernode/main.cpp
+++ b/examples/quick/scenegraph/rendernode/main.cpp
@@ -58,7 +58,9 @@ int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
+//! [1]
qmlRegisterType<CustomRenderItem>("SceneGraphRendering", 2, 0, "CustomRenderItem");
+//! [1]
QQuickView view;
diff --git a/examples/quick/scenegraph/rendernode/main.qml b/examples/quick/scenegraph/rendernode/main.qml
index d0ba4a4669..153a71e097 100644
--- a/examples/quick/scenegraph/rendernode/main.qml
+++ b/examples/quick/scenegraph/rendernode/main.qml
@@ -49,27 +49,65 @@
****************************************************************************/
import QtQuick 2.8
+//! [2]
import SceneGraphRendering 2.0
+//! [2]
Item {
Rectangle {
+ id: bg
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0; color: "steelblue" }
GradientStop { position: 1; color: "black" }
}
- CustomRenderItem {
- id: renderer
+ //! [5]
+ MouseArea {
anchors.fill: parent
- anchors.margins: 10
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ onClicked: {
+ if (mouse.button === Qt.LeftButton) {
+ clipper.clip = !clipper.clip
+ } else if (mouse.button === Qt.RightButton) {
+ nonRectClipAnim.running = !nonRectClipAnim.running
+ if (!nonRectClipAnim.running)
+ clipper.rotation = 0;
+ }
+ }
+ }
+ // ![5]
+
+ Rectangle {
+ id: clipper
+ width: parent.width / 2
+ height: parent.height / 2
+ anchors.centerIn: parent
+ border.color: "yellow"
+ border.width: 2
+ color: "transparent"
+ NumberAnimation on rotation {
+ id: nonRectClipAnim
+ from: 0; to: 360; duration: 5000; loops: Animation.Infinite
+ running: false
+ }
+
+ //! [3]
+ CustomRenderItem {
+ id: renderer
+ width: bg.width - 20
+ height: bg.height - 20
+ x: -clipper.x + 10
+ y: -clipper.y + 10
- 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 }
- ]
+ 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 }
+ ]
+ }
+ //! [3]
}
SequentialAnimation {
@@ -92,19 +130,39 @@ Item {
loops: Animation.Infinite
}
+ //! [4]
Text {
id: label
- anchors.bottom: renderer.bottom
- anchors.left: renderer.left
- anchors.right: renderer.right
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
anchors.margins: 20
+ color: "yellow"
wrapMode: Text.WordWrap
property int api: GraphicsInfo.api
- text: "Custom rendering via the graphics API "
- + (api === GraphicsInfo.OpenGL ? "OpenGL"
- : api === GraphicsInfo.Direct3D12 ? "Direct3D 12"
- : api === GraphicsInfo.Software ? "Software" : "")
+ text: {
+ var apiStr;
+ switch (api) {
+ case GraphicsInfo.OpenGL: apiStr = "OpenGL (direct)"; break;
+ case GraphicsInfo.Direct3D12: apiStr = "Direct3D 12 (direct)"; break;
+ case GraphicsInfo.Software: apiStr = "Software (QPainter)"; break;
+ case GraphicsInfo.OpenGLRhi: apiStr = "OpenGL (RHI)"; break;
+ case GraphicsInfo.MetalRhi: apiStr = "Metal (RHI)"; break;
+ // the example has no other QSGRenderNode subclasses
+ default: apiStr = "<UNSUPPORTED>"; break;
+ }
+ "Custom rendering via the graphics API " + apiStr
+ + "\nLeft click to toggle clipping to yellow rect"
+ + "\nRight click to rotate (can be used to exercise stencil clip instead of scissor)"
+ }
+ // ![4]
+ }
+
+ Text {
+ id: label2
+ anchors.top: parent.top
+ anchors.right: parent.right
color: "yellow"
+ text: "Clip: " + (clipper.clip ? "ON" : "OFF") + " Rotation: " + (nonRectClipAnim.running ? "ON" : "OFF")
}
}
}
diff --git a/examples/quick/scenegraph/rendernode/metalrenderer.h b/examples/quick/scenegraph/rendernode/metalrenderer.h
new file mode 100644
index 0000000000..77c9892313
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/metalrenderer.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 METALRENDERER_H
+#define METALRENDERER_H
+
+#include <qsgrendernode.h>
+
+#ifdef Q_OS_DARWIN
+
+QT_BEGIN_NAMESPACE
+
+class QQuickItem;
+class QQuickWindow;
+
+QT_END_NAMESPACE
+
+class MetalRenderNodeResourceBuilder : public QObject
+{
+ Q_OBJECT
+
+public:
+ void setWindow(QQuickWindow *w) { m_window = w; }
+
+public slots:
+ void build();
+
+private:
+ QQuickWindow *m_window = nullptr;
+};
+
+class MetalRenderNode : public QSGRenderNode
+{
+public:
+ MetalRenderNode(QQuickItem *item);
+ ~MetalRenderNode();
+
+ void render(const RenderState *state) override;
+ void releaseResources() override;
+ StateFlags changedStates() const override;
+ RenderingFlags flags() const override;
+ QRectF rect() const override;
+
+ MetalRenderNodeResourceBuilder *resourceBuilder() { return &m_resourceBuilder; }
+
+private:
+ QQuickItem *m_item;
+ MetalRenderNodeResourceBuilder m_resourceBuilder;
+};
+
+#endif // Q_OS_DARWIN
+
+#endif
diff --git a/examples/quick/scenegraph/rendernode/metalrenderer.mm b/examples/quick/scenegraph/rendernode/metalrenderer.mm
new file mode 100644
index 0000000000..4cb973abee
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/metalrenderer.mm
@@ -0,0 +1,326 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 "metalrenderer.h"
+#include <QQuickItem>
+#include <QQuickWindow>
+
+#include <Metal/Metal.h>
+
+using FuncAndLib = QPair<id<MTLFunction>, id<MTLLibrary> >;
+
+const int MAX_FRAMES_IN_FLIGHT = 3;
+
+struct {
+ id<MTLDevice> dev = nil;
+ QByteArray vsSource;
+ FuncAndLib vs;
+ QByteArray fsSource;
+ FuncAndLib fs;
+ id<MTLBuffer> vbuf[MAX_FRAMES_IN_FLIGHT];
+ id<MTLBuffer> ubuf[MAX_FRAMES_IN_FLIGHT];
+ id<MTLDepthStencilState> stencilEnabledDsState = nil;
+ id<MTLRenderPipelineState> pipeline = nil;
+} g;
+
+static FuncAndLib 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 = [g.dev 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;
+}
+
+const int VERTEX_SIZE = 6 * sizeof(float);
+
+static float colors[] = {
+ 1.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f
+};
+
+void MetalRenderNodeResourceBuilder::build()
+{
+ if (!g.dev) {
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::MetalRhi);
+
+ g.dev = (id<MTLDevice>) rif->getResource(m_window, QSGRendererInterface::DeviceResource);
+ Q_ASSERT(g.dev);
+ }
+
+ if (g.vsSource.isEmpty()) {
+ const QString filename = QLatin1String(":/scenegraph/rendernode/metalshader.vert");
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
+ qFatal("Failed to read shader %s", qPrintable(filename));
+ g.vsSource = f.readAll();
+ g.vs = compileShaderFromSource(g.vsSource, QByteArrayLiteral("main0"));
+ }
+
+ if (g.fsSource.isEmpty()) {
+ const QString filename = QLatin1String(":/scenegraph/rendernode/metalshader.frag");
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
+ qFatal("Failed to read shader %s", qPrintable(filename));
+ g.fsSource = f.readAll();
+ g.fs = compileShaderFromSource(g.fsSource, QByteArrayLiteral("main0"));
+ }
+
+ const int framesInFlight = m_window->graphicsStateInfo()->framesInFlight;
+
+ // For simplicity's sake we use shared mode (something like host visible +
+ // host coherent) for everything.
+
+ for (int i = 0; i < framesInFlight; ++i) {
+ // Have multiple versions for vertex too since we'll just memcpy new
+ // vertices based on item width and height on every render(). This could
+ // be optimized further however.
+ if (!g.vbuf[i]) {
+ g.vbuf[i] = [g.dev newBufferWithLength: VERTEX_SIZE + sizeof(colors) options: MTLResourceStorageModeShared];
+ char *p = (char *) [g.vbuf[i] contents];
+ memcpy(p + VERTEX_SIZE, colors, sizeof(colors));
+ }
+
+ if (!g.ubuf[i])
+ g.ubuf[i] = [g.dev newBufferWithLength: 256 options: MTLResourceStorageModeShared];
+ }
+
+ if (!g.stencilEnabledDsState) {
+ MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
+ dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
+ dsDesc.frontFaceStencil.stencilFailureOperation = MTLStencilOperationKeep;
+ dsDesc.frontFaceStencil.depthFailureOperation = MTLStencilOperationKeep;
+ dsDesc.frontFaceStencil.depthStencilPassOperation = MTLStencilOperationKeep;
+ dsDesc.frontFaceStencil.stencilCompareFunction = MTLCompareFunctionEqual;
+ dsDesc.frontFaceStencil.readMask = 0xFF;
+ dsDesc.frontFaceStencil.writeMask = 0xFF;
+
+ dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
+ dsDesc.backFaceStencil.stencilFailureOperation = MTLStencilOperationKeep;
+ dsDesc.backFaceStencil.depthFailureOperation = MTLStencilOperationKeep;
+ dsDesc.backFaceStencil.depthStencilPassOperation = MTLStencilOperationKeep;
+ dsDesc.backFaceStencil.stencilCompareFunction = MTLCompareFunctionEqual;
+ dsDesc.backFaceStencil.readMask = 0xFF;
+ dsDesc.backFaceStencil.writeMask = 0xFF;
+
+ g.stencilEnabledDsState = [g.dev newDepthStencilStateWithDescriptor: dsDesc];
+ [dsDesc release];
+ }
+
+ if (!g.pipeline) {
+ 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 and 2
+ inputLayout.attributes[1].format = MTLVertexFormatFloat3;
+ inputLayout.attributes[1].offset = 0;
+ inputLayout.attributes[1].bufferIndex = 2;
+ inputLayout.layouts[1].stride = 2 * sizeof(float);
+ inputLayout.layouts[2].stride = 3 * sizeof(float);
+
+ MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
+ rpDesc.vertexDescriptor = inputLayout;
+
+ rpDesc.vertexFunction = g.vs.first;
+ rpDesc.fragmentFunction = g.fs.first;
+
+ rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
+ rpDesc.colorAttachments[0].blendingEnabled = true;
+ rpDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorOne;
+ rpDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne;
+ rpDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
+ rpDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
+
+ if (g.dev.depth24Stencil8PixelFormatSupported) {
+ rpDesc.depthAttachmentPixelFormat = MTLPixelFormatDepth24Unorm_Stencil8;
+ rpDesc.stencilAttachmentPixelFormat = MTLPixelFormatDepth24Unorm_Stencil8;
+ } else {
+ rpDesc.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
+ rpDesc.stencilAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
+ }
+
+ NSError *err = nil;
+ g.pipeline = [g.dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
+ if (!g.pipeline) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qFatal("Failed to create render pipeline state: %s", qPrintable(msg));
+ }
+ [rpDesc release];
+ }
+}
+
+MetalRenderNode::MetalRenderNode(QQuickItem *item)
+ : m_item(item)
+{
+ g.vs.first = g.fs.first = nil;
+ g.vs.second = g.fs.second = nil;
+
+ for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
+ g.vbuf[i] = nil;
+ g.ubuf[i] = nil;
+ }
+}
+
+MetalRenderNode::~MetalRenderNode()
+{
+ releaseResources();
+}
+
+void MetalRenderNode::releaseResources()
+{
+ [g.stencilEnabledDsState release];
+ g.stencilEnabledDsState = nil;
+
+ [g.pipeline release];
+ g.pipeline = nil;
+
+ [g.vs.first release];
+ [g.vs.second release];
+
+ [g.fs.first release];
+ [g.fs.second release];
+
+ g.vs.first = g.fs.first = nil;
+ g.vs.second = g.fs.second = nil;
+
+ for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
+ [g.vbuf[i] release];
+ g.vbuf[i] = nil;
+ [g.ubuf[i] release];
+ g.ubuf[i] = nil;
+ }
+}
+
+void MetalRenderNode::render(const RenderState *state)
+{
+ QQuickWindow *window = m_item->window();
+ const QQuickWindow::GraphicsStateInfo *stateInfo = 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 p1(0, 0);
+ QPointF p2(0, m_item->height() - 1);
+
+ float vertices[6] = { float(p0.x()), float(p0.y()),
+ float(p1.x()), float(p1.y()),
+ float(p2.x()), float(p2.y()) };
+ char *p = (char *) [vbuf contents];
+ memcpy(p, vertices, VERTEX_SIZE);
+
+ const QMatrix4x4 mvp = *state->projectionMatrix() * *matrix();
+ const float opacity = inheritedOpacity();
+
+ p = (char *) [ubuf contents];
+ memcpy(p, mvp.constData(), 64);
+ memcpy(p + 64, &opacity, 4);
+
+ QSGRendererInterface *rif = window->rendererInterface();
+ id<MTLRenderCommandEncoder> encoder = (id<MTLRenderCommandEncoder>) rif->getResource(
+ window, QSGRendererInterface::CommandEncoderResource);
+ Q_ASSERT(encoder);
+
+ [encoder setVertexBuffer: vbuf offset: 0 atIndex: 1];
+ [encoder setVertexBuffer: vbuf offset: VERTEX_SIZE atIndex: 2];
+
+ [encoder setVertexBuffer: ubuf offset: 0 atIndex: 0];
+ [encoder setFragmentBuffer: ubuf offset: 0 atIndex: 0];
+
+ // Clip support.
+ if (state->scissorEnabled()) {
+ const QRect r = state->scissorRect(); // bottom-up
+ MTLScissorRect s;
+ s.x = r.x();
+ s.y = (window->height() * window->effectiveDevicePixelRatio()) - (r.y() + r.height());
+ s.width = r.width();
+ s.height = r.height();
+ [encoder setScissorRect: s];
+ }
+ if (state->stencilEnabled()) {
+ [encoder setDepthStencilState: g.stencilEnabledDsState];
+ [encoder setStencilReferenceValue: state->stencilValue()];
+ }
+
+ [encoder setRenderPipelineState: g.pipeline];
+ [encoder drawPrimitives: MTLPrimitiveTypeTriangle vertexStart: 0 vertexCount: 3 instanceCount: 1 baseInstance: 0];
+}
+
+QSGRenderNode::StateFlags MetalRenderNode::changedStates() const
+{
+ return BlendState | ScissorState | StencilState;
+}
+
+QSGRenderNode::RenderingFlags MetalRenderNode::flags() const
+{
+ return BoundedRectRendering | DepthAwareRendering;
+}
+
+QRectF MetalRenderNode::rect() const
+{
+ return QRect(0, 0, m_item->width(), m_item->height());
+}
diff --git a/examples/quick/scenegraph/rendernode/metalshader.frag b/examples/quick/scenegraph/rendernode/metalshader.frag
new file mode 100644
index 0000000000..907faa537f
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/metalshader.frag
@@ -0,0 +1,28 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct buf
+{
+ float4x4 matrix;
+ float opacity;
+};
+
+struct main0_out
+{
+ float4 fragColor [[color(0)]];
+};
+
+struct main0_in
+{
+ float4 v_color [[user(locn0)]];
+};
+
+fragment main0_out main0(main0_in in [[stage_in]], constant buf& ubuf [[buffer(0)]])
+{
+ main0_out out = {};
+ out.fragColor = in.v_color * ubuf.opacity;
+ return out;
+}
+
diff --git a/examples/quick/scenegraph/rendernode/metalshader.vert b/examples/quick/scenegraph/rendernode/metalshader.vert
new file mode 100644
index 0000000000..12b721f524
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/metalshader.vert
@@ -0,0 +1,31 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct buf
+{
+ float4x4 matrix;
+ float opacity;
+};
+
+struct main0_out
+{
+ float4 v_color [[user(locn0)]];
+ float4 gl_Position [[position]];
+};
+
+struct main0_in
+{
+ float4 pos [[attribute(0)]];
+ float4 color [[attribute(1)]];
+};
+
+vertex main0_out main0(main0_in in [[stage_in]], constant buf& ubuf [[buffer(0)]])
+{
+ main0_out out = {};
+ out.v_color = in.color;
+ out.gl_Position = ubuf.matrix * in.pos;
+ return out;
+}
+
diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.cpp b/examples/quick/scenegraph/rendernode/openglrenderer.cpp
index 3c68830db6..0633731617 100644
--- a/examples/quick/scenegraph/rendernode/openglrenderer.cpp
+++ b/examples/quick/scenegraph/rendernode/openglrenderer.cpp
@@ -57,6 +57,7 @@
#include <QOpenGLBuffer>
#include <QOpenGLFunctions>
+//! [1]
OpenGLRenderNode::OpenGLRenderNode(QQuickItem *item)
: m_item(item)
{
@@ -74,6 +75,7 @@ void OpenGLRenderNode::releaseResources()
delete m_vbo;
m_vbo = nullptr;
}
+//! [1]
void OpenGLRenderNode::init()
{
@@ -121,19 +123,21 @@ void OpenGLRenderNode::init()
m_vbo->release();
}
+//! [2]
void OpenGLRenderNode::render(const RenderState *state)
{
if (!m_program)
init();
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
-
m_program->bind();
m_program->setUniformValue(m_matrixUniform, *state->projectionMatrix() * *matrix());
m_program->setUniformValue(m_opacityUniform, float(inheritedOpacity()));
+//! [2]
m_vbo->bind();
+//! [5]
QPointF p0(m_item->width() - 1, m_item->height() - 1);
QPointF p1(0, 0);
QPointF p2(0, m_item->height() - 1);
@@ -142,23 +146,43 @@ void OpenGLRenderNode::render(const RenderState *state)
GLfloat(p1.x()), GLfloat(p1.y()),
GLfloat(p2.x()), GLfloat(p2.y()) };
m_vbo->write(0, vertices, sizeof(vertices));
+//! [5]
m_program->setAttributeBuffer(0, GL_FLOAT, 0, 2);
m_program->setAttributeBuffer(1, GL_FLOAT, sizeof(vertices), 3);
m_program->enableAttributeArray(0);
m_program->enableAttributeArray(1);
- // Note that clipping (scissor or stencil) is ignored in this example.
+ // We are prepared both for the legacy (direct OpenGL) and the modern
+ // (abstracted by RHI) OpenGL scenegraph. So set all the states that are
+ // important to us.
+
+ //! [3]
+ f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
f->glEnable(GL_BLEND);
f->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ // Clip support.
+ if (state->scissorEnabled()) {
+ f->glEnable(GL_SCISSOR_TEST);
+ const QRect r = state->scissorRect(); // already bottom-up
+ f->glScissor(r.x(), r.y(), r.width(), r.height());
+ }
+ if (state->stencilEnabled()) {
+ f->glEnable(GL_STENCIL_TEST);
+ f->glStencilFunc(GL_EQUAL, state->stencilValue(), 0xFF);
+ f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ }
+
f->glDrawArrays(GL_TRIANGLES, 0, 3);
+ //! [3]
}
+//! [4]
QSGRenderNode::StateFlags OpenGLRenderNode::changedStates() const
{
- return BlendState;
+ return BlendState | ScissorState | StencilState;
}
QSGRenderNode::RenderingFlags OpenGLRenderNode::flags() const
@@ -170,5 +194,6 @@ QRectF OpenGLRenderNode::rect() const
{
return QRect(0, 0, m_item->width(), m_item->height());
}
+//! [4]
#endif // opengl
diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.h b/examples/quick/scenegraph/rendernode/openglrenderer.h
index ac640405c5..8d2d3caad1 100644
--- a/examples/quick/scenegraph/rendernode/openglrenderer.h
+++ b/examples/quick/scenegraph/rendernode/openglrenderer.h
@@ -63,6 +63,7 @@ class QOpenGLBuffer;
QT_END_NAMESPACE
+//! [1]
class OpenGLRenderNode : public QSGRenderNode
{
public:
@@ -74,6 +75,7 @@ public:
StateFlags changedStates() const override;
RenderingFlags flags() const override;
QRectF rect() const override;
+//! [1]
private:
void init();
diff --git a/examples/quick/scenegraph/rendernode/rendernode.pro b/examples/quick/scenegraph/rendernode/rendernode.pro
index 76e498042b..897b0b1f08 100644
--- a/examples/quick/scenegraph/rendernode/rendernode.pro
+++ b/examples/quick/scenegraph/rendernode/rendernode.pro
@@ -22,3 +22,9 @@ qtConfig(d3d12) {
SOURCES += d3d12renderer.cpp
LIBS += -ld3d12
}
+
+macos {
+ HEADERS += metalrenderer.h
+ SOURCES += metalrenderer.mm
+ LIBS += -framework Metal -framework AppKit
+}
diff --git a/examples/quick/scenegraph/rendernode/rendernode.qrc b/examples/quick/scenegraph/rendernode/rendernode.qrc
index 049adcf8a6..5907eab62c 100644
--- a/examples/quick/scenegraph/rendernode/rendernode.qrc
+++ b/examples/quick/scenegraph/rendernode/rendernode.qrc
@@ -3,5 +3,7 @@
<file>main.qml</file>
<file>shader_vert.cso</file>
<file>shader_frag.cso</file>
+ <file>metalshader.vert</file>
+ <file>metalshader.frag</file>
</qresource>
</RCC>
diff --git a/examples/quick/shared/CheckBox.qml b/examples/quick/shared/CheckBox.qml
index 7b1588d2d9..51b6aabc09 100644
--- a/examples/quick/shared/CheckBox.qml
+++ b/examples/quick/shared/CheckBox.qml
@@ -87,7 +87,7 @@ Item {
anchors.margins: frame.width / 5
fillMode: Image.PreserveAspectFit
smooth: true
- visible: checked
+ visible: root.checked
}
}
Text {
diff --git a/examples/quick/shared/LauncherList.qml b/examples/quick/shared/LauncherList.qml
index fe44cf3634..9859b5b635 100644
--- a/examples/quick/shared/LauncherList.qml
+++ b/examples/quick/shared/LauncherList.qml
@@ -51,6 +51,7 @@
import QtQuick 2.12
Rectangle {
+ id: root
property int activePageCount: 0
//model is a list of {"name":"somename", "url":"file:///some/url/mainfile.qml"}
@@ -74,7 +75,7 @@ Rectangle {
id: launcherList
clip: true
delegate: SimpleLauncherDelegate{
- onClicked: showExample(url)
+ onClicked: root.showExample(url)
}
model: ListModel {id:myModel}
anchors.fill: parent
@@ -116,7 +117,7 @@ Rectangle {
ParallelAnimation {
id: showAnim
ScriptAction {
- script: activePageCount++
+ script: root.activePageCount++
}
NumberAnimation {
target: launcherList
@@ -144,7 +145,7 @@ Rectangle {
id: exitAnim
ScriptAction {
- script: activePageCount--
+ script: root.activePageCount--
}
ParallelAnimation {
@@ -182,7 +183,7 @@ Rectangle {
visible: height > 0
anchors.bottom: parent.bottom
width: parent.width
- height: activePageCount > 0 ? 40 : 0
+ height: root.activePageCount > 0 ? 40 : 0
Behavior on height {
NumberAnimation {
@@ -222,7 +223,7 @@ Rectangle {
TapHandler {
id: tapHandler
- enabled: activePageCount > 0
+ enabled: root.activePageCount > 0
onTapped: {
pageContainer.children[pageContainer.children.length - 1].exit()
}
diff --git a/examples/quick/shared/Slider.qml b/examples/quick/shared/Slider.qml
index cdda86e39e..5b08034571 100644
--- a/examples/quick/shared/Slider.qml
+++ b/examples/quick/shared/Slider.qml
@@ -83,7 +83,7 @@ Item {
anchors.left: parent.left
anchors.leftMargin: 16
height: childrenRect.height
- width: Math.max(minLabelWidth, childrenRect.width)
+ width: Math.max(slider.minLabelWidth, childrenRect.width)
anchors.verticalCenter: parent.verticalCenter
Text {
text: slider.name + ":"