maybeUpdate() has an optimization to void locking the Gui thread
on every QQuickItem::update() call, which was faulty. When the
render thread was done rendering the flag should have been reset
which would have meant another locked sync between render and GUI.
Solve it slightly differently by posting an event to ourselves in GUI
and resetting the state once the event is processed. This batches all
update calls made until the GUI thread returns to the event loop, aka
all animation updates, all responds to one touch event, etc.
The isExternalUpdatePending variable, written from maybeUpdate, was
accumulated per canvas, but is used per render thread only, so this
change simplifies the variable to be per render thread only.
Task-number: QTBUG-23478
Change-Id: I067a9918383e3e05e2feebcc6dfa3163b032eb5b
Reviewed-by: Samuel Rødal <samuel.rodal@nokia.com>
+const QEvent::Type QEvent_Sync = QEvent::Type(QEvent::User);
+const QEvent::Type QEvent_DeferredUpdate = QEvent::Type(QEvent::User + 1);
+
#define QQUICK_CANVAS_TIMING
#ifdef QQUICK_CANVAS_TIMING
#define QQUICK_CANVAS_TIMING
#ifdef QQUICK_CANVAS_TIMING
, isPaintCompleted(false)
, isPostingSyncEvent(false)
, isRenderBlocked(false)
, isPaintCompleted(false)
, isPostingSyncEvent(false)
, isRenderBlocked(false)
+ , isExternalUpdatePending(false)
, syncAlreadyHappened(false)
, inSync(false)
, shouldExit(false)
, hasExited(false)
, syncAlreadyHappened(false)
, inSync(false)
, shouldExit(false)
, hasExited(false)
- , renderThreadAwakened(false)
+ , isDeferredUpdatePosted(false)
, canvasToGrab(0)
{
sg->moveToThread(this);
, canvasToGrab(0)
{
sg->moveToThread(this);
void paint(QQuickCanvas *canvas);
QImage grab(QQuickCanvas *canvas);
void resize(QQuickCanvas *canvas, const QSize &size);
void paint(QQuickCanvas *canvas);
QImage grab(QQuickCanvas *canvas);
void resize(QQuickCanvas *canvas, const QSize &size);
+ void handleDeferredUpdate();
void maybeUpdate(QQuickCanvas *canvas);
void startRendering();
void maybeUpdate(QQuickCanvas *canvas);
void startRendering();
uint inSync : 1;
uint shouldExit : 1;
uint hasExited : 1;
uint inSync : 1;
uint shouldExit : 1;
uint hasExited : 1;
- uint renderThreadAwakened : 1;
- uint isGuiAboutToBeBlockedForSync : 1;
+ uint isDeferredUpdatePosted : 1;
QQuickCanvas *canvasToGrab;
QImage grabContent;
QQuickCanvas *canvasToGrab;
QImage grabContent;
QSize viewportSize;
uint sizeWasChanged : 1;
QSize viewportSize;
uint sizeWasChanged : 1;
- uint isExternalUpdatePending : 1;
};
QHash<QQuickCanvas *, CanvasData *> m_rendered_windows;
};
QHash<QQuickCanvas *, CanvasData *> m_rendered_windows;
#endif
CanvasData *data = new CanvasData;
#endif
CanvasData *data = new CanvasData;
- data->isExternalUpdatePending = true;
data->sizeWasChanged = false;
data->windowSize = canvas->size();
m_rendered_windows[canvas] = data;
data->sizeWasChanged = false;
data->windowSize = canvas->size();
m_rendered_windows[canvas] = data;
+
+ isExternalUpdatePending = true;
printf(" RenderThread: *** NEW FRAME ***\n");
#endif
printf(" RenderThread: *** NEW FRAME ***\n");
#endif
+ isExternalUpdatePending = false;
handleAddedWindows();
if (!isGuiLocked) {
handleAddedWindows();
if (!isGuiLocked) {
printf(" RenderThread: aquired sync lock...\n");
#endif
allowMainThreadProcessingFlag = false;
printf(" RenderThread: aquired sync lock...\n");
#endif
allowMainThreadProcessingFlag = false;
- QCoreApplication::postEvent(this, new QEvent(QEvent::User));
+ QCoreApplication::postEvent(this, new QEvent(QEvent_Sync));
#ifdef THREAD_DEBUG
printf(" RenderThread: going to sleep...\n");
#ifdef THREAD_DEBUG
printf(" RenderThread: going to sleep...\n");
glViewport(0, 0, canvasData->viewportSize.width(), canvasData->viewportSize.height());
}
glViewport(0, 0, canvasData->viewportSize.width(), canvasData->viewportSize.height());
}
- canvasData->isExternalUpdatePending = false;
canvasPrivate->syncSceneGraph();
}
inSync = false;
canvasPrivate->syncSceneGraph();
}
inSync = false;
- bool isExternalUpdatePending = false;
-
// Update sizes...
for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
it != m_rendered_windows.constEnd(); ++it) {
// Update sizes...
for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
it != m_rendered_windows.constEnd(); ++it) {
canvasData->renderedSize = canvasData->viewportSize;
canvasData->sizeWasChanged = false;
}
canvasData->renderedSize = canvasData->viewportSize;
canvasData->sizeWasChanged = false;
}
- isExternalUpdatePending |= canvasData->isExternalUpdatePending;
{
Q_ASSERT(QThread::currentThread() == qApp->thread());
{
Q_ASSERT(QThread::currentThread() == qApp->thread());
- if (e->type() == QEvent::User) {
+ if (e->type() == QEvent_Sync) {
// If all canvases have been hidden, ignore the event
if (!isRunning())
// If all canvases have been hidden, ignore the event
if (!isRunning())
+ } else if (e->type() == QEvent_DeferredUpdate) {
+ handleDeferredUpdate();
} else if (e->type() == QEvent::Timer) {
#ifdef THREAD_DEBUG
} else if (e->type() == QEvent::Timer) {
#ifdef THREAD_DEBUG
if (!guiAlreadyLocked)
lockInGui();
if (!guiAlreadyLocked)
lockInGui();
- renderThreadAwakened = false;
-
for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
it != m_rendered_windows.constEnd(); ++it) {
QQuickCanvasPrivate::get(it.key())->polishItems();
for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
it != m_rendered_windows.constEnd(); ++it) {
QQuickCanvasPrivate::get(it.key())->polishItems();
isGuiLocked = 0;
isPostingSyncEvent = false;
syncAlreadyHappened = false;
isGuiLocked = 0;
isPostingSyncEvent = false;
syncAlreadyHappened = false;
- renderThreadAwakened = false;
inSync = false;
lockInGui();
inSync = false;
lockInGui();
+void QQuickRenderThreadSingleContextWindowManager::handleDeferredUpdate()
+{
+#ifdef THREAD_DEBUG
+ printf("GUI: handling update to ourselves...\n");
+#endif
+
+ isDeferredUpdatePosted = false;
-void QQuickRenderThreadSingleContextWindowManager::maybeUpdate(QQuickCanvas *canvas)
+ lockInGui();
+ isExternalUpdatePending = true;
+ if (isRenderBlocked)
+ wake();
+ unlockInGui();
+}
+
+void QQuickRenderThreadSingleContextWindowManager::maybeUpdate(QQuickCanvas *)
{
Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
"QQuickCanvas::update",
"Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
if (inSync) {
{
Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
"QQuickCanvas::update",
"Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
if (inSync) {
- CanvasData *canvasData = m_rendered_windows.value(canvas);
- if (canvasData)
- canvasData->isExternalUpdatePending = true;
+ isExternalUpdatePending = true;
- } else if (!renderThreadAwakened) {
+ } else if (!isDeferredUpdatePosted) {
- printf("GUI: doing update...\n");
+ printf("GUI: posting update to ourselves...\n");
- renderThreadAwakened = true;
-
- // If we are getting here from the renderer's sync event, the renderer is about
- // to go to sleep anyway.
- if (!isGuiAboutToBeBlockedForSync)
- lockInGui();
- CanvasData *canvasData = m_rendered_windows.value(canvas);
- if (canvasData)
- canvasData->isExternalUpdatePending = true;
- if (isRenderBlocked)
- wake();
- if (!isGuiAboutToBeBlockedForSync)
- unlockInGui();
+ isDeferredUpdatePosted = true;
+ QCoreApplication::postEvent(this, new QEvent(QEvent_DeferredUpdate));
}
QQuickTrivialWindowManager::QQuickTrivialWindowManager()
}
QQuickTrivialWindowManager::QQuickTrivialWindowManager()
// and column's implicit resizing should still work
QVERIFY(!QQuickItemPrivate::get(column)->heightValid);
QVERIFY(!QQuickItemPrivate::get(column)->widthValid);
// and column's implicit resizing should still work
QVERIFY(!QQuickItemPrivate::get(column)->heightValid);
QVERIFY(!QQuickItemPrivate::get(column)->widthValid);
-#ifdef Q_OS_MAC
- QEXPECT_FAIL("", "QTBUG-23478", Abort);
-#endif
QTRY_COMPARE(column->height(), 200.0);
delete view;
QTRY_COMPARE(column->height(), 200.0);
delete view;
QTRY_VERIFY(rect1->property("index").toInt() == 1);
QTRY_VERIFY(rect1->property("firstItem").toBool() == false);
QTRY_VERIFY(rect1->property("index").toInt() == 1);
QTRY_VERIFY(rect1->property("firstItem").toBool() == false);
-#ifdef Q_OS_MAC
- QEXPECT_FAIL("", "QTBUG-23483", Abort);
-#endif
QTRY_VERIFY(rect1->property("lastItem").toBool() == true);
delete canvas;
QTRY_VERIFY(rect1->property("lastItem").toBool() == true);
delete canvas;