aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGunnar Sletta <gunnar.sletta@jollamobile.com>2014-04-03 11:16:42 +0000
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-05-05 16:02:45 +0200
commit6dc8f47bb05a8acb3cbcc697e0dc05356a01d4cf (patch)
tree81c7ebe9b3a55f2c8c4cdbc2fe6115024d145435 /src
parentc87ae78dd78f4a8cee040b5d27e7a6f2425f3aaf (diff)
Compress touch events in QQuickWindow.
Instead of sending multiple touch updates per frame, we store the last one and flush the pending events just before we enter into the scene graph sync phase. [ChangeLog][QtQuick] QQuickWindow will compresses touch events and delivers at most one touch event per frame. Done-with: Robin Burchell <robin.burchell@jollamobile.com> Change-Id: Ia0169bc4a3f0da67709b91ca65c326934b55d372 Reviewed-by: Laszlo Agocs <laszlo.agocs@digia.com> Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/quick/items/qquickrendercontrol.cpp3
-rw-r--r--src/quick/items/qquickwindow.cpp100
-rw-r--r--src/quick/items/qquickwindow_p.h6
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp6
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp61
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop_p.h1
-rw-r--r--src/quick/scenegraph/qsgwindowsrenderloop.cpp5
7 files changed, 162 insertions, 20 deletions
diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp
index 10a9691b65..4a199d9352 100644
--- a/src/quick/items/qquickrendercontrol.cpp
+++ b/src/quick/items/qquickrendercontrol.cpp
@@ -140,6 +140,9 @@ void QQuickRenderControl::polishItems()
return;
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window);
+ cd->flushDelayedTouchEvent();
+ if (!d->window)
+ return;
cd->polishItems();
}
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 49e8f3e373..d6cbae7d2a 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -400,6 +400,7 @@ QQuickWindowPrivate::QQuickWindowPrivate()
, renderer(0)
, windowManager(0)
, renderControl(0)
+ , touchRecursionGuard(0)
, clearColor(Qt::white)
, clearBeforeRendering(true)
, persistentGLContext(true)
@@ -458,6 +459,8 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
animationController = new QQuickAnimatorController();
animationController->m_window = q;
+ delayedTouch = 0;
+
QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection);
QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection);
QObject::connect(context, SIGNAL(invalidated()), q, SLOT(cleanupSceneGraph()), Qt::DirectConnection);
@@ -1712,11 +1715,104 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
return true;
}
+static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet("QML_NO_TOUCH_COMPRESSION");
+
// check what kind of touch we have (begin/update) and
// call deliverTouchPoints to actually dispatch the points
-bool QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event)
+void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event)
{
qCDebug(DBG_TOUCH) << event;
+ Q_Q(QQuickWindow);
+
+ if (qquickwindow_no_touch_compression || touchRecursionGuard) {
+ reallyDeliverTouchEvent(event);
+ return;
+ }
+
+ Qt::TouchPointStates states = event->touchPointStates();
+ if (((states & (Qt::TouchPointMoved | Qt::TouchPointStationary)) != 0)
+ && ((states & (Qt::TouchPointPressed | Qt::TouchPointReleased)) == 0)) {
+ // we can only compress something that isn't a press or release
+ if (!delayedTouch) {
+ delayedTouch = new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints());
+ delayedTouch->setTimestamp(event->timestamp());
+ if (windowManager)
+ windowManager->maybeUpdate(q);
+ return;
+ } else {
+ // check if this looks like the last touch event
+ if (delayedTouch->type() == event->type() &&
+ delayedTouch->device() == event->device() &&
+ delayedTouch->modifiers() == event->modifiers() &&
+ delayedTouch->touchPoints().count() == event->touchPoints().count())
+ {
+ // possible match.. is it really the same?
+ bool mismatch = false;
+
+ QList<QTouchEvent::TouchPoint> tpts = event->touchPoints();
+ Qt::TouchPointStates states;
+ for (int i = 0; i < event->touchPoints().count(); ++i) {
+ const QTouchEvent::TouchPoint &tp = tpts.at(i);
+ const QTouchEvent::TouchPoint &tp2 = delayedTouch->touchPoints().at(i);
+ if (tp.id() != tp2.id()) {
+ mismatch = true;
+ break;
+ }
+
+ if (tp2.state() == Qt::TouchPointMoved && tp.state() == Qt::TouchPointStationary)
+ tpts[i].setState(Qt::TouchPointMoved);
+
+ states |= tpts.at(i).state();
+ }
+
+ // same touch event? then merge if so
+ if (!mismatch) {
+ delayedTouch->setTouchPoints(tpts);
+ delayedTouch->setTimestamp(event->timestamp());
+ return;
+ }
+ }
+
+ // otherwise; we need to deliver the delayed event first, and
+ // then delay this one..
+ reallyDeliverTouchEvent(delayedTouch);
+ delete delayedTouch;
+ delayedTouch = new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints());
+ delayedTouch->setTimestamp(event->timestamp());
+ return;
+ }
+ } else {
+ if (delayedTouch) {
+ // deliver the delayed touch first
+ reallyDeliverTouchEvent(delayedTouch);
+ delete delayedTouch;
+ delayedTouch = 0;
+ }
+ reallyDeliverTouchEvent(event);
+ }
+}
+
+void QQuickWindowPrivate::flushDelayedTouchEvent()
+{
+ if (delayedTouch) {
+ reallyDeliverTouchEvent(delayedTouch);
+ delete delayedTouch;
+ delayedTouch = 0;
+
+ // To flush pending meta-calls triggered from the recently flushed touch events.
+ // This is safe because flushDelayedEvent is only called from eventhandlers.
+ QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+ }
+}
+
+void QQuickWindowPrivate::reallyDeliverTouchEvent(QTouchEvent *event)
+{
+ qCDebug(DBG_TOUCH) << " - delivering" << event;
+
+ // If users spin the eventloop as a result of touch delivery, we disable
+ // touch compression and send events directly. This is because we consider
+ // the usecase a bit evil, but we at least don't want to lose events.
+ ++touchRecursionGuard;
// List of all items that received an event before
// When we have TouchBegin this is and will stay empty
@@ -1766,7 +1862,7 @@ bool QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event)
Q_ASSERT(itemForTouchPointId.isEmpty());
}
- return event->isAccepted();
+ --touchRecursionGuard;
}
// This function recurses and sends the events to the individual items
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index 872a054666..218425c08c 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -141,8 +141,10 @@ public:
#endif
bool deliverTouchPoints(QQuickItem *, QTouchEvent *, const QList<QTouchEvent::TouchPoint> &, QSet<int> *,
QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *);
- bool deliverTouchEvent(QTouchEvent *);
+ void deliverTouchEvent(QTouchEvent *);
+ void reallyDeliverTouchEvent(QTouchEvent *);
bool deliverTouchCancelEvent(QTouchEvent *);
+ void flushDelayedTouchEvent();
bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted);
bool deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints);
QTouchEvent *touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent);
@@ -210,6 +212,8 @@ public:
QSGRenderLoop *windowManager;
QQuickRenderControl *renderControl;
QQuickAnimatorController *animationController;
+ QTouchEvent *delayedTouch;
+ int touchRecursionGuard;
QColor clearColor;
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index 6647ec83d0..00e67aa944 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -334,6 +334,12 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
if (!current)
return;
+ if (!data.grabOnly) {
+ cd->flushDelayedTouchEvent();
+ // Event delivery/processing triggered the window to be deleted or stop rendering.
+ if (!m_windows.contains(window))
+ return;
+ }
cd->polishItems();
emit window->afterAnimating();
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index 7b01f64ee0..a5b46b7c75 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -51,6 +51,8 @@
#include <QtGui/QScreen>
#include <QtGui/QOffscreenSurface>
+#include <qpa/qwindowsysteminterface.h>
+
#include <QtQuick/QQuickWindow>
#include <private/qquickwindow_p.h>
@@ -1063,13 +1065,33 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
{
QSG_GUI_DEBUG(w->window, "polishAndSync()");
- if (!w->window->isExposed() || !w->window->isVisible() || w->window->size().isEmpty()) {
+ QQuickWindow *window = w->window;
+ if (!window->isExposed() || !window->isVisible() || window->size().isEmpty()) {
QSG_GUI_DEBUG(w->window, " - not exposed, aborting...");
killTimer(w->timerId);
w->timerId = 0;
return;
}
+ // Flush pending touch events.
+ // First we force flushing of the windowing system events, so that we're
+ // working with the latest possible data. This can trigger event processing
+ // which in turn can stop rendering this window, so verify that before
+ // proceeding. Then we flush the touch event and as that also does event
+ // processing, verify again that we still are active and rendering.
+ QWindowSystemInterface::flushWindowSystemEvents();
+ w = windowFor(m_windows, window);
+ if (w) {
+ QQuickWindowPrivate::get(window)->flushDelayedTouchEvent();
+ w = windowFor(m_windows, window);
+ }
+ if (!w) {
+ QSG_GUI_DEBUG(w->window, " - removed after event flushing..");
+ killTimer(w->timerId);
+ w->timerId = 0;
+ return;
+ }
+
#ifndef QSG_NO_RENDER_TIMING
QElapsedTimer timer;
@@ -1081,7 +1103,7 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
timer.start();
#endif
- QQuickWindowPrivate *d = QQuickWindowPrivate::get(w->window);
+ QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
d->polishItems();
#ifndef QSG_NO_RENDER_TIMING
@@ -1091,14 +1113,14 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
w->updateDuringSync = false;
- emit w->window->afterAnimating();
+ emit window->afterAnimating();
- QSG_GUI_DEBUG(w->window, " - lock for sync...");
+ QSG_GUI_DEBUG(window, " - lock for sync...");
w->thread->mutex.lock();
m_lockedForSync = true;
- w->thread->postEvent(new WMSyncEvent(w->window, inExpose));
+ w->thread->postEvent(new WMSyncEvent(window, inExpose));
- QSG_GUI_DEBUG(w->window, " - wait for sync...");
+ QSG_GUI_DEBUG(window, " - wait for sync...");
#ifndef QSG_NO_RENDER_TIMING
if (profileFrames)
waitTime = timer.nsecsElapsed();
@@ -1106,7 +1128,7 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
w->thread->waitCondition.wait(&w->thread->mutex);
m_lockedForSync = false;
w->thread->mutex.unlock();
- QSG_GUI_DEBUG(w->window, " - unlocked after sync...");
+ QSG_GUI_DEBUG(window, " - unlocked after sync...");
#ifndef QSG_NO_RENDER_TIMING
if (profileFrames)
@@ -1117,9 +1139,9 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
w->timerId = 0;
if (m_animation_timer == 0 && m_animation_driver->isRunning()) {
- QSG_GUI_DEBUG(w->window, " - animations advancing");
+ QSG_GUI_DEBUG(window, " - animations advancing");
m_animation_driver->advance();
- QSG_GUI_DEBUG(w->window, " - animations done");
+ QSG_GUI_DEBUG(window, " - animations done");
// We need to trigger another sync to keep animations running...
maybePostPolishRequest(w);
emit timeToIncubate();
@@ -1131,7 +1153,7 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
#ifndef QSG_NO_RENDER_TIMING
if (qsg_render_timing)
qDebug(" - Gui Thread: window=%p, polish=%d, lock=%d, block/sync=%d -- animations=%d",
- w->window,
+ window,
int(polishTime/1000000),
int((waitTime - polishTime)/1000000),
int((syncTime - waitTime)/1000000),
@@ -1145,6 +1167,17 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
#endif
}
+QSGThreadedRenderLoop::Window *QSGThreadedRenderLoop::windowForTimer(int timerId) const
+{
+ for (int i=0; i<m_windows.size(); ++i) {
+ if (m_windows.at(i).timerId == timerId) {
+ return const_cast<Window *>(&m_windows.at(i));
+ break;
+ }
+ }
+ return 0;
+}
+
bool QSGThreadedRenderLoop::event(QEvent *e)
{
switch ((int) e->type()) {
@@ -1157,13 +1190,7 @@ bool QSGThreadedRenderLoop::event(QEvent *e)
emit timeToIncubate();
} else {
QSG_GUI_DEBUG((void *) 0, "QEvent::Timer -> Polish & Sync");
- Window *w = 0;
- for (int i=0; i<m_windows.size(); ++i) {
- if (m_windows.at(i).timerId == te->timerId()) {
- w = const_cast<Window *>(&m_windows.at(i));
- break;
- }
- }
+ Window *w = windowForTimer(te->timerId());
if (w)
polishAndSync(w);
}
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop_p.h b/src/quick/scenegraph/qsgthreadedrenderloop_p.h
index c347190e2b..970bd63040 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop_p.h
+++ b/src/quick/scenegraph/qsgthreadedrenderloop_p.h
@@ -95,6 +95,7 @@ private:
void releaseResources(Window *window, bool inDestructor);
bool checkAndResetForceUpdate(QQuickWindow *window);
+ Window *windowForTimer(int timerId) const;
bool anyoneShowing() const;
void initialize();
diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp
index 913b737798..32f52417bb 100644
--- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp
+++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp
@@ -442,6 +442,11 @@ void QSGWindowsRenderLoop::renderWindow(QQuickWindow *window)
if (!m_gl->makeCurrent(window))
return;
+ d->flushDelayedTouchEvent();
+ // Event delivery or processing has caused the window to stop rendering.
+ if (!windowData(window))
+ return;
+
QSG_RENDER_TIMING_SAMPLE(time_start);
RLDEBUG(" - polishing");