aboutsummaryrefslogtreecommitdiffstats
path: root/examples/quick/scenegraph/metaltextureimport
diff options
context:
space:
mode:
Diffstat (limited to 'examples/quick/scenegraph/metaltextureimport')
-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.pro12
-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
10 files changed, 877 insertions, 0 deletions
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..66a39083f7
--- /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..5b11606946
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/metaltextureimport.pro
@@ -0,0 +1,12 @@
+!macos: error("This example requires macOS")
+
+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;
+}