aboutsummaryrefslogtreecommitdiffstats
path: root/examples/quick/scenegraph/rendernode/doc/src/rendernode.qdoc
blob: 87e3c8b5064dc78970137827eaf0e1595155bd70 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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/customrenderitem.h 0

    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.

*/