1// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
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 "qframegraphnode.h"
5#include "qframegraphnode_p.h"
6#include <Qt3DRender/qfilterkey.h>
7#include <Qt3DRender/qtechniquefilter.h>
8#include <Qt3DRender/qrenderpassfilter.h>
9
10#include <Qt3DCore/QNode>
11#include <QList>
12#include <QQueue>
13
14using namespace Qt3DCore;
15
16QT_BEGIN_NAMESPACE
17
18namespace {
19
20QString dumpNode(const Qt3DRender::QFrameGraphNode *n) {
21 QString res = QLatin1String(n->metaObject()->className());
22 if (!n->objectName().isEmpty())
23 res += QString(QLatin1String(" (%1)")).arg(a: n->objectName());
24 if (!n->isEnabled())
25 res += QLatin1String(" [D]");
26 return res;
27}
28
29QString dumpNodeFilters(const Qt3DRender::QFrameGraphNode *n, const QList<Qt3DRender::QFilterKey *> &filters) {
30 QString res = QLatin1String(n->metaObject()->className());
31 if (!n->objectName().isEmpty())
32 res += QString(QLatin1String(" (%1)")).arg(a: n->objectName());
33
34 QStringList kv;
35 for (auto filter: filters)
36 kv.push_back(t: QString(QLatin1String("%1: %2")).arg(args: filter->name(), args: filter->value().toString()));
37 if (kv.size())
38 res += QString(QLatin1String(" <%1>")).arg(a: kv.join(sep: QLatin1String(", ")));
39
40 return res;
41}
42
43QStringList dumpFG(const Qt3DCore::QNode *n, int level = 0)
44{
45 QStringList reply;
46
47 const Qt3DRender::QFrameGraphNode *fgNode = qobject_cast<const Qt3DRender::QFrameGraphNode *>(object: n);
48 if (fgNode) {
49 QString res = dumpNode(n: fgNode);
50 reply += res.rightJustified(width: res.size() + level * 2, fill: QLatin1Char(' '));
51 }
52
53 const auto children = n->childNodes();
54 const int inc = fgNode ? 1 : 0;
55 for (auto *child: children) {
56 auto *childFGNode = qobject_cast<Qt3DCore::QNode *>(object: child);
57 if (childFGNode != nullptr)
58 reply += dumpFG(n: childFGNode, level: level + inc);
59 }
60
61 return reply;
62}
63
64struct HierarchyFGNode
65{
66 const Qt3DRender::QFrameGraphNode *root;
67 QList<QSharedPointer<HierarchyFGNode>> children;
68};
69using HierarchyFGNodePtr = QSharedPointer<HierarchyFGNode>;
70
71HierarchyFGNodePtr buildFGHierarchy(const Qt3DCore::QNode *n, HierarchyFGNodePtr lastFGParent = HierarchyFGNodePtr())
72{
73 const Qt3DRender::QFrameGraphNode *fgNode = qobject_cast<const Qt3DRender::QFrameGraphNode *>(object: n);
74
75 // Only happens for the root case
76 if (!lastFGParent) {
77 lastFGParent = HierarchyFGNodePtr::create();
78 lastFGParent->root = fgNode;
79 } else {
80 if (fgNode != nullptr) {
81 HierarchyFGNodePtr hN = HierarchyFGNodePtr::create();
82 hN->root = fgNode;
83 if (lastFGParent)
84 lastFGParent->children.push_back(t: hN);
85 lastFGParent = hN;
86 }
87 }
88
89 const auto children = n->childNodes();
90 for (auto *child: children)
91 buildFGHierarchy(n: child, lastFGParent);
92
93 return lastFGParent;
94}
95
96void findFGLeaves(const HierarchyFGNodePtr root, QList<const Qt3DRender::QFrameGraphNode *> &fgLeaves)
97{
98 const auto children = root->children;
99 for (const auto &child : children)
100 findFGLeaves(root: child, fgLeaves);
101
102 if (children.empty())
103 fgLeaves.push_back(t: root->root);
104}
105
106void dumpFGPaths(const Qt3DRender::QFrameGraphNode *n, QStringList &result)
107{
108 // Build FG node hierarchy
109 const HierarchyFGNodePtr rootHFg = buildFGHierarchy(n);
110
111 // Gather FG leaves
112 QList<const Qt3DRender::QFrameGraphNode *> fgLeaves;
113 findFGLeaves(root: rootHFg, fgLeaves);
114
115 // Traverse back to root
116 int rv = 1;
117 for (const Qt3DRender::QFrameGraphNode *fgNode : fgLeaves) {
118 QStringList parents;
119 while (fgNode != nullptr) {
120 parents.prepend(t: dumpNode(n: fgNode));
121 fgNode = fgNode->parentFrameGraphNode();
122 }
123 if (parents.size()) {
124 result << QString(QLatin1String("%1 [ %2 ]")).arg(args: QString::number(rv), args: parents.join(sep: QLatin1String(", ")));
125 ++rv;
126 }
127 }
128}
129
130void dumpFGFilterState(const Qt3DRender::QFrameGraphNode *n, QStringList &result)
131{
132 // Build FG node hierarchy
133 const HierarchyFGNodePtr rootHFg = buildFGHierarchy(n);
134
135 // Gather FG leaves
136 QList<const Qt3DRender::QFrameGraphNode *> fgLeaves;
137 findFGLeaves(root: rootHFg, fgLeaves);
138
139 // Traverse back to root
140 int rv = 1;
141 for (const Qt3DRender::QFrameGraphNode *fgNode : fgLeaves) {
142 int parents = 0;
143 QStringList filters;
144 while (fgNode != nullptr) {
145 ++parents;
146 if (fgNode->isEnabled()) {
147 auto techniqueFilter = qobject_cast<const Qt3DRender::QTechniqueFilter *>(object: fgNode);
148 if (techniqueFilter && techniqueFilter->matchAll().size())
149 filters.prepend(t: dumpNodeFilters(n: techniqueFilter, filters: techniqueFilter->matchAll()));
150 auto renderPassFilter = qobject_cast<const Qt3DRender::QRenderPassFilter *>(object: fgNode);
151 if (renderPassFilter)
152 filters.prepend(t: dumpNodeFilters(n: renderPassFilter, filters: renderPassFilter->matchAny()));
153 }
154 fgNode = fgNode->parentFrameGraphNode();
155 }
156 if (parents) {
157 if (filters.size())
158 result << QString(QLatin1String("%1 [ %2 ]")).arg(args: QString::number(rv), args: filters.join(sep: QLatin1String(", ")));
159 else
160 result << QString(QObject::tr(s: "%1 [ No Filters ]")).arg(a: rv);
161 ++rv;
162 }
163 }
164}
165
166}
167
168namespace Qt3DRender {
169
170QFrameGraphNodePrivate::QFrameGraphNodePrivate()
171 : QNodePrivate()
172{
173}
174
175/*!
176 \class Qt3DRender::QFrameGraphNode
177 \inmodule Qt3DRender
178 \since 5.5
179
180 \brief Base class of all FrameGraph configuration nodes.
181
182 This class is rarely instanced directly since it doesn't provide
183 any frame graph specific behavior, although it can be convenient
184 to use for grouping other nodes together in dynamic frame graphs.
185 The actual behavior comes from the subclasses.
186
187 The subclasses are:
188 \table
189 \header
190 \li class
191 \li description
192 \row
193 \li Qt3DRender::QCameraSelector
194 \li Select camera from all available cameras in the scene
195 \row
196 \li Qt3DRender::QClearBuffers
197 \li Specify which buffers to clear and to what values
198 \row
199 \li Qt3DRender::QDispatchCompute
200 \li Specify Compute operation kernels
201 \row
202 \li Qt3DRender::QFrustumCulling
203 \li Enable frustum culling
204 \row
205 \li Qt3DRender::QLayerFilter
206 \li Select which layers to draw
207 \row
208 \li Qt3DRender::QNoDraw
209 \li Disable drawing
210 \row
211 \li Qt3DRender::QRenderPassFilter
212 \li Select which render passes to draw
213 \row
214 \li Qt3DRender::QRenderStateSet
215 \li Set render states
216 \row
217 \li Qt3DRender::QRenderSurfaceSelector
218 \li Select which surface to draw to
219 \row
220 \li Qt3DRender::QRenderTargetSelector
221 \li Select which QRenderTarget to draw to
222 \row
223 \li Qt3DRender::QSortPolicy
224 \li Specify how entities are sorted to determine draw order
225 \row
226 \li Qt3DRender::QTechniqueFilter
227 \li Select which techniques to draw
228 \row
229 \li Qt3DRender::QViewport
230 \li Specify viewport
231 \row
232 \li Qt3DRender::QMemoryBarrier
233 \li Places a memory barrier
234 \endtable
235
236 */
237
238/*!
239 \qmltype FrameGraphNode
240 \inqmlmodule Qt3D.Render
241 \instantiates Qt3DRender::QFrameGraphNode
242 \inherits Node
243 \since 5.5
244 \brief Base class of all FrameGraph configuration nodes.
245
246 This class is rarely instanced directly since it doesn't provide
247 any frame graph specific behavior, although it can be convenient
248 to use for grouping other nodes together in dynamic frame graphs.
249 The actual behavior comes from the subclasses.
250
251 The subclasses are:
252 \table
253 \header
254 \li class
255 \li description
256 \row
257 \li CameraSelector
258 \li Select camera from all available cameras in the scene
259 \row
260 \li ClearBuffers
261 \li Specify which buffers to clear and to what values
262 \row
263 \li DispatchCompute
264 \li Specify compute operation kernels
265 \row
266 \li FrustumCulling
267 \li Enable frustum culling
268 \row
269 \li LayerFilter
270 \li Select which layers to draw
271 \row
272 \li NoDraw
273 \li Disable drawing
274 \row
275 \li RenderPassFilter
276 \li Select which render passes to draw
277 \row
278 \li RenderStateSet
279 \li Set render states
280 \row
281 \li RenderSurfaceSelector
282 \li Select which surface to draw to
283 \row
284 \li RenderTargetSelector
285 \li Select which RenderTarget to draw to
286 \row
287 \li SortPolicy
288 \li Specify how entities are sorted to determine draw order
289 \row
290 \li TechniqueFilter
291 \li Select which techniques to draw
292 \row
293 \li Viewport
294 \li Specify viewport
295 \row
296 \li MemoryBarrier
297 \li Places a memory barrier
298 \endtable
299*/
300
301/*!
302 The constructor creates an instance with the specified \a parent.
303 */
304QFrameGraphNode::QFrameGraphNode(QNode *parent)
305 : QNode(*new QFrameGraphNodePrivate, parent)
306{
307}
308
309/*! \internal */
310QFrameGraphNode::~QFrameGraphNode()
311{
312}
313
314/*!
315 Returns a pointer to the parent frame graph node.
316
317 If the parent of this node is not a frame graph node,
318 this method will recursively look for a parent node that is a frame graph node.
319 */
320QFrameGraphNode *QFrameGraphNode::parentFrameGraphNode() const
321{
322 QFrameGraphNode *parentFGNode = nullptr;
323 QNode *parentN = parentNode();
324
325 while (parentN) {
326 if ((parentFGNode = qobject_cast<QFrameGraphNode *>(object: parentN)) != nullptr)
327 break;
328 parentN = parentN->parentNode();
329 }
330 return parentFGNode;
331}
332
333/*!
334 \internal
335 * Returns a list of the children that are frame graph nodes.
336 * If this function encounters a child node that is not a frame graph node,
337 * it will go through the children of the child node and look for frame graph nodes.
338 * If any of these are not frame graph nodes, they will be further searched as
339 * if they were direct children of this node.
340 */
341QList<QFrameGraphNode *> QFrameGraphNodePrivate::childFrameGraphNodes() const
342{
343 Q_Q(const QFrameGraphNode);
344 QList<QFrameGraphNode *> result;
345 QQueue<QNode *> queue;
346 const auto childNodes = q->childNodes();
347 for (auto c: childNodes)
348 queue.append(t: c);
349 result.reserve(size: queue.size());
350 while (!queue.isEmpty()) {
351 auto *child = queue.dequeue();
352 auto *childFGNode = qobject_cast<QFrameGraphNode *>(object: child);
353 if (childFGNode != nullptr)
354 result.push_back(t: childFGNode);
355 else {
356 const auto childNodes = child->childNodes();
357 for (auto c: childNodes)
358 queue.append(t: c);
359 }
360 }
361 return result;
362}
363
364QString QFrameGraphNodePrivate::dumpFrameGraph() const
365{
366 Q_Q(const QFrameGraphNode);
367 return dumpFG(n: q).join(sep: QLatin1Char('\n'));
368}
369
370QStringList QFrameGraphNodePrivate::dumpFrameGraphPaths() const
371{
372 Q_Q(const QFrameGraphNode);
373 QStringList result;
374 dumpFGPaths(n: q, result);
375 return result;
376}
377
378QStringList QFrameGraphNodePrivate::dumpFrameGraphFilterState() const
379{
380 Q_Q(const QFrameGraphNode);
381 QStringList result;
382 dumpFGFilterState(n: q, result);
383 return result;
384}
385
386/*! \internal */
387QFrameGraphNode::QFrameGraphNode(QFrameGraphNodePrivate &dd, QNode *parent)
388 : QNode(dd, parent)
389{
390}
391
392void QFrameGraphNode::onParentChanged(QObject *)
393{
394 // Direct sync update request
395 Q_D(QFrameGraphNode);
396 d->update();
397}
398
399} // namespace Qt3DRender
400
401QT_END_NAMESPACE
402
403#include "moc_qframegraphnode.cpp"
404

source code of qt3d/src/render/framegraph/qframegraphnode.cpp