Mir

Merge lp:~afrantzis/mir/swapper-multiple-compositor-acquire into lp:~mir-team/mir/trunk

Proposed by Alexandros Frantzis
Status: Rejected
Rejected by: Alexandros Frantzis
Proposed branch: lp:~afrantzis/mir/swapper-multiple-compositor-acquire
Merge into: lp:~mir-team/mir/trunk
Diff against target: 430 lines (+238/-26)
7 files modified
include/server/mir/compositor/buffer_swapper.h (+18/-3)
include/server/mir/compositor/buffer_swapper_multi.h (+26/-1)
src/server/compositor/buffer_swapper_multi.cpp (+109/-10)
src/server/compositor/buffer_swapper_spin.cpp (+3/-0)
tests/integration-tests/compositor/test_stress_buffer_swapper.cpp (+15/-3)
tests/integration-tests/compositor/test_swapping_swappers.cpp (+8/-9)
tests/unit-tests/compositor/test_buffer_swapper_double.cpp (+59/-0)
To merge this branch: bzr merge lp:~afrantzis/mir/swapper-multiple-compositor-acquire
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Mir development team Pending
Review via email: mp+169473@code.launchpad.net

Commit message

compositor: Support multiple compositor_acquire() calls for BufferSwapperMulti

Description of the change

compositor: Support multiple compositor_acquire() calls for BufferSwapperMulti

This is the next step in the session snapshots saga: being able to hand out compositor buffers to multiple consumers. Note that this deals with normal acquires, not buffer peeking. Buffer peeking complicates the swapper even more, and after a lot of experimentation I decided not to pursue it further until there is demonstrated need for it (the current code can be modified to support peeking).

Acquiring (instead of peeking) a buffer when taking a snapshot may lead to the compositor losing a buffer (= glitch). However, this may not actually be a problem in practice since in the current use cases snapshotting happens when a surface is about to be hidden. In any case, consider this to be first step which we can refine in the future if there is a need.

Also, note that this work will benefit multiple monitor support, for which the compositor may need to acquire the buffer multiple times (one for each screen), for example, if a surface is visible on more than one screens (and we are not in clone mode).

The semantics of multiple compositor acquires for BufferSwapperMulti are:

1. All calls in a series of acquires without any intervening releases will return the same buffer, even if newer buffers have been posted.
2. The acquired buffer will return to the client only after all acquires have been released.
3. The next acquire after any release, even if it is not a final release, will get the last posted buffer (which may be the already acquired buffer if there is no newer buffer).

(1) Reduces the missing of buffers:

Although this scenario may still cause the compositors to miss buffers:

Comp1: AR__AR
Comp2: __AR__AR

...in the following scenarios buffers are not missed:

Comp1: A__R
Comp2: _AR_

Comp1: A_R_
Comp2: _A_R_

(3) This ensures we don't get stuck with the same buffer forever, e.g., in the following scenario:

Comp1: A_RA__RA__RA ...
Comp2: _A__RA__RA__ ...

In the scenario above the buffer stays acquired forever. If we didn't have (3) or a similar escape route, the two compositors would get the same buffer forever (because of (1))!

These rules are not set in stone, they were chosen as a decent starting poing that seems to work well, but we are free to change them as needed. For example we can get rid of (1)+(3), and decide that acquire() will always return the last posted buffer.

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
Alexandros Frantzis (afrantzis) wrote :

Spurious jenkins failure... resubmitted.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

the new tests look like what we want to do. I'm not sure if the approach with AcquiredBuffers is the simplest though. We could do something like have SwitchingSwapper take care of 'cloning' and refcounting the outgoing buffers on the compositor side (and keeping the mc::BufferSwapper requirements the same).

I also don't mind having a strong reference to the Buffer in both the TemporaryBuffer the compositor owns and the BufferBundle, if that makes it simpler.

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

I guess with all the new swapper functionality we're finding that we need (like, resizing a bundle, switching swapping algorithms mid-flight, multimonitor, and peeking for individual snapshots (maybe even screencasting?)) I'd rather solve the problems in the layer between the BufferStream and the BufferSwappers than by making BufferSwapper have more responsibilities. Hopefully this will keep our BufferSwappers steady state algorithms for swapping buffers, and the 'corner cases' (mentioned above) are taken care of mostly by diverting the buffers once we've ensured sync in the BufferSwappers.

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

> I'd rather solve the problems in the layer between the
> BufferStream and the BufferSwappers than by making BufferSwapper have more
> responsibilities. Hopefully this will keep our BufferSwappers steady state
> algorithms for swapping buffers, and the 'corner cases' (mentioned above)
> are taken care of mostly by diverting the buffers once we've ensured sync
> in the BufferSwappers.

Good idea. I am unhappy with adding complexity to BufferSwapper myself.

I will move the multiple acquire support to SwitchingBundle. Note that we still need to use the AcquiredBuffers class (or something similar) in SwitchingBundle, to enforce the behaviour mentioned in the description.

Unmerged revisions

743. By Alexandros Frantzis

compositor: Support multiple compositor_acquire() calls for BufferSwapperMulti

742. By Daniel van Vugt

setup-android-dependencies.sh: Fix typo causing libxkbcommon0 to not be
installed and the script never quite finish. (LP: #1190884). Fixes: https://bugs.launchpad.net/bugs/1190884.

Approved by Alexandros Frantzis, PS Jenkins bot.

741. By Daniel van Vugt

Introducing fingerpaint. It's not just a multi-touch demo that responds to
pressure and size, but more importantly it's a future testbed for localized
input coordinates.

Pretty boring on desktop. Less so on Nexus 4.
.

Approved by Kevin DuBois, Alan Griffiths, PS Jenkins bot.

740. By Alan Griffiths

compositor: Restore force_requests_to_complete() logic to what we had before -r 728. Fixes: https://bugs.launchpad.net/bugs/1189443.

Approved by PS Jenkins bot, Kevin DuBois, Alexandros Frantzis.

739. By Daniel van Vugt

Fix debuild failures on saucy / gcc-4.8.

Approved by Alexandros Frantzis, Alan Griffiths, PS Jenkins bot, Chris Halse Rogers.

738. By Daniel van Vugt

Minor correction to documentation building_source_for_pc so that the
instructions will actually work :)

Remember sudo.

Approved by Alexandros Frantzis, PS Jenkins bot.

737. By Daniel van Vugt

Resolve the two differing interpretations of "stride" which were causing
software surfaces to be distorted on Android. (LP: #1116452)

Just make sure that "stride" in any Android structure is a number of pixels
and in Mir types it is always a number of bytes. Fixes: https://bugs.launchpad.net/bugs/1116452.

Approved by Kevin DuBois, Alexandros Frantzis, Alan Griffiths, PS Jenkins bot.

736. By Kevin DuBois

Iterate on the SwapperFactory so it can meet the more complex demands of different allocations. This is the cleanup of the the rough edges of the SwapperSwitcher algorithm branch.

Approved by PS Jenkins bot, Alexandros Frantzis, Alan Griffiths.

735. By Robert Carr

Depthify the surface stack to support the launcher use case.

Approved by Alan Griffiths, PS Jenkins bot, Alexandros Frantzis.

734. By Daniel van Vugt

Fix backward error message and improve error details (mention the errno).
(LP: #1189726). Fixes: https://bugs.launchpad.net/bugs/1189726.

Approved by Alexandros Frantzis, Alan Griffiths, PS Jenkins bot.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/server/mir/compositor/buffer_swapper.h'
2--- include/server/mir/compositor/buffer_swapper.h 2013-06-13 10:40:42 +0000
3+++ include/server/mir/compositor/buffer_swapper.h 2013-06-14 15:58:28 +0000
4@@ -41,11 +41,26 @@
5 it. This modifies the buffer the compositor posts to the screen */
6 virtual void client_release(std::shared_ptr<Buffer> const& queued_buffer) = 0;
7
8- /* caller of compositor_acquire buffer should get no-wait access to the
9- last posted buffer. However, the client will potentially stall
10- until control of the buffer is returned via compositor_release() */
11+ /**
12+ * Acquires the last posted buffer.
13+ *
14+ * Callers of compositor_acquire() should get no-wait access to the
15+ * last posted buffer. However, the client will potentially stall
16+ * until control of the buffer is returned via matching calls to
17+ * compositor_release().
18+ *
19+ * Each call in a series of compositor_acquire() calls is guaranteed to
20+ * return a buffer no older than the ones returned in the previous calls
21+ * (may return the same buffer).
22+ */
23 virtual std::shared_ptr<Buffer> compositor_acquire() = 0;
24
25+ /**
26+ * Releases a buffer from the compositor to the client.
27+ *
28+ * Note that the buffer is made available to the client only when all users
29+ * who have called compositor_acquire() have released the buffer.
30+ */
31 virtual void compositor_release(std::shared_ptr<Buffer> const& released_buffer) = 0;
32
33 /**
34
35=== modified file 'include/server/mir/compositor/buffer_swapper_multi.h'
36--- include/server/mir/compositor/buffer_swapper_multi.h 2013-06-12 16:56:32 +0000
37+++ include/server/mir/compositor/buffer_swapper_multi.h 2013-06-14 15:58:28 +0000
38@@ -27,12 +27,36 @@
39 #include <vector>
40 #include <memory>
41 #include <deque>
42-#include <map>
43
44 namespace mir
45 {
46 namespace compositor
47 {
48+namespace detail
49+{
50+
51+struct AcquiredBufferInfo
52+{
53+ std::weak_ptr<Buffer> buffer;
54+ bool can_be_reacquired;
55+ int use_count;
56+};
57+
58+class AcquiredBuffers
59+{
60+public:
61+ void acquire(std::shared_ptr<Buffer> const& buffer);
62+ std::pair<bool,bool> release(std::shared_ptr<Buffer> const& buffer);
63+ std::shared_ptr<Buffer> find_reacquirable_buffer();
64+ std::shared_ptr<Buffer> last_buffer();
65+
66+private:
67+ std::vector<AcquiredBufferInfo>::iterator find_info(std::shared_ptr<Buffer> const& buffer);
68+
69+ std::vector<AcquiredBufferInfo> acquired_buffers;
70+};
71+
72+}
73
74 class Buffer;
75
76@@ -56,6 +80,7 @@
77
78 std::deque<std::shared_ptr<Buffer>> client_queue;
79 std::deque<std::shared_ptr<Buffer>> compositor_queue;
80+ detail::AcquiredBuffers acquired_buffers;
81 unsigned int in_use_by_client;
82 unsigned int const swapper_size;
83 int clients_trying_to_acquire;
84
85=== modified file 'src/server/compositor/buffer_swapper_multi.cpp'
86--- src/server/compositor/buffer_swapper_multi.cpp 2013-06-12 16:56:32 +0000
87+++ src/server/compositor/buffer_swapper_multi.cpp 2013-06-14 15:58:28 +0000
88@@ -18,9 +18,90 @@
89
90 #include "mir/compositor/buffer_swapper_multi.h"
91 #include <boost/throw_exception.hpp>
92+#include <algorithm>
93
94 namespace mc = mir::compositor;
95
96+std::vector<mc::detail::AcquiredBufferInfo>::iterator
97+mc::detail::AcquiredBuffers::find_info(std::shared_ptr<Buffer> const& buffer)
98+{
99+ return std::find_if(acquired_buffers.begin(), acquired_buffers.end(),
100+ [&buffer] (AcquiredBufferInfo const& info)
101+ {
102+ return info.buffer.lock() == buffer;
103+ });
104+}
105+
106+void mc::detail::AcquiredBuffers::acquire(std::shared_ptr<Buffer> const& buffer)
107+{
108+ auto iter = find_info(buffer);
109+
110+ if (iter != acquired_buffers.end())
111+ ++iter->use_count;
112+ else
113+ acquired_buffers.push_back(AcquiredBufferInfo{buffer, true, 1});
114+
115+}
116+
117+std::pair<bool,bool>
118+mc::detail::AcquiredBuffers::release(std::shared_ptr<Buffer> const& buffer)
119+{
120+ auto iter = find_info(buffer);
121+
122+ bool released{false};
123+ bool found{false};
124+
125+ if (iter != acquired_buffers.end())
126+ {
127+ AcquiredBufferInfo& info = *iter;
128+ found = true;
129+
130+ --info.use_count;
131+
132+ if (info.use_count == 0)
133+ {
134+ acquired_buffers.erase(iter);
135+ released = true;
136+ }
137+ else
138+ {
139+ info.can_be_reacquired = false;
140+ }
141+ }
142+
143+ return std::make_pair(released, found);
144+}
145+
146+std::shared_ptr<mc::Buffer> mc::detail::AcquiredBuffers::find_reacquirable_buffer()
147+{
148+ auto iter = std::find_if(acquired_buffers.begin(), acquired_buffers.end(),
149+ [] (AcquiredBufferInfo const& info)
150+ {
151+ return info.can_be_reacquired == true;
152+ });
153+
154+ if (iter != acquired_buffers.end())
155+ {
156+ return iter->buffer.lock();
157+ }
158+ else
159+ {
160+ return {};
161+ }
162+}
163+
164+std::shared_ptr<mc::Buffer> mc::detail::AcquiredBuffers::last_buffer()
165+{
166+ if (!acquired_buffers.empty())
167+ {
168+ return acquired_buffers.back().buffer.lock();
169+ }
170+ else
171+ {
172+ return {};
173+ }
174+}
175+
176 mc::BufferSwapperMulti::BufferSwapperMulti(std::vector<std::shared_ptr<compositor::Buffer>>& buffer_list, size_t swapper_size)
177 : in_use_by_client(0),
178 swapper_size(swapper_size),
179@@ -57,6 +138,7 @@
180 //we have been forced to shutdown
181 if (force_clients_to_complete)
182 {
183+ clients_trying_to_acquire--;
184 BOOST_THROW_EXCEPTION(std::logic_error("forced_completion"));
185 }
186
187@@ -89,31 +171,48 @@
188 {
189 std::unique_lock<std::mutex> lk(swapper_mutex);
190
191- if (compositor_queue.empty() && client_queue.empty())
192- {
193- BOOST_THROW_EXCEPTION(std::logic_error("forced_completion"));
194- }
195-
196 std::shared_ptr<mc::Buffer> dequeued_buffer;
197- if (compositor_queue.empty())
198+
199+ if ((dequeued_buffer = acquired_buffers.find_reacquirable_buffer()) != nullptr)
200+ {
201+ }
202+ else if (!compositor_queue.empty())
203+ {
204+ dequeued_buffer = compositor_queue.front();
205+ compositor_queue.pop_front();
206+ }
207+ else if (!client_queue.empty())
208 {
209 dequeued_buffer = client_queue.back();
210 client_queue.pop_back();
211 }
212+ else if ((dequeued_buffer = acquired_buffers.last_buffer()) != nullptr)
213+ {
214+ }
215 else
216 {
217- dequeued_buffer = compositor_queue.front();
218- compositor_queue.pop_front();
219+ BOOST_THROW_EXCEPTION(std::logic_error("forced_completion"));
220 }
221
222+ acquired_buffers.acquire(dequeued_buffer);
223+
224 return dequeued_buffer;
225 }
226
227 void mc::BufferSwapperMulti::compositor_release(std::shared_ptr<Buffer> const& released_buffer)
228 {
229 std::unique_lock<std::mutex> lk(swapper_mutex);
230- client_queue.push_back(released_buffer);
231- client_available_cv.notify_one();
232+
233+ auto const rel_pair = acquired_buffers.release(released_buffer);
234+
235+ bool const buffer_released{rel_pair.first};
236+ bool const buffer_found{rel_pair.second};
237+
238+ if (buffer_released || !buffer_found)
239+ {
240+ client_queue.push_back(released_buffer);
241+ client_available_cv.notify_one();
242+ }
243 }
244
245 void mc::BufferSwapperMulti::force_client_abort()
246
247=== modified file 'src/server/compositor/buffer_swapper_spin.cpp'
248--- src/server/compositor/buffer_swapper_spin.cpp 2013-06-12 16:56:32 +0000
249+++ src/server/compositor/buffer_swapper_spin.cpp 2013-06-14 15:58:28 +0000
250@@ -17,6 +17,9 @@
251 */
252
253 #include "mir/compositor/buffer_swapper_spin.h"
254+#include <boost/throw_exception.hpp>
255+
256+#include <stdexcept>
257
258 namespace mc = mir::compositor;
259
260
261=== modified file 'tests/integration-tests/compositor/test_stress_buffer_swapper.cpp'
262--- tests/integration-tests/compositor/test_stress_buffer_swapper.cpp 2013-05-29 23:49:32 +0000
263+++ tests/integration-tests/compositor/test_stress_buffer_swapper.cpp 2013-06-14 15:58:28 +0000
264@@ -51,12 +51,14 @@
265
266 mt::Synchronizer compositor_controller;
267 mt::Synchronizer client_controller;
268+ mt::Synchronizer compositor_controller1;
269
270 std::chrono::microseconds const sleep_duration;
271 int const num_iterations;
272
273 std::thread thread1;
274 std::thread thread2;
275+ std::thread thread3;
276
277 void test_distinct_buffers(mc::BufferSwapper& swapper);
278 void test_valid_buffers(mc::BufferSwapper& swapper);
279@@ -107,33 +109,43 @@
280 }
281 }
282
283-/* test that the compositor and the client are never in ownership of the same
284- buffer */
285+/*
286+ * Test that multiple compositor buffer consumers and the
287+ * client are never in ownership of the same buffer.
288+ */
289 void BufferSwapperStress::test_distinct_buffers(mc::BufferSwapper& swapper)
290 {
291 std::shared_ptr<mc::Buffer> compositor_buffer;
292 std::shared_ptr<mc::Buffer> client_buffer;
293+ std::shared_ptr<mc::Buffer> compositor_buffer1;
294
295 thread1 = std::thread(compositor_grab_loop, std::ref(compositor_buffer),
296 std::ref(compositor_controller), std::ref(swapper));
297 thread2 = std::thread(client_request_loop, std::ref(client_buffer),
298 std::ref(client_controller), std::ref(swapper));
299+ thread3 = std::thread(compositor_grab_loop, std::ref(compositor_buffer1),
300+ std::ref(compositor_controller1), std::ref(swapper));
301
302- for(int i=0; i< num_iterations; i++)
303+ for(int i = 0; i < num_iterations; i++)
304 {
305 compositor_controller.ensure_child_is_waiting();
306 client_controller.ensure_child_is_waiting();
307+ compositor_controller1.ensure_child_is_waiting();
308
309 EXPECT_NE(compositor_buffer, client_buffer);
310+ EXPECT_NE(compositor_buffer1, client_buffer);
311
312 compositor_controller.activate_waiting_child();
313 client_controller.activate_waiting_child();
314+ compositor_controller1.activate_waiting_child();
315
316 main_test_loop_pause(sleep_duration);
317 }
318
319+ terminate_child_thread(compositor_controller1);
320 terminate_child_thread(client_controller);
321 terminate_child_thread(compositor_controller);
322+ thread3.join();
323 thread2.join();
324 thread1.join();
325 }
326
327=== modified file 'tests/integration-tests/compositor/test_swapping_swappers.cpp'
328--- tests/integration-tests/compositor/test_swapping_swappers.cpp 2013-06-11 19:55:10 +0000
329+++ tests/integration-tests/compositor/test_swapping_swappers.cpp 2013-06-14 15:58:28 +0000
330@@ -39,25 +39,24 @@
331 namespace
332 {
333
334-struct MockBufferAllocator : public mc::GraphicBufferAllocator
335+struct StubBufferAllocator : public mc::GraphicBufferAllocator
336 {
337- MockBufferAllocator()
338+ std::shared_ptr<mc::Buffer> alloc_buffer(mc::BufferProperties const&)
339 {
340- using namespace testing;
341- ON_CALL(*this, alloc_buffer(_))
342- .WillByDefault(Return(std::make_shared<mtd::StubBuffer>()));
343+ return std::make_shared<mtd::StubBuffer>();
344 }
345- ~MockBufferAllocator() noexcept{}
346
347- MOCK_METHOD1(alloc_buffer, std::shared_ptr<mc::Buffer>(mc::BufferProperties const&));
348- MOCK_METHOD0(supported_pixel_formats, std::vector<geom::PixelFormat>());
349+ std::vector<geom::PixelFormat> supported_pixel_formats()
350+ {
351+ return {};
352+ }
353 };
354
355 struct SwapperSwappingStress : public ::testing::Test
356 {
357 void SetUp()
358 {
359- auto allocator = std::make_shared<MockBufferAllocator>();
360+ auto allocator = std::make_shared<StubBufferAllocator>();
361 auto factory = std::make_shared<mc::SwapperFactory>(allocator, 3);
362 auto properties = mc::BufferProperties{geom::Size{geom::Width{380}, geom::Height{210}},
363 geom::PixelFormat::abgr_8888, mc::BufferUsage::hardware};
364
365=== modified file 'tests/unit-tests/compositor/test_buffer_swapper_double.cpp'
366--- tests/unit-tests/compositor/test_buffer_swapper_double.cpp 2013-06-13 10:40:42 +0000
367+++ tests/unit-tests/compositor/test_buffer_swapper_double.cpp 2013-06-14 15:58:28 +0000
368@@ -166,3 +166,62 @@
369 swapper->client_acquire();
370 }, std::logic_error);
371 }
372+
373+TEST_F(BufferSwapperDouble, compositor_acquire_gives_same_buffer_until_released)
374+{
375+ auto a = swapper->client_acquire();
376+ swapper->client_release(a);
377+ auto b = swapper->client_acquire();
378+ swapper->client_release(b);
379+
380+ auto comp1 = swapper->compositor_acquire();
381+ auto comp2 = swapper->compositor_acquire();
382+
383+ EXPECT_EQ(comp1, comp2);
384+
385+ swapper->compositor_release(comp1);
386+
387+ auto comp3 = swapper->compositor_acquire();
388+ swapper->compositor_release(comp3);
389+
390+ EXPECT_NE(comp1, comp3);
391+}
392+
393+TEST_F(BufferSwapperDouble, multiple_compositor_releases_return_buffer_to_client)
394+{
395+ auto a = swapper->client_acquire();
396+ swapper->client_release(a);
397+ auto b = swapper->client_acquire();
398+ swapper->client_release(b);
399+
400+ auto comp1 = swapper->compositor_acquire();
401+ auto comp2 = swapper->compositor_acquire();
402+
403+ EXPECT_EQ(comp1, comp2);
404+
405+ swapper->compositor_release(comp1);
406+ swapper->compositor_release(comp2);
407+
408+ auto client1 = swapper->client_acquire();
409+ swapper->client_release(client1);
410+
411+ EXPECT_EQ(comp1, comp2);
412+}
413+
414+TEST_F(BufferSwapperDouble, compositor_acquire_can_return_partly_released_buffer)
415+{
416+ auto a = swapper->client_acquire();
417+ swapper->client_release(a);
418+ auto b = swapper->client_acquire();
419+
420+ auto comp1 = swapper->compositor_acquire();
421+ auto comp2 = swapper->compositor_acquire();
422+
423+ EXPECT_EQ(comp1, comp2);
424+
425+ swapper->compositor_release(comp1);
426+
427+ auto comp3 = swapper->compositor_acquire();
428+
429+ EXPECT_EQ(comp1, comp3);
430+}

Subscribers

People subscribed via source and target branches