1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 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:GPL-EXCEPT$ |
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 General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include <QtTest/QtTest> |
30 | #include <QMutex> |
31 | #include <QWaitCondition> |
32 | #include <QThread> |
33 | #include <renderer_p.h> |
34 | #include <renderview_p.h> |
35 | #include <renderviewbuilder_p.h> |
36 | #include <renderqueue_p.h> |
37 | #include <Qt3DRender/private/viewportnode_p.h> |
38 | #include <Qt3DRender/private/offscreensurfacehelper_p.h> |
39 | #include <Qt3DRender/private/qrenderaspect_p.h> |
40 | #include <Qt3DRender/qmaterial.h> |
41 | |
42 | #include "testaspect.h" |
43 | |
44 | class tst_Renderer : public QObject |
45 | { |
46 | Q_OBJECT |
47 | public : |
48 | tst_Renderer() {} |
49 | ~tst_Renderer() {} |
50 | |
51 | private Q_SLOTS: |
52 | |
53 | void checkPreRenderBinJobs() |
54 | { |
55 | // GIVEN |
56 | Qt3DRender::Render::NodeManagers nodeManagers; |
57 | Qt3DRender::Render::OpenGL::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous); |
58 | Qt3DRender::Render::OffscreenSurfaceHelper offscreenHelper(&renderer); |
59 | Qt3DRender::Render::RenderSettings settings; |
60 | // owned by FG manager |
61 | Qt3DRender::Render::ViewportNode *fgRoot = new Qt3DRender::Render::ViewportNode(); |
62 | const Qt3DCore::QNodeId fgRootId = Qt3DCore::QNodeId::createId(); |
63 | |
64 | nodeManagers.frameGraphManager()->appendNode(id: fgRootId, node: fgRoot); |
65 | settings.setActiveFrameGraphId(fgRootId); |
66 | |
67 | renderer.setNodeManagers(&nodeManagers); |
68 | renderer.setSettings(&settings); |
69 | renderer.setOffscreenSurfaceHelper(&offscreenHelper); |
70 | renderer.initialize(); |
71 | |
72 | // Ensure invoke calls are performed |
73 | QCoreApplication::processEvents(); |
74 | |
75 | // WHEN (nothing dirty, no buffers, no layers to be rebuilt, no materials to be rebuilt) |
76 | QVector<Qt3DCore::QAspectJobPtr> jobs = renderer.preRenderingJobs(); |
77 | |
78 | // THEN |
79 | QCOMPARE(jobs.size(), 0); |
80 | |
81 | // WHEN |
82 | renderer.m_sendBufferCaptureJob->addRequest(request: {Qt3DCore::QNodeId(), {}}); |
83 | jobs = renderer.preRenderingJobs(); |
84 | |
85 | // THEN |
86 | QCOMPARE(jobs.size(), 1); // SendBufferCaptureJob |
87 | // Note: pending render buffer captures are only cleared when the job is run |
88 | |
89 | // WHEN |
90 | renderer.m_updatedSetFences.push_back(t: {Qt3DCore::QNodeId(), nullptr}); |
91 | jobs = renderer.preRenderingJobs(); |
92 | |
93 | // THEN |
94 | QCOMPARE(jobs.size(), |
95 | 1 + // SendBufferCaptureJob |
96 | 1); // SendSetFenceHandlesJob |
97 | // Note: pending set fence handles are only cleared when the job is run |
98 | |
99 | // Properly shutdown command thread |
100 | renderer.shutdown(); |
101 | } |
102 | |
103 | void checkRenderBinJobs() |
104 | { |
105 | // GIVEN |
106 | Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity(); |
107 | QScopedPointer<Qt3DRender::TestAspect> aspect(new Qt3DRender::TestAspect(rootEntity)); |
108 | auto daspect = Qt3DRender::QRenderAspectPrivate::get(q: aspect.data()); |
109 | |
110 | Qt3DRender::Render::NodeManagers nodeManagers; |
111 | auto &renderer = *(static_cast<Qt3DRender::Render::OpenGL::Renderer *>(daspect->m_renderer)); |
112 | Qt3DRender::Render::OpenGL::RenderQueue *renderQueue = renderer.renderQueue(); |
113 | Qt3DRender::Render::OffscreenSurfaceHelper offscreenHelper(&renderer); |
114 | Qt3DRender::Render::RenderSettings settings; |
115 | // owned by FG manager |
116 | Qt3DRender::Render::ViewportNode *fgRoot = new Qt3DRender::Render::ViewportNode(); |
117 | const Qt3DCore::QNodeId fgRootId = Qt3DCore::QNodeId::createId(); |
118 | |
119 | // Create fake material so that we crean materialGathererJobs |
120 | const Qt3DCore::QNodeId materialId = Qt3DCore::QNodeId::createId(); |
121 | nodeManagers.materialManager()->getOrCreateResource(id: materialId); |
122 | |
123 | nodeManagers.frameGraphManager()->appendNode(id: fgRootId, node: fgRoot); |
124 | settings.setActiveFrameGraphId(fgRootId); |
125 | |
126 | renderer.setNodeManagers(&nodeManagers); |
127 | renderer.setSettings(&settings); |
128 | renderer.setOffscreenSurfaceHelper(&offscreenHelper); |
129 | renderer.initialize(); |
130 | |
131 | // Ensure invoke calls are performed |
132 | QCoreApplication::processEvents(); |
133 | |
134 | // NOTE: FilterCompatibleTechniqueJob and ShaderGathererJob cannot run because the context |
135 | // is not initialized in this test |
136 | |
137 | const int renderViewBuilderMaterialCacheJobCount = 1 + 1; |
138 | // syncMaterialGathererJob |
139 | // n * materialGathererJob (where n depends on the numbers of available threads and the number of materials) |
140 | const int layerCacheJobCount = 2; |
141 | // filterEntityByLayerJob, |
142 | // syncFilterEntityByLayerJob |
143 | |
144 | const int singleRenderViewCommandRebuildJobCount = 1 + Qt3DRender::Render::OpenGL::RenderViewBuilder::defaultJobCount(); |
145 | |
146 | const int singleRenderViewJobCount = 8 + 1 * Qt3DRender::Render::OpenGL::RenderViewBuilder::defaultJobCount(); |
147 | // RenderViewBuilder renderViewJob, |
148 | // syncRenderViewInitializationJob, |
149 | // syncFrustumCullingJob, |
150 | // filterProximityJob, |
151 | // setClearDrawBufferIndexJob, |
152 | // frustumCullingJob, |
153 | // syncRenderCommandUpdateJob, |
154 | // syncRenderViewCommandPostUpdateJob |
155 | // n * (RenderViewCommandBuildJobs) |
156 | |
157 | // WHEN |
158 | QVector<Qt3DCore::QAspectJobPtr> jobs = renderer.renderBinJobs(); |
159 | |
160 | // THEN -> AllDirty |
161 | // (Renderer is not initialized so FilterCompatibleTechniqueJob |
162 | // and ShaderGathererJob are not added here) |
163 | QCOMPARE(jobs.size(), |
164 | 1 + // UpdateShaderDataTransform |
165 | 1 + // cleanupJob |
166 | 1 + // VAOGatherer |
167 | 1 + // BufferGathererJob |
168 | 1 + // TexturesGathererJob |
169 | 1 + // LightGathererJob |
170 | 1 + // RenderableEntityFilterJob |
171 | 1 + // ComputableEntityFilterJob |
172 | singleRenderViewJobCount + |
173 | singleRenderViewCommandRebuildJobCount + |
174 | layerCacheJobCount + |
175 | renderViewBuilderMaterialCacheJobCount); |
176 | |
177 | renderer.clearDirtyBits(changes: Qt3DRender::Render::AbstractRenderer::AllDirty); |
178 | renderQueue->reset(); |
179 | |
180 | // WHEN (nothing dirty, no buffers, no layers to be rebuilt, no materials to be rebuilt) (Nothing in cache) |
181 | renderer.markDirty(changes: Qt3DRender::Render::AbstractRenderer::FrameGraphDirty, node: nullptr); |
182 | jobs = renderer.renderBinJobs(); |
183 | |
184 | // THEN (level |
185 | QCOMPARE(jobs.size(), |
186 | 1 + // cleanupJob |
187 | 1 + // VAOGatherer |
188 | singleRenderViewJobCount + |
189 | singleRenderViewCommandRebuildJobCount + |
190 | renderViewBuilderMaterialCacheJobCount + |
191 | layerCacheJobCount); |
192 | |
193 | renderer.clearDirtyBits(changes: Qt3DRender::Render::AbstractRenderer::AllDirty); |
194 | renderQueue->reset(); |
195 | |
196 | // WHEN (nothing dirty, no buffers, no layers to be rebuilt, no materials to be rebuilt) (RV leaf in cache) |
197 | renderer.markDirty(changes: Qt3DRender::Render::AbstractRenderer::FrameGraphDirty, node: nullptr); |
198 | renderer.cache()->leafNodeCache[renderer.m_frameGraphLeaves.first()] = {}; |
199 | jobs = renderer.renderBinJobs(); |
200 | |
201 | // THEN (level |
202 | QCOMPARE(jobs.size(), |
203 | 1 + // cleanupJob |
204 | 1 + // VAOGatherer |
205 | singleRenderViewJobCount + |
206 | singleRenderViewCommandRebuildJobCount + |
207 | renderViewBuilderMaterialCacheJobCount + |
208 | layerCacheJobCount); |
209 | |
210 | renderer.clearDirtyBits(changes: Qt3DRender::Render::AbstractRenderer::AllDirty); |
211 | renderQueue->reset(); |
212 | |
213 | // WHEN |
214 | renderer.markDirty(changes: Qt3DRender::Render::AbstractRenderer::EntityEnabledDirty, node: nullptr); |
215 | jobs = renderer.renderBinJobs(); |
216 | |
217 | // THEN (level |
218 | QCOMPARE(jobs.size(), |
219 | 1 + // cleanupJob |
220 | 1 + // VAOGatherer |
221 | singleRenderViewJobCount + |
222 | layerCacheJobCount); |
223 | |
224 | renderer.clearDirtyBits(changes: Qt3DRender::Render::AbstractRenderer::AllDirty); |
225 | renderQueue->reset(); |
226 | |
227 | // WHEN |
228 | renderer.markDirty(changes: Qt3DRender::Render::AbstractRenderer::TransformDirty, node: nullptr); |
229 | jobs = renderer.renderBinJobs(); |
230 | |
231 | // THEN (level |
232 | QCOMPARE(jobs.size(), |
233 | 1 + // cleanupJob |
234 | 1 + // VAOGatherer |
235 | 1 + // UpdateShaderDataTransform |
236 | singleRenderViewJobCount); |
237 | |
238 | renderer.clearDirtyBits(changes: Qt3DRender::Render::AbstractRenderer::AllDirty); |
239 | renderQueue->reset(); |
240 | |
241 | // WHEN |
242 | renderer.markDirty(changes: Qt3DRender::Render::AbstractRenderer::MaterialDirty, node: nullptr); |
243 | jobs = renderer.renderBinJobs(); |
244 | |
245 | // THEN (level |
246 | QCOMPARE(jobs.size(), |
247 | 1 + // cleanupJob |
248 | 1 + // VAOGatherer |
249 | singleRenderViewJobCount + |
250 | singleRenderViewCommandRebuildJobCount + |
251 | renderViewBuilderMaterialCacheJobCount); |
252 | |
253 | renderer.clearDirtyBits(changes: Qt3DRender::Render::AbstractRenderer::AllDirty); |
254 | renderQueue->reset(); |
255 | |
256 | // WHEN |
257 | renderer.markDirty(changes: Qt3DRender::Render::AbstractRenderer::GeometryDirty, node: nullptr); |
258 | jobs = renderer.renderBinJobs(); |
259 | |
260 | // THEN (level |
261 | QCOMPARE(jobs.size(), |
262 | 1 + // cleanupJob |
263 | 1 + // VAOGatherer |
264 | 1 + // RenderableEntityFilterPtr |
265 | singleRenderViewCommandRebuildJobCount + |
266 | singleRenderViewJobCount); |
267 | |
268 | renderer.clearDirtyBits(changes: Qt3DRender::Render::AbstractRenderer::AllDirty); |
269 | renderQueue->reset(); |
270 | |
271 | // WHEN |
272 | renderer.markDirty(changes: Qt3DRender::Render::AbstractRenderer::BuffersDirty, node: nullptr); |
273 | jobs = renderer.renderBinJobs(); |
274 | |
275 | // THEN (level |
276 | QCOMPARE(jobs.size(), |
277 | 1 + // cleanupJob |
278 | 1 + // VAOGatherer |
279 | 1 + // BufferGathererJob |
280 | singleRenderViewJobCount); |
281 | |
282 | renderer.clearDirtyBits(changes: Qt3DRender::Render::AbstractRenderer::AllDirty); |
283 | renderQueue->reset(); |
284 | |
285 | // WHEN |
286 | renderer.markDirty(changes: Qt3DRender::Render::AbstractRenderer::TexturesDirty, node: nullptr); |
287 | jobs = renderer.renderBinJobs(); |
288 | |
289 | // THEN (level |
290 | QCOMPARE(jobs.size(), |
291 | 1 + // cleanupJob |
292 | 1 + // VAOGatherer |
293 | 1 + // TexturesGathererJob |
294 | singleRenderViewJobCount); |
295 | |
296 | renderer.clearDirtyBits(changes: Qt3DRender::Render::AbstractRenderer::AllDirty); |
297 | renderQueue->reset(); |
298 | |
299 | // Properly shutdown command thread |
300 | renderer.shutdown(); |
301 | } |
302 | }; |
303 | |
304 | QTEST_MAIN(tst_Renderer) |
305 | |
306 | #include "tst_renderer.moc" |
307 | |