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
1=== modified file 'benchmarks/frame-uniformity/vsync_simulating_graphics_platform.cpp'
2--- benchmarks/frame-uniformity/vsync_simulating_graphics_platform.cpp 2015-06-25 03:00:08 +0000
3+++ benchmarks/frame-uniformity/vsync_simulating_graphics_platform.cpp 2015-06-30 06:52:15 +0000
4@@ -60,6 +60,11 @@
5
6 last_sync = now;
7 }
8+
9+ std::chrono::milliseconds recommended_sleep() const override
10+ {
11+ return std::chrono::milliseconds::zero();
12+ }
13
14 double const vsync_rate_in_hz;
15
16
17=== modified file 'include/platform/mir/graphics/display.h'
18--- include/platform/mir/graphics/display.h 2015-06-17 05:20:42 +0000
19+++ include/platform/mir/graphics/display.h 2015-06-30 06:52:15 +0000
20@@ -21,6 +21,7 @@
21
22 #include <memory>
23 #include <functional>
24+#include <chrono>
25
26 namespace mir
27 {
28@@ -63,6 +64,16 @@
29 * in the near future. On some platforms, this may wait a potentially long time for vsync.
30 **/
31 virtual void post() = 0;
32+
33+ /**
34+ * Returns a recommendation to the compositor as to how long it should
35+ * wait before sampling the scene for the next frame. Sampling the
36+ * scene too early results in up to one whole frame of extra lag if
37+ * rendering is fast or skipped altogether (bypass/overlays). But sampling
38+ * too late and we might miss the deadline. If unsure just return zero.
39+ */
40+ virtual std::chrono::milliseconds recommended_sleep() const = 0;
41+
42 virtual ~DisplaySyncGroup() = default;
43 protected:
44 DisplaySyncGroup() = default;
45
46=== modified file 'include/test/mir/test/doubles/null_display_sync_group.h'
47--- include/test/mir/test/doubles/null_display_sync_group.h 2015-06-25 03:00:08 +0000
48+++ include/test/mir/test/doubles/null_display_sync_group.h 2015-06-30 06:52:15 +0000
49@@ -55,6 +55,11 @@
50 std::this_thread::yield();
51 }
52
53+ std::chrono::milliseconds recommended_sleep() const override
54+ {
55+ return std::chrono::milliseconds::zero();
56+ }
57+
58 private:
59 std::vector<geometry::Rectangle> const output_rects;
60 std::vector<StubDisplayBuffer> display_buffers;
61@@ -71,6 +76,12 @@
62 /* yield() is needed to ensure reasonable runtime under valgrind for some tests */
63 std::this_thread::yield();
64 }
65+
66+ std::chrono::milliseconds recommended_sleep() const override
67+ {
68+ return std::chrono::milliseconds::zero();
69+ }
70+
71 NullDisplayBuffer db;
72 };
73
74
75=== modified file 'src/include/platform/mir/options/configuration.h'
76--- src/include/platform/mir/options/configuration.h 2015-06-17 05:20:42 +0000
77+++ src/include/platform/mir/options/configuration.h 2015-06-30 06:52:15 +0000
78@@ -47,6 +47,7 @@
79 extern char const* const fatal_abort_opt;
80 extern char const* const debug_opt;
81 extern char const* const nbuffers_opt;
82+extern char const* const composite_delay_opt;
83 extern char const* const enable_key_repeat_opt;
84
85 extern char const* const name_opt;
86
87=== modified file 'src/platform/options/default_configuration.cpp'
88--- src/platform/options/default_configuration.cpp 2015-06-17 05:20:42 +0000
89+++ src/platform/options/default_configuration.cpp 2015-06-30 06:52:15 +0000
90@@ -49,6 +49,7 @@
91 char const* const mo::fatal_abort_opt = "on-fatal-error-abort";
92 char const* const mo::debug_opt = "debug";
93 char const* const mo::nbuffers_opt = "nbuffers";
94+char const* const mo::composite_delay_opt = "composite-delay";
95 char const* const mo::enable_key_repeat_opt = "enable-key-repeat";
96
97 char const* const mo::off_opt_value = "off";
98@@ -173,6 +174,11 @@
99 "threads in frontend thread pool.")
100 (nbuffers_opt, po::value<int>()->default_value(3),
101 "Number of buffers per surface.")
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 but risk causing frame skipping. "
106+ "Default: A negative value means decide automatically.")
107 (name_opt, po::value<std::string>(),
108 "When nested, the name Mir uses when registering with the host.")
109 (offscreen_opt,
110
111=== modified file 'src/platform/symbols.map'
112--- src/platform/symbols.map 2015-06-17 05:20:42 +0000
113+++ src/platform/symbols.map 2015-06-30 06:52:15 +0000
114@@ -276,3 +276,13 @@
115 };
116 local: *;
117 };
118+
119+# Why are server-only options here in libmirplatform?...
120+MIRPLATFORM_7.1 {
121+ global:
122+ extern "C++" {
123+ mir::options::composite_delay_opt*;
124+ };
125+ local: *;
126+} MIRPLATFORM_7;
127+
128
129=== modified file 'src/platforms/android/server/display_device.h'
130--- src/platforms/android/server/display_device.h 2015-06-17 05:20:42 +0000
131+++ src/platforms/android/server/display_device.h 2015-06-30 06:52:15 +0000
132@@ -25,6 +25,7 @@
133 #include "display_name.h"
134 #include <EGL/egl.h>
135 #include <list>
136+#include <chrono>
137
138 namespace mir
139 {
140@@ -66,6 +67,8 @@
141 //notify the DisplayDevice that the screen content was cleared in a way other than the above fns
142 virtual void content_cleared() = 0;
143
144+ virtual std::chrono::milliseconds recommended_sleep() const = 0;
145+
146 protected:
147 DisplayDevice() = default;
148 DisplayDevice& operator=(DisplayDevice const&) = delete;
149
150=== modified file 'src/platforms/android/server/display_group.cpp'
151--- src/platforms/android/server/display_group.cpp 2015-05-19 17:17:39 +0000
152+++ src/platforms/android/server/display_group.cpp 2015-06-30 06:52:15 +0000
153@@ -81,3 +81,8 @@
154 contents.emplace_back(db.second->contents());
155 device->commit(contents);
156 }
157+
158+std::chrono::milliseconds mga::DisplayGroup::recommended_sleep() const
159+{
160+ return device->recommended_sleep();
161+}
162
163=== modified file 'src/platforms/android/server/display_group.h'
164--- src/platforms/android/server/display_group.h 2015-05-19 17:16:12 +0000
165+++ src/platforms/android/server/display_group.h 2015-06-30 06:52:15 +0000
166@@ -44,6 +44,7 @@
167
168 void for_each_display_buffer(std::function<void(graphics::DisplayBuffer&)> const& f) override;
169 void post() override;
170+ std::chrono::milliseconds recommended_sleep() const override;
171
172 void add(DisplayName name, std::unique_ptr<ConfigurableDisplayBuffer> buffer);
173 void remove(DisplayName name);
174
175=== modified file 'src/platforms/android/server/fb_device.cpp'
176--- src/platforms/android/server/fb_device.cpp 2015-06-17 05:20:42 +0000
177+++ src/platforms/android/server/fb_device.cpp 2015-06-30 06:52:15 +0000
178@@ -115,3 +115,8 @@
179 void mga::FBDevice::content_cleared()
180 {
181 }
182+
183+std::chrono::milliseconds mga::FBDevice::recommended_sleep() const
184+{
185+ return std::chrono::milliseconds::zero();
186+}
187
188=== modified file 'src/platforms/android/server/fb_device.h'
189--- src/platforms/android/server/fb_device.h 2015-06-17 05:20:42 +0000
190+++ src/platforms/android/server/fb_device.h 2015-06-30 06:52:15 +0000
191@@ -51,6 +51,7 @@
192
193 bool compatible_renderlist(RenderableList const& renderlist) override;
194 void commit(std::list<DisplayContents> const& contents) override;
195+ std::chrono::milliseconds recommended_sleep() const override;
196
197 private:
198 std::shared_ptr<framebuffer_device_t> const fb_device;
199
200=== modified file 'src/platforms/android/server/hwc_device.cpp'
201--- src/platforms/android/server/hwc_device.cpp 2015-06-17 05:20:42 +0000
202+++ src/platforms/android/server/hwc_device.cpp 2015-06-30 06:52:15 +0000
203@@ -27,6 +27,8 @@
204 #include "mir/raii.h"
205 #include <limits>
206 #include <algorithm>
207+#include <chrono>
208+#include <thread>
209
210 namespace mg = mir::graphics;
211 namespace mga=mir::graphics::android;
212@@ -97,6 +99,8 @@
213
214 hwc_wrapper->prepare(lists);
215
216+ bool purely_overlays = true;
217+
218 for (auto& content : contents)
219 {
220 if (content.list.needs_swapbuffers())
221@@ -111,6 +115,7 @@
222 content.compositor.render(std::move(rejected_renderables), content.list_offset, content.context);
223 content.list.setup_fb(content.context.last_rendered_buffer());
224 content.list.swap_occurred();
225+ purely_overlays = false;
226 }
227
228 //setup overlays
229@@ -136,6 +141,20 @@
230
231 mir::Fd retire_fd(content.list.retirement_fence());
232 }
233+
234+ /*
235+ * Test results (how long can we sleep for without missing a frame?):
236+ * arale: 10ms (TODO: Find out why arale is so slow)
237+ * mako: 15ms
238+ * krillin: 11ms (to be fair, the display is 67Hz)
239+ */
240+ using namespace std;
241+ recommend_sleep = purely_overlays ? 10ms : 0ms;
242+}
243+
244+std::chrono::milliseconds mga::HwcDevice::recommended_sleep() const
245+{
246+ return recommend_sleep;
247 }
248
249 void mga::HwcDevice::content_cleared()
250
251=== modified file 'src/platforms/android/server/hwc_device.h'
252--- src/platforms/android/server/hwc_device.h 2015-04-28 07:54:10 +0000
253+++ src/platforms/android/server/hwc_device.h 2015-06-30 06:52:15 +0000
254@@ -46,6 +46,7 @@
255 bool compatible_renderlist(RenderableList const& renderlist) override;
256 void commit(std::list<DisplayContents> const& contents) override;
257 void content_cleared() override;
258+ std::chrono::milliseconds recommended_sleep() const override;
259
260 private:
261 bool buffer_is_onscreen(Buffer const&) const;
262@@ -53,6 +54,7 @@
263
264 std::shared_ptr<HwcWrapper> const hwc_wrapper;
265 std::shared_ptr<SyncFileOps> const sync_ops;
266+ std::chrono::milliseconds recommend_sleep{0};
267 };
268
269 }
270
271=== modified file 'src/platforms/android/server/hwc_fb_device.cpp'
272--- src/platforms/android/server/hwc_fb_device.cpp 2015-06-17 05:20:42 +0000
273+++ src/platforms/android/server/hwc_fb_device.cpp 2015-06-30 06:52:15 +0000
274@@ -113,3 +113,8 @@
275 void mga::HwcFbDevice::content_cleared()
276 {
277 }
278+
279+std::chrono::milliseconds mga::HwcFbDevice::recommended_sleep() const
280+{
281+ return std::chrono::milliseconds::zero();
282+}
283
284=== modified file 'src/platforms/android/server/hwc_fb_device.h'
285--- src/platforms/android/server/hwc_fb_device.h 2015-04-28 07:54:10 +0000
286+++ src/platforms/android/server/hwc_fb_device.h 2015-06-30 06:52:15 +0000
287@@ -43,6 +43,7 @@
288
289 bool compatible_renderlist(RenderableList const& renderlist) override;
290 void commit(std::list<DisplayContents> const& contents) override;
291+ std::chrono::milliseconds recommended_sleep() const override;
292
293 private:
294 void content_cleared() override;
295
296=== modified file 'src/platforms/mesa/server/kms/display_buffer.cpp'
297--- src/platforms/mesa/server/kms/display_buffer.cpp 2015-06-17 05:20:42 +0000
298+++ src/platforms/mesa/server/kms/display_buffer.cpp 2015-06-30 06:52:15 +0000
299@@ -28,6 +28,8 @@
300 #include <GLES2/gl2.h>
301
302 #include <stdexcept>
303+#include <chrono>
304+#include <thread>
305
306 namespace mgm = mir::graphics::mesa;
307 namespace geom = mir::geometry;
308@@ -282,6 +284,11 @@
309 needs_set_crtc = false;
310 }
311
312+ using namespace std; // For operator""ms()
313+
314+ // Predicted worst case render time for the next frame...
315+ auto predicted_render_time = 50ms;
316+
317 if (bypass_buf)
318 {
319 /*
320@@ -295,6 +302,10 @@
321 */
322 scheduled_bypass_frame = bypass_buf;
323 wait_for_page_flip();
324+
325+ // It's very likely the next frame will be bypassed like this one so
326+ // we only need time for kernel page flip scheduling...
327+ predicted_render_time = 5ms;
328 }
329 else
330 {
331@@ -306,11 +317,33 @@
332 scheduled_composite_frame = bufobj;
333 if (outputs.size() == 1)
334 wait_for_page_flip();
335+
336+ /*
337+ * TODO: If you're optimistic about your GPU performance and/or
338+ * measure it carefully you may wish to set predicted_render_time
339+ * to a lower value here for lower latency.
340+ *
341+ *predicted_render_time = 9ms; // e.g. about the same as Weston
342+ */
343 }
344
345 // Buffer lifetimes are managed exclusively by scheduled*/visible* now
346 bypass_buf = nullptr;
347 bypass_bufobj = nullptr;
348+
349+ recommend_sleep = 0ms;
350+ if (outputs.size() == 1)
351+ {
352+ auto const& output = outputs.front();
353+ auto const min_frame_interval = 1000ms / output->max_refresh_rate();
354+ if (predicted_render_time < min_frame_interval)
355+ recommend_sleep = min_frame_interval - predicted_render_time;
356+ }
357+}
358+
359+std::chrono::milliseconds mgm::DisplayBuffer::recommended_sleep() const
360+{
361+ return recommend_sleep;
362 }
363
364 mgm::BufferObject* mgm::DisplayBuffer::get_front_buffer_object()
365
366=== modified file 'src/platforms/mesa/server/kms/display_buffer.h'
367--- src/platforms/mesa/server/kms/display_buffer.h 2015-06-17 05:20:42 +0000
368+++ src/platforms/mesa/server/kms/display_buffer.h 2015-06-30 06:52:15 +0000
369@@ -65,6 +65,7 @@
370 void for_each_display_buffer(
371 std::function<void(graphics::DisplayBuffer&)> const& f) override;
372 void post() override;
373+ std::chrono::milliseconds recommended_sleep() const override;
374
375 MirOrientation orientation() const override;
376 void set_orientation(MirOrientation const rot, geometry::Rectangle const& a);
377@@ -93,6 +94,7 @@
378 uint32_t fb_width, fb_height;
379 MirOrientation rotation;
380 std::atomic<bool> needs_set_crtc;
381+ std::chrono::milliseconds recommend_sleep{0};
382 bool page_flips_pending;
383 };
384
385
386=== modified file 'src/platforms/mesa/server/kms/kms_output.h'
387--- src/platforms/mesa/server/kms/kms_output.h 2015-06-17 05:20:42 +0000
388+++ src/platforms/mesa/server/kms/kms_output.h 2015-06-30 06:52:15 +0000
389@@ -43,6 +43,14 @@
390 virtual void configure(geometry::Displacement fb_offset, size_t kms_mode_index) = 0;
391 virtual geometry::Size size() const = 0;
392
393+ /**
394+ * Approximate maximum refresh rate of this output to within 1Hz.
395+ * Typically the rate is fixed (e.g. 60Hz) but it may also be variable as
396+ * in Nvidia G-Sync/AMD FreeSync/VESA Adaptive Sync. So this function
397+ * returns the maximum rate to expect.
398+ */
399+ virtual int max_refresh_rate() const = 0;
400+
401 virtual bool set_crtc(uint32_t fb_id) = 0;
402 virtual void clear_crtc() = 0;
403 virtual bool schedule_page_flip(uint32_t fb_id) = 0;
404
405=== modified file 'src/platforms/mesa/server/kms/real_kms_output.cpp'
406--- src/platforms/mesa/server/kms/real_kms_output.cpp 2015-06-17 05:20:42 +0000
407+++ src/platforms/mesa/server/kms/real_kms_output.cpp 2015-06-30 06:52:15 +0000
408@@ -188,6 +188,14 @@
409 return {mode.hdisplay, mode.vdisplay};
410 }
411
412+int mgm::RealKMSOutput::max_refresh_rate() const
413+{
414+ // TODO: In future when DRM exposes FreeSync/Adaptive Sync/G-Sync info
415+ // this value may be calculated differently.
416+ drmModeModeInfo const& current_mode = connector->modes[mode_index];
417+ return current_mode.vrefresh;
418+}
419+
420 void mgm::RealKMSOutput::configure(geom::Displacement offset, size_t kms_mode_index)
421 {
422 fb_offset = offset;
423
424=== modified file 'src/platforms/mesa/server/kms/real_kms_output.h'
425--- src/platforms/mesa/server/kms/real_kms_output.h 2015-06-17 05:20:42 +0000
426+++ src/platforms/mesa/server/kms/real_kms_output.h 2015-06-30 06:52:15 +0000
427@@ -44,6 +44,7 @@
428 void reset();
429 void configure(geometry::Displacement fb_offset, size_t kms_mode_index);
430 geometry::Size size() const;
431+ int max_refresh_rate() const;
432
433 bool set_crtc(uint32_t fb_id);
434 void clear_crtc();
435
436=== modified file 'src/server/compositor/default_configuration.cpp'
437--- src/server/compositor/default_configuration.cpp 2015-06-17 05:20:42 +0000
438+++ src/server/compositor/default_configuration.cpp 2015-06-30 06:52:15 +0000
439@@ -84,12 +84,16 @@
440 return compositor(
441 [this]()
442 {
443+ std::chrono::milliseconds const composite_delay(
444+ the_options()->get<int>(options::composite_delay_opt));
445+
446 return std::make_shared<mc::MultiThreadedCompositor>(
447 the_display(),
448 the_scene(),
449 the_display_buffer_compositor_factory(),
450 the_shell(),
451 the_compositor_report(),
452+ composite_delay,
453 !the_options()->is_set(options::host_socket_opt));
454 });
455 }
456
457=== modified file 'src/server/compositor/multi_threaded_compositor.cpp'
458--- src/server/compositor/multi_threaded_compositor.cpp 2015-06-18 14:33:10 +0000
459+++ src/server/compositor/multi_threaded_compositor.cpp 2015-06-30 06:52:15 +0000
460@@ -101,12 +101,14 @@
461 mg::DisplaySyncGroup& group,
462 std::shared_ptr<mc::Scene> const& scene,
463 std::shared_ptr<DisplayListener> const& display_listener,
464+ std::chrono::milliseconds fixed_composite_delay,
465 std::shared_ptr<CompositorReport> const& report) :
466 compositor_factory{db_compositor_factory},
467 group(group),
468 scene(scene),
469 running{true},
470 frames_scheduled{0},
471+ force_sleep{fixed_composite_delay},
472 display_listener{display_listener},
473 report{report},
474 started_future{started.get_future()}
475@@ -182,6 +184,17 @@
476 }
477 group.post();
478
479+ /*
480+ * "Predictive bypass" optimization: If the last frame was
481+ * bypassed/overlayed or you simply have a fast GPU, it is
482+ * beneficial to sleep for most of the next frame. This reduces
483+ * the latency between snapshotting the scene and post()
484+ * completing by almost a whole frame.
485+ */
486+ auto delay = force_sleep >= std::chrono::milliseconds::zero() ?
487+ force_sleep : group.recommended_sleep();
488+ std::this_thread::sleep_for(delay);
489+
490 lock.lock();
491
492 /*
493@@ -231,6 +244,7 @@
494 std::shared_ptr<mc::Scene> const scene;
495 bool running;
496 int frames_scheduled;
497+ std::chrono::milliseconds force_sleep{-1};
498 std::mutex run_mutex;
499 std::condition_variable run_cv;
500 std::shared_ptr<DisplayListener> const display_listener;
501@@ -248,6 +262,7 @@
502 std::shared_ptr<DisplayBufferCompositorFactory> const& db_compositor_factory,
503 std::shared_ptr<DisplayListener> const& display_listener,
504 std::shared_ptr<CompositorReport> const& compositor_report,
505+ std::chrono::milliseconds fixed_composite_delay,
506 bool compose_on_start)
507 : display{display},
508 scene{scene},
509@@ -255,6 +270,7 @@
510 display_listener{display_listener},
511 report{compositor_report},
512 state{CompositorState::stopped},
513+ fixed_composite_delay{fixed_composite_delay},
514 compose_on_start{compose_on_start},
515 thread_pool{1}
516 {
517@@ -348,7 +364,8 @@
518 display->for_each_display_sync_group([this](mg::DisplaySyncGroup& group)
519 {
520 auto thread_functor = std::make_unique<mc::CompositingFunctor>(
521- display_buffer_compositor_factory, group, scene, display_listener, report);
522+ display_buffer_compositor_factory, group, scene, display_listener,
523+ fixed_composite_delay, report);
524
525 futures.push_back(thread_pool.run(std::ref(*thread_functor), &group));
526 thread_functors.push_back(std::move(thread_functor));
527
528=== modified file 'src/server/compositor/multi_threaded_compositor.h'
529--- src/server/compositor/multi_threaded_compositor.h 2015-06-17 05:20:42 +0000
530+++ src/server/compositor/multi_threaded_compositor.h 2015-06-30 06:52:15 +0000
531@@ -65,6 +65,7 @@
532 std::shared_ptr<DisplayBufferCompositorFactory> const& db_compositor_factory,
533 std::shared_ptr<DisplayListener> const& display_listener,
534 std::shared_ptr<CompositorReport> const& compositor_report,
535+ std::chrono::milliseconds fixed_composite_delay, // -1 = automatic
536 bool compose_on_start);
537 ~MultiThreadedCompositor();
538
539@@ -86,6 +87,7 @@
540
541 std::mutex state_guard;
542 CompositorState state;
543+ std::chrono::milliseconds fixed_composite_delay;
544 bool compose_on_start;
545
546 void schedule_compositing(int number_composites);
547
548=== modified file 'src/server/graphics/nested/display.cpp'
549--- src/server/graphics/nested/display.cpp 2015-06-17 05:20:42 +0000
550+++ src/server/graphics/nested/display.cpp 2015-06-30 06:52:15 +0000
551@@ -137,6 +137,14 @@
552 {
553 }
554
555+std::chrono::milliseconds
556+mgn::detail::DisplaySyncGroup::recommended_sleep() const
557+{
558+ // TODO: Might make sense in future with nested bypass. We could save
559+ // almost another frame of lag!
560+ return std::chrono::milliseconds::zero();
561+}
562+
563 mgn::Display::Display(
564 std::shared_ptr<mg::Platform> const& platform,
565 std::shared_ptr<HostConnection> const& connection,
566
567=== modified file 'src/server/graphics/nested/display.h'
568--- src/server/graphics/nested/display.h 2015-06-17 05:20:42 +0000
569+++ src/server/graphics/nested/display.h 2015-06-30 06:52:15 +0000
570@@ -97,6 +97,7 @@
571 DisplaySyncGroup(std::shared_ptr<detail::DisplayBuffer> const& output);
572 void for_each_display_buffer(std::function<void(graphics::DisplayBuffer&)> const&) override;
573 void post() override;
574+ std::chrono::milliseconds recommended_sleep() const override;
575 private:
576 std::shared_ptr<detail::DisplayBuffer> const output;
577 };
578
579=== modified file 'src/server/graphics/offscreen/display.cpp'
580--- src/server/graphics/offscreen/display.cpp 2015-06-17 05:20:42 +0000
581+++ src/server/graphics/offscreen/display.cpp 2015-06-30 06:52:15 +0000
582@@ -87,6 +87,12 @@
583 {
584 }
585
586+std::chrono::milliseconds
587+mgo::detail::DisplaySyncGroup::recommended_sleep() const
588+{
589+ return std::chrono::milliseconds::zero();
590+}
591+
592 mgo::Display::Display(
593 EGLNativeDisplayType egl_native_display,
594 std::shared_ptr<DisplayConfigurationPolicy> const& initial_conf_policy,
595
596=== modified file 'src/server/graphics/offscreen/display.h'
597--- src/server/graphics/offscreen/display.h 2015-06-17 05:20:42 +0000
598+++ src/server/graphics/offscreen/display.h 2015-06-30 06:52:15 +0000
599@@ -64,6 +64,7 @@
600 DisplaySyncGroup(std::unique_ptr<DisplayBuffer> output);
601 void for_each_display_buffer(std::function<void(DisplayBuffer&)> const&) override;
602 void post() override;
603+ std::chrono::milliseconds recommended_sleep() const override;
604 private:
605 std::unique_ptr<DisplayBuffer> const output;
606 };
607
608=== modified file 'tests/include/mir/test/doubles/mock_display_device.h'
609--- tests/include/mir/test/doubles/mock_display_device.h 2015-04-28 07:54:10 +0000
610+++ tests/include/mir/test/doubles/mock_display_device.h 2015-06-30 06:52:15 +0000
611@@ -40,6 +40,7 @@
612 MOCK_METHOD1(commit, void(std::list<graphics::android::DisplayContents> const&));
613 MOCK_METHOD1(compatible_renderlist, bool(
614 graphics::RenderableList const&));
615+ MOCK_CONST_METHOD0(recommended_sleep, std::chrono::milliseconds());
616 };
617 }
618 }
619
620=== modified file 'tests/integration-tests/test_surface_stack_with_compositor.cpp'
621--- tests/integration-tests/test_surface_stack_with_compositor.cpp 2015-06-25 03:00:08 +0000
622+++ tests/integration-tests/test_surface_stack_with_compositor.cpp 2015-06-30 06:52:15 +0000
623@@ -153,6 +153,9 @@
624 mt::fake_shared(renderer_factory),
625 null_comp_report};
626 };
627+
628+std::chrono::milliseconds const default_delay{-1};
629+
630 }
631
632 TEST_F(SurfaceStackCompositor, composes_on_start_if_told_to_in_constructor)
633@@ -162,7 +165,7 @@
634 mt::fake_shared(stack),
635 mt::fake_shared(dbc_factory),
636 mt::fake_shared(stub_display_listener),
637- null_comp_report, true);
638+ null_comp_report, default_delay, true);
639 mt_compositor.start();
640
641 EXPECT_TRUE(stub_primary_db.has_posted_at_least(1, timeout));
642@@ -176,7 +179,7 @@
643 mt::fake_shared(stack),
644 mt::fake_shared(dbc_factory),
645 mt::fake_shared(stub_display_listener),
646- null_comp_report, false);
647+ null_comp_report, default_delay, false);
648 mt_compositor.start();
649
650 EXPECT_TRUE(stub_primary_db.has_posted_at_least(0, timeout));
651@@ -190,7 +193,7 @@
652 mt::fake_shared(stack),
653 mt::fake_shared(dbc_factory),
654 mt::fake_shared(stub_display_listener),
655- null_comp_report, false);
656+ null_comp_report, default_delay, false);
657 mt_compositor.start();
658
659 stack.add_surface(stub_surface, default_params.depth, default_params.input_mode);
660@@ -212,7 +215,7 @@
661 mt::fake_shared(stack),
662 mt::fake_shared(dbc_factory),
663 mt::fake_shared(stub_display_listener),
664- null_comp_report, false);
665+ null_comp_report, default_delay, false);
666 mt_compositor.start();
667
668 stack.add_surface(stub_surface, default_params.depth, default_params.input_mode);
669@@ -235,7 +238,7 @@
670 mt::fake_shared(stack),
671 mt::fake_shared(dbc_factory),
672 mt::fake_shared(stub_display_listener),
673- null_comp_report, false);
674+ null_comp_report, default_delay, false);
675 mt_compositor.start();
676
677 stack.add_surface(stub_surface, default_params.depth, default_params.input_mode);
678@@ -256,7 +259,7 @@
679 mt::fake_shared(stack),
680 mt::fake_shared(dbc_factory),
681 mt::fake_shared(stub_display_listener),
682- null_comp_report, false);
683+ null_comp_report, default_delay, false);
684 mt_compositor.start();
685
686 stack.add_surface(stub_surface, default_params.depth, default_params.input_mode);
687@@ -281,7 +284,7 @@
688 mt::fake_shared(stack),
689 mt::fake_shared(dbc_factory),
690 mt::fake_shared(stub_display_listener),
691- null_comp_report, false);
692+ null_comp_report, default_delay, false);
693
694 mt_compositor.start();
695 stub_surface->move_to(geom::Point{1,1});
696@@ -300,7 +303,7 @@
697 mt::fake_shared(stack),
698 mt::fake_shared(dbc_factory),
699 mt::fake_shared(stub_display_listener),
700- null_comp_report, false);
701+ null_comp_report, default_delay, false);
702
703 mt_compositor.start();
704 stack.remove_surface(stub_surface);
705@@ -322,7 +325,7 @@
706 mt::fake_shared(stack),
707 mt::fake_shared(dbc_factory),
708 mt::fake_shared(stub_display_listener),
709- null_comp_report, false);
710+ null_comp_report, default_delay, false);
711
712 mt_compositor.start();
713 stub_surface->primary_buffer_stream()->swap_buffers(&stubbuf, [](mg::Buffer*){});
714
715=== modified file 'tests/mir_test_doubles/mock_drm.cpp'
716--- tests/mir_test_doubles/mock_drm.cpp 2015-06-25 03:00:08 +0000
717+++ tests/mir_test_doubles/mock_drm.cpp 2015-06-30 06:52:15 +0000
718@@ -216,6 +216,11 @@
719 mode.clock = clock;
720 mode.htotal = htotal;
721 mode.vtotal = vtotal;
722+
723+ uint32_t total = htotal;
724+ total *= vtotal; // extend to 32 bits
725+ mode.vrefresh = clock * 1000UL / total;
726+
727 if (preferred)
728 mode.type |= DRM_MODE_TYPE_PREFERRED;
729
730
731=== modified file 'tests/unit-tests/compositor/test_multi_threaded_compositor.cpp'
732--- tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2015-06-25 03:00:08 +0000
733+++ tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2015-06-30 06:52:15 +0000
734@@ -83,6 +83,10 @@
735 f(buffer);
736 }
737 void post() override {}
738+ std::chrono::milliseconds recommended_sleep() const override
739+ {
740+ return std::chrono::milliseconds::zero();
741+ }
742 testing::NiceMock<mtd::MockDisplayBuffer> buffer;
743 };
744
745@@ -358,6 +362,7 @@
746 auto const null_report = mr::null_compositor_report();
747 unsigned int const composites_per_update{1};
748 auto const null_display_listener = std::make_shared<StubDisplayListener>();
749+std::chrono::milliseconds const default_delay{-1};
750
751 }
752
753@@ -370,7 +375,7 @@
754 auto display = std::make_shared<mtd::StubDisplay>(nbuffers);
755 auto scene = std::make_shared<StubScene>();
756 auto db_compositor_factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
757- mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, true};
758+ mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, true};
759
760 compositor.start();
761
762@@ -396,6 +401,7 @@
763 db_compositor_factory,
764 null_display_listener,
765 mock_report,
766+ default_delay,
767 true};
768
769 EXPECT_CALL(*mock_report, started())
770@@ -446,7 +452,7 @@
771 auto display = std::make_shared<mtd::StubDisplay>(nbuffers);
772 auto scene = std::make_shared<StubScene>();
773 auto db_compositor_factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
774- mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, true};
775+ mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, true};
776
777 // Verify we're actually starting at zero frames
778 EXPECT_TRUE(db_compositor_factory->check_record_count_for_each_buffer(nbuffers, 0, 0));
779@@ -507,7 +513,7 @@
780 auto scene = std::make_shared<StubScene>();
781 auto factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
782 mc::MultiThreadedCompositor compositor{display, scene, factory,
783- null_display_listener, null_report, true};
784+ null_display_listener, null_report, default_delay, true};
785
786 EXPECT_TRUE(factory->check_record_count_for_each_buffer(nbuffers, 0, 0));
787
788@@ -570,6 +576,58 @@
789 compositor.stop();
790 }
791
792+TEST(MultiThreadedCompositor, recommended_sleep_throttles_compositor_loop)
793+{
794+ using namespace testing;
795+ using namespace std::chrono;
796+
797+ unsigned int const nbuffers = 3;
798+ milliseconds const recommendation(10);
799+
800+ auto display = std::make_shared<mtd::StubDisplay>(nbuffers);
801+ auto scene = std::make_shared<StubScene>();
802+ auto factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
803+ mc::MultiThreadedCompositor compositor{display, scene, factory,
804+ null_display_listener, null_report,
805+ recommendation, false};
806+
807+ EXPECT_TRUE(factory->check_record_count_for_each_buffer(nbuffers, 0, 0));
808+
809+ compositor.start();
810+
811+ int const max_retries = 100;
812+ int const nframes = 10;
813+ auto start = system_clock::now();
814+
815+ for (int frame = 1; frame <= nframes; ++frame)
816+ {
817+ scene->emit_change_event();
818+
819+ int retry = 0;
820+ while (retry < max_retries &&
821+ !factory->check_record_count_for_each_buffer(nbuffers, frame))
822+ {
823+ std::this_thread::sleep_for(milliseconds(1));
824+ ++retry;
825+ }
826+ ASSERT_LT(retry, max_retries);
827+ }
828+
829+ /*
830+ * Detecting the throttling from outside the compositor thread is actually
831+ * trickier than you think. Because the display buffer counter won't be
832+ * delayed by the sleep; only the subsequent frame will be delayed. So
833+ * that's why we measure overall duration here...
834+ */
835+ auto duration = system_clock::now() - start;
836+ // Minus 2 because the first won't be throttled, and the last not detected.
837+ int minimum = recommendation.count() * (nframes - 2);
838+ ASSERT_THAT(duration_cast<milliseconds>(duration).count(),
839+ Ge(minimum));
840+
841+ compositor.stop();
842+}
843+
844 TEST(MultiThreadedCompositor, when_no_initial_composite_is_needed_there_is_none)
845 {
846 using namespace testing;
847@@ -579,7 +637,7 @@
848 auto display = std::make_shared<mtd::StubDisplay>(nbuffers);
849 auto scene = std::make_shared<StubScene>();
850 auto db_compositor_factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
851- mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, false};
852+ mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, false};
853
854 // Verify we're actually starting at zero frames
855 ASSERT_TRUE(db_compositor_factory->check_record_count_for_each_buffer(nbuffers, 0, 0));
856@@ -602,7 +660,7 @@
857 auto display = std::make_shared<mtd::StubDisplay>(nbuffers);
858 auto scene = std::make_shared<StubScene>();
859 auto db_compositor_factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
860- mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, false};
861+ mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, false};
862
863 compositor.start();
864
865@@ -637,7 +695,7 @@
866 auto display = std::make_shared<mtd::StubDisplay>(nbuffers);
867 auto scene = std::make_shared<StubScene>();
868 auto db_compositor_factory = std::make_shared<SurfaceUpdatingDisplayBufferCompositorFactory>(scene);
869- mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, true};
870+ mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, true};
871
872 compositor.start();
873
874@@ -656,7 +714,7 @@
875 auto display = std::make_shared<StubDisplayWithMockBuffers>(nbuffers);
876 auto scene = std::make_shared<StubScene>();
877 auto db_compositor_factory = std::make_shared<mtd::NullDisplayBufferCompositorFactory>();
878- mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, true};
879+ mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, true};
880
881 display->for_each_mock_buffer([](mtd::MockDisplayBuffer& mock_buf)
882 {
883@@ -692,7 +750,7 @@
884 .Times(AtLeast(0))
885 .WillRepeatedly(Return(mc::SceneElementSequence{}));
886
887- mc::MultiThreadedCompositor compositor{display, mock_scene, db_compositor_factory, null_display_listener, mock_report, true};
888+ mc::MultiThreadedCompositor compositor{display, mock_scene, db_compositor_factory, null_display_listener, mock_report, default_delay, true};
889
890 compositor.start();
891 compositor.start();
892@@ -707,7 +765,7 @@
893 auto display = std::make_shared<StubDisplayWithMockBuffers>(nbuffers);
894 auto scene = std::make_shared<StubScene>();
895 auto db_compositor_factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
896- mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, true};
897+ mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, true};
898
899 scene->throw_on_add_observer(true);
900
901@@ -757,7 +815,7 @@
902 auto display = std::make_shared<StubDisplayWithMockBuffers>(nbuffers);
903 auto scene = std::make_shared<StubScene>();
904 auto db_compositor_factory = std::make_shared<ThreadNameDisplayBufferCompositorFactory>();
905- mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, true};
906+ mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, true};
907
908 compositor.start();
909
910@@ -786,7 +844,7 @@
911 EXPECT_CALL(*mock_scene, register_compositor(_))
912 .Times(nbuffers);
913 mc::MultiThreadedCompositor compositor{
914- display, mock_scene, db_compositor_factory, null_display_listener, mock_report, true};
915+ display, mock_scene, db_compositor_factory, null_display_listener, mock_report, default_delay, true};
916
917 compositor.start();
918
919@@ -809,7 +867,7 @@
920 auto mock_report = std::make_shared<testing::NiceMock<mtd::MockCompositorReport>>();
921
922 mc::MultiThreadedCompositor compositor{
923- display, stub_scene, db_compositor_factory, mock_display_listener, mock_report, true};
924+ display, stub_scene, db_compositor_factory, mock_display_listener, mock_report, default_delay, true};
925
926 EXPECT_CALL(*mock_display_listener, add_display(_)).Times(nbuffers);
927
928@@ -840,7 +898,7 @@
929 [&] { signal(SIGTERM, old_sigterm_handler); });
930
931 mc::MultiThreadedCompositor compositor{
932- display, stub_scene, db_compositor_factory, mock_display_listener, mock_report, true};
933+ display, stub_scene, db_compositor_factory, mock_display_listener, mock_report, default_delay, true};
934
935 EXPECT_CALL(*mock_display_listener, add_display(_))
936 .WillRepeatedly(Throw(std::runtime_error("Failed to add display")));
937
938=== modified file 'tests/unit-tests/graphics/android/test_fb_device.cpp'
939--- tests/unit-tests/graphics/android/test_fb_device.cpp 2015-06-25 03:00:08 +0000
940+++ tests/unit-tests/graphics/android/test_fb_device.cpp 2015-06-30 06:52:15 +0000
941@@ -100,6 +100,9 @@
942 }, std::runtime_error);
943
944 fbdev.commit({content});
945+
946+ // Predictive bypass not enabled in FBDevice
947+ EXPECT_EQ(0, fbdev.recommended_sleep().count());
948 }
949
950 //not all fb devices provide a swap interval hook. make sure we don't explode if thats the case
951
952=== modified file 'tests/unit-tests/graphics/android/test_hwc_device.cpp'
953--- tests/unit-tests/graphics/android/test_hwc_device.cpp 2015-06-25 03:00:08 +0000
954+++ tests/unit-tests/graphics/android/test_hwc_device.cpp 2015-06-30 06:52:15 +0000
955@@ -439,6 +439,49 @@
956 EXPECT_THAT(stub_buffer1.use_count(), Eq(use_count_before));
957 }
958
959+TEST_F(HwcDevice, overlays_are_throttled_per_predictive_bypass)
960+{
961+ using namespace testing;
962+ EXPECT_CALL(*mock_device, prepare(_))
963+ .WillRepeatedly(Invoke(set_all_layers_to_overlay));
964+
965+ mga::HwcDevice device(mock_device);
966+
967+ mga::LayerList list(layer_adapter, {stub_renderable1}, {0,0});
968+ mga::DisplayContents content{primary, list, offset, stub_context,
969+ stub_compositor};
970+
971+ for (int frame = 0; frame < 5; ++frame)
972+ {
973+ device.commit({content});
974+ ASSERT_THAT(device.recommended_sleep().count(), Ge(8));
975+ }
976+}
977+
978+TEST_F(HwcDevice, compositing_disables_predictive_bypass)
979+{
980+ using namespace testing;
981+
982+ NiceMock<mtd::MockSwappingGLContext> mock_context;
983+ ON_CALL(mock_context, last_rendered_buffer())
984+ .WillByDefault(Return(stub_fb_buffer));
985+ EXPECT_CALL(mock_context, swap_buffers())
986+ .Times(AtLeast(5));
987+
988+ mga::LayerList list(layer_adapter, {}, geom::Displacement{});
989+ mtd::MockRenderableListCompositor mock_compositor;
990+ mga::DisplayContents content{primary, list, offset, mock_context,
991+ mock_compositor};
992+
993+ mga::HwcDevice device(mock_device);
994+ device.commit({content});
995+ for (int frame = 0; frame < 5; ++frame)
996+ {
997+ device.commit({content});
998+ ASSERT_EQ(0, device.recommended_sleep().count());
999+ }
1000+}
1001+
1002 TEST_F(HwcDevice, does_not_set_acquirefences_when_it_has_set_them_previously_without_update)
1003 {
1004 using namespace testing;
1005
1006=== modified file 'tests/unit-tests/graphics/android/test_hwc_fb_device.cpp'
1007--- tests/unit-tests/graphics/android/test_hwc_fb_device.cpp 2015-06-25 03:00:08 +0000
1008+++ tests/unit-tests/graphics/android/test_hwc_fb_device.cpp 2015-06-30 06:52:15 +0000
1009@@ -174,4 +174,7 @@
1010
1011 mga::DisplayContents content{primary, list, geom::Displacement{}, mock_context, stub_compositor};
1012 device.commit({content});
1013+
1014+ // Predictive bypass not enabled in HwcFbDevice
1015+ EXPECT_EQ(0, device.recommended_sleep().count());
1016 }
1017
1018=== modified file 'tests/unit-tests/graphics/mesa/kms/mock_kms_output.h'
1019--- tests/unit-tests/graphics/mesa/kms/mock_kms_output.h 2015-06-17 05:20:42 +0000
1020+++ tests/unit-tests/graphics/mesa/kms/mock_kms_output.h 2015-06-30 06:52:15 +0000
1021@@ -32,6 +32,7 @@
1022 MOCK_METHOD0(reset, void());
1023 MOCK_METHOD2(configure, void(geometry::Displacement, size_t));
1024 MOCK_CONST_METHOD0(size, geometry::Size());
1025+ MOCK_CONST_METHOD0(max_refresh_rate, int());
1026
1027 MOCK_METHOD1(set_crtc, bool(uint32_t));
1028 MOCK_METHOD0(clear_crtc, void());
1029
1030=== modified file 'tests/unit-tests/graphics/mesa/kms/test_display_buffer.cpp'
1031--- tests/unit-tests/graphics/mesa/kms/test_display_buffer.cpp 2015-06-25 03:00:08 +0000
1032+++ tests/unit-tests/graphics/mesa/kms/test_display_buffer.cpp 2015-06-30 06:52:15 +0000
1033@@ -46,6 +46,8 @@
1034 class MesaDisplayBufferTest : public Test
1035 {
1036 public:
1037+ int const mock_refresh_rate = 60;
1038+
1039 MesaDisplayBufferTest()
1040 : mock_bypassable_buffer{std::make_shared<NiceMock<MockBuffer>>()}
1041 , fake_bypassable_renderable{
1042@@ -78,6 +80,8 @@
1043 .WillByDefault(Return(true));
1044 ON_CALL(*mock_kms_output, schedule_page_flip(_))
1045 .WillByDefault(Return(true));
1046+ ON_CALL(*mock_kms_output, max_refresh_rate())
1047+ .WillByDefault(Return(mock_refresh_rate));
1048
1049 ON_CALL(*mock_bypassable_buffer, size())
1050 .WillByDefault(Return(display_area.size));
1051@@ -154,6 +158,56 @@
1052 EXPECT_EQ(original_count, mock_bypassable_buffer.use_count());
1053 }
1054
1055+TEST_F(MesaDisplayBufferTest, predictive_bypass_is_throttled)
1056+{
1057+ graphics::mesa::DisplayBuffer db(
1058+ create_platform(),
1059+ null_display_report(),
1060+ {mock_kms_output},
1061+ nullptr,
1062+ display_area,
1063+ mir_orientation_normal,
1064+ gl_config,
1065+ mock_egl.fake_egl_context);
1066+
1067+ for (int frame = 0; frame < 5; ++frame)
1068+ {
1069+ ASSERT_TRUE(db.post_renderables_if_optimizable(bypassable_list));
1070+ db.post();
1071+
1072+ // Cast to a simple int type so that test failures are readable
1073+ int milliseconds_per_frame = 1000 / mock_refresh_rate;
1074+ ASSERT_THAT(db.recommended_sleep().count(),
1075+ Ge(milliseconds_per_frame/2));
1076+ }
1077+}
1078+
1079+TEST_F(MesaDisplayBufferTest, frames_requiring_gl_are_not_throttled)
1080+{
1081+ graphics::RenderableList non_bypassable_list{
1082+ std::make_shared<FakeRenderable>(geometry::Rectangle{{12, 34}, {1, 1}})
1083+ };
1084+
1085+ graphics::mesa::DisplayBuffer db(
1086+ create_platform(),
1087+ null_display_report(),
1088+ {mock_kms_output},
1089+ nullptr,
1090+ display_area,
1091+ mir_orientation_normal,
1092+ gl_config,
1093+ mock_egl.fake_egl_context);
1094+
1095+ for (int frame = 0; frame < 5; ++frame)
1096+ {
1097+ ASSERT_FALSE(db.post_renderables_if_optimizable(non_bypassable_list));
1098+ db.post();
1099+
1100+ // Cast to a simple int type so that test failures are readable
1101+ ASSERT_EQ(0, db.recommended_sleep().count());
1102+ }
1103+}
1104+
1105 TEST_F(MesaDisplayBufferTest, bypass_buffer_only_referenced_once_by_db)
1106 {
1107 graphics::mesa::DisplayBuffer db(
1108
1109=== modified file 'tests/unit-tests/graphics/nested/test_nested_display.cpp'
1110--- tests/unit-tests/graphics/nested/test_nested_display.cpp 2015-06-25 13:43:43 +0000
1111+++ tests/unit-tests/graphics/nested/test_nested_display.cpp 2015-06-30 06:52:15 +0000
1112@@ -167,6 +167,23 @@
1113 EXPECT_FALSE(weak_platform.expired());
1114 }
1115
1116+TEST_F(NestedDisplay, never_enables_predictive_bypass)
1117+{ // This test can be removed after it's implemented (after nested bypass)
1118+ auto const nested_display = create_nested_display(
1119+ null_platform,
1120+ mt::fake_shared(stub_gl_config));
1121+
1122+ int groups = 0;
1123+ nested_display->for_each_display_sync_group(
1124+ [&groups](mg::DisplaySyncGroup& g)
1125+ {
1126+ EXPECT_EQ(0, g.recommended_sleep().count());
1127+ ++groups;
1128+ }
1129+ );
1130+
1131+ ASSERT_NE(0, groups);
1132+}
1133
1134 TEST_F(NestedDisplay, makes_context_current_on_creation_and_releases_on_destruction)
1135 {
1136
1137=== modified file 'tests/unit-tests/graphics/offscreen/test_offscreen_display.cpp'
1138--- tests/unit-tests/graphics/offscreen/test_offscreen_display.cpp 2015-06-25 13:43:43 +0000
1139+++ tests/unit-tests/graphics/offscreen/test_offscreen_display.cpp 2015-06-30 06:52:15 +0000
1140@@ -78,6 +78,22 @@
1141 EXPECT_TRUE(count);
1142 }
1143
1144+TEST_F(OffscreenDisplayTest, never_enables_predictive_bypass)
1145+{
1146+ mgo::Display display{
1147+ native_display,
1148+ std::make_shared<mg::CloneDisplayConfigurationPolicy>(),
1149+ mr::null_display_report()};
1150+
1151+ int groups = 0;
1152+ display.for_each_display_sync_group([&](mg::DisplaySyncGroup& group){
1153+ ++groups;
1154+ EXPECT_EQ(0, group.recommended_sleep().count());
1155+ });
1156+
1157+ EXPECT_TRUE(groups);
1158+}
1159+
1160 TEST_F(OffscreenDisplayTest, makes_fbo_current_rendering_target)
1161 {
1162 using namespace ::testing;

Subscribers

People subscribed via source and target branches