1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite 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 <QtCore/QString>
30#include <QtTest/QtTest>
31
32#include <QtGui/QOffscreenSurface>
33#include <QtGui/QOpenGLContext>
34#include <QtQuick/qsgnode.h>
35#include <QtQuick/private/qsgbatchrenderer_p.h>
36#include <QtQuick/private/qsgnodeupdater_p.h>
37#include <QtQuick/private/qsgrenderloop_p.h>
38#include <QtQuick/private/qsgcontext_p.h>
39
40#include <QtQuick/qsgsimplerectnode.h>
41#include <QtQuick/qsgsimpletexturenode.h>
42#include <QtQuick/private/qsgplaintexture_p.h>
43
44#include <QtGui/private/qguiapplication_p.h>
45#include <QtGui/qpa/qplatformintegration.h>
46
47QT_BEGIN_NAMESPACE
48inline bool operator==(const QSGGeometry::TexturedPoint2D& l, const QSGGeometry::TexturedPoint2D& r)
49{
50 return l.x == r.x && l.y == r.y && l.tx == r.tx && l.ty == r.ty;
51}
52QT_END_NAMESPACE
53
54class NodesTest : public QObject
55{
56 Q_OBJECT
57
58public:
59 NodesTest();
60
61private Q_SLOTS:
62 void initTestCase();
63 void cleanupTestCase();
64
65 // Root nodes
66 void propegate();
67 void propegateWithMultipleRoots();
68
69 // Opacity nodes
70 void basicOpacityNode();
71 void opacityPropegation();
72
73 void isBlockedCheck();
74
75 void textureNodeTextureOwnership();
76 void textureNodeRect();
77
78private:
79 QOffscreenSurface *surface = nullptr;
80 QOpenGLContext *context = nullptr;
81 QSGDefaultRenderContext *renderContext = nullptr;
82};
83
84void NodesTest::initTestCase()
85{
86 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::OpenGL))
87 QSKIP("OpenGL not supported by the platform");
88
89 QSGRenderLoop *renderLoop = QSGRenderLoop::instance();
90
91 surface = new QOffscreenSurface;
92 surface->create();
93 QVERIFY(surface->isValid());
94
95 context = new QOpenGLContext();
96 QVERIFY(context->create());
97 QVERIFY(context->makeCurrent(surface));
98
99 auto rc = renderLoop->createRenderContext(renderLoop->sceneGraphContext());
100 renderContext = static_cast<QSGDefaultRenderContext *>(rc);
101 QVERIFY(renderContext);
102 QSGDefaultRenderContext::InitParams rcParams;
103 rcParams.openGLContext = context;
104 rcParams.initialSurfacePixelSize = QSize(512, 512); // dummy, make up something
105 renderContext->initialize(params: &rcParams);
106 QVERIFY(renderContext->isValid());
107}
108
109void NodesTest::cleanupTestCase()
110{
111 if (renderContext)
112 renderContext->invalidate();
113 if (context)
114 context->doneCurrent();
115 delete context;
116 delete surface;
117}
118
119class DummyRenderer : public QSGBatchRenderer::Renderer
120{
121public:
122 DummyRenderer(QSGRootNode *root, QSGDefaultRenderContext *renderContext)
123 : QSGBatchRenderer::Renderer(renderContext)
124 {
125 setRootNode(root);
126 }
127
128 void render() override {
129 ++renderCount;
130 renderingOrder = ++globalRendereringOrder;
131 }
132
133 void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override {
134 changedNode = node;
135 changedState = state;
136 QSGBatchRenderer::Renderer::nodeChanged(node, state);
137 }
138
139 QSGNode *changedNode = nullptr;
140 QSGNode::DirtyState changedState;
141
142 int renderCount = 0;
143 int renderingOrder = 0;
144 static int globalRendereringOrder;
145};
146
147int DummyRenderer::globalRendereringOrder;
148
149NodesTest::NodesTest()
150{
151}
152
153void NodesTest::propegate()
154{
155 QSGRootNode root;
156 QSGNode child; child.setFlag(QSGNode::OwnedByParent, false);
157 root.appendChildNode(node: &child);
158
159 DummyRenderer renderer(&root, renderContext);
160
161 child.markDirty(bits: QSGNode::DirtyGeometry);
162
163 QCOMPARE(&child, renderer.changedNode);
164 QCOMPARE((int) renderer.changedState, (int) QSGNode::DirtyGeometry);
165}
166
167
168void NodesTest::propegateWithMultipleRoots()
169{
170 QSGRootNode root1;
171 QSGNode child2; child2.setFlag(QSGNode::OwnedByParent, false);
172 QSGRootNode root3; root3.setFlag(QSGNode::OwnedByParent, false);
173 QSGNode child4; child4.setFlag(QSGNode::OwnedByParent, false);
174
175 root1.appendChildNode(node: &child2);
176 child2.appendChildNode(node: &root3);
177 root3.appendChildNode(node: &child4);
178
179 DummyRenderer ren1(&root1, renderContext);
180 DummyRenderer ren2(&root3, renderContext);
181
182 child4.markDirty(bits: QSGNode::DirtyGeometry);
183
184 QCOMPARE(ren1.changedNode, &child4);
185 QCOMPARE(ren2.changedNode, &child4);
186
187 QCOMPARE((int) ren1.changedState, (int) QSGNode::DirtyGeometry);
188 QCOMPARE((int) ren2.changedState, (int) QSGNode::DirtyGeometry);
189}
190
191void NodesTest::basicOpacityNode()
192{
193 QSGOpacityNode n;
194 QCOMPARE(n.opacity(), 1.);
195
196 n.setOpacity(0.5);
197 QCOMPARE(n.opacity(), 0.5);
198
199 n.setOpacity(-1);
200 QCOMPARE(n.opacity(), 0.);
201
202 n.setOpacity(2);
203 QCOMPARE(n.opacity(), 1.);
204}
205
206void NodesTest::opacityPropegation()
207{
208 QSGRootNode root;
209 QSGOpacityNode *a = new QSGOpacityNode;
210 QSGOpacityNode *b = new QSGOpacityNode;
211 QSGOpacityNode *c = new QSGOpacityNode;
212
213 QSGSimpleRectNode *geometry = new QSGSimpleRectNode;
214 geometry->setRect(x: 0, y: 0, w: 100, h: 100);
215
216 DummyRenderer renderer(&root, renderContext);
217
218 root.appendChildNode(node: a);
219 a->appendChildNode(node: b);
220 b->appendChildNode(node: c);
221 c->appendChildNode(node: geometry);
222
223 a->setOpacity(0.9);
224 b->setOpacity(0.8);
225 c->setOpacity(0.7);
226
227 renderer.renderScene();
228
229 QCOMPARE(a->combinedOpacity(), 0.9);
230 QCOMPARE(b->combinedOpacity(), 0.9 * 0.8);
231 QCOMPARE(c->combinedOpacity(), 0.9 * 0.8 * 0.7);
232 QCOMPARE(geometry->inheritedOpacity(), 0.9 * 0.8 * 0.7);
233
234 b->setOpacity(0.1);
235 renderer.renderScene();
236
237 QCOMPARE(a->combinedOpacity(), 0.9);
238 QCOMPARE(b->combinedOpacity(), 0.9 * 0.1);
239 QCOMPARE(c->combinedOpacity(), 0.9 * 0.1 * 0.7);
240 QCOMPARE(geometry->inheritedOpacity(), 0.9 * 0.1 * 0.7);
241
242 b->setOpacity(0);
243 renderer.renderScene();
244
245 QVERIFY(b->isSubtreeBlocked());
246
247 // Verify that geometry did not get updated as it is in a blocked
248 // subtree
249 QCOMPARE(c->combinedOpacity(), 0.9 * 0.1 * 0.7);
250 QCOMPARE(geometry->inheritedOpacity(), 0.9 * 0.1 * 0.7);
251}
252
253void NodesTest::isBlockedCheck()
254{
255 QSGRootNode root;
256 QSGOpacityNode *opacity = new QSGOpacityNode();
257 QSGNode *node = new QSGNode();
258
259 root.appendChildNode(node: opacity);
260 opacity->appendChildNode(node);
261
262 QSGNodeUpdater updater;
263
264 opacity->setOpacity(0);
265 QVERIFY(updater.isNodeBlocked(node, &root));
266
267 opacity->setOpacity(1);
268 QVERIFY(!updater.isNodeBlocked(node, &root));
269}
270
271void NodesTest::textureNodeTextureOwnership()
272{
273 { // Check that it is not deleted by default
274 QPointer<QSGTexture> texture(new QSGPlainTexture());
275
276 QSGSimpleTextureNode *tn = new QSGSimpleTextureNode();
277 QVERIFY(!tn->ownsTexture());
278
279 tn->setTexture(texture);
280 delete tn;
281 QVERIFY(!texture.isNull());
282 }
283
284 { // Check that it is deleted on destruction when we so desire
285 QPointer<QSGTexture> texture(new QSGPlainTexture());
286
287 QSGSimpleTextureNode *tn = new QSGSimpleTextureNode();
288 tn->setOwnsTexture(true);
289 QVERIFY(tn->ownsTexture());
290
291 tn->setTexture(texture);
292 delete tn;
293 QVERIFY(texture.isNull());
294 }
295
296 { // Check that it is deleted on update when we so desire
297 QPointer<QSGTexture> oldTexture(new QSGPlainTexture());
298 QPointer<QSGTexture> newTexture(new QSGPlainTexture());
299
300 QSGSimpleTextureNode *tn = new QSGSimpleTextureNode();
301 tn->setOwnsTexture(true);
302 QVERIFY(tn->ownsTexture());
303
304 tn->setTexture(oldTexture);
305 QVERIFY(!oldTexture.isNull());
306 QVERIFY(!newTexture.isNull());
307
308 tn->setTexture(newTexture);
309 QVERIFY(oldTexture.isNull());
310 QVERIFY(!newTexture.isNull());
311
312 delete tn;
313 }
314}
315
316void NodesTest::textureNodeRect()
317{
318 QSGPlainTexture texture;
319 texture.setTextureSize(QSize(400, 400));
320 QSGSimpleTextureNode tn;
321 tn.setTexture(&texture);
322 QSGGeometry::TexturedPoint2D *vertices = tn.geometry()->vertexDataAsTexturedPoint2D();
323
324 QSGGeometry::TexturedPoint2D topLeft, bottomLeft, topRight, bottomRight;
325 topLeft.set(nx: 0, ny: 0, ntx: 0, nty: 0);
326 bottomLeft.set(nx: 0, ny: 0, ntx: 0, nty: 1);
327 topRight.set(nx: 0, ny: 0, ntx: 1, nty: 0);
328 bottomRight.set(nx: 0, ny: 0, ntx: 1, nty: 1);
329 QCOMPARE(vertices[0], topLeft);
330 QCOMPARE(vertices[1], bottomLeft);
331 QCOMPARE(vertices[2], topRight);
332 QCOMPARE(vertices[3], bottomRight);
333
334 tn.setRect(x: 1, y: 2, w: 100, h: 100);
335 topLeft.set(nx: 1, ny: 2, ntx: 0, nty: 0);
336 bottomLeft.set(nx: 1, ny: 102, ntx: 0, nty: 1);
337 topRight.set(nx: 101, ny: 2, ntx: 1, nty: 0);
338 bottomRight.set(nx: 101, ny: 102, ntx: 1, nty: 1);
339 QCOMPARE(vertices[0], topLeft);
340 QCOMPARE(vertices[1], bottomLeft);
341 QCOMPARE(vertices[2], topRight);
342 QCOMPARE(vertices[3], bottomRight);
343
344 tn.setRect(x: 0, y: 0, w: 100, h: 100);
345 tn.setSourceRect(x: 100, y: 100, w: 200, h: 200);
346 topLeft.set(nx: 0, ny: 0, ntx: 0.25, nty: 0.25);
347 bottomLeft.set(nx: 0, ny: 100, ntx: 0.25, nty: 0.75);
348 topRight.set(nx: 100, ny: 0, ntx: 0.75, nty: 0.25);
349 bottomRight.set(nx: 100, ny: 100, ntx: 0.75, nty: 0.75);
350 QCOMPARE(vertices[0], topLeft);
351 QCOMPARE(vertices[1], bottomLeft);
352 QCOMPARE(vertices[2], topRight);
353 QCOMPARE(vertices[3], bottomRight);
354
355 tn.setSourceRect(x: 300, y: 300, w: -200, h: -200);
356 topLeft.set(nx: 0, ny: 0, ntx: 0.75, nty: 0.75);
357 bottomLeft.set(nx: 0, ny: 100, ntx: 0.75, nty: 0.25);
358 topRight.set(nx: 100, ny: 0, ntx: 0.25, nty: 0.75);
359 bottomRight.set(nx: 100, ny: 100, ntx: 0.25, nty: 0.25);
360 QCOMPARE(vertices[0], topLeft);
361 QCOMPARE(vertices[1], bottomLeft);
362 QCOMPARE(vertices[2], topRight);
363 QCOMPARE(vertices[3], bottomRight);
364}
365
366QTEST_MAIN(NodesTest);
367
368#include "tst_nodestest.moc"
369

source code of qtdeclarative/tests/auto/quick/nodes/tst_nodestest.cpp