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 "qsgnodeupdater_p.h"
41#include "qsgnode.h"
42#include "qsgrendernode_p.h"
43
44QT_BEGIN_NAMESPACE
45
46// #define QSG_UPDATER_DEBUG
47
48QSGNodeUpdater::QSGNodeUpdater()
49 : m_combined_matrix_stack(64)
50 , m_opacity_stack(64)
51 , m_current_clip(nullptr)
52 , m_force_update(0)
53{
54 m_opacity_stack.add(1);
55}
56
57QSGNodeUpdater::~QSGNodeUpdater()
58{
59}
60
61void QSGNodeUpdater::updateStates(QSGNode *n)
62{
63 m_current_clip = nullptr;
64 m_force_update = 0;
65
66 Q_ASSERT(m_opacity_stack.size() == 1); // The one we added in the constructr...
67 Q_ASSERT(m_combined_matrix_stack.isEmpty());
68
69 visitNode(n);
70}
71
72
73/*!
74 Returns true if \a node is has something that blocks it in the chain from
75 \a node to \a root doing a full state update pass.
76
77 This function does not process dirty states, simply does a simple traversion
78 up to the top.
79
80 The function assumes that \a root exists in the parent chain of \a node.
81 */
82
83bool QSGNodeUpdater::isNodeBlocked(QSGNode *node, QSGNode *root) const
84{
85 while (node != root && node != nullptr) {
86 if (node->isSubtreeBlocked())
87 return true;
88 node = node->parent();
89 }
90 return false;
91}
92
93
94void QSGNodeUpdater::enterTransformNode(QSGTransformNode *t)
95{
96#ifdef QSG_UPDATER_DEBUG
97 qDebug() << "enter transform:" << t << "force=" << m_force_update;
98#endif
99
100 if (!t->matrix().isIdentity()) {
101 if (!m_combined_matrix_stack.isEmpty()) {
102 t->setCombinedMatrix(*m_combined_matrix_stack.last() * t->matrix());
103 } else {
104 t->setCombinedMatrix(t->matrix());
105 }
106 m_combined_matrix_stack.add(&t->combinedMatrix());
107 } else {
108 if (!m_combined_matrix_stack.isEmpty()) {
109 t->setCombinedMatrix(*m_combined_matrix_stack.last());
110 } else {
111 t->setCombinedMatrix(QMatrix4x4());
112 }
113 }
114}
115
116
117void QSGNodeUpdater::leaveTransformNode(QSGTransformNode *t)
118{
119#ifdef QSG_UPDATER_DEBUG
120 qDebug() << "leave transform:" << t;
121#endif
122
123 if (!t->matrix().isIdentity()) {
124 m_combined_matrix_stack.pop_back();
125 }
126
127}
128
129
130void QSGNodeUpdater::enterClipNode(QSGClipNode *c)
131{
132#ifdef QSG_UPDATER_DEBUG
133 qDebug() << "enter clip:" << c;
134#endif
135
136 c->m_matrix = m_combined_matrix_stack.isEmpty() ? 0 : m_combined_matrix_stack.last();
137 c->m_clip_list = m_current_clip;
138 m_current_clip = c;
139}
140
141
142void QSGNodeUpdater::leaveClipNode(QSGClipNode *c)
143{
144#ifdef QSG_UPDATER_DEBUG
145 qDebug() << "leave clip:" << c;
146#endif
147
148 m_current_clip = c->m_clip_list;
149}
150
151
152void QSGNodeUpdater::enterGeometryNode(QSGGeometryNode *g)
153{
154#ifdef QSG_UPDATER_DEBUG
155 qDebug() << "enter geometry:" << g;
156#endif
157
158 g->m_matrix = m_combined_matrix_stack.isEmpty() ? 0 : m_combined_matrix_stack.last();
159 g->m_clip_list = m_current_clip;
160 g->setInheritedOpacity(m_opacity_stack.last());
161}
162
163void QSGNodeUpdater::leaveGeometryNode(QSGGeometryNode *g)
164{
165#ifdef QSG_UPDATER_DEBUG
166 qDebug() << "leave geometry" << g;
167#else
168 Q_UNUSED(g)
169#endif
170}
171
172void QSGNodeUpdater::enterRenderNode(QSGRenderNode *r)
173{
174#ifdef QSG_UPDATER_DEBUG
175 qDebug() << "enter render:" << r;
176#endif
177
178 QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(r);
179 rd->m_matrix = m_combined_matrix_stack.isEmpty() ? 0 : m_combined_matrix_stack.last();
180 rd->m_clip_list = m_current_clip;
181 rd->m_opacity = m_opacity_stack.last();
182}
183
184void QSGNodeUpdater::leaveRenderNode(QSGRenderNode *r)
185{
186#ifdef QSG_UPDATER_DEBUG
187 qDebug() << "leave render" << r;
188#else
189 Q_UNUSED(r)
190#endif
191}
192
193void QSGNodeUpdater::enterOpacityNode(QSGOpacityNode *o)
194{
195 qreal opacity = m_opacity_stack.last() * o->opacity();
196 o->setCombinedOpacity(opacity);
197 m_opacity_stack.add(opacity);
198
199#ifdef QSG_UPDATER_DEBUG
200 qDebug() << "enter opacity" << o;
201#endif
202}
203
204void QSGNodeUpdater::leaveOpacityNode(QSGOpacityNode *o)
205{
206#ifdef QSG_UPDATER_DEBUG
207 qDebug() << "leave opacity" << o;
208#endif
209 if (o->flags() & QSGNode::DirtyOpacity)
210 --m_force_update;
211
212 m_opacity_stack.pop_back();
213}
214
215void QSGNodeUpdater::visitChildren(QSGNode *n)
216{
217 for (QSGNode *c = n->firstChild(); c; c = c->nextSibling())
218 visitNode(c);
219}
220
221void QSGNodeUpdater::visitNode(QSGNode *n)
222{
223#ifdef QSG_UPDATER_DEBUG
224 qDebug() << "enter:" << n;
225#endif
226
227 if (!m_force_update)
228 return;
229 if (n->isSubtreeBlocked())
230 return;
231
232 switch (n->type()) {
233 case QSGNode::TransformNodeType: {
234 QSGTransformNode *t = static_cast<QSGTransformNode *>(n);
235 enterTransformNode(t);
236 visitChildren(t);
237 leaveTransformNode(t);
238 break; }
239 case QSGNode::GeometryNodeType: {
240 QSGGeometryNode *g = static_cast<QSGGeometryNode *>(n);
241 enterGeometryNode(g);
242 visitChildren(g);
243 leaveGeometryNode(g);
244 break; }
245 case QSGNode::RenderNodeType: {
246 QSGRenderNode *r = static_cast<QSGRenderNode *>(n);
247 enterRenderNode(r);
248 visitChildren(r);
249 leaveRenderNode(r);
250 break; }
251 case QSGNode::ClipNodeType: {
252 QSGClipNode *c = static_cast<QSGClipNode *>(n);
253 enterClipNode(c);
254 visitChildren(c);
255 leaveClipNode(c);
256 break; }
257 case QSGNode::OpacityNodeType: {
258 QSGOpacityNode *o = static_cast<QSGOpacityNode *>(n);
259 enterOpacityNode(o);
260 visitChildren(o);
261 leaveOpacityNode(o);
262 break; }
263 default:
264 visitChildren(n);
265 break;
266 }
267}
268
269QT_END_NAMESPACE
270