Mir

Merge lp:~kdub/mir/mesa-display-group into lp:mir

Proposed by Kevin DuBois
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
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

To post a comment you must log in.
lp:~kdub/mir/mesa-display-group updated
2292. By Kevin DuBois

merge base

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

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.

review: Needs Information

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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/platforms/mesa/server/CMakeLists.txt'
--- src/platforms/mesa/server/CMakeLists.txt 2015-02-04 08:45:38 +0000
+++ src/platforms/mesa/server/CMakeLists.txt 2015-02-10 18:28:23 +0000
@@ -27,6 +27,7 @@
27 real_kms_display_configuration.cpp27 real_kms_display_configuration.cpp
28 drm_mode_resources.cpp28 drm_mode_resources.cpp
29 display_buffer.cpp29 display_buffer.cpp
30 display_group.cpp
30 real_kms_output.cpp31 real_kms_output.cpp
31 real_kms_output_container.cpp32 real_kms_output_container.cpp
32 kms_page_flipper.cpp33 kms_page_flipper.cpp
@@ -38,6 +39,7 @@
38 bypass.cpp39 bypass.cpp
39 ipc_operations.cpp40 ipc_operations.cpp
40 nested_authentication.cpp41 nested_authentication.cpp
42 buffer_object.cpp
41)43)
4244
43add_library(45add_library(
4446
=== added file 'src/platforms/mesa/server/buffer_object.cpp'
--- src/platforms/mesa/server/buffer_object.cpp 1970-01-01 00:00:00 +0000
+++ src/platforms/mesa/server/buffer_object.cpp 2015-02-10 18:28:23 +0000
@@ -0,0 +1,46 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Kevin DuBois <kevin.dubois@canonical.com>
17 */
18
19#include "buffer_object.h"
20#include <xf86drmMode.h>
21
22namespace mgm = mir::graphics::mesa;
23
24mgm::BufferObject::BufferObject(gbm_surface* surface, gbm_bo* bo, uint32_t drm_fb_id) :
25 surface{surface}, bo{bo}, drm_fb_id{drm_fb_id}
26{
27}
28
29mgm::BufferObject::~BufferObject()
30{
31 if (drm_fb_id)
32 {
33 int drm_fd = gbm_device_get_fd(gbm_bo_get_device(bo));
34 drmModeRmFB(drm_fd, drm_fb_id);
35 }
36}
37
38void mgm::BufferObject::release() const
39{
40 gbm_surface_release_buffer(surface, bo);
41}
42
43uint32_t mgm::BufferObject::get_drm_fb_id() const
44{
45 return drm_fb_id;
46}
047
=== added file 'src/platforms/mesa/server/buffer_object.h'
--- src/platforms/mesa/server/buffer_object.h 1970-01-01 00:00:00 +0000
+++ src/platforms/mesa/server/buffer_object.h 2015-02-10 18:28:23 +0000
@@ -0,0 +1,51 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Kevin DuBois <kevin.dubois@canonical.com>
17 */
18
19#ifndef MIR_GRAPHICS_MESA_BUFFER_OBJECT_H_
20#define MIR_GRAPHICS_MESA_BUFFER_OBJECT_H_
21
22#include <stddef.h>
23#include <gbm.h>
24
25namespace mir
26{
27namespace graphics
28{
29namespace mesa
30{
31class BufferObject
32{
33public:
34 BufferObject(gbm_surface* surface, gbm_bo* bo, uint32_t drm_fb_id);
35 ~BufferObject();
36
37 void release() const;
38 uint32_t get_drm_fb_id() const;
39private:
40 BufferObject(BufferObject const&) = delete;
41 BufferObject& operator=(BufferObject const&) = delete;
42
43 gbm_surface *surface;
44 gbm_bo *bo;
45 uint32_t drm_fb_id;
46};
47}
48}
49}
50
51#endif /* MIR_GRAPHICS_MESA_BUFFER_OBJECT_H_ */
052
=== modified file 'src/platforms/mesa/server/display.cpp'
--- src/platforms/mesa/server/display.cpp 2015-02-10 18:28:22 +0000
+++ src/platforms/mesa/server/display.cpp 2015-02-10 18:28:23 +0000
@@ -19,7 +19,7 @@
19#include "display.h"19#include "display.h"
20#include "cursor.h"20#include "cursor.h"
21#include "platform.h"21#include "platform.h"
22#include "display_buffer.h"22#include "display_group.h"
23#include "kms_display_configuration.h"23#include "kms_display_configuration.h"
24#include "kms_output.h"24#include "kms_output.h"
25#include "kms_page_flipper.h"25#include "kms_page_flipper.h"
@@ -118,8 +118,8 @@
118{118{
119 std::lock_guard<std::mutex> lg{configuration_mutex};119 std::lock_guard<std::mutex> lg{configuration_mutex};
120120
121 for (auto& db_ptr : display_buffers)121 for (auto& dg_ptr : display_groups)
122 f(*db_ptr);122 f(*dg_ptr);
123}123}
124124
125std::unique_ptr<mg::DisplayConfiguration> mgm::Display::configuration() const125std::unique_ptr<mg::DisplayConfiguration> mgm::Display::configuration() const
@@ -149,7 +149,7 @@
149 // before it's fully constructed, to force proper initialization.149 // before it's fully constructed, to force proper initialization.
150 bool const comp{(&conf != &current_display_configuration) &&150 bool const comp{(&conf != &current_display_configuration) &&
151 compatible(kms_conf, current_display_configuration)};151 compatible(kms_conf, current_display_configuration)};
152 std::vector<std::unique_ptr<DisplayBuffer>> display_buffers_new;152 std::vector<std::unique_ptr<DisplayGroup>> display_groups_new;
153153
154 if (!comp)154 if (!comp)
155 {155 {
@@ -161,8 +161,8 @@
161 * sure we wait for all pending page flips to finish before the161 * sure we wait for all pending page flips to finish before the
162 * display_buffers_new are created and take control of the outputs.162 * display_buffers_new are created and take control of the outputs.
163 */163 */
164 for (auto& db : display_buffers)164 for (auto& dg : display_groups)
165 db->wait_for_page_flip();165 dg->wait_for_page_flip();
166166
167 /* Reset the state of all outputs */167 /* Reset the state of all outputs */
168 kms_conf.for_each_output([&](DisplayConfigurationOutput const& conf_output)168 kms_conf.for_each_output([&](DisplayConfigurationOutput const& conf_output)
@@ -207,7 +207,7 @@
207207
208 if (comp)208 if (comp)
209 {209 {
210 display_buffers[group_idx++]->set_orientation(orientation, bounding_rect);210 display_groups[group_idx++]->set_orientation(orientation, bounding_rect);
211 }211 }
212 else212 else
213 {213 {
@@ -221,8 +221,8 @@
221221
222 auto surface = platform->gbm.create_scanout_surface(width, height);222 auto surface = platform->gbm.create_scanout_surface(width, height);
223223
224 std::unique_ptr<DisplayBuffer> db{224 std::unique_ptr<DisplayGroup> dg{
225 new DisplayBuffer{platform, listener,225 new DisplayGroup{platform, listener,
226 kms_outputs,226 kms_outputs,
227 std::move(surface),227 std::move(surface),
228 bounding_rect,228 bounding_rect,
@@ -230,12 +230,12 @@
230 *gl_config,230 *gl_config,
231 shared_egl.context()}};231 shared_egl.context()}};
232232
233 display_buffers_new.push_back(std::move(db));233 display_groups_new.push_back(std::move(dg));
234 }234 }
235 });235 });
236236
237 if (!comp)237 if (!comp)
238 display_buffers = std::move(display_buffers_new);238 display_groups = std::move(display_groups_new);
239239
240 /* Store applied configuration */240 /* Store applied configuration */
241 current_display_configuration = kms_conf;241 current_display_configuration = kms_conf;
@@ -307,7 +307,7 @@
307 * we need to reset the CRTCs. For active displays we schedule a CRTC reset307 * we need to reset the CRTCs. For active displays we schedule a CRTC reset
308 * on the next swap. For connected but unused outputs we clear the CRTC.308 * on the next swap. For connected but unused outputs we clear the CRTC.
309 */309 */
310 for (auto& db_ptr : display_buffers)310 for (auto& db_ptr : display_groups)
311 db_ptr->schedule_set_crtc();311 db_ptr->schedule_set_crtc();
312312
313 clear_connected_unused_outputs();313 clear_connected_unused_outputs();
314314
=== modified file 'src/platforms/mesa/server/display.h'
--- src/platforms/mesa/server/display.h 2015-02-10 18:28:22 +0000
+++ src/platforms/mesa/server/display.h 2015-02-10 18:28:23 +0000
@@ -23,6 +23,7 @@
23#include "real_kms_output_container.h"23#include "real_kms_output_container.h"
24#include "real_kms_display_configuration.h"24#include "real_kms_display_configuration.h"
25#include "display_helpers.h"25#include "display_helpers.h"
26#include "display_group.h"
2627
27#include <mutex>28#include <mutex>
2829
@@ -90,7 +91,7 @@
90 std::shared_ptr<DisplayReport> const listener;91 std::shared_ptr<DisplayReport> const listener;
91 mir::udev::Monitor monitor;92 mir::udev::Monitor monitor;
92 helpers::EGLHelper shared_egl;93 helpers::EGLHelper shared_egl;
93 std::vector<std::unique_ptr<DisplayBuffer>> display_buffers;94 std::vector<std::unique_ptr<DisplayGroup>> display_groups;
94 RealKMSOutputContainer output_container;95 RealKMSOutputContainer output_container;
95 mutable RealKMSDisplayConfiguration current_display_configuration;96 mutable RealKMSDisplayConfiguration current_display_configuration;
96 97
9798
=== modified file 'src/platforms/mesa/server/display_buffer.cpp'
--- src/platforms/mesa/server/display_buffer.cpp 2015-02-10 18:28:22 +0000
+++ src/platforms/mesa/server/display_buffer.cpp 2015-02-10 18:28:23 +0000
@@ -22,6 +22,7 @@
22#include "mir/graphics/display_report.h"22#include "mir/graphics/display_report.h"
23#include "bypass.h"23#include "bypass.h"
24#include "gbm_buffer.h"24#include "gbm_buffer.h"
25#include "buffer_object.h"
25#include "mir/fatal.h"26#include "mir/fatal.h"
2627
27#include <boost/throw_exception.hpp>28#include <boost/throw_exception.hpp>
@@ -32,39 +33,6 @@
32namespace mgm = mir::graphics::mesa;33namespace mgm = mir::graphics::mesa;
33namespace geom = mir::geometry;34namespace geom = mir::geometry;
3435
35class mgm::BufferObject
36{
37public:
38 BufferObject(gbm_surface* surface, gbm_bo* bo, uint32_t drm_fb_id)
39 : surface{surface}, bo{bo}, drm_fb_id{drm_fb_id}
40 {
41 }
42
43 ~BufferObject()
44 {
45 if (drm_fb_id)
46 {
47 int drm_fd = gbm_device_get_fd(gbm_bo_get_device(bo));
48 drmModeRmFB(drm_fd, drm_fb_id);
49 }
50 }
51
52 void release() const
53 {
54 gbm_surface_release_buffer(surface, bo);
55 }
56
57 uint32_t get_drm_fb_id() const
58 {
59 return drm_fb_id;
60 }
61
62private:
63 gbm_surface *surface;
64 gbm_bo *bo;
65 uint32_t drm_fb_id;
66};
67
68namespace36namespace
69{37{
7038
@@ -90,24 +58,17 @@
90mgm::DisplayBuffer::DisplayBuffer(58mgm::DisplayBuffer::DisplayBuffer(
91 std::shared_ptr<Platform> const& platform,59 std::shared_ptr<Platform> const& platform,
92 std::shared_ptr<DisplayReport> const& listener,60 std::shared_ptr<DisplayReport> const& listener,
93 std::vector<std::shared_ptr<KMSOutput>> const& outputs,
94 GBMSurfaceUPtr surface_gbm_param,61 GBMSurfaceUPtr surface_gbm_param,
95 geom::Rectangle const& area,62 geom::Rectangle const& area,
96 MirOrientation rot,63 MirOrientation rot,
97 GLConfig const& gl_config,64 GLConfig const& gl_config,
98 EGLContext shared_context)65 EGLContext shared_context) :
99 : last_flipped_bufobj{nullptr},66 platform_bypass_option{platform->bypass_option()},
100 scheduled_bufobj{nullptr},67 drm(*platform->drm),
101 platform(platform),68 surface_gbm{std::move(surface_gbm_param)},
102 listener(listener),69 egl{gl_config},
103 drm(*platform->drm),70 area(area),
104 outputs(outputs),71 rotation(rot)
105 surface_gbm{std::move(surface_gbm_param)},
106 egl{gl_config},
107 area(area),
108 rotation(rot),
109 needs_set_crtc{false},
110 page_flips_pending{false}
111{72{
112 uint32_t area_width = area.size.width.as_uint32_t();73 uint32_t area_width = area.size.width.as_uint32_t();
113 uint32_t area_height = area.size.height.as_uint32_t();74 uint32_t area_height = area.size.height.as_uint32_t();
@@ -139,16 +100,6 @@
139100
140 listener->report_successful_egl_buffer_swap_on_construction();101 listener->report_successful_egl_buffer_swap_on_construction();
141102
142 scheduled_bufobj = get_front_buffer_object();
143 if (!scheduled_bufobj)
144 fatal_error("Failed to get frontbuffer");
145
146 for (auto& output : outputs)
147 {
148 if (!output->set_crtc(scheduled_bufobj->get_drm_fb_id()))
149 fatal_error("Failed to set DRM crtc");
150 }
151
152 egl.release_current();103 egl.release_current();
153104
154 listener->report_successful_drm_mode_set_crtc_on_construction();105 listener->report_successful_drm_mode_set_crtc_on_construction();
@@ -160,19 +111,6 @@
160 });111 });
161}112}
162113
163mgm::DisplayBuffer::~DisplayBuffer()
164{
165 /*
166 * There is no need to destroy last_flipped_bufobj manually.
167 * It will be destroyed when its gbm_surface gets destroyed.
168 */
169 if (last_flipped_bufobj)
170 last_flipped_bufobj->release();
171
172 if (scheduled_bufobj)
173 scheduled_bufobj->release();
174}
175
176geom::Rectangle mgm::DisplayBuffer::view_area() const114geom::Rectangle mgm::DisplayBuffer::view_area() const
177{115{
178 return area;116 return area;
@@ -198,7 +136,7 @@
198bool mgm::DisplayBuffer::post_renderables_if_optimizable(RenderableList const& renderable_list)136bool mgm::DisplayBuffer::post_renderables_if_optimizable(RenderableList const& renderable_list)
199{137{
200 if ((rotation == mir_orientation_normal) &&138 if ((rotation == mir_orientation_normal) &&
201 (platform->bypass_option() == mgm::BypassOption::allowed))139 (platform_bypass_option == mgm::BypassOption::allowed))
202 {140 {
203 mgm::BypassMatch bypass_match(area);141 mgm::BypassMatch bypass_match(area);
204 auto bypass_it = std::find_if(renderable_list.rbegin(), renderable_list.rend(), bypass_match);142 auto bypass_it = std::find_if(renderable_list.rbegin(), renderable_list.rend(), bypass_match);
@@ -227,12 +165,6 @@
227 return false;165 return false;
228}166}
229167
230void mgm::DisplayBuffer::for_each_display_buffer(
231 std::function<void(graphics::DisplayBuffer&)> const& f)
232{
233 f(*this);
234}
235
236void mgm::DisplayBuffer::gl_swap_buffers()168void mgm::DisplayBuffer::gl_swap_buffers()
237{169{
238 if (!egl.swap_buffers())170 if (!egl.swap_buffers())
@@ -241,120 +173,6 @@
241 bypass_bufobj = nullptr;173 bypass_bufobj = nullptr;
242}174}
243175
244void mgm::DisplayBuffer::post()
245{
246 /*
247 * We might not have waited for the previous frame to page flip yet.
248 * This is good because it maximizes the time available to spend rendering
249 * each frame. Just remember wait_for_page_flip() must be called at some
250 * point before the next schedule_page_flip().
251 */
252 wait_for_page_flip();
253
254 /*
255 * Switching from bypass to compositing? Now is the earliest safe time
256 * we can unreference the bypass buffer...
257 */
258 if (scheduled_bufobj)
259 last_flipped_bypass_buf = nullptr;
260 /*
261 * Release the last flipped buffer object (which is not displayed anymore)
262 * to make it available for future rendering.
263 */
264 if (last_flipped_bufobj)
265 last_flipped_bufobj->release();
266
267 last_flipped_bufobj = scheduled_bufobj;
268 scheduled_bufobj = nullptr;
269
270 mgm::BufferObject *bufobj;
271 if (bypass_buf)
272 {
273 bufobj = bypass_bufobj;
274 }
275 else
276 {
277 bufobj = get_front_buffer_object();
278 if (!bufobj)
279 fatal_error("Failed to get front buffer object");
280 }
281
282 /*
283 * Schedule the current front buffer object for display, and wait
284 * for it to be actually displayed (flipped).
285 *
286 * If the flip fails, release the buffer object to make it available
287 * for future rendering.
288 */
289 if (!needs_set_crtc && !schedule_page_flip(bufobj))
290 {
291 if (!bypass_buf)
292 bufobj->release();
293 fatal_error("Failed to schedule page flip");
294 }
295 else if (needs_set_crtc)
296 {
297 for (auto& output : outputs)
298 {
299 if (!output->set_crtc(bufobj->get_drm_fb_id()))
300 fatal_error("Failed to set DRM crtc");
301 }
302 needs_set_crtc = false;
303 }
304
305 if (bypass_buf)
306 {
307 /*
308 * For composited frames we defer wait_for_page_flip till just before
309 * the next frame, but not for bypass frames. Deferring the flip of
310 * bypass frames would increase the time we held
311 * last_flipped_bypass_buf unacceptably, resulting in client stuttering
312 * unless we allocate more buffers (which I'm trying to avoid).
313 * Also, bypass does not need the deferred page flip because it has
314 * no compositing/rendering step for which to save time for.
315 */
316 wait_for_page_flip();
317 scheduled_bufobj = nullptr;
318
319 /*
320 * Keep a reference to the buffer being bypassed for the entire
321 * duration of the frame. This ensures the buffer doesn't get reused by
322 * the client while its on-screen, which would be seen as tearing or
323 * worse.
324 */
325 last_flipped_bypass_buf = bypass_buf;
326 }
327 else
328 {
329 /*
330 * Not in clone mode? We can afford to wait for the page flip then,
331 * making us double-buffered (noticeably less laggy than the triple
332 * buffering that clone mode requires).
333 */
334 if (outputs.size() == 1)
335 {
336 wait_for_page_flip();
337
338 /*
339 * bufobj is now physically on screen. Release the old frame...
340 */
341 if (last_flipped_bufobj)
342 {
343 last_flipped_bufobj->release();
344 last_flipped_bufobj = nullptr;
345 }
346
347 /*
348 * last_flipped_bufobj will be set correctly on the next iteration
349 * Don't do it here or else bufobj would be released while still
350 * on screen (hence tearing and artefacts).
351 */
352 }
353
354 scheduled_bufobj = bufobj;
355 }
356}
357
358mgm::BufferObject* mgm::DisplayBuffer::get_front_buffer_object()176mgm::BufferObject* mgm::DisplayBuffer::get_front_buffer_object()
359{177{
360 auto front = gbm_surface_lock_front_buffer(surface_gbm.get());178 auto front = gbm_surface_lock_front_buffer(surface_gbm.get());
@@ -408,33 +226,6 @@
408 return bufobj;226 return bufobj;
409}227}
410228
411
412bool mgm::DisplayBuffer::schedule_page_flip(BufferObject* bufobj)
413{
414 /*
415 * Schedule the current front buffer object for display. Note that
416 * the page flip is asynchronous and synchronized with vertical refresh.
417 */
418 for (auto& output : outputs)
419 {
420 if (output->schedule_page_flip(bufobj->get_drm_fb_id()))
421 page_flips_pending = true;
422 }
423
424 return page_flips_pending;
425}
426
427void mgm::DisplayBuffer::wait_for_page_flip()
428{
429 if (page_flips_pending)
430 {
431 for (auto& output : outputs)
432 output->wait_for_page_flip();
433
434 page_flips_pending = false;
435 }
436}
437
438void mgm::DisplayBuffer::make_current()229void mgm::DisplayBuffer::make_current()
439{230{
440 if (!egl.make_current())231 if (!egl.make_current())
@@ -448,8 +239,23 @@
448 egl.release_current();239 egl.release_current();
449}240}
450241
451void mgm::DisplayBuffer::schedule_set_crtc()242
452{243std::shared_ptr<mir::graphics::Buffer> mgm::DisplayBuffer::bypass_buffer()
453 needs_set_crtc = true;244{
454}245 return bypass_buf;
455246}
247
248mgm::BufferObject* mgm::DisplayBuffer::buffer_obj_to_be_posted()
249{
250 if (bypass_buf)
251 {
252 return bypass_bufobj;
253 }
254 else
255 {
256 auto bufobj = get_front_buffer_object();
257 if (!bufobj)
258 fatal_error("Failed to get front buffer object");
259 return bufobj;
260 }
261}
456262
=== modified file 'src/platforms/mesa/server/display_buffer.h'
--- src/platforms/mesa/server/display_buffer.h 2015-02-10 18:28:22 +0000
+++ src/platforms/mesa/server/display_buffer.h 2015-02-10 18:28:23 +0000
@@ -22,10 +22,10 @@
22#include "mir/graphics/display_buffer.h"22#include "mir/graphics/display_buffer.h"
23#include "mir/graphics/display.h"23#include "mir/graphics/display.h"
24#include "display_helpers.h"24#include "display_helpers.h"
25#include "platform.h"
2526
26#include <vector>27#include <vector>
27#include <memory>28#include <memory>
28#include <atomic>
2929
30namespace mir30namespace mir
31{31{
@@ -40,21 +40,17 @@
4040
41class Platform;41class Platform;
42class BufferObject;42class BufferObject;
43class KMSOutput;
4443
45class DisplayBuffer : public graphics::DisplayBuffer,44class DisplayBuffer : public graphics::DisplayBuffer
46 public graphics::DisplayGroup
47{45{
48public:46public:
49 DisplayBuffer(std::shared_ptr<Platform> const& platform,47 DisplayBuffer(std::shared_ptr<Platform> const& platform,
50 std::shared_ptr<DisplayReport> const& listener,48 std::shared_ptr<DisplayReport> const& listener,
51 std::vector<std::shared_ptr<KMSOutput>> const& outputs,
52 GBMSurfaceUPtr surface_gbm,49 GBMSurfaceUPtr surface_gbm,
53 geometry::Rectangle const& area,50 geometry::Rectangle const& area,
54 MirOrientation rot,51 MirOrientation rot,
55 GLConfig const& gl_config,52 GLConfig const& gl_config,
56 EGLContext shared_context);53 EGLContext shared_context);
57 ~DisplayBuffer();
5854
59 geometry::Rectangle view_area() const override;55 geometry::Rectangle view_area() const override;
60 void make_current() override;56 void make_current() override;
@@ -62,38 +58,29 @@
62 void gl_swap_buffers() override;58 void gl_swap_buffers() override;
63 bool post_renderables_if_optimizable(RenderableList const& renderlist) override;59 bool post_renderables_if_optimizable(RenderableList const& renderlist) override;
6460
65 void for_each_display_buffer(
66 std::function<void(graphics::DisplayBuffer&)> const& f) override;
67 void post() override;
68
69 MirOrientation orientation() const override;61 MirOrientation orientation() const override;
70 void set_orientation(MirOrientation const rot, geometry::Rectangle const& a);62 void set_orientation(MirOrientation const rot, geometry::Rectangle const& a);
71 bool uses_alpha() const override;63 bool uses_alpha() const override;
72 void schedule_set_crtc();64
73 void wait_for_page_flip();65 std::shared_ptr<graphics::Buffer> bypass_buffer();
66 BufferObject* buffer_obj_to_be_posted();
7467
75private:68private:
76 BufferObject* get_front_buffer_object();69 BufferObject* get_front_buffer_object();
77 BufferObject* get_buffer_object(struct gbm_bo *bo);70 BufferObject* get_buffer_object(struct gbm_bo *bo);
78 bool schedule_page_flip(BufferObject* bufobj);
7971
80 BufferObject* last_flipped_bufobj;
81 BufferObject* scheduled_bufobj;
82 std::shared_ptr<graphics::Buffer> last_flipped_bypass_buf;
83 std::shared_ptr<Buffer> bypass_buf{nullptr};72 std::shared_ptr<Buffer> bypass_buf{nullptr};
84 BufferObject* bypass_bufobj{nullptr};73 BufferObject* bypass_bufobj{nullptr};
85 std::shared_ptr<Platform> const platform;74
86 std::shared_ptr<DisplayReport> const listener;75 BypassOption platform_bypass_option;
76
87 /* DRM helper from mgm::Platform */77 /* DRM helper from mgm::Platform */
88 helpers::DRMHelper& drm;78 helpers::DRMHelper& drm;
89 std::vector<std::shared_ptr<KMSOutput>> outputs;
90 GBMSurfaceUPtr surface_gbm;79 GBMSurfaceUPtr surface_gbm;
91 helpers::EGLHelper egl;80 helpers::EGLHelper egl;
92 geometry::Rectangle area;81 geometry::Rectangle area;
93 uint32_t fb_width, fb_height;82 uint32_t fb_width, fb_height;
94 MirOrientation rotation;83 MirOrientation rotation;
95 std::atomic<bool> needs_set_crtc;
96 bool page_flips_pending;
97};84};
9885
99}86}
10087
=== added file 'src/platforms/mesa/server/display_group.cpp'
--- src/platforms/mesa/server/display_group.cpp 1970-01-01 00:00:00 +0000
+++ src/platforms/mesa/server/display_group.cpp 2015-02-10 18:28:23 +0000
@@ -0,0 +1,219 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Kevin DuBois <kevin.dubois@canonical.com>
17 */
18
19#include "display_group.h"
20#include "buffer_object.h"
21
22#include "platform.h"
23#include "kms_output.h"
24#include "mir/graphics/display_report.h"
25#include "bypass.h"
26#include "gbm_buffer.h"
27#include "mir/fatal.h"
28#include <boost/throw_exception.hpp>
29#include <GLES2/gl2.h>
30
31#include <stdexcept>
32
33namespace mgm = mir::graphics::mesa;
34namespace geom = mir::geometry;
35
36mgm::DisplayGroup::DisplayGroup(
37 std::shared_ptr<Platform> const& platform,
38 std::shared_ptr<DisplayReport> const& listener,
39 std::vector<std::shared_ptr<KMSOutput>> const& outputs,
40 GBMSurfaceUPtr surface_gbm,
41 geometry::Rectangle const& area,
42 MirOrientation rot,
43 GLConfig const& gl_config,
44 EGLContext shared_context) :
45 db{platform, listener, std::move(surface_gbm), area, rot, gl_config, shared_context},
46 platform(platform),
47 listener(listener),
48 needs_set_crtc{false},
49 page_flips_pending{false},
50 outputs(outputs),
51 last_flipped_bufobj{nullptr},
52 scheduled_bufobj{db.buffer_obj_to_be_posted()},
53 last_flipped_bypass_buf{nullptr}
54{
55 for (auto& output : outputs)
56 {
57 if (!output->set_crtc(scheduled_bufobj->get_drm_fb_id()))
58 fatal_error("Failed to set DRM crtc");
59 }
60}
61
62mgm::DisplayGroup::~DisplayGroup()
63{
64 /*
65 * There is no need to destroy last_flipped_bufobj manually.
66 * It will be destroyed when its gbm_surface gets destroyed.
67 */
68 if (last_flipped_bufobj)
69 last_flipped_bufobj->release();
70
71 if (scheduled_bufobj)
72 scheduled_bufobj->release();
73}
74
75void mgm::DisplayGroup::for_each_display_buffer(
76 std::function<void(graphics::DisplayBuffer&)> const& f)
77{
78 f(db);
79}
80
81void mgm::DisplayGroup::post()
82{
83 /*
84 * We might not have waited for the previous frame to page flip yet.
85 * This is good because it maximizes the time available to spend rendering
86 * each frame. Just remember wait_for_page_flip() must be called at some
87 * point before the next schedule_page_flip().
88 */
89 wait_for_page_flip();
90
91 /*
92 * Switching from bypass to compositing? Now is the earliest safe time
93 * we can unreference the bypass buffer...
94 */
95 if (scheduled_bufobj)
96 last_flipped_bypass_buf = nullptr;
97 /*
98 * Release the last flipped buffer object (which is not displayed anymore)
99 * to make it available for future rendering.
100 */
101 if (last_flipped_bufobj)
102 last_flipped_bufobj->release();
103
104 last_flipped_bufobj = scheduled_bufobj;
105 scheduled_bufobj = nullptr;
106
107 mgm::BufferObject *bufobj = db.buffer_obj_to_be_posted();
108 auto bypass_buffer = db.bypass_buffer();
109 /*
110 * Schedule the current front buffer object for display, and wait
111 * for it to be actually displayed (flipped).
112 *
113 * If the flip fails, release the buffer object to make it available
114 * for future rendering.
115 */
116 if (!needs_set_crtc && !schedule_page_flip(bufobj))
117 {
118 if (!bypass_buffer)
119 bufobj->release();
120 fatal_error("Failed to schedule page flip");
121 }
122 else if (needs_set_crtc)
123 {
124 for (auto& output : outputs)
125 {
126 if (!output->set_crtc(bufobj->get_drm_fb_id()))
127 fatal_error("Failed to set DRM crtc");
128 }
129 needs_set_crtc = false;
130 }
131
132 if (bypass_buffer)
133 {
134 /*
135 * For composited frames we defer wait_for_page_flip till just before
136 * the next frame, but not for bypass frames. Deferring the flip of
137 * bypass frames would increase the time we held
138 * last_flipped_bypass_buf unacceptably, resulting in client stuttering
139 * unless we allocate more buffers (which I'm trying to avoid).
140 * Also, bypass does not need the deferred page flip because it has
141 * no compositing/rendering step for which to save time for.
142 */
143 wait_for_page_flip();
144 scheduled_bufobj = nullptr;
145
146 /*
147 * Keep a reference to the buffer being bypassed for the entire
148 * duration of the frame. This ensures the buffer doesn't get reused by
149 * the client while its on-screen, which would be seen as tearing or
150 * worse.
151 */
152 last_flipped_bypass_buf = db.bypass_buffer();
153 }
154 else
155 {
156 /*
157 * Not in clone mode? We can afford to wait for the page flip then,
158 * making us double-buffered (noticeably less laggy than the triple
159 * buffering that clone mode requires).
160 */
161 if (outputs.size() == 1)
162 {
163 wait_for_page_flip();
164
165 /*
166 * bufobj is now physically on screen. Release the old frame...
167 */
168 if (last_flipped_bufobj)
169 {
170 last_flipped_bufobj->release();
171 last_flipped_bufobj = nullptr;
172 }
173
174 /*
175 * last_flipped_bufobj will be set correctly on the next iteration
176 * Don't do it here or else bufobj would be released while still
177 * on screen (hence tearing and artefacts).
178 */
179 }
180
181 scheduled_bufobj = bufobj;
182 }
183}
184
185void mgm::DisplayGroup::schedule_set_crtc()
186{
187 needs_set_crtc = true;
188}
189
190bool mgm::DisplayGroup::schedule_page_flip(BufferObject* bufobj)
191{
192 /*
193 * Schedule the current front buffer object for display. Note that
194 * the page flip is asynchronous and synchronized with vertical refresh.
195 */
196 for (auto& output : outputs)
197 {
198 if (output->schedule_page_flip(bufobj->get_drm_fb_id()))
199 page_flips_pending = true;
200 }
201
202 return page_flips_pending;
203}
204
205void mgm::DisplayGroup::wait_for_page_flip()
206{
207 if (page_flips_pending)
208 {
209 for (auto& output : outputs)
210 output->wait_for_page_flip();
211
212 page_flips_pending = false;
213 }
214}
215
216void mgm::DisplayGroup::set_orientation(MirOrientation const rot, geometry::Rectangle const& a)
217{
218 db.set_orientation(rot, a);
219}
0220
=== added file 'src/platforms/mesa/server/display_group.h'
--- src/platforms/mesa/server/display_group.h 1970-01-01 00:00:00 +0000
+++ src/platforms/mesa/server/display_group.h 2015-02-10 18:28:23 +0000
@@ -0,0 +1,86 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Kevin DuBois <kevin.dubois@canonical.com>
17 */
18
19#ifndef MIR_GRAPHICS_MESA_DISPLAY_GROUP_H_
20#define MIR_GRAPHICS_MESA_DISPLAY_GROUP_H_
21
22#include "mir/graphics/display.h"
23#include "display_buffer.h"
24
25#include <vector>
26#include <memory>
27#include <atomic>
28
29namespace mir
30{
31namespace graphics
32{
33
34class DisplayReport;
35class GLConfig;
36
37namespace mesa
38{
39
40class Platform;
41class BufferObject;
42class KMSOutput;
43
44class DisplayGroup : public graphics::DisplayGroup
45{
46public:
47 DisplayGroup(
48 std::shared_ptr<Platform> const& platform,
49 std::shared_ptr<DisplayReport> const& listener,
50 std::vector<std::shared_ptr<KMSOutput>> const& outputs,
51 GBMSurfaceUPtr surface_gbm,
52 geometry::Rectangle const& area,
53 MirOrientation rot,
54 GLConfig const& gl_config,
55 EGLContext shared_context);
56 ~DisplayGroup();
57
58 void for_each_display_buffer(
59 std::function<void(graphics::DisplayBuffer&)> const& f) override;
60 void post() override;
61
62 void schedule_set_crtc();
63 void wait_for_page_flip();
64 void set_orientation(MirOrientation const rot, geometry::Rectangle const& a);
65
66private:
67 bool schedule_page_flip(BufferObject* bufobj);
68
69 DisplayBuffer db;
70 std::shared_ptr<Platform> const platform;
71 std::shared_ptr<DisplayReport> const listener;
72
73 std::atomic<bool> needs_set_crtc;
74 bool page_flips_pending;
75 std::vector<std::shared_ptr<KMSOutput>> outputs;
76
77 BufferObject* last_flipped_bufobj;
78 BufferObject* scheduled_bufobj;
79 std::shared_ptr<graphics::Buffer> last_flipped_bypass_buf;
80};
81
82}
83}
84}
85
86#endif /* MIR_GRAPHICS_MESA_DISPLAY_GROUP_H_ */
087
=== modified file 'tests/unit-tests/graphics/mesa/CMakeLists.txt'
--- tests/unit-tests/graphics/mesa/CMakeLists.txt 2015-01-21 07:34:50 +0000
+++ tests/unit-tests/graphics/mesa/CMakeLists.txt 2015-02-10 18:28:23 +0000
@@ -17,6 +17,7 @@
17 ${CMAKE_CURRENT_SOURCE_DIR}/test_bypass.cpp17 ${CMAKE_CURRENT_SOURCE_DIR}/test_bypass.cpp
18 ${CMAKE_CURRENT_SOURCE_DIR}/test_ipc_operations.cpp18 ${CMAKE_CURRENT_SOURCE_DIR}/test_ipc_operations.cpp
19 ${CMAKE_CURRENT_SOURCE_DIR}/test_nested_authentication.cpp19 ${CMAKE_CURRENT_SOURCE_DIR}/test_nested_authentication.cpp
20 ${CMAKE_CURRENT_SOURCE_DIR}/test_display_group.cpp
20 $<TARGET_OBJECTS:mirplatformgraphicsmesaobjects>21 $<TARGET_OBJECTS:mirplatformgraphicsmesaobjects>
21)22)
2223
2324
=== modified file 'tests/unit-tests/graphics/mesa/test_display_buffer.cpp'
--- tests/unit-tests/graphics/mesa/test_display_buffer.cpp 2015-02-10 18:28:22 +0000
+++ tests/unit-tests/graphics/mesa/test_display_buffer.cpp 2015-02-10 18:28:23 +0000
@@ -28,7 +28,6 @@
28#include "mir_test_doubles/stub_gbm_native_buffer.h"28#include "mir_test_doubles/stub_gbm_native_buffer.h"
29#include "mir_test_framework/udev_environment.h"29#include "mir_test_framework/udev_environment.h"
30#include "mir_test_doubles/fake_renderable.h"30#include "mir_test_doubles/fake_renderable.h"
31#include "mock_kms_output.h"
3231
33#include <gtest/gtest.h>32#include <gtest/gtest.h>
34#include <gmock/gmock.h>33#include <gmock/gmock.h>
@@ -73,12 +72,6 @@
7372
74 fake_devices.add_standard_device("standard-drm-devices");73 fake_devices.add_standard_device("standard-drm-devices");
7574
76 mock_kms_output = std::make_shared<NiceMock<MockKMSOutput>>();
77 ON_CALL(*mock_kms_output, set_crtc(_))
78 .WillByDefault(Return(true));
79 ON_CALL(*mock_kms_output, schedule_page_flip(_))
80 .WillByDefault(Return(true));
81
82 ON_CALL(*mock_bypassable_buffer, size())75 ON_CALL(*mock_bypassable_buffer, size())
83 .WillByDefault(Return(display_area.size));76 .WillByDefault(Return(display_area.size));
84 ON_CALL(*mock_bypassable_buffer, can_bypass())77 ON_CALL(*mock_bypassable_buffer, can_bypass())
@@ -109,7 +102,6 @@
109 gbm_bo* fake_bo;102 gbm_bo* fake_bo;
110 gbm_bo_handle fake_handle;103 gbm_bo_handle fake_handle;
111 UdevEnvironment fake_devices;104 UdevEnvironment fake_devices;
112 std::shared_ptr<MockKMSOutput> mock_kms_output;
113 StubGLConfig gl_config;105 StubGLConfig gl_config;
114 mir::graphics::RenderableList const bypassable_list;106 mir::graphics::RenderableList const bypassable_list;
115107
@@ -120,7 +112,6 @@
120 graphics::mesa::DisplayBuffer db(112 graphics::mesa::DisplayBuffer db(
121 create_platform(),113 create_platform(),
122 null_display_report(),114 null_display_report(),
123 {},
124 nullptr,115 nullptr,
125 display_area,116 display_area,
126 mir_orientation_normal,117 mir_orientation_normal,
@@ -135,7 +126,6 @@
135 graphics::mesa::DisplayBuffer db(126 graphics::mesa::DisplayBuffer db(
136 create_platform(),127 create_platform(),
137 null_display_report(),128 null_display_report(),
138 {mock_kms_output},
139 nullptr,129 nullptr,
140 display_area,130 display_area,
141 mir_orientation_normal,131 mir_orientation_normal,
@@ -148,20 +138,20 @@
148TEST_F(MesaDisplayBufferTest, failed_bypass_falls_back_gracefully)138TEST_F(MesaDisplayBufferTest, failed_bypass_falls_back_gracefully)
149{ // Regression test for LP: #1398296139{ // Regression test for LP: #1398296
150 EXPECT_CALL(mock_drm, drmModeAddFB2(_, _, _, _, _, _, _, _, _))140 EXPECT_CALL(mock_drm, drmModeAddFB2(_, _, _, _, _, _, _, _, _))
151 .WillOnce(Return(0)) // During the DisplayBuffer constructor141 .WillOnce(Return(0))
152 .WillOnce(Return(-22)) // Fail first bypass attempt142 .WillOnce(Return(-22))
153 .WillOnce(Return(0)); // Succeed second bypass attempt143 .WillOnce(Return(0));
154144
155 graphics::mesa::DisplayBuffer db(145 graphics::mesa::DisplayBuffer db(
156 create_platform(),146 create_platform(),
157 null_display_report(),147 null_display_report(),
158 {mock_kms_output},
159 nullptr,148 nullptr,
160 display_area,149 display_area,
161 mir_orientation_normal,150 mir_orientation_normal,
162 gl_config,151 gl_config,
163 mock_egl.fake_egl_context);152 mock_egl.fake_egl_context);
164153
154 EXPECT_TRUE(db.post_renderables_if_optimizable(bypassable_list));
165 EXPECT_FALSE(db.post_renderables_if_optimizable(bypassable_list));155 EXPECT_FALSE(db.post_renderables_if_optimizable(bypassable_list));
166 // And then we recover. DRM finds enough resources to AddFB ...156 // And then we recover. DRM finds enough resources to AddFB ...
167 EXPECT_TRUE(db.post_renderables_if_optimizable(bypassable_list));157 EXPECT_TRUE(db.post_renderables_if_optimizable(bypassable_list));
@@ -184,7 +174,6 @@
184 graphics::mesa::DisplayBuffer db(174 graphics::mesa::DisplayBuffer db(
185 create_platform(),175 create_platform(),
186 null_display_report(),176 null_display_report(),
187 {mock_kms_output},
188 nullptr,177 nullptr,
189 display_area,178 display_area,
190 mir_orientation_normal,179 mir_orientation_normal,
@@ -199,7 +188,6 @@
199 graphics::mesa::DisplayBuffer db(188 graphics::mesa::DisplayBuffer db(
200 create_platform(),189 create_platform(),
201 null_display_report(),190 null_display_report(),
202 {},
203 nullptr,191 nullptr,
204 display_area,192 display_area,
205 mir_orientation_right,193 mir_orientation_right,
@@ -214,7 +202,6 @@
214 graphics::mesa::DisplayBuffer db(202 graphics::mesa::DisplayBuffer db(
215 create_platform(),203 create_platform(),
216 null_display_report(),204 null_display_report(),
217 {},
218 nullptr,205 nullptr,
219 display_area,206 display_area,
220 mir_orientation_left,207 mir_orientation_left,
@@ -224,153 +211,6 @@
224 EXPECT_EQ(mir_orientation_left, db.orientation());211 EXPECT_EQ(mir_orientation_left, db.orientation());
225}212}
226213
227TEST_F(MesaDisplayBufferTest, normal_rotation_constructs_normal_fb)
228{
229 EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_))
230 .WillOnce(Return((void*)0));
231 EXPECT_CALL(mock_drm, drmModeAddFB2(_, width, height, _, _, _, _, _, _))
232 .Times(1);
233
234 graphics::mesa::DisplayBuffer db(
235 create_platform(),
236 null_display_report(),
237 {},
238 nullptr,
239 display_area,
240 mir_orientation_normal,
241 gl_config,
242 mock_egl.fake_egl_context);
243}
244
245TEST_F(MesaDisplayBufferTest, left_rotation_constructs_transposed_fb)
246{
247 EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_))
248 .WillOnce(Return((void*)0));
249 EXPECT_CALL(mock_drm, drmModeAddFB2(_, height, width, _, _, _, _, _, _))
250 .Times(1);
251
252 graphics::mesa::DisplayBuffer db(
253 create_platform(),
254 null_display_report(),
255 {},
256 nullptr,
257 display_area,
258 mir_orientation_left,
259 gl_config,
260 mock_egl.fake_egl_context);
261}
262
263TEST_F(MesaDisplayBufferTest, inverted_rotation_constructs_normal_fb)
264{
265 EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_))
266 .WillOnce(Return((void*)0));
267 EXPECT_CALL(mock_drm, drmModeAddFB2(_, width, height, _, _, _, _, _, _))
268 .Times(1);
269
270 graphics::mesa::DisplayBuffer db(
271 create_platform(),
272 null_display_report(),
273 {},
274 nullptr,
275 display_area,
276 mir_orientation_inverted,
277 gl_config,
278 mock_egl.fake_egl_context);
279}
280
281TEST_F(MesaDisplayBufferTest, right_rotation_constructs_transposed_fb)
282{
283 EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_))
284 .WillOnce(Return((void*)0));
285 EXPECT_CALL(mock_drm, drmModeAddFB2(_, height, width, _, _, _, _, _, _))
286 .Times(1);
287
288 graphics::mesa::DisplayBuffer db(
289 create_platform(),
290 null_display_report(),
291 {},
292 nullptr,
293 display_area,
294 mir_orientation_right,
295 gl_config,
296 mock_egl.fake_egl_context);
297}
298
299TEST_F(MesaDisplayBufferTest, clone_mode_first_flip_flips_but_no_wait)
300{
301 // Ensure clone mode can do multiple page flips in parallel without
302 // blocking on either (at least till the second post)
303 EXPECT_CALL(*mock_kms_output, schedule_page_flip(_))
304 .Times(2);
305 EXPECT_CALL(*mock_kms_output, wait_for_page_flip())
306 .Times(0);
307
308 graphics::mesa::DisplayBuffer db(
309 create_platform(),
310 null_display_report(),
311 {mock_kms_output, mock_kms_output},
312 nullptr,
313 display_area,
314 mir_orientation_normal,
315 gl_config,
316 mock_egl.fake_egl_context);
317
318 db.gl_swap_buffers();
319 db.post();
320}
321
322TEST_F(MesaDisplayBufferTest, single_mode_first_post_flips_with_wait)
323{
324 EXPECT_CALL(*mock_kms_output, schedule_page_flip(_))
325 .Times(1);
326 EXPECT_CALL(*mock_kms_output, wait_for_page_flip())
327 .Times(1);
328
329 graphics::mesa::DisplayBuffer db(
330 create_platform(),
331 null_display_report(),
332 {mock_kms_output},
333 nullptr,
334 display_area,
335 mir_orientation_normal,
336 gl_config,
337 mock_egl.fake_egl_context);
338
339 db.gl_swap_buffers();
340 db.post();
341}
342
343TEST_F(MesaDisplayBufferTest, clone_mode_waits_for_page_flip_on_second_flip)
344{
345 InSequence seq;
346
347 EXPECT_CALL(*mock_kms_output, wait_for_page_flip())
348 .Times(0);
349 EXPECT_CALL(*mock_kms_output, schedule_page_flip(_))
350 .Times(2);
351 EXPECT_CALL(*mock_kms_output, wait_for_page_flip())
352 .Times(2);
353 EXPECT_CALL(*mock_kms_output, schedule_page_flip(_))
354 .Times(2);
355 EXPECT_CALL(*mock_kms_output, wait_for_page_flip())
356 .Times(0);
357
358 graphics::mesa::DisplayBuffer db(
359 create_platform(),
360 null_display_report(),
361 {mock_kms_output, mock_kms_output},
362 nullptr,
363 display_area,
364 mir_orientation_normal,
365 gl_config,
366 mock_egl.fake_egl_context);
367
368 db.gl_swap_buffers();
369 db.post();
370
371 db.gl_swap_buffers();
372 db.post();
373}
374214
375TEST_F(MesaDisplayBufferTest, skips_bypass_because_of_incompatible_list)215TEST_F(MesaDisplayBufferTest, skips_bypass_because_of_incompatible_list)
376{216{
@@ -382,7 +222,6 @@
382 graphics::mesa::DisplayBuffer db(222 graphics::mesa::DisplayBuffer db(
383 create_platform(),223 create_platform(),
384 null_display_report(),224 null_display_report(),
385 {mock_kms_output},
386 nullptr,225 nullptr,
387 display_area,226 display_area,
388 mir_orientation_normal,227 mir_orientation_normal,
@@ -406,7 +245,6 @@
406 graphics::mesa::DisplayBuffer db(245 graphics::mesa::DisplayBuffer db(
407 create_platform(),246 create_platform(),
408 null_display_report(),247 null_display_report(),
409 {mock_kms_output},
410 nullptr,248 nullptr,
411 display_area,249 display_area,
412 mir_orientation_normal,250 mir_orientation_normal,
@@ -423,7 +261,6 @@
423 graphics::mesa::DisplayBuffer db(261 graphics::mesa::DisplayBuffer db(
424 create_platform(),262 create_platform(),
425 null_display_report(),263 null_display_report(),
426 {mock_kms_output},
427 nullptr,264 nullptr,
428 area,265 area,
429 mir_orientation_normal,266 mir_orientation_normal,
430267
=== added file 'tests/unit-tests/graphics/mesa/test_display_group.cpp'
--- tests/unit-tests/graphics/mesa/test_display_group.cpp 1970-01-01 00:00:00 +0000
+++ tests/unit-tests/graphics/mesa/test_display_group.cpp 2015-02-10 18:28:23 +0000
@@ -0,0 +1,247 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Kevin DuBois <kevin.dubois@canonical.com>
17 */
18
19#include "src/platforms/mesa/server/platform.h"
20#include "src/platforms/mesa/server/display_group.h"
21#include "src/server/report/null_report_factory.h"
22#include "mir_test_doubles/mock_egl.h"
23#include "mir_test_doubles/mock_gl.h"
24#include "mir_test_doubles/mock_drm.h"
25#include "mir_test_doubles/mock_buffer.h"
26#include "mir_test_doubles/mock_gbm.h"
27#include "mir_test_doubles/stub_gl_config.h"
28#include "mir_test_doubles/platform_factory.h"
29#include "mir_test_doubles/stub_gbm_native_buffer.h"
30#include "mir_test_framework/udev_environment.h"
31#include "mir_test_doubles/fake_renderable.h"
32#include "mock_kms_output.h"
33
34#include <gtest/gtest.h>
35#include <gmock/gmock.h>
36#include <gbm.h>
37
38using namespace testing;
39using namespace mir;
40using namespace std;
41using namespace mir::test;
42using namespace mir::test::doubles;
43using namespace mir_test_framework;
44using namespace mir::graphics;
45using mir::report::null_display_report;
46
47class MesaDisplayGroup : public Test
48{
49public:
50 MesaDisplayGroup()
51 : mock_bypassable_buffer{std::make_shared<NiceMock<MockBuffer>>()}
52 , stub_gbm_native_buffer{
53 std::make_shared<StubGBMNativeBuffer>(display_area.size)}
54 {
55 ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_))
56 .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]),
57 SetArgPointee<4>(1),
58 Return(EGL_TRUE)));
59
60 mock_egl.provide_egl_extensions();
61 mock_gl.provide_gles_extensions();
62
63 fake_bo = reinterpret_cast<gbm_bo*>(123);
64 ON_CALL(mock_gbm, gbm_surface_lock_front_buffer(_))
65 .WillByDefault(Return(fake_bo));
66 fake_handle.u32 = 123;
67 ON_CALL(mock_gbm, gbm_bo_get_handle(_))
68 .WillByDefault(Return(fake_handle));
69 ON_CALL(mock_gbm, gbm_bo_get_stride(_))
70 .WillByDefault(Return(456));
71
72 fake_devices.add_standard_device("standard-drm-devices");
73
74 mock_kms_output = std::make_shared<NiceMock<MockKMSOutput>>();
75 ON_CALL(*mock_kms_output, set_crtc(_))
76 .WillByDefault(Return(true));
77 ON_CALL(*mock_kms_output, schedule_page_flip(_))
78 .WillByDefault(Return(true));
79 }
80
81 // The platform has an implicit dependency on mock_gbm etc so must be
82 // reconstructed locally to ensure its lifetime is shorter than mock_gbm.
83 shared_ptr<graphics::mesa::Platform> create_platform()
84 {
85 return mir::test::doubles::create_mesa_platform_with_null_dependencies();
86 }
87
88protected:
89 int const width{56};
90 int const height{78};
91 mir::geometry::Rectangle const display_area{{12,34}, {width,height}};
92 NiceMock<MockGBM> mock_gbm;
93 NiceMock<MockEGL> mock_egl;
94 NiceMock<MockGL> mock_gl;
95 NiceMock<MockDRM> mock_drm;
96 std::shared_ptr<MockBuffer> mock_bypassable_buffer;
97 std::shared_ptr<mesa::GBMNativeBuffer> stub_gbm_native_buffer;
98 gbm_bo* fake_bo;
99 gbm_bo_handle fake_handle;
100 UdevEnvironment fake_devices;
101 std::shared_ptr<MockKMSOutput> mock_kms_output;
102 StubGLConfig gl_config;
103
104};
105
106TEST_F(MesaDisplayGroup, clone_mode_first_flip_flips_but_no_wait)
107{
108 // Ensure clone mode can do multiple page flips in parallel without
109 // blocking on either (at least till the second post)
110 EXPECT_CALL(*mock_kms_output, schedule_page_flip(_))
111 .Times(2);
112 EXPECT_CALL(*mock_kms_output, wait_for_page_flip())
113 .Times(0);
114
115 graphics::mesa::DisplayGroup dg(
116 create_platform(),
117 null_display_report(),
118 {mock_kms_output, mock_kms_output},
119 nullptr,
120 display_area,
121 mir_orientation_normal,
122 gl_config,
123 mock_egl.fake_egl_context);
124
125 dg.post();
126}
127
128TEST_F(MesaDisplayGroup, single_mode_first_post_flips_with_wait)
129{
130 EXPECT_CALL(*mock_kms_output, schedule_page_flip(_))
131 .Times(1);
132 EXPECT_CALL(*mock_kms_output, wait_for_page_flip())
133 .Times(1);
134
135 graphics::mesa::DisplayGroup dg(
136 create_platform(),
137 null_display_report(),
138 {mock_kms_output},
139 nullptr,
140 display_area,
141 mir_orientation_normal,
142 gl_config,
143 mock_egl.fake_egl_context);
144
145 dg.post();
146}
147
148TEST_F(MesaDisplayGroup, clone_mode_waits_for_page_flip_on_second_flip)
149{
150 InSequence seq;
151
152 EXPECT_CALL(*mock_kms_output, wait_for_page_flip())
153 .Times(0);
154 EXPECT_CALL(*mock_kms_output, schedule_page_flip(_))
155 .Times(2);
156 EXPECT_CALL(*mock_kms_output, wait_for_page_flip())
157 .Times(2);
158 EXPECT_CALL(*mock_kms_output, schedule_page_flip(_))
159 .Times(2);
160 EXPECT_CALL(*mock_kms_output, wait_for_page_flip())
161 .Times(0);
162
163 graphics::mesa::DisplayGroup dg(
164 create_platform(),
165 null_display_report(),
166 {mock_kms_output, mock_kms_output},
167 nullptr,
168 display_area,
169 mir_orientation_normal,
170 gl_config,
171 mock_egl.fake_egl_context);
172
173 dg.post();
174 dg.post();
175}
176
177TEST_F(MesaDisplayGroup, normal_rotation_constructs_normal_fb)
178{
179 EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_))
180 .WillOnce(Return((void*)0));
181 EXPECT_CALL(mock_drm, drmModeAddFB2(_, width, height, _, _, _, _, _, _))
182 .Times(1);
183
184 graphics::mesa::DisplayGroup dg(
185 create_platform(),
186 null_display_report(),
187 {},
188 nullptr,
189 display_area,
190 mir_orientation_normal,
191 gl_config,
192 mock_egl.fake_egl_context);
193}
194
195TEST_F(MesaDisplayGroup, left_rotation_constructs_transposed_fb)
196{
197 EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_))
198 .WillOnce(Return((void*)0));
199 EXPECT_CALL(mock_drm, drmModeAddFB2(_, height, width, _, _, _, _, _, _))
200 .Times(1);
201
202 graphics::mesa::DisplayGroup dg(
203 create_platform(),
204 null_display_report(),
205 {},
206 nullptr,
207 display_area,
208 mir_orientation_left,
209 gl_config,
210 mock_egl.fake_egl_context);
211}
212
213TEST_F(MesaDisplayGroup, inverted_rotation_constructs_normal_fb)
214{
215 EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_))
216 .WillOnce(Return((void*)0));
217 EXPECT_CALL(mock_drm, drmModeAddFB2(_, width, height, _, _, _, _, _, _))
218 .Times(1);
219
220 graphics::mesa::DisplayGroup dg(
221 create_platform(),
222 null_display_report(),
223 {},
224 nullptr,
225 display_area,
226 mir_orientation_inverted,
227 gl_config,
228 mock_egl.fake_egl_context);
229}
230
231TEST_F(MesaDisplayGroup, right_rotation_constructs_transposed_fb)
232{
233 EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_))
234 .WillOnce(Return((void*)0));
235 EXPECT_CALL(mock_drm, drmModeAddFB2(_, height, width, _, _, _, _, _, _))
236 .Times(1);
237
238 graphics::mesa::DisplayGroup dg(
239 create_platform(),
240 null_display_report(),
241 {},
242 nullptr,
243 display_area,
244 mir_orientation_right,
245 gl_config,
246 mock_egl.fake_egl_context);
247}

Subscribers

People subscribed via source and target branches