Merge lp:~unity-team/qtmir/touch_tracing into lp:qtmir
- touch_tracing
- Merge into trunk
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 |
Related bugs: |
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
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:351
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
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/
I don't understand what this is useful for. Isn't the "qml" binary sufficient?
=== added file 'demos/
same here.
=== added file 'src/common/
file name "utils" as vague as possible:) Can you be more specific?
+++ src/common/
+qint64 resetStartTime(
+qint64 getStartTime_
please stick into an anonymous namespace
The indenting is inconsistent in the whole file.
+ QCoreApplicatio
I suspect a static variable would do the job just as well.
+ qint64 startTime = getStartTime_
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 QWindowSystemIn
ulong timestamp, const QPointF & local, const QPointF & global,
Qt:
Qt:
we could make our own handle*Event() methods. In the end, the above method simply does:
QWindowSystemIn
new QWindowSystemIn
QWindowSystemIn
If we subclassed QWindowSystemIn
To be investigated. For now, your approach is good.
====
std::chrono:
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/
=== modified file 'src/platforms/
Typos:
touchEventDispt
touchEventDispt
and these propagate throughout the whole of mirserver.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:352
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:370
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:372
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:373
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
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/
> I don't understand what this is useful for. Isn't the "qml" binary sufficient?
>
> === added file 'demos/
> 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/
> file name "utils" as vague as possible:) Can you be more specific?
timestamp.h
>
> +++ src/common/
> +qint64 resetStartTime(
> +qint64 getStartTime_
> please stick into an anonymous namespace
>
> The indenting is inconsistent in the whole file.
>
> + QCoreApplicatio
> 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_
> 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 QWindowSystemIn
> ulong timestamp, const QPointF & local, const QPointF & global,
> Qt::MouseButtons b, Qt::KeyboardMod
> Qt::MouseEventS
>
> we could make our own handle*Event() methods. In the end, the above method
> simply does:
>
> QWindowSystemIn
> new QWindowSystemIn
> QWindowSystemIn
> QHighDpi:
> QHighDpi:
>
> QWindowSystemIn
>
> If we subclassed QWindowSystemIn
> 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:
> 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/
Done. in the header.
>
>
> === modified file 'src/platforms/
> Typos:
> touchEventDispt
> touchEventDispt
> and these propagate throughout the whole of mirserver.
Fixed.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:374
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:375
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:376
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal | # |
+$ cd benchmarks
+$ sudo python3 touch_event_
I roughly followed this, but since I saw this script was installed by qtmir-tests in /usr/share/
sudo python3 /usr/share/
but it doesn't fully work:
file://
- 377. By Nick Dedekind
-
newline
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:376
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:377
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 378. By Nick Dedekind
-
merged with trunk
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:378
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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.
- 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
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 |
PASSED: Continuous integration, rev:350 jenkins. qa.ubuntu. com/job/ qtmir-ci/ 343/ jenkins. qa.ubuntu. com/job/ qtmir-wily- amd64-ci/ 76 jenkins. qa.ubuntu. com/job/ qtmir-wily- armhf-ci/ 76 jenkins. qa.ubuntu. com/job/ qtmir-wily- armhf-ci/ 76/artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/qtmir- ci/343/ rebuild
http://