aboutsummaryrefslogtreecommitdiffstats
path: root/examples/quick/scenegraph/rendernode
diff options
context:
space:
mode:
Diffstat (limited to 'examples/quick/scenegraph/rendernode')
-rw-r--r--examples/quick/scenegraph/rendernode/customrenderitem.cpp70
-rw-r--r--examples/quick/scenegraph/rendernode/d3d12renderer.cpp34
-rw-r--r--examples/quick/scenegraph/rendernode/d3d12renderer.h11
-rw-r--r--examples/quick/scenegraph/rendernode/doc/images/rendernode-example.jpgbin0 -> 77210 bytes
-rw-r--r--examples/quick/scenegraph/rendernode/doc/src/rendernode.qdoc165
-rw-r--r--examples/quick/scenegraph/rendernode/main.cpp2
-rw-r--r--examples/quick/scenegraph/rendernode/main.qml90
-rw-r--r--examples/quick/scenegraph/rendernode/metalrenderer.h104
-rw-r--r--examples/quick/scenegraph/rendernode/metalrenderer.mm333
-rw-r--r--examples/quick/scenegraph/rendernode/metalshader.frag28
-rw-r--r--examples/quick/scenegraph/rendernode/metalshader.vert31
-rw-r--r--examples/quick/scenegraph/rendernode/openglrenderer.cpp48
-rw-r--r--examples/quick/scenegraph/rendernode/openglrenderer.h10
-rw-r--r--examples/quick/scenegraph/rendernode/rendernode.pro6
-rw-r--r--examples/quick/scenegraph/rendernode/rendernode.qrc2
-rw-r--r--examples/quick/scenegraph/rendernode/softwarerenderer.cpp26
-rw-r--r--examples/quick/scenegraph/rendernode/softwarerenderer.h7
17 files changed, 888 insertions, 79 deletions
diff --git a/examples/quick/scenegraph/rendernode/customrenderitem.cpp b/examples/quick/scenegraph/rendernode/customrenderitem.cpp
index 8f248e2ecb..e55cf0a2f4 100644
--- a/examples/quick/scenegraph/rendernode/customrenderitem.cpp
+++ b/examples/quick/scenegraph/rendernode/customrenderitem.cpp
@@ -53,42 +53,78 @@
#include <QSGRendererInterface>
#include "openglrenderer.h"
+#include "metalrenderer.h"
#include "d3d12renderer.h"
#include "softwarerenderer.h"
+//! [1]
CustomRenderItem::CustomRenderItem(QQuickItem *parent)
: QQuickItem(parent)
{
// Our item shows something so set the flag.
setFlag(ItemHasContents);
}
+//! [1]
+//! [2]
QSGNode *CustomRenderItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
{
QSGRenderNode *n = static_cast<QSGRenderNode *>(node);
- if (!n) {
- QSGRendererInterface *ri = window()->rendererInterface();
- if (!ri)
- return nullptr;
- switch (ri->graphicsApi()) {
- case QSGRendererInterface::OpenGL:
+
+ 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);
- break;
+ if (!n)
+ n = new OpenGLRenderNode;
+ static_cast<OpenGLRenderNode *>(n)->sync(this);
+#endif
+ break;
+
+ 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
- case QSGRendererInterface::Direct3D12:
+ break;
+
+ case QSGRendererInterface::Direct3D12: // ### Qt 6: remove
#if QT_CONFIG(d3d12)
- n = new D3D12RenderNode(this);
- break;
+ if (!n)
+ n = new D3D12RenderNode;
+ static_cast<D3D12RenderNode *>(n)->sync(this);
#endif
- case QSGRendererInterface::Software:
- n = new SoftwareRenderNode(this);
- break;
+ break;
- default:
- return nullptr;
- }
+ case QSGRendererInterface::Software:
+ if (!n)
+ 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]
+
+// This item does not support being moved between windows. If that is desired,
+// itemChange() should be reimplemented as well.
diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp
index df0e29eb67..e85811c089 100644
--- a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp
+++ b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp
@@ -54,12 +54,9 @@
#include <QSGRendererInterface>
#include <QFile>
-#if QT_CONFIG(d3d12)
+// ### Qt 6: remove
-D3D12RenderNode::D3D12RenderNode(QQuickItem *item)
- : m_item(item)
-{
-}
+#if QT_CONFIG(d3d12)
D3D12RenderNode::~D3D12RenderNode()
{
@@ -85,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;
@@ -166,8 +163,8 @@ void D3D12RenderNode::init()
psoDesc.RasterizerState = rastDesc;
psoDesc.BlendState = blendDesc;
// No depth. The correct stacking of the item is ensured by the projection matrix.
- // Do not bother with stencil since we do not apply clipping in the
- // example. If clipping is desired, render() needs to set a different PSO
+ // Note that this does not support clipping.
+ // If clipping is desired, render() needs to set a different PSO
// with stencil enabled whenever the RenderState indicates so.
psoDesc.SampleMask = UINT_MAX;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
@@ -176,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 = {};
@@ -255,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);
@@ -266,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();
@@ -299,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/doc/images/rendernode-example.jpg b/examples/quick/scenegraph/rendernode/doc/images/rendernode-example.jpg
new file mode 100644
index 0000000000..cbb59b950d
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/doc/images/rendernode-example.jpg
Binary files differ
diff --git a/examples/quick/scenegraph/rendernode/doc/src/rendernode.qdoc b/examples/quick/scenegraph/rendernode/doc/src/rendernode.qdoc
new file mode 100644
index 0000000000..ba6551fddf
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/doc/src/rendernode.qdoc
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example scenegraph/rendernode
+ \title Scene Graph - Custom Rendering with QSGRenderNode
+ \ingroup qtquickexamples
+ \brief Shows how to integrate drawing via the native graphics API with the Qt Quick scene graph.
+
+ \image rendernode-example.jpg
+
+ \l QSGRenderNode allows integrating draw and other calls made directly via
+ the Qt Quick scene graph's underlying native graphics API (such as, Vulkan,
+ Metal, Direct 3D, or OpenGL). This example demonstrates implementing a
+ custom QQuickItem backed by a QSGRenderNode implementation, where the node
+ renders a triangle directly via the graphics API. The rest of the scene
+ (background, text, rectangles) are standard Qt Quick items. The example has
+ full support for OpenGL and Metal, as well as the software backend of Qt
+ Quick.
+
+ The custom item behaves like any other Qt Quick item, meaning it
+ participates and stacking and clipping as usual, which is a big difference
+ to the alternative approaches like having the custom rendering as an
+ overlay (connecting to \l QQuickWindow::afterRendering()) and underlay
+ (connecting to \l QQuickWindow::beforeRendering()) because those do not
+ offer the possibility of proper mixing of the custom content with the Qt
+ Quick scene.
+
+ Another important feature is that QSGRenderNode can be helpful to preserve
+ performance, when compared to some of the alternatives. Going through \l
+ QQuickFramebufferObject allows creating a custom item similarly to what
+ this example does, but it does it by rendering the custom content in a
+ texture, and then drawing a textured quad with that texture. This can be
+ expensive on some systems due to the cost of texturing and blending.
+ QSGRenderNode avoids this since the native graphics calls are issued in
+ line with the draw calls for the scene graph's batches.
+
+ All this comes at the cost of being more complex, and not necessarily being
+ suitable for all types of 3D content, in particular where vertices and
+ different depth would clash with the 2D content in the Qt Quick scene
+ graph's batches (those are better served by "flattening" into a 2D texture
+ via approaches like QQuickFramebufferObject). Therefore QSGRenderNode is
+ not always the right choice. It can however a good and powerful choice in
+ many cases. This is what the example demonstrates.
+
+ Let's go through the most important parts of the code:
+
+ \snippet scenegraph/rendernode/main.cpp 1
+
+ Our custom QML type is implemented in the class CustomRenderItem.
+
+ \snippet scenegraph/rendernode/main.qml 2
+
+ The corresponding import in the QML document.
+
+ \snippet scenegraph/rendernode/main.qml 3
+
+ The CustomRenderItem object. It is positioned to fill a big part of the
+ scene, covering its parent (the yellow rectangle; this will be used to
+ demonstrate clipping). The item will have its scale and rotation animated.
+
+ \snippet scenegraph/rendernode/main.qml 4
+
+ Text items are used to show some helpful information, such as, the
+ active graphics API Qt Quick uses.
+
+ \snippet scenegraph/rendernode/main.qml 5
+
+ Clicking the left mouse button is used to toggle clipping on the custom
+ item's parent item. By default this is done using scissoring (GL_SCISSOR_TEST
+ with OpenGL). A well-written QSGRenderNode implementation is expected to be
+ able to take this into account and enable scissor testing when the scene graph
+ indicates that it is necessary.
+
+ The right mouse button is used to toggle an animation on the rotation of
+ the parent item. With clipping enabled, this demonstrates clipping via the
+ stencil buffer since a rectangular scissor is not appropriate when we need
+ to clip to a rotated rectangle shape. The scene graph fills up the stencil
+ buffer as necessary, the QSGRenderNode implementation just has to enable
+ stencil testing using the provided reference value.
+
+ \snippet scenegraph/rendernode/customrenderitem.cpp 1
+
+ Moving on to the CustomRenderItem implementation. This is a visual item.
+
+ \snippet scenegraph/rendernode/customrenderitem.cpp 2
+
+ The implementation of \l QQuickItem::updatePaintNode() creates (if not yet
+ done) and returns an instance of a suitable QSGRenderNode subclass. The
+ example supports multiple graphics APIs, and also the \c software backend.
+
+ Let's look at the the render node for OpenGL (supporting both the
+ traditional, direct OpenGL-based scene graph, and also the modern,
+ abstracted variant using the RHI). For other graphics APIs, the concepts
+ and the outline of a QSGRenderNode implementation are the same. It is worth
+ noting that in some cases it will also be necessary to connect to a signal
+ like \l QQuickWindow::beforeRendering() to perform copy type of operations
+ (such as, vertex buffer uploads). This is not necessary for OpenGL, but it
+ is essential for Vulkan or Metal since there such operations cannot be
+ issued in render() as there is a renderpass being recorded when render() is
+ called.
+
+ \snippet scenegraph/rendernode/openglrenderer.h 1
+
+ The main job is to provide implementations of the virtual QSGRenderNode functions.
+
+ \snippet scenegraph/rendernode/openglrenderer.cpp 1
+
+ The pattern for safe graphics resource management is to do any cleanup in
+ \l{QSGRenderNode::releaseResources()}{releaseResources()}, while also
+ calling this from the destructor.
+
+ \snippet scenegraph/rendernode/openglrenderer.cpp 2
+
+ The render() function initializes graphics resources (in this case, an
+ OpenGL shader program and a vertex buffer), if not yet done. It then
+ makes sure the necessary resources are bound and updates uniforms.
+ The transformation matrix and the opacity are provided by the scene graph
+ either via the \c state argument or base class functions.
+
+ \snippet scenegraph/rendernode/openglrenderer.cpp 5
+
+ This render node is well-behaving since it basically renders in 2D,
+ respecting the item's geometry. This is not mandatory, but then flags() has
+ to return (or not return) the appropriate flags.
+
+ \snippet scenegraph/rendernode/openglrenderer.cpp 3
+
+ After setting up vertex inputs, but before recording a draw call for our
+ triangle, it is important to set some state in order to integrate with the
+ rest of the scene correctly. Setting scissor and stencil as instructed by
+ \c state allows our item to render correctly even when there are one or
+ more clips in the parent chain.
+
+ \snippet scenegraph/rendernode/openglrenderer.cpp 4
+
+ As shown above, we only really render in 2D (no depth), within the item's
+ geometry. changedStates() returns the flags corresponding to the OpenGL
+ states render() touches.
+
+*/
diff --git a/examples/quick/scenegraph/rendernode/main.cpp b/examples/quick/scenegraph/rendernode/main.cpp
index 21419abfc9..146d787e50 100644
--- a/examples/quick/scenegraph/rendernode/main.cpp
+++ b/examples/quick/scenegraph/rendernode/main.cpp
@@ -58,7 +58,9 @@ int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
+//! [1]
qmlRegisterType<CustomRenderItem>("SceneGraphRendering", 2, 0, "CustomRenderItem");
+//! [1]
QQuickView view;
diff --git a/examples/quick/scenegraph/rendernode/main.qml b/examples/quick/scenegraph/rendernode/main.qml
index d0ba4a4669..153a71e097 100644
--- a/examples/quick/scenegraph/rendernode/main.qml
+++ b/examples/quick/scenegraph/rendernode/main.qml
@@ -49,27 +49,65 @@
****************************************************************************/
import QtQuick 2.8
+//! [2]
import SceneGraphRendering 2.0
+//! [2]
Item {
Rectangle {
+ id: bg
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0; color: "steelblue" }
GradientStop { position: 1; color: "black" }
}
- CustomRenderItem {
- id: renderer
+ //! [5]
+ MouseArea {
anchors.fill: parent
- anchors.margins: 10
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ onClicked: {
+ if (mouse.button === Qt.LeftButton) {
+ clipper.clip = !clipper.clip
+ } else if (mouse.button === Qt.RightButton) {
+ nonRectClipAnim.running = !nonRectClipAnim.running
+ if (!nonRectClipAnim.running)
+ clipper.rotation = 0;
+ }
+ }
+ }
+ // ![5]
+
+ Rectangle {
+ id: clipper
+ width: parent.width / 2
+ height: parent.height / 2
+ anchors.centerIn: parent
+ border.color: "yellow"
+ border.width: 2
+ color: "transparent"
+ NumberAnimation on rotation {
+ id: nonRectClipAnim
+ from: 0; to: 360; duration: 5000; loops: Animation.Infinite
+ running: false
+ }
+
+ //! [3]
+ CustomRenderItem {
+ id: renderer
+ width: bg.width - 20
+ height: bg.height - 20
+ x: -clipper.x + 10
+ y: -clipper.y + 10
- transform: [
- Rotation { id: rotation; axis.x: 0; axis.z: 0; axis.y: 1; angle: 0; origin.x: renderer.width / 2; origin.y: renderer.height / 2; },
- Translate { id: txOut; x: -renderer.width / 2; y: -renderer.height / 2 },
- Scale { id: scale; },
- Translate { id: txIn; x: renderer.width / 2; y: renderer.height / 2 }
- ]
+ transform: [
+ Rotation { id: rotation; axis.x: 0; axis.z: 0; axis.y: 1; angle: 0; origin.x: renderer.width / 2; origin.y: renderer.height / 2; },
+ Translate { id: txOut; x: -renderer.width / 2; y: -renderer.height / 2 },
+ Scale { id: scale; },
+ Translate { id: txIn; x: renderer.width / 2; y: renderer.height / 2 }
+ ]
+ }
+ //! [3]
}
SequentialAnimation {
@@ -92,19 +130,39 @@ Item {
loops: Animation.Infinite
}
+ //! [4]
Text {
id: label
- anchors.bottom: renderer.bottom
- anchors.left: renderer.left
- anchors.right: renderer.right
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
anchors.margins: 20
+ color: "yellow"
wrapMode: Text.WordWrap
property int api: GraphicsInfo.api
- text: "Custom rendering via the graphics API "
- + (api === GraphicsInfo.OpenGL ? "OpenGL"
- : api === GraphicsInfo.Direct3D12 ? "Direct3D 12"
- : api === GraphicsInfo.Software ? "Software" : "")
+ text: {
+ var apiStr;
+ switch (api) {
+ case GraphicsInfo.OpenGL: apiStr = "OpenGL (direct)"; break;
+ case GraphicsInfo.Direct3D12: apiStr = "Direct3D 12 (direct)"; break;
+ case GraphicsInfo.Software: apiStr = "Software (QPainter)"; break;
+ case GraphicsInfo.OpenGLRhi: apiStr = "OpenGL (RHI)"; break;
+ case GraphicsInfo.MetalRhi: apiStr = "Metal (RHI)"; break;
+ // the example has no other QSGRenderNode subclasses
+ default: apiStr = "<UNSUPPORTED>"; break;
+ }
+ "Custom rendering via the graphics API " + apiStr
+ + "\nLeft click to toggle clipping to yellow rect"
+ + "\nRight click to rotate (can be used to exercise stencil clip instead of scissor)"
+ }
+ // ![4]
+ }
+
+ Text {
+ id: label2
+ anchors.top: parent.top
+ anchors.right: parent.right
color: "yellow"
+ text: "Clip: " + (clipper.clip ? "ON" : "OFF") + " Rotation: " + (nonRectClipAnim.running ? "ON" : "OFF")
}
}
}
diff --git a/examples/quick/scenegraph/rendernode/metalrenderer.h b/examples/quick/scenegraph/rendernode/metalrenderer.h
new file mode 100644
index 0000000000..cf7fccb930
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/metalrenderer.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef METALRENDERER_H
+#define METALRENDERER_H
+
+#include <qsgrendernode.h>
+#include <QQuickItem>
+
+//#ifdef Q_OS_DARWIN
+#ifdef Q_OS_MACOS
+
+QT_BEGIN_NAMESPACE
+
+QT_END_NAMESPACE
+
+class MetalRenderNodeResourceBuilder : public QObject
+{
+ Q_OBJECT
+
+public:
+ void setWindow(QQuickWindow *w) { m_window = w; }
+
+public slots:
+ void build();
+
+private:
+ QQuickWindow *m_window = nullptr;
+};
+
+class MetalRenderNode : public QSGRenderNode
+{
+public:
+ MetalRenderNode();
+ ~MetalRenderNode();
+
+ void render(const RenderState *state) override;
+ void releaseResources() override;
+ StateFlags changedStates() const override;
+ RenderingFlags flags() const override;
+ QRectF rect() const override;
+
+ MetalRenderNodeResourceBuilder *resourceBuilder() { return &m_resourceBuilder; }
+
+ void sync(QQuickItem *item);
+
+private:
+ MetalRenderNodeResourceBuilder m_resourceBuilder;
+ QQuickWindow *m_window = nullptr;
+ int m_width = 0;
+ int m_height = 0;
+ int m_outputHeight = 0;
+};
+
+#endif // Q_OS_DARWIN
+
+#endif
diff --git a/examples/quick/scenegraph/rendernode/metalrenderer.mm b/examples/quick/scenegraph/rendernode/metalrenderer.mm
new file mode 100644
index 0000000000..b83dc62c48
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/metalrenderer.mm
@@ -0,0 +1,333 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "metalrenderer.h"
+#include <QQuickItem>
+#include <QQuickWindow>
+
+#include <Metal/Metal.h>
+
+using FuncAndLib = QPair<id<MTLFunction>, id<MTLLibrary> >;
+
+const int MAX_FRAMES_IN_FLIGHT = 3;
+
+struct {
+ id<MTLDevice> dev = nil;
+ QByteArray vsSource;
+ FuncAndLib vs;
+ QByteArray fsSource;
+ FuncAndLib fs;
+ id<MTLBuffer> vbuf[MAX_FRAMES_IN_FLIGHT];
+ id<MTLBuffer> ubuf[MAX_FRAMES_IN_FLIGHT];
+ id<MTLDepthStencilState> stencilEnabledDsState = nil;
+ id<MTLRenderPipelineState> pipeline = nil;
+} g;
+
+static FuncAndLib compileShaderFromSource(const QByteArray &src, const QByteArray &entryPoint)
+{
+ FuncAndLib fl;
+
+ NSString *srcstr = [NSString stringWithUTF8String: src.constData()];
+ MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
+ opts.languageVersion = MTLLanguageVersion1_2;
+ NSError *err = nil;
+ fl.second = [g.dev newLibraryWithSource: srcstr options: opts error: &err];
+ [opts release];
+ // srcstr is autoreleased
+
+ if (err) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qFatal("%s", qPrintable(msg));
+ return fl;
+ }
+
+ NSString *name = [NSString stringWithUTF8String: entryPoint.constData()];
+ fl.first = [fl.second newFunctionWithName: name];
+ [name release];
+
+ return fl;
+}
+
+const int VERTEX_SIZE = 6 * sizeof(float);
+
+static float colors[] = {
+ 1.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f
+};
+
+void MetalRenderNodeResourceBuilder::build()
+{
+ if (!g.dev) {
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::MetalRhi);
+
+ g.dev = (id<MTLDevice>) rif->getResource(m_window, QSGRendererInterface::DeviceResource);
+ Q_ASSERT(g.dev);
+ }
+
+ if (g.vsSource.isEmpty()) {
+ const QString filename = QLatin1String(":/scenegraph/rendernode/metalshader.vert");
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
+ qFatal("Failed to read shader %s", qPrintable(filename));
+ g.vsSource = f.readAll();
+ g.vs = compileShaderFromSource(g.vsSource, QByteArrayLiteral("main0"));
+ }
+
+ if (g.fsSource.isEmpty()) {
+ const QString filename = QLatin1String(":/scenegraph/rendernode/metalshader.frag");
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
+ qFatal("Failed to read shader %s", qPrintable(filename));
+ g.fsSource = f.readAll();
+ g.fs = compileShaderFromSource(g.fsSource, QByteArrayLiteral("main0"));
+ }
+
+ const int framesInFlight = m_window->graphicsStateInfo().framesInFlight;
+
+ // For simplicity's sake we use shared mode (something like host visible +
+ // host coherent) for everything.
+
+ for (int i = 0; i < framesInFlight; ++i) {
+ // Have multiple versions for vertex too since we'll just memcpy new
+ // vertices based on item width and height on every render(). This could
+ // be optimized further however.
+ if (!g.vbuf[i]) {
+ g.vbuf[i] = [g.dev newBufferWithLength: VERTEX_SIZE + sizeof(colors) options: MTLResourceStorageModeShared];
+ char *p = (char *) [g.vbuf[i] contents];
+ memcpy(p + VERTEX_SIZE, colors, sizeof(colors));
+ }
+
+ if (!g.ubuf[i])
+ g.ubuf[i] = [g.dev newBufferWithLength: 256 options: MTLResourceStorageModeShared];
+ }
+
+ if (!g.stencilEnabledDsState) {
+ MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
+ dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
+ dsDesc.frontFaceStencil.stencilFailureOperation = MTLStencilOperationKeep;
+ dsDesc.frontFaceStencil.depthFailureOperation = MTLStencilOperationKeep;
+ dsDesc.frontFaceStencil.depthStencilPassOperation = MTLStencilOperationKeep;
+ dsDesc.frontFaceStencil.stencilCompareFunction = MTLCompareFunctionEqual;
+ dsDesc.frontFaceStencil.readMask = 0xFF;
+ dsDesc.frontFaceStencil.writeMask = 0xFF;
+
+ dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
+ dsDesc.backFaceStencil.stencilFailureOperation = MTLStencilOperationKeep;
+ dsDesc.backFaceStencil.depthFailureOperation = MTLStencilOperationKeep;
+ dsDesc.backFaceStencil.depthStencilPassOperation = MTLStencilOperationKeep;
+ dsDesc.backFaceStencil.stencilCompareFunction = MTLCompareFunctionEqual;
+ dsDesc.backFaceStencil.readMask = 0xFF;
+ dsDesc.backFaceStencil.writeMask = 0xFF;
+
+ g.stencilEnabledDsState = [g.dev newDepthStencilStateWithDescriptor: dsDesc];
+ [dsDesc release];
+ }
+
+ if (!g.pipeline) {
+ MTLVertexDescriptor *inputLayout = [MTLVertexDescriptor vertexDescriptor];
+ inputLayout.attributes[0].format = MTLVertexFormatFloat2;
+ inputLayout.attributes[0].offset = 0;
+ inputLayout.attributes[0].bufferIndex = 1; // ubuf is 0, vbuf is 1 and 2
+ inputLayout.attributes[1].format = MTLVertexFormatFloat3;
+ inputLayout.attributes[1].offset = 0;
+ inputLayout.attributes[1].bufferIndex = 2;
+ inputLayout.layouts[1].stride = 2 * sizeof(float);
+ inputLayout.layouts[2].stride = 3 * sizeof(float);
+
+ MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
+ rpDesc.vertexDescriptor = inputLayout;
+
+ rpDesc.vertexFunction = g.vs.first;
+ rpDesc.fragmentFunction = g.fs.first;
+
+ rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
+ rpDesc.colorAttachments[0].blendingEnabled = true;
+ rpDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorOne;
+ rpDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne;
+ rpDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
+ rpDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
+
+ if (g.dev.depth24Stencil8PixelFormatSupported) {
+ rpDesc.depthAttachmentPixelFormat = MTLPixelFormatDepth24Unorm_Stencil8;
+ rpDesc.stencilAttachmentPixelFormat = MTLPixelFormatDepth24Unorm_Stencil8;
+ } else {
+ rpDesc.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
+ rpDesc.stencilAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
+ }
+
+ NSError *err = nil;
+ g.pipeline = [g.dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
+ if (!g.pipeline) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qFatal("Failed to create render pipeline state: %s", qPrintable(msg));
+ }
+ [rpDesc release];
+ }
+}
+
+MetalRenderNode::MetalRenderNode()
+{
+ g.vs.first = g.fs.first = nil;
+ g.vs.second = g.fs.second = nil;
+
+ for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
+ g.vbuf[i] = nil;
+ g.ubuf[i] = nil;
+ }
+}
+
+MetalRenderNode::~MetalRenderNode()
+{
+ releaseResources();
+}
+
+void MetalRenderNode::releaseResources()
+{
+ [g.stencilEnabledDsState release];
+ g.stencilEnabledDsState = nil;
+
+ [g.pipeline release];
+ g.pipeline = nil;
+
+ [g.vs.first release];
+ [g.vs.second release];
+
+ [g.fs.first release];
+ [g.fs.second release];
+
+ g.vs.first = g.fs.first = nil;
+ g.vs.second = g.fs.second = nil;
+
+ for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
+ [g.vbuf[i] release];
+ g.vbuf[i] = nil;
+ [g.ubuf[i] release];
+ g.ubuf[i] = nil;
+ }
+}
+
+void MetalRenderNode::render(const RenderState *state)
+{
+ 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_width - 1, m_height - 1);
+ QPointF p1(0, 0);
+ QPointF p2(0, m_height - 1);
+
+ float vertices[6] = { float(p0.x()), float(p0.y()),
+ float(p1.x()), float(p1.y()),
+ float(p2.x()), float(p2.y()) };
+ char *p = (char *) [vbuf contents];
+ memcpy(p, vertices, VERTEX_SIZE);
+
+ const QMatrix4x4 mvp = *state->projectionMatrix() * *matrix();
+ const float opacity = inheritedOpacity();
+
+ p = (char *) [ubuf contents];
+ memcpy(p, mvp.constData(), 64);
+ memcpy(p + 64, &opacity, 4);
+
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ id<MTLRenderCommandEncoder> encoder = (id<MTLRenderCommandEncoder>) rif->getResource(
+ m_window, QSGRendererInterface::CommandEncoderResource);
+ Q_ASSERT(encoder);
+
+ [encoder setVertexBuffer: vbuf offset: 0 atIndex: 1];
+ [encoder setVertexBuffer: vbuf offset: VERTEX_SIZE atIndex: 2];
+
+ [encoder setVertexBuffer: ubuf offset: 0 atIndex: 0];
+ [encoder setFragmentBuffer: ubuf offset: 0 atIndex: 0];
+
+ // Clip support.
+ if (state->scissorEnabled()) {
+ const QRect r = state->scissorRect(); // bottom-up
+ MTLScissorRect s;
+ s.x = r.x();
+ s.y = m_outputHeight - (r.y() + r.height());
+ s.width = r.width();
+ s.height = r.height();
+ [encoder setScissorRect: s];
+ }
+ if (state->stencilEnabled()) {
+ [encoder setDepthStencilState: g.stencilEnabledDsState];
+ [encoder setStencilReferenceValue: state->stencilValue()];
+ }
+
+ [encoder setRenderPipelineState: g.pipeline];
+ [encoder drawPrimitives: MTLPrimitiveTypeTriangle vertexStart: 0 vertexCount: 3 instanceCount: 1 baseInstance: 0];
+}
+
+QSGRenderNode::StateFlags MetalRenderNode::changedStates() const
+{
+ return BlendState | ScissorState | StencilState;
+}
+
+QSGRenderNode::RenderingFlags MetalRenderNode::flags() const
+{
+ return BoundedRectRendering | DepthAwareRendering;
+}
+
+QRectF MetalRenderNode::rect() const
+{
+ return QRect(0, 0, m_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/metalshader.frag b/examples/quick/scenegraph/rendernode/metalshader.frag
new file mode 100644
index 0000000000..907faa537f
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/metalshader.frag
@@ -0,0 +1,28 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct buf
+{
+ float4x4 matrix;
+ float opacity;
+};
+
+struct main0_out
+{
+ float4 fragColor [[color(0)]];
+};
+
+struct main0_in
+{
+ float4 v_color [[user(locn0)]];
+};
+
+fragment main0_out main0(main0_in in [[stage_in]], constant buf& ubuf [[buffer(0)]])
+{
+ main0_out out = {};
+ out.fragColor = in.v_color * ubuf.opacity;
+ return out;
+}
+
diff --git a/examples/quick/scenegraph/rendernode/metalshader.vert b/examples/quick/scenegraph/rendernode/metalshader.vert
new file mode 100644
index 0000000000..12b721f524
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/metalshader.vert
@@ -0,0 +1,31 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct buf
+{
+ float4x4 matrix;
+ float opacity;
+};
+
+struct main0_out
+{
+ float4 v_color [[user(locn0)]];
+ float4 gl_Position [[position]];
+};
+
+struct main0_in
+{
+ float4 pos [[attribute(0)]];
+ float4 color [[attribute(1)]];
+};
+
+vertex main0_out main0(main0_in in [[stage_in]], constant buf& ubuf [[buffer(0)]])
+{
+ main0_out out = {};
+ out.v_color = in.color;
+ out.gl_Position = ubuf.matrix * in.pos;
+ return out;
+}
+
diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.cpp b/examples/quick/scenegraph/rendernode/openglrenderer.cpp
index 3c68830db6..a4e619bea9 100644
--- a/examples/quick/scenegraph/rendernode/openglrenderer.cpp
+++ b/examples/quick/scenegraph/rendernode/openglrenderer.cpp
@@ -57,11 +57,7 @@
#include <QOpenGLBuffer>
#include <QOpenGLFunctions>
-OpenGLRenderNode::OpenGLRenderNode(QQuickItem *item)
- : m_item(item)
-{
-}
-
+//! [1]
OpenGLRenderNode::~OpenGLRenderNode()
{
releaseResources();
@@ -74,6 +70,7 @@ void OpenGLRenderNode::releaseResources()
delete m_vbo;
m_vbo = nullptr;
}
+//! [1]
void OpenGLRenderNode::init()
{
@@ -121,44 +118,66 @@ void OpenGLRenderNode::init()
m_vbo->release();
}
+//! [2]
void OpenGLRenderNode::render(const RenderState *state)
{
if (!m_program)
init();
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
-
m_program->bind();
m_program->setUniformValue(m_matrixUniform, *state->projectionMatrix() * *matrix());
m_program->setUniformValue(m_opacityUniform, float(inheritedOpacity()));
+//! [2]
m_vbo->bind();
- QPointF p0(m_item->width() - 1, m_item->height() - 1);
+//! [5]
+ 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()),
GLfloat(p2.x()), GLfloat(p2.y()) };
m_vbo->write(0, vertices, sizeof(vertices));
+//! [5]
m_program->setAttributeBuffer(0, GL_FLOAT, 0, 2);
m_program->setAttributeBuffer(1, GL_FLOAT, sizeof(vertices), 3);
m_program->enableAttributeArray(0);
m_program->enableAttributeArray(1);
- // Note that clipping (scissor or stencil) is ignored in this example.
+ // We are prepared both for the legacy (direct OpenGL) and the modern
+ // (abstracted by RHI) OpenGL scenegraph. So set all the states that are
+ // important to us.
+
+ //! [3]
+ f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
f->glEnable(GL_BLEND);
f->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ // Clip support.
+ if (state->scissorEnabled()) {
+ f->glEnable(GL_SCISSOR_TEST);
+ const QRect r = state->scissorRect(); // already bottom-up
+ f->glScissor(r.x(), r.y(), r.width(), r.height());
+ }
+ if (state->stencilEnabled()) {
+ f->glEnable(GL_STENCIL_TEST);
+ f->glStencilFunc(GL_EQUAL, state->stencilValue(), 0xFF);
+ f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ }
+
f->glDrawArrays(GL_TRIANGLES, 0, 3);
+ //! [3]
}
+//! [4]
QSGRenderNode::StateFlags OpenGLRenderNode::changedStates() const
{
- return BlendState;
+ return BlendState | ScissorState | StencilState;
}
QSGRenderNode::RenderingFlags OpenGLRenderNode::flags() const
@@ -168,7 +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 ac640405c5..1e7977481a 100644
--- a/examples/quick/scenegraph/rendernode/openglrenderer.h
+++ b/examples/quick/scenegraph/rendernode/openglrenderer.h
@@ -52,21 +52,21 @@
#define OPENGLRENDERER_H
#include <qsgrendernode.h>
+#include <QQuickItem>
#if QT_CONFIG(opengl)
QT_BEGIN_NAMESPACE
-class QQuickItem;
class QOpenGLShaderProgram;
class QOpenGLBuffer;
QT_END_NAMESPACE
+//! [1]
class OpenGLRenderNode : public QSGRenderNode
{
public:
- OpenGLRenderNode(QQuickItem *item);
~OpenGLRenderNode();
void render(const RenderState *state) override;
@@ -74,11 +74,15 @@ public:
StateFlags changedStates() const override;
RenderingFlags flags() const override;
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/rendernode.pro b/examples/quick/scenegraph/rendernode/rendernode.pro
index 76e498042b..897b0b1f08 100644
--- a/examples/quick/scenegraph/rendernode/rendernode.pro
+++ b/examples/quick/scenegraph/rendernode/rendernode.pro
@@ -22,3 +22,9 @@ qtConfig(d3d12) {
SOURCES += d3d12renderer.cpp
LIBS += -ld3d12
}
+
+macos {
+ HEADERS += metalrenderer.h
+ SOURCES += metalrenderer.mm
+ LIBS += -framework Metal -framework AppKit
+}
diff --git a/examples/quick/scenegraph/rendernode/rendernode.qrc b/examples/quick/scenegraph/rendernode/rendernode.qrc
index 049adcf8a6..5907eab62c 100644
--- a/examples/quick/scenegraph/rendernode/rendernode.qrc
+++ b/examples/quick/scenegraph/rendernode/rendernode.qrc
@@ -3,5 +3,7 @@
<file>main.qml</file>
<file>shader_vert.cso</file>
<file>shader_frag.cso</file>
+ <file>metalshader.vert</file>
+ <file>metalshader.frag</file>
</qresource>
</RCC>
diff --git a/examples/quick/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