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