Mir

Merge lp:~vanvugt/mir/earlier-release-2 into lp:mir

Proposed by Daniel van Vugt
Status: Merged
Merged at revision: 2891
Proposed branch: lp:~vanvugt/mir/earlier-release-2
Merge into: lp:mir
Diff against target: 309 lines (+252/-0)
4 files modified
src/server/compositor/buffer_queue.cpp (+24/-0)
src/server/compositor/buffer_queue.h (+1/-0)
tests/integration-tests/test_buffer_scheduling.cpp (+115/-0)
tests/unit-tests/compositor/test_buffer_queue.cpp (+112/-0)
To merge this branch: bzr merge lp:~vanvugt/mir/earlier-release-2
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Alan Griffiths Needs Fixing
Kevin DuBois (community) Approve
Review via email: mp+269327@code.launchpad.net

Commit message

Reintroduce very short buffer holds so that clients can get a new
buffer to render to much sooner, and potentially appear much
smoother (LP: #1480164)

"very short" means the compositor only holds the buffer for its
render time (often 1ms or less) rather than the full frame time
(16ms).

This also helps in future work to increase concurrency as the
compositor can now better control where in its render loop the
buffer reply will occur (LP: #1395421).

Description of the change

Compositor report render time results from a relatively fast desktop running 50 instances of egltriangle:

Trunk branch: 4.0ms per frame
This branch : 1.7ms per frame

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

LGTM. I think that the tricky part (single vs multimonitor detection) comes from the fact that the MM code doesn't know how many user id's can exist, so we have to infer after-the-call (of compositor acquire). We could resolve if we handed out the ID's, and then we'd know for sure.

I think the term 'overclocking' is introducing another loaded word into the system... Its really designating a different consumption pattern with different tradeoffs. At any rate, I think I have to update the logic in the nbs multimonitor....

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

+1 to the logic, could the integration tests make more use of the scheduling system? There's some rigging already for blocking detection, and the tests will be slightly tricky as-is to refactor to get the new buffer semantics under this test.

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

All good points and they were on my mind already.

It really is a prediction algorithm, whether there's a single or multiple compositors. If we get the prediction wrong for a frame or so (second monitor hotplugged?) the client will get a buffer back a little too quickly, but that's really just one frame so not important.

Yes "overclocking" is an overloaded word here. I was looking for a single term to describe the problem and that's the best short description I've found. But it's definitely confusing and not related to physical monitor signal overclocking, which is a thing.

The new integration tests - I've used your new interfaces previously where it was clear how to do so. Sorry in some of this work it's not clear to me how to do so. I imagine some translation will happen later...

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

71 + mtd::MockFrameDroppingPolicyFactory policy_factory;
72 + mc::BufferQueue queue{nbuffers, mt::fake_shared(server_buffer_factory),
73 + properties, policy_factory};
74 + queue.allow_framedropping(false);
75 +
76 + mg::Buffer* client_buffer = nullptr;
77 + auto callback = [&](mg::Buffer* buffer)
78 + {
79 + client_buffer = buffer;
80 + };
81 +
82 + auto client_try_acquire = [&]() -> bool
83 + {
84 + queue.client_acquire(callback);
85 + return client_buffer != nullptr;
86 + };

C++11 is shiny bit this is being too clever.

Why aren't queue, client_try_acquire etc. just members of a fixture?

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

To keep the diff small. Also it's temporary and will be replaced when BufferQueue goes away, so better to not bake it into a fixture.

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/server/compositor/buffer_queue.cpp'
2--- src/server/compositor/buffer_queue.cpp 2015-08-24 10:23:21 +0000
3+++ src/server/compositor/buffer_queue.cpp 2015-08-27 10:34:15 +0000
4@@ -144,6 +144,7 @@
5 the_properties{props},
6 force_new_compositor_buffer{false},
7 callbacks_allowed{true},
8+ single_compositor{false}, // When true we can optimize performance
9 gralloc{gralloc}
10 {
11 if (nbuffers < 1)
12@@ -286,6 +287,9 @@
13 current_buffer_users.push_back(user_id);
14 }
15
16+ single_compositor = current_compositor_buffer_valid &&
17+ current_buffer_users.size() <= 1; // might be zero
18+
19 if (ready_to_composite_queue.empty())
20 {
21 use_current_buffer = true;
22@@ -357,6 +361,26 @@
23
24 if (current_compositor_buffer != buffer.get())
25 release(buffer.get(), std::move(lock));
26+ else if (!ready_to_composite_queue.empty() && single_compositor)
27+ {
28+ /*
29+ * The "early release" optimisation: Note "single_compositor" above
30+ * is because this path will break (actually just overclock) the
31+ * multi-monitor frame sync algorithm. For the moment we prefer
32+ * /perfect/ multi-monitor frame sync all the time. But if you so
33+ * choose that overclocking with multi-monitors is acceptable then
34+ * you could remove the above "single_compositor" check and get this
35+ * optimised code path with multi-monitors too...
36+ */
37+ current_compositor_buffer = pop(ready_to_composite_queue);
38+ current_buffer_users.clear();
39+ /*
40+ * As we have now caused the next compositor_acquire() to skip the
41+ * frame_deadlines_met update, we need to do it here:
42+ */
43+ ++frame_deadlines_met;
44+ release(buffer.get(), std::move(lock));
45+ }
46 }
47
48 std::shared_ptr<mg::Buffer> mc::BufferQueue::snapshot_acquire()
49
50=== modified file 'src/server/compositor/buffer_queue.h'
51--- src/server/compositor/buffer_queue.h 2015-08-21 02:02:07 +0000
52+++ src/server/compositor/buffer_queue.h 2015-08-27 10:34:15 +0000
53@@ -114,6 +114,7 @@
54 graphics::BufferProperties the_properties;
55 bool force_new_compositor_buffer;
56 bool callbacks_allowed;
57+ bool single_compositor;
58
59 std::condition_variable snapshot_released;
60 std::shared_ptr<graphics::GraphicBufferAllocator> gralloc;
61
62=== modified file 'tests/integration-tests/test_buffer_scheduling.cpp'
63--- tests/integration-tests/test_buffer_scheduling.cpp 2015-08-26 09:20:44 +0000
64+++ tests/integration-tests/test_buffer_scheduling.cpp 2015-08-27 10:34:15 +0000
65@@ -714,6 +714,121 @@
66 }
67 }
68
69+TEST_P(WithTwoOrMoreBuffers, clients_get_new_buffers_on_compositor_release)
70+{ // Regression test for LP: #1480164
71+ mtd::MockFrameDroppingPolicyFactory policy_factory;
72+ mc::BufferQueue queue{nbuffers, mt::fake_shared(server_buffer_factory),
73+ properties, policy_factory};
74+ queue.allow_framedropping(false);
75+
76+ mg::Buffer* client_buffer = nullptr;
77+ auto callback = [&](mg::Buffer* buffer)
78+ {
79+ client_buffer = buffer;
80+ };
81+
82+ auto client_try_acquire = [&]() -> bool
83+ {
84+ queue.client_acquire(callback);
85+ return client_buffer != nullptr;
86+ };
87+
88+ auto client_release = [&]()
89+ {
90+ EXPECT_TRUE(client_buffer);
91+ queue.client_release(client_buffer);
92+ client_buffer = nullptr;
93+ };
94+
95+ // Skip over the first frame. The early release optimization is too
96+ // conservative to allow it to happen right at the start (so as to
97+ // maintain correct multimonitor frame rates if required).
98+ ASSERT_TRUE(client_try_acquire());
99+ client_release();
100+ queue.compositor_release(queue.compositor_acquire(this));
101+
102+ auto onscreen = queue.compositor_acquire(this);
103+
104+ bool blocking;
105+ do
106+ {
107+ blocking = !client_try_acquire();
108+ if (!blocking)
109+ client_release();
110+ } while (!blocking);
111+
112+ for (int f = 0; f < 100; ++f)
113+ {
114+ ASSERT_FALSE(client_buffer);
115+ queue.compositor_release(onscreen);
116+ ASSERT_TRUE(client_buffer) << "frame# " << f;
117+ client_release();
118+ onscreen = queue.compositor_acquire(this);
119+ client_try_acquire();
120+ }
121+}
122+
123+TEST_P(WithTwoOrMoreBuffers, short_buffer_holds_dont_overclock_multimonitor)
124+{ // Regression test related to LP: #1480164
125+ mtd::MockFrameDroppingPolicyFactory policy_factory;
126+ mc::BufferQueue queue{nbuffers, mt::fake_shared(server_buffer_factory),
127+ properties, policy_factory};
128+ queue.allow_framedropping(false);
129+
130+ const void* const leftid = "left";
131+ const void* const rightid = "right";
132+
133+ mg::Buffer* client_buffer = nullptr;
134+ auto callback = [&](mg::Buffer* buffer)
135+ {
136+ client_buffer = buffer;
137+ };
138+
139+ auto client_try_acquire = [&]() -> bool
140+ {
141+ queue.client_acquire(callback);
142+ return client_buffer != nullptr;
143+ };
144+
145+ auto client_release = [&]()
146+ {
147+ EXPECT_TRUE(client_buffer);
148+ queue.client_release(client_buffer);
149+ client_buffer = nullptr;
150+ };
151+
152+ // Skip over the first frame. The early release optimization is too
153+ // conservative to allow it to happen right at the start (so as to
154+ // maintain correct multimonitor frame rates if required).
155+ ASSERT_TRUE(client_try_acquire());
156+ client_release();
157+ queue.compositor_release(queue.compositor_acquire(leftid));
158+
159+ auto left = queue.compositor_acquire(leftid);
160+ auto right = queue.compositor_acquire(rightid);
161+
162+ bool blocking;
163+ do
164+ {
165+ blocking = !client_try_acquire();
166+ if (!blocking)
167+ client_release();
168+ } while (!blocking);
169+
170+ for (int f = 0; f < 100; ++f)
171+ {
172+ ASSERT_FALSE(client_buffer);
173+ queue.compositor_release(left);
174+ queue.compositor_release(right);
175+ ASSERT_FALSE(client_buffer);
176+ left = queue.compositor_acquire(leftid);
177+ right = queue.compositor_acquire(rightid);
178+ ASSERT_TRUE(client_buffer);
179+ client_release();
180+ client_try_acquire();
181+ }
182+}
183+
184 TEST_P(WithAnyNumberOfBuffers, compositor_inflates_ready_count_for_slow_clients)
185 {
186 queue.set_scaling_delay(3);
187
188=== modified file 'tests/unit-tests/compositor/test_buffer_queue.cpp'
189--- tests/unit-tests/compositor/test_buffer_queue.cpp 2015-08-24 10:22:40 +0000
190+++ tests/unit-tests/compositor/test_buffer_queue.cpp 2015-08-27 10:34:15 +0000
191@@ -477,6 +477,118 @@
192 EXPECT_NO_THROW(q.compositor_release(comp_buffer));
193 }
194
195+TEST_P(WithTwoOrMoreBuffers, clients_get_new_buffers_on_compositor_release)
196+{ // Regression test for LP: #1480164
197+ q.allow_framedropping(false);
198+
199+ // Skip over the first frame. The early release optimization is too
200+ // conservative to allow it to happen right at the start (so as to
201+ // maintain correct multimonitor frame rates if required).
202+ auto handle = client_acquire_async(q);
203+ ASSERT_TRUE(handle->has_acquired_buffer());
204+ handle->release_buffer();
205+ q.compositor_release(q.compositor_acquire(this));
206+
207+ auto onscreen = q.compositor_acquire(this);
208+
209+ // This is what tests should do instead of using buffers_free_for_client()
210+ bool blocking;
211+ do
212+ {
213+ handle = client_acquire_async(q);
214+ blocking = !handle->has_acquired_buffer();
215+ if (!blocking)
216+ handle->release_buffer();
217+ } while (!blocking);
218+
219+ for (int f = 0; f < 100; ++f)
220+ {
221+ ASSERT_FALSE(handle->has_acquired_buffer());
222+ q.compositor_release(onscreen);
223+ ASSERT_TRUE(handle->has_acquired_buffer()) << "frame# " << f;
224+ handle->release_buffer();
225+ onscreen = q.compositor_acquire(this);
226+ handle = client_acquire_async(q);
227+ }
228+}
229+
230+TEST_P(WithTwoOrMoreBuffers, short_buffer_holds_dont_overclock_multimonitor)
231+{ // Regression test related to LP: #1480164
232+ q.allow_framedropping(false);
233+
234+ // Skip over the first frame. The early release optimization is too
235+ // conservative to allow it to happen right at the start (so as to
236+ // maintain correct multimonitor frame rates if required).
237+ auto handle = client_acquire_async(q);
238+ ASSERT_TRUE(handle->has_acquired_buffer());
239+ handle->release_buffer();
240+
241+ const void* const leftid = "left";
242+ const void* const rightid = "right";
243+ auto left = q.compositor_acquire(leftid);
244+ q.compositor_release(left);
245+ left = q.compositor_acquire(leftid);
246+ auto right = q.compositor_acquire(rightid);
247+
248+ // This is what tests should do instead of using buffers_free_for_client()
249+ bool blocking;
250+ do
251+ {
252+ handle = client_acquire_async(q);
253+ blocking = !handle->has_acquired_buffer();
254+ if (!blocking)
255+ handle->release_buffer();
256+ } while (!blocking);
257+
258+ for (int f = 0; f < 100; ++f)
259+ {
260+ ASSERT_FALSE(handle->has_acquired_buffer());
261+ q.compositor_release(left);
262+ q.compositor_release(right);
263+ ASSERT_FALSE(handle->has_acquired_buffer());
264+ left = q.compositor_acquire(leftid);
265+ right = q.compositor_acquire(rightid);
266+ ASSERT_TRUE(handle->has_acquired_buffer());
267+ handle->release_buffer();
268+ handle = client_acquire_async(q);
269+ }
270+}
271+
272+TEST_P(WithThreeOrMoreBuffers, greedy_clients_get_new_buffers_on_compositor_release)
273+{ // Regression test for LP: #1480164
274+ q.allow_framedropping(false);
275+
276+ // Skip over the first frame. The early release optimization is too
277+ // conservative to allow it to happen right at the start (so as to
278+ // maintain correct multimonitor frame rates if required).
279+ auto handle = client_acquire_async(q);
280+ ASSERT_TRUE(handle->has_acquired_buffer());
281+ handle->release_buffer();
282+ q.compositor_release(q.compositor_acquire(this));
283+
284+ auto onscreen = q.compositor_acquire(this);
285+ auto old_handle = handle;
286+ old_handle.reset();
287+ bool blocking;
288+ do
289+ {
290+ handle = client_acquire_async(q);
291+ blocking = !handle->has_acquired_buffer();
292+ if (!blocking)
293+ {
294+ if (old_handle)
295+ old_handle->release_buffer();
296+ old_handle = handle;
297+ handle.reset();
298+ }
299+ } while (!blocking);
300+
301+ ASSERT_TRUE(old_handle->has_acquired_buffer());
302+ ASSERT_FALSE(handle->has_acquired_buffer());
303+ q.compositor_release(onscreen);
304+ ASSERT_TRUE(handle->has_acquired_buffer());
305+}
306+
307 TEST_P(WithAnyNumberOfBuffers, multiple_compositors_are_in_sync)
308 {
309 auto handle = client_acquire_async(q);

Subscribers

People subscribed via source and target branches