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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtdeclarative/src/quick/scenegraph/coreapi/qsgrenderer.cpp