1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qsgrenderer_p.h"
41#include "qsgnodeupdater_p.h"
42#if QT_CONFIG(opengl)
43# include <QtGui/QOpenGLFramebufferObject>
44# include <QtGui/QOpenGLContext>
45# include <QtGui/QOpenGLFunctions>
46#endif
47#include <private/qquickprofiler_p.h>
48#include <qtquick_tracepoints_p.h>
49
50#include <QtCore/QElapsedTimer>
51
52QT_BEGIN_NAMESPACE
53
54#if QT_CONFIG(opengl)
55static const bool qsg_sanity_check = qEnvironmentVariableIntValue(varName: "QSG_SANITY_CHECK");
56#endif
57
58static QElapsedTimer frameTimer;
59static qint64 preprocessTime;
60static qint64 updatePassTime;
61
62int qt_sg_envInt(const char *name, int defaultValue)
63{
64 if (Q_LIKELY(!qEnvironmentVariableIsSet(name)))
65 return defaultValue;
66 bool ok = false;
67 int value = qgetenv(varName: name).toInt(ok: &ok);
68 return ok ? value : defaultValue;
69}
70
71void QSGBindable::clear(QSGAbstractRenderer::ClearMode mode) const
72{
73#if QT_CONFIG(opengl)
74 GLuint bits = 0;
75 if (mode & QSGAbstractRenderer::ClearColorBuffer) bits |= GL_COLOR_BUFFER_BIT;
76 if (mode & QSGAbstractRenderer::ClearDepthBuffer) bits |= GL_DEPTH_BUFFER_BIT;
77 if (mode & QSGAbstractRenderer::ClearStencilBuffer) bits |= GL_STENCIL_BUFFER_BIT;
78 QOpenGLContext::currentContext()->functions()->glClear(mask: bits);
79#else
80 Q_UNUSED(mode)
81#endif
82}
83
84// Reactivate the color buffer after switching to the stencil.
85void QSGBindable::reactivate() const
86{
87#if QT_CONFIG(opengl)
88 QOpenGLContext::currentContext()->functions()->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
89#endif
90}
91#if QT_CONFIG(opengl)
92QSGBindableFboId::QSGBindableFboId(GLuint id)
93 : m_id(id)
94{
95}
96
97
98void QSGBindableFboId::bind() const
99{
100 QOpenGLContext::currentContext()->functions()->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: m_id);
101}
102#endif
103/*!
104 \class QSGRenderer
105 \brief The renderer class is the abstract baseclass used for rendering the
106 QML scene graph.
107
108 The renderer is not tied to any particular surface. It expects a context to
109 be current and will render into that surface according to how the device rect,
110 viewport rect and projection transformation are set up.
111
112 Rendering is a sequence of steps initiated by calling renderScene(). This will
113 effectively draw the scene graph starting at the root node. The QSGNode::preprocess()
114 function will be called for all the nodes in the graph, followed by an update
115 pass which updates all matrices, opacity, clip states and similar in the graph.
116 Because the update pass is called after preprocess, it is safe to modify the graph
117 during preprocess. To run a custom update pass over the graph, install a custom
118 QSGNodeUpdater using setNodeUpdater(). Once all the graphs dirty states are updated,
119 the virtual render() function is called.
120
121 The render() function is implemented by QSGRenderer subclasses to render the graph
122 in the most optimal way for a given hardware.
123
124 The renderer can make use of stencil, depth and color buffers in addition to the
125 scissor rect.
126
127 \internal
128 */
129
130
131QSGRenderer::QSGRenderer(QSGRenderContext *context)
132 : m_current_opacity(1)
133 , m_current_determinant(1)
134 , m_device_pixel_ratio(1)
135 , m_context(context)
136 , m_current_uniform_data(nullptr)
137 , m_current_resource_update_batch(nullptr)
138 , m_rhi(nullptr)
139 , m_rt(nullptr)
140 , m_cb(nullptr)
141 , m_rp_desc(nullptr)
142 , m_node_updater(nullptr)
143 , m_bindable(nullptr)
144 , m_changed_emitted(false)
145 , m_is_rendering(false)
146 , m_is_preprocessing(false)
147{
148}
149
150
151QSGRenderer::~QSGRenderer()
152{
153 setRootNode(nullptr);
154 delete m_node_updater;
155}
156
157/*!
158 Returns the node updater that this renderer uses to update states in the
159 scene graph.
160
161 If no updater is specified a default one is constructed.
162 */
163
164QSGNodeUpdater *QSGRenderer::nodeUpdater() const
165{
166 if (!m_node_updater)
167 const_cast<QSGRenderer *>(this)->m_node_updater = new QSGNodeUpdater();
168 return m_node_updater;
169}
170
171
172/*!
173 Sets the node updater that this renderer uses to update states in the
174 scene graph.
175
176 This will delete and override any existing node updater
177 */
178void QSGRenderer::setNodeUpdater(QSGNodeUpdater *updater)
179{
180 if (m_node_updater)
181 delete m_node_updater;
182 m_node_updater = updater;
183}
184
185bool QSGRenderer::isMirrored() const
186{
187 QMatrix4x4 matrix = projectionMatrix();
188 // Mirrored relative to the usual Qt coordinate system with origin in the top left corner.
189 return matrix(0, 0) * matrix(1, 1) - matrix(0, 1) * matrix(1, 0) > 0;
190}
191
192void QSGRenderer::renderScene(uint fboId)
193{
194 if (m_rt) {
195 class B : public QSGBindable
196 {
197 public:
198 void bind() const override { }
199 } bindable;
200 renderScene(bindable);
201 } else {
202#if QT_CONFIG(opengl)
203 if (fboId) {
204 QSGBindableFboId bindable(fboId);
205 renderScene(bindable);
206 } else {
207 class B : public QSGBindable
208 {
209 public:
210 void bind() const override { QOpenGLFramebufferObject::bindDefault(); }
211 } bindable;
212 renderScene(bindable);
213 }
214#else
215 Q_UNUSED(fboId)
216#endif
217 }
218}
219
220void QSGRenderer::renderScene(const QSGBindable &bindable)
221{
222 if (!rootNode())
223 return;
224
225 Q_TRACE_SCOPE(QSG_renderScene);
226 m_is_rendering = true;
227
228
229 bool profileFrames = QSG_LOG_TIME_RENDERER().isDebugEnabled();
230 if (profileFrames)
231 frameTimer.start();
232 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRendererFrame);
233
234 qint64 bindTime = 0;
235 qint64 renderTime = 0;
236
237 m_bindable = &bindable;
238 preprocess();
239
240 Q_TRACE(QSG_binding_entry);
241 bindable.bind();
242 if (profileFrames)
243 bindTime = frameTimer.nsecsElapsed();
244 Q_TRACE(QSG_binding_exit);
245 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRendererFrame,
246 QQuickProfiler::SceneGraphRendererBinding);
247 Q_TRACE(QSG_render_entry);
248
249#if QT_CONFIG(opengl)
250 // Sanity check that attribute registers are disabled
251 if (qsg_sanity_check) {
252 GLint count = 0;
253 QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, params: &count);
254 GLint enabled;
255 for (int i=0; i<count; ++i) {
256 QOpenGLContext::currentContext()->functions()->glGetVertexAttribiv(index: i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, params: &enabled);
257 if (enabled) {
258 qWarning(msg: "QSGRenderer: attribute %d is enabled, this can lead to memory corruption and crashes.", i);
259 }
260 }
261 }
262#endif
263
264 render();
265 if (profileFrames)
266 renderTime = frameTimer.nsecsElapsed();
267 Q_TRACE(QSG_render_exit);
268 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRendererFrame,
269 QQuickProfiler::SceneGraphRendererRender);
270
271 m_is_rendering = false;
272 m_changed_emitted = false;
273 m_bindable = nullptr;
274
275 qCDebug(QSG_LOG_TIME_RENDERER,
276 "time in renderer: total=%dms, preprocess=%d, updates=%d, binding=%d, rendering=%d",
277 int(renderTime / 1000000),
278 int(preprocessTime / 1000000),
279 int((updatePassTime - preprocessTime) / 1000000),
280 int((bindTime - updatePassTime) / 1000000),
281 int((renderTime - bindTime) / 1000000));
282}
283
284/*!
285 Updates internal data structures and emits the sceneGraphChanged() signal.
286
287 If \a flags contains the QSGNode::DirtyNodeRemoved flag, the node might be
288 in the process of being destroyed. It is then not safe to downcast the node
289 pointer.
290*/
291
292void QSGRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
293{
294 if (state & QSGNode::DirtyNodeAdded)
295 addNodesToPreprocess(node);
296 if (state & QSGNode::DirtyNodeRemoved)
297 removeNodesToPreprocess(node);
298 if (state & QSGNode::DirtyUsePreprocess) {
299 if (node->flags() & QSGNode::UsePreprocess)
300 m_nodes_to_preprocess.insert(value: node);
301 else
302 m_nodes_to_preprocess.remove(value: node);
303 }
304
305 if (!m_changed_emitted && !m_is_rendering) {
306 // Premature overoptimization to avoid excessive signal emissions
307 m_changed_emitted = true;
308 emit sceneGraphChanged();
309 }
310}
311
312void QSGRenderer::preprocess()
313{
314 Q_TRACE(QSG_preprocess_entry);
315
316 m_is_preprocessing = true;
317
318 QSGRootNode *root = rootNode();
319 Q_ASSERT(root);
320
321 // We need to take a copy here, in case any of the preprocess calls deletes a node that
322 // is in the preprocess list and thus, changes the m_nodes_to_preprocess behind our backs
323 // For the default case, when this does not happen, the cost is negligible.
324 QSet<QSGNode *> items = m_nodes_to_preprocess;
325
326 m_context->preprocess();
327
328 for (QSet<QSGNode *>::const_iterator it = items.constBegin();
329 it != items.constEnd(); ++it) {
330 QSGNode *n = *it;
331
332 // If we are currently preprocessing, check this node hasn't been
333 // deleted or something. we don't want a use-after-free!
334 if (m_nodes_dont_preprocess.contains(value: n)) // skip
335 continue;
336 if (!nodeUpdater()->isNodeBlocked(n, root)) {
337 n->preprocess();
338 }
339 }
340
341 bool profileFrames = QSG_LOG_TIME_RENDERER().isDebugEnabled();
342 if (profileFrames)
343 preprocessTime = frameTimer.nsecsElapsed();
344 Q_TRACE(QSG_preprocess_exit);
345 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRendererFrame,
346 QQuickProfiler::SceneGraphRendererPreprocess);
347 Q_TRACE(QSG_update_entry);
348
349 nodeUpdater()->updateStates(n: root);
350
351 if (profileFrames)
352 updatePassTime = frameTimer.nsecsElapsed();
353 Q_TRACE(QSG_update_exit);
354 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRendererFrame,
355 QQuickProfiler::SceneGraphRendererUpdate);
356
357 m_is_preprocessing = false;
358 m_nodes_dont_preprocess.clear();
359}
360
361
362
363void QSGRenderer::addNodesToPreprocess(QSGNode *node)
364{
365 for (QSGNode *c = node->firstChild(); c; c = c->nextSibling())
366 addNodesToPreprocess(node: c);
367 if (node->flags() & QSGNode::UsePreprocess)
368 m_nodes_to_preprocess.insert(value: node);
369}
370
371void QSGRenderer::removeNodesToPreprocess(QSGNode *node)
372{
373 for (QSGNode *c = node->firstChild(); c; c = c->nextSibling())
374 removeNodesToPreprocess(node: c);
375 if (node->flags() & QSGNode::UsePreprocess) {
376 m_nodes_to_preprocess.remove(value: node);
377
378 // If preprocessing *now*, mark the node as gone.
379 if (m_is_preprocessing)
380 m_nodes_dont_preprocess.insert(value: node);
381 }
382}
383
384
385/*!
386 \class QSGNodeDumper
387 \brief The QSGNodeDumper class provides a way of dumping a scene grahp to the console.
388
389 This class is solely for debugging purposes.
390
391 \internal
392 */
393
394void QSGNodeDumper::dump(QSGNode *n)
395{
396 QSGNodeDumper dump;
397 dump.visitNode(n);
398}
399
400void QSGNodeDumper::visitNode(QSGNode *n)
401{
402 qDebug() << QByteArray(m_indent * 2, ' ').constData() << n;
403 QSGNodeVisitor::visitNode(n);
404}
405
406void QSGNodeDumper::visitChildren(QSGNode *n)
407{
408 ++m_indent;
409 QSGNodeVisitor::visitChildren(n);
410 --m_indent;
411}
412
413
414QT_END_NAMESPACE
415

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