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 
52 QT_BEGIN_NAMESPACE
53 
54 #if QT_CONFIG(opengl)
55 static const bool qsg_sanity_check = qEnvironmentVariableIntValue("QSG_SANITY_CHECK");
56 #endif
57 
58 static QElapsedTimer frameTimer;
59 static qint64 preprocessTime;
60 static qint64 updatePassTime;
61 
qt_sg_envInt(const char * name,int defaultValue)62 int 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(name).toInt(&ok);
68     return ok ? value : defaultValue;
69 }
70 
clear(QSGAbstractRenderer::ClearMode mode) const71 void 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(bits);
79 #else
80     Q_UNUSED(mode)
81 #endif
82 }
83 
84 // Reactivate the color buffer after switching to the stencil.
reactivate() const85 void 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)
QSGBindableFboId(GLuint id)92 QSGBindableFboId::QSGBindableFboId(GLuint id)
93     : m_id(id)
94 {
95 }
96 
97 
bind() const98 void QSGBindableFboId::bind() const
99 {
100     QOpenGLContext::currentContext()->functions()->glBindFramebuffer(GL_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 
QSGRenderer(QSGRenderContext * context)131 QSGRenderer::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 
~QSGRenderer()151 QSGRenderer::~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 
nodeUpdater() const164 QSGNodeUpdater *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   */
setNodeUpdater(QSGNodeUpdater * updater)178 void QSGRenderer::setNodeUpdater(QSGNodeUpdater *updater)
179 {
180     if (m_node_updater)
181         delete m_node_updater;
182     m_node_updater = updater;
183 }
184 
isMirrored() const185 bool 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 
renderScene(uint fboId)192 void 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 
renderScene(const QSGBindable & bindable)220 void 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, &count);
254         GLint enabled;
255         for (int i=0; i<count; ++i) {
256             QOpenGLContext::currentContext()->functions()->glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled);
257             if (enabled) {
258                 qWarning("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 
nodeChanged(QSGNode * node,QSGNode::DirtyState state)292 void 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(node);
301         else
302             m_nodes_to_preprocess.remove(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 
preprocess()312 void 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(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(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 
addNodesToPreprocess(QSGNode * node)363 void QSGRenderer::addNodesToPreprocess(QSGNode *node)
364 {
365     for (QSGNode *c = node->firstChild(); c; c = c->nextSibling())
366         addNodesToPreprocess(c);
367     if (node->flags() & QSGNode::UsePreprocess)
368         m_nodes_to_preprocess.insert(node);
369 }
370 
removeNodesToPreprocess(QSGNode * node)371 void QSGRenderer::removeNodesToPreprocess(QSGNode *node)
372 {
373     for (QSGNode *c = node->firstChild(); c; c = c->nextSibling())
374         removeNodesToPreprocess(c);
375     if (node->flags() & QSGNode::UsePreprocess) {
376         m_nodes_to_preprocess.remove(node);
377 
378         // If preprocessing *now*, mark the node as gone.
379         if (m_is_preprocessing)
380             m_nodes_dont_preprocess.insert(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 
dump(QSGNode * n)394 void QSGNodeDumper::dump(QSGNode *n)
395 {
396     QSGNodeDumper dump;
397     dump.visitNode(n);
398 }
399 
visitNode(QSGNode * n)400 void QSGNodeDumper::visitNode(QSGNode *n)
401 {
402     qDebug() << QByteArray(m_indent * 2, ' ').constData() << n;
403     QSGNodeVisitor::visitNode(n);
404 }
405 
visitChildren(QSGNode * n)406 void QSGNodeDumper::visitChildren(QSGNode *n)
407 {
408     ++m_indent;
409     QSGNodeVisitor::visitChildren(n);
410     --m_indent;
411 }
412 
413 
414 QT_END_NAMESPACE
415