summaryrefslogtreecommitdiffstats
path: root/customcontext
diff options
context:
space:
mode:
authorGunnar Sletta <gunnar.sletta@nokia.com>2012-08-10 13:53:40 +0200
committerGunnar Sletta <gunnar.sletta@nokia.com>2012-08-10 13:59:27 +0200
commit0e5a9dfd37cb4500196e50be87487eb85e348ef4 (patch)
tree4825395aa8ef93b2ea3801c26faa5aeef3b07e43 /customcontext
parentca0510a56828617041f2f41683018ae5d7920b22 (diff)
Animation system to run on the rendering thread
Initial code dump Change-Id: I628e9ea14d9984e1d82502f77444313357275d94 Reviewed-by: Gunnar Sletta <gunnar.sletta@nokia.com>
Diffstat (limited to 'customcontext')
-rw-r--r--customcontext/animationdriver.cpp4
-rw-r--r--customcontext/customcontext.pro1
-rw-r--r--customcontext/windowmanager.cpp235
-rw-r--r--customcontext/windowmanager.h5
4 files changed, 179 insertions, 66 deletions
diff --git a/customcontext/animationdriver.cpp b/customcontext/animationdriver.cpp
index f1abe9b..0b882db 100644
--- a/customcontext/animationdriver.cpp
+++ b/customcontext/animationdriver.cpp
@@ -88,7 +88,7 @@ void AnimationDriver::maybeUpdateDelta()
qint64 AnimationDriver::elapsed() const
{
- if (WindowManager::fakeRendering || !isRunning() || m_stable_vsync < -1)
+ if (!isRunning() || m_stable_vsync < -1)
return startTime() + m_timer.elapsed();
else
return startTime() + m_current_animation_time;
@@ -108,7 +108,7 @@ void AnimationDriver::advance()
{
maybeUpdateDelta();
- if (WindowManager::fakeRendering || m_stable_vsync < 0) {
+ if (m_stable_vsync < 0) {
m_current_animation_time = m_timer.elapsed();
} else {
diff --git a/customcontext/customcontext.pro b/customcontext/customcontext.pro
index 432f215..cf0c255 100644
--- a/customcontext/customcontext.pro
+++ b/customcontext/customcontext.pro
@@ -36,3 +36,4 @@ arm_build {
DEFINES += DESKTOP_BUILD
}
+verbose:DEFINES+=CUSTOMCONTEXT_DEBUG
diff --git a/customcontext/windowmanager.cpp b/customcontext/windowmanager.cpp
index 5f86b03..fbe97c5 100644
--- a/customcontext/windowmanager.cpp
+++ b/customcontext/windowmanager.cpp
@@ -71,6 +71,16 @@
# define WMDEBUG(x)
#endif
+
+int get_env_int(const char *name, int defaultValue)
+{
+ QByteArray content = qgetenv(name);
+
+ bool ok = false;
+ int value = content.toInt(&ok);
+ return ok ? value : defaultValue;
+}
+
#define QQUICK_WINDOW_TIMING
#ifdef QQUICK_WINDOW_TIMING
static bool qquick_window_timing = !qgetenv("QML_WINDOW_TIMING").isEmpty();
@@ -80,13 +90,11 @@ static int renderTime;
static int sinceLastTime;
#endif
-bool WindowManager::fakeRendering = false;
-
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
const QEvent::Type WM_Show = QEvent::Type(QEvent::User + 1);
const QEvent::Type WM_Hide = QEvent::Type(QEvent::User + 2);
-const QEvent::Type WM_SyncAndAdvance = QEvent::Type(QEvent::User + 3);
+const QEvent::Type WM_LockAndSync = QEvent::Type(QEvent::User + 3);
const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 4);
const QEvent::Type WM_NotifyDoneRender = QEvent::Type(QEvent::User + 5);
const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 6);
@@ -94,7 +102,7 @@ const QEvent::Type WM_ReleaseHandled = QEvent::Type(QEvent::User + 7);
const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 8);
const QEvent::Type WM_GrabResult = QEvent::Type(QEvent::User + 9);
const QEvent::Type WM_EnterWait = QEvent::Type(QEvent::User + 10);
-const QEvent::Type WM_Polish = QEvent::Type(QEvent::User + 11);
+const QEvent::Type WM_AdvanceAnimations = QEvent::Type(QEvent::User + 11);
template <typename T> T *windowFor(const QList<T> list, QQuickWindow *window)
{
@@ -142,10 +150,20 @@ class RenderThread : public QThread
{
Q_OBJECT
public:
+
+ enum SyncState {
+ SyncNotSpecified,
+ SyncPendingInGui,
+ SyncAcceptedInGui,
+ SyncAbortedByRender
+ };
+
+
RenderThread(WindowManager *w)
: wm(w)
, gl(0)
, sg(QSGContext::createDefaultContext())
+ , waitForGuiTime(0)
, pendingUpdate(false)
, sleeping(false)
, animationRunning(false)
@@ -153,10 +171,18 @@ public:
, shouldExit(false)
, allowMainThreadProcessing(true)
, inSync(true)
+ , syncState(SyncNotSpecified)
{
sg->moveToThread(this);
+
+ waitForGuiTime = get_env_int("QML_RENDERLOOP_WAIT_FOR_GUI_TIME", 5);
+
+#ifdef CUSTOMCONTEXT_DEBUG
+ qDebug("CustomContext: setting GUI wait to %d ms", waitForGuiTime);
+#endif
}
+
void invalidateOpenGL(QQuickWindow *window);
void initializeOpenGL();
@@ -164,6 +190,7 @@ public:
void run();
void syncAndRender();
+ void sync();
void lockGuiFromRender(QEvent::Type eventType)
{
@@ -197,6 +224,9 @@ public slots:
sceneChanged = true;
}
+
+
+
public:
WindowManager *wm;
QOpenGLContext *gl;
@@ -204,19 +234,29 @@ public:
QEventLoop eventLoop;
+ int waitForGuiTime;
+
uint pendingUpdate : 1;
uint sleeping : 1;
uint animationRunning : 1;
uint sceneChanged : 1;
+ // New canvas has been added, we must perform a full sync in order for
+ // the renderer to be created and the initial scene graph built.
+ uint canvasAdded : 1;
+
volatile bool shouldExit;
+
volatile bool allowMainThreadProcessing;
volatile bool inSync : 1;
+ volatile SyncState syncState;
QMutex mutex;
QWaitCondition waitCondition;
+ QElapsedTimer m_timer;
+
struct Window {
QQuickWindow *window;
QSize size;
@@ -241,8 +281,10 @@ bool RenderThread::event(QEvent *e)
Window window;
window.window = se->window;
window.size = se->size;
+ window.sceneChanged = true;
m_windows << window;
pendingUpdate = true;
+ canvasAdded = true;
if (sleeping)
exit();
return true; }
@@ -364,6 +406,57 @@ void RenderThread::initializeOpenGL()
sg->initialize(gl);
}
+/*!
+ * Makes an attemt to lock the GUI thread so we can perform the synchronizaiton
+ * of the QML scene's state into the scene graph.
+ *
+ * We return the number of views that did not trigger any changes.
+ */
+void RenderThread::sync()
+{
+ WMDEBUG(" Render: about to lock for sync");
+ mutex.lock();
+ allowMainThreadProcessing = false;
+ syncState = SyncPendingInGui;
+
+ WMDEBUG(" Render: posting WM_SyncAndAdvance to GUI");
+ QCoreApplication::postEvent(wm, new QEvent(WM_LockAndSync));
+ waitCondition.wait(&mutex, canvasAdded ? 99999 : waitForGuiTime);
+
+ // Gui thread did not process the event in time, so abort it...
+ if (syncState == SyncPendingInGui) {
+ WMDEBUG(" Render: aborting...");
+ syncState = SyncAbortedByRender;
+ }
+ pendingUpdate = false;
+
+ if (syncState == SyncAcceptedInGui) {
+ WMDEBUG(" Render: performing sync");
+ inSync = true;
+ for (int i=0; i<m_windows.size(); ++i) {
+ Window &w = const_cast<Window &>(m_windows.at(i));
+ gl->makeCurrent(w.window);
+ QQuickWindowPrivate *d = QQuickWindowPrivate::get(w.window);
+
+ bool hasRenderer = d->renderer != 0;
+ sceneChanged = !hasRenderer || wm->checkAndResetForceUpdate(w.window);
+ d->syncSceneGraph(); // May trigger sceneWasChanged...
+ w.sceneChanged |= sceneChanged;
+
+ // If there was no renderer before, this was the first render pass and
+ // we register for the sceneGraphChanged signal..
+ if (!hasRenderer)
+ connect(d->renderer, SIGNAL(sceneGraphChanged()), this, SLOT(sceneWasChanged()));
+
+ canvasAdded = false;
+ }
+ inSync = false;
+ }
+
+ WMDEBUG(" Render: unlocking after sync");
+ unlockGuiFromRender();
+}
+
void RenderThread::syncAndRender()
{
@@ -371,41 +464,22 @@ void RenderThread::syncAndRender()
if (qquick_window_timing)
sinceLastTime = threadTimer.restart();
#endif
+ WMDEBUG(" Render: Sync & Render")
- WMDEBUG(" Render: about to lock");
- lockGuiFromRender(WM_SyncAndAdvance);
- pendingUpdate = false;
- inSync = true;
- WMDEBUG(" Render: performing sync");
- int skippedRenders = 0;
- for (int i=0; i<m_windows.size(); ++i) {
- Window &w = const_cast<Window &>(m_windows.at(i));
- gl->makeCurrent(w.window);
- QQuickWindowPrivate *d = QQuickWindowPrivate::get(w.window);
-
- bool hasRenderer = d->renderer != 0;
- sceneChanged = !hasRenderer || wm->checkAndResetForceUpdate(w.window);
- d->syncSceneGraph(); // May trigger sceneWasChanged...
- w.sceneChanged = sceneChanged;
+ bool updateWasPending = pendingUpdate;
- if (!sceneChanged)
- ++skippedRenders;
+ if (pendingUpdate)
+ sync();
- // If there was no renderer before, this was the first render pass and
- // we register for the sceneGraphChanged signal..
- if (!hasRenderer)
- connect(d->renderer, SIGNAL(sceneGraphChanged()), this, SLOT(sceneWasChanged()));
+ // Posting animation request to GUI, but only if it is currently alive and
+ // kicking, otherwise we would pile up events and the animation system
+ // would spend a lot of time advancing in addition to the predictive times
+ // being way off.
+ if (animationRunning && syncState == SyncAcceptedInGui) {
+ WMDEBUG(" Render: posting animate to gui..");
+ QCoreApplication::postEvent(wm, new QEvent(WM_AdvanceAnimations));
}
- // When we are skipping renders, animations we are not tracking vsync, so the
- // increment in the animation driver will be wrong, causing a drift over time.
- // Make this knowledge public so the animation driver can pick it up.
- WindowManager::fakeRendering = skippedRenders == m_windows.size();
-
- inSync = false;
- unlockGuiFromRender();
- WMDEBUG(" Render: sync done, on to rendering..");
-
#ifdef QQUICK_WINDOW_TIMING
if (qquick_window_timing)
syncTime = threadTimer.elapsed();
@@ -414,30 +488,24 @@ void RenderThread::syncAndRender()
for (int i=0; i<m_windows.size(); ++i) {
Window &w = const_cast<Window &>(m_windows.at(i));
QQuickWindowPrivate *d = QQuickWindowPrivate::get(w.window);
- if (w.sceneChanged) {
- gl->makeCurrent(w.window);
- d->renderSceneGraph(w.size);
+ gl->makeCurrent(w.window);
+ d->renderSceneGraph(w.size);
#ifdef QQUICK_WINDOW_TIMING
- if (qquick_window_timing && i == 0)
- renderTime = threadTimer.elapsed();
+ if (qquick_window_timing && i == 0)
+ renderTime = threadTimer.elapsed();
#endif
- gl->swapBuffers(w.window);
- d->fireFrameSwapped();
- }
+ gl->swapBuffers(w.window);
+ d->fireFrameSwapped();
}
WMDEBUG(" Render: rendering done");
- if (WindowManager::fakeRendering)
- msleep(16);
-
#ifdef QQUICK_WINDOW_TIMING
if (qquick_window_timing)
- qDebug("window Time: sinceLast=%d, sync=%d, render=%d, swap=%d%s",
+ qDebug("window Time: sinceLast=%d, sync=%d, render=%d, swap=%d",
sinceLastTime,
syncTime,
renderTime - syncTime,
- threadTimer.elapsed() - renderTime,
- WindowManager::fakeRendering ? ", fake frame" : "");
+ threadTimer.elapsed() - renderTime);
#endif
}
@@ -691,12 +759,13 @@ void WindowManager::handleObscurity(QQuickWindow *window)
*/
void WindowManager::maybeUpdate(QQuickWindow *window)
{
- WMDEBUG("GUI: maybeUpdate called");
- if (m_thread->inSync) {
+ if (QThread::currentThread() == m_thread) {
+ WMDEBUG("GUI: maybeUpdate on render thread...");
m_thread->pendingUpdate = true;
return;
}
+ WMDEBUG("GUI: maybeUpdate...");
Window *w = windowFor(m_windows, window);
if (!w || w->pendingUpdate || !m_thread->isRunning())
return;
@@ -716,6 +785,16 @@ void WindowManager::maybeUpdate(QQuickWindow *window)
*/
void WindowManager::update(QQuickWindow *window)
{
+ if (QThread::currentThread() == m_thread) {
+ WMDEBUG("Gui: update called on render thread");
+ RenderThread::Window *w = windowFor(m_thread->m_windows, window);
+ if (w) {
+ w->sceneChanged = true;
+ m_thread->pendingUpdate = true;
+ }
+ return;
+ }
+
WMDEBUG("Gui: update called");
maybeUpdate(window);
Window *w = windowFor(m_windows, window);
@@ -821,7 +900,7 @@ bool WindowManager::event(QEvent *e)
wakeAndWait(m_thread);
break;
- case WM_SyncAndAdvance: {
+ case WM_LockAndSync: {
#ifdef QQUICK_WINDOW_TIMING
QElapsedTimer timer;
int polishTime;
@@ -839,24 +918,62 @@ bool WindowManager::event(QEvent *e)
if (qquick_window_timing)
polishTime = timer.elapsed();
#endif
- wakeAndWait(m_thread);
+
+ bool aborted = false;
+ m_thread->mutex.lock();
+
+ if (m_thread->syncState == RenderThread::SyncPendingInGui) {
+ m_thread->syncState = RenderThread::SyncAcceptedInGui;
+
+ // The lock down the GUI thread and wake render for doing the sync
+ m_thread->waitCondition.wakeOne();
+ WMDEBUG("GUI: woke up render, now waiting...");
+ m_thread->waitCondition.wait(&m_thread->mutex);
+
+ WMDEBUG("GUI: done with the waiting...");
+
+ } else {
+ WMDEBUG("GUI: sync aborted...")
+ aborted = true;
+ }
+ m_thread->mutex.unlock();
+
WMDEBUG("GUI: clearing update flags...");
- // The lock down the GUI thread and wake render for doing the sync
renderPassScheduled = false;
for (int i=0; i<m_windows.size(); ++i) {
m_windows[i].pendingUpdate = false;
}
+
+ if (aborted) {
+ // Force another sync-round since this one is aborted..
+ if (m_windows.size())
+ maybeUpdate(m_windows.at(0).window);
+ }
+
+#ifdef QQUICK_CANVAS_TIMING
+ if (qquick_canvas_timing)
+ qDebug(" - polish=%d aborted=%s", polishTime, aborted ? "yes" : "no");
+#endif
+ WMDEBUG("GUI: sync done...");
+ return true;
+ }
+
+ case WM_AdvanceAnimations:
+ WMDEBUG("GUI: got animate request..");
if (m_animation_driver->isRunning()) {
+#ifdef QQUICK_CANVAS_TIMING
+ QElapsedTimer timer;
+ timer.start();
+#endif
m_animation_driver->advance();
WMDEBUG("GUI: animations advanced..");
- }
-#ifdef QQUICK_WINDOW_TIMING
- if (qquick_window_timing)
- qDebug(" - polish=%d, animation=%d", polishTime, (int) (timer.elapsed() - polishTime));
+#ifdef QQUICK_CANVAS_TIMING
+ if (qquick_canvas_timing)
+ qDebug(" - animation: %d", (int) timer.elapsed());
#endif
- WMDEBUG("GUI: sync and animate done...");
+ }
return true;
- }
+
case WM_NotifyDoneRender:
WMDEBUG("GUI: render done notification...");
diff --git a/customcontext/windowmanager.h b/customcontext/windowmanager.h
index 9066a7d..29e0e1f 100644
--- a/customcontext/windowmanager.h
+++ b/customcontext/windowmanager.h
@@ -80,11 +80,6 @@ public:
void wakeup();
- // Set to true when we are spinning the scene graph render loop to
- // drive animations but not really rendering or swapping, only
- // sync'ing and animating.
- static bool fakeRendering;
-
public slots:
void animationStarted();
void animationStopped();