1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2015 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 | // TODO Remove in Qt6 |
30 | #include <QtCore/qcompilerdetection.h> |
31 | QT_WARNING_DISABLE_DEPRECATED |
32 | |
33 | #include <QtTest/QTest> |
34 | #include <qbackendnodetester.h> |
35 | #include <Qt3DRender/private/buffer_p.h> |
36 | #include <Qt3DRender/private/qbuffer_p.h> |
37 | #include <Qt3DRender/private/buffermanager_p.h> |
38 | #include <Qt3DCore/qpropertyupdatedchange.h> |
39 | #include <Qt3DCore/private/qbackendnode_p.h> |
40 | #include "testpostmanarbiter.h" |
41 | #include "testrenderer.h" |
42 | |
43 | class TestFunctor : public Qt3DRender::QBufferDataGenerator |
44 | { |
45 | public: |
46 | explicit TestFunctor(int size) |
47 | : m_size(size) |
48 | {} |
49 | |
50 | QByteArray operator ()() final |
51 | { |
52 | return QByteArrayLiteral("454" ); |
53 | } |
54 | |
55 | bool operator ==(const Qt3DRender::QBufferDataGenerator &other) const final |
56 | { |
57 | const TestFunctor *otherFunctor = Qt3DRender::functor_cast<TestFunctor>(other: &other); |
58 | if (otherFunctor != nullptr) |
59 | return otherFunctor->m_size == m_size; |
60 | return false; |
61 | } |
62 | |
63 | QT3D_FUNCTOR(TestFunctor) |
64 | |
65 | private: |
66 | int m_size; |
67 | }; |
68 | |
69 | class tst_RenderBuffer : public Qt3DCore::QBackendNodeTester |
70 | { |
71 | Q_OBJECT |
72 | |
73 | private Q_SLOTS: |
74 | |
75 | void checkPeerPropertyMirroring() |
76 | { |
77 | // GIVEN |
78 | Qt3DRender::Render::Buffer renderBuffer; |
79 | Qt3DRender::QBuffer buffer; |
80 | Qt3DRender::Render::BufferManager bufferManager; |
81 | TestRenderer renderer; |
82 | |
83 | buffer.setUsage(Qt3DRender::QBuffer::DynamicCopy); |
84 | buffer.setData(QByteArrayLiteral("Corvette" )); |
85 | buffer.setDataGenerator(Qt3DRender::QBufferDataGeneratorPtr(new TestFunctor(883))); |
86 | |
87 | // WHEN |
88 | renderBuffer.setRenderer(&renderer); |
89 | renderBuffer.setManager(&bufferManager); |
90 | simulateInitializationSync(frontend: &buffer, backend: &renderBuffer); |
91 | |
92 | // THEN |
93 | QCOMPARE(renderBuffer.peerId(), buffer.id()); |
94 | QCOMPARE(renderBuffer.isDirty(), true); |
95 | QCOMPARE(renderBuffer.usage(), buffer.usage()); |
96 | QCOMPARE(renderBuffer.data(), buffer.data()); |
97 | QCOMPARE(renderBuffer.dataGenerator(), buffer.dataGenerator()); |
98 | QVERIFY(*renderBuffer.dataGenerator() == *buffer.dataGenerator()); |
99 | QCOMPARE(renderBuffer.pendingBufferUpdates().size(), 1); |
100 | QCOMPARE(renderBuffer.pendingBufferUpdates().first().offset, -1); |
101 | } |
102 | |
103 | void checkInitialAndCleanedUpState() |
104 | { |
105 | // GIVEN |
106 | Qt3DRender::Render::Buffer backendBuffer; |
107 | Qt3DRender::Render::BufferManager bufferManager; |
108 | TestRenderer renderer; |
109 | |
110 | // THEN |
111 | QCOMPARE(backendBuffer.isDirty(), false); |
112 | QCOMPARE(backendBuffer.usage(), Qt3DRender::QBuffer::StaticDraw); |
113 | QVERIFY(backendBuffer.data().isEmpty()); |
114 | QVERIFY(backendBuffer.peerId().isNull()); |
115 | QVERIFY(backendBuffer.dataGenerator().isNull()); |
116 | QVERIFY(backendBuffer.pendingBufferUpdates().empty()); |
117 | |
118 | // GIVEN |
119 | Qt3DRender::QBuffer frontendBuffer; |
120 | |
121 | // WHEN |
122 | backendBuffer.setManager(&bufferManager); |
123 | backendBuffer.setRenderer(&renderer); |
124 | simulateInitializationSync(frontend: &frontendBuffer, backend: &backendBuffer); |
125 | |
126 | // THEN |
127 | QCOMPARE(backendBuffer.isDirty(), true); |
128 | QCOMPARE(backendBuffer.usage(), Qt3DRender::QBuffer::StaticDraw); |
129 | QVERIFY(backendBuffer.data().isEmpty()); |
130 | QVERIFY(backendBuffer.dataGenerator().isNull()); |
131 | QVERIFY(backendBuffer.pendingBufferUpdates().empty()); |
132 | |
133 | // WHEN |
134 | frontendBuffer.setUsage(Qt3DRender::QBuffer::DynamicCopy); |
135 | frontendBuffer.setData(QByteArrayLiteral("C7KR4" )); |
136 | frontendBuffer.setDataGenerator(Qt3DRender::QBufferDataGeneratorPtr(new TestFunctor(73))); |
137 | backendBuffer.syncFromFrontEnd(frontEnd: &frontendBuffer, firstTime: false); |
138 | |
139 | // THEN |
140 | QCOMPARE(backendBuffer.usage(), Qt3DRender::QBuffer::DynamicCopy); |
141 | QCOMPARE(backendBuffer.isDirty(), true); |
142 | QCOMPARE(backendBuffer.data(), QByteArrayLiteral("C7KR4" )); |
143 | QVERIFY(!backendBuffer.dataGenerator().isNull()); |
144 | QVERIFY(!backendBuffer.pendingBufferUpdates().empty()); |
145 | |
146 | // WHEN |
147 | frontendBuffer.updateData(offset: 2, QByteArrayLiteral("LS5" )); |
148 | backendBuffer.syncFromFrontEnd(frontEnd: &frontendBuffer, firstTime: false); |
149 | |
150 | // THEN |
151 | QCOMPARE(backendBuffer.isDirty(), true); |
152 | QCOMPARE(backendBuffer.data(), QByteArrayLiteral("C7LS5" )); |
153 | // WHEN |
154 | backendBuffer.cleanup(); |
155 | |
156 | // THEN |
157 | QCOMPARE(backendBuffer.isDirty(), false); |
158 | QCOMPARE(backendBuffer.usage(), Qt3DRender::QBuffer::StaticDraw); |
159 | QVERIFY(backendBuffer.data().isEmpty()); |
160 | QVERIFY(backendBuffer.dataGenerator().isNull()); |
161 | QVERIFY(backendBuffer.pendingBufferUpdates().empty()); |
162 | } |
163 | |
164 | |
165 | void checkForceFullUploadOnFirstTime() |
166 | { |
167 | // GIVEN |
168 | Qt3DRender::Render::Buffer backendBuffer; |
169 | Qt3DRender::Render::BufferManager bufferManager; |
170 | TestRenderer renderer; |
171 | Qt3DRender::QBuffer frontendBuffer; |
172 | |
173 | QByteArray data("111456789\0" ); |
174 | |
175 | frontendBuffer.setData(data); |
176 | frontendBuffer.updateData(offset: 1, bytes: QByteArray("23\0" )); |
177 | |
178 | // THEN |
179 | QCOMPARE(frontendBuffer.data(), QByteArray("123456789\0" )); |
180 | |
181 | // WHEN |
182 | backendBuffer.setManager(&bufferManager); |
183 | backendBuffer.setRenderer(&renderer); |
184 | simulateInitializationSync(frontend: &frontendBuffer, backend: &backendBuffer); |
185 | |
186 | // THEN |
187 | QCOMPARE(backendBuffer.pendingBufferUpdates().size(), 1); |
188 | Qt3DRender::QBufferUpdate fullUpdate = backendBuffer.pendingBufferUpdates().first(); |
189 | QCOMPARE(fullUpdate.offset, -1); |
190 | QVERIFY(fullUpdate.data.isEmpty()); |
191 | QCOMPARE(frontendBuffer.data(), backendBuffer.data()); |
192 | |
193 | backendBuffer.pendingBufferUpdates().clear(); |
194 | |
195 | // WHEN |
196 | frontendBuffer.updateData(offset: 1, bytes: QByteArray("00\0" )); |
197 | backendBuffer.syncFromFrontEnd(frontEnd: &frontendBuffer, firstTime: false); |
198 | |
199 | // THEN |
200 | QCOMPARE(frontendBuffer.data(), QByteArray("100456789\0" )); |
201 | QCOMPARE(backendBuffer.pendingBufferUpdates().size(), 1); |
202 | fullUpdate = backendBuffer.pendingBufferUpdates().first(); |
203 | QCOMPARE(fullUpdate.offset, 1); |
204 | QCOMPARE(fullUpdate.data, QByteArray("00\0" )); |
205 | QCOMPARE(frontendBuffer.data(), backendBuffer.data()); |
206 | |
207 | // WHEN |
208 | frontendBuffer.updateData(offset: 1, bytes: QByteArray("22\0" )); |
209 | backendBuffer.syncFromFrontEnd(frontEnd: &frontendBuffer, firstTime: true); |
210 | |
211 | // THEN |
212 | QCOMPARE(frontendBuffer.data(), QByteArray("122456789\0" )); |
213 | fullUpdate = backendBuffer.pendingBufferUpdates().first(); |
214 | QCOMPARE(fullUpdate.offset, -1); |
215 | QVERIFY(fullUpdate.data.isEmpty()); |
216 | QCOMPARE(frontendBuffer.data(), backendBuffer.data()); |
217 | } |
218 | |
219 | void checkPropertyChanges() |
220 | { |
221 | // GIVEN |
222 | TestRenderer renderer; |
223 | Qt3DRender::QBuffer frontendBuffer; |
224 | Qt3DRender::Render::Buffer backendBuffer; |
225 | backendBuffer.setRenderer(&renderer); |
226 | simulateInitializationSync(frontend: &frontendBuffer, backend: &backendBuffer); |
227 | |
228 | // THEN |
229 | QVERIFY(backendBuffer.data().isEmpty()); |
230 | QVERIFY(backendBuffer.usage() != Qt3DRender::QBuffer::DynamicRead); |
231 | QVERIFY(!backendBuffer.isDirty()); |
232 | QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::BuffersDirty); |
233 | renderer.clearDirtyBits(changes: Qt3DRender::Render::AbstractRenderer::AllDirty); |
234 | |
235 | // WHEN |
236 | frontendBuffer.setUsage(Qt3DRender::QBuffer::DynamicRead); |
237 | backendBuffer.syncFromFrontEnd(frontEnd: &frontendBuffer, firstTime: false); |
238 | |
239 | // THEN |
240 | QCOMPARE(backendBuffer.usage(), Qt3DRender::QBuffer::DynamicRead); |
241 | QVERIFY(backendBuffer.isDirty()); |
242 | |
243 | QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::BuffersDirty); |
244 | renderer.clearDirtyBits(changes: Qt3DRender::Render::AbstractRenderer::AllDirty); |
245 | |
246 | backendBuffer.unsetDirty(); |
247 | QVERIFY(!backendBuffer.isDirty()); |
248 | |
249 | // WHEN |
250 | frontendBuffer.setData(QByteArrayLiteral("LS9SL" )); |
251 | backendBuffer.syncFromFrontEnd(frontEnd: &frontendBuffer, firstTime: false); |
252 | |
253 | // THEN |
254 | QCOMPARE(backendBuffer.data(), QByteArrayLiteral("LS9SL" )); |
255 | QVERIFY(backendBuffer.isDirty()); |
256 | QCOMPARE(backendBuffer.pendingBufferUpdates().size(), 1); |
257 | QCOMPARE(backendBuffer.pendingBufferUpdates().first().offset, -1); |
258 | |
259 | backendBuffer.pendingBufferUpdates().clear(); |
260 | |
261 | QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::BuffersDirty); |
262 | renderer.clearDirtyBits(changes: Qt3DRender::Render::AbstractRenderer::AllDirty); |
263 | |
264 | backendBuffer.unsetDirty(); |
265 | QVERIFY(!backendBuffer.isDirty()); |
266 | |
267 | // WHEN |
268 | Qt3DRender::QBufferDataGeneratorPtr functor(new TestFunctor(355)); |
269 | frontendBuffer.setDataGenerator(functor); |
270 | backendBuffer.syncFromFrontEnd(frontEnd: &frontendBuffer, firstTime: false); |
271 | |
272 | // THEN |
273 | QCOMPARE(backendBuffer.dataGenerator(), functor); |
274 | QVERIFY(backendBuffer.isDirty()); |
275 | |
276 | QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::BuffersDirty); |
277 | renderer.clearDirtyBits(changes: Qt3DRender::Render::AbstractRenderer::AllDirty); |
278 | |
279 | backendBuffer.unsetDirty(); |
280 | QVERIFY(!backendBuffer.isDirty()); |
281 | |
282 | // WHEN |
283 | frontendBuffer.setSyncData(true); |
284 | backendBuffer.syncFromFrontEnd(frontEnd: &frontendBuffer, firstTime: false); |
285 | |
286 | // THEN |
287 | QCOMPARE(backendBuffer.isSyncData(), true); |
288 | QVERIFY(!backendBuffer.isDirty()); |
289 | |
290 | QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::BuffersDirty); |
291 | renderer.clearDirtyBits(changes: Qt3DRender::Render::AbstractRenderer::AllDirty); |
292 | |
293 | // WHEN |
294 | TestArbiter arbiter; |
295 | Qt3DCore::QBackendNodePrivate::get(n: &backendBuffer)->setArbiter(&arbiter); |
296 | backendBuffer.executeFunctor(); |
297 | |
298 | // THEN |
299 | QCOMPARE(arbiter.events.count(), 0); |
300 | QCOMPARE(backendBuffer.pendingBufferUpdates().size(), 1); |
301 | QCOMPARE(backendBuffer.pendingBufferUpdates().first().offset, -1); |
302 | |
303 | arbiter.events.clear(); |
304 | backendBuffer.pendingBufferUpdates().clear(); |
305 | |
306 | // WHEN |
307 | frontendBuffer.updateData(offset: 2, QByteArrayLiteral("LS5" )); |
308 | backendBuffer.syncFromFrontEnd(frontEnd: &frontendBuffer, firstTime: false); |
309 | |
310 | // THEN |
311 | QVERIFY(!backendBuffer.pendingBufferUpdates().empty()); |
312 | QCOMPARE(backendBuffer.pendingBufferUpdates().first().offset, 2); |
313 | QVERIFY(backendBuffer.isDirty()); |
314 | |
315 | QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::BuffersDirty); |
316 | renderer.clearDirtyBits(changes: Qt3DRender::Render::AbstractRenderer::AllDirty); |
317 | |
318 | backendBuffer.unsetDirty(); |
319 | QVERIFY(!backendBuffer.isDirty()); |
320 | } |
321 | |
322 | void checkBufferManagerReferenceCount() |
323 | { |
324 | // GIVEN |
325 | Qt3DRender::Render::Buffer renderBuffer; |
326 | Qt3DRender::QBuffer buffer; |
327 | Qt3DRender::Render::BufferManager bufferManager; |
328 | TestRenderer renderer; |
329 | |
330 | // WHEN |
331 | renderBuffer.setRenderer(&renderer); |
332 | renderBuffer.setManager(&bufferManager); |
333 | simulateInitializationSync(frontend: &buffer, backend: &renderBuffer); |
334 | |
335 | // THEN |
336 | QVERIFY(bufferManager.takeBuffersToRelease().empty()); |
337 | |
338 | // WHEN |
339 | bufferManager.removeBufferReference(bufferId: renderBuffer.peerId()); |
340 | auto buffers = bufferManager.takeBuffersToRelease(); |
341 | |
342 | // THEN |
343 | QVERIFY(buffers.size() == 1); |
344 | QVERIFY(buffers.first() == renderBuffer.peerId()); |
345 | QVERIFY(bufferManager.takeBuffersToRelease().empty()); |
346 | } |
347 | |
348 | void checkSetRendererDirtyOnInitialization() |
349 | { |
350 | // GIVEN |
351 | Qt3DRender::Render::Buffer renderBuffer; |
352 | Qt3DRender::QBuffer buffer; |
353 | Qt3DRender::Render::BufferManager bufferManager; |
354 | TestRenderer renderer; |
355 | |
356 | renderBuffer.setRenderer(&renderer); |
357 | renderBuffer.setManager(&bufferManager); |
358 | |
359 | // THEN |
360 | QCOMPARE(renderer.dirtyBits(), 0); |
361 | |
362 | // WHEN |
363 | simulateInitializationSync(frontend: &buffer, backend: &renderBuffer); |
364 | |
365 | // THEN |
366 | QCOMPARE(renderer.dirtyBits(), Qt3DRender::Render::AbstractRenderer::BuffersDirty); |
367 | } |
368 | |
369 | void checkHandlesMultipleUpdates() |
370 | { |
371 | // GIVEN |
372 | Qt3DRender::Render::Buffer renderBuffer; |
373 | Qt3DRender::QBuffer buffer; |
374 | Qt3DRender::Render::BufferManager bufferManager; |
375 | TestRenderer renderer; |
376 | |
377 | const QByteArray initData("000000" ); |
378 | buffer.setData(initData); |
379 | |
380 | renderBuffer.setRenderer(&renderer); |
381 | renderBuffer.setManager(&bufferManager); |
382 | |
383 | simulateInitializationSync(frontend: &buffer, backend: &renderBuffer); |
384 | |
385 | // THEN |
386 | QCOMPARE(renderBuffer.data(), initData); |
387 | renderBuffer.pendingBufferUpdates().clear(); |
388 | |
389 | // WHEN |
390 | buffer.updateData(offset: 0, bytes: QByteArray("012" )); |
391 | buffer.updateData(offset: 3, bytes: QByteArray("345" )); |
392 | renderBuffer.syncFromFrontEnd(frontEnd: &buffer, firstTime: false); |
393 | |
394 | // THEN |
395 | QCOMPARE(renderBuffer.pendingBufferUpdates().size(), 2); |
396 | QCOMPARE(renderBuffer.pendingBufferUpdates().first().offset, 0); |
397 | QCOMPARE(renderBuffer.pendingBufferUpdates().first().data, QByteArray("012" )); |
398 | QCOMPARE(renderBuffer.pendingBufferUpdates().last().offset, 3); |
399 | QCOMPARE(renderBuffer.pendingBufferUpdates().last().data, QByteArray("345" )); |
400 | QCOMPARE(renderBuffer.data(), QByteArray("012345" )); |
401 | } |
402 | }; |
403 | |
404 | |
405 | QTEST_APPLESS_MAIN(tst_RenderBuffer) |
406 | |
407 | #include "tst_buffer.moc" |
408 | |