Mir

Merge lp:~kdub/mir/multiple-bufferstream-api into lp:mir

Proposed by Kevin DuBois
Status: Merged
Approved by: Kevin DuBois
Approved revision: no longer in the source branch.
Merged at revision: 2670
Proposed branch: lp:~kdub/mir/multiple-bufferstream-api
Merge into: lp:mir
Prerequisite: lp:~kdub/mir/add-streams-to-surface
Diff against target: 644 lines (+402/-15)
18 files modified
include/client/mir_toolkit/client_types.h (+10/-0)
include/client/mir_toolkit/mir_surface.h (+22/-0)
include/server/mir/scene/session.h (+3/-0)
src/client/mir_surface.cpp (+13/-0)
src/client/mir_surface.h (+1/-0)
src/client/mir_surface_api.cpp (+20/-0)
src/client/symbols.map (+1/-0)
src/server/frontend/session_mediator.cpp (+10/-7)
src/server/scene/application_session.cpp (+9/-0)
src/server/scene/application_session.h (+1/-0)
src/server/scene/basic_surface.cpp (+10/-7)
src/server/shell/canonical_window_manager.cpp (+8/-1)
tests/acceptance-tests/CMakeLists.txt (+1/-0)
tests/acceptance-tests/test_buffer_stream_arrangement.cpp (+239/-0)
tests/include/mir_test_doubles/mock_scene_session.h (+2/-0)
tests/include/mir_test_doubles/mock_surface.h (+1/-0)
tests/include/mir_test_doubles/stub_scene_session.h (+4/-0)
tests/unit-tests/scene/test_application_session.cpp (+47/-0)
To merge this branch: bzr merge lp:~kdub/mir/multiple-bufferstream-api
Reviewer Review Type Date Requested Status
Alberto Aguirre (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Alexandros Frantzis (community) Approve
Review via email: mp+261123@code.launchpad.net

Commit message

Add the client api to associate multiple streams together. Connect bits throughout the system to get the acceptance test for this feature to pass.

Description of the change

Add the client api to associate multiple streams together. Connect bits throughout the system to get the acceptance test for this feature to pass.

notes:
The acceptance test here is about the same as the one from the forerunner mp:
https://code.launchpad.net/~kdub/mir/RFC-surface-arrangements/+merge/256186
The only difference is that it only adds one spec-modifying function though... the other client api functions there don't seem useful.

This is the multibufferstream with-async-streams plumbed all the way through. It gets the other bufferstreams onscreen. Currently working on renovating scroll.cpp to have a demo.

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 :

10+ MirBufferStream * stream;

'*' with type

26+/** Set the streams associated with the spec.
27+ * streams[0] is the bottom-most stream, and streams[size-1] is the topmost.

Space before this line.

28+ * On application of the spec, A stream that is present in the surface,

Space before this line (different topic started).
'a stream'

30+ * On application of the spec, A stream that is not present in the surface,
31+ * but is in the list will be associated with the surface.

'<some other connecting phrase>, a stream that is not present' ...

32+ * Streams set a displacement from the top-left corner of the surface.

Space before this line (change of topic), or perhaps move this information to the documentation of the MirBufferStreamInfo structure?

57+#include <list>
69+ virtual void configure_streams(Surface& surface, std::list<shell::StreamSpecification> const& config) = 0;

Somewhat pre-existing: Is a vector<> not sufficient for this use case? I see that Surface::set_streams() uses a list<> which I don't think offers any advantages over vector<> for our usage patterns and is a probably a pessimization ("use vector<> by default").

91+ for(auto
110+ for(auto
... and elsewhere

'for ('

517+ primary_stream = std::unique_ptr<Stream>(
525+ streams.emplace_back(std::unique_ptr<Stream>(

make_unique

517+ primary_stream = std::unique_ptr<Stream>(
549+ infos[i++] = MirBufferStreamInfo{

I doesn't seem that we need to treat the primary stream specially. Treating it as yet another stream simplifies the code.

553+ for(auto &stream

'auto const&'

442+ bool ensure_last_ordering_is_consistent_with(443+ std::vector<geom::Displacement> const& arrangement)
444+ {

... return arrangment == displacement;

Or even better, change:

574+ EXPECT_TRUE(ordering->ensure_last_ordering_is_consistent_with(displacements))

to EXPECT_THAT(ordering->last_arrangement(), ContainerEq(displacements)) to get more detailed information in case of failure.

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

Issues should be fixed.
With:
'for(' vs 'for ('
I obviously don't find this to affect readability... maybe will suggest a change to style.

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

===
45 + * \param [in] size The size of streams.

49 + unsigned int size);
===

Maybe change size to num_elements?
"The number of elements in the streams array" to avoid confusion?

===
148 +void mir_surface_spec_set_streams(MirSurfaceSpec* spec, MirBufferStreamInfo* streams, unsigned int size)
===

The implementation should validate its preconditions with mir::require(mir_buffer_stream_is_valid(...)) for all buffer streams.

===
172 +MIR_CLIENT_9.1 { # New in Mir 0.14
173 + global:
174 + mir_surface_spec_set_streams;
175 +} MIR_CLIENT_9;
===

No need for a 9.1 stanza as we haven't released anything with MIR_CLIENT_9 yet.

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

===
45 + * \param [in] size The number of elements in the streams array.
===

doxygen comment still refers to size instead of num_streams.

"[0;32m[ RUN ] BufferStreamArrangement.arrangements_are_applied
/tmp/buildd/mir-0.14.0bzr2469pkg0wily2729+autopilot0/tests/acceptance-tests/test_buffer_stream_arrangement.cpp:236: Failure
Value of: ordering->wait_for_another_post_within(1s)
Actual: false
Expected: true
timed out waiting for another post"

===
561 + EXPECT_TRUE(ordering->wait_for_another_post_within(1s))
===
I guess running with Valgrind in CI needs more than 1s.

Looks good otherwise.

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

Looks good (besides what others have already mentioned, of course)

Typo:

34+ * On application of the spec, A stream that is not present in the surface,

'a stream'

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

LGTM.

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

gl2mark error?

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

If you take the 'artifacts' zip and install it on the device (along with the glmark debian package, like the script does), the glmark error is reproducible. It seems that there was some abi-incompatible mixing and matching of (mirclient8, mirclient9) and libmirprotobuf0. Seems appropriate to bump the protobuf versioning so that the right libraries get picked up.

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

side-stepped protobuf problem by not adding new field. The SurfaceParameter changes broke it, but the test in this MP only needed already existing protobuf fields.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alberto Aguirre (albaguirre) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/client/mir_toolkit/client_types.h'
2--- include/client/mir_toolkit/client_types.h 2015-05-29 04:23:56 +0000
3+++ include/client/mir_toolkit/client_types.h 2015-06-16 12:28:06 +0000
4@@ -283,6 +283,16 @@
5 MirDisplayCard *cards;
6 } MirDisplayConfiguration;
7
8+/**
9+ * The displacement from the top-left corner of the surface.
10+ */
11+typedef struct MirBufferStreamInfo
12+{
13+ MirBufferStream* stream;
14+ int displacement_x;
15+ int displacement_y;
16+} MirBufferStreamInfo;
17+
18 typedef struct MirRectangle
19 {
20 int left;
21
22=== modified file 'include/client/mir_toolkit/mir_surface.h'
23--- include/client/mir_toolkit/mir_surface.h 2015-06-10 09:00:22 +0000
24+++ include/client/mir_toolkit/mir_surface.h 2015-06-16 12:28:06 +0000
25@@ -433,6 +433,28 @@
26 void mir_surface_spec_release(MirSurfaceSpec* spec);
27
28 /**
29+ * Set the streams associated with the spec.
30+ * streams[0] is the bottom-most stream, and streams[size-1] is the topmost.
31+ * On application of the spec, a stream that is present in the surface,
32+ * but is not in the list will be disassociated from the surface.
33+ * On application of the spec, a stream that is not present in the surface,
34+ * but is in the list will be associated with the surface.
35+ * Streams set a displacement from the top-left corner of the surface.
36+ *
37+ * \warning disassociating streams from the surface will not release() them.
38+ * \warning It is wiser to arrange the streams within the bounds of the
39+ * surface the spec is applied to. Shells can define their own
40+ * behavior as to what happens to an out-of-bound stream.
41+ *
42+ * \param [in] spec The spec to accumulate the request in.
43+ * \param [in] streams The an array of non-null streams info.
44+ * \param [in] num_streams The number of elements in the streams array.
45+ */
46+void mir_surface_spec_set_streams(MirSurfaceSpec* spec,
47+ MirBufferStreamInfo* streams,
48+ unsigned int num_streams);
49+
50+/**
51 * Set the event handler to be called when events arrive for a surface.
52 * \warning event_handler could be called from another thread. You must do
53 * any locking appropriate to protect your data accessed in the
54
55=== modified file 'include/server/mir/scene/session.h'
56--- include/server/mir/scene/session.h 2015-05-19 21:34:34 +0000
57+++ include/server/mir/scene/session.h 2015-06-16 12:28:06 +0000
58@@ -22,10 +22,12 @@
59 #include "mir/frontend/session.h"
60 #include "mir/scene/snapshot.h"
61
62+#include <vector>
63 #include <sys/types.h>
64
65 namespace mir
66 {
67+namespace shell { struct StreamSpecification; }
68 namespace scene
69 {
70 class Surface;
71@@ -60,6 +62,7 @@
72
73 virtual frontend::BufferStreamId create_buffer_stream(graphics::BufferProperties const& props) = 0;
74 virtual void destroy_buffer_stream(frontend::BufferStreamId stream) = 0;
75+ virtual void configure_streams(Surface& surface, std::vector<shell::StreamSpecification> const& config) = 0;
76 };
77 }
78 }
79
80=== modified file 'src/client/mir_surface.cpp'
81--- src/client/mir_surface.cpp 2015-06-10 11:13:15 +0000
82+++ src/client/mir_surface.cpp 2015-06-16 12:28:06 +0000
83@@ -23,6 +23,7 @@
84 #include "cursor_configuration.h"
85 #include "client_buffer_stream_factory.h"
86 #include "mir_connection.h"
87+#include "client_buffer_stream.h"
88 #include "mir/dispatch/threaded_dispatcher.h"
89 #include "mir/input/input_platform.h"
90 #include "mir/input/xkb_mapper.h"
91@@ -664,6 +665,18 @@
92 aspect->set_height(spec.max_aspect.value().height);
93 }
94
95+ if (spec.streams.is_set())
96+ {
97+ for(auto const& stream : spec.streams.value())
98+ {
99+ auto const new_stream = surface_specification->add_stream();
100+ new_stream->set_displacement_x(stream.displacement_x);
101+ new_stream->set_displacement_y(stream.displacement_y);
102+ new_stream->mutable_id()->set_value(
103+ reinterpret_cast<mcl::ClientBufferStream*>(stream.stream)->rpc_id().as_value());
104+ }
105+ }
106+
107 modify_wait_handle.expect_result();
108 server->modify_surface(0, &mods, &modify_result,
109 google::protobuf::NewCallback(this, &MirSurface::on_modified));
110
111=== modified file 'src/client/mir_surface.h'
112--- src/client/mir_surface.h 2015-05-29 04:23:56 +0000
113+++ src/client/mir_surface.h 2015-06-16 12:28:06 +0000
114@@ -97,6 +97,7 @@
115 mir::optional_value<int> height_inc;
116 mir::optional_value<AspectRatio> min_aspect;
117 mir::optional_value<AspectRatio> max_aspect;
118+ mir::optional_value<std::vector<MirBufferStreamInfo>> streams;
119 };
120
121 struct MirPersistentId
122
123=== modified file 'src/client/mir_surface_api.cpp'
124--- src/client/mir_surface_api.cpp 2015-06-10 09:00:22 +0000
125+++ src/client/mir_surface_api.cpp 2015-06-16 12:28:06 +0000
126@@ -516,9 +516,29 @@
127 // Keep calm and carry on
128 }
129
130+void mir_surface_spec_set_streams(MirSurfaceSpec* spec, MirBufferStreamInfo* streams, unsigned int size)
131+try
132+{
133+ mir::require(spec);
134+
135+ std::vector<MirBufferStreamInfo> copy;
136+ for (auto i = 0u; i < size; i++)
137+ {
138+ mir::require(mir_buffer_stream_is_valid(streams[i].stream));
139+ copy.emplace_back(streams[i]);
140+ }
141+ spec->streams = copy;
142+}
143+catch (std::exception const& ex)
144+{
145+ MIR_LOG_UNCAUGHT_EXCEPTION(ex);
146+}
147+
148+
149 void mir_surface_spec_set_parent(MirSurfaceSpec* spec, MirSurface* parent)
150 try
151 {
152+ mir::require(spec);
153 spec->parent = parent;
154 }
155 catch (std::exception const& ex)
156
157=== modified file 'src/client/symbols.map'
158--- src/client/symbols.map 2015-06-10 23:37:28 +0000
159+++ src/client/symbols.map 2015-06-16 12:28:06 +0000
160@@ -142,6 +142,7 @@
161 mir_surface_spec_set_parent;
162 mir_surface_spec_set_pixel_format;
163 mir_surface_spec_set_preferred_orientation;
164+ mir_surface_spec_set_streams;
165 mir_surface_spec_set_type;
166 mir_surface_spec_set_width;
167 mir_surface_spec_set_width_increment;
168
169=== modified file 'src/server/frontend/session_mediator.cpp'
170--- src/server/frontend/session_mediator.cpp 2015-06-09 07:20:56 +0000
171+++ src/server/frontend/session_mediator.cpp 2015-06-16 12:28:06 +0000
172@@ -451,15 +451,18 @@
173 // max_aspect is a special case (below)
174
175 #undef COPY_IF_SET
176- std::vector<msh::StreamSpecification> stream_spec;
177- for(auto& stream : surface_specification.stream())
178+ if (surface_specification.stream_size() > 0)
179 {
180- stream_spec.emplace_back(
181- msh::StreamSpecification{
182- mf::BufferStreamId{stream.id().value()},
183- geom::Displacement{stream.displacement_x(), stream.displacement_y()}});
184+ std::vector<msh::StreamSpecification> stream_spec;
185+ for (auto& stream : surface_specification.stream())
186+ {
187+ stream_spec.emplace_back(
188+ msh::StreamSpecification{
189+ mf::BufferStreamId{stream.id().value()},
190+ geom::Displacement{stream.displacement_x(), stream.displacement_y()}});
191+ }
192+ mods.streams = std::move(stream_spec);
193 }
194- mods.streams = std::move(stream_spec);
195
196 if (surface_specification.has_aux_rect())
197 {
198
199=== modified file 'src/server/scene/application_session.cpp'
200--- src/server/scene/application_session.cpp 2015-06-02 13:41:40 +0000
201+++ src/server/scene/application_session.cpp 2015-06-16 12:28:06 +0000
202@@ -322,3 +322,12 @@
203 std::unique_lock<std::mutex> lock(surfaces_and_streams_mutex);
204 streams.erase(checked_find(id));
205 }
206+
207+void ms::ApplicationSession::configure_streams(
208+ ms::Surface& surface, std::vector<shell::StreamSpecification> const& streams)
209+{
210+ std::list<ms::StreamInfo> list;
211+ for (auto& stream : streams)
212+ list.emplace_back(ms::StreamInfo{checked_find(stream.stream_id)->second, stream.displacement});
213+ surface.set_streams(list);
214+}
215
216=== modified file 'src/server/scene/application_session.h'
217--- src/server/scene/application_session.h 2015-05-19 21:34:34 +0000
218+++ src/server/scene/application_session.h 2015-06-16 12:28:06 +0000
219@@ -85,6 +85,7 @@
220 std::shared_ptr<frontend::BufferStream> get_buffer_stream(frontend::BufferStreamId stream) const override;
221 frontend::BufferStreamId create_buffer_stream(graphics::BufferProperties const& params) override;
222 void destroy_buffer_stream(frontend::BufferStreamId stream) override;
223+ void configure_streams(Surface& surface, std::vector<shell::StreamSpecification> const& config) override;
224
225 protected:
226 ApplicationSession(ApplicationSession const&) = delete;
227
228=== modified file 'src/server/scene/basic_surface.cpp'
229--- src/server/scene/basic_surface.cpp 2015-06-08 17:22:32 +0000
230+++ src/server/scene/basic_surface.cpp 2015-06-16 12:28:06 +0000
231@@ -844,15 +844,18 @@
232
233 void ms::BasicSurface::set_streams(std::list<scene::StreamInfo> const& s)
234 {
235- std::unique_lock<std::mutex> lk(guard);
236-
237- if (s.end() == std::find_if(s.begin(), s.end(),
238- [this] (ms::StreamInfo const& info) { return info.stream == surface_buffer_stream; }))
239 {
240- BOOST_THROW_EXCEPTION(std::logic_error("cannot remove the created-with buffer stream yet"));
241+ std::unique_lock<std::mutex> lk(guard);
242+
243+ if (s.end() == std::find_if(s.begin(), s.end(),
244+ [this] (ms::StreamInfo const& info) { return info.stream == surface_buffer_stream; }))
245+ {
246+ BOOST_THROW_EXCEPTION(std::logic_error("cannot remove the created-with buffer stream yet"));
247+ }
248+
249+ layers = s;
250 }
251-
252- layers = s;
253+ observers.moved_to(surface_rect.top_left);
254 }
255
256 mg::RenderableList ms::BasicSurface::generate_renderables(mc::CompositorID id) const
257
258=== modified file 'src/server/shell/canonical_window_manager.cpp'
259--- src/server/shell/canonical_window_manager.cpp 2015-06-11 09:41:30 +0000
260+++ src/server/shell/canonical_window_manager.cpp 2015-06-16 12:28:06 +0000
261@@ -274,7 +274,7 @@
262 }
263
264 void msh::CanonicalWindowManagerPolicy::handle_modify_surface(
265- std::shared_ptr<scene::Session> const& /*session*/,
266+ std::shared_ptr<scene::Session> const& session,
267 std::shared_ptr<scene::Surface> const& surface,
268 SurfaceSpecification const& modifications)
269 {
270@@ -369,6 +369,13 @@
271 if (modifications.name.is_set())
272 surface->rename(modifications.name.value());
273
274+ if (modifications.streams.is_set())
275+ {
276+ auto v = modifications.streams.value();
277+ std::vector<shell::StreamSpecification> l (v.begin(), v.end());
278+ session->configure_streams(*surface, l);
279+ }
280+
281 if (modifications.width.is_set() || modifications.height.is_set())
282 {
283 auto new_size = surface->size();
284
285=== modified file 'tests/acceptance-tests/CMakeLists.txt'
286--- tests/acceptance-tests/CMakeLists.txt 2015-06-11 09:41:30 +0000
287+++ tests/acceptance-tests/CMakeLists.txt 2015-06-16 12:28:06 +0000
288@@ -32,6 +32,7 @@
289 test_prompt_session_client_api.cpp
290 test_client_screencast.cpp
291 test_client_surface_visibility.cpp
292+ test_buffer_stream_arrangement.cpp
293 test_client_with_custom_display_config_deadlock.cpp
294 test_server_without_active_outputs.cpp
295 test_server_startup.cpp
296
297=== added file 'tests/acceptance-tests/test_buffer_stream_arrangement.cpp'
298--- tests/acceptance-tests/test_buffer_stream_arrangement.cpp 1970-01-01 00:00:00 +0000
299+++ tests/acceptance-tests/test_buffer_stream_arrangement.cpp 2015-06-16 12:28:06 +0000
300@@ -0,0 +1,239 @@
301+/*
302+ * Copyright © 2015 Canonical Ltd.
303+ *
304+ * This program is free software: you can redistribute it and/or modify it
305+ * under the terms of the GNU General Public License version 3,
306+ * as published by the Free Software Foundation.
307+ *
308+ * This program is distributed in the hope that it will be useful,
309+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
310+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
311+ * GNU General Public License for more details.
312+ *
313+ * You should have received a copy of the GNU General Public License
314+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
315+ *
316+ * Authored by: Kevin DuBois <kevin.dubois@canonical.com>
317+ */
318+
319+#include "mir_toolkit/mir_client_library.h"
320+#include "mir_test_framework/connected_client_with_a_surface.h"
321+#include "mir/compositor/display_buffer_compositor.h"
322+#include "mir/compositor/display_buffer_compositor_factory.h"
323+#include "mir/compositor/scene_element.h"
324+#include "mir/graphics/renderable.h"
325+#include "mir/graphics/cursor.h"
326+#include "mir/geometry/displacement.h"
327+
328+#include <mutex>
329+#include <condition_variable>
330+#include <stdexcept>
331+#include <gtest/gtest.h>
332+#include <gmock/gmock.h>
333+
334+namespace mtf = mir_test_framework;
335+namespace geom = mir::geometry;
336+namespace mc = mir::compositor;
337+namespace mg = mir::graphics;
338+namespace
339+{
340+
341+MirPixelFormat an_available_format(MirConnection* connection)
342+{
343+ using namespace testing;
344+ MirPixelFormat format{mir_pixel_format_invalid};
345+ unsigned int valid_formats{0};
346+ mir_connection_get_available_surface_formats(connection, &format, 1, &valid_formats);
347+ return format;
348+}
349+
350+struct Stream
351+{
352+ Stream(MirBufferStream* stream, geom::Point pt) :
353+ stream(stream),
354+ pos{pt},
355+ needs_release{false}
356+ {
357+ mir_buffer_stream_swap_buffers_sync(stream);
358+ }
359+
360+ Stream(MirConnection* connection, geom::Rectangle rect) :
361+ stream(mir_connection_create_buffer_stream_sync(
362+ connection,
363+ rect.size.width.as_int(),
364+ rect.size.height.as_int(),
365+ an_available_format(connection),
366+ mir_buffer_usage_hardware)),
367+ pos{rect.top_left},
368+ needs_release{true}
369+ {
370+ mir_buffer_stream_swap_buffers_sync(stream);
371+ }
372+
373+ ~Stream()
374+ {
375+ if (needs_release)
376+ mir_buffer_stream_release_sync(stream);
377+ }
378+
379+ MirBufferStream* handle() const
380+ {
381+ return stream;
382+ }
383+
384+ geom::Point position()
385+ {
386+ return pos;
387+ }
388+
389+ Stream(Stream const&) = delete;
390+ Stream& operator=(Stream const&) = delete;
391+private:
392+ MirBufferStream* stream;
393+ geom::Point const pos;
394+ bool const needs_release;
395+};
396+
397+struct Ordering
398+{
399+ void note_scene_element_sequence(mc::SceneElementSequence& sequence)
400+ {
401+ if (sequence.empty())
402+ return;
403+
404+ std::unique_lock<decltype(mutex)> lk(mutex);
405+ displacements.clear();
406+
407+ auto first_position = (*sequence.begin())->renderable()->screen_position().top_left;
408+ for (auto const& element : sequence)
409+ displacements.emplace_back(element->renderable()->screen_position().top_left - first_position);
410+ post_count++;
411+
412+ cv.notify_all();
413+ }
414+
415+ template<typename T, typename S>
416+ bool wait_for_another_post_within(std::chrono::duration<T,S> duration)
417+ {
418+ std::unique_lock<decltype(mutex)> lk(mutex);
419+ auto count = post_count + 1;
420+ return cv.wait_for(lk, duration, [this, count]{return (post_count >= count);});
421+ }
422+
423+ std::vector<geom::Displacement> last_ordering()
424+ {
425+ return displacements;
426+ }
427+private:
428+ std::mutex mutex;
429+ std::condition_variable cv;
430+ unsigned int post_count{0};
431+ std::vector<geom::Displacement> displacements;
432+};
433+
434+struct OrderTrackingDBC : mc::DisplayBufferCompositor
435+{
436+ OrderTrackingDBC(
437+ std::shared_ptr<mc::DisplayBufferCompositor> const& wrapped,
438+ std::shared_ptr<Ordering> const& ordering) :
439+ wrapped(wrapped),
440+ ordering(ordering)
441+ {
442+ }
443+
444+ void composite(mc::SceneElementSequence&& scene_sequence) override
445+ {
446+ ordering->note_scene_element_sequence(scene_sequence);
447+ wrapped->composite(std::move(scene_sequence));
448+ }
449+
450+ std::shared_ptr<mc::DisplayBufferCompositor> const wrapped;
451+ std::shared_ptr<Ordering> const ordering;
452+};
453+
454+struct OrderTrackingDBCFactory : mc::DisplayBufferCompositorFactory
455+{
456+ OrderTrackingDBCFactory(
457+ std::shared_ptr<mc::DisplayBufferCompositorFactory> const& wrapped,
458+ std::shared_ptr<Ordering> const& ordering) :
459+ wrapped(wrapped),
460+ ordering(ordering)
461+ {
462+ }
463+
464+ std::unique_ptr<mc::DisplayBufferCompositor> create_compositor_for(mg::DisplayBuffer& db) override
465+ {
466+ return std::make_unique<OrderTrackingDBC>(wrapped->create_compositor_for(db), ordering);
467+ }
468+
469+ std::shared_ptr<OrderTrackingDBC> last_dbc{nullptr};
470+ std::shared_ptr<mc::DisplayBufferCompositorFactory> const wrapped;
471+ std::shared_ptr<Ordering> const ordering;
472+};
473+
474+struct BufferStreamArrangement : mtf::ConnectedClientWithASurface
475+{
476+ void SetUp() override
477+ {
478+ ordering = std::make_shared<Ordering>();
479+ server.wrap_display_buffer_compositor_factory(
480+ [this](std::shared_ptr<mc::DisplayBufferCompositorFactory> const& wrapped)
481+ {
482+ order_tracker = std::make_shared<OrderTrackingDBCFactory>(wrapped, ordering);
483+ return order_tracker;
484+ });
485+
486+ ConnectedClientWithASurface::SetUp();
487+ server.the_cursor()->hide();
488+
489+ streams.emplace_back(
490+ std::make_unique<Stream>(mir_surface_get_buffer_stream(surface), geom::Point{0,0}));
491+ int const additional_streams{3};
492+ for (auto i = 0; i < additional_streams; i++)
493+ {
494+ geom::Size size{30 * i + 1, 40* i + 1};
495+ geom::Point position{i * 2, i * 3};
496+ streams.emplace_back(std::make_unique<Stream>(connection, geom::Rectangle{position, size}));
497+ }
498+ }
499+
500+ void TearDown() override
501+ {
502+ streams.clear();
503+ ConnectedClientWithASurface::TearDown();
504+ }
505+
506+ std::shared_ptr<Ordering> ordering;
507+ std::shared_ptr<OrderTrackingDBCFactory> order_tracker{nullptr};
508+ std::vector<std::unique_ptr<Stream>> streams;
509+};
510+}
511+
512+TEST_F(BufferStreamArrangement, arrangements_are_applied)
513+{
514+ using namespace testing;
515+ std::vector<MirBufferStreamInfo> infos(streams.size());
516+ auto i = 0u;
517+ for (auto const& stream : streams)
518+ {
519+ infos[i++] = MirBufferStreamInfo{
520+ stream->handle(),
521+ stream->position().x.as_int(),
522+ stream->position().y.as_int()};
523+ }
524+
525+ auto change_spec = mir_connection_create_spec_for_changes(connection);
526+ mir_surface_spec_set_streams(change_spec, infos.data(), infos.size());
527+ mir_surface_apply_spec(surface, change_spec);
528+ mir_surface_spec_release(change_spec);
529+
530+ std::vector<geom::Displacement> displacements;
531+ for (auto& info : infos)
532+ displacements.emplace_back(geom::Displacement{info.displacement_x, info.displacement_y});
533+
534+ //check that the compositor rendered correctly
535+ using namespace std::literals::chrono_literals;
536+ EXPECT_TRUE(ordering->wait_for_another_post_within(5s))
537+ << "timed out waiting for another post";
538+ EXPECT_THAT(ordering->last_ordering(), ContainerEq(displacements));
539+}
540
541=== modified file 'tests/include/mir_test_doubles/mock_scene_session.h'
542--- tests/include/mir_test_doubles/mock_scene_session.h 2015-05-19 21:34:34 +0000
543+++ tests/include/mir_test_doubles/mock_scene_session.h 2015-06-16 12:28:06 +0000
544@@ -20,6 +20,7 @@
545 #define MIR_TEST_DOUBLES_MOCK_SCENE_SESSION_H_
546
547 #include "mir/scene/session.h"
548+#include "mir/scene/surface.h"
549 #include "mir/scene/surface_creation_parameters.h"
550 #include "mir/graphics/display_configuration.h"
551
552@@ -64,6 +65,7 @@
553 MOCK_METHOD1(destroy_buffer_stream, void(frontend::BufferStreamId));
554 MOCK_METHOD1(create_buffer_stream, frontend::BufferStreamId(graphics::BufferProperties const&));
555
556+ MOCK_METHOD2(configure_streams, void(scene::Surface&, std::vector<shell::StreamSpecification> const&));
557 };
558
559 }
560
561=== modified file 'tests/include/mir_test_doubles/mock_surface.h'
562--- tests/include/mir_test_doubles/mock_surface.h 2015-06-08 17:28:38 +0000
563+++ tests/include/mir_test_doubles/mock_surface.h 2015-06-16 12:28:06 +0000
564@@ -74,6 +74,7 @@
565 MOCK_METHOD1(consume, void(MirEvent const&));
566
567 MOCK_CONST_METHOD0(primary_buffer_stream, std::shared_ptr<frontend::BufferStream>());
568+ MOCK_METHOD1(set_streams, void(std::list<scene::StreamInfo> const&));
569
570 };
571
572
573=== modified file 'tests/include/mir_test_doubles/stub_scene_session.h'
574--- tests/include/mir_test_doubles/stub_scene_session.h 2015-05-19 21:34:34 +0000
575+++ tests/include/mir_test_doubles/stub_scene_session.h 2015-06-16 12:28:06 +0000
576@@ -117,6 +117,10 @@
577 return frontend::BufferStreamId();
578 }
579
580+ void configure_streams(scene::Surface&, std::vector<shell::StreamSpecification> const&)
581+ {
582+ }
583+
584 pid_t const pid;
585 };
586
587
588=== modified file 'tests/unit-tests/scene/test_application_session.cpp'
589--- tests/unit-tests/scene/test_application_session.cpp 2015-05-29 17:36:46 +0000
590+++ tests/unit-tests/scene/test_application_session.cpp 2015-06-16 12:28:06 +0000
591@@ -515,6 +515,53 @@
592 session->destroy_surface(id);
593 }
594
595+MATCHER(StreamEq, "")
596+{
597+ return (std::get<0>(arg).stream == std::get<1>(arg).stream) &&
598+ (std::get<0>(arg).displacement == std::get<1>(arg).displacement);
599+}
600+
601+TEST_F(ApplicationSession, sets_and_looks_up_surface_streams)
602+{
603+ using namespace testing;
604+ NiceMock<MockBufferStreamFactory> mock_bufferstream_factory;
605+ NiceMock<MockSurfaceFactory> mock_surface_factory;
606+
607+ auto mock_surface = make_mock_surface();
608+ EXPECT_CALL(mock_surface_factory, create_surface(_,_))
609+ .WillOnce(Return(mock_surface));
610+
611+ std::array<std::shared_ptr<mc::BufferStream>,3> streams {{
612+ std::make_shared<mtd::StubBufferStream>(),
613+ std::make_shared<mtd::StubBufferStream>(),
614+ std::make_shared<mtd::StubBufferStream>()
615+ }};
616+ EXPECT_CALL(mock_bufferstream_factory, create_buffer_stream(_))
617+ .WillOnce(Return(streams[0]))
618+ .WillOnce(Return(streams[1]))
619+ .WillOnce(Return(streams[2]));
620+
621+ auto stream_properties = mg::BufferProperties{{8,8}, mir_pixel_format_argb_8888, mg::BufferUsage::hardware};
622+ auto session = make_application_session(
623+ mt::fake_shared(mock_bufferstream_factory),
624+ mt::fake_shared(mock_surface_factory));
625+ auto stream_id0 = mf::BufferStreamId(session->create_surface(ms::a_surface().of_position({1,1})).as_value());
626+ auto stream_id1 = session->create_buffer_stream(stream_properties);
627+ auto stream_id2 = session->create_buffer_stream(stream_properties);
628+
629+ std::list<ms::StreamInfo> info {
630+ {streams[2], geom::Displacement{0,3}},
631+ {streams[0], geom::Displacement{-1,1}},
632+ {streams[1], geom::Displacement{0,2}}
633+ };
634+ EXPECT_CALL(*mock_surface, set_streams(Pointwise(StreamEq(), info)));
635+ session->configure_streams(*mock_surface, {
636+ {stream_id2, geom::Displacement{0,3}},
637+ {stream_id0, geom::Displacement{-1,1}},
638+ {stream_id1, geom::Displacement{0,2}}
639+ });
640+}
641+
642 TEST_F(ApplicationSession, buffer_stream_constructed_with_requested_parameters)
643 {
644 using namespace ::testing;

Subscribers

People subscribed via source and target branches