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
=== modified file 'examples/eglapp.c'
--- examples/eglapp.c 2017-04-05 03:11:28 +0000
+++ examples/eglapp.c 2017-04-27 08:36:53 +0000
@@ -22,6 +22,7 @@
22#include <stdlib.h>22#include <stdlib.h>
23#include <signal.h>23#include <signal.h>
24#include <time.h>24#include <time.h>
25#include <errno.h>
25#include <string.h>26#include <string.h>
26#include <EGL/egl.h>27#include <EGL/egl.h>
27#include <GLES2/gl2.h>28#include <GLES2/gl2.h>
@@ -38,6 +39,7 @@
38static EGLSurface eglsurface;39static EGLSurface eglsurface;
39static volatile sig_atomic_t running = 0;40static volatile sig_atomic_t running = 0;
40static double refresh_rate = 0.0;41static double refresh_rate = 0.0;
42static mir_eglapp_bool alt_vsync = 0;
4143
42#pragma GCC diagnostic push44#pragma GCC diagnostic push
43#pragma GCC diagnostic ignored "-Wdeprecated-declarations"45#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
@@ -84,6 +86,15 @@
84 return running;86 return running;
85}87}
8688
89/* Apparently usleep is deprecated so we write own own... */
90static void micro_sleep(unsigned long usec)
91{
92 struct timespec ts = {0, usec * 1000};
93 while (nanosleep(&ts, &ts) && errno == EINTR)
94 {
95 }
96}
97
87void mir_eglapp_swap_buffers(void)98void mir_eglapp_swap_buffers(void)
88{99{
89 EGLint width, height;100 EGLint width, height;
@@ -92,6 +103,8 @@
92 return;103 return;
93104
94 eglSwapBuffers(egldisplay, eglsurface);105 eglSwapBuffers(egldisplay, eglsurface);
106 if (alt_vsync)
107 micro_sleep(mir_window_get_microseconds_till_vblank(window));
95108
96 /*109 /*
97 * Querying the surface (actually the current buffer) dimensions here is110 * Querying the surface (actually the current buffer) dimensions here is
@@ -347,6 +360,7 @@
347 {"-h", "!", &help, "Show this help text"},360 {"-h", "!", &help, "Show this help text"},
348 {"-m <socket>", "=", &mir_socket, "Mir server socket"},361 {"-m <socket>", "=", &mir_socket, "Mir server socket"},
349 {"-n", "!", &no_vsync, "Don't sync to vblank"},362 {"-n", "!", &no_vsync, "Don't sync to vblank"},
363 {"-v", "!", &alt_vsync, "Sync to vblank using the alternate method (manual timing)"},
350 {"-o <id>", "%u", &output_id, "Force placement on output monitor ID"},364 {"-o <id>", "%u", &output_id, "Force placement on output monitor ID"},
351 {"-q", "!", &quiet, "Quiet mode (no messages output)"},365 {"-q", "!", &quiet, "Quiet mode (no messages output)"},
352 {"-s <width>x<height>", "=", &dims, "Force window size"},366 {"-s <width>x<height>", "=", &dims, "Force window size"},
@@ -372,7 +386,7 @@
372 return 0;386 return 0;
373 }387 }
374388
375 if (no_vsync)389 if (no_vsync || alt_vsync)
376 swapinterval = 0;390 swapinterval = 0;
377391
378 if (dims)392 if (dims)
379393
=== modified file 'include/client/mir_toolkit/mir_window.h'
--- include/client/mir_toolkit/mir_window.h 2017-04-13 07:00:27 +0000
+++ include/client/mir_toolkit/mir_window.h 2017-04-27 08:36:53 +0000
@@ -859,6 +859,22 @@
859MirPersistentId* mir_window_request_persistent_id_sync(MirWindow* window)859MirPersistentId* mir_window_request_persistent_id_sync(MirWindow* window)
860__attribute__((deprecated("Use mir_window_request_window_id_sync")));860__attribute__((deprecated("Use mir_window_request_window_id_sync")));
861MirWindowId* mir_window_request_window_id_sync(MirWindow* window);861MirWindowId* mir_window_request_window_id_sync(MirWindow* window);
862
863/**
864 * Query the approximate time interval in microseconds until the next vblank for
865 * a given window (actually the next vblank for the monitor deemed most
866 * relevant to the window's current placement). A relative time is returned so
867 * as to avoid clock/API portability issues. And microsecond precision is used
868 * because it's the largest unit of time that still provides ample precision
869 * to be a few orders or magnitude more precise than human perception, or than
870 * the refresh inverval of display hardware).
871 *
872 * \param [in] w The MirWindow
873 * \return Time in microseconds to the next vblank for the given
874 * window (may be as low as zero).
875 */
876unsigned long mir_window_get_microseconds_till_vblank(MirWindow const* w);
877
862#ifdef __cplusplus878#ifdef __cplusplus
863}879}
864/**@}*/880/**@}*/
865881
=== modified file 'src/client/frame_clock.cpp'
--- src/client/frame_clock.cpp 2016-11-29 08:00:45 +0000
+++ src/client/frame_clock.cpp 2017-04-27 08:36:53 +0000
@@ -154,3 +154,13 @@
154154
155 return target;155 return target;
156}156}
157
158PosixTimestamp FrameClock::next_frame_after_now() const
159{
160 auto result = next_frame_after({});
161 auto const now = get_current_time(result.clock_id);
162 if (result <= now)
163 result = next_frame_after(now);
164 return result;
165}
166
157167
=== modified file 'src/client/frame_clock.h'
--- src/client/frame_clock.h 2016-12-15 08:15:54 +0000
+++ src/client/frame_clock.h 2017-04-27 08:36:53 +0000
@@ -57,6 +57,14 @@
57 */57 */
58 time::PosixTimestamp next_frame_after(time::PosixTimestamp prev) const;58 time::PosixTimestamp next_frame_after(time::PosixTimestamp prev) const;
5959
60 /**
61 * Return the next frame that's in the future. This is a simplified
62 * stateless version of next_frame_after() with the caveat that it lacks
63 * the ability to detect and catch up missed frames (because there is no
64 * state information indicating which frame deadline the caller last met).
65 */
66 time::PosixTimestamp next_frame_after_now() const;
67
60private:68private:
61 time::PosixTimestamp fallback_resync_callback() const;69 time::PosixTimestamp fallback_resync_callback() const;
6270
6371
=== modified file 'src/client/mir_surface.cpp'
--- src/client/mir_surface.cpp 2017-04-13 07:00:27 +0000
+++ src/client/mir_surface.cpp 2017-04-27 08:36:53 +0000
@@ -238,6 +238,22 @@
238 */238 */
239}239}
240240
241std::chrono::microseconds MirSurface::microseconds_till_vblank() const
242{
243 std::chrono::microseconds ret(0);
244
245 auto const target = frame_clock->next_frame_after_now();
246 auto const now = mir::time::PosixTimestamp::now(target.clock_id);
247
248 if (target > now)
249 {
250 auto const delta = target - now;
251 ret = std::chrono::duration_cast<std::chrono::microseconds>(delta);
252 }
253
254 return ret;
255}
256
241MirWindowParameters MirSurface::get_parameters() const257MirWindowParameters MirSurface::get_parameters() const
242{258{
243 std::lock_guard<decltype(mutex)> lock(mutex);259 std::lock_guard<decltype(mutex)> lock(mutex);
244260
=== modified file 'src/client/mir_surface.h'
--- src/client/mir_surface.h 2017-04-13 07:00:27 +0000
+++ src/client/mir_surface.h 2017-04-27 08:36:53 +0000
@@ -224,6 +224,8 @@
224 std::shared_ptr<mir::client::FrameClock> get_frame_clock() const;224 std::shared_ptr<mir::client::FrameClock> get_frame_clock() const;
225 std::shared_ptr<mir::input::receiver::XKBMapper> get_keymapper() const;225 std::shared_ptr<mir::input::receiver::XKBMapper> get_keymapper() const;
226226
227 std::chrono::microseconds microseconds_till_vblank() const;
228
227private:229private:
228 std::mutex mutable mutex; // Protects all members of *this230 std::mutex mutable mutex; // Protects all members of *this
229231
230232
=== modified file 'src/client/mir_surface_api.cpp'
--- src/client/mir_surface_api.cpp 2017-04-13 07:00:27 +0000
+++ src/client/mir_surface_api.cpp 2017-04-27 08:36:53 +0000
@@ -1385,3 +1385,8 @@
1385 wh->wait_for_all();1385 wh->wait_for_all();
1386 return result;1386 return result;
1387}1387}
1388
1389unsigned long mir_window_get_microseconds_till_vblank(MirWindow const* w)
1390{
1391 return w->microseconds_till_vblank().count();
1392}
13881393
=== modified file 'src/client/symbols.map'
--- src/client/symbols.map 2017-04-13 07:00:27 +0000
+++ src/client/symbols.map 2017-04-27 08:36:53 +0000
@@ -579,7 +579,7 @@
579 };579 };
580} MIR_CLIENT_DETAIL_0.26.1;580} MIR_CLIENT_DETAIL_0.26.1;
581581
582MIR_CLIENT_0.27 { # New functions in Mir 0.27 or 1.0582MIR_CLIENT_0.27 { # New functions in Mir 0.27
583 global:583 global:
584 mir_connection_apply_session_input_config;584 mir_connection_apply_session_input_config;
585 mir_connection_set_base_input_config;585 mir_connection_set_base_input_config;
@@ -611,3 +611,8 @@
611 mir_touchscreen_config_set_mapping_mode;611 mir_touchscreen_config_set_mapping_mode;
612 mir_touchscreen_config_set_output_id;612 mir_touchscreen_config_set_output_id;
613} MIR_CLIENT_0.26.1;613} MIR_CLIENT_0.26.1;
614
615MIR_CLIENT_0.28 { # New functions in Mir 0.28 (or 1.0)
616 global:
617 mir_window_get_microseconds_till_vblank;
618} MIR_CLIENT_0.27;
614619
=== modified file 'tests/unit-tests/client/test_frame_clock.cpp'
--- tests/unit-tests/client/test_frame_clock.cpp 2017-03-13 08:12:52 +0000
+++ tests/unit-tests/client/test_frame_clock.cpp 2017-04-27 08:36:53 +0000
@@ -426,3 +426,28 @@
426 EXPECT_EQ(one_frame, in2 - in1);426 EXPECT_EQ(one_frame, in2 - in1);
427 EXPECT_EQ(one_frame, out2 - out1);427 EXPECT_EQ(one_frame, out2 - out1);
428}428}
429
430TEST_F(FrameClockTest, next_frame_after_now_is_exactly_that)
431{
432 auto const& now = fake_time[CLOCK_MONOTONIC];
433 FrameClock clock(with_fake_time);
434 clock.set_period(one_frame);
435
436 auto a = now;
437 auto b = clock.next_frame_after_now();
438 EXPECT_GE(b, a);
439 EXPECT_LE(b-now, one_frame);
440
441 for (int same = 0; same < 3; ++same)
442 EXPECT_EQ(b, clock.next_frame_after_now()) << "i=" << same;
443
444 fake_sleep_until(b);
445
446 auto c = clock.next_frame_after_now();
447 EXPECT_LT(b, c);
448 EXPECT_EQ(b+one_frame, c);
449 EXPECT_LE(c-now, one_frame);
450
451 for (int same = 0; same < 3; ++same)
452 EXPECT_EQ(c, clock.next_frame_after_now()) << "i=" << same;
453}

Subscribers

People subscribed via source and target branches