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