diff options
45 files changed, 767 insertions, 131 deletions
diff --git a/src/animation/backend/animationutils.cpp b/src/animation/backend/animationutils.cpp index 80c296a89..3f386d92a 100644 --- a/src/animation/backend/animationutils.cpp +++ b/src/animation/backend/animationutils.cpp @@ -158,11 +158,13 @@ ComponentIndices channelComponentsToIndices(const Channel &channel, #if defined Q_COMPILER_UNIFORM_INIT static const QVector<char> standardSuffixes = { 'X', 'Y', 'Z', 'W' }; static const QVector<char> quaternionSuffixes = { 'W', 'X', 'Y', 'Z' }; - static const QVector<char> colorSuffixes = { 'R', 'G', 'B' }; + static const QVector<char> colorSuffixesRGB = { 'R', 'G', 'B' }; + static const QVector<char> colorSuffixesRGBA = { 'R', 'G', 'B', 'A' }; #else static const QVector<char> standardSuffixes = (QVector<char>() << 'X' << 'Y' << 'Z' << 'W'); static const QVector<char> quaternionSuffixes = (QVector<char>() << 'W' << 'X' << 'Y' << 'Z'); - static const QVector<char> colorSuffixes = (QVector<char>() << 'R' << 'G' << 'B'); + static const QVector<char> colorSuffixesRGB = (QVector<char>() << 'R' << 'G' << 'B'); + static const QVector<char> colorSuffixesRGBA = (QVector<char>() << 'R' << 'G' << 'B' << 'A'); #endif switch (dataType) { @@ -170,8 +172,12 @@ ComponentIndices channelComponentsToIndices(const Channel &channel, return channelComponentsToIndicesHelper(channel, expectedComponentCount, offset, quaternionSuffixes); case QVariant::Color: + if (expectedComponentCount == 3) + return channelComponentsToIndicesHelper(channel, expectedComponentCount, + offset, colorSuffixesRGB); + Q_ASSERT(expectedComponentCount == 4); return channelComponentsToIndicesHelper(channel, expectedComponentCount, - offset, colorSuffixes); + offset, colorSuffixesRGBA); default: return channelComponentsToIndicesHelper(channel, expectedComponentCount, offset, standardSuffixes); @@ -385,10 +391,12 @@ QVariant buildPropertyValue(const MappingData &mappingData, const QVector<float> } case QVariant::Color: { + // A color can either be a vec3 or a vec4 const QColor color = QColor::fromRgbF(channelResults[mappingData.channelIndices[0]], channelResults[mappingData.channelIndices[1]], - channelResults[mappingData.channelIndices[2]]); + channelResults[mappingData.channelIndices[2]], + mappingData.channelIndices.size() > 3 ? channelResults[mappingData.channelIndices[3]] : 1.0f); return QVariant::fromValue(color); } diff --git a/src/core/aspects/qaspectengine.cpp b/src/core/aspects/qaspectengine.cpp index c11de2e26..d73771910 100644 --- a/src/core/aspects/qaspectengine.cpp +++ b/src/core/aspects/qaspectengine.cpp @@ -396,6 +396,10 @@ QVariant QAspectEngine::executeCommand(const QString &command) const QStringList names = d->m_aspectManager->serviceLocator()->systemInformation()->aspectNames(); return names.join(QLatin1String("\n")); } + if (command == QLatin1String("dump jobs")) { + d->m_aspectManager->dumpJobsOnNextFrame(); + return QLatin1String("Dump in next frame in working directory"); + } QStringList args = command.split(QLatin1Char(' ')); QString aspectName = args.takeFirst(); diff --git a/src/core/aspects/qaspectmanager.cpp b/src/core/aspects/qaspectmanager.cpp index 225d7550e..28c820e27 100644 --- a/src/core/aspects/qaspectmanager.cpp +++ b/src/core/aspects/qaspectmanager.cpp @@ -128,6 +128,7 @@ QAspectManager::QAspectManager(QAspectEngine *parent) , m_simulationAnimation(nullptr) #endif , m_jobsInLastFrame(0) + , m_dumpJobs(false) { qRegisterMetaType<QSurface *>("QSurface*"); qCDebug(Aspects) << Q_FUNC_INFO; @@ -430,6 +431,11 @@ QVector<QNode *> QAspectManager::lookupNodes(const QVector<QNodeId> &ids) const return d->m_scene ? d->m_scene->lookupNodes(ids) : QVector<QNode *>{}; } +void QAspectManager::dumpJobsOnNextFrame() +{ + m_dumpJobs = true; +} + #if !QT_CONFIG(animation) /*! \internal @@ -528,7 +534,8 @@ void QAspectManager::processFrame() // For each Aspect // Ask them to launch set of jobs for the current frame // Updates matrices, bounding volumes, render bins ... - m_jobsInLastFrame = m_scheduler->scheduleAndWaitForFrameAspectJobs(t); + m_jobsInLastFrame = m_scheduler->scheduleAndWaitForFrameAspectJobs(t, m_dumpJobs); + m_dumpJobs = false; // Tell the aspect the frame is complete (except rendering) for (QAbstractAspect *aspect : qAsConst(m_aspects)) diff --git a/src/core/aspects/qaspectmanager_p.h b/src/core/aspects/qaspectmanager_p.h index e6772e095..e0aefdfde 100644 --- a/src/core/aspects/qaspectmanager_p.h +++ b/src/core/aspects/qaspectmanager_p.h @@ -116,6 +116,7 @@ public: QVector<QNode *> lookupNodes(const QVector<QNodeId> &ids) const; int jobsInLastFrame() const { return m_jobsInLastFrame; } + void dumpJobsOnNextFrame(); private: #if !QT_CONFIG(animation) @@ -140,6 +141,7 @@ private: RequestFrameAnimation *m_simulationAnimation; #endif int m_jobsInLastFrame; + bool m_dumpJobs; }; } // namespace Qt3DCore diff --git a/src/core/jobs/qabstractaspectjobmanager_p.h b/src/core/jobs/qabstractaspectjobmanager_p.h index b3e76b86d..c7f8cf721 100644 --- a/src/core/jobs/qabstractaspectjobmanager_p.h +++ b/src/core/jobs/qabstractaspectjobmanager_p.h @@ -66,7 +66,7 @@ public: virtual void initialize() {} virtual void enqueueJobs(const QVector<QAspectJobPtr> &jobQueue) = 0; - virtual void waitForAllJobs() = 0; + virtual int waitForAllJobs() = 0; // Callback signature for running SynchronizedJobs typedef void (*JobFunction)(void *); diff --git a/src/core/jobs/qaspectjob.cpp b/src/core/jobs/qaspectjob.cpp index 00ad1ace8..0a2ee9841 100644 --- a/src/core/jobs/qaspectjob.cpp +++ b/src/core/jobs/qaspectjob.cpp @@ -56,6 +56,7 @@ bool isDependencyNull(const QWeakPointer<QAspectJob> &dep) } // anonymous QAspectJobPrivate::QAspectJobPrivate() + : m_jobName(QLatin1String("UnknowJob")) { } @@ -66,6 +67,11 @@ QAspectJobPrivate *QAspectJobPrivate::get(QAspectJob *job) return job->d_func(); } +bool QAspectJobPrivate::isRequired() +{ + return true; +} + void QAspectJobPrivate::postFrame(QAspectManager *aspectManager) { Q_UNUSED(aspectManager) diff --git a/src/core/jobs/qaspectjob_p.h b/src/core/jobs/qaspectjob_p.h index ddad09c86..63a2cc572 100644 --- a/src/core/jobs/qaspectjob_p.h +++ b/src/core/jobs/qaspectjob_p.h @@ -72,18 +72,31 @@ public: static QAspectJobPrivate *get(QAspectJob *job); + virtual bool isRequired(); virtual void postFrame(QAspectManager *aspectManager); QVector<QWeakPointer<QAspectJob> > m_dependencies; JobId m_jobId; + QString m_jobName; }; } // Qt3D #define SET_JOB_RUN_STAT_TYPE(job, type, instance) \ { \ - auto &jobId = Qt3DCore::QAspectJobPrivate::get(job)->m_jobId; \ + auto djob = Qt3DCore::QAspectJobPrivate::get(job); \ + auto &jobId = djob->m_jobId; \ jobId.typeAndInstance[0] = type; \ jobId.typeAndInstance[1] = instance; \ + djob->m_jobName = QLatin1String(#type); \ + } + +#define SET_JOB_RUN_STAT_TYPE_AND_NAME(job, type, name, instance) \ + { \ + auto djob = Qt3DCore::QAspectJobPrivate::get(job); \ + auto &jobId = djob->m_jobId; \ + jobId.typeAndInstance[0] = type; \ + jobId.typeAndInstance[1] = instance; \ + djob->m_jobName = QLatin1String(name); \ } QT_END_NAMESPACE diff --git a/src/core/jobs/qaspectjobmanager.cpp b/src/core/jobs/qaspectjobmanager.cpp index be7942359..7103f5100 100644 --- a/src/core/jobs/qaspectjobmanager.cpp +++ b/src/core/jobs/qaspectjobmanager.cpp @@ -86,7 +86,7 @@ void QAspectJobManager::enqueueJobs(const QVector<QAspectJobPtr> &jobQueue) taskList << task; } - for (const QSharedPointer<QAspectJob> &job : jobQueue) { + for (const QAspectJobPtr &job : jobQueue) { const QVector<QWeakPointer<QAspectJob> > &deps = job->dependencies(); AspectTaskRunnable *taskDepender = tasksMap.value(job.data()); @@ -108,9 +108,9 @@ void QAspectJobManager::enqueueJobs(const QVector<QAspectJobPtr> &jobQueue) } // Wait for all aspects jobs to be completed -void QAspectJobManager::waitForAllJobs() +int QAspectJobManager::waitForAllJobs() { - m_threadPooler->future().waitForFinished(); + return m_threadPooler->waitForAllJobs(); } void QAspectJobManager::waitForPerThreadFunction(JobFunction func, void *arg) diff --git a/src/core/jobs/qaspectjobmanager_p.h b/src/core/jobs/qaspectjobmanager_p.h index acbd0263e..db0075443 100644 --- a/src/core/jobs/qaspectjobmanager_p.h +++ b/src/core/jobs/qaspectjobmanager_p.h @@ -76,7 +76,7 @@ public: void enqueueJobs(const QVector<QAspectJobPtr> &jobQueue) override; - void waitForAllJobs() override; + int waitForAllJobs() override; void waitForPerThreadFunction(JobFunction func, void *arg) override; diff --git a/src/core/jobs/qthreadpooler.cpp b/src/core/jobs/qthreadpooler.cpp index f5c50062a..621881597 100644 --- a/src/core/jobs/qthreadpooler.cpp +++ b/src/core/jobs/qthreadpooler.cpp @@ -50,6 +50,7 @@ QThreadPooler::QThreadPooler(QObject *parent) , m_mutex() , m_taskCount(0) , m_threadPool(QThreadPool::globalInstance()) + , m_totalRunJobs(0) { const QByteArray maxThreadCount = qgetenv("QT3D_MAX_THREAD_COUNT"); if (!maxThreadCount.isEmpty()) { @@ -75,6 +76,7 @@ void QThreadPooler::enqueueTasks(const QVector<RunnableInterface *> &tasks) // The caller have to set the mutex const QVector<RunnableInterface *>::const_iterator end = tasks.cend(); + m_totalRunJobs = 0; for (QVector<RunnableInterface *>::const_iterator it = tasks.cbegin(); it != end; ++it) { @@ -86,32 +88,48 @@ void QThreadPooler::enqueueTasks(const QVector<RunnableInterface *> &tasks) if (!hasDependencies(*it) && !(*it)->reserved()) { (*it)->setReserved(true); - (*it)->setPooler(this); - m_threadPool->start((*it)); + if ((*it)->isRequired()) { + (*it)->setPooler(this); + m_threadPool->start((*it)); + } else { + release(); + enqueueDepencies(*it); + } } } } -void QThreadPooler::taskFinished(RunnableInterface *task) +void QThreadPooler::enqueueDepencies(RunnableInterface *task) { - const QMutexLocker locker(&m_mutex); - - release(); - if (task->type() == RunnableInterface::RunnableType::AspectTask) { AspectTaskRunnable *aspectTask = static_cast<AspectTaskRunnable *>(task); const auto &dependers = aspectTask->m_dependers; for (auto it = dependers.begin(); it != dependers.end(); ++it) { - aspectTask = static_cast<AspectTaskRunnable *>(*it); - if (--aspectTask->m_dependerCount == 0) { - if (!aspectTask->reserved()) { - aspectTask->setReserved(true); - aspectTask->setPooler(this); - m_threadPool->start(aspectTask); + AspectTaskRunnable *dependerTask = static_cast<AspectTaskRunnable *>(*it); + if (--dependerTask->m_dependerCount == 0) { + if (!dependerTask->reserved()) { + dependerTask->setReserved(true); + if ((*it)->isRequired()) { + dependerTask->setPooler(this); + m_threadPool->start(dependerTask); + } else { + release(); + enqueueDepencies(*it); + } } } } } +} + +void QThreadPooler::taskFinished(RunnableInterface *task) +{ + const QMutexLocker locker(&m_mutex); + + release(); + m_totalRunJobs++; + + enqueueDepencies(task); if (currentCount() == 0) { if (m_futureInterface) { @@ -137,6 +155,12 @@ QFuture<void> QThreadPooler::mapDependables(QVector<RunnableInterface *> &taskQu return QFuture<void>(m_futureInterface); } +int QThreadPooler::waitForAllJobs() +{ + future().waitForFinished(); + return m_totalRunJobs; +} + QFuture<void> QThreadPooler::future() { const QMutexLocker locker(&m_mutex); diff --git a/src/core/jobs/qthreadpooler_p.h b/src/core/jobs/qthreadpooler_p.h index 1970641b8..fafd549b2 100644 --- a/src/core/jobs/qthreadpooler_p.h +++ b/src/core/jobs/qthreadpooler_p.h @@ -69,10 +69,11 @@ class Q_3DCORE_PRIVATE_EXPORT QThreadPooler : public QObject Q_OBJECT public: - explicit QThreadPooler(QObject *parent = 0); + explicit QThreadPooler(QObject *parent = nullptr); ~QThreadPooler(); QFuture<void> mapDependables(QVector<RunnableInterface *> &taskQueue); + int waitForAllJobs(); void taskFinished(RunnableInterface *task); QFuture<void> future(); @@ -80,6 +81,7 @@ public: private: void enqueueTasks(const QVector<RunnableInterface *> &tasks); + void enqueueDepencies(RunnableInterface *task); void acquire(int add); void release(); int currentCount() const; @@ -89,6 +91,7 @@ private: QMutex m_mutex; QAtomicInt m_taskCount; QThreadPool *m_threadPool; + int m_totalRunJobs; }; } // namespace Qt3DCore diff --git a/src/core/jobs/task.cpp b/src/core/jobs/task.cpp index 1dd5712c9..f5bfae014 100644 --- a/src/core/jobs/task.cpp +++ b/src/core/jobs/task.cpp @@ -68,6 +68,11 @@ AspectTaskRunnable::~AspectTaskRunnable() { } +bool AspectTaskRunnable::isRequired() +{ + return m_job ? QAspectJobPrivate::get(m_job.data())->isRequired() : false; +} + void AspectTaskRunnable::run() { if (m_job) { @@ -100,6 +105,11 @@ SyncTaskRunnable::~SyncTaskRunnable() { } +bool SyncTaskRunnable::isRequired() +{ + return true; +} + void SyncTaskRunnable::run() { // Call the function diff --git a/src/core/jobs/task_p.h b/src/core/jobs/task_p.h index ff411539f..90d0674b4 100644 --- a/src/core/jobs/task_p.h +++ b/src/core/jobs/task_p.h @@ -77,6 +77,7 @@ public: virtual ~RunnableInterface(); + virtual bool isRequired() = 0; virtual void run() = 0; virtual int id() = 0; @@ -96,6 +97,7 @@ public: AspectTaskRunnable(QSystemInformationService *service); ~AspectTaskRunnable(); + bool isRequired() override; void run() override; void setPooler(QThreadPooler *pooler) override { m_pooler = pooler; } @@ -127,6 +129,7 @@ public: QAtomicInt *atomicCount); ~SyncTaskRunnable(); + bool isRequired() override; void run() override; void setPooler(QThreadPooler *pooler) override { m_pooler = pooler; } diff --git a/src/core/qscheduler.cpp b/src/core/qscheduler.cpp index 17ca1fbfd..c52445a35 100644 --- a/src/core/qscheduler.cpp +++ b/src/core/qscheduler.cpp @@ -46,8 +46,48 @@ #include <Qt3DCore/private/qaspectjob_p.h> #include <Qt3DCore/private/qabstractaspectjobmanager_p.h> +#include <QtCore/QCoreApplication> +#include <QtCore/QDateTime> +#include <QtCore/QRegularExpression> + QT_BEGIN_NAMESPACE +namespace { + +// Creates a graphviz dot file. To view online: https://dreampuf.github.io/GraphvizOnline/ +void dumpJobs(QVector<Qt3DCore::QAspectJobPtr> jobs) { + const QString fileName = QStringLiteral("qt3djobs_") + QCoreApplication::applicationName() + + QDateTime::currentDateTime().toString(QStringLiteral("_yyMMdd-hhmmss")) + QStringLiteral(".dot"); + + QFile f(fileName); + if (!f.open(QFile::WriteOnly)) + return; + + auto formatJob = [](Qt3DCore::QAspectJob *job) -> QString { + auto jobId = Qt3DCore::QAspectJobPrivate::get(job)->m_jobId; + auto type = Qt3DCore::QAspectJobPrivate::get(job)->m_jobName.replace(QRegularExpression(QLatin1String("(^.*::)")), QLatin1String("")); + return QString(QLatin1String("\"%1_%2\"")).arg(type).arg(jobId.typeAndInstance[1]); + }; + + QTextStream stream(&f); + stream << "digraph qt3d_jobs {" << Qt::endl; + + for (const auto &job: jobs) { + if (!Qt3DCore::QAspectJobPrivate::get(job.data())->isRequired()) + stream << QLatin1String("\t") << formatJob(job.data()) << QLatin1String(" [style=dotted]") << Qt::endl; + } + + for (const auto &job: jobs) { + auto dependencies = job->dependencies(); + for (const auto &dependency: dependencies) + stream << QLatin1String("\t") << formatJob(job.data()) << QLatin1String(" -> ") << formatJob(dependency.toStrongRef().data()) << Qt::endl; + } + + stream << "}" << Qt::endl; +} + +} + namespace Qt3DCore { QScheduler::QScheduler(QObject *parent) @@ -70,7 +110,7 @@ QAspectManager *QScheduler::aspectManager() const return m_aspectManager; } -int QScheduler::scheduleAndWaitForFrameAspectJobs(qint64 time) +int QScheduler::scheduleAndWaitForFrameAspectJobs(qint64 time, bool dumpJobs) { QVector<QAspectJobPtr> jobQueue; @@ -86,12 +126,15 @@ int QScheduler::scheduleAndWaitForFrameAspectJobs(qint64 time) jobQueue << aspectJobs; } + if (dumpJobs) + ::dumpJobs(jobQueue); + m_aspectManager->jobManager()->enqueueJobs(jobQueue); // Do any other work here that the aspect thread can usefully be doing // whilst the threadpool works its way through the jobs - m_aspectManager->jobManager()->waitForAllJobs(); + int totalJobs = m_aspectManager->jobManager()->waitForAllJobs(); { QTaskLogger logger(m_aspectManager->serviceLocator()->systemInformation(), 4097, 0, QTaskLogger::AspectJob); @@ -103,7 +146,7 @@ int QScheduler::scheduleAndWaitForFrameAspectJobs(qint64 time) QAbstractAspectPrivate::get(aspect)->jobsDone(); } - return jobQueue.size(); + return totalJobs; } } // namespace Qt3DCore diff --git a/src/core/qscheduler_p.h b/src/core/qscheduler_p.h index 92e8e04fb..a781e1cbb 100644 --- a/src/core/qscheduler_p.h +++ b/src/core/qscheduler_p.h @@ -70,7 +70,7 @@ public: void setAspectManager(QAspectManager *aspectManager); QAspectManager *aspectManager() const; - virtual int scheduleAndWaitForFrameAspectJobs(qint64 time); + virtual int scheduleAndWaitForFrameAspectJobs(qint64 time, bool dumpJobs); private: QAspectManager *m_aspectManager; diff --git a/src/plugins/renderers/opengl/debug/imguirenderer.cpp b/src/plugins/renderers/opengl/debug/imguirenderer.cpp index e23476860..687f5e4fc 100644 --- a/src/plugins/renderers/opengl/debug/imguirenderer.cpp +++ b/src/plugins/renderers/opengl/debug/imguirenderer.cpp @@ -289,7 +289,10 @@ void ImGuiRenderer::renderDebugOverlay(const QVector<RenderView *> &renderViews, if (ImGui::Button("FrameGraph Paths##1")) QMetaObject::invokeMethod(m_renderer->services()->systemInformation(), "dumpCommand", Qt::QueuedConnection, Q_ARG(QString, QLatin1String("render framepaths"))); - + ImGui::SameLine(); + if (ImGui::Button("JobsGraph##1")) + QMetaObject::invokeMethod(m_renderer->services()->systemInformation(), "dumpCommand", + Qt::QueuedConnection, Q_ARG(QString, QLatin1String("dump jobs"))); ImGui::End(); if (m_showGLInfoWindow) @@ -334,7 +337,8 @@ void ImGuiRenderer::showRenderDetails(const QVector<RenderView *> &renderViews) int j = 1; const auto commands = view->commands(); for (const RenderCommand &command: commands) { - QString label(QLatin1String("Command ") + QString::number(j++)); + GeometryRenderer *rGeometryRenderer = m_renderer->nodeManagers()->data<GeometryRenderer, GeometryRendererManager>(command.m_geometryRenderer); + QString label = QString(QLatin1String("Command %1 {%2}")).arg(QString::number(j++), QString::number(rGeometryRenderer->peerId().id())); if (ImGui::TreeNode(label.toLatin1().data())) { ImGui::Text("Primitive Type: %s %s", primitiveTypeName(command.m_primitiveType), command.m_drawIndexed ? "(indexed)" : ""); diff --git a/src/plugins/renderers/opengl/graphicshelpers/graphicscontext.cpp b/src/plugins/renderers/opengl/graphicshelpers/graphicscontext.cpp index 16cc3ff8f..508b38755 100644 --- a/src/plugins/renderers/opengl/graphicshelpers/graphicscontext.cpp +++ b/src/plugins/renderers/opengl/graphicshelpers/graphicscontext.cpp @@ -239,7 +239,7 @@ void GraphicsContext::doneCurrent() m_glHelper = nullptr; } -// Called by GL Command Thread +// Called by GraphicsContext::loadShader itself called by Renderer::updateGLResources GraphicsContext::ShaderCreationInfo GraphicsContext::createShaderProgram(GLShader *shader) { QOpenGLShaderProgram *shaderProgram = shader->shaderProgram(); @@ -309,14 +309,17 @@ void GraphicsContext::loadShader(Shader *shaderNode, const QVector<Qt3DCore::QNodeId> sharedShaderIds = glShaderManager->shaderIdsForProgram(glShader); if (sharedShaderIds.size() == 1) { - // Shader in the cache hasn't been loaded yet - glShader->setGraphicsContext(this); - glShader->setShaderCode(shaderNode->shaderCode()); - const ShaderCreationInfo loadResult = createShaderProgram(glShader); - shaderNode->setStatus(loadResult.linkSucceeded ? QShaderProgram::Ready : QShaderProgram::Error); - shaderNode->setLog(loadResult.logs); - // Loaded in the sense we tried to load it (and maybe it failed) - glShader->setLoaded(true); + // The Shader could already be loaded if we retrieved one + // that had been marked for destruction + if (!glShader->isLoaded()) { + glShader->setGraphicsContext(this); + glShader->setShaderCode(shaderNode->shaderCode()); + const ShaderCreationInfo loadResult = createShaderProgram(glShader); + shaderNode->setStatus(loadResult.linkSucceeded ? QShaderProgram::Ready : QShaderProgram::Error); + shaderNode->setLog(loadResult.logs); + // Loaded in the sense we tried to load it (and maybe it failed) + glShader->setLoaded(true); + } } else { // Find an already loaded shader that shares the same QOpenGLShaderProgram for (const Qt3DCore::QNodeId sharedShaderId : sharedShaderIds) { diff --git a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp index 25a7d0036..b22477435 100644 --- a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp +++ b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp @@ -486,9 +486,8 @@ void SubmissionContext::activateRenderTarget(Qt3DCore::QNodeId renderTargetNodeI // New RenderTarget if (!m_renderTargets.contains(renderTargetNodeId)) { if (m_defaultFBO && fboId == m_defaultFBO) { - // this is the default fbo that some platforms create (iOS), we just register it - // Insert FBO into hash - m_renderTargets.insert(renderTargetNodeId, fboId); + // this is the default fbo that some platforms create (iOS), we never + // register it } else { fboId = createRenderTarget(renderTargetNodeId, attachments); } @@ -497,21 +496,31 @@ void SubmissionContext::activateRenderTarget(Qt3DCore::QNodeId renderTargetNodeI } } m_activeFBO = fboId; + m_activeFBONodeId = renderTargetNodeId; m_glHelper->bindFrameBufferObject(m_activeFBO, GraphicsHelperInterface::FBODraw); // Set active drawBuffers activateDrawBuffers(attachments); } +void SubmissionContext::releaseRenderTarget(const Qt3DCore::QNodeId id) +{ + if (m_renderTargets.contains(id)) { + const RenderTargetInfo targetInfo = m_renderTargets.take(id); + const GLuint fboId = targetInfo.fboId; + m_glHelper->releaseFrameBufferObject(fboId); + } +} + GLuint SubmissionContext::createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments) { const GLuint fboId = m_glHelper->createFrameBufferObject(); if (fboId) { // The FBO is created and its attachments are set once - // Insert FBO into hash - m_renderTargets.insert(renderTargetNodeId, fboId); // Bind FBO m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw); - bindFrameBufferAttachmentHelper(fboId, attachments); + // Insert FBO into hash + const RenderTargetInfo info = bindFrameBufferAttachmentHelper(fboId, attachments); + m_renderTargets.insert(renderTargetNodeId, info); } else { qCritical("Failed to create FBO"); } @@ -520,31 +529,38 @@ GLuint SubmissionContext::createRenderTarget(Qt3DCore::QNodeId renderTargetNodeI GLuint SubmissionContext::updateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, bool isActiveRenderTarget) { - const GLuint fboId = m_renderTargets.value(renderTargetNodeId); + const RenderTargetInfo fboInfo = m_renderTargets.value(renderTargetNodeId); + const GLuint fboId =fboInfo.fboId; - // We need to check if one of the attachment was resized - bool needsResize = !m_renderTargetsSize.contains(fboId); // not even initialized yet? - if (!needsResize) { + // We need to check if one of the attachnent have changed QTBUG-64757 + bool needsRebuild = attachments != fboInfo.attachments; + + // Even if the attachment packs are the same, one of the inner texture might have + // been resized or recreated, we need to check for that + if (!needsRebuild) { // render target exists, has attachment been resized? GLTextureManager *glTextureManager = m_renderer->glResourceManagers()->glTextureManager(); - const QSize s = m_renderTargetsSize[fboId]; + const QSize s = fboInfo.size; + const auto attachments_ = attachments.attachments(); for (const Attachment &attachment : attachments_) { + const bool textureWasUpdated = m_updateTextureIds.contains(attachment.m_textureUuid); GLTexture *rTex = glTextureManager->lookupResource(attachment.m_textureUuid); - // ### TODO QTBUG-64757 this check is insufficient since the - // texture may have changed to another one with the same size. That - // case is not handled atm. if (rTex) { - needsResize |= rTex->size() != s; + const bool sizeHasChanged = rTex->size() != s; + needsRebuild |= sizeHasChanged; if (isActiveRenderTarget && attachment.m_point == QRenderTargetOutput::Color0) m_renderTargetFormat = rTex->properties().format; } + needsRebuild |= textureWasUpdated; } } - if (needsResize) { + if (needsRebuild) { m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw); - bindFrameBufferAttachmentHelper(fboId, attachments); + const RenderTargetInfo updatedInfo = bindFrameBufferAttachmentHelper(fboId, attachments); + // Update our stored Render Target Info + m_renderTargets.insert(renderTargetNodeId, updatedInfo); } return fboId; @@ -555,8 +571,8 @@ QSize SubmissionContext::renderTargetSize(const QSize &surfaceSize) const QSize renderTargetSize; if (m_activeFBO != m_defaultFBO) { // For external FBOs we may not have a m_renderTargets entry. - if (m_renderTargetsSize.contains(m_activeFBO)) { - renderTargetSize = m_renderTargetsSize[m_activeFBO]; + if (m_renderTargets.contains(m_activeFBONodeId)) { + renderTargetSize = m_renderTargets[m_activeFBONodeId].size; } else if (surfaceSize.isValid()) { renderTargetSize = surfaceSize; } else { @@ -800,7 +816,7 @@ bool SubmissionContext::activateShader(GLShader *shader) return true; } -void SubmissionContext::bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments) +SubmissionContext::RenderTargetInfo SubmissionContext::bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments) { // Set FBO attachments. These are normally textures, except that on Open GL // ES <= 3.1 we must use a renderbuffer if a combined depth+stencil is @@ -835,7 +851,7 @@ void SubmissionContext::bindFrameBufferAttachmentHelper(GLuint fboId, const Atta } } } - m_renderTargetsSize.insert(fboId, fboSize); + return {fboId, fboSize, attachments}; } void SubmissionContext::activateDrawBuffers(const AttachmentPack &attachments) @@ -1141,6 +1157,11 @@ void SubmissionContext::deleteSync(GLFence sync) m_glHelper->deleteSync(sync); } +void SubmissionContext::setUpdatedTexture(const Qt3DCore::QNodeIdVector &updatedTextureIds) +{ + m_updateTextureIds = updatedTextureIds; +} + // It will be easier if the QGraphicContext applies the QUniformPack // than the other way around bool SubmissionContext::setParameters(ShaderParameterPack ¶meterPack) @@ -1526,13 +1547,13 @@ void SubmissionContext::blitFramebuffer(Qt3DCore::QNodeId inputRenderTargetId, // Up until this point the input and output rects are normal Qt rectangles. // Convert them to GL rectangles (Y at bottom). - const int inputFboHeight = inputFboId == defaultFboId ? m_surfaceSize.height() : m_renderTargetsSize[inputFboId].height(); + const int inputFboHeight = inputFboId == defaultFboId ? m_surfaceSize.height() : m_renderTargets[inputRenderTargetId].size.height(); const GLint srcX0 = inputRect.left(); const GLint srcY0 = inputFboHeight - (inputRect.top() + inputRect.height()); const GLint srcX1 = srcX0 + inputRect.width(); const GLint srcY1 = srcY0 + inputRect.height(); - const int outputFboHeight = outputFboId == defaultFboId ? m_surfaceSize.height() : m_renderTargetsSize[outputFboId].height(); + const int outputFboHeight = outputFboId == defaultFboId ? m_surfaceSize.height() : m_renderTargets[outputRenderTargetId].size.height(); const GLint dstX0 = outputRect.left(); const GLint dstY0 = outputFboHeight - (outputRect.top() + outputRect.height()); const GLint dstX1 = dstX0 + outputRect.width(); diff --git a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h index b663e47ec..d43cae4f8 100644 --- a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h +++ b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h @@ -61,6 +61,7 @@ #include <Qt3DRender/qclearbuffers.h> #include <Qt3DRender/qattribute.h> #include <Qt3DRender/private/handle_types_p.h> +#include <Qt3DRender/private/attachmentpack_p.h> QT_BEGIN_NAMESPACE @@ -113,6 +114,7 @@ public: // FBO GLuint activeFBO() const { return m_activeFBO; } void activateRenderTarget(const Qt3DCore::QNodeId id, const AttachmentPack &attachments, GLuint defaultFboId); + void releaseRenderTarget(const Qt3DCore::QNodeId id); QSize renderTargetSize(const QSize &surfaceSize) const; QImage readFramebuffer(const QRect &rect); void blitFramebuffer(Qt3DCore::QNodeId outputRenderTargetId, Qt3DCore::QNodeId inputRenderTargetId, @@ -159,7 +161,16 @@ public: bool wasSyncSignaled(GLFence sync); void deleteSync(GLFence sync); + // Textures + void setUpdatedTexture(const Qt3DCore::QNodeIdVector &updatedTextureIds); + private: + struct RenderTargetInfo { + GLuint fboId; + QSize size; + AttachmentPack attachments; + }; + void initialize(); // Material @@ -167,7 +178,7 @@ private: void setActiveMaterial(Material* rmat); // FBO - void bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments); + RenderTargetInfo bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments); void activateDrawBuffers(const AttachmentPack &attachments); void resolveRenderTargetFormat(); GLuint createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments); @@ -187,8 +198,9 @@ private: QOpenGLShaderProgram *m_activeShader; QHash<Qt3DCore::QNodeId, HGLBuffer> m_renderBufferHash; - QHash<Qt3DCore::QNodeId, GLuint> m_renderTargets; - QHash<GLuint, QSize> m_renderTargetsSize; + + + QHash<Qt3DCore::QNodeId, RenderTargetInfo> m_renderTargets; QAbstractTexture::TextureFormat m_renderTargetFormat; // cache some current state, to make sure we don't issue unnecessary GL calls @@ -199,6 +211,7 @@ private: Material* m_material; QRectF m_viewport; GLuint m_activeFBO; + Qt3DCore::QNodeId m_activeFBONodeId; GLBuffer *m_boundArrayBuffer; RenderStateSet* m_stateSet; @@ -227,6 +240,8 @@ private: using VAOIndexAttribute = HGLBuffer; void enableAttribute(const VAOVertexAttribute &attr); void disableAttribute(const VAOVertexAttribute &attr); + + Qt3DCore::QNodeIdVector m_updateTextureIds; }; } // namespace OpenGL diff --git a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp index 79a115525..ed0854ecf 100644 --- a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp +++ b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp @@ -54,9 +54,36 @@ namespace { int renderViewInstanceCounter = 0; } // anonymous +class RenderViewCommandUpdaterJobPrivate : public Qt3DCore::QAspectJobPrivate +{ +public: + RenderViewCommandUpdaterJobPrivate(RenderViewCommandUpdaterJob *q) : q_ptr(q) { } + ~RenderViewCommandUpdaterJobPrivate() override = default; + + bool isRequired() override; + void postFrame(Qt3DCore::QAspectManager *manager) override; + + RenderViewCommandUpdaterJob *q_ptr; + Q_DECLARE_PUBLIC(RenderViewCommandUpdaterJob) +}; + +bool RenderViewCommandUpdaterJobPrivate::isRequired() +{ + Q_Q(RenderViewCommandUpdaterJob); + + return q->m_renderView && !q->m_renderView->noDraw() && q->m_count > 0; +} + +void RenderViewCommandUpdaterJobPrivate::postFrame(Qt3DCore::QAspectManager *manager) +{ + Q_UNUSED(manager) + + // reset to 0 after every frame, stops the number growing indefinitely + renderViewInstanceCounter = 0; +} RenderViewCommandUpdaterJob::RenderViewCommandUpdaterJob() - : Qt3DCore::QAspectJob() + : Qt3DCore::QAspectJob(*new RenderViewCommandUpdaterJobPrivate(this)) , m_offset(0) , m_count(0) , m_renderView(nullptr) diff --git a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h index 2fe5749ad..bb1975c82 100644 --- a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h +++ b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h @@ -65,6 +65,7 @@ namespace OpenGL { class RenderView; class Renderer; +class RenderViewCommandUpdaterJobPrivate; class Q_AUTOTEST_EXPORT RenderViewCommandUpdaterJob : public Qt3DCore::QAspectJob { @@ -92,6 +93,8 @@ private: Renderer *m_renderer; EntityRenderCommandDataPtr m_renderables; QVector<RenderCommand> m_commands; + + Q_DECLARE_PRIVATE(RenderViewCommandUpdaterJob) }; typedef QSharedPointer<RenderViewCommandUpdaterJob> RenderViewCommandUpdaterJobPtr; diff --git a/src/plugins/renderers/opengl/renderer/renderer.cpp b/src/plugins/renderers/opengl/renderer/renderer.cpp index a50a770eb..2dafa15c5 100644 --- a/src/plugins/renderers/opengl/renderer/renderer.cpp +++ b/src/plugins/renderers/opengl/renderer/renderer.cpp @@ -267,19 +267,19 @@ Renderer::Renderer(QRenderAspect::RenderType type) , m_lightGathererJob(Render::LightGathererPtr::create()) , m_renderableEntityFilterJob(Render::RenderableEntityFilterPtr::create()) , m_computableEntityFilterJob(Render::ComputableEntityFilterPtr::create()) - , m_bufferGathererJob(SynchronizerJobPtr::create([this] { lookForDirtyBuffers(); }, JobTypes::DirtyBufferGathering)) - , m_vaoGathererJob(SynchronizerJobPtr::create([this] { lookForAbandonedVaos(); }, JobTypes::DirtyVaoGathering)) - , m_textureGathererJob(SynchronizerJobPtr::create([this] { lookForDirtyTextures(); }, JobTypes::DirtyTextureGathering)) - , m_introspectShaderJob(SynchronizerPostFramePtr::create([this] { reloadDirtyShaders(); }, - [this] (Qt3DCore::QAspectManager *m) { sendShaderChangesToFrontend(m); }, - JobTypes::DirtyShaderGathering)) - , m_syncLoadingJobs(SynchronizerJobPtr::create([] {}, JobTypes::SyncLoadingJobs)) - , m_cacheRenderableEntitiesJob(SynchronizerJobPtr::create(SyncRenderableEntities(m_renderableEntityFilterJob, &m_cache), - JobTypes::EntityComponentTypeFiltering)) - , m_cacheComputableEntitiesJob(SynchronizerJobPtr::create(SyncComputableEntities(m_computableEntityFilterJob, &m_cache), - JobTypes::EntityComponentTypeFiltering)) - , m_cacheLightsJob(SynchronizerJobPtr::create(SyncLightsGatherer(m_lightGathererJob, &m_cache), - JobTypes::EntityComponentTypeFiltering)) + , m_bufferGathererJob(CreateSynchronizerJobPtr([this] { lookForDirtyBuffers(); }, JobTypes::DirtyBufferGathering)) + , m_vaoGathererJob(CreateSynchronizerJobPtr([this] { lookForAbandonedVaos(); }, JobTypes::DirtyVaoGathering)) + , m_textureGathererJob(CreateSynchronizerJobPtr([this] { lookForDirtyTextures(); }, JobTypes::DirtyTextureGathering)) + , m_introspectShaderJob(CreateSynchronizerPostFramePtr([this] { reloadDirtyShaders(); }, + [this] (Qt3DCore::QAspectManager *m) { sendShaderChangesToFrontend(m); }, + JobTypes::DirtyShaderGathering)) + , m_syncLoadingJobs(CreateSynchronizerJobPtr([] {}, JobTypes::SyncLoadingJobs)) + , m_cacheRenderableEntitiesJob(CreateSynchronizerJobPtr(SyncRenderableEntities(m_renderableEntityFilterJob, &m_cache), + JobTypes::EntityComponentTypeFiltering)) + , m_cacheComputableEntitiesJob(CreateSynchronizerJobPtr(SyncComputableEntities(m_computableEntityFilterJob, &m_cache), + JobTypes::EntityComponentTypeFiltering)) + , m_cacheLightsJob(CreateSynchronizerJobPtr(SyncLightsGatherer(m_lightGathererJob, &m_cache), + JobTypes::EntityComponentTypeFiltering)) , m_ownedContext(false) , m_offscreenHelper(nullptr) , m_glResourceManagers(nullptr) @@ -300,6 +300,7 @@ Renderer::Renderer(QRenderAspect::RenderType type) m_updateWorldBoundingVolumeJob->addDependency(m_calculateBoundingVolumeJob); m_expandBoundingVolumeJob->addDependency(m_updateWorldBoundingVolumeJob); m_updateShaderDataTransformJob->addDependency(m_worldTransformJob); + m_updateLevelOfDetailJob->addDependency(m_expandBoundingVolumeJob); m_pickBoundingVolumeJob->addDependency(m_expandBoundingVolumeJob); m_rayCastingJob->addDependency(m_expandBoundingVolumeJob); // m_calculateBoundingVolumeJob's dependency on m_updateTreeEnabledJob is set in renderBinJobs @@ -1178,6 +1179,10 @@ void Renderer::reloadDirtyShaders() HShader shaderHandle = m_nodesManager->shaderManager()->lookupHandle(renderPass->shaderProgram()); Shader *shader = m_nodesManager->shaderManager()->data(shaderHandle); + // Shader could be null if the pass doesn't reference one yet + if (!shader) + continue; + ShaderBuilder *shaderBuilder = nullptr; for (const HShaderBuilder &builderHandle : activeBuilders) { ShaderBuilder *builder = m_nodesManager->shaderBuilderManager()->data(builderHandle); @@ -1205,7 +1210,7 @@ void Renderer::reloadDirtyShaders() } } - if (shader != nullptr && shader->isDirty()) + if (shader->isDirty()) loadShader(shader, shaderHandle); } } @@ -1393,13 +1398,15 @@ void Renderer::updateGLResources() if (texture == nullptr) continue; - // Create or Update GLTexture (the GLTexture instance is created if required - // and all things that can take place without a GL context are done here) + // Create or Update GLTexture (the GLTexture instance is created + // (not the underlying GL instance) if required and all things that + // can take place without a GL context are done here) updateTexture(texture); } // We want to upload textures data at this point as the SubmissionThread and // AspectThread are locked ensuring no races between Texture/TextureImage and // GLTexture + QNodeIdVector updatedTexturesForFrame; if (m_submissionContext != nullptr) { GLTextureManager *glTextureManager = m_glResourceManagers->glTextureManager(); const QVector<HGLTexture> glTextureHandles = glTextureManager->activeHandles(); @@ -1421,16 +1428,28 @@ void Renderer::updateGLResources() updateInfo.handleType = QAbstractTexture::OpenGLTextureId; updateInfo.handle = info.texture ? QVariant(info.texture->textureId()) : QVariant(); m_updatedTextureProperties.push_back({updateInfo, referenceTextureIds}); + updatedTexturesForFrame += referenceTextureIds; } } } + // If the underlying GL Texture was for whatever reason recreated, we need to make sure + // that if it is used as a color attachment, we rebuild the FBO next time it is used + m_submissionContext->setUpdatedTexture(std::move(updatedTexturesForFrame)); + // Record ids of texture to cleanup while we are still blocking the aspect thread m_textureIdsToCleanup += m_nodesManager->textureManager()->takeTexturesIdsToCleanup(); } // Record list of buffer that might need uploading lookForDownloadableBuffers(); + + // Remove destroyed FBOs + { + const QNodeIdVector destroyedRenderTargetIds = m_nodesManager->renderTargetManager()->takeRenderTargetIdsToCleanup(); + for (const Qt3DCore::QNodeId &renderTargetId : destroyedRenderTargetIds) + m_submissionContext->releaseRenderTarget(renderTargetId); + } } // Render Thread @@ -1569,6 +1588,9 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren // If the RenderView has a RenderStateSet defined const RenderView *renderView = renderViews.at(i); + if (renderView->shouldSkipSubmission()) + continue; + // Check if using the same surface as the previous RenderView. // If not, we have to free up the context from the previous surface // and make the context current on the new surface diff --git a/src/plugins/renderers/opengl/renderer/renderer_p.h b/src/plugins/renderers/opengl/renderer/renderer_p.h index 5999aabe3..8ab56bd01 100644 --- a/src/plugins/renderers/opengl/renderer/renderer_p.h +++ b/src/plugins/renderers/opengl/renderer/renderer_p.h @@ -164,6 +164,8 @@ using RenderableEntityFilterPtr = FilterEntityByComponentJobPtr<Render::Geometry using SynchronizerJobPtr = GenericLambdaJobPtr<std::function<void()>>; using SynchronizerPostFramePtr = GenericLambdaJobAndPostFramePtr<std::function<void ()>, std::function<void (Qt3DCore::QAspectManager *)>>; +#define CreateSynchronizerPostFramePtr(lambda, postlambda, type) \ + SynchronizerPostFramePtr::create(lambda, postlambda, type, #type) namespace Debug { class ImGuiRenderer; @@ -326,6 +328,7 @@ public: RendererCache *cache() { return &m_cache; } void setScreen(QScreen *scr) override; QScreen *screen() const override; + NodeManagers *nodesManager() const { return m_nodesManager; } #ifdef QT3D_RENDER_UNIT_TESTS public: diff --git a/src/plugins/renderers/opengl/renderer/renderview.cpp b/src/plugins/renderers/opengl/renderer/renderview.cpp index f18f1a06a..47f4beffd 100644 --- a/src/plugins/renderers/opengl/renderer/renderview.cpp +++ b/src/plugins/renderers/opengl/renderer/renderview.cpp @@ -1213,6 +1213,29 @@ void RenderView::setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo) m_hasBlitFramebufferInfo = hasBlitFramebufferInfo; } +bool RenderView::shouldSkipSubmission() const +{ + if (!m_commands.empty()) + return false; + + if (m_hasBlitFramebufferInfo) + return false; + + if (m_isDownloadBuffersEnable) + return false; + + if (m_showDebugOverlay) + return false; + + if (!m_waitFences.empty() || !m_insertFenceIds.empty()) + return false; + + if (m_clearBuffer != QClearBuffers::None) + return false; + + return true; +} + BlitFramebufferInfo RenderView::blitFrameBufferInfo() const { return m_blitFrameBufferInfo; diff --git a/src/plugins/renderers/opengl/renderer/renderview_p.h b/src/plugins/renderers/opengl/renderer/renderview_p.h index 40e7a77fe..adab30f4e 100644 --- a/src/plugins/renderers/opengl/renderer/renderview_p.h +++ b/src/plugins/renderers/opengl/renderer/renderview_p.h @@ -289,6 +289,8 @@ public: bool hasBlitFramebufferInfo() const; void setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo); + bool shouldSkipSubmission() const; + private: void setShaderAndUniforms(RenderCommand *command, ParameterInfoList ¶meters, diff --git a/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp index 1e16209c6..a80fbe445 100644 --- a/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp +++ b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp @@ -463,8 +463,8 @@ RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int rende , m_renderViewJob(RenderViewInitializerJobPtr::create()) , m_filterEntityByLayerJob() , m_frustumCullingJob(new Render::FrustumCullingJob()) - , m_syncPreFrustumCullingJob(SynchronizerJobPtr::create(SyncPreFrustumCulling(m_renderViewJob, m_frustumCullingJob), JobTypes::SyncFrustumCulling)) - , m_setClearDrawBufferIndexJob(SynchronizerJobPtr::create(SetClearDrawBufferIndex(m_renderViewJob), JobTypes::ClearBufferDrawIndex)) + , m_syncPreFrustumCullingJob(CreateSynchronizerJobPtr(SyncPreFrustumCulling(m_renderViewJob, m_frustumCullingJob), JobTypes::SyncFrustumCulling)) + , m_setClearDrawBufferIndexJob(CreateSynchronizerJobPtr(SetClearDrawBufferIndex(m_renderViewJob), JobTypes::ClearBufferDrawIndex)) , m_syncFilterEntityByLayerJob() , m_filterProximityJob(Render::FilterProximityDistanceJobPtr::create()) { @@ -558,7 +558,7 @@ void RenderViewBuilder::prepareJobs() auto renderViewCommandBuilder = Render::OpenGL::RenderViewCommandBuilderJobPtr::create(); m_renderViewCommandBuilderJobs.push_back(renderViewCommandBuilder); } - m_syncRenderViewPreCommandBuildingJob = SynchronizerJobPtr::create(SyncPreCommandBuilding(m_renderViewJob, + m_syncRenderViewPreCommandBuildingJob = CreateSynchronizerJobPtr(SyncPreCommandBuilding(m_renderViewJob, m_renderViewCommandBuilderJobs, m_renderer, m_leafNode), @@ -593,7 +593,7 @@ void RenderViewBuilder::prepareJobs() materialGatherer->setHandles(materialHandles.mid(i * elementsPerJob, elementsPerJob)); m_materialGathererJobs.push_back(materialGatherer); } - m_syncMaterialGathererJob = SynchronizerJobPtr::create(SyncMaterialParameterGatherer(m_materialGathererJobs, + m_syncMaterialGathererJob = CreateSynchronizerJobPtr(SyncMaterialParameterGatherer(m_materialGathererJobs, m_renderer, m_leafNode), JobTypes::SyncMaterialGatherer); @@ -602,13 +602,13 @@ void RenderViewBuilder::prepareJobs() if (m_layerCacheNeedsToBeRebuilt) { m_filterEntityByLayerJob = Render::FilterLayerEntityJobPtr::create(); m_filterEntityByLayerJob->setManager(m_renderer->nodeManagers()); - m_syncFilterEntityByLayerJob = SynchronizerJobPtr::create(SyncFilterEntityByLayer(m_filterEntityByLayerJob, + m_syncFilterEntityByLayerJob = CreateSynchronizerJobPtr(SyncFilterEntityByLayer(m_filterEntityByLayerJob, m_renderer, m_leafNode), JobTypes::SyncFilterEntityByLayer); } - m_syncRenderViewPreCommandUpdateJob = SynchronizerJobPtr::create(SyncRenderViewPreCommandUpdate(m_renderViewJob, + m_syncRenderViewPreCommandUpdateJob = CreateSynchronizerJobPtr(SyncRenderViewPreCommandUpdate(m_renderViewJob, m_frustumCullingJob, m_filterProximityJob, m_materialGathererJobs, @@ -619,12 +619,12 @@ void RenderViewBuilder::prepareJobs() m_renderCommandCacheNeedsToBeRebuilt), JobTypes::SyncRenderViewPreCommandUpdate); - m_syncRenderViewPostCommandUpdateJob = SynchronizerJobPtr::create(SyncRenderViewPostCommandUpdate(m_renderViewJob, + m_syncRenderViewPostCommandUpdateJob = CreateSynchronizerJobPtr(SyncRenderViewPostCommandUpdate(m_renderViewJob, m_renderViewCommandUpdaterJobs, m_renderer), JobTypes::SyncRenderViewPostCommandUpdate); - m_syncRenderViewPostInitializationJob = SynchronizerJobPtr::create(SyncRenderViewPostInitialization(m_renderViewJob, + m_syncRenderViewPostInitializationJob = CreateSynchronizerJobPtr(SyncRenderViewPostInitialization(m_renderViewJob, m_frustumCullingJob, m_filterEntityByLayerJob, m_filterProximityJob, diff --git a/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h b/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h index 98202670e..54fc98352 100644 --- a/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h +++ b/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h @@ -75,6 +75,8 @@ namespace OpenGL { class Renderer; using SynchronizerJobPtr = GenericLambdaJobPtr<std::function<void()>>; +#define CreateSynchronizerJobPtr(lambda, type) \ + SynchronizerJobPtr::create(lambda, type, #type) class Q_AUTOTEST_EXPORT RenderViewBuilder { diff --git a/src/render/backend/attachmentpack.cpp b/src/render/backend/attachmentpack.cpp index a2ac8c30c..0cd5d8673 100644 --- a/src/render/backend/attachmentpack.cpp +++ b/src/render/backend/attachmentpack.cpp @@ -88,6 +88,32 @@ int AttachmentPack::getDrawBufferIndex(QRenderTargetOutput::AttachmentPoint atta return -1; } +bool operator ==(const Attachment &a, const Attachment &b) +{ + return (a.m_name == b.m_name && + a.m_mipLevel == b.m_mipLevel && + a.m_layer == b.m_layer && + a.m_textureUuid == b.m_textureUuid && + a.m_point == b.m_point && + a.m_face == b.m_face); +} + +bool operator !=(const Attachment &a, const Attachment &b) +{ + return !(a == b); +} + +bool operator ==(const AttachmentPack &packA, const AttachmentPack &packB) +{ + return (packA.attachments() == packB.attachments() && + packA.getGlDrawBuffers() == packB.getGlDrawBuffers()); +} + +bool operator !=(const AttachmentPack &packA, const AttachmentPack &packB) +{ + return !(packA == packB); +} + } // namespace Render } // namespace Qt3DRender diff --git a/src/render/backend/attachmentpack_p.h b/src/render/backend/attachmentpack_p.h index 32ea774b6..7f3733a65 100644 --- a/src/render/backend/attachmentpack_p.h +++ b/src/render/backend/attachmentpack_p.h @@ -100,6 +100,12 @@ private: QVector<int> m_drawBuffers; }; +Q_3DRENDERSHARED_PRIVATE_EXPORT bool operator ==(const Attachment &a, const Attachment &b); +Q_3DRENDERSHARED_PRIVATE_EXPORT bool operator !=(const Attachment &a, const Attachment &b); + +Q_3DRENDERSHARED_PRIVATE_EXPORT bool operator ==(const AttachmentPack &packA, const AttachmentPack &packB); +Q_3DRENDERSHARED_PRIVATE_EXPORT bool operator !=(const AttachmentPack &packA, const AttachmentPack &packB); + } // namespace Render } // namespace Qt3DRender diff --git a/src/render/backend/managers_p.h b/src/render/backend/managers_p.h index db12a8a7b..a3d42d24a 100644 --- a/src/render/backend/managers_p.h +++ b/src/render/backend/managers_p.h @@ -211,6 +211,21 @@ public: m_shaderIdsToCleanup.push_back(id); } + void removeShaderIdFromIdsToCleanup(Qt3DCore::QNodeId id) + { + m_shaderIdsToCleanup.removeAll(id); + } + + bool hasShaderIdToCleanup(Qt3DCore::QNodeId id) const + { + return m_shaderIdsToCleanup.contains(id); + } + + QVector<Qt3DCore::QNodeId> shaderIdsToCleanup() const + { + return m_shaderIdsToCleanup; + } + // Called by RenderThread in updateGLResources (locked) QVector<Qt3DCore::QNodeId> takeShaderIdsToCleanup() { @@ -285,6 +300,35 @@ class Q_3DRENDERSHARED_PRIVATE_EXPORT RenderTargetManager : public Qt3DCore::QRe { public: RenderTargetManager() {} + + // Called in AspectThread by RenderTarget node functor destroy + void addRenderTargetIdToCleanup(Qt3DCore::QNodeId id) + { + m_renderTargetIdsToCleanup.push_back(id); + } + + // Called in AspectThread by RenderTarget node functor create + void removeRenderTargetToCleanup(Qt3DCore::QNodeId id) + { + m_renderTargetIdsToCleanup.removeAll(id); + } + + // Called by RenderThread in updateGLResources (locked) + QVector<Qt3DCore::QNodeId> takeRenderTargetIdsToCleanup() + { + return std::move(m_renderTargetIdsToCleanup); + } + +#ifdef QT_BUILD_INTERNAL + // For unit testing purposes only + QVector<Qt3DCore::QNodeId> renderTargetIdsToCleanup() const + { + return m_renderTargetIdsToCleanup; + } +#endif + +private: + QVector<Qt3DCore::QNodeId> m_renderTargetIdsToCleanup; }; class Q_3DRENDERSHARED_PRIVATE_EXPORT RenderPassManager : public Qt3DCore::QResourceManager< diff --git a/src/render/backend/rendertarget.cpp b/src/render/backend/rendertarget.cpp index 088818c94..afe4bc23b 100644 --- a/src/render/backend/rendertarget.cpp +++ b/src/render/backend/rendertarget.cpp @@ -41,6 +41,7 @@ #include <Qt3DRender/qrendertarget.h> #include <Qt3DRender/private/qrendertarget_p.h> #include <Qt3DRender/qrendertargetoutput.h> +#include <Qt3DRender/private/managers_p.h> #include <QVariant> QT_BEGIN_NAMESPACE @@ -94,6 +95,33 @@ QVector<Qt3DCore::QNodeId> RenderTarget::renderOutputs() const return m_renderOutputs; } +RenderTargetFunctor::RenderTargetFunctor(AbstractRenderer *renderer, RenderTargetManager *manager) + : m_renderer(renderer) + , m_renderTargetManager(manager) +{ +} + +QBackendNode *RenderTargetFunctor::create(QNodeId id) const +{ + RenderTarget *backend = m_renderTargetManager->getOrCreateResource(id); + // Remove from the list of ids to destroy in case we were added to it + m_renderTargetManager->removeRenderTargetToCleanup(id); + backend->setRenderer(m_renderer); + return backend; +} + +QBackendNode *RenderTargetFunctor::get(QNodeId id) const +{ + return m_renderTargetManager->lookupResource(id); +} + +void RenderTargetFunctor::destroy(QNodeId id) const +{ + // We add ourselves to the dirty list to tell the renderer that this rendertarget has been destroyed + m_renderTargetManager->addRenderTargetIdToCleanup(id); + m_renderTargetManager->releaseResource(id); +} + } // namespace Render } // namespace Qt3DRender diff --git a/src/render/backend/rendertarget_p.h b/src/render/backend/rendertarget_p.h index eeaf94940..67465cc1e 100644 --- a/src/render/backend/rendertarget_p.h +++ b/src/render/backend/rendertarget_p.h @@ -82,6 +82,21 @@ private: QVector<Qt3DCore::QNodeId> m_renderOutputs; }; +class Q_AUTOTEST_EXPORT RenderTargetFunctor : public Qt3DCore::QBackendNodeMapper +{ +public: + explicit RenderTargetFunctor(AbstractRenderer *renderer, + RenderTargetManager *manager); + Qt3DCore::QBackendNode *create(Qt3DCore::QNodeId id) const final; + Qt3DCore::QBackendNode *get(Qt3DCore::QNodeId id) const final; + void destroy(Qt3DCore::QNodeId id) const final; + +private: + AbstractRenderer *m_renderer; + RenderTargetManager *m_renderTargetManager; +}; + + } // namespace Render } // namespace Qt3DRender diff --git a/src/render/framegraph/qframegraphnode.cpp b/src/render/framegraph/qframegraphnode.cpp index 797e24324..489fa03b4 100644 --- a/src/render/framegraph/qframegraphnode.cpp +++ b/src/render/framegraph/qframegraphnode.cpp @@ -41,7 +41,7 @@ #include "qframegraphnode_p.h" #include <Qt3DCore/QNode> - +#include <QVector> #include <QQueue> using namespace Qt3DCore; @@ -59,35 +59,87 @@ QString dumpNode(const Qt3DRender::QFrameGraphNode *n) { return res; } -QStringList dumpFG(const Qt3DRender::QFrameGraphNode *n, int level = 0) +QStringList dumpFG(const Qt3DCore::QNode *n, int level = 0) { QStringList reply; - QString res = dumpNode(n); - reply += res.rightJustified(res.length() + level * 2, ' '); + + const Qt3DRender::QFrameGraphNode *fgNode = qobject_cast<const Qt3DRender::QFrameGraphNode *>(n); + if (fgNode) { + QString res = dumpNode(fgNode); + reply += res.rightJustified(res.length() + level * 2, ' '); + } const auto children = n->childNodes(); + const int inc = fgNode ? 1 : 0; for (auto *child: children) { - auto *childFGNode = qobject_cast<Qt3DRender::QFrameGraphNode *>(child); + auto *childFGNode = qobject_cast<Qt3DCore::QNode *>(child); if (childFGNode != nullptr) - reply += dumpFG(childFGNode, level + 1); + reply += dumpFG(childFGNode, level + inc); } return reply; } -void dumpFGPaths(const Qt3DRender::QFrameGraphNode *n, QStringList &result, QStringList parents = {}) +struct HierarchyFGNode +{ + const Qt3DRender::QFrameGraphNode *root; + QVector<QSharedPointer<HierarchyFGNode>> children; +}; +using HierarchyFGNodePtr = QSharedPointer<HierarchyFGNode>; + +HierarchyFGNodePtr buildFGHierarchy(const Qt3DCore::QNode *n, HierarchyFGNodePtr lastFGParent = HierarchyFGNodePtr()) { - parents += dumpNode(n); + const Qt3DRender::QFrameGraphNode *fgNode = qobject_cast<const Qt3DRender::QFrameGraphNode *>(n); + + // Only happens for the root case + if (!lastFGParent) { + lastFGParent = HierarchyFGNodePtr::create(); + lastFGParent->root = fgNode; + } else { + if (fgNode != nullptr) { + HierarchyFGNodePtr hN = HierarchyFGNodePtr::create(); + hN->root = fgNode; + if (lastFGParent) + lastFGParent->children.push_back(hN); + lastFGParent = hN; + } + } const auto children = n->childNodes(); - if (children.length()) { - for (auto *child: children) { - auto *childFGNode = qobject_cast<Qt3DRender::QFrameGraphNode *>(child); - if (childFGNode != nullptr) - dumpFGPaths(childFGNode, result, parents); + for (auto *child: children) + buildFGHierarchy(child, lastFGParent); + + return lastFGParent; +} + +void findFGLeaves(const HierarchyFGNodePtr root, QVector<const Qt3DRender::QFrameGraphNode *> &fgLeaves) +{ + const auto children = root->children; + for (const auto &child : children) + findFGLeaves(child, fgLeaves); + + if (children.empty()) + fgLeaves.push_back(root->root); +} + +void dumpFGPaths(const Qt3DRender::QFrameGraphNode *n, QStringList &result) +{ + // Build FG node hierarchy + const HierarchyFGNodePtr rootHFg = buildFGHierarchy(n); + + // Gather FG leaves + QVector<const Qt3DRender::QFrameGraphNode *> fgLeaves; + findFGLeaves(rootHFg, fgLeaves); + + // Traverse back to root + for (const Qt3DRender::QFrameGraphNode *fgNode : fgLeaves) { + QStringList parents; + while (fgNode != nullptr) { + parents.prepend(dumpNode(fgNode)); + fgNode = fgNode->parentFrameGraphNode(); } - } else { - result << QLatin1String("[ ") + parents.join(QLatin1String(", ")) + QLatin1String(" ]"); + if (parents.size()) + result << QLatin1String("[ ") + parents.join(QLatin1String(", ")) + QLatin1String(" ]"); } } diff --git a/src/render/frontend/qrenderaspect.cpp b/src/render/frontend/qrenderaspect.cpp index fa6416199..e929f532f 100644 --- a/src/render/frontend/qrenderaspect.cpp +++ b/src/render/frontend/qrenderaspect.cpp @@ -283,7 +283,7 @@ void QRenderAspectPrivate::registerBackendTypes() q->registerBackendType<QLevelOfDetail>(QSharedPointer<Render::NodeFunctor<Render::LevelOfDetail, Render::LevelOfDetailManager> >::create(m_renderer)); q->registerBackendType<QLevelOfDetailSwitch>(QSharedPointer<Render::NodeFunctor<Render::LevelOfDetail, Render::LevelOfDetailManager> >::create(m_renderer)); q->registerBackendType<QSceneLoader>(QSharedPointer<Render::RenderSceneFunctor>::create(m_renderer, m_nodeManagers->sceneManager())); - q->registerBackendType<QRenderTarget>(QSharedPointer<Render::NodeFunctor<Render::RenderTarget, Render::RenderTargetManager> >::create(m_renderer)); + q->registerBackendType<QRenderTarget>(QSharedPointer<Render::RenderTargetFunctor>::create(m_renderer, m_nodeManagers->renderTargetManager())); q->registerBackendType<QRenderTargetOutput>(QSharedPointer<Render::NodeFunctor<Render::RenderTargetOutput, Render::AttachmentManager> >::create(m_renderer)); q->registerBackendType<QRenderSettings>(QSharedPointer<Render::RenderSettingsFunctor>::create(m_renderer)); q->registerBackendType<QRenderState>(QSharedPointer<Render::NodeFunctor<Render::RenderStateNode, Render::RenderStateManager> >::create(m_renderer)); diff --git a/src/render/jobs/genericlambdajob_p.h b/src/render/jobs/genericlambdajob_p.h index 8cf4276f6..994cd3a14 100644 --- a/src/render/jobs/genericlambdajob_p.h +++ b/src/render/jobs/genericlambdajob_p.h @@ -64,11 +64,11 @@ template<typename T> class GenericLambdaJob : public Qt3DCore::QAspectJob { public: - explicit GenericLambdaJob(T callable, JobTypes::JobType type = JobTypes::GenericLambda) + explicit GenericLambdaJob(T callable, JobTypes::JobType type = JobTypes::GenericLambda, const char *name = "GenericLambda") : Qt3DCore::QAspectJob() , m_callable(callable) { - SET_JOB_RUN_STAT_TYPE(this, type, 0) + SET_JOB_RUN_STAT_TYPE_AND_NAME(this, type, name, 0) } // QAspectJob interface @@ -107,11 +107,11 @@ template<typename T, typename U> class GenericLambdaJobAndPostFrame : public Qt3DCore::QAspectJob { public: - explicit GenericLambdaJobAndPostFrame(T runCallable, U postFrameCallable, JobTypes::JobType type = JobTypes::GenericLambda) + explicit GenericLambdaJobAndPostFrame(T runCallable, U postFrameCallable, JobTypes::JobType type = JobTypes::GenericLambda, const char *name = "GenericLambda") : Qt3DCore::QAspectJob(*new GenericLambdaJobAndPostFramePrivate<T, U>(postFrameCallable)) , m_runCallable(runCallable) { - SET_JOB_RUN_STAT_TYPE(this, type, 0) + SET_JOB_RUN_STAT_TYPE_AND_NAME(this, type, name, 0) } // QAspectJob interface diff --git a/src/render/jobs/pickboundingvolumejob.cpp b/src/render/jobs/pickboundingvolumejob.cpp index dce184f46..96610f9f7 100644 --- a/src/render/jobs/pickboundingvolumejob.cpp +++ b/src/render/jobs/pickboundingvolumejob.cpp @@ -71,9 +71,10 @@ namespace Render { class PickBoundingVolumeJobPrivate : public Qt3DCore::QAspectJobPrivate { public: - PickBoundingVolumeJobPrivate() = default; + PickBoundingVolumeJobPrivate(PickBoundingVolumeJob *q) : q_ptr(q) { } ~PickBoundingVolumeJobPrivate() override = default; + bool isRequired() override; void postFrame(Qt3DCore::QAspectManager *manager) override; enum CustomEventType { @@ -88,9 +89,17 @@ public: }; QVector<EventDetails> dispatches; + PickBoundingVolumeJob *q_ptr; + Q_DECLARE_PUBLIC(PickBoundingVolumeJob) }; +bool PickBoundingVolumeJobPrivate::isRequired() +{ + Q_Q(PickBoundingVolumeJob); + return !q->m_pendingMouseEvents.isEmpty() || q->m_pickersDirty || q->m_oneEnabledAtLeast; +} + void PickBoundingVolumeJobPrivate::postFrame(Qt3DCore::QAspectManager *manager) { using namespace Qt3DCore; @@ -188,7 +197,7 @@ void setEventButtonAndModifiers(const QMouseEvent &event, QPickEvent::Buttons &e } // anonymous PickBoundingVolumeJob::PickBoundingVolumeJob() - : AbstractPickingJob(*new PickBoundingVolumeJobPrivate) + : AbstractPickingJob(*new PickBoundingVolumeJobPrivate(this)) , m_pickersDirty(true) { SET_JOB_RUN_STAT_TYPE(this, JobTypes::PickBoundingVolume, 0) diff --git a/src/render/jobs/raycastingjob.cpp b/src/render/jobs/raycastingjob.cpp index 50dcaecde..df01213f0 100644 --- a/src/render/jobs/raycastingjob.cpp +++ b/src/render/jobs/raycastingjob.cpp @@ -86,15 +86,25 @@ public: class Qt3DRender::Render::RayCastingJobPrivate : public Qt3DCore::QAspectJobPrivate { public: - RayCastingJobPrivate() { } + RayCastingJobPrivate(RayCastingJob *q) : q_ptr(q) { } ~RayCastingJobPrivate() override { Q_ASSERT(dispatches.isEmpty()); } + bool isRequired() override; void postFrame(Qt3DCore::QAspectManager *manager) override; QVector<QPair<RayCaster *, QAbstractRayCaster::Hits>> dispatches; + + RayCastingJob *q_ptr; + Q_DECLARE_PUBLIC(RayCastingJob) }; +bool RayCastingJobPrivate::isRequired() +{ + Q_Q(RayCastingJob); + return q->m_castersDirty || q->m_oneEnabledAtLeast; +} + void RayCastingJobPrivate::postFrame(Qt3DCore::QAspectManager *manager) { for (auto res: qAsConst(dispatches)) { @@ -116,7 +126,7 @@ void RayCastingJobPrivate::postFrame(Qt3DCore::QAspectManager *manager) RayCastingJob::RayCastingJob() - : AbstractPickingJob(*new RayCastingJobPrivate()) + : AbstractPickingJob(*new RayCastingJobPrivate(this)) , m_castersDirty(true) { SET_JOB_RUN_STAT_TYPE(this, JobTypes::RayCasting, 0) diff --git a/src/render/jobs/updatelevelofdetailjob.cpp b/src/render/jobs/updatelevelofdetailjob.cpp index 0a28b7628..946d3bca8 100644 --- a/src/render/jobs/updatelevelofdetailjob.cpp +++ b/src/render/jobs/updatelevelofdetailjob.cpp @@ -210,15 +210,19 @@ namespace Render { class UpdateLevelOfDetailJobPrivate : public Qt3DCore::QAspectJobPrivate { public: - UpdateLevelOfDetailJobPrivate() { } + UpdateLevelOfDetailJobPrivate(UpdateLevelOfDetailJob *q) : q_ptr(q) { } + bool isRequired() override; void postFrame(Qt3DCore::QAspectManager *manager) override; QVector<QPair<Qt3DCore::QNodeId, int>> m_updatedIndices; + + UpdateLevelOfDetailJob *q_ptr; + Q_DECLARE_PUBLIC(UpdateLevelOfDetailJob) }; UpdateLevelOfDetailJob::UpdateLevelOfDetailJob() - : Qt3DCore::QAspectJob(*new UpdateLevelOfDetailJobPrivate) + : Qt3DCore::QAspectJob(*new UpdateLevelOfDetailJobPrivate(this)) , m_manager(nullptr) , m_frameGraphRoot(nullptr) , m_root(nullptr) @@ -262,6 +266,12 @@ void UpdateLevelOfDetailJob::run() d->m_updatedIndices = visitor.updatedIndices(); } +bool UpdateLevelOfDetailJobPrivate::isRequired() +{ + Q_Q(UpdateLevelOfDetailJob); + return q->m_manager->levelOfDetailManager()->count() > 0; +} + void UpdateLevelOfDetailJobPrivate::postFrame(Qt3DCore::QAspectManager *manager) { for (const auto &updatedNode: qAsConst(m_updatedIndices)) { diff --git a/src/render/materialsystem/shader.cpp b/src/render/materialsystem/shader.cpp index ff56a74e9..d0e2e9a76 100644 --- a/src/render/materialsystem/shader.cpp +++ b/src/render/materialsystem/shader.cpp @@ -189,15 +189,21 @@ ShaderFunctor::ShaderFunctor(AbstractRenderer *renderer, ShaderManager *manager) { } -QBackendNode *ShaderFunctor::create(const QNodeId id) const +QBackendNode *ShaderFunctor::create(QNodeId id) const { Shader *backend = m_shaderManager->getOrCreateResource(id); + // Remove from the list of ids to destroy in case we were added to it + m_shaderManager->removeShaderIdFromIdsToCleanup(id); backend->setRenderer(m_renderer); return backend; } QBackendNode *ShaderFunctor::get(QNodeId id) const { + // If we are marked for destruction, return nullptr so that + // if we were to be recreated, create would be called again + if (m_shaderManager->hasShaderIdToCleanup(id)) + return nullptr; return m_shaderManager->lookupResource(id); } diff --git a/src/render/materialsystem/shader_p.h b/src/render/materialsystem/shader_p.h index 8f78163be..31603bcf7 100644 --- a/src/render/materialsystem/shader_p.h +++ b/src/render/materialsystem/shader_p.h @@ -140,7 +140,7 @@ inline QDebug operator<<(QDebug dbg, const Shader &shader) } #endif -class ShaderFunctor : public Qt3DCore::QBackendNodeMapper +class Q_AUTOTEST_EXPORT ShaderFunctor : public Qt3DCore::QBackendNodeMapper { public: explicit ShaderFunctor(AbstractRenderer *renderer, diff --git a/src/render/texture/qtextureimagedata.h b/src/render/texture/qtextureimagedata.h index 5bc71480e..8daf1becb 100644 --- a/src/render/texture/qtextureimagedata.h +++ b/src/render/texture/qtextureimagedata.h @@ -40,10 +40,14 @@ #ifndef QT3DRENDER_TEXTUREIMAGEDATA_H #define QT3DRENDER_TEXTUREIMAGEDATA_H -#include <QOpenGLTexture> +#include <Qt3DRender/qt3drender_global.h> +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +# include <QOpenGLTexture> +#else +# include <QtGui/qopengltexture.h> +#endif #include <QtGui/QImage> #include <QtCore/QSharedPointer> -#include <Qt3DRender/qt3drender_global.h> QT_BEGIN_NAMESPACE diff --git a/tests/auto/animation/animationutils/tst_animationutils.cpp b/tests/auto/animation/animationutils/tst_animationutils.cpp index 0ee455daf..8c340f56e 100644 --- a/tests/auto/animation/animationutils/tst_animationutils.cpp +++ b/tests/auto/animation/animationutils/tst_animationutils.cpp @@ -323,9 +323,19 @@ private Q_SLOTS: qMetaTypeId<QVector<float>>(), 6, channelMapping->peerId() }; + ChannelNameAndType rgbColor = { QLatin1String("rgbColor"), + static_cast<int>(QVariant::Color), + 3, + channelMapping->peerId() }; + + ChannelNameAndType rgbaColor = { QLatin1String("rgbaColor"), + static_cast<int>(QVariant::Color), + 4, + channelMapping->peerId() }; + QVector<ChannelNameAndType> channelNamesAndTypes = { rotation, location, baseColor, metalness, roughness, - morphTargetWeightsList, morphTargetWeightsVec }; + morphTargetWeightsList, morphTargetWeightsVec, rgbColor, rgbaColor }; // And the matching indices ComponentIndices rotationIndices = { 0, 1, 2, 3 }; @@ -335,10 +345,12 @@ private Q_SLOTS: ComponentIndices roughnessIndices = { 11 }; ComponentIndices morphTargetListIndices = { 12, 13, 14, 15, 16 }; ComponentIndices morphTargetVecIndices = { 17, 18, 19, 20, 21, 22 }; + ComponentIndices rgbColorIndices = { 23, 24, 25 }; + ComponentIndices rgbaColorIndices = { 26, 27, 28, 29 }; QVector<ComponentIndices> channelComponentIndices = { rotationIndices, locationIndices, baseColorIndices, metalnessIndices, roughnessIndices, morphTargetListIndices, - morphTargetVecIndices }; + morphTargetVecIndices, rgbColorIndices, rgbaColorIndices }; QVector<QBitArray> sourceClipMask = { QBitArray(4, true), QBitArray(3, true), @@ -346,7 +358,10 @@ private Q_SLOTS: QBitArray(1, true), QBitArray(1, true), QBitArray(5, true), - QBitArray(6, true) }; + QBitArray(6, true), + QBitArray(3, true), + QBitArray(4, true), + }; MappingData expectedMapping; expectedMapping.targetId = channelMapping->targetId(); @@ -969,7 +984,29 @@ private Q_SLOTS: expectedChanges.normalizedTime = 1.1f; // Invalid expectedChanges.targetChanges.push_back({mapping.targetId, mapping.propertyName, QVariant::fromValue(QColor::fromRgbF(0.5f, 0.4f, 0.3f))}); - QTest::newRow("QColor color") + QTest::newRow("QColor rgb color") + << animatorId << mappingData << channelResults << expectedChanges; + + mappingData.clear(); + channelResults.clear(); + expectedChanges.targetChanges.clear(); + } + + // Single property, QColor + { + animatorId = Qt3DCore::QNodeId::createId(); + MappingData mapping; + mapping.targetId = Qt3DCore::QNodeId::createId(); + mapping.propertyName = "color"; + mapping.type = static_cast<int>(QVariant::Color); + mapping.channelIndices = QVector<int>() << 0 << 1 << 2 << 3; + mappingData.push_back(mapping); + channelResults = QVector<float>() << 0.5f << 0.4f << 0.3f << 0.2f; + expectedChanges.finalFrame = false; + expectedChanges.normalizedTime = 1.1f; // Invalid + expectedChanges.targetChanges.push_back({mapping.targetId, mapping.propertyName, QVariant::fromValue(QColor::fromRgbF(0.5f, 0.4f, 0.3f, 0.2f))}); + + QTest::newRow("QColor rgba color") << animatorId << mappingData << channelResults << expectedChanges; mappingData.clear(); @@ -1613,7 +1650,7 @@ private Q_SLOTS: expectedResults.clear(); } - // color with and without offset + // color with and without offset 3 components { channel = Channel(); channel.name = QLatin1String("Color"); @@ -1628,7 +1665,7 @@ private Q_SLOTS: suffixes = (QVector<char>() << 'R' << 'G' << 'B'); expectedResults = (QVector<int>() << 0 << 1 << 2); - QTest::newRow("QColor Color, offset = 0") + QTest::newRow("QColor RGB Color, offset = 0") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; @@ -1636,7 +1673,39 @@ private Q_SLOTS: offset = 10; expectedResults = (QVector<int>() << 10 << 11 << 12); - QTest::newRow("QColor Color, offset = 10") + QTest::newRow("QColor RGB Color, offset = 10") + << channel << dataType << expectedChannelComponentCount + << offset << suffixes << expectedResults; + + suffixes.clear(); + expectedResults.clear(); + } + + // color with and without offset 4 components + { + channel = Channel(); + channel.name = QLatin1String("Color"); + channel.channelComponents.resize(4); + channel.channelComponents[0].name = QLatin1String("Color R"); + channel.channelComponents[1].name = QLatin1String("Color G"); + channel.channelComponents[2].name = QLatin1String("Color B"); + channel.channelComponents[3].name = QLatin1String("Color A"); + + dataType = static_cast<int>(QVariant::Color); + expectedChannelComponentCount = 4; + offset = 0; + suffixes = (QVector<char>() << 'R' << 'G' << 'B' << 'A'); + expectedResults = (QVector<int>() << 0 << 1 << 2 << 3); + + QTest::newRow("QColor RGBA Color, offset = 0") + << channel << dataType << expectedChannelComponentCount + << offset << suffixes << expectedResults; + + expectedResults.clear(); + + offset = 10; + expectedResults = (QVector<int>() << 10 << 11 << 12 << 13); + QTest::newRow("QColor RGBA Color, offset = 10") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; @@ -1803,14 +1872,41 @@ private Q_SLOTS: offset = 0; expectedResults = (QVector<int>() << 0 << 1 << 2); - QTest::newRow("QColor Color, offset = 0") + QTest::newRow("QColor RGB Color, offset = 0") << channel << dataType << componentCount << offset << expectedResults; expectedResults.clear(); offset = 10; expectedResults = (QVector<int>() << 10 << 11 << 12); - QTest::newRow("QColor Color, offset = 10") + QTest::newRow("QColor RGB Color, offset = 10") + << channel << dataType << componentCount << offset << expectedResults; + + expectedResults.clear(); + } + + { + channel = Channel(); + channel.name = QLatin1String("Color"); + channel.channelComponents.resize(4); + channel.channelComponents[0].name = QLatin1String("Color R"); + channel.channelComponents[1].name = QLatin1String("Color G"); + channel.channelComponents[2].name = QLatin1String("Color B"); + channel.channelComponents[3].name = QLatin1String("Color A"); + + dataType = static_cast<int>(QVariant::Color); + componentCount = 4; + offset = 0; + expectedResults = (QVector<int>() << 0 << 1 << 2 << 3); + + QTest::newRow("QColor RGBA Color, offset = 0") + << channel << dataType << componentCount << offset << expectedResults; + + expectedResults.clear(); + + offset = 10; + expectedResults = (QVector<int>() << 10 << 11 << 12 << 13); + QTest::newRow("QColor RGBA Color, offset = 10") << channel << dataType << componentCount << offset << expectedResults; expectedResults.clear(); diff --git a/tests/auto/core/qscheduler/tst_qscheduler.cpp b/tests/auto/core/qscheduler/tst_qscheduler.cpp index d1afb5aac..5d858a77a 100644 --- a/tests/auto/core/qscheduler/tst_qscheduler.cpp +++ b/tests/auto/core/qscheduler/tst_qscheduler.cpp @@ -181,7 +181,7 @@ private Q_SLOTS: QVERIFY(!second->postFrameCalled()); // WHEN - const int count = scheduler.scheduleAndWaitForFrameAspectJobs(0); + const int count = scheduler.scheduleAndWaitForFrameAspectJobs(0, false); // THEN QCOMPARE(count, 2); diff --git a/tests/auto/render/rendertarget/tst_rendertarget.cpp b/tests/auto/render/rendertarget/tst_rendertarget.cpp index 31eee6ec5..8170c022a 100644 --- a/tests/auto/render/rendertarget/tst_rendertarget.cpp +++ b/tests/auto/render/rendertarget/tst_rendertarget.cpp @@ -32,6 +32,7 @@ #include <Qt3DRender/qrendertargetoutput.h> #include <Qt3DRender/private/qrendertarget_p.h> #include <Qt3DRender/private/rendertarget_p.h> +#include <Qt3DRender/private/managers_p.h> #include "qbackendnodetester.h" #include "testrenderer.h" @@ -205,6 +206,45 @@ private Q_SLOTS: } } + void checkRenderTargetManager() + { + // GIVEN + Qt3DRender::QRenderTarget renderTarget; + TestRenderer renderer; + Qt3DRender::Render::RenderTargetManager manager; + Qt3DRender::Render::RenderTargetFunctor creationFunctor(&renderer, &manager); + + // THEN + QVERIFY(manager.renderTargetIdsToCleanup().isEmpty()); + + // WHEN + auto backend = creationFunctor.create(renderTarget.id()); + + // THEN + QVERIFY(backend != nullptr); + QVERIFY(manager.renderTargetIdsToCleanup().isEmpty()); + + { + // WHEN + auto sameBackend = creationFunctor.get(renderTarget.id()); + // THEN + QCOMPARE(backend, sameBackend); + } + + // WHEN + creationFunctor.destroy(renderTarget.id()); + + // THEN -> Should be in list of ids to remove and return null on get + QVERIFY(manager.renderTargetIdsToCleanup().contains(renderTarget.id())); + QVERIFY(creationFunctor.get(renderTarget.id()) == nullptr); + + // WHEN -> Should be removed from list of ids to remove + creationFunctor.create(renderTarget.id()); + + // THEN + QVERIFY(manager.renderTargetIdsToCleanup().isEmpty()); + QVERIFY(creationFunctor.get(renderTarget.id()) != nullptr); + } }; QTEST_MAIN(tst_RenderTarget) diff --git a/tests/auto/render/shader/tst_shader.cpp b/tests/auto/render/shader/tst_shader.cpp index a1f837010..d9d0f58e9 100644 --- a/tests/auto/render/shader/tst_shader.cpp +++ b/tests/auto/render/shader/tst_shader.cpp @@ -29,6 +29,7 @@ #include <QtTest/QTest> #include <qbackendnodetester.h> #include <Qt3DRender/private/shader_p.h> +#include <Qt3DRender/private/managers_p.h> #include <Qt3DRender/qshaderprogram.h> #include "testrenderer.h" @@ -46,6 +47,7 @@ private slots: void checkSetRendererDirtyOnInitialization(); void allowToChangeShaderCode_data(); void allowToChangeShaderCode(); + void checkShaderManager(); }; @@ -278,6 +280,46 @@ void tst_RenderShader::allowToChangeShaderCode() renderer.resetDirty(); } +void tst_RenderShader::checkShaderManager() +{ + // GIVEN + Qt3DRender::QShaderProgram shader; + TestRenderer renderer; + Qt3DRender::Render::ShaderManager manager; + Qt3DRender::Render::ShaderFunctor creationFunctor(&renderer, &manager); + + // THEN + QVERIFY(manager.shaderIdsToCleanup().isEmpty()); + + // WHEN + auto backend = creationFunctor.create(shader.id()); + + // THEN + QVERIFY(backend != nullptr); + QVERIFY(manager.shaderIdsToCleanup().isEmpty()); + + { + // WHEN + auto sameBackend = creationFunctor.get(shader.id()); + // THEN + QCOMPARE(backend, sameBackend); + } + + // WHEN + creationFunctor.destroy(shader.id()); + + // THEN -> Should be in list of ids to remove and return null on get + QVERIFY(manager.hasShaderIdToCleanup(shader.id())); + QVERIFY(creationFunctor.get(shader.id()) == nullptr); + + // WHEN -> Should be removed from list of ids to remove + creationFunctor.create(shader.id()); + + // THEN + QVERIFY(manager.shaderIdsToCleanup().isEmpty()); + QCOMPARE(creationFunctor.get(shader.id()), backend); +} + QTEST_APPLESS_MAIN(tst_RenderShader) #include "tst_shader.moc" |