Merge lp:~unity-team/qtmir/touch_tracing into lp:qtmir

Proposed by Nick Dedekind
Status: Merged
Approved by: Gerry Boland
Approved revision: 378
Merged at revision: 395
Proposed branch: lp:~unity-team/qtmir/touch_tracing
Merge into: lp:qtmir
Prerequisite: lp:~nick-dedekind/qtmir/remove-dpkg-CMAK_INSTALL_PREFIX
Diff against target: 1257 lines (+941/-18)
28 files modified
CMakeLists.txt (+3/-1)
benchmarks/CMakeLists.txt (+8/-0)
benchmarks/README (+12/-0)
benchmarks/common.py (+33/-0)
benchmarks/report_types.py (+121/-0)
benchmarks/touch_event_latency.R (+6/-0)
benchmarks/touch_event_latency.py (+271/-0)
debian/control (+20/-0)
debian/qtmir-tests.install (+6/-0)
debian/rules (+1/-1)
demos/CMakeLists.txt (+4/-0)
demos/paths.h.in (+40/-0)
demos/qml-demo-client/CMakeLists.txt (+41/-0)
demos/qml-demo-client/main.cpp (+73/-0)
demos/qml-demo-client/qtmir-demo-client.desktop.in (+9/-0)
demos/qml-demo-shell/CMakeLists.txt (+35/-0)
demos/qml-demo-shell/main.cpp (+57/-0)
src/common/timestamp.cpp (+18/-0)
src/common/timestamp.h (+39/-0)
src/common/timestamp_impl.h (+34/-0)
src/modules/Unity/Application/mirsurface.cpp (+5/-4)
src/modules/Unity/Application/mirsurfaceitem.cpp (+6/-0)
src/modules/Unity/Application/tracepoints.tp (+5/-0)
src/platforms/mirserver/CMakeLists.txt (+1/-0)
src/platforms/mirserver/qteventfeeder.cpp (+14/-4)
src/platforms/mirserver/tracepoints.tp (+5/-0)
tests/modules/General/CMakeLists.txt (+2/-8)
tests/modules/General/timestamp_test.cpp (+72/-0)
To merge this branch: bzr merge lp:~unity-team/qtmir/touch_tracing
Reviewer Review Type Date Requested Status
Gerry Boland (community) Approve
PS Jenkins bot (community) continuous-integration Needs Fixing
Review via email: mp+271655@code.launchpad.net

This proposal supersedes a proposal from 2015-08-05.

Commit message

Added touch performance tracing and test.

Description of the change

Added touch performance tracing and test.

 * Are there any related MPs required for this MP to build/function as expected? Please list.
Yes

 * Did you perform an exploratory manual test run of your code change and any related functionality?
Yes

 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal

=== modified file 'debian/rules'
-DNO_TESTS=1 - please undo, we want tests!

=== added file 'demos/qml-demo-client/main.cpp'
I don't understand what this is useful for. Isn't the "qml" binary sufficient?

=== added file 'demos/qml-demo-shell/main.cpp'
same here.

=== added file 'src/common/utils.h'
file name "utils" as vague as possible:) Can you be more specific?

+++ src/common/utils.cpp
+qint64 resetStartTime(qint64 timestamp) {
+qint64 getStartTime_nsec(qint64 timestamp) {
please stick into an anonymous namespace

The indenting is inconsistent in the whole file.

+ QCoreApplication::instance()->setProperty("appStartTime", timestamp);
I suspect a static variable would do the job just as well.

+ qint64 startTime = getStartTime_nsec(timestamp.count())/1000000;
std::chrono has nanosecond to milisecond conversion abilities built in, I think you can just cast and it works. It would be safer than dividing by 1000000. I suspect you can avoid all usages of qint64 as a result.

====
This compression is a clever idea to work around the difference in timestamp units Qt & Mir uses. I'm still curious if instead of tying ourselves to the event APIs Qt has, e.g.

void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *w,
    ulong timestamp, const QPointF & local, const QPointF & global,
    Qt::MouseButtons b, Qt::KeyboardModifiers mods,
    Qt::MouseEventSource source)

we could make our own handle*Event() methods. In the end, the above method simply does:

QWindowSystemInterfacePrivate::MouseEvent * e =
    new QWindowSystemInterfacePrivate::MouseEvent(w, timestamp,
        QWindowSystemInterfacePrivate::FrameStrutMouse,
        QHighDpi::fromNativeLocalPosition(local, w),
        QHighDpi::fromNativePixels(global, w), b, mods, source);

QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);

If we subclassed QWindowSystemInterfacePrivate::MouseEvent, and added the timestamp type we want, that event might pass through Qt safely. This would be especially usesful for Mir events which have more detail that Qt events have.

To be investigated. For now, your approach is good.
====

std::chrono::milliseconds compressTimestamp(std::chrono::nanoseconds timestamp);
Do you need the milliseconds anywhere, as I only see you calling .count() on this everywhere it's used. Why not just return ulong?

Also, would be good to document *why* you've done the timestamp compression/decompression, for future souls.

=== modified file 'src/platforms/mirserver/tracepoints.tp'
Typos:
touchEventDisptach_start
touchEventDisptach_end
and these propagate throughout the whole of mirserver.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal

> === modified file 'debian/rules'
> -DNO_TESTS=1 - please undo, we want tests!
>
> === added file 'demos/qml-demo-client/main.cpp'
> I don't understand what this is useful for. Isn't the "qml" binary sufficient?
>
> === added file 'demos/qml-demo-shell/main.cpp'
> same here.
>

It's so we can get them to work with the benchmark util. The client needs to parse the command line parameters for the socket.

>
> === added file 'src/common/utils.h'
> file name "utils" as vague as possible:) Can you be more specific?

timestamp.h

>
> +++ src/common/utils.cpp
> +qint64 resetStartTime(qint64 timestamp) {
> +qint64 getStartTime_nsec(qint64 timestamp) {
> please stick into an anonymous namespace
>
> The indenting is inconsistent in the whole file.
>
> + QCoreApplication::instance()->setProperty("appStartTime", timestamp);
> I suspect a static variable would do the job just as well.

Used extern functions with a variable. Needs to be shared by the different modules.

>
> + qint64 startTime = getStartTime_nsec(timestamp.count())/1000000;
> std::chrono has nanosecond to milisecond conversion abilities built in, I
> think you can just cast and it works. It would be safer than dividing by
> 1000000. I suspect you can avoid all usages of qint64 as a result.
>
>
> ====
> This compression is a clever idea to work around the difference in timestamp
> units Qt & Mir uses. I'm still curious if instead of tying ourselves to the
> event APIs Qt has, e.g.
>
> void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *w,
> ulong timestamp, const QPointF & local, const QPointF & global,
> Qt::MouseButtons b, Qt::KeyboardModifiers mods,
> Qt::MouseEventSource source)
>
> we could make our own handle*Event() methods. In the end, the above method
> simply does:
>
> QWindowSystemInterfacePrivate::MouseEvent * e =
> new QWindowSystemInterfacePrivate::MouseEvent(w, timestamp,
> QWindowSystemInterfacePrivate::FrameStrutMouse,
> QHighDpi::fromNativeLocalPosition(local, w),
> QHighDpi::fromNativePixels(global, w), b, mods, source);
>
> QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);
>
> If we subclassed QWindowSystemInterfacePrivate::MouseEvent, and added the
> timestamp type we want, that event might pass through Qt safely. This would be
> especially usesful for Mir events which have more detail that Qt events have.
>
> To be investigated. For now, your approach is good.

I'll take a look into it when i get a minute.

> ====
>
>
> std::chrono::milliseconds compressTimestamp(std::chrono::nanoseconds
> timestamp);
> Do you need the milliseconds anywhere, as I only see you calling .count() on
> this everywhere it's used. Why not just return ulong?
>
> Also, would be good to document *why* you've done the timestamp
> compression/decompression, for future souls.

Done. in the header.

>
>
> === modified file 'src/platforms/mirserver/tracepoints.tp'
> Typos:
> touchEventDisptach_start
> touchEventDisptach_end
> and these propagate throughout the whole of mirserver.

Fixed.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal

+$ cd benchmarks
+$ sudo python3 touch_event_latency.py
I roughly followed this, but since I saw this script was installed by qtmir-tests in /usr/share/qtmir/benchmarks, I did

sudo python3 /usr/share/qtmir/benchmarks/touch_event_latency.py

but it doesn't fully work:

file:///build/qtmir-7Gjfwp/qtmir-0.4.6+15.04.20150827.1/demos/qtmir-demo-shell/qml-demo-shell.qml: File not found

review: Needs Fixing
lp:~unity-team/qtmir/touch_tracing updated
377. By Nick Dedekind

newline

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~unity-team/qtmir/touch_tracing updated
378. By Nick Dedekind

merged with trunk

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

We're having mild CI issues. Testing locally, all is good. We've a bunch of landings coming up, so I expect conflicts before this appears at the top of the queue unfortunately.

review: Approve
lp:~unity-team/qtmir/touch_tracing updated
379. By Nick Dedekind

fixed debian/control for qtmir-tests

380. By Nick Dedekind

merged with lp:~dandrader/qtmir/multimonitorNext

381. By Nick Dedekind

merged with cmake branch

382. By Nick Dedekind

merged parent

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2015-10-14 13:06:12 +0000
3+++ CMakeLists.txt 2015-10-14 13:06:12 +0000
4@@ -120,6 +120,7 @@
5
6 # Set QML module install path
7 set(QML_MODULE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/qt5/qml")
8+set(QTMIR_DATA_DIR ${CMAKE_INSTALL_DATADIR}/qtmir)
9
10
11 # Customisations for the build type
12@@ -156,6 +157,7 @@
13 message(STATUS "Tests disabled")
14 endif()
15
16-
17 # add subdirectories to build
18 add_subdirectory(src)
19+add_subdirectory(demos)
20+add_subdirectory(benchmarks)
21
22=== added directory 'benchmarks'
23=== added file 'benchmarks/CMakeLists.txt'
24--- benchmarks/CMakeLists.txt 1970-01-01 00:00:00 +0000
25+++ benchmarks/CMakeLists.txt 2015-10-14 13:06:12 +0000
26@@ -0,0 +1,8 @@
27+file(GLOB BENCHMARK_FILES
28+ *.py
29+ *.R
30+)
31+
32+install(FILES ${BENCHMARK_FILES}
33+ DESTINATION ${QTMIR_DATA_DIR}/benchmarks
34+)
35
36=== added file 'benchmarks/README'
37--- benchmarks/README 1970-01-01 00:00:00 +0000
38+++ benchmarks/README 2015-10-14 13:06:12 +0000
39@@ -0,0 +1,12 @@
40+To run performance tests on device:
41+
42+Firstly, you will need to install the qtmir-tests package. So either install the package using apt or build
43+and install from source
44+$ sudo apt-get install qtmir-tests
45+
46+Then you need to stop unity8 & unity-system compositor so that we can start out test in a controlled environment.
47+$ sudo stop lightdm
48+
49+Next, start the test!
50+$ cd benchmarks
51+$ sudo python3 touch_event_latency.py
52\ No newline at end of file
53
54=== added file 'benchmarks/common.py'
55--- benchmarks/common.py 1970-01-01 00:00:00 +0000
56+++ benchmarks/common.py 2015-10-14 13:06:12 +0000
57@@ -0,0 +1,33 @@
58+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
59+#
60+# Copyright (C) 2015 Canonical Ltd.
61+#
62+# This program is free software; you can redistribute it and/or modify
63+# it under the terms of the GNU Lesser General Public License as published by
64+# the Free Software Foundation; version 3.
65+#
66+# This program is distributed in the hope that it will be useful,
67+# but WITHOUT ANY WARRANTY; without even the implied warranty of
68+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
69+# GNU Lesser General Public License for more details.
70+#
71+# You should have received a copy of the GNU Lesser General Public License
72+# along with this program. If not, see <http://www.gnu.org/licenses/>.
73+
74+from autopilot import (
75+ input,
76+ platform
77+)
78+
79+def get_pointing_device():
80+ """Return the pointing device depending on the platform.
81+
82+ If the platform is `Desktop`, the pointing device will be a `Mouse`.
83+ If not, the pointing device will be `Touch`.
84+
85+ """
86+ if platform.model() == 'Desktop':
87+ input_device_class = input.Mouse
88+ else:
89+ input_device_class = input.Touch
90+ return input.Pointer(device=input_device_class.create())
91\ No newline at end of file
92
93=== added file 'benchmarks/report_types.py'
94--- benchmarks/report_types.py 1970-01-01 00:00:00 +0000
95+++ benchmarks/report_types.py 2015-10-14 13:06:12 +0000
96@@ -0,0 +1,121 @@
97+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
98+#
99+# Copyright (C) 2015 Canonical Ltd.
100+#
101+# This program is free software; you can redistribute it and/or modify
102+# it under the terms of the GNU Lesser General Public License as published by
103+# the Free Software Foundation; version 3.
104+#
105+# This program is distributed in the hope that it will be useful,
106+# but WITHOUT ANY WARRANTY; without even the implied warranty of
107+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
108+# GNU Lesser General Public License for more details.
109+#
110+# You should have received a copy of the GNU Lesser General Public License
111+# along with this program. If not, see <http://www.gnu.org/licenses/>.
112+
113+import subprocess
114+import os
115+import shutil
116+
117+class Node:
118+ def __init__(self, name):
119+ self.name = name
120+ self.children = []
121+
122+ def add_child(self, child):
123+ self.children.append(child)
124+
125+ def to_string(self):
126+ output = "<%s>" % self.name
127+ for child in self.children:
128+ output += child.to_string()
129+ output += "</%s>" % self.name
130+ return output
131+
132+class Results(Node):
133+ def __init__(self):
134+ super().__init__("results")
135+
136+class Events(Node):
137+ def __init__(self):
138+ super().__init__("events")
139+
140+class Event:
141+ def __init__(self, event):
142+ self.pid = event["vpid"]
143+ self.name = event.name
144+ self.timestamp = event.timestamp
145+
146+ def to_string(self):
147+ output = "<event "
148+ output += "pid='{}' ".format(self.pid)
149+ output += "name='{}' ".format(self.name)
150+ output += "timestamp='{}' ".format(self.timestamp)
151+ output += "/>"
152+ return output
153+
154+class Processes(Node):
155+ def __init__(self):
156+ super().__init__("processes")
157+
158+class Process:
159+ def __init__(self, name, pid):
160+ self.name = name
161+ self.pid = pid
162+
163+ def to_string(self):
164+ output = "<process "
165+ output += "name='{}' ".format(self.name)
166+ output += "pid='{}' ".format(self.pid)
167+ output += "/>"
168+ return output
169+
170+class ResultsData:
171+ def __init__(self, name, mean=0.0, deviation=0.0, comment=""):
172+ self.data = []
173+ self.name = name
174+ self.mean = mean
175+ self.deviation = deviation
176+ self.comment = comment
177+
178+ def add_data(self, value):
179+ self.data.append(value)
180+
181+ def to_string(self):
182+ output = "<data name='{}' mean='{}' deviation='{}' comment='{}' count='{}'>".format(
183+ self.name,
184+ self.mean,
185+ self.deviation,
186+ self.comment,
187+ len(self.data))
188+ output += "<values>"
189+ output += ",".join(map( lambda x: str(x), self.data))
190+ output += "</values>"
191+ output += "</data>"
192+ return output;
193+
194+ def generate_histogram(self, filename):
195+ cmdline = [
196+ shutil.which("Rscript"),
197+ os.path.split(os.path.abspath(__file__))[0] + "/touch_event_latency.R",
198+ "data.csv",
199+ "%s.png" % filename]
200+ # Use R to generate a histogram plot
201+ f = open("data.csv", "w")
202+ f.write(",".join(map( lambda x: str(x), self.data)));
203+ f.close()
204+ process = subprocess.Popen(cmdline)
205+ process.wait()
206+ if process.returncode != 0:
207+ print("Failed to generate histogram");
208+ os.remove("data.csv")
209+
210+
211+class Error:
212+ def __init__(self, comment):
213+ self.comment = comment
214+
215+ def to_string(self):
216+ return "<error comment='{}' />".format(self.comment)
217+ return output
218\ No newline at end of file
219
220=== added file 'benchmarks/touch_event_latency.R'
221--- benchmarks/touch_event_latency.R 1970-01-01 00:00:00 +0000
222+++ benchmarks/touch_event_latency.R 2015-10-14 13:06:12 +0000
223@@ -0,0 +1,6 @@
224+args <- commandArgs(trailingOnly = TRUE)
225+data <- scan(args[1], numeric(), sep=",")
226+colors = c("red", "yellow", "green", "violet", "orange", "pink", "blue")
227+png(filename=args[2])
228+hist(data, breaks=max(data), col=colors)
229+dev.off()
230
231=== added file 'benchmarks/touch_event_latency.py'
232--- benchmarks/touch_event_latency.py 1970-01-01 00:00:00 +0000
233+++ benchmarks/touch_event_latency.py 2015-10-14 13:06:12 +0000
234@@ -0,0 +1,271 @@
235+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
236+#
237+# Copyright (C) 2015 Canonical Ltd.
238+#
239+# This program is free software; you can redistribute it and/or modify
240+# it under the terms of the GNU Lesser General Public License as published by
241+# the Free Software Foundation; version 3.
242+#
243+# This program is distributed in the hope that it will be useful,
244+# but WITHOUT ANY WARRANTY; without even the implied warranty of
245+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
246+# GNU Lesser General Public License for more details.
247+#
248+# You should have received a copy of the GNU Lesser General Public License
249+# along with this program. If not, see <http://www.gnu.org/licenses/>.
250+
251+from mir_perf_framework import PerformanceTest, Server, Client
252+import time
253+import statistics
254+import shutil
255+import sys
256+from common import get_pointing_device
257+import report_types
258+
259+####### TEST #######
260+
261+
262+def perform_test():
263+ host = Server(reports=["input"])
264+ nested = Server(executable=shutil.which("qtmir-demo-shell"),
265+ host=host,
266+ reports=["input","client-input-receiver"],
267+ env={"QT_QPA_PLATFORM": "mirserver", "QML_NO_TOUCH_COMPRESSION": "1"})
268+ client = Client(executable=shutil.which("qtmir-demo-client"),
269+ server=nested,
270+ reports=["input","client-input-receiver"],
271+ env={"QT_QPA_PLATFORM": "ubuntumirclient", "QML_NO_TOUCH_COMPRESSION": "1"},
272+ options=["--", "--desktop_file_hint=/usr/share/applications/qtmir-demo-client.desktop"])
273+
274+ test = PerformanceTest([host, nested, client])
275+ test.start()
276+
277+ results = report_types.Results()
278+ processes = report_types.Processes()
279+ processes.add_child(report_types.Process("Host", host.process.pid))
280+ processes.add_child(report_types.Process("Nested Server", nested.process.pid))
281+ processes.add_child(report_types.Process("Client", client.process.pid))
282+ results.add_child(processes)
283+
284+ time.sleep(3) # wait for settle
285+
286+ host_pid = host.process.pid
287+ nested_pid = nested.process.pid
288+ client_pid = client.process.pid
289+
290+ touch = get_pointing_device()
291+ time.sleep(1) # let mir pick up the new input device
292+ touch.drag(100, 100, 1000, 100, 5, 0.006)
293+ touch.drag(1000, 100, 1000, 1000, 5, 0.006)
294+ touch.drag(1000, 1000, 100, 1000, 5, 0.006)
295+ touch.drag(100, 1000, 100, 100, 5, 0.006)
296+
297+ # time.sleep(5) # wait for settle
298+ time.sleep(2) # wait for settle
299+ test.stop()
300+
301+ ####### TRACE PARSING #######
302+
303+ trace = test.babeltrace()
304+
305+ server_touch_data_timestamps = {}
306+ client_touch_data_timestamps = {}
307+ client_touch_data_latency = {}
308+
309+ qtmir_touch_dispatch_start = {}
310+ qtmir_touch_dispatch_end = {}
311+ qtmir_touch_consume_start = {}
312+ qtmir_touch_consume_end = {}
313+
314+ events = report_types.Events()
315+
316+ for event in trace.events:
317+ events.add_child(report_types.Event(event))
318+ pid = event["vpid"]
319+ if event.name == "mir_client_input_receiver:touch_event":
320+ if pid not in client_touch_data_timestamps: client_touch_data_timestamps[pid] = []
321+ if pid not in client_touch_data_latency: client_touch_data_latency[pid] = []
322+ diff = (event.timestamp - event["event_time"]) / 1000000.0
323+ if diff > 0:
324+ client_touch_data_timestamps[pid].append(event.timestamp)
325+ client_touch_data_latency[pid].append(diff)
326+
327+ elif event.name == "mir_server_input:published_motion_event":
328+ if pid not in server_touch_data_timestamps: server_touch_data_timestamps[pid] = []
329+ server_touch_data_timestamps[pid].append(event["event_time"])
330+
331+ elif event.name == "qtmirserver:touchEventDispatch_start":
332+ if pid not in qtmir_touch_dispatch_start: qtmir_touch_dispatch_start[pid] = []
333+ qtmir_touch_dispatch_start[pid].append(event.timestamp)
334+
335+ elif event.name == "qtmirserver:touchEventDispatch_end":
336+ if pid not in qtmir_touch_dispatch_end: qtmir_touch_dispatch_end[pid] = []
337+ qtmir_touch_dispatch_end[pid].append(event.timestamp)
338+
339+ elif event.name == "qtmir:touchEventConsume_start":
340+ if pid not in qtmir_touch_consume_start: qtmir_touch_consume_start[pid] = []
341+ qtmir_touch_consume_start[pid].append(event.timestamp)
342+
343+ elif event.name == "qtmir:touchEventConsume_end":
344+ if pid not in qtmir_touch_consume_end: qtmir_touch_consume_end[pid] = []
345+ qtmir_touch_consume_end[pid].append(event.timestamp)
346+
347+ # LATENCY MEANS
348+
349+ if nested_pid in client_touch_data_latency:
350+ nested_data = client_touch_data_latency[nested_pid]
351+ nested_latency_xml = report_types.ResultsData(
352+ "nested_latency",
353+ statistics.mean(nested_data),
354+ statistics.stdev(nested_data),
355+ "Kernel to nested server latency")
356+ for value in nested_data:
357+ nested_latency_xml.add_data(value)
358+ results.add_child(nested_latency_xml)
359+ nested_latency_xml.generate_histogram("nested_latency")
360+ else:
361+ results.add_child(report_types.Error("No nested server touch latency data"))
362+
363+ if client_pid in client_touch_data_latency:
364+ client_data = client_touch_data_latency[client_pid]
365+
366+ client_latency_xml = report_types.ResultsData(
367+ "client_latency",
368+ statistics.mean(client_data),
369+ statistics.stdev(client_data),
370+ "Kernel to client latency")
371+ for value in client_data:
372+ client_latency_xml.add_data(value)
373+ results.add_child(client_latency_xml)
374+ client_latency_xml.generate_histogram("client_latency")
375+ else:
376+ results.add_child(report_types.Error("No client touch latency data"))
377+
378+ # EVENT RATES
379+ if host_pid in server_touch_data_timestamps:
380+ last_timestamp = -1
381+ input_rate = []
382+
383+ for next_timestamp in server_touch_data_timestamps[host_pid]:
384+ if last_timestamp != -1:
385+ diff = (next_timestamp - last_timestamp) / 1000000.0
386+ input_rate.append(diff)
387+ last_timestamp = next_timestamp
388+
389+ input_rate_xml = report_types.ResultsData(
390+ "host_input_ate",
391+ statistics.mean(input_rate),
392+ statistics.stdev(input_rate),
393+ "Host input event rate")
394+ for value in input_rate:
395+ input_rate_xml.add_data(value)
396+ results.add_child(input_rate_xml)
397+ input_rate_xml.generate_histogram("host_input_rate")
398+ else:
399+ results.add_child(report_types.Error("No host server input event timestamp data"))
400+
401+ if nested_pid in client_touch_data_timestamps:
402+ last_timestamp = -1
403+ input_rate = []
404+ for next_timestamp in client_touch_data_timestamps[nested_pid]:
405+ if last_timestamp != -1:
406+ diff = (next_timestamp - last_timestamp) / 1000000.0
407+ input_rate.append(diff)
408+ last_timestamp = next_timestamp
409+
410+ input_rate_xml = report_types.ResultsData(
411+ "nested_input_rate",
412+ statistics.mean(input_rate),
413+ statistics.stdev(input_rate),
414+ "Nested server event rate")
415+ for value in input_rate:
416+ input_rate_xml.add_data(value)
417+ results.add_child(input_rate_xml)
418+ input_rate_xml.generate_histogram("nested_input_rate")
419+ else:
420+ results.add_child(report_types.Error("No nested server input event timestamp data"))
421+
422+ if client_pid in client_touch_data_timestamps:
423+ last_timestamp = -1
424+ input_rate = []
425+ for next_timestamp in client_touch_data_timestamps[client_pid]:
426+ if last_timestamp != -1:
427+ diff = (next_timestamp - last_timestamp) / 1000000.0
428+ input_rate.append(diff)
429+ last_timestamp = next_timestamp
430+
431+ input_rate_xml = report_types.ResultsData(
432+ "client_input_rate",
433+ statistics.mean(input_rate),
434+ statistics.stdev(input_rate),
435+ "Client event rate")
436+ for value in input_rate:
437+ input_rate_xml.add_data(value)
438+ results.add_child(input_rate_xml)
439+ input_rate_xml.generate_histogram("client_input_rate")
440+ else:
441+ results.add_child(report_types.Error("No client event timestamp data"))
442+
443+ qtmir_loop_data = []
444+ dispatch_data = []
445+ consume_data = []
446+
447+ # TIME BETWEEN TRACEPOINTS
448+ dispatch_starts = qtmir_touch_dispatch_start[nested_pid] if nested_pid in qtmir_touch_dispatch_start else []
449+ dispatch_ends = qtmir_touch_dispatch_end[nested_pid] if nested_pid in qtmir_touch_dispatch_end else []
450+ consume_starts = qtmir_touch_consume_start[nested_pid] if nested_pid in qtmir_touch_consume_start else []
451+ consume_ends = qtmir_touch_consume_end[nested_pid] if nested_pid in qtmir_touch_consume_end else []
452+
453+ # since there's no uniqueness to events, we need to assume all events are 1:1 through system
454+ if len(dispatch_starts) > 0 and len(dispatch_starts) == len(dispatch_ends) and len(dispatch_starts) == len(consume_starts) and len(consume_starts) == len(consume_ends):
455+ i = 0
456+
457+ for start in dispatch_starts:
458+ dispatch_diff = (dispatch_ends[i] - start) / 1000000.0
459+ consume_diff = (consume_ends[i] - consume_starts[i]) / 1000000.0
460+ loop_dif = (consume_starts[i] - dispatch_ends[i]) / 1000000.0
461+ dispatch_data.append(dispatch_diff);
462+ consume_data.append(consume_diff);
463+ qtmir_loop_data.append(loop_dif);
464+ i=i+1
465+
466+ qtmir_loop_xml = report_types.ResultsData(
467+ "qtmir_eventloop",
468+ statistics.mean(qtmir_loop_data),
469+ statistics.stdev(qtmir_loop_data),
470+ "Time spent in qtmir event loop")
471+ for value in qtmir_loop_data:
472+ qtmir_loop_xml.add_data(value)
473+ results.add_child(qtmir_loop_xml)
474+ qtmir_loop_xml.generate_histogram("qtmir_eventloop")
475+
476+ qtmir_dispatch_xml = report_types.ResultsData(
477+ "qtmir_dispatch",
478+ statistics.mean(dispatch_data),
479+ statistics.stdev(dispatch_data),
480+ "Time QteventFeeder spent dispatching event to qt")
481+ for value in dispatch_data:
482+ qtmir_dispatch_xml.add_data(value)
483+ results.add_child(qtmir_dispatch_xml)
484+ qtmir_dispatch_xml.generate_histogram("qtmir_dispatch")
485+
486+ qtmir_consume_xml = report_types.ResultsData(
487+ "qtmir_consume",
488+ statistics.mean(consume_data),
489+ statistics.stdev(consume_data),
490+ "Time MirSurfaceItem spent consiming event")
491+ for value in consume_data:
492+ qtmir_consume_xml.add_data(value)
493+ results.add_child(qtmir_consume_xml)
494+ qtmir_consume_xml.generate_histogram("qtmir_consume")
495+
496+ else:
497+ results.add_child(report_types.Error("Cannot calculate QtMir loop data - Dispatch event count did not match surface consume event count"))
498+
499+ results.add_child(events)
500+ return results
501+
502+if __name__ == "__main__":
503+ results = perform_test();
504+ f = open("touch_event_latency.xml", "w")
505+ f.write(results.to_string())
506
507=== modified file 'debian/control'
508--- debian/control 2015-10-14 13:06:12 +0000
509+++ debian/control 2015-10-14 13:06:12 +0000
510@@ -98,3 +98,23 @@
511 QtMir provides Qt/QML bindings for Mir features that are exposed through the
512 qtmir-desktop or qtmir-android QPA plugin such as Application management
513 (start/stop/suspend/resume) and surface management.
514+
515+Package: qtmir-tests
516+Architecture: any
517+Multi-Arch: same
518+Pre-Depends: ${misc:Pre-Depends},
519+Depends: autopilot-qt5,
520+ littler,
521+ lttng-tools,
522+ mir-test-tools,
523+ python3-autopilot,
524+ python3-babeltrace,
525+ python3-evdev,
526+ python3-mir-perf-framework,
527+ qtmir-desktop (= ${source:Version}) | qtmir-android (= ${source:Version}),
528+ qtdeclarative5-qtmir-plugin,
529+ ${misc:Depends},
530+ ${shlibs:Depends},
531+Description: QtMir tests and demos
532+ This package provides benchmark tests and a simple shell and client using the
533+ QtMir QPA.
534
535=== added file 'debian/qtmir-tests.install'
536--- debian/qtmir-tests.install 1970-01-01 00:00:00 +0000
537+++ debian/qtmir-tests.install 2015-10-14 13:06:12 +0000
538@@ -0,0 +1,6 @@
539+usr/bin/qtmir-demo-shell
540+usr/bin/qtmir-demo-client
541+usr/share/applications/qtmir-demo-client.desktop
542+usr/share/qtmir/qtmir-demo-shell/
543+usr/share/qtmir/qtmir-demo-client/*
544+usr/share/qtmir/benchmarks/*
545
546=== modified file 'debian/rules'
547--- debian/rules 2015-10-14 13:06:12 +0000
548+++ debian/rules 2015-10-14 13:06:12 +0000
549@@ -59,4 +59,4 @@
550 endif
551 dh_install --sourcedir=$(TMP2_DIR) -pqtmir-desktop
552 dh_install --sourcedir=$(TMP2_DIR) -pqtdeclarative5-qtmir-plugin
553-
554+ dh_install --sourcedir=$(TMP2_DIR) -pqtmir-tests
555
556=== added file 'demos/CMakeLists.txt'
557--- demos/CMakeLists.txt 1970-01-01 00:00:00 +0000
558+++ demos/CMakeLists.txt 2015-10-14 13:06:12 +0000
559@@ -0,0 +1,4 @@
560+configure_file(paths.h.in ${CMAKE_CURRENT_BINARY_DIR}/paths.h @ONLY)
561+
562+add_subdirectory(qml-demo-client)
563+add_subdirectory(qml-demo-shell)
564\ No newline at end of file
565
566=== added file 'demos/paths.h.in'
567--- demos/paths.h.in 1970-01-01 00:00:00 +0000
568+++ demos/paths.h.in 2015-10-14 13:06:12 +0000
569@@ -0,0 +1,40 @@
570+/*
571+ * Copyright (C) 2015 Canonical, Ltd.
572+ *
573+ * This program is free software; you can redistribute it and/or modify
574+ * it under the terms of the GNU General Public License as published by
575+ * the Free Software Foundation; version 3.
576+ *
577+ * This program is distributed in the hope that it will be useful,
578+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
579+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
580+ * GNU General Public License for more details.
581+ *
582+ * You should have received a copy of the GNU General Public License
583+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
584+ */
585+
586+#ifndef PATHS_H
587+#define PATHS_H
588+
589+// Qt
590+#include <QtCore/QCoreApplication>
591+#include <QtCore/QDir>
592+#include <QtGui/QIcon>
593+#include <QtQml/QQmlEngine>
594+#include <QStandardPaths>
595+
596+inline bool isRunningInstalled() {
597+ static bool installed = (QCoreApplication::applicationDirPath() ==
598+ QDir(("@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@")).canonicalPath());
599+ return installed;
600+}
601+
602+inline QString qmlDirectory() {
603+ if (isRunningInstalled()) {
604+ return QString("@CMAKE_INSTALL_PREFIX@/@QTMIR_DATA_DIR@/");
605+ } else {
606+ return QString("@CMAKE_SOURCE_DIR@/demos/");
607+ }
608+}
609+#endif
610\ No newline at end of file
611
612=== added file 'demos/qml-demo-client/CMakeLists.txt'
613--- demos/qml-demo-client/CMakeLists.txt 1970-01-01 00:00:00 +0000
614+++ demos/qml-demo-client/CMakeLists.txt 2015-10-14 13:06:12 +0000
615@@ -0,0 +1,41 @@
616+set(DEMO_CLIENT qtmir-demo-client)
617+configure_file(${DEMO_CLIENT}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${DEMO_CLIENT}.desktop @ONLY)
618+
619+include_directories(
620+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
621+)
622+
623+add_executable(${DEMO_CLIENT}
624+ main.cpp
625+)
626+
627+include_directories(
628+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
629+ ${Qt5Qml_PRIVATE_INCLUDE_DIRS}
630+ ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
631+)
632+
633+target_link_libraries(
634+ ${DEMO_CLIENT}
635+ Qt5::Core
636+ Qt5::DBus
637+ Qt5::Qml
638+ Qt5::Quick
639+)
640+
641+file(GLOB QML_JS_FILES *.qml *.js *.png)
642+
643+# install binaries
644+install(TARGETS ${DEMO_CLIENT}
645+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
646+ )
647+
648+install(FILES
649+ ${QML_JS_FILES}
650+ DESTINATION ${QTMIR_DATA_DIR}/${DEMO_CLIENT}
651+)
652+
653+install(FILES
654+ ${CMAKE_CURRENT_BINARY_DIR}/${DEMO_CLIENT}.desktop
655+ DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications
656+)
657\ No newline at end of file
658
659=== added file 'demos/qml-demo-client/main.cpp'
660--- demos/qml-demo-client/main.cpp 1970-01-01 00:00:00 +0000
661+++ demos/qml-demo-client/main.cpp 2015-10-14 13:06:12 +0000
662@@ -0,0 +1,73 @@
663+/*
664+ * Copyright (C) 2012-2015 Canonical, Ltd.
665+ *
666+ * This program is free software; you can redistribute it and/or modify
667+ * it under the terms of the GNU General Public License as published by
668+ * the Free Software Foundation; version 3.
669+ *
670+ * This program is distributed in the hope that it will be useful,
671+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
672+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
673+ * GNU General Public License for more details.
674+ *
675+ * You should have received a copy of the GNU General Public License
676+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
677+ */
678+
679+// Qt
680+#include <QtQuick/QQuickView>
681+#include <QtGui/QGuiApplication>
682+#include <QDebug>
683+#include <csignal>
684+#include <libintl.h>
685+#include <getopt.h>
686+#include "../paths.h"
687+
688+// REMOVEME - Should be able to use qmlscene, but in order to use the mir benchmarking we need
689+// to parse command line switches. Wait until MIR_SOCKET supported by the benchmark framework.
690+
691+int main(int argc, char **argv)
692+{
693+ int arg;
694+ opterr = 0;
695+ while ((arg = getopt (argc, argv, "hm:")) != -1)
696+ {
697+ switch (arg)
698+ {
699+ case 'm':
700+ setenv("MIR_SOCKET", optarg, 1);
701+ break;
702+
703+ case '?':
704+ case 'h':
705+ default:
706+ puts(argv[0]);
707+ puts("Usage:");
708+ puts(" -m <Mir server socket>");
709+ puts(" -h: this help text");
710+ return -1;
711+ }
712+ }
713+
714+ QGuiApplication::setApplicationName("qml-demo-client");
715+ QGuiApplication *application;
716+
717+ application = new QGuiApplication(argc, (char**)argv);
718+ QQuickView* view = new QQuickView();
719+ view->setResizeMode(QQuickView::SizeRootObjectToView);
720+ view->setColor("black");
721+ view->setTitle("Demo Client");
722+
723+ QUrl source(::qmlDirectory() + "qtmir-demo-client/qml-demo-client.qml");
724+
725+ view->setSource(source);
726+ QObject::connect(view->engine(), SIGNAL(quit()), application, SLOT(quit()));
727+
728+ view->showFullScreen();
729+ int result = application->exec();
730+
731+ delete view;
732+ delete application;
733+
734+ return result;
735+}
736
737=== added file 'demos/qml-demo-client/qtmir-demo-client.desktop.in'
738--- demos/qml-demo-client/qtmir-demo-client.desktop.in 1970-01-01 00:00:00 +0000
739+++ demos/qml-demo-client/qtmir-demo-client.desktop.in 2015-10-14 13:06:12 +0000
740@@ -0,0 +1,9 @@
741+[Desktop Entry]
742+Type=Application
743+Name=QtMir Demo Client
744+Comment=QtMir demo client
745+Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/qtmir-demo-client
746+Terminal=false
747+Icon=
748+NoDisplay=false
749+X-Ubuntu-Touch=true
750
751=== added file 'demos/qml-demo-shell/CMakeLists.txt'
752--- demos/qml-demo-shell/CMakeLists.txt 1970-01-01 00:00:00 +0000
753+++ demos/qml-demo-shell/CMakeLists.txt 2015-10-14 13:06:12 +0000
754@@ -0,0 +1,35 @@
755+set(DEMO_SHELL qtmir-demo-shell)
756+
757+include_directories(
758+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
759+)
760+
761+add_executable(${DEMO_SHELL}
762+ main.cpp
763+)
764+
765+include_directories(
766+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
767+ ${Qt5Qml_PRIVATE_INCLUDE_DIRS}
768+ ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
769+)
770+
771+target_link_libraries(
772+ ${DEMO_SHELL}
773+ Qt5::Core
774+ Qt5::DBus
775+ Qt5::Qml
776+ Qt5::Quick
777+)
778+
779+file(GLOB QML_JS_FILES *.qml *.js *.png)
780+
781+# install binaries
782+install(TARGETS ${DEMO_SHELL}
783+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
784+ )
785+
786+install(FILES
787+ ${QML_JS_FILES}
788+ DESTINATION ${QTMIR_DATA_DIR}/${DEMO_SHELL}
789+)
790
791=== added file 'demos/qml-demo-shell/main.cpp'
792--- demos/qml-demo-shell/main.cpp 1970-01-01 00:00:00 +0000
793+++ demos/qml-demo-shell/main.cpp 2015-10-14 13:06:12 +0000
794@@ -0,0 +1,57 @@
795+/*
796+ * Copyright (C) 2012-2015 Canonical, Ltd.
797+ *
798+ * This program is free software; you can redistribute it and/or modify
799+ * it under the terms of the GNU General Public License as published by
800+ * the Free Software Foundation; version 3.
801+ *
802+ * This program is distributed in the hope that it will be useful,
803+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
804+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
805+ * GNU General Public License for more details.
806+ *
807+ * You should have received a copy of the GNU General Public License
808+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
809+ */
810+
811+// Qt
812+#include <QCommandLineParser>
813+#include <QtQuick/QQuickView>
814+#include <QtGui/QGuiApplication>
815+#include <QtQml/QQmlEngine>
816+#include <QtQml/QQmlContext>
817+#include <QLibrary>
818+#include <QDebug>
819+#include <csignal>
820+#include <libintl.h>
821+#include "../paths.h"
822+
823+#include <private/qobject_p.h>
824+
825+// REMOVEME - Should be able to use qmlscene, but in order to use the mir benchmarking we need
826+// to parse command line switches. Wait until MIR_SOCKET supported by the benchmark framework.
827+
828+int main(int argc, const char *argv[])
829+{
830+ QGuiApplication::setApplicationName("qml-demo-shell");
831+ QGuiApplication *application;
832+
833+ application = new QGuiApplication(argc, (char**)argv);
834+ QQuickView* view = new QQuickView();
835+ view->setResizeMode(QQuickView::SizeRootObjectToView);
836+ view->setColor("black");
837+ view->setTitle("Demo Shell");
838+
839+ QUrl source(::qmlDirectory() + "qtmir-demo-shell/qml-demo-shell.qml");
840+
841+ view->setSource(source);
842+ QObject::connect(view->engine(), SIGNAL(quit()), application, SLOT(quit()));
843+
844+ view->showFullScreen();
845+ int result = application->exec();
846+
847+ delete view;
848+ delete application;
849+
850+ return result;
851+}
852
853=== added file 'src/common/timestamp.cpp'
854--- src/common/timestamp.cpp 1970-01-01 00:00:00 +0000
855+++ src/common/timestamp.cpp 2015-10-14 13:06:12 +0000
856@@ -0,0 +1,18 @@
857+
858+#include <chrono>
859+#include "timestamp_impl.h"
860+
861+std::chrono::nanoseconds appStartTime(0);
862+
863+void resetStartTime(std::chrono::nanoseconds timestamp)
864+{
865+ appStartTime = timestamp;
866+}
867+
868+std::chrono::nanoseconds getStartTime(std::chrono::nanoseconds timestamp, bool allowReset)
869+{
870+ if (allowReset && appStartTime.count() == 0) {
871+ resetStartTime(timestamp);
872+ }
873+ return appStartTime;
874+}
875
876=== added file 'src/common/timestamp.h'
877--- src/common/timestamp.h 1970-01-01 00:00:00 +0000
878+++ src/common/timestamp.h 2015-10-14 13:06:12 +0000
879@@ -0,0 +1,39 @@
880+/*
881+ * Copyright (C) 2015 Canonical, Ltd.
882+ *
883+ * This program is free software: you can redistribute it and/or modify it under
884+ * the terms of the GNU Lesser General Public License version 3, as published by
885+ * the Free Software Foundation.
886+ *
887+ * This program is distributed in the hope that it will be useful, but WITHOUT
888+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
889+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
890+ * Lesser General Public License for more details.
891+ *
892+ * You should have received a copy of the GNU Lesser General Public License
893+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
894+ */
895+
896+#ifndef QTMIR_TIMESTAMP_H
897+#define QTMIR_TIMESTAMP_H
898+
899+#include <QtCore/qglobal.h>
900+#include <chrono>
901+
902+namespace qtmir {
903+
904+// Converts a mir timestamp (in nanoseconds) to and from a timestamp in milliseconds.
905+// Qt system events only work with ulong timestamps. On 32bit archs a ulong is 4 bytes long, so the 64 bit nanoseconds
906+// will be truncated and skewed. In order to fix this, we truncate the result by using time since "first call"
907+template<typename T>
908+T compressTimestamp(std::chrono::nanoseconds timestamp);
909+
910+ // "Re-inflate" a truncated timestamp.
911+template<typename T>
912+std::chrono::nanoseconds uncompressTimestamp(T timestamp);
913+
914+}
915+
916+#include "timestamp_impl.h"
917+
918+#endif // QTMIR_TIMESTAMP_H
919
920=== added file 'src/common/timestamp_impl.h'
921--- src/common/timestamp_impl.h 1970-01-01 00:00:00 +0000
922+++ src/common/timestamp_impl.h 2015-10-14 13:06:12 +0000
923@@ -0,0 +1,34 @@
924+#include <QCoreApplication>
925+#include <QVariant>
926+
927+extern "C" {
928+ void resetStartTime(std::chrono::nanoseconds timestamp);
929+ std::chrono::nanoseconds getStartTime(std::chrono::nanoseconds timestamp, bool allowReset = true);
930+}
931+
932+namespace qtmir {
933+
934+template<typename T>
935+T compressTimestamp(std::chrono::nanoseconds timestamp)
936+{
937+ std::chrono::nanoseconds startTime = getStartTime(timestamp);
938+ std::chrono::nanoseconds result = timestamp - startTime;
939+
940+ if (std::numeric_limits<std::chrono::nanoseconds::rep>::max() > std::numeric_limits<T>::max() &&
941+ result > std::chrono::nanoseconds(std::numeric_limits<T>::max())) {
942+ // we've overflowed the boundaries of the millisecond type.
943+ resetStartTime(timestamp);
944+ return 0;
945+ }
946+
947+ return result.count();
948+}
949+
950+template<typename T>
951+std::chrono::nanoseconds uncompressTimestamp(T timestamp)
952+{
953+ auto tsNS = std::chrono::nanoseconds(timestamp);
954+ return getStartTime(tsNS, false) + std::chrono::nanoseconds(tsNS);
955+}
956+
957+}
958\ No newline at end of file
959
960=== modified file 'src/modules/Unity/Application/mirsurface.cpp'
961--- src/modules/Unity/Application/mirsurface.cpp 2015-10-14 13:06:12 +0000
962+++ src/modules/Unity/Application/mirsurface.cpp 2015-10-14 13:06:12 +0000
963@@ -15,6 +15,7 @@
964 */
965
966 #include "mirsurface.h"
967+#include "timestamp.h"
968
969 // mirserver
970 #include <surfaceobserver.h>
971@@ -51,7 +52,7 @@
972
973 mir::EventUPtr makeMirEvent(QMouseEvent *qtEvent, MirPointerAction action)
974 {
975- auto timestamp = std::chrono::milliseconds(qtEvent->timestamp());
976+ auto timestamp = uncompressTimestamp<ulong>(qtEvent->timestamp());
977 auto modifiers = getMirModifiersFromQt(qtEvent->modifiers());
978
979 MirPointerButtons buttons = 0;
980@@ -68,7 +69,7 @@
981
982 mir::EventUPtr makeMirEvent(QHoverEvent *qtEvent, MirPointerAction action)
983 {
984- auto timestamp = std::chrono::milliseconds(qtEvent->timestamp());
985+ auto timestamp = uncompressTimestamp<ulong>(qtEvent->timestamp());
986
987 MirPointerButtons buttons = 0;
988
989@@ -93,7 +94,7 @@
990 if (qtEvent->isAutoRepeat())
991 action = mir_keyboard_action_repeat;
992
993- return mir::events::make_event(0 /* DeviceID */, std::chrono::milliseconds(qtEvent->timestamp()),
994+ return mir::events::make_event(0 /* DeviceID */, uncompressTimestamp<ulong>(qtEvent->timestamp()),
995 0 /* mac */, action, qtEvent->nativeVirtualKey(),
996 qtEvent->nativeScanCode(),
997 qtEvent->nativeModifiers());
998@@ -105,7 +106,7 @@
999 ulong qtTimestamp)
1000 {
1001 auto modifiers = getMirModifiersFromQt(qmods);
1002- auto ev = mir::events::make_event(0, std::chrono::milliseconds(qtTimestamp),
1003+ auto ev = mir::events::make_event(0, uncompressTimestamp<ulong>(qtTimestamp),
1004 0 /* mac */, modifiers);
1005
1006 for (int i = 0; i < qtTouchPoints.count(); ++i) {
1007
1008=== modified file 'src/modules/Unity/Application/mirsurfaceitem.cpp'
1009--- src/modules/Unity/Application/mirsurfaceitem.cpp 2015-10-14 13:06:12 +0000
1010+++ src/modules/Unity/Application/mirsurfaceitem.cpp 2015-10-14 13:06:12 +0000
1011@@ -20,6 +20,8 @@
1012 #include "mirsurfaceitem.h"
1013 #include "logging.h"
1014 #include "ubuntukeyboardinfo.h"
1015+#include "tracepoints.h" // generated from tracepoints.tp
1016+#include "timestamp.h"
1017
1018 // common
1019 #include <debughelpers.h>
1020@@ -409,10 +411,14 @@
1021 m_lastTouchEvent->timestamp = timestamp;
1022 m_lastTouchEvent->touchPoints = touchPoints;
1023 m_lastTouchEvent->touchPointStates = touchPointStates;
1024+
1025+ tracepoint(qtmir, touchEventConsume_end, uncompressTimestamp<ulong>(timestamp).count());
1026 }
1027
1028 void MirSurfaceItem::touchEvent(QTouchEvent *event)
1029 {
1030+ tracepoint(qtmir, touchEventConsume_start, uncompressTimestamp<ulong>(event->timestamp()).count());
1031+
1032 bool accepted = processTouchEvent(event->type(),
1033 event->timestamp(),
1034 event->modifiers(),
1035
1036=== modified file 'src/modules/Unity/Application/tracepoints.tp'
1037--- src/modules/Unity/Application/tracepoints.tp 2014-09-04 11:11:26 +0000
1038+++ src/modules/Unity/Application/tracepoints.tp 2015-10-14 13:06:12 +0000
1039@@ -1,3 +1,5 @@
1040+#include <stdint.h>
1041+
1042 TRACEPOINT_EVENT(qtmir, startApplication, TP_ARGS(0), TP_FIELDS())
1043 TRACEPOINT_EVENT(qtmir, onProcessStarting, TP_ARGS(0), TP_FIELDS())
1044 TRACEPOINT_EVENT(qtmir, authorizeSession, TP_ARGS(0), TP_FIELDS())
1045@@ -7,3 +9,6 @@
1046 TRACEPOINT_EVENT(qtmir, firstFrameDrawn, TP_ARGS(0), TP_FIELDS())
1047 TRACEPOINT_EVENT(qtmir, appIdHasProcessId_start, TP_ARGS(0), TP_FIELDS())
1048 TRACEPOINT_EVENT(qtmir, appIdHasProcessId_end, TP_ARGS(int, found), TP_FIELDS(ctf_integer(int, found, found)))
1049+
1050+TRACEPOINT_EVENT(qtmir, touchEventConsume_start, TP_ARGS(int64_t, event_time), TP_FIELDS(ctf_integer(int64_t, event_time, event_time)))
1051+TRACEPOINT_EVENT(qtmir, touchEventConsume_end, TP_ARGS(int64_t, event_time), TP_FIELDS(ctf_integer(int64_t, event_time, event_time)))
1052
1053=== modified file 'src/platforms/mirserver/CMakeLists.txt'
1054--- src/platforms/mirserver/CMakeLists.txt 2015-10-14 13:06:12 +0000
1055+++ src/platforms/mirserver/CMakeLists.txt 2015-10-14 13:06:12 +0000
1056@@ -42,6 +42,7 @@
1057
1058 set(MIRSERVER_QPA_PLUGIN_SRC
1059 ${CMAKE_SOURCE_DIR}/src/common/debughelpers.cpp
1060+ ${CMAKE_SOURCE_DIR}/src/common/timestamp.cpp
1061 cursor.cpp
1062 mirwindowmanager.cpp
1063 mirsingleton.cpp
1064
1065=== modified file 'src/platforms/mirserver/qteventfeeder.cpp'
1066--- src/platforms/mirserver/qteventfeeder.cpp 2015-10-14 13:06:12 +0000
1067+++ src/platforms/mirserver/qteventfeeder.cpp 2015-10-14 13:06:12 +0000
1068@@ -17,6 +17,8 @@
1069 #include "qteventfeeder.h"
1070 #include "cursor.h"
1071 #include "logging.h"
1072+#include "timestamp.h"
1073+#include "tracepoints.h" // generated from tracepoints.tp
1074 #include "screen.h" // NEEDED?
1075 #include "screencontroller.h"
1076
1077@@ -530,7 +532,7 @@
1078
1079 void QtEventFeeder::dispatchPointer(MirInputEvent const* ev)
1080 {
1081- auto timestamp = mir_input_event_get_event_time(ev) / 1000000;
1082+ auto timestamp = qtmir::compressTimestamp<ulong>(std::chrono::nanoseconds(mir_input_event_get_event_time(ev)));
1083
1084 auto pev = mir_input_event_get_pointer_event(ev);
1085 qCDebug(QTMIR_MIR_INPUT) << "Received" << qPrintable(mirPointerEventToString(pev));
1086@@ -546,7 +548,7 @@
1087
1088 void QtEventFeeder::dispatchKey(MirInputEvent const* event)
1089 {
1090- ulong timestamp = mir_input_event_get_event_time(event) / 1000000;
1091+ auto timestamp = qtmir::compressTimestamp<ulong>(std::chrono::nanoseconds(mir_input_event_get_event_time(event)));
1092
1093 auto kev = mir_input_event_get_keyboard_event(event);
1094 xkb_keysym_t xk_sym = mir_keyboard_event_key_code(kev);
1095@@ -605,6 +607,10 @@
1096
1097 void QtEventFeeder::dispatchTouch(MirInputEvent const* event)
1098 {
1099+ auto timestamp = std::chrono::nanoseconds(mir_input_event_get_event_time(event));
1100+
1101+ tracepoint(qtmirserver, touchEventDispatch_start, timestamp.count());
1102+
1103 auto tev = mir_input_event_get_touch_event(event);
1104 qCDebug(QTMIR_MIR_INPUT) << "Received" << qPrintable(mirTouchEventToString(tev));
1105
1106@@ -661,17 +667,21 @@
1107 }
1108 }
1109
1110+ auto compressedTimestamp = qtmir::compressTimestamp<ulong>(timestamp);
1111+
1112 // Qt needs a happy, sane stream of touch events. So let's make sure we're not forwarding
1113 // any insanity.
1114- validateTouches(window, mir_input_event_get_event_time(event) / 1000000, touchPoints);
1115+ validateTouches(window, compressedTimestamp, touchPoints);
1116
1117 // Touch event propagation.
1118 qCDebug(QTMIR_MIR_INPUT) << "Sending to Qt" << qPrintable(touchesToString(touchPoints));
1119 mQtWindowSystem->handleTouchEvent(window,
1120 //scales down the nsec_t (int64) to fit a ulong, precision lost but time difference suitable
1121- mir_input_event_get_event_time(event) / 1000000,
1122+ compressedTimestamp,
1123 mTouchDevice,
1124 touchPoints);
1125+
1126+ tracepoint(qtmirserver, touchEventDispatch_end, timestamp.count());
1127 }
1128
1129 void QtEventFeeder::start()
1130
1131=== modified file 'src/platforms/mirserver/tracepoints.tp'
1132--- src/platforms/mirserver/tracepoints.tp 2014-09-22 18:06:58 +0000
1133+++ src/platforms/mirserver/tracepoints.tp 2015-10-14 13:06:12 +0000
1134@@ -1,3 +1,5 @@
1135+#include <stdint.h>
1136+
1137 TRACEPOINT_EVENT(qtmirserver, starting, TP_ARGS(0), TP_FIELDS())
1138 TRACEPOINT_EVENT(qtmirserver, stopping, TP_ARGS(0), TP_FIELDS())
1139 TRACEPOINT_EVENT(qtmirserver, surfaceCreated, TP_ARGS(0), TP_FIELDS())
1140@@ -8,3 +10,6 @@
1141
1142 TRACEPOINT_EVENT(qtmirserver, surfacePlacementStart, TP_ARGS(0), TP_FIELDS())
1143 TRACEPOINT_EVENT(qtmirserver, surfacePlacementEnd, TP_ARGS(0), TP_FIELDS())
1144+
1145+TRACEPOINT_EVENT(qtmirserver, touchEventDispatch_start, TP_ARGS(int64_t, event_time), TP_FIELDS(ctf_integer(int64_t, event_time, event_time)))
1146+TRACEPOINT_EVENT(qtmirserver, touchEventDispatch_end, TP_ARGS(int64_t, event_time), TP_FIELDS(ctf_integer(int64_t, event_time, event_time)))
1147
1148=== modified file 'tests/modules/General/CMakeLists.txt'
1149--- tests/modules/General/CMakeLists.txt 2014-12-03 08:56:35 +0000
1150+++ tests/modules/General/CMakeLists.txt 2015-10-14 13:06:12 +0000
1151@@ -1,16 +1,13 @@
1152 set(
1153 GENERAL_TEST_SOURCES
1154 objectlistmodel_test.cpp
1155- ${CMAKE_SOURCE_DIR}/src/common/debughelpers.cpp
1156+ timestamp_test.cpp
1157+ ${CMAKE_SOURCE_DIR}/src/common/timestamp.cpp
1158 )
1159
1160 include_directories(
1161- ${CMAKE_SOURCE_DIR}/src/platforms/mirserver
1162 ${CMAKE_SOURCE_DIR}/src/common
1163 ${CMAKE_SOURCE_DIR}/src/modules
1164- ${CMAKE_SOURCE_DIR}/tests/modules/common
1165- ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
1166- ${MIRSERVER_INCLUDE_DIRS}
1167 )
1168
1169 add_executable(general_test ${GENERAL_TEST_SOURCES})
1170@@ -18,9 +15,6 @@
1171 target_link_libraries(
1172 general_test
1173
1174- qpa-mirserver
1175- unityapplicationplugin
1176-
1177 Qt5::Gui
1178
1179 ${GTEST_BOTH_LIBRARIES}
1180
1181=== added file 'tests/modules/General/timestamp_test.cpp'
1182--- tests/modules/General/timestamp_test.cpp 1970-01-01 00:00:00 +0000
1183+++ tests/modules/General/timestamp_test.cpp 2015-10-14 13:06:12 +0000
1184@@ -0,0 +1,72 @@
1185+/*
1186+ * Copyright (C) 2014-2015 Canonical, Ltd.
1187+ *
1188+ * This program is free software: you can redistribute it and/or modify it under
1189+ * the terms of the GNU Lesser General Public License version 3, as published by
1190+ * the Free Software Foundation.
1191+ *
1192+ * This program is distributed in the hope that it will be useful, but WITHOUT
1193+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1194+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1195+ * Lesser General Public License for more details.
1196+ *
1197+ * You should have received a copy of the GNU Lesser General Public License
1198+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1199+ */
1200+
1201+#include "timestamp.h"
1202+
1203+#include <gtest/gtest.h>
1204+#include <gmock/gmock.h>
1205+
1206+#include <QCoreApplication>
1207+#include <QDebug>
1208+
1209+using namespace qtmir;
1210+
1211+TEST(TimestampTest, TestCompressAndUncompress)
1212+{
1213+ using namespace testing;
1214+
1215+ int argc = 0;
1216+ QCoreApplication app(argc, NULL);
1217+
1218+ std::chrono::time_point<std::chrono::system_clock> now;
1219+ now = std::chrono::system_clock::now();
1220+ auto original_timestamp = now.time_since_epoch();
1221+
1222+ std::chrono::nanoseconds addToTimestamp(0);
1223+ for (int i = 0; i < 100; i++) {
1224+ auto timestamp = original_timestamp + addToTimestamp;
1225+
1226+ ulong compressedTimestamp = qtmir::compressTimestamp<ulong>(timestamp);
1227+
1228+ EXPECT_EQ(addToTimestamp.count(), compressedTimestamp);
1229+ EXPECT_EQ(qtmir::uncompressTimestamp<ulong>(compressedTimestamp), timestamp);
1230+
1231+ addToTimestamp += std::chrono::milliseconds(1);
1232+ }
1233+}
1234+
1235+TEST(TimestampTest, TestOverflowWhenExceeding32bitCompression)
1236+{
1237+ using namespace testing;
1238+
1239+ int argc = 0;
1240+ QCoreApplication app(argc, NULL);
1241+
1242+ std::chrono::time_point<std::chrono::system_clock> now;
1243+ now = std::chrono::system_clock::now();
1244+ auto timestamp = now.time_since_epoch();
1245+
1246+ // Do first compression. This will result in qield of 0 as seen in TestCompressUncompress
1247+ quint32 compressedTimestamp = qtmir::compressTimestamp<quint32>(timestamp);
1248+
1249+ // Add the quint32 limit +1 to get an overflow when we compress the timestamp
1250+ timestamp += std::chrono::nanoseconds(std::numeric_limits<quint32>::max()) + std::chrono::nanoseconds(1);
1251+ compressedTimestamp = qtmir::compressTimestamp<quint32>(timestamp);
1252+
1253+ EXPECT_EQ(0, compressedTimestamp);
1254+ // ensure the uncompression will yields the original timestamp
1255+ EXPECT_EQ(qtmir::uncompressTimestamp<quint32>(compressedTimestamp), timestamp);
1256+}
1257\ No newline at end of file

Subscribers

People subscribed via source and target branches