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

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