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