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 "qsgnode.h" |
41 | #include "qsgnode_p.h" |
42 | #include "qsgrenderer_p.h" |
43 | #include "qsgnodeupdater_p.h" |
44 | #include "qsgmaterial.h" |
45 | |
46 | #include "limits.h" |
47 | |
48 | QT_BEGIN_NAMESPACE |
49 | |
50 | #ifndef QT_NO_DEBUG |
51 | static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK" ); |
52 | static int qt_node_count = 0; |
53 | |
54 | static void qt_print_node_count() |
55 | { |
56 | qDebug("Number of leaked nodes: %i" , qt_node_count); |
57 | qt_node_count = -1; |
58 | } |
59 | #endif |
60 | |
61 | /*! |
62 | \group qtquick-scenegraph-nodes |
63 | \title Qt Quick Scene Graph Node classes |
64 | \brief Nodes that can be used as part of the scene graph. |
65 | |
66 | This page lists the nodes in \l {Qt Quick}'s \l {scene graph}{Qt Quick Scene Graph}. |
67 | */ |
68 | |
69 | /*! |
70 | \class QSGNode |
71 | \brief The QSGNode class is the base class for all nodes in the scene graph. |
72 | |
73 | \inmodule QtQuick |
74 | \ingroup qtquick-scenegraph-nodes |
75 | |
76 | The QSGNode class can be used as a child container. Children are added with |
77 | the appendChildNode(), prependChildNode(), insertChildNodeBefore() and |
78 | insertChildNodeAfter(). The order of nodes is important as geometry nodes |
79 | are rendered according to their ordering in the scene graph. |
80 | |
81 | The scene graph nodes contains a mechanism to describe which |
82 | parts of the scene has changed. This includes the combined matrices, |
83 | accumulated opacity, changes to the node hierarchy, and so on. This |
84 | information can be used for optimizations inside the scene graph renderer. |
85 | For the renderer to properly render the nodes, it is important that users |
86 | call QSGNode::markDirty() with the correct flags when nodes are changed. |
87 | Most of the functions on the node classes will implicitly call markDirty(). |
88 | For example, QSGNode::appendChildNode() will call markDirty() passing in |
89 | QSGNode::DirtyNodeAdded. |
90 | |
91 | If nodes change every frame, the preprocess() function can be used to |
92 | apply changes to a node for every frame it is rendered. The use of |
93 | preprocess() must be explicitly enabled by setting the |
94 | QSGNode::UsePreprocess flag on the node. |
95 | |
96 | The virtual isSubtreeBlocked() function can be used to disable a subtree all |
97 | together. Nodes in a blocked subtree will not be preprocessed() and not |
98 | rendered. |
99 | |
100 | \note All classes with QSG prefix should be used solely on the scene graph's |
101 | rendering thread. See \l {Scene Graph and Rendering} for more information. |
102 | */ |
103 | |
104 | /*! |
105 | \enum QSGNode::DirtyStateBit |
106 | |
107 | Used in QSGNode::markDirty() to indicate how the scene graph has changed. |
108 | |
109 | \value DirtyMatrix The matrix in a QSGTransformNode has changed. |
110 | \value DirtyNodeAdded A node was added. |
111 | \value DirtyNodeRemoved A node was removed. |
112 | \value DirtyGeometry The geometry of a QSGGeometryNode has changed. |
113 | \value DirtyMaterial The material of a QSGGeometryNode has changed. |
114 | \value DirtyOpacity The opacity of a QSGOpacityNode has changed. |
115 | \value DirtySubtreeBlocked The subtree has been blocked. |
116 | |
117 | \omitvalue DirtyForceUpdate |
118 | \omitvalue DirtyUsePreprocess |
119 | \omitvalue DirtyPropagationMask |
120 | |
121 | \sa QSGNode::markDirty() |
122 | */ |
123 | |
124 | /*! |
125 | \enum QSGNode::Flag |
126 | |
127 | The QSGNode::Flag enum describes flags on the QSGNode |
128 | |
129 | \value OwnedByParent The node is owned by its parent and will be deleted |
130 | when the parent is deleted. |
131 | \value UsePreprocess The node's virtual preprocess() function will be called |
132 | before rendering starts. |
133 | \value OwnsGeometry Only valid for QSGGeometryNode and QSGClipNode. |
134 | The node has ownership over the QSGGeometry instance and will |
135 | delete it when the node is destroyed or a geometry is assigned. |
136 | \value OwnsMaterial Only valid for QSGGeometryNode. The node has ownership |
137 | over the material and will delete it when the node is destroyed or a material is assigned. |
138 | \value OwnsOpaqueMaterial Only valid for QSGGeometryNode. The node has |
139 | ownership over the opaque material and will delete it when the node is |
140 | destroyed or a material is assigned. |
141 | \value InternalReserved Reserved for internal use. |
142 | |
143 | \omitvalue IsVisitableNode |
144 | */ |
145 | |
146 | /*! |
147 | \enum QSGNode::NodeType |
148 | |
149 | Can be used to figure out the type of node. |
150 | |
151 | \value BasicNodeType The type of QSGNode |
152 | \value GeometryNodeType The type of QSGGeometryNode |
153 | \value TransformNodeType The type of QSGTransformNode |
154 | \value ClipNodeType The type of QSGClipNode |
155 | \value OpacityNodeType The type of QSGOpacityNode |
156 | \value RenderNodeType The type of QSGRenderNode |
157 | |
158 | \omitvalue RootNodeType |
159 | |
160 | \sa type() |
161 | */ |
162 | |
163 | /*! |
164 | \fn QSGNode *QSGNode::childAtIndex(int i) const |
165 | |
166 | Returns the child at index \a i. |
167 | |
168 | Children are stored internally as a linked list, so iterating |
169 | over the children via the index is suboptimal. |
170 | */ |
171 | |
172 | /*! |
173 | \fn int QSGNode::childCount() const |
174 | |
175 | Returns the number of child nodes. |
176 | */ |
177 | |
178 | /*! |
179 | \fn void QSGNode::clearDirty() |
180 | |
181 | \internal |
182 | */ |
183 | |
184 | /*! |
185 | \fn QSGNode *QSGNode::firstChild() const |
186 | |
187 | Returns the first child of this node. |
188 | |
189 | The children are stored in a linked list. |
190 | */ |
191 | |
192 | /*! |
193 | \fn QSGNode *QSGNode::lastChild() const |
194 | |
195 | Returns the last child of this node. |
196 | |
197 | The children are stored as a linked list. |
198 | */ |
199 | |
200 | /*! |
201 | \fn QSGNode::Flags QSGNode::flags() const |
202 | |
203 | Returns the set of flags for this node. |
204 | */ |
205 | |
206 | /*! |
207 | \fn QSGNode *QSGNode::nextSibling() const |
208 | |
209 | Returns the node after this in the parent's list of children. |
210 | |
211 | The children are stored as a linked list. |
212 | */ |
213 | |
214 | /*! |
215 | \fn QSGNode *QSGNode::previousSibling() const |
216 | |
217 | Returns the node before this in the parent's list of children. |
218 | |
219 | The children are stored as a linked list. |
220 | */ |
221 | |
222 | /*! |
223 | \fn QSGNode::Type QSGNode::type() const |
224 | |
225 | Returns the type of this node. The node type must be one of the |
226 | predefined types defined in QSGNode::NodeType and can safely be |
227 | used to cast to the corresponding class. |
228 | */ |
229 | |
230 | /*! |
231 | \fn QSGNode::DirtyState QSGNode::dirtyState() const |
232 | |
233 | \internal |
234 | */ |
235 | |
236 | /*! |
237 | \fn QSGNode *QSGNode::parent() const |
238 | |
239 | Returns the parent node of this node. |
240 | */ |
241 | |
242 | |
243 | /*! |
244 | * Constructs a new node |
245 | */ |
246 | QSGNode::QSGNode() |
247 | : m_nodeFlags(OwnedByParent) |
248 | , m_dirtyState(nullptr) |
249 | { |
250 | init(); |
251 | } |
252 | |
253 | /*! |
254 | * Constructs a new node with the given node type. |
255 | * |
256 | * \internal |
257 | */ |
258 | QSGNode::QSGNode(NodeType type) |
259 | : m_parent(nullptr) |
260 | , m_type(type) |
261 | , m_firstChild(nullptr) |
262 | , m_lastChild(nullptr) |
263 | , m_nextSibling(nullptr) |
264 | , m_previousSibling(nullptr) |
265 | , m_subtreeRenderableCount(type == GeometryNodeType || type == RenderNodeType ? 1 : 0) |
266 | , m_nodeFlags(OwnedByParent) |
267 | , m_dirtyState(nullptr) |
268 | { |
269 | init(); |
270 | } |
271 | |
272 | /*! |
273 | * Constructs a new node with the given node type. |
274 | * |
275 | * \internal |
276 | */ |
277 | QSGNode::QSGNode(QSGNodePrivate &dd, NodeType type) |
278 | : m_parent(nullptr) |
279 | , m_type(type) |
280 | , m_firstChild(nullptr) |
281 | , m_lastChild(nullptr) |
282 | , m_nextSibling(nullptr) |
283 | , m_previousSibling(nullptr) |
284 | , m_subtreeRenderableCount(type == GeometryNodeType || type == RenderNodeType ? 1 : 0) |
285 | , m_nodeFlags(OwnedByParent) |
286 | , m_dirtyState(nullptr) |
287 | , d_ptr(&dd) |
288 | { |
289 | init(); |
290 | } |
291 | |
292 | /*! |
293 | * \internal |
294 | */ |
295 | void QSGNode::init() |
296 | { |
297 | #ifndef QT_NO_DEBUG |
298 | if (qsg_leak_check) { |
299 | ++qt_node_count; |
300 | static bool atexit_registered = false; |
301 | if (!atexit_registered) { |
302 | atexit(qt_print_node_count); |
303 | atexit_registered = true; |
304 | } |
305 | } |
306 | #endif |
307 | |
308 | #ifdef QSG_RUNTIME_DESCRIPTION |
309 | if (d_ptr.isNull()) |
310 | d_ptr.reset(new QSGNodePrivate()); |
311 | #endif |
312 | } |
313 | |
314 | /*! |
315 | * Destroys the node. |
316 | * |
317 | * Every child of this node that has the flag QSGNode::OwnedByParent set, |
318 | * will also be deleted. |
319 | */ |
320 | QSGNode::~QSGNode() |
321 | { |
322 | #ifndef QT_NO_DEBUG |
323 | if (qsg_leak_check) { |
324 | --qt_node_count; |
325 | if (qt_node_count < 0) |
326 | qDebug("Node destroyed after qt_print_node_count() was called." ); |
327 | } |
328 | #endif |
329 | destroy(); |
330 | } |
331 | |
332 | |
333 | /*! |
334 | \fn void QSGNode::preprocess() |
335 | |
336 | Override this function to do processing on the node before it is rendered. |
337 | |
338 | Preprocessing needs to be explicitly enabled by setting the flag |
339 | QSGNode::UsePreprocess. The flag needs to be set before the node is added |
340 | to the scene graph and will cause the preprocess() function to be called |
341 | for every frame the node is rendered. |
342 | |
343 | \warning Beware of deleting nodes while they are being preprocessed. It is |
344 | possible, with a small performance hit, to delete a single node during its |
345 | own preprocess call. Deleting a subtree which has nodes that also use |
346 | preprocessing may result in a segmentation fault. This is done for |
347 | performance reasons. |
348 | */ |
349 | |
350 | |
351 | |
352 | |
353 | /*! |
354 | Returns whether this node and its subtree is available for use. |
355 | |
356 | Blocked subtrees will not get their dirty states updated and they |
357 | will not be rendered. |
358 | |
359 | The QSGOpacityNode will return a blocked subtree when accumulated opacity |
360 | is 0, for instance. |
361 | */ |
362 | |
363 | bool QSGNode::isSubtreeBlocked() const |
364 | { |
365 | return false; |
366 | } |
367 | |
368 | /*! |
369 | \internal |
370 | Detaches the node from the scene graph and deletes any children it owns. |
371 | |
372 | This function is called from QSGNode's and QSGRootNode's destructor. It |
373 | should not be called explicitly in user code. QSGRootNode needs to call |
374 | destroy() because destroy() calls removeChildNode() which in turn calls |
375 | markDirty() which type-casts the node to QSGRootNode. This type-cast is not |
376 | valid at the time QSGNode's destructor is called because the node will |
377 | already be partially destroyed at that point. |
378 | */ |
379 | |
380 | void QSGNode::destroy() |
381 | { |
382 | if (m_parent) { |
383 | m_parent->removeChildNode(this); |
384 | Q_ASSERT(m_parent == nullptr); |
385 | } |
386 | while (m_firstChild) { |
387 | QSGNode *child = m_firstChild; |
388 | removeChildNode(child); |
389 | Q_ASSERT(child->m_parent == nullptr); |
390 | if (child->flags() & OwnedByParent) |
391 | delete child; |
392 | } |
393 | |
394 | Q_ASSERT(m_firstChild == nullptr && m_lastChild == nullptr); |
395 | } |
396 | |
397 | |
398 | /*! |
399 | Prepends \a node to this node's the list of children. |
400 | |
401 | Ordering of nodes is important as geometry nodes will be rendered in the |
402 | order they are added to the scene graph. |
403 | */ |
404 | |
405 | void QSGNode::prependChildNode(QSGNode *node) |
406 | { |
407 | //Q_ASSERT_X(!m_children.contains(node), "QSGNode::prependChildNode", "QSGNode is already a child!"); |
408 | Q_ASSERT_X(!node->m_parent, "QSGNode::prependChildNode" , "QSGNode already has a parent" ); |
409 | |
410 | #ifndef QT_NO_DEBUG |
411 | if (node->type() == QSGNode::GeometryNodeType) { |
412 | QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node); |
413 | Q_ASSERT_X(g->material(), "QSGNode::prependChildNode" , "QSGGeometryNode is missing material" ); |
414 | Q_ASSERT_X(g->geometry(), "QSGNode::prependChildNode" , "QSGGeometryNode is missing geometry" ); |
415 | } |
416 | #endif |
417 | |
418 | if (m_firstChild) |
419 | m_firstChild->m_previousSibling = node; |
420 | else |
421 | m_lastChild = node; |
422 | node->m_nextSibling = m_firstChild; |
423 | m_firstChild = node; |
424 | node->m_parent = this; |
425 | |
426 | node->markDirty(DirtyNodeAdded); |
427 | } |
428 | |
429 | /*! |
430 | Appends \a node to this node's list of children. |
431 | |
432 | Ordering of nodes is important as geometry nodes will be rendered in the |
433 | order they are added to the scene graph. |
434 | */ |
435 | |
436 | void QSGNode::appendChildNode(QSGNode *node) |
437 | { |
438 | //Q_ASSERT_X(!m_children.contains(node), "QSGNode::appendChildNode", "QSGNode is already a child!"); |
439 | Q_ASSERT_X(!node->m_parent, "QSGNode::appendChildNode" , "QSGNode already has a parent" ); |
440 | |
441 | #ifndef QT_NO_DEBUG |
442 | if (node->type() == QSGNode::GeometryNodeType) { |
443 | QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node); |
444 | Q_ASSERT_X(g->material(), "QSGNode::appendChildNode" , "QSGGeometryNode is missing material" ); |
445 | Q_ASSERT_X(g->geometry(), "QSGNode::appendChildNode" , "QSGGeometryNode is missing geometry" ); |
446 | } |
447 | #endif |
448 | |
449 | if (m_lastChild) |
450 | m_lastChild->m_nextSibling = node; |
451 | else |
452 | m_firstChild = node; |
453 | node->m_previousSibling = m_lastChild; |
454 | m_lastChild = node; |
455 | node->m_parent = this; |
456 | |
457 | node->markDirty(DirtyNodeAdded); |
458 | } |
459 | |
460 | |
461 | |
462 | /*! |
463 | Inserts \a node to this node's list of children before the node specified with \a before. |
464 | |
465 | Ordering of nodes is important as geometry nodes will be rendered in the |
466 | order they are added to the scene graph. |
467 | */ |
468 | |
469 | void QSGNode::insertChildNodeBefore(QSGNode *node, QSGNode *before) |
470 | { |
471 | //Q_ASSERT_X(!m_children.contains(node), "QSGNode::insertChildNodeBefore", "QSGNode is already a child!"); |
472 | Q_ASSERT_X(!node->m_parent, "QSGNode::insertChildNodeBefore" , "QSGNode already has a parent" ); |
473 | Q_ASSERT_X(before && before->m_parent == this, "QSGNode::insertChildNodeBefore" , "The parent of \'before\' is wrong" ); |
474 | |
475 | #ifndef QT_NO_DEBUG |
476 | if (node->type() == QSGNode::GeometryNodeType) { |
477 | QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node); |
478 | Q_ASSERT_X(g->material(), "QSGNode::insertChildNodeBefore" , "QSGGeometryNode is missing material" ); |
479 | Q_ASSERT_X(g->geometry(), "QSGNode::insertChildNodeBefore" , "QSGGeometryNode is missing geometry" ); |
480 | } |
481 | #endif |
482 | |
483 | QSGNode *previous = before->m_previousSibling; |
484 | if (previous) |
485 | previous->m_nextSibling = node; |
486 | else |
487 | m_firstChild = node; |
488 | node->m_previousSibling = previous; |
489 | node->m_nextSibling = before; |
490 | before->m_previousSibling = node; |
491 | node->m_parent = this; |
492 | |
493 | node->markDirty(DirtyNodeAdded); |
494 | } |
495 | |
496 | |
497 | |
498 | /*! |
499 | Inserts \a node to this node's list of children after the node specified with \a after. |
500 | |
501 | Ordering of nodes is important as geometry nodes will be rendered in the |
502 | order they are added to the scene graph. |
503 | */ |
504 | |
505 | void QSGNode::insertChildNodeAfter(QSGNode *node, QSGNode *after) |
506 | { |
507 | //Q_ASSERT_X(!m_children.contains(node), "QSGNode::insertChildNodeAfter", "QSGNode is already a child!"); |
508 | Q_ASSERT_X(!node->m_parent, "QSGNode::insertChildNodeAfter" , "QSGNode already has a parent" ); |
509 | Q_ASSERT_X(after && after->m_parent == this, "QSGNode::insertChildNodeAfter" , "The parent of \'after\' is wrong" ); |
510 | |
511 | #ifndef QT_NO_DEBUG |
512 | if (node->type() == QSGNode::GeometryNodeType) { |
513 | QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node); |
514 | Q_ASSERT_X(g->material(), "QSGNode::insertChildNodeAfter" , "QSGGeometryNode is missing material" ); |
515 | Q_ASSERT_X(g->geometry(), "QSGNode::insertChildNodeAfter" , "QSGGeometryNode is missing geometry" ); |
516 | } |
517 | #endif |
518 | |
519 | QSGNode *next = after->m_nextSibling; |
520 | if (next) |
521 | next->m_previousSibling = node; |
522 | else |
523 | m_lastChild = node; |
524 | node->m_nextSibling = next; |
525 | node->m_previousSibling = after; |
526 | after->m_nextSibling = node; |
527 | node->m_parent = this; |
528 | |
529 | node->markDirty(DirtyNodeAdded); |
530 | } |
531 | |
532 | |
533 | |
534 | /*! |
535 | Removes \a node from this node's list of children. |
536 | */ |
537 | |
538 | void QSGNode::removeChildNode(QSGNode *node) |
539 | { |
540 | //Q_ASSERT(m_children.contains(node)); |
541 | Q_ASSERT(node->parent() == this); |
542 | |
543 | QSGNode *previous = node->m_previousSibling; |
544 | QSGNode *next = node->m_nextSibling; |
545 | if (previous) |
546 | previous->m_nextSibling = next; |
547 | else |
548 | m_firstChild = next; |
549 | if (next) |
550 | next->m_previousSibling = previous; |
551 | else |
552 | m_lastChild = previous; |
553 | node->m_previousSibling = nullptr; |
554 | node->m_nextSibling = nullptr; |
555 | |
556 | node->markDirty(DirtyNodeRemoved); |
557 | node->m_parent = nullptr; |
558 | } |
559 | |
560 | |
561 | /*! |
562 | Removes all child nodes from this node's list of children. |
563 | */ |
564 | |
565 | void QSGNode::removeAllChildNodes() |
566 | { |
567 | while (m_firstChild) { |
568 | QSGNode *node = m_firstChild; |
569 | m_firstChild = node->m_nextSibling; |
570 | node->m_nextSibling = nullptr; |
571 | if (m_firstChild) |
572 | m_firstChild->m_previousSibling = nullptr; |
573 | else |
574 | m_lastChild = nullptr; |
575 | node->markDirty(DirtyNodeRemoved); |
576 | node->m_parent = nullptr; |
577 | } |
578 | } |
579 | |
580 | /*! |
581 | * \internal |
582 | * |
583 | * Reparents all nodes of this node to \a newParent. |
584 | */ |
585 | void QSGNode::reparentChildNodesTo(QSGNode *newParent) |
586 | { |
587 | for (QSGNode *c = firstChild(); c; c = firstChild()) { |
588 | removeChildNode(c); |
589 | newParent->appendChildNode(c); |
590 | } |
591 | } |
592 | |
593 | |
594 | int QSGNode::childCount() const |
595 | { |
596 | int count = 0; |
597 | QSGNode *n = m_firstChild; |
598 | while (n) { |
599 | ++count; |
600 | n = n->m_nextSibling; |
601 | } |
602 | return count; |
603 | } |
604 | |
605 | |
606 | QSGNode *QSGNode::childAtIndex(int i) const |
607 | { |
608 | QSGNode *n = m_firstChild; |
609 | while (i && n) { |
610 | --i; |
611 | n = n->m_nextSibling; |
612 | } |
613 | return n; |
614 | } |
615 | |
616 | |
617 | /*! |
618 | Sets the flag \a f on this node if \a enabled is true; |
619 | otherwise clears the flag. |
620 | |
621 | \sa flags() |
622 | */ |
623 | |
624 | void QSGNode::setFlag(Flag f, bool enabled) |
625 | { |
626 | if (bool(m_nodeFlags & f) == enabled) |
627 | return; |
628 | m_nodeFlags ^= f; |
629 | Q_ASSERT(int(UsePreprocess) == int(DirtyUsePreprocess)); |
630 | int changedFlag = f & UsePreprocess; |
631 | if (changedFlag) |
632 | markDirty(DirtyState(changedFlag)); |
633 | } |
634 | |
635 | |
636 | /*! |
637 | Sets the flags \a f on this node if \a enabled is true; |
638 | otherwise clears the flags. |
639 | |
640 | \sa flags() |
641 | */ |
642 | |
643 | void QSGNode::setFlags(Flags f, bool enabled) |
644 | { |
645 | Flags oldFlags = m_nodeFlags; |
646 | if (enabled) |
647 | m_nodeFlags |= f; |
648 | else |
649 | m_nodeFlags &= ~f; |
650 | Q_ASSERT(int(UsePreprocess) == int(DirtyUsePreprocess)); |
651 | int changedFlags = (oldFlags ^ m_nodeFlags) & UsePreprocess; |
652 | if (changedFlags) |
653 | markDirty(DirtyState(changedFlags)); |
654 | } |
655 | |
656 | |
657 | |
658 | /*! |
659 | Notifies all connected renderers that the node has dirty \a bits. |
660 | */ |
661 | |
662 | void QSGNode::markDirty(DirtyState bits) |
663 | { |
664 | int renderableCountDiff = 0; |
665 | if (bits & DirtyNodeAdded) |
666 | renderableCountDiff += m_subtreeRenderableCount; |
667 | if (bits & DirtyNodeRemoved) |
668 | renderableCountDiff -= m_subtreeRenderableCount; |
669 | |
670 | QSGNode *p = m_parent; |
671 | while (p) { |
672 | p->m_subtreeRenderableCount += renderableCountDiff; |
673 | if (p->type() == RootNodeType) |
674 | static_cast<QSGRootNode *>(p)->notifyNodeChange(this, bits); |
675 | p = p->m_parent; |
676 | } |
677 | } |
678 | |
679 | void qsgnode_set_description(QSGNode *node, const QString &description) |
680 | { |
681 | #ifdef QSG_RUNTIME_DESCRIPTION |
682 | QSGNodePrivate::setDescription(node, description); |
683 | #else |
684 | Q_UNUSED(node); |
685 | Q_UNUSED(description); |
686 | #endif |
687 | } |
688 | |
689 | /*! |
690 | \class QSGBasicGeometryNode |
691 | \brief The QSGBasicGeometryNode class serves as a baseclass for geometry based nodes. |
692 | |
693 | \inmodule QtQuick |
694 | |
695 | The QSGBasicGeometryNode class should not be used by itself. It is only encapsulates |
696 | shared functionality between the QSGGeometryNode and QSGClipNode classes. |
697 | |
698 | \note All classes with QSG prefix should be used solely on the scene graph's |
699 | rendering thread. See \l {Scene Graph and Rendering} for more information. |
700 | */ |
701 | |
702 | |
703 | /*! |
704 | Creates a new basic geometry node of type \a type |
705 | |
706 | \internal |
707 | */ |
708 | QSGBasicGeometryNode::QSGBasicGeometryNode(NodeType type) |
709 | : QSGNode(type) |
710 | , m_geometry(nullptr) |
711 | , m_matrix(nullptr) |
712 | , m_clip_list(nullptr) |
713 | { |
714 | } |
715 | |
716 | |
717 | /*! |
718 | \internal |
719 | */ |
720 | QSGBasicGeometryNode::QSGBasicGeometryNode(QSGBasicGeometryNodePrivate &dd, NodeType type) |
721 | : QSGNode(dd, type) |
722 | , m_geometry(nullptr) |
723 | , m_matrix(nullptr) |
724 | , m_clip_list(nullptr) |
725 | { |
726 | } |
727 | |
728 | |
729 | /*! |
730 | Deletes this QSGBasicGeometryNode. |
731 | |
732 | If the node has the flag QSGNode::OwnsGeometry set, it will also delete the |
733 | geometry object it is pointing to. This flag is not set by default. |
734 | */ |
735 | |
736 | QSGBasicGeometryNode::~QSGBasicGeometryNode() |
737 | { |
738 | if (flags() & OwnsGeometry) |
739 | delete m_geometry; |
740 | } |
741 | |
742 | |
743 | /*! |
744 | \fn QSGGeometry *QSGBasicGeometryNode::geometry() |
745 | |
746 | Returns this node's geometry. |
747 | |
748 | The geometry is null by default. |
749 | */ |
750 | |
751 | /*! |
752 | \fn const QSGGeometry *QSGBasicGeometryNode::geometry() const |
753 | |
754 | Returns this node's geometry. |
755 | |
756 | The geometry is null by default. |
757 | */ |
758 | |
759 | /*! |
760 | \fn QMatrix4x4 *QSGBasicGeometryNode::matrix() const |
761 | |
762 | Will be set during rendering to contain transformation of the geometry |
763 | for that rendering pass. |
764 | |
765 | \internal |
766 | */ |
767 | |
768 | /*! |
769 | \fn QSGClipNode *QSGBasicGeometryNode::clipList() const |
770 | |
771 | Will be set during rendering to contain the clip of the geometry |
772 | for that rendering pass. |
773 | |
774 | \internal |
775 | */ |
776 | |
777 | /*! |
778 | \fn void QSGBasicGeometryNode::setRendererMatrix(const QMatrix4x4 *m) |
779 | |
780 | \internal |
781 | */ |
782 | |
783 | /*! |
784 | \fn void QSGBasicGeometryNode::setRendererClipList(const QSGClipNode *c) |
785 | |
786 | \internal |
787 | */ |
788 | |
789 | |
790 | /*! |
791 | Sets the geometry of this node to \a geometry. |
792 | |
793 | If the node has the flag QSGNode::OwnsGeometry set, it will also delete the |
794 | geometry object it is pointing to. This flag is not set by default. |
795 | |
796 | If the geometry is changed without calling setGeometry() again, the user |
797 | must also mark the geometry as dirty using QSGNode::markDirty(). |
798 | |
799 | \sa markDirty() |
800 | */ |
801 | |
802 | void QSGBasicGeometryNode::setGeometry(QSGGeometry *geometry) |
803 | { |
804 | if ((flags() & OwnsGeometry) != 0 && m_geometry != geometry) |
805 | delete m_geometry; |
806 | m_geometry = geometry; |
807 | markDirty(DirtyGeometry); |
808 | } |
809 | |
810 | |
811 | |
812 | /*! |
813 | \class QSGGeometryNode |
814 | \brief The QSGGeometryNode class is used for all rendered content in the scene graph. |
815 | |
816 | \inmodule QtQuick |
817 | \ingroup qtquick-scenegraph-nodes |
818 | |
819 | The QSGGeometryNode consists of geometry and material. The geometry defines the mesh, |
820 | the vertices and their structure, to be drawn. The Material defines how the shape is |
821 | filled. |
822 | |
823 | The following is a code snippet illustrating how to create a red |
824 | line using a QSGGeometryNode: |
825 | \code |
826 | QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2); |
827 | geometry->setDrawingMode(GL_LINES); |
828 | geometry->setLineWidth(3); |
829 | geometry->vertexDataAsPoint2D()[0].set(0, 0); |
830 | geometry->vertexDataAsPoint2D()[1].set(width(), height()); |
831 | |
832 | QSGFlatColorMaterial *material = new QSGFlatColorMaterial; |
833 | material->setColor(QColor(255, 0, 0)); |
834 | |
835 | QSGGeometryNode *node = new QSGGeometryNode; |
836 | node->setGeometry(geometry); |
837 | node->setFlag(QSGNode::OwnsGeometry); |
838 | node->setMaterial(material); |
839 | node->setFlag(QSGNode::OwnsMaterial); |
840 | \endcode |
841 | |
842 | A geometry node must have both geometry and a normal material before it is added to |
843 | the scene graph. When the geometry and materials are changed after the node has |
844 | been added to the scene graph, the user should also mark them as dirty using |
845 | QSGNode::markDirty(). |
846 | |
847 | The geometry node supports two types of materials, the opaqueMaterial and the normal |
848 | material. The opaqueMaterial is used when the accumulated scene graph opacity at the |
849 | time of rendering is 1. The primary use case is to special case opaque rendering |
850 | to avoid an extra operation in the fragment shader can have significant performance |
851 | impact on embedded graphics chips. The opaque material is optional. |
852 | |
853 | \note All classes with QSG prefix should be used solely on the scene graph's |
854 | rendering thread. See \l {Scene Graph and Rendering} for more information. |
855 | |
856 | \sa QSGGeometry, QSGMaterial, QSGSimpleMaterial |
857 | */ |
858 | |
859 | |
860 | /*! |
861 | Creates a new geometry node without geometry and material. |
862 | */ |
863 | |
864 | QSGGeometryNode::QSGGeometryNode() |
865 | : QSGBasicGeometryNode(GeometryNodeType) |
866 | { |
867 | } |
868 | |
869 | |
870 | /*! |
871 | \internal |
872 | */ |
873 | QSGGeometryNode::QSGGeometryNode(QSGGeometryNodePrivate &dd) |
874 | : QSGBasicGeometryNode(dd, GeometryNodeType) |
875 | , m_render_order(0) |
876 | , m_material(nullptr) |
877 | , m_opaque_material(nullptr) |
878 | , m_opacity(1) |
879 | { |
880 | } |
881 | |
882 | |
883 | /*! |
884 | Deletes this geometry node. |
885 | |
886 | The flags QSGNode::OwnsMaterial, QSGNode::OwnsOpaqueMaterial and |
887 | QSGNode::OwnsGeometry decides whether the geometry node should also |
888 | delete the materials and geometry. By default, these flags are disabled. |
889 | */ |
890 | |
891 | QSGGeometryNode::~QSGGeometryNode() |
892 | { |
893 | if (flags() & OwnsMaterial) |
894 | delete m_material; |
895 | if (flags() & OwnsOpaqueMaterial) |
896 | delete m_opaque_material; |
897 | } |
898 | |
899 | |
900 | |
901 | /*! |
902 | \fn int QSGGeometryNode::renderOrder() const |
903 | |
904 | Returns the render order of this geometry node. |
905 | |
906 | \internal |
907 | */ |
908 | |
909 | /*! |
910 | \fn QSGMaterial *QSGGeometryNode::material() const |
911 | |
912 | Returns the material of the QSGGeometryNode. |
913 | |
914 | \sa setMaterial() |
915 | */ |
916 | |
917 | /*! |
918 | \fn QSGMaterial *QSGGeometryNode::opaqueMaterial() const |
919 | |
920 | Returns the opaque material of the QSGGeometryNode. |
921 | |
922 | \sa setOpaqueMaterial() |
923 | */ |
924 | |
925 | /*! |
926 | \fn qreal QSGGeometryNode::inheritedOpacity() const |
927 | |
928 | Set during rendering to specify the inherited opacity for that |
929 | rendering pass. |
930 | |
931 | \internal |
932 | */ |
933 | |
934 | |
935 | /*! |
936 | Sets the render order of this node to be \a order. |
937 | |
938 | Geometry nodes are rendered in an order that visually looks like |
939 | low order nodes are rendered prior to high order nodes. For opaque |
940 | geometry there is little difference as z-testing will handle |
941 | the discard, but for translucent objects, the rendering should |
942 | normally be specified in the order of back-to-front. |
943 | |
944 | The default render order is \c 0. |
945 | |
946 | \internal |
947 | */ |
948 | void QSGGeometryNode::setRenderOrder(int order) |
949 | { |
950 | m_render_order = order; |
951 | } |
952 | |
953 | |
954 | |
955 | /*! |
956 | Sets the material of this geometry node to \a material. |
957 | |
958 | Geometry nodes must have a material before they can be added to the |
959 | scene graph. |
960 | |
961 | If the material is changed without calling setMaterial() again, the user |
962 | must also mark the material as dirty using QSGNode::markDirty(). |
963 | |
964 | */ |
965 | void QSGGeometryNode::setMaterial(QSGMaterial *material) |
966 | { |
967 | if ((flags() & OwnsMaterial) != 0 && m_material != material) |
968 | delete m_material; |
969 | m_material = material; |
970 | #ifndef QT_NO_DEBUG |
971 | if (m_material != nullptr && m_opaque_material == m_material) |
972 | qWarning("QSGGeometryNode: using same material for both opaque and translucent" ); |
973 | #endif |
974 | markDirty(DirtyMaterial); |
975 | } |
976 | |
977 | |
978 | |
979 | /*! |
980 | Sets the opaque material of this geometry to \a material. |
981 | |
982 | The opaque material will be preferred by the renderer over the |
983 | default material, as returned by the material() function, if |
984 | it is not null and the geometry item has an inherited opacity of |
985 | 1. |
986 | |
987 | The opaqueness refers to scene graph opacity, the material is still |
988 | allowed to set QSGMaterial::Blending to true and draw transparent |
989 | pixels. |
990 | |
991 | If the material is changed without calling setOpaqueMaterial() |
992 | again, the user must also mark the opaque material as dirty using |
993 | QSGNode::markDirty(). |
994 | |
995 | */ |
996 | void QSGGeometryNode::setOpaqueMaterial(QSGMaterial *material) |
997 | { |
998 | if ((flags() & OwnsOpaqueMaterial) != 0 && m_opaque_material != m_material) |
999 | delete m_opaque_material; |
1000 | m_opaque_material = material; |
1001 | #ifndef QT_NO_DEBUG |
1002 | if (m_opaque_material != nullptr && m_opaque_material == m_material) |
1003 | qWarning("QSGGeometryNode: using same material for both opaque and translucent" ); |
1004 | #endif |
1005 | |
1006 | markDirty(DirtyMaterial); |
1007 | } |
1008 | |
1009 | |
1010 | |
1011 | /*! |
1012 | Returns the material which should currently be used for geometry node. |
1013 | |
1014 | If the inherited opacity of the node is 1 and there is an opaque material |
1015 | set on this node, it will be returned; otherwise, the default material |
1016 | will be returned. |
1017 | |
1018 | \warning This function requires the scene graph above this item to be |
1019 | completely free of dirty states, so it can only be called during rendering |
1020 | |
1021 | \internal |
1022 | |
1023 | \sa setMaterial, setOpaqueMaterial |
1024 | */ |
1025 | QSGMaterial *QSGGeometryNode::activeMaterial() const |
1026 | { |
1027 | if (m_opaque_material && m_opacity > 0.999) |
1028 | return m_opaque_material; |
1029 | return m_material; |
1030 | } |
1031 | |
1032 | |
1033 | /*! |
1034 | Sets the inherited opacity of this geometry to \a opacity. |
1035 | |
1036 | This function is meant to be called by the node preprocessing |
1037 | prior to rendering the tree, so it will not mark the tree as |
1038 | dirty. |
1039 | |
1040 | \internal |
1041 | */ |
1042 | void QSGGeometryNode::setInheritedOpacity(qreal opacity) |
1043 | { |
1044 | Q_ASSERT(opacity >= 0 && opacity <= 1); |
1045 | m_opacity = opacity; |
1046 | } |
1047 | |
1048 | |
1049 | /*! |
1050 | \class QSGClipNode |
1051 | \brief The QSGClipNode class implements the clipping functionality in the scene graph. |
1052 | |
1053 | \inmodule QtQuick |
1054 | \ingroup qtquick-scenegraph-nodes |
1055 | |
1056 | Clipping applies to the node's subtree and can be nested. Multiple clip nodes will be |
1057 | accumulated by intersecting all their geometries. The accumulation happens |
1058 | as part of the rendering. |
1059 | |
1060 | Clip nodes must have a geometry before they can be added to the scene graph. |
1061 | |
1062 | Clipping is usually implemented by using the stencil buffer. |
1063 | |
1064 | \note All classes with QSG prefix should be used solely on the scene graph's |
1065 | rendering thread. See \l {Scene Graph and Rendering} for more information. |
1066 | */ |
1067 | |
1068 | |
1069 | |
1070 | /*! |
1071 | Creates a new QSGClipNode without a geometry. |
1072 | |
1073 | The clip node must have a geometry before it can be added to the |
1074 | scene graph. |
1075 | */ |
1076 | |
1077 | QSGClipNode::QSGClipNode() |
1078 | : QSGBasicGeometryNode(ClipNodeType) |
1079 | , m_is_rectangular(false) |
1080 | { |
1081 | Q_UNUSED(m_reserved); |
1082 | } |
1083 | |
1084 | |
1085 | |
1086 | /*! |
1087 | Deletes this QSGClipNode. |
1088 | |
1089 | If the flag QSGNode::OwnsGeometry is set, the geometry will also be |
1090 | deleted. |
1091 | */ |
1092 | |
1093 | QSGClipNode::~QSGClipNode() |
1094 | { |
1095 | } |
1096 | |
1097 | |
1098 | |
1099 | /*! |
1100 | \fn bool QSGClipNode::isRectangular() const |
1101 | |
1102 | Returns if this clip node has a rectangular clip. |
1103 | */ |
1104 | |
1105 | |
1106 | |
1107 | /*! |
1108 | Sets whether this clip node has a rectangular clip to \a rectHint. |
1109 | |
1110 | This is an optimization hint which means that the renderer can |
1111 | use scissoring instead of stencil, which is significantly faster. |
1112 | |
1113 | When this hint is set and it is applicable, the clip region will be |
1114 | generated from clipRect() rather than geometry(). |
1115 | |
1116 | By default this property is \c false. |
1117 | */ |
1118 | |
1119 | void QSGClipNode::setIsRectangular(bool rectHint) |
1120 | { |
1121 | m_is_rectangular = rectHint; |
1122 | } |
1123 | |
1124 | |
1125 | |
1126 | /*! |
1127 | \fn QRectF QSGClipNode::clipRect() const |
1128 | |
1129 | Returns the clip rect of this node. |
1130 | */ |
1131 | |
1132 | |
1133 | /*! |
1134 | Sets the clip rect of this clip node to \a rect. |
1135 | |
1136 | When a rectangular clip is set in combination with setIsRectangular |
1137 | the renderer may in some cases use a more optimal clip method. |
1138 | */ |
1139 | void QSGClipNode::setClipRect(const QRectF &rect) |
1140 | { |
1141 | m_clip_rect = rect; |
1142 | } |
1143 | |
1144 | |
1145 | /*! |
1146 | \class QSGTransformNode |
1147 | \brief The QSGTransformNode class implements transformations in the scene graph. |
1148 | |
1149 | \inmodule QtQuick |
1150 | \ingroup qtquick-scenegraph-nodes |
1151 | |
1152 | Transformations apply the node's subtree and can be nested. Multiple transform nodes |
1153 | will be accumulated by intersecting all their matrices. The accumulation happens |
1154 | as part of the rendering. |
1155 | |
1156 | The transform nodes implement a 4x4 matrix which in theory supports full 3D |
1157 | transformations. However, because the renderer optimizes for 2D use-cases rather |
1158 | than 3D use-cases, rendering a scene with full 3D transformations needs to |
1159 | be done with some care. |
1160 | |
1161 | \note All classes with QSG prefix should be used solely on the scene graph's |
1162 | rendering thread. See \l {Scene Graph and Rendering} for more information. |
1163 | |
1164 | */ |
1165 | |
1166 | |
1167 | /*! |
1168 | Create a new QSGTransformNode with its matrix set to the identity matrix. |
1169 | */ |
1170 | |
1171 | QSGTransformNode::QSGTransformNode() |
1172 | : QSGNode(TransformNodeType) |
1173 | { |
1174 | } |
1175 | |
1176 | |
1177 | |
1178 | /*! |
1179 | Deletes this transform node. |
1180 | */ |
1181 | |
1182 | QSGTransformNode::~QSGTransformNode() |
1183 | { |
1184 | } |
1185 | |
1186 | |
1187 | |
1188 | /*! |
1189 | \fn QMatrix4x4 QSGTransformNode::matrix() const |
1190 | |
1191 | Returns this transform node's matrix. |
1192 | */ |
1193 | |
1194 | |
1195 | |
1196 | /*! |
1197 | Sets this transform node's matrix to \a matrix. |
1198 | */ |
1199 | |
1200 | void QSGTransformNode::setMatrix(const QMatrix4x4 &matrix) |
1201 | { |
1202 | m_matrix = matrix; |
1203 | markDirty(DirtyMatrix); |
1204 | } |
1205 | |
1206 | /*! |
1207 | \fn const QMatrix4x4 &QSGTransformNode::combinedMatrix() const |
1208 | |
1209 | Set during rendering to the combination of all parent matrices for |
1210 | that rendering pass. |
1211 | |
1212 | \internal |
1213 | */ |
1214 | |
1215 | |
1216 | |
1217 | /*! |
1218 | Sets the combined matrix of this matrix to \a transform. |
1219 | |
1220 | This function is meant to be called by the node preprocessing |
1221 | prior to rendering the tree, so it will not mark the tree as |
1222 | dirty. |
1223 | |
1224 | \internal |
1225 | */ |
1226 | void QSGTransformNode::setCombinedMatrix(const QMatrix4x4 &matrix) |
1227 | { |
1228 | m_combined_matrix = matrix; |
1229 | } |
1230 | |
1231 | |
1232 | |
1233 | /*! |
1234 | \class QSGRootNode |
1235 | \brief The QSGRootNode is the toplevel root of any scene graph. |
1236 | |
1237 | The root node is used to attach a scene graph to a renderer. |
1238 | |
1239 | \internal |
1240 | */ |
1241 | |
1242 | |
1243 | |
1244 | /*! |
1245 | \fn QSGRootNode::QSGRootNode() |
1246 | |
1247 | Creates a new root node. |
1248 | */ |
1249 | |
1250 | QSGRootNode::QSGRootNode() |
1251 | : QSGNode(RootNodeType) |
1252 | { |
1253 | } |
1254 | |
1255 | |
1256 | /*! |
1257 | Deletes the root node. |
1258 | |
1259 | When a root node is deleted it removes itself from all of renderers |
1260 | that are referencing it. |
1261 | */ |
1262 | |
1263 | QSGRootNode::~QSGRootNode() |
1264 | { |
1265 | while (!m_renderers.isEmpty()) |
1266 | m_renderers.constLast()->setRootNode(nullptr); |
1267 | destroy(); // Must call destroy() here because markDirty() casts this to QSGRootNode. |
1268 | } |
1269 | |
1270 | |
1271 | |
1272 | /*! |
1273 | Called to notify all renderers that \a node has been marked as dirty |
1274 | with \a flags. |
1275 | */ |
1276 | |
1277 | void QSGRootNode::notifyNodeChange(QSGNode *node, DirtyState state) |
1278 | { |
1279 | for (int i=0; i<m_renderers.size(); ++i) { |
1280 | m_renderers.at(i)->nodeChanged(node, state); |
1281 | } |
1282 | } |
1283 | |
1284 | |
1285 | |
1286 | /*! |
1287 | \class QSGOpacityNode |
1288 | \brief The QSGOpacityNode class is used to change opacity of nodes. |
1289 | |
1290 | \inmodule QtQuick |
1291 | \ingroup qtquick-scenegraph-nodes |
1292 | |
1293 | Opacity applies to its subtree and can be nested. Multiple opacity nodes |
1294 | will be accumulated by multiplying their opacity. The accumulation happens |
1295 | as part of the rendering. |
1296 | |
1297 | When nested opacity gets below a certain threshold, the subtree might |
1298 | be marked as blocked, causing isSubtreeBlocked() to return true. This |
1299 | is done for performance reasons. |
1300 | |
1301 | \note All classes with QSG prefix should be used solely on the scene graph's |
1302 | rendering thread. See \l {Scene Graph and Rendering} for more information. |
1303 | */ |
1304 | |
1305 | |
1306 | |
1307 | /*! |
1308 | Constructs an opacity node with a default opacity of 1. |
1309 | |
1310 | Opacity accumulates downwards in the scene graph so a node with two |
1311 | QSGOpacityNode instances above it, both with opacity of 0.5, will have |
1312 | effective opacity of 0.25. |
1313 | |
1314 | The default opacity of nodes is 1. |
1315 | */ |
1316 | QSGOpacityNode::QSGOpacityNode() |
1317 | : QSGNode(OpacityNodeType) |
1318 | { |
1319 | } |
1320 | |
1321 | |
1322 | |
1323 | /*! |
1324 | Deletes the opacity node. |
1325 | */ |
1326 | |
1327 | QSGOpacityNode::~QSGOpacityNode() |
1328 | { |
1329 | } |
1330 | |
1331 | |
1332 | |
1333 | /*! |
1334 | \fn qreal QSGOpacityNode::opacity() const |
1335 | |
1336 | Returns this opacity node's opacity. |
1337 | */ |
1338 | |
1339 | const qreal OPACITY_THRESHOLD = 0.001; |
1340 | |
1341 | /*! |
1342 | Sets the opacity of this node to \a opacity. |
1343 | |
1344 | Before rendering the graph, the renderer will do an update pass |
1345 | over the subtree to propagate the opacity to its children. |
1346 | |
1347 | The value will be bounded to the range 0 to 1. |
1348 | */ |
1349 | |
1350 | void QSGOpacityNode::setOpacity(qreal opacity) |
1351 | { |
1352 | opacity = qBound<qreal>(0, opacity, 1); |
1353 | if (m_opacity == opacity) |
1354 | return; |
1355 | DirtyState dirtyState = DirtyOpacity; |
1356 | |
1357 | if ((m_opacity < OPACITY_THRESHOLD && opacity >= OPACITY_THRESHOLD) // blocked to unblocked |
1358 | || (m_opacity >= OPACITY_THRESHOLD && opacity < OPACITY_THRESHOLD)) // unblocked to blocked |
1359 | dirtyState |= DirtySubtreeBlocked; |
1360 | |
1361 | m_opacity = opacity; |
1362 | markDirty(dirtyState); |
1363 | } |
1364 | |
1365 | |
1366 | |
1367 | /*! |
1368 | \fn qreal QSGOpacityNode::combinedOpacity() const |
1369 | |
1370 | Returns this node's accumulated opacity. |
1371 | |
1372 | This value is calculated during rendering and only stored |
1373 | in the opacity node temporarily. |
1374 | |
1375 | \internal |
1376 | */ |
1377 | |
1378 | |
1379 | |
1380 | /*! |
1381 | Sets the combined opacity of this node to \a opacity. |
1382 | |
1383 | This function is meant to be called by the node preprocessing |
1384 | prior to rendering the tree, so it will not mark the tree as |
1385 | dirty. |
1386 | |
1387 | \internal |
1388 | */ |
1389 | |
1390 | void QSGOpacityNode::setCombinedOpacity(qreal opacity) |
1391 | { |
1392 | m_combined_opacity = opacity; |
1393 | } |
1394 | |
1395 | |
1396 | |
1397 | /*! |
1398 | For performance reasons, we block the subtree when the opacity |
1399 | is below a certain threshold. |
1400 | |
1401 | \internal |
1402 | */ |
1403 | |
1404 | bool QSGOpacityNode::isSubtreeBlocked() const |
1405 | { |
1406 | return m_opacity < OPACITY_THRESHOLD; |
1407 | } |
1408 | |
1409 | |
1410 | /*! |
1411 | \class QSGNodeVisitor |
1412 | \brief The QSGNodeVisitor class is a helper class for traversing the scene graph. |
1413 | |
1414 | \internal |
1415 | */ |
1416 | |
1417 | QSGNodeVisitor::~QSGNodeVisitor() |
1418 | { |
1419 | |
1420 | } |
1421 | |
1422 | |
1423 | void QSGNodeVisitor::visitNode(QSGNode *n) |
1424 | { |
1425 | switch (n->type()) { |
1426 | case QSGNode::TransformNodeType: { |
1427 | QSGTransformNode *t = static_cast<QSGTransformNode *>(n); |
1428 | enterTransformNode(t); |
1429 | visitChildren(t); |
1430 | leaveTransformNode(t); |
1431 | break; } |
1432 | case QSGNode::GeometryNodeType: { |
1433 | QSGGeometryNode *g = static_cast<QSGGeometryNode *>(n); |
1434 | enterGeometryNode(g); |
1435 | visitChildren(g); |
1436 | leaveGeometryNode(g); |
1437 | break; } |
1438 | case QSGNode::ClipNodeType: { |
1439 | QSGClipNode *c = static_cast<QSGClipNode *>(n); |
1440 | enterClipNode(c); |
1441 | visitChildren(c); |
1442 | leaveClipNode(c); |
1443 | break; } |
1444 | case QSGNode::OpacityNodeType: { |
1445 | QSGOpacityNode *o = static_cast<QSGOpacityNode *>(n); |
1446 | enterOpacityNode(o); |
1447 | visitChildren(o); |
1448 | leaveOpacityNode(o); |
1449 | break; } |
1450 | default: |
1451 | visitChildren(n); |
1452 | break; |
1453 | } |
1454 | } |
1455 | |
1456 | void QSGNodeVisitor::visitChildren(QSGNode *n) |
1457 | { |
1458 | for (QSGNode *c = n->firstChild(); c; c = c->nextSibling()) |
1459 | visitNode(c); |
1460 | } |
1461 | |
1462 | #ifndef QT_NO_DEBUG_STREAM |
1463 | QDebug operator<<(QDebug d, const QSGGeometryNode *n) |
1464 | { |
1465 | if (!n) { |
1466 | d << "Geometry(null)" ; |
1467 | return d; |
1468 | } |
1469 | d << "GeometryNode(" << hex << (const void *) n << dec; |
1470 | |
1471 | const QSGGeometry *g = n->geometry(); |
1472 | |
1473 | if (!g) { |
1474 | d << "no geometry" ; |
1475 | } else { |
1476 | |
1477 | switch (g->drawingMode()) { |
1478 | case QSGGeometry::DrawTriangleStrip: d << "strip" ; break; |
1479 | case QSGGeometry::DrawTriangleFan: d << "fan" ; break; |
1480 | case QSGGeometry::DrawTriangles: d << "triangles" ; break; |
1481 | default: break; |
1482 | } |
1483 | |
1484 | d << "#V:" << g->vertexCount() << "#I:" << g->indexCount(); |
1485 | |
1486 | if (g->attributeCount() > 0 && g->attributes()->type == QSGGeometry::FloatType) { |
1487 | float x1 = 1e10, x2 = -1e10, y1=1e10, y2=-1e10; |
1488 | int stride = g->sizeOfVertex(); |
1489 | for (int i = 0; i < g->vertexCount(); ++i) { |
1490 | float x = ((float *)((char *)const_cast<QSGGeometry *>(g)->vertexData() + i * stride))[0]; |
1491 | float y = ((float *)((char *)const_cast<QSGGeometry *>(g)->vertexData() + i * stride))[1]; |
1492 | |
1493 | x1 = qMin(x1, x); |
1494 | x2 = qMax(x2, x); |
1495 | y1 = qMin(y1, y); |
1496 | y2 = qMax(y2, y); |
1497 | } |
1498 | |
1499 | d << "x1=" << x1 << "y1=" << y1 << "x2=" << x2 << "y2=" << y2; |
1500 | } |
1501 | } |
1502 | |
1503 | if (n->material()) |
1504 | d << "materialtype=" << n->material()->type(); |
1505 | |
1506 | |
1507 | d << ')'; |
1508 | #ifdef QSG_RUNTIME_DESCRIPTION |
1509 | d << QSGNodePrivate::description(n); |
1510 | #endif |
1511 | return d; |
1512 | } |
1513 | |
1514 | QDebug operator<<(QDebug d, const QSGClipNode *n) |
1515 | { |
1516 | if (!n) { |
1517 | d << "ClipNode(null)" ; |
1518 | return d; |
1519 | } |
1520 | d << "ClipNode(" << hex << (const void *) n << dec; |
1521 | |
1522 | if (n->childCount()) |
1523 | d << "children=" << n->childCount(); |
1524 | |
1525 | d << "is rect?" << (n->isRectangular() ? "yes" : "no" ); |
1526 | |
1527 | d << ')'; |
1528 | #ifdef QSG_RUNTIME_DESCRIPTION |
1529 | d << QSGNodePrivate::description(n); |
1530 | #endif |
1531 | d << (n->isSubtreeBlocked() ? "*BLOCKED*" : "" ); |
1532 | return d; |
1533 | } |
1534 | |
1535 | QDebug operator<<(QDebug d, const QSGTransformNode *n) |
1536 | { |
1537 | if (!n) { |
1538 | d << "TransformNode(null)" ; |
1539 | return d; |
1540 | } |
1541 | const QMatrix4x4 m = n->matrix(); |
1542 | d << "TransformNode(" ; |
1543 | d << hex << (const void *) n << dec; |
1544 | if (m.isIdentity()) |
1545 | d << "identity" ; |
1546 | else if (m.determinant() == 1 && m(0, 0) == 1 && m(1, 1) == 1 && m(2, 2) == 1) |
1547 | d << "translate" << m(0, 3) << m(1, 3) << m(2, 3); |
1548 | else |
1549 | d << "det=" << n->matrix().determinant(); |
1550 | #ifdef QSG_RUNTIME_DESCRIPTION |
1551 | d << QSGNodePrivate::description(n); |
1552 | #endif |
1553 | d << (n->isSubtreeBlocked() ? "*BLOCKED*" : "" ); |
1554 | d << ')'; |
1555 | return d; |
1556 | } |
1557 | |
1558 | QDebug operator<<(QDebug d, const QSGOpacityNode *n) |
1559 | { |
1560 | if (!n) { |
1561 | d << "OpacityNode(null)" ; |
1562 | return d; |
1563 | } |
1564 | d << "OpacityNode(" ; |
1565 | d << hex << (const void *) n << dec; |
1566 | d << "opacity=" << n->opacity() |
1567 | << "combined=" << n->combinedOpacity() |
1568 | << (n->isSubtreeBlocked() ? "*BLOCKED*" : "" ); |
1569 | #ifdef QSG_RUNTIME_DESCRIPTION |
1570 | d << QSGNodePrivate::description(n); |
1571 | #endif |
1572 | d << ')'; |
1573 | return d; |
1574 | } |
1575 | |
1576 | |
1577 | QDebug operator<<(QDebug d, const QSGRootNode *n) |
1578 | { |
1579 | if (!n) { |
1580 | d << "RootNode(null)" ; |
1581 | return d; |
1582 | } |
1583 | QDebugStateSaver saver(d); |
1584 | d << "RootNode" << hex << (const void *) n << (n->isSubtreeBlocked() ? "*BLOCKED*" : "" ); |
1585 | #ifdef QSG_RUNTIME_DESCRIPTION |
1586 | d << QSGNodePrivate::description(n); |
1587 | #endif |
1588 | d << ')'; |
1589 | return d; |
1590 | } |
1591 | |
1592 | |
1593 | |
1594 | QDebug operator<<(QDebug d, const QSGNode *n) |
1595 | { |
1596 | if (!n) { |
1597 | d << "Node(null)" ; |
1598 | return d; |
1599 | } |
1600 | switch (n->type()) { |
1601 | case QSGNode::GeometryNodeType: |
1602 | d << static_cast<const QSGGeometryNode *>(n); |
1603 | break; |
1604 | case QSGNode::TransformNodeType: |
1605 | d << static_cast<const QSGTransformNode *>(n); |
1606 | break; |
1607 | case QSGNode::ClipNodeType: |
1608 | d << static_cast<const QSGClipNode *>(n); |
1609 | break; |
1610 | case QSGNode::RootNodeType: |
1611 | d << static_cast<const QSGRootNode *>(n); |
1612 | break; |
1613 | case QSGNode::OpacityNodeType: |
1614 | d << static_cast<const QSGOpacityNode *>(n); |
1615 | break; |
1616 | case QSGNode::RenderNodeType: |
1617 | d << "RenderNode(" << hex << (const void *) n << dec |
1618 | << "flags=" << (int) n->flags() << dec |
1619 | << (n->isSubtreeBlocked() ? "*BLOCKED*" : "" ); |
1620 | #ifdef QSG_RUNTIME_DESCRIPTION |
1621 | d << QSGNodePrivate::description(n); |
1622 | #endif |
1623 | d << ')'; |
1624 | break; |
1625 | default: |
1626 | d << "Node(" << hex << (const void *) n << dec |
1627 | << "flags=" << (int) n->flags() << dec |
1628 | << (n->isSubtreeBlocked() ? "*BLOCKED*" : "" ); |
1629 | #ifdef QSG_RUNTIME_DESCRIPTION |
1630 | d << QSGNodePrivate::description(n); |
1631 | #endif |
1632 | d << ')'; |
1633 | break; |
1634 | } |
1635 | return d; |
1636 | } |
1637 | |
1638 | #endif |
1639 | |
1640 | QT_END_NAMESPACE |
1641 | |