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
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 != &current_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+}

Subscribers

People subscribed via source and target branches