Mir

Merge lp:~raof/mir/mesa-hybrid-output into lp:mir

Proposed by Chris Halse Rogers
Status: Merged
Approved by: Daniel van Vugt
Approved revision: no longer in the source branch.
Merged at revision: 4137
Proposed branch: lp:~raof/mir/mesa-hybrid-output
Merge into: lp:mir
Prerequisite: lp:~raof/mir/rearrange-drm-configuration
Diff against target: 4141 lines (+1816/-509)
38 files modified
CMakeLists.txt (+4/-0)
src/platforms/mesa/server/display_helpers.cpp (+95/-2)
src/platforms/mesa/server/display_helpers.h (+7/-1)
src/platforms/mesa/server/kms/CMakeLists.txt (+1/-0)
src/platforms/mesa/server/kms/display.cpp (+188/-32)
src/platforms/mesa/server/kms/display.h (+2/-2)
src/platforms/mesa/server/kms/display_buffer.cpp (+414/-19)
src/platforms/mesa/server/kms/display_buffer.h (+6/-5)
src/platforms/mesa/server/kms/kms_output.h (+20/-1)
src/platforms/mesa/server/kms/platform.cpp (+36/-9)
src/platforms/mesa/server/kms/platform.h (+1/-1)
src/platforms/mesa/server/kms/real_kms_output.cpp (+41/-19)
src/platforms/mesa/server/kms/real_kms_output.h (+6/-1)
src/platforms/mesa/server/kms/real_kms_output_container.cpp (+42/-21)
src/platforms/mesa/server/kms/real_kms_output_container.h (+7/-5)
src/server/input/default_input_device_hub.cpp (+1/-1)
tests/include/mir/test/doubles/mock_drm.h (+35/-1)
tests/include/mir/test/doubles/mock_gbm.h (+1/-0)
tests/include/mir/test/doubles/mock_gl.h (+1/-0)
tests/mir_test_doubles/mock_drm.cpp (+180/-7)
tests/mir_test_doubles/mock_egl.cpp (+1/-1)
tests/mir_test_doubles/mock_gbm.cpp (+5/-0)
tests/mir_test_doubles/mock_gl.cpp (+8/-0)
tests/unit-tests/platforms/mesa/CMakeLists.txt (+2/-1)
tests/unit-tests/platforms/mesa/kms-utils/test_connector_utils.cpp (+74/-43)
tests/unit-tests/platforms/mesa/kms/mock_kms_output.h (+3/-0)
tests/unit-tests/platforms/mesa/kms/test_display.cpp (+55/-54)
tests/unit-tests/platforms/mesa/kms/test_display_buffer.cpp (+17/-0)
tests/unit-tests/platforms/mesa/kms/test_display_configuration.cpp (+308/-136)
tests/unit-tests/platforms/mesa/kms/test_display_generic.cpp (+13/-0)
tests/unit-tests/platforms/mesa/kms/test_display_multi_monitor.cpp (+49/-28)
tests/unit-tests/platforms/mesa/kms/test_drm_helper.cpp (+20/-2)
tests/unit-tests/platforms/mesa/kms/test_guest_platform.cpp (+5/-1)
tests/unit-tests/platforms/mesa/kms/test_kms_page_flipper.cpp (+42/-31)
tests/unit-tests/platforms/mesa/kms/test_platform.cpp (+13/-23)
tests/unit-tests/platforms/mesa/kms/test_real_kms_output.cpp (+111/-60)
tests/unit-tests/platforms/mesa/x11/test_guest_platform.cpp (+1/-1)
tests/unit-tests/platforms/mesa/x11/test_platform.cpp (+1/-1)
To merge this branch: bzr merge lp:~raof/mir/mesa-hybrid-output
Reviewer Review Type Date Requested Status
Kevin DuBois (community) Approve
Mir CI Bot continuous-integration Approve
Michael Zanetti (community) Needs Information
Alan Griffiths Approve
Cemil Azizoglu (community) Approve
Review via email: mp+320140@code.launchpad.net

Commit message

Initial hybrid-output support for mesa-kms platform.

This is (mostly) what is known in the X world as reverse-prime - rendering on the integrated card, displaying on the external GPU.

This selects whatever card is first to initialise in DRM (which is [usually?] the boot VGA device) to do all shell rendering on, but exposes all outputs across all GPUs for display.

As a first cut, it misses out on hardware cursor support for hybrid systems, and performance on hybrid systems will be non-optimal, but this should not significantly change the code flow for non-hybrid systems.

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

FAILED: Continuous integration, rev:4077
https://mir-jenkins.ubuntu.com/job/mir-ci/3181/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4275/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4363/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4352/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4352/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4352/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4302/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4302/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4302/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4302/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4302/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4302/console

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

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

FAILED: Continuous integration, rev:4080
https://mir-jenkins.ubuntu.com/job/mir-ci/3189/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4288/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4375
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4365
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4365
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4365
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4315
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4315/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/4315
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4315/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4315
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4315/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4315/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4315/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4315
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4315/artifact/output/*zip*/output.zip

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

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

FAILED: Continuous integration, rev:4081
https://mir-jenkins.ubuntu.com/job/mir-ci/3192/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4291/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4378
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4368
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4368
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4368
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4318
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4318/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/4318
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4318/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4318
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4318/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4318/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4318/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4318
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4318/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

I guess you have to predefine GBM_BO_LINEAR ..

I will try this mp later today.. it used to work fine with the two cards here.

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

vivid+overlay builds no longer try building mesa-kms, so that should work :)

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

FAILED: Continuous integration, rev:4083
https://mir-jenkins.ubuntu.com/job/mir-ci/3209/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4319/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4406/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4396/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4396/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4396/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4351/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4351/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4351/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4351/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4351/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4351/console

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

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

FAILED: Continuous integration, rev:4084
https://mir-jenkins.ubuntu.com/job/mir-ci/3217/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4330/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4417
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4407
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4407
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4407
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4362
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4362/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/4362
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4362/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4362
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4362/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4362/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4362/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4362
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4362/artifact/output/*zip*/output.zip

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

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

FAILED: Continuous integration, rev:4085
https://mir-jenkins.ubuntu.com/job/mir-ci/3219/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4333/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4420
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4410
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4410
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4410
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4365
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4365/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/4365
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4365/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4365
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4365/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4365/console
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4365/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/4365/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4365/console

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

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

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

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

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

How do I test this branch? I have two GPUs, what do I expect to see happen differently? I usually have to do

echo OFF > /sys/kernel/debug/vgaswitcheroo/switch

to turn off the discrete GPU (nouveau, this case) to get non-garbage output (in Mir-on-X, though).

What should happen with this branch if I have two GPUs but one of them is disabled?

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

Hm. I was sure I'd written a response to that. Anyway...

If you've got two GPUs you should *at least* expect to see Mir to say “using /dev/dri/card0” and “/dev/dri/card1” on startup. The rest depends on your setup...

If you've got two GPUs *and* you've got outputs plugged in to both, mir_demo_server should, by default, mirror across all plugged in outputs across all GPUs (I've not tried it with > 2 GPUs, but the code doesn't discriminate between 2 and > 2).

Revision history for this message
Cemil Azizoglu (cemil-azizoglu) wrote :

Conflicts...

Also it won't build at the moment:

...
[ 45%] Linking CXX executable ../../../../bin/mir_demo_standalone_render_overlays.bin
//usr/lib/x86_64-linux-gnu/libmirclient.so.9: undefined reference to `MirInputDeviceStateEvent::device_pressed_keys_for_index(unsigned long, unsigned long) const@MIR_COMMON_0.25_PRIVATE'
//usr/lib/x86_64-linux-gnu/libmirclient.so.9: undefined reference to `MirPointerEvent::set_x(float)@MIR_COMMON_0.25_PRIVATE'
//usr/lib/x86_64-linux-gnu/libmirclient.so.9: undefined reference to `MirTouchEvent::pressure(unsigned long) const@MIR_COMMON_0.25_PRIVATE'
...

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

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

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

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

I tested the branch on a laptop with integrated Intel and discrete NVidia GPU (with only one monitor) and it seems to work fine. When I have both GPUs powered, it's able to open both cards. When I have the discrete GPU turned off, it complains that it cannot open card1, which is expected.

I did observe that if I do

sudo bin/mir_demo_server --vt 1

and then close the server with ctl-alt-bkspace //without running any clients on it first// it seems to hang my machine (black screen, vt switches no working). Not sure if it's due to this branch.

I'm not sure if it's prudent to mirror the output on all displays. It's not clear to me if it's the default policy or if it's something this branch does. I think it's a 'to-do' per this comment :

1041 + // TODO: expose multiple rendering GPUs to the shell.

I did spot the following nit :

142 + ".... configuration requries hybrid ..."

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

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

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

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

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

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

review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

+ private:
+ static mgm::GBMSurfaceUPtr create_scanout_surface(

Don't we usually indent like this:

private:
    static mgm::GBMSurfaceUPtr create_scanout_surface(

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

There's spurious whitespace at the end of at least one line.

But it all looks plausible and doesn't break anything obvious on my (non-hybrid) system.

review: Approve
Revision history for this message
Michael Zanetti (mzanetti) wrote :

I've tried this with silo 2373. The good news is, I do get the shell on the external screen now, the bad news is, the internal one claims to be disconnected now. This is on a MacBook Pro 10.1 (1st gen Retina) with an integrated Intel HD440 and an NVidia GeForce GT 650M Mac Edition as discrete graphics.

Here's the relevant log I get from Mir:

[2017-03-29 18:54:38.940700] mirserver: Initial display configuration:
[2017-03-29 18:54:38.941421] mirserver: Output 47: eDP disconnected
[2017-03-29 18:54:38.941781] mirserver: Output 50: DisplayPort disconnected
[2017-03-29 18:54:38.942547] mirserver: Output 54: DisplayPort disconnected
[2017-03-29 18:54:38.942908] mirserver: Output 58: HDMI-A connected, used
[2017-03-29 18:54:38.943267] mirserver: EDID monitor name: Panasonic-TV
[2017-03-29 18:54:38.946217] mirserver: EDID manufacturer: MEI
[2017-03-29 18:54:38.946267] mirserver: EDID product code: 41127
[2017-03-29 18:54:38.946288] mirserver: Physical size 0,0" 0x0mm
[2017-03-29 18:54:38.946303] mirserver: Power is on
[2017-03-29 18:54:38.946322] mirserver: Current mode 1920x1080 50,00Hz
[2017-03-29 18:54:38.946337] mirserver: Preferred mode 1920x1080 50,00Hz
[2017-03-29 18:54:38.946352] mirserver: Orientation normal
[2017-03-29 18:54:38.946365] mirserver: Logical size 1920x1080
[2017-03-29 18:54:38.946388] mirserver: Logical position +0+0

Not sure if I need to fiddle something with the drivers but it looks to me like Output 47 claims to be disconnected while it is not (as it is the internal display).

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

If you're using the latest Mir code that this branch depends on, your outputs would be numbered 1-N. I'm not sure if that indicates your silo is incomplete...

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

Additionally, next time please post the full Mir log; you've cut out some relevant information (particularly: has it actually picked up both your GPUs!)

Revision history for this message
Michael Zanetti (mzanetti) wrote :

Here's the complete log: http://paste.ubuntu.com/24279666/

And here's the silo with this branch: https://bileto.ubuntu.com/#/ticket/2373

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

Hmm, that's the (nested) Unity 8 log. Could you attach the USC log? USC is the one that actually​ interfaces with the hardware.

Revision history for this message
Michael Zanetti (mzanetti) wrote :

Sorry, here's unity-system-compositor's log: http://paste.ubuntu.com/24280507/

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

Just a warning: nouveau is highly unstable and will crash if used from multiple threads (or multiple heads) -> bug 1553328

I strongly recommend not using nouveau (which also means not using Nvidia at all) for now. At least until we have adequate workarounds for bug 1553328 in place, or complete Nvidia proprietary driver support.

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

I've just added some extra startup logging that will make it clear
a) What cards are being driven, and by what driver,
b) What outputs are attached, to what card, and
c) What outputs are *connected*, and the modes drm gives us (if any)

Please pull that into your silo, and try again :)

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

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

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

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

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

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

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

lgtm overall.

low-hanging fix:
EGLBufferCopier should check more of the RC codes coming from egl, and throw or at least log if there are failures in setup.
eg,
758+ eglMakeCurrent(display, egl_surface, egl_surface, context);

future improvements/opinions that don't need fixing / are non-blocking.
+ EGLBufferCopier::EGLBufferCopier()
some of the egl/gl initialization stuff could use RAII to clean up better if an exception is thrown. (additionally, maybe some of the stuff in src/include/platform/mir/graphics could be reused? a specific example would be that mg::EGLExtensions would load the 3 buffer import functions )

+ static std::vector<std::shared_ptr<DRMHelper>> open_all_devices(
why static?

+ double hz = (mode.clock * 100000LL /
253 + ((long)mode.htotal * (long)mode.vtotal)
254 + ) / 100.0;
could use std::chrono conversions (even if they're long, they make for quicker visual checks imo)

492 + auto const raw_surface = surface.get();
could just call surface.get() when making the db a few lines later

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2017-03-24 15:20:01 +0000
3+++ CMakeLists.txt 2017-03-31 06:57:08 +0000
4@@ -264,6 +264,10 @@
5 find_package( PkgConfig )
6 pkg_check_modules( GBM REQUIRED gbm>=9.0.0)
7 pkg_check_modules( DRM REQUIRED libdrm )
8+ if (GBM_VERSION VERSION_LESS 11)
9+ message(WARNING "Hybrid support requires libgbm from Mesa 11.0 or greater. Hybrid setups will not work")
10+ add_definitions(-DMIR_NO_HYBRID_SUPPORT)
11+ endif()
12 endif()
13
14 if (MIR_BUILD_PLATFORM_EGLSTREAM_KMS)
15
16=== modified file 'src/platforms/mesa/server/display_helpers.cpp'
17--- src/platforms/mesa/server/display_helpers.cpp 2017-03-16 07:37:11 +0000
18+++ src/platforms/mesa/server/display_helpers.cpp 2017-03-31 06:57:08 +0000
19@@ -20,9 +20,14 @@
20 #include "drm_close_threadsafe.h"
21
22 #include "kms-utils/drm_mode_resources.h"
23+#include "mir/graphics/gl_config.h"
24+#include "mir/graphics/egl_error.h"
25
26 #include "mir/udev/wrapper.h"
27
28+#define MIR_LOG_COMPONENT "mesa-kms"
29+#include "mir/log.h"
30+
31 #include <boost/exception/errinfo_errno.hpp>
32 #include <boost/throw_exception.hpp>
33
34@@ -31,6 +36,7 @@
35 #include <stdexcept>
36 #include <xf86drm.h>
37 #include <fcntl.h>
38+#include <vector>
39
40 namespace mg = mir::graphics;
41 namespace mgm = mir::graphics::mesa;
42@@ -40,6 +46,69 @@
43 * DRMHelper *
44 *************/
45
46+std::vector<std::shared_ptr<mgmh::DRMHelper>>
47+mgmh::DRMHelper::open_all_devices(std::shared_ptr<mir::udev::Context> const& udev)
48+{
49+ int tmp_fd = -1;
50+ int error = ENODEV; //Default error is "there are no DRM devices"
51+
52+ mir::udev::Enumerator devices(udev);
53+ devices.match_subsystem("drm");
54+ devices.match_sysname("card[0-9]");
55+
56+ devices.scan_devices();
57+
58+ std::vector<std::shared_ptr<DRMHelper>> opened_devices;
59+
60+ for(auto& device : devices)
61+ {
62+ // If directly opening the DRM device is good enough for X it's good enough for us!
63+ tmp_fd = open(device.devnode(), O_RDWR | O_CLOEXEC);
64+ if (tmp_fd < 0)
65+ {
66+ error = errno;
67+ mir::log_warning(
68+ "Failed to open DRM device node %s: %i (%s)",
69+ device.devnode(),
70+ error,
71+ strerror(error));
72+ continue;
73+ }
74+
75+ // Check that the drm device is usable by setting the interface version we use (1.4)
76+ drmSetVersion sv;
77+ sv.drm_di_major = 1;
78+ sv.drm_di_minor = 4;
79+ sv.drm_dd_major = -1; /* Don't care */
80+ sv.drm_dd_minor = -1; /* Don't care */
81+
82+ if ((error = -drmSetInterfaceVersion(tmp_fd, &sv)))
83+ {
84+ close(tmp_fd);
85+ mir::log_warning(
86+ "Failed to set DRM interface version on device %s: %i (%s)",
87+ device.devnode(),
88+ error,
89+ strerror(error));
90+ tmp_fd = -1;
91+ continue;
92+ }
93+
94+ // Can't use make_shared with the private constructor.
95+ opened_devices.push_back(std::shared_ptr<DRMHelper>{new DRMHelper{tmp_fd}});
96+ mir::log_info("Using DRM device %s", device.devnode());
97+ tmp_fd = -1;
98+ }
99+
100+ if (opened_devices.size() == 0)
101+ {
102+ BOOST_THROW_EXCEPTION((
103+ std::system_error{error, std::system_category(), "Error opening DRM device"}));
104+ }
105+
106+ return opened_devices;
107+}
108+
109 void mgmh::DRMHelper::setup(std::shared_ptr<mir::udev::Context> const& udev)
110 {
111 fd = open_drm_device(udev);
112@@ -160,6 +229,12 @@
113 }
114 }
115
116+mgmh::DRMHelper::DRMHelper(int fd)
117+ : fd{fd},
118+ node_to_use{DRMNodeToUse::card}
119+{
120+}
121+
122 int mgmh::DRMHelper::is_appropriate_device(std::shared_ptr<mir::udev::Context> const& udev, mir::udev::Device const& drm_device)
123 {
124 mir::udev::Enumerator children(udev);
125@@ -285,11 +360,29 @@
126 std::runtime_error("Failed to create GBM device"));
127 }
128
129-mgm::GBMSurfaceUPtr mgmh::GBMHelper::create_scanout_surface(uint32_t width, uint32_t height)
130+mgm::GBMSurfaceUPtr mgmh::GBMHelper::create_scanout_surface(
131+ uint32_t width,
132+ uint32_t height,
133+ bool sharable)
134 {
135+ auto format_flags = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT;
136+
137+ if (sharable)
138+ {
139+#ifdef MIR_NO_HYBRID_SUPPORT
140+ BOOST_THROW_EXCEPTION((
141+ std::runtime_error{
142+ "Mir built without hybrid support, but configuration requires hybrid outputs.\n"
143+ "This will not work unless Mir is rebuilt against Mesa >= 11.0"}
144+ ));
145+#else
146+ format_flags |= GBM_BO_USE_LINEAR;
147+#endif
148+ }
149+
150 auto surface_raw = gbm_surface_create(device, width, height,
151 GBM_BO_FORMAT_XRGB8888,
152- GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
153+ format_flags);
154
155 auto gbm_surface_deleter = [](gbm_surface *p) { if (p) gbm_surface_destroy(p); };
156 GBMSurfaceUPtr surface{surface_raw, gbm_surface_deleter};
157
158=== modified file 'src/platforms/mesa/server/display_helpers.h'
159--- src/platforms/mesa/server/display_helpers.h 2017-03-16 07:37:11 +0000
160+++ src/platforms/mesa/server/display_helpers.h 2017-03-31 06:57:08 +0000
161@@ -24,6 +24,7 @@
162
163 #include <cstddef>
164 #include <memory>
165+#include <vector>
166
167 #pragma GCC diagnostic push
168 #pragma GCC diagnostic warning "-Wall"
169@@ -59,6 +60,9 @@
170 DRMHelper(const DRMHelper &) = delete;
171 DRMHelper& operator=(const DRMHelper&) = delete;
172
173+ static std::vector<std::shared_ptr<DRMHelper>> open_all_devices(
174+ std::shared_ptr<mir::udev::Context> const& udev);
175+
176 void setup(std::shared_ptr<mir::udev::Context> const& udev);
177 mir::Fd authenticated_fd();
178 void auth_magic(drm_magic_t magic);
179@@ -70,6 +74,8 @@
180 DRMNodeToUse const node_to_use;
181
182 private:
183+ DRMHelper(int fd);
184+
185 // TODO: This herustic is temporary; should be replaced with
186 // handling >1 DRM device.
187 int is_appropriate_device(std::shared_ptr<mir::udev::Context> const& udev, mir::udev::Device const& dev);
188@@ -90,7 +96,7 @@
189
190 void setup(const DRMHelper& drm);
191 void setup(int drm_fd);
192- GBMSurfaceUPtr create_scanout_surface(uint32_t width, uint32_t height);
193+ GBMSurfaceUPtr create_scanout_surface(uint32_t width, uint32_t height, bool sharable);
194
195 gbm_device* device;
196 };
197
198=== modified file 'src/platforms/mesa/server/kms/CMakeLists.txt'
199--- src/platforms/mesa/server/kms/CMakeLists.txt 2017-03-16 07:37:11 +0000
200+++ src/platforms/mesa/server/kms/CMakeLists.txt 2017-03-31 06:57:08 +0000
201@@ -38,6 +38,7 @@
202 kms_display_configuration.h
203 real_kms_display_configuration.cpp
204 kms_output.h
205+ real_kms_output.h
206 real_kms_output.cpp
207 kms_output_container.h
208 real_kms_output_container.cpp
209
210=== modified file 'src/platforms/mesa/server/kms/display.cpp'
211--- src/platforms/mesa/server/kms/display.cpp 2017-03-22 07:01:56 +0000
212+++ src/platforms/mesa/server/kms/display.cpp 2017-03-31 06:57:08 +0000
213@@ -37,8 +37,14 @@
214 #include <boost/exception/get_error_info.hpp>
215 #include <boost/exception/errinfo_errno.hpp>
216
217+#define MIR_LOG_COMPONENT "mesa-kms"
218+#include "mir/log.h"
219+#include "kms-utils/drm_mode_resources.h"
220+#include "kms-utils/kms_connector.h"
221+
222 #include <stdexcept>
223 #include <algorithm>
224+#include <unordered_map>
225
226 namespace mgm = mir::graphics::mesa;
227 namespace mg = mir::graphics;
228@@ -78,23 +84,118 @@
229 mgm::helpers::EGLHelper egl;
230 };
231
232-}
233-
234-mgm::Display::Display(std::shared_ptr<helpers::DRMHelper> const& drm,
235+std::vector<int> drm_fds_from_drm_helpers(
236+ std::vector<std::shared_ptr<mgm::helpers::DRMHelper>> const& helpers)
237+{
238+ std::vector<int> fds;
239+ for (auto const& helper: helpers)
240+ {
241+ fds.push_back(helper->fd);
242+ }
243+ return fds;
244+}
245+
246+double calculate_vrefresh_hz(drmModeModeInfo const& mode)
247+{
248+ if (mode.htotal == 0 || mode.vtotal == 0)
249+ return 0.0;
250+
251+ /* mode.clock is in KHz */
252+ double hz = (mode.clock * 100000LL /
253+ ((long)mode.htotal * (long)mode.vtotal)
254+ ) / 100.0;
255+
256+ return hz;
257+}
258+
259+char const* describe_connection_status(drmModeConnector const& connection)
260+{
261+ switch (connection.connection)
262+ {
263+ case DRM_MODE_CONNECTED:
264+ return "connected";
265+ case DRM_MODE_DISCONNECTED:
266+ return "disconnected";
267+ case DRM_MODE_UNKNOWNCONNECTION:
268+ return "UNKNOWN";
269+ default:
270+ return "<Unexpected connection value>";
271+ }
272+}
273+
274+void log_drm_details(std::vector<std::shared_ptr<mgm::helpers::DRMHelper>> const& drm)
275+{
276+ mir::log_info("DRM device details:");
277+ for (auto const& device : drm)
278+ {
279+ auto version = std::unique_ptr<drmVersion, decltype(&drmFreeVersion)>{
280+ drmGetVersion(device->fd),
281+ &drmFreeVersion};
282+
283+ auto device_name = std::unique_ptr<char, decltype(&free)>{
284+ drmGetDeviceNameFromFd(device->fd),
285+ &free
286+ };
287+
288+ mir::log_info(
289+ "%s: using driver %s [%s] (version: %i.%i.%i driver date: %s)",
290+ device_name.get(),
291+ version->name,
292+ version->desc,
293+ version->version_major,
294+ version->version_minor,
295+ version->version_patchlevel,
296+ version->date);
297+
298+ mg::kms::DRMModeResources resources{device->fd};
299+ for (auto const& connector : resources.connectors())
300+ {
301+ mir::log_info(
302+ "\tOutput: %s (%s)",
303+ mg::kms::connector_name(connector).c_str(),
304+ describe_connection_status(*connector));
305+ for (auto i = 0; i < connector->count_modes; ++i)
306+ {
307+ mir::log_info(
308+ "\t\tMode: %i×%i@%.2f",
309+ connector->modes[i].hdisplay,
310+ connector->modes[i].vdisplay,
311+ calculate_vrefresh_hz(connector->modes[i]));
312+ }
313+ }
314+ }
315+}
316+
317+}
318+
319+mgm::Display::Display(std::vector<std::shared_ptr<helpers::DRMHelper>> const& drm,
320 std::shared_ptr<helpers::GBMHelper> const& gbm,
321 std::shared_ptr<VirtualTerminal> const& vt,
322 mgm::BypassOption bypass_option,
323 std::shared_ptr<DisplayConfigurationPolicy> const& initial_conf_policy,
324 std::shared_ptr<GLConfig> const& gl_config,
325 std::shared_ptr<DisplayReport> const& listener)
326- : drm(drm),
327+ : drm{drm},
328 gbm(gbm),
329 vt(vt),
330 listener(listener),
331 monitor(mir::udev::Context()),
332 shared_egl{*gl_config},
333- output_container{std::make_shared<RealKMSOutputContainer>(drm->fd,
334- std::make_shared<KMSPageFlipper>(drm->fd, listener))},
335+ output_container{
336+ std::make_shared<RealKMSOutputContainer>(
337+ drm_fds_from_drm_helpers(drm),
338+ [
339+ listener,
340+ flippers = std::unordered_map<int, std::shared_ptr<KMSPageFlipper>>{}
341+ ](int drm_fd) mutable
342+ {
343+ auto& flipper = flippers[drm_fd];
344+ if (!flipper)
345+ {
346+ flipper = std::make_shared<KMSPageFlipper>(drm_fd, listener);
347+ }
348+ return flipper;
349+ })},
350 current_display_configuration{output_container},
351 dirty_configuration{false},
352 bypass_option(bypass_option),
353@@ -107,6 +208,8 @@
354 monitor.filter_by_subsystem_and_type("drm", "drm_minor");
355 monitor.enable();
356
357+ log_drm_details(drm);
358+
359 initial_conf_policy->apply_to(current_display_configuration);
360
361 configure(current_display_configuration);
362@@ -193,7 +296,8 @@
363 try
364 {
365 if (auto c = cursor.lock()) c->suspend();
366- drm->drop_master();
367+ for (auto& helper : drm)
368+ helper->drop_master();
369 }
370 catch(std::runtime_error const& e)
371 {
372@@ -206,7 +310,8 @@
373 {
374 try
375 {
376- drm->set_master();
377+ for (auto& helper : drm)
378+ helper->set_master();
379 }
380 catch(std::runtime_error const& e)
381 {
382@@ -233,6 +338,17 @@
383
384 auto mgm::Display::create_hardware_cursor() -> std::shared_ptr<graphics::Cursor>
385 {
386+ /*
387+ * TODO: Using the hardware cursor in a hybrid-output situation requires making
388+ * mgm::Cursor hybrid-aware so it can create a cursor bo on each GPU.
389+ *
390+ * For a first cut, just disable the hardware cursor on hybrid systems.
391+ */
392+ if (drm.size() > 1)
393+ {
394+ return nullptr;
395+ }
396+
397 // There is only one hardware cursor. We do not keep a strong reference to it in the display though,
398 // if no other component of Mir is interested (i.e. the input stack does not keep a reference to send
399 // position updates) we must be configured not to use a cursor and thusly let it deallocate.
400@@ -331,6 +447,35 @@
401 return output->last_frame();
402 }
403
404+namespace
405+{
406+/*
407+ * Add output to the grouping, maintaining the invariant that each vector of outputs
408+ * is a single GPU memory domain.
409+ */
410+void add_to_drm_device_group(
411+ std::vector<std::vector<std::shared_ptr<mgm::KMSOutput>>>& grouping,
412+ std::shared_ptr<mgm::KMSOutput>&& output)
413+{
414+ for (auto &group : grouping)
415+ {
416+ /*
417+ * We could be smarter about this, but being on the same DRM device is guaranteed
418+ * to be in the same GPU memory domain :).
419+ */
420+ if (group.front()->drm_fd() == output->drm_fd())
421+ {
422+ group.push_back(std::move(output));
423+ break;
424+ }
425+ }
426+ if (output)
427+ {
428+ grouping.push_back(std::vector<std::shared_ptr<mgm::KMSOutput>>{std::move(output)});
429+ }
430+}
431+}
432+
433 void mgm::Display::configure_locked(
434 mgm::RealKMSDisplayConfiguration const& kms_conf,
435 std::lock_guard<std::mutex> const&)
436@@ -373,7 +518,8 @@
437 [&](OverlappingOutputGroup const& group)
438 {
439 auto bounding_rect = group.bounding_rectangle();
440- std::vector<std::shared_ptr<KMSOutput>> kms_outputs;
441+ // Each vector<KMSOutput> is a single GPU memory domain
442+ std::vector<std::vector<std::shared_ptr<KMSOutput>>> kms_output_groups;
443 MirOrientation orientation = mir_orientation_normal;
444
445 group.for_each_output(
446@@ -388,7 +534,7 @@
447 {
448 kms_output->set_power_mode(conf_output.power_mode);
449 kms_output->set_gamma(conf_output.gamma);
450- kms_outputs.push_back(kms_output);
451+ add_to_drm_device_group(kms_output_groups, std::move(kms_output));
452 }
453
454 /*
455@@ -411,28 +557,38 @@
456 std::swap(width, height);
457 }
458
459- auto surface = gbm->create_scanout_surface(width, height);
460- auto const raw_surface = surface.get();
461-
462- std::unique_ptr<DisplayBuffer> db{
463- new DisplayBuffer{bypass_option,
464- listener,
465- kms_outputs,
466- GBMOutputSurface{
467- drm->fd,
468- std::move(surface),
469- width, height,
470- helpers::EGLHelper{
471- *gl_config,
472- *gbm,
473- raw_surface,
474- shared_egl.context()
475- }
476- },
477- bounding_rect,
478- orientation}};
479-
480- display_buffers_new.push_back(std::move(db));
481+ for (auto const& group : kms_output_groups)
482+ {
483+ /*
484+ * In a hybrid setup a scanout surface needs to be allocated differently if it
485+ * needs to be able to be shared across GPUs. This likely reduces performance.
486+ *
487+ * As a first cut, assume every scanout buffer in a hybrid setup might need
488+ * to be shared.
489+ */
490+ auto surface = gbm->create_scanout_surface(width, height, drm.size() != 1);
491+ auto const raw_surface = surface.get();
492+
493+ auto db = std::make_unique<DisplayBuffer>(
494+ bypass_option,
495+ listener,
496+ group,
497+ GBMOutputSurface{
498+ group.front()->drm_fd(),
499+ std::move(surface),
500+ width, height,
501+ helpers::EGLHelper{
502+ *gl_config,
503+ *gbm,
504+ raw_surface,
505+ shared_egl.context()
506+ }
507+ },
508+ bounding_rect,
509+ orientation);
510+
511+ display_buffers_new.push_back(std::move(db));
512+ }
513 }
514 });
515
516
517=== modified file 'src/platforms/mesa/server/kms/display.h'
518--- src/platforms/mesa/server/kms/display.h 2017-03-22 07:01:56 +0000
519+++ src/platforms/mesa/server/kms/display.h 2017-03-31 06:57:08 +0000
520@@ -65,7 +65,7 @@
521 public renderer::gl::ContextSource
522 {
523 public:
524- Display(std::shared_ptr<helpers::DRMHelper> const& drm,
525+ Display(std::vector<std::shared_ptr<helpers::DRMHelper>> const& drm,
526 std::shared_ptr<helpers::GBMHelper> const& gbm,
527 std::shared_ptr<VirtualTerminal> const& vt,
528 BypassOption bypass_option,
529@@ -106,7 +106,7 @@
530 void clear_connected_unused_outputs();
531
532 mutable std::mutex configuration_mutex;
533- std::shared_ptr<helpers::DRMHelper> const drm;
534+ std::vector<std::shared_ptr<helpers::DRMHelper>> const drm;
535 std::shared_ptr<helpers::GBMHelper> const gbm;
536 std::shared_ptr<VirtualTerminal> const vt;
537 std::shared_ptr<DisplayReport> const listener;
538
539=== modified file 'src/platforms/mesa/server/kms/display_buffer.cpp'
540--- src/platforms/mesa/server/kms/display_buffer.cpp 2017-03-24 07:06:18 +0000
541+++ src/platforms/mesa/server/kms/display_buffer.cpp 2017-03-31 06:57:08 +0000
542@@ -28,8 +28,13 @@
543 #include "mir/graphics/egl_error.h"
544
545 #include <boost/throw_exception.hpp>
546+#include <EGL/egl.h>
547+#include <EGL/eglext.h>
548 #include MIR_SERVER_GL_H
549+#include <GLES2/gl2ext.h>
550+#include <drm/drm_fourcc.h>
551
552+#include <sstream>
553 #include <stdexcept>
554 #include <chrono>
555 #include <thread>
556@@ -104,18 +109,378 @@
557
558 namespace
559 {
560-
561-void ensure_egl_image_extensions()
562-{
563- std::string ext_string;
564- const char* exts = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
565- if (exts)
566- ext_string = exts;
567-
568- if (ext_string.find("GL_OES_EGL_image") == std::string::npos)
569- BOOST_THROW_EXCEPTION(std::runtime_error("GLES2 implementation doesn't support GL_OES_EGL_image extension"));
570-}
571-
572+void require_extensions(
573+ std::initializer_list<char const*> extensions,
574+ std::function<std::string()> const& extension_getter)
575+{
576+ std::stringstream missing_extensions;
577+
578+ std::string const ext_string = extension_getter();
579+
580+ for (auto extension : extensions)
581+ {
582+ if (ext_string.find(extension) == std::string::npos)
583+ {
584+ missing_extensions << "Missing " << extension << std::endl;
585+ }
586+ }
587+
588+ if (!missing_extensions.str().empty())
589+ {
590+ BOOST_THROW_EXCEPTION(std::runtime_error(
591+ std::string("Missing required extensions:\n") + missing_extensions.str()));
592+ }
593+}
594+
595+void require_egl_extensions(EGLDisplay dpy, std::initializer_list<char const*> extensions)
596+{
597+ require_extensions(
598+ extensions,
599+ [dpy]() -> std::string
600+ {
601+ char const* maybe_exts = eglQueryString(dpy, EGL_EXTENSIONS);
602+ if (maybe_exts)
603+ return maybe_exts;
604+ return {};
605+ });
606+}
607+
608+void require_gl_extensions(std::initializer_list<char const*> extensions)
609+{
610+ require_extensions(
611+ extensions,
612+ []() -> std::string
613+ {
614+ char const *maybe_exts =
615+ reinterpret_cast<char const*>(glGetString(GL_EXTENSIONS));
616+ if (maybe_exts)
617+ return maybe_exts;
618+ return {};
619+ });
620+}
621+
622+bool needs_bounce_buffer(mgm::KMSOutput const& destination, gbm_bo* source)
623+{
624+ return destination.buffer_requires_migration(source);
625+}
626+
627+const GLchar* const vshader =
628+ {
629+ "attribute vec4 position;\n"
630+ "attribute vec2 texcoord;\n"
631+ "varying vec2 v_texcoord;\n"
632+ "void main() {\n"
633+ " gl_Position = position;\n"
634+ " v_texcoord = texcoord;\n"
635+ "}\n"
636+ };
637+
638+const GLchar* const fshader =
639+ {
640+ "#ifdef GL_ES\n"
641+ "precision mediump float;\n"
642+ "#endif\n"
643+ "uniform sampler2D tex;"
644+ "varying vec2 v_texcoord;\n"
645+ "void main() {\n"
646+ " gl_FragColor = texture2D(tex, v_texcoord);\n"
647+ "}\n"
648+ };
649+
650+class VBO
651+{
652+public:
653+ VBO(void const* data, size_t size)
654+ {
655+ glGenBuffers(1, &buf_id);
656+ glBindBuffer(GL_ARRAY_BUFFER, buf_id);
657+ glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
658+ glBindBuffer(GL_ARRAY_BUFFER, 0);
659+ }
660+
661+ ~VBO()
662+ {
663+ glDeleteBuffers(1, &buf_id);
664+ }
665+
666+ void bind()
667+ {
668+ glBindBuffer(GL_ARRAY_BUFFER, buf_id);
669+ }
670+
671+private:
672+ GLuint buf_id;
673+};
674+
675+class EGLBufferCopier
676+{
677+public:
678+ EGLBufferCopier(
679+ int drm_fd,
680+ uint32_t width,
681+ uint32_t height,
682+ uint32_t format)
683+ : eglCreateImageKHR{
684+ reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"))},
685+ eglDestroyImageKHR{
686+ reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR"))},
687+ glEGLImageTargetTexture2DOES{
688+ reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"))},
689+ device{gbm_create_device(drm_fd), &gbm_device_destroy},
690+ width{width},
691+ height{height},
692+ surface{create_scanout_surface(*device, width, height, format)}
693+ {
694+ require_gl_extensions({
695+ "GL_OES_EGL_image"
696+ });
697+
698+ EGLint const config_attr[] = {
699+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
700+ EGL_RED_SIZE, 5,
701+ EGL_GREEN_SIZE, 5,
702+ EGL_BLUE_SIZE, 5,
703+ EGL_ALPHA_SIZE, 0,
704+ EGL_DEPTH_SIZE, 0,
705+ EGL_STENCIL_SIZE, 0,
706+ EGL_RENDERABLE_TYPE, MIR_SERVER_EGL_OPENGL_BIT,
707+ EGL_NONE
708+ };
709+
710+ static const EGLint required_egl_version_major = 1;
711+ static const EGLint required_egl_version_minor = 4;
712+
713+ EGLint num_egl_configs;
714+ EGLConfig egl_config;
715+
716+ display = eglGetDisplay(static_cast<EGLNativeDisplayType>(device.get()));
717+ if (display == EGL_NO_DISPLAY)
718+ BOOST_THROW_EXCEPTION(mg::egl_error("Failed to get EGL display"));
719+
720+ EGLint major, minor;
721+
722+ if (eglInitialize(display, &major, &minor) == EGL_FALSE)
723+ BOOST_THROW_EXCEPTION(mg::egl_error("Failed to initialize EGL display"));
724+
725+ if ((major < required_egl_version_major) ||
726+ (major == required_egl_version_major && minor < required_egl_version_minor))
727+ {
728+ BOOST_THROW_EXCEPTION(std::runtime_error("Incompatible EGL version"));
729+ }
730+
731+ require_egl_extensions(
732+ display,
733+ {
734+ "EGL_KHR_image_base",
735+ "EGL_EXT_image_dma_buf_import"
736+ });
737+
738+ if (eglChooseConfig(display, config_attr, &egl_config, 1, &num_egl_configs) == EGL_FALSE ||
739+ num_egl_configs != 1)
740+ {
741+ BOOST_THROW_EXCEPTION(mg::egl_error("Failed to choose ARGB EGL config"));
742+ }
743+
744+ eglBindAPI(MIR_SERVER_EGL_OPENGL_API);
745+ static const EGLint context_attr[] = {
746+#if MIR_SERVER_EGL_OPENGL_BIT == EGL_OPENGL_ES2_BIT
747+ EGL_CONTEXT_CLIENT_VERSION, 2,
748+#endif
749+ EGL_NONE
750+ };
751+
752+ context = eglCreateContext(display, egl_config, EGL_NO_CONTEXT, context_attr);
753+ if (context == EGL_NO_CONTEXT)
754+ BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context"));
755+
756+ egl_surface = eglCreateWindowSurface(display, egl_config, surface.get(), nullptr);
757+ eglMakeCurrent(display, egl_surface, egl_surface, context);
758+
759+ auto vertex = glCreateShader(GL_VERTEX_SHADER);
760+ glShaderSource(vertex, 1, &vshader, nullptr);
761+ glCompileShader(vertex);
762+
763+ int compiled;
764+ glGetShaderiv (vertex, GL_COMPILE_STATUS, &compiled);
765+
766+ if (!compiled) {
767+ GLchar log[1024];
768+
769+ glGetShaderInfoLog (vertex, sizeof log - 1, NULL, log);
770+ log[sizeof log - 1] = '\0';
771+ glDeleteShader (vertex);
772+
773+ BOOST_THROW_EXCEPTION(
774+ std::runtime_error(std::string{"Failed to compile vertex shader:\n"} + log));
775+ }
776+
777+
778+ auto fragment = glCreateShader(GL_FRAGMENT_SHADER);
779+ glShaderSource(fragment, 1, &fshader, nullptr);
780+ glCompileShader(fragment);
781+
782+ glGetShaderiv (fragment, GL_COMPILE_STATUS, &compiled);
783+ if (!compiled) {
784+ GLchar log[1024];
785+
786+ glGetShaderInfoLog (fragment, sizeof log - 1, NULL, log);
787+ log[sizeof log - 1] = '\0';
788+ glDeleteShader (fragment);
789+
790+ BOOST_THROW_EXCEPTION(
791+ std::runtime_error(std::string{"Failed to compile fragment shader:\n"} + log));
792+ }
793+
794+ prog = glCreateProgram();
795+ glAttachShader(prog, vertex);
796+ glAttachShader(prog, fragment);
797+ glLinkProgram(prog);
798+ glGetProgramiv (prog, GL_LINK_STATUS, &compiled);
799+ if (!compiled) {
800+ GLchar log[1024];
801+
802+ glGetProgramInfoLog (prog, sizeof log - 1, NULL, log);
803+ log[sizeof log - 1] = '\0';
804+
805+ BOOST_THROW_EXCEPTION(
806+ std::runtime_error(std::string{"Failed to link shader prog:\n"} + log));
807+ }
808+
809+ glUseProgram(prog);
810+
811+ attrpos = glGetAttribLocation(prog, "position");
812+ attrtex = glGetAttribLocation(prog, "texcoord");
813+ auto unitex = glGetUniformLocation(prog, "tex");
814+
815+ glGenTextures(1, &tex);
816+ glActiveTexture(GL_TEXTURE0);
817+ glBindTexture(GL_TEXTURE_2D, tex);
818+
819+ glUniform1i(unitex, 0);
820+
821+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
822+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
823+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
824+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
825+
826+ static GLfloat const dest_vert[4][2] =
827+ { { -1.f, 1.f }, { 1.f, 1.f }, { 1.f, -1.f }, { -1.f, -1.f } };
828+ vert_data = std::make_unique<VBO>(dest_vert, sizeof(dest_vert));
829+
830+ static GLfloat const tex_vert[4][2] =
831+ {
832+ { 0.f, 0.f }, { 1.f, 0.f }, { 1.f, 1.f }, { 0.f, 1.f },
833+ };
834+ tex_data = std::make_unique<VBO>(tex_vert, sizeof(tex_vert));
835+ }
836+
837+ EGLBufferCopier(EGLBufferCopier const&) = delete;
838+ EGLBufferCopier& operator==(EGLBufferCopier const&) = delete;
839+
840+ ~EGLBufferCopier()
841+ {
842+ eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context);
843+ vert_data = nullptr;
844+ tex_data = nullptr;
845+ eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
846+ eglDestroySurface(display, egl_surface);
847+ eglDestroyContext(display, context);
848+ eglTerminate(display);
849+ }
850+
851+ mgm::GBMOutputSurface::FrontBuffer copy_front_buffer_from(mgm::GBMOutputSurface::FrontBuffer&& from)
852+ {
853+ eglMakeCurrent(display, egl_surface, egl_surface, context);
854+ mir::Fd const dma_buf{gbm_bo_get_fd(from)};
855+
856+ glUseProgram(prog);
857+
858+ EGLint const image_attrs[] = {
859+ EGL_WIDTH, static_cast<EGLint>(width),
860+ EGL_HEIGHT, static_cast<EGLint>(height),
861+ EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_XRGB8888,
862+ EGL_DMA_BUF_PLANE0_FD_EXT, static_cast<int>(dma_buf),
863+ EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
864+ EGL_DMA_BUF_PLANE0_PITCH_EXT, static_cast<EGLint>(gbm_bo_get_stride(from)),
865+ EGL_NONE
866+ };
867+
868+ auto image = eglCreateImageKHR(
869+ display,
870+ EGL_NO_CONTEXT,
871+ EGL_LINUX_DMA_BUF_EXT,
872+ nullptr,
873+ image_attrs);
874+
875+ if (image == EGL_NO_IMAGE_KHR)
876+ {
877+ BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGLImage from dma_buf"));
878+ }
879+
880+ glActiveTexture(GL_TEXTURE0);
881+ glBindTexture(GL_TEXTURE_2D, tex);
882+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
883+
884+ vert_data->bind();
885+ glVertexAttribPointer (attrpos, 2, GL_FLOAT, GL_FALSE, 0, 0);
886+
887+ tex_data->bind();
888+ glVertexAttribPointer (attrtex, 2, GL_FLOAT, GL_FALSE, 0, 0);
889+
890+ glEnableVertexAttribArray(attrpos);
891+ glEnableVertexAttribArray(attrtex);
892+
893+ GLubyte const idx[] = { 0, 1, 3, 2 };
894+ glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, idx);
895+
896+ if (eglSwapBuffers(display, egl_surface) != EGL_TRUE)
897+ {
898+ BOOST_THROW_EXCEPTION(mg::egl_error("Failed to swap bounce buffers"));
899+ }
900+
901+ eglDestroyImageKHR(display, image);
902+
903+ eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
904+ return mgm::GBMOutputSurface::FrontBuffer(surface.get());
905+ }
906+
907+ private:
908+ static mgm::GBMSurfaceUPtr create_scanout_surface(
909+ gbm_device& on,
910+ uint32_t width,
911+ uint32_t height,
912+ uint32_t format)
913+ {
914+ auto* const device = &on;
915+
916+ return {
917+ gbm_surface_create(
918+ device,
919+ width,
920+ height,
921+ format,
922+ GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING),
923+ &gbm_surface_destroy};
924+ }
925+
926+ PFNEGLCREATEIMAGEKHRPROC const eglCreateImageKHR;
927+ PFNEGLDESTROYIMAGEKHRPROC const eglDestroyImageKHR;
928+ PFNGLEGLIMAGETARGETTEXTURE2DOESPROC const glEGLImageTargetTexture2DOES;
929+
930+ std::unique_ptr<gbm_device, decltype(&gbm_device_destroy)> const device;
931+ uint32_t const width;
932+ uint32_t const height;
933+ mgm::GBMSurfaceUPtr const surface;
934+ EGLDisplay display;
935+ EGLContext context;
936+ EGLSurface egl_surface;
937+ GLuint prog;
938+ GLuint tex;
939+ GLint attrtex;
940+ GLint attrpos;
941+ std::unique_ptr<VBO> vert_data;
942+ std::unique_ptr<VBO> tex_data;
943+};
944 }
945
946 mgm::DisplayBuffer::DisplayBuffer(
947@@ -152,19 +517,48 @@
948 make_current();
949
950 listener->report_successful_egl_make_current_on_construction();
951-
952- ensure_egl_image_extensions();
953-
954+
955 glClear(GL_COLOR_BUFFER_BIT);
956
957 surface.swap_buffers();
958
959 listener->report_successful_egl_buffer_swap_on_construction();
960
961- visible_composite_frame = surface.lock_front();
962- if (!visible_composite_frame)
963+ auto temporary_front = surface.lock_front();
964+ if (!temporary_front)
965 fatal_error("Failed to get frontbuffer");
966
967+ if (needs_bounce_buffer(*outputs.front(), temporary_front))
968+ {
969+ get_front_buffer = std::bind(
970+ std::mem_fn(&EGLBufferCopier::copy_front_buffer_from),
971+ std::make_shared<EGLBufferCopier>(
972+ outputs.front()->drm_fd(),
973+ fb_width,
974+ fb_height,
975+ GBM_BO_FORMAT_XRGB8888),
976+ std::placeholders::_1);
977+ }
978+ else
979+ {
980+ get_front_buffer = [](auto&& fb) { return std::move(fb); };
981+ }
982+
983+ visible_composite_frame = get_front_buffer(std::move(temporary_front));
984+
985+ /*
986+ * Check that our (possibly bounced) front buffer is usable on *all* the
987+ * outputs we've been asked to output on.
988+ */
989+ for (auto const& output : outputs)
990+ {
991+ if (output->buffer_requires_migration(visible_composite_frame))
992+ {
993+ BOOST_THROW_EXCEPTION(std::invalid_argument(
994+ "Attempted to create a DisplayBuffer spanning multiple GPU memory domains"));
995+ }
996+ }
997+
998 set_crtc(*outputs.front()->fb_for(visible_composite_frame));
999
1000 release_current();
1001@@ -213,7 +607,8 @@
1002 if (!native)
1003 BOOST_THROW_EXCEPTION(std::invalid_argument("could not convert NativeBuffer"));
1004 if (native->flags & mir_buffer_flag_can_scanout &&
1005- bypass_buffer->size() == geom::Size{fb_width,fb_height})
1006+ bypass_buffer->size() == geom::Size{fb_width,fb_height} &&
1007+ !needs_bounce_buffer(*outputs.front(), native->bo))
1008 {
1009 if (auto bufobj = outputs.front()->fb_for(native->bo))
1010 {
1011@@ -278,7 +673,7 @@
1012 }
1013 else
1014 {
1015- scheduled_composite_frame = surface.lock_front();
1016+ scheduled_composite_frame = get_front_buffer(surface.lock_front());
1017 bufobj = outputs.front()->fb_for(scheduled_composite_frame);
1018 if (!bufobj)
1019 fatal_error("Failed to get front buffer object");
1020
1021=== modified file 'src/platforms/mesa/server/kms/display_buffer.h'
1022--- src/platforms/mesa/server/kms/display_buffer.h 2017-03-16 07:37:11 +0000
1023+++ src/platforms/mesa/server/kms/display_buffer.h 2017-03-31 06:57:08 +0000
1024@@ -53,19 +53,18 @@
1025 {
1026 public:
1027 FrontBuffer();
1028+ FrontBuffer(gbm_surface* surface);
1029+ FrontBuffer(FrontBuffer&& from);
1030+
1031 ~FrontBuffer();
1032
1033- FrontBuffer(FrontBuffer&& from);
1034-
1035 FrontBuffer& operator=(FrontBuffer&& from);
1036 FrontBuffer& operator=(std::nullptr_t);
1037
1038 operator gbm_bo*();
1039 operator bool() const;
1040+
1041 private:
1042- friend class GBMOutputSurface;
1043- FrontBuffer(gbm_surface* surface);
1044-
1045 gbm_surface* const surf;
1046 gbm_bo* const bo;
1047 };
1048@@ -146,6 +145,8 @@
1049 GBMOutputSurface::FrontBuffer visible_composite_frame;
1050 GBMOutputSurface::FrontBuffer scheduled_composite_frame;
1051
1052+ std::function<GBMOutputSurface::FrontBuffer(GBMOutputSurface::FrontBuffer&&)> get_front_buffer;
1053+
1054 geometry::Rectangle area;
1055 uint32_t fb_width, fb_height;
1056 glm::mat2 transform;
1057
1058=== modified file 'src/platforms/mesa/server/kms/kms_output.h'
1059--- src/platforms/mesa/server/kms/kms_output.h 2017-03-24 07:06:18 +0000
1060+++ src/platforms/mesa/server/kms/kms_output.h 2017-03-31 06:57:08 +0000
1061@@ -26,6 +26,8 @@
1062 #include "mir/graphics/frame.h"
1063 #include "mir_toolkit/common.h"
1064
1065+#include "kms-utils/drm_mode_resources.h"
1066+
1067 #include <gbm.h>
1068
1069 namespace mir
1070@@ -44,6 +46,12 @@
1071 public:
1072 virtual ~KMSOutput() = default;
1073
1074+ /*
1075+ * I'm not sure that DRM guarantees ID uniqueness in the presence of hotplug/unplug;
1076+ * this may want to be an opaque class Id + operator== in future.
1077+ */
1078+ virtual uint32_t id() const = 0;
1079+
1080 virtual void reset() = 0;
1081 virtual void configure(geometry::Displacement fb_offset, size_t kms_mode_index) = 0;
1082 virtual geometry::Size size() const = 0;
1083@@ -86,12 +94,23 @@
1084 virtual void update_from_hardware_state(DisplayConfigurationOutput& to_update) const = 0;
1085 virtual FBHandle* fb_for(gbm_bo* bo) const = 0;
1086
1087+ /**
1088+ * Check whether buffer need to be migrated to GPU-private memory for display.
1089+ *
1090+ * \param [in] bo GBM buffer to test
1091+ * \return True if buffer must be migrated to display-private memory in order to be displayed.
1092+ * If this method returns true the caller should probably copy it to a new buffer before
1093+ * calling fb_for(buffer), as acquiring a FBHandle to the buffer will likely make it
1094+ * unusable for rendering on the original GPU.
1095+ */
1096+ virtual bool buffer_requires_migration(gbm_bo* bo) const = 0;
1097+
1098+ virtual int drm_fd() const = 0;
1099 protected:
1100 KMSOutput() = default;
1101 KMSOutput(const KMSOutput&) = delete;
1102 KMSOutput& operator=(const KMSOutput&) = delete;
1103 };
1104-
1105 }
1106 }
1107 }
1108
1109=== modified file 'src/platforms/mesa/server/kms/platform.cpp'
1110--- src/platforms/mesa/server/kms/platform.cpp 2017-03-23 17:18:29 +0000
1111+++ src/platforms/mesa/server/kms/platform.cpp 2017-03-31 06:57:08 +0000
1112@@ -41,17 +41,25 @@
1113 EmergencyCleanupRegistry& emergency_cleanup_registry,
1114 BypassOption bypass_option)
1115 : udev{std::make_shared<mir::udev::Context>()},
1116- drm{std::make_shared<mgmh::DRMHelper>(mgmh::DRMNodeToUse::card)},
1117+ drm{helpers::DRMHelper::open_all_devices(udev)},
1118 gbm{std::make_shared<mgmh::GBMHelper>()},
1119 listener{listener},
1120 vt{vt},
1121 bypass_option_{bypass_option}
1122 {
1123- drm->setup(udev);
1124- gbm->setup(*drm);
1125+ // We assume the first DRM device is the boot GPU, and arbitrarily pick it as our
1126+ // shell renderer.
1127+ //
1128+ // TODO: expose multiple rendering GPUs to the shell.
1129+ gbm->setup(*drm.front());
1130
1131 std::weak_ptr<VirtualTerminal> weak_vt = vt;
1132- std::weak_ptr<mgmh::DRMHelper> weak_drm = drm;
1133+ std::vector<std::weak_ptr<mgmh::DRMHelper>> weak_drm;
1134+
1135+ for (auto const &helper : drm)
1136+ {
1137+ weak_drm.push_back(helper);
1138+ }
1139 emergency_cleanup_registry.add(
1140 make_module_ptr<EmergencyCleanupHandler>(
1141 [weak_vt,weak_drm]
1142@@ -59,10 +67,22 @@
1143 if (auto const vt = weak_vt.lock())
1144 try { vt->restore(); } catch (...) {}
1145
1146- if (auto const drm = weak_drm.lock())
1147- try { drm->drop_master(); } catch (...) {}
1148+ for (auto helper : weak_drm)
1149+ {
1150+ if (auto const drm = helper.lock())
1151+ {
1152+ try
1153+ {
1154+ drm->drop_master();
1155+ }
1156+ catch (...)
1157+ {
1158+ }
1159+ }
1160+ }
1161 }));
1162- native_platform = std::make_unique<mgm::DRMNativePlatform>(*drm);
1163+
1164+ native_platform = std::make_unique<mgm::DRMNativePlatform>(*drm.front());
1165 }
1166
1167 mir::UniqueModulePtr<mg::GraphicBufferAllocator> mgm::Platform::create_buffer_allocator()
1168@@ -73,7 +93,14 @@
1169 mir::UniqueModulePtr<mg::Display> mgm::Platform::create_display(
1170 std::shared_ptr<DisplayConfigurationPolicy> const& initial_conf_policy, std::shared_ptr<GLConfig> const& gl_config)
1171 {
1172- return make_module_ptr<mgm::Display>(drm, gbm, vt, bypass_option_, initial_conf_policy, gl_config, listener);
1173+ return make_module_ptr<mgm::Display>(
1174+ drm,
1175+ gbm,
1176+ vt,
1177+ bypass_option_,
1178+ initial_conf_policy,
1179+ gl_config,
1180+ listener);
1181 }
1182
1183 mg::NativeDisplayPlatform* mgm::Platform::native_display_platform()
1184@@ -83,7 +110,7 @@
1185
1186 mir::UniqueModulePtr<mg::PlatformIpcOperations> mgm::Platform::make_ipc_operations() const
1187 {
1188- return make_module_ptr<mgm::IpcOperations>(drm);
1189+ return make_module_ptr<mgm::IpcOperations>(drm.front());
1190 }
1191
1192 mg::NativeRenderingPlatform* mgm::Platform::native_rendering_platform()
1193
1194=== modified file 'src/platforms/mesa/server/kms/platform.h'
1195--- src/platforms/mesa/server/kms/platform.h 2017-03-23 17:18:29 +0000
1196+++ src/platforms/mesa/server/kms/platform.h 2017-03-31 06:57:08 +0000
1197@@ -58,7 +58,7 @@
1198 EGLNativeDisplayType egl_native_display() const override;
1199
1200 std::shared_ptr<mir::udev::Context> udev;
1201- std::shared_ptr<helpers::DRMHelper> const drm;
1202+ std::vector<std::shared_ptr<helpers::DRMHelper>> const drm;
1203 std::shared_ptr<helpers::GBMHelper> const gbm;
1204
1205 std::shared_ptr<DisplayReport> const listener;
1206
1207=== modified file 'src/platforms/mesa/server/kms/real_kms_output.cpp'
1208--- src/platforms/mesa/server/kms/real_kms_output.cpp 2017-03-24 07:06:18 +0000
1209+++ src/platforms/mesa/server/kms/real_kms_output.cpp 2017-03-31 06:57:08 +0000
1210@@ -73,7 +73,7 @@
1211 int drm_fd,
1212 kms::DRMModeConnectorUPtr&& connector,
1213 std::shared_ptr<PageFlipper> const& page_flipper)
1214- : drm_fd{drm_fd},
1215+ : drm_fd_{drm_fd},
1216 page_flipper{page_flipper},
1217 connector{std::move(connector)},
1218 mode_index{0},
1219@@ -85,7 +85,7 @@
1220 {
1221 reset();
1222
1223- kms::DRMModeResources resources{drm_fd};
1224+ kms::DRMModeResources resources{drm_fd_};
1225
1226 if (this->connector->encoder_id)
1227 {
1228@@ -102,9 +102,14 @@
1229 restore_saved_crtc();
1230 }
1231
1232+uint32_t mgm::RealKMSOutput::id() const
1233+{
1234+ return connector->connector_id;
1235+}
1236+
1237 void mgm::RealKMSOutput::reset()
1238 {
1239- kms::DRMModeResources resources{drm_fd};
1240+ kms::DRMModeResources resources{drm_fd_};
1241
1242 /* Update the connector to ensure we have the latest information */
1243 try
1244@@ -119,7 +124,7 @@
1245 // TODO: What if we can't locate the DPMS property?
1246 for (int i = 0; i < connector->count_props; i++)
1247 {
1248- auto prop = drmModeGetProperty(drm_fd, connector->props[i]);
1249+ auto prop = drmModeGetProperty(drm_fd_, connector->props[i]);
1250 if (prop && (prop->flags & DRM_MODE_PROP_ENUM)) {
1251 if (!strcmp(prop->name, "DPMS"))
1252 {
1253@@ -162,7 +167,7 @@
1254 return false;
1255 }
1256
1257- auto ret = drmModeSetCrtc(drm_fd, current_crtc->crtc_id,
1258+ auto ret = drmModeSetCrtc(drm_fd_, current_crtc->crtc_id,
1259 fb.get_drm_fb_id(), fb_offset.dx.as_int(), fb_offset.dy.as_int(),
1260 &connector->connector_id, 1,
1261 &connector->modes[mode_index]);
1262@@ -193,7 +198,7 @@
1263 return;
1264 }
1265
1266- auto result = drmModeSetCrtc(drm_fd, current_crtc->crtc_id,
1267+ auto result = drmModeSetCrtc(drm_fd_, current_crtc->crtc_id,
1268 0, 0, 0, nullptr, 0, nullptr);
1269 if (result)
1270 {
1271@@ -247,7 +252,7 @@
1272 {
1273 has_cursor_ = true;
1274 result = drmModeSetCursor(
1275- drm_fd,
1276+ drm_fd_,
1277 current_crtc->crtc_id,
1278 gbm_bo_get_handle(buffer).u32,
1279 gbm_bo_get_width(buffer),
1280@@ -266,7 +271,7 @@
1281 {
1282 if (current_crtc)
1283 {
1284- if (auto result = drmModeMoveCursor(drm_fd, current_crtc->crtc_id,
1285+ if (auto result = drmModeMoveCursor(drm_fd_, current_crtc->crtc_id,
1286 destination.x.as_uint32_t(),
1287 destination.y.as_uint32_t()))
1288 {
1289@@ -281,7 +286,7 @@
1290 int result = 0;
1291 if (current_crtc)
1292 {
1293- result = drmModeSetCursor(drm_fd, current_crtc->crtc_id, 0, 0, 0);
1294+ result = drmModeSetCursor(drm_fd_, current_crtc->crtc_id, 0, 0, 0);
1295
1296 if (result)
1297 mir::log_warning("clear_cursor: drmModeSetCursor failed (%s)",
1298@@ -307,7 +312,7 @@
1299 if (connector->connection != DRM_MODE_CONNECTED)
1300 return false;
1301
1302- current_crtc = mgk::find_crtc_for_connector(drm_fd, connector);
1303+ current_crtc = mgk::find_crtc_for_connector(drm_fd_, connector);
1304
1305
1306 return (current_crtc != nullptr);
1307@@ -317,7 +322,7 @@
1308 {
1309 if (!using_saved_crtc)
1310 {
1311- drmModeSetCrtc(drm_fd, saved_crtc.crtc_id, saved_crtc.buffer_id,
1312+ drmModeSetCrtc(drm_fd_, saved_crtc.crtc_id, saved_crtc.buffer_id,
1313 saved_crtc.x, saved_crtc.y,
1314 &connector->connector_id, 1, &saved_crtc.mode);
1315
1316@@ -333,7 +338,7 @@
1317 {
1318 power_mode = mode;
1319 drmModeConnectorSetProperty(
1320- drm_fd,
1321+ drm_fd_,
1322 connector->connector_id,
1323 dpms_enum_id,
1324 mode);
1325@@ -357,7 +362,7 @@
1326 }
1327
1328 int ret = drmModeCrtcSetGamma(
1329- drm_fd,
1330+ drm_fd_,
1331 current_crtc->crtc_id,
1332 gamma.red.size(),
1333 const_cast<uint16_t*>(gamma.red.data()),
1334@@ -373,21 +378,21 @@
1335
1336 void mgm::RealKMSOutput::refresh_hardware_state()
1337 {
1338- connector = kms::get_connector(drm_fd, connector->connector_id);
1339+ connector = kms::get_connector(drm_fd_, connector->connector_id);
1340 current_crtc = nullptr;
1341
1342 if (connector->encoder_id)
1343 {
1344- auto encoder = kms::get_encoder(drm_fd, connector->encoder_id);
1345+ auto encoder = kms::get_encoder(drm_fd_, connector->encoder_id);
1346
1347 if (encoder->crtc_id)
1348 {
1349- current_crtc = kms::get_crtc(drm_fd, encoder->crtc_id);
1350+ current_crtc = kms::get_crtc(drm_fd_, encoder->crtc_id);
1351 }
1352 }
1353 }
1354
1355- namespace
1356+namespace
1357 {
1358
1359 bool kms_modes_are_equal(drmModeModeInfo const& info1, drmModeModeInfo const& info2)
1360@@ -540,7 +545,7 @@
1361 /* Only ask for the EDID on connected outputs. There's obviously no monitor EDID
1362 * when there is no monitor connected!
1363 */
1364- edid = edid_for_connector(drm_fd, connector->connector_id);
1365+ edid = edid_for_connector(drm_fd_, connector->connector_id);
1366 }
1367
1368 drmModeModeInfo current_mode_info = drmModeModeInfo();
1369@@ -615,7 +620,7 @@
1370 auto const height = gbm_bo_get_height(bo);
1371
1372 /* Create a KMS FB object with the gbm_bo attached to it. */
1373- auto ret = drmModeAddFB2(drm_fd, width, height, format,
1374+ auto ret = drmModeAddFB2(drm_fd_, width, height, format,
1375 handles, strides, offsets, &fb_id, 0);
1376 if (ret)
1377 return nullptr;
1378@@ -626,3 +631,20 @@
1379
1380 return bufobj;
1381 }
1382+
1383+bool mgm::RealKMSOutput::buffer_requires_migration(gbm_bo* bo) const
1384+{
1385+ /*
1386+ * It's possible that some devices will not require migration -
1387+ * Intel GPUs can obviously scanout from main memory, as can USB outputs such as
1388+ * DisplayLink.
1389+ *
1390+ * For a first go, just say that *every* device scans out of GPU-private memory.
1391+ */
1392+ return gbm_device_get_fd(gbm_bo_get_device(bo)) != drm_fd_;
1393+}
1394+
1395+int mgm::RealKMSOutput::drm_fd() const
1396+{
1397+ return drm_fd_;
1398+}
1399
1400=== modified file 'src/platforms/mesa/server/kms/real_kms_output.h'
1401--- src/platforms/mesa/server/kms/real_kms_output.h 2017-03-24 07:06:18 +0000
1402+++ src/platforms/mesa/server/kms/real_kms_output.h 2017-03-31 06:57:08 +0000
1403@@ -44,6 +44,8 @@
1404 std::shared_ptr<PageFlipper> const& page_flipper);
1405 ~RealKMSOutput();
1406
1407+ uint32_t id() const override;
1408+
1409 void reset() override;
1410 void configure(geometry::Displacement fb_offset, size_t kms_mode_index) override;
1411 geometry::Size size() const override;
1412@@ -68,11 +70,14 @@
1413 void update_from_hardware_state(DisplayConfigurationOutput& output) const override;
1414
1415 FBHandle* fb_for(gbm_bo* bo) const override;
1416+
1417+ bool buffer_requires_migration(gbm_bo* bo) const override;
1418+ int drm_fd() const override;
1419 private:
1420 bool ensure_crtc();
1421 void restore_saved_crtc();
1422
1423- int const drm_fd;
1424+ int const drm_fd_;
1425 std::shared_ptr<PageFlipper> const page_flipper;
1426
1427 kms::DRMModeConnectorUPtr connector;
1428
1429=== modified file 'src/platforms/mesa/server/kms/real_kms_output_container.cpp'
1430--- src/platforms/mesa/server/kms/real_kms_output_container.cpp 2017-03-10 10:20:24 +0000
1431+++ src/platforms/mesa/server/kms/real_kms_output_container.cpp 2017-03-31 06:57:08 +0000
1432@@ -16,6 +16,7 @@
1433 * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
1434 */
1435
1436+#include <algorithm>
1437 #include "real_kms_output_container.h"
1438 #include "real_kms_output.h"
1439 #include "kms-utils/drm_mode_resources.h"
1440@@ -23,40 +24,60 @@
1441 namespace mgm = mir::graphics::mesa;
1442
1443 mgm::RealKMSOutputContainer::RealKMSOutputContainer(
1444- int drm_fd, std::shared_ptr<PageFlipper> const& page_flipper)
1445- : drm_fd{drm_fd},
1446- page_flipper{page_flipper}
1447+ std::vector<int> const& drm_fds,
1448+ std::function<std::shared_ptr<PageFlipper>(int)> const& construct_page_flipper)
1449+ : drm_fds{drm_fds},
1450+ construct_page_flipper{construct_page_flipper}
1451 {
1452 }
1453
1454 void mgm::RealKMSOutputContainer::for_each_output(std::function<void(std::shared_ptr<KMSOutput> const&)> functor) const
1455 {
1456 for(auto& output: outputs)
1457- functor(output.second);
1458+ functor(output);
1459 }
1460
1461 void mgm::RealKMSOutputContainer::update_from_hardware_state()
1462 {
1463- kms::DRMModeResources resources{drm_fd};
1464-
1465 decltype(outputs) new_outputs;
1466
1467- for (auto&& connector : resources.connectors())
1468+ for (auto drm_fd : drm_fds)
1469 {
1470- if (outputs.count(connector->connector_id))
1471- {
1472- new_outputs[connector->connector_id] = std::move(outputs[connector->connector_id]);
1473- new_outputs[connector->connector_id]->refresh_hardware_state();
1474- }
1475- else
1476- {
1477- auto const id = connector->connector_id;
1478- new_outputs[id] = std::make_shared<RealKMSOutput>(
1479- drm_fd,
1480- std::move(connector),
1481- page_flipper);
1482- }
1483+ kms::DRMModeResources resources{drm_fd};
1484+
1485+
1486+ for (auto &&connector : resources.connectors())
1487+ {
1488+ // Caution: O(n²) here, but n is the number of outputs, so should
1489+ // conservatively be << 100.
1490+ auto existing_output = std::find_if(
1491+ outputs.begin(),
1492+ outputs.end(),
1493+ [&connector, drm_fd](auto const &candidate)
1494+ {
1495+ return
1496+ connector->connector_id == candidate->id() &&
1497+ drm_fd == candidate->drm_fd();
1498+ });
1499+
1500+ if (existing_output != outputs.end())
1501+ {
1502+ // We could drop this down to O(n) by being smarter about moving out
1503+ // of the outputs vector.
1504+ //
1505+ // That's a bit of a faff, so just do the simple thing for now.
1506+ new_outputs.push_back(*existing_output);
1507+ new_outputs.back()->refresh_hardware_state();
1508+ }
1509+ else
1510+ {
1511+ new_outputs.push_back(std::make_shared<RealKMSOutput>(
1512+ drm_fd,
1513+ std::move(connector),
1514+ construct_page_flipper(drm_fd)));
1515+ }
1516+ }
1517+
1518 }
1519-
1520 outputs = new_outputs;
1521 }
1522
1523=== modified file 'src/platforms/mesa/server/kms/real_kms_output_container.h'
1524--- src/platforms/mesa/server/kms/real_kms_output_container.h 2017-03-10 10:20:24 +0000
1525+++ src/platforms/mesa/server/kms/real_kms_output_container.h 2017-03-31 06:57:08 +0000
1526@@ -20,7 +20,7 @@
1527 #define MIR_GRAPHICS_MESA_REAL_KMS_OUTPUT_CONTAINER_H_
1528
1529 #include "kms_output_container.h"
1530-#include <unordered_map>
1531+#include <vector>
1532
1533 namespace mir
1534 {
1535@@ -34,15 +34,17 @@
1536 class RealKMSOutputContainer : public KMSOutputContainer
1537 {
1538 public:
1539- RealKMSOutputContainer(int drm_fd, std::shared_ptr<PageFlipper> const& page_flipper);
1540+ RealKMSOutputContainer(
1541+ std::vector<int> const& drm_fds,
1542+ std::function<std::shared_ptr<PageFlipper>(int drm_fd)> const& construct_page_flipper);
1543
1544 void for_each_output(std::function<void(std::shared_ptr<KMSOutput> const&)> functor) const override;
1545
1546 void update_from_hardware_state() override;
1547 private:
1548- int const drm_fd;
1549- std::unordered_map<uint32_t,std::shared_ptr<KMSOutput>> outputs;
1550- std::shared_ptr<PageFlipper> const page_flipper;
1551+ std::vector<int> const drm_fds;
1552+ std::vector<std::shared_ptr<KMSOutput>> outputs;
1553+ std::function<std::shared_ptr<PageFlipper>(int drm_fd)> const construct_page_flipper;
1554 };
1555
1556 }
1557
1558=== modified file 'src/server/input/default_input_device_hub.cpp'
1559--- src/server/input/default_input_device_hub.cpp 2017-03-28 12:21:33 +0000
1560+++ src/server/input/default_input_device_hub.cpp 2017-03-31 06:57:08 +0000
1561@@ -377,7 +377,7 @@
1562 handles.push_back(handle);
1563 }
1564
1565- observers.for_each([&handle, this](std::shared_ptr<InputDeviceObserver> const& observer)
1566+ observers.for_each([&handle](std::shared_ptr<InputDeviceObserver> const& observer)
1567 {
1568 observer->device_added(handle);
1569 observer->changes_complete();
1570
1571=== modified file 'tests/include/mir/test/doubles/mock_drm.h'
1572--- tests/include/mir/test/doubles/mock_drm.h 2017-01-18 02:29:37 +0000
1573+++ tests/include/mir/test/doubles/mock_drm.h 2017-03-31 06:57:08 +0000
1574@@ -23,6 +23,7 @@
1575
1576 #include <xf86drm.h>
1577 #include <xf86drmMode.h>
1578+#include <unordered_map>
1579
1580 namespace mir
1581 {
1582@@ -154,12 +155,45 @@
1583 MOCK_METHOD6(drmModeCrtcSetGamma, int(int fd, uint32_t crtc_id, uint32_t size,
1584 uint16_t* red, uint16_t* green, uint16_t* blue));
1585
1586- FakeDRMResources fake_drm;
1587+ MOCK_METHOD1(drmGetVersion, drmVersionPtr(int));
1588+ MOCK_METHOD1(drmFreeVersion, void(drmVersionPtr));
1589+
1590+
1591+ void add_crtc(
1592+ char const* device,
1593+ uint32_t id,
1594+ drmModeModeInfo mode);
1595+ void add_encoder(
1596+ char const* device,
1597+ uint32_t encoder_id,
1598+ uint32_t crtc_id,
1599+ uint32_t possible_crtcs_mask);
1600+ void add_connector(
1601+ char const* device,
1602+ uint32_t connector_id,
1603+ uint32_t type,
1604+ drmModeConnection connection,
1605+ uint32_t encoder_id,
1606+ std::vector<drmModeModeInfo>& modes,
1607+ std::vector<uint32_t>& possible_encoder_ids,
1608+ geometry::Size const& physical_size,
1609+ drmModeSubPixel subpixel_arrangement = DRM_MODE_SUBPIXEL_UNKNOWN);
1610+
1611+ void prepare(char const* device);
1612+ void reset(char const* device);
1613+
1614+ void generate_event_on(char const* device);
1615+
1616+ class IsFdOfDeviceMatcher;
1617+ friend class IsFdOfDeviceMatcher;
1618
1619 private:
1620+ std::unordered_map<std::string, FakeDRMResources> fake_drms;
1621+ std::unordered_map<int, FakeDRMResources&> fd_to_drm;
1622 drmModeObjectProperties empty_object_props;
1623 };
1624
1625+testing::Matcher<int> IsFdOfDevice(char const* device);
1626 }
1627 }
1628 }
1629
1630=== modified file 'tests/include/mir/test/doubles/mock_gbm.h'
1631--- tests/include/mir/test/doubles/mock_gbm.h 2015-07-16 07:03:19 +0000
1632+++ tests/include/mir/test/doubles/mock_gbm.h 2017-03-31 06:57:08 +0000
1633@@ -80,6 +80,7 @@
1634 MOCK_METHOD3(gbm_bo_write, bool(struct gbm_bo *bo, const void *buf, size_t count));
1635 MOCK_METHOD1(gbm_bo_destroy, void(struct gbm_bo *bo));
1636 MOCK_METHOD4(gbm_bo_import, struct gbm_bo*(struct gbm_device*, uint32_t, void*, uint32_t));
1637+ MOCK_METHOD1(gbm_bo_get_fd, int(gbm_bo*));
1638
1639 FakeGBMResources fake_gbm;
1640
1641
1642=== modified file 'tests/include/mir/test/doubles/mock_gl.h'
1643--- tests/include/mir/test/doubles/mock_gl.h 2016-06-30 13:41:11 +0000
1644+++ tests/include/mir/test/doubles/mock_gl.h 2017-03-31 06:57:08 +0000
1645@@ -110,6 +110,7 @@
1646 const GLvoid *));
1647 MOCK_METHOD4(glViewport, void(GLint, GLint, GLsizei, GLsizei));
1648 MOCK_METHOD1(glGenerateMipmap, void(GLenum target));
1649+ MOCK_METHOD4(glDrawElements, void(GLenum, GLsizei, GLenum, const GLvoid*));
1650 };
1651
1652 }
1653
1654=== modified file 'tests/mir_test_doubles/mock_drm.cpp'
1655--- tests/mir_test_doubles/mock_drm.cpp 2017-01-18 02:29:37 +0000
1656+++ tests/mir_test_doubles/mock_drm.cpp 2017-03-31 06:57:08 +0000
1657@@ -24,6 +24,8 @@
1658 #include <stdexcept>
1659 #include <unistd.h>
1660 #include <dlfcn.h>
1661+#include <system_error>
1662+#include <boost/throw_exception.hpp>
1663
1664 namespace mtd=mir::test::doubles;
1665 namespace geom = mir::geometry;
1666@@ -239,25 +241,60 @@
1667 memset(&empty_object_props, 0, sizeof(empty_object_props));
1668
1669 ON_CALL(*this, open(_,_,_))
1670- .WillByDefault(Return(fake_drm.fd()));
1671+ .WillByDefault(
1672+ WithArg<0>(
1673+ Invoke(
1674+ [this](char const* device_path)
1675+ {
1676+ auto fd = fake_drms[device_path].fd();
1677+ fd_to_drm.insert({fd, fake_drms[device_path]});
1678+ return fd;
1679+ })));
1680
1681 ON_CALL(*this, drmOpen(_,_))
1682- .WillByDefault(Return(fake_drm.fd()));
1683+ .WillByDefault(
1684+ InvokeWithoutArgs(
1685+ [this]()
1686+ {
1687+ auto fd = fake_drms["/dev/dri/card0"].fd();
1688+ fd_to_drm.insert({fd, fake_drms["/dev/dri/card0"]});
1689+ return fd;
1690+ }));
1691
1692 ON_CALL(*this, drmClose(_))
1693- .WillByDefault(WithArg<0>(Invoke([&](int fd){ return close(fd); })));
1694+ .WillByDefault(Invoke([](int fd){ return close(fd); }));
1695
1696 ON_CALL(*this, drmModeGetResources(_))
1697- .WillByDefault(Return(fake_drm.resources_ptr()));
1698+ .WillByDefault(
1699+ Invoke(
1700+ [this](int fd)
1701+ {
1702+ return fd_to_drm.at(fd).resources_ptr();
1703+ }));
1704
1705 ON_CALL(*this, drmModeGetCrtc(_, _))
1706- .WillByDefault(WithArgs<1>(Invoke(&fake_drm, &FakeDRMResources::find_crtc)));
1707+ .WillByDefault(
1708+ Invoke(
1709+ [this](int fd, uint32_t crtc_id)
1710+ {
1711+ return fd_to_drm.at(fd).find_crtc(crtc_id);
1712+ }));
1713
1714 ON_CALL(*this, drmModeGetEncoder(_, _))
1715- .WillByDefault(WithArgs<1>(Invoke(&fake_drm, &FakeDRMResources::find_encoder)));
1716+ .WillByDefault(
1717+ Invoke(
1718+ [this](int fd, uint32_t encoder_id)
1719+ {
1720+ return fd_to_drm.at(fd).find_encoder(encoder_id);
1721+ }));
1722
1723 ON_CALL(*this, drmModeGetConnector(_, _))
1724- .WillByDefault(WithArgs<1>(Invoke(&fake_drm, &FakeDRMResources::find_connector)));
1725+ .WillByDefault(
1726+ Invoke(
1727+ [this](int fd, uint32_t connector_id)
1728+ {
1729+ return fd_to_drm.at(fd).find_connector(connector_id);
1730+ }));
1731
1732 ON_CALL(*this, drmModeObjectGetProperties(_, _, _))
1733 .WillByDefault(Return(&empty_object_props));
1734@@ -270,6 +307,20 @@
1735
1736 ON_CALL(*this, drmFreeBusid(_))
1737 .WillByDefault(WithArg<0>(Invoke([&](const char* busid){ free(const_cast<char*>(busid)); })));
1738+
1739+ static drmVersion const version{
1740+ 1,
1741+ 2,
1742+ 3,
1743+ static_cast<int>(strlen("mock_driver")),
1744+ const_cast<char*>("mock_driver"),
1745+ static_cast<int>(strlen("1 Jan 1970")),
1746+ const_cast<char*>("1 Jan 1970"),
1747+ static_cast<int>(strlen("Not really a driver")),
1748+ const_cast<char*>("Not really a driver")
1749+ };
1750+ ON_CALL(*this, drmGetVersion(_))
1751+ .WillByDefault(Return(const_cast<drmVersionPtr>(&version)));
1752 }
1753
1754 mtd::MockDRM::~MockDRM() noexcept
1755@@ -277,6 +328,118 @@
1756 global_mock = nullptr;
1757 }
1758
1759+void mtd::MockDRM::add_crtc(char const *device, uint32_t id, drmModeModeInfo mode)
1760+{
1761+ fake_drms[device].add_crtc(id, mode);
1762+}
1763+
1764+void mtd::MockDRM::add_encoder(
1765+ char const *device,
1766+ uint32_t encoder_id,
1767+ uint32_t crtc_id,
1768+ uint32_t possible_crtcs_mask)
1769+{
1770+ fake_drms[device].add_encoder(encoder_id, crtc_id, possible_crtcs_mask);
1771+}
1772+
1773+void mtd::MockDRM::prepare(char const *device)
1774+{
1775+ fake_drms[device].prepare();
1776+}
1777+
1778+void mtd::MockDRM::reset(char const *device)
1779+{
1780+ fake_drms[device].reset();
1781+}
1782+
1783+void mtd::MockDRM::generate_event_on(char const *device)
1784+{
1785+ auto const fd = fake_drms[device].write_fd();
1786+
1787+ if (write(fd, "a", 1) != 1)
1788+ {
1789+ BOOST_THROW_EXCEPTION(
1790+ std::system_error(errno, std::system_category(), "Failed to make fake DRM event"));
1791+ }
1792+}
1793+
1794+void mtd::MockDRM::add_connector(
1795+ char const *device,
1796+ uint32_t connector_id,
1797+ uint32_t type,
1798+ drmModeConnection connection,
1799+ uint32_t encoder_id,
1800+ std::vector<drmModeModeInfo> &modes,
1801+ std::vector<uint32_t> &possible_encoder_ids,
1802+ geometry::Size const &physical_size,
1803+ drmModeSubPixel subpixel_arrangement)
1804+{
1805+ fake_drms[device].add_connector(
1806+ connector_id,
1807+ type,
1808+ connection,
1809+ encoder_id,
1810+ modes,
1811+ possible_encoder_ids,
1812+ physical_size,
1813+ subpixel_arrangement);
1814+}
1815+
1816+MATCHER_P2(IsFdOfDevice, devname, fds, "")
1817+{
1818+ return std::find(
1819+ fds.begin(),
1820+ fds.end(),
1821+ arg) != fds.end();
1822+}
1823+
1824+class mtd::MockDRM::IsFdOfDeviceMatcher : public ::testing::MatcherInterface<int>
1825+{
1826+public:
1827+ IsFdOfDeviceMatcher(char const* device)
1828+ : device{device}
1829+ {
1830+ }
1831+
1832+ void DescribeTo(::std::ostream *os) const override
1833+ {
1834+
1835+ *os
1836+ << "Is an fd of DRM device "
1837+ << device
1838+ << " (one of: "
1839+ << ::testing::PrintToString(fds_for_device(device.c_str()))
1840+ << ")";
1841+ }
1842+
1843+ bool MatchAndExplain(int x, testing::MatchResultListener *listener) const override
1844+ {
1845+ testing::Matcher<std::vector<int>> matcher = testing::Contains(x);
1846+ return matcher.MatchAndExplain(fds_for_device(device.c_str()), listener);
1847+ }
1848+
1849+private:
1850+ std::vector<int> fds_for_device(char const* device) const
1851+ {
1852+ std::vector<int> device_fds;
1853+ for (auto const& pair : global_mock->fd_to_drm)
1854+ {
1855+ if (&global_mock->fake_drms[device] == &pair.second)
1856+ {
1857+ device_fds.push_back(pair.first);
1858+ }
1859+ }
1860+ return device_fds;
1861+ }
1862+
1863+ std::string const device;
1864+};
1865+
1866+testing::Matcher<int> mtd::IsFdOfDevice(char const* device)
1867+{
1868+ return ::testing::MakeMatcher(new mtd::MockDRM::IsFdOfDeviceMatcher(device));
1869+}
1870+
1871 int drmOpen(const char *name, const char *busid)
1872 {
1873 return global_mock->drmOpen(name, busid);
1874@@ -362,6 +525,16 @@
1875 return global_mock->drmGetCap(fd, capability, value);
1876 }
1877
1878+drmVersionPtr drmGetVersion(int fd)
1879+{
1880+ return global_mock->drmGetVersion(fd);
1881+}
1882+
1883+void drmFreeVersion(drmVersionPtr version)
1884+{
1885+ return global_mock->drmFreeVersion(version);
1886+}
1887+
1888 int drmSetClientCap(int fd, uint64_t capability, uint64_t value)
1889 {
1890 return global_mock->drmSetClientCap(fd, capability, value);
1891
1892=== modified file 'tests/mir_test_doubles/mock_egl.cpp'
1893--- tests/mir_test_doubles/mock_egl.cpp 2017-01-18 02:29:37 +0000
1894+++ tests/mir_test_doubles/mock_egl.cpp 2017-03-31 06:57:08 +0000
1895@@ -156,7 +156,7 @@
1896 {
1897 using namespace testing;
1898
1899- const char* egl_exts = "EGL_KHR_image EGL_KHR_image_base EGL_KHR_image_pixmap";
1900+ const char* egl_exts = "EGL_KHR_image EGL_KHR_image_base EGL_KHR_image_pixmap EGL_EXT_image_dma_buf_import";
1901 ON_CALL(*this, eglQueryString(_,EGL_EXTENSIONS))
1902 .WillByDefault(Return(egl_exts));
1903 }
1904
1905=== modified file 'tests/mir_test_doubles/mock_gbm.cpp'
1906--- tests/mir_test_doubles/mock_gbm.cpp 2015-07-16 07:03:19 +0000
1907+++ tests/mir_test_doubles/mock_gbm.cpp 2017-03-31 06:57:08 +0000
1908@@ -182,3 +182,8 @@
1909 {
1910 return global_mock->gbm_bo_import(device, type, data, flags);
1911 }
1912+
1913+int gbm_bo_get_fd(gbm_bo* bo)
1914+{
1915+ return global_mock->gbm_bo_get_fd(bo);
1916+}
1917
1918=== modified file 'tests/mir_test_doubles/mock_gl.cpp'
1919--- tests/mir_test_doubles/mock_gl.cpp 2016-06-30 13:41:11 +0000
1920+++ tests/mir_test_doubles/mock_gl.cpp 2017-03-31 06:57:08 +0000
1921@@ -20,6 +20,8 @@
1922 #include "mir/test/doubles/mock_gl.h"
1923 #include <gtest/gtest.h>
1924
1925+#include <GLES2/gl2.h>
1926+
1927 #include <cstring>
1928
1929 namespace mtd = mir::test::doubles;
1930@@ -462,3 +464,9 @@
1931 CHECK_GLOBAL_VOID_MOCK();
1932 global_mock_gl->glPixelStorei(pname, param);
1933 }
1934+
1935+void glDrawElements(GLenum mode, GLsizei count, GLenum type, const void* indicies)
1936+{
1937+ CHECK_GLOBAL_VOID_MOCK();
1938+ global_mock_gl->glDrawElements(mode, count, type, indicies);
1939+}
1940
1941=== modified file 'tests/unit-tests/platforms/mesa/CMakeLists.txt'
1942--- tests/unit-tests/platforms/mesa/CMakeLists.txt 2017-01-18 02:29:37 +0000
1943+++ tests/unit-tests/platforms/mesa/CMakeLists.txt 2017-03-31 06:57:08 +0000
1944@@ -5,7 +5,8 @@
1945
1946 add_subdirectory(kms-utils/)
1947
1948-if (MIR_BUILD_PLATFORM_MESA_KMS)
1949+if (MIR_BUILD_PLATFORM_MESA_KMS AND
1950+ (GBM_VERSION VERSION_GREATER 11.0.0))
1951 add_subdirectory(kms/)
1952 endif()
1953
1954
1955=== modified file 'tests/unit-tests/platforms/mesa/kms-utils/test_connector_utils.cpp'
1956--- tests/unit-tests/platforms/mesa/kms-utils/test_connector_utils.cpp 2016-05-20 02:15:41 +0000
1957+++ tests/unit-tests/platforms/mesa/kms-utils/test_connector_utils.cpp 2017-03-31 06:57:08 +0000
1958@@ -25,6 +25,7 @@
1959
1960 #include <gtest/gtest.h>
1961 #include <gmock/gmock.h>
1962+#include <fcntl.h>
1963
1964 namespace mtd = mir::test::doubles;
1965 namespace mgk = mir::graphics::kms;
1966@@ -35,30 +36,37 @@
1967 {
1968 using namespace testing;
1969 NiceMock<mtd::MockDRM> drm;
1970-
1971- auto& resources = drm.fake_drm;
1972+ char const* const drm_device = "/dev/dri/card0";
1973
1974 std::array<uint32_t, 3> const crtc_ids = {{21, 25, 30}};
1975 std::array<uint32_t, 3> const encoder_ids = {{3, 5, 7}};
1976 std::array<uint32_t, 3> const connector_id = {{9, 10, 11}};
1977
1978- resources.reset();
1979+ drm.reset(drm_device);
1980 // Add a bunch of CRTCs
1981 for (auto id : crtc_ids)
1982 {
1983- resources.add_crtc(id, drmModeModeInfo());
1984+ drm.add_crtc(drm_device, id, drmModeModeInfo());
1985 }
1986 // Add an encoder that can only drive any CRTC...
1987- resources.add_encoder(encoder_ids[0], 0, 1 << 0 | 1 << 1 | 1 << 2);
1988+ drm.add_encoder(drm_device, encoder_ids[0], 0, 1 << 0 | 1 << 1 | 1 << 2);
1989 // ...and one that can only drive the third...
1990- resources.add_encoder(encoder_ids[1], 0, 1 << 2);
1991+ drm.add_encoder(drm_device, encoder_ids[1], 0, 1 << 2);
1992 // ...and one that can only drive the second two...
1993- resources.add_encoder(encoder_ids[2], 0, 1 << 1 | 1 << 2);
1994+ drm.add_encoder(drm_device, encoder_ids[2], 0, 1 << 1 | 1 << 2);
1995
1996- std::vector<drmModeModeInfo> modes{resources.create_mode(1200, 1600, 138500, 1400, 1800, mtd::FakeDRMResources::ModePreference::PreferredMode)};
1997+ std::vector<drmModeModeInfo> modes{
1998+ mtd::FakeDRMResources::create_mode(
1999+ 1200,
2000+ 1600,
2001+ 138500,
2002+ 1400,
2003+ 1800,
2004+ mtd::FakeDRMResources::ModePreference::PreferredMode)};
2005 // Finally, add a connector that can only be driven by any encoder...
2006 std::vector<uint32_t> any_encoder{encoder_ids.begin(), encoder_ids.end()};
2007- resources.add_connector(
2008+ drm.add_connector(
2009+ drm_device,
2010 connector_id[0],
2011 DRM_MODE_CONNECTOR_VGA,
2012 DRM_MODE_CONNECTED,
2013@@ -69,7 +77,8 @@
2014
2015 // ...then one that can only be driven by the third...
2016 std::vector<uint32_t> third_encoder{encoder_ids[2]};
2017- resources.add_connector(
2018+ drm.add_connector(
2019+ drm_device,
2020 connector_id[1],
2021 DRM_MODE_CONNECTOR_VGA,
2022 DRM_MODE_CONNECTED,
2023@@ -80,7 +89,8 @@
2024
2025 // ...and finally one that can only be driven by the second...
2026 std::vector<uint32_t> second_encoder{encoder_ids[1]};
2027- resources.add_connector(
2028+ drm.add_connector(
2029+ drm_device,
2030 connector_id[2],
2031 DRM_MODE_CONNECTOR_VGA,
2032 DRM_MODE_CONNECTED,
2033@@ -89,21 +99,23 @@
2034 second_encoder,
2035 mir::geometry::Size{300, 200});
2036
2037- resources.prepare();
2038+ drm.prepare(drm_device);
2039+
2040+ auto const drm_fd = open(drm_device, 0, 0);
2041
2042 auto crtc = mgk::find_crtc_for_connector(
2043- resources.fd(),
2044- mgk::get_connector(resources.fd(), connector_id[0]));
2045+ drm_fd,
2046+ mgk::get_connector(drm_fd, connector_id[0]));
2047 EXPECT_THAT(crtc->crtc_id, AnyOf(crtc_ids[0], crtc_ids[1], crtc_ids[2]));
2048
2049 crtc = mgk::find_crtc_for_connector(
2050- resources.fd(),
2051- mgk::get_connector(resources.fd(), connector_id[1]));
2052+ drm_fd,
2053+ mgk::get_connector(drm_fd, connector_id[1]));
2054 EXPECT_THAT(crtc->crtc_id, AnyOf(crtc_ids[1], crtc_ids[2]));
2055
2056 crtc = mgk::find_crtc_for_connector(
2057- resources.fd(),
2058- mgk::get_connector(resources.fd(), connector_id[2]));
2059+ drm_fd,
2060+ mgk::get_connector(drm_fd, connector_id[2]));
2061 EXPECT_THAT(crtc->crtc_id, Eq(crtc_ids[2]));
2062 }
2063
2064@@ -111,30 +123,38 @@
2065 {
2066 using namespace testing;
2067 NiceMock<mtd::MockDRM> drm;
2068-
2069- auto& resources = drm.fake_drm;
2070+ char const* const drm_device = "/dev/dri/card0";
2071+ int const drm_fd = open(drm_device, 0, 0);
2072
2073 std::array<uint32_t, 2> const crtc_ids = {{21, 25}};
2074 std::array<uint32_t, 2> const encoder_ids = {{3, 5}};
2075 std::array<uint32_t, 2> const connector_id = {{9, 10}};
2076
2077- resources.reset();
2078- auto boring_mode = resources.create_mode(1200, 1600, 138500, 1400, 1800, mtd::FakeDRMResources::ModePreference::PreferredMode);
2079+ drm.reset(drm_device);
2080+ auto boring_mode =
2081+ mtd::FakeDRMResources::create_mode(
2082+ 1200,
2083+ 1600,
2084+ 138500,
2085+ 1400,
2086+ 1800,
2087+ mtd::FakeDRMResources::ModePreference::PreferredMode);
2088 // Add an active CRTC...
2089- resources.add_crtc(crtc_ids[0], boring_mode);
2090+ drm.add_crtc(drm_device, crtc_ids[0], boring_mode);
2091 // ...and an inactive one.
2092- resources.add_crtc(crtc_ids[1], drmModeModeInfo());
2093+ drm.add_crtc(drm_device, crtc_ids[1], drmModeModeInfo());
2094
2095 // Add an encoder hooked up to the active CRTC
2096- resources.add_encoder(encoder_ids[0], crtc_ids[0], 1 << 0 | 1 << 1);
2097+ drm.add_encoder(drm_device, encoder_ids[0], crtc_ids[0], 1 << 0 | 1 << 1);
2098 // ...and one not connected to anything
2099- resources.add_encoder(encoder_ids[1], 0, 1 << 0 | 1 << 1);
2100+ drm.add_encoder(drm_device, encoder_ids[1], 0, 1 << 0 | 1 << 1);
2101
2102 std::vector<drmModeModeInfo> modes{boring_mode};
2103 std::vector<uint32_t> any_encoder{encoder_ids.begin(), encoder_ids.end()};
2104
2105 // Finally, a connector hooked up to a CRTC-encoder
2106- resources.add_connector(
2107+ drm.add_connector(
2108+ drm_device,
2109 connector_id[0],
2110 DRM_MODE_CONNECTOR_VGA,
2111 DRM_MODE_CONNECTED,
2112@@ -144,7 +164,8 @@
2113 mir::geometry::Size{300, 200});
2114
2115 // ... and one not hooked up to anything
2116- resources.add_connector(
2117+ drm.add_connector(
2118+ drm_device,
2119 connector_id[1],
2120 DRM_MODE_CONNECTOR_VGA,
2121 DRM_MODE_CONNECTED,
2122@@ -153,11 +174,11 @@
2123 any_encoder,
2124 mir::geometry::Size{300, 200});
2125
2126- resources.prepare();
2127+ drm.prepare(drm_device);
2128
2129 auto crtc = mgk::find_crtc_for_connector(
2130- resources.fd(),
2131- mgk::get_connector(resources.fd(), connector_id[1]));
2132+ drm_fd,
2133+ mgk::get_connector(drm_fd, connector_id[1]));
2134 EXPECT_THAT(crtc->crtc_id, Eq(crtc_ids[1]));
2135 }
2136
2137@@ -165,30 +186,39 @@
2138 {
2139 using namespace testing;
2140 NiceMock<mtd::MockDRM> drm;
2141+ char const* const drm_device = "/dev/dri/card0";
2142+ int const drm_fd = open(drm_device, 0, 0);
2143
2144- auto& resources = drm.fake_drm;
2145
2146 std::array<uint32_t, 2> const crtc_ids = {{21, 25}};
2147 std::array<uint32_t, 2> const encoder_ids = {{3, 5}};
2148 std::array<uint32_t, 2> const connector_id = {{9, 10}};
2149
2150- resources.reset();
2151- auto boring_mode = resources.create_mode(1200, 1600, 138500, 1400, 1800, mtd::FakeDRMResources::ModePreference::PreferredMode);
2152+ drm.reset(drm_device);
2153+ auto boring_mode =
2154+ mtd::FakeDRMResources::create_mode(
2155+ 1200,
2156+ 1600,
2157+ 138500,
2158+ 1400,
2159+ 1800,
2160+ mtd::FakeDRMResources::ModePreference::PreferredMode);
2161 // Add an active CRTC...
2162- resources.add_crtc(crtc_ids[0], boring_mode);
2163+ drm.add_crtc(drm_device, crtc_ids[0], boring_mode);
2164 // ...and an inactive one.
2165- resources.add_crtc(crtc_ids[1], drmModeModeInfo());
2166+ drm.add_crtc(drm_device, crtc_ids[1], drmModeModeInfo());
2167
2168 // Add an encoder hooked up to the active CRTC
2169- resources.add_encoder(encoder_ids[0], crtc_ids[0], 1 << 0 | 1 << 1);
2170+ drm.add_encoder(drm_device, encoder_ids[0], crtc_ids[0], 1 << 0 | 1 << 1);
2171 // ...and one not connected to anything
2172- resources.add_encoder(encoder_ids[1], 0, 1 << 0 | 1 << 1);
2173+ drm.add_encoder(drm_device, encoder_ids[1], 0, 1 << 0 | 1 << 1);
2174
2175 std::vector<drmModeModeInfo> modes{boring_mode};
2176 std::vector<uint32_t> any_encoder{encoder_ids.begin(), encoder_ids.end()};
2177
2178 // Finally, a connector hooked up to a CRTC-encoder
2179- resources.add_connector(
2180+ drm.add_connector(
2181+ drm_device,
2182 connector_id[0],
2183 DRM_MODE_CONNECTOR_VGA,
2184 DRM_MODE_CONNECTED,
2185@@ -198,7 +228,8 @@
2186 mir::geometry::Size{300, 200});
2187
2188 // ... and one not hooked up to anything
2189- resources.add_connector(
2190+ drm.add_connector(
2191+ drm_device,
2192 connector_id[1],
2193 DRM_MODE_CONNECTOR_VGA,
2194 DRM_MODE_CONNECTED,
2195@@ -207,10 +238,10 @@
2196 any_encoder,
2197 mir::geometry::Size{300, 200});
2198
2199- resources.prepare();
2200+ drm.prepare(drm_device);
2201
2202 auto crtc = mgk::find_crtc_for_connector(
2203- resources.fd(),
2204- mgk::get_connector(resources.fd(), connector_id[0]));
2205+ drm_fd,
2206+ mgk::get_connector(drm_fd, connector_id[0]));
2207 EXPECT_THAT(crtc->crtc_id, Eq(crtc_ids[0]));
2208 }
2209
2210=== modified file 'tests/unit-tests/platforms/mesa/kms/mock_kms_output.h'
2211--- tests/unit-tests/platforms/mesa/kms/mock_kms_output.h 2017-03-24 07:15:51 +0000
2212+++ tests/unit-tests/platforms/mesa/kms/mock_kms_output.h 2017-03-31 06:57:08 +0000
2213@@ -38,6 +38,7 @@
2214
2215 struct MockKMSOutput : public graphics::mesa::KMSOutput
2216 {
2217+ MOCK_CONST_METHOD0(id, uint32_t());
2218 MOCK_METHOD0(reset, void());
2219 MOCK_METHOD2(configure, void(geometry::Displacement, size_t));
2220 MOCK_CONST_METHOD0(size, geometry::Size());
2221@@ -72,6 +73,8 @@
2222 MOCK_CONST_METHOD1(update_from_hardware_state, void(graphics::DisplayConfigurationOutput&));
2223
2224 MOCK_CONST_METHOD1(fb_for, graphics::mesa::FBHandle*(gbm_bo*));
2225+ MOCK_CONST_METHOD1(buffer_requires_migration, bool(gbm_bo*));
2226+ MOCK_CONST_METHOD0(drm_fd, int());
2227 };
2228
2229 } // namespace test
2230
2231=== modified file 'tests/unit-tests/platforms/mesa/kms/test_display.cpp'
2232--- tests/unit-tests/platforms/mesa/kms/test_display.cpp 2017-02-28 08:53:57 +0000
2233+++ tests/unit-tests/platforms/mesa/kms/test_display.cpp 2017-03-31 06:57:08 +0000
2234@@ -27,6 +27,7 @@
2235 #include "mir/time/steady_clock.h"
2236 #include "mir/glib_main_loop.h"
2237 #include "mir/fatal.h"
2238+#include "src/platforms/common/server/kms-utils/drm_mode_resources.h"
2239
2240 #include "mir/test/doubles/mock_egl.h"
2241 #include "mir/test/doubles/mock_gl.h"
2242@@ -55,6 +56,7 @@
2243 #include <atomic>
2244 #include <mutex>
2245 #include <condition_variable>
2246+#include <fcntl.h>
2247
2248 namespace mg=mir::graphics;
2249 namespace mgm=mir::graphics::mesa;
2250@@ -80,7 +82,8 @@
2251 public:
2252 MesaDisplayTest() :
2253 mock_report{std::make_shared<testing::NiceMock<mtd::MockDisplayReport>>()},
2254- null_report{mr::null_display_report()}
2255+ null_report{mr::null_display_report()},
2256+ drm_fd{open(drm_device, 0, 0)}
2257 {
2258 using namespace testing;
2259 ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_))
2260@@ -95,11 +98,16 @@
2261 * the MockGBM destructor, and which are not handled by NiceMock<>.
2262 */
2263 EXPECT_CALL(mock_gbm, gbm_bo_get_device(_))
2264- .Times(AtLeast(0));
2265+ .Times(AtLeast(0));
2266 EXPECT_CALL(mock_gbm, gbm_device_get_fd(_))
2267- .Times(AtLeast(0));
2268+ .Times(AtLeast(0))
2269+ .WillRepeatedly(Return(drm_fd));
2270
2271 fake_devices.add_standard_device("standard-drm-devices");
2272+
2273+ // Our standard mock devices have 2 DRM devices; kill all the outputs on
2274+ // the second one, so we don't try to test hybrid (for now)
2275+ mock_drm.reset("/dev/dri/card1");
2276 }
2277
2278 std::shared_ptr<mgm::Platform> create_platform()
2279@@ -145,14 +153,14 @@
2280 .Times(Exactly(1))
2281 .WillOnce(Return(fake.bo_handle2));
2282
2283- EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(),
2284+ EXPECT_CALL(mock_drm, drmModeAddFB2(drm_fd,
2285 _, _, _,
2286 Pointee(fake.bo_handle1.u32),
2287 _, _, _, _))
2288 .Times(Exactly(1))
2289 .WillOnce(DoAll(SetArgPointee<7>(fake.fb_id1), Return(0)));
2290
2291- EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(),
2292+ EXPECT_CALL(mock_drm, drmModeAddFB2(drm_fd,
2293 _, _, _,
2294 Pointee(fake.bo_handle2.u32),
2295 _, _, _, _))
2296@@ -162,26 +170,27 @@
2297
2298 uint32_t get_connected_connector_id()
2299 {
2300- auto drm_res = mock_drm.fake_drm.resources_ptr();
2301-
2302- for (int i = 0; i < drm_res->count_connectors; i++)
2303- {
2304- auto connector = mock_drm.fake_drm.find_connector(drm_res->connectors[i]);
2305- if (connector->connection == DRM_MODE_CONNECTED)
2306- return connector->connector_id;
2307- }
2308-
2309- return 0;
2310+ mg::kms::DRMModeResources resources{drm_fd};
2311+
2312+ int connected_id = 0;
2313+ resources.for_each_connector(
2314+ [&connected_id](auto const& connector)
2315+ {
2316+ if (connector->connection == DRM_MODE_CONNECTED)
2317+ connected_id = connector->connector_id;
2318+ });
2319+
2320+ return connected_id;
2321 }
2322
2323 uint32_t get_connected_crtc_id()
2324 {
2325 auto connector_id = get_connected_connector_id();
2326- auto connector = mock_drm.fake_drm.find_connector(connector_id);
2327+ auto connector = mg::kms::get_connector(drm_fd, connector_id);
2328
2329 if (connector)
2330 {
2331- auto encoder = mock_drm.fake_drm.find_encoder(connector->encoder_id);
2332+ auto encoder = mg::kms::get_encoder(drm_fd, connector->encoder_id);
2333 if (encoder)
2334 return encoder->crtc_id;
2335 }
2336@@ -217,6 +226,9 @@
2337 std::shared_ptr<testing::NiceMock<mtd::MockDisplayReport>> const mock_report;
2338 std::shared_ptr<mg::DisplayReport> const null_report;
2339 mtf::UdevEnvironment fake_devices;
2340+
2341+ char const* const drm_device = "/dev/dri/card0";
2342+ int const drm_fd;
2343 };
2344
2345 }
2346@@ -256,7 +268,7 @@
2347 .WillOnce(Return(fake.bo_handle1));
2348
2349 /* Create a a DRM FB with the DRM buffer attached */
2350- EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(),
2351+ EXPECT_CALL(mock_drm, drmModeAddFB2(drm_fd,
2352 _, _, _,
2353 Pointee(fake.bo_handle1.u32),
2354 _, _, _, _))
2355@@ -264,14 +276,14 @@
2356 .WillOnce(DoAll(SetArgPointee<7>(fake.fb_id1), Return(0)));
2357
2358 /* Display the DRM FB (first expectation is for cleanup) */
2359- EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),
2360+ EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd,
2361 crtc_id, Ne(fake.fb_id1),
2362 _, _,
2363 Pointee(connector_id),
2364 _, _))
2365 .Times(AtLeast(0));
2366
2367- EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),
2368+ EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd,
2369 crtc_id, fake.fb_id1,
2370 _, _,
2371 Pointee(connector_id),
2372@@ -291,7 +303,7 @@
2373 uint32_t const fb_id{66};
2374
2375 /* Create DRM FBs */
2376- EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(),
2377+ EXPECT_CALL(mock_drm, drmModeAddFB2(drm_fd,
2378 _, _, _, _, _, _, _, _))
2379 .WillRepeatedly(DoAll(SetArgPointee<7>(fb_id), Return(0)));
2380
2381@@ -300,7 +312,7 @@
2382 InSequence s;
2383
2384 /* crtc is set */
2385- EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),
2386+ EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd,
2387 crtc_id, fb_id,
2388 _, _,
2389 Pointee(connector_id),
2390@@ -308,7 +320,7 @@
2391 .Times(AtLeast(1));
2392
2393 /* crtc is reset */
2394- EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),
2395+ EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd,
2396 crtc_id, Ne(fb_id),
2397 _, _,
2398 Pointee(connector_id),
2399@@ -325,7 +337,10 @@
2400
2401 EXPECT_CALL(mock_drm, open(_,_,_))
2402 .Times(AtLeast(1))
2403- .WillRepeatedly(Return(-1));
2404+ .WillRepeatedly(
2405+ DoAll(
2406+ InvokeWithoutArgs([]() { errno = ENODEV; }),
2407+ Return(-1)));
2408
2409 EXPECT_CALL(mock_drm, drmClose(_))
2410 .Times(Exactly(0));
2411@@ -351,8 +366,9 @@
2412 EXPECT_CALL(mock_drm, drmModeFreeResources(_))
2413 .Times(Exactly(0));
2414
2415+ // There are 2 DRM device nodes in our mock environment.
2416 EXPECT_CALL(mock_drm, drmClose(_))
2417- .Times(Exactly(1));
2418+ .Times(Exactly(2));
2419
2420 EXPECT_THROW({
2421 auto display = create_display(platform);
2422@@ -371,7 +387,7 @@
2423 .Times(Exactly(0));
2424
2425 EXPECT_CALL(mock_drm, drmClose(_))
2426- .Times(Exactly(1));
2427+ .Times(Exactly(2));
2428
2429 EXPECT_THROW({
2430 auto platform = create_platform();
2431@@ -381,9 +397,9 @@
2432 namespace
2433 {
2434
2435-ACTION_P(QueuePageFlipEvent, write_drm_fd)
2436+ACTION_P(QueuePageFlipEvent, mock_drm)
2437 {
2438- EXPECT_EQ(1, write(write_drm_fd, "a", 1));
2439+ static_cast<mtd::MockDRM&>(mock_drm).generate_event_on("/dev/dri/card0");
2440 }
2441
2442 ACTION_P(InvokePageFlipHandler, param)
2443@@ -410,17 +426,17 @@
2444 InSequence s;
2445
2446 /* Flip the new FB */
2447- EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(),
2448+ EXPECT_CALL(mock_drm, drmModePageFlip(drm_fd,
2449 crtc_id,
2450 fake.fb_id2,
2451 _, _))
2452 .Times(Exactly(1))
2453- .WillOnce(DoAll(QueuePageFlipEvent(mock_drm.fake_drm.write_fd()),
2454+ .WillOnce(DoAll(QueuePageFlipEvent(std::ref(mock_drm)),
2455 SaveArg<4>(&user_data),
2456 Return(0)));
2457
2458 /* Handle the flip event */
2459- EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _))
2460+ EXPECT_CALL(mock_drm, drmHandleEvent(drm_fd, _))
2461 .Times(1)
2462 .WillOnce(DoAll(InvokePageFlipHandler(&user_data), Return(0)));
2463
2464@@ -455,7 +471,7 @@
2465 setup_post_update_expectations();
2466
2467 // clear_crtc happens at some stage. Not interesting.
2468- EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),
2469+ EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd,
2470 crtc_id, 0,
2471 _, _, _, _, _))
2472 .WillOnce(Return(0));
2473@@ -465,13 +481,13 @@
2474
2475 // DisplayBuffer construction paints an empty screen.
2476 // That's probably less than ideal but we've always had it that way.
2477- EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),
2478+ EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd,
2479 crtc_id, fake.fb_id1,
2480 _, _, _, _, _))
2481 .WillOnce(Return(0));
2482
2483 // New FB flip failure
2484- EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(),
2485+ EXPECT_CALL(mock_drm, drmModePageFlip(drm_fd,
2486 crtc_id,
2487 fake.fb_id2,
2488 _, _))
2489@@ -479,7 +495,7 @@
2490 .WillOnce(Return(-1));
2491
2492 // Expect fallback to blitting
2493- EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),
2494+ EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd,
2495 crtc_id, fake.fb_id2,
2496 _, _, _, _, _))
2497 .WillOnce(Return(0));
2498@@ -623,21 +639,6 @@
2499 }, std::runtime_error);
2500 }
2501
2502-TEST_F(MesaDisplayTest, constructor_throws_if_gl_oes_image_not_supported)
2503-{
2504- using namespace ::testing;
2505-
2506- const char* gl_exts = "GL_OES_texture_npot GL_OES_blend_func_separate";
2507-
2508- EXPECT_CALL(mock_gl, glGetString(GL_EXTENSIONS))
2509- .WillOnce(Return(reinterpret_cast<const GLubyte*>(gl_exts)));
2510-
2511- EXPECT_THROW(
2512- {
2513- auto display = create_display(create_platform());
2514- }, std::runtime_error);
2515-}
2516-
2517 TEST_F(MesaDisplayTest, for_each_display_buffer_calls_callback)
2518 {
2519 using namespace ::testing;
2520@@ -677,8 +678,8 @@
2521 {
2522 using namespace testing;
2523
2524- EXPECT_CALL(mock_drm, drmDropMaster(mock_drm.fake_drm.fd()))
2525- .Times(1);
2526+ EXPECT_CALL(mock_drm, drmDropMaster(_))
2527+ .Times(2);
2528
2529 auto display = create_display(create_platform());
2530
2531@@ -689,8 +690,8 @@
2532 {
2533 using namespace testing;
2534
2535- EXPECT_CALL(mock_drm, drmSetMaster(mock_drm.fake_drm.fd()))
2536- .Times(1);
2537+ EXPECT_CALL(mock_drm, drmSetMaster(_))
2538+ .Times(2);
2539
2540 auto display = create_display(create_platform());
2541
2542
2543=== modified file 'tests/unit-tests/platforms/mesa/kms/test_display_buffer.cpp'
2544--- tests/unit-tests/platforms/mesa/kms/test_display_buffer.cpp 2017-03-24 07:15:51 +0000
2545+++ tests/unit-tests/platforms/mesa/kms/test_display_buffer.cpp 2017-03-31 06:57:08 +0000
2546@@ -93,6 +93,8 @@
2547 .WillByDefault(Return(mock_refresh_rate));
2548 ON_CALL(*mock_kms_output, fb_for(_))
2549 .WillByDefault(Return(reinterpret_cast<FBHandle*>(0x12ad)));
2550+ ON_CALL(*mock_kms_output, buffer_requires_migration(_))
2551+ .WillByDefault(Return(false));
2552
2553 ON_CALL(*mock_bypassable_buffer, size())
2554 .WillByDefault(Return(display_area.size));
2555@@ -486,3 +488,18 @@
2556 EXPECT_FALSE(db.overlay(list));
2557 }
2558
2559+TEST_F(MesaDisplayBufferTest, buffer_requiring_migration_is_ineligable_for_bypass)
2560+{
2561+ ON_CALL(*mock_kms_output, buffer_requires_migration(Eq(stub_gbm_native_buffer->bo)))
2562+ .WillByDefault(Return(true));
2563+
2564+ graphics::mesa::DisplayBuffer db(
2565+ graphics::mesa::BypassOption::allowed,
2566+ null_display_report(),
2567+ {mock_kms_output},
2568+ make_output_surface(),
2569+ display_area,
2570+ mir_orientation_normal);
2571+
2572+ EXPECT_FALSE(db.overlay(bypassable_list));
2573+}
2574
2575=== modified file 'tests/unit-tests/platforms/mesa/kms/test_display_configuration.cpp'
2576--- tests/unit-tests/platforms/mesa/kms/test_display_configuration.cpp 2017-03-22 07:01:56 +0000
2577+++ tests/unit-tests/platforms/mesa/kms/test_display_configuration.cpp 2017-03-31 06:57:08 +0000
2578@@ -44,6 +44,7 @@
2579 #include <gtest/gtest.h>
2580
2581 #include <stdexcept>
2582+#include <fcntl.h>
2583
2584 namespace mg = mir::graphics;
2585 namespace mgm = mir::graphics::mesa;
2586@@ -92,21 +93,30 @@
2587 {
2588 public:
2589 MesaDisplayConfigurationTest()
2590+ : drm_fd{open(drm_device, 0, 0)}
2591 {
2592 using namespace testing;
2593
2594 /* Needed for display start-up */
2595- ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_))
2596- .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]),
2597- SetArgPointee<4>(1),
2598- Return(EGL_TRUE)));
2599+ ON_CALL(mock_egl, eglChooseConfig(_, _, _, 1, _))
2600+ .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]),
2601+ SetArgPointee<4>(1),
2602+ Return(EGL_TRUE)));
2603
2604 mock_egl.provide_egl_extensions();
2605 mock_gl.provide_gles_extensions();
2606
2607+ ON_CALL(mock_gbm, gbm_device_get_fd(_))
2608+ .WillByDefault(Return(drm_fd));
2609+
2610 setup_sample_modes();
2611
2612 fake_devices.add_standard_device("standard-drm-devices");
2613+
2614+ // Remove all outputs from all but one of the DRM devices we access;
2615+ // these tests are not set up to test hybrid.
2616+ mock_drm.reset("/dev/dri/card1");
2617+ mock_drm.reset("/dev/dri/card2");
2618 }
2619
2620 std::shared_ptr<mg::Platform> create_platform()
2621@@ -160,6 +170,9 @@
2622 std::vector<drmModeModeInfo> modes_empty;
2623
2624 mtf::UdevEnvironment fake_devices;
2625+
2626+ char const* const drm_device = "/dev/dri/card0";
2627+ int const drm_fd;
2628 };
2629
2630 MATCHER_P(Unique, scratch_vector, "")
2631@@ -217,29 +230,53 @@
2632 std::vector<uint32_t> possible_encoder_ids_empty;
2633 uint32_t const possible_crtcs_mask_empty{0};
2634
2635- mtd::FakeDRMResources& resources(mock_drm.fake_drm);
2636-
2637- resources.reset();
2638-
2639- resources.add_crtc(crtc0_id, modes0[1]);
2640-
2641- resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty);
2642- resources.add_encoder(encoder1_id, invalid_id, possible_crtcs_mask_empty);
2643-
2644- resources.add_connector(connector0_id, DRM_MODE_CONNECTOR_HDMIA,
2645- DRM_MODE_CONNECTED, encoder0_id,
2646- modes0, possible_encoder_ids_empty,
2647- connector0_physical_size_mm);
2648- resources.add_connector(connector1_id, DRM_MODE_CONNECTOR_Unknown,
2649- DRM_MODE_DISCONNECTED, invalid_id,
2650- modes_empty, possible_encoder_ids_empty,
2651- connector1_physical_size_mm);
2652- resources.add_connector(connector2_id, DRM_MODE_CONNECTOR_eDP,
2653- DRM_MODE_DISCONNECTED, encoder1_id,
2654- modes_empty, possible_encoder_ids_empty,
2655- connector2_physical_size_mm);
2656-
2657- resources.prepare();
2658+ mock_drm.reset(drm_device);
2659+
2660+ mock_drm.add_crtc(
2661+ drm_device,
2662+ crtc0_id,
2663+ modes0[1]);
2664+
2665+ mock_drm.add_encoder(
2666+ drm_device,
2667+ encoder0_id,
2668+ crtc0_id,
2669+ possible_crtcs_mask_empty);
2670+ mock_drm.add_encoder(
2671+ drm_device,
2672+ encoder1_id,
2673+ invalid_id,
2674+ possible_crtcs_mask_empty);
2675+
2676+ mock_drm.add_connector(
2677+ drm_device,
2678+ connector0_id,
2679+ DRM_MODE_CONNECTOR_HDMIA,
2680+ DRM_MODE_CONNECTED,
2681+ encoder0_id,
2682+ modes0,
2683+ possible_encoder_ids_empty,
2684+ connector0_physical_size_mm);
2685+ mock_drm.add_connector(
2686+ drm_device,
2687+ connector1_id,
2688+ DRM_MODE_CONNECTOR_Unknown,
2689+ DRM_MODE_DISCONNECTED,
2690+ invalid_id,
2691+ modes_empty,
2692+ possible_encoder_ids_empty,
2693+ connector1_physical_size_mm);
2694+ mock_drm.add_connector(
2695+ drm_device,
2696+ connector2_id,
2697+ DRM_MODE_CONNECTOR_eDP,
2698+ DRM_MODE_DISCONNECTED,
2699+ encoder1_id,
2700+ modes_empty,
2701+ possible_encoder_ids_empty,
2702+ connector2_physical_size_mm);
2703+
2704+ mock_drm.prepare(drm_device);
2705
2706 std::vector<mg::DisplayConfigurationOutput> const expected_outputs =
2707 {
2708@@ -331,8 +368,6 @@
2709 std::vector<uint32_t> possible_encoder_ids_empty;
2710 uint32_t const possible_crtcs_mask_empty{0};
2711
2712- mtd::FakeDRMResources& resources(mock_drm.fake_drm);
2713-
2714 struct TestData
2715 {
2716 drmModeSubPixel drm_subpixel;
2717@@ -350,19 +385,31 @@
2718 for (auto& data : test_data)
2719 {
2720
2721- resources.reset();
2722-
2723- resources.add_crtc(crtc0_id, modes0[1]);
2724-
2725- resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty);
2726-
2727- resources.add_connector(connector0_id, DRM_MODE_CONNECTOR_HDMIA,
2728- DRM_MODE_CONNECTED, encoder0_id,
2729- modes0, possible_encoder_ids_empty,
2730- connector0_physical_size_mm,
2731- data.drm_subpixel);
2732-
2733- resources.prepare();
2734+ mock_drm.reset(drm_device);
2735+
2736+ mock_drm.add_crtc(
2737+ drm_device,
2738+ crtc0_id,
2739+ modes0[1]);
2740+
2741+ mock_drm.add_encoder(
2742+ drm_device,
2743+ encoder0_id,
2744+ crtc0_id,
2745+ possible_crtcs_mask_empty);
2746+
2747+ mock_drm.add_connector(
2748+ drm_device,
2749+ connector0_id,
2750+ DRM_MODE_CONNECTOR_HDMIA,
2751+ DRM_MODE_CONNECTED,
2752+ encoder0_id,
2753+ modes0,
2754+ possible_encoder_ids_empty,
2755+ connector0_physical_size_mm,
2756+ data.drm_subpixel);
2757+
2758+ mock_drm.prepare(drm_device);
2759
2760 /* Test body */
2761 auto display = create_display(create_platform());
2762@@ -395,21 +442,31 @@
2763 std::vector<uint32_t> possible_encoder_ids_empty;
2764 uint32_t const possible_crtcs_mask_empty{0};
2765
2766- mtd::FakeDRMResources& resources(mock_drm.fake_drm);
2767-
2768- resources.reset();
2769-
2770- resources.add_crtc(crtc0_id, modes0[1]);
2771-
2772- resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty);
2773-
2774- resources.add_connector(connector0_id, DRM_MODE_CONNECTOR_HDMIA,
2775- DRM_MODE_CONNECTED, encoder0_id,
2776- modes0, possible_encoder_ids_empty,
2777- connector0_physical_size_mm,
2778- DRM_MODE_SUBPIXEL_NONE);
2779-
2780- resources.prepare();
2781+ mock_drm.reset(drm_device);
2782+
2783+ mock_drm.add_crtc(
2784+ drm_device,
2785+ crtc0_id,
2786+ modes0[1]);
2787+
2788+ mock_drm.add_encoder(
2789+ drm_device,
2790+ encoder0_id,
2791+ crtc0_id,
2792+ possible_crtcs_mask_empty);
2793+
2794+ mock_drm.add_connector(
2795+ drm_device,
2796+ connector0_id,
2797+ DRM_MODE_CONNECTOR_HDMIA,
2798+ DRM_MODE_CONNECTED,
2799+ encoder0_id,
2800+ modes0,
2801+ possible_encoder_ids_empty,
2802+ connector0_physical_size_mm,
2803+ DRM_MODE_SUBPIXEL_NONE);
2804+
2805+ mock_drm.prepare(drm_device);
2806
2807 struct TestData
2808 {
2809@@ -431,19 +488,31 @@
2810 for (auto& data : test_data)
2811 {
2812
2813- resources.reset();
2814-
2815- resources.add_crtc(crtc0_id, modes0[1]);
2816-
2817- resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty);
2818-
2819- resources.add_connector(connector0_id, DRM_MODE_CONNECTOR_HDMIA,
2820- DRM_MODE_CONNECTED, encoder0_id,
2821- modes0, possible_encoder_ids_empty,
2822- connector0_physical_size_mm,
2823- data.drm_subpixel);
2824-
2825- resources.prepare();
2826+ mock_drm.reset(drm_device);
2827+
2828+ mock_drm.add_crtc(
2829+ drm_device,
2830+ crtc0_id,
2831+ modes0[1]);
2832+
2833+ mock_drm.add_encoder(
2834+ drm_device,
2835+ encoder0_id,
2836+ crtc0_id,
2837+ possible_crtcs_mask_empty);
2838+
2839+ mock_drm.add_connector(
2840+ drm_device,
2841+ connector0_id,
2842+ DRM_MODE_CONNECTOR_HDMIA,
2843+ DRM_MODE_CONNECTED,
2844+ encoder0_id,
2845+ modes0,
2846+ possible_encoder_ids_empty,
2847+ connector0_physical_size_mm,
2848+ data.drm_subpixel);
2849+
2850+ mock_drm.prepare(drm_device);
2851
2852 mt::Signal handler_signal;
2853 MainLoop ml;
2854@@ -583,26 +652,54 @@
2855 };
2856
2857 /* Set up DRM resources and check */
2858- mtd::FakeDRMResources& resources(mock_drm.fake_drm);
2859- auto const syspath = fake_devices.add_device("drm", "card2", NULL, {}, {"DEVTYPE", "drm_minor"});
2860-
2861- resources.reset();
2862-
2863- resources.add_crtc(crtc_ids[0], modes0[1]);
2864-
2865- resources.add_encoder(encoder_ids[0], crtc_ids[0], possible_crtcs_mask_empty);
2866- resources.add_encoder(encoder_ids[1], invalid_id, possible_crtcs_mask_empty);
2867-
2868- resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_Composite,
2869- DRM_MODE_CONNECTED, encoder_ids[0],
2870- modes0, possible_encoder_ids_empty,
2871- connector_physical_sizes_mm_before[0]);
2872- resources.add_connector(connector_ids[1], DRM_MODE_CONNECTOR_VGA,
2873- DRM_MODE_DISCONNECTED, invalid_id,
2874- modes_empty, possible_encoder_ids_empty,
2875- connector_physical_sizes_mm_before[1]);
2876-
2877- resources.prepare();
2878+ auto const syspath = fake_devices.add_device(
2879+ "drm",
2880+ "card2",
2881+ NULL,
2882+ {},
2883+ {
2884+ "DEVTYPE", "drm_minor",
2885+ "DEVNAME", "/dev/dri/card2"
2886+ });
2887+
2888+ mock_drm.reset(drm_device);
2889+
2890+ mock_drm.add_crtc(
2891+ drm_device,
2892+ crtc_ids[0],
2893+ modes0[1]);
2894+
2895+ mock_drm.add_encoder(
2896+ drm_device,
2897+ encoder_ids[0],
2898+ crtc_ids[0],
2899+ possible_crtcs_mask_empty);
2900+ mock_drm.add_encoder(
2901+ drm_device,
2902+ encoder_ids[1],
2903+ invalid_id,
2904+ possible_crtcs_mask_empty);
2905+
2906+ mock_drm.add_connector(
2907+ drm_device,
2908+ connector_ids[0],
2909+ DRM_MODE_CONNECTOR_Composite,
2910+ DRM_MODE_CONNECTED,
2911+ encoder_ids[0],
2912+ modes0,
2913+ possible_encoder_ids_empty,
2914+ connector_physical_sizes_mm_before[0]);
2915+ mock_drm.add_connector(
2916+ drm_device,
2917+ connector_ids[1],
2918+ DRM_MODE_CONNECTOR_VGA,
2919+ DRM_MODE_DISCONNECTED,
2920+ invalid_id,
2921+ modes_empty,
2922+ possible_encoder_ids_empty,
2923+ connector_physical_sizes_mm_before[1]);
2924+
2925+ mock_drm.prepare(drm_device);
2926
2927 auto display = create_display(create_platform());
2928
2929@@ -611,23 +708,44 @@
2930 EXPECT_THAT(conf, DisplayConfigsAreEquivalent(expected_outputs_before));
2931
2932 /* Reset DRM resources and check again */
2933- resources.reset();
2934-
2935- resources.add_crtc(crtc_ids[1], modes0[1]);
2936-
2937- resources.add_encoder(encoder_ids[0], invalid_id, possible_crtcs_mask_empty);
2938- resources.add_encoder(encoder_ids[1], crtc_ids[1], possible_crtcs_mask_empty);
2939-
2940- resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_Composite,
2941- DRM_MODE_DISCONNECTED, invalid_id,
2942- modes_empty, possible_encoder_ids_empty,
2943- connector_physical_sizes_mm_after[0]);
2944- resources.add_connector(connector_ids[1], DRM_MODE_CONNECTOR_VGA,
2945- DRM_MODE_CONNECTED, encoder_ids[1],
2946- modes0, possible_encoder_ids_empty,
2947- connector_physical_sizes_mm_after[1]);
2948-
2949- resources.prepare();
2950+ mock_drm.reset(drm_device);
2951+
2952+ mock_drm.add_crtc(
2953+ drm_device,
2954+ crtc_ids[1],
2955+ modes0[1]);
2956+
2957+ mock_drm.add_encoder(
2958+ drm_device,
2959+ encoder_ids[0],
2960+ invalid_id,
2961+ possible_crtcs_mask_empty);
2962+ mock_drm.add_encoder(
2963+ drm_device,
2964+ encoder_ids[1],
2965+ crtc_ids[1],
2966+ possible_crtcs_mask_empty);
2967+
2968+ mock_drm.add_connector(
2969+ drm_device,
2970+ connector_ids[0],
2971+ DRM_MODE_CONNECTOR_Composite,
2972+ DRM_MODE_DISCONNECTED,
2973+ invalid_id,
2974+ modes_empty,
2975+ possible_encoder_ids_empty,
2976+ connector_physical_sizes_mm_after[0]);
2977+ mock_drm.add_connector(
2978+ drm_device,
2979+ connector_ids[1],
2980+ DRM_MODE_CONNECTOR_VGA,
2981+ DRM_MODE_CONNECTED,
2982+ encoder_ids[1],
2983+ modes0,
2984+ possible_encoder_ids_empty,
2985+ connector_physical_sizes_mm_after[1]);
2986+
2987+ mock_drm.prepare(drm_device);
2988
2989 /* Fake a device change so display can fetch updated configuration*/
2990 MainLoop ml;
2991@@ -708,17 +826,36 @@
2992 },
2993 };
2994
2995- mtd::FakeDRMResources& resources(mock_drm.fake_drm);
2996- auto const syspath = fake_devices.add_device("drm", "card2", NULL, {}, {"DEVTYPE", "drm_minor"});
2997+ auto const syspath = fake_devices.add_device(
2998+ "drm",
2999+ "card2",
3000+ NULL,
3001+ {},
3002+ {
3003+ "DEVTYPE", "drm_minor",
3004+ "DEVNAME", "/dev/dri/card2"
3005+ });
3006
3007- resources.reset();
3008- resources.add_crtc(crtc_ids[0], modes0[1]);
3009- resources.add_encoder(encoder_ids[0], crtc_ids[0], possible_crtcs_mask_empty);
3010- resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_Composite,
3011- DRM_MODE_CONNECTED, encoder_ids[0],
3012- modes0, possible_encoder_ids_empty,
3013- connector_physical_sizes_mm_before);
3014- resources.prepare();
3015+ mock_drm.reset(drm_device);
3016+ mock_drm.add_crtc(
3017+ drm_device,
3018+ crtc_ids[0],
3019+ modes0[1]);
3020+ mock_drm.add_encoder(
3021+ drm_device,
3022+ encoder_ids[0],
3023+ crtc_ids[0],
3024+ possible_crtcs_mask_empty);
3025+ mock_drm.add_connector(
3026+ drm_device,
3027+ connector_ids[0],
3028+ DRM_MODE_CONNECTOR_Composite,
3029+ DRM_MODE_CONNECTED,
3030+ encoder_ids[0],
3031+ modes0,
3032+ possible_encoder_ids_empty,
3033+ connector_physical_sizes_mm_before);
3034+ mock_drm.prepare(drm_device);
3035
3036 auto display = create_display(create_platform());
3037 auto conf = display->configuration();
3038@@ -726,15 +863,31 @@
3039
3040 // Now simulate a change of monitor, with no CRTC attached (and hence no current mode).
3041 // The configuration should mirror this state.
3042- resources.reset();
3043- resources.add_crtc(crtc_ids[0], modes1[1]);
3044- resources.add_encoder(encoder_ids[0], crtc_ids[0], possible_crtcs_mask_empty);
3045- resources.add_encoder(encoder_ids[1], 0, possible_crtcs_mask_empty);
3046- resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_Composite,
3047- DRM_MODE_CONNECTED, encoder_ids[1],
3048- modes1, possible_encoder_ids_empty,
3049- connector_physical_sizes_mm_after);
3050- resources.prepare();
3051+ mock_drm.reset(drm_device);
3052+ mock_drm.add_crtc(
3053+ drm_device,
3054+ crtc_ids[0],
3055+ modes1[1]);
3056+ mock_drm.add_encoder(
3057+ drm_device,
3058+ encoder_ids[0],
3059+ crtc_ids[0],
3060+ possible_crtcs_mask_empty);
3061+ mock_drm.add_encoder(
3062+ drm_device,
3063+ encoder_ids[1],
3064+ 0,
3065+ possible_crtcs_mask_empty);
3066+ mock_drm.add_connector(
3067+ drm_device,
3068+ connector_ids[0],
3069+ DRM_MODE_CONNECTOR_Composite,
3070+ DRM_MODE_CONNECTED,
3071+ encoder_ids[1],
3072+ modes1,
3073+ possible_encoder_ids_empty,
3074+ connector_physical_sizes_mm_after);
3075+ mock_drm.prepare(drm_device);
3076
3077 MainLoop ml;
3078 mt::Signal handler_signal;
3079@@ -758,17 +911,36 @@
3080 std::vector<uint32_t> possible_encoder_ids_empty;
3081
3082 uint32_t const possible_crtcs_mask_empty{0};
3083- mtd::FakeDRMResources& resources(mock_drm.fake_drm);
3084- resources.reset();
3085- resources.add_crtc(crtc_id, modes0[1]);
3086- resources.add_encoder(encoder_id, crtc_id, possible_crtcs_mask_empty);
3087- resources.add_connector(connector_id, DRM_MODE_CONNECTOR_Composite,
3088- DRM_MODE_CONNECTED, encoder_id,
3089- modes0, possible_encoder_ids_empty,
3090- connector_physical_sizes_mm);
3091- resources.prepare();
3092+ mock_drm.reset(drm_device);
3093+ mock_drm.add_crtc(
3094+ drm_device,
3095+ crtc_id,
3096+ modes0[1]);
3097+ mock_drm.add_encoder(
3098+ drm_device,
3099+ encoder_id,
3100+ crtc_id,
3101+ possible_crtcs_mask_empty);
3102+ mock_drm.add_connector(
3103+ drm_device,
3104+ connector_id,
3105+ DRM_MODE_CONNECTOR_Composite,
3106+ DRM_MODE_CONNECTED,
3107+ encoder_id,
3108+ modes0,
3109+ possible_encoder_ids_empty,
3110+ connector_physical_sizes_mm);
3111+ mock_drm.prepare(drm_device);
3112
3113- auto const syspath = fake_devices.add_device("drm", "card2", NULL, {}, {"DEVTYPE", "drm_minor"});
3114+ auto const syspath = fake_devices.add_device(
3115+ "drm",
3116+ "card2",
3117+ NULL,
3118+ {},
3119+ {
3120+ "DEVTYPE", "drm_minor",
3121+ "DEVNAME", "/dev/dri/card2"
3122+ });
3123
3124 auto display = create_display(create_platform());
3125
3126
3127=== modified file 'tests/unit-tests/platforms/mesa/kms/test_display_generic.cpp'
3128--- tests/unit-tests/platforms/mesa/kms/test_display_generic.cpp 2016-08-24 02:09:08 +0000
3129+++ tests/unit-tests/platforms/mesa/kms/test_display_generic.cpp 2017-03-31 06:57:08 +0000
3130@@ -35,6 +35,7 @@
3131
3132 #include <gtest/gtest.h>
3133 #include <gmock/gmock.h>
3134+#include <fcntl.h>
3135
3136 namespace mg = mir::graphics;
3137 namespace mgm = mg::mesa;
3138@@ -45,6 +46,7 @@
3139 {
3140 public:
3141 DisplayTestGeneric()
3142+ : drm_fd{open(drm_device, 0, 0)}
3143 {
3144 using namespace testing;
3145
3146@@ -56,7 +58,15 @@
3147 mock_egl.provide_egl_extensions();
3148 mock_gl.provide_gles_extensions();
3149
3150+ ON_CALL(mock_gbm, gbm_device_get_fd(_))
3151+ .WillByDefault(Return(drm_fd));
3152+
3153 fake_devices.add_standard_device("standard-drm-devices");
3154+
3155+ // Remove all outputs from all but one of the DRM devices we access;
3156+ // these tests are not set up to test hybrid.
3157+ mock_drm.reset("/dev/dri/card1");
3158+ mock_drm.reset("/dev/dri/card2");
3159 }
3160
3161 std::shared_ptr<mg::Display> create_display()
3162@@ -76,6 +86,9 @@
3163 ::testing::NiceMock<mtd::MockDRM> mock_drm;
3164 ::testing::NiceMock<mtd::MockGBM> mock_gbm;
3165 mtf::UdevEnvironment fake_devices;
3166+
3167+ char const* const drm_device = "/dev/dri/card0";
3168+ int const drm_fd;
3169 };
3170
3171 #include "../../test_display.h"
3172
3173=== modified file 'tests/unit-tests/platforms/mesa/kms/test_display_multi_monitor.cpp'
3174--- tests/unit-tests/platforms/mesa/kms/test_display_multi_monitor.cpp 2016-01-29 08:18:22 +0000
3175+++ tests/unit-tests/platforms/mesa/kms/test_display_multi_monitor.cpp 2017-03-31 06:57:08 +0000
3176@@ -41,6 +41,7 @@
3177 #include <gmock/gmock.h>
3178
3179 #include <unordered_set>
3180+#include <fcntl.h>
3181
3182 namespace mg = mir::graphics;
3183 namespace mgm = mg::mesa;
3184@@ -109,6 +110,7 @@
3185 {
3186 public:
3187 MesaDisplayMultiMonitorTest()
3188+ : drm_fd{open(drm_device, 0, 0)}
3189 {
3190 using namespace testing;
3191
3192@@ -128,9 +130,15 @@
3193 EXPECT_CALL(mock_gbm, gbm_bo_get_device(_))
3194 .Times(AtLeast(0));
3195 EXPECT_CALL(mock_gbm, gbm_device_get_fd(_))
3196- .Times(AtLeast(0));
3197+ .Times(AtLeast(0))
3198+ .WillRepeatedly(Return(drm_fd));
3199
3200 fake_devices.add_standard_device("standard-drm-devices");
3201+
3202+ // Remove all outputs from all but one of the DRM devices we access;
3203+ // these tests are not set up to test hybrid.
3204+ mock_drm.reset("/dev/dri/card1");
3205+ mock_drm.reset("/dev/dri/card2");
3206 }
3207
3208 std::shared_ptr<mgm::Platform> create_platform()
3209@@ -162,8 +170,6 @@
3210 {
3211 using fake = mtd::FakeDRMResources;
3212
3213- mtd::FakeDRMResources& resources(mock_drm.fake_drm);
3214-
3215 modes0.clear();
3216 modes0.push_back(fake::create_mode(1920, 1080, 138500, 2080, 1111, fake::NormalMode));
3217 modes0.push_back(fake::create_mode(1920, 1080, 148500, 2200, 1125, fake::PreferredMode));
3218@@ -172,7 +178,7 @@
3219
3220 geom::Size const connector_physical_size_mm{1597, 987};
3221
3222- resources.reset();
3223+ mock_drm.reset(drm_device);
3224
3225 uint32_t const crtc_base_id{10};
3226 uint32_t const encoder_base_id{20};
3227@@ -185,10 +191,10 @@
3228 uint32_t const all_crtcs_mask{0xff};
3229
3230 crtc_ids.push_back(crtc_id);
3231- resources.add_crtc(crtc_id, drmModeModeInfo());
3232+ mock_drm.add_crtc(drm_device, crtc_id, drmModeModeInfo());
3233
3234 encoder_ids.push_back(encoder_id);
3235- resources.add_encoder(encoder_id, crtc_id, all_crtcs_mask);
3236+ mock_drm.add_encoder(drm_device, encoder_id, crtc_id, all_crtcs_mask);
3237 }
3238
3239 for (int i = 0; i < connected; i++)
3240@@ -196,9 +202,15 @@
3241 uint32_t const connector_id{connector_base_id + i};
3242
3243 connector_ids.push_back(connector_id);
3244- resources.add_connector(connector_id, DRM_MODE_CONNECTOR_VGA,
3245- DRM_MODE_CONNECTED, encoder_ids[i],
3246- modes0, encoder_ids, connector_physical_size_mm);
3247+ mock_drm.add_connector(
3248+ drm_device,
3249+ connector_id,
3250+ DRM_MODE_CONNECTOR_VGA,
3251+ DRM_MODE_CONNECTED,
3252+ encoder_ids[i],
3253+ modes0,
3254+ encoder_ids,
3255+ connector_physical_size_mm);
3256 }
3257
3258 for (int i = 0; i < disconnected; i++)
3259@@ -206,12 +218,18 @@
3260 uint32_t const connector_id{connector_base_id + connected + i};
3261
3262 connector_ids.push_back(connector_id);
3263- resources.add_connector(connector_id, DRM_MODE_CONNECTOR_VGA,
3264- DRM_MODE_DISCONNECTED, 0,
3265- modes_empty, encoder_ids, geom::Size{});
3266+ mock_drm.add_connector(
3267+ drm_device,
3268+ connector_id,
3269+ DRM_MODE_CONNECTOR_VGA,
3270+ DRM_MODE_DISCONNECTED,
3271+ 0,
3272+ modes_empty,
3273+ encoder_ids,
3274+ geom::Size{});
3275 }
3276
3277- resources.prepare();
3278+ mock_drm.prepare(drm_device);
3279 }
3280
3281
3282@@ -227,6 +245,9 @@
3283 std::vector<uint32_t> connector_ids;
3284
3285 mtf::UdevEnvironment fake_devices;
3286+
3287+ char const* const drm_device = "/dev/dri/card0";
3288+ int const drm_fd;
3289 };
3290
3291 }
3292@@ -242,7 +263,7 @@
3293 setup_outputs(num_connected_outputs, num_disconnected_outputs);
3294
3295 /* Create DRM FBs */
3296- EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(),
3297+ EXPECT_CALL(mock_drm, drmModeAddFB2(mtd::IsFdOfDevice(drm_device),
3298 _, _, _, _, _, _, _, _))
3299 .WillRepeatedly(DoAll(SetArgPointee<7>(fb_id), Return(0)));
3300
3301@@ -252,7 +273,7 @@
3302 for (int i = 0; i < num_connected_outputs; i++)
3303 {
3304 crtc_setups += EXPECT_CALL(mock_drm,
3305- drmModeSetCrtc(mock_drm.fake_drm.fd(),
3306+ drmModeSetCrtc(mtd::IsFdOfDevice(drm_device),
3307 crtc_ids[i], fb_id,
3308 _, _,
3309 Pointee(connector_ids[i]),
3310@@ -263,7 +284,7 @@
3311 /* All crtcs are restored at teardown */
3312 for (int i = 0; i < num_connected_outputs; i++)
3313 {
3314- EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),
3315+ EXPECT_CALL(mock_drm, drmModeSetCrtc(mtd::IsFdOfDevice(drm_device),
3316 crtc_ids[i], Ne(fb_id),
3317 _, _,
3318 Pointee(connector_ids[i]),
3319@@ -338,25 +359,25 @@
3320 setup_outputs(num_connected_outputs, num_disconnected_outputs);
3321
3322 /* Create DRM FBs */
3323- EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(),
3324+ EXPECT_CALL(mock_drm, drmModeAddFB2(mtd::IsFdOfDevice(drm_device),
3325 _, _, _, _, _, _, _, _))
3326 .WillRepeatedly(DoAll(SetArgPointee<7>(fb_id), Return(0)));
3327
3328 /* All crtcs are flipped */
3329 for (int i = 0; i < num_connected_outputs; i++)
3330 {
3331- EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(),
3332+ EXPECT_CALL(mock_drm, drmModePageFlip(mtd::IsFdOfDevice(drm_device),
3333 crtc_ids[i], fb_id,
3334 _, _))
3335 .Times(2)
3336 .WillRepeatedly(DoAll(SaveArg<4>(&user_data[i]), Return(0)));
3337
3338 /* Emit fake DRM page-flip events */
3339- EXPECT_EQ(1, write(mock_drm.fake_drm.write_fd(), "a", 1));
3340+ mock_drm.generate_event_on(drm_device);
3341 }
3342
3343 /* Handle the events properly */
3344- EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _))
3345+ EXPECT_CALL(mock_drm, drmHandleEvent(mtd::IsFdOfDevice(drm_device), _))
3346 .Times(num_connected_outputs)
3347 .WillOnce(DoAll(InvokePageFlipHandler(&user_data[0]), Return(0)))
3348 .WillOnce(DoAll(InvokePageFlipHandler(&user_data[1]), Return(0)))
3349@@ -426,7 +447,7 @@
3350 setup_outputs(num_connected_outputs, num_disconnected_outputs);
3351
3352 /* Create DRM FBs */
3353- EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(),
3354+ EXPECT_CALL(mock_drm, drmModeAddFB2(mtd::IsFdOfDevice(drm_device),
3355 _, _, _, _, _, _, _, _))
3356 .Times(num_connected_outputs)
3357 .WillRepeatedly(Invoke(&fb_id_container, &FBIDContainer::add_fb2));
3358@@ -437,7 +458,7 @@
3359 for (int i = 0; i < num_connected_outputs; i++)
3360 {
3361 crtc_setups += EXPECT_CALL(mock_drm,
3362- drmModeSetCrtc(mock_drm.fake_drm.fd(),
3363+ drmModeSetCrtc(mtd::IsFdOfDevice(drm_device),
3364 crtc_ids[i],
3365 IsValidFB(&fb_id_container),
3366 _, _,
3367@@ -449,7 +470,7 @@
3368 /* All crtcs are restored at teardown */
3369 for (int i = 0; i < num_connected_outputs; i++)
3370 {
3371- EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),
3372+ EXPECT_CALL(mock_drm, drmModeSetCrtc(mtd::IsFdOfDevice(drm_device),
3373 crtc_ids[i], 0,
3374 _, _,
3375 Pointee(connector_ids[i]),
3376@@ -478,11 +499,11 @@
3377 for (int i = 0; i < num_connected_outputs; i++)
3378 {
3379 EXPECT_CALL(mock_drm,
3380- drmModeSetCursor(mock_drm.fake_drm.fd(),
3381+ drmModeSetCursor(mtd::IsFdOfDevice(drm_device),
3382 crtc_ids[i], 0, 0, 0))
3383 .Times(1);
3384 EXPECT_CALL(mock_drm,
3385- drmModeSetCrtc(mock_drm.fake_drm.fd(),
3386+ drmModeSetCrtc(mtd::IsFdOfDevice(drm_device),
3387 crtc_ids[i], 0, 0, 0,
3388 nullptr, 0, nullptr))
3389 .Times(1);
3390@@ -508,7 +529,7 @@
3391 for (int i = 0; i < num_connected_outputs; i++)
3392 {
3393 EXPECT_CALL(mock_drm,
3394- drmModeSetCrtc(mock_drm.fake_drm.fd(), crtc_ids[i],
3395+ drmModeSetCrtc(mtd::IsFdOfDevice(drm_device), crtc_ids[i],
3396 0, _, _, Pointee(connector_ids[i]),
3397 _, _))
3398 .Times(1);
3399@@ -548,7 +569,7 @@
3400 for (int i = 0; i < num_connected_outputs; i++)
3401 {
3402 EXPECT_CALL(mock_drm,
3403- drmModeSetCrtc(mock_drm.fake_drm.fd(),
3404+ drmModeSetCrtc(mtd::IsFdOfDevice(drm_device),
3405 crtc_ids[i], 0, 0, 0,
3406 nullptr, 0, nullptr))
3407 .Times(1);
3408@@ -562,7 +583,7 @@
3409 for (int i = 0; i < num_connected_outputs; i++)
3410 {
3411 EXPECT_CALL(mock_drm,
3412- drmModeSetCrtc(mock_drm.fake_drm.fd(), crtc_ids[i],
3413+ drmModeSetCrtc(mtd::IsFdOfDevice(drm_device), crtc_ids[i],
3414 0, _, _, Pointee(connector_ids[i]),
3415 _, _))
3416 .Times(1);
3417
3418=== modified file 'tests/unit-tests/platforms/mesa/kms/test_drm_helper.cpp'
3419--- tests/unit-tests/platforms/mesa/kms/test_drm_helper.cpp 2017-01-18 02:29:37 +0000
3420+++ tests/unit-tests/platforms/mesa/kms/test_drm_helper.cpp 2017-03-31 06:57:08 +0000
3421@@ -70,8 +70,14 @@
3422
3423 drm_magic_t const magic{0x10111213};
3424
3425- EXPECT_CALL(mock_drm, drmAuthMagic(mock_drm.fake_drm.fd(), magic))
3426- .WillOnce(Return(-1));
3427+ EXPECT_CALL(
3428+ mock_drm,
3429+ drmAuthMagic(
3430+ AnyOf(
3431+ mtd::IsFdOfDevice("/dev/dri/card0"),
3432+ mtd::IsFdOfDevice("/dev/dri/card1")),
3433+ magic))
3434+ .WillOnce(Return(-1));
3435
3436 drm_helper.setup(std::make_shared<mir::udev::Context>());
3437
3438@@ -79,3 +85,15 @@
3439 drm_helper.auth_magic(magic);
3440 }, std::runtime_error);
3441 }
3442+
3443+TEST_F(DRMHelperTest, open_all_devices_opens_with_cloexec)
3444+{
3445+ using namespace testing;
3446+
3447+ EXPECT_CALL(mock_drm, open(StrEq("/dev/dri/card0"), FlagSet(O_CLOEXEC), _))
3448+ .Times(1);
3449+ EXPECT_CALL(mock_drm, open(StrEq("/dev/dri/card1"), FlagSet(O_CLOEXEC), _))
3450+ .Times(1);
3451+
3452+ mgm::helpers::DRMHelper::open_all_devices(std::make_shared<mir::udev::Context>());
3453+}
3454\ No newline at end of file
3455
3456=== modified file 'tests/unit-tests/platforms/mesa/kms/test_guest_platform.cpp'
3457--- tests/unit-tests/platforms/mesa/kms/test_guest_platform.cpp 2017-03-20 19:06:00 +0000
3458+++ tests/unit-tests/platforms/mesa/kms/test_guest_platform.cpp 2017-03-31 06:57:08 +0000
3459@@ -36,6 +36,7 @@
3460 #include <gmock/gmock.h>
3461
3462 #include <cstring>
3463+#include <fcntl.h>
3464
3465 namespace mg = mir::graphics;
3466 namespace mgm = mir::graphics::mesa;
3467@@ -56,10 +57,11 @@
3468 {
3469 public:
3470 MesaGuestPlatformTest()
3471+ : drm_fd{open(drm_device, 0, 0)}
3472 {
3473 int fake_fd = 4939;
3474 ON_CALL(mock_platform_authentication, platform_fd_items())
3475- .WillByDefault(Return(std::vector<int>{mock_drm.fake_drm.fd()}));
3476+ .WillByDefault(Return(std::vector<int>{drm_fd}));
3477 ON_CALL(mock_platform_authentication, set_gbm_extension())
3478 .WillByDefault(Return(mir::optional_value<std::shared_ptr<mg::SetGbmExtension>>{mock_gbm_ext}));
3479 ON_CALL(mock_platform_authentication, auth_extension())
3480@@ -74,6 +76,8 @@
3481 ::testing::NiceMock<mtd::MockPlatformAuthentication> mock_platform_authentication;
3482 std::shared_ptr<mtd::MockMesaExt> mock_ext = std::make_shared<mtd::MockMesaExt>();
3483 std::shared_ptr<MockSetGbmExt> mock_gbm_ext = std::make_shared<MockSetGbmExt>();
3484+ char const* const drm_device = "/dev/dri/card0";
3485+ int const drm_fd;
3486 };
3487
3488 }
3489
3490=== modified file 'tests/unit-tests/platforms/mesa/kms/test_kms_page_flipper.cpp'
3491--- tests/unit-tests/platforms/mesa/kms/test_kms_page_flipper.cpp 2017-01-18 02:29:37 +0000
3492+++ tests/unit-tests/platforms/mesa/kms/test_kms_page_flipper.cpp 2017-03-31 06:57:08 +0000
3493@@ -32,6 +32,7 @@
3494 #include <unordered_set>
3495
3496 #include <sys/time.h>
3497+#include <fcntl.h>
3498
3499 namespace mg = mir::graphics;
3500 namespace mgm = mir::graphics::mesa;
3501@@ -45,12 +46,17 @@
3502 {
3503 public:
3504 KMSPageFlipperTest()
3505- : page_flipper{mock_drm.fake_drm.fd(), mt::fake_shared(report)}
3506+ : drm_fd{open(drm_device, 0, 0)},
3507+ page_flipper{drm_fd, mt::fake_shared(report)}
3508 {
3509 }
3510
3511 testing::NiceMock<mtd::MockDisplayReport> report;
3512 testing::NiceMock<mtd::MockDRM> mock_drm;
3513+
3514+ char const* const drm_device = "/dev/dri/card0";
3515+ int const drm_fd;
3516+
3517 mgm::KMSPageFlipper page_flipper;
3518 };
3519
3520@@ -73,8 +79,7 @@
3521 uint32_t const fb_id{101};
3522 uint32_t const connector_id{345};
3523
3524- EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(),
3525- crtc_id, fb_id, _, _))
3526+ EXPECT_CALL(mock_drm, drmModePageFlip(drm_fd, crtc_id, fb_id, _, _))
3527 .Times(1);
3528
3529 page_flipper.schedule_flip(crtc_id, fb_id, connector_id);
3530@@ -88,8 +93,7 @@
3531 uint32_t const fb_id{101};
3532 uint32_t const connector_id{345};
3533
3534- EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(),
3535- crtc_id, fb_id, _, _))
3536+ EXPECT_CALL(mock_drm, drmModePageFlip(drm_fd, crtc_id, fb_id, _, _))
3537 .Times(1);
3538
3539 page_flipper.schedule_flip(crtc_id, fb_id, connector_id);
3540@@ -108,19 +112,18 @@
3541 uint32_t const connector_id{345};
3542 void* user_data{nullptr};
3543
3544- EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(),
3545- crtc_id, fb_id, _, _))
3546+ EXPECT_CALL(mock_drm, drmModePageFlip(drm_fd, crtc_id, fb_id, _, _))
3547 .Times(1)
3548 .WillOnce(DoAll(SaveArg<4>(&user_data), Return(0)));
3549
3550- EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _))
3551+ EXPECT_CALL(mock_drm, drmHandleEvent(drm_fd, _))
3552 .Times(1)
3553 .WillOnce(DoAll(InvokePageFlipHandler(&user_data), Return(0)));
3554
3555 page_flipper.schedule_flip(crtc_id, fb_id, connector_id);
3556
3557 /* Fake a DRM event */
3558- EXPECT_EQ(1, write(mock_drm.fake_drm.write_fd(), "a", 1));
3559+ mock_drm.generate_event_on(drm_device);
3560
3561 page_flipper.wait_for_flip(crtc_id);
3562 }
3563@@ -142,7 +145,7 @@
3564 EXPECT_CALL(report, report_vsync(connector_id, _));
3565
3566 page_flipper.schedule_flip(crtc_id, fb_id, connector_id);
3567- EXPECT_EQ(1, write(mock_drm.fake_drm.write_fd(), "a", 1));
3568+ mock_drm.generate_event_on(drm_device);
3569 page_flipper.wait_for_flip(crtc_id);
3570 }
3571
3572@@ -170,8 +173,7 @@
3573 uint32_t const connector_id{345};
3574 void* user_data{nullptr};
3575
3576- EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(),
3577- crtc_id, fb_id, _, _))
3578+ EXPECT_CALL(mock_drm, drmModePageFlip(drm_fd, crtc_id, fb_id, _, _))
3579 .Times(1)
3580 .WillOnce(DoAll(SaveArg<4>(&user_data), Return(0)));
3581
3582@@ -181,7 +183,7 @@
3583 page_flipper.schedule_flip(crtc_id, fb_id, connector_id);
3584
3585 /* Cause a failure in wait_for_flip */
3586- close(mock_drm.fake_drm.fd());
3587+ close(drm_fd);
3588
3589 EXPECT_THROW({
3590 page_flipper.wait_for_flip(crtc_id);
3591@@ -198,14 +200,13 @@
3592 uint32_t const connector_ids[flips] = {23, 45, 67};
3593 void* user_data[flips] = {nullptr, nullptr, nullptr};
3594
3595- EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(),
3596- _, fb_id, _, _))
3597+ EXPECT_CALL(mock_drm, drmModePageFlip(drm_fd, _, fb_id, _, _))
3598 .Times(3)
3599 .WillOnce(DoAll(SaveArg<4>(&user_data[0]), Return(0)))
3600 .WillOnce(DoAll(SaveArg<4>(&user_data[1]), Return(0)))
3601 .WillOnce(DoAll(SaveArg<4>(&user_data[2]), Return(0)));
3602
3603- EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _))
3604+ EXPECT_CALL(mock_drm, drmHandleEvent(drm_fd, _))
3605 .Times(3)
3606 .WillOnce(DoAll(InvokePageFlipHandler(&user_data[1]), Return(0)))
3607 .WillOnce(DoAll(InvokePageFlipHandler(&user_data[2]), Return(0)))
3608@@ -215,7 +216,9 @@
3609 page_flipper.schedule_flip(crtc_ids[i], fb_id, connector_ids[i]);
3610
3611 /* Fake 3 DRM events */
3612- EXPECT_EQ(flips, write(mock_drm.fake_drm.write_fd(), "abc", 3));
3613+ mock_drm.generate_event_on(drm_device);
3614+ mock_drm.generate_event_on(drm_device);
3615+ mock_drm.generate_event_on(drm_device);
3616
3617 for (auto crtc_id : crtc_ids)
3618 page_flipper.wait_for_flip(crtc_id);
3619@@ -283,7 +286,7 @@
3620 std::vector<std::thread> page_flipping_threads;
3621 std::thread::id tid;
3622
3623- EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(), _, _, _, _))
3624+ EXPECT_CALL(mock_drm, drmModePageFlip(drm_fd, _, _, _, _))
3625 .Times(2)
3626 .WillOnce(DoAll(SaveArg<4>(&user_data[worker_index]), Return(0)))
3627 .WillOnce(DoAll(SaveArg<4>(&user_data[other_index]), Return(0)));
3628@@ -292,7 +295,7 @@
3629 * The first event releases the original worker, hence we expect that
3630 * then the other thread will become the worker.
3631 */
3632- EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _))
3633+ EXPECT_CALL(mock_drm, drmHandleEvent(drm_fd, _))
3634 .Times(2)
3635 .WillOnce(DoAll(InvokePageFlipHandler(&user_data[worker_index]), Return(0)))
3636 .WillOnce(DoAll(InvokePageFlipHandler(&user_data[other_index]), Return(0)));
3637@@ -320,7 +323,7 @@
3638 EXPECT_EQ(page_flipping_threads[worker_index].get_id(), tid);
3639
3640 /* Fake a DRM event */
3641- EXPECT_EQ(1, write(mock_drm.fake_drm.write_fd(), "a", 1));
3642+ mock_drm.generate_event_on(drm_device);
3643
3644 page_flipping_threads[worker_index].join();
3645
3646@@ -332,7 +335,7 @@
3647 }
3648
3649 /* Fake another DRM event to unblock the remaining thread */
3650- EXPECT_EQ(1, write(mock_drm.fake_drm.write_fd(), "a", 1));
3651+ mock_drm.generate_event_on(drm_device);
3652
3653 page_flipping_threads[other_index].join();
3654 }
3655@@ -349,7 +352,7 @@
3656 std::vector<std::thread> page_flipping_threads;
3657 std::thread::id tid;
3658
3659- EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(), _, _, _, _))
3660+ EXPECT_CALL(mock_drm, drmModePageFlip(drm_fd, _, _, _, _))
3661 .Times(2)
3662 .WillOnce(DoAll(SaveArg<4>(&user_data[worker_index]), Return(0)))
3663 .WillOnce(DoAll(SaveArg<4>(&user_data[other_index]), Return(0)));
3664@@ -358,7 +361,7 @@
3665 * The first event releases the non-worker thread, hence we expect that
3666 * original worker not change.
3667 */
3668- EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _))
3669+ EXPECT_CALL(mock_drm, drmHandleEvent(drm_fd, _))
3670 .Times(2)
3671 .WillOnce(DoAll(InvokePageFlipHandler(&user_data[other_index]), Return(0)))
3672 .WillOnce(DoAll(InvokePageFlipHandler(&user_data[worker_index]), Return(0)));
3673@@ -386,7 +389,7 @@
3674 EXPECT_EQ(page_flipping_threads[worker_index].get_id(), tid);
3675
3676 /* Fake a DRM event */
3677- EXPECT_EQ(1, write(mock_drm.fake_drm.write_fd(), "a", 1));
3678+ mock_drm.generate_event_on(drm_device);
3679
3680 /* Wait for the non-worker thread to exit */
3681 page_flipping_threads[other_index].join();
3682@@ -396,7 +399,7 @@
3683 page_flipper.debug_get_worker_tid());
3684
3685 /* Fake another DRM event to unblock the remaining thread */
3686- EXPECT_EQ(1, write(mock_drm.fake_drm.write_fd(), "a", 1));
3687+ mock_drm.generate_event_on(drm_device);
3688
3689 page_flipping_threads[worker_index].join();
3690 }
3691@@ -515,7 +518,7 @@
3692 counter->add_handle_event(crtc_id);
3693 }
3694
3695-ACTION_P2(AddPageFlipEvent, counter, write_drm_fd)
3696+ACTION_P2(AddPageFlipEvent, counter, generate_event_functor)
3697 {
3698 uint32_t const crtc_id{arg0};
3699 void* const user_data{arg1};
3700@@ -523,7 +526,7 @@
3701 /* Record this call */
3702 counter->add_flip(crtc_id, user_data);
3703 /* Add an event to the drm event queue */
3704- EXPECT_EQ(1, write(write_drm_fd, "a", 1));
3705+ generate_event_functor();
3706 }
3707
3708 }
3709@@ -537,11 +540,19 @@
3710 std::vector<std::thread> page_flipping_threads;
3711 PageFlipCounter counter;
3712
3713- EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(), _, _, _, _))
3714- .WillRepeatedly(DoAll(WithArgs<1,4>(AddPageFlipEvent(&counter, mock_drm.fake_drm.write_fd())),
3715- Return(0)));
3716+ EXPECT_CALL(mock_drm, drmModePageFlip(drm_fd, _, _, _, _))
3717+ .WillRepeatedly(
3718+ DoAll(
3719+ WithArgs<1,4>(
3720+ AddPageFlipEvent(
3721+ &counter,
3722+ [this]()
3723+ {
3724+ mock_drm.generate_event_on(drm_device);
3725+ })),
3726+ Return(0)));
3727
3728- EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _))
3729+ EXPECT_CALL(mock_drm, drmHandleEvent(drm_fd, _))
3730 .WillRepeatedly(DoAll(InvokePageFlipHandlerWithPendingData(&counter),
3731 Return(0)));
3732
3733
3734=== modified file 'tests/unit-tests/platforms/mesa/kms/test_platform.cpp'
3735--- tests/unit-tests/platforms/mesa/kms/test_platform.cpp 2017-03-09 12:52:48 +0000
3736+++ tests/unit-tests/platforms/mesa/kms/test_platform.cpp 2017-03-31 06:57:08 +0000
3737@@ -97,16 +97,18 @@
3738 int const auth_fd{auth_pipe.read_fd()};
3739
3740 /* First time for master DRM fd, second for authenticated fd */
3741- EXPECT_CALL(mock_drm, open(_,_,_))
3742- .WillOnce(Return(mock_drm.fake_drm.fd()));
3743+ EXPECT_CALL(mock_drm, open(StrEq("/dev/dri/card0"),_,_));
3744+ EXPECT_CALL(mock_drm, open(StrEq("/dev/dri/card1"),_,_));
3745+
3746 EXPECT_CALL(mock_drm, drmOpen(_,_))
3747 .WillOnce(Return(auth_fd));
3748
3749 /* Expect proper authorization */
3750 EXPECT_CALL(mock_drm, drmGetMagic(auth_fd,_));
3751- EXPECT_CALL(mock_drm, drmAuthMagic(mock_drm.fake_drm.fd(),_));
3752+ EXPECT_CALL(mock_drm, drmAuthMagic(mtd::IsFdOfDevice("/dev/dri/card0"),_));
3753
3754- EXPECT_CALL(mock_drm, drmClose(mock_drm.fake_drm.fd()));
3755+ EXPECT_CALL(mock_drm, drmClose(mtd::IsFdOfDevice("/dev/dri/card0")));
3756+ EXPECT_CALL(mock_drm, drmClose(mtd::IsFdOfDevice("/dev/dri/card1")));
3757
3758 /* Expect authenticated fd to be closed when package is destroyed */
3759 EXPECT_CALL(mock_drm, drmClose(auth_fd));
3760@@ -140,22 +142,6 @@
3761 FAIL() << "Expected an exception to be thrown.";
3762 }
3763
3764-TEST_F(MesaGraphicsPlatform, fails_if_no_resources)
3765-{
3766- using namespace ::testing;
3767-
3768- EXPECT_CALL(mock_drm, drmModeGetResources(_))
3769- .Times(Exactly(1))
3770- .WillOnce(Return(reinterpret_cast<drmModeRes*>(0)));
3771-
3772- EXPECT_CALL(mock_drm, drmModeFreeResources(_))
3773- .Times(Exactly(0));
3774-
3775- EXPECT_THROW({
3776- auto platform = create_platform();
3777- }, std::runtime_error) << "Expected that c'tor of Platform throws";
3778-}
3779-
3780 TEST_F(MesaGraphicsPlatform, egl_native_display_is_gbm_device)
3781 {
3782 auto platform = create_platform();
3783@@ -284,7 +270,9 @@
3784 mgm::BypassOption::allowed};
3785
3786 int const success_code = 0;
3787- EXPECT_CALL(mock_drm, drmDropMaster(mock_drm.fake_drm.fd()))
3788+ EXPECT_CALL(mock_drm, drmDropMaster(mtd::IsFdOfDevice("/dev/dri/card0")))
3789+ .WillOnce(Return(success_code));
3790+ EXPECT_CALL(mock_drm, drmDropMaster(mtd::IsFdOfDevice("/dev/dri/card1")))
3791 .WillOnce(Return(success_code));
3792
3793 (*emergency_cleanup_registry.handler)();
3794@@ -296,6 +284,8 @@
3795 {
3796 using namespace testing;
3797
3798+
3799+
3800 auto const mock_vt = std::make_shared<mtd::MockVirtualTerminal>();
3801 StubEmergencyCleanupRegistry emergency_cleanup_registry;
3802 mgm::Platform platform{
3803@@ -306,8 +296,8 @@
3804
3805 EXPECT_CALL(*mock_vt, restore())
3806 .WillOnce(Throw(std::runtime_error("vt restore exception")));
3807- EXPECT_CALL(mock_drm, drmDropMaster(mock_drm.fake_drm.fd()))
3808- .WillOnce(Throw(std::runtime_error("drm drop master exception")));
3809+ EXPECT_CALL(mock_drm, drmDropMaster(_))
3810+ .WillRepeatedly(Throw(std::runtime_error("drm drop master exception")));
3811
3812 (*emergency_cleanup_registry.handler)();
3813
3814
3815=== modified file 'tests/unit-tests/platforms/mesa/kms/test_real_kms_output.cpp'
3816--- tests/unit-tests/platforms/mesa/kms/test_real_kms_output.cpp 2017-03-24 07:15:51 +0000
3817+++ tests/unit-tests/platforms/mesa/kms/test_real_kms_output.cpp 2017-03-31 06:57:08 +0000
3818@@ -29,6 +29,7 @@
3819
3820 #include <gtest/gtest.h>
3821 #include <gmock/gmock.h>
3822+#include <fcntl.h>
3823
3824 namespace mg = mir::graphics;
3825 namespace mgm = mir::graphics::mesa;
3826@@ -59,7 +60,8 @@
3827 {
3828 public:
3829 RealKMSOutputTest()
3830- : invalid_id{0}, crtc_ids{10, 11},
3831+ : drm_fd{open(drm_device, 0, 0)},
3832+ invalid_id{0}, crtc_ids{10, 11},
3833 encoder_ids{20, 21}, connector_ids{30, 21},
3834 possible_encoder_ids1{encoder_ids[0]},
3835 possible_encoder_ids2{encoder_ids[0], encoder_ids[1]}
3836@@ -73,40 +75,77 @@
3837
3838 void setup_outputs_connected_crtc()
3839 {
3840- mtd::FakeDRMResources& resources(mock_drm.fake_drm);
3841 uint32_t const possible_crtcs_mask{0x1};
3842
3843- resources.reset();
3844-
3845- resources.add_crtc(crtc_ids[0], drmModeModeInfo());
3846- resources.add_encoder(encoder_ids[0], crtc_ids[0], possible_crtcs_mask);
3847- resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_VGA,
3848- DRM_MODE_CONNECTED, encoder_ids[0],
3849- modes_empty, possible_encoder_ids1, geom::Size());
3850-
3851- resources.prepare();
3852+ mock_drm.reset(drm_device);
3853+
3854+ mock_drm.add_crtc(
3855+ drm_device,
3856+ crtc_ids[0],
3857+ drmModeModeInfo());
3858+ mock_drm.add_encoder(
3859+ drm_device,
3860+ encoder_ids[0],
3861+ crtc_ids[0],
3862+ possible_crtcs_mask);
3863+ mock_drm.add_connector(
3864+ drm_device,
3865+ connector_ids[0],
3866+ DRM_MODE_CONNECTOR_VGA,
3867+ DRM_MODE_CONNECTED,
3868+ encoder_ids[0],
3869+ modes_empty,
3870+ possible_encoder_ids1,
3871+ geom::Size());
3872+
3873+ mock_drm.prepare(drm_device);
3874 }
3875
3876 void setup_outputs_no_connected_crtc()
3877 {
3878- mtd::FakeDRMResources& resources(mock_drm.fake_drm);
3879 uint32_t const possible_crtcs_mask1{0x1};
3880 uint32_t const possible_crtcs_mask_all{0x3};
3881
3882- resources.reset();
3883-
3884- resources.add_crtc(crtc_ids[0], drmModeModeInfo());
3885- resources.add_crtc(crtc_ids[1], drmModeModeInfo());
3886- resources.add_encoder(encoder_ids[0], crtc_ids[0], possible_crtcs_mask1);
3887- resources.add_encoder(encoder_ids[1], invalid_id, possible_crtcs_mask_all);
3888- resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_Composite,
3889- DRM_MODE_CONNECTED, invalid_id,
3890- modes_empty, possible_encoder_ids2, geom::Size());
3891- resources.add_connector(connector_ids[1], DRM_MODE_CONNECTOR_DVIA,
3892- DRM_MODE_CONNECTED, encoder_ids[0],
3893- modes_empty, possible_encoder_ids2, geom::Size());
3894-
3895- resources.prepare();
3896+ mock_drm.reset(drm_device);
3897+
3898+ mock_drm.add_crtc(
3899+ drm_device,
3900+ crtc_ids[0],
3901+ drmModeModeInfo());
3902+ mock_drm.add_crtc(
3903+ drm_device,
3904+ crtc_ids[1],
3905+ drmModeModeInfo());
3906+ mock_drm.add_encoder(
3907+ drm_device,
3908+ encoder_ids[0],
3909+ crtc_ids[0],
3910+ possible_crtcs_mask1);
3911+ mock_drm.add_encoder(
3912+ drm_device,
3913+ encoder_ids[1],
3914+ invalid_id,
3915+ possible_crtcs_mask_all);
3916+ mock_drm.add_connector(
3917+ drm_device,
3918+ connector_ids[0],
3919+ DRM_MODE_CONNECTOR_Composite,
3920+ DRM_MODE_CONNECTED,
3921+ invalid_id,
3922+ modes_empty,
3923+ possible_encoder_ids2,
3924+ geom::Size());
3925+ mock_drm.add_connector(
3926+ drm_device,
3927+ connector_ids[1],
3928+ DRM_MODE_CONNECTOR_DVIA,
3929+ DRM_MODE_CONNECTED,
3930+ encoder_ids[0],
3931+ modes_empty,
3932+ possible_encoder_ids2,
3933+ geom::Size());
3934+
3935+ mock_drm.prepare(drm_device);
3936 }
3937
3938 void append_fb_id(uint32_t fb_id)
3939@@ -122,8 +161,11 @@
3940 testing::NiceMock<mtd::MockGBM> mock_gbm;
3941 MockPageFlipper mock_page_flipper;
3942 NullPageFlipper null_page_flipper;
3943-
3944 std::vector<drmModeModeInfo> modes_empty;
3945+
3946+ char const* const drm_device = "/dev/dri/card0";
3947+ int const drm_fd;
3948+
3949 gbm_bo* const fake_bo{reinterpret_cast<gbm_bo*>(0x123ba)};
3950 uint32_t const invalid_id;
3951 std::vector<uint32_t> const crtc_ids;
3952@@ -165,8 +207,8 @@
3953 }
3954
3955 mgm::RealKMSOutput output{
3956- mock_drm.fake_drm.fd(),
3957- mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
3958+ drm_fd,
3959+ mg::kms::get_connector(drm_fd, connector_ids[0]),
3960 mt::fake_shared(mock_page_flipper)};
3961
3962 auto fb = output.fb_for(fake_bo);
3963@@ -207,8 +249,8 @@
3964 append_fb_id(fb_id);
3965
3966 mgm::RealKMSOutput output{
3967- mock_drm.fake_drm.fd(),
3968- mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
3969+ drm_fd,
3970+ mg::kms::get_connector(drm_fd, connector_ids[0]),
3971 mt::fake_shared(mock_page_flipper)};
3972
3973 auto fb = output.fb_for(fake_bo);
3974@@ -247,8 +289,8 @@
3975 append_fb_id(fb_id);
3976
3977 mgm::RealKMSOutput output{
3978- mock_drm.fake_drm.fd(),
3979- mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
3980+ drm_fd,
3981+ mg::kms::get_connector(drm_fd, connector_ids[0]),
3982 mt::fake_shared(mock_page_flipper)};
3983
3984 auto fb = output.fb_for(fake_bo);
3985@@ -270,8 +312,8 @@
3986 setup_outputs_connected_crtc();
3987
3988 mgm::RealKMSOutput output{
3989- mock_drm.fake_drm.fd(),
3990- mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
3991+ drm_fd,
3992+ mg::kms::get_connector(drm_fd, connector_ids[0]),
3993 mt::fake_shared(mock_page_flipper)};
3994
3995 EXPECT_CALL(mock_drm, drmModeSetCrtc(_, crtc_ids[0], 0, 0, 0, nullptr, 0, nullptr))
3996@@ -285,21 +327,30 @@
3997 {
3998 using namespace testing;
3999
4000- mtd::FakeDRMResources& resources(mock_drm.fake_drm);
4001 uint32_t const possible_crtcs_mask_empty{0x0};
4002
4003- resources.reset();
4004-
4005- resources.add_encoder(encoder_ids[0], invalid_id, possible_crtcs_mask_empty);
4006- resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_VGA,
4007- DRM_MODE_CONNECTED, encoder_ids[0],
4008- modes_empty, possible_encoder_ids1, geom::Size());
4009-
4010- resources.prepare();
4011+ mock_drm.reset(drm_device);
4012+
4013+ mock_drm.add_encoder(
4014+ drm_device,
4015+ encoder_ids[0],
4016+ invalid_id,
4017+ possible_crtcs_mask_empty);
4018+ mock_drm.add_connector(
4019+ drm_device,
4020+ connector_ids[0],
4021+ DRM_MODE_CONNECTOR_VGA,
4022+ DRM_MODE_CONNECTED,
4023+ encoder_ids[0],
4024+ modes_empty,
4025+ possible_encoder_ids1,
4026+ geom::Size());
4027+
4028+ mock_drm.prepare(drm_device);
4029
4030 mgm::RealKMSOutput output{
4031- mock_drm.fake_drm.fd(),
4032- mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
4033+ drm_fd,
4034+ mg::kms::get_connector(drm_fd, connector_ids[0]),
4035 mt::fake_shared(mock_page_flipper)};
4036
4037 EXPECT_CALL(mock_drm, drmModeSetCrtc(_, _, 0, 0, 0, nullptr, 0, nullptr))
4038@@ -319,8 +370,8 @@
4039 setup_outputs_connected_crtc();
4040
4041 mgm::RealKMSOutput output{
4042- mock_drm.fake_drm.fd(),
4043- mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
4044+ drm_fd,
4045+ mg::kms::get_connector(drm_fd, connector_ids[0]),
4046 mt::fake_shared(mock_page_flipper)};
4047
4048 auto fb = output.fb_for(fake_bo);
4049@@ -347,8 +398,8 @@
4050 setup_outputs_connected_crtc();
4051
4052 mgm::RealKMSOutput output{
4053- mock_drm.fake_drm.fd(),
4054- mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
4055+ drm_fd,
4056+ mg::kms::get_connector(drm_fd, connector_ids[0]),
4057 mt::fake_shared(mock_page_flipper)};
4058
4059 auto fb = output.fb_for(fake_bo);
4060@@ -376,8 +427,8 @@
4061 setup_outputs_connected_crtc();
4062
4063 mgm::RealKMSOutput output{
4064- mock_drm.fake_drm.fd(),
4065- mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
4066+ drm_fd,
4067+ mg::kms::get_connector(drm_fd, connector_ids[0]),
4068 mt::fake_shared(mock_page_flipper)};
4069
4070 auto fb = output.fb_for(fake_bo);
4071@@ -397,8 +448,8 @@
4072 setup_outputs_connected_crtc();
4073
4074 mgm::RealKMSOutput output{
4075- mock_drm.fake_drm.fd(),
4076- mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
4077+ drm_fd,
4078+ mg::kms::get_connector(drm_fd, connector_ids[0]),
4079 mt::fake_shared(mock_page_flipper)};
4080
4081 EXPECT_CALL(mock_drm, drmModeSetCrtc(_, crtc_ids[0], 0, 0, 0, nullptr, 0, nullptr))
4082@@ -419,13 +470,13 @@
4083 setup_outputs_connected_crtc();
4084
4085 mgm::RealKMSOutput output{
4086- mock_drm.fake_drm.fd(),
4087- mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
4088+ drm_fd,
4089+ mg::kms::get_connector(drm_fd, connector_ids[0]),
4090 mt::fake_shared(mock_page_flipper)};
4091
4092 mg::GammaCurves gamma{{1}, {2}, {3}};
4093
4094- EXPECT_CALL(mock_drm, drmModeCrtcSetGamma(mock_drm.fake_drm.fd(), crtc_ids[0],
4095+ EXPECT_CALL(mock_drm, drmModeCrtcSetGamma(drm_fd, crtc_ids[0],
4096 gamma.red.size(),
4097 const_cast<uint16_t*>(gamma.red.data()),
4098 const_cast<uint16_t*>(gamma.green.data()),
4099@@ -450,13 +501,13 @@
4100 setup_outputs_connected_crtc();
4101
4102 mgm::RealKMSOutput output{
4103- mock_drm.fake_drm.fd(),
4104- mg::kms::get_connector(mock_drm.fake_drm.fd(), connector_ids[0]),
4105+ drm_fd,
4106+ mg::kms::get_connector(drm_fd, connector_ids[0]),
4107 mt::fake_shared(mock_page_flipper)};
4108
4109 mg::GammaCurves gamma{{1}, {2}, {3}};
4110
4111- EXPECT_CALL(mock_drm, drmModeCrtcSetGamma(mock_drm.fake_drm.fd(), crtc_ids[0],
4112+ EXPECT_CALL(mock_drm, drmModeCrtcSetGamma(drm_fd, crtc_ids[0],
4113 gamma.red.size(),
4114 const_cast<uint16_t*>(gamma.red.data()),
4115 const_cast<uint16_t*>(gamma.green.data()),
4116
4117=== modified file 'tests/unit-tests/platforms/mesa/x11/test_guest_platform.cpp'
4118--- tests/unit-tests/platforms/mesa/x11/test_guest_platform.cpp 2017-03-23 17:18:29 +0000
4119+++ tests/unit-tests/platforms/mesa/x11/test_guest_platform.cpp 2017-03-31 06:57:08 +0000
4120@@ -63,7 +63,7 @@
4121 {
4122 using namespace ::testing;
4123
4124- EXPECT_CALL(mock_gbm, gbm_create_device(mock_drm.fake_drm.fd()))
4125+ EXPECT_CALL(mock_gbm, gbm_create_device(_))
4126 .WillRepeatedly(Return(nullptr));
4127
4128 EXPECT_THROW({ create_guest_platform(); }, std::exception);
4129
4130=== modified file 'tests/unit-tests/platforms/mesa/x11/test_platform.cpp'
4131--- tests/unit-tests/platforms/mesa/x11/test_platform.cpp 2017-01-18 02:29:37 +0000
4132+++ tests/unit-tests/platforms/mesa/x11/test_platform.cpp 2017-03-31 06:57:08 +0000
4133@@ -88,7 +88,7 @@
4134 {
4135 using namespace ::testing;
4136
4137- EXPECT_CALL(mock_gbm, gbm_create_device(mock_drm.fake_drm.fd()))
4138+ EXPECT_CALL(mock_gbm, gbm_create_device(_))
4139 .WillRepeatedly(Return(nullptr));
4140
4141 EXPECT_THROW({ create_platform(); }, std::exception);

Subscribers

People subscribed via source and target branches