summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/animation/backend/animationutils.cpp16
-rw-r--r--src/core/aspects/qaspectengine.cpp4
-rw-r--r--src/core/aspects/qaspectmanager.cpp9
-rw-r--r--src/core/aspects/qaspectmanager_p.h2
-rw-r--r--src/core/jobs/qabstractaspectjobmanager_p.h2
-rw-r--r--src/core/jobs/qaspectjob.cpp6
-rw-r--r--src/core/jobs/qaspectjob_p.h15
-rw-r--r--src/core/jobs/qaspectjobmanager.cpp6
-rw-r--r--src/core/jobs/qaspectjobmanager_p.h2
-rw-r--r--src/core/jobs/qthreadpooler.cpp50
-rw-r--r--src/core/jobs/qthreadpooler_p.h5
-rw-r--r--src/core/jobs/task.cpp10
-rw-r--r--src/core/jobs/task_p.h3
-rw-r--r--src/core/qscheduler.cpp49
-rw-r--r--src/core/qscheduler_p.h2
-rw-r--r--src/plugins/renderers/opengl/debug/imguirenderer.cpp8
-rw-r--r--src/plugins/renderers/opengl/graphicshelpers/graphicscontext.cpp21
-rw-r--r--src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp67
-rw-r--r--src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h21
-rw-r--r--src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp29
-rw-r--r--src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h3
-rw-r--r--src/plugins/renderers/opengl/renderer/renderer.cpp54
-rw-r--r--src/plugins/renderers/opengl/renderer/renderer_p.h3
-rw-r--r--src/plugins/renderers/opengl/renderer/renderview.cpp23
-rw-r--r--src/plugins/renderers/opengl/renderer/renderview_p.h2
-rw-r--r--src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp16
-rw-r--r--src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h2
-rw-r--r--src/render/backend/attachmentpack.cpp26
-rw-r--r--src/render/backend/attachmentpack_p.h6
-rw-r--r--src/render/backend/managers_p.h44
-rw-r--r--src/render/backend/rendertarget.cpp28
-rw-r--r--src/render/backend/rendertarget_p.h15
-rw-r--r--src/render/framegraph/qframegraphnode.cpp82
-rw-r--r--src/render/frontend/qrenderaspect.cpp2
-rw-r--r--src/render/jobs/genericlambdajob_p.h8
-rw-r--r--src/render/jobs/pickboundingvolumejob.cpp13
-rw-r--r--src/render/jobs/raycastingjob.cpp14
-rw-r--r--src/render/jobs/updatelevelofdetailjob.cpp14
-rw-r--r--src/render/materialsystem/shader.cpp8
-rw-r--r--src/render/materialsystem/shader_p.h2
-rw-r--r--src/render/texture/qtextureimagedata.h8
-rw-r--r--tests/auto/animation/animationutils/tst_animationutils.cpp114
-rw-r--r--tests/auto/core/qscheduler/tst_qscheduler.cpp2
-rw-r--r--tests/auto/render/rendertarget/tst_rendertarget.cpp40
-rw-r--r--tests/auto/render/shader/tst_shader.cpp42
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 &parameterPack)
@@ -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 &parameters,
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"