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