// Copyright (C) 2015 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 #include #include #include #include #include #include QT_BEGIN_NAMESPACE namespace Qt3DRender { namespace Render { namespace OpenGL { namespace { void compareShaderParameterPacks(const ShaderParameterPack &t1, const ShaderParameterPack &t2) { const PackUniformHash hash1 = t1.uniforms(); const PackUniformHash hash2 = t2.uniforms(); QCOMPARE(hash1.keys.size(), hash2.keys.size()); for (int i = 0, m = hash1.keys.size(); i < m; ++i) { const int key = hash1.keys.at(i); QCOMPARE(hash1.value(key), hash2.value(key)); } } } // anonymous class tst_RenderViews : public Qt3DCore::QBackendNodeTester { Q_OBJECT private Q_SLOTS: void checkRenderViewSizeFitsWithAllocator() { QSKIP("Allocated Disabled"); QVERIFY(sizeof(RenderView) <= 192); } void checkRenderViewInitialState() { // GIVEN RenderView renderView; // THEN QCOMPARE(renderView.memoryBarrier(), QMemoryBarrier::None); } void checkMemoryBarrierInitialization() { // GIVEN RenderView renderView; // THEN QCOMPARE(renderView.memoryBarrier(), QMemoryBarrier::None); // WHEN const QMemoryBarrier::Operations barriers(QMemoryBarrier::BufferUpdate|QMemoryBarrier::ShaderImageAccess); renderView.setMemoryBarrier(barriers); // THEN QCOMPARE(renderView.memoryBarrier(), barriers); } void checkSetRenderViewConfig() { TestRenderer renderer; { // GIVEN const QMemoryBarrier::Operations barriers(QMemoryBarrier::AtomicCounter|QMemoryBarrier::ShaderStorage); Qt3DRender::QMemoryBarrier frontendBarrier; FrameGraphManager frameGraphManager; MemoryBarrier backendBarrier; RenderView renderView; // setRenderViewConfigFromFrameGraphLeafNode assumes node has a manager backendBarrier.setFrameGraphManager(&frameGraphManager); backendBarrier.setRenderer(&renderer); // WHEN frontendBarrier.setWaitOperations(barriers); simulateInitializationSync(&frontendBarrier, &backendBarrier); // THEN QCOMPARE(renderView.memoryBarrier(), QMemoryBarrier::None); QCOMPARE(backendBarrier.waitOperations(), barriers); // WHEN Qt3DRender::Render::OpenGL::RenderView::setRenderViewConfigFromFrameGraphLeafNode(&renderView, &backendBarrier); // THEN QCOMPARE(backendBarrier.waitOperations(), renderView.memoryBarrier()); } // TO DO: Complete tests for other framegraph node types } void checkDoesntCrashWhenNoCommandsToSort() { // GIVEN Qt3DRender::Render::NodeManagers nodeManagers; Renderer renderer; RenderView renderView; renderer.setNodeManagers(&nodeManagers); renderView.setRenderer(&renderer); // WHEN renderView.addSortType(QList { QSortPolicy::BackToFront }); EntityRenderCommandDataViewPtr view = EntityRenderCommandDataViewPtr::create(); renderView.setRenderCommandDataView(view); // THEN -> shouldn't crash renderView.sort(); // RenderCommands are deleted by RenderView dtor renderer.shutdown(); } void checkRenderCommandBackToFrontSorting() { // GIVEN Qt3DRender::Render::NodeManagers nodeManagers; Renderer renderer; RenderView renderView; std::vector rawCommands; renderer.setNodeManagers(&nodeManagers); renderView.setRenderer(&renderer); for (int i = 0; i < 200; ++i) { RenderCommand c; c.m_depth = float(i); rawCommands.push_back(c); } // WHEN renderView.addSortType(QList { QSortPolicy::BackToFront }); EntityRenderCommandDataViewPtr view = EntityRenderCommandDataViewPtr::create(); view->data.commands = rawCommands; view->indices.resize(rawCommands.size()); std::iota(view->indices.begin(), view->indices.end(), 0); renderView.setRenderCommandDataView(view); renderView.sort(); // THEN int j = 0; RenderCommand previousRC; renderView.forEachCommand([&] (const RenderCommand &command) { if (j > 0) QVERIFY(previousRC.m_depth > command.m_depth); previousRC = command; ++j; }); // RenderCommands are deleted by RenderView dtor renderer.shutdown(); } void checkRenderCommandMaterialSorting() { // GIVEN Qt3DRender::Render::NodeManagers nodeManagers; Renderer renderer; RenderView renderView; std::vector rawCommands; renderer.setNodeManagers(&nodeManagers); renderView.setRenderer(&renderer); GLShader *dnas[5] = { reinterpret_cast(0x250), reinterpret_cast(0x500), reinterpret_cast(0x1000), reinterpret_cast(0x1500), reinterpret_cast(0x2000) }; for (int i = 0; i < 20; ++i) { RenderCommand c; c.m_glShader = dnas[i % 5]; rawCommands.push_back(c); } // WHEN renderView.addSortType(QList { QSortPolicy::Material }); EntityRenderCommandDataViewPtr view = EntityRenderCommandDataViewPtr::create(); view->data.commands = rawCommands; view->indices.resize(rawCommands.size()); std::iota(view->indices.begin(), view->indices.end(), 0); renderView.setRenderCommandDataView(view); renderView.sort(); // THEN int j = 0; GLShader *targetShader; RenderCommand previousRC; renderView.forEachCommand([&] (const RenderCommand &command) { if (j % 4 == 0) { targetShader = command.m_glShader; if (j > 0) QVERIFY(targetShader != previousRC.m_glShader); } QCOMPARE(targetShader, command.m_glShader); previousRC = command; ++j; }); // RenderCommands are deleted by RenderView dtor renderer.shutdown(); } void checkRenderViewUniformMinification_data() { QTest::addColumn>("shaders"); QTest::addColumn>("rawParameters"); QTest::addColumn>("expectedMinimizedParameters"); Qt3DCore::QNodeId fakeTextureNodeId = Qt3DCore::QNodeId::createId(); ShaderParameterPack pack1; pack1.setUniform(1, UniformValue(883)); pack1.setUniform(2, UniformValue(1584.0f)); pack1.setTexture(3, 0, fakeTextureNodeId); QShaderProgram *shader1 = new QShaderProgram(); QShaderProgram *shader2 = new QShaderProgram(); shader1->setShaderCode(QShaderProgram::Vertex, QByteArrayLiteral("1")); shader2->setShaderCode(QShaderProgram::Vertex, QByteArrayLiteral("2")); ShaderParameterPack minifiedPack1; QTest::newRow("NoMinification") << QList { shader1, shader2 } << QList { pack1, pack1 } << QList { pack1, pack1 }; QTest::newRow("SingleShaderMinified") << QList { shader1, shader1, shader1 } << QList { pack1, pack1, pack1 } << QList { pack1, minifiedPack1, minifiedPack1 }; QTest::newRow("MultipleShadersMinified") << QList { shader1, shader1, shader1, shader2, shader2, shader2 } << QList { pack1, pack1, pack1, pack1, pack1, pack1 } << QList { pack1, minifiedPack1, minifiedPack1, pack1, minifiedPack1, minifiedPack1 }; } void checkShaderParameterPackStoresSingleUBOPerBlockIndex() { // GIVEN ShaderParameterPack pack; auto nodeId1 = Qt3DCore::QNodeId::createId(); auto nodeId2 = Qt3DCore::QNodeId::createId(); BlockToUBO ubo1 { 1, nodeId1, false, {}}; BlockToUBO ubo2 { 1, nodeId2, false, {}}; // WHEN pack.setUniformBuffer(ubo1); pack.setUniformBuffer(ubo2); // THEN QCOMPARE(pack.uniformBuffers().size(), 1U); QCOMPARE(pack.uniformBuffers().front().m_blockIndex, 1); QCOMPARE(pack.uniformBuffers().front().m_bufferID, nodeId2); // WHEN BlockToUBO ubo3 { 2, nodeId2, false, {}}; pack.setUniformBuffer(ubo3); // THEN QCOMPARE(pack.uniformBuffers().size(), 2U); QCOMPARE(pack.uniformBuffers().front().m_blockIndex, 1); QCOMPARE(pack.uniformBuffers().front().m_bufferID, nodeId2); QCOMPARE(pack.uniformBuffers().back().m_blockIndex, 2); QCOMPARE(pack.uniformBuffers().back().m_bufferID, nodeId2); } void checkShaderParameterPackStoresSingleSSBOPerBlockIndex() { // GIVEN ShaderParameterPack pack; auto nodeId1 = Qt3DCore::QNodeId::createId(); auto nodeId2 = Qt3DCore::QNodeId::createId(); BlockToSSBO ssbo1 { 1, 0, nodeId1}; BlockToSSBO ssbo2 { 1, 0, nodeId2}; // WHEN pack.setShaderStorageBuffer(ssbo1); pack.setShaderStorageBuffer(ssbo2); // THEN QCOMPARE(pack.shaderStorageBuffers().size(), 1U); QCOMPARE(pack.shaderStorageBuffers().front().m_blockIndex, 1); QCOMPARE(pack.shaderStorageBuffers().front().m_bufferID, nodeId2); // WHEN BlockToSSBO ssbo3 { 2, 1, nodeId2}; pack.setShaderStorageBuffer(ssbo3); // THEN QCOMPARE(pack.shaderStorageBuffers().size(), 2U); QCOMPARE(pack.shaderStorageBuffers().front().m_blockIndex, 1); QCOMPARE(pack.shaderStorageBuffers().front().m_bufferID, nodeId2); QCOMPARE(pack.shaderStorageBuffers().back().m_blockIndex, 2); QCOMPARE(pack.shaderStorageBuffers().back().m_bufferID, nodeId2); } void checkRenderViewUniformMinification() { QFETCH(QList, shaders); QFETCH(QList, rawParameters); QFETCH(QList, expectedMinimizedParameters); Qt3DRender::Render::NodeManagers nodeManagers; Renderer renderer; renderer.setNodeManagers(&nodeManagers); GLShaderManager *shaderManager = renderer.glResourceManagers()->glShaderManager(); for (int i = 0, m = shaders.size(); i < m; ++i) { Shader* backend = new Shader(); backend->setRenderer(&renderer); simulateInitializationSync(shaders.at(i), backend); shaderManager->createOrAdoptExisting(backend); } RenderView renderView; std::vector rawCommands; renderView.setRenderer(&renderer); for (int i = 0, m = shaders.size(); i < m; ++i) { RenderCommand c; c.m_shaderId = shaders.at(i)->id(); c.m_glShader = shaderManager->lookupResource(c.m_shaderId); c.m_parameterPack = rawParameters.at(i); rawCommands.push_back(c); } // WHEN renderView.addSortType(QList { QSortPolicy::Uniform }); EntityRenderCommandDataViewPtr view = EntityRenderCommandDataViewPtr::create(); view->data.commands = rawCommands; view->indices.resize(rawCommands.size()); std::iota(view->indices.begin(), view->indices.end(), 0); renderView.setRenderCommandDataView(view); renderView.sort(); // THEN int j = 0; renderView.forEachCommand([&] (const RenderCommand &command) { QCOMPARE(command.m_shaderId, shaders.at(j)->id()); compareShaderParameterPacks(command.m_parameterPack, expectedMinimizedParameters.at(j)); ++j; }); renderer.shutdown(); } void checkRenderCommandFrontToBackSorting() { // GIVEN Qt3DRender::Render::NodeManagers nodeManagers; Renderer renderer; RenderView renderView; std::vector rawCommands; renderer.setNodeManagers(&nodeManagers); renderView.setRenderer(&renderer); for (int i = 0; i < 200; ++i) { RenderCommand c; c.m_depth = float(i); rawCommands.push_back(c); } // WHEN renderView.addSortType(QList { QSortPolicy::FrontToBack }); EntityRenderCommandDataViewPtr view = EntityRenderCommandDataViewPtr::create(); view->data.commands = rawCommands; view->indices.resize(rawCommands.size()); std::iota(view->indices.begin(), view->indices.end(), 0); renderView.setRenderCommandDataView(view); renderView.sort(); // THEN int j = 0; RenderCommand previousRC; renderView.forEachCommand([&] (const RenderCommand &command) { if (j > 0) QVERIFY(previousRC.m_depth < command.m_depth); previousRC = command; ++j; }); // RenderCommands are deleted by RenderView dtor renderer.shutdown(); } void checkRenderCommandStateCostSorting() { // GIVEN Qt3DRender::Render::NodeManagers nodeManagers; Renderer renderer; RenderView renderView; std::vector rawCommands; renderer.setNodeManagers(&nodeManagers); renderView.setRenderer(&renderer); for (int i = 0; i < 200; ++i) { RenderCommand c; c.m_changeCost = i; rawCommands.push_back(c); } // WHEN renderView.addSortType(QList { QSortPolicy::StateChangeCost }); EntityRenderCommandDataViewPtr view = EntityRenderCommandDataViewPtr::create(); view->data.commands = rawCommands; view->indices.resize(rawCommands.size()); std::iota(view->indices.begin(), view->indices.end(), 0); renderView.setRenderCommandDataView(view); renderView.sort(); // THEN int j = 0; RenderCommand previousRC; renderView.forEachCommand([&] (const RenderCommand &command) { if (j > 0) QVERIFY(previousRC.m_changeCost > command.m_changeCost); previousRC = command; ++j; }); // RenderCommands are deleted by RenderView dtor renderer.shutdown(); } void checkRenderCommandCombinedStateMaterialDepthSorting() { // GIVEN Qt3DRender::Render::NodeManagers nodeManagers; Renderer renderer; RenderView renderView; std::vector rawCommands; renderer.setNodeManagers(&nodeManagers); renderView.setRenderer(&renderer); GLShader *dna[5] = { reinterpret_cast(0x250), reinterpret_cast(0x500), reinterpret_cast(0x1000), reinterpret_cast(0x1500), reinterpret_cast(0x2000) }; float depth[3] = { 10.0f, 25.0f, 30.0f }; int stateChangeCost[2] = { 100, 200 }; auto buildRC = [] (GLShader *dna, float depth, int changeCost) { RenderCommand c; c.m_glShader = dna; c.m_depth = depth; c.m_changeCost = changeCost; return c; }; RenderCommand c5 = buildRC(dna[3], depth[1], stateChangeCost[1]); RenderCommand c3 = buildRC(dna[3], depth[0], stateChangeCost[1]); RenderCommand c4 = buildRC(dna[2], depth[1], stateChangeCost[1]); RenderCommand c8 = buildRC(dna[1], depth[1], stateChangeCost[1]); RenderCommand c0 = buildRC(dna[0], depth[2], stateChangeCost[1]); RenderCommand c2 = buildRC(dna[2], depth[2], stateChangeCost[0]); RenderCommand c9 = buildRC(dna[2], depth[0], stateChangeCost[0]); RenderCommand c1 = buildRC(dna[1], depth[0], stateChangeCost[0]); RenderCommand c7 = buildRC(dna[0], depth[2], stateChangeCost[0]); RenderCommand c6 = buildRC(dna[0], depth[1], stateChangeCost[0]); rawCommands = { c0, c1, c2, c3, c4, c5, c6, c7, c8, c9 }; // WHEN renderView.addSortType(QList { QSortPolicy::StateChangeCost, QSortPolicy::Material, QSortPolicy::BackToFront }); EntityRenderCommandDataViewPtr view = EntityRenderCommandDataViewPtr::create(); view->data.commands = rawCommands; view->indices.resize(rawCommands.size()); std::iota(view->indices.begin(), view->indices.end(), 0); renderView.setRenderCommandDataView(view); renderView.sort(); // THEN const std::vector &sortedCommands = view->data.commands; const std::vector &sortedCommandIndices = view->indices; QCOMPARE(rawCommands.size(), sortedCommands.size()); // Ordered by higher state, higher shaderDNA and higher depth QCOMPARE(c0, sortedCommands.at(sortedCommandIndices[4])); QCOMPARE(c3, sortedCommands.at(sortedCommandIndices[1])); QCOMPARE(c4, sortedCommands.at(sortedCommandIndices[2])); QCOMPARE(c5, sortedCommands.at(sortedCommandIndices[0])); QCOMPARE(c8, sortedCommands.at(sortedCommandIndices[3])); QCOMPARE(c1, sortedCommands.at(sortedCommandIndices[7])); QCOMPARE(c2, sortedCommands.at(sortedCommandIndices[5])); QCOMPARE(c6, sortedCommands.at(sortedCommandIndices[9])); QCOMPARE(c7, sortedCommands.at(sortedCommandIndices[8])); QCOMPARE(c9, sortedCommands.at(sortedCommandIndices[6])); // RenderCommands are deleted by RenderView dtor renderer.shutdown(); } void checkRenderCommandTextureSorting() { // GIVEN RenderView renderView; Qt3DCore::QNodeId tex1 = Qt3DCore::QNodeId::createId(); Qt3DCore::QNodeId tex2 = Qt3DCore::QNodeId::createId(); Qt3DCore::QNodeId tex3 = Qt3DCore::QNodeId::createId(); Qt3DCore::QNodeId tex4 = Qt3DCore::QNodeId::createId(); RenderCommand a; { ShaderParameterPack pack; pack.setTexture(0, 0, tex1); pack.setTexture(1, 0, tex3); pack.setTexture(2, 0, tex4); pack.setTexture(3, 0, tex2); a.m_parameterPack = pack; } RenderCommand b; RenderCommand c; { ShaderParameterPack pack; pack.setTexture(0, 0, tex1); pack.setTexture(3, 0, tex2); c.m_parameterPack = pack; } RenderCommand d; { ShaderParameterPack pack; pack.setTexture(1, 0, tex3); pack.setTexture(2, 0, tex4); d.m_parameterPack = pack; } RenderCommand e; { ShaderParameterPack pack; pack.setTexture(3, 0, tex2); e.m_parameterPack = pack; } RenderCommand f; { ShaderParameterPack pack; pack.setTexture(3, 0, tex2); f.m_parameterPack = pack; } RenderCommand g; { ShaderParameterPack pack; pack.setTexture(0, 0, tex1); pack.setTexture(1, 0, tex3); pack.setTexture(2, 0, tex4); pack.setTexture(3, 0, tex2); g.m_parameterPack = pack; } // WHEN std::vector rawCommands = {a, b, c, d, e, f, g}; renderView.addSortType(QList { QSortPolicy::Texture }); EntityRenderCommandDataViewPtr view = EntityRenderCommandDataViewPtr::create(); view->data.commands = rawCommands; view->indices.resize(rawCommands.size()); std::iota(view->indices.begin(), view->indices.end(), 0); renderView.setRenderCommandDataView(view); renderView.sort(); // THEN const std::vector &sortedCommands = view->data.commands; const std::vector &sortedCommandIndices = view->indices; QCOMPARE(rawCommands.size(), sortedCommands.size()); QCOMPARE(sortedCommands.at(sortedCommandIndices[0]), a); QCOMPARE(sortedCommands.at(sortedCommandIndices[1]), g); QCOMPARE(sortedCommands.at(sortedCommandIndices[2]), d); QCOMPARE(sortedCommands.at(sortedCommandIndices[3]), c); QCOMPARE(sortedCommands.at(sortedCommandIndices[4]), e); QCOMPARE(sortedCommands.at(sortedCommandIndices[5]), f); QCOMPARE(sortedCommands.at(sortedCommandIndices[6]), b); // RenderCommands are deleted by RenderView dtor } private: }; } // OpenGL } // Render } // Qt3DRender QT_END_NAMESPACE //APPLESS_ QTEST_MAIN(Qt3DRender::Render::OpenGL::tst_RenderViews) #include "tst_renderviews.moc"