Mir

Merge lp:~vanvugt/mir/fix-1665802 into lp:mir

Proposed by Daniel van Vugt
Status: Rejected
Rejected by: Daniel van Vugt
Proposed branch: lp:~vanvugt/mir/fix-1665802
Merge into: lp:mir
Diff against target: 163 lines (+105/-5)
3 files modified
src/client/frame_clock.cpp (+10/-1)
src/client/frame_clock.h (+1/-0)
tests/unit-tests/client/test_frame_clock.cpp (+94/-4)
To merge this branch: bzr merge lp:~vanvugt/mir/fix-1665802
Reviewer Review Type Date Requested Status
Daniel van Vugt Disapprove
Review via email: mp+318565@code.launchpad.net

Commit message

Proof of concept fix. This might be enough, or maybe not.

To post a comment you must log in.
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

I'm hesitant to endorse this still, because it reduces our ability to catch up in almost-full-framerate scenarios across the board (instead opting to sleep and hope that's enough to give the server more GPU time). I feel this is something we shouldn't be "fixing" because it's not a Mir bug and more important devices (like everything other than Freedreno) could suffer slightly if we land this.

I'll look at other workarounds still that don't make the same sacrifice (although may be much less clean in code than this).

review: Disapprove

Unmerged revisions

4068. By Daniel van Vugt

Tidy up the test

4067. By Daniel van Vugt

Correct and clean up the fix. Tests all pass

4066. By Daniel van Vugt

Expand the test more to fail due to the latching

4065. By Daniel van Vugt

Enable the fix (and debug output). Tests pass but the flag is latching.
Needs a better test

4064. By Daniel van Vugt

Disable the fix and rewrite the regression test

4063. By Daniel van Vugt

Merge latest trunk

4062. By Daniel van Vugt

Expand the test and leave in debug output

4061. By Daniel van Vugt

Fix the fix. Now all tests pass.

4060. By Daniel van Vugt

Try to fix the bug, and bad expectations in older tests

4059. By Daniel van Vugt

Write a detailed regression test (presently failing)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/client/frame_clock.cpp'
2--- src/client/frame_clock.cpp 2016-11-29 08:00:45 +0000
3+++ src/client/frame_clock.cpp 2017-03-01 04:44:28 +0000
4@@ -103,7 +103,7 @@
5 * Crucially this is not required on most frames, so that even if it is
6 * implemented as a round trip to the server, that won't happen often.
7 */
8- if (missed_frames > 1 || config_changed)
9+ if (missed_frames > 1 || config_changed || !throttled)
10 {
11 lock.unlock();
12 auto const server_frame = resync_callback();
13@@ -152,5 +152,14 @@
14 target = target + missed_frames * period;
15 }
16
17+ /*
18+ * Often we'll be targeting the past in order to catch up after a previous
19+ * slow frame. That's correct, but try to not do it multiple frames in a
20+ * row, since that might saturate a low-power GPU, which might then starve
21+ * the server of GPU time if the driver lacks adequate fair scheduling
22+ * (e.g. Freedreno/Gallium LP: #1665802)
23+ */
24+ throttled = target > now;
25+
26 return target;
27 }
28
29=== modified file 'src/client/frame_clock.h'
30--- src/client/frame_clock.h 2016-12-15 08:15:54 +0000
31+++ src/client/frame_clock.h 2017-03-01 04:44:28 +0000
32@@ -66,6 +66,7 @@
33 mutable bool config_changed;
34 std::chrono::nanoseconds period;
35 ResyncCallback resync_callback;
36+ mutable bool throttled = true;
37 };
38
39 }} // namespace mir::client
40
41=== modified file 'tests/unit-tests/client/test_frame_clock.cpp'
42--- tests/unit-tests/client/test_frame_clock.cpp 2017-02-28 13:25:18 +0000
43+++ tests/unit-tests/client/test_frame_clock.cpp 2017-03-01 04:44:28 +0000
44@@ -135,9 +135,9 @@
45 fake_sleep_for(one_frame * 7 / 6); // long render time; over a frame
46
47 auto d = clock.next_frame_after(c);
48- EXPECT_EQ(one_frame, d - c);
49+ EXPECT_EQ(2*one_frame, d - c);
50
51- EXPECT_LT(d, now);
52+ EXPECT_GT(d, now);
53
54 fake_sleep_until(d);
55 fake_sleep_for(one_frame/4); // short render time
56@@ -392,8 +392,9 @@
57 fake_sleep_for(one_frame * 8 / 5); // Render time: 1.6 frames
58
59 auto d = clock.next_frame_after(c);
60- EXPECT_EQ(2*one_frame, d - c); // One frame skipped
61- EXPECT_LT(d, now); // Targets the past, catching up again
62+ EXPECT_EQ(3*one_frame, d - c); // Two frames skipped (since c targeted
63+ // the past but d the future)
64+ EXPECT_GT(d, now); // Targets the future
65
66 fake_sleep_until(d);
67 fake_sleep_for(one_frame/4); // Short render time, immediate recovery
68@@ -403,6 +404,95 @@
69 EXPECT_GT(e, now); // And targeting the future
70 }
71
72+TEST_F(FrameClockTest, does_not_continuously_target_the_past)
73+{ // Regression test for LP: #1665802
74+ FrameClock clock(with_fake_time);
75+ auto& now = fake_time[CLOCK_MONOTONIC];
76+ clock.set_period(one_frame);
77+
78+ auto a = clock.next_frame_after(PosixTimestamp());
79+ fake_sleep_until(a);
80+ fake_sleep_for(one_frame / 9); // Start with a fast frame
81+
82+ auto b = clock.next_frame_after(a);
83+ ASSERT_GT(b, now);
84+
85+ fake_sleep_until(b);
86+ fake_sleep_for(one_frame * 3 / 2); // Render time: 1.5 frames
87+
88+ auto c = clock.next_frame_after(b);
89+ ASSERT_LT(c, now); // Targets the past, catching up
90+ ASSERT_EQ(one_frame, c - b); // No frame skipped
91+
92+ fake_sleep_until(c);
93+
94+ bool targeted_past = false;
95+
96+ auto d = c;
97+ for (int slow_frame = 0; slow_frame < 10; ++slow_frame)
98+ {
99+ fake_sleep_for(one_frame * 8 / 5); // Render time: 1.6 frames
100+ d = clock.next_frame_after(c);
101+
102+ bool targets_past = d < now;
103+ if (targeted_past)
104+ ASSERT_FALSE(targets_past);
105+ targeted_past = targets_past;
106+
107+ // May target the future, but not too far in the future:
108+ ASSERT_LE(d, now+one_frame);
109+ c = d;
110+ fake_sleep_until(d);
111+ }
112+
113+ fake_sleep_for(one_frame/4); // Short render time, immediate recovery
114+
115+ auto e = clock.next_frame_after(d);
116+ ASSERT_EQ(one_frame, e - d); // No frame skipped. We have recovered.
117+ ASSERT_GT(e, now); // And targeting the future
118+ targeted_past = false;
119+
120+ /*
121+ * In the original bug LP: #1665802 it appeared that the GPU could only
122+ * sustain quarter frame rate. So test that scenario too...
123+ */
124+ auto f = e;
125+ for (int slower_frame = 0; slower_frame < 10; ++slower_frame)
126+ {
127+ fake_sleep_for(one_frame * 17 / 5); // Render time: 3.4 frames
128+ f = clock.next_frame_after(e);
129+
130+ bool targets_past = f < now;
131+ if (targeted_past)
132+ ASSERT_FALSE(targets_past);
133+ targeted_past = targets_past;
134+
135+ // May target the future, but not too far in the future:
136+ ASSERT_LE(f, now+one_frame); // But not too far in the future
137+ e = f;
138+ fake_sleep_until(f);
139+ }
140+
141+ auto g = f;
142+
143+ for (int recovered_frame = 0; recovered_frame < 10; ++recovered_frame)
144+ {
145+ fake_sleep_for(one_frame / 10);
146+ g = clock.next_frame_after(f);
147+ ASSERT_GT(g, now);
148+ ASSERT_LE(g, now+one_frame);
149+ ASSERT_EQ(one_frame, g - f);
150+ f = g;
151+ fake_sleep_until(g);
152+ }
153+
154+ fake_sleep_for(one_frame * 3 / 2); // Render time: 1.5 frames
155+
156+ auto h = clock.next_frame_after(g);
157+ ASSERT_LT(h, now); // Targets the past, catching up
158+ ASSERT_EQ(one_frame, h - g); // No frame skipped
159+}
160+
161 TEST_F(FrameClockTest, nesting_adds_zero_lag)
162 {
163 FrameClock inner(with_fake_time);

Subscribers

People subscribed via source and target branches