Mir

Merge lp:~vanvugt/mir/predictive-bypass-v3 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: 2719
Proposed branch: lp:~vanvugt/mir/predictive-bypass-v3
Merge into: lp:mir
Diff against target: 1162 lines (+404/-23)
38 files modified
benchmarks/frame-uniformity/vsync_simulating_graphics_platform.cpp (+5/-0)
include/platform/mir/graphics/display.h (+11/-0)
include/test/mir/test/doubles/null_display_sync_group.h (+11/-0)
src/include/platform/mir/options/configuration.h (+1/-0)
src/platform/options/default_configuration.cpp (+6/-0)
src/platform/symbols.map (+10/-0)
src/platforms/android/server/display_device.h (+3/-0)
src/platforms/android/server/display_group.cpp (+5/-0)
src/platforms/android/server/display_group.h (+1/-0)
src/platforms/android/server/fb_device.cpp (+5/-0)
src/platforms/android/server/fb_device.h (+1/-0)
src/platforms/android/server/hwc_device.cpp (+19/-0)
src/platforms/android/server/hwc_device.h (+2/-0)
src/platforms/android/server/hwc_fb_device.cpp (+5/-0)
src/platforms/android/server/hwc_fb_device.h (+1/-0)
src/platforms/mesa/server/kms/display_buffer.cpp (+33/-0)
src/platforms/mesa/server/kms/display_buffer.h (+2/-0)
src/platforms/mesa/server/kms/kms_output.h (+8/-0)
src/platforms/mesa/server/kms/real_kms_output.cpp (+8/-0)
src/platforms/mesa/server/kms/real_kms_output.h (+1/-0)
src/server/compositor/default_configuration.cpp (+4/-0)
src/server/compositor/multi_threaded_compositor.cpp (+18/-1)
src/server/compositor/multi_threaded_compositor.h (+2/-0)
src/server/graphics/nested/display.cpp (+8/-0)
src/server/graphics/nested/display.h (+1/-0)
src/server/graphics/offscreen/display.cpp (+6/-0)
src/server/graphics/offscreen/display.h (+1/-0)
tests/include/mir/test/doubles/mock_display_device.h (+1/-0)
tests/integration-tests/test_surface_stack_with_compositor.cpp (+12/-9)
tests/mir_test_doubles/mock_drm.cpp (+5/-0)
tests/unit-tests/compositor/test_multi_threaded_compositor.cpp (+71/-13)
tests/unit-tests/graphics/android/test_fb_device.cpp (+3/-0)
tests/unit-tests/graphics/android/test_hwc_device.cpp (+43/-0)
tests/unit-tests/graphics/android/test_hwc_fb_device.cpp (+3/-0)
tests/unit-tests/graphics/mesa/kms/mock_kms_output.h (+1/-0)
tests/unit-tests/graphics/mesa/kms/test_display_buffer.cpp (+54/-0)
tests/unit-tests/graphics/nested/test_nested_display.cpp (+17/-0)
tests/unit-tests/graphics/offscreen/test_offscreen_display.cpp (+16/-0)
To merge this branch: bzr merge lp:~vanvugt/mir/predictive-bypass-v3
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Daniel van Vugt Approve
Chris Halse Rogers Approve
Kevin DuBois (community) Approve
Review via email: mp+263213@code.launchpad.net

Commit message

Introducing "predictive bypass"; this provides a constant ~10ms reduction
in latency when fully bypassed/overlayed. This benefit is in addition to
any lag reductions provided by other branches.

Additional unexpected benefits (free!):
  * In some cases even smoothness/frame rate is improved by this branch
    (LP: #1447896).
  * Software cursors/touchspots appear to "stick" to the client app better
    with this branch. Because the underlying client surface has the
    additional time it needs to update for the new cursor/touch position
    before the frame is posted.

Description of the change

This is version 3. Based on changes requested/suggested from version 2:
https://code.launchpad.net/~vanvugt/mir/predictive-bypass-2/+merge/261941

The sleep is still only allowed during bypass/overlaying so that time is never taken away from GL rendering. But you can force a specific sleep duration always-on using --composite-delay=MILLISECONDS
which is equivalent to what Weston does:
https://www.collabora.com/about-us/blog/2015/02/12/weston-repaint-scheduling/

Conversely, to turn off predictive bypass: --composite-delay=0

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

my complaints from v1 and v2 look addressed, lgtm!
Further improvements might be:
1) We can detect the device name on the android platform (via mga::DeviceQuirks), so we could have device-specific times.
2) This is passive (the MTC asks the DisplayGroup how long to wait), but I wouldn't mind the display hardware telling the MTC its ready to composite. (and mtc gets final say whether it does composite or not). This would also make the test for MTC less timing dependendent.

review: Approve
Revision history for this message
Kevin DuBois (kdub) wrote :

nit:
102 + (composite_delay_opt, po::value<int>()->default_value(-1),
103 + "Compositor frame delay in milliseconds (how long to wait for new "
104 + "frames from clients before compositing). Higher values result in "
105 + "lower latency. Default: Automatically determined.")

Instead of "higher values result in lower latency", perhaps "a properly tuned value can result in lower latency"? The current phrasing makes it tempting to set to a very high value.

Revision history for this message
Chris Halse Rogers (raof) wrote :

This seems good to me. Kevin's DisplayGroup driven composite idea seems good, too, but can be done later.

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

I'm intentionally steering away from that right now. Because it's only an ideal time to post the next frame. Priority must still be given to the compositor which knows whether there needs to be a next frame at all. If nothing in the scene (or the "view") is changing then we need to stay asleep, and ignore what the recommended delay to the next frame is.

Revision history for this message
Daniel van Vugt (vanvugt) :
review: Approve
Revision history for this message
Chris Halse Rogers (raof) wrote :

Hmm. It seems that we've been unclear.

The active display wouldn't *drive* the MTC. It would just replace the "you should probably wait at least $X ms" data member with a "you should probably wait at least until I signal you" signal. The MTC can still choose to wait longer (if there has been no scene change) or less time if it wanted to.

-----Original Message-----
From: "Daniel van Vugt" <email address hidden>
Sent: ‎30/‎06/‎2015 16:49
To: "Daniel van Vugt" <email address hidden>
Subject: Re: [Merge] lp:~vanvugt/mir/predictive-bypass-v3 into lp:mir

I'm intentionally steering away from that right now. Because it's only an ideal time to post the next frame. Priority must still be given to the compositor which knows whether there needs to be a next frame at all. If nothing in the scene (or the "view") is changing then we need to stay asleep, and ignore what the recommended delay to the next frame is.
--
https://code.launchpad.net/~vanvugt/mir/predictive-bypass-v3/+merge/263213
You are reviewing the proposed merge of lp:~vanvugt/mir/predictive-bypass-v3 into lp:mir.

Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Chris Halse Rogers (raof) wrote :

Did Jenkins just merge this after it failed CI?

--
Sent using Dekko from my Ubuntu device

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'benchmarks/frame-uniformity/vsync_simulating_graphics_platform.cpp'
--- benchmarks/frame-uniformity/vsync_simulating_graphics_platform.cpp 2015-06-25 03:00:08 +0000
+++ benchmarks/frame-uniformity/vsync_simulating_graphics_platform.cpp 2015-06-30 06:52:15 +0000
@@ -60,6 +60,11 @@
60 60
61 last_sync = now;61 last_sync = now;
62 }62 }
63
64 std::chrono::milliseconds recommended_sleep() const override
65 {
66 return std::chrono::milliseconds::zero();
67 }
63 68
64 double const vsync_rate_in_hz;69 double const vsync_rate_in_hz;
6570
6671
=== modified file 'include/platform/mir/graphics/display.h'
--- include/platform/mir/graphics/display.h 2015-06-17 05:20:42 +0000
+++ include/platform/mir/graphics/display.h 2015-06-30 06:52:15 +0000
@@ -21,6 +21,7 @@
2121
22#include <memory>22#include <memory>
23#include <functional>23#include <functional>
24#include <chrono>
2425
25namespace mir26namespace mir
26{27{
@@ -63,6 +64,16 @@
63 * in the near future. On some platforms, this may wait a potentially long time for vsync. 64 * in the near future. On some platforms, this may wait a potentially long time for vsync.
64 **/65 **/
65 virtual void post() = 0;66 virtual void post() = 0;
67
68 /**
69 * Returns a recommendation to the compositor as to how long it should
70 * wait before sampling the scene for the next frame. Sampling the
71 * scene too early results in up to one whole frame of extra lag if
72 * rendering is fast or skipped altogether (bypass/overlays). But sampling
73 * too late and we might miss the deadline. If unsure just return zero.
74 */
75 virtual std::chrono::milliseconds recommended_sleep() const = 0;
76
66 virtual ~DisplaySyncGroup() = default;77 virtual ~DisplaySyncGroup() = default;
67protected:78protected:
68 DisplaySyncGroup() = default;79 DisplaySyncGroup() = default;
6980
=== modified file 'include/test/mir/test/doubles/null_display_sync_group.h'
--- include/test/mir/test/doubles/null_display_sync_group.h 2015-06-25 03:00:08 +0000
+++ include/test/mir/test/doubles/null_display_sync_group.h 2015-06-30 06:52:15 +0000
@@ -55,6 +55,11 @@
55 std::this_thread::yield();55 std::this_thread::yield();
56 }56 }
5757
58 std::chrono::milliseconds recommended_sleep() const override
59 {
60 return std::chrono::milliseconds::zero();
61 }
62
58private:63private:
59 std::vector<geometry::Rectangle> const output_rects;64 std::vector<geometry::Rectangle> const output_rects;
60 std::vector<StubDisplayBuffer> display_buffers;65 std::vector<StubDisplayBuffer> display_buffers;
@@ -71,6 +76,12 @@
71 /* yield() is needed to ensure reasonable runtime under valgrind for some tests */76 /* yield() is needed to ensure reasonable runtime under valgrind for some tests */
72 std::this_thread::yield();77 std::this_thread::yield();
73 }78 }
79
80 std::chrono::milliseconds recommended_sleep() const override
81 {
82 return std::chrono::milliseconds::zero();
83 }
84
74 NullDisplayBuffer db;85 NullDisplayBuffer db;
75};86};
7687
7788
=== modified file 'src/include/platform/mir/options/configuration.h'
--- src/include/platform/mir/options/configuration.h 2015-06-17 05:20:42 +0000
+++ src/include/platform/mir/options/configuration.h 2015-06-30 06:52:15 +0000
@@ -47,6 +47,7 @@
47extern char const* const fatal_abort_opt;47extern char const* const fatal_abort_opt;
48extern char const* const debug_opt;48extern char const* const debug_opt;
49extern char const* const nbuffers_opt;49extern char const* const nbuffers_opt;
50extern char const* const composite_delay_opt;
50extern char const* const enable_key_repeat_opt;51extern char const* const enable_key_repeat_opt;
5152
52extern char const* const name_opt;53extern char const* const name_opt;
5354
=== modified file 'src/platform/options/default_configuration.cpp'
--- src/platform/options/default_configuration.cpp 2015-06-17 05:20:42 +0000
+++ src/platform/options/default_configuration.cpp 2015-06-30 06:52:15 +0000
@@ -49,6 +49,7 @@
49char const* const mo::fatal_abort_opt = "on-fatal-error-abort";49char const* const mo::fatal_abort_opt = "on-fatal-error-abort";
50char const* const mo::debug_opt = "debug";50char const* const mo::debug_opt = "debug";
51char const* const mo::nbuffers_opt = "nbuffers";51char const* const mo::nbuffers_opt = "nbuffers";
52char const* const mo::composite_delay_opt = "composite-delay";
52char const* const mo::enable_key_repeat_opt = "enable-key-repeat";53char const* const mo::enable_key_repeat_opt = "enable-key-repeat";
5354
54char const* const mo::off_opt_value = "off";55char const* const mo::off_opt_value = "off";
@@ -173,6 +174,11 @@
173 "threads in frontend thread pool.")174 "threads in frontend thread pool.")
174 (nbuffers_opt, po::value<int>()->default_value(3),175 (nbuffers_opt, po::value<int>()->default_value(3),
175 "Number of buffers per surface.")176 "Number of buffers per surface.")
177 (composite_delay_opt, po::value<int>()->default_value(-1),
178 "Compositor frame delay in milliseconds (how long to wait for new "
179 "frames from clients before compositing). Higher values result in "
180 "lower latency but risk causing frame skipping. "
181 "Default: A negative value means decide automatically.")
176 (name_opt, po::value<std::string>(),182 (name_opt, po::value<std::string>(),
177 "When nested, the name Mir uses when registering with the host.")183 "When nested, the name Mir uses when registering with the host.")
178 (offscreen_opt,184 (offscreen_opt,
179185
=== modified file 'src/platform/symbols.map'
--- src/platform/symbols.map 2015-06-17 05:20:42 +0000
+++ src/platform/symbols.map 2015-06-30 06:52:15 +0000
@@ -276,3 +276,13 @@
276 };276 };
277 local: *;277 local: *;
278};278};
279
280# Why are server-only options here in libmirplatform?...
281MIRPLATFORM_7.1 {
282 global:
283 extern "C++" {
284 mir::options::composite_delay_opt*;
285 };
286 local: *;
287} MIRPLATFORM_7;
288
279289
=== modified file 'src/platforms/android/server/display_device.h'
--- src/platforms/android/server/display_device.h 2015-06-17 05:20:42 +0000
+++ src/platforms/android/server/display_device.h 2015-06-30 06:52:15 +0000
@@ -25,6 +25,7 @@
25#include "display_name.h"25#include "display_name.h"
26#include <EGL/egl.h>26#include <EGL/egl.h>
27#include <list>27#include <list>
28#include <chrono>
2829
29namespace mir30namespace mir
30{31{
@@ -66,6 +67,8 @@
66 //notify the DisplayDevice that the screen content was cleared in a way other than the above fns67 //notify the DisplayDevice that the screen content was cleared in a way other than the above fns
67 virtual void content_cleared() = 0;68 virtual void content_cleared() = 0;
6869
70 virtual std::chrono::milliseconds recommended_sleep() const = 0;
71
69protected:72protected:
70 DisplayDevice() = default;73 DisplayDevice() = default;
71 DisplayDevice& operator=(DisplayDevice const&) = delete;74 DisplayDevice& operator=(DisplayDevice const&) = delete;
7275
=== modified file 'src/platforms/android/server/display_group.cpp'
--- src/platforms/android/server/display_group.cpp 2015-05-19 17:17:39 +0000
+++ src/platforms/android/server/display_group.cpp 2015-06-30 06:52:15 +0000
@@ -81,3 +81,8 @@
81 contents.emplace_back(db.second->contents());81 contents.emplace_back(db.second->contents());
82 device->commit(contents); 82 device->commit(contents);
83}83}
84
85std::chrono::milliseconds mga::DisplayGroup::recommended_sleep() const
86{
87 return device->recommended_sleep();
88}
8489
=== modified file 'src/platforms/android/server/display_group.h'
--- src/platforms/android/server/display_group.h 2015-05-19 17:16:12 +0000
+++ src/platforms/android/server/display_group.h 2015-06-30 06:52:15 +0000
@@ -44,6 +44,7 @@
4444
45 void for_each_display_buffer(std::function<void(graphics::DisplayBuffer&)> const& f) override;45 void for_each_display_buffer(std::function<void(graphics::DisplayBuffer&)> const& f) override;
46 void post() override;46 void post() override;
47 std::chrono::milliseconds recommended_sleep() const override;
4748
48 void add(DisplayName name, std::unique_ptr<ConfigurableDisplayBuffer> buffer);49 void add(DisplayName name, std::unique_ptr<ConfigurableDisplayBuffer> buffer);
49 void remove(DisplayName name);50 void remove(DisplayName name);
5051
=== modified file 'src/platforms/android/server/fb_device.cpp'
--- src/platforms/android/server/fb_device.cpp 2015-06-17 05:20:42 +0000
+++ src/platforms/android/server/fb_device.cpp 2015-06-30 06:52:15 +0000
@@ -115,3 +115,8 @@
115void mga::FBDevice::content_cleared()115void mga::FBDevice::content_cleared()
116{116{
117}117}
118
119std::chrono::milliseconds mga::FBDevice::recommended_sleep() const
120{
121 return std::chrono::milliseconds::zero();
122}
118123
=== modified file 'src/platforms/android/server/fb_device.h'
--- src/platforms/android/server/fb_device.h 2015-06-17 05:20:42 +0000
+++ src/platforms/android/server/fb_device.h 2015-06-30 06:52:15 +0000
@@ -51,6 +51,7 @@
5151
52 bool compatible_renderlist(RenderableList const& renderlist) override;52 bool compatible_renderlist(RenderableList const& renderlist) override;
53 void commit(std::list<DisplayContents> const& contents) override;53 void commit(std::list<DisplayContents> const& contents) override;
54 std::chrono::milliseconds recommended_sleep() const override;
5455
55private:56private:
56 std::shared_ptr<framebuffer_device_t> const fb_device;57 std::shared_ptr<framebuffer_device_t> const fb_device;
5758
=== modified file 'src/platforms/android/server/hwc_device.cpp'
--- src/platforms/android/server/hwc_device.cpp 2015-06-17 05:20:42 +0000
+++ src/platforms/android/server/hwc_device.cpp 2015-06-30 06:52:15 +0000
@@ -27,6 +27,8 @@
27#include "mir/raii.h"27#include "mir/raii.h"
28#include <limits>28#include <limits>
29#include <algorithm>29#include <algorithm>
30#include <chrono>
31#include <thread>
3032
31namespace mg = mir::graphics;33namespace mg = mir::graphics;
32namespace mga=mir::graphics::android;34namespace mga=mir::graphics::android;
@@ -97,6 +99,8 @@
9799
98 hwc_wrapper->prepare(lists);100 hwc_wrapper->prepare(lists);
99101
102 bool purely_overlays = true;
103
100 for (auto& content : contents)104 for (auto& content : contents)
101 {105 {
102 if (content.list.needs_swapbuffers())106 if (content.list.needs_swapbuffers())
@@ -111,6 +115,7 @@
111 content.compositor.render(std::move(rejected_renderables), content.list_offset, content.context);115 content.compositor.render(std::move(rejected_renderables), content.list_offset, content.context);
112 content.list.setup_fb(content.context.last_rendered_buffer());116 content.list.setup_fb(content.context.last_rendered_buffer());
113 content.list.swap_occurred();117 content.list.swap_occurred();
118 purely_overlays = false;
114 }119 }
115 120
116 //setup overlays121 //setup overlays
@@ -136,6 +141,20 @@
136141
137 mir::Fd retire_fd(content.list.retirement_fence());142 mir::Fd retire_fd(content.list.retirement_fence());
138 }143 }
144
145 /*
146 * Test results (how long can we sleep for without missing a frame?):
147 * arale: 10ms (TODO: Find out why arale is so slow)
148 * mako: 15ms
149 * krillin: 11ms (to be fair, the display is 67Hz)
150 */
151 using namespace std;
152 recommend_sleep = purely_overlays ? 10ms : 0ms;
153}
154
155std::chrono::milliseconds mga::HwcDevice::recommended_sleep() const
156{
157 return recommend_sleep;
139}158}
140159
141void mga::HwcDevice::content_cleared()160void mga::HwcDevice::content_cleared()
142161
=== modified file 'src/platforms/android/server/hwc_device.h'
--- src/platforms/android/server/hwc_device.h 2015-04-28 07:54:10 +0000
+++ src/platforms/android/server/hwc_device.h 2015-06-30 06:52:15 +0000
@@ -46,6 +46,7 @@
46 bool compatible_renderlist(RenderableList const& renderlist) override;46 bool compatible_renderlist(RenderableList const& renderlist) override;
47 void commit(std::list<DisplayContents> const& contents) override;47 void commit(std::list<DisplayContents> const& contents) override;
48 void content_cleared() override;48 void content_cleared() override;
49 std::chrono::milliseconds recommended_sleep() const override;
4950
50private:51private:
51 bool buffer_is_onscreen(Buffer const&) const;52 bool buffer_is_onscreen(Buffer const&) const;
@@ -53,6 +54,7 @@
5354
54 std::shared_ptr<HwcWrapper> const hwc_wrapper;55 std::shared_ptr<HwcWrapper> const hwc_wrapper;
55 std::shared_ptr<SyncFileOps> const sync_ops;56 std::shared_ptr<SyncFileOps> const sync_ops;
57 std::chrono::milliseconds recommend_sleep{0};
56};58};
5759
58}60}
5961
=== modified file 'src/platforms/android/server/hwc_fb_device.cpp'
--- src/platforms/android/server/hwc_fb_device.cpp 2015-06-17 05:20:42 +0000
+++ src/platforms/android/server/hwc_fb_device.cpp 2015-06-30 06:52:15 +0000
@@ -113,3 +113,8 @@
113void mga::HwcFbDevice::content_cleared()113void mga::HwcFbDevice::content_cleared()
114{114{
115}115}
116
117std::chrono::milliseconds mga::HwcFbDevice::recommended_sleep() const
118{
119 return std::chrono::milliseconds::zero();
120}
116121
=== modified file 'src/platforms/android/server/hwc_fb_device.h'
--- src/platforms/android/server/hwc_fb_device.h 2015-04-28 07:54:10 +0000
+++ src/platforms/android/server/hwc_fb_device.h 2015-06-30 06:52:15 +0000
@@ -43,6 +43,7 @@
4343
44 bool compatible_renderlist(RenderableList const& renderlist) override;44 bool compatible_renderlist(RenderableList const& renderlist) override;
45 void commit(std::list<DisplayContents> const& contents) override;45 void commit(std::list<DisplayContents> const& contents) override;
46 std::chrono::milliseconds recommended_sleep() const override;
4647
47private:48private:
48 void content_cleared() override;49 void content_cleared() override;
4950
=== modified file 'src/platforms/mesa/server/kms/display_buffer.cpp'
--- src/platforms/mesa/server/kms/display_buffer.cpp 2015-06-17 05:20:42 +0000
+++ src/platforms/mesa/server/kms/display_buffer.cpp 2015-06-30 06:52:15 +0000
@@ -28,6 +28,8 @@
28#include <GLES2/gl2.h>28#include <GLES2/gl2.h>
2929
30#include <stdexcept>30#include <stdexcept>
31#include <chrono>
32#include <thread>
3133
32namespace mgm = mir::graphics::mesa;34namespace mgm = mir::graphics::mesa;
33namespace geom = mir::geometry;35namespace geom = mir::geometry;
@@ -282,6 +284,11 @@
282 needs_set_crtc = false;284 needs_set_crtc = false;
283 }285 }
284286
287 using namespace std; // For operator""ms()
288
289 // Predicted worst case render time for the next frame...
290 auto predicted_render_time = 50ms;
291
285 if (bypass_buf)292 if (bypass_buf)
286 {293 {
287 /*294 /*
@@ -295,6 +302,10 @@
295 */302 */
296 scheduled_bypass_frame = bypass_buf;303 scheduled_bypass_frame = bypass_buf;
297 wait_for_page_flip();304 wait_for_page_flip();
305
306 // It's very likely the next frame will be bypassed like this one so
307 // we only need time for kernel page flip scheduling...
308 predicted_render_time = 5ms;
298 }309 }
299 else310 else
300 {311 {
@@ -306,11 +317,33 @@
306 scheduled_composite_frame = bufobj;317 scheduled_composite_frame = bufobj;
307 if (outputs.size() == 1)318 if (outputs.size() == 1)
308 wait_for_page_flip();319 wait_for_page_flip();
320
321 /*
322 * TODO: If you're optimistic about your GPU performance and/or
323 * measure it carefully you may wish to set predicted_render_time
324 * to a lower value here for lower latency.
325 *
326 *predicted_render_time = 9ms; // e.g. about the same as Weston
327 */
309 }328 }
310329
311 // Buffer lifetimes are managed exclusively by scheduled*/visible* now330 // Buffer lifetimes are managed exclusively by scheduled*/visible* now
312 bypass_buf = nullptr;331 bypass_buf = nullptr;
313 bypass_bufobj = nullptr;332 bypass_bufobj = nullptr;
333
334 recommend_sleep = 0ms;
335 if (outputs.size() == 1)
336 {
337 auto const& output = outputs.front();
338 auto const min_frame_interval = 1000ms / output->max_refresh_rate();
339 if (predicted_render_time < min_frame_interval)
340 recommend_sleep = min_frame_interval - predicted_render_time;
341 }
342}
343
344std::chrono::milliseconds mgm::DisplayBuffer::recommended_sleep() const
345{
346 return recommend_sleep;
314}347}
315348
316mgm::BufferObject* mgm::DisplayBuffer::get_front_buffer_object()349mgm::BufferObject* mgm::DisplayBuffer::get_front_buffer_object()
317350
=== modified file 'src/platforms/mesa/server/kms/display_buffer.h'
--- src/platforms/mesa/server/kms/display_buffer.h 2015-06-17 05:20:42 +0000
+++ src/platforms/mesa/server/kms/display_buffer.h 2015-06-30 06:52:15 +0000
@@ -65,6 +65,7 @@
65 void for_each_display_buffer(65 void for_each_display_buffer(
66 std::function<void(graphics::DisplayBuffer&)> const& f) override;66 std::function<void(graphics::DisplayBuffer&)> const& f) override;
67 void post() override;67 void post() override;
68 std::chrono::milliseconds recommended_sleep() const override;
6869
69 MirOrientation orientation() const override;70 MirOrientation orientation() const override;
70 void set_orientation(MirOrientation const rot, geometry::Rectangle const& a);71 void set_orientation(MirOrientation const rot, geometry::Rectangle const& a);
@@ -93,6 +94,7 @@
93 uint32_t fb_width, fb_height;94 uint32_t fb_width, fb_height;
94 MirOrientation rotation;95 MirOrientation rotation;
95 std::atomic<bool> needs_set_crtc;96 std::atomic<bool> needs_set_crtc;
97 std::chrono::milliseconds recommend_sleep{0};
96 bool page_flips_pending;98 bool page_flips_pending;
97};99};
98100
99101
=== modified file 'src/platforms/mesa/server/kms/kms_output.h'
--- src/platforms/mesa/server/kms/kms_output.h 2015-06-17 05:20:42 +0000
+++ src/platforms/mesa/server/kms/kms_output.h 2015-06-30 06:52:15 +0000
@@ -43,6 +43,14 @@
43 virtual void configure(geometry::Displacement fb_offset, size_t kms_mode_index) = 0;43 virtual void configure(geometry::Displacement fb_offset, size_t kms_mode_index) = 0;
44 virtual geometry::Size size() const = 0;44 virtual geometry::Size size() const = 0;
4545
46 /**
47 * Approximate maximum refresh rate of this output to within 1Hz.
48 * Typically the rate is fixed (e.g. 60Hz) but it may also be variable as
49 * in Nvidia G-Sync/AMD FreeSync/VESA Adaptive Sync. So this function
50 * returns the maximum rate to expect.
51 */
52 virtual int max_refresh_rate() const = 0;
53
46 virtual bool set_crtc(uint32_t fb_id) = 0;54 virtual bool set_crtc(uint32_t fb_id) = 0;
47 virtual void clear_crtc() = 0;55 virtual void clear_crtc() = 0;
48 virtual bool schedule_page_flip(uint32_t fb_id) = 0;56 virtual bool schedule_page_flip(uint32_t fb_id) = 0;
4957
=== modified file 'src/platforms/mesa/server/kms/real_kms_output.cpp'
--- src/platforms/mesa/server/kms/real_kms_output.cpp 2015-06-17 05:20:42 +0000
+++ src/platforms/mesa/server/kms/real_kms_output.cpp 2015-06-30 06:52:15 +0000
@@ -188,6 +188,14 @@
188 return {mode.hdisplay, mode.vdisplay};188 return {mode.hdisplay, mode.vdisplay};
189}189}
190190
191int mgm::RealKMSOutput::max_refresh_rate() const
192{
193 // TODO: In future when DRM exposes FreeSync/Adaptive Sync/G-Sync info
194 // this value may be calculated differently.
195 drmModeModeInfo const& current_mode = connector->modes[mode_index];
196 return current_mode.vrefresh;
197}
198
191void mgm::RealKMSOutput::configure(geom::Displacement offset, size_t kms_mode_index)199void mgm::RealKMSOutput::configure(geom::Displacement offset, size_t kms_mode_index)
192{200{
193 fb_offset = offset;201 fb_offset = offset;
194202
=== modified file 'src/platforms/mesa/server/kms/real_kms_output.h'
--- src/platforms/mesa/server/kms/real_kms_output.h 2015-06-17 05:20:42 +0000
+++ src/platforms/mesa/server/kms/real_kms_output.h 2015-06-30 06:52:15 +0000
@@ -44,6 +44,7 @@
44 void reset();44 void reset();
45 void configure(geometry::Displacement fb_offset, size_t kms_mode_index);45 void configure(geometry::Displacement fb_offset, size_t kms_mode_index);
46 geometry::Size size() const;46 geometry::Size size() const;
47 int max_refresh_rate() const;
4748
48 bool set_crtc(uint32_t fb_id);49 bool set_crtc(uint32_t fb_id);
49 void clear_crtc();50 void clear_crtc();
5051
=== modified file 'src/server/compositor/default_configuration.cpp'
--- src/server/compositor/default_configuration.cpp 2015-06-17 05:20:42 +0000
+++ src/server/compositor/default_configuration.cpp 2015-06-30 06:52:15 +0000
@@ -84,12 +84,16 @@
84 return compositor(84 return compositor(
85 [this]()85 [this]()
86 {86 {
87 std::chrono::milliseconds const composite_delay(
88 the_options()->get<int>(options::composite_delay_opt));
89
87 return std::make_shared<mc::MultiThreadedCompositor>(90 return std::make_shared<mc::MultiThreadedCompositor>(
88 the_display(),91 the_display(),
89 the_scene(),92 the_scene(),
90 the_display_buffer_compositor_factory(),93 the_display_buffer_compositor_factory(),
91 the_shell(),94 the_shell(),
92 the_compositor_report(),95 the_compositor_report(),
96 composite_delay,
93 !the_options()->is_set(options::host_socket_opt));97 !the_options()->is_set(options::host_socket_opt));
94 });98 });
95}99}
96100
=== modified file 'src/server/compositor/multi_threaded_compositor.cpp'
--- src/server/compositor/multi_threaded_compositor.cpp 2015-06-18 14:33:10 +0000
+++ src/server/compositor/multi_threaded_compositor.cpp 2015-06-30 06:52:15 +0000
@@ -101,12 +101,14 @@
101 mg::DisplaySyncGroup& group,101 mg::DisplaySyncGroup& group,
102 std::shared_ptr<mc::Scene> const& scene,102 std::shared_ptr<mc::Scene> const& scene,
103 std::shared_ptr<DisplayListener> const& display_listener,103 std::shared_ptr<DisplayListener> const& display_listener,
104 std::chrono::milliseconds fixed_composite_delay,
104 std::shared_ptr<CompositorReport> const& report) :105 std::shared_ptr<CompositorReport> const& report) :
105 compositor_factory{db_compositor_factory},106 compositor_factory{db_compositor_factory},
106 group(group),107 group(group),
107 scene(scene),108 scene(scene),
108 running{true},109 running{true},
109 frames_scheduled{0},110 frames_scheduled{0},
111 force_sleep{fixed_composite_delay},
110 display_listener{display_listener},112 display_listener{display_listener},
111 report{report},113 report{report},
112 started_future{started.get_future()}114 started_future{started.get_future()}
@@ -182,6 +184,17 @@
182 }184 }
183 group.post();185 group.post();
184186
187 /*
188 * "Predictive bypass" optimization: If the last frame was
189 * bypassed/overlayed or you simply have a fast GPU, it is
190 * beneficial to sleep for most of the next frame. This reduces
191 * the latency between snapshotting the scene and post()
192 * completing by almost a whole frame.
193 */
194 auto delay = force_sleep >= std::chrono::milliseconds::zero() ?
195 force_sleep : group.recommended_sleep();
196 std::this_thread::sleep_for(delay);
197
185 lock.lock();198 lock.lock();
186199
187 /*200 /*
@@ -231,6 +244,7 @@
231 std::shared_ptr<mc::Scene> const scene;244 std::shared_ptr<mc::Scene> const scene;
232 bool running;245 bool running;
233 int frames_scheduled;246 int frames_scheduled;
247 std::chrono::milliseconds force_sleep{-1};
234 std::mutex run_mutex;248 std::mutex run_mutex;
235 std::condition_variable run_cv;249 std::condition_variable run_cv;
236 std::shared_ptr<DisplayListener> const display_listener;250 std::shared_ptr<DisplayListener> const display_listener;
@@ -248,6 +262,7 @@
248 std::shared_ptr<DisplayBufferCompositorFactory> const& db_compositor_factory,262 std::shared_ptr<DisplayBufferCompositorFactory> const& db_compositor_factory,
249 std::shared_ptr<DisplayListener> const& display_listener,263 std::shared_ptr<DisplayListener> const& display_listener,
250 std::shared_ptr<CompositorReport> const& compositor_report,264 std::shared_ptr<CompositorReport> const& compositor_report,
265 std::chrono::milliseconds fixed_composite_delay,
251 bool compose_on_start)266 bool compose_on_start)
252 : display{display},267 : display{display},
253 scene{scene},268 scene{scene},
@@ -255,6 +270,7 @@
255 display_listener{display_listener},270 display_listener{display_listener},
256 report{compositor_report},271 report{compositor_report},
257 state{CompositorState::stopped},272 state{CompositorState::stopped},
273 fixed_composite_delay{fixed_composite_delay},
258 compose_on_start{compose_on_start},274 compose_on_start{compose_on_start},
259 thread_pool{1}275 thread_pool{1}
260{276{
@@ -348,7 +364,8 @@
348 display->for_each_display_sync_group([this](mg::DisplaySyncGroup& group)364 display->for_each_display_sync_group([this](mg::DisplaySyncGroup& group)
349 {365 {
350 auto thread_functor = std::make_unique<mc::CompositingFunctor>(366 auto thread_functor = std::make_unique<mc::CompositingFunctor>(
351 display_buffer_compositor_factory, group, scene, display_listener, report);367 display_buffer_compositor_factory, group, scene, display_listener,
368 fixed_composite_delay, report);
352369
353 futures.push_back(thread_pool.run(std::ref(*thread_functor), &group));370 futures.push_back(thread_pool.run(std::ref(*thread_functor), &group));
354 thread_functors.push_back(std::move(thread_functor));371 thread_functors.push_back(std::move(thread_functor));
355372
=== modified file 'src/server/compositor/multi_threaded_compositor.h'
--- src/server/compositor/multi_threaded_compositor.h 2015-06-17 05:20:42 +0000
+++ src/server/compositor/multi_threaded_compositor.h 2015-06-30 06:52:15 +0000
@@ -65,6 +65,7 @@
65 std::shared_ptr<DisplayBufferCompositorFactory> const& db_compositor_factory,65 std::shared_ptr<DisplayBufferCompositorFactory> const& db_compositor_factory,
66 std::shared_ptr<DisplayListener> const& display_listener,66 std::shared_ptr<DisplayListener> const& display_listener,
67 std::shared_ptr<CompositorReport> const& compositor_report,67 std::shared_ptr<CompositorReport> const& compositor_report,
68 std::chrono::milliseconds fixed_composite_delay, // -1 = automatic
68 bool compose_on_start);69 bool compose_on_start);
69 ~MultiThreadedCompositor();70 ~MultiThreadedCompositor();
7071
@@ -86,6 +87,7 @@
8687
87 std::mutex state_guard;88 std::mutex state_guard;
88 CompositorState state;89 CompositorState state;
90 std::chrono::milliseconds fixed_composite_delay;
89 bool compose_on_start;91 bool compose_on_start;
9092
91 void schedule_compositing(int number_composites);93 void schedule_compositing(int number_composites);
9294
=== modified file 'src/server/graphics/nested/display.cpp'
--- src/server/graphics/nested/display.cpp 2015-06-17 05:20:42 +0000
+++ src/server/graphics/nested/display.cpp 2015-06-30 06:52:15 +0000
@@ -137,6 +137,14 @@
137{137{
138}138}
139139
140std::chrono::milliseconds
141mgn::detail::DisplaySyncGroup::recommended_sleep() const
142{
143 // TODO: Might make sense in future with nested bypass. We could save
144 // almost another frame of lag!
145 return std::chrono::milliseconds::zero();
146}
147
140mgn::Display::Display(148mgn::Display::Display(
141 std::shared_ptr<mg::Platform> const& platform,149 std::shared_ptr<mg::Platform> const& platform,
142 std::shared_ptr<HostConnection> const& connection,150 std::shared_ptr<HostConnection> const& connection,
143151
=== modified file 'src/server/graphics/nested/display.h'
--- src/server/graphics/nested/display.h 2015-06-17 05:20:42 +0000
+++ src/server/graphics/nested/display.h 2015-06-30 06:52:15 +0000
@@ -97,6 +97,7 @@
97 DisplaySyncGroup(std::shared_ptr<detail::DisplayBuffer> const& output);97 DisplaySyncGroup(std::shared_ptr<detail::DisplayBuffer> const& output);
98 void for_each_display_buffer(std::function<void(graphics::DisplayBuffer&)> const&) override;98 void for_each_display_buffer(std::function<void(graphics::DisplayBuffer&)> const&) override;
99 void post() override;99 void post() override;
100 std::chrono::milliseconds recommended_sleep() const override;
100private:101private:
101 std::shared_ptr<detail::DisplayBuffer> const output;102 std::shared_ptr<detail::DisplayBuffer> const output;
102};103};
103104
=== modified file 'src/server/graphics/offscreen/display.cpp'
--- src/server/graphics/offscreen/display.cpp 2015-06-17 05:20:42 +0000
+++ src/server/graphics/offscreen/display.cpp 2015-06-30 06:52:15 +0000
@@ -87,6 +87,12 @@
87{87{
88}88}
8989
90std::chrono::milliseconds
91mgo::detail::DisplaySyncGroup::recommended_sleep() const
92{
93 return std::chrono::milliseconds::zero();
94}
95
90mgo::Display::Display(96mgo::Display::Display(
91 EGLNativeDisplayType egl_native_display,97 EGLNativeDisplayType egl_native_display,
92 std::shared_ptr<DisplayConfigurationPolicy> const& initial_conf_policy,98 std::shared_ptr<DisplayConfigurationPolicy> const& initial_conf_policy,
9399
=== modified file 'src/server/graphics/offscreen/display.h'
--- src/server/graphics/offscreen/display.h 2015-06-17 05:20:42 +0000
+++ src/server/graphics/offscreen/display.h 2015-06-30 06:52:15 +0000
@@ -64,6 +64,7 @@
64 DisplaySyncGroup(std::unique_ptr<DisplayBuffer> output);64 DisplaySyncGroup(std::unique_ptr<DisplayBuffer> output);
65 void for_each_display_buffer(std::function<void(DisplayBuffer&)> const&) override;65 void for_each_display_buffer(std::function<void(DisplayBuffer&)> const&) override;
66 void post() override;66 void post() override;
67 std::chrono::milliseconds recommended_sleep() const override;
67private:68private:
68 std::unique_ptr<DisplayBuffer> const output;69 std::unique_ptr<DisplayBuffer> const output;
69};70};
7071
=== modified file 'tests/include/mir/test/doubles/mock_display_device.h'
--- tests/include/mir/test/doubles/mock_display_device.h 2015-04-28 07:54:10 +0000
+++ tests/include/mir/test/doubles/mock_display_device.h 2015-06-30 06:52:15 +0000
@@ -40,6 +40,7 @@
40 MOCK_METHOD1(commit, void(std::list<graphics::android::DisplayContents> const&));40 MOCK_METHOD1(commit, void(std::list<graphics::android::DisplayContents> const&));
41 MOCK_METHOD1(compatible_renderlist, bool(41 MOCK_METHOD1(compatible_renderlist, bool(
42 graphics::RenderableList const&));42 graphics::RenderableList const&));
43 MOCK_CONST_METHOD0(recommended_sleep, std::chrono::milliseconds());
43};44};
44}45}
45}46}
4647
=== modified file 'tests/integration-tests/test_surface_stack_with_compositor.cpp'
--- tests/integration-tests/test_surface_stack_with_compositor.cpp 2015-06-25 03:00:08 +0000
+++ tests/integration-tests/test_surface_stack_with_compositor.cpp 2015-06-30 06:52:15 +0000
@@ -153,6 +153,9 @@
153 mt::fake_shared(renderer_factory),153 mt::fake_shared(renderer_factory),
154 null_comp_report};154 null_comp_report};
155};155};
156
157std::chrono::milliseconds const default_delay{-1};
158
156}159}
157160
158TEST_F(SurfaceStackCompositor, composes_on_start_if_told_to_in_constructor)161TEST_F(SurfaceStackCompositor, composes_on_start_if_told_to_in_constructor)
@@ -162,7 +165,7 @@
162 mt::fake_shared(stack),165 mt::fake_shared(stack),
163 mt::fake_shared(dbc_factory),166 mt::fake_shared(dbc_factory),
164 mt::fake_shared(stub_display_listener),167 mt::fake_shared(stub_display_listener),
165 null_comp_report, true);168 null_comp_report, default_delay, true);
166 mt_compositor.start();169 mt_compositor.start();
167170
168 EXPECT_TRUE(stub_primary_db.has_posted_at_least(1, timeout));171 EXPECT_TRUE(stub_primary_db.has_posted_at_least(1, timeout));
@@ -176,7 +179,7 @@
176 mt::fake_shared(stack),179 mt::fake_shared(stack),
177 mt::fake_shared(dbc_factory),180 mt::fake_shared(dbc_factory),
178 mt::fake_shared(stub_display_listener),181 mt::fake_shared(stub_display_listener),
179 null_comp_report, false);182 null_comp_report, default_delay, false);
180 mt_compositor.start();183 mt_compositor.start();
181184
182 EXPECT_TRUE(stub_primary_db.has_posted_at_least(0, timeout));185 EXPECT_TRUE(stub_primary_db.has_posted_at_least(0, timeout));
@@ -190,7 +193,7 @@
190 mt::fake_shared(stack),193 mt::fake_shared(stack),
191 mt::fake_shared(dbc_factory),194 mt::fake_shared(dbc_factory),
192 mt::fake_shared(stub_display_listener),195 mt::fake_shared(stub_display_listener),
193 null_comp_report, false);196 null_comp_report, default_delay, false);
194 mt_compositor.start();197 mt_compositor.start();
195198
196 stack.add_surface(stub_surface, default_params.depth, default_params.input_mode);199 stack.add_surface(stub_surface, default_params.depth, default_params.input_mode);
@@ -212,7 +215,7 @@
212 mt::fake_shared(stack),215 mt::fake_shared(stack),
213 mt::fake_shared(dbc_factory),216 mt::fake_shared(dbc_factory),
214 mt::fake_shared(stub_display_listener),217 mt::fake_shared(stub_display_listener),
215 null_comp_report, false);218 null_comp_report, default_delay, false);
216 mt_compositor.start();219 mt_compositor.start();
217220
218 stack.add_surface(stub_surface, default_params.depth, default_params.input_mode);221 stack.add_surface(stub_surface, default_params.depth, default_params.input_mode);
@@ -235,7 +238,7 @@
235 mt::fake_shared(stack),238 mt::fake_shared(stack),
236 mt::fake_shared(dbc_factory),239 mt::fake_shared(dbc_factory),
237 mt::fake_shared(stub_display_listener),240 mt::fake_shared(stub_display_listener),
238 null_comp_report, false);241 null_comp_report, default_delay, false);
239 mt_compositor.start();242 mt_compositor.start();
240243
241 stack.add_surface(stub_surface, default_params.depth, default_params.input_mode);244 stack.add_surface(stub_surface, default_params.depth, default_params.input_mode);
@@ -256,7 +259,7 @@
256 mt::fake_shared(stack),259 mt::fake_shared(stack),
257 mt::fake_shared(dbc_factory),260 mt::fake_shared(dbc_factory),
258 mt::fake_shared(stub_display_listener),261 mt::fake_shared(stub_display_listener),
259 null_comp_report, false);262 null_comp_report, default_delay, false);
260 mt_compositor.start();263 mt_compositor.start();
261264
262 stack.add_surface(stub_surface, default_params.depth, default_params.input_mode);265 stack.add_surface(stub_surface, default_params.depth, default_params.input_mode);
@@ -281,7 +284,7 @@
281 mt::fake_shared(stack),284 mt::fake_shared(stack),
282 mt::fake_shared(dbc_factory),285 mt::fake_shared(dbc_factory),
283 mt::fake_shared(stub_display_listener),286 mt::fake_shared(stub_display_listener),
284 null_comp_report, false);287 null_comp_report, default_delay, false);
285288
286 mt_compositor.start();289 mt_compositor.start();
287 stub_surface->move_to(geom::Point{1,1});290 stub_surface->move_to(geom::Point{1,1});
@@ -300,7 +303,7 @@
300 mt::fake_shared(stack),303 mt::fake_shared(stack),
301 mt::fake_shared(dbc_factory),304 mt::fake_shared(dbc_factory),
302 mt::fake_shared(stub_display_listener),305 mt::fake_shared(stub_display_listener),
303 null_comp_report, false);306 null_comp_report, default_delay, false);
304307
305 mt_compositor.start();308 mt_compositor.start();
306 stack.remove_surface(stub_surface);309 stack.remove_surface(stub_surface);
@@ -322,7 +325,7 @@
322 mt::fake_shared(stack),325 mt::fake_shared(stack),
323 mt::fake_shared(dbc_factory),326 mt::fake_shared(dbc_factory),
324 mt::fake_shared(stub_display_listener),327 mt::fake_shared(stub_display_listener),
325 null_comp_report, false);328 null_comp_report, default_delay, false);
326329
327 mt_compositor.start();330 mt_compositor.start();
328 stub_surface->primary_buffer_stream()->swap_buffers(&stubbuf, [](mg::Buffer*){});331 stub_surface->primary_buffer_stream()->swap_buffers(&stubbuf, [](mg::Buffer*){});
329332
=== modified file 'tests/mir_test_doubles/mock_drm.cpp'
--- tests/mir_test_doubles/mock_drm.cpp 2015-06-25 03:00:08 +0000
+++ tests/mir_test_doubles/mock_drm.cpp 2015-06-30 06:52:15 +0000
@@ -216,6 +216,11 @@
216 mode.clock = clock;216 mode.clock = clock;
217 mode.htotal = htotal;217 mode.htotal = htotal;
218 mode.vtotal = vtotal;218 mode.vtotal = vtotal;
219
220 uint32_t total = htotal;
221 total *= vtotal; // extend to 32 bits
222 mode.vrefresh = clock * 1000UL / total;
223
219 if (preferred)224 if (preferred)
220 mode.type |= DRM_MODE_TYPE_PREFERRED;225 mode.type |= DRM_MODE_TYPE_PREFERRED;
221226
222227
=== modified file 'tests/unit-tests/compositor/test_multi_threaded_compositor.cpp'
--- tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2015-06-25 03:00:08 +0000
+++ tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2015-06-30 06:52:15 +0000
@@ -83,6 +83,10 @@
83 f(buffer); 83 f(buffer);
84 }84 }
85 void post() override {}85 void post() override {}
86 std::chrono::milliseconds recommended_sleep() const override
87 {
88 return std::chrono::milliseconds::zero();
89 }
86 testing::NiceMock<mtd::MockDisplayBuffer> buffer; 90 testing::NiceMock<mtd::MockDisplayBuffer> buffer;
87 };91 };
8892
@@ -358,6 +362,7 @@
358auto const null_report = mr::null_compositor_report();362auto const null_report = mr::null_compositor_report();
359unsigned int const composites_per_update{1};363unsigned int const composites_per_update{1};
360auto const null_display_listener = std::make_shared<StubDisplayListener>();364auto const null_display_listener = std::make_shared<StubDisplayListener>();
365std::chrono::milliseconds const default_delay{-1};
361366
362}367}
363368
@@ -370,7 +375,7 @@
370 auto display = std::make_shared<mtd::StubDisplay>(nbuffers);375 auto display = std::make_shared<mtd::StubDisplay>(nbuffers);
371 auto scene = std::make_shared<StubScene>();376 auto scene = std::make_shared<StubScene>();
372 auto db_compositor_factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();377 auto db_compositor_factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
373 mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, true};378 mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, true};
374379
375 compositor.start();380 compositor.start();
376381
@@ -396,6 +401,7 @@
396 db_compositor_factory,401 db_compositor_factory,
397 null_display_listener,402 null_display_listener,
398 mock_report,403 mock_report,
404 default_delay,
399 true};405 true};
400406
401 EXPECT_CALL(*mock_report, started())407 EXPECT_CALL(*mock_report, started())
@@ -446,7 +452,7 @@
446 auto display = std::make_shared<mtd::StubDisplay>(nbuffers);452 auto display = std::make_shared<mtd::StubDisplay>(nbuffers);
447 auto scene = std::make_shared<StubScene>();453 auto scene = std::make_shared<StubScene>();
448 auto db_compositor_factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();454 auto db_compositor_factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
449 mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, true};455 mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, true};
450456
451 // Verify we're actually starting at zero frames457 // Verify we're actually starting at zero frames
452 EXPECT_TRUE(db_compositor_factory->check_record_count_for_each_buffer(nbuffers, 0, 0));458 EXPECT_TRUE(db_compositor_factory->check_record_count_for_each_buffer(nbuffers, 0, 0));
@@ -507,7 +513,7 @@
507 auto scene = std::make_shared<StubScene>();513 auto scene = std::make_shared<StubScene>();
508 auto factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();514 auto factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
509 mc::MultiThreadedCompositor compositor{display, scene, factory,515 mc::MultiThreadedCompositor compositor{display, scene, factory,
510 null_display_listener, null_report, true};516 null_display_listener, null_report, default_delay, true};
511517
512 EXPECT_TRUE(factory->check_record_count_for_each_buffer(nbuffers, 0, 0));518 EXPECT_TRUE(factory->check_record_count_for_each_buffer(nbuffers, 0, 0));
513519
@@ -570,6 +576,58 @@
570 compositor.stop();576 compositor.stop();
571}577}
572578
579TEST(MultiThreadedCompositor, recommended_sleep_throttles_compositor_loop)
580{
581 using namespace testing;
582 using namespace std::chrono;
583
584 unsigned int const nbuffers = 3;
585 milliseconds const recommendation(10);
586
587 auto display = std::make_shared<mtd::StubDisplay>(nbuffers);
588 auto scene = std::make_shared<StubScene>();
589 auto factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
590 mc::MultiThreadedCompositor compositor{display, scene, factory,
591 null_display_listener, null_report,
592 recommendation, false};
593
594 EXPECT_TRUE(factory->check_record_count_for_each_buffer(nbuffers, 0, 0));
595
596 compositor.start();
597
598 int const max_retries = 100;
599 int const nframes = 10;
600 auto start = system_clock::now();
601
602 for (int frame = 1; frame <= nframes; ++frame)
603 {
604 scene->emit_change_event();
605
606 int retry = 0;
607 while (retry < max_retries &&
608 !factory->check_record_count_for_each_buffer(nbuffers, frame))
609 {
610 std::this_thread::sleep_for(milliseconds(1));
611 ++retry;
612 }
613 ASSERT_LT(retry, max_retries);
614 }
615
616 /*
617 * Detecting the throttling from outside the compositor thread is actually
618 * trickier than you think. Because the display buffer counter won't be
619 * delayed by the sleep; only the subsequent frame will be delayed. So
620 * that's why we measure overall duration here...
621 */
622 auto duration = system_clock::now() - start;
623 // Minus 2 because the first won't be throttled, and the last not detected.
624 int minimum = recommendation.count() * (nframes - 2);
625 ASSERT_THAT(duration_cast<milliseconds>(duration).count(),
626 Ge(minimum));
627
628 compositor.stop();
629}
630
573TEST(MultiThreadedCompositor, when_no_initial_composite_is_needed_there_is_none)631TEST(MultiThreadedCompositor, when_no_initial_composite_is_needed_there_is_none)
574{632{
575 using namespace testing;633 using namespace testing;
@@ -579,7 +637,7 @@
579 auto display = std::make_shared<mtd::StubDisplay>(nbuffers);637 auto display = std::make_shared<mtd::StubDisplay>(nbuffers);
580 auto scene = std::make_shared<StubScene>();638 auto scene = std::make_shared<StubScene>();
581 auto db_compositor_factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();639 auto db_compositor_factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
582 mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, false};640 mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, false};
583641
584 // Verify we're actually starting at zero frames642 // Verify we're actually starting at zero frames
585 ASSERT_TRUE(db_compositor_factory->check_record_count_for_each_buffer(nbuffers, 0, 0));643 ASSERT_TRUE(db_compositor_factory->check_record_count_for_each_buffer(nbuffers, 0, 0));
@@ -602,7 +660,7 @@
602 auto display = std::make_shared<mtd::StubDisplay>(nbuffers);660 auto display = std::make_shared<mtd::StubDisplay>(nbuffers);
603 auto scene = std::make_shared<StubScene>();661 auto scene = std::make_shared<StubScene>();
604 auto db_compositor_factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();662 auto db_compositor_factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
605 mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, false};663 mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, false};
606664
607 compositor.start();665 compositor.start();
608666
@@ -637,7 +695,7 @@
637 auto display = std::make_shared<mtd::StubDisplay>(nbuffers);695 auto display = std::make_shared<mtd::StubDisplay>(nbuffers);
638 auto scene = std::make_shared<StubScene>();696 auto scene = std::make_shared<StubScene>();
639 auto db_compositor_factory = std::make_shared<SurfaceUpdatingDisplayBufferCompositorFactory>(scene);697 auto db_compositor_factory = std::make_shared<SurfaceUpdatingDisplayBufferCompositorFactory>(scene);
640 mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, true};698 mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, true};
641699
642 compositor.start();700 compositor.start();
643701
@@ -656,7 +714,7 @@
656 auto display = std::make_shared<StubDisplayWithMockBuffers>(nbuffers);714 auto display = std::make_shared<StubDisplayWithMockBuffers>(nbuffers);
657 auto scene = std::make_shared<StubScene>();715 auto scene = std::make_shared<StubScene>();
658 auto db_compositor_factory = std::make_shared<mtd::NullDisplayBufferCompositorFactory>();716 auto db_compositor_factory = std::make_shared<mtd::NullDisplayBufferCompositorFactory>();
659 mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, true};717 mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, true};
660718
661 display->for_each_mock_buffer([](mtd::MockDisplayBuffer& mock_buf)719 display->for_each_mock_buffer([](mtd::MockDisplayBuffer& mock_buf)
662 {720 {
@@ -692,7 +750,7 @@
692 .Times(AtLeast(0))750 .Times(AtLeast(0))
693 .WillRepeatedly(Return(mc::SceneElementSequence{}));751 .WillRepeatedly(Return(mc::SceneElementSequence{}));
694752
695 mc::MultiThreadedCompositor compositor{display, mock_scene, db_compositor_factory, null_display_listener, mock_report, true};753 mc::MultiThreadedCompositor compositor{display, mock_scene, db_compositor_factory, null_display_listener, mock_report, default_delay, true};
696754
697 compositor.start();755 compositor.start();
698 compositor.start();756 compositor.start();
@@ -707,7 +765,7 @@
707 auto display = std::make_shared<StubDisplayWithMockBuffers>(nbuffers);765 auto display = std::make_shared<StubDisplayWithMockBuffers>(nbuffers);
708 auto scene = std::make_shared<StubScene>();766 auto scene = std::make_shared<StubScene>();
709 auto db_compositor_factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();767 auto db_compositor_factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
710 mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, true};768 mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, true};
711769
712 scene->throw_on_add_observer(true);770 scene->throw_on_add_observer(true);
713771
@@ -757,7 +815,7 @@
757 auto display = std::make_shared<StubDisplayWithMockBuffers>(nbuffers);815 auto display = std::make_shared<StubDisplayWithMockBuffers>(nbuffers);
758 auto scene = std::make_shared<StubScene>();816 auto scene = std::make_shared<StubScene>();
759 auto db_compositor_factory = std::make_shared<ThreadNameDisplayBufferCompositorFactory>();817 auto db_compositor_factory = std::make_shared<ThreadNameDisplayBufferCompositorFactory>();
760 mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, true};818 mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, true};
761819
762 compositor.start();820 compositor.start();
763821
@@ -786,7 +844,7 @@
786 EXPECT_CALL(*mock_scene, register_compositor(_))844 EXPECT_CALL(*mock_scene, register_compositor(_))
787 .Times(nbuffers);845 .Times(nbuffers);
788 mc::MultiThreadedCompositor compositor{846 mc::MultiThreadedCompositor compositor{
789 display, mock_scene, db_compositor_factory, null_display_listener, mock_report, true};847 display, mock_scene, db_compositor_factory, null_display_listener, mock_report, default_delay, true};
790848
791 compositor.start();849 compositor.start();
792850
@@ -809,7 +867,7 @@
809 auto mock_report = std::make_shared<testing::NiceMock<mtd::MockCompositorReport>>();867 auto mock_report = std::make_shared<testing::NiceMock<mtd::MockCompositorReport>>();
810868
811 mc::MultiThreadedCompositor compositor{869 mc::MultiThreadedCompositor compositor{
812 display, stub_scene, db_compositor_factory, mock_display_listener, mock_report, true};870 display, stub_scene, db_compositor_factory, mock_display_listener, mock_report, default_delay, true};
813871
814 EXPECT_CALL(*mock_display_listener, add_display(_)).Times(nbuffers);872 EXPECT_CALL(*mock_display_listener, add_display(_)).Times(nbuffers);
815873
@@ -840,7 +898,7 @@
840 [&] { signal(SIGTERM, old_sigterm_handler); });898 [&] { signal(SIGTERM, old_sigterm_handler); });
841899
842 mc::MultiThreadedCompositor compositor{900 mc::MultiThreadedCompositor compositor{
843 display, stub_scene, db_compositor_factory, mock_display_listener, mock_report, true};901 display, stub_scene, db_compositor_factory, mock_display_listener, mock_report, default_delay, true};
844902
845 EXPECT_CALL(*mock_display_listener, add_display(_))903 EXPECT_CALL(*mock_display_listener, add_display(_))
846 .WillRepeatedly(Throw(std::runtime_error("Failed to add display")));904 .WillRepeatedly(Throw(std::runtime_error("Failed to add display")));
847905
=== modified file 'tests/unit-tests/graphics/android/test_fb_device.cpp'
--- tests/unit-tests/graphics/android/test_fb_device.cpp 2015-06-25 03:00:08 +0000
+++ tests/unit-tests/graphics/android/test_fb_device.cpp 2015-06-30 06:52:15 +0000
@@ -100,6 +100,9 @@
100 }, std::runtime_error);100 }, std::runtime_error);
101101
102 fbdev.commit({content});102 fbdev.commit({content});
103
104 // Predictive bypass not enabled in FBDevice
105 EXPECT_EQ(0, fbdev.recommended_sleep().count());
103}106}
104107
105//not all fb devices provide a swap interval hook. make sure we don't explode if thats the case108//not all fb devices provide a swap interval hook. make sure we don't explode if thats the case
106109
=== modified file 'tests/unit-tests/graphics/android/test_hwc_device.cpp'
--- tests/unit-tests/graphics/android/test_hwc_device.cpp 2015-06-25 03:00:08 +0000
+++ tests/unit-tests/graphics/android/test_hwc_device.cpp 2015-06-30 06:52:15 +0000
@@ -439,6 +439,49 @@
439 EXPECT_THAT(stub_buffer1.use_count(), Eq(use_count_before));439 EXPECT_THAT(stub_buffer1.use_count(), Eq(use_count_before));
440}440}
441441
442TEST_F(HwcDevice, overlays_are_throttled_per_predictive_bypass)
443{
444 using namespace testing;
445 EXPECT_CALL(*mock_device, prepare(_))
446 .WillRepeatedly(Invoke(set_all_layers_to_overlay));
447
448 mga::HwcDevice device(mock_device);
449
450 mga::LayerList list(layer_adapter, {stub_renderable1}, {0,0});
451 mga::DisplayContents content{primary, list, offset, stub_context,
452 stub_compositor};
453
454 for (int frame = 0; frame < 5; ++frame)
455 {
456 device.commit({content});
457 ASSERT_THAT(device.recommended_sleep().count(), Ge(8));
458 }
459}
460
461TEST_F(HwcDevice, compositing_disables_predictive_bypass)
462{
463 using namespace testing;
464
465 NiceMock<mtd::MockSwappingGLContext> mock_context;
466 ON_CALL(mock_context, last_rendered_buffer())
467 .WillByDefault(Return(stub_fb_buffer));
468 EXPECT_CALL(mock_context, swap_buffers())
469 .Times(AtLeast(5));
470
471 mga::LayerList list(layer_adapter, {}, geom::Displacement{});
472 mtd::MockRenderableListCompositor mock_compositor;
473 mga::DisplayContents content{primary, list, offset, mock_context,
474 mock_compositor};
475
476 mga::HwcDevice device(mock_device);
477 device.commit({content});
478 for (int frame = 0; frame < 5; ++frame)
479 {
480 device.commit({content});
481 ASSERT_EQ(0, device.recommended_sleep().count());
482 }
483}
484
442TEST_F(HwcDevice, does_not_set_acquirefences_when_it_has_set_them_previously_without_update)485TEST_F(HwcDevice, does_not_set_acquirefences_when_it_has_set_them_previously_without_update)
443{486{
444 using namespace testing;487 using namespace testing;
445488
=== modified file 'tests/unit-tests/graphics/android/test_hwc_fb_device.cpp'
--- tests/unit-tests/graphics/android/test_hwc_fb_device.cpp 2015-06-25 03:00:08 +0000
+++ tests/unit-tests/graphics/android/test_hwc_fb_device.cpp 2015-06-30 06:52:15 +0000
@@ -174,4 +174,7 @@
174174
175 mga::DisplayContents content{primary, list, geom::Displacement{}, mock_context, stub_compositor};175 mga::DisplayContents content{primary, list, geom::Displacement{}, mock_context, stub_compositor};
176 device.commit({content});176 device.commit({content});
177
178 // Predictive bypass not enabled in HwcFbDevice
179 EXPECT_EQ(0, device.recommended_sleep().count());
177}180}
178181
=== modified file 'tests/unit-tests/graphics/mesa/kms/mock_kms_output.h'
--- tests/unit-tests/graphics/mesa/kms/mock_kms_output.h 2015-06-17 05:20:42 +0000
+++ tests/unit-tests/graphics/mesa/kms/mock_kms_output.h 2015-06-30 06:52:15 +0000
@@ -32,6 +32,7 @@
32 MOCK_METHOD0(reset, void());32 MOCK_METHOD0(reset, void());
33 MOCK_METHOD2(configure, void(geometry::Displacement, size_t));33 MOCK_METHOD2(configure, void(geometry::Displacement, size_t));
34 MOCK_CONST_METHOD0(size, geometry::Size());34 MOCK_CONST_METHOD0(size, geometry::Size());
35 MOCK_CONST_METHOD0(max_refresh_rate, int());
3536
36 MOCK_METHOD1(set_crtc, bool(uint32_t));37 MOCK_METHOD1(set_crtc, bool(uint32_t));
37 MOCK_METHOD0(clear_crtc, void());38 MOCK_METHOD0(clear_crtc, void());
3839
=== modified file 'tests/unit-tests/graphics/mesa/kms/test_display_buffer.cpp'
--- tests/unit-tests/graphics/mesa/kms/test_display_buffer.cpp 2015-06-25 03:00:08 +0000
+++ tests/unit-tests/graphics/mesa/kms/test_display_buffer.cpp 2015-06-30 06:52:15 +0000
@@ -46,6 +46,8 @@
46class MesaDisplayBufferTest : public Test46class MesaDisplayBufferTest : public Test
47{47{
48public:48public:
49 int const mock_refresh_rate = 60;
50
49 MesaDisplayBufferTest()51 MesaDisplayBufferTest()
50 : mock_bypassable_buffer{std::make_shared<NiceMock<MockBuffer>>()}52 : mock_bypassable_buffer{std::make_shared<NiceMock<MockBuffer>>()}
51 , fake_bypassable_renderable{53 , fake_bypassable_renderable{
@@ -78,6 +80,8 @@
78 .WillByDefault(Return(true));80 .WillByDefault(Return(true));
79 ON_CALL(*mock_kms_output, schedule_page_flip(_))81 ON_CALL(*mock_kms_output, schedule_page_flip(_))
80 .WillByDefault(Return(true));82 .WillByDefault(Return(true));
83 ON_CALL(*mock_kms_output, max_refresh_rate())
84 .WillByDefault(Return(mock_refresh_rate));
8185
82 ON_CALL(*mock_bypassable_buffer, size())86 ON_CALL(*mock_bypassable_buffer, size())
83 .WillByDefault(Return(display_area.size));87 .WillByDefault(Return(display_area.size));
@@ -154,6 +158,56 @@
154 EXPECT_EQ(original_count, mock_bypassable_buffer.use_count());158 EXPECT_EQ(original_count, mock_bypassable_buffer.use_count());
155}159}
156160
161TEST_F(MesaDisplayBufferTest, predictive_bypass_is_throttled)
162{
163 graphics::mesa::DisplayBuffer db(
164 create_platform(),
165 null_display_report(),
166 {mock_kms_output},
167 nullptr,
168 display_area,
169 mir_orientation_normal,
170 gl_config,
171 mock_egl.fake_egl_context);
172
173 for (int frame = 0; frame < 5; ++frame)
174 {
175 ASSERT_TRUE(db.post_renderables_if_optimizable(bypassable_list));
176 db.post();
177
178 // Cast to a simple int type so that test failures are readable
179 int milliseconds_per_frame = 1000 / mock_refresh_rate;
180 ASSERT_THAT(db.recommended_sleep().count(),
181 Ge(milliseconds_per_frame/2));
182 }
183}
184
185TEST_F(MesaDisplayBufferTest, frames_requiring_gl_are_not_throttled)
186{
187 graphics::RenderableList non_bypassable_list{
188 std::make_shared<FakeRenderable>(geometry::Rectangle{{12, 34}, {1, 1}})
189 };
190
191 graphics::mesa::DisplayBuffer db(
192 create_platform(),
193 null_display_report(),
194 {mock_kms_output},
195 nullptr,
196 display_area,
197 mir_orientation_normal,
198 gl_config,
199 mock_egl.fake_egl_context);
200
201 for (int frame = 0; frame < 5; ++frame)
202 {
203 ASSERT_FALSE(db.post_renderables_if_optimizable(non_bypassable_list));
204 db.post();
205
206 // Cast to a simple int type so that test failures are readable
207 ASSERT_EQ(0, db.recommended_sleep().count());
208 }
209}
210
157TEST_F(MesaDisplayBufferTest, bypass_buffer_only_referenced_once_by_db)211TEST_F(MesaDisplayBufferTest, bypass_buffer_only_referenced_once_by_db)
158{212{
159 graphics::mesa::DisplayBuffer db(213 graphics::mesa::DisplayBuffer db(
160214
=== modified file 'tests/unit-tests/graphics/nested/test_nested_display.cpp'
--- tests/unit-tests/graphics/nested/test_nested_display.cpp 2015-06-25 13:43:43 +0000
+++ tests/unit-tests/graphics/nested/test_nested_display.cpp 2015-06-30 06:52:15 +0000
@@ -167,6 +167,23 @@
167 EXPECT_FALSE(weak_platform.expired());167 EXPECT_FALSE(weak_platform.expired());
168}168}
169169
170TEST_F(NestedDisplay, never_enables_predictive_bypass)
171{ // This test can be removed after it's implemented (after nested bypass)
172 auto const nested_display = create_nested_display(
173 null_platform,
174 mt::fake_shared(stub_gl_config));
175
176 int groups = 0;
177 nested_display->for_each_display_sync_group(
178 [&groups](mg::DisplaySyncGroup& g)
179 {
180 EXPECT_EQ(0, g.recommended_sleep().count());
181 ++groups;
182 }
183 );
184
185 ASSERT_NE(0, groups);
186}
170187
171TEST_F(NestedDisplay, makes_context_current_on_creation_and_releases_on_destruction)188TEST_F(NestedDisplay, makes_context_current_on_creation_and_releases_on_destruction)
172{189{
173190
=== modified file 'tests/unit-tests/graphics/offscreen/test_offscreen_display.cpp'
--- tests/unit-tests/graphics/offscreen/test_offscreen_display.cpp 2015-06-25 13:43:43 +0000
+++ tests/unit-tests/graphics/offscreen/test_offscreen_display.cpp 2015-06-30 06:52:15 +0000
@@ -78,6 +78,22 @@
78 EXPECT_TRUE(count);78 EXPECT_TRUE(count);
79}79}
8080
81TEST_F(OffscreenDisplayTest, never_enables_predictive_bypass)
82{
83 mgo::Display display{
84 native_display,
85 std::make_shared<mg::CloneDisplayConfigurationPolicy>(),
86 mr::null_display_report()};
87
88 int groups = 0;
89 display.for_each_display_sync_group([&](mg::DisplaySyncGroup& group){
90 ++groups;
91 EXPECT_EQ(0, group.recommended_sleep().count());
92 });
93
94 EXPECT_TRUE(groups);
95}
96
81TEST_F(OffscreenDisplayTest, makes_fbo_current_rendering_target)97TEST_F(OffscreenDisplayTest, makes_fbo_current_rendering_target)
82{98{
83 using namespace ::testing;99 using namespace ::testing;

Subscribers

People subscribed via source and target branches