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 | using namespace Qt3DCore; |
15 | |
16 | QT_BEGIN_NAMESPACE |
17 | |
18 | namespace { |
19 | |
20 | QString 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 | |
29 | QString 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 | |
43 | QStringList 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 | |
64 | struct HierarchyFGNode |
65 | { |
66 | const Qt3DRender::QFrameGraphNode *root; |
67 | QList<QSharedPointer<HierarchyFGNode>> children; |
68 | }; |
69 | using HierarchyFGNodePtr = QSharedPointer<HierarchyFGNode>; |
70 | |
71 | HierarchyFGNodePtr 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 | |
96 | void 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 | |
106 | void 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 | |
130 | void 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 | |
168 | namespace Qt3DRender { |
169 | |
170 | QFrameGraphNodePrivate::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 | */ |
304 | QFrameGraphNode::QFrameGraphNode(QNode *parent) |
305 | : QNode(*new QFrameGraphNodePrivate, parent) |
306 | { |
307 | } |
308 | |
309 | /*! \internal */ |
310 | QFrameGraphNode::~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 | */ |
320 | QFrameGraphNode *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 | */ |
341 | QList<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 | |
364 | QString QFrameGraphNodePrivate::dumpFrameGraph() const |
365 | { |
366 | Q_Q(const QFrameGraphNode); |
367 | return dumpFG(n: q).join(sep: QLatin1Char('\n')); |
368 | } |
369 | |
370 | QStringList QFrameGraphNodePrivate::dumpFrameGraphPaths() const |
371 | { |
372 | Q_Q(const QFrameGraphNode); |
373 | QStringList result; |
374 | dumpFGPaths(n: q, result); |
375 | return result; |
376 | } |
377 | |
378 | QStringList QFrameGraphNodePrivate::dumpFrameGraphFilterState() const |
379 | { |
380 | Q_Q(const QFrameGraphNode); |
381 | QStringList result; |
382 | dumpFGFilterState(n: q, result); |
383 | return result; |
384 | } |
385 | |
386 | /*! \internal */ |
387 | QFrameGraphNode::QFrameGraphNode(QFrameGraphNodePrivate &dd, QNode *parent) |
388 | : QNode(dd, parent) |
389 | { |
390 | } |
391 | |
392 | void QFrameGraphNode::onParentChanged(QObject *) |
393 | { |
394 | // Direct sync update request |
395 | Q_D(QFrameGraphNode); |
396 | d->update(); |
397 | } |
398 | |
399 | } // namespace Qt3DRender |
400 | |
401 | QT_END_NAMESPACE |
402 | |
403 | #include "moc_qframegraphnode.cpp" |
404 | |