Mir

Merge lp:~vanvugt/mir/async-page-flips into lp:mir

Proposed by Daniel van Vugt
Status: Merged
Approved by: Daniel van Vugt
Approved revision: no longer in the source branch.
Merged at revision: 1380
Proposed branch: lp:~vanvugt/mir/async-page-flips
Merge into: lp:mir
Diff against target: 496 lines (+218/-60)
8 files modified
src/platform/graphics/mesa/display.cpp (+11/-0)
src/platform/graphics/mesa/display_buffer.cpp (+72/-31)
src/platform/graphics/mesa/display_buffer.h (+4/-1)
tests/unit-tests/graphics/mesa/mock_kms_output.h (+52/-0)
tests/unit-tests/graphics/mesa/test_cursor.cpp (+2/-19)
tests/unit-tests/graphics/mesa/test_display.cpp (+5/-7)
tests/unit-tests/graphics/mesa/test_display_buffer.cpp (+62/-0)
tests/unit-tests/graphics/mesa/test_display_multi_monitor.cpp (+10/-2)
To merge this branch: bzr merge lp:~vanvugt/mir/async-page-flips
Reviewer Review Type Date Requested Status
Alexandros Frantzis (community) Approve
Alan Griffiths Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+203902@code.launchpad.net

Commit message

Parallelize page flipping with rendering (of the next frame).

Blocking rendering of the next frame until your page flip has finished is
pretty harmless on a single-headed system. However, in the case of cloned
outputs, waiting for multiple flips can take up to the whole frame time
(as monitors could easily be 16ms out of phase). This left little or no time
for rendering, resulting in dropped frames (stuttering) with cloned outputs
(LP: #1213801).

The solution is to wait for the page flips in parallel to rendering the next
frame. So it doesn't matter if your cloned outputs are many milliseconds
out of phase. That will no longer take away from your rendering time.

To post a comment you must log in.
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

> The solution is to wait for the page flips in parallel to rendering the next frame

Unless I am missing something, this is not safe. After we schedule a page flip we can't start rendering to the buffer that is currently visible before the page flip is complete. It's important to note that when we flip a buffer, the buffer itself becomes the display framebuffer (i.e. where the display hardware reads from for the whole duration of the frame), its contents are not copied to some other internal framebuffer. Here is a sample run with the proposed changes:

(Front,Back): which buffer is front/back from gbm's point of view

== Initial setup ==

make_current: (A,B) current rendering target is B
draw/clear: (A,B) clearing B
swap: (B,A)
set_crtc: (B,A) B is immediately visible on screen

== First iter ==

make_current: (B,A) current rendering target is A
draw: (B,A) drawing to A
wait_for_page_flip: (B,A) nothing to wait for
swap: (A,B)
schedule_page_flip: (A,B) scheduling for A to become visible on screen

== Second iter ==

make_current: (A,B) current rendering target is B
*** draw: (A,B) drawing to B => there is no guarantee that the page flip has happened, so B may still be visible on screen!

Disapproving, although I do hope I am missing something and thus proved wrong.

review: Disapprove
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Alexandros,

You are mistaken and I am indeed aware of the perils of touching the currently-scanning-out-buffer. Learned that the hard way when I did bypass... Fortunately when you do make that mistake it's usually visibly obvious.

What makes this proposal safe is that we hold both the buffer being scheduled and the buffer currently visible ("last_flipped_"), to ensure neither of them ever get modified until they have been retired by a later page flip.

Buffers are only released when they are neither scheduled, nor being scanned out:

// Presently visible is either last_flipped_bufobj (composited), last_flipped_bypass_buf (bypassed) or
// scheduled_bufobj (recently flipped which replaced last_flipped_bufobj).
wait_for_page_flip();

// Now presently visible is either scheduled_bufobj (composited) or last_flipped_bypass_buf (bypassed), so
// those are the ones we need to continue holding references to.

if (scheduled_bufobj) // switched from bypass to now compositing
    last_flipped_bypass_buf = nullptr; // release formerly bypassed buffer

// last_flipped_bufobj is now guaranteed no longer on screen, since wait_for_page_flip
if (last_flipped_bufobj)
    last_flipped_bufobj->release();

// Hold the reference so we don't release/reuse the on-screen buffer until something replaces it
last_flipped_bufobj = scheduled_bufobj;

It's confusing I know. But I believe it is correct.

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

Alexandros,

I think I see where you're getting confused. You're assuming that GBM only has two buffers (A and B). Therefore you're also assuming that GBM can willingly violate its own lock semantics [1] and reuse a buffer as the back while Mir still has it locked as a front buffer. Either that or you're assuming that I'm violating GBM lock semantics. None of those are true.

GBM will not allow a currently locked front buffer to be reused as a back buffer. You have to release it first, which we only do after a replacement has been flipped. In fact, if you look at the Mesa source, it allocates a fixed limit of 3-4 color buffers [2] depending on which version of Mesa you have (trusty has 3, upstream Mesa has 4).

The 3 buffers we use are:
  A: last_flipped_bufobj
  B: scheduled_bufobj
  C: The EGL back buffer currently being rendered to (there is no GBM function to access this until you swap and it becomes front).

Thus, yes, this proposal will trigger the internal use of 3 color buffers (as opposed to 2 at present) in Mesa. But it does so safely and guarantees that the visible buffer can not get reused as a back buffer while it is visible.

[1] http://cgit.freedesktop.org/mesa/mesa/tree/src/gbm/main/gbm.c#n406
[2] http://cgit.freedesktop.org/mesa/mesa/tree/src/egl/drivers/dri2/egl_dri2.h#n197

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

> I think I see where you're getting confused. You're assuming that GBM only has two buffers (A and B).
...

Excellent, that was indeed my incorrect assumption. With more than 2 buffers this optimization is safe.

I haven't yet processed the interaction with bypass, but for normal rendering the optimization seems ok, apart from a small glitch in the set up: I think that in the constructor we should be storing the locked front buffer in scheduled_bufobj instead of last_flipped_bufobj. Using last_flipped_bufobj will lead to the release of the front buffer in the first post_update(), after the null wait_for_page_flip, while that front buffer is still visible.

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

Alexandros:
Thanks. Constructor issue fixed.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

This looks like "no obvious problems" - I can't help wondering if there's a way to achieve "obviously no problems".

If I get any good ideas I'll be back.

review: Abstain
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> This looks like "no obvious problems" - I can't help wondering if there's a
> way to achieve "obviously no problems".
>
> If I get any good ideas I'll be back.

The more I think about this the clearer the code looks. That's probably an illusion, but I'll go along with it.

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

No new issues raised since the last one was fixed 7 days ago... Top approving.

Revision history for this message
Alexandros Frantzis (afrantzis) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/platform/graphics/mesa/display.cpp'
2--- src/platform/graphics/mesa/display.cpp 2014-01-22 08:32:55 +0000
3+++ src/platform/graphics/mesa/display.cpp 2014-02-03 10:43:40 +0000
4@@ -132,6 +132,17 @@
5 auto const& kms_conf = dynamic_cast<RealKMSDisplayConfiguration const&>(conf);
6 std::vector<std::unique_ptr<DisplayBuffer>> display_buffers_new;
7
8+ /*
9+ * Notice for a little while here we will have duplicate
10+ * DisplayBuffers attached to each output, and the display_buffers_new
11+ * will take over the outputs before the old display_buffers are
12+ * destroyed. So to avoid page flipping confusion in-between, make
13+ * sure we wait for all pending page flips to finish before the
14+ * display_buffers_new are created and take control of the outputs.
15+ */
16+ for (auto& db : display_buffers)
17+ db->wait_for_page_flip();
18+
19 /* Reset the state of all outputs */
20 kms_conf.for_each_output([&](DisplayConfigurationOutput const& conf_output)
21 {
22
23=== modified file 'src/platform/graphics/mesa/display_buffer.cpp'
24--- src/platform/graphics/mesa/display_buffer.cpp 2014-01-23 17:26:51 +0000
25+++ src/platform/graphics/mesa/display_buffer.cpp 2014-02-03 10:43:40 +0000
26@@ -103,6 +103,7 @@
27 MirOrientation rot,
28 EGLContext shared_context)
29 : last_flipped_bufobj{nullptr},
30+ scheduled_bufobj{nullptr},
31 platform(platform),
32 listener(listener),
33 drm(platform->drm),
34@@ -110,7 +111,8 @@
35 surface_gbm{std::move(surface_gbm_param)},
36 area(area),
37 rotation(rot),
38- needs_set_crtc{false}
39+ needs_set_crtc{false},
40+ page_flips_pending{false}
41 {
42 uint32_t area_width = area.size.width.as_uint32_t();
43 uint32_t area_height = area.size.height.as_uint32_t();
44@@ -142,13 +144,13 @@
45
46 listener->report_successful_egl_buffer_swap_on_construction();
47
48- last_flipped_bufobj = get_front_buffer_object();
49- if (!last_flipped_bufobj)
50+ scheduled_bufobj = get_front_buffer_object();
51+ if (!scheduled_bufobj)
52 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to get frontbuffer"));
53
54 for (auto& output : outputs)
55 {
56- if (!output->set_crtc(last_flipped_bufobj->get_drm_fb_id()))
57+ if (!output->set_crtc(scheduled_bufobj->get_drm_fb_id()))
58 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to set DRM crtc"));
59 }
60
61@@ -171,6 +173,9 @@
62 */
63 if (last_flipped_bufobj)
64 last_flipped_bufobj->release();
65+
66+ if (scheduled_bufobj)
67+ scheduled_bufobj->release();
68 }
69
70 geom::Rectangle mgm::DisplayBuffer::view_area() const
71@@ -206,6 +211,31 @@
72 std::shared_ptr<graphics::Buffer> bypass_buf)
73 {
74 /*
75+ * If the last frame was composited then we haven't waited for the
76+ * page flips yet. This is good because it maximizes the time available
77+ * to spend rendering each frame. However we have to wait here, because
78+ * it will be unsafe to swap_buffers before guaranteeing the previous
79+ * page flip finished.
80+ */
81+ wait_for_page_flip();
82+
83+ /*
84+ * Switching from bypass to compositing? Now is the earliest safe time
85+ * we can unreference the bypass buffer...
86+ */
87+ if (scheduled_bufobj)
88+ last_flipped_bypass_buf = nullptr;
89+ /*
90+ * Release the last flipped buffer object (which is not displayed anymore)
91+ * to make it available for future rendering.
92+ */
93+ if (last_flipped_bufobj)
94+ last_flipped_bufobj->release();
95+
96+ last_flipped_bufobj = scheduled_bufobj;
97+ scheduled_bufobj = nullptr;
98+
99+ /*
100 * Bring the back buffer to the front and get the buffer object
101 * corresponding to the front buffer.
102 */
103@@ -234,7 +264,7 @@
104 * If the flip fails, release the buffer object to make it available
105 * for future rendering.
106 */
107- if (!needs_set_crtc && !schedule_and_wait_for_page_flip(bufobj))
108+ if (!needs_set_crtc && !schedule_page_flip(bufobj))
109 {
110 if (!bypass_buf)
111 bufobj->release();
112@@ -250,22 +280,32 @@
113 needs_set_crtc = false;
114 }
115
116- /*
117- * Release the last flipped buffer object (which is not displayed anymore)
118- * to make it available for future rendering.
119- */
120- if (last_flipped_bufobj)
121- last_flipped_bufobj->release();
122-
123- last_flipped_bufobj = bypass_buf ? nullptr : bufobj;
124-
125- /*
126- * Keep a reference to the buffer being bypassed for the entire duration
127- * of the frame. This ensures the buffer doesn't get reused by the client
128- * prematurely, which would be seen as tearing.
129- * If not bypassing, then bypass_buf will be nullptr.
130- */
131- last_flipped_bypass_buf = bypass_buf;
132+ if (bypass_buf)
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 quad-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 = bypass_buf;
153+ }
154+ else
155+ {
156+ scheduled_bufobj = bufobj;
157+ }
158 }
159
160 mgm::BufferObject* mgm::DisplayBuffer::get_front_buffer_object()
161@@ -311,10 +351,8 @@
162 }
163
164
165-bool mgm::DisplayBuffer::schedule_and_wait_for_page_flip(BufferObject* bufobj)
166+bool mgm::DisplayBuffer::schedule_page_flip(BufferObject* bufobj)
167 {
168- int page_flips_pending{0};
169-
170 /*
171 * Schedule the current front buffer object for display. Note that
172 * the page flip is asynchronous and synchronized with vertical refresh.
173@@ -322,18 +360,21 @@
174 for (auto& output : outputs)
175 {
176 if (output->schedule_page_flip(bufobj->get_drm_fb_id()))
177- ++page_flips_pending;
178+ page_flips_pending = true;
179 }
180
181- if (page_flips_pending == 0)
182- return false;
183+ return page_flips_pending;
184+}
185
186- for (auto& output : outputs)
187+void mgm::DisplayBuffer::wait_for_page_flip()
188+{
189+ if (page_flips_pending)
190 {
191- output->wait_for_page_flip();
192+ for (auto& output : outputs)
193+ output->wait_for_page_flip();
194+
195+ page_flips_pending = false;
196 }
197-
198- return true;
199 }
200
201 void mgm::DisplayBuffer::make_current()
202
203=== modified file 'src/platform/graphics/mesa/display_buffer.h'
204--- src/platform/graphics/mesa/display_buffer.h 2014-01-23 17:26:51 +0000
205+++ src/platform/graphics/mesa/display_buffer.h 2014-02-03 10:43:40 +0000
206@@ -63,13 +63,15 @@
207 std::function<void(Renderable const&)> const& render_fn);
208 MirOrientation orientation() const override;
209 void schedule_set_crtc();
210+ void wait_for_page_flip();
211
212 private:
213 BufferObject* get_front_buffer_object();
214 BufferObject* get_buffer_object(struct gbm_bo *bo);
215- bool schedule_and_wait_for_page_flip(BufferObject* bufobj);
216+ bool schedule_page_flip(BufferObject* bufobj);
217
218 BufferObject* last_flipped_bufobj;
219+ BufferObject* scheduled_bufobj;
220 std::shared_ptr<graphics::Buffer> last_flipped_bypass_buf;
221 std::shared_ptr<Platform> const platform;
222 std::shared_ptr<DisplayReport> const listener;
223@@ -82,6 +84,7 @@
224 uint32_t fb_width, fb_height;
225 MirOrientation rotation;
226 std::atomic<bool> needs_set_crtc;
227+ bool page_flips_pending;
228 };
229
230 }
231
232=== added file 'tests/unit-tests/graphics/mesa/mock_kms_output.h'
233--- tests/unit-tests/graphics/mesa/mock_kms_output.h 1970-01-01 00:00:00 +0000
234+++ tests/unit-tests/graphics/mesa/mock_kms_output.h 2014-02-03 10:43:40 +0000
235@@ -0,0 +1,52 @@
236+/*
237+ * Copyright © 2013 Canonical Ltd.
238+ *
239+ * This program is free software: you can redistribute it and/or modify
240+ * it under the terms of the GNU General Public License version 3 as
241+ * published by the Free Software Foundation.
242+ *
243+ * This program is distributed in the hope that it will be useful,
244+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
245+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
246+ * GNU General Public License for more details.
247+ *
248+ * You should have received a copy of the GNU General Public License
249+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
250+ *
251+ * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
252+ */
253+
254+#ifndef MOCK_KMS_OUTPUT_H_
255+#define MOCK_KMS_OUTPUT_H_
256+
257+#include "src/platform/graphics/mesa/kms_output.h"
258+#include <gmock/gmock.h>
259+
260+namespace mir
261+{
262+namespace test
263+{
264+
265+struct MockKMSOutput : public graphics::mesa::KMSOutput
266+{
267+ MOCK_METHOD0(reset, void());
268+ MOCK_METHOD2(configure, void(geometry::Displacement, size_t));
269+ MOCK_CONST_METHOD0(size, geometry::Size());
270+
271+ MOCK_METHOD1(set_crtc, bool(uint32_t));
272+ MOCK_METHOD0(clear_crtc, void());
273+ MOCK_METHOD1(schedule_page_flip, bool(uint32_t));
274+ MOCK_METHOD0(wait_for_page_flip, void());
275+
276+ MOCK_METHOD1(set_cursor, void(gbm_bo*));
277+ MOCK_METHOD1(move_cursor, void(geometry::Point));
278+ MOCK_METHOD0(clear_cursor, void());
279+ MOCK_CONST_METHOD0(has_cursor, bool());
280+
281+ MOCK_METHOD1(set_power_mode, void(MirPowerMode));
282+};
283+
284+} // namespace test
285+} // namespace mir
286+
287+#endif // MOCK_KMS_OUTPUT_H_
288
289=== modified file 'tests/unit-tests/graphics/mesa/test_cursor.cpp'
290--- tests/unit-tests/graphics/mesa/test_cursor.cpp 2014-01-13 06:12:33 +0000
291+++ tests/unit-tests/graphics/mesa/test_cursor.cpp 2014-02-03 10:43:40 +0000
292@@ -22,6 +22,7 @@
293 #include "src/platform/graphics/mesa/kms_display_configuration.h"
294
295 #include "mir_test_doubles/mock_gbm.h"
296+#include "mock_kms_output.h"
297
298 #include <gtest/gtest.h>
299 #include <gmock/gmock.h>
300@@ -32,29 +33,11 @@
301 namespace mgm = mir::graphics::mesa;
302 namespace geom = mir::geometry;
303 namespace mtd = mir::test::doubles;
304+using mir::test::MockKMSOutput;
305
306 namespace
307 {
308
309-struct MockKMSOutput : public mgm::KMSOutput
310-{
311- MOCK_METHOD0(reset, void());
312- MOCK_METHOD2(configure, void(geom::Displacement, size_t));
313- MOCK_CONST_METHOD0(size, geom::Size());
314-
315- MOCK_METHOD1(set_crtc, bool(uint32_t));
316- MOCK_METHOD0(clear_crtc, void());
317- MOCK_METHOD1(schedule_page_flip, bool(uint32_t));
318- MOCK_METHOD0(wait_for_page_flip, void());
319-
320- MOCK_METHOD1(set_cursor, void(gbm_bo*));
321- MOCK_METHOD1(move_cursor, void(geom::Point));
322- MOCK_METHOD0(clear_cursor, void());
323- MOCK_CONST_METHOD0(has_cursor, bool());
324-
325- MOCK_METHOD1(set_power_mode, void(MirPowerMode));
326-};
327-
328 struct StubKMSOutputContainer : public mgm::KMSOutputContainer
329 {
330 StubKMSOutputContainer()
331
332=== modified file 'tests/unit-tests/graphics/mesa/test_display.cpp'
333--- tests/unit-tests/graphics/mesa/test_display.cpp 2014-01-13 06:12:33 +0000
334+++ tests/unit-tests/graphics/mesa/test_display.cpp 2014-02-03 10:43:40 +0000
335@@ -428,14 +428,12 @@
336
337 /* Handle the flip event */
338 EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _))
339- .Times(1)
340- .WillOnce(DoAll(InvokePageFlipHandler(&user_data), Return(0)));
341+ .Times(0);
342
343- /* Release the current FB */
344+ /* Release last_flipped_bufobj (at destruction time) */
345 EXPECT_CALL(mock_gbm, gbm_surface_release_buffer(mock_gbm.fake_gbm.surface, fake.bo1))
346 .Times(Exactly(1));
347-
348- /* Release the new FB (at destruction time) */
349+ /* Release scheduled_bufobj (at destruction time) */
350 EXPECT_CALL(mock_gbm, gbm_surface_release_buffer(mock_gbm.fake_gbm.surface, fake.bo2))
351 .Times(Exactly(1));
352 }
353@@ -468,11 +466,11 @@
354 .Times(Exactly(1))
355 .WillOnce(Return(-1));
356
357- /* Release the new (not flipped) BO */
358+ /* Release failed bufobj */
359 EXPECT_CALL(mock_gbm, gbm_surface_release_buffer(mock_gbm.fake_gbm.surface, fake.bo2))
360 .Times(Exactly(1));
361
362- /* Release the current FB (at destruction time) */
363+ /* Release scheduled_bufobj (at destruction time) */
364 EXPECT_CALL(mock_gbm, gbm_surface_release_buffer(mock_gbm.fake_gbm.surface, fake.bo1))
365 .Times(Exactly(1));
366 }
367
368=== modified file 'tests/unit-tests/graphics/mesa/test_display_buffer.cpp'
369--- tests/unit-tests/graphics/mesa/test_display_buffer.cpp 2014-01-21 06:27:40 +0000
370+++ tests/unit-tests/graphics/mesa/test_display_buffer.cpp 2014-02-03 10:43:40 +0000
371@@ -25,6 +25,7 @@
372 #include "mir_test_doubles/mock_drm.h"
373 #include "mir_test_doubles/mock_gbm.h"
374 #include "mir_test_framework/udev_environment.h"
375+#include "mock_kms_output.h"
376
377 #include <gtest/gtest.h>
378 #include <gmock/gmock.h>
379@@ -33,8 +34,10 @@
380 using namespace testing;
381 using namespace mir;
382 using namespace std;
383+using namespace mir::test;
384 using namespace mir::test::doubles;
385 using namespace mir::mir_test_framework;
386+using namespace mir::graphics;
387
388 class MesaDisplayBufferTest : public Test
389 {
390@@ -66,6 +69,12 @@
391 .WillByDefault(Return(456));
392
393 fake_devices.add_standard_drm_devices();
394+
395+ mock_kms_output = std::make_shared<NiceMock<MockKMSOutput>>();
396+ ON_CALL(*mock_kms_output, set_crtc(_))
397+ .WillByDefault(Return(true));
398+ ON_CALL(*mock_kms_output, schedule_page_flip(_))
399+ .WillByDefault(Return(true));
400 }
401
402 // The platform has an implicit dependency on mock_gbm etc so must be
403@@ -85,6 +94,7 @@
404 gbm_bo* fake_bo;
405 gbm_bo_handle fake_handle;
406 UdevEnvironment fake_devices;
407+ std::shared_ptr<MockKMSOutput> mock_kms_output;
408 };
409
410 TEST_F(MesaDisplayBufferTest, unrotated_view_area_is_untouched)
411@@ -234,3 +244,55 @@
412 mir_orientation_right,
413 mock_egl.fake_egl_context);
414 }
415+
416+TEST_F(MesaDisplayBufferTest, first_post_flips_but_no_wait)
417+{
418+ geometry::Rectangle const area{{12,34}, {56,78}};
419+
420+ EXPECT_CALL(*mock_kms_output, schedule_page_flip(_))
421+ .Times(1);
422+ EXPECT_CALL(*mock_kms_output, wait_for_page_flip())
423+ .Times(0);
424+
425+ graphics::mesa::DisplayBuffer db(
426+ create_platform(),
427+ make_shared<graphics::NullDisplayReport>(),
428+ {mock_kms_output},
429+ nullptr,
430+ area,
431+ mir_orientation_normal,
432+ mock_egl.fake_egl_context);
433+
434+ db.post_update();
435+}
436+
437+TEST_F(MesaDisplayBufferTest, waits_for_page_flip_on_second_post)
438+{
439+ geometry::Rectangle const area{{12,34}, {56,78}};
440+
441+ InSequence seq;
442+
443+ EXPECT_CALL(*mock_kms_output, wait_for_page_flip())
444+ .Times(0);
445+ EXPECT_CALL(*mock_kms_output, schedule_page_flip(_))
446+ .Times(1);
447+ EXPECT_CALL(*mock_kms_output, wait_for_page_flip())
448+ .Times(1);
449+ EXPECT_CALL(*mock_kms_output, schedule_page_flip(_))
450+ .Times(1);
451+ EXPECT_CALL(*mock_kms_output, wait_for_page_flip())
452+ .Times(0);
453+
454+ graphics::mesa::DisplayBuffer db(
455+ create_platform(),
456+ make_shared<graphics::NullDisplayReport>(),
457+ {mock_kms_output},
458+ nullptr,
459+ area,
460+ mir_orientation_normal,
461+ mock_egl.fake_egl_context);
462+
463+ db.post_update();
464+ db.post_update();
465+}
466+
467
468=== modified file 'tests/unit-tests/graphics/mesa/test_display_multi_monitor.cpp'
469--- tests/unit-tests/graphics/mesa/test_display_multi_monitor.cpp 2014-01-13 06:12:33 +0000
470+++ tests/unit-tests/graphics/mesa/test_display_multi_monitor.cpp 2014-02-03 10:43:40 +0000
471@@ -349,8 +349,8 @@
472 EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(),
473 crtc_ids[i], fb_id,
474 _, _))
475- .Times(1)
476- .WillOnce(DoAll(SaveArg<4>(&user_data[i]), Return(0)));
477+ .Times(2)
478+ .WillRepeatedly(DoAll(SaveArg<4>(&user_data[i]), Return(0)));
479
480 /* Emit fake DRM page-flip events */
481 EXPECT_EQ(1, write(mock_drm.fake_drm.write_fd(), "a", 1));
482@@ -365,6 +365,14 @@
483
484 auto display = create_display_cloned(create_platform());
485
486+ /* First frame: Page flips are scheduled, but not waited for */
487+ display->for_each_display_buffer([](mg::DisplayBuffer& buffer)
488+ {
489+ buffer.post_update();
490+ });
491+
492+ /* Second frame: Previous page flips finish (drmHandleEvent) and new ones
493+ are scheduled */
494 display->for_each_display_buffer([](mg::DisplayBuffer& buffer)
495 {
496 buffer.post_update();

Subscribers

People subscribed via source and target branches