Mir

Merge lp:~vanvugt/mir/fix-motion-bugs-0.7 into lp:mir/0.7

Proposed by Daniel van Vugt
Status: Merged
Approved by: Daniel van Vugt
Approved revision: no longer in the source branch.
Merged at revision: 1915
Proposed branch: lp:~vanvugt/mir/fix-motion-bugs-0.7
Merge into: lp:mir/0.7
Diff against target: 208 lines (+135/-13)
4 files modified
CMakeLists.txt (+1/-1)
debian/changelog (+11/-0)
src/shared/input/android/android_input_receiver.cpp (+16/-12)
tests/unit-tests/client/input/test_android_input_receiver.cpp (+107/-0)
To merge this branch: bzr merge lp:~vanvugt/mir/fix-motion-bugs-0.7
Reviewer Review Type Date Requested Status
Cemil Azizoglu (community) Abstain
Kevin DuBois (community) Approve
Alexandros Frantzis (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+236066@code.launchpad.net

Commit message

Backport improvements to touch (reduced latency and increased smoothness).

Description of the change

Tested with krillin image 67. Seems to work well for Unity8 and apps.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Oh, we just killed the build host with the compiler. Retry...
virtual memory exhausted: Cannot allocate memory

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

review: Approve
Revision history for this message
Kevin DuBois (kdub) wrote :

lgtm

review: Approve
Revision history for this message
Cemil Azizoglu (cemil-azizoglu) wrote :

We'll be releasing 0.8 soon to rtm and ubuntu. So this is really unnecessary. It doesn't hurt, though, if you want it on the 0.7 branch. (Then again, we won't be landing 0.7 on rtm anymore, unless you wanna do that yourself).

review: Abstain
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

I think this is still important. 0.8 has no chance of getting into RTM updates, whereas 0.7 (included this proposal) does.

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 2014-09-16 08:33:51 +0000
3+++ CMakeLists.txt 2014-09-26 09:05:45 +0000
4@@ -28,7 +28,7 @@
5
6 set(MIR_VERSION_MAJOR 0)
7 set(MIR_VERSION_MINOR 7) # This should change at least with every MIRSERVER_ABI
8-set(MIR_VERSION_PATCH 3)
9+set(MIR_VERSION_PATCH 4)
10
11 set(MIR_VERSION ${MIR_VERSION_MAJOR}.${MIR_VERSION_MINOR}.${MIR_VERSION_PATCH})
12
13
14=== modified file 'debian/changelog'
15--- debian/changelog 2014-09-18 14:54:51 +0000
16+++ debian/changelog 2014-09-26 09:05:45 +0000
17@@ -1,3 +1,14 @@
18+mir (0.7.4-0ubuntu1) UNRELEASED; urgency=medium
19+
20+ * New upstream release 0.7.4 (https://launchpad.net/mir/+milestone/0.7.4)
21+ - Bug fixes
22+ . Fix jerky/stuttering event delivery that could happen inside
23+ nested servers sometimes (LP: #1372300)
24+ - Enhancement
25+ . Reduced touch input lag slightly for 60Hz displays.
26+
27+ -- Daniel van Vugt <daniel.van.vugt@canonical.com> Thu, 25 Sep 2014 16:18:10 +0800
28+
29 mir (0.7.3+14.10.20140918.1-0ubuntu1) utopic; urgency=medium
30
31 [ Andreas Pokorny ]
32
33=== modified file 'src/shared/input/android/android_input_receiver.cpp'
34--- src/shared/input/android/android_input_receiver.cpp 2014-08-29 13:25:18 +0000
35+++ src/shared/input/android/android_input_receiver.cpp 2014-09-26 09:05:45 +0000
36@@ -90,18 +90,26 @@
37 * consumeBatches = true, so as to ensure the "cooked" event rate that
38 * clients experience is at least the minimum of event_rate_hz
39 * and the raw device event rate.
40- * frame_time = A regular interval of 60Hz. This provides a virtual frame
41+ * frame_time = A regular interval. This provides a virtual frame
42 * interval during which InputConsumer will collect raw events,
43- * resample them and emit a "cooked" event back to us at least every
44+ * resample them and emit a "cooked" event back to us at roughly every
45 * 60th of a second. "cooked" events are both smoothed and
46 * extrapolated/predicted into the future (for tool=finger) giving the
47 * appearance of lower latency. Getting a real frame time from the
48 * graphics logic (which is messy) does not appear to be necessary to
49 * gain significant benefit.
50+ *
51+ * Note event_rate_hz is only 55Hz. This allows rendering to catch up and
52+ * overtake the event rate every ~12th frame (200ms) on a 60Hz display.
53+ * Thus on every 12th+1 frame, there will be zero buffer lag in responding
54+ * to the cooked input event we have given the client.
55+ * This phase control is useful as it eliminates the one frame of lag you
56+ * would otherwise never catch up to if the event rate was exactly the same
57+ * as the display refresh rate.
58 */
59
60 nsecs_t const now = android_clock(SYSTEM_TIME_MONOTONIC);
61- int const event_rate_hz = 60;
62+ int const event_rate_hz = 55;
63 nsecs_t const one_frame = 1000000000ULL / event_rate_hz;
64 nsecs_t frame_time = (now / one_frame) * one_frame;
65
66@@ -141,15 +149,11 @@
67 }
68 else if (input_consumer->hasPendingBatch())
69 {
70- /*
71- * A batch is pending and must be completed or else the client could
72- * be starved of events. But don't hurry. A continuous motion gesture
73- * will wake us up much sooner than 50ms. This timeout is only reached
74- * in the case that motion has ended (fingers lifted).
75- */
76- std::chrono::milliseconds const motion_idle_timeout(50);
77- if (timeout.count() < 0 || timeout > motion_idle_timeout)
78- reduced_timeout = motion_idle_timeout;
79+ // When in constant motion we will usually "hasPendingBatch".
80+ // But the batch won't get flushed until the next frame interval,
81+ // so be sure to use a non-zero sleep time to avoid spinning the CPU
82+ // for the whole interval...
83+ reduced_timeout = std::chrono::milliseconds(1);
84 }
85
86 auto result = looper->pollOnce(reduced_timeout.count());
87
88=== modified file 'tests/unit-tests/client/input/test_android_input_receiver.cpp'
89--- tests/unit-tests/client/input/test_android_input_receiver.cpp 2014-08-29 13:25:18 +0000
90+++ tests/unit-tests/client/input/test_android_input_receiver.cpp 2014-09-26 09:05:45 +0000
91@@ -204,6 +204,54 @@
92 EXPECT_FALSE(receiver.next_event(std::chrono::milliseconds(1), ev)); // Minimal timeout needed for valgrind
93 }
94
95+TEST_F(AndroidInputReceiverSetup, slow_raw_input_doesnt_cause_frameskipping)
96+{ // Regression test for LP: #1372300
97+ using namespace testing;
98+ using namespace std::chrono;
99+
100+ nsecs_t t = 0;
101+
102+ mircva::InputReceiver receiver(
103+ client_fd, std::make_shared<mircv::NullInputReceiverReport>(),
104+ [&t](int) { return t; }
105+ );
106+ TestingInputProducer producer(server_fd);
107+
108+ nsecs_t const one_millisecond = 1000000ULL;
109+ nsecs_t const one_second = 1000 * one_millisecond;
110+ nsecs_t const one_frame = one_second / 60;
111+
112+ MirEvent ev;
113+
114+ producer.produce_a_motion_event(123, 456, t);
115+ producer.produce_a_key_event();
116+ flush_channels();
117+
118+ std::chrono::milliseconds const max_timeout(1000);
119+
120+ // Key events don't get resampled. Will be reported first.
121+ ASSERT_TRUE(receiver.next_event(max_timeout, ev));
122+ ASSERT_EQ(mir_event_type_key, ev.type);
123+
124+ // The motion is still too new. Won't be reported yet, but is batched.
125+ auto start = high_resolution_clock::now();
126+ ASSERT_FALSE(receiver.next_event(max_timeout, ev));
127+ auto end = high_resolution_clock::now();
128+ auto duration = end - start;
129+
130+ // Verify we timed out in under a frame (LP: #1372300)
131+ // Sadly using real time as droidinput::Looper doesn't use a mocked clock.
132+ ASSERT_LT(duration_cast<nanoseconds>(duration).count(), one_frame);
133+
134+ // Verify we don't use all the CPU by not sleeping (LP: #1373809)
135+ EXPECT_GT(duration_cast<nanoseconds>(duration).count(), one_millisecond);
136+
137+ // But later in a frame or so, the motion will be reported:
138+ t += 2 * one_frame; // Account for the new slower 55Hz event rate
139+ ASSERT_TRUE(receiver.next_event(max_timeout, ev));
140+ ASSERT_EQ(mir_event_type_motion, ev.type);
141+}
142+
143 TEST_F(AndroidInputReceiverSetup, rendering_does_not_lag_behind_input)
144 {
145 using namespace testing;
146@@ -252,3 +300,62 @@
147 EXPECT_THAT(average_lag_milliseconds, Le(1));
148 }
149
150+TEST_F(AndroidInputReceiverSetup, input_comes_in_phase_with_rendering)
151+{
152+ using namespace testing;
153+
154+ nsecs_t t = 0;
155+
156+ mircva::InputReceiver receiver(
157+ client_fd, std::make_shared<mircv::NullInputReceiverReport>(),
158+ [&t](int) { return t; }
159+ );
160+ TestingInputProducer producer(server_fd);
161+
162+ nsecs_t const one_millisecond = 1000000ULL;
163+ nsecs_t const one_second = 1000 * one_millisecond;
164+ nsecs_t const device_sample_interval = one_second / 125;
165+ nsecs_t const frame_interval = one_second / 60;
166+ nsecs_t const gesture_duration = 10 * one_second;
167+
168+ nsecs_t last_produced = 0, last_consumed = 0, last_rendered = 0;
169+ nsecs_t last_in_phase = 0;
170+
171+ for (t = 0; t < gesture_duration; t += one_millisecond)
172+ {
173+ if (!t || t >= (last_produced + device_sample_interval))
174+ {
175+ last_produced = t;
176+ float a = t * M_PI / 1000000.0f;
177+ float x = 500.0f * sinf(a);
178+ float y = 1000.0f * cosf(a);
179+ producer.produce_a_motion_event(x, y, t);
180+ flush_channels();
181+ }
182+
183+ MirEvent ev;
184+ if (receiver.next_event(std::chrono::milliseconds(0), ev))
185+ last_consumed = t;
186+
187+ if (t >= (last_rendered + frame_interval))
188+ {
189+ last_rendered = t;
190+
191+ if (last_consumed)
192+ {
193+ auto lag = last_rendered - last_consumed;
194+
195+ // How often does vsync drift in phase (very close) with the
196+ // time that we emitted/consumed input events?
197+ if (lag < 4*one_millisecond)
198+ last_in_phase = t;
199+ last_consumed = 0;
200+ }
201+ }
202+
203+ // Verify input and vsync come into phase at least a few times every
204+ // second (if not always). This ensure visible lag is minimized.
205+ ASSERT_GE(250*one_millisecond, (t - last_in_phase));
206+ }
207+}
208+

Subscribers

People subscribed via source and target branches