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/QTest> |
30 | #include <qbackendnodetester.h> |
31 | #include <testrenderer.h> |
32 | |
33 | #include <Qt3DCore/private/qscene_p.h> |
34 | #include <Qt3DRender/private/renderstatenode_p.h> |
35 | #include <Qt3DRender/private/managers_p.h> |
36 | |
37 | #include <Qt3DRender/QRenderState> |
38 | #include <Qt3DRender/QAlphaCoverage> |
39 | #include <Qt3DRender/QAlphaTest> |
40 | #include <Qt3DRender/QBlendEquation> |
41 | #include <Qt3DRender/QBlendEquationArguments> |
42 | #include <Qt3DRender/QColorMask> |
43 | #include <Qt3DRender/QCullFace> |
44 | #include <Qt3DRender/QDepthRange> |
45 | #include <Qt3DRender/QDepthTest> |
46 | #include <Qt3DRender/QDithering> |
47 | #include <Qt3DRender/QFrontFace> |
48 | #include <Qt3DRender/QPointSize> |
49 | #include <Qt3DRender/QPolygonOffset> |
50 | #include <Qt3DRender/QScissorTest> |
51 | #include <Qt3DRender/QStencilTest> |
52 | #include <Qt3DRender/QStencilTestArguments> |
53 | #include <Qt3DRender/QStencilMask> |
54 | #include <Qt3DRender/QStencilOperation> |
55 | #include <Qt3DRender/QStencilOperationArguments> |
56 | #include <Qt3DRender/QClipPlane> |
57 | |
58 | #include "testpostmanarbiter.h" |
59 | |
60 | using namespace Qt3DCore; |
61 | using namespace Qt3DRender; |
62 | using namespace Qt3DRender::Render; |
63 | |
64 | class tst_QRenderState : public QBackendNodeTester |
65 | { |
66 | Q_OBJECT |
67 | public: |
68 | tst_QRenderState() {} |
69 | ~tst_QRenderState() {} |
70 | |
71 | private: |
72 | RenderStateManager m_renderStateManager; |
73 | TestRenderer m_renderer; |
74 | |
75 | RenderStateNode* createBackendNode(QRenderState *frontend) |
76 | { |
77 | RenderStateNode *backend = m_renderStateManager.getOrCreateResource(id: frontend->id()); |
78 | backend->setRenderer(&m_renderer); |
79 | simulateInitializationSync(frontend, backend); |
80 | return backend; |
81 | } |
82 | |
83 | // Create two frontend objects of class T and set given property to v1 / v2 respectively |
84 | template <class T, class V, class Setter> |
85 | void addTestCase(Qt3DRender::Render::StateMask mask, const char *property, Setter setter, V v1, V v2) |
86 | { |
87 | T *obj1 = new T(); |
88 | T *obj2 = new T(); |
89 | |
90 | (obj1->*(setter))(v1); |
91 | (obj2->*(setter))(v2); |
92 | |
93 | QTest::addRow(format: "%s::%s" , obj1->metaObject()->className(), property) |
94 | << (QRenderState*) obj1 |
95 | << (QRenderState*) obj2 |
96 | << (quint64) mask |
97 | << QString(property) |
98 | << QVariant(v2); |
99 | } |
100 | |
101 | // Create two frontend objects of class T and set given property to v1 / v2 respectively, for the specified arguments sub-object |
102 | template <class T, class Args, class V, class Setter> |
103 | void addStencilTestCase(Qt3DRender::Render::StateMask mask, const char *argsName, const char *property, Setter setter, V v1, V v2) |
104 | { |
105 | T *obj1 = new T(); |
106 | T *obj2 = new T(); |
107 | |
108 | const QMetaObject *metaObj = obj1->metaObject(); |
109 | const int pIndex = metaObj->indexOfProperty(name: argsName); |
110 | const QMetaProperty prop = metaObj->property(index: pIndex); |
111 | Args *args1 = qvariant_cast<Args*>(prop.read(obj: obj1)); |
112 | Args *args2 = qvariant_cast<Args*>(prop.read(obj: obj2)); |
113 | |
114 | (args1->*(setter))(v1); |
115 | (args2->*(setter))(v2); |
116 | |
117 | QTest::addRow(format: "%s::%s::%s" , metaObj->className(), argsName, property) |
118 | << (QRenderState*) obj1 |
119 | << (QObject*) args1 |
120 | << (QRenderState*) obj2 |
121 | << (quint64) mask |
122 | << QString(property) |
123 | << QVariant(v2); |
124 | } |
125 | |
126 | private Q_SLOTS: |
127 | |
128 | void checkPropertyUpdates_data() |
129 | { |
130 | QTest::addColumn<QRenderState *>(name: "frontend1" ); |
131 | QTest::addColumn<QRenderState *>(name: "frontend2" ); |
132 | QTest::addColumn<quint64>(name: "mask" ); |
133 | QTest::addColumn<QString>(name: "propertyName" ); |
134 | QTest::addColumn<QVariant>(name: "value" ); |
135 | |
136 | addTestCase<QAlphaTest>(mask: AlphaTestMask, property: "alphaFunction" , setter: &QAlphaTest::setAlphaFunction, v1: QAlphaTest::Always, v2: QAlphaTest::LessOrEqual); |
137 | addTestCase<QAlphaTest>(mask: AlphaTestMask, property: "referenceValue" , setter: &QAlphaTest::setReferenceValue, v1: 0.f, v2: 1.f); |
138 | |
139 | addTestCase<QBlendEquation>(mask: BlendStateMask, property: "blendFunction" , setter: &QBlendEquation::setBlendFunction, v1: QBlendEquation::Add, v2: QBlendEquation::Subtract); |
140 | |
141 | addTestCase<QBlendEquationArguments>(mask: BlendEquationArgumentsMask, property: "sourceRgb" , setter: &QBlendEquationArguments::setSourceRgb, v1: QBlendEquationArguments::Zero, v2: QBlendEquationArguments::One); |
142 | addTestCase<QBlendEquationArguments>(mask: BlendEquationArgumentsMask, property: "sourceAlpha" , setter: &QBlendEquationArguments::setSourceAlpha, v1: QBlendEquationArguments::SourceAlpha, v2: QBlendEquationArguments::OneMinusSource1Color); |
143 | addTestCase<QBlendEquationArguments>(mask: BlendEquationArgumentsMask, property: "destinationRgb" , setter: &QBlendEquationArguments::setDestinationRgb, v1: QBlendEquationArguments::DestinationColor, v2: QBlendEquationArguments::OneMinusSourceAlpha); |
144 | addTestCase<QBlendEquationArguments>(mask: BlendEquationArgumentsMask, property: "destinationAlpha" , setter: &QBlendEquationArguments::setDestinationAlpha, v1: QBlendEquationArguments::DestinationAlpha, v2: QBlendEquationArguments::DestinationColor); |
145 | addTestCase<QBlendEquationArguments>(mask: BlendEquationArgumentsMask, property: "bufferIndex" , setter: &QBlendEquationArguments::setBufferIndex, v1: 0, v2: 1); |
146 | |
147 | addTestCase<QClipPlane>(mask: ClipPlaneMask, property: "planeIndex" , setter: &QClipPlane::setPlaneIndex, v1: 0, v2: 1); |
148 | addTestCase<QClipPlane>(mask: ClipPlaneMask, property: "normal" , setter: &QClipPlane::setNormal, v1: QVector3D(0, 1, 0), v2: QVector3D(0, 0, 1)); |
149 | addTestCase<QClipPlane>(mask: ClipPlaneMask, property: "distance" , setter: &QClipPlane::setDistance, v1: 1.f, v2: 2.f); |
150 | |
151 | addTestCase<QColorMask>(mask: ColorStateMask, property: "redMasked" , setter: &QColorMask::setRedMasked, v1: false, v2: true); |
152 | addTestCase<QColorMask>(mask: ColorStateMask, property: "blueMasked" , setter: &QColorMask::setBlueMasked, v1: false, v2: true); |
153 | addTestCase<QColorMask>(mask: ColorStateMask, property: "greenMasked" , setter: &QColorMask::setGreenMasked, v1: false, v2: true); |
154 | addTestCase<QColorMask>(mask: ColorStateMask, property: "alphaMasked" , setter: &QColorMask::setAlphaMasked, v1: false, v2: true); |
155 | |
156 | addTestCase<QCullFace>(mask: CullFaceStateMask, property: "mode" , setter: &QCullFace::setMode, v1: QCullFace::Back, v2: QCullFace::FrontAndBack); |
157 | |
158 | addTestCase<QFrontFace>(mask: FrontFaceStateMask, property: "direction" , setter: &QFrontFace::setDirection, v1: QFrontFace::ClockWise, v2: QFrontFace::CounterClockWise); |
159 | |
160 | addTestCase<QPointSize>(mask: PointSizeMask, property: "sizeMode" , setter: &QPointSize::setSizeMode, v1: QPointSize::Programmable, v2: QPointSize::Fixed); |
161 | addTestCase<QPointSize>(mask: PointSizeMask, property: "value" , setter: &QPointSize::setValue, v1: 2.f, v2: 4.f); |
162 | |
163 | addTestCase<QPolygonOffset>(mask: PolygonOffsetStateMask, property: "scaleFactor" , setter: &QPolygonOffset::setScaleFactor, v1: 1.f, v2: 2.f); |
164 | addTestCase<QPolygonOffset>(mask: PolygonOffsetStateMask, property: "depthSteps" , setter: &QPolygonOffset::setDepthSteps, v1: 1.f, v2: 2.f); |
165 | |
166 | addTestCase<QScissorTest>(mask: ScissorStateMask, property: "left" , setter: &QScissorTest::setLeft, v1: 10, v2: 20); |
167 | addTestCase<QScissorTest>(mask: ScissorStateMask, property: "bottom" , setter: &QScissorTest::setBottom, v1: 10, v2: 20); |
168 | addTestCase<QScissorTest>(mask: ScissorStateMask, property: "width" , setter: &QScissorTest::setWidth, v1: 10, v2: 20); |
169 | addTestCase<QScissorTest>(mask: ScissorStateMask, property: "height" , setter: &QScissorTest::setHeight, v1: 10, v2: 20); |
170 | |
171 | addTestCase<QStencilMask>(mask: StencilWriteStateMask, property: "frontOutputMask" , setter: &QStencilMask::setFrontOutputMask, v1: 0x12, v2: 0x34); |
172 | addTestCase<QStencilMask>(mask: StencilWriteStateMask, property: "backOutputMask" , setter: &QStencilMask::setBackOutputMask, v1: 0x12, v2: 0x34); |
173 | |
174 | addTestCase<QDepthRange>(mask: DepthRangeMask, property: "nearValue" , setter: &QDepthRange::setNearValue, v1: 0.1, v2: 0.2); |
175 | addTestCase<QDepthRange>(mask: DepthRangeMask, property: "farValue" , setter: &QDepthRange::setFarValue, v1: 0.5, v2: 0.6); |
176 | } |
177 | |
178 | void checkPropertyUpdates() |
179 | { |
180 | // GIVEN |
181 | QFETCH(QRenderState*, frontend1); |
182 | QFETCH(QRenderState*, frontend2); |
183 | QFETCH(quint64, mask); |
184 | QFETCH(QString, propertyName); |
185 | QFETCH(QVariant, value); |
186 | |
187 | // THEN |
188 | RenderStateNode *backend1 = createBackendNode(frontend: frontend1); |
189 | RenderStateNode *backend2 = createBackendNode(frontend: frontend2); |
190 | |
191 | QVERIFY(backend1->type() == mask); |
192 | QVERIFY(backend2->type() == mask); |
193 | QVERIFY(backend1->impl() != backend2->impl()); |
194 | |
195 | // WHEN |
196 | TestArbiter arbiter; |
197 | arbiter.setArbiterOnNode(frontend1); |
198 | const QMetaObject *metaObj = frontend1->metaObject(); |
199 | const int pIndex = metaObj->indexOfProperty(name: propertyName.toStdString().c_str()); |
200 | metaObj->property(index: pIndex).write(obj: frontend1, value); |
201 | QCoreApplication::processEvents(); |
202 | |
203 | // THEN |
204 | QCOMPARE(arbiter.events.size(), 0); |
205 | QCOMPARE(arbiter.dirtyNodes.size(), 1); |
206 | QCOMPARE(arbiter.dirtyNodes.front(), frontend1); |
207 | |
208 | // WHEN |
209 | backend1->syncFromFrontEnd(frontEnd: frontend1, firstTime: false); |
210 | |
211 | // THEN |
212 | QVERIFY(backend1->impl() == backend2->impl()); |
213 | |
214 | arbiter.dirtyNodes.clear(); |
215 | } |
216 | |
217 | void checkStencilUpdates_data() |
218 | { |
219 | QTest::addColumn<QRenderState *>(name: "frontend1" ); |
220 | QTest::addColumn<QObject *>(name: "args1" ); |
221 | QTest::addColumn<QRenderState *>(name: "frontend2" ); |
222 | QTest::addColumn<quint64>(name: "mask" ); |
223 | QTest::addColumn<QString>(name: "propertyName" ); |
224 | QTest::addColumn<QVariant>(name: "value" ); |
225 | |
226 | qRegisterMetaType<QStencilOperationArguments*>(typeName: "QStencilOperationArguments*" ); |
227 | qRegisterMetaType<QStencilTestArguments*>(typeName: "QStencilTestArguments*" ); |
228 | |
229 | for (bool front : QVector<bool>{false, true}) { |
230 | const char *argsProperty = front ? "front" : "back" ; |
231 | |
232 | addStencilTestCase<QStencilOperation, QStencilOperationArguments>( |
233 | mask: StencilOpMask, argsName: argsProperty, property: "allTestsPassOperation" , |
234 | setter: &QStencilOperationArguments::setAllTestsPassOperation, |
235 | v1: QStencilOperationArguments::Zero, v2: QStencilOperationArguments::Keep); |
236 | |
237 | addStencilTestCase<QStencilOperation, QStencilOperationArguments>( |
238 | mask: StencilOpMask, argsName: argsProperty, property: "depthTestFailureOperation" , |
239 | setter: &QStencilOperationArguments::setDepthTestFailureOperation, |
240 | v1: QStencilOperationArguments::Replace, v2: QStencilOperationArguments::Zero); |
241 | |
242 | addStencilTestCase<QStencilOperation, QStencilOperationArguments>( |
243 | mask: StencilOpMask, argsName: argsProperty, property: "stencilTestFailureOperation" , |
244 | setter: &QStencilOperationArguments::setStencilTestFailureOperation, |
245 | v1: QStencilOperationArguments::Increment, v2: QStencilOperationArguments::Decrement); |
246 | |
247 | addStencilTestCase<QStencilTest, QStencilTestArguments>( |
248 | mask: StencilTestStateMask, argsName: argsProperty, property: "comparisonMask" , |
249 | setter: &QStencilTestArguments::setComparisonMask, v1: 0x12, v2: 0x34); |
250 | |
251 | addStencilTestCase<QStencilTest, QStencilTestArguments>( |
252 | mask: StencilTestStateMask, argsName: argsProperty, property: "referenceValue" , |
253 | setter: &QStencilTestArguments::setReferenceValue, v1: 1, v2: 2); |
254 | |
255 | addStencilTestCase<QStencilTest, QStencilTestArguments>( |
256 | mask: StencilTestStateMask, argsName: argsProperty, property: "stencilFunction" , |
257 | setter: &QStencilTestArguments::setStencilFunction, |
258 | v1: QStencilTestArguments::Always, v2: QStencilTestArguments::Equal); |
259 | } |
260 | } |
261 | |
262 | void checkStencilUpdates() |
263 | { |
264 | // GIVEN |
265 | QFETCH(QRenderState*, frontend1); |
266 | QFETCH(QObject*, args1); |
267 | QFETCH(QRenderState*, frontend2); |
268 | QFETCH(quint64, mask); |
269 | QFETCH(QString, propertyName); |
270 | QFETCH(QVariant, value); |
271 | |
272 | // THEN |
273 | RenderStateNode *backend1 = createBackendNode(frontend: frontend1); |
274 | RenderStateNode *backend2 = createBackendNode(frontend: frontend2); |
275 | QVERIFY(backend1->type() == mask); |
276 | QVERIFY(backend2->type() == mask); |
277 | QVERIFY(backend1->impl() != backend2->impl()); |
278 | |
279 | // WHEN |
280 | TestArbiter arbiter; |
281 | arbiter.setArbiterOnNode(frontend1); |
282 | const QMetaObject *metaObj = args1->metaObject(); |
283 | const int pIndex = metaObj->indexOfProperty(name: propertyName.toStdString().c_str()); |
284 | metaObj->property(index: pIndex).write(obj: args1, value); |
285 | QCoreApplication::processEvents(); |
286 | |
287 | // THEN |
288 | QCOMPARE(arbiter.events.size(), 0); |
289 | QCOMPARE(arbiter.dirtyNodes.size(), 1); |
290 | QCOMPARE(arbiter.dirtyNodes.front(), frontend1); |
291 | |
292 | // WHEN |
293 | backend1->syncFromFrontEnd(frontEnd: frontend1, firstTime: false); |
294 | |
295 | // THEN |
296 | QVERIFY(backend1->impl() == backend2->impl()); |
297 | |
298 | arbiter.events.clear(); |
299 | } |
300 | }; |
301 | |
302 | QTEST_MAIN(tst_QRenderState) |
303 | |
304 | #include "tst_qrenderstate.moc" |
305 | |