Mir

Merge lp:~vanvugt/mir/manual-vsync-api-v2 into lp:mir

Proposed by Daniel van Vugt
Status: Work in progress
Proposed branch: lp:~vanvugt/mir/manual-vsync-api-v2
Merge into: lp:mir
Diff against target: 233 lines (+103/-2)
9 files modified
examples/eglapp.c (+15/-1)
include/client/mir_toolkit/mir_window.h (+16/-0)
src/client/frame_clock.cpp (+10/-0)
src/client/frame_clock.h (+8/-0)
src/client/mir_surface.cpp (+16/-0)
src/client/mir_surface.h (+2/-0)
src/client/mir_surface_api.cpp (+5/-0)
src/client/symbols.map (+6/-1)
tests/unit-tests/client/test_frame_clock.cpp (+25/-0)
To merge this branch: bzr merge lp:~vanvugt/mir/manual-vsync-api-v2
Reviewer Review Type Date Requested Status
Mir CI Bot continuous-integration Approve
Mir development team Pending
Review via email: mp+323313@code.launchpad.net

Commit message

Introducing a trivial API function that allows clients to throttle
themselves without ever blocking inside thirdparty code:
mir_window_get_microseconds_till_vblank

The intended methodology is for a client to set swap interval zero
and then use this new function to throttle its own swaps.

The intended use cases are:
 * Toolkits/apps that have an existing event loop (particularly
   single threaded ones) and don't want anything that complicates
   that architecture, but do want synchronized/throttled rendering.
 * Existing single-threaded users (Xmir only?) of the asynchronous
   mir_buffer_stream_swap_buffers(), can continue to have vsync and
   asynchronous buffer returns, and single-threaded simplicity,
   without putting a queuing/threading burden on libmirclient to
   throttle buffer returns at all. The existing client event loop can
   be used instead.

To post a comment you must log in.
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:4167
https://mir-jenkins.ubuntu.com/job/mir-ci/3380/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4574
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4700
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4689
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4689
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4606
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4606/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4606
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4606/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4606
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4606/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4606
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4606/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4606
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4606/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/3380/rebuild

review: Approve (continuous-integration)

Unmerged revisions

4167. By Daniel van Vugt

Rename the new unit test to match what it actually does

4166. By Daniel van Vugt

Use the new feature in examples and fix up C symbol exports.

4165. By Daniel van Vugt

And use the new function

4164. By Daniel van Vugt

Implement a new FrameClock method for the new API, which eases unit
testing and is crucially also right... so the test now passes.

4163. By Daniel van Vugt

Reintroduce and rewrite the unit test

4162. By Daniel van Vugt

Revert back to a simpler algorithm

4161. By Daniel van Vugt

A little progress

4160. By Daniel van Vugt

A new approach. Needs testing...

4159. By Daniel van Vugt

Drop the new unit test

4158. By Daniel van Vugt

Add a unit test for the expected behaviour from the new MirWindow
function... presently failing.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'examples/eglapp.c'
2--- examples/eglapp.c 2017-04-05 03:11:28 +0000
3+++ examples/eglapp.c 2017-04-27 08:36:53 +0000
4@@ -22,6 +22,7 @@
5 #include <stdlib.h>
6 #include <signal.h>
7 #include <time.h>
8+#include <errno.h>
9 #include <string.h>
10 #include <EGL/egl.h>
11 #include <GLES2/gl2.h>
12@@ -38,6 +39,7 @@
13 static EGLSurface eglsurface;
14 static volatile sig_atomic_t running = 0;
15 static double refresh_rate = 0.0;
16+static mir_eglapp_bool alt_vsync = 0;
17
18 #pragma GCC diagnostic push
19 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
20@@ -84,6 +86,15 @@
21 return running;
22 }
23
24+/* Apparently usleep is deprecated so we write own own... */
25+static void micro_sleep(unsigned long usec)
26+{
27+ struct timespec ts = {0, usec * 1000};
28+ while (nanosleep(&ts, &ts) && errno == EINTR)
29+ {
30+ }
31+}
32+
33 void mir_eglapp_swap_buffers(void)
34 {
35 EGLint width, height;
36@@ -92,6 +103,8 @@
37 return;
38
39 eglSwapBuffers(egldisplay, eglsurface);
40+ if (alt_vsync)
41+ micro_sleep(mir_window_get_microseconds_till_vblank(window));
42
43 /*
44 * Querying the surface (actually the current buffer) dimensions here is
45@@ -347,6 +360,7 @@
46 {"-h", "!", &help, "Show this help text"},
47 {"-m <socket>", "=", &mir_socket, "Mir server socket"},
48 {"-n", "!", &no_vsync, "Don't sync to vblank"},
49+ {"-v", "!", &alt_vsync, "Sync to vblank using the alternate method (manual timing)"},
50 {"-o <id>", "%u", &output_id, "Force placement on output monitor ID"},
51 {"-q", "!", &quiet, "Quiet mode (no messages output)"},
52 {"-s <width>x<height>", "=", &dims, "Force window size"},
53@@ -372,7 +386,7 @@
54 return 0;
55 }
56
57- if (no_vsync)
58+ if (no_vsync || alt_vsync)
59 swapinterval = 0;
60
61 if (dims)
62
63=== modified file 'include/client/mir_toolkit/mir_window.h'
64--- include/client/mir_toolkit/mir_window.h 2017-04-13 07:00:27 +0000
65+++ include/client/mir_toolkit/mir_window.h 2017-04-27 08:36:53 +0000
66@@ -859,6 +859,22 @@
67 MirPersistentId* mir_window_request_persistent_id_sync(MirWindow* window)
68 __attribute__((deprecated("Use mir_window_request_window_id_sync")));
69 MirWindowId* mir_window_request_window_id_sync(MirWindow* window);
70+
71+/**
72+ * Query the approximate time interval in microseconds until the next vblank for
73+ * a given window (actually the next vblank for the monitor deemed most
74+ * relevant to the window's current placement). A relative time is returned so
75+ * as to avoid clock/API portability issues. And microsecond precision is used
76+ * because it's the largest unit of time that still provides ample precision
77+ * to be a few orders or magnitude more precise than human perception, or than
78+ * the refresh inverval of display hardware).
79+ *
80+ * \param [in] w The MirWindow
81+ * \return Time in microseconds to the next vblank for the given
82+ * window (may be as low as zero).
83+ */
84+unsigned long mir_window_get_microseconds_till_vblank(MirWindow const* w);
85+
86 #ifdef __cplusplus
87 }
88 /**@}*/
89
90=== modified file 'src/client/frame_clock.cpp'
91--- src/client/frame_clock.cpp 2016-11-29 08:00:45 +0000
92+++ src/client/frame_clock.cpp 2017-04-27 08:36:53 +0000
93@@ -154,3 +154,13 @@
94
95 return target;
96 }
97+
98+PosixTimestamp FrameClock::next_frame_after_now() const
99+{
100+ auto result = next_frame_after({});
101+ auto const now = get_current_time(result.clock_id);
102+ if (result <= now)
103+ result = next_frame_after(now);
104+ return result;
105+}
106+
107
108=== modified file 'src/client/frame_clock.h'
109--- src/client/frame_clock.h 2016-12-15 08:15:54 +0000
110+++ src/client/frame_clock.h 2017-04-27 08:36:53 +0000
111@@ -57,6 +57,14 @@
112 */
113 time::PosixTimestamp next_frame_after(time::PosixTimestamp prev) const;
114
115+ /**
116+ * Return the next frame that's in the future. This is a simplified
117+ * stateless version of next_frame_after() with the caveat that it lacks
118+ * the ability to detect and catch up missed frames (because there is no
119+ * state information indicating which frame deadline the caller last met).
120+ */
121+ time::PosixTimestamp next_frame_after_now() const;
122+
123 private:
124 time::PosixTimestamp fallback_resync_callback() const;
125
126
127=== modified file 'src/client/mir_surface.cpp'
128--- src/client/mir_surface.cpp 2017-04-13 07:00:27 +0000
129+++ src/client/mir_surface.cpp 2017-04-27 08:36:53 +0000
130@@ -238,6 +238,22 @@
131 */
132 }
133
134+std::chrono::microseconds MirSurface::microseconds_till_vblank() const
135+{
136+ std::chrono::microseconds ret(0);
137+
138+ auto const target = frame_clock->next_frame_after_now();
139+ auto const now = mir::time::PosixTimestamp::now(target.clock_id);
140+
141+ if (target > now)
142+ {
143+ auto const delta = target - now;
144+ ret = std::chrono::duration_cast<std::chrono::microseconds>(delta);
145+ }
146+
147+ return ret;
148+}
149+
150 MirWindowParameters MirSurface::get_parameters() const
151 {
152 std::lock_guard<decltype(mutex)> lock(mutex);
153
154=== modified file 'src/client/mir_surface.h'
155--- src/client/mir_surface.h 2017-04-13 07:00:27 +0000
156+++ src/client/mir_surface.h 2017-04-27 08:36:53 +0000
157@@ -224,6 +224,8 @@
158 std::shared_ptr<mir::client::FrameClock> get_frame_clock() const;
159 std::shared_ptr<mir::input::receiver::XKBMapper> get_keymapper() const;
160
161+ std::chrono::microseconds microseconds_till_vblank() const;
162+
163 private:
164 std::mutex mutable mutex; // Protects all members of *this
165
166
167=== modified file 'src/client/mir_surface_api.cpp'
168--- src/client/mir_surface_api.cpp 2017-04-13 07:00:27 +0000
169+++ src/client/mir_surface_api.cpp 2017-04-27 08:36:53 +0000
170@@ -1385,3 +1385,8 @@
171 wh->wait_for_all();
172 return result;
173 }
174+
175+unsigned long mir_window_get_microseconds_till_vblank(MirWindow const* w)
176+{
177+ return w->microseconds_till_vblank().count();
178+}
179
180=== modified file 'src/client/symbols.map'
181--- src/client/symbols.map 2017-04-13 07:00:27 +0000
182+++ src/client/symbols.map 2017-04-27 08:36:53 +0000
183@@ -579,7 +579,7 @@
184 };
185 } MIR_CLIENT_DETAIL_0.26.1;
186
187-MIR_CLIENT_0.27 { # New functions in Mir 0.27 or 1.0
188+MIR_CLIENT_0.27 { # New functions in Mir 0.27
189 global:
190 mir_connection_apply_session_input_config;
191 mir_connection_set_base_input_config;
192@@ -611,3 +611,8 @@
193 mir_touchscreen_config_set_mapping_mode;
194 mir_touchscreen_config_set_output_id;
195 } MIR_CLIENT_0.26.1;
196+
197+MIR_CLIENT_0.28 { # New functions in Mir 0.28 (or 1.0)
198+ global:
199+ mir_window_get_microseconds_till_vblank;
200+} MIR_CLIENT_0.27;
201
202=== modified file 'tests/unit-tests/client/test_frame_clock.cpp'
203--- tests/unit-tests/client/test_frame_clock.cpp 2017-03-13 08:12:52 +0000
204+++ tests/unit-tests/client/test_frame_clock.cpp 2017-04-27 08:36:53 +0000
205@@ -426,3 +426,28 @@
206 EXPECT_EQ(one_frame, in2 - in1);
207 EXPECT_EQ(one_frame, out2 - out1);
208 }
209+
210+TEST_F(FrameClockTest, next_frame_after_now_is_exactly_that)
211+{
212+ auto const& now = fake_time[CLOCK_MONOTONIC];
213+ FrameClock clock(with_fake_time);
214+ clock.set_period(one_frame);
215+
216+ auto a = now;
217+ auto b = clock.next_frame_after_now();
218+ EXPECT_GE(b, a);
219+ EXPECT_LE(b-now, one_frame);
220+
221+ for (int same = 0; same < 3; ++same)
222+ EXPECT_EQ(b, clock.next_frame_after_now()) << "i=" << same;
223+
224+ fake_sleep_until(b);
225+
226+ auto c = clock.next_frame_after_now();
227+ EXPECT_LT(b, c);
228+ EXPECT_EQ(b+one_frame, c);
229+ EXPECT_LE(c-now, one_frame);
230+
231+ for (int same = 0; same < 3; ++same)
232+ EXPECT_EQ(c, clock.next_frame_after_now()) << "i=" << same;
233+}

Subscribers

People subscribed via source and target branches