Merge lp:~raof/mir/mesa-hybrid-output into lp:mir
- mesa-hybrid-output
- Merge into development-branch
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 |
Related bugs: |
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.
Description of the change
Mir CI Bot (mir-ci-bot) wrote : | # |
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4080
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4081
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
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.
Chris Halse Rogers (raof) wrote : | # |
vivid+overlay builds no longer try building mesa-kms, so that should work :)
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4082
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4083
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4083
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4084
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4085
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4086
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
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/
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?
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).
Cemil Azizoglu (cemil-azizoglu) wrote : | # |
Conflicts...
Also it won't build at the moment:
...
[ 45%] Linking CXX executable ../../.
//usr/lib/
//usr/lib/
//usr/lib/
...
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4087
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
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 ..."
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4088
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4089
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Alan Griffiths (alan-griffiths) wrote : | # |
+ private:
+ static mgm::GBMSurfaceUPtr create_
Don't we usually indent like this:
private:
static mgm::GBMSurfaceUPtr create_
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.
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).
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...
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!)
Michael Zanetti (mzanetti) wrote : | # |
Here's the complete log: http://
And here's the silo with this branch: https:/
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.
Michael Zanetti (mzanetti) wrote : | # |
Sorry, here's unity-system-
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.
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 :)
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4091
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4092
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
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(
future improvements/
+ 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/
+ static std::vector<
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
Preview Diff
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); |
FAILED: Continuous integration, rev:4077 /mir-jenkins. ubuntu. com/job/ mir-ci/ 3181/ /mir-jenkins. ubuntu. com/job/ build-mir/ 4275/console /mir-jenkins. ubuntu. com/job/ build-0- fetch/4363/ console /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= vivid+overlay/ 4352/console /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= xenial+ overlay/ 4352/console /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= zesty/4352/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= zesty/4302/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= xenial+ overlay/ 4302/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= zesty/4302/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= android, release= vivid+overlay/ 4302/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= android, release= vivid+overlay/ 4302/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= mesa,release= xenial+ overlay/ 4302/console
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /mir-jenkins. ubuntu. com/job/ mir-ci/ 3181/rebuild
https:/