// Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include #include #include #include #include #include #include "testrenderer.h" #include #include Q_DECLARE_METATYPE(Qt3DRender::QShaderProgram::ShaderType) class tst_ShaderBuilder : public Qt3DCore::QBackendNodeTester { Q_OBJECT private slots: void shouldHaveGlobalDefaultPrototypes() { // GIVEN // THEN QCOMPARE(Qt3DRender::Render::ShaderBuilder::getPrototypesFile(), QStringLiteral(":/prototypes/default.json")); QVERIFY(!Qt3DRender::Render::ShaderBuilder::getPrototypeNames().isEmpty()); // WHEN Qt3DRender::Render::ShaderBuilder::setPrototypesFile(":/prototypes.json"); // THEN QCOMPARE(Qt3DRender::Render::ShaderBuilder::getPrototypesFile(), QStringLiteral(":/prototypes.json")); auto prototypeNames = Qt3DRender::Render::ShaderBuilder::getPrototypeNames(); prototypeNames.sort(); const auto expectedPrototypeNames = QStringList() << "exposure" << "exposureFunction" << "fragColor" << "lightIntensity" << "lightModel" << "sampleTexture" << "texCoord" << "texture" << "worldPosition"; QCOMPARE(prototypeNames, expectedPrototypeNames); } void shouldHaveInitialState() { // GIVEN Qt3DRender::Render::ShaderBuilder shaderBuilder; // THEN QVERIFY(!shaderBuilder.isEnabled()); QVERIFY(shaderBuilder.enabledLayers().isEmpty()); for (int i = 0; i <= Qt3DRender::QShaderProgram::Compute; i++) { const auto type = static_cast(i); QCOMPARE(shaderBuilder.shaderGraph(type), QUrl()); QCOMPARE(shaderBuilder.shaderCode(type), QByteArray()); QVERIFY(!shaderBuilder.isShaderCodeDirty(type)); } } void shouldHavePropertiesMirroringFromItsPeer_data() { QTest::addColumn("frontend"); { auto frontend = new Qt3DRender::QShaderProgramBuilder; QTest::newRow("empty") << frontend; } { auto frontend = new Qt3DRender::QShaderProgramBuilder; auto program = new Qt3DRender::QShaderProgram(frontend); frontend->setShaderProgram(program); QTest::newRow("shaderProgram") << frontend; } { auto frontend = new Qt3DRender::QShaderProgramBuilder; frontend->setEnabledLayers({"foo", "bar"}); QTest::newRow("enabledLayers") << frontend; } { auto frontend = new Qt3DRender::QShaderProgramBuilder; frontend->setVertexShaderGraph(QUrl::fromEncoded("qrc:/vertex.json")); QTest::newRow("vertex") << frontend; } { auto frontend = new Qt3DRender::QShaderProgramBuilder; frontend->setTessellationControlShaderGraph(QUrl::fromEncoded("qrc:/tesscontrol.json")); QTest::newRow("tessellationControl") << frontend; } { auto frontend = new Qt3DRender::QShaderProgramBuilder; frontend->setTessellationEvaluationShaderGraph(QUrl::fromEncoded("qrc:/tesseval.json")); QTest::newRow("tessellationEvaluation") << frontend; } { auto frontend = new Qt3DRender::QShaderProgramBuilder; frontend->setGeometryShaderGraph(QUrl::fromEncoded("qrc:/geometry.json")); QTest::newRow("geometry") << frontend; } { auto frontend = new Qt3DRender::QShaderProgramBuilder; frontend->setFragmentShaderGraph(QUrl::fromEncoded("qrc:/fragment.json")); QTest::newRow("fragment") << frontend; } { auto frontend = new Qt3DRender::QShaderProgramBuilder; frontend->setComputeShaderGraph(QUrl::fromEncoded("qrc:/compute.json")); QTest::newRow("compute") << frontend; } } void shouldHavePropertiesMirroringFromItsPeer() { // GIVEN QFETCH(Qt3DRender::QShaderProgramBuilder*, frontend); Qt3DRender::Render::ShaderBuilder backend; TestRenderer renderer; // WHEN backend.setRenderer(&renderer); simulateInitializationSync(frontend, &backend); // THEN QVERIFY(backend.isEnabled() == frontend->isEnabled()); if (frontend->shaderProgram()) QCOMPARE(backend.shaderProgramId(), frontend->shaderProgram()->id()); else QVERIFY(backend.shaderProgramId().isNull()); QCOMPARE(backend.enabledLayers(), frontend->enabledLayers()); QCOMPARE(backend.shaderGraph(Qt3DRender::QShaderProgram::Vertex), frontend->vertexShaderGraph()); QCOMPARE(backend.shaderCode(Qt3DRender::QShaderProgram::Vertex), QByteArray()); QCOMPARE(backend.isShaderCodeDirty(Qt3DRender::QShaderProgram::Vertex), !frontend->vertexShaderGraph().isEmpty()); QCOMPARE(backend.shaderGraph(Qt3DRender::QShaderProgram::TessellationControl), frontend->tessellationControlShaderGraph()); QCOMPARE(backend.shaderCode(Qt3DRender::QShaderProgram::TessellationControl), QByteArray()); QCOMPARE(backend.isShaderCodeDirty(Qt3DRender::QShaderProgram::TessellationControl), !frontend->tessellationControlShaderGraph().isEmpty()); QCOMPARE(backend.shaderGraph(Qt3DRender::QShaderProgram::TessellationEvaluation), frontend->tessellationEvaluationShaderGraph()); QCOMPARE(backend.shaderCode(Qt3DRender::QShaderProgram::TessellationEvaluation), QByteArray()); QCOMPARE(backend.isShaderCodeDirty(Qt3DRender::QShaderProgram::TessellationEvaluation), !frontend->tessellationEvaluationShaderGraph().isEmpty()); QCOMPARE(backend.shaderGraph(Qt3DRender::QShaderProgram::Geometry), frontend->geometryShaderGraph()); QCOMPARE(backend.shaderCode(Qt3DRender::QShaderProgram::Geometry), QByteArray()); QCOMPARE(backend.isShaderCodeDirty(Qt3DRender::QShaderProgram::Geometry), !frontend->geometryShaderGraph().isEmpty()); QCOMPARE(backend.shaderGraph(Qt3DRender::QShaderProgram::Fragment), frontend->fragmentShaderGraph()); QCOMPARE(backend.shaderCode(Qt3DRender::QShaderProgram::Fragment), QByteArray()); QCOMPARE(backend.isShaderCodeDirty(Qt3DRender::QShaderProgram::Fragment), !frontend->fragmentShaderGraph().isEmpty()); QCOMPARE(backend.shaderGraph(Qt3DRender::QShaderProgram::Compute), frontend->computeShaderGraph()); QCOMPARE(backend.shaderCode(Qt3DRender::QShaderProgram::Compute), QByteArray()); QCOMPARE(backend.isShaderCodeDirty(Qt3DRender::QShaderProgram::Compute), !frontend->computeShaderGraph().isEmpty()); // WHEN backend.cleanup(); // THEN QVERIFY(!backend.isEnabled()); QVERIFY(backend.enabledLayers().isEmpty()); for (int i = 0; i <= Qt3DRender::QShaderProgram::Compute; i++) { const auto type = static_cast(i); QCOMPARE(backend.shaderGraph(type), QUrl()); QCOMPARE(backend.shaderCode(type), QByteArray()); QVERIFY(!backend.isShaderCodeDirty(type)); } delete frontend; } void shouldHandleEnablePropertyChange() { // GIVEN Qt3DRender::Render::ShaderBuilder backend; Qt3DRender::QShaderProgramBuilder frontend; TestRenderer renderer; backend.setRenderer(&renderer); simulateInitializationSync(&frontend, &backend); // WHEN frontend.setEnabled(false); backend.syncFromFrontEnd(&frontend, false); // THEN QVERIFY(!backend.isEnabled()); QCOMPARE(renderer.dirtyBits(), Qt3DRender::Render::AbstractRenderer::ShadersDirty); renderer.resetDirty(); // WHEN frontend.setEnabled(true); backend.syncFromFrontEnd(&frontend, false); // AND backend.cleanup(); // THEN QVERIFY(!backend.isEnabled()); QCOMPARE(renderer.dirtyBits(), Qt3DRender::Render::AbstractRenderer::ShadersDirty); renderer.resetDirty(); } void shouldHandleShaderProgramPropertyChange() { // GIVEN Qt3DRender::Render::ShaderBuilder backend; Qt3DRender::QShaderProgramBuilder frontend; TestRenderer renderer; backend.setRenderer(&renderer); simulateInitializationSync(&frontend, &backend); // WHEN Qt3DRender::QShaderProgram prog; frontend.setShaderProgram(&prog); backend.syncFromFrontEnd(&frontend, false); // THEN QCOMPARE(backend.shaderProgramId(), prog.id()); QCOMPARE(renderer.dirtyBits(), Qt3DRender::Render::AbstractRenderer::ShadersDirty); renderer.resetDirty(); // WHEN frontend.setShaderProgram(nullptr); backend.syncFromFrontEnd(&frontend, false); // THEN QVERIFY(backend.shaderProgramId().isNull()); QCOMPARE(renderer.dirtyBits(), Qt3DRender::Render::AbstractRenderer::ShadersDirty); renderer.resetDirty(); // WHEN frontend.setShaderProgram(&prog); backend.syncFromFrontEnd(&frontend, false); // AND backend.cleanup(); // THEN QVERIFY(backend.shaderProgramId().isNull()); QCOMPARE(renderer.dirtyBits(), Qt3DRender::Render::AbstractRenderer::ShadersDirty); renderer.resetDirty(); } void shouldHandleEnabledLayersPropertyChange() { // GIVEN qputenv("QT3D_DISABLE_SHADER_CACHE", "1"); Qt3DRender::Render::ShaderBuilder backend; Qt3DRender::QShaderProgramBuilder frontend; TestRenderer renderer; backend.setRenderer(&renderer); simulateInitializationSync(&frontend, &backend); const auto layers = QStringList() << "foo" << "bar"; static const std::pair< Qt3DRender::QShaderProgram::ShaderType, void (Qt3DRender::QShaderProgramBuilder::*)(const QUrl &) > shaderTypesToSetters[] = { {Qt3DRender::QShaderProgram::Vertex, &Qt3DRender::QShaderProgramBuilder::setVertexShaderGraph}, {Qt3DRender::QShaderProgram::TessellationControl, &Qt3DRender::QShaderProgramBuilder::setTessellationControlShaderGraph}, {Qt3DRender::QShaderProgram::TessellationEvaluation, &Qt3DRender::QShaderProgramBuilder::setTessellationEvaluationShaderGraph}, {Qt3DRender::QShaderProgram::Geometry, &Qt3DRender::QShaderProgramBuilder::setGeometryShaderGraph}, {Qt3DRender::QShaderProgram::Fragment, &Qt3DRender::QShaderProgramBuilder::setFragmentShaderGraph}, {Qt3DRender::QShaderProgram::Compute, &Qt3DRender::QShaderProgramBuilder::setComputeShaderGraph}, }; for (auto it = std::begin(shaderTypesToSetters), end = std::end(shaderTypesToSetters); it != end; ++it) { const auto graphUrl = QUrl::fromEncoded("qrc:/input.json"); (frontend.*(it->second))(graphUrl); } // WHEN frontend.setEnabledLayers(layers); backend.syncFromFrontEnd(&frontend, false); // THEN QCOMPARE(backend.enabledLayers(), layers); for (int i = 0; i <= Qt3DRender::QShaderProgram::Compute; i++) { const auto type = static_cast(i); QVERIFY(backend.isShaderCodeDirty(type)); backend.generateCode(type); // Resets the dirty flag } QCOMPARE(renderer.dirtyBits(), Qt3DRender::Render::AbstractRenderer::ShadersDirty); renderer.resetDirty(); // WHEN frontend.setEnabledLayers(layers); backend.syncFromFrontEnd(&frontend, false); // THEN QCOMPARE(backend.enabledLayers(), layers); for (int i = 0; i <= Qt3DRender::QShaderProgram::Compute; i++) { const auto type = static_cast(i); QVERIFY(!backend.isShaderCodeDirty(type)); backend.generateCode(type); // Resets the dirty flag } QCOMPARE(renderer.dirtyBits(), 0); renderer.resetDirty(); // WHEN frontend.setEnabledLayers(QStringList()); backend.syncFromFrontEnd(&frontend, false); // THEN QVERIFY(backend.shaderProgramId().isNull()); for (int i = 0; i <= Qt3DRender::QShaderProgram::Compute; i++) { const auto type = static_cast(i); QVERIFY(backend.isShaderCodeDirty(type)); backend.generateCode(type); // Resets the dirty flag } QCOMPARE(renderer.dirtyBits(), Qt3DRender::Render::AbstractRenderer::ShadersDirty); renderer.resetDirty(); // WHEN frontend.setEnabledLayers(layers); backend.syncFromFrontEnd(&frontend, false); // AND backend.cleanup(); // THEN QVERIFY(backend.enabledLayers().isEmpty()); for (int i = 0; i <= Qt3DRender::QShaderProgram::Compute; i++) { const auto type = static_cast(i); QVERIFY(!backend.isShaderCodeDirty(type)); backend.generateCode(type); // Resets the dirty flag } QCOMPARE(renderer.dirtyBits(), Qt3DRender::Render::AbstractRenderer::ShadersDirty); renderer.resetDirty(); } void shouldHandleShaderGraphPropertiesChanges_data() { QTest::addColumn("type"); QTest::addColumn("graphUrl"); QTest::newRow("vertex") << Qt3DRender::QShaderProgram::Vertex << QUrl::fromEncoded("qrc:/vertex.json"); QTest::newRow("tessControl") << Qt3DRender::QShaderProgram::TessellationControl << QUrl::fromEncoded("qrc:/tesscontrol.json"); QTest::newRow("tessEval") << Qt3DRender::QShaderProgram::TessellationEvaluation << QUrl::fromEncoded("qrc:/tesseval.json"); QTest::newRow("geometry") << Qt3DRender::QShaderProgram::Geometry << QUrl::fromEncoded("qrc:/geometry.json"); QTest::newRow("fragment") << Qt3DRender::QShaderProgram::Fragment << QUrl::fromEncoded("qrc:/fragment.json"); QTest::newRow("compute") << Qt3DRender::QShaderProgram::Compute << QUrl::fromEncoded("qrc:/compute.json"); } void shouldHandleShaderGraphPropertiesChanges() { // GIVEN QFETCH(Qt3DRender::QShaderProgram::ShaderType, type); QFETCH(QUrl, graphUrl); Qt3DRender::Render::ShaderBuilder backend; Qt3DRender::QShaderProgramBuilder frontend; TestRenderer renderer; backend.setRenderer(&renderer); simulateInitializationSync(&frontend, &backend); static const QHash< Qt3DRender::QShaderProgram::ShaderType, void (Qt3DRender::QShaderProgramBuilder::*)(const QUrl &) > shaderTypesToSetters = { {Qt3DRender::QShaderProgram::Vertex, &Qt3DRender::QShaderProgramBuilder::setVertexShaderGraph}, {Qt3DRender::QShaderProgram::TessellationControl, &Qt3DRender::QShaderProgramBuilder::setTessellationControlShaderGraph}, {Qt3DRender::QShaderProgram::TessellationEvaluation, &Qt3DRender::QShaderProgramBuilder::setTessellationEvaluationShaderGraph}, {Qt3DRender::QShaderProgram::Geometry, &Qt3DRender::QShaderProgramBuilder::setGeometryShaderGraph}, {Qt3DRender::QShaderProgram::Fragment, &Qt3DRender::QShaderProgramBuilder::setFragmentShaderGraph}, {Qt3DRender::QShaderProgram::Compute, &Qt3DRender::QShaderProgramBuilder::setComputeShaderGraph}, }; // WHEN (frontend.*(shaderTypesToSetters[type]))(QUrl()); backend.syncFromFrontEnd(&frontend, false); // THEN QCOMPARE(backend.shaderGraph(type), QUrl()); QVERIFY(!backend.isShaderCodeDirty(type)); QVERIFY(backend.shaderCode(type).isEmpty()); QCOMPARE(renderer.dirtyBits(), 0); renderer.resetDirty(); // WHEN (frontend.*(shaderTypesToSetters[type]))(graphUrl); backend.syncFromFrontEnd(&frontend, false); // THEN QCOMPARE(backend.shaderGraph(type), graphUrl); QVERIFY(backend.isShaderCodeDirty(type)); QVERIFY(backend.shaderCode(type).isEmpty()); QCOMPARE(renderer.dirtyBits(), Qt3DRender::Render::AbstractRenderer::ShadersDirty); renderer.resetDirty(); // WHEN (frontend.*(shaderTypesToSetters[type]))(QUrl()); backend.syncFromFrontEnd(&frontend, false); // THEN QCOMPARE(backend.shaderGraph(type), QUrl()); QVERIFY(backend.isShaderCodeDirty(type)); QVERIFY(backend.shaderCode(type).isEmpty()); QCOMPARE(renderer.dirtyBits(), Qt3DRender::Render::AbstractRenderer::ShadersDirty); renderer.resetDirty(); // WHEN (frontend.*(shaderTypesToSetters[type]))(graphUrl); backend.syncFromFrontEnd(&frontend, false); // AND backend.cleanup(); // THEN QCOMPARE(backend.shaderGraph(type), QUrl()); QVERIFY(!backend.isShaderCodeDirty(type)); QVERIFY(backend.shaderCode(type).isEmpty()); QCOMPARE(renderer.dirtyBits(), Qt3DRender::Render::AbstractRenderer::ShadersDirty); renderer.resetDirty(); } void shouldHandleShaderCodeGeneration_data() { QTest::addColumn("type"); QTest::newRow("vertex") << Qt3DRender::QShaderProgram::Vertex; QTest::newRow("tessControl") << Qt3DRender::QShaderProgram::TessellationControl; QTest::newRow("tessEval") << Qt3DRender::QShaderProgram::TessellationEvaluation; QTest::newRow("geometry") << Qt3DRender::QShaderProgram::Geometry; QTest::newRow("fragment") << Qt3DRender::QShaderProgram::Fragment; QTest::newRow("compute") << Qt3DRender::QShaderProgram::Compute; } void shouldHandleShaderCodeGeneration() { // GIVEN qputenv("QT3D_DISABLE_SHADER_CACHE", "1"); Qt3DRender::Render::ShaderBuilder::setPrototypesFile(":/prototypes.json"); QVERIFY(!Qt3DRender::Render::ShaderBuilder::getPrototypeNames().isEmpty()); QFETCH(Qt3DRender::QShaderProgram::ShaderType, type); const auto gl3Api = []{ auto api = Qt3DRender::GraphicsApiFilterData(); api.m_api = Qt3DRender::QGraphicsApiFilter::OpenGL; api.m_profile = Qt3DRender::QGraphicsApiFilter::CoreProfile; api.m_major = 3; api.m_minor = 2; return api; }(); const auto es2Api = []{ auto api = Qt3DRender::GraphicsApiFilterData(); api.m_api = Qt3DRender::QGraphicsApiFilter::OpenGLES; api.m_major = 2; api.m_minor = 0; return api; }(); const auto readCode = [](const QString &suffix) -> QByteArray { const auto filePath = QStringLiteral(":/output.") + suffix; QFile file(filePath); if (!file.open(QFile::ReadOnly | QFile::Text)) qFatal("File open failed: %s", qPrintable(filePath)); return file.readAll(); }; const auto gl3Code = readCode("gl3"); const auto es2Code = readCode("es2"); Qt3DRender::Render::ShaderBuilder backend; // WHEN const auto graphUrl = QUrl::fromEncoded("qrc:/input.json"); backend.setShaderGraph(type, graphUrl); // THEN QCOMPARE(backend.shaderGraph(type), graphUrl); QVERIFY(backend.isShaderCodeDirty(type)); QVERIFY(backend.shaderCode(type).isEmpty()); // WHEN backend.setGraphicsApi(gl3Api); backend.generateCode(type); // THEN QCOMPARE(backend.shaderGraph(type), graphUrl); QVERIFY(!backend.isShaderCodeDirty(type)); QCOMPARE(backend.shaderCode(type), gl3Code); // WHEN backend.setGraphicsApi(es2Api); // THEN QCOMPARE(backend.shaderGraph(type), graphUrl); QVERIFY(backend.isShaderCodeDirty(type)); QCOMPARE(backend.shaderCode(type), gl3Code); // WHEN backend.generateCode(type); // THEN QCOMPARE(backend.shaderGraph(type), graphUrl); QVERIFY(!backend.isShaderCodeDirty(type)); QCOMPARE(backend.shaderCode(type), es2Code); } void checkCodeUpdatedNotification_data() { QTest::addColumn("type"); QTest::addColumn("notificationType"); QTest::newRow("vertex") << Qt3DRender::QShaderProgram::Vertex << Qt3DRender::QShaderProgram::Vertex; QTest::newRow("tessControl") << Qt3DRender::QShaderProgram::TessellationControl << Qt3DRender::QShaderProgram::TessellationControl; QTest::newRow("tessEval") << Qt3DRender::QShaderProgram::TessellationEvaluation << Qt3DRender::QShaderProgram::TessellationEvaluation; QTest::newRow("geometry") << Qt3DRender::QShaderProgram::Geometry << Qt3DRender::QShaderProgram::Geometry; QTest::newRow("fragment") << Qt3DRender::QShaderProgram::Fragment << Qt3DRender::QShaderProgram::Fragment; QTest::newRow("compute") << Qt3DRender::QShaderProgram::Compute << Qt3DRender::QShaderProgram::Compute; } void checkCodeUpdatedNotification() { // GIVEN qputenv("QT3D_DISABLE_SHADER_CACHE", "1"); Qt3DRender::Render::ShaderBuilder::setPrototypesFile(":/prototypes.json"); QVERIFY(!Qt3DRender::Render::ShaderBuilder::getPrototypeNames().isEmpty()); QFETCH(Qt3DRender::QShaderProgram::ShaderType, type); QFETCH(Qt3DRender::QShaderProgram::ShaderType, notificationType); Q_UNUSED(notificationType); const auto gl3Api = []{ auto api = Qt3DRender::GraphicsApiFilterData(); api.m_api = Qt3DRender::QGraphicsApiFilter::OpenGL; api.m_profile = Qt3DRender::QGraphicsApiFilter::CoreProfile; api.m_major = 3; api.m_minor = 2; return api; }(); const auto readCode = [](const QString &suffix) -> QByteArray { const auto filePath = QStringLiteral(":/output.") + suffix; QFile file(filePath); if (!file.open(QFile::ReadOnly | QFile::Text)) qFatal("File open failed: %s", qPrintable(filePath)); return file.readAll(); }; const auto gl3Code = readCode("gl3"); Qt3DRender::Render::ShaderBuilder backend; TestArbiter arbiter; // WHEN const auto graphUrl = QUrl::fromEncoded("qrc:/input.json"); backend.setShaderGraph(type, graphUrl); // THEN QCOMPARE(backend.shaderGraph(type), graphUrl); QVERIFY(backend.isShaderCodeDirty(type)); QVERIFY(backend.shaderCode(type).isEmpty()); // WHEN backend.setGraphicsApi(gl3Api); backend.generateCode(type); // THEN QCOMPARE(backend.shaderGraph(type), graphUrl); QVERIFY(!backend.isShaderCodeDirty(type)); QCOMPARE(backend.shaderCode(type), gl3Code); } void checkFileCaching() { // GIVEN qunsetenv("QT3D_DISABLE_SHADER_CACHE"); QTemporaryDir cacheDir; if (!cacheDir.isValid()) { QSKIP("Unable to generate cache dir, skipping"); return; } const auto gl3Api = []{ auto api = Qt3DRender::GraphicsApiFilterData(); api.m_api = Qt3DRender::QGraphicsApiFilter::OpenGL; api.m_profile = Qt3DRender::QGraphicsApiFilter::CoreProfile; api.m_major = 3; api.m_minor = 2; return api; }(); const auto gl2Api = []{ auto api = Qt3DRender::GraphicsApiFilterData(); api.m_api = Qt3DRender::QGraphicsApiFilter::OpenGL; api.m_profile = Qt3DRender::QGraphicsApiFilter::NoProfile; api.m_major = 2; api.m_minor = 0; return api; }(); Qt3DRender::Render::ShaderBuilder::setPrototypesFile(":/prototypes.json"); qputenv("QT3D_WRITABLE_CACHE_PATH", cacheDir.path().toUtf8()); // THEN QVERIFY(QDir(cacheDir.path()).entryList(QDir::Files).empty()); QByteArray hashKey; { // WHEN Qt3DRender::Render::ShaderBuilder b; b.setGraphicsApi(gl3Api); const auto graphUrl = QUrl::fromEncoded("qrc:/input.json"); b.setShaderGraph(Qt3DRender::QShaderProgram::Vertex, graphUrl); b.generateCode(Qt3DRender::QShaderProgram::Vertex); // THEN QCOMPARE(QDir(cacheDir.path()).entryList(QDir::Files).size(), 1); hashKey = b.hashKeyForShaderGraph(Qt3DRender::QShaderProgram::Vertex); QCOMPARE(hashKey.size(), 40); QCOMPARE(QDir(cacheDir.path()).entryList(QDir::Files).first(), QString::fromUtf8(hashKey) + QLatin1String(".qt3d")); } { // WHEN Qt3DRender::Render::ShaderBuilder b; b.setGraphicsApi(gl3Api); const auto graphUrl = QUrl::fromEncoded("qrc:/input.json"); b.setShaderGraph(Qt3DRender::QShaderProgram::Vertex, graphUrl); b.generateCode(Qt3DRender::QShaderProgram::Vertex); // THEN QCOMPARE(QDir(cacheDir.path()).entryList(QDir::Files).size(), 1); QCOMPARE(QDir(cacheDir.path()).entryList(QDir::Files).first(), QString::fromUtf8(hashKey) + QLatin1String(".qt3d")); } { // WHEN Qt3DRender::Render::ShaderBuilder b; b.setGraphicsApi(gl2Api); const auto graphUrl = QUrl::fromEncoded("qrc:/input.json"); b.setShaderGraph(Qt3DRender::QShaderProgram::Vertex, graphUrl); b.generateCode(Qt3DRender::QShaderProgram::Vertex); QByteArray gl2HashKey = b.hashKeyForShaderGraph(Qt3DRender::QShaderProgram::Vertex); // THEN QCOMPARE(QDir(cacheDir.path()).entryList(QDir::Files).size(), 2); QVERIFY(gl2HashKey != hashKey); } } void checkRuntimeCaching() { // GIVEN qunsetenv("QT3D_DISABLE_SHADER_CACHE"); TestRenderer renderer; QTemporaryDir cacheDir; if (!cacheDir.isValid()) { QSKIP("Unable to generate cache dir, skipping"); return; } const auto gl3Api = []{ auto api = Qt3DRender::GraphicsApiFilterData(); api.m_api = Qt3DRender::QGraphicsApiFilter::OpenGL; api.m_profile = Qt3DRender::QGraphicsApiFilter::CoreProfile; api.m_major = 3; api.m_minor = 2; return api; }(); Qt3DRender::Render::ShaderBuilder::setPrototypesFile(":/prototypes.json"); qputenv("QT3D_WRITABLE_CACHE_PATH", cacheDir.path().toUtf8()); // THEN QVERIFY(QDir(cacheDir.path()).entryList(QDir::Files).empty()); // WHEN Qt3DRender::Render::ShaderBuilder b; b.setGraphicsApi(gl3Api); const auto graphUrl = QUrl::fromEncoded("qrc:/input.json"); b.setShaderGraph(Qt3DRender::QShaderProgram::Vertex, graphUrl); b.generateCode(Qt3DRender::QShaderProgram::Vertex); // THEN QCOMPARE(QDir(cacheDir.path()).entryList(QDir::Files).size(), 1); const QByteArray hashKey = b.hashKeyForShaderGraph(Qt3DRender::QShaderProgram::Vertex); QCOMPARE(hashKey.size(), 40); QCOMPARE(QDir(cacheDir.path()).entryList(QDir::Files).first(), QString::fromUtf8(hashKey) + QLatin1String(".qt3d")); QVERIFY(!renderer.containsGeneratedShaderGraph(hashKey)); // WHEN b.setRenderer(&renderer); b.generateCode(Qt3DRender::QShaderProgram::Vertex); // THEN QVERIFY(renderer.containsGeneratedShaderGraph(hashKey)); } void checkDontUseCache() { // GIVEN QTemporaryDir cacheDir; if (!cacheDir.isValid()) { QSKIP("Unable to generate cache dir, skipping"); return; } const auto gl3Api = []{ auto api = Qt3DRender::GraphicsApiFilterData(); api.m_api = Qt3DRender::QGraphicsApiFilter::OpenGL; api.m_profile = Qt3DRender::QGraphicsApiFilter::CoreProfile; api.m_major = 3; api.m_minor = 2; return api; }(); Qt3DRender::Render::ShaderBuilder::setPrototypesFile(":/prototypes.json"); // THEN QVERIFY(QDir(cacheDir.path()).entryList(QDir::Files).empty()); // WHEN qputenv("QT3D_DISABLE_SHADER_CACHE", "1"); Qt3DRender::Render::ShaderBuilder b; b.setGraphicsApi(gl3Api); const auto graphUrl = QUrl::fromEncoded("qrc:/input.json"); b.setShaderGraph(Qt3DRender::QShaderProgram::Vertex, graphUrl); b.generateCode(Qt3DRender::QShaderProgram::Vertex); // THEN QCOMPARE(QDir(cacheDir.path()).entryList(QDir::Files).size(), 0); } void checkForceRebuildCache() { // GIVEN QTemporaryDir cacheDir; if (!cacheDir.isValid()) { QSKIP("Unable to generate cache dir, skipping"); return; } const auto gl3Api = []{ auto api = Qt3DRender::GraphicsApiFilterData(); api.m_api = Qt3DRender::QGraphicsApiFilter::OpenGL; api.m_profile = Qt3DRender::QGraphicsApiFilter::CoreProfile; api.m_major = 3; api.m_minor = 2; return api; }(); Qt3DRender::Render::ShaderBuilder::setPrototypesFile(":/prototypes.json"); // THEN QVERIFY(QDir(cacheDir.path()).entryList(QDir::Files).empty()); // WHEN qputenv("QT3D_WRITABLE_CACHE_PATH", cacheDir.path().toUtf8()); qputenv("QT3D_DISABLE_SHADER_CACHE", "1"); qputenv("QT3D_REBUILD_SHADER_CACHE", "1"); Qt3DRender::Render::ShaderBuilder b; b.setGraphicsApi(gl3Api); const auto graphUrl = QUrl::fromEncoded("qrc:/input.json"); b.setShaderGraph(Qt3DRender::QShaderProgram::Vertex, graphUrl); b.generateCode(Qt3DRender::QShaderProgram::Vertex); // THEN -> We have rebuilt the shader file (even if we don't use it) QCOMPARE(QDir(cacheDir.path()).entryList(QDir::Files).size(), 1); } }; QTEST_MAIN(tst_ShaderBuilder) #include "tst_shaderbuilder.moc"