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
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2014-09-16 08:33:51 +0000
+++ CMakeLists.txt 2014-09-26 09:05:45 +0000
@@ -28,7 +28,7 @@
2828
29set(MIR_VERSION_MAJOR 0)29set(MIR_VERSION_MAJOR 0)
30set(MIR_VERSION_MINOR 7) # This should change at least with every MIRSERVER_ABI30set(MIR_VERSION_MINOR 7) # This should change at least with every MIRSERVER_ABI
31set(MIR_VERSION_PATCH 3)31set(MIR_VERSION_PATCH 4)
3232
33set(MIR_VERSION ${MIR_VERSION_MAJOR}.${MIR_VERSION_MINOR}.${MIR_VERSION_PATCH})33set(MIR_VERSION ${MIR_VERSION_MAJOR}.${MIR_VERSION_MINOR}.${MIR_VERSION_PATCH})
3434
3535
=== modified file 'debian/changelog'
--- debian/changelog 2014-09-18 14:54:51 +0000
+++ debian/changelog 2014-09-26 09:05:45 +0000
@@ -1,3 +1,14 @@
1mir (0.7.4-0ubuntu1) UNRELEASED; urgency=medium
2
3 * New upstream release 0.7.4 (https://launchpad.net/mir/+milestone/0.7.4)
4 - Bug fixes
5 . Fix jerky/stuttering event delivery that could happen inside
6 nested servers sometimes (LP: #1372300)
7 - Enhancement
8 . Reduced touch input lag slightly for 60Hz displays.
9
10 -- Daniel van Vugt <daniel.van.vugt@canonical.com> Thu, 25 Sep 2014 16:18:10 +0800
11
1mir (0.7.3+14.10.20140918.1-0ubuntu1) utopic; urgency=medium12mir (0.7.3+14.10.20140918.1-0ubuntu1) utopic; urgency=medium
213
3 [ Andreas Pokorny ]14 [ Andreas Pokorny ]
415
=== modified file 'src/shared/input/android/android_input_receiver.cpp'
--- src/shared/input/android/android_input_receiver.cpp 2014-08-29 13:25:18 +0000
+++ src/shared/input/android/android_input_receiver.cpp 2014-09-26 09:05:45 +0000
@@ -90,18 +90,26 @@
90 * consumeBatches = true, so as to ensure the "cooked" event rate that90 * consumeBatches = true, so as to ensure the "cooked" event rate that
91 * clients experience is at least the minimum of event_rate_hz91 * clients experience is at least the minimum of event_rate_hz
92 * and the raw device event rate.92 * and the raw device event rate.
93 * frame_time = A regular interval of 60Hz. This provides a virtual frame93 * frame_time = A regular interval. This provides a virtual frame
94 * interval during which InputConsumer will collect raw events,94 * interval during which InputConsumer will collect raw events,
95 * resample them and emit a "cooked" event back to us at least every95 * resample them and emit a "cooked" event back to us at roughly every
96 * 60th of a second. "cooked" events are both smoothed and96 * 60th of a second. "cooked" events are both smoothed and
97 * extrapolated/predicted into the future (for tool=finger) giving the97 * extrapolated/predicted into the future (for tool=finger) giving the
98 * appearance of lower latency. Getting a real frame time from the98 * appearance of lower latency. Getting a real frame time from the
99 * graphics logic (which is messy) does not appear to be necessary to99 * graphics logic (which is messy) does not appear to be necessary to
100 * gain significant benefit.100 * gain significant benefit.
101 *
102 * Note event_rate_hz is only 55Hz. This allows rendering to catch up and
103 * overtake the event rate every ~12th frame (200ms) on a 60Hz display.
104 * Thus on every 12th+1 frame, there will be zero buffer lag in responding
105 * to the cooked input event we have given the client.
106 * This phase control is useful as it eliminates the one frame of lag you
107 * would otherwise never catch up to if the event rate was exactly the same
108 * as the display refresh rate.
101 */109 */
102110
103 nsecs_t const now = android_clock(SYSTEM_TIME_MONOTONIC);111 nsecs_t const now = android_clock(SYSTEM_TIME_MONOTONIC);
104 int const event_rate_hz = 60;112 int const event_rate_hz = 55;
105 nsecs_t const one_frame = 1000000000ULL / event_rate_hz;113 nsecs_t const one_frame = 1000000000ULL / event_rate_hz;
106 nsecs_t frame_time = (now / one_frame) * one_frame;114 nsecs_t frame_time = (now / one_frame) * one_frame;
107115
@@ -141,15 +149,11 @@
141 }149 }
142 else if (input_consumer->hasPendingBatch())150 else if (input_consumer->hasPendingBatch())
143 {151 {
144 /*152 // When in constant motion we will usually "hasPendingBatch".
145 * A batch is pending and must be completed or else the client could153 // But the batch won't get flushed until the next frame interval,
146 * be starved of events. But don't hurry. A continuous motion gesture154 // so be sure to use a non-zero sleep time to avoid spinning the CPU
147 * will wake us up much sooner than 50ms. This timeout is only reached155 // for the whole interval...
148 * in the case that motion has ended (fingers lifted).156 reduced_timeout = std::chrono::milliseconds(1);
149 */
150 std::chrono::milliseconds const motion_idle_timeout(50);
151 if (timeout.count() < 0 || timeout > motion_idle_timeout)
152 reduced_timeout = motion_idle_timeout;
153 }157 }
154158
155 auto result = looper->pollOnce(reduced_timeout.count());159 auto result = looper->pollOnce(reduced_timeout.count());
156160
=== modified file 'tests/unit-tests/client/input/test_android_input_receiver.cpp'
--- tests/unit-tests/client/input/test_android_input_receiver.cpp 2014-08-29 13:25:18 +0000
+++ tests/unit-tests/client/input/test_android_input_receiver.cpp 2014-09-26 09:05:45 +0000
@@ -204,6 +204,54 @@
204 EXPECT_FALSE(receiver.next_event(std::chrono::milliseconds(1), ev)); // Minimal timeout needed for valgrind204 EXPECT_FALSE(receiver.next_event(std::chrono::milliseconds(1), ev)); // Minimal timeout needed for valgrind
205}205}
206206
207TEST_F(AndroidInputReceiverSetup, slow_raw_input_doesnt_cause_frameskipping)
208{ // Regression test for LP: #1372300
209 using namespace testing;
210 using namespace std::chrono;
211
212 nsecs_t t = 0;
213
214 mircva::InputReceiver receiver(
215 client_fd, std::make_shared<mircv::NullInputReceiverReport>(),
216 [&t](int) { return t; }
217 );
218 TestingInputProducer producer(server_fd);
219
220 nsecs_t const one_millisecond = 1000000ULL;
221 nsecs_t const one_second = 1000 * one_millisecond;
222 nsecs_t const one_frame = one_second / 60;
223
224 MirEvent ev;
225
226 producer.produce_a_motion_event(123, 456, t);
227 producer.produce_a_key_event();
228 flush_channels();
229
230 std::chrono::milliseconds const max_timeout(1000);
231
232 // Key events don't get resampled. Will be reported first.
233 ASSERT_TRUE(receiver.next_event(max_timeout, ev));
234 ASSERT_EQ(mir_event_type_key, ev.type);
235
236 // The motion is still too new. Won't be reported yet, but is batched.
237 auto start = high_resolution_clock::now();
238 ASSERT_FALSE(receiver.next_event(max_timeout, ev));
239 auto end = high_resolution_clock::now();
240 auto duration = end - start;
241
242 // Verify we timed out in under a frame (LP: #1372300)
243 // Sadly using real time as droidinput::Looper doesn't use a mocked clock.
244 ASSERT_LT(duration_cast<nanoseconds>(duration).count(), one_frame);
245
246 // Verify we don't use all the CPU by not sleeping (LP: #1373809)
247 EXPECT_GT(duration_cast<nanoseconds>(duration).count(), one_millisecond);
248
249 // But later in a frame or so, the motion will be reported:
250 t += 2 * one_frame; // Account for the new slower 55Hz event rate
251 ASSERT_TRUE(receiver.next_event(max_timeout, ev));
252 ASSERT_EQ(mir_event_type_motion, ev.type);
253}
254
207TEST_F(AndroidInputReceiverSetup, rendering_does_not_lag_behind_input)255TEST_F(AndroidInputReceiverSetup, rendering_does_not_lag_behind_input)
208{256{
209 using namespace testing;257 using namespace testing;
@@ -252,3 +300,62 @@
252 EXPECT_THAT(average_lag_milliseconds, Le(1));300 EXPECT_THAT(average_lag_milliseconds, Le(1));
253}301}
254302
303TEST_F(AndroidInputReceiverSetup, input_comes_in_phase_with_rendering)
304{
305 using namespace testing;
306
307 nsecs_t t = 0;
308
309 mircva::InputReceiver receiver(
310 client_fd, std::make_shared<mircv::NullInputReceiverReport>(),
311 [&t](int) { return t; }
312 );
313 TestingInputProducer producer(server_fd);
314
315 nsecs_t const one_millisecond = 1000000ULL;
316 nsecs_t const one_second = 1000 * one_millisecond;
317 nsecs_t const device_sample_interval = one_second / 125;
318 nsecs_t const frame_interval = one_second / 60;
319 nsecs_t const gesture_duration = 10 * one_second;
320
321 nsecs_t last_produced = 0, last_consumed = 0, last_rendered = 0;
322 nsecs_t last_in_phase = 0;
323
324 for (t = 0; t < gesture_duration; t += one_millisecond)
325 {
326 if (!t || t >= (last_produced + device_sample_interval))
327 {
328 last_produced = t;
329 float a = t * M_PI / 1000000.0f;
330 float x = 500.0f * sinf(a);
331 float y = 1000.0f * cosf(a);
332 producer.produce_a_motion_event(x, y, t);
333 flush_channels();
334 }
335
336 MirEvent ev;
337 if (receiver.next_event(std::chrono::milliseconds(0), ev))
338 last_consumed = t;
339
340 if (t >= (last_rendered + frame_interval))
341 {
342 last_rendered = t;
343
344 if (last_consumed)
345 {
346 auto lag = last_rendered - last_consumed;
347
348 // How often does vsync drift in phase (very close) with the
349 // time that we emitted/consumed input events?
350 if (lag < 4*one_millisecond)
351 last_in_phase = t;
352 last_consumed = 0;
353 }
354 }
355
356 // Verify input and vsync come into phase at least a few times every
357 // second (if not always). This ensure visible lag is minimized.
358 ASSERT_GE(250*one_millisecond, (t - last_in_phase));
359 }
360}
361

Subscribers

People subscribed via source and target branches