Mir

Merge lp:~vanvugt/mir/physical-frame 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: 3748
Proposed branch: lp:~vanvugt/mir/physical-frame
Merge into: lp:mir
Diff against target: 1934 lines (+617/-124)
64 files modified
include/common/mir/time/posix_timestamp.h (+59/-0)
include/platform/mir/graphics/atomic_frame.h (+43/-0)
include/platform/mir/graphics/display.h (+17/-0)
include/platform/mir/graphics/frame.h (+47/-0)
include/test/mir/test/doubles/mock_egl.h (+4/-0)
include/test/mir/test/doubles/null_display.h (+4/-0)
src/include/platform/mir/graphics/display_report.h (+2/-1)
src/platform/graphics/CMakeLists.txt (+1/-0)
src/platform/graphics/atomic_frame.cpp (+50/-0)
src/platform/symbols.map (+4/-0)
src/platforms/android/server/device_quirks.cpp (+1/-1)
src/platforms/android/server/display.cpp (+30/-4)
src/platforms/android/server/display.h (+9/-1)
src/platforms/android/server/fb_device.cpp (+1/-1)
src/platforms/android/server/fb_device.h (+1/-1)
src/platforms/android/server/hwc_blanking_control.cpp (+4/-4)
src/platforms/android/server/hwc_configuration.h (+4/-3)
src/platforms/android/server/hwc_fb_device.cpp (+1/-1)
src/platforms/android/server/hwc_fb_device.h (+2/-1)
src/platforms/android/server/hwc_wrapper.h (+2/-2)
src/platforms/android/server/real_hwc_wrapper.cpp (+11/-3)
src/platforms/android/server/real_hwc_wrapper.h (+3/-3)
src/platforms/android/utils/render_overlays.cpp (+1/-1)
src/platforms/eglstream-kms/server/display.cpp (+9/-0)
src/platforms/eglstream-kms/server/display.h (+1/-0)
src/platforms/mesa/server/kms/display.cpp (+6/-0)
src/platforms/mesa/server/kms/display.h (+3/-1)
src/platforms/mesa/server/kms/kms_output.h (+2/-0)
src/platforms/mesa/server/kms/kms_page_flipper.cpp (+21/-7)
src/platforms/mesa/server/kms/kms_page_flipper.h (+5/-3)
src/platforms/mesa/server/kms/page_flipper.h (+2/-1)
src/platforms/mesa/server/kms/real_kms_output.cpp (+7/-1)
src/platforms/mesa/server/kms/real_kms_output.h (+5/-0)
src/platforms/mesa/server/x11/graphics/display.cpp (+9/-1)
src/platforms/mesa/server/x11/graphics/display.h (+6/-0)
src/platforms/mesa/server/x11/graphics/display_buffer.cpp (+63/-2)
src/platforms/mesa/server/x11/graphics/display_buffer.h (+8/-0)
src/platforms/mesa/server/x11/graphics/egl_helper.h (+1/-0)
src/server/graphics/nested/display.cpp (+5/-0)
src/server/graphics/nested/display.h (+1/-0)
src/server/report/logging/display_report.cpp (+29/-20)
src/server/report/logging/display_report.h (+5/-7)
src/server/report/logging/logging_report_factory.cpp (+1/-1)
src/server/report/lttng/display_report.cpp (+2/-1)
src/server/report/lttng/display_report.h (+1/-1)
src/server/report/null/display_report.cpp (+1/-1)
src/server/report/null/display_report.h (+1/-1)
tests/include/mir/test/doubles/mock_display.h (+1/-0)
tests/include/mir/test/doubles/mock_display_report.h (+2/-2)
tests/include/mir/test/doubles/mock_hwc_device_wrapper.h (+1/-1)
tests/include/mir/test/doubles/stub_display_builder.h (+5/-2)
tests/mir_test_doubles/mock_egl.cpp (+14/-0)
tests/mir_test_framework/stubbed_graphics_platform.cpp (+4/-1)
tests/unit-tests/logging/test_display_report.cpp (+61/-20)
tests/unit-tests/platforms/android/server/test_display.cpp (+3/-3)
tests/unit-tests/platforms/android/server/test_display_hotplug.cpp (+2/-2)
tests/unit-tests/platforms/android/server/test_hwc_configuration.cpp (+8/-6)
tests/unit-tests/platforms/android/server/test_hwc_fb_device.cpp (+2/-2)
tests/unit-tests/platforms/android/server/test_hwc_wrapper.cpp (+3/-2)
tests/unit-tests/platforms/mesa/kms/mock_kms_output.h (+2/-0)
tests/unit-tests/platforms/mesa/kms/test_display.cpp (+4/-5)
tests/unit-tests/platforms/mesa/kms/test_kms_page_flipper.cpp (+1/-1)
tests/unit-tests/platforms/mesa/kms/test_real_kms_output.cpp (+6/-2)
tests/unit-tests/platforms/mesa/x11/test_display.cpp (+3/-0)
To merge this branch: bzr merge lp:~vanvugt/mir/physical-frame
Reviewer Review Type Date Requested Status
Cemil Azizoglu (community) Approve
Mir CI Bot continuous-integration Approve
Chris Halse Rogers Needs Fixing
Review via email: mp+306199@code.launchpad.net

Commit message

Introducing backend plumbing for getting high precision frame timing.

Presently this is only complete for the android, kms and x11 graphics
platforms. Nested and eglstream will come later.

Description of the change

This is mostly just interface changes required to pass around timing
information so all the testing emphasis is on enhancing the display
report right now. I plan on automating that (as a
performance/acceptance test) for verifying each platform's timing
consistency but (a) that's not written yet, (b) this branch is already
too big and (c) that requires a real graphics head [since it's actually more about testing the graphics driver than Mir] so where to put
that test may become contentious. For the moment, just use
--display-report=log to verify your graphics driver is emitting sane
timing data.

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

FAILED: Continuous integration, rev:3639
https://mir-jenkins.ubuntu.com/job/mir-ci/1772/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/2220/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/2283
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/2274
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/2274
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=yakkety/2274
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=yakkety/2248
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=yakkety/2248/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/2248
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/2248/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=yakkety/2248
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=yakkety/2248/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/2248/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/2248
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/2248/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3640
https://mir-jenkins.ubuntu.com/job/mir-ci/1776/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/2224/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/2287
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/2278
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/2278
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=yakkety/2278
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=yakkety/2252
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=yakkety/2252/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/2252
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/2252/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=yakkety/2252
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=yakkety/2252/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/2252/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/2252
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/2252/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3641
https://mir-jenkins.ubuntu.com/job/mir-ci/1777/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/2226/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/2289
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/2280
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/2280
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=yakkety/2280
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=yakkety/2254
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=yakkety/2254/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/2254
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/2254/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=yakkety/2254
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=yakkety/2254/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/2254/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/2254
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/2254/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
Revision history for this message
Chris Halse Rogers (raof) wrote :

Hm. Do we actually need Display::last_frame_on() and AtomicFrame?

The platforms already call display_report::report_vsync, and it would seem that we could drive everything from there, without storing any intermediates.

Looking at GLX_OML_SYNC, we probably don't need an SBC-analogue in mg::Frame.

+ // long long to match printf format on all architectures
+ const long long msc = frame.msc,

You might be looking for the “PRIix” series of macros; in this case, PRIi64.

so that would be:
+ "vsync on %u: #" PRIi64 ", " PRIi64 ".%03" PRIi64 " ms %s, ...

It's a minor nit; long long is apparently guaranteed to be at least 64bits on any standard-compliant C++ compiler.

Otherwise looks good.

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

Oh, also:
+ auto extensions = eglQueryString(egl_dpy, EGL_EXTENSIONS);
+ eglGetSyncValues =
+ reinterpret_cast<EglGetSyncValuesCHROMIUM*>(
+ strstr(extensions, "EGL_CHROMIUM_sync_control") ?
+ eglGetProcAddress("eglGetSyncValuesCHROMIUM") : NULL
+ );

is (probably not importantly) wrong (you can't naively use strstr to search through the extension string, because of substring matches).

We've already got a dependency on epoxy - why not use epoxy_has_egl_extension() and then just call eglGetSyncValues()?

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

Excellent question. Yes we will need Display::last_frame_on() because that data will in future be provided to clients on request only. This is so idle clients don't get woken by events they don't care about. I chose the Display class because that appeared to be the most available object to the frontend code that will soon use it. Also, there may in future be some hypothetical backend that can't provide callbacks (and can't report_vsync) but can provide accurate timestamps by query. So I am requiring only the minimal API for the maximum portability (and to avoid unwanted wakeups).

And yes I did think of PRIi64 too. However it is a bit ugly and has the additional disadvantage that the reporting code then has implied knowledge of the data type used in a distant header. This way using long long conversion that's not required, and is nicer to read.

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

So, the first point - provide on client request only - can be more easily handled further up the stack.

The second point - that there might be some future Display that can't provide vsync events information but *can* tell you frame timing - is already going to have a bad time in Mir. If we can't detect frames our compositor is going to spin at 100%, and if we provide a synthetic vsync then... we've got vsync events ☺.

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

OK, if it proves convenient and if we change DisplayReport to DisplayObserver then we could possibly use that. But it's easy to remove/replace and I would rather keep it until such time as that's proven to be equally convenient.

I also just remembered last_frame_on existed long before I started using DisplayReport here. And also long before the Report/Observer discussion. So it wasn't an option at the time.

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3643
https://mir-jenkins.ubuntu.com/job/mir-ci/1778/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/2228/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/2291
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/2282
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/2282
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=yakkety/2282
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=yakkety/2256
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=yakkety/2256/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/2256
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/2256/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=yakkety/2256
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=yakkety/2256/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/2256/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/2256
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/2256/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3644
https://mir-jenkins.ubuntu.com/job/mir-ci/1779/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/2229
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/2292
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/2283
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/2283
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=yakkety/2283
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=yakkety/2257
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=yakkety/2257/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/2257
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/2257/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=yakkety/2257
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=yakkety/2257/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/2257
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/2257/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/2257
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/2257/artifact/output/*zip*/output.zip

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

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

Also strstr is OK because as a prefix "EGL_CHROMIUM_sync_control" is adequately unique.

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

On Fri, Sep 23, 2016 at 12:51 PM, Daniel van Vugt
<email address hidden> wrote:
> Also strstr is OK because as a prefix "EGL_CHROMIUM_sync_control" is
> adequately unique.

At the moment, until someone introduces
EGL_CHROMIUM_sync_control_with_knobs_on ☺

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

So, I'm a weak needs-fixing; I'd prefer Display::last_frame_on() and AtomicFrame to be removed.

If others are OK with this as-is I'm happy to be overruled on this.

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

Removing last_frame_on is definitely an option in future. But not an option right now because a nice alternative does not exist yet (morphing DisplayReport into DisplayObserver). And even if it did exist, I wouldn't remove the dead code until it was proven the next step (IPC) could use the alternative successfully.

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

It's worth noting that an event-driven solution all the way through is not what we want. Clients shouldn't be woken up on every frame -- they will only get timing information on demand and then sleep properly when idle. So the current design is based on that. It's possible even after the observer work is done this branch won't need changing.

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

Work in progress again.

Although I've resolved the conflicts, Cemil's recent rewrite of mesa-x11 has made this (just the Chromium extension) stop working.

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

All fixed.

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3654
https://mir-jenkins.ubuntu.com/job/mir-ci/1893/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/2404
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/2467
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/2459
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/2459
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=yakkety/2459
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=yakkety/2433
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=yakkety/2433/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/2433
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/2433/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=yakkety/2433
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=yakkety/2433/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/2433
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/2433/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/2433
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/2433/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/2433
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/2433/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)
Revision history for this message
Cemil Azizoglu (cemil-azizoglu) wrote :

Looks good, we could also make the frequency at which frame data is obtained configurable. But I'm fine with this too.

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

I don't think that makes any sense. I think there is really only one answer...

The client will simply request information about the last frame from the server. So the server needs to know about every frame. But the client doesn't need to know unless it keeps asking (continuous rendering).

It's important to not deliver events or such to clients when they should be completely sleeping. But we also don't need to make it another on-demand RPC round trip either. One compromise might be to include the frame information in future buffer return messages. Because that's the only time the client will need fresh timing information. That way the client gets the information it needs without any extra roundtrips or wakeups.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'include/common/mir/time/posix_timestamp.h'
2--- include/common/mir/time/posix_timestamp.h 1970-01-01 00:00:00 +0000
3+++ include/common/mir/time/posix_timestamp.h 2016-10-05 09:20:48 +0000
4@@ -0,0 +1,59 @@
5+/*
6+ * Copyright © 2016 Canonical Ltd.
7+ *
8+ * This program is free software: you can redistribute it and/or modify it
9+ * under the terms of the GNU Lesser General Public License version 3,
10+ * as published by the Free Software Foundation.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU Lesser General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU Lesser General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ *
20+ * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
21+ */
22+
23+#ifndef MIR_TIME_POSIX_TIMESTAMP_H_
24+#define MIR_TIME_POSIX_TIMESTAMP_H_
25+
26+#include <chrono>
27+#include <ctime>
28+
29+namespace mir { namespace time {
30+
31+/*
32+ * We need absolute precision here so sadly can't use high-level C++ clocks...
33+ * - Graphics frame timing needs support for at least the kernel clocks
34+ * CLOCK_REALTIME and CLOCK_MONOTONIC, to be selected at runtime, whereas
35+ * std::chrono does not support CLOCK_REALTIME or easily switching clocks.
36+ * - mir::time::Timestamp is relative to the (wrong) epoch of steady_clock,
37+ * so converting to/from mir::time::Timestamp would be dangerously
38+ * inaccurate at best.
39+ */
40+
41+struct PosixTimestamp
42+{
43+ clockid_t clock_id;
44+ std::chrono::nanoseconds nanoseconds;
45+
46+ PosixTimestamp()
47+ : clock_id{CLOCK_MONOTONIC}, nanoseconds{0} {}
48+ PosixTimestamp(clockid_t clk, std::chrono::nanoseconds ns)
49+ : clock_id{clk}, nanoseconds{ns} {}
50+ PosixTimestamp(clockid_t clk, struct timespec const& ts)
51+ : clock_id{clk}, nanoseconds{ts.tv_sec*1000000000LL + ts.tv_nsec} {}
52+
53+ static PosixTimestamp now(clockid_t clock_id)
54+ {
55+ struct timespec ts;
56+ clock_gettime(clock_id, &ts);
57+ return PosixTimestamp(clock_id, ts);
58+ }
59+};
60+
61+}} // namespace mir::time
62+
63+#endif // MIR_TIME_POSIX_TIMESTAMP_H_
64
65=== added file 'include/platform/mir/graphics/atomic_frame.h'
66--- include/platform/mir/graphics/atomic_frame.h 1970-01-01 00:00:00 +0000
67+++ include/platform/mir/graphics/atomic_frame.h 2016-10-05 09:20:48 +0000
68@@ -0,0 +1,43 @@
69+/*
70+ * Copyright © 2016 Canonical Ltd.
71+ *
72+ * This program is free software: you can redistribute it and/or modify it
73+ * under the terms of the GNU Lesser General Public License version 3,
74+ * as published by the Free Software Foundation.
75+ *
76+ * This program is distributed in the hope that it will be useful,
77+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
78+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
79+ * GNU Lesser General Public License for more details.
80+ *
81+ * You should have received a copy of the GNU Lesser General Public License
82+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
83+ *
84+ * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
85+ */
86+
87+#ifndef MIR_GRAPHICS_ATOMIC_FRAME_H_
88+#define MIR_GRAPHICS_ATOMIC_FRAME_H_
89+
90+#include "mir/graphics/frame.h"
91+#include <mutex>
92+
93+namespace mir { namespace graphics {
94+
95+class AtomicFrame
96+{
97+public:
98+ Frame load() const;
99+ // Preferably use this and provide all fields from the driver:
100+ void store(Frame const&);
101+ // Or if your driver is limited these will suffice:
102+ void increment_now();
103+ void increment_with_timestamp(Frame::Timestamp t);
104+private:
105+ mutable std::mutex mutex;
106+ Frame frame;
107+};
108+
109+}} // namespace mir::graphics
110+
111+#endif // MIR_GRAPHICS_ATOMIC_FRAME_H_
112
113=== modified file 'include/platform/mir/graphics/display.h'
114--- include/platform/mir/graphics/display.h 2016-09-15 00:57:29 +0000
115+++ include/platform/mir/graphics/display.h 2016-10-05 09:20:48 +0000
116@@ -19,6 +19,7 @@
117 #ifndef MIR_GRAPHICS_DISPLAY_H_
118 #define MIR_GRAPHICS_DISPLAY_H_
119
120+#include "mir/graphics/frame.h"
121 #include <memory>
122 #include <functional>
123 #include <chrono>
124@@ -188,6 +189,22 @@
125 */
126 virtual NativeDisplay* native_display() = 0;
127
128+ /**
129+ * Returns timing information for the last frame displayed on a given
130+ * output.
131+ *
132+ * Frame timing will be provided to clients only when they request it.
133+ * This is to ensure idle clients never get woken by unwanted events.
134+ * It is also distinctly separate from the display configuration as this
135+ * timing information changes many times per second and should not interfere
136+ * with the more static display configuration.
137+ *
138+ * Note: Using unsigned here because DisplayConfigurationOutputId is
139+ * troublesome (can't be forward declared) and including
140+ * display_configuration.h to get it would be an overkill.
141+ */
142+ virtual Frame last_frame_on(unsigned output_id) const = 0;
143+
144 Display() = default;
145 virtual ~Display() = default;
146 private:
147
148=== added file 'include/platform/mir/graphics/frame.h'
149--- include/platform/mir/graphics/frame.h 1970-01-01 00:00:00 +0000
150+++ include/platform/mir/graphics/frame.h 2016-10-05 09:20:48 +0000
151@@ -0,0 +1,47 @@
152+/*
153+ * Copyright © 2016 Canonical Ltd.
154+ *
155+ * This program is free software: you can redistribute it and/or modify it
156+ * under the terms of the GNU Lesser General Public License version 3,
157+ * as published by the Free Software Foundation.
158+ *
159+ * This program is distributed in the hope that it will be useful,
160+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
161+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
162+ * GNU Lesser General Public License for more details.
163+ *
164+ * You should have received a copy of the GNU Lesser General Public License
165+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
166+ *
167+ * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
168+ */
169+
170+#ifndef MIR_GRAPHICS_FRAME_H_
171+#define MIR_GRAPHICS_FRAME_H_
172+
173+#include "mir/time/posix_timestamp.h"
174+#include <cstdint>
175+
176+namespace mir { namespace graphics {
177+
178+/**
179+ * Frame is a unique identifier for a frame displayed on an output.
180+ *
181+ * This MSC/UST terminology is used because that's what the rest of the
182+ * industry calls it:
183+ * GLX: https://www.opengl.org/registry/specs/OML/glx_sync_control.txt
184+ * WGL: https://www.opengl.org/registry/specs/OML/wgl_sync_control.txt
185+ * EGL: https://bugs.chromium.org/p/chromium/issues/attachmentText?aid=178027
186+ * Mesa: "get_sync_values" functions
187+ */
188+struct Frame
189+{
190+ typedef mir::time::PosixTimestamp Timestamp;
191+
192+ int64_t msc = 0; /**< Media Stream Counter */
193+ Timestamp ust; /**< Unadjusted System Time */
194+};
195+
196+}} // namespace mir::graphics
197+
198+#endif // MIR_GRAPHICS_FRAME_H_
199
200=== modified file 'include/test/mir/test/doubles/mock_egl.h'
201--- include/test/mir/test/doubles/mock_egl.h 2016-07-15 09:37:28 +0000
202+++ include/test/mir/test/doubles/mock_egl.h 2016-10-05 09:20:48 +0000
203@@ -154,6 +154,10 @@
204 MOCK_METHOD2(eglDestroySyncKHR, EGLBoolean(EGLDisplay, EGLSyncKHR));
205 MOCK_METHOD4(eglClientWaitSyncKHR, EGLint(EGLDisplay, EGLSyncKHR, EGLint, EGLTimeKHR));
206
207+ MOCK_METHOD5(eglGetSyncValuesCHROMIUM, EGLBoolean(EGLDisplay, EGLSurface,
208+ int64_t*, int64_t*,
209+ int64_t*));
210+
211 EGLDisplay const fake_egl_display;
212 EGLConfig const* const fake_configs;
213 EGLint const fake_configs_num;
214
215=== modified file 'include/test/mir/test/doubles/null_display.h'
216--- include/test/mir/test/doubles/null_display.h 2016-09-15 00:57:28 +0000
217+++ include/test/mir/test/doubles/null_display.h 2016-10-05 09:20:48 +0000
218@@ -82,6 +82,10 @@
219 {
220 return std::unique_ptr<NullGLContext>{new NullGLContext()};
221 }
222+ graphics::Frame last_frame_on(unsigned) const override
223+ {
224+ return {};
225+ }
226 NullDisplaySyncGroup group;
227 };
228
229
230=== modified file 'src/include/platform/mir/graphics/display_report.h'
231--- src/include/platform/mir/graphics/display_report.h 2016-09-09 07:10:01 +0000
232+++ src/include/platform/mir/graphics/display_report.h 2016-10-05 09:20:48 +0000
233@@ -25,6 +25,7 @@
234 {
235 namespace graphics
236 {
237+struct Frame;
238
239 class DisplayReport
240 {
241@@ -35,7 +36,7 @@
242 virtual void report_successful_egl_buffer_swap_on_construction() = 0;
243 virtual void report_successful_display_construction() = 0;
244 virtual void report_egl_configuration(EGLDisplay disp, EGLConfig cfg) = 0;
245- virtual void report_vsync(unsigned int output_id) = 0;
246+ virtual void report_vsync(unsigned int output_id, Frame const& f) = 0;
247
248 /* gbm specific */
249 virtual void report_successful_drm_mode_set_crtc_on_construction() = 0;
250
251=== modified file 'src/platform/graphics/CMakeLists.txt'
252--- src/platform/graphics/CMakeLists.txt 2016-09-01 17:02:56 +0000
253+++ src/platform/graphics/CMakeLists.txt 2016-10-05 09:20:48 +0000
254@@ -12,6 +12,7 @@
255 pixel_format_utils.cpp
256 overlapping_output_grouping.cpp
257 platform_probe.cpp
258+ atomic_frame.cpp
259 )
260
261 add_library(mirplatformgraphicscommon OBJECT
262
263=== added file 'src/platform/graphics/atomic_frame.cpp'
264--- src/platform/graphics/atomic_frame.cpp 1970-01-01 00:00:00 +0000
265+++ src/platform/graphics/atomic_frame.cpp 2016-10-05 09:20:48 +0000
266@@ -0,0 +1,50 @@
267+/*
268+ * Copyright © 2016 Canonical Ltd.
269+ *
270+ * This program is free software: you can redistribute it and/or modify it
271+ * under the terms of the GNU Lesser General Public License version 3,
272+ * as published by the Free Software Foundation.
273+ *
274+ * This program is distributed in the hope that it will be useful,
275+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
276+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
277+ * GNU Lesser General Public License for more details.
278+ *
279+ * You should have received a copy of the GNU Lesser General Public License
280+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
281+ *
282+ * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
283+ */
284+
285+#include "mir/graphics/atomic_frame.h"
286+#include "mir/log.h"
287+
288+namespace mir { namespace graphics {
289+
290+Frame AtomicFrame::load() const
291+{
292+ std::lock_guard<decltype(mutex)> lock(mutex);
293+ return frame;
294+}
295+
296+void AtomicFrame::store(Frame const& f)
297+{
298+ std::lock_guard<decltype(mutex)> lock(mutex);
299+ frame = f;
300+}
301+
302+void AtomicFrame::increment_now()
303+{
304+ std::lock_guard<decltype(mutex)> lock(mutex);
305+ frame.ust = Frame::Timestamp::now(frame.ust.clock_id);
306+ frame.msc++;
307+}
308+
309+void AtomicFrame::increment_with_timestamp(Frame::Timestamp t)
310+{
311+ std::lock_guard<decltype(mutex)> lock(mutex);
312+ frame.ust = t;
313+ frame.msc++;
314+}
315+
316+}} // namespace mir::graphics
317
318=== modified file 'src/platform/symbols.map'
319--- src/platform/symbols.map 2016-09-19 06:09:48 +0000
320+++ src/platform/symbols.map 2016-10-05 09:20:48 +0000
321@@ -271,6 +271,10 @@
322 android::isEligibleBuiltInKeyboard*;
323 __android_log_assert;
324 __android_log_print;
325+
326+ mir::graphics::AtomicFrame::load*;
327+ mir::graphics::AtomicFrame::store*;
328+ mir::graphics::AtomicFrame::increment*
329 };
330 local: *;
331 };
332
333=== modified file 'src/platforms/android/server/device_quirks.cpp'
334--- src/platforms/android/server/device_quirks.cpp 2016-08-07 20:38:35 +0000
335+++ src/platforms/android/server/device_quirks.cpp 2016-10-05 09:20:48 +0000
336@@ -107,7 +107,7 @@
337 void report_successful_egl_buffer_swap_on_construction() override {};
338 void report_successful_display_construction() override {};
339 void report_egl_configuration(EGLDisplay, EGLConfig) override {};
340- void report_vsync(unsigned int) override {};
341+ void report_vsync(unsigned int, mg::Frame const&) override {};
342 void report_successful_drm_mode_set_crtc_on_construction() override {};
343 void report_drm_master_failure(int) override {};
344 void report_vt_switch_away_failure() override {};
345
346=== modified file 'src/platforms/android/server/display.cpp'
347--- src/platforms/android/server/display.cpp 2016-09-15 00:57:28 +0000
348+++ src/platforms/android/server/display.cpp 2016-10-05 09:20:48 +0000
349@@ -17,6 +17,7 @@
350 */
351
352 #include "mir/graphics/platform.h"
353+#include "mir/graphics/frame.h"
354 #include "display_configuration.h"
355 #include "mir/graphics/display_report.h"
356 #include "mir/graphics/display_buffer.h"
357@@ -151,7 +152,9 @@
358 hwc_config{display_buffer_builder->create_hwc_configuration()},
359 hotplug_subscription{hwc_config->subscribe_to_config_changes(
360 std::bind(&mga::Display::on_hotplug, this),
361- std::bind(&mga::Display::on_vsync, this, std::placeholders::_1))},
362+ std::bind(&mga::Display::on_vsync, this,
363+ std::placeholders::_1,
364+ std::placeholders::_2))},
365 config(
366 hwc_config->active_config_for(mga::DisplayName::primary),
367 mir_power_mode_off,
368@@ -296,9 +299,32 @@
369 display_change_pipe->notify_change();
370 }
371
372-void mga::Display::on_vsync(DisplayName name) const
373-{
374- display_report->report_vsync(as_output_id(name).as_value());
375+void mga::Display::on_vsync(DisplayName name, mg::Frame::Timestamp timestamp)
376+{
377+ std::lock_guard<decltype(vsync_mutex)> lock{vsync_mutex};
378+ /*
379+ * XXX It's presently useful but idealistically inefficient that we
380+ * get a callback on every frame, even when the compositor is idle.
381+ * (LP: #1374318)
382+ * Although we possibly shouldn't fix that at all because the
383+ * call to increment_with_timestamp would produce incorrect MSC values
384+ * if it were to miss a physical frame. The X11 platform has that
385+ * problem already, but it's less important for production use than
386+ * Android.
387+ */
388+ auto& f = last_frame[as_output_id(name).as_value()];
389+ f.increment_with_timestamp(timestamp);
390+ display_report->report_vsync(as_output_id(name).as_value(), f.load());
391+}
392+
393+mg::Frame mga::Display::last_frame_on(unsigned output_id) const
394+{
395+ std::lock_guard<decltype(vsync_mutex)> lock{vsync_mutex};
396+ auto last = last_frame.find(output_id);
397+ if (last == last_frame.end())
398+ return {}; // Not an error. It might be a valid output_id pre-vsync
399+ else
400+ return last->second.load();
401 }
402
403 void mga::Display::register_configuration_change_handler(
404
405=== modified file 'src/platforms/android/server/display.h'
406--- src/platforms/android/server/display.h 2016-09-15 00:57:28 +0000
407+++ src/platforms/android/server/display.h 2016-10-05 09:20:48 +0000
408@@ -20,6 +20,8 @@
409 #define MIR_GRAPHICS_ANDROID_DISPLAY_H_
410
411 #include "mir/graphics/display.h"
412+#include "mir/graphics/frame.h"
413+#include "mir/graphics/atomic_frame.h"
414 #include "mir/renderer/gl/context_source.h"
415 #include "gl_context.h"
416 #include "display_group.h"
417@@ -30,6 +32,7 @@
418 #include <memory>
419 #include <mutex>
420 #include <array>
421+#include <unordered_map>
422
423 namespace mir
424 {
425@@ -88,9 +91,11 @@
426
427 std::unique_ptr<renderer::gl::Context> create_gl_context() override;
428
429+ Frame last_frame_on(unsigned output_id) const override;
430+
431 private:
432 void on_hotplug();
433- void on_vsync(DisplayName) const;
434+ void on_vsync(DisplayName, graphics::Frame::Timestamp);
435
436 geometry::Point const origin{0,0};
437 std::shared_ptr<DisplayReport> const display_report;
438@@ -109,6 +114,9 @@
439 OverlayOptimization const overlay_option;
440
441 void update_configuration(std::lock_guard<decltype(configuration_mutex)> const&) const;
442+
443+ std::mutex mutable vsync_mutex;
444+ std::unordered_map<unsigned,AtomicFrame> last_frame;
445 };
446
447 }
448
449=== modified file 'src/platforms/android/server/fb_device.cpp'
450--- src/platforms/android/server/fb_device.cpp 2016-09-22 16:27:13 +0000
451+++ src/platforms/android/server/fb_device.cpp 2016-10-05 09:20:48 +0000
452@@ -97,7 +97,7 @@
453
454 mga::ConfigChangeSubscription mga::FbControl::subscribe_to_config_changes(
455 std::function<void()> const&,
456- std::function<void(DisplayName)> const&)
457+ std::function<void(DisplayName, mg::Frame::Timestamp)> const&)
458 {
459 return nullptr;
460 }
461
462=== modified file 'src/platforms/android/server/fb_device.h'
463--- src/platforms/android/server/fb_device.h 2016-05-03 06:55:25 +0000
464+++ src/platforms/android/server/fb_device.h 2016-10-05 09:20:48 +0000
465@@ -39,7 +39,7 @@
466 DisplayConfigurationOutput active_config_for(DisplayName) override;
467 ConfigChangeSubscription subscribe_to_config_changes(
468 std::function<void()> const& hotplug_cb,
469- std::function<void(DisplayName)> const& vsync_cb) override;
470+ std::function<void(DisplayName,graphics::Frame::Timestamp)> const& vsync_cb) override;
471 private:
472 std::shared_ptr<framebuffer_device_t> const fb_device;
473 };
474
475=== modified file 'src/platforms/android/server/hwc_blanking_control.cpp'
476--- src/platforms/android/server/hwc_blanking_control.cpp 2016-09-19 04:16:15 +0000
477+++ src/platforms/android/server/hwc_blanking_control.cpp 2016-10-05 09:20:48 +0000
478@@ -227,13 +227,13 @@
479 std::shared_ptr<mga::HwcWrapper> const& hwc_device,
480 void const* subscriber,
481 std::function<void()> const& hotplug,
482- std::function<void(mga::DisplayName)> const& vsync)
483+ std::function<void(mga::DisplayName, mg::Frame::Timestamp)> const& vsync)
484 {
485 return std::make_shared<
486 mir::raii::PairedCalls<std::function<void()>, std::function<void()>>>(
487 [hotplug, vsync, subscriber, hwc_device]{
488 hwc_device->subscribe_to_events(subscriber,
489- [vsync](mga::DisplayName name, std::chrono::nanoseconds){ vsync(name); },
490+ [vsync](mga::DisplayName name, mg::Frame::Timestamp ts){ vsync(name, ts); },
491 [hotplug](mga::DisplayName, bool){ hotplug(); },
492 []{});
493 },
494@@ -259,7 +259,7 @@
495
496 mga::ConfigChangeSubscription mga::HwcBlankingControl::subscribe_to_config_changes(
497 std::function<void()> const& hotplug,
498- std::function<void(DisplayName)> const& vsync)
499+ std::function<void(DisplayName, mg::Frame::Timestamp)> const& vsync)
500 {
501 return ::subscribe_to_config_changes(hwc_device, this, hotplug, vsync);
502 }
503@@ -322,7 +322,7 @@
504
505 mga::ConfigChangeSubscription mga::HwcPowerModeControl::subscribe_to_config_changes(
506 std::function<void()> const& hotplug,
507- std::function<void(DisplayName)> const& vsync)
508+ std::function<void(DisplayName, mg::Frame::Timestamp)> const& vsync)
509 {
510 return ::subscribe_to_config_changes(hwc_device, this, hotplug, vsync);
511 }
512
513=== modified file 'src/platforms/android/server/hwc_configuration.h'
514--- src/platforms/android/server/hwc_configuration.h 2016-08-24 02:09:08 +0000
515+++ src/platforms/android/server/hwc_configuration.h 2016-10-05 09:20:48 +0000
516@@ -20,6 +20,7 @@
517 #define MIR_GRAPHICS_ANDROID_HWC_CONFIGURATION_H_
518
519 #include "mir/graphics/display_configuration.h"
520+#include "mir/graphics/frame.h"
521 #include "mir/geometry/size.h"
522 #include "display_name.h"
523 #include <memory>
524@@ -42,7 +43,7 @@
525 virtual DisplayConfigurationOutput active_config_for(DisplayName) = 0;
526 virtual ConfigChangeSubscription subscribe_to_config_changes(
527 std::function<void()> const& hotplug_cb,
528- std::function<void(DisplayName)> const& vsync_cb) = 0;
529+ std::function<void(DisplayName,graphics::Frame::Timestamp)> const& vsync_cb) = 0;
530
531 protected:
532 HwcConfiguration() = default;
533@@ -60,7 +61,7 @@
534 DisplayConfigurationOutput active_config_for(DisplayName) override;
535 ConfigChangeSubscription subscribe_to_config_changes(
536 std::function<void()> const& hotplug_cb,
537- std::function<void(DisplayName)> const& vsync_cb) override;
538+ std::function<void(DisplayName,graphics::Frame::Timestamp)> const& vsync_cb) override;
539
540 private:
541 std::shared_ptr<HwcWrapper> const hwc_device;
542@@ -77,7 +78,7 @@
543 DisplayConfigurationOutput active_config_for(DisplayName) override;
544 ConfigChangeSubscription subscribe_to_config_changes(
545 std::function<void()> const& hotplug_cb,
546- std::function<void(DisplayName)> const& vsync_cb) override;
547+ std::function<void(DisplayName,graphics::Frame::Timestamp)> const& vsync_cb) override;
548
549 private:
550 std::shared_ptr<HwcWrapper> const hwc_device;
551
552=== modified file 'src/platforms/android/server/hwc_fb_device.cpp'
553--- src/platforms/android/server/hwc_fb_device.cpp 2016-09-19 04:16:15 +0000
554+++ src/platforms/android/server/hwc_fb_device.cpp 2016-10-05 09:20:48 +0000
555@@ -98,7 +98,7 @@
556 vsync_trigger.wait(lk, [this]{return vsync_occurred;});
557 }
558
559-void mga::HwcFbDevice::notify_vsync(mga::DisplayName, std::chrono::nanoseconds)
560+void mga::HwcFbDevice::notify_vsync(mga::DisplayName, mg::Frame::Timestamp)
561 {
562 std::unique_lock<std::mutex> lk(vsync_wait_mutex);
563 vsync_occurred = true;
564
565=== modified file 'src/platforms/android/server/hwc_fb_device.h'
566--- src/platforms/android/server/hwc_fb_device.h 2016-05-03 06:55:25 +0000
567+++ src/platforms/android/server/hwc_fb_device.h 2016-10-05 09:20:48 +0000
568@@ -19,6 +19,7 @@
569 #ifndef MIR_GRAPHICS_ANDROID_HWC_FB_DEVICE_H_
570 #define MIR_GRAPHICS_ANDROID_HWC_FB_DEVICE_H_
571
572+#include "mir/graphics/frame.h"
573 #include "display_device.h"
574 #include "hardware/gralloc.h"
575 #include "hardware/fb.h"
576@@ -56,7 +57,7 @@
577 std::mutex vsync_wait_mutex;
578 std::condition_variable vsync_trigger;
579 bool vsync_occurred;
580- void notify_vsync(DisplayName, std::chrono::nanoseconds);
581+ void notify_vsync(DisplayName, graphics::Frame::Timestamp);
582 };
583
584 }
585
586=== modified file 'src/platforms/android/server/hwc_wrapper.h'
587--- src/platforms/android/server/hwc_wrapper.h 2015-06-17 05:20:42 +0000
588+++ src/platforms/android/server/hwc_wrapper.h 2016-10-05 09:20:48 +0000
589@@ -19,12 +19,12 @@
590 #ifndef MIR_GRAPHICS_ANDROID_HWC_WRAPPER_H_
591 #define MIR_GRAPHICS_ANDROID_HWC_WRAPPER_H_
592
593+#include "mir/graphics/frame.h"
594 #include "mir/int_wrapper.h"
595 #include "display_name.h"
596 #include "power_mode.h"
597 #include <array>
598 #include <functional>
599-#include <chrono>
600 #include <vector>
601
602 struct hwc_display_contents_1;
603@@ -51,7 +51,7 @@
604 //As with the HWC api, these events MUST NOT call-back to the other functions in HwcWrapper.
605 virtual void subscribe_to_events(
606 void const* subscriber,
607- std::function<void(DisplayName, std::chrono::nanoseconds)> const& vsync_callback,
608+ std::function<void(DisplayName, graphics::Frame::Timestamp)> const& vsync_callback,
609 std::function<void(DisplayName, bool)> const& hotplug_callback,
610 std::function<void()> const& invalidate_callback) = 0;
611 virtual void unsubscribe_from_events(void const* subscriber) noexcept = 0;
612
613=== modified file 'src/platforms/android/server/real_hwc_wrapper.cpp'
614--- src/platforms/android/server/real_hwc_wrapper.cpp 2016-05-03 06:55:25 +0000
615+++ src/platforms/android/server/real_hwc_wrapper.cpp 2016-10-05 09:20:48 +0000
616@@ -16,6 +16,7 @@
617 * Authored by: Kevin DuBois <kevin.dubois@canonical.com>
618 */
619
620+#include "mir/graphics/frame.h"
621 #include "real_hwc_wrapper.h"
622 #include "hwc_report.h"
623 #include "display_device_exceptions.h"
624@@ -24,6 +25,7 @@
625 #include <sstream>
626 #include <algorithm>
627
628+namespace mg = mir::graphics;
629 namespace mga=mir::graphics::android;
630
631 namespace
632@@ -66,7 +68,13 @@
633 mga::HwcCallbacks const* callbacks{nullptr};
634 std::unique_lock<std::mutex> lk(callback_lock);
635 if ((callbacks = reinterpret_cast<mga::HwcCallbacks const*>(procs)) && callbacks->self)
636- callbacks->self->vsync(display_name(display), std::chrono::nanoseconds{timestamp});
637+ {
638+ // hwcomposer.h says the clock used is CLOCK_MONOTONIC, and testing
639+ // on various devices confirms this is the case...
640+ mg::Frame::Timestamp hwc_time{CLOCK_MONOTONIC,
641+ std::chrono::nanoseconds{timestamp}};
642+ callbacks->self->vsync(display_name(display), hwc_time);
643+ }
644 }
645
646 static void hotplug_hook(const struct hwc_procs* procs, int display, int connected)
647@@ -185,7 +193,7 @@
648
649 void mga::RealHwcWrapper::subscribe_to_events(
650 void const* subscriber,
651- std::function<void(DisplayName, std::chrono::nanoseconds)> const& vsync,
652+ std::function<void(DisplayName, mg::Frame::Timestamp)> const& vsync,
653 std::function<void(DisplayName, bool)> const& hotplug,
654 std::function<void()> const& invalidate)
655 {
656@@ -201,7 +209,7 @@
657 callback_map.erase(it);
658 }
659
660-void mga::RealHwcWrapper::vsync(DisplayName name, std::chrono::nanoseconds timestamp) noexcept
661+void mga::RealHwcWrapper::vsync(DisplayName name, mg::Frame::Timestamp timestamp) noexcept
662 {
663 std::unique_lock<std::mutex> lk(callback_map_lock);
664 for(auto const& callbacks : callback_map)
665
666=== modified file 'src/platforms/android/server/real_hwc_wrapper.h'
667--- src/platforms/android/server/real_hwc_wrapper.h 2016-05-03 06:55:25 +0000
668+++ src/platforms/android/server/real_hwc_wrapper.h 2016-10-05 09:20:48 +0000
669@@ -52,7 +52,7 @@
670
671 void subscribe_to_events(
672 void const* subscriber,
673- std::function<void(DisplayName, std::chrono::nanoseconds)> const& vsync_callback,
674+ std::function<void(DisplayName, graphics::Frame::Timestamp)> const& vsync_callback,
675 std::function<void(DisplayName, bool)> const& hotplug_callback,
676 std::function<void()> const& invalidate_callback) override;
677 void unsubscribe_from_events(void const* subscriber) noexcept override;
678@@ -71,7 +71,7 @@
679 ConfigId active_config_for(DisplayName name) const override;
680 void set_active_config(DisplayName name, ConfigId id) const override;
681
682- void vsync(DisplayName, std::chrono::nanoseconds) noexcept;
683+ void vsync(DisplayName, graphics::Frame::Timestamp) noexcept;
684 void hotplug(DisplayName, bool) noexcept;
685 void invalidate() noexcept;
686
687@@ -82,7 +82,7 @@
688 std::mutex callback_map_lock;
689 struct Callbacks
690 {
691- std::function<void(DisplayName, std::chrono::nanoseconds)> vsync;
692+ std::function<void(DisplayName, graphics::Frame::Timestamp)> vsync;
693 std::function<void(DisplayName, bool)> hotplug;
694 std::function<void()> invalidate;
695 };
696
697=== modified file 'src/platforms/android/utils/render_overlays.cpp'
698--- src/platforms/android/utils/render_overlays.cpp 2016-09-19 06:09:48 +0000
699+++ src/platforms/android/utils/render_overlays.cpp 2016-10-05 09:20:48 +0000
700@@ -233,7 +233,7 @@
701 void report_successful_egl_buffer_swap_on_construction() override {}
702 void report_successful_display_construction() override {}
703 void report_egl_configuration(EGLDisplay, EGLConfig) override {}
704- void report_vsync(unsigned int) override {}
705+ void report_vsync(unsigned int, mg::Frame const&) override {}
706 void report_successful_drm_mode_set_crtc_on_construction() override {}
707 void report_drm_master_failure(int) override {}
708 void report_vt_switch_away_failure() override {}
709
710=== modified file 'src/platforms/eglstream-kms/server/display.cpp'
711--- src/platforms/eglstream-kms/server/display.cpp 2016-09-21 06:15:45 +0000
712+++ src/platforms/eglstream-kms/server/display.cpp 2016-10-05 09:20:48 +0000
713@@ -373,3 +373,12 @@
714 return false;
715 }
716
717+mg::Frame mge::Display::last_frame_on(unsigned) const
718+{
719+ /*
720+ * TODO: Implement this later when we have the hardware + driver to test on.
721+ * If no proper hardware counters are available, just call
722+ * AtomicFrame.increment_now() in post() above.
723+ */
724+ return {};
725+}
726
727=== modified file 'src/platforms/eglstream-kms/server/display.h'
728--- src/platforms/eglstream-kms/server/display.h 2016-09-15 00:57:28 +0000
729+++ src/platforms/eglstream-kms/server/display.h 2016-10-05 09:20:48 +0000
730@@ -70,6 +70,7 @@
731 NativeDisplay* native_display() override;
732
733 std::unique_ptr<renderer::gl::Context> create_gl_context() override;
734+ Frame last_frame_on(unsigned output_id) const override;
735
736 private:
737 mir::Fd const drm_node;
738
739=== modified file 'src/platforms/mesa/server/kms/display.cpp'
740--- src/platforms/mesa/server/kms/display.cpp 2016-09-15 00:57:28 +0000
741+++ src/platforms/mesa/server/kms/display.cpp 2016-10-05 09:20:48 +0000
742@@ -421,3 +421,9 @@
743 {
744 return false;
745 }
746+
747+mg::Frame mgm::Display::last_frame_on(unsigned output_id) const
748+{
749+ auto output = output_container.get_kms_output_for(output_id);
750+ return output->last_frame();
751+}
752
753=== modified file 'src/platforms/mesa/server/kms/display.h'
754--- src/platforms/mesa/server/kms/display.h 2016-09-15 00:57:28 +0000
755+++ src/platforms/mesa/server/kms/display.h 2016-10-05 09:20:48 +0000
756@@ -99,6 +99,8 @@
757
758 std::unique_ptr<renderer::gl::Context> create_gl_context() override;
759
760+ Frame last_frame_on(unsigned output_id) const override;
761+
762 private:
763 void clear_connected_unused_outputs();
764
765@@ -110,7 +112,7 @@
766 mir::udev::Monitor monitor;
767 helpers::EGLHelper shared_egl;
768 std::vector<std::unique_ptr<DisplayBuffer>> display_buffers;
769- RealKMSOutputContainer output_container;
770+ mutable RealKMSOutputContainer output_container;
771 mutable RealKMSDisplayConfiguration current_display_configuration;
772 mutable std::atomic<bool> dirty_configuration;
773
774
775=== modified file 'src/platforms/mesa/server/kms/kms_output.h'
776--- src/platforms/mesa/server/kms/kms_output.h 2016-09-01 17:02:56 +0000
777+++ src/platforms/mesa/server/kms/kms_output.h 2016-10-05 09:20:48 +0000
778@@ -23,6 +23,7 @@
779 #include "mir/geometry/point.h"
780 #include "mir/geometry/displacement.h"
781 #include "mir/graphics/display_configuration.h"
782+#include "mir/graphics/frame.h"
783 #include "mir_toolkit/common.h"
784
785 #include <gbm.h>
786@@ -63,6 +64,7 @@
787
788 virtual void set_power_mode(MirPowerMode mode) = 0;
789 virtual void set_gamma(GammaCurves const& gamma) = 0;
790+ virtual Frame last_frame() const = 0;
791
792 protected:
793 KMSOutput() = default;
794
795=== modified file 'src/platforms/mesa/server/kms/kms_page_flipper.cpp'
796--- src/platforms/mesa/server/kms/kms_page_flipper.cpp 2016-09-09 07:51:51 +0000
797+++ src/platforms/mesa/server/kms/kms_page_flipper.cpp 2016-10-05 09:20:48 +0000
798@@ -25,18 +25,22 @@
799
800 #include <xf86drm.h>
801 #include <xf86drmMode.h>
802+#include <chrono>
803
804+namespace mg = mir::graphics;
805 namespace mgm = mir::graphics::mesa;
806
807 namespace
808 {
809
810-void page_flip_handler(int /*fd*/, unsigned int /*frame*/,
811- unsigned int /*sec*/, unsigned int /*usec*/,
812+void page_flip_handler(int /*fd*/, unsigned int seq,
813+ unsigned int sec, unsigned int usec,
814 void* data)
815 {
816 auto page_flip_data = static_cast<mgm::PageFlipEventData*>(data);
817- page_flip_data->flipper->notify_page_flip(page_flip_data->crtc_id);
818+ std::chrono::nanoseconds ns{sec*1000000000LL + usec*1000LL};
819+ page_flip_data->flipper->notify_page_flip(page_flip_data->crtc_id,
820+ seq, ns);
821 }
822
823 }
824@@ -49,6 +53,11 @@
825 pending_page_flips(),
826 worker_tid()
827 {
828+ uint64_t mono = 0;
829+ if (drmGetCap(drm_fd, DRM_CAP_TIMESTAMP_MONOTONIC, &mono) || !mono)
830+ clock_id = CLOCK_REALTIME;
831+ else
832+ clock_id = CLOCK_MONOTONIC;
833 }
834
835 bool mgm::KMSPageFlipper::schedule_flip(uint32_t crtc_id,
836@@ -72,7 +81,7 @@
837 return (ret == 0);
838 }
839
840-void mgm::KMSPageFlipper::wait_for_flip(uint32_t crtc_id)
841+mg::Frame mgm::KMSPageFlipper::wait_for_flip(uint32_t crtc_id)
842 {
843 static drmEventContext evctx =
844 {
845@@ -94,7 +103,7 @@
846
847 /* If the page flip we are waiting for has arrived we are done. */
848 if (page_flip_is_done(crtc_id))
849- return;
850+ return completed_page_flips[crtc_id];
851
852 /* ...otherwise we become the worker */
853 worker_tid = std::this_thread::get_id();
854@@ -144,6 +153,7 @@
855 */
856 pf_cv.notify_all();
857 }
858+ return completed_page_flips[crtc_id];
859 }
860
861 std::thread::id mgm::KMSPageFlipper::debug_get_worker_tid()
862@@ -159,12 +169,16 @@
863 return pending_page_flips.find(crtc_id) == pending_page_flips.end();
864 }
865
866-void mgm::KMSPageFlipper::notify_page_flip(uint32_t crtc_id)
867+void mgm::KMSPageFlipper::notify_page_flip(uint32_t crtc_id, int64_t msc,
868+ std::chrono::nanoseconds ust)
869 {
870 auto pending = pending_page_flips.find(crtc_id);
871 if (pending != pending_page_flips.end())
872 {
873- report->report_vsync(pending->second.connector_id);
874+ auto& frame = completed_page_flips[crtc_id];
875+ frame.msc = msc;
876+ frame.ust = {clock_id, ust};
877+ report->report_vsync(pending->second.connector_id, frame);
878 pending_page_flips.erase(pending);
879 }
880 }
881
882=== modified file 'src/platforms/mesa/server/kms/kms_page_flipper.h'
883--- src/platforms/mesa/server/kms/kms_page_flipper.h 2016-09-09 07:10:01 +0000
884+++ src/platforms/mesa/server/kms/kms_page_flipper.h 2016-10-05 09:20:48 +0000
885@@ -26,7 +26,7 @@
886 #include <mutex>
887 #include <condition_variable>
888 #include <thread>
889-
890+#include <ctime>
891 #include <sys/time.h>
892
893 namespace mir
894@@ -53,20 +53,22 @@
895 KMSPageFlipper(int drm_fd, std::shared_ptr<DisplayReport> const& report);
896
897 bool schedule_flip(uint32_t crtc_id, uint32_t fb_id, uint32_t connector_id) override;
898- void wait_for_flip(uint32_t crtc_id) override;
899+ Frame wait_for_flip(uint32_t crtc_id) override;
900
901 std::thread::id debug_get_worker_tid();
902
903- void notify_page_flip(uint32_t crtc_id);
904+ void notify_page_flip(uint32_t crtc_id, int64_t msc, std::chrono::nanoseconds ust);
905 private:
906 bool page_flip_is_done(uint32_t crtc_id);
907
908 int const drm_fd;
909 std::shared_ptr<DisplayReport> const report;
910 std::unordered_map<uint32_t,PageFlipEventData> pending_page_flips;
911+ std::unordered_map<uint32_t,Frame> completed_page_flips;
912 std::mutex pf_mutex;
913 std::condition_variable pf_cv;
914 std::thread::id worker_tid;
915+ clockid_t clock_id;
916 };
917
918 }
919
920=== modified file 'src/platforms/mesa/server/kms/page_flipper.h'
921--- src/platforms/mesa/server/kms/page_flipper.h 2016-09-09 07:10:01 +0000
922+++ src/platforms/mesa/server/kms/page_flipper.h 2016-10-05 09:20:48 +0000
923@@ -19,6 +19,7 @@
924 #ifndef MIR_GRAPHICS_MESA_PAGE_FLIPPER_H_
925 #define MIR_GRAPHICS_MESA_PAGE_FLIPPER_H_
926
927+#include "mir/graphics/frame.h"
928 #include <cstdint>
929
930 namespace mir
931@@ -34,7 +35,7 @@
932 virtual ~PageFlipper() {}
933
934 virtual bool schedule_flip(uint32_t crtc_id, uint32_t fb_id, uint32_t connector_id) = 0;
935- virtual void wait_for_flip(uint32_t crtc_id) = 0;
936+ virtual Frame wait_for_flip(uint32_t crtc_id) = 0;
937
938 protected:
939 PageFlipper() = default;
940
941=== modified file 'src/platforms/mesa/server/kms/real_kms_output.cpp'
942--- src/platforms/mesa/server/kms/real_kms_output.cpp 2016-09-09 07:10:01 +0000
943+++ src/platforms/mesa/server/kms/real_kms_output.cpp 2016-10-05 09:20:48 +0000
944@@ -181,7 +181,13 @@
945 fatal_error("Output %s has no associated CRTC to wait on",
946 mgk::connector_name(connector).c_str());
947 }
948- page_flipper->wait_for_flip(current_crtc->crtc_id);
949+
950+ last_frame_.store(page_flipper->wait_for_flip(current_crtc->crtc_id));
951+}
952+
953+mg::Frame mgm::RealKMSOutput::last_frame() const
954+{
955+ return last_frame_.load();
956 }
957
958 void mgm::RealKMSOutput::set_cursor(gbm_bo* buffer)
959
960=== modified file 'src/platforms/mesa/server/kms/real_kms_output.h'
961--- src/platforms/mesa/server/kms/real_kms_output.h 2016-09-01 17:02:56 +0000
962+++ src/platforms/mesa/server/kms/real_kms_output.h 2016-10-05 09:20:48 +0000
963@@ -19,6 +19,7 @@
964 #ifndef MIR_GRAPHICS_MESA_REAL_KMS_OUTPUT_H_
965 #define MIR_GRAPHICS_MESA_REAL_KMS_OUTPUT_H_
966
967+#include "mir/graphics/atomic_frame.h"
968 #include "kms_output.h"
969 #include "kms-utils/drm_mode_resources.h"
970
971@@ -59,6 +60,8 @@
972 void set_power_mode(MirPowerMode mode) override;
973 void set_gamma(GammaCurves const& gamma) override;
974
975+ Frame last_frame() const override;
976+
977 private:
978 bool ensure_crtc();
979 void restore_saved_crtc();
980@@ -79,6 +82,8 @@
981 int dpms_enum_id;
982
983 std::mutex power_mutex;
984+
985+ AtomicFrame last_frame_;
986 };
987
988 }
989
990=== modified file 'src/platforms/mesa/server/x11/graphics/display.cpp'
991--- src/platforms/mesa/server/x11/graphics/display.cpp 2016-10-04 08:15:29 +0000
992+++ src/platforms/mesa/server/x11/graphics/display.cpp 2016-10-05 09:20:48 +0000
993@@ -22,6 +22,7 @@
994 #include "mir/graphics/virtual_output.h"
995 #include "mir/renderer/gl/context.h"
996 #include "mir/graphics/gl_config.h"
997+#include "mir/graphics/atomic_frame.h"
998 #include "display_configuration.h"
999 #include "display.h"
1000 #include "display_buffer.h"
1001@@ -235,7 +236,8 @@
1002 pixel_width{get_pixel_width(x_dpy)},
1003 pixel_height{get_pixel_height(x_dpy)},
1004 report{report},
1005- orientation{mir_orientation_normal}
1006+ orientation{mir_orientation_normal},
1007+ last_frame{std::make_shared<AtomicFrame>()}
1008 {
1009 shared_egl.setup(x_dpy);
1010
1011@@ -253,6 +255,7 @@
1012 *win,
1013 actual_size,
1014 shared_egl.context(),
1015+ last_frame,
1016 report,
1017 orientation,
1018 *gl_config);
1019@@ -344,3 +347,8 @@
1020 {
1021 return false;
1022 }
1023+
1024+mg::Frame mgx::Display::last_frame_on(unsigned) const
1025+{
1026+ return last_frame->load();
1027+}
1028
1029=== modified file 'src/platforms/mesa/server/x11/graphics/display.h'
1030--- src/platforms/mesa/server/x11/graphics/display.h 2016-10-04 08:15:29 +0000
1031+++ src/platforms/mesa/server/x11/graphics/display.h 2016-10-05 09:20:48 +0000
1032@@ -29,11 +29,14 @@
1033 #include <X11/Xutil.h>
1034 #include <EGL/egl.h>
1035
1036+#include <memory>
1037+
1038 namespace mir
1039 {
1040 namespace graphics
1041 {
1042
1043+class AtomicFrame;
1044 class GLConfig;
1045 class DisplayReport;
1046
1047@@ -98,6 +101,8 @@
1048
1049 std::unique_ptr<renderer::gl::Context> create_gl_context() override;
1050
1051+ Frame last_frame_on(unsigned output_id) const override;
1052+
1053 private:
1054 helpers::EGLHelper shared_egl;
1055 ::Display* const x_dpy;
1056@@ -109,6 +114,7 @@
1057 MirPixelFormat pf;
1058 std::shared_ptr<DisplayReport> const report;
1059 MirOrientation orientation; //TODO: keep entire current display configuration
1060+ std::shared_ptr<AtomicFrame> last_frame;
1061 std::unique_ptr<DisplayBuffer> display_buffer;
1062 };
1063
1064
1065=== modified file 'src/platforms/mesa/server/x11/graphics/display_buffer.cpp'
1066--- src/platforms/mesa/server/x11/graphics/display_buffer.cpp 2016-10-04 08:15:29 +0000
1067+++ src/platforms/mesa/server/x11/graphics/display_buffer.cpp 2016-10-05 09:20:48 +0000
1068@@ -17,10 +17,12 @@
1069 *
1070 */
1071
1072+#include "mir/graphics/atomic_frame.h"
1073 #include "mir/fatal.h"
1074 #include "display_buffer.h"
1075 #include "display_configuration.h"
1076 #include "mir/graphics/display_report.h"
1077+#include <cstring>
1078
1079 namespace mg=mir::graphics;
1080 namespace mgx=mg::X;
1081@@ -30,13 +32,16 @@
1082 Window const win,
1083 geom::Size const sz,
1084 EGLContext const shared_context,
1085+ std::shared_ptr<AtomicFrame> const& f,
1086 std::shared_ptr<DisplayReport> const& r,
1087 MirOrientation const o,
1088 GLConfig const& gl_config)
1089 : size{sz},
1090 report{r},
1091 orientation_{o},
1092- egl{gl_config}
1093+ egl{gl_config},
1094+ last_frame{f},
1095+ eglGetSyncValues{nullptr}
1096 {
1097 egl.setup(x_dpy, win, shared_context);
1098 egl.report_egl_configuration(
1099@@ -44,6 +49,36 @@
1100 {
1101 r->report_egl_configuration(disp, cfg);
1102 });
1103+
1104+ /*
1105+ * EGL_CHROMIUM_sync_control is an EGL extension that Google invented/copied
1106+ * so they could switch Chrome(ium) from GLX to EGL:
1107+ *
1108+ * https://bugs.chromium.org/p/chromium/issues/detail?id=366935
1109+ * https://www.opengl.org/registry/specs/OML/glx_sync_control.txt
1110+ *
1111+ * Most noteworthy is that the EGL extension only has one function, as
1112+ * Google realized that's all you need. You do not need wait functions or
1113+ * events if you already have accurate timestamps and the ability to sleep
1114+ * with high precision. In fact sync logic in clients will have higher
1115+ * precision if you implement the wait yourself relative to the correct
1116+ * kernel clock, than using IPC to implement the wait on the server.
1117+ *
1118+ * EGL_CHROMIUM_sync_control never got formally standardized and no longer
1119+ * needs to be since they switched ChromeOS over to Freon (native KMS).
1120+ * However this remains the correct and only way of doing it in EGL on X11.
1121+ * AFAIK the only existing implementation is Mesa.
1122+ */
1123+ auto const extensions = eglQueryString(egl.display(), EGL_EXTENSIONS);
1124+ auto const extension = "EGL_CHROMIUM_sync_control";
1125+ auto const found = strstr(extensions, extension);
1126+ if (found)
1127+ {
1128+ char end = found[strlen(extension)];
1129+ if (end == '\0' || end == ' ')
1130+ eglGetSyncValues = reinterpret_cast<EglGetSyncValuesCHROMIUM*>(
1131+ eglGetProcAddress("eglGetSyncValuesCHROMIUM"));
1132+ }
1133 }
1134
1135 geom::Rectangle mgx::DisplayBuffer::view_area() const
1136@@ -80,11 +115,37 @@
1137 fatal_error("Failed to perform buffer swap");
1138
1139 /*
1140+ * It would be nice to call this on demand as required. However the
1141+ * implementation requires an EGL context. So for simplicity we call it here
1142+ * on every frame.
1143+ * This does mean the current last_frame will be a bit out of date if
1144+ * the compositor missed a frame. But that doesn't actually matter because
1145+ * the consequence of that would be the client scheduling the next frame
1146+ * immediately without waiting, which is probably ideal anyway.
1147+ */
1148+ int64_t ust_us, msc, sbc;
1149+ if (eglGetSyncValues &&
1150+ eglGetSyncValues(egl.display(), egl.surface(), &ust_us, &msc, &sbc))
1151+ {
1152+ std::chrono::nanoseconds const ust_ns{ust_us * 1000};
1153+ mg::Frame frame;
1154+ frame.msc = msc;
1155+ frame.ust = {CLOCK_MONOTONIC, ust_ns};
1156+ last_frame->store(frame);
1157+ (void)sbc; // unused
1158+ }
1159+ else // Extension not available? Fall back to a reasonable estimate:
1160+ {
1161+ last_frame->increment_now();
1162+ }
1163+
1164+ /*
1165 * Admittedly we are not a real display and will miss some real vsyncs
1166 * but this is best-effort. And besides, we don't want Mir reporting all
1167 * real vsyncs because that would mean the compositor never sleeps.
1168 */
1169- report->report_vsync(mgx::DisplayConfiguration::the_output_id.as_value());
1170+ report->report_vsync(mgx::DisplayConfiguration::the_output_id.as_value(),
1171+ last_frame->load());
1172 }
1173
1174 void mgx::DisplayBuffer::bind()
1175
1176=== modified file 'src/platforms/mesa/server/x11/graphics/display_buffer.h'
1177--- src/platforms/mesa/server/x11/graphics/display_buffer.h 2016-10-04 08:15:29 +0000
1178+++ src/platforms/mesa/server/x11/graphics/display_buffer.h 2016-10-05 09:20:48 +0000
1179@@ -33,6 +33,7 @@
1180 namespace graphics
1181 {
1182
1183+class AtomicFrame;
1184 class GLConfig;
1185 class DisplayReport;
1186
1187@@ -50,6 +51,7 @@
1188 Window const win,
1189 geometry::Size const sz,
1190 EGLContext const shared_context,
1191+ std::shared_ptr<AtomicFrame> const& f,
1192 std::shared_ptr<DisplayReport> const& r,
1193 MirOrientation const o,
1194 GLConfig const& gl_config);
1195@@ -76,6 +78,12 @@
1196 std::shared_ptr<DisplayReport> const report;
1197 MirOrientation orientation_;
1198 helpers::EGLHelper egl;
1199+ std::shared_ptr<AtomicFrame> const last_frame;
1200+
1201+ typedef EGLBoolean (EGLAPIENTRY EglGetSyncValuesCHROMIUM)
1202+ (EGLDisplay dpy, EGLSurface surface, int64_t *ust,
1203+ int64_t *msc, int64_t *sbc);
1204+ EglGetSyncValuesCHROMIUM* eglGetSyncValues;
1205 };
1206
1207 }
1208
1209=== modified file 'src/platforms/mesa/server/x11/graphics/egl_helper.h'
1210--- src/platforms/mesa/server/x11/graphics/egl_helper.h 2016-09-23 22:45:16 +0000
1211+++ src/platforms/mesa/server/x11/graphics/egl_helper.h 2016-10-05 09:20:48 +0000
1212@@ -57,6 +57,7 @@
1213 EGLContext context() { return egl_context; }
1214 EGLDisplay display() { return egl_display; }
1215 EGLConfig config() { return egl_config; }
1216+ EGLSurface surface() const { return egl_surface; }
1217
1218 void report_egl_configuration(std::function<void(EGLDisplay, EGLConfig)>);
1219 private:
1220
1221=== modified file 'src/server/graphics/nested/display.cpp'
1222--- src/server/graphics/nested/display.cpp 2016-09-15 00:57:28 +0000
1223+++ src/server/graphics/nested/display.cpp 2016-10-05 09:20:48 +0000
1224@@ -372,3 +372,8 @@
1225 {
1226 return false;
1227 }
1228+
1229+mg::Frame mgn::Display::last_frame_on(unsigned) const
1230+{
1231+ return {}; // TODO after the client API exists for us to get it
1232+}
1233
1234=== modified file 'src/server/graphics/nested/display.h'
1235--- src/server/graphics/nested/display.h 2016-09-15 00:57:28 +0000
1236+++ src/server/graphics/nested/display.h 2016-10-05 09:20:48 +0000
1237@@ -150,6 +150,7 @@
1238
1239 NativeDisplay* native_display() override;
1240 std::unique_ptr<renderer::gl::Context> create_gl_context() override;
1241+ Frame last_frame_on(unsigned output_id) const override;
1242
1243 private:
1244 std::shared_ptr<graphics::Platform> const platform;
1245
1246=== modified file 'src/server/report/logging/display_report.cpp'
1247--- src/server/report/logging/display_report.cpp 2016-09-12 07:00:54 +0000
1248+++ src/server/report/logging/display_report.cpp 2016-10-05 09:20:48 +0000
1249@@ -21,16 +21,14 @@
1250 #include <EGL/eglext.h>
1251 #include <sstream>
1252 #include <cstring>
1253+#include <cstdlib>
1254
1255 namespace ml=mir::logging;
1256 namespace mrl=mir::report::logging;
1257
1258 mrl::DisplayReport::DisplayReport(
1259- std::shared_ptr<ml::Logger> const& logger,
1260- std::shared_ptr<time::Clock> const& clock) :
1261- logger(logger),
1262- clock(clock),
1263- last_report(clock->now())
1264+ std::shared_ptr<ml::Logger> const& logger)
1265+ : logger(logger)
1266 {
1267 }
1268
1269@@ -147,22 +145,33 @@
1270 }
1271 }
1272
1273-void mrl::DisplayReport::report_vsync(unsigned int output_id)
1274+void mrl::DisplayReport::report_vsync(unsigned int output_id,
1275+ graphics::Frame const& frame)
1276 {
1277- using namespace std::chrono;
1278- seconds const static report_interval{1};
1279- std::unique_lock<decltype(vsync_event_mutex)> lk(vsync_event_mutex);
1280- auto now = clock->now();
1281- event_map[output_id]++;
1282- if (now > last_report + report_interval)
1283+ std::lock_guard<decltype(vsync_event_mutex)> lk(vsync_event_mutex);
1284+ auto prev = prev_frame.find(output_id);
1285+ if (prev != prev_frame.end() && prev->second.msc < frame.msc)
1286 {
1287- for(auto const& event : event_map)
1288- logger->log(ml::Severity::informational,
1289- std::to_string(event.second) + " vsync events on [" +
1290- std::to_string(event.first) + "] over " +
1291- std::to_string(duration_cast<milliseconds>(now - last_report).count()) + "ms",
1292- component());
1293- event_map.clear();
1294- last_report = now;
1295+ auto const now = graphics::Frame::Timestamp::now(frame.ust.clock_id);
1296+ auto const age_ns = now.nanoseconds - frame.ust.nanoseconds;
1297+ auto const delta_ns = frame.ust.nanoseconds -
1298+ prev->second.ust.nanoseconds;
1299+
1300+ // long long to match printf format on all architectures
1301+ const long long msc = frame.msc,
1302+ age_us = age_ns.count() / 1000,
1303+ interval_us = delta_ns.count() /
1304+ (1000*(frame.msc - prev->second.msc)),
1305+ hz100 = 100000000LL / interval_us;
1306+
1307+ logger->log(component(), ml::Severity::informational,
1308+ "vsync on %u: #%lld, %lld.%03lldms %s, interval %lld.%03lldms (%lld.%02lldHz)",
1309+ output_id,
1310+ msc,
1311+ llabs(age_us/1000), llabs(age_us%1000),
1312+ age_us>=0 ? "ago" : "from now",
1313+ interval_us/1000, interval_us%1000,
1314+ hz100/100, hz100%100);
1315 }
1316+ prev_frame[output_id] = frame;
1317 }
1318
1319=== modified file 'src/server/report/logging/display_report.h'
1320--- src/server/report/logging/display_report.h 2016-09-12 07:00:54 +0000
1321+++ src/server/report/logging/display_report.h 2016-10-05 09:20:48 +0000
1322@@ -21,7 +21,7 @@
1323 #define MIR_REPORT_LOGGING_DISPLAY_REPORTER_H_
1324
1325 #include "mir/graphics/display_report.h"
1326-#include "mir/time/clock.h"
1327+#include "mir/graphics/frame.h"
1328
1329 #include <unordered_map>
1330 #include <memory>
1331@@ -33,6 +33,7 @@
1332 {
1333 class Logger;
1334 }
1335+namespace graphics { struct Frame; }
1336 namespace report
1337 {
1338 namespace logging
1339@@ -45,8 +46,7 @@
1340 static const char* component();
1341
1342 DisplayReport(
1343- std::shared_ptr<mir::logging::Logger> const& logger,
1344- std::shared_ptr<time::Clock> const& clock);
1345+ std::shared_ptr<mir::logging::Logger> const& logger);
1346
1347 virtual ~DisplayReport();
1348
1349@@ -55,7 +55,7 @@
1350 virtual void report_successful_egl_buffer_swap_on_construction() override;
1351 virtual void report_successful_drm_mode_set_crtc_on_construction() override;
1352 virtual void report_successful_display_construction() override;
1353- virtual void report_vsync(unsigned int output_id) override;
1354+ virtual void report_vsync(unsigned int output_id, graphics::Frame const&) override;
1355 virtual void report_drm_master_failure(int error) override;
1356 virtual void report_vt_switch_away_failure() override;
1357 virtual void report_vt_switch_back_failure() override;
1358@@ -67,10 +67,8 @@
1359
1360 private:
1361 std::shared_ptr<mir::logging::Logger> const logger;
1362- std::shared_ptr<time::Clock> const clock;
1363 std::mutex vsync_event_mutex;
1364- mir::time::Timestamp last_report;
1365- std::unordered_map<unsigned int, unsigned int> event_map;
1366+ std::unordered_map<unsigned int, mir::graphics::Frame> prev_frame;
1367 };
1368 }
1369 }
1370
1371=== modified file 'src/server/report/logging/logging_report_factory.cpp'
1372--- src/server/report/logging/logging_report_factory.cpp 2016-07-28 23:00:22 +0000
1373+++ src/server/report/logging/logging_report_factory.cpp 2016-10-05 09:20:48 +0000
1374@@ -47,7 +47,7 @@
1375
1376 std::shared_ptr<mir::graphics::DisplayReport> mr::LoggingReportFactory::create_display_report()
1377 {
1378- return std::make_shared<logging::DisplayReport>(logger, clock);
1379+ return std::make_shared<logging::DisplayReport>(logger);
1380 }
1381
1382 std::shared_ptr<mir::scene::SceneReport> mr::LoggingReportFactory::create_scene_report()
1383
1384=== modified file 'src/server/report/lttng/display_report.cpp'
1385--- src/server/report/lttng/display_report.cpp 2016-09-12 07:00:54 +0000
1386+++ src/server/report/lttng/display_report.cpp 2016-10-05 09:20:48 +0000
1387@@ -47,7 +47,8 @@
1388 mir_tracepoint(mir_server_display, report_drm_master_failure, strerror(error));
1389 }
1390
1391-void mir::report::lttng::DisplayReport::report_vsync(unsigned int output_id)
1392+void mir::report::lttng::DisplayReport::report_vsync(unsigned int output_id,
1393+ mir::graphics::Frame const&)
1394 {
1395 mir_tracepoint(mir_server_display, report_vsync, output_id);
1396 }
1397
1398=== modified file 'src/server/report/lttng/display_report.h'
1399--- src/server/report/lttng/display_report.h 2016-09-12 07:00:54 +0000
1400+++ src/server/report/lttng/display_report.h 2016-10-05 09:20:48 +0000
1401@@ -45,7 +45,7 @@
1402 virtual void report_drm_master_failure(int error) override;
1403 virtual void report_vt_switch_away_failure() override;
1404 virtual void report_vt_switch_back_failure() override;
1405- virtual void report_vsync(unsigned int output_id) override;
1406+ virtual void report_vsync(unsigned int output_id, graphics::Frame const&) override;
1407
1408 private:
1409 ServerTracepointProvider tp_provider;
1410
1411=== modified file 'src/server/report/null/display_report.cpp'
1412--- src/server/report/null/display_report.cpp 2015-04-28 07:54:10 +0000
1413+++ src/server/report/null/display_report.cpp 2016-10-05 09:20:48 +0000
1414@@ -29,4 +29,4 @@
1415 void mrn::DisplayReport::report_vt_switch_away_failure() {}
1416 void mrn::DisplayReport::report_vt_switch_back_failure() {}
1417 void mrn::DisplayReport::report_egl_configuration(EGLDisplay, EGLConfig) {}
1418-void mrn::DisplayReport::report_vsync(unsigned int) {}
1419+void mrn::DisplayReport::report_vsync(unsigned int, mir::graphics::Frame const&) {}
1420
1421=== modified file 'src/server/report/null/display_report.h'
1422--- src/server/report/null/display_report.h 2016-09-12 07:00:54 +0000
1423+++ src/server/report/null/display_report.h 2016-10-05 09:20:48 +0000
1424@@ -43,7 +43,7 @@
1425 void report_vt_switch_away_failure() override;
1426 void report_vt_switch_back_failure() override;
1427 void report_egl_configuration(EGLDisplay disp, EGLConfig cfg) override;
1428- void report_vsync(unsigned int output_id) override;
1429+ void report_vsync(unsigned int output_id, graphics::Frame const&) override;
1430 };
1431 }
1432 }
1433
1434=== modified file 'tests/include/mir/test/doubles/mock_display.h'
1435--- tests/include/mir/test/doubles/mock_display.h 2016-09-15 00:57:28 +0000
1436+++ tests/include/mir/test/doubles/mock_display.h 2016-10-05 09:20:48 +0000
1437@@ -51,6 +51,7 @@
1438 MOCK_METHOD1(create_hardware_cursor, std::shared_ptr<graphics::Cursor>(std::shared_ptr<graphics::CursorImage> const&));
1439 MOCK_METHOD2(create_virtual_output, std::unique_ptr<graphics::VirtualOutput>(int, int));
1440 MOCK_METHOD0(native_display, graphics::NativeDisplay*());
1441+ MOCK_CONST_METHOD1(last_frame_on, graphics::Frame(unsigned));
1442 };
1443
1444 }
1445
1446=== modified file 'tests/include/mir/test/doubles/mock_display_report.h'
1447--- tests/include/mir/test/doubles/mock_display_report.h 2015-04-28 07:54:10 +0000
1448+++ tests/include/mir/test/doubles/mock_display_report.h 2016-10-05 09:20:48 +0000
1449@@ -20,7 +20,7 @@
1450 #define MIR_TEST_DOUBLES_MOCK_DISPLAY_REPORT_H_
1451
1452 #include "mir/graphics/display_report.h"
1453-
1454+#include "mir/graphics/frame.h" // GMock can't live with just forward decls
1455 #include <gmock/gmock.h>
1456
1457 namespace mir
1458@@ -42,7 +42,7 @@
1459 MOCK_METHOD0(report_vt_switch_away_failure, void());
1460 MOCK_METHOD0(report_vt_switch_back_failure, void());
1461 MOCK_METHOD2(report_egl_configuration, void(EGLDisplay,EGLConfig));
1462- MOCK_METHOD1(report_vsync, void(unsigned int));
1463+ MOCK_METHOD2(report_vsync, void(unsigned int, graphics::Frame const&));
1464 };
1465
1466 }
1467
1468=== modified file 'tests/include/mir/test/doubles/mock_hwc_device_wrapper.h'
1469--- tests/include/mir/test/doubles/mock_hwc_device_wrapper.h 2015-06-17 05:20:42 +0000
1470+++ tests/include/mir/test/doubles/mock_hwc_device_wrapper.h 2016-10-05 09:20:48 +0000
1471@@ -46,7 +46,7 @@
1472 MOCK_CONST_METHOD1(display_on, void(graphics::android::DisplayName));
1473 MOCK_CONST_METHOD1(display_off, void(graphics::android::DisplayName));
1474 MOCK_METHOD4(subscribe_to_events, void(void const*,
1475- std::function<void(graphics::android::DisplayName, std::chrono::nanoseconds)> const&,
1476+ std::function<void(graphics::android::DisplayName, mir::graphics::Frame::Timestamp)> const&,
1477 std::function<void(graphics::android::DisplayName, bool)> const&,
1478 std::function<void()> const&));
1479 MOCK_METHOD1(unsubscribe_from_events_, void(void const*));
1480
1481=== modified file 'tests/include/mir/test/doubles/stub_display_builder.h'
1482--- tests/include/mir/test/doubles/stub_display_builder.h 2016-08-24 02:09:08 +0000
1483+++ tests/include/mir/test/doubles/stub_display_builder.h 2016-10-05 09:20:48 +0000
1484@@ -57,7 +57,7 @@
1485 MOCK_METHOD1(active_config_for, graphics::DisplayConfigurationOutput(graphics::android::DisplayName));
1486 MOCK_METHOD2(subscribe_to_config_changes,
1487 graphics::android::ConfigChangeSubscription(
1488- std::function<void()> const&, std::function<void(graphics::android::DisplayName)> const&));
1489+ std::function<void()> const&, std::function<void(graphics::android::DisplayName, mir::graphics::Frame::Timestamp)> const&));
1490 };
1491
1492 struct StubHwcConfiguration : public graphics::android::HwcConfiguration
1493@@ -75,7 +75,10 @@
1494 }
1495
1496 graphics::android::ConfigChangeSubscription subscribe_to_config_changes(
1497- std::function<void()> const&, std::function<void(graphics::android::DisplayName)> const&) override
1498+ std::function<void()> const&,
1499+ std::function<void(graphics::android::DisplayName,
1500+ graphics::Frame::Timestamp)> const&
1501+ ) override
1502 {
1503 return nullptr;
1504 }
1505
1506=== modified file 'tests/mir_test_doubles/mock_egl.cpp'
1507--- tests/mir_test_doubles/mock_egl.cpp 2016-07-15 09:37:28 +0000
1508+++ tests/mir_test_doubles/mock_egl.cpp 2016-10-05 09:20:48 +0000
1509@@ -49,6 +49,8 @@
1510 EGLSyncKHR extension_eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list);
1511 EGLBoolean extension_eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync);
1512 EGLint extension_eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout);
1513+EGLBoolean extension_eglGetSyncValuesCHROMIUM(EGLDisplay dpy,
1514+ EGLSurface surface, int64_t *ust, int64_t *msc, int64_t *sbc);
1515
1516 /* EGL{Surface,Display,Config,Context} are all opaque types, so we can put whatever
1517 we want in them for testing */
1518@@ -144,6 +146,10 @@
1519 .WillByDefault(Return(reinterpret_cast<func_ptr_t>(extension_eglDestroySyncKHR)));
1520 ON_CALL(*this, eglGetProcAddress(StrEq("eglClientWaitSyncKHR")))
1521 .WillByDefault(Return(reinterpret_cast<func_ptr_t>(extension_eglClientWaitSyncKHR)));
1522+ ON_CALL(*this, eglGetProcAddress(StrEq("eglGetSyncValuesCHROMIUM")))
1523+ .WillByDefault(Return(
1524+ reinterpret_cast<func_ptr_t>(extension_eglGetSyncValuesCHROMIUM)
1525+ ));
1526 }
1527
1528 void mtd::MockEGL::provide_egl_extensions()
1529@@ -416,3 +422,11 @@
1530 CHECK_GLOBAL_MOCK(EGLint);
1531 return global_mock_egl->eglClientWaitSyncKHR(dpy, sync, flags, timeout);
1532 }
1533+
1534+EGLBoolean extension_eglGetSyncValuesCHROMIUM(EGLDisplay dpy,
1535+ EGLSurface surface, int64_t *ust, int64_t *msc, int64_t *sbc)
1536+{
1537+ CHECK_GLOBAL_MOCK(EGLBoolean);
1538+ return global_mock_egl->eglGetSyncValuesCHROMIUM(dpy, surface,
1539+ ust, msc, sbc);
1540+}
1541
1542=== modified file 'tests/mir_test_framework/stubbed_graphics_platform.cpp'
1543--- tests/mir_test_framework/stubbed_graphics_platform.cpp 2016-09-22 16:27:13 +0000
1544+++ tests/mir_test_framework/stubbed_graphics_platform.cpp 2016-10-05 09:20:48 +0000
1545@@ -106,7 +106,10 @@
1546 {
1547 return display->native_display();
1548 }
1549-
1550+ mg::Frame last_frame_on(unsigned output_id) const override
1551+ {
1552+ return display->last_frame_on(output_id);
1553+ }
1554 std::shared_ptr<Display> const display;
1555 };
1556
1557
1558=== modified file 'tests/unit-tests/logging/test_display_report.cpp'
1559--- tests/unit-tests/logging/test_display_report.cpp 2015-06-25 03:00:08 +0000
1560+++ tests/unit-tests/logging/test_display_report.cpp 2016-10-05 09:20:48 +0000
1561@@ -17,9 +17,9 @@
1562 */
1563
1564 #include "src/server/report/logging/display_report.h"
1565+#include "mir/graphics/frame.h"
1566 #include "mir/logging/logger.h"
1567 #include "mir/test/doubles/mock_egl.h"
1568-#include "mir/test/doubles/advanceable_clock.h"
1569
1570 #include <gtest/gtest.h>
1571 #include <gmock/gmock.h>
1572@@ -40,7 +40,6 @@
1573
1574 struct DisplayReport : public testing::Test
1575 {
1576- std::shared_ptr<mtd::AdvanceableClock> const clock{std::make_shared<mtd::AdvanceableClock>()};
1577 std::shared_ptr<MockLogger> logger{std::make_shared<MockLogger>()};
1578 mtd::MockEGL mock_egl;
1579 };
1580@@ -116,29 +115,71 @@
1581 component));
1582 }
1583
1584- mrl::DisplayReport report(logger, clock);
1585+ mrl::DisplayReport report(logger);
1586 report.report_egl_configuration(disp, config);
1587 }
1588
1589 TEST_F(DisplayReport, reports_vsync)
1590 {
1591- std::chrono::milliseconds interval(1500);
1592+ using namespace testing;
1593+ std::chrono::nanoseconds const nanos_per_frame{16666666};
1594+ std::string const interval_str{"interval 16.666ms"};
1595 unsigned int display1_id {1223};
1596 unsigned int display2_id {4492};
1597- std::string display1_name(std::to_string(display1_id));
1598- std::string display2_name(std::to_string(display2_id));
1599- EXPECT_CALL(*logger, log(
1600- ml::Severity::informational,
1601- "2 vsync events on [" + display1_name + "] over " + std::to_string(interval.count()) + "ms",
1602- component));
1603- EXPECT_CALL(*logger, log(
1604- ml::Severity::informational,
1605- "1 vsync events on [" + display2_name + "] over " + std::to_string(interval.count()) + "ms",
1606- component));
1607- mrl::DisplayReport report(logger, clock);
1608-
1609- report.report_vsync(display1_id);
1610- report.report_vsync(display2_id);
1611- clock->advance_by(interval);
1612- report.report_vsync(display1_id);
1613+ EXPECT_CALL(*logger, log(
1614+ ml::Severity::informational,
1615+ AllOf(StartsWith("vsync on "+std::to_string(display1_id)),
1616+ HasSubstr(interval_str)),
1617+ component));
1618+ EXPECT_CALL(*logger, log(
1619+ ml::Severity::informational,
1620+ AllOf(StartsWith("vsync on "+std::to_string(display2_id)),
1621+ HasSubstr(interval_str)),
1622+ component));
1623+ mrl::DisplayReport report(logger);
1624+
1625+ mir::graphics::Frame frame;
1626+ report.report_vsync(display1_id, frame);
1627+ report.report_vsync(display2_id, frame);
1628+ frame.msc++;
1629+ frame.ust.nanoseconds += nanos_per_frame;
1630+ report.report_vsync(display1_id, frame);
1631+ report.report_vsync(display2_id, frame);
1632+}
1633+
1634+TEST_F(DisplayReport, reports_vsync_steady_interval_despite_missed_frames)
1635+{
1636+ using namespace testing;
1637+ int const hz = 60;
1638+ std::string const interval_str{"interval 16.666ms (60.00Hz)"};
1639+ unsigned const id{123};
1640+ int const d1 = 456;
1641+ int const d2 = 789;
1642+
1643+ InSequence seq;
1644+
1645+ auto const id_str = std::to_string(id);
1646+ EXPECT_CALL(*logger, log(
1647+ ml::Severity::informational,
1648+ AllOf(StartsWith("vsync on "+id_str+": #"+std::to_string(d1)+","),
1649+ HasSubstr(interval_str)),
1650+ component));
1651+ EXPECT_CALL(*logger, log(
1652+ ml::Severity::informational,
1653+ AllOf(StartsWith("vsync on "+id_str+": #"+std::to_string(d1+d2)+","),
1654+ HasSubstr(interval_str)),
1655+ component));
1656+
1657+ mrl::DisplayReport report(logger);
1658+ mir::graphics::Frame frame;
1659+
1660+ std::chrono::nanoseconds const nanos_per_frame{1000000000LL/hz};
1661+
1662+ report.report_vsync(id, frame);
1663+ frame.msc += d1;
1664+ frame.ust.nanoseconds += d1 * nanos_per_frame;
1665+ report.report_vsync(id, frame);
1666+ frame.msc += d2;
1667+ frame.ust.nanoseconds += d2 * nanos_per_frame;
1668+ report.report_vsync(id, frame);
1669 }
1670
1671=== modified file 'tests/unit-tests/platforms/android/server/test_display.cpp'
1672--- tests/unit-tests/platforms/android/server/test_display.cpp 2016-09-19 06:09:48 +0000
1673+++ tests/unit-tests/platforms/android/server/test_display.cpp 2016-10-05 09:20:48 +0000
1674@@ -893,9 +893,9 @@
1675 TEST_F(Display, reports_vsync)
1676 {
1677 using namespace testing;
1678- std::function<void(mga::DisplayName)> vsync_fn = [](mga::DisplayName){};
1679+ std::function<void(mga::DisplayName, mg::Frame::Timestamp)> vsync_fn = [](mga::DisplayName, mg::Frame::Timestamp){};
1680 auto report = std::make_shared<NiceMock<mtd::MockDisplayReport>>();
1681- EXPECT_CALL(*report, report_vsync(_));
1682+ EXPECT_CALL(*report, report_vsync(_, _));
1683 stub_db_factory->with_next_config([&](mtd::MockHwcConfiguration& mock_config)
1684 {
1685 EXPECT_CALL(mock_config, subscribe_to_config_changes(_,_))
1686@@ -910,7 +910,7 @@
1687 null_anw_report,
1688 mga::OverlayOptimization::enabled);
1689
1690- vsync_fn(mga::DisplayName::primary);
1691+ vsync_fn(mga::DisplayName::primary, {});
1692 }
1693
1694 TEST_F(Display, reports_correct_card_information)
1695
1696=== modified file 'tests/unit-tests/platforms/android/server/test_display_hotplug.cpp'
1697--- tests/unit-tests/platforms/android/server/test_display_hotplug.cpp 2016-07-20 04:54:07 +0000
1698+++ tests/unit-tests/platforms/android/server/test_display_hotplug.cpp 2016-10-05 09:20:48 +0000
1699@@ -49,7 +49,7 @@
1700 return mtd::StubDisplayConfig({{true,true}}).outputs[0];
1701 }
1702 mga::ConfigChangeSubscription subscribe_to_config_changes(
1703- std::function<void()> const& cb, std::function<void(mga::DisplayName)> const&) override
1704+ std::function<void()> const& cb, std::function<void(mga::DisplayName, mg::Frame::Timestamp)> const&) override
1705 {
1706 hotplug_fn = cb;
1707 return {};
1708@@ -74,7 +74,7 @@
1709 return wrapped.active_config_for(d);
1710 }
1711 mga::ConfigChangeSubscription subscribe_to_config_changes(
1712- std::function<void()> const& hotplug, std::function<void(mga::DisplayName)> const& vsync) override
1713+ std::function<void()> const& hotplug, std::function<void(mga::DisplayName, mg::Frame::Timestamp)> const& vsync) override
1714 {
1715 return wrapped.subscribe_to_config_changes(hotplug, vsync);
1716 }
1717
1718=== modified file 'tests/unit-tests/platforms/android/server/test_hwc_configuration.cpp'
1719--- tests/unit-tests/platforms/android/server/test_hwc_configuration.cpp 2016-06-22 22:45:56 +0000
1720+++ tests/unit-tests/platforms/android/server/test_hwc_configuration.cpp 2016-10-05 09:20:48 +0000
1721@@ -23,6 +23,7 @@
1722 #include <gtest/gtest.h>
1723 #include <chrono>
1724
1725+namespace mg = mir::graphics;
1726 namespace mga = mir::graphics::android;
1727 namespace mtd = mir::test::doubles;
1728 namespace geom = mir::geometry;
1729@@ -271,8 +272,8 @@
1730 {
1731 using namespace testing;
1732 std::function<void(mga::DisplayName, bool)> hotplug_fn([](mga::DisplayName, bool){});
1733- std::function<void(mga::DisplayName, std::chrono::nanoseconds)> vsync_fn(
1734- [](mga::DisplayName, std::chrono::nanoseconds){});
1735+ std::function<void(mga::DisplayName, mg::Frame::Timestamp)> vsync_fn(
1736+ [](mga::DisplayName, mg::Frame::Timestamp){});
1737 EXPECT_CALL(*mock_hwc_wrapper, subscribe_to_events(_,_,_,_))
1738 .WillOnce(DoAll(SaveArg<1>(&vsync_fn), SaveArg<2>(&hotplug_fn)));
1739 EXPECT_CALL(*mock_hwc_wrapper, unsubscribe_from_events_(_));
1740@@ -280,10 +281,11 @@
1741 unsigned int hotplug_call_count{0};
1742 unsigned int vsync_call_count{0};
1743 auto subscription = config.subscribe_to_config_changes(
1744- [&]{ hotplug_call_count++; }, [&](mga::DisplayName){ vsync_call_count++; });
1745- hotplug_fn(mga::DisplayName::primary, true);
1746- hotplug_fn(mga::DisplayName::primary, true);
1747- vsync_fn(mga::DisplayName::primary, std::chrono::nanoseconds(33));
1748+ [&]{ hotplug_call_count++; }, [&](mga::DisplayName, mg::Frame::Timestamp){ vsync_call_count++; });
1749+ hotplug_fn(mga::DisplayName::primary, true);
1750+ hotplug_fn(mga::DisplayName::primary, true);
1751+ using namespace std::literals::chrono_literals;
1752+ vsync_fn(mga::DisplayName::primary, mg::Frame::Timestamp{CLOCK_MONOTONIC,123ns});
1753 EXPECT_THAT(hotplug_call_count, Eq(2));
1754 EXPECT_THAT(vsync_call_count, Eq(1));
1755 }
1756
1757=== modified file 'tests/unit-tests/platforms/android/server/test_hwc_fb_device.cpp'
1758--- tests/unit-tests/platforms/android/server/test_hwc_fb_device.cpp 2016-05-03 06:55:25 +0000
1759+++ tests/unit-tests/platforms/android/server/test_hwc_fb_device.cpp 2016-10-05 09:20:48 +0000
1760@@ -142,7 +142,7 @@
1761 {
1762 using namespace testing;
1763 std::list<hwc_layer_1_t*> expected_list{&skip_layer};
1764- std::function<void(mga::DisplayName, std::chrono::nanoseconds)> vsync_cb;
1765+ std::function<void(mga::DisplayName, mg::Frame::Timestamp)> vsync_cb;
1766 EXPECT_CALL(*mock_hwc_device_wrapper, subscribe_to_events(_,_,_,_))
1767 .WillOnce(SaveArg<1>(&vsync_cb));
1768 mga::HwcFbDevice device(mock_hwc_device_wrapper, mock_fb_device);
1769@@ -155,7 +155,7 @@
1770 while(vsync_thread_on)
1771 {
1772 std::this_thread::sleep_for(std::chrono::microseconds(500));
1773- vsync_cb(mga::DisplayName::primary, std::chrono::nanoseconds(0));
1774+ vsync_cb(mga::DisplayName::primary, mg::Frame::Timestamp{});
1775 }});
1776
1777 Sequence seq;
1778
1779=== modified file 'tests/unit-tests/platforms/android/server/test_hwc_wrapper.cpp'
1780--- tests/unit-tests/platforms/android/server/test_hwc_wrapper.cpp 2016-08-24 02:09:08 +0000
1781+++ tests/unit-tests/platforms/android/server/test_hwc_wrapper.cpp 2016-10-05 09:20:48 +0000
1782@@ -23,6 +23,7 @@
1783 #include <gmock/gmock.h>
1784 #include <gtest/gtest.h>
1785
1786+namespace mg = mir::graphics;
1787 namespace mga = mir::graphics::android;
1788 namespace mtd = mir::test::doubles;
1789
1790@@ -314,7 +315,7 @@
1791 auto vsync_call_count = 0u;
1792 auto hotplug_call_count = 0u;
1793 wrapper.subscribe_to_events(this,
1794- [&](mga::DisplayName, std::chrono::nanoseconds){vsync_call_count++;},
1795+ [&](mga::DisplayName, mg::Frame::Timestamp){vsync_call_count++;},
1796 [&](mga::DisplayName, bool){hotplug_call_count++;},
1797 [&](){invalidate_call_count++;});
1798
1799@@ -349,7 +350,7 @@
1800 mga::RealHwcWrapper wrapper(mock_device, mock_report);
1801 wrapper.subscribe_to_events(
1802 this,
1803- [&](mga::DisplayName, std::chrono::nanoseconds){ call_count++; },
1804+ [&](mga::DisplayName, mg::Frame::Timestamp){ call_count++; },
1805 [](mga::DisplayName, bool){},
1806 []{});
1807
1808
1809=== modified file 'tests/unit-tests/platforms/mesa/kms/mock_kms_output.h'
1810--- tests/unit-tests/platforms/mesa/kms/mock_kms_output.h 2016-09-01 17:02:56 +0000
1811+++ tests/unit-tests/platforms/mesa/kms/mock_kms_output.h 2016-10-05 09:20:48 +0000
1812@@ -39,6 +39,8 @@
1813 MOCK_METHOD1(schedule_page_flip, bool(uint32_t));
1814 MOCK_METHOD0(wait_for_page_flip, void());
1815
1816+ MOCK_CONST_METHOD0(last_frame, graphics::Frame());
1817+
1818 MOCK_METHOD1(set_cursor, void(gbm_bo*));
1819 MOCK_METHOD1(move_cursor, void(geometry::Point));
1820 MOCK_METHOD0(clear_cursor, void());
1821
1822=== modified file 'tests/unit-tests/platforms/mesa/kms/test_display.cpp'
1823--- tests/unit-tests/platforms/mesa/kms/test_display.cpp 2016-08-23 20:21:27 +0000
1824+++ tests/unit-tests/platforms/mesa/kms/test_display.cpp 2016-10-05 09:20:48 +0000
1825@@ -30,7 +30,6 @@
1826
1827 #include "mir/test/doubles/mock_egl.h"
1828 #include "mir/test/doubles/mock_gl.h"
1829-#include "mir/test/doubles/advanceable_clock.h"
1830 #include "src/server/report/null_report_factory.h"
1831 #include "mir/test/doubles/mock_display_report.h"
1832 #include "mir/test/doubles/null_virtual_terminal.h"
1833@@ -534,7 +533,7 @@
1834 using namespace ::testing;
1835
1836 auto logger = std::make_shared<MockLogger>();
1837- auto reporter = std::make_shared<mrl::DisplayReport>(logger, std::make_shared<mtd::AdvanceableClock>());
1838+ auto reporter = std::make_shared<mrl::DisplayReport>(logger);
1839
1840 EXPECT_CALL(
1841 *logger,
1842@@ -550,7 +549,7 @@
1843 using namespace ::testing;
1844
1845 auto logger = std::make_shared<MockLogger>();
1846- auto reporter = std::make_shared<mrl::DisplayReport>(logger, std::make_shared<mtd::AdvanceableClock>());
1847+ auto reporter = std::make_shared<mrl::DisplayReport>(logger);
1848
1849 EXPECT_CALL(
1850 *logger,
1851@@ -566,7 +565,7 @@
1852 using namespace ::testing;
1853
1854 auto logger = std::make_shared<MockLogger>();
1855- auto reporter = std::make_shared<mrl::DisplayReport>(logger, std::make_shared<mtd::AdvanceableClock>());
1856+ auto reporter = std::make_shared<mrl::DisplayReport>(logger);
1857
1858 EXPECT_CALL(
1859 *logger,
1860@@ -582,7 +581,7 @@
1861 using namespace ::testing;
1862
1863 auto logger = std::make_shared<MockLogger>();
1864- auto reporter = std::make_shared<mrl::DisplayReport>(logger, std::make_shared<mtd::AdvanceableClock>());
1865+ auto reporter = std::make_shared<mrl::DisplayReport>(logger);
1866
1867 EXPECT_CALL(
1868 *logger,
1869
1870=== modified file 'tests/unit-tests/platforms/mesa/kms/test_kms_page_flipper.cpp'
1871--- tests/unit-tests/platforms/mesa/kms/test_kms_page_flipper.cpp 2016-09-13 07:24:11 +0000
1872+++ tests/unit-tests/platforms/mesa/kms/test_kms_page_flipper.cpp 2016-10-05 09:20:48 +0000
1873@@ -139,7 +139,7 @@
1874
1875 // Regression test for LP: #1621352
1876 ASSERT_NE(crtc_id, connector_id);
1877- EXPECT_CALL(report, report_vsync(connector_id));
1878+ EXPECT_CALL(report, report_vsync(connector_id, _));
1879
1880 page_flipper.schedule_flip(crtc_id, fb_id, connector_id);
1881 EXPECT_EQ(1, write(mock_drm.fake_drm.write_fd(), "a", 1));
1882
1883=== modified file 'tests/unit-tests/platforms/mesa/kms/test_real_kms_output.cpp'
1884--- tests/unit-tests/platforms/mesa/kms/test_real_kms_output.cpp 2016-09-09 07:47:18 +0000
1885+++ tests/unit-tests/platforms/mesa/kms/test_real_kms_output.cpp 2016-10-05 09:20:48 +0000
1886@@ -36,6 +36,8 @@
1887 namespace mt = mir::test;
1888 namespace mtd = mir::test::doubles;
1889
1890+using namespace ::testing;
1891+
1892 namespace
1893 {
1894
1895@@ -43,14 +45,14 @@
1896 {
1897 public:
1898 bool schedule_flip(uint32_t,uint32_t,uint32_t) override { return true; }
1899- void wait_for_flip(uint32_t) override { }
1900+ mg::Frame wait_for_flip(uint32_t) override { return {}; }
1901 };
1902
1903 class MockPageFlipper : public mgm::PageFlipper
1904 {
1905 public:
1906 MOCK_METHOD3(schedule_flip, bool(uint32_t,uint32_t,uint32_t));
1907- MOCK_METHOD1(wait_for_flip, void(uint32_t));
1908+ MOCK_METHOD1(wait_for_flip, mg::Frame(uint32_t));
1909 };
1910
1911 class RealKMSOutputTest : public ::testing::Test
1912@@ -62,6 +64,8 @@
1913 possible_encoder_ids1{encoder_ids[0]},
1914 possible_encoder_ids2{encoder_ids[0], encoder_ids[1]}
1915 {
1916+ ON_CALL(mock_page_flipper, wait_for_flip(_))
1917+ .WillByDefault(Return(mg::Frame{}));
1918 }
1919
1920 void setup_outputs_connected_crtc()
1921
1922=== modified file 'tests/unit-tests/platforms/mesa/x11/test_display.cpp'
1923--- tests/unit-tests/platforms/mesa/x11/test_display.cpp 2016-10-04 08:15:29 +0000
1924+++ tests/unit-tests/platforms/mesa/x11/test_display.cpp 2016-10-05 09:20:48 +0000
1925@@ -52,6 +52,9 @@
1926 using namespace testing;
1927 EGLint const client_version = 2;
1928
1929+ ON_CALL(mock_egl, eglQueryString(_, EGL_EXTENSIONS))
1930+ .WillByDefault(Return("other stuff and EGL_CHROMIUM_sync_control"));
1931+
1932 ON_CALL(mock_egl, eglQueryContext(mock_egl.fake_egl_display,
1933 mock_egl.fake_egl_context,
1934 EGL_CONTEXT_CLIENT_VERSION,

Subscribers

People subscribed via source and target branches