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
60using namespace Qt3DCore;
61using namespace Qt3DRender;
62using namespace Qt3DRender::Render;
63
64class tst_QRenderState : public QBackendNodeTester
65{
66 Q_OBJECT
67public:
68 tst_QRenderState() {}
69 ~tst_QRenderState() {}
70
71private:
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
126private 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
302QTEST_MAIN(tst_QRenderState)
303
304#include "tst_qrenderstate.moc"
305

source code of qt3d/tests/auto/render/qrenderstate/tst_qrenderstate.cpp