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