aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@theqtcompany.com>2015-03-18 14:13:10 +0200
committerSimon Hausmann <simon.hausmann@theqtcompany.com>2015-04-15 17:53:01 +0000
commite036a0d80cd78474a6bfe4a78c9ee108c1552f80 (patch)
tree156773f30f1e5406a8a54847ce1f1ce80a85f05f
parent327ddb7488805241fb48c20ec8628c630224f4ce (diff)
Added a new render job stage: NoStage
NoStage allows scheduling jobs for immediate execution on the renderer thread. [ChangeLog][QtQuick][QQuickWindow] Added a render job stage: NoStage Task-number: QTBUG-44953 Change-Id: I918c79f1d095bc27d911a88d81376d146a04313c Reviewed-by: Gunnar Sletta <gunnar@sletta.org> Reviewed-by: Venugopal Shivashankar <venugopal.shivashankar@digia.com> Reviewed-by: Pasi Keränen <pasi.keranen@digia.com>
-rw-r--r--src/quick/items/qquickwindow.cpp30
-rw-r--r--src/quick/items/qquickwindow.h3
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp16
-rw-r--r--src/quick/scenegraph/qsgrenderloop_p.h2
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp37
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop_p.h1
-rw-r--r--tests/auto/quick/qquickwindow/tst_qquickwindow.cpp90
7 files changed, 158 insertions, 21 deletions
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index bccebb1a14..664e0f791c 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -4111,6 +4111,7 @@ void QQuickWindow::resetOpenGLState()
\value BeforeRenderingStage Before rendering.
\value AfterRenderingStage After rendering.
\value AfterSwapStage After the frame is swapped.
+ \value NoStage As soon as possible. This value was added in Qt 5.6.
\sa {Scene Graph and Rendering}
*/
@@ -4136,8 +4137,17 @@ void QQuickWindow::resetOpenGLState()
If the rendering is happening on a different thread, then the job
will happen on the rendering thread.
- \note This function does not trigger rendering; the job
- will be stored run until rendering is triggered elsewhere.
+ If \a stage is \l NoStage, \a job will be run at the earliest opportunity
+ whenever the render thread is not busy rendering a frame. If there is no
+ OpenGL context available or the window is not exposed at the time the job is
+ either posted or handled, it is deleted without executing the run() method.
+ If a non-threaded renderer is in use, the run() method of the job is executed
+ synchronously.
+ The OpenGL context is changed to the renderer context before executing a
+ \l NoStage job.
+
+ \note This function does not trigger rendering; the jobs targeting any other
+ stage than NoStage will be stored run until rendering is triggered elsewhere.
To force the job to run earlier, call QQuickWindow::update();
\sa beforeRendering(), afterRendering(), beforeSynchronizing(),
@@ -4149,16 +4159,22 @@ void QQuickWindow::scheduleRenderJob(QRunnable *job, RenderStage stage)
Q_D(QQuickWindow);
d->renderJobMutex.lock();
- if (stage == BeforeSynchronizingStage)
+ if (stage == BeforeSynchronizingStage) {
d->beforeSynchronizingJobs << job;
- else if (stage == AfterSynchronizingStage)
+ } else if (stage == AfterSynchronizingStage) {
d->afterSynchronizingJobs << job;
- else if (stage == BeforeRenderingStage)
+ } else if (stage == BeforeRenderingStage) {
d->beforeRenderingJobs << job;
- else if (stage == AfterRenderingStage)
+ } else if (stage == AfterRenderingStage) {
d->afterRenderingJobs << job;
- else if (stage == AfterSwapStage)
+ } else if (stage == AfterSwapStage) {
d->afterSwapJobs << job;
+ } else if (stage == NoStage) {
+ if (isExposed())
+ d->windowManager->postJob(this, job);
+ else
+ delete job;
+ }
d->renderJobMutex.unlock();
}
diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h
index f7a1956120..1a1d44c3b4 100644
--- a/src/quick/items/qquickwindow.h
+++ b/src/quick/items/qquickwindow.h
@@ -77,7 +77,8 @@ public:
AfterSynchronizingStage,
BeforeRenderingStage,
AfterRenderingStage,
- AfterSwapStage
+ AfterSwapStage,
+ NoStage
};
Q_DECLARE_FLAGS(CreateTextureOptions, CreateTextureOption)
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index ce3bf7d61d..fd6dc76dc3 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -99,6 +99,22 @@ void QSGRenderLoop::cleanup()
s_instance = 0;
}
+/*!
+ * Non-threaded render loops immediately run the job if there is a context.
+ */
+void QSGRenderLoop::postJob(QQuickWindow *window, QRunnable *job)
+{
+ Q_ASSERT(window);
+ Q_ASSERT(job);
+
+ if (window->openglContext()) {
+ window->openglContext()->makeCurrent(window);
+ job->run();
+ }
+
+ delete job;
+}
+
class QSGGuiThreadRenderLoop : public QSGRenderLoop
{
Q_OBJECT
diff --git a/src/quick/scenegraph/qsgrenderloop_p.h b/src/quick/scenegraph/qsgrenderloop_p.h
index 4293015b96..3336731fda 100644
--- a/src/quick/scenegraph/qsgrenderloop_p.h
+++ b/src/quick/scenegraph/qsgrenderloop_p.h
@@ -45,6 +45,7 @@ class QQuickWindow;
class QSGContext;
class QSGRenderContext;
class QAnimationDriver;
+class QRunnable;
class Q_QUICK_PRIVATE_EXPORT QSGRenderLoop : public QObject
{
@@ -72,6 +73,7 @@ public:
virtual QSGRenderContext *createRenderContext(QSGContext *) const = 0;
virtual void releaseResources(QQuickWindow *window) = 0;
+ virtual void postJob(QQuickWindow *window, QRunnable *job);
void addWindow(QQuickWindow *win) { m_windows.insert(win); }
void removeWindow(QQuickWindow *win) { m_windows.remove(win); }
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index 7b1e24246b..682599c76e 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -145,6 +145,9 @@ const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4);
// called.
const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5);
+// Passed by the window when there is a render job to run
+const QEvent::Type WM_PostJob = QEvent::Type(QEvent::User + 6);
+
template <typename T> T *windowFor(const QList<T> &list, QQuickWindow *window)
{
for (int i=0; i<list.size(); ++i) {
@@ -198,6 +201,14 @@ public:
QImage *image;
};
+class WMJobEvent : public WMWindowEvent
+{
+public:
+ WMJobEvent(QQuickWindow *c, QRunnable *postedJob)
+ : WMWindowEvent(c, WM_PostJob), job(postedJob) {}
+ ~WMJobEvent() { delete job; }
+ QRunnable *job;
+};
class QSGRenderThreadEventQueue : public QQueue<QEvent *>
{
@@ -415,6 +426,20 @@ bool QSGRenderThread::event(QEvent *e)
return true;
}
+ case WM_PostJob: {
+ qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_PostJob";
+ WMJobEvent *ce = static_cast<WMJobEvent *>(e);
+ Q_ASSERT(ce->window == window);
+ if (window) {
+ gl->makeCurrent(window);
+ ce->job->run();
+ delete ce->job;
+ ce->job = 0;
+ qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- job done";
+ }
+ return true;
+ }
+
case WM_RequestRepaint:
qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_RequestPaint";
// When GUI posts this event, it is followed by a polishAndSync, so we mustn't
@@ -1232,6 +1257,18 @@ QImage QSGThreadedRenderLoop::grab(QQuickWindow *window)
return result;
}
+/*!
+ * Posts a new job event to the render thread.
+ * Returns true if posting succeeded.
+ */
+void QSGThreadedRenderLoop::postJob(QQuickWindow *window, QRunnable *job)
+{
+ Window *w = windowFor(m_windows, window);
+ if (w && w->thread && w->thread->window)
+ w->thread->postEvent(new WMJobEvent(window, job));
+ else
+ delete job;
+}
#include "qsgthreadedrenderloop.moc"
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop_p.h b/src/quick/scenegraph/qsgthreadedrenderloop_p.h
index d5ffbf10a3..67df9dcd31 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop_p.h
+++ b/src/quick/scenegraph/qsgthreadedrenderloop_p.h
@@ -71,6 +71,7 @@ public:
void releaseResources(QQuickWindow *window);
bool event(QEvent *);
+ void postJob(QQuickWindow *window, QRunnable *job);
bool interleaveIncubation() const;
diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
index 070d6636b0..09ee431422 100644
--- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
+++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
@@ -2008,43 +2008,107 @@ public:
static int deleted;
};
+class GlRenderJob : public QRunnable
+{
+public:
+ GlRenderJob(GLubyte *buf) : readPixel(buf), mutex(0), condition(0) {}
+ ~GlRenderJob() {}
+ void run() {
+ QOpenGLContext::currentContext()->functions()->glClearColor(1.0f, 0, 0, 1.0f);
+ QOpenGLContext::currentContext()->functions()->glClear(GL_COLOR_BUFFER_BIT);
+ QOpenGLContext::currentContext()->functions()->glReadPixels(0, 0, 1, 1, GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ (void *)readPixel);
+ if (mutex) {
+ mutex->lock();
+ condition->wakeOne();
+ mutex->unlock();
+ }
+ }
+ GLubyte *readPixel;
+ QMutex *mutex;
+ QWaitCondition *condition;
+};
+
int RenderJob::deleted = 0;
void tst_qquickwindow::testRenderJob()
{
QList<QQuickWindow::RenderStage> completedJobs;
- QQuickWindow window;
-
QQuickWindow::RenderStage stages[] = {
QQuickWindow::BeforeSynchronizingStage,
QQuickWindow::AfterSynchronizingStage,
QQuickWindow::BeforeRenderingStage,
QQuickWindow::AfterRenderingStage,
- QQuickWindow::AfterSwapStage
+ QQuickWindow::AfterSwapStage,
+ QQuickWindow::NoStage
};
- // Schedule the jobs
- for (int i=0; i<5; ++i)
- window.scheduleRenderJob(new RenderJob(stages[i], &completedJobs), stages[i]);
- window.show();
- QTRY_COMPARE(completedJobs.size(), 5);
+ const int numJobs = 6;
- for (int i=0; i<5; ++i) {
- QCOMPARE(completedJobs.at(i), stages[i]);
+ {
+ QQuickWindow window;
+ RenderJob::deleted = 0;
+
+ // Schedule the jobs
+ for (int i = 0; i < numJobs; ++i)
+ window.scheduleRenderJob(new RenderJob(stages[i], &completedJobs), stages[i]);
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ // All jobs should be deleted
+ QTRY_COMPARE(RenderJob::deleted, numJobs);
+
+ // The NoStage job is not completed, if it is issued when there is no context,
+ // but the rest will be queued and completed once relevant render stage is hit.
+ QCOMPARE(completedJobs.size(), numJobs - 1);
+
+ // Verify jobs were completed in correct order
+ for (int i = 0; i < numJobs - 1; ++i)
+ QCOMPARE(completedJobs.at(i), stages[i]);
+
+
+ // Check that NoStage job gets executed if it is scheduled when window is exposed
+ completedJobs.clear();
+ RenderJob::deleted = 0;
+ window.scheduleRenderJob(new RenderJob(QQuickWindow::NoStage, &completedJobs),
+ QQuickWindow::NoStage);
+ QTRY_COMPARE(RenderJob::deleted, 1);
+ QCOMPARE(completedJobs.size(), 1);
+
+ // Do a synchronized GL job.
+ GLubyte readPixel[4] = {0, 0, 0, 0};
+ GlRenderJob *glJob = new GlRenderJob(readPixel);
+ if (window.openglContext()->thread() != QThread::currentThread()) {
+ QMutex mutex;
+ QWaitCondition condition;
+ glJob->mutex = &mutex;
+ glJob->condition = &condition;
+ mutex.lock();
+ window.scheduleRenderJob(glJob, QQuickWindow::NoStage);
+ condition.wait(&mutex);
+ mutex.unlock();
+ } else {
+ window.scheduleRenderJob(glJob, QQuickWindow::NoStage);
+ }
+ QCOMPARE(int(readPixel[0]), 255);
+ QCOMPARE(int(readPixel[1]), 0);
+ QCOMPARE(int(readPixel[2]), 0);
+ QCOMPARE(int(readPixel[3]), 255);
}
- // Verify that jobs are deleted when window has not been rendered at all...
+ // Verify that jobs are deleted when window is not rendered at all
completedJobs.clear();
RenderJob::deleted = 0;
{
QQuickWindow window2;
- for (int i=0; i<5; ++i) {
+ for (int i = 0; i < numJobs; ++i) {
window2.scheduleRenderJob(new RenderJob(stages[i], &completedJobs), stages[i]);
}
}
+ QTRY_COMPARE(RenderJob::deleted, numJobs);
QCOMPARE(completedJobs.size(), 0);
- QCOMPARE(RenderJob::deleted, 5);
}
class EventCounter : public QQuickRectangle