blob: fb212bc8fa936837434df184796183ff0d238a39 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt3D module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QTest>
#include <qbackendnodetester.h>
#include <testrenderer.h>
#include <Qt3DCore/private/qscene_p.h>
#include <Qt3DRender/private/renderstatenode_p.h>
#include <Qt3DRender/private/managers_p.h>
#include <Qt3DRender/QRenderState>
#include <Qt3DRender/QAlphaCoverage>
#include <Qt3DRender/QAlphaTest>
#include <Qt3DRender/QBlendEquation>
#include <Qt3DRender/QBlendEquationArguments>
#include <Qt3DRender/QColorMask>
#include <Qt3DRender/QCullFace>
#include <Qt3DRender/QDepthRange>
#include <Qt3DRender/QDepthTest>
#include <Qt3DRender/QDithering>
#include <Qt3DRender/QFrontFace>
#include <Qt3DRender/QPointSize>
#include <Qt3DRender/QPolygonOffset>
#include <Qt3DRender/QScissorTest>
#include <Qt3DRender/QStencilTest>
#include <Qt3DRender/QStencilTestArguments>
#include <Qt3DRender/QStencilMask>
#include <Qt3DRender/QStencilOperation>
#include <Qt3DRender/QStencilOperationArguments>
#include <Qt3DRender/QClipPlane>
#include "testpostmanarbiter.h"
using namespace Qt3DCore;
using namespace Qt3DRender;
using namespace Qt3DRender::Render;
class tst_QRenderState : public QBackendNodeTester
{
Q_OBJECT
public:
tst_QRenderState() {}
~tst_QRenderState() {}
private:
RenderStateManager m_renderStateManager;
TestRenderer m_renderer;
RenderStateNode* createBackendNode(QRenderState *frontend)
{
RenderStateNode *backend = m_renderStateManager.getOrCreateResource(frontend->id());
backend->setRenderer(&m_renderer);
simulateInitializationSync(frontend, backend);
return backend;
}
// Create two frontend objects of class T and set given property to v1 / v2 respectively
template <class T, class V, class Setter>
void addTestCase(Qt3DRender::Render::StateMask mask, const char *property, Setter setter, V v1, V v2)
{
T *obj1 = new T();
T *obj2 = new T();
(obj1->*(setter))(v1);
(obj2->*(setter))(v2);
QTest::addRow("%s::%s", obj1->metaObject()->className(), property)
<< (QRenderState*) obj1
<< (QRenderState*) obj2
<< (quint64) mask
<< QString(property)
<< QVariant(v2);
}
// Create two frontend objects of class T and set given property to v1 / v2 respectively, for the specified arguments sub-object
template <class T, class Args, class V, class Setter>
void addStencilTestCase(Qt3DRender::Render::StateMask mask, const char *argsName, const char *property, Setter setter, V v1, V v2)
{
T *obj1 = new T();
T *obj2 = new T();
const QMetaObject *metaObj = obj1->metaObject();
const int pIndex = metaObj->indexOfProperty(argsName);
const QMetaProperty prop = metaObj->property(pIndex);
Args *args1 = qvariant_cast<Args*>(prop.read(obj1));
Args *args2 = qvariant_cast<Args*>(prop.read(obj2));
(args1->*(setter))(v1);
(args2->*(setter))(v2);
QTest::addRow("%s::%s::%s", metaObj->className(), argsName, property)
<< (QRenderState*) obj1
<< (QObject*) args1
<< (QRenderState*) obj2
<< (quint64) mask
<< QString(property)
<< QVariant(v2);
}
private Q_SLOTS:
void checkPropertyUpdates_data()
{
QTest::addColumn<QRenderState *>("frontend1");
QTest::addColumn<QRenderState *>("frontend2");
QTest::addColumn<quint64>("mask");
QTest::addColumn<QString>("propertyName");
QTest::addColumn<QVariant>("value");
addTestCase<QAlphaTest>(AlphaTestMask, "alphaFunction", &QAlphaTest::setAlphaFunction, QAlphaTest::Always, QAlphaTest::LessOrEqual);
addTestCase<QAlphaTest>(AlphaTestMask, "referenceValue", &QAlphaTest::setReferenceValue, 0.f, 1.f);
addTestCase<QBlendEquation>(BlendStateMask, "blendFunction", &QBlendEquation::setBlendFunction, QBlendEquation::Add, QBlendEquation::Subtract);
addTestCase<QBlendEquationArguments>(BlendEquationArgumentsMask, "sourceRgb", &QBlendEquationArguments::setSourceRgb, QBlendEquationArguments::Zero, QBlendEquationArguments::One);
addTestCase<QBlendEquationArguments>(BlendEquationArgumentsMask, "sourceAlpha", &QBlendEquationArguments::setSourceAlpha, QBlendEquationArguments::SourceAlpha, QBlendEquationArguments::OneMinusSource1Color);
addTestCase<QBlendEquationArguments>(BlendEquationArgumentsMask, "destinationRgb", &QBlendEquationArguments::setDestinationRgb, QBlendEquationArguments::DestinationColor, QBlendEquationArguments::OneMinusSourceAlpha);
addTestCase<QBlendEquationArguments>(BlendEquationArgumentsMask, "destinationAlpha", &QBlendEquationArguments::setDestinationAlpha, QBlendEquationArguments::DestinationAlpha, QBlendEquationArguments::DestinationColor);
addTestCase<QBlendEquationArguments>(BlendEquationArgumentsMask, "bufferIndex", &QBlendEquationArguments::setBufferIndex, 0, 1);
addTestCase<QClipPlane>(ClipPlaneMask, "planeIndex", &QClipPlane::setPlaneIndex, 0, 1);
addTestCase<QClipPlane>(ClipPlaneMask, "normal", &QClipPlane::setNormal, QVector3D(0, 1, 0), QVector3D(0, 0, 1));
addTestCase<QClipPlane>(ClipPlaneMask, "distance", &QClipPlane::setDistance, 1.f, 2.f);
addTestCase<QColorMask>(ColorStateMask, "redMasked", &QColorMask::setRedMasked, false, true);
addTestCase<QColorMask>(ColorStateMask, "blueMasked", &QColorMask::setBlueMasked, false, true);
addTestCase<QColorMask>(ColorStateMask, "greenMasked", &QColorMask::setGreenMasked, false, true);
addTestCase<QColorMask>(ColorStateMask, "alphaMasked", &QColorMask::setAlphaMasked, false, true);
addTestCase<QCullFace>(CullFaceStateMask, "mode", &QCullFace::setMode, QCullFace::Back, QCullFace::FrontAndBack);
addTestCase<QFrontFace>(FrontFaceStateMask, "direction", &QFrontFace::setDirection, QFrontFace::ClockWise, QFrontFace::CounterClockWise);
addTestCase<QPointSize>(PointSizeMask, "sizeMode", &QPointSize::setSizeMode, QPointSize::Programmable, QPointSize::Fixed);
addTestCase<QPointSize>(PointSizeMask, "value", &QPointSize::setValue, 2.f, 4.f);
addTestCase<QPolygonOffset>(PolygonOffsetStateMask, "scaleFactor", &QPolygonOffset::setScaleFactor, 1.f, 2.f);
addTestCase<QPolygonOffset>(PolygonOffsetStateMask, "depthSteps", &QPolygonOffset::setDepthSteps, 1.f, 2.f);
addTestCase<QScissorTest>(ScissorStateMask, "left", &QScissorTest::setLeft, 10, 20);
addTestCase<QScissorTest>(ScissorStateMask, "bottom", &QScissorTest::setBottom, 10, 20);
addTestCase<QScissorTest>(ScissorStateMask, "width", &QScissorTest::setWidth, 10, 20);
addTestCase<QScissorTest>(ScissorStateMask, "height", &QScissorTest::setHeight, 10, 20);
addTestCase<QStencilMask>(StencilWriteStateMask, "frontOutputMask", &QStencilMask::setFrontOutputMask, 0x12, 0x34);
addTestCase<QStencilMask>(StencilWriteStateMask, "backOutputMask", &QStencilMask::setBackOutputMask, 0x12, 0x34);
addTestCase<QDepthRange>(DepthRangeMask, "nearValue", &QDepthRange::setNearValue, 0.1, 0.2);
addTestCase<QDepthRange>(DepthRangeMask, "farValue", &QDepthRange::setFarValue, 0.5, 0.6);
}
void checkPropertyUpdates()
{
// GIVEN
QFETCH(QRenderState*, frontend1);
QFETCH(QRenderState*, frontend2);
QFETCH(quint64, mask);
QFETCH(QString, propertyName);
QFETCH(QVariant, value);
// THEN
RenderStateNode *backend1 = createBackendNode(frontend1);
RenderStateNode *backend2 = createBackendNode(frontend2);
QVERIFY(backend1->type() == mask);
QVERIFY(backend2->type() == mask);
QVERIFY(backend1->impl() != backend2->impl());
// WHEN
TestArbiter arbiter;
arbiter.setArbiterOnNode(frontend1);
const QMetaObject *metaObj = frontend1->metaObject();
const int pIndex = metaObj->indexOfProperty(propertyName.toStdString().c_str());
metaObj->property(pIndex).write(frontend1, value);
QCoreApplication::processEvents();
// THEN
QCOMPARE(arbiter.events.size(), 0);
QCOMPARE(arbiter.dirtyNodes.size(), 1);
QCOMPARE(arbiter.dirtyNodes.front(), frontend1);
// WHEN
backend1->syncFromFrontEnd(frontend1, false);
// THEN
QVERIFY(backend1->impl() == backend2->impl());
arbiter.dirtyNodes.clear();
}
void checkStencilUpdates_data()
{
QTest::addColumn<QRenderState *>("frontend1");
QTest::addColumn<QObject *>("args1");
QTest::addColumn<QRenderState *>("frontend2");
QTest::addColumn<quint64>("mask");
QTest::addColumn<QString>("propertyName");
QTest::addColumn<QVariant>("value");
qRegisterMetaType<QStencilOperationArguments*>("QStencilOperationArguments*");
qRegisterMetaType<QStencilTestArguments*>("QStencilTestArguments*");
for (bool front : QVector<bool>{false, true}) {
const char *argsProperty = front ? "front" : "back";
addStencilTestCase<QStencilOperation, QStencilOperationArguments>(
StencilOpMask, argsProperty, "allTestsPassOperation",
&QStencilOperationArguments::setAllTestsPassOperation,
QStencilOperationArguments::Zero, QStencilOperationArguments::Keep);
addStencilTestCase<QStencilOperation, QStencilOperationArguments>(
StencilOpMask, argsProperty, "depthTestFailureOperation",
&QStencilOperationArguments::setDepthTestFailureOperation,
QStencilOperationArguments::Replace, QStencilOperationArguments::Zero);
addStencilTestCase<QStencilOperation, QStencilOperationArguments>(
StencilOpMask, argsProperty, "stencilTestFailureOperation",
&QStencilOperationArguments::setStencilTestFailureOperation,
QStencilOperationArguments::Increment, QStencilOperationArguments::Decrement);
addStencilTestCase<QStencilTest, QStencilTestArguments>(
StencilTestStateMask, argsProperty, "comparisonMask",
&QStencilTestArguments::setComparisonMask, 0x12, 0x34);
addStencilTestCase<QStencilTest, QStencilTestArguments>(
StencilTestStateMask, argsProperty, "referenceValue",
&QStencilTestArguments::setReferenceValue, 1, 2);
addStencilTestCase<QStencilTest, QStencilTestArguments>(
StencilTestStateMask, argsProperty, "stencilFunction",
&QStencilTestArguments::setStencilFunction,
QStencilTestArguments::Always, QStencilTestArguments::Equal);
}
}
void checkStencilUpdates()
{
// GIVEN
QFETCH(QRenderState*, frontend1);
QFETCH(QObject*, args1);
QFETCH(QRenderState*, frontend2);
QFETCH(quint64, mask);
QFETCH(QString, propertyName);
QFETCH(QVariant, value);
// THEN
RenderStateNode *backend1 = createBackendNode(frontend1);
RenderStateNode *backend2 = createBackendNode(frontend2);
QVERIFY(backend1->type() == mask);
QVERIFY(backend2->type() == mask);
QVERIFY(backend1->impl() != backend2->impl());
// WHEN
TestArbiter arbiter;
arbiter.setArbiterOnNode(frontend1);
const QMetaObject *metaObj = args1->metaObject();
const int pIndex = metaObj->indexOfProperty(propertyName.toStdString().c_str());
metaObj->property(pIndex).write(args1, value);
QCoreApplication::processEvents();
// THEN
QCOMPARE(arbiter.events.size(), 0);
QCOMPARE(arbiter.dirtyNodes.size(), 1);
QCOMPARE(arbiter.dirtyNodes.front(), frontend1);
// WHEN
backend1->syncFromFrontEnd(frontend1, false);
// THEN
QVERIFY(backend1->impl() == backend2->impl());
arbiter.events.clear();
}
};
QTEST_MAIN(tst_QRenderState)
#include "tst_qrenderstate.moc"