// Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include #include #include #include #include #include #include "testarbiter.h" class tst_QShaderProgramBuilder : public Qt3DRender::QShaderProgramBuilder { Q_OBJECT public: tst_QShaderProgramBuilder() : Qt3DRender::QShaderProgramBuilder() { qRegisterMetaType("Qt3DRender::QShaderProgram*"); } private Q_SLOTS: void checkDefaultConstruction() { // GIVEN Qt3DRender::QShaderProgramBuilder builder; // THEN QVERIFY(!builder.shaderProgram()); QVERIFY(builder.enabledLayers().isEmpty()); QCOMPARE(builder.vertexShaderGraph(), QUrl()); QCOMPARE(builder.tessellationControlShaderGraph(), QUrl()); QCOMPARE(builder.tessellationEvaluationShaderGraph(), QUrl()); QCOMPARE(builder.geometryShaderGraph(), QUrl()); QCOMPARE(builder.fragmentShaderGraph(), QUrl()); QCOMPARE(builder.computeShaderGraph(), QUrl()); QCOMPARE(builder.vertexShaderCode(), QByteArray()); QCOMPARE(builder.fragmentShaderCode(), QByteArray()); QCOMPARE(builder.computeShaderCode(), QByteArray()); QCOMPARE(builder.geometryShaderCode(), QByteArray()); QCOMPARE(builder.tessellationEvaluationShaderCode(), QByteArray()); QCOMPARE(builder.tessellationControlShaderCode(), QByteArray()); } void checkPropertyChanges() { // GIVEN Qt3DRender::QShaderProgramBuilder builder; { // WHEN QSignalSpy spy(&builder, SIGNAL(shaderProgramChanged(Qt3DRender::QShaderProgram*))); auto newValue = new Qt3DRender::QShaderProgram(&builder); builder.setShaderProgram(newValue); // THEN QVERIFY(spy.isValid()); QCOMPARE(builder.shaderProgram(), newValue); QCOMPARE(spy.size(), 1); // WHEN spy.clear(); builder.setShaderProgram(newValue); // THEN QCOMPARE(builder.shaderProgram(), newValue); QCOMPARE(spy.size(), 0); } { // WHEN QSignalSpy spy(&builder, SIGNAL(enabledLayersChanged(QStringList))); const auto newValue = QStringList() << "foo" << "bar"; builder.setEnabledLayers(newValue); // THEN QVERIFY(spy.isValid()); QCOMPARE(builder.enabledLayers(), newValue); QCOMPARE(spy.size(), 1); // WHEN spy.clear(); builder.setEnabledLayers(newValue); // THEN QCOMPARE(builder.enabledLayers(), newValue); QCOMPARE(spy.size(), 0); } { // WHEN QSignalSpy spy(&builder, SIGNAL(vertexShaderGraphChanged(QUrl))); const auto newValue = QUrl::fromEncoded("qrc:/vertex.json"); builder.setVertexShaderGraph(newValue); // THEN QVERIFY(spy.isValid()); QCOMPARE(builder.vertexShaderGraph(), newValue); QCOMPARE(spy.size(), 1); // WHEN spy.clear(); builder.setVertexShaderGraph(newValue); // THEN QCOMPARE(builder.vertexShaderGraph(), newValue); QCOMPARE(spy.size(), 0); } { // WHEN QSignalSpy spy(&builder, SIGNAL(tessellationControlShaderGraphChanged(QUrl))); const auto newValue = QUrl::fromEncoded("qrc:/tesscontrol.json"); builder.setTessellationControlShaderGraph(newValue); // THEN QVERIFY(spy.isValid()); QCOMPARE(builder.tessellationControlShaderGraph(), newValue); QCOMPARE(spy.size(), 1); // WHEN spy.clear(); builder.setTessellationControlShaderGraph(newValue); // THEN QCOMPARE(builder.tessellationControlShaderGraph(), newValue); QCOMPARE(spy.size(), 0); } { // WHEN QSignalSpy spy(&builder, SIGNAL(tessellationEvaluationShaderGraphChanged(QUrl))); const auto newValue = QUrl::fromEncoded("qrc:/tesseval.json"); builder.setTessellationEvaluationShaderGraph(newValue); // THEN QVERIFY(spy.isValid()); QCOMPARE(builder.tessellationEvaluationShaderGraph(), newValue); QCOMPARE(spy.size(), 1); // WHEN spy.clear(); builder.setTessellationEvaluationShaderGraph(newValue); // THEN QCOMPARE(builder.tessellationEvaluationShaderGraph(), newValue); QCOMPARE(spy.size(), 0); } { // WHEN QSignalSpy spy(&builder, SIGNAL(geometryShaderGraphChanged(QUrl))); const auto newValue = QUrl::fromEncoded("qrc:/geometry.json"); builder.setGeometryShaderGraph(newValue); // THEN QVERIFY(spy.isValid()); QCOMPARE(builder.geometryShaderGraph(), newValue); QCOMPARE(spy.size(), 1); // WHEN spy.clear(); builder.setGeometryShaderGraph(newValue); // THEN QCOMPARE(builder.geometryShaderGraph(), newValue); QCOMPARE(spy.size(), 0); } { // WHEN QSignalSpy spy(&builder, SIGNAL(fragmentShaderGraphChanged(QUrl))); const auto newValue = QUrl::fromEncoded("qrc:/fragment.json"); builder.setFragmentShaderGraph(newValue); // THEN QVERIFY(spy.isValid()); QCOMPARE(builder.fragmentShaderGraph(), newValue); QCOMPARE(spy.size(), 1); // WHEN spy.clear(); builder.setFragmentShaderGraph(newValue); // THEN QCOMPARE(builder.fragmentShaderGraph(), newValue); QCOMPARE(spy.size(), 0); } { // WHEN QSignalSpy spy(&builder, SIGNAL(computeShaderGraphChanged(QUrl))); const auto newValue = QUrl::fromEncoded("qrc:/compute.json"); builder.setComputeShaderGraph(newValue); // THEN QVERIFY(spy.isValid()); QCOMPARE(builder.computeShaderGraph(), newValue); QCOMPARE(spy.size(), 1); // WHEN spy.clear(); builder.setComputeShaderGraph(newValue); // THEN QCOMPARE(builder.computeShaderGraph(), newValue); QCOMPARE(spy.size(), 0); } } void checkShaderProgramBookkeeping() { // GIVEN QScopedPointer builder(new Qt3DRender::QShaderProgramBuilder); { // WHEN Qt3DRender::QShaderProgram program; builder->setShaderProgram(&program); // THEN QCOMPARE(program.parent(), builder.data()); QCOMPARE(builder->shaderProgram(), &program); } // THEN (Should not crash and effect be unset) QVERIFY(!builder->shaderProgram()); { // WHEN Qt3DRender::QShaderProgramBuilder someOtherBuilder; QScopedPointer program(new Qt3DRender::QShaderProgram(&someOtherBuilder)); builder->setShaderProgram(program.data()); // THEN QCOMPARE(program->parent(), &someOtherBuilder); QCOMPARE(builder->shaderProgram(), program.data()); // WHEN builder.reset(); program.reset(); // THEN Should not crash when the effect is destroyed (tests for failed removal of destruction helper) } } void checkShaderProgramUpdate() { // GIVEN TestArbiter arbiter; Qt3DRender::QShaderProgramBuilder builder; arbiter.setArbiterOnNode(&builder); auto program = new Qt3DRender::QShaderProgram(&builder); { // WHEN builder.setShaderProgram(program); QCoreApplication::processEvents(); // THEN QCOMPARE(arbiter.dirtyNodes().size(), 1); QCOMPARE(arbiter.dirtyNodes().front(), &builder); arbiter.clear(); } { // WHEN builder.setShaderProgram(program); QCoreApplication::processEvents(); // THEN QCOMPARE(arbiter.dirtyNodes().size(), 0); } } void checkEnabledLayersUpdate() { // GIVEN TestArbiter arbiter; Qt3DRender::QShaderProgramBuilder builder; arbiter.setArbiterOnNode(&builder); const auto layers = QStringList() << "foo" << "bar"; { // WHEN builder.setEnabledLayers(layers); QCoreApplication::processEvents(); // THEN QCOMPARE(arbiter.dirtyNodes().size(), 1); QCOMPARE(arbiter.dirtyNodes().front(), &builder); arbiter.clear(); } { // WHEN builder.setEnabledLayers(layers); QCoreApplication::processEvents(); // THEN QCOMPARE(arbiter.dirtyNodes().size(), 0); } } void checkVertexShaderGraphUpdate() { // GIVEN TestArbiter arbiter; Qt3DRender::QShaderProgramBuilder builder; arbiter.setArbiterOnNode(&builder); { // WHEN builder.setVertexShaderGraph(QUrl::fromEncoded("qrc:/vertex.json")); QCoreApplication::processEvents(); // THEN QCOMPARE(arbiter.dirtyNodes().size(), 1); QCOMPARE(arbiter.dirtyNodes().front(), &builder); arbiter.clear(); } { // WHEN builder.setVertexShaderGraph(QUrl::fromEncoded("qrc:/vertex.json")); QCoreApplication::processEvents(); // THEN QCOMPARE(arbiter.dirtyNodes().size(), 0); } } void checkTessellationControlShaderGraphUpdate() { // GIVEN TestArbiter arbiter; Qt3DRender::QShaderProgramBuilder builder; arbiter.setArbiterOnNode(&builder); { // WHEN builder.setTessellationControlShaderGraph(QUrl::fromEncoded("qrc:/tesscontrol.json")); QCoreApplication::processEvents(); // THEN QCOMPARE(arbiter.dirtyNodes().size(), 1); QCOMPARE(arbiter.dirtyNodes().front(), &builder); arbiter.clear(); } { // WHEN builder.setTessellationControlShaderGraph(QUrl::fromEncoded("qrc:/tesscontrol.json")); QCoreApplication::processEvents(); // THEN QCOMPARE(arbiter.dirtyNodes().size(), 0); } } void checkTessellationEvaluationShaderGraphUpdate() { // GIVEN TestArbiter arbiter; Qt3DRender::QShaderProgramBuilder builder; arbiter.setArbiterOnNode(&builder); { // WHEN builder.setTessellationEvaluationShaderGraph(QUrl::fromEncoded("qrc:/tesseval.json")); QCoreApplication::processEvents(); // THEN QCOMPARE(arbiter.dirtyNodes().size(), 1); QCOMPARE(arbiter.dirtyNodes().front(), &builder); arbiter.clear(); } { // WHEN builder.setTessellationEvaluationShaderGraph(QUrl::fromEncoded("qrc:/tesseval.json")); QCoreApplication::processEvents(); // THEN QCOMPARE(arbiter.dirtyNodes().size(), 0); } } void checkGeometryShaderGraphUpdate() { // GIVEN TestArbiter arbiter; Qt3DRender::QShaderProgramBuilder builder; arbiter.setArbiterOnNode(&builder); { // WHEN builder.setGeometryShaderGraph(QUrl::fromEncoded("qrc:/geometry.json")); QCoreApplication::processEvents(); // THEN QCOMPARE(arbiter.dirtyNodes().size(), 1); QCOMPARE(arbiter.dirtyNodes().front(), &builder); arbiter.clear(); } { // WHEN builder.setGeometryShaderGraph(QUrl::fromEncoded("qrc:/geometry.json")); QCoreApplication::processEvents(); // THEN QCOMPARE(arbiter.dirtyNodes().size(), 0); } } void checkFragmentShaderGraphUpdate() { // GIVEN TestArbiter arbiter; Qt3DRender::QShaderProgramBuilder builder; arbiter.setArbiterOnNode(&builder); { // WHEN builder.setFragmentShaderGraph(QUrl::fromEncoded("qrc:/fragment.json")); QCoreApplication::processEvents(); // THEN QCOMPARE(arbiter.dirtyNodes().size(), 1); QCOMPARE(arbiter.dirtyNodes().front(), &builder); arbiter.clear(); } { // WHEN builder.setFragmentShaderGraph(QUrl::fromEncoded("qrc:/fragment.json")); QCoreApplication::processEvents(); // THEN QCOMPARE(arbiter.dirtyNodes().size(), 0); } } void checkComputeShaderGraphUpdate() { // GIVEN TestArbiter arbiter; Qt3DRender::QShaderProgramBuilder builder; arbiter.setArbiterOnNode(&builder); { // WHEN builder.setComputeShaderGraph(QUrl::fromEncoded("qrc:/compute.json")); QCoreApplication::processEvents(); // THEN QCOMPARE(arbiter.dirtyNodes().size(), 1); QCOMPARE(arbiter.dirtyNodes().front(), &builder); arbiter.clear(); } { // WHEN builder.setComputeShaderGraph(QUrl::fromEncoded("qrc:/compute.json")); QCoreApplication::processEvents(); // THEN QCOMPARE(arbiter.dirtyNodes().size(), 0); } } void checkGeneratedCodePropertyUpdates() { Qt3DRender::QShaderProgramBuilderPrivate *dBuilder = static_cast(Qt3DCore::QNodePrivate::get(this)); { // WHEN QSignalSpy spy(this, SIGNAL(vertexShaderCodeChanged(QByteArray))); dBuilder->setShaderCode(QByteArrayLiteral("vertex"), Qt3DRender::QShaderProgram::Vertex); // THEN QVERIFY(spy.isValid()); QCOMPARE(spy.size(), 1); QCOMPARE(vertexShaderCode(), QByteArrayLiteral("vertex")); } { // WHEN QSignalSpy spy(this, SIGNAL(fragmentShaderCodeChanged(QByteArray))); dBuilder->setShaderCode(QByteArrayLiteral("fragment"), Qt3DRender::QShaderProgram::Fragment); // THEN QVERIFY(spy.isValid()); QCOMPARE(spy.size(), 1); QCOMPARE(fragmentShaderCode(), QByteArrayLiteral("fragment")); } { // WHEN QSignalSpy spy(this, SIGNAL(geometryShaderCodeChanged(QByteArray))); dBuilder->setShaderCode(QByteArrayLiteral("geometry"), Qt3DRender::QShaderProgram::Geometry); // THEN QVERIFY(spy.isValid()); QCOMPARE(spy.size(), 1); QCOMPARE(geometryShaderCode(), QByteArrayLiteral("geometry")); } { // WHEN QSignalSpy spy(this, SIGNAL(computeShaderCodeChanged(QByteArray))); dBuilder->setShaderCode(QByteArrayLiteral("compute"), Qt3DRender::QShaderProgram::Compute); // THEN QVERIFY(spy.isValid()); QCOMPARE(spy.size(), 1); QCOMPARE(computeShaderCode(), QByteArrayLiteral("compute")); } { // WHEN QSignalSpy spy(this, SIGNAL(tessellationControlShaderCodeChanged(QByteArray))); dBuilder->setShaderCode(QByteArrayLiteral("control"), Qt3DRender::QShaderProgram::TessellationControl); // THEN QVERIFY(spy.isValid()); QCOMPARE(spy.size(), 1); QCOMPARE(tessellationControlShaderCode(), QByteArrayLiteral("control")); } { // WHEN QSignalSpy spy(this, SIGNAL(tessellationEvaluationShaderCodeChanged(QByteArray))); dBuilder->setShaderCode(QByteArrayLiteral("eval"), Qt3DRender::QShaderProgram::TessellationEvaluation); // THEN QVERIFY(spy.isValid()); QCOMPARE(spy.size(), 1); QCOMPARE(tessellationEvaluationShaderCode(), QByteArrayLiteral("eval")); } } }; QTEST_MAIN(tst_QShaderProgramBuilder) #include "tst_qshaderprogrambuilder.moc"