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(frontend->id());
78 simulateInitialization(frontend, backend);
79 backend->setRenderer(&m_renderer);
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("%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(argsName);
110 const QMetaProperty prop = metaObj->property(pIndex);
111 Args *args1 = qvariant_cast<Args*>(prop.read(obj1));
112 Args *args2 = qvariant_cast<Args*>(prop.read(obj2));
113
114 (args1->*(setter))(v1);
115 (args2->*(setter))(v2);
116
117 QTest::addRow("%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 *>("frontend1");
131 QTest::addColumn<QRenderState *>("frontend2");
132 QTest::addColumn<quint64>("mask");
133 QTest::addColumn<QString>("propertyName");
134 QTest::addColumn<QVariant>("value");
135
136 addTestCase<QAlphaTest>(AlphaTestMask, "alphaFunction", &QAlphaTest::setAlphaFunction, QAlphaTest::Always, QAlphaTest::LessOrEqual);
137 addTestCase<QAlphaTest>(AlphaTestMask, "referenceValue", &QAlphaTest::setReferenceValue, 0.f, 1.f);
138
139 addTestCase<QBlendEquation>(BlendStateMask, "blendFunction", &QBlendEquation::setBlendFunction, QBlendEquation::Add, QBlendEquation::Subtract);
140
141 addTestCase<QBlendEquationArguments>(BlendEquationArgumentsMask, "sourceRgb", &QBlendEquationArguments::setSourceRgb, QBlendEquationArguments::Zero, QBlendEquationArguments::One);
142 addTestCase<QBlendEquationArguments>(BlendEquationArgumentsMask, "sourceAlpha", &QBlendEquationArguments::setSourceAlpha, QBlendEquationArguments::SourceAlpha, QBlendEquationArguments::OneMinusSource1Color);
143 addTestCase<QBlendEquationArguments>(BlendEquationArgumentsMask, "destinationRgb", &QBlendEquationArguments::setDestinationRgb, QBlendEquationArguments::DestinationColor, QBlendEquationArguments::OneMinusSourceAlpha);
144 addTestCase<QBlendEquationArguments>(BlendEquationArgumentsMask, "destinationAlpha", &QBlendEquationArguments::setDestinationAlpha, QBlendEquationArguments::DestinationAlpha, QBlendEquationArguments::DestinationColor);
145 addTestCase<QBlendEquationArguments>(BlendEquationArgumentsMask, "bufferIndex", &QBlendEquationArguments::setBufferIndex, 0, 1);
146
147 addTestCase<QClipPlane>(ClipPlaneMask, "planeIndex", &QClipPlane::setPlaneIndex, 0, 1);
148 addTestCase<QClipPlane>(ClipPlaneMask, "normal", &QClipPlane::setNormal, QVector3D(0, 1, 0), QVector3D(0, 0, 1));
149 addTestCase<QClipPlane>(ClipPlaneMask, "distance", &QClipPlane::setDistance, 1.f, 2.f);
150
151 addTestCase<QColorMask>(ColorStateMask, "redMasked", &QColorMask::setRedMasked, false, true);
152 addTestCase<QColorMask>(ColorStateMask, "blueMasked", &QColorMask::setBlueMasked, false, true);
153 addTestCase<QColorMask>(ColorStateMask, "greenMasked", &QColorMask::setGreenMasked, false, true);
154 addTestCase<QColorMask>(ColorStateMask, "alphaMasked", &QColorMask::setAlphaMasked, false, true);
155
156 addTestCase<QCullFace>(CullFaceStateMask, "mode", &QCullFace::setMode, QCullFace::Back, QCullFace::FrontAndBack);
157
158 addTestCase<QFrontFace>(FrontFaceStateMask, "direction", &QFrontFace::setDirection, QFrontFace::ClockWise, QFrontFace::CounterClockWise);
159
160 addTestCase<QPointSize>(PointSizeMask, "sizeMode", &QPointSize::setSizeMode, QPointSize::Programmable, QPointSize::Fixed);
161 addTestCase<QPointSize>(PointSizeMask, "value", &QPointSize::setValue, 2.f, 4.f);
162
163 addTestCase<QPolygonOffset>(PolygonOffsetStateMask, "scaleFactor", &QPolygonOffset::setScaleFactor, 1.f, 2.f);
164 addTestCase<QPolygonOffset>(PolygonOffsetStateMask, "depthSteps", &QPolygonOffset::setDepthSteps, 1.f, 2.f);
165
166 addTestCase<QScissorTest>(ScissorStateMask, "left", &QScissorTest::setLeft, 10, 20);
167 addTestCase<QScissorTest>(ScissorStateMask, "bottom", &QScissorTest::setBottom, 10, 20);
168 addTestCase<QScissorTest>(ScissorStateMask, "width", &QScissorTest::setWidth, 10, 20);
169 addTestCase<QScissorTest>(ScissorStateMask, "height", &QScissorTest::setHeight, 10, 20);
170
171 addTestCase<QStencilMask>(StencilWriteStateMask, "frontOutputMask", &QStencilMask::setFrontOutputMask, 0x12, 0x34);
172 addTestCase<QStencilMask>(StencilWriteStateMask, "backOutputMask", &QStencilMask::setBackOutputMask, 0x12, 0x34);
173
174 addTestCase<QDepthRange>(DepthRangeMask, "nearValue", &QDepthRange::setNearValue, 0.1, 0.2);
175 addTestCase<QDepthRange>(DepthRangeMask, "farValue", &QDepthRange::setFarValue, 0.5, 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(frontend1);
189 RenderStateNode *backend2 = createBackendNode(frontend2);
190 QVERIFY(backend1->type() == mask);
191 QVERIFY(backend2->type() == mask);
192 QVERIFY(backend1->impl() != backend2->impl());
193
194 // WHEN
195 TestArbiter arbiter;
196 arbiter.setArbiterOnNode(frontend1);
197 const QMetaObject *metaObj = frontend1->metaObject();
198 const int pIndex = metaObj->indexOfProperty(propertyName.toStdString().c_str());
199 metaObj->property(pIndex).write(frontend1, value);
200 QCoreApplication::processEvents();
201
202 // THEN
203 QCOMPARE(arbiter.events.size(), 1);
204 QPropertyUpdatedChangePtr change = arbiter.events.first().staticCast<QPropertyUpdatedChange>();
205 QVERIFY(change->propertyName() == propertyName);
206 QCOMPARE(change->subjectId(), frontend1->id());
207
208 // WHEN
209 backend1->sceneChangeEvent(change.staticCast<QSceneChange>());
210
211 // THEN
212 QVERIFY(backend1->impl() == backend2->impl());
213
214 arbiter.events.clear();
215 }
216
217 void checkStencilUpdates_data()
218 {
219 QTest::addColumn<QRenderState *>("frontend1");
220 QTest::addColumn<QObject *>("args1");
221 QTest::addColumn<QRenderState *>("frontend2");
222 QTest::addColumn<quint64>("mask");
223 QTest::addColumn<QString>("propertyName");
224 QTest::addColumn<QVariant>("value");
225
226 qRegisterMetaType<QStencilOperationArguments*>("QStencilOperationArguments*");
227 qRegisterMetaType<QStencilTestArguments*>("QStencilTestArguments*");
228
229 for (bool front : QVector<bool>{false, true}) {
230 const char *argsProperty = front ? "front" : "back";
231
232 addStencilTestCase<QStencilOperation, QStencilOperationArguments>(
233 StencilOpMask, argsProperty, "allTestsPassOperation",
234 &QStencilOperationArguments::setAllTestsPassOperation,
235 QStencilOperationArguments::Zero, QStencilOperationArguments::Keep);
236
237 addStencilTestCase<QStencilOperation, QStencilOperationArguments>(
238 StencilOpMask, argsProperty, "depthTestFailureOperation",
239 &QStencilOperationArguments::setDepthTestFailureOperation,
240 QStencilOperationArguments::Replace, QStencilOperationArguments::Zero);
241
242 addStencilTestCase<QStencilOperation, QStencilOperationArguments>(
243 StencilOpMask, argsProperty, "stencilTestFailureOperation",
244 &QStencilOperationArguments::setStencilTestFailureOperation,
245 QStencilOperationArguments::Increment, QStencilOperationArguments::Decrement);
246
247 addStencilTestCase<QStencilTest, QStencilTestArguments>(
248 StencilTestStateMask, argsProperty, "comparisonMask",
249 &QStencilTestArguments::setComparisonMask, 0x12, 0x34);
250
251 addStencilTestCase<QStencilTest, QStencilTestArguments>(
252 StencilTestStateMask, argsProperty, "referenceValue",
253 &QStencilTestArguments::setReferenceValue, 1, 2);
254
255 addStencilTestCase<QStencilTest, QStencilTestArguments>(
256 StencilTestStateMask, argsProperty, "stencilFunction",
257 &QStencilTestArguments::setStencilFunction,
258 QStencilTestArguments::Always, 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(frontend1);
274 RenderStateNode *backend2 = createBackendNode(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(propertyName.toStdString().c_str());
284 metaObj->property(pIndex).write(args1, value);
285 QCoreApplication::processEvents();
286
287 // THEN
288 QCOMPARE(arbiter.events.size(), 1);
289 QPropertyUpdatedChangePtr change = arbiter.events.first().staticCast<QPropertyUpdatedChange>();
290 QCOMPARE(change->subjectId(), frontend1->id());
291
292 // WHEN
293 backend1->sceneChangeEvent(change.staticCast<QSceneChange>());
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