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