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
44class tst_Renderer : public QObject
45{
46 Q_OBJECT
47public :
48 tst_Renderer() {}
49 ~tst_Renderer() {}
50
51private 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
304QTEST_MAIN(tst_Renderer)
305
306#include "tst_renderer.moc"
307

source code of qt3d/tests/auto/render/opengl/renderer/tst_renderer.cpp