Merge lp:~kdub/mir/mesa-display-group into lp:mir
- mesa-display-group
- Merge into development-branch
Status: | Work in progress |
---|---|
Proposed branch: | lp:~kdub/mir/mesa-display-group |
Merge into: | lp:mir |
Prerequisite: | lp:~kdub/mir/display-groups |
Diff against target: |
1501 lines (+707/-424) 12 files modified
src/platforms/mesa/server/CMakeLists.txt (+2/-0) src/platforms/mesa/server/buffer_object.cpp (+46/-0) src/platforms/mesa/server/buffer_object.h (+51/-0) src/platforms/mesa/server/display.cpp (+12/-12) src/platforms/mesa/server/display.h (+2/-1) src/platforms/mesa/server/display_buffer.cpp (+29/-223) src/platforms/mesa/server/display_buffer.h (+8/-21) src/platforms/mesa/server/display_group.cpp (+219/-0) src/platforms/mesa/server/display_group.h (+86/-0) tests/unit-tests/graphics/mesa/CMakeLists.txt (+1/-0) tests/unit-tests/graphics/mesa/test_display_buffer.cpp (+4/-167) tests/unit-tests/graphics/mesa/test_display_group.cpp (+247/-0) |
To merge this branch: | bzr merge lp:~kdub/mir/mesa-display-group |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Daniel van Vugt | Needs Information | ||
Review via email: mp+249197@code.launchpad.net |
Commit message
mesa: split the mgm::DisplayBuffer into mgm::DisplayGroup, which handles posting to screen, and mgm::DisplayBuffer, which tracks the intended content.
Description of the change
mesa: split the mgm::DisplayBuffer into mgm::DisplayGroup, which handles posting to screen, and mgm::DisplayBuffer, which tracks the intended content.
notes: This is mostly splitting the code into two different classes. DisplayGroup is now nicely concerned only with posting, and DisplayBuffer is now nicely concerned only with the content.
lines 252-284 were moved to buffer_object.cpp and buffer_object.h
lines 289-542 were moved to display_group.cpp and display_group.h
- 2292. By Kevin DuBois
-
merge base
Unmerged revisions
- 2292. By Kevin DuBois
-
merge base
- 2291. By Kevin DuBois
-
add back some code that helps with rotation
- 2290. By Kevin DuBois
-
split out classes and headers as needed
- 2289. By Kevin DuBois
-
split out displaybuffer from displaygroup on mesa
Preview Diff
1 | === modified file 'src/platforms/mesa/server/CMakeLists.txt' |
2 | --- src/platforms/mesa/server/CMakeLists.txt 2015-02-04 08:45:38 +0000 |
3 | +++ src/platforms/mesa/server/CMakeLists.txt 2015-02-10 18:28:23 +0000 |
4 | @@ -27,6 +27,7 @@ |
5 | real_kms_display_configuration.cpp |
6 | drm_mode_resources.cpp |
7 | display_buffer.cpp |
8 | + display_group.cpp |
9 | real_kms_output.cpp |
10 | real_kms_output_container.cpp |
11 | kms_page_flipper.cpp |
12 | @@ -38,6 +39,7 @@ |
13 | bypass.cpp |
14 | ipc_operations.cpp |
15 | nested_authentication.cpp |
16 | + buffer_object.cpp |
17 | ) |
18 | |
19 | add_library( |
20 | |
21 | === added file 'src/platforms/mesa/server/buffer_object.cpp' |
22 | --- src/platforms/mesa/server/buffer_object.cpp 1970-01-01 00:00:00 +0000 |
23 | +++ src/platforms/mesa/server/buffer_object.cpp 2015-02-10 18:28:23 +0000 |
24 | @@ -0,0 +1,46 @@ |
25 | +/* |
26 | + * Copyright © 2015 Canonical Ltd. |
27 | + * |
28 | + * This program is free software: you can redistribute it and/or modify it |
29 | + * under the terms of the GNU Lesser General Public License version 3, |
30 | + * as published by the Free Software Foundation. |
31 | + * |
32 | + * This program is distributed in the hope that it will be useful, |
33 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
34 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
35 | + * GNU Lesser General Public License for more details. |
36 | + * |
37 | + * You should have received a copy of the GNU Lesser General Public License |
38 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
39 | + * |
40 | + * Authored by: Kevin DuBois <kevin.dubois@canonical.com> |
41 | + */ |
42 | + |
43 | +#include "buffer_object.h" |
44 | +#include <xf86drmMode.h> |
45 | + |
46 | +namespace mgm = mir::graphics::mesa; |
47 | + |
48 | +mgm::BufferObject::BufferObject(gbm_surface* surface, gbm_bo* bo, uint32_t drm_fb_id) : |
49 | + surface{surface}, bo{bo}, drm_fb_id{drm_fb_id} |
50 | +{ |
51 | +} |
52 | + |
53 | +mgm::BufferObject::~BufferObject() |
54 | +{ |
55 | + if (drm_fb_id) |
56 | + { |
57 | + int drm_fd = gbm_device_get_fd(gbm_bo_get_device(bo)); |
58 | + drmModeRmFB(drm_fd, drm_fb_id); |
59 | + } |
60 | +} |
61 | + |
62 | +void mgm::BufferObject::release() const |
63 | +{ |
64 | + gbm_surface_release_buffer(surface, bo); |
65 | +} |
66 | + |
67 | +uint32_t mgm::BufferObject::get_drm_fb_id() const |
68 | +{ |
69 | + return drm_fb_id; |
70 | +} |
71 | |
72 | === added file 'src/platforms/mesa/server/buffer_object.h' |
73 | --- src/platforms/mesa/server/buffer_object.h 1970-01-01 00:00:00 +0000 |
74 | +++ src/platforms/mesa/server/buffer_object.h 2015-02-10 18:28:23 +0000 |
75 | @@ -0,0 +1,51 @@ |
76 | +/* |
77 | + * Copyright © 2015 Canonical Ltd. |
78 | + * |
79 | + * This program is free software: you can redistribute it and/or modify it |
80 | + * under the terms of the GNU Lesser General Public License version 3, |
81 | + * as published by the Free Software Foundation. |
82 | + * |
83 | + * This program is distributed in the hope that it will be useful, |
84 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
85 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
86 | + * GNU Lesser General Public License for more details. |
87 | + * |
88 | + * You should have received a copy of the GNU Lesser General Public License |
89 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
90 | + * |
91 | + * Authored by: Kevin DuBois <kevin.dubois@canonical.com> |
92 | + */ |
93 | + |
94 | +#ifndef MIR_GRAPHICS_MESA_BUFFER_OBJECT_H_ |
95 | +#define MIR_GRAPHICS_MESA_BUFFER_OBJECT_H_ |
96 | + |
97 | +#include <stddef.h> |
98 | +#include <gbm.h> |
99 | + |
100 | +namespace mir |
101 | +{ |
102 | +namespace graphics |
103 | +{ |
104 | +namespace mesa |
105 | +{ |
106 | +class BufferObject |
107 | +{ |
108 | +public: |
109 | + BufferObject(gbm_surface* surface, gbm_bo* bo, uint32_t drm_fb_id); |
110 | + ~BufferObject(); |
111 | + |
112 | + void release() const; |
113 | + uint32_t get_drm_fb_id() const; |
114 | +private: |
115 | + BufferObject(BufferObject const&) = delete; |
116 | + BufferObject& operator=(BufferObject const&) = delete; |
117 | + |
118 | + gbm_surface *surface; |
119 | + gbm_bo *bo; |
120 | + uint32_t drm_fb_id; |
121 | +}; |
122 | +} |
123 | +} |
124 | +} |
125 | + |
126 | +#endif /* MIR_GRAPHICS_MESA_BUFFER_OBJECT_H_ */ |
127 | |
128 | === modified file 'src/platforms/mesa/server/display.cpp' |
129 | --- src/platforms/mesa/server/display.cpp 2015-02-10 18:28:22 +0000 |
130 | +++ src/platforms/mesa/server/display.cpp 2015-02-10 18:28:23 +0000 |
131 | @@ -19,7 +19,7 @@ |
132 | #include "display.h" |
133 | #include "cursor.h" |
134 | #include "platform.h" |
135 | -#include "display_buffer.h" |
136 | +#include "display_group.h" |
137 | #include "kms_display_configuration.h" |
138 | #include "kms_output.h" |
139 | #include "kms_page_flipper.h" |
140 | @@ -118,8 +118,8 @@ |
141 | { |
142 | std::lock_guard<std::mutex> lg{configuration_mutex}; |
143 | |
144 | - for (auto& db_ptr : display_buffers) |
145 | - f(*db_ptr); |
146 | + for (auto& dg_ptr : display_groups) |
147 | + f(*dg_ptr); |
148 | } |
149 | |
150 | std::unique_ptr<mg::DisplayConfiguration> mgm::Display::configuration() const |
151 | @@ -149,7 +149,7 @@ |
152 | // before it's fully constructed, to force proper initialization. |
153 | bool const comp{(&conf != ¤t_display_configuration) && |
154 | compatible(kms_conf, current_display_configuration)}; |
155 | - std::vector<std::unique_ptr<DisplayBuffer>> display_buffers_new; |
156 | + std::vector<std::unique_ptr<DisplayGroup>> display_groups_new; |
157 | |
158 | if (!comp) |
159 | { |
160 | @@ -161,8 +161,8 @@ |
161 | * sure we wait for all pending page flips to finish before the |
162 | * display_buffers_new are created and take control of the outputs. |
163 | */ |
164 | - for (auto& db : display_buffers) |
165 | - db->wait_for_page_flip(); |
166 | + for (auto& dg : display_groups) |
167 | + dg->wait_for_page_flip(); |
168 | |
169 | /* Reset the state of all outputs */ |
170 | kms_conf.for_each_output([&](DisplayConfigurationOutput const& conf_output) |
171 | @@ -207,7 +207,7 @@ |
172 | |
173 | if (comp) |
174 | { |
175 | - display_buffers[group_idx++]->set_orientation(orientation, bounding_rect); |
176 | + display_groups[group_idx++]->set_orientation(orientation, bounding_rect); |
177 | } |
178 | else |
179 | { |
180 | @@ -221,8 +221,8 @@ |
181 | |
182 | auto surface = platform->gbm.create_scanout_surface(width, height); |
183 | |
184 | - std::unique_ptr<DisplayBuffer> db{ |
185 | - new DisplayBuffer{platform, listener, |
186 | + std::unique_ptr<DisplayGroup> dg{ |
187 | + new DisplayGroup{platform, listener, |
188 | kms_outputs, |
189 | std::move(surface), |
190 | bounding_rect, |
191 | @@ -230,12 +230,12 @@ |
192 | *gl_config, |
193 | shared_egl.context()}}; |
194 | |
195 | - display_buffers_new.push_back(std::move(db)); |
196 | + display_groups_new.push_back(std::move(dg)); |
197 | } |
198 | }); |
199 | |
200 | if (!comp) |
201 | - display_buffers = std::move(display_buffers_new); |
202 | + display_groups = std::move(display_groups_new); |
203 | |
204 | /* Store applied configuration */ |
205 | current_display_configuration = kms_conf; |
206 | @@ -307,7 +307,7 @@ |
207 | * we need to reset the CRTCs. For active displays we schedule a CRTC reset |
208 | * on the next swap. For connected but unused outputs we clear the CRTC. |
209 | */ |
210 | - for (auto& db_ptr : display_buffers) |
211 | + for (auto& db_ptr : display_groups) |
212 | db_ptr->schedule_set_crtc(); |
213 | |
214 | clear_connected_unused_outputs(); |
215 | |
216 | === modified file 'src/platforms/mesa/server/display.h' |
217 | --- src/platforms/mesa/server/display.h 2015-02-10 18:28:22 +0000 |
218 | +++ src/platforms/mesa/server/display.h 2015-02-10 18:28:23 +0000 |
219 | @@ -23,6 +23,7 @@ |
220 | #include "real_kms_output_container.h" |
221 | #include "real_kms_display_configuration.h" |
222 | #include "display_helpers.h" |
223 | +#include "display_group.h" |
224 | |
225 | #include <mutex> |
226 | |
227 | @@ -90,7 +91,7 @@ |
228 | std::shared_ptr<DisplayReport> const listener; |
229 | mir::udev::Monitor monitor; |
230 | helpers::EGLHelper shared_egl; |
231 | - std::vector<std::unique_ptr<DisplayBuffer>> display_buffers; |
232 | + std::vector<std::unique_ptr<DisplayGroup>> display_groups; |
233 | RealKMSOutputContainer output_container; |
234 | mutable RealKMSDisplayConfiguration current_display_configuration; |
235 | |
236 | |
237 | === modified file 'src/platforms/mesa/server/display_buffer.cpp' |
238 | --- src/platforms/mesa/server/display_buffer.cpp 2015-02-10 18:28:22 +0000 |
239 | +++ src/platforms/mesa/server/display_buffer.cpp 2015-02-10 18:28:23 +0000 |
240 | @@ -22,6 +22,7 @@ |
241 | #include "mir/graphics/display_report.h" |
242 | #include "bypass.h" |
243 | #include "gbm_buffer.h" |
244 | +#include "buffer_object.h" |
245 | #include "mir/fatal.h" |
246 | |
247 | #include <boost/throw_exception.hpp> |
248 | @@ -32,39 +33,6 @@ |
249 | namespace mgm = mir::graphics::mesa; |
250 | namespace geom = mir::geometry; |
251 | |
252 | -class mgm::BufferObject |
253 | -{ |
254 | -public: |
255 | - BufferObject(gbm_surface* surface, gbm_bo* bo, uint32_t drm_fb_id) |
256 | - : surface{surface}, bo{bo}, drm_fb_id{drm_fb_id} |
257 | - { |
258 | - } |
259 | - |
260 | - ~BufferObject() |
261 | - { |
262 | - if (drm_fb_id) |
263 | - { |
264 | - int drm_fd = gbm_device_get_fd(gbm_bo_get_device(bo)); |
265 | - drmModeRmFB(drm_fd, drm_fb_id); |
266 | - } |
267 | - } |
268 | - |
269 | - void release() const |
270 | - { |
271 | - gbm_surface_release_buffer(surface, bo); |
272 | - } |
273 | - |
274 | - uint32_t get_drm_fb_id() const |
275 | - { |
276 | - return drm_fb_id; |
277 | - } |
278 | - |
279 | -private: |
280 | - gbm_surface *surface; |
281 | - gbm_bo *bo; |
282 | - uint32_t drm_fb_id; |
283 | -}; |
284 | - |
285 | namespace |
286 | { |
287 | |
288 | @@ -90,24 +58,17 @@ |
289 | mgm::DisplayBuffer::DisplayBuffer( |
290 | std::shared_ptr<Platform> const& platform, |
291 | std::shared_ptr<DisplayReport> const& listener, |
292 | - std::vector<std::shared_ptr<KMSOutput>> const& outputs, |
293 | GBMSurfaceUPtr surface_gbm_param, |
294 | geom::Rectangle const& area, |
295 | MirOrientation rot, |
296 | GLConfig const& gl_config, |
297 | - EGLContext shared_context) |
298 | - : last_flipped_bufobj{nullptr}, |
299 | - scheduled_bufobj{nullptr}, |
300 | - platform(platform), |
301 | - listener(listener), |
302 | - drm(*platform->drm), |
303 | - outputs(outputs), |
304 | - surface_gbm{std::move(surface_gbm_param)}, |
305 | - egl{gl_config}, |
306 | - area(area), |
307 | - rotation(rot), |
308 | - needs_set_crtc{false}, |
309 | - page_flips_pending{false} |
310 | + EGLContext shared_context) : |
311 | + platform_bypass_option{platform->bypass_option()}, |
312 | + drm(*platform->drm), |
313 | + surface_gbm{std::move(surface_gbm_param)}, |
314 | + egl{gl_config}, |
315 | + area(area), |
316 | + rotation(rot) |
317 | { |
318 | uint32_t area_width = area.size.width.as_uint32_t(); |
319 | uint32_t area_height = area.size.height.as_uint32_t(); |
320 | @@ -139,16 +100,6 @@ |
321 | |
322 | listener->report_successful_egl_buffer_swap_on_construction(); |
323 | |
324 | - scheduled_bufobj = get_front_buffer_object(); |
325 | - if (!scheduled_bufobj) |
326 | - fatal_error("Failed to get frontbuffer"); |
327 | - |
328 | - for (auto& output : outputs) |
329 | - { |
330 | - if (!output->set_crtc(scheduled_bufobj->get_drm_fb_id())) |
331 | - fatal_error("Failed to set DRM crtc"); |
332 | - } |
333 | - |
334 | egl.release_current(); |
335 | |
336 | listener->report_successful_drm_mode_set_crtc_on_construction(); |
337 | @@ -160,19 +111,6 @@ |
338 | }); |
339 | } |
340 | |
341 | -mgm::DisplayBuffer::~DisplayBuffer() |
342 | -{ |
343 | - /* |
344 | - * There is no need to destroy last_flipped_bufobj manually. |
345 | - * It will be destroyed when its gbm_surface gets destroyed. |
346 | - */ |
347 | - if (last_flipped_bufobj) |
348 | - last_flipped_bufobj->release(); |
349 | - |
350 | - if (scheduled_bufobj) |
351 | - scheduled_bufobj->release(); |
352 | -} |
353 | - |
354 | geom::Rectangle mgm::DisplayBuffer::view_area() const |
355 | { |
356 | return area; |
357 | @@ -198,7 +136,7 @@ |
358 | bool mgm::DisplayBuffer::post_renderables_if_optimizable(RenderableList const& renderable_list) |
359 | { |
360 | if ((rotation == mir_orientation_normal) && |
361 | - (platform->bypass_option() == mgm::BypassOption::allowed)) |
362 | + (platform_bypass_option == mgm::BypassOption::allowed)) |
363 | { |
364 | mgm::BypassMatch bypass_match(area); |
365 | auto bypass_it = std::find_if(renderable_list.rbegin(), renderable_list.rend(), bypass_match); |
366 | @@ -227,12 +165,6 @@ |
367 | return false; |
368 | } |
369 | |
370 | -void mgm::DisplayBuffer::for_each_display_buffer( |
371 | - std::function<void(graphics::DisplayBuffer&)> const& f) |
372 | -{ |
373 | - f(*this); |
374 | -} |
375 | - |
376 | void mgm::DisplayBuffer::gl_swap_buffers() |
377 | { |
378 | if (!egl.swap_buffers()) |
379 | @@ -241,120 +173,6 @@ |
380 | bypass_bufobj = nullptr; |
381 | } |
382 | |
383 | -void mgm::DisplayBuffer::post() |
384 | -{ |
385 | - /* |
386 | - * We might not have waited for the previous frame to page flip yet. |
387 | - * This is good because it maximizes the time available to spend rendering |
388 | - * each frame. Just remember wait_for_page_flip() must be called at some |
389 | - * point before the next schedule_page_flip(). |
390 | - */ |
391 | - wait_for_page_flip(); |
392 | - |
393 | - /* |
394 | - * Switching from bypass to compositing? Now is the earliest safe time |
395 | - * we can unreference the bypass buffer... |
396 | - */ |
397 | - if (scheduled_bufobj) |
398 | - last_flipped_bypass_buf = nullptr; |
399 | - /* |
400 | - * Release the last flipped buffer object (which is not displayed anymore) |
401 | - * to make it available for future rendering. |
402 | - */ |
403 | - if (last_flipped_bufobj) |
404 | - last_flipped_bufobj->release(); |
405 | - |
406 | - last_flipped_bufobj = scheduled_bufobj; |
407 | - scheduled_bufobj = nullptr; |
408 | - |
409 | - mgm::BufferObject *bufobj; |
410 | - if (bypass_buf) |
411 | - { |
412 | - bufobj = bypass_bufobj; |
413 | - } |
414 | - else |
415 | - { |
416 | - bufobj = get_front_buffer_object(); |
417 | - if (!bufobj) |
418 | - fatal_error("Failed to get front buffer object"); |
419 | - } |
420 | - |
421 | - /* |
422 | - * Schedule the current front buffer object for display, and wait |
423 | - * for it to be actually displayed (flipped). |
424 | - * |
425 | - * If the flip fails, release the buffer object to make it available |
426 | - * for future rendering. |
427 | - */ |
428 | - if (!needs_set_crtc && !schedule_page_flip(bufobj)) |
429 | - { |
430 | - if (!bypass_buf) |
431 | - bufobj->release(); |
432 | - fatal_error("Failed to schedule page flip"); |
433 | - } |
434 | - else if (needs_set_crtc) |
435 | - { |
436 | - for (auto& output : outputs) |
437 | - { |
438 | - if (!output->set_crtc(bufobj->get_drm_fb_id())) |
439 | - fatal_error("Failed to set DRM crtc"); |
440 | - } |
441 | - needs_set_crtc = false; |
442 | - } |
443 | - |
444 | - if (bypass_buf) |
445 | - { |
446 | - /* |
447 | - * For composited frames we defer wait_for_page_flip till just before |
448 | - * the next frame, but not for bypass frames. Deferring the flip of |
449 | - * bypass frames would increase the time we held |
450 | - * last_flipped_bypass_buf unacceptably, resulting in client stuttering |
451 | - * unless we allocate more buffers (which I'm trying to avoid). |
452 | - * Also, bypass does not need the deferred page flip because it has |
453 | - * no compositing/rendering step for which to save time for. |
454 | - */ |
455 | - wait_for_page_flip(); |
456 | - scheduled_bufobj = nullptr; |
457 | - |
458 | - /* |
459 | - * Keep a reference to the buffer being bypassed for the entire |
460 | - * duration of the frame. This ensures the buffer doesn't get reused by |
461 | - * the client while its on-screen, which would be seen as tearing or |
462 | - * worse. |
463 | - */ |
464 | - last_flipped_bypass_buf = bypass_buf; |
465 | - } |
466 | - else |
467 | - { |
468 | - /* |
469 | - * Not in clone mode? We can afford to wait for the page flip then, |
470 | - * making us double-buffered (noticeably less laggy than the triple |
471 | - * buffering that clone mode requires). |
472 | - */ |
473 | - if (outputs.size() == 1) |
474 | - { |
475 | - wait_for_page_flip(); |
476 | - |
477 | - /* |
478 | - * bufobj is now physically on screen. Release the old frame... |
479 | - */ |
480 | - if (last_flipped_bufobj) |
481 | - { |
482 | - last_flipped_bufobj->release(); |
483 | - last_flipped_bufobj = nullptr; |
484 | - } |
485 | - |
486 | - /* |
487 | - * last_flipped_bufobj will be set correctly on the next iteration |
488 | - * Don't do it here or else bufobj would be released while still |
489 | - * on screen (hence tearing and artefacts). |
490 | - */ |
491 | - } |
492 | - |
493 | - scheduled_bufobj = bufobj; |
494 | - } |
495 | -} |
496 | - |
497 | mgm::BufferObject* mgm::DisplayBuffer::get_front_buffer_object() |
498 | { |
499 | auto front = gbm_surface_lock_front_buffer(surface_gbm.get()); |
500 | @@ -408,33 +226,6 @@ |
501 | return bufobj; |
502 | } |
503 | |
504 | - |
505 | -bool mgm::DisplayBuffer::schedule_page_flip(BufferObject* bufobj) |
506 | -{ |
507 | - /* |
508 | - * Schedule the current front buffer object for display. Note that |
509 | - * the page flip is asynchronous and synchronized with vertical refresh. |
510 | - */ |
511 | - for (auto& output : outputs) |
512 | - { |
513 | - if (output->schedule_page_flip(bufobj->get_drm_fb_id())) |
514 | - page_flips_pending = true; |
515 | - } |
516 | - |
517 | - return page_flips_pending; |
518 | -} |
519 | - |
520 | -void mgm::DisplayBuffer::wait_for_page_flip() |
521 | -{ |
522 | - if (page_flips_pending) |
523 | - { |
524 | - for (auto& output : outputs) |
525 | - output->wait_for_page_flip(); |
526 | - |
527 | - page_flips_pending = false; |
528 | - } |
529 | -} |
530 | - |
531 | void mgm::DisplayBuffer::make_current() |
532 | { |
533 | if (!egl.make_current()) |
534 | @@ -448,8 +239,23 @@ |
535 | egl.release_current(); |
536 | } |
537 | |
538 | -void mgm::DisplayBuffer::schedule_set_crtc() |
539 | -{ |
540 | - needs_set_crtc = true; |
541 | -} |
542 | - |
543 | + |
544 | +std::shared_ptr<mir::graphics::Buffer> mgm::DisplayBuffer::bypass_buffer() |
545 | +{ |
546 | + return bypass_buf; |
547 | +} |
548 | + |
549 | +mgm::BufferObject* mgm::DisplayBuffer::buffer_obj_to_be_posted() |
550 | +{ |
551 | + if (bypass_buf) |
552 | + { |
553 | + return bypass_bufobj; |
554 | + } |
555 | + else |
556 | + { |
557 | + auto bufobj = get_front_buffer_object(); |
558 | + if (!bufobj) |
559 | + fatal_error("Failed to get front buffer object"); |
560 | + return bufobj; |
561 | + } |
562 | +} |
563 | |
564 | === modified file 'src/platforms/mesa/server/display_buffer.h' |
565 | --- src/platforms/mesa/server/display_buffer.h 2015-02-10 18:28:22 +0000 |
566 | +++ src/platforms/mesa/server/display_buffer.h 2015-02-10 18:28:23 +0000 |
567 | @@ -22,10 +22,10 @@ |
568 | #include "mir/graphics/display_buffer.h" |
569 | #include "mir/graphics/display.h" |
570 | #include "display_helpers.h" |
571 | +#include "platform.h" |
572 | |
573 | #include <vector> |
574 | #include <memory> |
575 | -#include <atomic> |
576 | |
577 | namespace mir |
578 | { |
579 | @@ -40,21 +40,17 @@ |
580 | |
581 | class Platform; |
582 | class BufferObject; |
583 | -class KMSOutput; |
584 | |
585 | -class DisplayBuffer : public graphics::DisplayBuffer, |
586 | - public graphics::DisplayGroup |
587 | +class DisplayBuffer : public graphics::DisplayBuffer |
588 | { |
589 | public: |
590 | DisplayBuffer(std::shared_ptr<Platform> const& platform, |
591 | std::shared_ptr<DisplayReport> const& listener, |
592 | - std::vector<std::shared_ptr<KMSOutput>> const& outputs, |
593 | GBMSurfaceUPtr surface_gbm, |
594 | geometry::Rectangle const& area, |
595 | MirOrientation rot, |
596 | GLConfig const& gl_config, |
597 | EGLContext shared_context); |
598 | - ~DisplayBuffer(); |
599 | |
600 | geometry::Rectangle view_area() const override; |
601 | void make_current() override; |
602 | @@ -62,38 +58,29 @@ |
603 | void gl_swap_buffers() override; |
604 | bool post_renderables_if_optimizable(RenderableList const& renderlist) override; |
605 | |
606 | - void for_each_display_buffer( |
607 | - std::function<void(graphics::DisplayBuffer&)> const& f) override; |
608 | - void post() override; |
609 | - |
610 | MirOrientation orientation() const override; |
611 | void set_orientation(MirOrientation const rot, geometry::Rectangle const& a); |
612 | bool uses_alpha() const override; |
613 | - void schedule_set_crtc(); |
614 | - void wait_for_page_flip(); |
615 | + |
616 | + std::shared_ptr<graphics::Buffer> bypass_buffer(); |
617 | + BufferObject* buffer_obj_to_be_posted(); |
618 | |
619 | private: |
620 | BufferObject* get_front_buffer_object(); |
621 | BufferObject* get_buffer_object(struct gbm_bo *bo); |
622 | - bool schedule_page_flip(BufferObject* bufobj); |
623 | |
624 | - BufferObject* last_flipped_bufobj; |
625 | - BufferObject* scheduled_bufobj; |
626 | - std::shared_ptr<graphics::Buffer> last_flipped_bypass_buf; |
627 | std::shared_ptr<Buffer> bypass_buf{nullptr}; |
628 | BufferObject* bypass_bufobj{nullptr}; |
629 | - std::shared_ptr<Platform> const platform; |
630 | - std::shared_ptr<DisplayReport> const listener; |
631 | + |
632 | + BypassOption platform_bypass_option; |
633 | + |
634 | /* DRM helper from mgm::Platform */ |
635 | helpers::DRMHelper& drm; |
636 | - std::vector<std::shared_ptr<KMSOutput>> outputs; |
637 | GBMSurfaceUPtr surface_gbm; |
638 | helpers::EGLHelper egl; |
639 | geometry::Rectangle area; |
640 | uint32_t fb_width, fb_height; |
641 | MirOrientation rotation; |
642 | - std::atomic<bool> needs_set_crtc; |
643 | - bool page_flips_pending; |
644 | }; |
645 | |
646 | } |
647 | |
648 | === added file 'src/platforms/mesa/server/display_group.cpp' |
649 | --- src/platforms/mesa/server/display_group.cpp 1970-01-01 00:00:00 +0000 |
650 | +++ src/platforms/mesa/server/display_group.cpp 2015-02-10 18:28:23 +0000 |
651 | @@ -0,0 +1,219 @@ |
652 | +/* |
653 | + * Copyright © 2015 Canonical Ltd. |
654 | + * |
655 | + * This program is free software: you can redistribute it and/or modify it |
656 | + * under the terms of the GNU Lesser General Public License version 3, |
657 | + * as published by the Free Software Foundation. |
658 | + * |
659 | + * This program is distributed in the hope that it will be useful, |
660 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
661 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
662 | + * GNU Lesser General Public License for more details. |
663 | + * |
664 | + * You should have received a copy of the GNU Lesser General Public License |
665 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
666 | + * |
667 | + * Authored by: Kevin DuBois <kevin.dubois@canonical.com> |
668 | + */ |
669 | + |
670 | +#include "display_group.h" |
671 | +#include "buffer_object.h" |
672 | + |
673 | +#include "platform.h" |
674 | +#include "kms_output.h" |
675 | +#include "mir/graphics/display_report.h" |
676 | +#include "bypass.h" |
677 | +#include "gbm_buffer.h" |
678 | +#include "mir/fatal.h" |
679 | +#include <boost/throw_exception.hpp> |
680 | +#include <GLES2/gl2.h> |
681 | + |
682 | +#include <stdexcept> |
683 | + |
684 | +namespace mgm = mir::graphics::mesa; |
685 | +namespace geom = mir::geometry; |
686 | + |
687 | +mgm::DisplayGroup::DisplayGroup( |
688 | + std::shared_ptr<Platform> const& platform, |
689 | + std::shared_ptr<DisplayReport> const& listener, |
690 | + std::vector<std::shared_ptr<KMSOutput>> const& outputs, |
691 | + GBMSurfaceUPtr surface_gbm, |
692 | + geometry::Rectangle const& area, |
693 | + MirOrientation rot, |
694 | + GLConfig const& gl_config, |
695 | + EGLContext shared_context) : |
696 | + db{platform, listener, std::move(surface_gbm), area, rot, gl_config, shared_context}, |
697 | + platform(platform), |
698 | + listener(listener), |
699 | + needs_set_crtc{false}, |
700 | + page_flips_pending{false}, |
701 | + outputs(outputs), |
702 | + last_flipped_bufobj{nullptr}, |
703 | + scheduled_bufobj{db.buffer_obj_to_be_posted()}, |
704 | + last_flipped_bypass_buf{nullptr} |
705 | +{ |
706 | + for (auto& output : outputs) |
707 | + { |
708 | + if (!output->set_crtc(scheduled_bufobj->get_drm_fb_id())) |
709 | + fatal_error("Failed to set DRM crtc"); |
710 | + } |
711 | +} |
712 | + |
713 | +mgm::DisplayGroup::~DisplayGroup() |
714 | +{ |
715 | + /* |
716 | + * There is no need to destroy last_flipped_bufobj manually. |
717 | + * It will be destroyed when its gbm_surface gets destroyed. |
718 | + */ |
719 | + if (last_flipped_bufobj) |
720 | + last_flipped_bufobj->release(); |
721 | + |
722 | + if (scheduled_bufobj) |
723 | + scheduled_bufobj->release(); |
724 | +} |
725 | + |
726 | +void mgm::DisplayGroup::for_each_display_buffer( |
727 | + std::function<void(graphics::DisplayBuffer&)> const& f) |
728 | +{ |
729 | + f(db); |
730 | +} |
731 | + |
732 | +void mgm::DisplayGroup::post() |
733 | +{ |
734 | + /* |
735 | + * We might not have waited for the previous frame to page flip yet. |
736 | + * This is good because it maximizes the time available to spend rendering |
737 | + * each frame. Just remember wait_for_page_flip() must be called at some |
738 | + * point before the next schedule_page_flip(). |
739 | + */ |
740 | + wait_for_page_flip(); |
741 | + |
742 | + /* |
743 | + * Switching from bypass to compositing? Now is the earliest safe time |
744 | + * we can unreference the bypass buffer... |
745 | + */ |
746 | + if (scheduled_bufobj) |
747 | + last_flipped_bypass_buf = nullptr; |
748 | + /* |
749 | + * Release the last flipped buffer object (which is not displayed anymore) |
750 | + * to make it available for future rendering. |
751 | + */ |
752 | + if (last_flipped_bufobj) |
753 | + last_flipped_bufobj->release(); |
754 | + |
755 | + last_flipped_bufobj = scheduled_bufobj; |
756 | + scheduled_bufobj = nullptr; |
757 | + |
758 | + mgm::BufferObject *bufobj = db.buffer_obj_to_be_posted(); |
759 | + auto bypass_buffer = db.bypass_buffer(); |
760 | + /* |
761 | + * Schedule the current front buffer object for display, and wait |
762 | + * for it to be actually displayed (flipped). |
763 | + * |
764 | + * If the flip fails, release the buffer object to make it available |
765 | + * for future rendering. |
766 | + */ |
767 | + if (!needs_set_crtc && !schedule_page_flip(bufobj)) |
768 | + { |
769 | + if (!bypass_buffer) |
770 | + bufobj->release(); |
771 | + fatal_error("Failed to schedule page flip"); |
772 | + } |
773 | + else if (needs_set_crtc) |
774 | + { |
775 | + for (auto& output : outputs) |
776 | + { |
777 | + if (!output->set_crtc(bufobj->get_drm_fb_id())) |
778 | + fatal_error("Failed to set DRM crtc"); |
779 | + } |
780 | + needs_set_crtc = false; |
781 | + } |
782 | + |
783 | + if (bypass_buffer) |
784 | + { |
785 | + /* |
786 | + * For composited frames we defer wait_for_page_flip till just before |
787 | + * the next frame, but not for bypass frames. Deferring the flip of |
788 | + * bypass frames would increase the time we held |
789 | + * last_flipped_bypass_buf unacceptably, resulting in client stuttering |
790 | + * unless we allocate more buffers (which I'm trying to avoid). |
791 | + * Also, bypass does not need the deferred page flip because it has |
792 | + * no compositing/rendering step for which to save time for. |
793 | + */ |
794 | + wait_for_page_flip(); |
795 | + scheduled_bufobj = nullptr; |
796 | + |
797 | + /* |
798 | + * Keep a reference to the buffer being bypassed for the entire |
799 | + * duration of the frame. This ensures the buffer doesn't get reused by |
800 | + * the client while its on-screen, which would be seen as tearing or |
801 | + * worse. |
802 | + */ |
803 | + last_flipped_bypass_buf = db.bypass_buffer(); |
804 | + } |
805 | + else |
806 | + { |
807 | + /* |
808 | + * Not in clone mode? We can afford to wait for the page flip then, |
809 | + * making us double-buffered (noticeably less laggy than the triple |
810 | + * buffering that clone mode requires). |
811 | + */ |
812 | + if (outputs.size() == 1) |
813 | + { |
814 | + wait_for_page_flip(); |
815 | + |
816 | + /* |
817 | + * bufobj is now physically on screen. Release the old frame... |
818 | + */ |
819 | + if (last_flipped_bufobj) |
820 | + { |
821 | + last_flipped_bufobj->release(); |
822 | + last_flipped_bufobj = nullptr; |
823 | + } |
824 | + |
825 | + /* |
826 | + * last_flipped_bufobj will be set correctly on the next iteration |
827 | + * Don't do it here or else bufobj would be released while still |
828 | + * on screen (hence tearing and artefacts). |
829 | + */ |
830 | + } |
831 | + |
832 | + scheduled_bufobj = bufobj; |
833 | + } |
834 | +} |
835 | + |
836 | +void mgm::DisplayGroup::schedule_set_crtc() |
837 | +{ |
838 | + needs_set_crtc = true; |
839 | +} |
840 | + |
841 | +bool mgm::DisplayGroup::schedule_page_flip(BufferObject* bufobj) |
842 | +{ |
843 | + /* |
844 | + * Schedule the current front buffer object for display. Note that |
845 | + * the page flip is asynchronous and synchronized with vertical refresh. |
846 | + */ |
847 | + for (auto& output : outputs) |
848 | + { |
849 | + if (output->schedule_page_flip(bufobj->get_drm_fb_id())) |
850 | + page_flips_pending = true; |
851 | + } |
852 | + |
853 | + return page_flips_pending; |
854 | +} |
855 | + |
856 | +void mgm::DisplayGroup::wait_for_page_flip() |
857 | +{ |
858 | + if (page_flips_pending) |
859 | + { |
860 | + for (auto& output : outputs) |
861 | + output->wait_for_page_flip(); |
862 | + |
863 | + page_flips_pending = false; |
864 | + } |
865 | +} |
866 | + |
867 | +void mgm::DisplayGroup::set_orientation(MirOrientation const rot, geometry::Rectangle const& a) |
868 | +{ |
869 | + db.set_orientation(rot, a); |
870 | +} |
871 | |
872 | === added file 'src/platforms/mesa/server/display_group.h' |
873 | --- src/platforms/mesa/server/display_group.h 1970-01-01 00:00:00 +0000 |
874 | +++ src/platforms/mesa/server/display_group.h 2015-02-10 18:28:23 +0000 |
875 | @@ -0,0 +1,86 @@ |
876 | +/* |
877 | + * Copyright © 2015 Canonical Ltd. |
878 | + * |
879 | + * This program is free software: you can redistribute it and/or modify it |
880 | + * under the terms of the GNU Lesser General Public License version 3, |
881 | + * as published by the Free Software Foundation. |
882 | + * |
883 | + * This program is distributed in the hope that it will be useful, |
884 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
885 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
886 | + * GNU Lesser General Public License for more details. |
887 | + * |
888 | + * You should have received a copy of the GNU Lesser General Public License |
889 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
890 | + * |
891 | + * Authored by: Kevin DuBois <kevin.dubois@canonical.com> |
892 | + */ |
893 | + |
894 | +#ifndef MIR_GRAPHICS_MESA_DISPLAY_GROUP_H_ |
895 | +#define MIR_GRAPHICS_MESA_DISPLAY_GROUP_H_ |
896 | + |
897 | +#include "mir/graphics/display.h" |
898 | +#include "display_buffer.h" |
899 | + |
900 | +#include <vector> |
901 | +#include <memory> |
902 | +#include <atomic> |
903 | + |
904 | +namespace mir |
905 | +{ |
906 | +namespace graphics |
907 | +{ |
908 | + |
909 | +class DisplayReport; |
910 | +class GLConfig; |
911 | + |
912 | +namespace mesa |
913 | +{ |
914 | + |
915 | +class Platform; |
916 | +class BufferObject; |
917 | +class KMSOutput; |
918 | + |
919 | +class DisplayGroup : public graphics::DisplayGroup |
920 | +{ |
921 | +public: |
922 | + DisplayGroup( |
923 | + std::shared_ptr<Platform> const& platform, |
924 | + std::shared_ptr<DisplayReport> const& listener, |
925 | + std::vector<std::shared_ptr<KMSOutput>> const& outputs, |
926 | + GBMSurfaceUPtr surface_gbm, |
927 | + geometry::Rectangle const& area, |
928 | + MirOrientation rot, |
929 | + GLConfig const& gl_config, |
930 | + EGLContext shared_context); |
931 | + ~DisplayGroup(); |
932 | + |
933 | + void for_each_display_buffer( |
934 | + std::function<void(graphics::DisplayBuffer&)> const& f) override; |
935 | + void post() override; |
936 | + |
937 | + void schedule_set_crtc(); |
938 | + void wait_for_page_flip(); |
939 | + void set_orientation(MirOrientation const rot, geometry::Rectangle const& a); |
940 | + |
941 | +private: |
942 | + bool schedule_page_flip(BufferObject* bufobj); |
943 | + |
944 | + DisplayBuffer db; |
945 | + std::shared_ptr<Platform> const platform; |
946 | + std::shared_ptr<DisplayReport> const listener; |
947 | + |
948 | + std::atomic<bool> needs_set_crtc; |
949 | + bool page_flips_pending; |
950 | + std::vector<std::shared_ptr<KMSOutput>> outputs; |
951 | + |
952 | + BufferObject* last_flipped_bufobj; |
953 | + BufferObject* scheduled_bufobj; |
954 | + std::shared_ptr<graphics::Buffer> last_flipped_bypass_buf; |
955 | +}; |
956 | + |
957 | +} |
958 | +} |
959 | +} |
960 | + |
961 | +#endif /* MIR_GRAPHICS_MESA_DISPLAY_GROUP_H_ */ |
962 | |
963 | === modified file 'tests/unit-tests/graphics/mesa/CMakeLists.txt' |
964 | --- tests/unit-tests/graphics/mesa/CMakeLists.txt 2015-01-21 07:34:50 +0000 |
965 | +++ tests/unit-tests/graphics/mesa/CMakeLists.txt 2015-02-10 18:28:23 +0000 |
966 | @@ -17,6 +17,7 @@ |
967 | ${CMAKE_CURRENT_SOURCE_DIR}/test_bypass.cpp |
968 | ${CMAKE_CURRENT_SOURCE_DIR}/test_ipc_operations.cpp |
969 | ${CMAKE_CURRENT_SOURCE_DIR}/test_nested_authentication.cpp |
970 | + ${CMAKE_CURRENT_SOURCE_DIR}/test_display_group.cpp |
971 | $<TARGET_OBJECTS:mirplatformgraphicsmesaobjects> |
972 | ) |
973 | |
974 | |
975 | === modified file 'tests/unit-tests/graphics/mesa/test_display_buffer.cpp' |
976 | --- tests/unit-tests/graphics/mesa/test_display_buffer.cpp 2015-02-10 18:28:22 +0000 |
977 | +++ tests/unit-tests/graphics/mesa/test_display_buffer.cpp 2015-02-10 18:28:23 +0000 |
978 | @@ -28,7 +28,6 @@ |
979 | #include "mir_test_doubles/stub_gbm_native_buffer.h" |
980 | #include "mir_test_framework/udev_environment.h" |
981 | #include "mir_test_doubles/fake_renderable.h" |
982 | -#include "mock_kms_output.h" |
983 | |
984 | #include <gtest/gtest.h> |
985 | #include <gmock/gmock.h> |
986 | @@ -73,12 +72,6 @@ |
987 | |
988 | fake_devices.add_standard_device("standard-drm-devices"); |
989 | |
990 | - mock_kms_output = std::make_shared<NiceMock<MockKMSOutput>>(); |
991 | - ON_CALL(*mock_kms_output, set_crtc(_)) |
992 | - .WillByDefault(Return(true)); |
993 | - ON_CALL(*mock_kms_output, schedule_page_flip(_)) |
994 | - .WillByDefault(Return(true)); |
995 | - |
996 | ON_CALL(*mock_bypassable_buffer, size()) |
997 | .WillByDefault(Return(display_area.size)); |
998 | ON_CALL(*mock_bypassable_buffer, can_bypass()) |
999 | @@ -109,7 +102,6 @@ |
1000 | gbm_bo* fake_bo; |
1001 | gbm_bo_handle fake_handle; |
1002 | UdevEnvironment fake_devices; |
1003 | - std::shared_ptr<MockKMSOutput> mock_kms_output; |
1004 | StubGLConfig gl_config; |
1005 | mir::graphics::RenderableList const bypassable_list; |
1006 | |
1007 | @@ -120,7 +112,6 @@ |
1008 | graphics::mesa::DisplayBuffer db( |
1009 | create_platform(), |
1010 | null_display_report(), |
1011 | - {}, |
1012 | nullptr, |
1013 | display_area, |
1014 | mir_orientation_normal, |
1015 | @@ -135,7 +126,6 @@ |
1016 | graphics::mesa::DisplayBuffer db( |
1017 | create_platform(), |
1018 | null_display_report(), |
1019 | - {mock_kms_output}, |
1020 | nullptr, |
1021 | display_area, |
1022 | mir_orientation_normal, |
1023 | @@ -148,20 +138,20 @@ |
1024 | TEST_F(MesaDisplayBufferTest, failed_bypass_falls_back_gracefully) |
1025 | { // Regression test for LP: #1398296 |
1026 | EXPECT_CALL(mock_drm, drmModeAddFB2(_, _, _, _, _, _, _, _, _)) |
1027 | - .WillOnce(Return(0)) // During the DisplayBuffer constructor |
1028 | - .WillOnce(Return(-22)) // Fail first bypass attempt |
1029 | - .WillOnce(Return(0)); // Succeed second bypass attempt |
1030 | + .WillOnce(Return(0)) |
1031 | + .WillOnce(Return(-22)) |
1032 | + .WillOnce(Return(0)); |
1033 | |
1034 | graphics::mesa::DisplayBuffer db( |
1035 | create_platform(), |
1036 | null_display_report(), |
1037 | - {mock_kms_output}, |
1038 | nullptr, |
1039 | display_area, |
1040 | mir_orientation_normal, |
1041 | gl_config, |
1042 | mock_egl.fake_egl_context); |
1043 | |
1044 | + EXPECT_TRUE(db.post_renderables_if_optimizable(bypassable_list)); |
1045 | EXPECT_FALSE(db.post_renderables_if_optimizable(bypassable_list)); |
1046 | // And then we recover. DRM finds enough resources to AddFB ... |
1047 | EXPECT_TRUE(db.post_renderables_if_optimizable(bypassable_list)); |
1048 | @@ -184,7 +174,6 @@ |
1049 | graphics::mesa::DisplayBuffer db( |
1050 | create_platform(), |
1051 | null_display_report(), |
1052 | - {mock_kms_output}, |
1053 | nullptr, |
1054 | display_area, |
1055 | mir_orientation_normal, |
1056 | @@ -199,7 +188,6 @@ |
1057 | graphics::mesa::DisplayBuffer db( |
1058 | create_platform(), |
1059 | null_display_report(), |
1060 | - {}, |
1061 | nullptr, |
1062 | display_area, |
1063 | mir_orientation_right, |
1064 | @@ -214,7 +202,6 @@ |
1065 | graphics::mesa::DisplayBuffer db( |
1066 | create_platform(), |
1067 | null_display_report(), |
1068 | - {}, |
1069 | nullptr, |
1070 | display_area, |
1071 | mir_orientation_left, |
1072 | @@ -224,153 +211,6 @@ |
1073 | EXPECT_EQ(mir_orientation_left, db.orientation()); |
1074 | } |
1075 | |
1076 | -TEST_F(MesaDisplayBufferTest, normal_rotation_constructs_normal_fb) |
1077 | -{ |
1078 | - EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_)) |
1079 | - .WillOnce(Return((void*)0)); |
1080 | - EXPECT_CALL(mock_drm, drmModeAddFB2(_, width, height, _, _, _, _, _, _)) |
1081 | - .Times(1); |
1082 | - |
1083 | - graphics::mesa::DisplayBuffer db( |
1084 | - create_platform(), |
1085 | - null_display_report(), |
1086 | - {}, |
1087 | - nullptr, |
1088 | - display_area, |
1089 | - mir_orientation_normal, |
1090 | - gl_config, |
1091 | - mock_egl.fake_egl_context); |
1092 | -} |
1093 | - |
1094 | -TEST_F(MesaDisplayBufferTest, left_rotation_constructs_transposed_fb) |
1095 | -{ |
1096 | - EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_)) |
1097 | - .WillOnce(Return((void*)0)); |
1098 | - EXPECT_CALL(mock_drm, drmModeAddFB2(_, height, width, _, _, _, _, _, _)) |
1099 | - .Times(1); |
1100 | - |
1101 | - graphics::mesa::DisplayBuffer db( |
1102 | - create_platform(), |
1103 | - null_display_report(), |
1104 | - {}, |
1105 | - nullptr, |
1106 | - display_area, |
1107 | - mir_orientation_left, |
1108 | - gl_config, |
1109 | - mock_egl.fake_egl_context); |
1110 | -} |
1111 | - |
1112 | -TEST_F(MesaDisplayBufferTest, inverted_rotation_constructs_normal_fb) |
1113 | -{ |
1114 | - EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_)) |
1115 | - .WillOnce(Return((void*)0)); |
1116 | - EXPECT_CALL(mock_drm, drmModeAddFB2(_, width, height, _, _, _, _, _, _)) |
1117 | - .Times(1); |
1118 | - |
1119 | - graphics::mesa::DisplayBuffer db( |
1120 | - create_platform(), |
1121 | - null_display_report(), |
1122 | - {}, |
1123 | - nullptr, |
1124 | - display_area, |
1125 | - mir_orientation_inverted, |
1126 | - gl_config, |
1127 | - mock_egl.fake_egl_context); |
1128 | -} |
1129 | - |
1130 | -TEST_F(MesaDisplayBufferTest, right_rotation_constructs_transposed_fb) |
1131 | -{ |
1132 | - EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_)) |
1133 | - .WillOnce(Return((void*)0)); |
1134 | - EXPECT_CALL(mock_drm, drmModeAddFB2(_, height, width, _, _, _, _, _, _)) |
1135 | - .Times(1); |
1136 | - |
1137 | - graphics::mesa::DisplayBuffer db( |
1138 | - create_platform(), |
1139 | - null_display_report(), |
1140 | - {}, |
1141 | - nullptr, |
1142 | - display_area, |
1143 | - mir_orientation_right, |
1144 | - gl_config, |
1145 | - mock_egl.fake_egl_context); |
1146 | -} |
1147 | - |
1148 | -TEST_F(MesaDisplayBufferTest, clone_mode_first_flip_flips_but_no_wait) |
1149 | -{ |
1150 | - // Ensure clone mode can do multiple page flips in parallel without |
1151 | - // blocking on either (at least till the second post) |
1152 | - EXPECT_CALL(*mock_kms_output, schedule_page_flip(_)) |
1153 | - .Times(2); |
1154 | - EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) |
1155 | - .Times(0); |
1156 | - |
1157 | - graphics::mesa::DisplayBuffer db( |
1158 | - create_platform(), |
1159 | - null_display_report(), |
1160 | - {mock_kms_output, mock_kms_output}, |
1161 | - nullptr, |
1162 | - display_area, |
1163 | - mir_orientation_normal, |
1164 | - gl_config, |
1165 | - mock_egl.fake_egl_context); |
1166 | - |
1167 | - db.gl_swap_buffers(); |
1168 | - db.post(); |
1169 | -} |
1170 | - |
1171 | -TEST_F(MesaDisplayBufferTest, single_mode_first_post_flips_with_wait) |
1172 | -{ |
1173 | - EXPECT_CALL(*mock_kms_output, schedule_page_flip(_)) |
1174 | - .Times(1); |
1175 | - EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) |
1176 | - .Times(1); |
1177 | - |
1178 | - graphics::mesa::DisplayBuffer db( |
1179 | - create_platform(), |
1180 | - null_display_report(), |
1181 | - {mock_kms_output}, |
1182 | - nullptr, |
1183 | - display_area, |
1184 | - mir_orientation_normal, |
1185 | - gl_config, |
1186 | - mock_egl.fake_egl_context); |
1187 | - |
1188 | - db.gl_swap_buffers(); |
1189 | - db.post(); |
1190 | -} |
1191 | - |
1192 | -TEST_F(MesaDisplayBufferTest, clone_mode_waits_for_page_flip_on_second_flip) |
1193 | -{ |
1194 | - InSequence seq; |
1195 | - |
1196 | - EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) |
1197 | - .Times(0); |
1198 | - EXPECT_CALL(*mock_kms_output, schedule_page_flip(_)) |
1199 | - .Times(2); |
1200 | - EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) |
1201 | - .Times(2); |
1202 | - EXPECT_CALL(*mock_kms_output, schedule_page_flip(_)) |
1203 | - .Times(2); |
1204 | - EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) |
1205 | - .Times(0); |
1206 | - |
1207 | - graphics::mesa::DisplayBuffer db( |
1208 | - create_platform(), |
1209 | - null_display_report(), |
1210 | - {mock_kms_output, mock_kms_output}, |
1211 | - nullptr, |
1212 | - display_area, |
1213 | - mir_orientation_normal, |
1214 | - gl_config, |
1215 | - mock_egl.fake_egl_context); |
1216 | - |
1217 | - db.gl_swap_buffers(); |
1218 | - db.post(); |
1219 | - |
1220 | - db.gl_swap_buffers(); |
1221 | - db.post(); |
1222 | -} |
1223 | |
1224 | TEST_F(MesaDisplayBufferTest, skips_bypass_because_of_incompatible_list) |
1225 | { |
1226 | @@ -382,7 +222,6 @@ |
1227 | graphics::mesa::DisplayBuffer db( |
1228 | create_platform(), |
1229 | null_display_report(), |
1230 | - {mock_kms_output}, |
1231 | nullptr, |
1232 | display_area, |
1233 | mir_orientation_normal, |
1234 | @@ -406,7 +245,6 @@ |
1235 | graphics::mesa::DisplayBuffer db( |
1236 | create_platform(), |
1237 | null_display_report(), |
1238 | - {mock_kms_output}, |
1239 | nullptr, |
1240 | display_area, |
1241 | mir_orientation_normal, |
1242 | @@ -423,7 +261,6 @@ |
1243 | graphics::mesa::DisplayBuffer db( |
1244 | create_platform(), |
1245 | null_display_report(), |
1246 | - {mock_kms_output}, |
1247 | nullptr, |
1248 | area, |
1249 | mir_orientation_normal, |
1250 | |
1251 | === added file 'tests/unit-tests/graphics/mesa/test_display_group.cpp' |
1252 | --- tests/unit-tests/graphics/mesa/test_display_group.cpp 1970-01-01 00:00:00 +0000 |
1253 | +++ tests/unit-tests/graphics/mesa/test_display_group.cpp 2015-02-10 18:28:23 +0000 |
1254 | @@ -0,0 +1,247 @@ |
1255 | +/* |
1256 | + * Copyright © 2015 Canonical Ltd. |
1257 | + * |
1258 | + * This program is free software: you can redistribute it and/or modify |
1259 | + * it under the terms of the GNU General Public License version 3 as |
1260 | + * published by the Free Software Foundation. |
1261 | + * |
1262 | + * This program is distributed in the hope that it will be useful, |
1263 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1264 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1265 | + * GNU General Public License for more details. |
1266 | + * |
1267 | + * You should have received a copy of the GNU General Public License |
1268 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1269 | + * |
1270 | + * Authored by: Kevin DuBois <kevin.dubois@canonical.com> |
1271 | + */ |
1272 | + |
1273 | +#include "src/platforms/mesa/server/platform.h" |
1274 | +#include "src/platforms/mesa/server/display_group.h" |
1275 | +#include "src/server/report/null_report_factory.h" |
1276 | +#include "mir_test_doubles/mock_egl.h" |
1277 | +#include "mir_test_doubles/mock_gl.h" |
1278 | +#include "mir_test_doubles/mock_drm.h" |
1279 | +#include "mir_test_doubles/mock_buffer.h" |
1280 | +#include "mir_test_doubles/mock_gbm.h" |
1281 | +#include "mir_test_doubles/stub_gl_config.h" |
1282 | +#include "mir_test_doubles/platform_factory.h" |
1283 | +#include "mir_test_doubles/stub_gbm_native_buffer.h" |
1284 | +#include "mir_test_framework/udev_environment.h" |
1285 | +#include "mir_test_doubles/fake_renderable.h" |
1286 | +#include "mock_kms_output.h" |
1287 | + |
1288 | +#include <gtest/gtest.h> |
1289 | +#include <gmock/gmock.h> |
1290 | +#include <gbm.h> |
1291 | + |
1292 | +using namespace testing; |
1293 | +using namespace mir; |
1294 | +using namespace std; |
1295 | +using namespace mir::test; |
1296 | +using namespace mir::test::doubles; |
1297 | +using namespace mir_test_framework; |
1298 | +using namespace mir::graphics; |
1299 | +using mir::report::null_display_report; |
1300 | + |
1301 | +class MesaDisplayGroup : public Test |
1302 | +{ |
1303 | +public: |
1304 | + MesaDisplayGroup() |
1305 | + : mock_bypassable_buffer{std::make_shared<NiceMock<MockBuffer>>()} |
1306 | + , stub_gbm_native_buffer{ |
1307 | + std::make_shared<StubGBMNativeBuffer>(display_area.size)} |
1308 | + { |
1309 | + ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_)) |
1310 | + .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), |
1311 | + SetArgPointee<4>(1), |
1312 | + Return(EGL_TRUE))); |
1313 | + |
1314 | + mock_egl.provide_egl_extensions(); |
1315 | + mock_gl.provide_gles_extensions(); |
1316 | + |
1317 | + fake_bo = reinterpret_cast<gbm_bo*>(123); |
1318 | + ON_CALL(mock_gbm, gbm_surface_lock_front_buffer(_)) |
1319 | + .WillByDefault(Return(fake_bo)); |
1320 | + fake_handle.u32 = 123; |
1321 | + ON_CALL(mock_gbm, gbm_bo_get_handle(_)) |
1322 | + .WillByDefault(Return(fake_handle)); |
1323 | + ON_CALL(mock_gbm, gbm_bo_get_stride(_)) |
1324 | + .WillByDefault(Return(456)); |
1325 | + |
1326 | + fake_devices.add_standard_device("standard-drm-devices"); |
1327 | + |
1328 | + mock_kms_output = std::make_shared<NiceMock<MockKMSOutput>>(); |
1329 | + ON_CALL(*mock_kms_output, set_crtc(_)) |
1330 | + .WillByDefault(Return(true)); |
1331 | + ON_CALL(*mock_kms_output, schedule_page_flip(_)) |
1332 | + .WillByDefault(Return(true)); |
1333 | + } |
1334 | + |
1335 | + // The platform has an implicit dependency on mock_gbm etc so must be |
1336 | + // reconstructed locally to ensure its lifetime is shorter than mock_gbm. |
1337 | + shared_ptr<graphics::mesa::Platform> create_platform() |
1338 | + { |
1339 | + return mir::test::doubles::create_mesa_platform_with_null_dependencies(); |
1340 | + } |
1341 | + |
1342 | +protected: |
1343 | + int const width{56}; |
1344 | + int const height{78}; |
1345 | + mir::geometry::Rectangle const display_area{{12,34}, {width,height}}; |
1346 | + NiceMock<MockGBM> mock_gbm; |
1347 | + NiceMock<MockEGL> mock_egl; |
1348 | + NiceMock<MockGL> mock_gl; |
1349 | + NiceMock<MockDRM> mock_drm; |
1350 | + std::shared_ptr<MockBuffer> mock_bypassable_buffer; |
1351 | + std::shared_ptr<mesa::GBMNativeBuffer> stub_gbm_native_buffer; |
1352 | + gbm_bo* fake_bo; |
1353 | + gbm_bo_handle fake_handle; |
1354 | + UdevEnvironment fake_devices; |
1355 | + std::shared_ptr<MockKMSOutput> mock_kms_output; |
1356 | + StubGLConfig gl_config; |
1357 | + |
1358 | +}; |
1359 | + |
1360 | +TEST_F(MesaDisplayGroup, clone_mode_first_flip_flips_but_no_wait) |
1361 | +{ |
1362 | + // Ensure clone mode can do multiple page flips in parallel without |
1363 | + // blocking on either (at least till the second post) |
1364 | + EXPECT_CALL(*mock_kms_output, schedule_page_flip(_)) |
1365 | + .Times(2); |
1366 | + EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) |
1367 | + .Times(0); |
1368 | + |
1369 | + graphics::mesa::DisplayGroup dg( |
1370 | + create_platform(), |
1371 | + null_display_report(), |
1372 | + {mock_kms_output, mock_kms_output}, |
1373 | + nullptr, |
1374 | + display_area, |
1375 | + mir_orientation_normal, |
1376 | + gl_config, |
1377 | + mock_egl.fake_egl_context); |
1378 | + |
1379 | + dg.post(); |
1380 | +} |
1381 | + |
1382 | +TEST_F(MesaDisplayGroup, single_mode_first_post_flips_with_wait) |
1383 | +{ |
1384 | + EXPECT_CALL(*mock_kms_output, schedule_page_flip(_)) |
1385 | + .Times(1); |
1386 | + EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) |
1387 | + .Times(1); |
1388 | + |
1389 | + graphics::mesa::DisplayGroup dg( |
1390 | + create_platform(), |
1391 | + null_display_report(), |
1392 | + {mock_kms_output}, |
1393 | + nullptr, |
1394 | + display_area, |
1395 | + mir_orientation_normal, |
1396 | + gl_config, |
1397 | + mock_egl.fake_egl_context); |
1398 | + |
1399 | + dg.post(); |
1400 | +} |
1401 | + |
1402 | +TEST_F(MesaDisplayGroup, clone_mode_waits_for_page_flip_on_second_flip) |
1403 | +{ |
1404 | + InSequence seq; |
1405 | + |
1406 | + EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) |
1407 | + .Times(0); |
1408 | + EXPECT_CALL(*mock_kms_output, schedule_page_flip(_)) |
1409 | + .Times(2); |
1410 | + EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) |
1411 | + .Times(2); |
1412 | + EXPECT_CALL(*mock_kms_output, schedule_page_flip(_)) |
1413 | + .Times(2); |
1414 | + EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) |
1415 | + .Times(0); |
1416 | + |
1417 | + graphics::mesa::DisplayGroup dg( |
1418 | + create_platform(), |
1419 | + null_display_report(), |
1420 | + {mock_kms_output, mock_kms_output}, |
1421 | + nullptr, |
1422 | + display_area, |
1423 | + mir_orientation_normal, |
1424 | + gl_config, |
1425 | + mock_egl.fake_egl_context); |
1426 | + |
1427 | + dg.post(); |
1428 | + dg.post(); |
1429 | +} |
1430 | + |
1431 | +TEST_F(MesaDisplayGroup, normal_rotation_constructs_normal_fb) |
1432 | +{ |
1433 | + EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_)) |
1434 | + .WillOnce(Return((void*)0)); |
1435 | + EXPECT_CALL(mock_drm, drmModeAddFB2(_, width, height, _, _, _, _, _, _)) |
1436 | + .Times(1); |
1437 | + |
1438 | + graphics::mesa::DisplayGroup dg( |
1439 | + create_platform(), |
1440 | + null_display_report(), |
1441 | + {}, |
1442 | + nullptr, |
1443 | + display_area, |
1444 | + mir_orientation_normal, |
1445 | + gl_config, |
1446 | + mock_egl.fake_egl_context); |
1447 | +} |
1448 | + |
1449 | +TEST_F(MesaDisplayGroup, left_rotation_constructs_transposed_fb) |
1450 | +{ |
1451 | + EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_)) |
1452 | + .WillOnce(Return((void*)0)); |
1453 | + EXPECT_CALL(mock_drm, drmModeAddFB2(_, height, width, _, _, _, _, _, _)) |
1454 | + .Times(1); |
1455 | + |
1456 | + graphics::mesa::DisplayGroup dg( |
1457 | + create_platform(), |
1458 | + null_display_report(), |
1459 | + {}, |
1460 | + nullptr, |
1461 | + display_area, |
1462 | + mir_orientation_left, |
1463 | + gl_config, |
1464 | + mock_egl.fake_egl_context); |
1465 | +} |
1466 | + |
1467 | +TEST_F(MesaDisplayGroup, inverted_rotation_constructs_normal_fb) |
1468 | +{ |
1469 | + EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_)) |
1470 | + .WillOnce(Return((void*)0)); |
1471 | + EXPECT_CALL(mock_drm, drmModeAddFB2(_, width, height, _, _, _, _, _, _)) |
1472 | + .Times(1); |
1473 | + |
1474 | + graphics::mesa::DisplayGroup dg( |
1475 | + create_platform(), |
1476 | + null_display_report(), |
1477 | + {}, |
1478 | + nullptr, |
1479 | + display_area, |
1480 | + mir_orientation_inverted, |
1481 | + gl_config, |
1482 | + mock_egl.fake_egl_context); |
1483 | +} |
1484 | + |
1485 | +TEST_F(MesaDisplayGroup, right_rotation_constructs_transposed_fb) |
1486 | +{ |
1487 | + EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_)) |
1488 | + .WillOnce(Return((void*)0)); |
1489 | + EXPECT_CALL(mock_drm, drmModeAddFB2(_, height, width, _, _, _, _, _, _)) |
1490 | + .Times(1); |
1491 | + |
1492 | + graphics::mesa::DisplayGroup dg( |
1493 | + create_platform(), |
1494 | + null_display_report(), |
1495 | + {}, |
1496 | + nullptr, |
1497 | + display_area, |
1498 | + mir_orientation_right, |
1499 | + gl_config, |
1500 | + mock_egl.fake_egl_context); |
1501 | +} |
I had planned to split Mesa DisplayBuffer into a generic DRMDisplayBuffer and MesaDisplayBuffer. So it's then obvious and demonstrated that Mir doesn't need any OpenGL to work. And Mir could be run without any GL support at all (e.g. fullscreen system compositors without effects).
But this might make that more difficult...?
Not sure yet, but make sure not to create any coupling between swap buffers and posting. I've been slowly teasing them apart and will continue to do so more clearly in future.