diff options
author | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-10-14 18:46:38 +0200 |
---|---|---|
committer | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-10-14 19:02:37 +0200 |
commit | c2f8b9535d34da6948ccf45b7d5fd90de2f1bc9e (patch) | |
tree | c6f7e058a985d7c18b51cadc76283caf555071c9 /examples/quick | |
parent | 9e633bbda7608ac0231809e2a6a97ae8f2d849d6 (diff) | |
parent | 803f18f02e5609a1ca00a5b78ea6d3613d44e1a0 (diff) |
Merge remote-tracking branch 'origin/dev' into wip/cmake
Removed dependencies.yaml because we don't use it yet in wip/cmake.
Fixed conflict in qmlcachegen.cpp.
Change-Id: Ie1060c737bee1daa85779903598e5b6d5020d922
Diffstat (limited to 'examples/quick')
79 files changed, 2115 insertions, 154 deletions
diff --git a/examples/quick/customitems/maskedmousearea/main.cpp b/examples/quick/customitems/maskedmousearea/main.cpp index 63558d1b20..626f0fd765 100644 --- a/examples/quick/customitems/maskedmousearea/main.cpp +++ b/examples/quick/customitems/maskedmousearea/main.cpp @@ -59,7 +59,7 @@ int main(int argc, char* argv[]) QGuiApplication app(argc,argv); QQuickView view; - qmlRegisterType<MaskedMouseArea>("Example", 1, 0, "MaskedMouseArea"); + qmlRegisterTypesAndRevisions<MaskedMouseArea>("Example", 1); view.setSource(QUrl("qrc:///customitems/maskedmousearea/maskedmousearea.qml")); view.show(); diff --git a/examples/quick/customitems/maskedmousearea/maskedmousearea.h b/examples/quick/customitems/maskedmousearea/maskedmousearea.h index 28ff6ca9df..39d78a019a 100644 --- a/examples/quick/customitems/maskedmousearea/maskedmousearea.h +++ b/examples/quick/customitems/maskedmousearea/maskedmousearea.h @@ -62,6 +62,7 @@ class MaskedMouseArea : public QQuickItem Q_PROPERTY(bool containsMouse READ containsMouse NOTIFY containsMouseChanged) Q_PROPERTY(QUrl maskSource READ maskSource WRITE setMaskSource NOTIFY maskSourceChanged) Q_PROPERTY(qreal alphaThreshold READ alphaThreshold WRITE setAlphaThreshold NOTIFY alphaThresholdChanged) + QML_ELEMENT public: MaskedMouseArea(QQuickItem *parent = 0); diff --git a/examples/quick/customitems/painteditem/TextBalloonPlugin/plugin.h b/examples/quick/customitems/painteditem/TextBalloonPlugin/plugin.h index d3a1f4ba91..41750d1647 100644 --- a/examples/quick/customitems/painteditem/TextBalloonPlugin/plugin.h +++ b/examples/quick/customitems/painteditem/TextBalloonPlugin/plugin.h @@ -59,6 +59,6 @@ class TextBalloonPlugin : public QQmlExtensionPlugin public: void registerTypes(const char *uri) { - qmlRegisterType<TextBalloon>(uri, 1, 0, "TextBalloon"); + qmlRegisterTypesAndRevisions<TextBalloon>(uri, 1); } }; diff --git a/examples/quick/customitems/painteditem/textballoon.h b/examples/quick/customitems/painteditem/textballoon.h index 81b3e2961d..eb72804602 100644 --- a/examples/quick/customitems/painteditem/textballoon.h +++ b/examples/quick/customitems/painteditem/textballoon.h @@ -58,6 +58,7 @@ class TextBalloon : public QQuickPaintedItem { Q_OBJECT Q_PROPERTY(bool rightAligned READ isRightAligned WRITE setRightAligned NOTIFY rightAlignedChanged) + QML_ELEMENT public: TextBalloon(QQuickItem *parent = 0); diff --git a/examples/quick/imageelements/doc/src/imageelements.qdoc b/examples/quick/imageelements/doc/src/imageelements.qdoc index 2c6490fb2c..4c00915e56 100644 --- a/examples/quick/imageelements/doc/src/imageelements.qdoc +++ b/examples/quick/imageelements/doc/src/imageelements.qdoc @@ -38,12 +38,12 @@ \section1 Scaling with BorderImage - \e BorderImage shows off the various scaling modes of the \l BorderImage + \e BorderImage shows the various scaling modes of the \l BorderImage type by setting its horizontalTileMode and verticalTileMode properties. \section1 Image Fill - \e Image shows off the various fill modes of the \l Image type. + \e Image shows the various fill modes of the \l Image type. \section1 Shadow Effects diff --git a/examples/quick/quickwidgets/qquickviewcomparison/fbitem.h b/examples/quick/quickwidgets/qquickviewcomparison/fbitem.h index 3a4c5a13c2..6270e7adff 100644 --- a/examples/quick/quickwidgets/qquickviewcomparison/fbitem.h +++ b/examples/quick/quickwidgets/qquickviewcomparison/fbitem.h @@ -111,6 +111,7 @@ class FbItem : public QQuickFramebufferObject Q_PROPERTY(QVector3D target READ target WRITE setTarget) Q_PROPERTY(QVector3D rotation READ rotation WRITE setRotation) Q_PROPERTY(bool multisample READ multisample WRITE setMultisample) + QML_ELEMENT public: explicit FbItem(QQuickItem *parent = 0); diff --git a/examples/quick/quickwidgets/qquickviewcomparison/mainwindow.cpp b/examples/quick/quickwidgets/qquickviewcomparison/mainwindow.cpp index 84e116dce6..6ebf36c454 100644 --- a/examples/quick/quickwidgets/qquickviewcomparison/mainwindow.cpp +++ b/examples/quick/quickwidgets/qquickviewcomparison/mainwindow.cpp @@ -86,7 +86,7 @@ MainWindow::MainWindow(bool transparency, bool noRenderAlpha) m_labelStatus = new QLabel; layout->addWidget(m_labelStatus); - qmlRegisterType<FbItem>("fbitem", 1, 0, "FbItem"); + qmlRegisterTypesAndRevisions<FbItem>("fbitem", 1); QWidget *quickContainer = new QWidget; layout->addWidget(quickContainer); diff --git a/examples/quick/quickwidgets/quickwidget/fbitem.h b/examples/quick/quickwidgets/quickwidget/fbitem.h index 7beb9874a5..c1fb866c9a 100644 --- a/examples/quick/quickwidgets/quickwidget/fbitem.h +++ b/examples/quick/quickwidgets/quickwidget/fbitem.h @@ -56,6 +56,7 @@ class FbItem : public QQuickFramebufferObject { Q_OBJECT + QML_ELEMENT public: Renderer *createRenderer() const; }; diff --git a/examples/quick/quickwidgets/quickwidget/main.cpp b/examples/quick/quickwidgets/quickwidget/main.cpp index c54586c5de..541f543f07 100644 --- a/examples/quick/quickwidgets/quickwidget/main.cpp +++ b/examples/quick/quickwidgets/quickwidget/main.cpp @@ -214,7 +214,7 @@ int main(int argc, char **argv) optMultipleSample = parser.isSet(multipleSampleOption); optCoreProfile = parser.isSet(coreProfileOption); - qmlRegisterType<FbItem>("QuickWidgetExample", 1, 0, "FbItem"); + qmlRegisterTypesAndRevisions<FbItem>("QuickWidgetExample", 1); MainWindow mainWindow; mainWindow.show(); diff --git a/examples/quick/scenegraph/customgeometry/beziercurve.h b/examples/quick/scenegraph/customgeometry/beziercurve.h index f2f7832e6d..be9e4ef49f 100644 --- a/examples/quick/scenegraph/customgeometry/beziercurve.h +++ b/examples/quick/scenegraph/customgeometry/beziercurve.h @@ -64,6 +64,7 @@ class BezierCurve : public QQuickItem Q_PROPERTY(QPointF p4 READ p4 WRITE setP4 NOTIFY p4Changed) Q_PROPERTY(int segmentCount READ segmentCount WRITE setSegmentCount NOTIFY segmentCountChanged) + QML_ELEMENT public: BezierCurve(QQuickItem *parent = 0); diff --git a/examples/quick/scenegraph/customgeometry/main.cpp b/examples/quick/scenegraph/customgeometry/main.cpp index 6f3c24e87b..9352e4bd24 100644 --- a/examples/quick/scenegraph/customgeometry/main.cpp +++ b/examples/quick/scenegraph/customgeometry/main.cpp @@ -58,7 +58,7 @@ int main(int argc, char **argv) { QGuiApplication app(argc, argv); - qmlRegisterType<BezierCurve>("CustomGeometry", 1, 0, "BezierCurve"); + qmlRegisterTypesAndRevisions<BezierCurve>("CustomGeometry", 1); QQuickView view; QSurfaceFormat format = view.format(); diff --git a/examples/quick/scenegraph/d3d11underqml/d3d11squircle.h b/examples/quick/scenegraph/d3d11underqml/d3d11squircle.h index be9aadc43b..4be3671d1c 100644 --- a/examples/quick/scenegraph/d3d11underqml/d3d11squircle.h +++ b/examples/quick/scenegraph/d3d11underqml/d3d11squircle.h @@ -59,6 +59,7 @@ class D3D11Squircle : public QQuickItem { Q_OBJECT Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged) + QML_ELEMENT public: D3D11Squircle(); diff --git a/examples/quick/scenegraph/d3d11underqml/d3d11underqml.pro b/examples/quick/scenegraph/d3d11underqml/d3d11underqml.pro index 3c94d48ac4..7658a9a813 100644 --- a/examples/quick/scenegraph/d3d11underqml/d3d11underqml.pro +++ b/examples/quick/scenegraph/d3d11underqml/d3d11underqml.pro @@ -1,3 +1,5 @@ +!win32: error("This example requires Windows") + QT += qml quick HEADERS += d3d11squircle.h diff --git a/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc b/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc index d7b60d3b81..1f0c15e1c6 100644 --- a/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc +++ b/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc @@ -51,8 +51,8 @@ program that draws the squircles. The example is equivalent in most ways to the \l{Scene Graph - OpenGL Under - QML}{OpenGL Under QML} and \l{Scene Graph - Metal Under QML}{Metal Under - QML} examples, they all render the same custom content, just via different - native APIs. + QML}{OpenGL Under QML}, \l{Scene Graph - Metal Under QML}{Metal Under QML}, + and \l{Scene Graph - Vulkan Under QML}{Vulkan Under QML} examples, they all + render the same custom content, just via different native APIs. */ diff --git a/examples/quick/scenegraph/d3d11underqml/main.cpp b/examples/quick/scenegraph/d3d11underqml/main.cpp index d26de1144a..dcab8c879c 100644 --- a/examples/quick/scenegraph/d3d11underqml/main.cpp +++ b/examples/quick/scenegraph/d3d11underqml/main.cpp @@ -56,7 +56,7 @@ int main(int argc, char **argv) { QGuiApplication app(argc, argv); - qmlRegisterType<D3D11Squircle>("D3D11UnderQML", 1, 0, "D3D11Squircle"); + qmlRegisterTypesAndRevisions<D3D11Squircle>("D3D11UnderQML", 1); QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Direct3D11Rhi); diff --git a/examples/quick/scenegraph/textureinsgnode/CMakeLists.txt b/examples/quick/scenegraph/fboitem/CMakeLists.txt index 1117ef22a9..1117ef22a9 100644 --- a/examples/quick/scenegraph/textureinsgnode/CMakeLists.txt +++ b/examples/quick/scenegraph/fboitem/CMakeLists.txt diff --git a/examples/quick/scenegraph/textureinsgnode/doc/images/textureinsgnode-example.jpg b/examples/quick/scenegraph/fboitem/doc/images/fboitem-example.jpg Binary files differindex 306b8bab20..306b8bab20 100644 --- a/examples/quick/scenegraph/textureinsgnode/doc/images/textureinsgnode-example.jpg +++ b/examples/quick/scenegraph/fboitem/doc/images/fboitem-example.jpg diff --git a/examples/quick/scenegraph/textureinsgnode/doc/src/textureinsgnode.qdoc b/examples/quick/scenegraph/fboitem/doc/src/fboitem.qdoc index c1c830338b..b5add02991 100644 --- a/examples/quick/scenegraph/textureinsgnode/doc/src/textureinsgnode.qdoc +++ b/examples/quick/scenegraph/fboitem/doc/src/fboitem.qdoc @@ -26,11 +26,11 @@ ****************************************************************************/ /*! - \example scenegraph/textureinsgnode + \example scenegraph/fboitem \title Scene Graph - Rendering FBOs \ingroup qtquickexamples \brief Shows how to use FramebufferObjects with Qt Quick. - \image textureinsgnode-example.jpg + \image fboitem-example.jpg */ diff --git a/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.cpp b/examples/quick/scenegraph/fboitem/fboinsgrenderer.cpp index 8ba5bddb2a..8ba5bddb2a 100644 --- a/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.cpp +++ b/examples/quick/scenegraph/fboitem/fboinsgrenderer.cpp diff --git a/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.h b/examples/quick/scenegraph/fboitem/fboinsgrenderer.h index e1a9ce22c8..1b92b56851 100644 --- a/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.h +++ b/examples/quick/scenegraph/fboitem/fboinsgrenderer.h @@ -58,6 +58,7 @@ class LogoRenderer; class FboInSGRenderer : public QQuickFramebufferObject { Q_OBJECT + QML_NAMED_ELEMENT(Renderer) public: Renderer *createRenderer() const; }; diff --git a/examples/quick/scenegraph/textureinsgnode/textureinsgnode.pro b/examples/quick/scenegraph/fboitem/fboitem.pro index 238e20a553..e40e5f4cf8 100644 --- a/examples/quick/scenegraph/textureinsgnode/textureinsgnode.pro +++ b/examples/quick/scenegraph/fboitem/fboitem.pro @@ -7,9 +7,9 @@ INCLUDEPATH += ../shared HEADERS += ../shared/logorenderer.h SOURCES += ../shared/logorenderer.cpp -RESOURCES += textureinsgnode.qrc +RESOURCES += fboitem.qrc -target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/textureinsgnode +target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/fboitem INSTALLS += target OTHER_FILES += \ diff --git a/examples/quick/scenegraph/textureinsgnode/textureinsgnode.qrc b/examples/quick/scenegraph/fboitem/fboitem.qrc index 9ecf0ada1c..9d9db70654 100644 --- a/examples/quick/scenegraph/textureinsgnode/textureinsgnode.qrc +++ b/examples/quick/scenegraph/fboitem/fboitem.qrc @@ -1,5 +1,5 @@ <RCC> - <qresource prefix="/scenegraph/textureinsgnode"> + <qresource prefix="/scenegraph/fboitem"> <file>main.qml</file> </qresource> </RCC> diff --git a/examples/quick/scenegraph/textureinsgnode/main.cpp b/examples/quick/scenegraph/fboitem/main.cpp index 8eececc0aa..056bf24ade 100644 --- a/examples/quick/scenegraph/textureinsgnode/main.cpp +++ b/examples/quick/scenegraph/fboitem/main.cpp @@ -58,11 +58,11 @@ int main(int argc, char **argv) { QGuiApplication app(argc, argv); - qmlRegisterType<FboInSGRenderer>("SceneGraphRendering", 1, 0, "Renderer"); + qmlRegisterTypesAndRevisions<FboInSGRenderer>("SceneGraphRendering", 1); QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView); - view.setSource(QUrl("qrc:///scenegraph/textureinsgnode/main.qml")); + view.setSource(QUrl("qrc:///scenegraph/fboitem/main.qml")); view.show(); return app.exec(); diff --git a/examples/quick/scenegraph/textureinsgnode/main.qml b/examples/quick/scenegraph/fboitem/main.qml index 92fa99e847..92fa99e847 100644 --- a/examples/quick/scenegraph/textureinsgnode/main.qml +++ b/examples/quick/scenegraph/fboitem/main.qml diff --git a/examples/quick/scenegraph/graph/graph.h b/examples/quick/scenegraph/graph/graph.h index a1a00cb6dc..e32e657e0e 100644 --- a/examples/quick/scenegraph/graph/graph.h +++ b/examples/quick/scenegraph/graph/graph.h @@ -56,6 +56,7 @@ class Graph : public QQuickItem { Q_OBJECT + QML_ELEMENT public: Graph(); diff --git a/examples/quick/scenegraph/graph/main.cpp b/examples/quick/scenegraph/graph/main.cpp index 2406457ab5..9a575944b9 100644 --- a/examples/quick/scenegraph/graph/main.cpp +++ b/examples/quick/scenegraph/graph/main.cpp @@ -58,7 +58,7 @@ int main(int argc, char *argv[]) { QGuiApplication a(argc, argv); - qmlRegisterType<Graph>("Graph", 1, 0, "Graph"); + qmlRegisterTypesAndRevisions<Graph>("Graph", 1); QQuickView view; view.resize(800, 400); diff --git a/examples/quick/scenegraph/metaltextureimport/doc/images/metaltextureimport-example.jpg b/examples/quick/scenegraph/metaltextureimport/doc/images/metaltextureimport-example.jpg Binary files differnew file mode 100644 index 0000000000..19ad40cd85 --- /dev/null +++ b/examples/quick/scenegraph/metaltextureimport/doc/images/metaltextureimport-example.jpg 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; +} diff --git a/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc b/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc index d499f47de3..fb28270315 100644 --- a/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc +++ b/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc @@ -61,8 +61,9 @@ program that draws the squircles. The example is equivalent in most ways to the \l{Scene Graph - OpenGL Under - QML}{OpenGL Under QML} and \l{Scene Graph - Direct3D 11 Under QML}{Direct3D - 11 Under QML} examples, they all render the same custom content, just via - different native APIs. + QML}{OpenGL Under QML}, \l{Scene Graph - Direct3D 11 Under QML}{Direct3D 11 + Under QML}, and \l{Scene Graph - Vulkan Under QML}{Vulkan Under QML} + examples, they all render the same custom content, just via different + native APIs. */ diff --git a/examples/quick/scenegraph/metalunderqml/main.cpp b/examples/quick/scenegraph/metalunderqml/main.cpp index 5ad337abb1..3e620137cd 100644 --- a/examples/quick/scenegraph/metalunderqml/main.cpp +++ b/examples/quick/scenegraph/metalunderqml/main.cpp @@ -56,7 +56,7 @@ int main(int argc, char **argv) { QGuiApplication app(argc, argv); - qmlRegisterType<MetalSquircle>("MetalUnderQML", 1, 0, "MetalSquircle"); + qmlRegisterTypesAndRevisions<MetalSquircle>("MetalUnderQML", 1); QQuickWindow::setSceneGraphBackend(QSGRendererInterface::MetalRhi); diff --git a/examples/quick/scenegraph/metalunderqml/metalsquircle.h b/examples/quick/scenegraph/metalunderqml/metalsquircle.h index 43c4afad21..18db7d45f3 100644 --- a/examples/quick/scenegraph/metalunderqml/metalsquircle.h +++ b/examples/quick/scenegraph/metalunderqml/metalsquircle.h @@ -59,6 +59,7 @@ class MetalSquircle : public QQuickItem { Q_OBJECT Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged) + QML_ELEMENT public: MetalSquircle(); diff --git a/examples/quick/scenegraph/metalunderqml/metalsquircle.mm b/examples/quick/scenegraph/metalunderqml/metalsquircle.mm index 92aceeb433..5ca6daa01a 100644 --- a/examples/quick/scenegraph/metalunderqml/metalsquircle.mm +++ b/examples/quick/scenegraph/metalunderqml/metalsquircle.mm @@ -186,7 +186,12 @@ void MetalSquircle::sync() { if (!m_renderer) { m_renderer = new SquircleRenderer; + // Initializing resources is done before starting to encode render + // commands, regardless of wanting an underlay or overlay. connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::frameStart, Qt::DirectConnection); + // Here we want an underlay and therefore connect to + // beforeRenderPassRecording. Changing to afterRenderPassRecording + // would render the squircle on top (overlay). connect(window(), &QQuickWindow::beforeRenderPassRecording, m_renderer, &SquircleRenderer::mainPassRecordingStart, Qt::DirectConnection); } m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio()); @@ -210,7 +215,7 @@ void SquircleRenderer::frameStart() prepareShader(FragmentStage); if (!m_initialized) - init(m_window->graphicsStateInfo()->framesInFlight); + init(m_window->graphicsStateInfo().framesInFlight); } static const float vertices[] = { @@ -228,7 +233,7 @@ void SquircleRenderer::mainPassRecordingStart() // the scenegraph's main renderpass. It does not create its own passes, // rendertargets, etc. so no synchronization is needed. - const QQuickWindow::GraphicsStateInfo *stateInfo = m_window->graphicsStateInfo(); + const QQuickWindow::GraphicsStateInfo &stateInfo(m_window->graphicsStateInfo()); QSGRendererInterface *rif = m_window->rendererInterface(); id<MTLRenderCommandEncoder> encoder = (id<MTLRenderCommandEncoder>) rif->getResource( @@ -237,7 +242,7 @@ void SquircleRenderer::mainPassRecordingStart() m_window->beginExternalCommands(); - void *p = [m_ubuf[stateInfo->currentFrameSlot] contents]; + void *p = [m_ubuf[stateInfo.currentFrameSlot] contents]; float t = m_t; memcpy(p, &t, 4); @@ -250,7 +255,7 @@ void SquircleRenderer::mainPassRecordingStart() vp.zfar = 1; [encoder setViewport: vp]; - [encoder setFragmentBuffer: m_ubuf[stateInfo->currentFrameSlot] offset: 0 atIndex: 0]; + [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]; diff --git a/examples/quick/scenegraph/metalunderqml/metalunderqml.pro b/examples/quick/scenegraph/metalunderqml/metalunderqml.pro index 9b27638a6d..9fd131fe1b 100644 --- a/examples/quick/scenegraph/metalunderqml/metalunderqml.pro +++ b/examples/quick/scenegraph/metalunderqml/metalunderqml.pro @@ -1,3 +1,5 @@ +!macos: error("This example requires macOS") + QT += qml quick HEADERS += metalsquircle.h diff --git a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc index ed46b40420..9676815c44 100644 --- a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc +++ b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc @@ -50,9 +50,10 @@ in the QML file and this value is used by the OpenGL shader program that draws the squircles. - The example is equivalent in most ways to the \l{Scene Graph - Direct3D 11 Under - QML}{Direct3D 11 Under QML} and \l{Scene Graph - Metal Under QML}{Metal Under - QML} examples, they all render the same custom content, just via different + The example is equivalent in most ways to the \l{Scene Graph - Direct3D 11 + Under QML}{Direct3D 11 Under QML}, \l{Scene Graph - Metal Under QML}{Metal + Under QML}, and \l{Scene Graph - Vulkan Under QML}{Vulkan Under QML} + examples, they all render the same custom content, just via different native APIs. \snippet scenegraph/openglunderqml/squircle.h 2 diff --git a/examples/quick/scenegraph/openglunderqml/main.cpp b/examples/quick/scenegraph/openglunderqml/main.cpp index 022d6a75bb..c04d0da68f 100644 --- a/examples/quick/scenegraph/openglunderqml/main.cpp +++ b/examples/quick/scenegraph/openglunderqml/main.cpp @@ -59,7 +59,7 @@ int main(int argc, char **argv) { QGuiApplication app(argc, argv); - qmlRegisterType<Squircle>("OpenGLUnderQML", 1, 0, "Squircle"); + qmlRegisterTypesAndRevisions<Squircle>("OpenGLUnderQML", 1); QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView); diff --git a/examples/quick/scenegraph/openglunderqml/squircle.h b/examples/quick/scenegraph/openglunderqml/squircle.h index 1b9995bc1e..c24fdd50c2 100644 --- a/examples/quick/scenegraph/openglunderqml/squircle.h +++ b/examples/quick/scenegraph/openglunderqml/squircle.h @@ -86,6 +86,7 @@ class Squircle : public QQuickItem { Q_OBJECT Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged) + QML_ELEMENT public: Squircle(); diff --git a/examples/quick/scenegraph/rendernode/customrenderitem.cpp b/examples/quick/scenegraph/rendernode/customrenderitem.cpp index 67a9cccfc6..e55cf0a2f4 100644 --- a/examples/quick/scenegraph/rendernode/customrenderitem.cpp +++ b/examples/quick/scenegraph/rendernode/customrenderitem.cpp @@ -70,48 +70,58 @@ CustomRenderItem::CustomRenderItem(QQuickItem *parent) QSGNode *CustomRenderItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) { QSGRenderNode *n = static_cast<QSGRenderNode *>(node); - if (!n) { - QSGRendererInterface *ri = window()->rendererInterface(); - if (!ri) - return nullptr; - switch (ri->graphicsApi()) { - case QSGRendererInterface::OpenGL: - Q_FALLTHROUGH(); - case QSGRendererInterface::OpenGLRhi: + + QSGRendererInterface *ri = window()->rendererInterface(); + if (!ri) + return nullptr; + + switch (ri->graphicsApi()) { + case QSGRendererInterface::OpenGL: + Q_FALLTHROUGH(); + case QSGRendererInterface::OpenGLRhi: #if QT_CONFIG(opengl) - n = new OpenGLRenderNode(this); + if (!n) + n = new OpenGLRenderNode; + static_cast<OpenGLRenderNode *>(n)->sync(this); #endif - break; + break; - case QSGRendererInterface::MetalRhi: -#ifdef Q_OS_DARWIN - { - MetalRenderNode *metalNode = new MetalRenderNode(this); + case QSGRendererInterface::MetalRhi: +// Restore when QTBUG-78580 is done and the .pro is updated accordingly +//#ifdef Q_OS_DARWIN +#ifdef Q_OS_MACOS + if (!n) { + MetalRenderNode *metalNode = new MetalRenderNode; n = metalNode; metalNode->resourceBuilder()->setWindow(window()); QObject::connect(window(), &QQuickWindow::beforeRendering, metalNode->resourceBuilder(), &MetalRenderNodeResourceBuilder::build); } + static_cast<MetalRenderNode *>(n)->sync(this); #endif - break; + break; - case QSGRendererInterface::Direct3D12: // ### Qt 6: remove + case QSGRendererInterface::Direct3D12: // ### Qt 6: remove #if QT_CONFIG(d3d12) - n = new D3D12RenderNode(this); + if (!n) + n = new D3D12RenderNode; + static_cast<D3D12RenderNode *>(n)->sync(this); #endif - break; + break; - case QSGRendererInterface::Software: - n = new SoftwareRenderNode(this); - break; - - default: - break; - } + case QSGRendererInterface::Software: if (!n) - qWarning("QSGRendererInterface reports unknown graphics API %d", ri->graphicsApi()); + n = new SoftwareRenderNode; + static_cast<SoftwareRenderNode *>(n)->sync(this); + break; + + default: + break; } + if (!n) + qWarning("QSGRendererInterface reports unknown graphics API %d", ri->graphicsApi()); + return n; } //! [2] diff --git a/examples/quick/scenegraph/rendernode/customrenderitem.h b/examples/quick/scenegraph/rendernode/customrenderitem.h index a0ac3468e4..74115b2505 100644 --- a/examples/quick/scenegraph/rendernode/customrenderitem.h +++ b/examples/quick/scenegraph/rendernode/customrenderitem.h @@ -56,6 +56,7 @@ class CustomRenderItem : public QQuickItem { Q_OBJECT + QML_ELEMENT public: CustomRenderItem(QQuickItem *parent = nullptr); diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp index 878b022950..e85811c089 100644 --- a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp +++ b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp @@ -58,11 +58,6 @@ #if QT_CONFIG(d3d12) -D3D12RenderNode::D3D12RenderNode(QQuickItem *item) - : m_item(item) -{ -} - D3D12RenderNode::~D3D12RenderNode() { releaseResources(); @@ -87,8 +82,8 @@ void D3D12RenderNode::releaseResources() void D3D12RenderNode::init() { - QSGRendererInterface *rif = m_item->window()->rendererInterface(); - m_device = static_cast<ID3D12Device *>(rif->getResource(m_item->window(), QSGRendererInterface::DeviceResource)); + QSGRendererInterface *rif = m_window->rendererInterface(); + m_device = static_cast<ID3D12Device *>(rif->getResource(m_window, QSGRendererInterface::DeviceResource)); Q_ASSERT(m_device); D3D12_ROOT_PARAMETER rootParameter; @@ -178,7 +173,7 @@ void D3D12RenderNode::init() psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; // not in use due to !DepthEnable, but this would be the correct format otherwise // We are rendering on the default render target so if the QuickWindow/View // has requested samples > 0 then we have to follow suit. - const uint samples = qMax(1, m_item->window()->format().samples()); + const uint samples = qMax(1, m_window->format().samples()); psoDesc.SampleDesc.Count = samples; if (samples > 1) { D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msaaInfo = {}; @@ -257,9 +252,9 @@ void D3D12RenderNode::render(const RenderState *state) if (!m_device) init(); - QSGRendererInterface *rif = m_item->window()->rendererInterface(); + QSGRendererInterface *rif = m_window->rendererInterface(); ID3D12GraphicsCommandList *commandList = static_cast<ID3D12GraphicsCommandList *>( - rif->getResource(m_item->window(), QSGRendererInterface::CommandListResource)); + rif->getResource(m_window, QSGRendererInterface::CommandListResource)); Q_ASSERT(commandList); const int msize = 16 * sizeof(float); @@ -268,9 +263,9 @@ void D3D12RenderNode::render(const RenderState *state) const float opacity = inheritedOpacity(); memcpy(cbPtr + 2 * msize, &opacity, sizeof(float)); - const QPointF p0(m_item->width() - 1, m_item->height() - 1); + const QPointF p0(m_width - 1, m_height - 1); const QPointF p1(0, 0); - const QPointF p2(0, m_item->height() - 1); + const QPointF p2(0, m_height - 1); float *vp = reinterpret_cast<float *>(vbPtr); *vp++ = p0.x(); @@ -301,7 +296,14 @@ QSGRenderNode::RenderingFlags D3D12RenderNode::flags() const QRectF D3D12RenderNode::rect() const { - return QRect(0, 0, m_item->width(), m_item->height()); + return QRect(0, 0, m_width, m_height); +} + +void D3D12RenderNode::sync(QQuickItem *item) +{ + m_window = item->window(); + m_width = item->width(); + m_height = item->height(); } #endif // d3d12 diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.h b/examples/quick/scenegraph/rendernode/d3d12renderer.h index ec4b5f85e8..7186b72c04 100644 --- a/examples/quick/scenegraph/rendernode/d3d12renderer.h +++ b/examples/quick/scenegraph/rendernode/d3d12renderer.h @@ -52,11 +52,10 @@ #define D3D12RENDERER_H #include <qsgrendernode.h> +#include <QQuickItem> #if QT_CONFIG(d3d12) -QT_FORWARD_DECLARE_CLASS(QQuickItem) - #include <d3d12.h> #include <wrl/client.h> @@ -65,7 +64,6 @@ using namespace Microsoft::WRL; class D3D12RenderNode : public QSGRenderNode { public: - D3D12RenderNode(QQuickItem *item); ~D3D12RenderNode(); void render(const RenderState *state) override; @@ -73,10 +71,15 @@ public: RenderingFlags flags() const override; QRectF rect() const override; + void sync(QQuickItem *item); + private: void init(); - QQuickItem *m_item; + QQuickWindow *m_window = nullptr; + int m_width = 0; + int m_height = 0; + ID3D12Device *m_device = nullptr; ComPtr<ID3D12PipelineState> pipelineState; ComPtr<ID3D12RootSignature> rootSignature; diff --git a/examples/quick/scenegraph/rendernode/main.cpp b/examples/quick/scenegraph/rendernode/main.cpp index 146d787e50..b1dfe47829 100644 --- a/examples/quick/scenegraph/rendernode/main.cpp +++ b/examples/quick/scenegraph/rendernode/main.cpp @@ -59,7 +59,7 @@ int main(int argc, char **argv) QGuiApplication app(argc, argv); //! [1] - qmlRegisterType<CustomRenderItem>("SceneGraphRendering", 2, 0, "CustomRenderItem"); + qmlRegisterTypesAndRevisions<CustomRenderItem>("SceneGraphRendering", 2); //! [1] QQuickView view; diff --git a/examples/quick/scenegraph/rendernode/metalrenderer.h b/examples/quick/scenegraph/rendernode/metalrenderer.h index 77c9892313..cf7fccb930 100644 --- a/examples/quick/scenegraph/rendernode/metalrenderer.h +++ b/examples/quick/scenegraph/rendernode/metalrenderer.h @@ -52,14 +52,13 @@ #define METALRENDERER_H #include <qsgrendernode.h> +#include <QQuickItem> -#ifdef Q_OS_DARWIN +//#ifdef Q_OS_DARWIN +#ifdef Q_OS_MACOS QT_BEGIN_NAMESPACE -class QQuickItem; -class QQuickWindow; - QT_END_NAMESPACE class MetalRenderNodeResourceBuilder : public QObject @@ -79,7 +78,7 @@ private: class MetalRenderNode : public QSGRenderNode { public: - MetalRenderNode(QQuickItem *item); + MetalRenderNode(); ~MetalRenderNode(); void render(const RenderState *state) override; @@ -90,9 +89,14 @@ public: MetalRenderNodeResourceBuilder *resourceBuilder() { return &m_resourceBuilder; } + void sync(QQuickItem *item); + private: - QQuickItem *m_item; MetalRenderNodeResourceBuilder m_resourceBuilder; + QQuickWindow *m_window = nullptr; + int m_width = 0; + int m_height = 0; + int m_outputHeight = 0; }; #endif // Q_OS_DARWIN diff --git a/examples/quick/scenegraph/rendernode/metalrenderer.mm b/examples/quick/scenegraph/rendernode/metalrenderer.mm index 4cb973abee..b83dc62c48 100644 --- a/examples/quick/scenegraph/rendernode/metalrenderer.mm +++ b/examples/quick/scenegraph/rendernode/metalrenderer.mm @@ -131,7 +131,7 @@ void MetalRenderNodeResourceBuilder::build() g.fs = compileShaderFromSource(g.fsSource, QByteArrayLiteral("main0")); } - const int framesInFlight = m_window->graphicsStateInfo()->framesInFlight; + const int framesInFlight = m_window->graphicsStateInfo().framesInFlight; // For simplicity's sake we use shared mode (something like host visible + // host coherent) for everything. @@ -214,8 +214,7 @@ void MetalRenderNodeResourceBuilder::build() } } -MetalRenderNode::MetalRenderNode(QQuickItem *item) - : m_item(item) +MetalRenderNode::MetalRenderNode() { g.vs.first = g.fs.first = nil; g.vs.second = g.fs.second = nil; @@ -258,14 +257,14 @@ void MetalRenderNode::releaseResources() void MetalRenderNode::render(const RenderState *state) { - QQuickWindow *window = m_item->window(); - const QQuickWindow::GraphicsStateInfo *stateInfo = window->graphicsStateInfo(); - id<MTLBuffer> vbuf = g.vbuf[stateInfo->currentFrameSlot]; - id<MTLBuffer> ubuf = g.ubuf[stateInfo->currentFrameSlot]; + Q_ASSERT(m_window); + const QQuickWindow::GraphicsStateInfo &stateInfo(m_window->graphicsStateInfo()); + id<MTLBuffer> vbuf = g.vbuf[stateInfo.currentFrameSlot]; + id<MTLBuffer> ubuf = g.ubuf[stateInfo.currentFrameSlot]; - QPointF p0(m_item->width() - 1, m_item->height() - 1); + QPointF p0(m_width - 1, m_height - 1); QPointF p1(0, 0); - QPointF p2(0, m_item->height() - 1); + QPointF p2(0, m_height - 1); float vertices[6] = { float(p0.x()), float(p0.y()), float(p1.x()), float(p1.y()), @@ -280,9 +279,9 @@ void MetalRenderNode::render(const RenderState *state) memcpy(p, mvp.constData(), 64); memcpy(p + 64, &opacity, 4); - QSGRendererInterface *rif = window->rendererInterface(); + QSGRendererInterface *rif = m_window->rendererInterface(); id<MTLRenderCommandEncoder> encoder = (id<MTLRenderCommandEncoder>) rif->getResource( - window, QSGRendererInterface::CommandEncoderResource); + m_window, QSGRendererInterface::CommandEncoderResource); Q_ASSERT(encoder); [encoder setVertexBuffer: vbuf offset: 0 atIndex: 1]; @@ -296,7 +295,7 @@ void MetalRenderNode::render(const RenderState *state) const QRect r = state->scissorRect(); // bottom-up MTLScissorRect s; s.x = r.x(); - s.y = (window->height() * window->effectiveDevicePixelRatio()) - (r.y() + r.height()); + s.y = m_outputHeight - (r.y() + r.height()); s.width = r.width(); s.height = r.height(); [encoder setScissorRect: s]; @@ -322,5 +321,13 @@ QSGRenderNode::RenderingFlags MetalRenderNode::flags() const QRectF MetalRenderNode::rect() const { - return QRect(0, 0, m_item->width(), m_item->height()); + return QRect(0, 0, m_width, m_height); +} + +void MetalRenderNode::sync(QQuickItem *item) +{ + m_window = item->window(); + m_width = item->width(); + m_height = item->height(); + m_outputHeight = m_window->height() * m_window->effectiveDevicePixelRatio(); } diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.cpp b/examples/quick/scenegraph/rendernode/openglrenderer.cpp index 0633731617..a4e619bea9 100644 --- a/examples/quick/scenegraph/rendernode/openglrenderer.cpp +++ b/examples/quick/scenegraph/rendernode/openglrenderer.cpp @@ -58,11 +58,6 @@ #include <QOpenGLFunctions> //! [1] -OpenGLRenderNode::OpenGLRenderNode(QQuickItem *item) - : m_item(item) -{ -} - OpenGLRenderNode::~OpenGLRenderNode() { releaseResources(); @@ -138,9 +133,9 @@ void OpenGLRenderNode::render(const RenderState *state) m_vbo->bind(); //! [5] - QPointF p0(m_item->width() - 1, m_item->height() - 1); + QPointF p0(m_width - 1, m_height - 1); QPointF p1(0, 0); - QPointF p2(0, m_item->height() - 1); + QPointF p2(0, m_height - 1); GLfloat vertices[6] = { GLfloat(p0.x()), GLfloat(p0.y()), GLfloat(p1.x()), GLfloat(p1.y()), @@ -192,8 +187,14 @@ QSGRenderNode::RenderingFlags OpenGLRenderNode::flags() const QRectF OpenGLRenderNode::rect() const { - return QRect(0, 0, m_item->width(), m_item->height()); + return QRect(0, 0, m_width, m_height); } //! [4] +void OpenGLRenderNode::sync(QQuickItem *item) +{ + m_width = item->width(); + m_height = item->height(); +} + #endif // opengl diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.h b/examples/quick/scenegraph/rendernode/openglrenderer.h index 8d2d3caad1..1e7977481a 100644 --- a/examples/quick/scenegraph/rendernode/openglrenderer.h +++ b/examples/quick/scenegraph/rendernode/openglrenderer.h @@ -52,12 +52,12 @@ #define OPENGLRENDERER_H #include <qsgrendernode.h> +#include <QQuickItem> #if QT_CONFIG(opengl) QT_BEGIN_NAMESPACE -class QQuickItem; class QOpenGLShaderProgram; class QOpenGLBuffer; @@ -67,7 +67,6 @@ QT_END_NAMESPACE class OpenGLRenderNode : public QSGRenderNode { public: - OpenGLRenderNode(QQuickItem *item); ~OpenGLRenderNode(); void render(const RenderState *state) override; @@ -77,10 +76,13 @@ public: QRectF rect() const override; //! [1] + void sync(QQuickItem *item); + private: void init(); - QQuickItem *m_item; + int m_width = 0; + int m_height = 0; QOpenGLShaderProgram *m_program = nullptr; int m_matrixUniform; int m_opacityUniform; diff --git a/examples/quick/scenegraph/rendernode/softwarerenderer.cpp b/examples/quick/scenegraph/rendernode/softwarerenderer.cpp index 0a0ec4b485..bba364ac97 100644 --- a/examples/quick/scenegraph/rendernode/softwarerenderer.cpp +++ b/examples/quick/scenegraph/rendernode/softwarerenderer.cpp @@ -54,11 +54,6 @@ #include <QSGRendererInterface> #include <QPainter> -SoftwareRenderNode::SoftwareRenderNode(QQuickItem *item) - : m_item(item) -{ -} - SoftwareRenderNode::~SoftwareRenderNode() { releaseResources(); @@ -70,8 +65,10 @@ void SoftwareRenderNode::releaseResources() void SoftwareRenderNode::render(const RenderState *renderState) { - QSGRendererInterface *rif = m_item->window()->rendererInterface(); - QPainter *p = static_cast<QPainter *>(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource)); + Q_ASSERT(m_window); + + QSGRendererInterface *rif = m_window->rendererInterface(); + QPainter *p = static_cast<QPainter *>(rif->getResource(m_window, QSGRendererInterface::PainterResource)); Q_ASSERT(p); const QRegion *clipRegion = renderState->clipRegion(); @@ -81,15 +78,15 @@ void SoftwareRenderNode::render(const RenderState *renderState) p->setTransform(matrix()->toTransform()); p->setOpacity(inheritedOpacity()); - const QPointF p0(m_item->width() - 1, m_item->height() - 1); + const QPointF p0(m_width - 1, m_height - 1); const QPointF p1(0, 0); - const QPointF p2(0, m_item->height() - 1); + const QPointF p2(0, m_height - 1); QPainterPath path(p0); path.lineTo(p1); path.lineTo(p2); path.closeSubpath(); - QLinearGradient gradient(QPointF(0, 0), QPointF(m_item->width(), m_item->height())); + QLinearGradient gradient(QPointF(0, 0), QPointF(m_width, m_height)); gradient.setColorAt(0, Qt::green); gradient.setColorAt(1, Qt::red); @@ -108,5 +105,12 @@ QSGRenderNode::RenderingFlags SoftwareRenderNode::flags() const QRectF SoftwareRenderNode::rect() const { - return QRect(0, 0, m_item->width(), m_item->height()); + return QRect(0, 0, m_width, m_height); +} + +void SoftwareRenderNode::sync(QQuickItem *item) +{ + m_window = item->window(); + m_width = item->width(); + m_height = item->height(); } diff --git a/examples/quick/scenegraph/rendernode/softwarerenderer.h b/examples/quick/scenegraph/rendernode/softwarerenderer.h index cc2aaf7ed3..6091a13ca3 100644 --- a/examples/quick/scenegraph/rendernode/softwarerenderer.h +++ b/examples/quick/scenegraph/rendernode/softwarerenderer.h @@ -57,7 +57,6 @@ class SoftwareRenderNode : public QSGRenderNode { public: - SoftwareRenderNode(QQuickItem *item); ~SoftwareRenderNode(); void render(const RenderState *state) override; @@ -66,8 +65,12 @@ public: RenderingFlags flags() const override; QRectF rect() const override; + void sync(QQuickItem *item); + private: - QQuickItem *m_item; + QQuickWindow *m_window = nullptr; + int m_width = 0; + int m_height = 0; }; #endif diff --git a/examples/quick/scenegraph/scenegraph.pro b/examples/quick/scenegraph/scenegraph.pro index ac368c79a5..5fea3b974a 100644 --- a/examples/quick/scenegraph/scenegraph.pro +++ b/examples/quick/scenegraph/scenegraph.pro @@ -5,7 +5,7 @@ qtConfig(opengl(es1|es2)?) { graph \ simplematerial \ sgengine \ - textureinsgnode \ + fboitem \ openglunderqml \ textureinthread \ twotextureproviders @@ -17,12 +17,18 @@ SUBDIRS += \ threadedanimation macos { - SUBDIRS += metalunderqml + SUBDIRS += \ + metalunderqml \ + metaltextureimport } win32 { SUBDIRS += d3d11underqml } +qtConfig(vulkan) { + SUBDIRS += vulkanunderqml +} + EXAMPLE_FILES += \ shared diff --git a/examples/quick/scenegraph/simplematerial/simplematerial.cpp b/examples/quick/scenegraph/simplematerial/simplematerial.cpp index 6773b6fb5a..55e1d879ca 100644 --- a/examples/quick/scenegraph/simplematerial/simplematerial.cpp +++ b/examples/quick/scenegraph/simplematerial/simplematerial.cpp @@ -157,6 +157,7 @@ class Item : public QQuickItem Q_OBJECT Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + QML_NAMED_ELEMENT(SimpleMaterialItem) public: @@ -203,7 +204,7 @@ int main(int argc, char **argv) { QGuiApplication app(argc, argv); - qmlRegisterType<Item>("SimpleMaterial", 1, 0, "SimpleMaterialItem"); + qmlRegisterTypesAndRevisions<Item>("SimpleMaterial", 1); QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView); diff --git a/examples/quick/scenegraph/textureinthread/main.cpp b/examples/quick/scenegraph/textureinthread/main.cpp index 7a46f25390..7c22fa7fdf 100644 --- a/examples/quick/scenegraph/textureinthread/main.cpp +++ b/examples/quick/scenegraph/textureinthread/main.cpp @@ -70,7 +70,7 @@ int main(int argc, char **argv) return app.exec(); } - qmlRegisterType<ThreadRenderer>("SceneGraphRendering", 1, 0, "Renderer"); + qmlRegisterTypesAndRevisions<ThreadRenderer>("SceneGraphRendering", 1); int execReturn = 0; { diff --git a/examples/quick/scenegraph/textureinthread/threadrenderer.h b/examples/quick/scenegraph/textureinthread/threadrenderer.h index 8442041bf8..96c00e7b2e 100644 --- a/examples/quick/scenegraph/textureinthread/threadrenderer.h +++ b/examples/quick/scenegraph/textureinthread/threadrenderer.h @@ -58,6 +58,7 @@ class RenderThread; class ThreadRenderer : public QQuickItem { Q_OBJECT + QML_NAMED_ELEMENT(Renderer) public: ThreadRenderer(); diff --git a/examples/quick/scenegraph/threadedanimation/main.cpp b/examples/quick/scenegraph/threadedanimation/main.cpp index b1b0c05085..c77a291f5d 100644 --- a/examples/quick/scenegraph/threadedanimation/main.cpp +++ b/examples/quick/scenegraph/threadedanimation/main.cpp @@ -57,7 +57,7 @@ int main(int argc, char **argv) { QGuiApplication app(argc, argv); - qmlRegisterType<Spinner>("Spinner", 1, 0, "Spinner"); + qmlRegisterTypesAndRevisions<Spinner>("Spinner", 1); QQuickView view; view.setSource(QUrl("qrc:///scenegraph/threadedanimation/main.qml")); diff --git a/examples/quick/scenegraph/threadedanimation/spinner.h b/examples/quick/scenegraph/threadedanimation/spinner.h index c3f3394c93..fd3eaa7751 100644 --- a/examples/quick/scenegraph/threadedanimation/spinner.h +++ b/examples/quick/scenegraph/threadedanimation/spinner.h @@ -58,6 +58,7 @@ class Spinner : public QQuickItem Q_OBJECT Q_PROPERTY(bool spinning READ spinning WRITE setSpinning NOTIFY spinningChanged) + QML_ELEMENT public: Spinner(); diff --git a/examples/quick/scenegraph/twotextureproviders/main.cpp b/examples/quick/scenegraph/twotextureproviders/main.cpp index a5d23b6adc..3f53bb6e32 100644 --- a/examples/quick/scenegraph/twotextureproviders/main.cpp +++ b/examples/quick/scenegraph/twotextureproviders/main.cpp @@ -58,7 +58,7 @@ int main(int argc, char **argv) { QGuiApplication app(argc, argv); - qmlRegisterType<XorBlender>("SceneGraphRendering", 1, 0, "XorBlender"); + qmlRegisterTypesAndRevisions<XorBlender>("SceneGraphRendering", 1); QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView); diff --git a/examples/quick/scenegraph/twotextureproviders/xorblender.h b/examples/quick/scenegraph/twotextureproviders/xorblender.h index 94132f09fb..17557b8efd 100644 --- a/examples/quick/scenegraph/twotextureproviders/xorblender.h +++ b/examples/quick/scenegraph/twotextureproviders/xorblender.h @@ -58,6 +58,7 @@ class XorBlender : public QQuickItem Q_OBJECT Q_PROPERTY(QQuickItem *source1 READ source1 WRITE setSource1 NOTIFY source1Changed) Q_PROPERTY(QQuickItem *source2 READ source2 WRITE setSource2 NOTIFY source2Changed) + QML_ELEMENT public: explicit XorBlender(QQuickItem *parent = 0); diff --git a/examples/quick/scenegraph/vulkanunderqml/doc/images/vulkanunderqml-example.jpg b/examples/quick/scenegraph/vulkanunderqml/doc/images/vulkanunderqml-example.jpg Binary files differnew file mode 100644 index 0000000000..c3f51b6194 --- /dev/null +++ b/examples/quick/scenegraph/vulkanunderqml/doc/images/vulkanunderqml-example.jpg diff --git a/examples/quick/scenegraph/vulkanunderqml/doc/src/vulkanunderqml.qdoc b/examples/quick/scenegraph/vulkanunderqml/doc/src/vulkanunderqml.qdoc new file mode 100644 index 0000000000..d11982c074 --- /dev/null +++ b/examples/quick/scenegraph/vulkanunderqml/doc/src/vulkanunderqml.qdoc @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example scenegraph/vulkanunderqml + \title Scene Graph - Vulkan Under QML + \ingroup qtquickexamples + \brief Shows how to render directly with vulkan under a Qt Quick scene. + + \image vulkanunderqml-example.jpg + + The Vulkan Under QML example shows how an application can make use of the + \l QQuickWindow::beforeRendering() and \l + QQuickWindow::beforeRenderPassRecording() signals to draw custom Vulkan + content under a Qt Quick scene. This signal is emitted at the start of + every frame, before the scene graph starts its rendering, thus any Vulkan + draw calls that are made as a response to this signal, will stack under the + Qt Quick items. There are two signals, because the custom Vulkan commands + are recorded onto the same command buffer that is used by the scene graph. + beforeRendering() on its own is not sufficient for this because it gets + emitted at the start of the frame, before recording the start of a + renderpass instance via + \l{https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCmdBeginRenderPass.html}{vkCmdBeginRenderPass}. + By also connecting to beforeRenderPassRecording(), the application's own + commands and the scene graph's scaffolding will end up in the right order. + + As an alternative, applications that wish to render Vulkan content + on top of the Qt Quick scene, can do so by connecting to the \l + QQuickWindow::afterRendering() and \l + QQuickWindow::afterRenderPassRecording() signals. + + In this example, we will also see how it is possible to have + values that are exposed to QML which affect the Vulkan + rendering. We animate the threshold value using a NumberAnimation + in the QML file and this value is used by the SPIR-V shader + program that draws the squircles. + + The example is equivalent in most ways to the \l{Scene Graph - OpenGL Under + QML}{OpenGL Under QML}, \l{Scene Graph - Direct3D 11 Under QML}{Direct3D 11 + Under QML}, and \l{Scene Graph - Metal Under QML}{Metal Under QML} + examples, they all render the same custom content, just via different + native APIs. + + */ diff --git a/examples/quick/scenegraph/vulkanunderqml/main.cpp b/examples/quick/scenegraph/vulkanunderqml/main.cpp new file mode 100644 index 0000000000..a04497b1d6 --- /dev/null +++ b/examples/quick/scenegraph/vulkanunderqml/main.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QGuiApplication> +#include <QtQuick/QQuickView> +#include "vulkansquircle.h" + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + + qmlRegisterType<VulkanSquircle>("VulkanUnderQML", 1, 0, "VulkanSquircle"); + + // This example needs Vulkan. It will not run otherwise. + QQuickWindow::setSceneGraphBackend(QSGRendererInterface::VulkanRhi); + + QQuickView view; + view.setResizeMode(QQuickView::SizeRootObjectToView); + view.setSource(QUrl("qrc:///scenegraph/vulkanunderqml/main.qml")); + view.show(); + + return app.exec(); +} diff --git a/examples/quick/scenegraph/vulkanunderqml/main.qml b/examples/quick/scenegraph/vulkanunderqml/main.qml new file mode 100644 index 0000000000..c364ca7636 --- /dev/null +++ b/examples/quick/scenegraph/vulkanunderqml/main.qml @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [1] +import QtQuick 2.0 +import VulkanUnderQML 1.0 + +Item { + + width: 320 + height: 480 + + VulkanSquircle { + SequentialAnimation on t { + NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad } + NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad } + loops: Animation.Infinite + running: true + } + } +//! [1] //! [2] + Rectangle { + color: Qt.rgba(1, 1, 1, 0.7) + radius: 10 + border.width: 1 + border.color: "white" + anchors.fill: label + anchors.margins: -10 + } + + Text { + id: label + color: "black" + wrapMode: Text.WordWrap + text: "The background here is a squircle rendered with raw Vulkan using the beforeRendering() and beforeRenderPassRecording() signals in QQuickWindow. This text label and its border is rendered using QML" + anchors.right: parent.right + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.margins: 20 + } +} +//! [2] diff --git a/examples/quick/scenegraph/vulkanunderqml/squircle.frag.spv b/examples/quick/scenegraph/vulkanunderqml/squircle.frag.spv Binary files differnew file mode 100644 index 0000000000..e4d13a871d --- /dev/null +++ b/examples/quick/scenegraph/vulkanunderqml/squircle.frag.spv diff --git a/examples/quick/scenegraph/vulkanunderqml/squircle.vert.spv b/examples/quick/scenegraph/vulkanunderqml/squircle.vert.spv Binary files differnew file mode 100644 index 0000000000..5df94a47e4 --- /dev/null +++ b/examples/quick/scenegraph/vulkanunderqml/squircle.vert.spv diff --git a/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp b/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp new file mode 100644 index 0000000000..21f46a25c1 --- /dev/null +++ b/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp @@ -0,0 +1,623 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "vulkansquircle.h" +#include <QtCore/QRunnable> +#include <QtQuick/QQuickWindow> + +#include <QVulkanInstance> +#include <QVulkanFunctions> + +class SquircleRenderer : public QObject +{ + Q_OBJECT +public: + ~SquircleRenderer(); + + void setT(qreal t) { m_t = t; } + void setViewportSize(const QSize &size) { m_viewportSize = size; } + void setWindow(QQuickWindow *window) { m_window = window; } + +public slots: + void frameStart(); + void mainPassRecordingStart(); + +private: + enum Stage { + VertexStage, + FragmentStage + }; + void prepareShader(Stage stage); + void init(int framesInFlight); + + QSize m_viewportSize; + qreal m_t = 0; + QQuickWindow *m_window; + + QByteArray m_vert; + QByteArray m_frag; + + bool m_initialized = false; + VkPhysicalDevice m_physDev = VK_NULL_HANDLE; + VkDevice m_dev = VK_NULL_HANDLE; + QVulkanDeviceFunctions *m_devFuncs = nullptr; + QVulkanFunctions *m_funcs = nullptr; + + VkBuffer m_vbuf = VK_NULL_HANDLE; + VkDeviceMemory m_vbufMem = VK_NULL_HANDLE; + VkBuffer m_ubuf = VK_NULL_HANDLE; + VkDeviceMemory m_ubufMem = VK_NULL_HANDLE; + VkDeviceSize m_allocPerUbuf = 0; + + VkPipelineCache m_pipelineCache = VK_NULL_HANDLE; + + VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE; + VkDescriptorSetLayout m_resLayout = VK_NULL_HANDLE; + VkPipeline m_pipeline = VK_NULL_HANDLE; + + VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE; + VkDescriptorSet m_ubufDescriptor = VK_NULL_HANDLE; +}; + +VulkanSquircle::VulkanSquircle() +{ + connect(this, &QQuickItem::windowChanged, this, &VulkanSquircle::handleWindowChanged); +} + +void VulkanSquircle::setT(qreal t) +{ + if (t == m_t) + return; + m_t = t; + emit tChanged(); + if (window()) + window()->update(); +} + +void VulkanSquircle::handleWindowChanged(QQuickWindow *win) +{ + if (win) { + connect(win, &QQuickWindow::beforeSynchronizing, this, &VulkanSquircle::sync, Qt::DirectConnection); + connect(win, &QQuickWindow::sceneGraphInvalidated, this, &VulkanSquircle::cleanup, Qt::DirectConnection); + + // Ensure we start with cleared to black. The squircle's blend mode relies on this. + win->setColor(Qt::black); + } +} + +// The safe way to release custom graphics resources is to both connect to +// sceneGraphInvalidated() and implement releaseResources(). To support +// threaded render loops the latter performs the SquircleRenderer destruction +// via scheduleRenderJob(). Note that the VulkanSquircle may be gone by the time +// the QRunnable is invoked. + +void VulkanSquircle::cleanup() +{ + delete m_renderer; + m_renderer = nullptr; +} + +class CleanupJob : public QRunnable +{ +public: + CleanupJob(SquircleRenderer *renderer) : m_renderer(renderer) { } + void run() override { delete m_renderer; } +private: + SquircleRenderer *m_renderer; +}; + +void VulkanSquircle::releaseResources() +{ + window()->scheduleRenderJob(new CleanupJob(m_renderer), QQuickWindow::BeforeSynchronizingStage); + m_renderer = nullptr; +} + +SquircleRenderer::~SquircleRenderer() +{ + qDebug("cleanup"); + if (!m_devFuncs) + return; + + m_devFuncs->vkDestroyPipeline(m_dev, m_pipeline, nullptr); + m_devFuncs->vkDestroyPipelineLayout(m_dev, m_pipelineLayout, nullptr); + m_devFuncs->vkDestroyDescriptorSetLayout(m_dev, m_resLayout, nullptr); + + m_devFuncs->vkDestroyDescriptorPool(m_dev, m_descriptorPool, nullptr); + + m_devFuncs->vkDestroyPipelineCache(m_dev, m_pipelineCache, nullptr); + + m_devFuncs->vkDestroyBuffer(m_dev, m_vbuf, nullptr); + m_devFuncs->vkFreeMemory(m_dev, m_vbufMem, nullptr); + + m_devFuncs->vkDestroyBuffer(m_dev, m_ubuf, nullptr); + m_devFuncs->vkFreeMemory(m_dev, m_ubufMem, nullptr); + + qDebug("released"); +} + +void VulkanSquircle::sync() +{ + if (!m_renderer) { + m_renderer = new SquircleRenderer; + // Initializing resources is done before starting to record the + // renderpass, regardless of wanting an underlay or overlay. + connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::frameStart, Qt::DirectConnection); + // Here we want an underlay and therefore connect to + // beforeRenderPassRecording. Changing to afterRenderPassRecording + // would render the squircle on top (overlay). + connect(window(), &QQuickWindow::beforeRenderPassRecording, m_renderer, &SquircleRenderer::mainPassRecordingStart, Qt::DirectConnection); + } + m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio()); + m_renderer->setT(m_t); + m_renderer->setWindow(window()); +} + +void SquircleRenderer::frameStart() +{ + QSGRendererInterface *rif = m_window->rendererInterface(); + + // We are not prepared for anything other than running with the RHI and its Vulkan backend. + Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::VulkanRhi); + + if (m_vert.isEmpty()) + prepareShader(VertexStage); + if (m_frag.isEmpty()) + prepareShader(FragmentStage); + + if (!m_initialized) + init(m_window->graphicsStateInfo().framesInFlight); +} + +static const float vertices[] = { + -1, -1, + 1, -1, + -1, 1, + 1, 1 +}; + +const int UBUF_SIZE = 4; + +void SquircleRenderer::mainPassRecordingStart() +{ + // This example demonstrates the simple case: prepending some commands to + // the scenegraph's main renderpass. It does not create its own passes, + // rendertargets, etc. so no synchronization is needed. + + const QQuickWindow::GraphicsStateInfo &stateInfo(m_window->graphicsStateInfo()); + QSGRendererInterface *rif = m_window->rendererInterface(); + + VkDeviceSize ubufOffset = stateInfo.currentFrameSlot * m_allocPerUbuf; + void *p = nullptr; + VkResult err = m_devFuncs->vkMapMemory(m_dev, m_ubufMem, ubufOffset, m_allocPerUbuf, 0, &p); + if (err != VK_SUCCESS || !p) + qFatal("Failed to map uniform buffer memory: %d", err); + float t = m_t; + memcpy(p, &t, 4); + m_devFuncs->vkUnmapMemory(m_dev, m_ubufMem); + + m_window->beginExternalCommands(); + + // Must query the command buffer _after_ beginExternalCommands(), this is + // actually important when running on Vulkan because what we get here is a + // new secondary command buffer, not the primary one. + VkCommandBuffer cb = *reinterpret_cast<VkCommandBuffer *>( + rif->getResource(m_window, QSGRendererInterface::CommandListResource)); + Q_ASSERT(cb); + + // Do not assume any state persists on the command buffer. (it may be a + // brand new one that just started recording) + + m_devFuncs->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline); + + VkDeviceSize vbufOffset = 0; + m_devFuncs->vkCmdBindVertexBuffers(cb, 0, 1, &m_vbuf, &vbufOffset); + + uint32_t dynamicOffset = m_allocPerUbuf * stateInfo.currentFrameSlot; + m_devFuncs->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, + &m_ubufDescriptor, 1, &dynamicOffset); + + VkViewport vp = { 0, 0, float(m_viewportSize.width()), float(m_viewportSize.height()), 0.0f, 1.0f }; + m_devFuncs->vkCmdSetViewport(cb, 0, 1, &vp); + VkRect2D scissor = { { 0, 0 }, { uint32_t(m_viewportSize.width()), uint32_t(m_viewportSize.height()) } }; + m_devFuncs->vkCmdSetScissor(cb, 0, 1, &scissor); + + m_devFuncs->vkCmdDraw(cb, 4, 1, 0, 0); + + m_window->endExternalCommands(); +} + +void SquircleRenderer::prepareShader(Stage stage) +{ + QString filename; + if (stage == VertexStage) { + filename = QLatin1String(":/scenegraph/vulkanunderqml/squircle.vert.spv"); + } else { + Q_ASSERT(stage == FragmentStage); + filename = QLatin1String(":/scenegraph/vulkanunderqml/squircle.frag.spv"); + } + QFile f(filename); + if (!f.open(QIODevice::ReadOnly)) + qFatal("Failed to read shader %s", qPrintable(filename)); + + const QByteArray contents = f.readAll(); + + if (stage == VertexStage) { + m_vert = contents; + Q_ASSERT(!m_vert.isEmpty()); + } else { + m_frag = contents; + Q_ASSERT(!m_frag.isEmpty()); + } +} + +static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign) +{ + return (v + byteAlign - 1) & ~(byteAlign - 1); +} + +void SquircleRenderer::init(int framesInFlight) +{ + qDebug("init"); + + Q_ASSERT(framesInFlight <= 3); + m_initialized = true; + + QSGRendererInterface *rif = m_window->rendererInterface(); + QVulkanInstance *inst = reinterpret_cast<QVulkanInstance *>( + rif->getResource(m_window, QSGRendererInterface::VulkanInstanceResource)); + Q_ASSERT(inst && inst->isValid()); + + m_physDev = *reinterpret_cast<VkPhysicalDevice *>(rif->getResource(m_window, QSGRendererInterface::PhysicalDeviceResource)); + m_dev = *reinterpret_cast<VkDevice *>(rif->getResource(m_window, QSGRendererInterface::DeviceResource)); + Q_ASSERT(m_physDev && m_dev); + + m_devFuncs = inst->deviceFunctions(m_dev); + m_funcs = inst->functions(); + Q_ASSERT(m_devFuncs && m_funcs); + + VkRenderPass rp = *reinterpret_cast<VkRenderPass *>( + rif->getResource(m_window, QSGRendererInterface::RenderPassResource)); + Q_ASSERT(rp); + + // For simplicity we just use host visible buffers instead of device local + staging. + + VkPhysicalDeviceProperties physDevProps; + m_funcs->vkGetPhysicalDeviceProperties(m_physDev, &physDevProps); + + VkPhysicalDeviceMemoryProperties physDevMemProps; + m_funcs->vkGetPhysicalDeviceMemoryProperties(m_physDev, &physDevMemProps); + + VkBufferCreateInfo bufferInfo; + memset(&bufferInfo, 0, sizeof(bufferInfo)); + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = sizeof(vertices); + bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + VkResult err = m_devFuncs->vkCreateBuffer(m_dev, &bufferInfo, nullptr, &m_vbuf); + if (err != VK_SUCCESS) + qFatal("Failed to create vertex buffer: %d", err); + + VkMemoryRequirements memReq; + m_devFuncs->vkGetBufferMemoryRequirements(m_dev, m_vbuf, &memReq); + VkMemoryAllocateInfo allocInfo; + memset(&allocInfo, 0, sizeof(allocInfo)); + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memReq.size; + + uint32_t memTypeIndex = uint32_t(-1); + const VkMemoryType *memType = physDevMemProps.memoryTypes; + for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) { + if (memReq.memoryTypeBits & (1 << i)) { + if ((memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) + && (memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) + { + memTypeIndex = i; + break; + } + } + } + if (memTypeIndex == uint32_t(-1)) + qFatal("Failed to find host visible and coherent memory type"); + + allocInfo.memoryTypeIndex = memTypeIndex; + err = m_devFuncs->vkAllocateMemory(m_dev, &allocInfo, nullptr, &m_vbufMem); + if (err != VK_SUCCESS) + qFatal("Failed to allocate vertex buffer memory of size %u: %d", uint(allocInfo.allocationSize), err); + + void *p = nullptr; + err = m_devFuncs->vkMapMemory(m_dev, m_vbufMem, 0, allocInfo.allocationSize, 0, &p); + if (err != VK_SUCCESS || !p) + qFatal("Failed to map vertex buffer memory: %d", err); + memcpy(p, vertices, sizeof(vertices)); + m_devFuncs->vkUnmapMemory(m_dev, m_vbufMem); + err = m_devFuncs->vkBindBufferMemory(m_dev, m_vbuf, m_vbufMem, 0); + if (err != VK_SUCCESS) + qFatal("Failed to bind vertex buffer memory: %d", err); + + // Now have a uniform buffer with enough space for the buffer data for each + // (potentially) in-flight frame. (as we will write the contents every + // frame, and so would need to wait for command buffer completion if there + // was only one, and that would not be nice) + + // Could have three buffers and three descriptor sets, or one buffer and + // one descriptor set and dynamic offset. We chose the latter in this + // example. + + // We use one memory allocation for all uniform buffers, but then have to + // watch out for the buffer offset aligment requirement, which may be as + // large as 256 bytes. + + m_allocPerUbuf = aligned(UBUF_SIZE, physDevProps.limits.minUniformBufferOffsetAlignment); + + bufferInfo.size = framesInFlight * m_allocPerUbuf; + bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + err = m_devFuncs->vkCreateBuffer(m_dev, &bufferInfo, nullptr, &m_ubuf); + if (err != VK_SUCCESS) + qFatal("Failed to create uniform buffer: %d", err); + m_devFuncs->vkGetBufferMemoryRequirements(m_dev, m_ubuf, &memReq); + memTypeIndex = -1; + for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) { + if (memReq.memoryTypeBits & (1 << i)) { + if ((memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) + && (memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) + { + memTypeIndex = i; + break; + } + } + } + if (memTypeIndex == uint32_t(-1)) + qFatal("Failed to find host visible and coherent memory type"); + + allocInfo.allocationSize = framesInFlight * m_allocPerUbuf; + allocInfo.memoryTypeIndex = memTypeIndex; + err = m_devFuncs->vkAllocateMemory(m_dev, &allocInfo, nullptr, &m_ubufMem); + if (err != VK_SUCCESS) + qFatal("Failed to allocate uniform buffer memory of size %u: %d", uint(allocInfo.allocationSize), err); + + err = m_devFuncs->vkBindBufferMemory(m_dev, m_ubuf, m_ubufMem, 0); + if (err != VK_SUCCESS) + qFatal("Failed to bind uniform buffer memory: %d", err); + + // Now onto the pipeline. + + VkPipelineCacheCreateInfo pipelineCacheInfo; + memset(&pipelineCacheInfo, 0, sizeof(pipelineCacheInfo)); + pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + err = m_devFuncs->vkCreatePipelineCache(m_dev, &pipelineCacheInfo, nullptr, &m_pipelineCache); + if (err != VK_SUCCESS) + qFatal("Failed to create pipeline cache: %d", err); + + VkDescriptorSetLayoutBinding descLayoutBinding; + memset(&descLayoutBinding, 0, sizeof(descLayoutBinding)); + descLayoutBinding.binding = 0; + descLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + descLayoutBinding.descriptorCount = 1; + descLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + VkDescriptorSetLayoutCreateInfo layoutInfo; + memset(&layoutInfo, 0, sizeof(layoutInfo)); + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = 1; + layoutInfo.pBindings = &descLayoutBinding; + err = m_devFuncs->vkCreateDescriptorSetLayout(m_dev, &layoutInfo, nullptr, &m_resLayout); + if (err != VK_SUCCESS) + qFatal("Failed to create descriptor set layout: %d", err); + + VkPipelineLayoutCreateInfo pipelineLayoutInfo; + memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo)); + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &m_resLayout; + err = m_devFuncs->vkCreatePipelineLayout(m_dev, &pipelineLayoutInfo, nullptr, &m_pipelineLayout); + if (err != VK_SUCCESS) + qWarning("Failed to create pipeline layout: %d", err); + + VkGraphicsPipelineCreateInfo pipelineInfo; + memset(&pipelineInfo, 0, sizeof(pipelineInfo)); + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + + VkShaderModuleCreateInfo shaderInfo; + memset(&shaderInfo, 0, sizeof(shaderInfo)); + shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + shaderInfo.codeSize = m_vert.size(); + shaderInfo.pCode = reinterpret_cast<const quint32 *>(m_vert.constData()); + VkShaderModule vertShaderModule; + err = m_devFuncs->vkCreateShaderModule(m_dev, &shaderInfo, nullptr, &vertShaderModule); + if (err != VK_SUCCESS) + qFatal("Failed to create vertex shader module: %d", err); + + shaderInfo.codeSize = m_frag.size(); + shaderInfo.pCode = reinterpret_cast<const quint32 *>(m_frag.constData()); + VkShaderModule fragShaderModule; + err = m_devFuncs->vkCreateShaderModule(m_dev, &shaderInfo, nullptr, &fragShaderModule); + if (err != VK_SUCCESS) + qFatal("Failed to create fragment shader module: %d", err); + + VkPipelineShaderStageCreateInfo stageInfo[2]; + memset(&stageInfo, 0, sizeof(stageInfo)); + stageInfo[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stageInfo[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + stageInfo[0].module = vertShaderModule; + stageInfo[0].pName = "main"; + stageInfo[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stageInfo[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + stageInfo[1].module = fragShaderModule; + stageInfo[1].pName = "main"; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = stageInfo; + + VkVertexInputBindingDescription vertexBinding = { + 0, // binding + 2 * sizeof(float), // stride + VK_VERTEX_INPUT_RATE_VERTEX + }; + VkVertexInputAttributeDescription vertexAttr = { + 0, // location + 0, // binding + VK_FORMAT_R32G32_SFLOAT, // 'vertices' only has 2 floats per vertex + 0 // offset + }; + VkPipelineVertexInputStateCreateInfo vertexInputInfo; + memset(&vertexInputInfo, 0, sizeof(vertexInputInfo)); + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &vertexBinding; + vertexInputInfo.vertexAttributeDescriptionCount = 1; + vertexInputInfo.pVertexAttributeDescriptions = &vertexAttr; + pipelineInfo.pVertexInputState = &vertexInputInfo; + + VkDynamicState dynStates[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamicInfo; + memset(&dynamicInfo, 0, sizeof(dynamicInfo)); + dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicInfo.dynamicStateCount = 2; + dynamicInfo.pDynamicStates = dynStates; + pipelineInfo.pDynamicState = &dynamicInfo; + + VkPipelineViewportStateCreateInfo viewportInfo; + memset(&viewportInfo, 0, sizeof(viewportInfo)); + viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportInfo.viewportCount = viewportInfo.scissorCount = 1; + pipelineInfo.pViewportState = &viewportInfo; + + VkPipelineInputAssemblyStateCreateInfo iaInfo; + memset(&iaInfo, 0, sizeof(iaInfo)); + iaInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + iaInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + pipelineInfo.pInputAssemblyState = &iaInfo; + + VkPipelineRasterizationStateCreateInfo rsInfo; + memset(&rsInfo, 0, sizeof(rsInfo)); + rsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rsInfo.lineWidth = 1.0f; + pipelineInfo.pRasterizationState = &rsInfo; + + VkPipelineMultisampleStateCreateInfo msInfo; + memset(&msInfo, 0, sizeof(msInfo)); + msInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + msInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + pipelineInfo.pMultisampleState = &msInfo; + + VkPipelineDepthStencilStateCreateInfo dsInfo; + memset(&dsInfo, 0, sizeof(dsInfo)); + dsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + pipelineInfo.pDepthStencilState = &dsInfo; + + // SrcAlpha, One + VkPipelineColorBlendStateCreateInfo blendInfo; + memset(&blendInfo, 0, sizeof(blendInfo)); + blendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + VkPipelineColorBlendAttachmentState blend; + memset(&blend, 0, sizeof(blend)); + blend.blendEnable = true; + blend.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + blend.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; + blend.colorBlendOp = VK_BLEND_OP_ADD; + blend.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + blend.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + blend.alphaBlendOp = VK_BLEND_OP_ADD; + blend.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT + | VK_COLOR_COMPONENT_A_BIT; + blendInfo.attachmentCount = 1; + blendInfo.pAttachments = &blend; + pipelineInfo.pColorBlendState = &blendInfo; + + pipelineInfo.layout = m_pipelineLayout; + + pipelineInfo.renderPass = rp; + + err = m_devFuncs->vkCreateGraphicsPipelines(m_dev, m_pipelineCache, 1, &pipelineInfo, nullptr, &m_pipeline); + + m_devFuncs->vkDestroyShaderModule(m_dev, vertShaderModule, nullptr); + m_devFuncs->vkDestroyShaderModule(m_dev, fragShaderModule, nullptr); + + if (err != VK_SUCCESS) + qFatal("Failed to create graphics pipeline: %d", err); + + // Now just need some descriptors. + VkDescriptorPoolSize descPoolSizes[] = { + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1 } + }; + VkDescriptorPoolCreateInfo descPoolInfo; + memset(&descPoolInfo, 0, sizeof(descPoolInfo)); + descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + descPoolInfo.flags = 0; // won't use vkFreeDescriptorSets + descPoolInfo.maxSets = 1; + descPoolInfo.poolSizeCount = sizeof(descPoolSizes) / sizeof(descPoolSizes[0]); + descPoolInfo.pPoolSizes = descPoolSizes; + err = m_devFuncs->vkCreateDescriptorPool(m_dev, &descPoolInfo, nullptr, &m_descriptorPool); + if (err != VK_SUCCESS) + qFatal("Failed to create descriptor pool: %d", err); + + VkDescriptorSetAllocateInfo descAllocInfo; + memset(&descAllocInfo, 0, sizeof(descAllocInfo)); + descAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + descAllocInfo.descriptorPool = m_descriptorPool; + descAllocInfo.descriptorSetCount = 1; + descAllocInfo.pSetLayouts = &m_resLayout; + err = m_devFuncs->vkAllocateDescriptorSets(m_dev, &descAllocInfo, &m_ubufDescriptor); + if (err != VK_SUCCESS) + qFatal("Failed to allocate descriptor set"); + + VkWriteDescriptorSet writeInfo; + memset(&writeInfo, 0, sizeof(writeInfo)); + writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeInfo.dstSet = m_ubufDescriptor; + writeInfo.dstBinding = 0; + writeInfo.descriptorCount = 1; + writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + VkDescriptorBufferInfo bufInfo; + bufInfo.buffer = m_ubuf; + bufInfo.offset = 0; // dynamic offset is used so this is ignored + bufInfo.range = UBUF_SIZE; + writeInfo.pBufferInfo = &bufInfo; + m_devFuncs->vkUpdateDescriptorSets(m_dev, 1, &writeInfo, 0, nullptr); +} + +#include "vulkansquircle.moc" diff --git a/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.h b/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.h new file mode 100644 index 0000000000..7e65d01a15 --- /dev/null +++ b/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VULKANSQUIRCLE_H +#define VULKANSQUIRCLE_H + +#include <QtQuick/QQuickItem> + +class SquircleRenderer; + +class VulkanSquircle : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged) + +public: + VulkanSquircle(); + + qreal t() const { return m_t; } + void setT(qreal t); + +signals: + void tChanged(); + +public slots: + void sync(); + void cleanup(); + +private slots: + void handleWindowChanged(QQuickWindow *win); + +private: + void releaseResources() override; + + qreal m_t = 0; + SquircleRenderer *m_renderer = nullptr; +}; + +#endif diff --git a/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.pro b/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.pro new file mode 100644 index 0000000000..9ea57b91c3 --- /dev/null +++ b/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.pro @@ -0,0 +1,10 @@ +!qtConfig(vulkan): error("This example requires Qt built with Vulkan support") + +QT += qml quick + +HEADERS += vulkansquircle.h +SOURCES += vulkansquircle.cpp main.cpp +RESOURCES += vulkanunderqml.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/vulkanunderqml +INSTALLS += target diff --git a/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.qrc b/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.qrc new file mode 100644 index 0000000000..c85be0f238 --- /dev/null +++ b/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/scenegraph/vulkanunderqml"> + <file>main.qml</file> + <file>squircle.vert.spv</file> + <file>squircle.frag.spv</file> + </qresource> +</RCC> diff --git a/examples/quick/shapes/content/interactive.qml b/examples/quick/shapes/content/interactive.qml index 55a1d16299..78413db3f9 100644 --- a/examples/quick/shapes/content/interactive.qml +++ b/examples/quick/shapes/content/interactive.qml @@ -170,14 +170,45 @@ Rectangle { property variant resizers: [] property variant funcs + property Component mouseArea: Component { + Rectangle { + id: rr + + property variant obj + property string xprop + property string yprop + + width: 20 + height: 20 + + MouseArea { + property bool a: false + + anchors.fill: parent + hoverEnabled: true + onEntered: color = "yellow" + onExited: color = rr.color + onPressed: a = true + onReleased: a = false + onPositionChanged: { + if (a) { + var pt = mapToItem(rr.parent, mouse.x, mouse.y); + rr.obj[rr.xprop] = pt.x + rr.obj[rr.yprop] = pt.y + rr.x = pt.x - 10 + rr.y = pt.y - 10 + } + } + } + } + } + function genResizer(obj, x, y, xprop, yprop, color) { - var ma = Qt.createQmlObject('import QtQuick 2.9; import QtQuick.Shapes 1.0; Rectangle { id: rr; property variant obj; color: "' + color + '"; width: 20; height: 20;'+ - 'MouseArea { anchors.fill: parent; hoverEnabled: true;' + - 'onEntered: color = "yellow"; onExited: color = "' + color + '";' + - 'property bool a: false; onPressed: a = true; onReleased: a = false; ' + - 'onPositionChanged: if (a) { var pt = mapToItem(rr.parent, mouse.x, mouse.y);' + - 'obj.' + xprop + ' = pt.x; obj.' + yprop + ' = pt.y; rr.x = pt.x - 10; rr.y = pt.y - 10; } } }', - canvas, "resizer_item"); + var ma = mouseArea.createObject(canvas, { + color: color, + xprop: xprop, + yprop: yprop + }); ma.visible = root.showResizers; ma.obj = obj; ma.x = x - 10; @@ -186,15 +217,55 @@ Rectangle { return ma; } + property Component linePath: Component { + ShapePath { + id: lineShapePath + strokeColor: "black" + strokeWidth: widthSlider.value + fillColor: "transparent" + PathLine { + x: lineShapePath.startX + 1 + y: lineShapePath.startY + 1 + } + } + } + + property Component cubicPath: Component { + ShapePath { + id: cubicShapePath + strokeColor: "black" + strokeWidth: widthSlider.value + fillColor: root.fill ? 'green' : 'transparent' + PathCubic { + x: cubicShapePath.startX + 1 + y: cubicShapePath.startY + 1 + control1X: cubicShapePath.startX + 50; + control1Y: cubicShapePath.startY + 50; + control2X: cubicShapePath.startX + 150; + control2Y: cubicShapePath.startY + 50; + } + } + } + + property Component quadPath: Component { + ShapePath { + id: quadShapePath + strokeColor: "black" + strokeWidth: widthSlider.value + fillColor: root.fill ? 'green' : 'transparent' + PathQuad { + x: quadShapePath.startx + 1 + y: quadShapePath.startY + 1 + controlX: quadShapePath.startX + 50 + controlY: quadShapePath.startY + 50 + } + } + } + Component.onCompleted: { funcs = [ { "start": function(x, y) { - var p = Qt.createQmlObject('import QtQuick 2.9; import QtQuick.Shapes 1.0; ShapePath {' + - 'strokeColor: "black"; fillColor: "transparent";'+ - 'strokeWidth: ' + widthSlider.value + ';' + - 'startX: ' + x + '; startY: ' + y + ';' + - 'PathLine { x: ' + x + ' + 1; y: ' + y + ' + 1 } }', - root, "dynamic_visual_path"); + var p = linePath.createObject(root, { startX: x, startY: y }); shape.data.push(p); activePath = p; }, "move": function(x, y) { @@ -211,13 +282,7 @@ Rectangle { } }, { "start": function(x, y) { - var p = Qt.createQmlObject('import QtQuick 2.9; import QtQuick.Shapes 1.0; ShapePath {' + - 'strokeColor: "black"; fillColor: "' + (root.fill ? 'green' : 'transparent') + '";'+ - 'strokeWidth: ' + widthSlider.value + ';' + - 'startX: ' + x + '; startY: ' + y + ';' + - 'PathCubic { x: ' + x + ' + 1; y: ' + y + ' + 1;' + - 'control1X: ' + x + ' + 50; control1Y: ' + y + ' + 50; control2X: ' + x + ' + 150; control2Y: ' + y + ' + 50; } }', - root, "dynamic_visual_path"); + var p = cubicPath.createObject(root, { startX: x, startY: y }); shape.data.push(p); activePath = p; }, "move": function(x, y) { @@ -236,13 +301,7 @@ Rectangle { } }, { "start": function(x, y) { - var p = Qt.createQmlObject('import QtQuick 2.9; import QtQuick.Shapes 1.0; ShapePath {' + - 'strokeColor: "black"; fillColor: "' + (root.fill ? 'green' : 'transparent') + '";'+ - 'strokeWidth: ' + widthSlider.value + ';' + - 'startX: ' + x + '; startY: ' + y + ';' + - 'PathQuad { x: ' + x + ' + 1; y: ' + y + ' + 1;' + - 'controlX: ' + x + ' + 50; controlY: ' + y + ' + 50 } }', - root, "dynamic_visual_path"); + var p = quadPath.createObject(root, { startX: x, startY: y }); shape.data.push(p); activePath = p; }, "move": function(x, y) { diff --git a/examples/quick/tableview/gameoflife/gameoflifemodel.h b/examples/quick/tableview/gameoflife/gameoflifemodel.h index 3ea1469861..161e0b2cfc 100644 --- a/examples/quick/tableview/gameoflife/gameoflifemodel.h +++ b/examples/quick/tableview/gameoflife/gameoflifemodel.h @@ -54,11 +54,13 @@ #include <array> #include <QAbstractTableModel> #include <QPoint> +#include <QtQml/qqml.h> //! [modelclass] class GameOfLifeModel : public QAbstractTableModel { Q_OBJECT + QML_ELEMENT Q_ENUMS(Roles) public: diff --git a/examples/quick/tableview/gameoflife/main.cpp b/examples/quick/tableview/gameoflife/main.cpp index 5101880b06..fcea7080ea 100644 --- a/examples/quick/tableview/gameoflife/main.cpp +++ b/examples/quick/tableview/gameoflife/main.cpp @@ -59,7 +59,7 @@ int main(int argc, char *argv[]) QQmlApplicationEngine engine; //! [registertype] - qmlRegisterType<GameOfLifeModel>("GameOfLifeModel", 1, 0, "GameOfLifeModel"); + qmlRegisterTypesAndRevisions<GameOfLifeModel>("GameOfLifeModel", 1); //! [registertype] engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); diff --git a/examples/quick/tableview/pixelator/imagemodel.h b/examples/quick/tableview/pixelator/imagemodel.h index bf0ec90da4..de8ad7cd8d 100644 --- a/examples/quick/tableview/pixelator/imagemodel.h +++ b/examples/quick/tableview/pixelator/imagemodel.h @@ -53,12 +53,14 @@ #include <QAbstractTableModel> #include <QImage> +#include <QtQml/qqml.h> //! [model] class ImageModel : public QAbstractTableModel { Q_OBJECT Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged) + QML_ELEMENT public: ImageModel(QObject *parent = nullptr); diff --git a/examples/quick/tableview/pixelator/main.cpp b/examples/quick/tableview/pixelator/main.cpp index c57039556a..a8cb6c4e86 100644 --- a/examples/quick/tableview/pixelator/main.cpp +++ b/examples/quick/tableview/pixelator/main.cpp @@ -60,7 +60,7 @@ int main(int argc, char *argv[]) QQmlApplicationEngine engine; //! [registertype] - qmlRegisterType<ImageModel>("ImageModel", 1, 0, "ImageModel"); + qmlRegisterTypesAndRevisions<ImageModel>("ImageModel", 1); //! [registertype] engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); diff --git a/examples/quick/views/doc/src/views.qdoc b/examples/quick/views/doc/src/views.qdoc index 294b88542b..16237a68e0 100644 --- a/examples/quick/views/doc/src/views.qdoc +++ b/examples/quick/views/doc/src/views.qdoc @@ -87,7 +87,7 @@ \section1 Packages - \e Packages uses the \l Package type to transition delegates between + \e Packages use the \l [QML]{Package} type to transition delegates between two views. It has a Package object which defines delegate items for each view and an |