Merge lp:~kdub/mir/android-multimonitor into lp:mir
- android-multimonitor
- Merge into development-branch
Status: | Merged |
---|---|
Approved by: | Kevin DuBois |
Approved revision: | no longer in the source branch. |
Merged at revision: | 2356 |
Proposed branch: | lp:~kdub/mir/android-multimonitor |
Merge into: | lp:mir |
Prerequisite: | lp:~kdub/mir/display-groups |
Diff against target: |
1345 lines (+531/-208) 29 files modified
src/platforms/android/server/CMakeLists.txt (+1/-0) src/platforms/android/server/configurable_display_buffer.h (+4/-5) src/platforms/android/server/display.cpp (+40/-41) src/platforms/android/server/display.h (+3/-3) src/platforms/android/server/display_buffer.cpp (+14/-13) src/platforms/android/server/display_buffer.h (+3/-3) src/platforms/android/server/display_device.h (+10/-5) src/platforms/android/server/display_group.cpp (+81/-0) src/platforms/android/server/display_group.h (+62/-0) src/platforms/android/server/fb_device.cpp (+9/-5) src/platforms/android/server/fb_device.h (+1/-5) src/platforms/android/server/hwc_blanking_control.cpp (+13/-2) src/platforms/android/server/hwc_device.cpp (+52/-36) src/platforms/android/server/hwc_device.h (+1/-5) src/platforms/android/server/hwc_fb_device.cpp (+10/-5) src/platforms/android/server/hwc_fb_device.h (+1/-5) src/platforms/android/server/swapping_gl_context.h (+2/-0) tests/include/mir_test_doubles/mock_display_device.h (+1/-5) tests/include/mir_test_doubles/mock_swapping_gl_context.h (+2/-0) tests/include/mir_test_doubles/stub_swapping_gl_context.h (+2/-0) tests/unit-tests/graphics/android/CMakeLists.txt (+1/-0) tests/unit-tests/graphics/android/hwc_struct_helpers.h (+18/-5) tests/unit-tests/graphics/android/test_display.cpp (+11/-4) tests/unit-tests/graphics/android/test_display_buffer.cpp (+12/-35) tests/unit-tests/graphics/android/test_display_group.cpp (+85/-0) tests/unit-tests/graphics/android/test_fb_device.cpp (+3/-2) tests/unit-tests/graphics/android/test_hwc_configuration.cpp (+15/-0) tests/unit-tests/graphics/android/test_hwc_device.cpp (+72/-23) tests/unit-tests/graphics/android/test_hwc_fb_device.cpp (+2/-1) |
To merge this branch: | bzr merge lp:~kdub/mir/android-multimonitor |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Alexandros Frantzis (community) | Approve | ||
Alan Griffiths | Approve | ||
Review via email:
|
Commit message
android: post to the external display over MHL/Slimport when present.
Description of the change
android: post to the external display over MHL/Slimport when present.
Last branch in the android multimonitor line of work!
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Alan Griffiths (alan-griffiths) wrote : | # |
I'm not set up to test the code, but it looks reasonable[1].
[1] That is reasonable apart from the pre-existing question of why we have this primary + (optional) external assumption rather than a list of polymorphic things.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Kevin DuBois (kdub) wrote : | # |
> [1] That is reasonable apart from the pre-existing question of why we have
> this primary + (optional) external assumption rather than a list of
> polymorphic things.
Assuming you mean in some of the internals of the android classes, this is the next thing to sort out in a clean up pass.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2307
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2308
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Alexandros Frantzis (afrantzis) wrote : | # |
139 + if (config.
140 + displays.
141 + create_
142 + display_device,
143 + mga::DisplayNam
144 + *display_
145 + external_attribs,
146 + gl_program_factory,
147 + gl_context,
148 + mga::OverlayOpt
Should we be creating a new display buffer every time we call for_each_
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Kevin DuBois (kdub) wrote : | # |
@alf, good catch, it was wasteful to create the object every time.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Kevin DuBois (kdub) wrote : | # |
> @alf, good catch, it was wasteful to create the object every time.
Ah, also there's some reshuffling of the display configuration object's dependencies that are needed as follow-up/clean-up. (such as removing external/primary designations where we can, and merging the DisplayGroups and the configuration)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Alexandros Frantzis (afrantzis) wrote : | # |
Looks good.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2310
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'src/platforms/android/server/CMakeLists.txt' |
2 | --- src/platforms/android/server/CMakeLists.txt 2015-02-20 12:11:25 +0000 |
3 | +++ src/platforms/android/server/CMakeLists.txt 2015-03-02 14:38:17 +0000 |
4 | @@ -17,6 +17,7 @@ |
5 | android_buffer_allocator.cpp |
6 | buffer.cpp |
7 | display.cpp |
8 | + display_group.cpp |
9 | display_configuration.cpp |
10 | display_buffer.cpp |
11 | hal_component_factory.cpp |
12 | |
13 | === modified file 'src/platforms/android/server/configurable_display_buffer.h' |
14 | --- src/platforms/android/server/configurable_display_buffer.h 2015-02-26 03:20:09 +0000 |
15 | +++ src/platforms/android/server/configurable_display_buffer.h 2015-03-02 14:38:17 +0000 |
16 | @@ -19,9 +19,8 @@ |
17 | #ifndef MIR_GRAPHICS_ANDROID_CONFIGURABLE_DISPLAY_BUFFER_H_ |
18 | #define MIR_GRAPHICS_ANDROID_CONFIGURABLE_DISPLAY_BUFFER_H_ |
19 | |
20 | -#include "mir/graphics/display.h" |
21 | #include "mir/graphics/display_buffer.h" |
22 | -#include "mir/graphics/display_configuration.h" |
23 | +#include "display_device.h" |
24 | |
25 | namespace mir |
26 | { |
27 | @@ -30,12 +29,12 @@ |
28 | namespace android |
29 | { |
30 | |
31 | -//TODO: break this dependency, android displaybuffers shouldn't be their own DisplaySyncGroups |
32 | -class ConfigurableDisplayBuffer : public graphics::DisplayBuffer, |
33 | - public graphics::DisplaySyncGroup |
34 | +class ConfigurableDisplayBuffer : public graphics::DisplayBuffer |
35 | { |
36 | public: |
37 | virtual void configure(MirPowerMode power_mode, MirOrientation orientation) = 0; |
38 | + virtual DisplayContents contents() = 0; |
39 | + virtual MirPowerMode power_mode() const = 0; |
40 | }; |
41 | |
42 | } |
43 | |
44 | === modified file 'src/platforms/android/server/display.cpp' |
45 | --- src/platforms/android/server/display.cpp 2015-02-26 03:20:09 +0000 |
46 | +++ src/platforms/android/server/display.cpp 2015-03-02 14:38:17 +0000 |
47 | @@ -147,30 +147,33 @@ |
48 | mir_power_mode_off), |
49 | gl_context{config.primary().current_format, *gl_config, *display_report}, |
50 | display_device(display_buffer_builder->create_display_device()), |
51 | - primary_db{create_display_buffer( |
52 | - display_device, |
53 | - mga::DisplayName::primary, |
54 | - *display_buffer_builder, |
55 | - primary_attribs, |
56 | - gl_program_factory, |
57 | - gl_context, |
58 | - overlay_option)}, |
59 | display_change_pipe(new DisplayChangePipe), |
60 | - gl_program_factory(gl_program_factory) |
61 | -{ |
62 | - //Some drivers (depending on kernel state) incorrectly report an error code indicating that the display is already on. Ignore the first failure. |
63 | - set_powermode_all_displays(*hwc_config, config, mir_power_mode_on); |
64 | - |
65 | - if (config.external().connected) |
66 | - { |
67 | - external_db = create_display_buffer( |
68 | + gl_program_factory(gl_program_factory), |
69 | + displays( |
70 | + display_device, |
71 | + create_display_buffer( |
72 | display_device, |
73 | - mga::DisplayName::external, |
74 | + mga::DisplayName::primary, |
75 | *display_buffer_builder, |
76 | - external_attribs, |
77 | + primary_attribs, |
78 | gl_program_factory, |
79 | gl_context, |
80 | - mga::OverlayOptimization::disabled); |
81 | + overlay_option)) |
82 | +{ |
83 | + //Some drivers (depending on kernel state) incorrectly report an error code indicating that the display is already on. Ignore the first failure. |
84 | + set_powermode_all_displays(*hwc_config, config, mir_power_mode_on); |
85 | + |
86 | + if (config.external().connected) |
87 | + { |
88 | + displays.add(mga::DisplayName::external, |
89 | + create_display_buffer( |
90 | + display_device, |
91 | + mga::DisplayName::external, |
92 | + *display_buffer_builder, |
93 | + external_attribs, |
94 | + gl_program_factory, |
95 | + gl_context, |
96 | + mga::OverlayOptimization::disabled)); |
97 | } |
98 | |
99 | display_report->report_successful_setup_of_native_resources(); |
100 | @@ -190,8 +193,8 @@ |
101 | { |
102 | if (configuration_dirty) |
103 | { |
104 | - auto attribs = hwc_config->active_attribs_for(mga::DisplayName::external); |
105 | - if (attribs.connected) |
106 | + external_attribs = hwc_config->active_attribs_for(mga::DisplayName::external); |
107 | + if (external_attribs.connected) |
108 | power_mode(mga::DisplayName::external, *hwc_config, config.external(), mir_power_mode_on); |
109 | else |
110 | config.external().power_mode = mir_power_mode_off; |
111 | @@ -199,34 +202,30 @@ |
112 | config = mga::DisplayConfiguration( |
113 | hwc_config->active_attribs_for(mga::DisplayName::primary), |
114 | config.primary().power_mode, |
115 | - attribs, |
116 | + external_attribs, |
117 | config.external().power_mode); |
118 | configuration_dirty = false; |
119 | - |
120 | - if (config.external().connected) |
121 | - external_db = create_display_buffer( |
122 | - display_device, |
123 | - mga::DisplayName::external, |
124 | - *display_buffer_builder, |
125 | - attribs, |
126 | - gl_program_factory, |
127 | - gl_context, |
128 | - mga::OverlayOptimization::disabled); |
129 | - else |
130 | - external_db.reset(); |
131 | } |
132 | - |
133 | } |
134 | |
135 | void mga::Display::for_each_display_sync_group(std::function<void(mg::DisplaySyncGroup&)> const& f) |
136 | { |
137 | std::lock_guard<decltype(configuration_mutex)> lock{configuration_mutex}; |
138 | update_configuration(lock); |
139 | + if ((config.external().connected) && !displays.display_present(mga::DisplayName::external)) |
140 | + displays.add(mga::DisplayName::external, |
141 | + create_display_buffer( |
142 | + display_device, |
143 | + mga::DisplayName::external, |
144 | + *display_buffer_builder, |
145 | + external_attribs, |
146 | + gl_program_factory, |
147 | + gl_context, |
148 | + mga::OverlayOptimization::disabled)); |
149 | + if ((!config.external().connected) && displays.display_present(mga::DisplayName::external)) |
150 | + displays.remove(mga::DisplayName::external); |
151 | |
152 | - if (external_db && config.external().power_mode == mir_power_mode_on) |
153 | - f(*external_db); |
154 | - if (config.primary().power_mode == mir_power_mode_on) |
155 | - f(*primary_db); |
156 | + f(displays); |
157 | } |
158 | |
159 | std::unique_ptr<mg::DisplayConfiguration> mga::Display::configuration() const |
160 | @@ -252,12 +251,12 @@ |
161 | if (config.primary().id == output.id) |
162 | { |
163 | power_mode(mga::DisplayName::primary, *hwc_config, config.primary(), output.power_mode); |
164 | - primary_db->configure(output.power_mode, output.orientation); |
165 | + displays.configure(mga::DisplayName::primary, output.power_mode, output.orientation); |
166 | } |
167 | else if (config.external().connected) |
168 | { |
169 | power_mode(mga::DisplayName::external, *hwc_config, config.external(), output.power_mode); |
170 | - if (external_db) external_db->configure(output.power_mode, output.orientation); |
171 | + displays.configure(mga::DisplayName::external, output.power_mode, output.orientation); |
172 | } |
173 | }); |
174 | } |
175 | |
176 | === modified file 'src/platforms/android/server/display.h' |
177 | --- src/platforms/android/server/display.h 2015-02-26 03:20:09 +0000 |
178 | +++ src/platforms/android/server/display.h 2015-03-02 14:38:17 +0000 |
179 | @@ -21,6 +21,7 @@ |
180 | |
181 | #include "mir/graphics/display.h" |
182 | #include "gl_context.h" |
183 | +#include "display_group.h" |
184 | #include "hwc_configuration.h" |
185 | #include "display_configuration.h" |
186 | #include "overlay_optimization.h" |
187 | @@ -86,14 +87,13 @@ |
188 | std::unique_ptr<HwcConfiguration> const hwc_config; |
189 | ConfigChangeSubscription const hotplug_subscription; |
190 | DisplayAttribs const primary_attribs; //TODO: could be removed, really only useful in construction |
191 | - DisplayAttribs const external_attribs; //TODO: could be removed, really only useful in construction |
192 | + DisplayAttribs mutable external_attribs; //TODO: could be removed, really only useful in construction |
193 | DisplayConfiguration mutable config; |
194 | PbufferGLContext gl_context; |
195 | std::shared_ptr<DisplayDevice> display_device; |
196 | - std::unique_ptr<ConfigurableDisplayBuffer> const primary_db; |
197 | - std::unique_ptr<ConfigurableDisplayBuffer> mutable external_db; |
198 | std::unique_ptr<DisplayChangePipe> display_change_pipe; |
199 | std::shared_ptr<GLProgramFactory> const gl_program_factory; |
200 | + DisplayGroup mutable displays; |
201 | |
202 | void update_configuration(std::lock_guard<decltype(configuration_mutex)> const&) const; |
203 | }; |
204 | |
205 | === modified file 'src/platforms/android/server/display_buffer.cpp' |
206 | --- src/platforms/android/server/display_buffer.cpp 2015-02-26 03:20:09 +0000 |
207 | +++ src/platforms/android/server/display_buffer.cpp 2015-03-02 14:38:17 +0000 |
208 | @@ -49,7 +49,8 @@ |
209 | gl_context{shared_gl_context, fb_bundle, native_window}, |
210 | overlay_program{program_factory, gl_context, geom::Rectangle{{0,0},fb_bundle->fb_size()}}, |
211 | overlay_enabled{overlay_option == mga::OverlayOptimization::enabled}, |
212 | - orientation_{orientation} |
213 | + orientation_{orientation}, |
214 | + power_mode_{mir_power_mode_on} |
215 | { |
216 | } |
217 | |
218 | @@ -88,14 +89,12 @@ |
219 | if (!needs_commit) |
220 | return false; |
221 | |
222 | - display_device->commit(display_name, *layer_list, gl_context, overlay_program); |
223 | return true; |
224 | } |
225 | |
226 | void mga::DisplayBuffer::gl_swap_buffers() |
227 | { |
228 | layer_list->update_list({}); |
229 | - display_device->commit(display_name, *layer_list, gl_context, overlay_program); |
230 | } |
231 | |
232 | MirOrientation mga::DisplayBuffer::orientation() const |
233 | @@ -114,18 +113,20 @@ |
234 | return false; |
235 | } |
236 | |
237 | -void mga::DisplayBuffer::for_each_display_buffer(std::function<void(mg::DisplayBuffer&)> const& f) |
238 | -{ |
239 | - f(*this); |
240 | -} |
241 | - |
242 | -void mga::DisplayBuffer::post() |
243 | -{ |
244 | -} |
245 | - |
246 | void mga::DisplayBuffer::configure(MirPowerMode power_mode, MirOrientation orientation) |
247 | { |
248 | - if (power_mode != mir_power_mode_on) |
249 | + power_mode_ = power_mode; |
250 | + if (power_mode_ != mir_power_mode_on) |
251 | display_device->content_cleared(); |
252 | orientation_ = orientation; |
253 | } |
254 | + |
255 | +mga::DisplayContents mga::DisplayBuffer::contents() |
256 | +{ |
257 | + return mga::DisplayContents{display_name, *layer_list, gl_context, overlay_program}; |
258 | +} |
259 | + |
260 | +MirPowerMode mga::DisplayBuffer::power_mode() const |
261 | +{ |
262 | + return power_mode_; |
263 | +} |
264 | |
265 | === modified file 'src/platforms/android/server/display_buffer.h' |
266 | --- src/platforms/android/server/display_buffer.h 2015-02-26 03:20:09 +0000 |
267 | +++ src/platforms/android/server/display_buffer.h 2015-03-02 14:38:17 +0000 |
268 | @@ -61,12 +61,11 @@ |
269 | void gl_swap_buffers() override; |
270 | bool post_renderables_if_optimizable(RenderableList const& renderlist) override; |
271 | |
272 | - void for_each_display_buffer(std::function<void(graphics::DisplayBuffer&)> const& f) override; |
273 | - void post() override; |
274 | - |
275 | MirOrientation orientation() const override; |
276 | bool uses_alpha() const override; |
277 | void configure(MirPowerMode power_mode, MirOrientation orientation) override; |
278 | + DisplayContents contents() override; |
279 | + MirPowerMode power_mode() const override; |
280 | private: |
281 | DisplayName display_name; |
282 | std::unique_ptr<LayerList> layer_list; |
283 | @@ -77,6 +76,7 @@ |
284 | HWCFallbackGLRenderer overlay_program; |
285 | bool overlay_enabled; |
286 | MirOrientation orientation_; |
287 | + MirPowerMode power_mode_; |
288 | }; |
289 | |
290 | } |
291 | |
292 | === modified file 'src/platforms/android/server/display_device.h' |
293 | --- src/platforms/android/server/display_device.h 2015-02-26 03:20:09 +0000 |
294 | +++ src/platforms/android/server/display_device.h 2015-03-02 14:38:17 +0000 |
295 | @@ -23,6 +23,7 @@ |
296 | #include "mir_toolkit/common.h" |
297 | #include "display_name.h" |
298 | #include <EGL/egl.h> |
299 | +#include <list> |
300 | |
301 | namespace mir |
302 | { |
303 | @@ -37,6 +38,14 @@ |
304 | class SwappingGLContext; |
305 | class LayerList; |
306 | |
307 | +struct DisplayContents |
308 | +{ |
309 | + DisplayName name; |
310 | + LayerList& list; |
311 | + SwappingGLContext& context; |
312 | + RenderableListCompositor& compositor; |
313 | +}; |
314 | + |
315 | class DisplayDevice |
316 | { |
317 | public: |
318 | @@ -50,11 +59,7 @@ |
319 | /* post the layer list to the display, optionally drawing using the context/compositor if |
320 | * instructed to by the driver |
321 | */ |
322 | - virtual void commit( |
323 | - DisplayName display_name, |
324 | - LayerList& layer_list, |
325 | - SwappingGLContext const& context, |
326 | - RenderableListCompositor const& compositor) = 0; |
327 | + virtual void commit(std::list<DisplayContents> const& contents) = 0; |
328 | |
329 | //notify the DisplayDevice that the screen content was cleared in a way other than the above fns |
330 | virtual void content_cleared() = 0; |
331 | |
332 | === added file 'src/platforms/android/server/display_group.cpp' |
333 | --- src/platforms/android/server/display_group.cpp 1970-01-01 00:00:00 +0000 |
334 | +++ src/platforms/android/server/display_group.cpp 2015-03-02 14:38:17 +0000 |
335 | @@ -0,0 +1,81 @@ |
336 | +/* |
337 | + * Copyright © 2015 Canonical Ltd. |
338 | + * |
339 | + * This program is free software: you can redistribute it and/or modify it |
340 | + * under the terms of the GNU Lesser General Public License version 3, |
341 | + * as published by the Free Software Foundation. |
342 | + * |
343 | + * This program is distributed in the hope that it will be useful, |
344 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
345 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
346 | + * GNU Lesser General Public License for more details. |
347 | + * |
348 | + * You should have received a copy of the GNU Lesser General Public License |
349 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
350 | + * |
351 | + * Authored by: Kevin DuBois <kevin.dubois@canonical.com> |
352 | + */ |
353 | + |
354 | +#include "display_group.h" |
355 | +#include "configurable_display_buffer.h" |
356 | +#include <boost/throw_exception.hpp> |
357 | +#include <stdexcept> |
358 | + |
359 | +namespace mg = mir::graphics; |
360 | +namespace mga = mir::graphics::android; |
361 | + |
362 | +mga::DisplayGroup::DisplayGroup( |
363 | + std::shared_ptr<mga::DisplayDevice> const& device, |
364 | + std::unique_ptr<mga::ConfigurableDisplayBuffer> primary_buffer) : |
365 | + device(device) |
366 | +{ |
367 | + dbs.emplace(std::make_pair(mga::DisplayName::primary, std::move(primary_buffer))); |
368 | +} |
369 | + |
370 | +void mga::DisplayGroup::for_each_display_buffer(std::function<void(mg::DisplayBuffer&)> const& f) |
371 | +{ |
372 | + std::unique_lock<decltype(guard)> lk(guard); |
373 | + for(auto const& db : dbs) |
374 | + if (db.second->power_mode() != mir_power_mode_off) |
375 | + f(*db.second); |
376 | +} |
377 | + |
378 | +void mga::DisplayGroup::add(DisplayName name, std::unique_ptr<ConfigurableDisplayBuffer> buffer) |
379 | +{ |
380 | + std::unique_lock<decltype(guard)> lk(guard); |
381 | + dbs.emplace(std::make_pair(name, std::move(buffer))); |
382 | +} |
383 | + |
384 | +void mga::DisplayGroup::remove(DisplayName name) |
385 | +{ |
386 | + if (name == mga::DisplayName::primary) |
387 | + BOOST_THROW_EXCEPTION(std::logic_error("cannot remove primary display")); |
388 | + |
389 | + std::unique_lock<decltype(guard)> lk(guard); |
390 | + auto it = dbs.find(name); |
391 | + if (it != dbs.end()) |
392 | + dbs.erase(it); |
393 | +} |
394 | + |
395 | +bool mga::DisplayGroup::display_present(DisplayName name) const |
396 | +{ |
397 | + std::unique_lock<decltype(guard)> lk(guard); |
398 | + return (dbs.end() != dbs.find(name)); |
399 | +} |
400 | + |
401 | +void mga::DisplayGroup::configure(DisplayName name, MirPowerMode mode, MirOrientation orientation) |
402 | +{ |
403 | + std::unique_lock<decltype(guard)> lk(guard); |
404 | + auto it = dbs.find(name); |
405 | + if (it != dbs.end()) |
406 | + it->second->configure(mode, orientation); |
407 | +} |
408 | + |
409 | +void mga::DisplayGroup::post() |
410 | +{ |
411 | + std::list<DisplayContents> contents; |
412 | + std::unique_lock<decltype(guard)> lk(guard); |
413 | + for(auto const& db : dbs) |
414 | + contents.emplace_back(db.second->contents()); |
415 | + device->commit(contents); |
416 | +} |
417 | |
418 | === added file 'src/platforms/android/server/display_group.h' |
419 | --- src/platforms/android/server/display_group.h 1970-01-01 00:00:00 +0000 |
420 | +++ src/platforms/android/server/display_group.h 2015-03-02 14:38:17 +0000 |
421 | @@ -0,0 +1,62 @@ |
422 | +/* |
423 | + * Copyright © 2015 Canonical Ltd. |
424 | + * |
425 | + * This program is free software: you can redistribute it and/or modify it |
426 | + * under the terms of the GNU Lesser General Public License version 3, |
427 | + * as published by the Free Software Foundation. |
428 | + * |
429 | + * This program is distributed in the hope that it will be useful, |
430 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
431 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
432 | + * GNU Lesser General Public License for more details. |
433 | + * |
434 | + * You should have received a copy of the GNU Lesser General Public License |
435 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
436 | + * |
437 | + * Authored by: Kevin DuBois <kevin.dubois@canonical.com> |
438 | + */ |
439 | + |
440 | +#ifndef MIR_GRAPHICS_ANDROID_DISPLAY_GROUP_H_ |
441 | +#define MIR_GRAPHICS_ANDROID_DISPLAY_GROUP_H_ |
442 | + |
443 | +#include "mir_toolkit/common.h" |
444 | +#include "mir/graphics/display.h" |
445 | +#include "display_name.h" |
446 | +#include <map> |
447 | +#include <mutex> |
448 | + |
449 | +namespace mir |
450 | +{ |
451 | +namespace graphics |
452 | +{ |
453 | +namespace android |
454 | +{ |
455 | +class ConfigurableDisplayBuffer; |
456 | +class DisplayDevice; |
457 | + |
458 | +class DisplayGroup : public graphics::DisplaySyncGroup |
459 | +{ |
460 | +public: |
461 | + DisplayGroup( |
462 | + std::shared_ptr<DisplayDevice> const& device, |
463 | + std::unique_ptr<ConfigurableDisplayBuffer> primary_buffer); |
464 | + |
465 | + void for_each_display_buffer(std::function<void(graphics::DisplayBuffer&)> const& f) override; |
466 | + void post() override; |
467 | + |
468 | + void add(DisplayName name, std::unique_ptr<ConfigurableDisplayBuffer> buffer); |
469 | + void remove(DisplayName name); |
470 | + void configure(DisplayName name, MirPowerMode, MirOrientation); |
471 | + bool display_present(DisplayName name) const; |
472 | + |
473 | +private: |
474 | + std::mutex mutable guard; |
475 | + std::shared_ptr<DisplayDevice> const device; |
476 | + std::map<DisplayName, std::unique_ptr<ConfigurableDisplayBuffer>> dbs; |
477 | +}; |
478 | + |
479 | +} |
480 | +} |
481 | +} |
482 | + |
483 | +#endif /* MIR_GRAPHICS_ANDROID_DISPLAY_GROUP_H_ */ |
484 | |
485 | === modified file 'src/platforms/android/server/fb_device.cpp' |
486 | --- src/platforms/android/server/fb_device.cpp 2015-02-26 03:20:09 +0000 |
487 | +++ src/platforms/android/server/fb_device.cpp 2015-03-02 14:38:17 +0000 |
488 | @@ -28,6 +28,7 @@ |
489 | |
490 | #include <boost/throw_exception.hpp> |
491 | #include <stdexcept> |
492 | +#include <algorithm> |
493 | |
494 | namespace mg = mir::graphics; |
495 | namespace mga=mir::graphics::android; |
496 | @@ -77,12 +78,15 @@ |
497 | { |
498 | } |
499 | |
500 | -void mga::FBDevice::commit( |
501 | - DisplayName, |
502 | - LayerList&, |
503 | - SwappingGLContext const& context, |
504 | - RenderableListCompositor const&) |
505 | +void mga::FBDevice::commit(std::list<DisplayContents> const& contents) |
506 | { |
507 | + auto primary_contents = std::find_if(contents.begin(), contents.end(), |
508 | + [](mga::DisplayContents const& c) { |
509 | + return (c.name == mga::DisplayName::primary); |
510 | + }); |
511 | + if (primary_contents == contents.end()) return; |
512 | + auto& context = primary_contents->context; |
513 | + |
514 | context.swap_buffers(); |
515 | auto const& buffer = context.last_rendered_buffer(); |
516 | auto native_buffer = buffer->native_buffer_handle(); |
517 | |
518 | === modified file 'src/platforms/android/server/fb_device.h' |
519 | --- src/platforms/android/server/fb_device.h 2015-02-26 03:20:09 +0000 |
520 | +++ src/platforms/android/server/fb_device.h 2015-03-02 14:38:17 +0000 |
521 | @@ -48,11 +48,7 @@ |
522 | FBDevice(std::shared_ptr<framebuffer_device_t> const& fbdev); |
523 | |
524 | bool compatible_renderlist(RenderableList const& renderlist) override; |
525 | - void commit( |
526 | - DisplayName, |
527 | - LayerList&, |
528 | - SwappingGLContext const& context, |
529 | - RenderableListCompositor const& list_compositor) override; |
530 | + void commit(std::list<DisplayContents> const& contents) override; |
531 | |
532 | private: |
533 | std::shared_ptr<framebuffer_device_t> const fb_device; |
534 | |
535 | === modified file 'src/platforms/android/server/hwc_blanking_control.cpp' |
536 | --- src/platforms/android/server/hwc_blanking_control.cpp 2015-02-13 06:12:34 +0000 |
537 | +++ src/platforms/android/server/hwc_blanking_control.cpp 2015-03-02 14:38:17 +0000 |
538 | @@ -18,6 +18,7 @@ |
539 | |
540 | #include "hwc_configuration.h" |
541 | #include "hwc_wrapper.h" |
542 | +#include "mir/raii.h" |
543 | #include "android_format_conversion-inl.h" |
544 | #include <EGL/egl.h> |
545 | #include <EGL/eglext.h> |
546 | @@ -145,7 +146,17 @@ |
547 | } |
548 | |
549 | mga::ConfigChangeSubscription mga::HwcBlankingControl::subscribe_to_config_changes( |
550 | - std::function<void()> const&) |
551 | + std::function<void()> const& hotplug) |
552 | { |
553 | - return nullptr; |
554 | + return std::make_shared< |
555 | + mir::raii::PairedCalls<std::function<void()>, std::function<void()>>>( |
556 | + [hotplug, this]{ |
557 | + hwc_device->subscribe_to_events(this, |
558 | + [](DisplayName, std::chrono::nanoseconds){}, |
559 | + [hotplug](DisplayName, bool){ hotplug(); }, |
560 | + []{}); |
561 | + }, |
562 | + [this]{ |
563 | + hwc_device->unsubscribe_from_events(this); |
564 | + }); |
565 | } |
566 | |
567 | === modified file 'src/platforms/android/server/hwc_device.cpp' |
568 | --- src/platforms/android/server/hwc_device.cpp 2015-02-26 03:20:09 +0000 |
569 | +++ src/platforms/android/server/hwc_device.cpp 2015-03-02 14:38:17 +0000 |
570 | @@ -24,6 +24,7 @@ |
571 | #include "framebuffer_bundle.h" |
572 | #include "buffer.h" |
573 | #include "hwc_fallback_gl_renderer.h" |
574 | +#include "mir/raii.h" |
575 | #include <limits> |
576 | #include <algorithm> |
577 | |
578 | @@ -79,47 +80,62 @@ |
579 | return it != onscreen_overlay_buffers.end(); |
580 | } |
581 | |
582 | -void mga::HwcDevice::commit( |
583 | - mga::DisplayName, |
584 | - mga::LayerList& hwc_list, |
585 | - SwappingGLContext const& context, |
586 | - RenderableListCompositor const& list_compositor) |
587 | +void mga::HwcDevice::commit(std::list<DisplayContents> const& contents) |
588 | { |
589 | - hwc_list.setup_fb(context.last_rendered_buffer()); |
590 | - |
591 | - hwc_wrapper->prepare({{hwc_list.native_list(), nullptr, nullptr}}); |
592 | - |
593 | - if (hwc_list.needs_swapbuffers()) |
594 | - { |
595 | - auto rejected_renderables = hwc_list.rejected_renderables(); |
596 | - if (rejected_renderables.empty()) |
597 | - context.swap_buffers(); |
598 | - else |
599 | - list_compositor.render(std::move(rejected_renderables), context); |
600 | - hwc_list.setup_fb(context.last_rendered_buffer()); |
601 | - hwc_list.swap_occurred(); |
602 | - } |
603 | - |
604 | - //setup overlays |
605 | + std::array<hwc_display_contents_1*, HWC_NUM_DISPLAY_TYPES> lists{{ nullptr, nullptr, nullptr }}; |
606 | std::vector<std::shared_ptr<mg::Buffer>> next_onscreen_overlay_buffers; |
607 | - for (auto& layer : hwc_list) |
608 | - { |
609 | - auto buffer = layer.layer.buffer(); |
610 | - if (layer.layer.is_overlay() && buffer) |
611 | - { |
612 | - if (!buffer_is_onscreen(*buffer)) |
613 | - layer.layer.set_acquirefence(); |
614 | - next_onscreen_overlay_buffers.push_back(buffer); |
615 | - } |
616 | - } |
617 | - |
618 | - hwc_wrapper->set({{hwc_list.native_list(), nullptr, nullptr}}); |
619 | + |
620 | + for (auto& content : contents) |
621 | + { |
622 | + if (content.name == mga::DisplayName::primary) |
623 | + lists[HWC_DISPLAY_PRIMARY] = content.list.native_list(); |
624 | + else if (content.name == mga::DisplayName::external) |
625 | + lists[HWC_DISPLAY_EXTERNAL] = content.list.native_list(); |
626 | + |
627 | + content.list.setup_fb(content.context.last_rendered_buffer()); |
628 | + } |
629 | + |
630 | + hwc_wrapper->prepare(lists); |
631 | + |
632 | + for (auto& content : contents) |
633 | + { |
634 | + if (content.list.needs_swapbuffers()) |
635 | + { |
636 | + auto rejected_renderables = content.list.rejected_renderables(); |
637 | + auto current_context = mir::raii::paired_calls( |
638 | + [&]{ content.context.make_current(); }, |
639 | + [&]{ content.context.release_current(); }); |
640 | + if (rejected_renderables.empty()) |
641 | + content.context.swap_buffers(); |
642 | + else |
643 | + content.compositor.render(std::move(rejected_renderables), content.context); |
644 | + content.list.setup_fb(content.context.last_rendered_buffer()); |
645 | + content.list.swap_occurred(); |
646 | + } |
647 | + |
648 | + //setup overlays |
649 | + for (auto& layer : content.list) |
650 | + { |
651 | + auto buffer = layer.layer.buffer(); |
652 | + if (layer.layer.is_overlay() && buffer) |
653 | + { |
654 | + if (!buffer_is_onscreen(*buffer)) |
655 | + layer.layer.set_acquirefence(); |
656 | + next_onscreen_overlay_buffers.push_back(buffer); |
657 | + } |
658 | + } |
659 | + } |
660 | + |
661 | + hwc_wrapper->set(lists); |
662 | onscreen_overlay_buffers = std::move(next_onscreen_overlay_buffers); |
663 | |
664 | - for (auto& it : hwc_list) |
665 | - it.layer.release_buffer(); |
666 | + for (auto& content : contents) |
667 | + { |
668 | + for (auto& it : content.list) |
669 | + it.layer.release_buffer(); |
670 | |
671 | - mir::Fd retire_fd(hwc_list.retirement_fence()); |
672 | + mir::Fd retire_fd(content.list.retirement_fence()); |
673 | + } |
674 | } |
675 | |
676 | void mga::HwcDevice::content_cleared() |
677 | |
678 | === modified file 'src/platforms/android/server/hwc_device.h' |
679 | --- src/platforms/android/server/hwc_device.h 2015-02-26 03:20:09 +0000 |
680 | +++ src/platforms/android/server/hwc_device.h 2015-03-02 14:38:17 +0000 |
681 | @@ -44,11 +44,7 @@ |
682 | HwcDevice(std::shared_ptr<HwcWrapper> const& hwc_wrapper); |
683 | |
684 | bool compatible_renderlist(RenderableList const& renderlist) override; |
685 | - void commit( |
686 | - DisplayName, |
687 | - LayerList&, |
688 | - SwappingGLContext const& context, |
689 | - RenderableListCompositor const& list_compositor) override; |
690 | + void commit(std::list<DisplayContents> const& contents) override; |
691 | void content_cleared() override; |
692 | |
693 | private: |
694 | |
695 | === modified file 'src/platforms/android/server/hwc_fb_device.cpp' |
696 | --- src/platforms/android/server/hwc_fb_device.cpp 2015-02-26 03:20:09 +0000 |
697 | +++ src/platforms/android/server/hwc_fb_device.cpp 2015-03-02 14:38:17 +0000 |
698 | @@ -30,6 +30,7 @@ |
699 | #include <boost/throw_exception.hpp> |
700 | #include <sstream> |
701 | #include <stdexcept> |
702 | +#include <algorithm> |
703 | |
704 | namespace mg = mir::graphics; |
705 | namespace mga = mir::graphics::android; |
706 | @@ -55,12 +56,16 @@ |
707 | { |
708 | } |
709 | |
710 | -void mga::HwcFbDevice::commit( |
711 | - mga::DisplayName, |
712 | - mga::LayerList& layer_list, |
713 | - SwappingGLContext const& context, |
714 | - RenderableListCompositor const&) |
715 | +void mga::HwcFbDevice::commit(std::list<DisplayContents> const& contents) |
716 | { |
717 | + auto primary_contents = std::find_if(contents.begin(), contents.end(), |
718 | + [](mga::DisplayContents const& c) { |
719 | + return (c.name == mga::DisplayName::primary); |
720 | + }); |
721 | + if (primary_contents == contents.end()) return; |
722 | + auto& layer_list = primary_contents->list; |
723 | + auto& context = primary_contents->context; |
724 | + |
725 | layer_list.setup_fb(context.last_rendered_buffer()); |
726 | |
727 | if (auto display_list = layer_list.native_list()) |
728 | |
729 | === modified file 'src/platforms/android/server/hwc_fb_device.h' |
730 | --- src/platforms/android/server/hwc_fb_device.h 2015-02-26 03:20:09 +0000 |
731 | +++ src/platforms/android/server/hwc_fb_device.h 2015-03-02 14:38:17 +0000 |
732 | @@ -42,11 +42,7 @@ |
733 | std::shared_ptr<framebuffer_device_t> const& fb_device); |
734 | |
735 | bool compatible_renderlist(RenderableList const& renderlist) override; |
736 | - void commit( |
737 | - DisplayName, |
738 | - LayerList&, |
739 | - SwappingGLContext const& context, |
740 | - RenderableListCompositor const& list_compositor) override; |
741 | + void commit(std::list<DisplayContents> const& contents) override; |
742 | |
743 | private: |
744 | void content_cleared() override; |
745 | |
746 | === modified file 'src/platforms/android/server/swapping_gl_context.h' |
747 | --- src/platforms/android/server/swapping_gl_context.h 2015-02-26 03:20:09 +0000 |
748 | +++ src/platforms/android/server/swapping_gl_context.h 2015-03-02 14:38:17 +0000 |
749 | @@ -32,6 +32,8 @@ |
750 | { |
751 | public: |
752 | virtual ~SwappingGLContext() = default; |
753 | + virtual void make_current() const = 0; |
754 | + virtual void release_current() const = 0; |
755 | virtual void swap_buffers() const = 0; |
756 | virtual std::shared_ptr<Buffer> last_rendered_buffer() const = 0; |
757 | |
758 | |
759 | === modified file 'tests/include/mir_test_doubles/mock_display_device.h' |
760 | --- tests/include/mir_test_doubles/mock_display_device.h 2015-02-26 03:20:09 +0000 |
761 | +++ tests/include/mir_test_doubles/mock_display_device.h 2015-03-02 14:38:17 +0000 |
762 | @@ -37,11 +37,7 @@ |
763 | public: |
764 | ~MockDisplayDevice() noexcept {} |
765 | MOCK_METHOD0(content_cleared, void()); |
766 | - MOCK_METHOD4(commit, void( |
767 | - graphics::android::DisplayName, |
768 | - graphics::android::LayerList&, |
769 | - graphics::android::SwappingGLContext const&, |
770 | - graphics::android::RenderableListCompositor const&)); |
771 | + MOCK_METHOD1(commit, void(std::list<graphics::android::DisplayContents> const&)); |
772 | MOCK_METHOD1(compatible_renderlist, bool( |
773 | graphics::RenderableList const&)); |
774 | }; |
775 | |
776 | === modified file 'tests/include/mir_test_doubles/mock_swapping_gl_context.h' |
777 | --- tests/include/mir_test_doubles/mock_swapping_gl_context.h 2015-02-26 03:20:09 +0000 |
778 | +++ tests/include/mir_test_doubles/mock_swapping_gl_context.h 2015-03-02 14:38:17 +0000 |
779 | @@ -31,6 +31,8 @@ |
780 | struct MockSwappingGLContext : public graphics::android::SwappingGLContext |
781 | { |
782 | MOCK_CONST_METHOD0(swap_buffers, void()); |
783 | + MOCK_CONST_METHOD0(make_current, void()); |
784 | + MOCK_CONST_METHOD0(release_current, void()); |
785 | MOCK_CONST_METHOD0(last_rendered_buffer, std::shared_ptr<graphics::Buffer>()); |
786 | }; |
787 | |
788 | |
789 | === modified file 'tests/include/mir_test_doubles/stub_swapping_gl_context.h' |
790 | --- tests/include/mir_test_doubles/stub_swapping_gl_context.h 2015-02-26 03:20:09 +0000 |
791 | +++ tests/include/mir_test_doubles/stub_swapping_gl_context.h 2015-03-02 14:38:17 +0000 |
792 | @@ -40,6 +40,8 @@ |
793 | StubSwappingGLContext(std::make_shared<StubBuffer>()) |
794 | { |
795 | } |
796 | + void make_current() const {} |
797 | + void release_current() const {} |
798 | void swap_buffers() const {} |
799 | std::shared_ptr<graphics::Buffer> last_rendered_buffer() const |
800 | { |
801 | |
802 | === modified file 'tests/unit-tests/graphics/android/CMakeLists.txt' |
803 | --- tests/unit-tests/graphics/android/CMakeLists.txt 2015-02-13 06:12:34 +0000 |
804 | +++ tests/unit-tests/graphics/android/CMakeLists.txt 2015-03-02 14:38:17 +0000 |
805 | @@ -5,6 +5,7 @@ |
806 | ${CMAKE_CURRENT_SOURCE_DIR}/test_android_buffer_allocator.cpp |
807 | ${CMAKE_CURRENT_SOURCE_DIR}/test_buffer_tex_bind.cpp |
808 | ${CMAKE_CURRENT_SOURCE_DIR}/test_display.cpp |
809 | + ${CMAKE_CURRENT_SOURCE_DIR}/test_display_group.cpp |
810 | ${CMAKE_CURRENT_SOURCE_DIR}/test_android_alloc_adaptor.cpp |
811 | ${CMAKE_CURRENT_SOURCE_DIR}/test_android_buffer_allocator.cpp |
812 | ${CMAKE_CURRENT_SOURCE_DIR}/test_hwc_device.cpp |
813 | |
814 | === modified file 'tests/unit-tests/graphics/android/hwc_struct_helpers.h' |
815 | --- tests/unit-tests/graphics/android/hwc_struct_helpers.h 2015-02-26 03:20:09 +0000 |
816 | +++ tests/unit-tests/graphics/android/hwc_struct_helpers.h 2015-03-02 14:38:17 +0000 |
817 | @@ -108,23 +108,36 @@ |
818 | return !(::testing::Test::HasFailure()); |
819 | } |
820 | |
821 | -MATCHER_P(MatchesPrimaryList, value, std::string("")) |
822 | +MATCHER_P(MatchesList, value, std::string("")) |
823 | { |
824 | - if (arg[0] == nullptr) |
825 | + if (arg == nullptr) |
826 | return (value.empty()); |
827 | - auto const& primary_list = *arg[0]; |
828 | + auto const& list = *arg; |
829 | |
830 | - EXPECT_EQ(primary_list.numHwLayers, value.size()); |
831 | + EXPECT_EQ(list.numHwLayers, value.size()); |
832 | auto i = 0u; |
833 | for(auto layer : value) |
834 | { |
835 | - EXPECT_THAT(primary_list.hwLayers[i++], MatchesLegacyLayer(*layer)); |
836 | + EXPECT_THAT(list.hwLayers[i++], MatchesLegacyLayer(*layer)); |
837 | if (::testing::Test::HasFailure()) |
838 | return false; |
839 | } |
840 | return !(::testing::Test::HasFailure()); |
841 | } |
842 | |
843 | +MATCHER_P(MatchesPrimaryList, value, std::string("")) |
844 | +{ |
845 | + EXPECT_THAT(arg[0], MatchesList(value)); |
846 | + return !(::testing::Test::HasFailure()); |
847 | +} |
848 | + |
849 | +MATCHER_P2(MatchesLists, primary, external, std::string("")) |
850 | +{ |
851 | + EXPECT_THAT(arg[0], MatchesList(primary)); |
852 | + EXPECT_THAT(arg[1], MatchesList(external)); |
853 | + return !(::testing::Test::HasFailure()); |
854 | +} |
855 | + |
856 | MATCHER_P3(MatchesListWithEglFields, value, dpy, sur, std::string("")) |
857 | { |
858 | if (arg[0] == nullptr) |
859 | |
860 | === modified file 'tests/unit-tests/graphics/android/test_display.cpp' |
861 | --- tests/unit-tests/graphics/android/test_display.cpp 2015-02-26 03:20:09 +0000 |
862 | +++ tests/unit-tests/graphics/android/test_display.cpp 2015-03-02 14:38:17 +0000 |
863 | @@ -656,28 +656,35 @@ |
864 | null_display_report, |
865 | mga::OverlayOptimization::enabled); |
866 | |
867 | - auto group_count = 0; |
868 | - auto db_group_counter = [&](mg::DisplaySyncGroup&) { |
869 | + auto group_count = 0; |
870 | + auto db_count = 0; |
871 | + auto db_group_counter = [&](mg::DisplaySyncGroup& group) { |
872 | group_count++; |
873 | + group.for_each_display_buffer([&](mg::DisplayBuffer&) {db_count++;}); |
874 | }; |
875 | display.for_each_display_sync_group(db_group_counter); |
876 | - EXPECT_THAT(group_count, Eq(2)); |
877 | + EXPECT_THAT(group_count, Eq(1)); |
878 | + EXPECT_THAT(db_count, Eq(2)); |
879 | |
880 | //hotplug external away |
881 | external_connected = false; |
882 | hotplug_fn(); |
883 | |
884 | group_count = 0; |
885 | + db_count = 0; |
886 | display.for_each_display_sync_group(db_group_counter); |
887 | EXPECT_THAT(group_count, Eq(1)); |
888 | + EXPECT_THAT(db_count, Eq(1)); |
889 | |
890 | //hotplug external back |
891 | external_connected = true; |
892 | hotplug_fn(); |
893 | |
894 | group_count = 0; |
895 | + db_count = 0; |
896 | display.for_each_display_sync_group(db_group_counter); |
897 | - EXPECT_THAT(group_count, Eq(2)); |
898 | + EXPECT_THAT(group_count, Eq(1)); |
899 | + EXPECT_THAT(db_count, Eq(2)); |
900 | } |
901 | |
902 | TEST_F(Display, turns_external_display_on_with_hotplug) |
903 | |
904 | === modified file 'tests/unit-tests/graphics/android/test_display_buffer.cpp' |
905 | --- tests/unit-tests/graphics/android/test_display_buffer.cpp 2015-02-26 03:20:09 +0000 |
906 | +++ tests/unit-tests/graphics/android/test_display_buffer.cpp 2015-03-02 14:38:17 +0000 |
907 | @@ -85,27 +85,6 @@ |
908 | }; |
909 | } |
910 | |
911 | -TEST_F(DisplayBuffer, can_post_update_with_gl_only) |
912 | -{ |
913 | - using namespace testing; |
914 | - mga::DisplayName external{mga::DisplayName::external}; |
915 | - std::unique_ptr<mga::LayerList> list(new mga::LayerList(std::make_shared<mga::IntegerSourceCrop>(), {})); |
916 | - EXPECT_CALL(*mock_display_device, commit(external, Ref(*list), _, _)); |
917 | - |
918 | - mga::DisplayBuffer db{ |
919 | - external, |
920 | - std::move(list), |
921 | - mock_fb_bundle, |
922 | - mock_display_device, |
923 | - native_window, |
924 | - *gl_context, |
925 | - stub_program_factory, |
926 | - orientation, |
927 | - mga::OverlayOptimization::enabled}; |
928 | - |
929 | - db.gl_swap_buffers(); |
930 | -} |
931 | - |
932 | TEST_F(DisplayBuffer, posts_overlay_list_returns_display_device_decision) |
933 | { |
934 | using namespace testing; |
935 | @@ -295,30 +274,28 @@ |
936 | |
937 | ON_CALL(*mock_display_device, compatible_renderlist(_)) |
938 | .WillByDefault(Return(true)); |
939 | - ON_CALL(*mock_display_device, commit(_,_,_,_)) |
940 | - .WillByDefault(Invoke([]( |
941 | - mga::DisplayName, |
942 | - mga::LayerList& list, |
943 | - mga::SwappingGLContext const&, |
944 | - mga::RenderableListCompositor const&) |
945 | + auto set_to_overlays = [](mga::LayerList& list) |
946 | + { |
947 | + auto native_list = list.native_list(); |
948 | + for (auto i = 0u; i < native_list->numHwLayers; i++) |
949 | { |
950 | - auto native_list = list.native_list(); |
951 | - for (auto i = 0u; i < native_list->numHwLayers; i++) |
952 | - { |
953 | - if (native_list->hwLayers[i].compositionType == HWC_FRAMEBUFFER) |
954 | - native_list->hwLayers[i].compositionType = HWC_OVERLAY; |
955 | - } |
956 | - })); |
957 | + if (native_list->hwLayers[i].compositionType == HWC_FRAMEBUFFER) |
958 | + native_list->hwLayers[i].compositionType = HWC_OVERLAY; |
959 | + } |
960 | + }; |
961 | |
962 | mg::RenderableList renderlist{buffer1, buffer2}; |
963 | - EXPECT_TRUE(db.post_renderables_if_optimizable(renderlist)); |
964 | + EXPECT_TRUE(db.post_renderables_if_optimizable(renderlist)); |
965 | + set_to_overlays(db.contents().list); |
966 | EXPECT_FALSE(db.post_renderables_if_optimizable(renderlist)); |
967 | |
968 | renderlist = mg::RenderableList{buffer2, buffer1}; //ordering changed |
969 | EXPECT_TRUE(db.post_renderables_if_optimizable(renderlist)); |
970 | + set_to_overlays(db.contents().list); |
971 | EXPECT_FALSE(db.post_renderables_if_optimizable(renderlist)); |
972 | |
973 | renderlist = mg::RenderableList{buffer3, buffer1}; //buffer changed |
974 | EXPECT_TRUE(db.post_renderables_if_optimizable(renderlist)); |
975 | + set_to_overlays(db.contents().list); |
976 | EXPECT_FALSE(db.post_renderables_if_optimizable(renderlist)); |
977 | } |
978 | |
979 | === added file 'tests/unit-tests/graphics/android/test_display_group.cpp' |
980 | --- tests/unit-tests/graphics/android/test_display_group.cpp 1970-01-01 00:00:00 +0000 |
981 | +++ tests/unit-tests/graphics/android/test_display_group.cpp 2015-03-02 14:38:17 +0000 |
982 | @@ -0,0 +1,85 @@ |
983 | +/* |
984 | + * Copyright © 2015 Canonical Ltd. |
985 | + * |
986 | + * This program is free software: you can redistribute it and/or modify |
987 | + * it under the terms of the GNU General Public License version 3 as |
988 | + * published by the Free Software Foundation. |
989 | + * |
990 | + * This program is distributed in the hope that it will be useful, |
991 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
992 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
993 | + * GNU General Public License for more details. |
994 | + * |
995 | + * You should have received a copy of the GNU General Public License |
996 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
997 | + * |
998 | + * Authored by: Kevin DuBois <kevin.dubois@canonical.com> |
999 | + */ |
1000 | + |
1001 | +#include "src/platforms/android/server/configurable_display_buffer.h" |
1002 | +#include "src/platforms/android/server/display_group.h" |
1003 | +#include "mir_test_doubles/mock_display_device.h" |
1004 | +#include "mir_test_doubles/stub_renderable_list_compositor.h" |
1005 | +#include "mir_test_doubles/stub_swapping_gl_context.h" |
1006 | +#include "mir_test/fake_shared.h" |
1007 | +#include <memory> |
1008 | + |
1009 | +namespace mg=mir::graphics; |
1010 | +namespace mga=mir::graphics::android; |
1011 | +namespace mtd=mir::test::doubles; |
1012 | +namespace mt=mir::test; |
1013 | + |
1014 | +namespace |
1015 | +{ |
1016 | +struct StubConfigurableDB : mga::ConfigurableDisplayBuffer |
1017 | +{ |
1018 | + mir::geometry::Rectangle view_area() const override { return {}; } |
1019 | + void make_current() override {} |
1020 | + void release_current() override {} |
1021 | + void gl_swap_buffers() override {} |
1022 | + bool post_renderables_if_optimizable(mg::RenderableList const&) override { return false; } |
1023 | + MirOrientation orientation() const override { return mir_orientation_normal; } |
1024 | + bool uses_alpha() const override { return false; } |
1025 | + void configure(MirPowerMode, MirOrientation) override {} |
1026 | + mga::DisplayContents contents() override |
1027 | + { |
1028 | + return mga::DisplayContents{mga::DisplayName::primary, list, context, compositor}; |
1029 | + } |
1030 | + MirPowerMode power_mode() const override { return mir_power_mode_on; } |
1031 | + mga::LayerList mutable list{std::make_shared<mga::IntegerSourceCrop>(), {}}; |
1032 | + mtd::StubRenderableListCompositor mutable compositor; |
1033 | + mtd::StubSwappingGLContext mutable context; |
1034 | +}; |
1035 | +} |
1036 | + |
1037 | +TEST(DisplayGroup, db_additions_and_removals) |
1038 | +{ |
1039 | + using namespace testing; |
1040 | + NiceMock<mtd::MockDisplayDevice> mock_device; |
1041 | + InSequence seq; |
1042 | + EXPECT_CALL(mock_device, commit(SizeIs(1))); |
1043 | + EXPECT_CALL(mock_device, commit(SizeIs(2))); |
1044 | + EXPECT_CALL(mock_device, commit(SizeIs(2))); |
1045 | + EXPECT_CALL(mock_device, commit(SizeIs(1))); |
1046 | + |
1047 | + mga::DisplayGroup group(mt::fake_shared(mock_device), std::unique_ptr<StubConfigurableDB>{new StubConfigurableDB}); |
1048 | + group.post(); |
1049 | + |
1050 | + //hwc api does not allow removing primary |
1051 | + EXPECT_THROW({ |
1052 | + group.remove(mga::DisplayName::primary); |
1053 | + }, std::logic_error); |
1054 | + |
1055 | + EXPECT_FALSE(group.display_present(mga::DisplayName::external)); |
1056 | + group.add(mga::DisplayName::external, std::unique_ptr<StubConfigurableDB>(new StubConfigurableDB)); |
1057 | + EXPECT_TRUE(group.display_present(mga::DisplayName::external)); |
1058 | + group.post(); |
1059 | + |
1060 | + //can replace primary or external |
1061 | + group.add(mga::DisplayName::primary, std::unique_ptr<StubConfigurableDB>(new StubConfigurableDB)); |
1062 | + group.add(mga::DisplayName::external, std::unique_ptr<StubConfigurableDB>(new StubConfigurableDB)); |
1063 | + group.post(); |
1064 | + group.remove(mga::DisplayName::external); |
1065 | + group.remove(mga::DisplayName::external); |
1066 | + group.post(); |
1067 | +} |
1068 | |
1069 | === modified file 'tests/unit-tests/graphics/android/test_fb_device.cpp' |
1070 | --- tests/unit-tests/graphics/android/test_fb_device.cpp 2015-02-02 12:18:18 +0000 |
1071 | +++ tests/unit-tests/graphics/android/test_fb_device.cpp 2015-03-02 14:38:17 +0000 |
1072 | @@ -93,12 +93,13 @@ |
1073 | .WillOnce(Return(0)); |
1074 | |
1075 | mga::FBDevice fbdev(fb_hal_mock); |
1076 | + mga::DisplayContents content{primary, list, mock_context, stub_compositor}; |
1077 | |
1078 | EXPECT_THROW({ |
1079 | - fbdev.commit(primary, list, mock_context, stub_compositor); |
1080 | + fbdev.commit({content}); |
1081 | }, std::runtime_error); |
1082 | |
1083 | - fbdev.commit(primary, list, mock_context, stub_compositor); |
1084 | + fbdev.commit({content}); |
1085 | } |
1086 | |
1087 | //not all fb devices provide a swap interval hook. make sure we don't explode if thats the case |
1088 | |
1089 | === modified file 'tests/unit-tests/graphics/android/test_hwc_configuration.cpp' |
1090 | --- tests/unit-tests/graphics/android/test_hwc_configuration.cpp 2015-02-13 06:12:34 +0000 |
1091 | +++ tests/unit-tests/graphics/android/test_hwc_configuration.cpp 2015-03-02 14:38:17 +0000 |
1092 | @@ -227,3 +227,18 @@ |
1093 | auto attribs = config.active_attribs_for(mga::DisplayName::external); |
1094 | EXPECT_THAT(attribs.vrefresh_hz, Eq(0.0f)); |
1095 | } |
1096 | + |
1097 | +TEST_F(HwcConfiguration, subscribes_to_hotplug) |
1098 | +{ |
1099 | + using namespace testing; |
1100 | + std::function<void(mga::DisplayName, bool)> hotplug_fn([](mga::DisplayName, bool){}); |
1101 | + EXPECT_CALL(*mock_hwc_wrapper, subscribe_to_events(_,_,_,_)) |
1102 | + .WillOnce(SaveArg<2>(&hotplug_fn)); |
1103 | + EXPECT_CALL(*mock_hwc_wrapper, unsubscribe_from_events_(_)); |
1104 | + |
1105 | + unsigned int call_count{0}; |
1106 | + auto subscription = config.subscribe_to_config_changes([&]{ call_count++; }); |
1107 | + hotplug_fn(mga::DisplayName::primary, true); |
1108 | + hotplug_fn(mga::DisplayName::primary, true); |
1109 | + EXPECT_THAT(call_count, Eq(2)); |
1110 | +} |
1111 | |
1112 | === modified file 'tests/unit-tests/graphics/android/test_hwc_device.cpp' |
1113 | --- tests/unit-tests/graphics/android/test_hwc_device.cpp 2015-02-26 03:20:09 +0000 |
1114 | +++ tests/unit-tests/graphics/android/test_hwc_device.cpp 2015-03-02 14:38:17 +0000 |
1115 | @@ -112,7 +112,7 @@ |
1116 | std::shared_ptr<mtd::StubRenderable> const stub_renderable1; |
1117 | std::shared_ptr<mtd::StubRenderable> const stub_renderable2; |
1118 | std::shared_ptr<mtd::MockHWCDeviceWrapper> const mock_device; |
1119 | - mtd::StubSwappingGLContext const stub_context; |
1120 | + mtd::StubSwappingGLContext stub_context; |
1121 | mg::RenderableList renderlist; |
1122 | std::shared_ptr<mga::LayerAdapter> const layer_adapter; |
1123 | mga::DisplayName primary{mga::DisplayName::primary}; |
1124 | @@ -132,8 +132,9 @@ |
1125 | .Times(1); |
1126 | |
1127 | mga::LayerList list(layer_adapter, {}); |
1128 | + mga::DisplayContents content{primary, list, stub_context, stub_compositor}; |
1129 | mga::HwcDevice device(mock_device); |
1130 | - device.commit(primary, list, stub_context, stub_compositor); |
1131 | + device.commit({content}); |
1132 | } |
1133 | |
1134 | TEST_F(HwcDevice, calls_backup_compositor_when_overlay_rejected) |
1135 | @@ -167,8 +168,9 @@ |
1136 | .InSequence(seq); |
1137 | |
1138 | mga::LayerList list(layer_adapter, renderlist); |
1139 | + mga::DisplayContents content{primary, list, stub_context, mock_compositor}; |
1140 | mga::HwcDevice device(mock_device); |
1141 | - device.commit(primary, list, stub_context, mock_compositor); |
1142 | + device.commit({content}); |
1143 | } |
1144 | |
1145 | TEST_F(HwcDevice, swaps_buffers_directly_when_no_renderables) |
1146 | @@ -184,8 +186,9 @@ |
1147 | EXPECT_CALL(mock_context, swap_buffers()); |
1148 | |
1149 | mga::LayerList list(layer_adapter, {}); |
1150 | + mga::DisplayContents content{primary, list, mock_context, mock_compositor}; |
1151 | mga::HwcDevice device(mock_device); |
1152 | - device.commit(primary, list, mock_context, mock_compositor); |
1153 | + device.commit({content}); |
1154 | } |
1155 | |
1156 | TEST_F(HwcDevice, resets_layers_when_prepare_gl_called) |
1157 | @@ -212,9 +215,10 @@ |
1158 | |
1159 | mga::HwcDevice device(mock_device); |
1160 | mga::LayerList list(layer_adapter, renderlist); |
1161 | - device.commit(primary, list, stub_context, stub_compositor); |
1162 | + mga::DisplayContents content{primary, list, stub_context, stub_compositor}; |
1163 | + device.commit({content}); |
1164 | list.update_list({}); |
1165 | - device.commit(primary, list, stub_context, stub_compositor); |
1166 | + device.commit({content}); |
1167 | } |
1168 | |
1169 | TEST_F(HwcDevice, sets_and_updates_fences) |
1170 | @@ -246,7 +250,8 @@ |
1171 | |
1172 | mga::HwcDevice device(mock_device); |
1173 | mga::LayerList list(layer_adapter, {}); |
1174 | - device.commit(primary, list, stub_context, stub_compositor); |
1175 | + mga::DisplayContents content{primary, list, stub_context, stub_compositor}; |
1176 | + device.commit({content}); |
1177 | |
1178 | //check that the retire fence is closed |
1179 | bool retire_fence_was_closed{fcntl(hwc_retire_fence, F_GETFD) == -1}; |
1180 | @@ -297,7 +302,8 @@ |
1181 | |
1182 | mga::HwcDevice device(mock_device); |
1183 | mga::LayerList list(layer_adapter, {stub_renderable1}); |
1184 | - device.commit(primary, list, stub_context, stub_compositor); |
1185 | + mga::DisplayContents content{primary, list, stub_context, stub_compositor}; |
1186 | + device.commit({content}); |
1187 | } |
1188 | |
1189 | TEST_F(HwcDevice, commits_correct_list_when_all_accepted_as_overlays) |
1190 | @@ -360,7 +366,8 @@ |
1191 | |
1192 | mga::HwcDevice device(mock_device); |
1193 | mga::LayerList list(layer_adapter, renderlist); |
1194 | - device.commit(primary, list, stub_context, stub_compositor); |
1195 | + mga::DisplayContents content{primary, list, stub_context, stub_compositor}; |
1196 | + device.commit({content}); |
1197 | } |
1198 | |
1199 | TEST_F(HwcDevice, submits_every_time_if_at_least_one_layer_is_gl_rendered) |
1200 | @@ -381,9 +388,10 @@ |
1201 | .Times(2); |
1202 | |
1203 | mga::LayerList list(layer_adapter, renderlist); |
1204 | - device.commit(primary, list, stub_context, stub_compositor); |
1205 | + mga::DisplayContents content{primary, list, stub_context, stub_compositor}; |
1206 | + device.commit({content}); |
1207 | list.update_list(renderlist); |
1208 | - device.commit(primary, list, stub_context, stub_compositor); |
1209 | + device.commit({content}); |
1210 | } |
1211 | |
1212 | TEST_F(HwcDevice, resets_composition_type_with_prepare) //lp:1314399 |
1213 | @@ -404,9 +412,10 @@ |
1214 | .InSequence(seq); |
1215 | |
1216 | mga::LayerList list(layer_adapter, renderlist); |
1217 | - device.commit(primary, list, stub_context, stub_compositor); |
1218 | + mga::DisplayContents content{primary, list, stub_context, stub_compositor}; |
1219 | + device.commit({content}); |
1220 | list.update_list(renderlist2); |
1221 | - device.commit(primary, list, stub_context, stub_compositor); |
1222 | + device.commit({content}); |
1223 | } |
1224 | |
1225 | //note: HWC models overlay layer buffers as owned by the display hardware until a subsequent set. |
1226 | @@ -421,10 +430,11 @@ |
1227 | |
1228 | auto use_count_before = stub_buffer1.use_count(); |
1229 | mga::LayerList list(layer_adapter, {stub_renderable1}); |
1230 | - device.commit(primary, list, stub_context, stub_compositor); |
1231 | + mga::DisplayContents content{primary, list, stub_context, stub_compositor}; |
1232 | + device.commit({content}); |
1233 | EXPECT_THAT(stub_buffer1.use_count(), Gt(use_count_before)); |
1234 | list.update_list({stub_renderable1}); |
1235 | - device.commit(primary, list, stub_context, stub_compositor); |
1236 | + device.commit({content}); |
1237 | EXPECT_THAT(stub_buffer1.use_count(), Eq(use_count_before)); |
1238 | } |
1239 | |
1240 | @@ -503,12 +513,13 @@ |
1241 | |
1242 | mga::HwcDevice device(mock_device); |
1243 | mga::LayerList list(layer_adapter, renderlist); |
1244 | - device.commit(primary, list, stub_context, stub_compositor); |
1245 | + mga::DisplayContents content{primary, list, stub_context, stub_compositor}; |
1246 | + device.commit({content}); |
1247 | //set only the 2nd layer to a new buffer. the first buffer has the same buffer, and would |
1248 | //still be onscreen if this wasn't against a mock |
1249 | stub_renderable1->set_buffer(updated_buffer); |
1250 | list.update_list(renderlist); |
1251 | - device.commit(primary, list, stub_context, stub_compositor); |
1252 | + device.commit({content}); |
1253 | } |
1254 | |
1255 | TEST_F(HwcDevice, does_not_own_framebuffer_buffers_past_set) |
1256 | @@ -527,7 +538,8 @@ |
1257 | auto use_count_before = stub_buffer1.use_count(); |
1258 | |
1259 | mga::LayerList list(layer_adapter, {stub_renderable1}); |
1260 | - device.commit(primary, list, stub_context, stub_compositor); |
1261 | + mga::DisplayContents content{primary, list, stub_context, stub_compositor}; |
1262 | + device.commit({content}); |
1263 | EXPECT_THAT(stub_buffer1.use_count(), Eq(use_count_before)); |
1264 | } |
1265 | |
1266 | @@ -566,7 +578,8 @@ |
1267 | |
1268 | auto use_count_before = stub_buffer1.use_count(); |
1269 | mga::LayerList list(layer_adapter, {stub_renderable1}); |
1270 | - device.commit(primary, list, stub_context, stub_compositor); |
1271 | + mga::DisplayContents content{primary, list, stub_context, stub_compositor}; |
1272 | + device.commit({content}); |
1273 | EXPECT_THAT(stub_buffer1.use_count(), Gt(use_count_before)); |
1274 | |
1275 | device.content_cleared(); |
1276 | @@ -660,9 +673,10 @@ |
1277 | |
1278 | mga::HwcDevice device(mock_device); |
1279 | mga::LayerList list(layer_adapter, renderlist1); |
1280 | - device.commit(primary, list, stub_context, stub_compositor); |
1281 | + mga::DisplayContents content{primary, list, stub_context, stub_compositor}; |
1282 | + device.commit({content}); |
1283 | list.update_list(renderlist2); |
1284 | - device.commit(primary, list, stub_context, stub_compositor); |
1285 | + device.commit({content}); |
1286 | } |
1287 | |
1288 | TEST_F(HwcDevice, tracks_hwc_owned_fences_across_list_rearrange) |
1289 | @@ -763,7 +777,42 @@ |
1290 | |
1291 | mga::HwcDevice device(mock_device); |
1292 | mga::LayerList list(layer_adapter, renderlist); |
1293 | - device.commit(primary, list, stub_context, stub_compositor); |
1294 | + mga::DisplayContents content{primary, list, stub_context, stub_compositor}; |
1295 | + device.commit({content}); |
1296 | list.update_list(renderlist2); |
1297 | - device.commit(primary, list, stub_context, stub_compositor); |
1298 | + device.commit({content}); |
1299 | +} |
1300 | + |
1301 | +TEST_F(HwcDevice, commits_external_list_with_both_force_gl) |
1302 | +{ |
1303 | + using namespace testing; |
1304 | + testing::NiceMock<mtd::MockSwappingGLContext> mock_context1; |
1305 | + testing::NiceMock<mtd::MockSwappingGLContext> mock_context2; |
1306 | + ON_CALL(mock_context1, last_rendered_buffer()) |
1307 | + .WillByDefault(Return(stub_fb_buffer)); |
1308 | + ON_CALL(mock_context2, last_rendered_buffer()) |
1309 | + .WillByDefault(Return(stub_fb_buffer)); |
1310 | + std::list<hwc_layer_1_t*> expected_list |
1311 | + { |
1312 | + &skip_layer, |
1313 | + &target_layer |
1314 | + }; |
1315 | + |
1316 | + InSequence seq; |
1317 | + EXPECT_CALL(*mock_device, prepare(MatchesLists(expected_list, expected_list))); |
1318 | + EXPECT_CALL(mock_context1, make_current()); |
1319 | + EXPECT_CALL(mock_context1, release_current()); |
1320 | + EXPECT_CALL(mock_context2, make_current()); |
1321 | + EXPECT_CALL(mock_context2, release_current()); |
1322 | + EXPECT_CALL(*mock_device, set(MatchesLists(expected_list, expected_list))); |
1323 | + |
1324 | + mga::LayerList primary_list(layer_adapter, {}); |
1325 | + mga::LayerList external_list(layer_adapter, {}); |
1326 | + |
1327 | + mga::HwcDevice device(mock_device); |
1328 | + |
1329 | + mga::DisplayContents primary_content{primary, primary_list, mock_context1, stub_compositor}; |
1330 | + mga::DisplayContents external_content{mga::DisplayName::external, external_list, mock_context2, stub_compositor}; |
1331 | + |
1332 | + device.commit({primary_content, external_content}); |
1333 | } |
1334 | |
1335 | === modified file 'tests/unit-tests/graphics/android/test_hwc_fb_device.cpp' |
1336 | --- tests/unit-tests/graphics/android/test_hwc_fb_device.cpp 2015-02-02 12:18:18 +0000 |
1337 | +++ tests/unit-tests/graphics/android/test_hwc_fb_device.cpp 2015-03-02 14:38:17 +0000 |
1338 | @@ -172,5 +172,6 @@ |
1339 | EXPECT_CALL(*mock_fb_device, post_interface(mock_fb_device.get(), &stub_native_buffer->native_handle)) |
1340 | .InSequence(seq); |
1341 | |
1342 | - device.commit(primary, list, mock_context, stub_compositor); |
1343 | + mga::DisplayContents content{primary, list, mock_context, stub_compositor}; |
1344 | + device.commit({content}); |
1345 | } |
FAILED: Continuous integration, rev:2306 jenkins. qa.ubuntu. com/job/ mir-ci/ 3041/ jenkins. qa.ubuntu. com/job/ mir-android- vivid-i386- build/1387 jenkins. qa.ubuntu. com/job/ mir-clang- vivid-amd64- build/1386/ console jenkins. qa.ubuntu. com/job/ mir-mediumtests -vivid- touch/1343 jenkins. qa.ubuntu. com/job/ mir-vivid- amd64-ci/ 1038 jenkins. qa.ubuntu. com/job/ mir-vivid- amd64-ci/ 1038/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-mediumtests -builder- vivid-armhf/ 1343 jenkins. qa.ubuntu. com/job/ mir-mediumtests -builder- vivid-armhf/ 1343/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-mediumtests -runner- mako/4372 s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 18275
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/mir- ci/3041/ rebuild
http://