Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsgrenderer.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
7#include <private/qquickprofiler_p.h>
8#include <qtquick_tracepoints_p.h>
9
10#include <QtCore/QElapsedTimer>
11
13
17
18Q_TRACE_POINT(qtquick, QSG_preprocess_entry)
19Q_TRACE_POINT(qtquick, QSG_preprocess_exit)
20Q_TRACE_POINT(qtquick, QSG_update_entry)
21Q_TRACE_POINT(qtquick, QSG_update_exit)
22Q_TRACE_POINT(qtquick, QSG_renderScene_entry)
23Q_TRACE_POINT(qtquick, QSG_renderScene_exit)
24
25int qt_sg_envInt(const char *name, int defaultValue)
26{
27 if (Q_LIKELY(!qEnvironmentVariableIsSet(name)))
28 return defaultValue;
29 bool ok = false;
30 int value = qEnvironmentVariableIntValue(name, &ok);
31 return ok ? value : defaultValue;
32}
33
34/*!
35 \class QSGRenderer
36 \brief The renderer class is the abstract baseclass used for rendering the
37 QML scene graph.
38
39 The renderer is not tied to any particular surface. It expects a context to
40 be current and will render into that surface according to how the device rect,
41 viewport rect and projection transformation are set up.
42
43 Rendering is a sequence of steps initiated by calling renderScene(). This will
44 effectively draw the scene graph starting at the root node. The QSGNode::preprocess()
45 function will be called for all the nodes in the graph, followed by an update
46 pass which updates all matrices, opacity, clip states and similar in the graph.
47 Because the update pass is called after preprocess, it is safe to modify the graph
48 during preprocess. To run a custom update pass over the graph, install a custom
49 QSGNodeUpdater using setNodeUpdater(). Once all the graphs dirty states are updated,
50 the virtual render() function is called.
51
52 The render() function is implemented by QSGRenderer subclasses to render the graph
53 in the most optimal way for a given hardware.
54
55 The renderer can make use of stencil, depth and color buffers in addition to the
56 scissor rect.
57
58 \internal
59 */
60
61
62QSGRenderer::QSGRenderer(QSGRenderContext *context)
63 : m_current_opacity(1)
64 , m_current_determinant(1)
65 , m_device_pixel_ratio(1)
66 , m_context(context)
67 , m_current_uniform_data(nullptr)
68 , m_current_resource_update_batch(nullptr)
69 , m_rhi(nullptr)
70 , m_node_updater(nullptr)
71 , m_changed_emitted(false)
72 , m_is_rendering(false)
73 , m_is_preprocessing(false)
74{
75 m_current_projection_matrix.resize(1);
76 m_current_projection_matrix_native_ndc.resize(1);
77}
78
79
80QSGRenderer::~QSGRenderer()
81{
82 setRootNode(nullptr);
83 delete m_node_updater;
84}
85
86/*!
87 Returns the node updater that this renderer uses to update states in the
88 scene graph.
89
90 If no updater is specified a default one is constructed.
91 */
92
93QSGNodeUpdater *QSGRenderer::nodeUpdater() const
94{
95 if (!m_node_updater)
96 const_cast<QSGRenderer *>(this)->m_node_updater = new QSGNodeUpdater();
97 return m_node_updater;
98}
99
100
101/*!
102 Sets the node updater that this renderer uses to update states in the
103 scene graph.
104
105 This will delete and override any existing node updater
106 */
107void QSGRenderer::setNodeUpdater(QSGNodeUpdater *updater)
108{
109 if (m_node_updater)
110 delete m_node_updater;
111 m_node_updater = updater;
112}
113
114bool QSGRenderer::isMirrored() const
115{
116 QMatrix4x4 matrix = projectionMatrix(0);
117 // Mirrored relative to the usual Qt coordinate system with origin in the top left corner.
118 return matrix(0, 0) * matrix(1, 1) - matrix(0, 1) * matrix(1, 0) > 0;
119}
120
121void QSGRenderer::renderScene()
122{
123 if (!rootNode())
124 return;
125
126 Q_TRACE_SCOPE(QSG_renderScene);
127 m_is_rendering = true;
128
129 bool profileFrames = QSG_LOG_TIME_RENDERER().isDebugEnabled();
130 if (profileFrames)
131 frameTimer.start();
132 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRendererFrame);
133
134 // The QML Profiler architecture is extremely fragile: we have to record a
135 // hardcoded number of data points for each event, otherwise the view will
136 // show weird things in Creator. So record a dummy Binding data point, even
137 // though it is meaningless for our purposes.
138 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRendererFrame,
139 QQuickProfiler::SceneGraphRendererBinding);
140
141 qint64 renderTime = 0;
142
143 preprocess();
144
145 Q_TRACE(QSG_render_entry);
146 render();
147 if (profileFrames)
148 renderTime = frameTimer.nsecsElapsed();
149 Q_TRACE(QSG_render_exit);
150 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRendererFrame,
151 QQuickProfiler::SceneGraphRendererRender);
152
153 m_is_rendering = false;
154 m_changed_emitted = false;
155
156 qCDebug(QSG_LOG_TIME_RENDERER,
157 "time in renderer: total=%dms, preprocess=%d, updates=%d, rendering=%d",
158 int(renderTime / 1000000),
159 int(preprocessTime / 1000000),
160 int((updatePassTime - preprocessTime) / 1000000),
161 int((renderTime - updatePassTime) / 1000000));
162}
163
164void QSGRenderer::prepareSceneInline()
165{
166 if (!rootNode())
167 return;
168
169 Q_ASSERT(!m_is_rendering);
170 m_is_rendering = true;
171
172 preprocess();
173
174 prepareInline();
175}
176
177void QSGRenderer::renderSceneInline()
178{
179 Q_ASSERT(m_is_rendering);
180
181 renderInline();
182
183 m_is_rendering = false;
184 m_changed_emitted = false;
185}
186
187/*!
188 Updates internal data structures and emits the sceneGraphChanged() signal.
189
190 If \a flags contains the QSGNode::DirtyNodeRemoved flag, the node might be
191 in the process of being destroyed. It is then not safe to downcast the node
192 pointer.
193*/
194
195void QSGRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
196{
197 if (state & QSGNode::DirtyNodeAdded)
198 addNodesToPreprocess(node);
199 if (state & QSGNode::DirtyNodeRemoved)
200 removeNodesToPreprocess(node);
201 if (state & QSGNode::DirtyUsePreprocess) {
202 if (node->flags() & QSGNode::UsePreprocess)
203 m_nodes_to_preprocess.insert(node);
204 else
205 m_nodes_to_preprocess.remove(node);
206 }
207
208 if (!m_changed_emitted && !m_is_rendering) {
209 // Premature overoptimization to avoid excessive signal emissions
210 m_changed_emitted = true;
211 emit sceneGraphChanged();
212 }
213}
214
215void QSGRenderer::preprocess()
216{
217 Q_TRACE(QSG_preprocess_entry);
218
219 m_is_preprocessing = true;
220
221 QSGRootNode *root = rootNode();
222 Q_ASSERT(root);
223
224 // We need to take a copy here, in case any of the preprocess calls deletes a node that
225 // is in the preprocess list and thus, changes the m_nodes_to_preprocess behind our backs
226 // For the default case, when this does not happen, the cost is negligible.
227 QSet<QSGNode *> items = m_nodes_to_preprocess;
228
229 m_context->preprocess();
230
231 for (QSet<QSGNode *>::const_iterator it = items.constBegin();
232 it != items.constEnd(); ++it) {
233 QSGNode *n = *it;
234
235 // If we are currently preprocessing, check this node hasn't been
236 // deleted or something. we don't want a use-after-free!
237 if (m_nodes_dont_preprocess.contains(n)) // skip
238 continue;
239 if (!nodeUpdater()->isNodeBlocked(n, root)) {
240 n->preprocess();
241 }
242 }
243
244 bool profileFrames = QSG_LOG_TIME_RENDERER().isDebugEnabled();
245 if (profileFrames)
246 preprocessTime = frameTimer.nsecsElapsed();
247 Q_TRACE(QSG_preprocess_exit);
248 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRendererFrame,
249 QQuickProfiler::SceneGraphRendererPreprocess);
250 Q_TRACE(QSG_update_entry);
251
252 nodeUpdater()->updateStates(root);
253
254 if (profileFrames)
255 updatePassTime = frameTimer.nsecsElapsed();
256 Q_TRACE(QSG_update_exit);
257 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRendererFrame,
258 QQuickProfiler::SceneGraphRendererUpdate);
259
260 m_is_preprocessing = false;
261 m_nodes_dont_preprocess.clear();
262}
263
264
265
266void QSGRenderer::addNodesToPreprocess(QSGNode *node)
267{
268 for (QSGNode *c = node->firstChild(); c; c = c->nextSibling())
269 addNodesToPreprocess(c);
270 if (node->flags() & QSGNode::UsePreprocess)
271 m_nodes_to_preprocess.insert(node);
272}
273
274void QSGRenderer::removeNodesToPreprocess(QSGNode *node)
275{
276 for (QSGNode *c = node->firstChild(); c; c = c->nextSibling())
277 removeNodesToPreprocess(c);
278 if (node->flags() & QSGNode::UsePreprocess) {
279 m_nodes_to_preprocess.remove(node);
280
281 // If preprocessing *now*, mark the node as gone.
282 if (m_is_preprocessing)
283 m_nodes_dont_preprocess.insert(node);
284 }
285}
286
287void QSGRenderer::prepareInline()
288{
289}
290
291void QSGRenderer::renderInline()
292{
293}
294
295
296/*!
297 \class QSGNodeDumper
298 \brief The QSGNodeDumper class provides a way of dumping a scene grahp to the console.
299
300 This class is solely for debugging purposes.
301
302 \internal
303 */
304
305void QSGNodeDumper::dump(QSGNode *n)
306{
307 QSGNodeDumper dump;
308 dump.visitNode(n);
309}
310
311void QSGNodeDumper::visitNode(QSGNode *n)
312{
313 qDebug() << QByteArray(m_indent * 2, ' ').constData() << n;
314 QSGNodeVisitor::visitNode(n);
315}
316
317void QSGNodeDumper::visitChildren(QSGNode *n)
318{
319 ++m_indent;
320 QSGNodeVisitor::visitChildren(n);
321 --m_indent;
322}
323
324
325QT_END_NAMESPACE
Combined button and popup list for selecting options.
static QT_BEGIN_NAMESPACE QElapsedTimer frameTimer
static qint64 preprocessTime
static qint64 updatePassTime