Merge lp:~raof/mir/mesa-hybrid-cursor into lp:mir
- mesa-hybrid-cursor
- Merge into development-branch
Proposed by
Chris Halse Rogers
Status: | Superseded |
---|---|
Proposed branch: | lp:~raof/mir/mesa-hybrid-cursor |
Merge into: | lp:mir |
Diff against target: |
6238 lines (+2551/-1159) (has conflicts) 45 files modified
src/common/CMakeLists.txt (+2/-1) 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 (+7/-0) src/platforms/mesa/server/kms/cursor.cpp (+118/-35) src/platforms/mesa/server/kms/cursor.h (+22/-8) src/platforms/mesa/server/kms/display.cpp (+108/-43) src/platforms/mesa/server/kms/display.h (+3/-3) 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_display_configuration.h (+6/-3) src/platforms/mesa/server/kms/kms_output.h (+36/-1) src/platforms/mesa/server/kms/kms_output_container.h (+5/-2) src/platforms/mesa/server/kms/mutex.h (+104/-0) src/platforms/mesa/server/kms/platform.cpp (+34/-8) src/platforms/mesa/server/kms/platform.h (+1/-1) src/platforms/mesa/server/kms/real_kms_display_configuration.cpp (+92/-293) src/platforms/mesa/server/kms/real_kms_display_configuration.h (+6/-7) src/platforms/mesa/server/kms/real_kms_output.cpp (+271/-23) src/platforms/mesa/server/kms/real_kms_output.h (+12/-4) src/platforms/mesa/server/kms/real_kms_output_container.cpp (+58/-29) src/platforms/mesa/server/kms/real_kms_output_container.h (+11/-9) tests/acceptance-tests/test_client_input.cpp (+2/-2) tests/include/mir/test/doubles/mock_drm.h (+32/-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 (+156/-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/kms-utils/test_connector_utils.cpp (+74/-43) tests/unit-tests/platforms/mesa/kms/mock_kms_output.h (+6/-0) tests/unit-tests/platforms/mesa/kms/test_cursor.cpp (+131/-117) 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 (+361/-277) 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 (+10/-0) 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 (+134/-74) tests/unit-tests/platforms/mesa/x11/test_guest_platform.cpp (+1/-1) tests/unit-tests/platforms/mesa/x11/test_platform.cpp (+1/-1) Text conflict in tests/unit-tests/platforms/mesa/kms/test_guest_platform.cpp |
To merge this branch: | bzr merge lp:~raof/mir/mesa-hybrid-cursor |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mir development team | Pending | ||
Review via email: mp+320591@code.launchpad.net |
Commit message
mesa-kms: Support hardware cursors in hybrid setups.
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/common/CMakeLists.txt' | |||
2 | --- src/common/CMakeLists.txt 2017-02-15 14:45:41 +0000 | |||
3 | +++ src/common/CMakeLists.txt 2017-03-22 06:55:00 +0000 | |||
4 | @@ -20,7 +20,8 @@ | |||
5 | 20 | $<TARGET_OBJECTS:mirtime> | 20 | $<TARGET_OBJECTS:mirtime> |
6 | 21 | ${CMAKE_CURRENT_SOURCE_DIR}/output_type_names.cpp | 21 | ${CMAKE_CURRENT_SOURCE_DIR}/output_type_names.cpp |
7 | 22 | ${CMAKE_CURRENT_SOURCE_DIR}/log.cpp | 22 | ${CMAKE_CURRENT_SOURCE_DIR}/log.cpp |
9 | 23 | ${CMAKE_CURRENT_SOURCE_DIR}/libname.cpp ${PROJECT_SOURCE_DIR}/include/common/mir/libname.h | 23 | ${CMAKE_CURRENT_SOURCE_DIR}/libname.cpp |
10 | 24 | ${PROJECT_SOURCE_DIR}/include/common/mir/libname.h | ||
11 | 24 | ${PROJECT_SOURCE_DIR}/include/common/mir/posix_rw_mutex.h | 25 | ${PROJECT_SOURCE_DIR}/include/common/mir/posix_rw_mutex.h |
12 | 25 | posix_rw_mutex.cpp | 26 | posix_rw_mutex.cpp |
13 | 26 | edid.cpp | 27 | edid.cpp |
14 | 27 | 28 | ||
15 | === modified file 'src/platforms/mesa/server/display_helpers.cpp' | |||
16 | --- src/platforms/mesa/server/display_helpers.cpp 2017-03-16 07:37:11 +0000 | |||
17 | +++ src/platforms/mesa/server/display_helpers.cpp 2017-03-22 06:55:00 +0000 | |||
18 | @@ -20,9 +20,14 @@ | |||
19 | 20 | #include "drm_close_threadsafe.h" | 20 | #include "drm_close_threadsafe.h" |
20 | 21 | 21 | ||
21 | 22 | #include "kms-utils/drm_mode_resources.h" | 22 | #include "kms-utils/drm_mode_resources.h" |
22 | 23 | #include "mir/graphics/gl_config.h" | ||
23 | 24 | #include "mir/graphics/egl_error.h" | ||
24 | 23 | 25 | ||
25 | 24 | #include "mir/udev/wrapper.h" | 26 | #include "mir/udev/wrapper.h" |
26 | 25 | 27 | ||
27 | 28 | #define MIR_LOG_COMPONENT "mesa-kms" | ||
28 | 29 | #include "mir/log.h" | ||
29 | 30 | |||
30 | 26 | #include <boost/exception/errinfo_errno.hpp> | 31 | #include <boost/exception/errinfo_errno.hpp> |
31 | 27 | #include <boost/throw_exception.hpp> | 32 | #include <boost/throw_exception.hpp> |
32 | 28 | 33 | ||
33 | @@ -31,6 +36,7 @@ | |||
34 | 31 | #include <stdexcept> | 36 | #include <stdexcept> |
35 | 32 | #include <xf86drm.h> | 37 | #include <xf86drm.h> |
36 | 33 | #include <fcntl.h> | 38 | #include <fcntl.h> |
37 | 39 | #include <vector> | ||
38 | 34 | 40 | ||
39 | 35 | namespace mg = mir::graphics; | 41 | namespace mg = mir::graphics; |
40 | 36 | namespace mgm = mir::graphics::mesa; | 42 | namespace mgm = mir::graphics::mesa; |
41 | @@ -40,6 +46,69 @@ | |||
42 | 40 | * DRMHelper * | 46 | * DRMHelper * |
43 | 41 | *************/ | 47 | *************/ |
44 | 42 | 48 | ||
45 | 49 | std::vector<std::shared_ptr<mgmh::DRMHelper>> | ||
46 | 50 | mgmh::DRMHelper::open_all_devices(std::shared_ptr<mir::udev::Context> const& udev) | ||
47 | 51 | { | ||
48 | 52 | int tmp_fd = -1; | ||
49 | 53 | int error = ENODEV; //Default error is "there are no DRM devices" | ||
50 | 54 | |||
51 | 55 | mir::udev::Enumerator devices(udev); | ||
52 | 56 | devices.match_subsystem("drm"); | ||
53 | 57 | devices.match_sysname("card[0-9]"); | ||
54 | 58 | |||
55 | 59 | devices.scan_devices(); | ||
56 | 60 | |||
57 | 61 | std::vector<std::shared_ptr<DRMHelper>> opened_devices; | ||
58 | 62 | |||
59 | 63 | for(auto& device : devices) | ||
60 | 64 | { | ||
61 | 65 | // If directly opening the DRM device is good enough for X it's good enough for us! | ||
62 | 66 | tmp_fd = open(device.devnode(), O_RDWR | O_CLOEXEC); | ||
63 | 67 | if (tmp_fd < 0) | ||
64 | 68 | { | ||
65 | 69 | error = errno; | ||
66 | 70 | mir::log_warning( | ||
67 | 71 | "Failed to open DRM device node %s: %i (%s)", | ||
68 | 72 | device.devnode(), | ||
69 | 73 | error, | ||
70 | 74 | strerror(error)); | ||
71 | 75 | continue; | ||
72 | 76 | } | ||
73 | 77 | |||
74 | 78 | // Check that the drm device is usable by setting the interface version we use (1.4) | ||
75 | 79 | drmSetVersion sv; | ||
76 | 80 | sv.drm_di_major = 1; | ||
77 | 81 | sv.drm_di_minor = 4; | ||
78 | 82 | sv.drm_dd_major = -1; /* Don't care */ | ||
79 | 83 | sv.drm_dd_minor = -1; /* Don't care */ | ||
80 | 84 | |||
81 | 85 | if ((error = -drmSetInterfaceVersion(tmp_fd, &sv))) | ||
82 | 86 | { | ||
83 | 87 | close(tmp_fd); | ||
84 | 88 | mir::log_warning( | ||
85 | 89 | "Failed to set DRM interface version on device %s: %i (%s)", | ||
86 | 90 | device.devnode(), | ||
87 | 91 | error, | ||
88 | 92 | strerror(error)); | ||
89 | 93 | tmp_fd = -1; | ||
90 | 94 | continue; | ||
91 | 95 | } | ||
92 | 96 | |||
93 | 97 | // Can't use make_shared with the private constructor. | ||
94 | 98 | opened_devices.push_back(std::shared_ptr<DRMHelper>{new DRMHelper{tmp_fd}}); | ||
95 | 99 | mir::log_info("Using DRM device %s", device.devnode()); | ||
96 | 100 | tmp_fd = -1; | ||
97 | 101 | } | ||
98 | 102 | |||
99 | 103 | if (opened_devices.size() == 0) | ||
100 | 104 | { | ||
101 | 105 | BOOST_THROW_EXCEPTION(( | ||
102 | 106 | std::system_error{error, std::system_category(), "Error opening DRM device"})); | ||
103 | 107 | } | ||
104 | 108 | |||
105 | 109 | return opened_devices; | ||
106 | 110 | } | ||
107 | 111 | |||
108 | 43 | void mgmh::DRMHelper::setup(std::shared_ptr<mir::udev::Context> const& udev) | 112 | void mgmh::DRMHelper::setup(std::shared_ptr<mir::udev::Context> const& udev) |
109 | 44 | { | 113 | { |
110 | 45 | fd = open_drm_device(udev); | 114 | fd = open_drm_device(udev); |
111 | @@ -160,6 +229,12 @@ | |||
112 | 160 | } | 229 | } |
113 | 161 | } | 230 | } |
114 | 162 | 231 | ||
115 | 232 | mgmh::DRMHelper::DRMHelper(int fd) | ||
116 | 233 | : fd{fd}, | ||
117 | 234 | node_to_use{DRMNodeToUse::card} | ||
118 | 235 | { | ||
119 | 236 | } | ||
120 | 237 | |||
121 | 163 | int mgmh::DRMHelper::is_appropriate_device(std::shared_ptr<mir::udev::Context> const& udev, mir::udev::Device const& drm_device) | 238 | int mgmh::DRMHelper::is_appropriate_device(std::shared_ptr<mir::udev::Context> const& udev, mir::udev::Device const& drm_device) |
122 | 164 | { | 239 | { |
123 | 165 | mir::udev::Enumerator children(udev); | 240 | mir::udev::Enumerator children(udev); |
124 | @@ -285,11 +360,29 @@ | |||
125 | 285 | std::runtime_error("Failed to create GBM device")); | 360 | std::runtime_error("Failed to create GBM device")); |
126 | 286 | } | 361 | } |
127 | 287 | 362 | ||
129 | 288 | mgm::GBMSurfaceUPtr mgmh::GBMHelper::create_scanout_surface(uint32_t width, uint32_t height) | 363 | mgm::GBMSurfaceUPtr mgmh::GBMHelper::create_scanout_surface( |
130 | 364 | uint32_t width, | ||
131 | 365 | uint32_t height, | ||
132 | 366 | bool sharable) | ||
133 | 289 | { | 367 | { |
134 | 368 | auto format_flags = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT; | ||
135 | 369 | |||
136 | 370 | if (sharable) | ||
137 | 371 | { | ||
138 | 372 | #ifdef MIR_NO_HYBRID_SUPPORT | ||
139 | 373 | BOOST_THROW_EXCEPTION(( | ||
140 | 374 | std::runtime_error{ | ||
141 | 375 | "Mir built without hybrid support, but configuration requries hybrid outputs.\n" | ||
142 | 376 | "This will not work unless Mir is rebuilt against Mesa >= 11.0"} | ||
143 | 377 | )); | ||
144 | 378 | #else | ||
145 | 379 | format_flags |= GBM_BO_USE_LINEAR; | ||
146 | 380 | #endif | ||
147 | 381 | } | ||
148 | 382 | |||
149 | 290 | auto surface_raw = gbm_surface_create(device, width, height, | 383 | auto surface_raw = gbm_surface_create(device, width, height, |
150 | 291 | GBM_BO_FORMAT_XRGB8888, | 384 | GBM_BO_FORMAT_XRGB8888, |
152 | 292 | GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); | 385 | format_flags); |
153 | 293 | 386 | ||
154 | 294 | auto gbm_surface_deleter = [](gbm_surface *p) { if (p) gbm_surface_destroy(p); }; | 387 | auto gbm_surface_deleter = [](gbm_surface *p) { if (p) gbm_surface_destroy(p); }; |
155 | 295 | GBMSurfaceUPtr surface{surface_raw, gbm_surface_deleter}; | 388 | GBMSurfaceUPtr surface{surface_raw, gbm_surface_deleter}; |
156 | 296 | 389 | ||
157 | === modified file 'src/platforms/mesa/server/display_helpers.h' | |||
158 | --- src/platforms/mesa/server/display_helpers.h 2017-03-16 07:37:11 +0000 | |||
159 | +++ src/platforms/mesa/server/display_helpers.h 2017-03-22 06:55:00 +0000 | |||
160 | @@ -24,6 +24,7 @@ | |||
161 | 24 | 24 | ||
162 | 25 | #include <cstddef> | 25 | #include <cstddef> |
163 | 26 | #include <memory> | 26 | #include <memory> |
164 | 27 | #include <vector> | ||
165 | 27 | 28 | ||
166 | 28 | #pragma GCC diagnostic push | 29 | #pragma GCC diagnostic push |
167 | 29 | #pragma GCC diagnostic warning "-Wall" | 30 | #pragma GCC diagnostic warning "-Wall" |
168 | @@ -59,6 +60,9 @@ | |||
169 | 59 | DRMHelper(const DRMHelper &) = delete; | 60 | DRMHelper(const DRMHelper &) = delete; |
170 | 60 | DRMHelper& operator=(const DRMHelper&) = delete; | 61 | DRMHelper& operator=(const DRMHelper&) = delete; |
171 | 61 | 62 | ||
172 | 63 | static std::vector<std::shared_ptr<DRMHelper>> open_all_devices( | ||
173 | 64 | std::shared_ptr<mir::udev::Context> const& udev); | ||
174 | 65 | |||
175 | 62 | void setup(std::shared_ptr<mir::udev::Context> const& udev); | 66 | void setup(std::shared_ptr<mir::udev::Context> const& udev); |
176 | 63 | mir::Fd authenticated_fd(); | 67 | mir::Fd authenticated_fd(); |
177 | 64 | void auth_magic(drm_magic_t magic); | 68 | void auth_magic(drm_magic_t magic); |
178 | @@ -70,6 +74,8 @@ | |||
179 | 70 | DRMNodeToUse const node_to_use; | 74 | DRMNodeToUse const node_to_use; |
180 | 71 | 75 | ||
181 | 72 | private: | 76 | private: |
182 | 77 | DRMHelper(int fd); | ||
183 | 78 | |||
184 | 73 | // TODO: This herustic is temporary; should be replaced with | 79 | // TODO: This herustic is temporary; should be replaced with |
185 | 74 | // handling >1 DRM device. | 80 | // handling >1 DRM device. |
186 | 75 | int is_appropriate_device(std::shared_ptr<mir::udev::Context> const& udev, mir::udev::Device const& dev); | 81 | int is_appropriate_device(std::shared_ptr<mir::udev::Context> const& udev, mir::udev::Device const& dev); |
187 | @@ -90,7 +96,7 @@ | |||
188 | 90 | 96 | ||
189 | 91 | void setup(const DRMHelper& drm); | 97 | void setup(const DRMHelper& drm); |
190 | 92 | void setup(int drm_fd); | 98 | void setup(int drm_fd); |
192 | 93 | GBMSurfaceUPtr create_scanout_surface(uint32_t width, uint32_t height); | 99 | GBMSurfaceUPtr create_scanout_surface(uint32_t width, uint32_t height, bool sharable); |
193 | 94 | 100 | ||
194 | 95 | gbm_device* device; | 101 | gbm_device* device; |
195 | 96 | }; | 102 | }; |
196 | 97 | 103 | ||
197 | === modified file 'src/platforms/mesa/server/kms/CMakeLists.txt' | |||
198 | --- src/platforms/mesa/server/kms/CMakeLists.txt 2017-03-16 07:37:11 +0000 | |||
199 | +++ src/platforms/mesa/server/kms/CMakeLists.txt 2017-03-22 06:55:00 +0000 | |||
200 | @@ -13,6 +13,11 @@ | |||
201 | 13 | ${PROJECT_SOURCE_DIR}/include/client | 13 | ${PROJECT_SOURCE_DIR}/include/client |
202 | 14 | ) | 14 | ) |
203 | 15 | 15 | ||
204 | 16 | if (GBM_VERSION VERSION_LESS 11) | ||
205 | 17 | message(WARNING "Hybrid support requires libgbm from Mesa 11.0 or greater. Hybrid setups will not work") | ||
206 | 18 | add_definitions(-DMIR_NO_HYBRID_SUPPORT) | ||
207 | 19 | endif() | ||
208 | 20 | |||
209 | 16 | # gbm.h and drm.h have trailing commas at the end of enum definitions | 21 | # gbm.h and drm.h have trailing commas at the end of enum definitions |
210 | 17 | # This is valid C99, but g++ 4.4 flags it as an error with -pedantic | 22 | # This is valid C99, but g++ 4.4 flags it as an error with -pedantic |
211 | 18 | string(REPLACE "-pedantic" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) | 23 | string(REPLACE "-pedantic" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) |
212 | @@ -38,11 +43,13 @@ | |||
213 | 38 | kms_display_configuration.h | 43 | kms_display_configuration.h |
214 | 39 | real_kms_display_configuration.cpp | 44 | real_kms_display_configuration.cpp |
215 | 40 | kms_output.h | 45 | kms_output.h |
216 | 46 | real_kms_output.h | ||
217 | 41 | real_kms_output.cpp | 47 | real_kms_output.cpp |
218 | 42 | kms_output_container.h | 48 | kms_output_container.h |
219 | 43 | real_kms_output_container.cpp | 49 | real_kms_output_container.cpp |
220 | 44 | egl_helper.h | 50 | egl_helper.h |
221 | 45 | egl_helper.cpp | 51 | egl_helper.cpp |
222 | 52 | mutex.h | ||
223 | 46 | ) | 53 | ) |
224 | 47 | 54 | ||
225 | 48 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/symbols.map.in | 55 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/symbols.map.in |
226 | 49 | 56 | ||
227 | === modified file 'src/platforms/mesa/server/kms/cursor.cpp' | |||
228 | --- src/platforms/mesa/server/kms/cursor.cpp 2017-02-15 07:38:33 +0000 | |||
229 | +++ src/platforms/mesa/server/kms/cursor.cpp 2017-03-22 06:55:00 +0000 | |||
230 | @@ -88,34 +88,73 @@ | |||
231 | 88 | drmGetCap(fd, DRM_CAP_CURSOR_WIDTH, &width); | 88 | drmGetCap(fd, DRM_CAP_CURSOR_WIDTH, &width); |
232 | 89 | return int(width); | 89 | return int(width); |
233 | 90 | } | 90 | } |
243 | 91 | } | 91 | |
244 | 92 | 92 | gbm_device* gbm_create_device_checked(int fd) | |
245 | 93 | mgm::Cursor::GBMBOWrapper::GBMBOWrapper(gbm_device* gbm) : | 93 | { |
246 | 94 | buffer(gbm_bo_create( | 94 | auto device = gbm_create_device(fd); |
247 | 95 | gbm, | 95 | if (!device) |
248 | 96 | get_drm_cursor_width(gbm_device_get_fd(gbm)), | 96 | { |
249 | 97 | get_drm_cursor_height(gbm_device_get_fd(gbm)), | 97 | BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create gbm device")); |
250 | 98 | GBM_FORMAT_ARGB8888, | 98 | } |
251 | 99 | GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) | 99 | return device; |
252 | 100 | } | ||
253 | 101 | } | ||
254 | 102 | |||
255 | 103 | mgm::Cursor::GBMBOWrapper::GBMBOWrapper(int fd) : | ||
256 | 104 | device{gbm_create_device_checked(fd)}, | ||
257 | 105 | buffer{ | ||
258 | 106 | gbm_bo_create( | ||
259 | 107 | device, | ||
260 | 108 | get_drm_cursor_width(fd), | ||
261 | 109 | get_drm_cursor_height(fd), | ||
262 | 110 | GBM_FORMAT_ARGB8888, | ||
263 | 111 | GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)} | ||
264 | 100 | { | 112 | { |
265 | 101 | if (!buffer) BOOST_THROW_EXCEPTION(std::runtime_error("failed to create gbm buffer")); | 113 | if (!buffer) BOOST_THROW_EXCEPTION(std::runtime_error("failed to create gbm buffer")); |
266 | 102 | } | 114 | } |
267 | 103 | 115 | ||
270 | 104 | inline mgm::Cursor::GBMBOWrapper::operator gbm_bo*() { return buffer; } | 116 | inline mgm::Cursor::GBMBOWrapper::operator gbm_bo*() |
271 | 105 | inline mgm::Cursor::GBMBOWrapper::~GBMBOWrapper() { gbm_bo_destroy(buffer); } | 117 | { |
272 | 118 | return buffer; | ||
273 | 119 | } | ||
274 | 120 | |||
275 | 121 | inline mgm::Cursor::GBMBOWrapper::~GBMBOWrapper() | ||
276 | 122 | { | ||
277 | 123 | if (device) | ||
278 | 124 | gbm_device_destroy(device); | ||
279 | 125 | if (buffer) | ||
280 | 126 | gbm_bo_destroy(buffer); | ||
281 | 127 | } | ||
282 | 128 | |||
283 | 129 | mgm::Cursor::GBMBOWrapper::GBMBOWrapper(GBMBOWrapper&& from) | ||
284 | 130 | : device{from.device}, | ||
285 | 131 | buffer{from.buffer} | ||
286 | 132 | { | ||
287 | 133 | const_cast<gbm_bo*&>(from.buffer) = nullptr; | ||
288 | 134 | const_cast<gbm_device*&>(from.device) = nullptr; | ||
289 | 135 | } | ||
290 | 106 | 136 | ||
291 | 107 | mgm::Cursor::Cursor( | 137 | mgm::Cursor::Cursor( |
292 | 108 | gbm_device* gbm, | ||
293 | 109 | KMSOutputContainer& output_container, | 138 | KMSOutputContainer& output_container, |
294 | 110 | std::shared_ptr<CurrentConfiguration> const& current_configuration) : | 139 | std::shared_ptr<CurrentConfiguration> const& current_configuration) : |
295 | 111 | output_container(output_container), | 140 | output_container(output_container), |
296 | 112 | current_position(), | 141 | current_position(), |
297 | 113 | last_set_failed(false), | 142 | last_set_failed(false), |
301 | 114 | buffer(gbm), | 143 | min_buffer_width{std::numeric_limits<uint32_t>::max()}, |
302 | 115 | buffer_width(gbm_bo_get_width(buffer)), | 144 | min_buffer_height{std::numeric_limits<uint32_t>::max()}, |
300 | 116 | buffer_height(gbm_bo_get_height(buffer)), | ||
303 | 117 | current_configuration(current_configuration) | 145 | current_configuration(current_configuration) |
304 | 118 | { | 146 | { |
305 | 147 | // Generate the buffers for the initial configuration. | ||
306 | 148 | current_configuration->with_current_configuration_do( | ||
307 | 149 | [this](KMSDisplayConfiguration const& kms_conf) | ||
308 | 150 | { | ||
309 | 151 | kms_conf.for_each_output( | ||
310 | 152 | [this, &kms_conf](auto const& output) | ||
311 | 153 | { | ||
312 | 154 | buffer_for_output(*kms_conf.get_output_for(output.id)); | ||
313 | 155 | }); | ||
314 | 156 | }); | ||
315 | 157 | |||
316 | 119 | hide(); | 158 | hide(); |
317 | 120 | if (last_set_failed) | 159 | if (last_set_failed) |
318 | 121 | throw std::runtime_error("Initial KMS cursor set failed"); | 160 | throw std::runtime_error("Initial KMS cursor set failed"); |
319 | @@ -126,7 +165,11 @@ | |||
320 | 126 | hide(); | 165 | hide(); |
321 | 127 | } | 166 | } |
322 | 128 | 167 | ||
324 | 129 | void mgm::Cursor::write_buffer_data_locked(std::lock_guard<std::mutex> const&, void const* data, size_t count) | 168 | void mgm::Cursor::write_buffer_data_locked( |
325 | 169 | std::lock_guard<std::mutex> const&, | ||
326 | 170 | gbm_bo* buffer, | ||
327 | 171 | void const* data, | ||
328 | 172 | size_t count) | ||
329 | 130 | { | 173 | { |
330 | 131 | if (auto result = gbm_bo_write(buffer, data, count)) | 174 | if (auto result = gbm_bo_write(buffer, data, count)) |
331 | 132 | { | 175 | { |
332 | @@ -136,20 +179,23 @@ | |||
333 | 136 | } | 179 | } |
334 | 137 | } | 180 | } |
335 | 138 | 181 | ||
337 | 139 | void mgm::Cursor::pad_and_write_image_data_locked(std::lock_guard<std::mutex> const& lg, CursorImage const& image) | 182 | void mgm::Cursor::pad_and_write_image_data_locked( |
338 | 183 | std::lock_guard<std::mutex> const& lg, | ||
339 | 184 | gbm_bo* buffer, | ||
340 | 185 | CursorImage const& image) | ||
341 | 140 | { | 186 | { |
342 | 141 | auto image_argb = static_cast<uint8_t const*>(image.as_argb_8888()); | 187 | auto image_argb = static_cast<uint8_t const*>(image.as_argb_8888()); |
343 | 142 | auto image_width = image.size().width.as_uint32_t(); | 188 | auto image_width = image.size().width.as_uint32_t(); |
344 | 143 | auto image_height = image.size().height.as_uint32_t(); | 189 | auto image_height = image.size().height.as_uint32_t(); |
345 | 144 | auto image_stride = image_width * 4; | 190 | auto image_stride = image_width * 4; |
346 | 145 | 191 | ||
348 | 146 | if (image_width > buffer_width || image_height > buffer_height) | 192 | if (image_width > min_buffer_width || image_height > min_buffer_height) |
349 | 147 | { | 193 | { |
350 | 148 | BOOST_THROW_EXCEPTION(std::logic_error("Image is too big for GBM cursor buffer")); | 194 | BOOST_THROW_EXCEPTION(std::logic_error("Image is too big for GBM cursor buffer")); |
351 | 149 | } | 195 | } |
352 | 150 | 196 | ||
353 | 151 | size_t buffer_stride = gbm_bo_get_stride(buffer); // in bytes | 197 | size_t buffer_stride = gbm_bo_get_stride(buffer); // in bytes |
355 | 152 | size_t padded_size = buffer_stride * buffer_height; | 198 | size_t padded_size = buffer_stride * gbm_bo_get_height(buffer); |
356 | 153 | auto padded = std::unique_ptr<uint8_t[]>(new uint8_t[padded_size]); | 199 | auto padded = std::unique_ptr<uint8_t[]>(new uint8_t[padded_size]); |
357 | 154 | size_t rhs_padding = buffer_stride - image_stride; | 200 | size_t rhs_padding = buffer_stride - image_stride; |
358 | 155 | 201 | ||
359 | @@ -164,9 +210,9 @@ | |||
360 | 164 | src += image_stride; | 210 | src += image_stride; |
361 | 165 | } | 211 | } |
362 | 166 | 212 | ||
364 | 167 | memset(dest, 0, buffer_stride * (buffer_height - image_height)); | 213 | memset(dest, 0, buffer_stride * (gbm_bo_get_height(buffer) - image_height)); |
365 | 168 | 214 | ||
367 | 169 | write_buffer_data_locked(lg, &padded[0], padded_size); | 215 | write_buffer_data_locked(lg, buffer, &padded[0], padded_size); |
368 | 170 | } | 216 | } |
369 | 171 | 217 | ||
370 | 172 | void mgm::Cursor::show() | 218 | void mgm::Cursor::show() |
371 | @@ -186,14 +232,20 @@ | |||
372 | 186 | 232 | ||
373 | 187 | auto const& size = cursor_image.size(); | 233 | auto const& size = cursor_image.size(); |
374 | 188 | 234 | ||
383 | 189 | if (size != geometry::Size{buffer_width, buffer_height}) | 235 | { |
384 | 190 | { | 236 | auto locked_buffers = buffers.lock(); |
385 | 191 | pad_and_write_image_data_locked(lg, cursor_image); | 237 | for (auto& pair : *locked_buffers) |
386 | 192 | } | 238 | { |
387 | 193 | else | 239 | auto& buffer = pair.second; |
388 | 194 | { | 240 | if (size != geometry::Size{gbm_bo_get_width(buffer), gbm_bo_get_height(buffer)}) |
389 | 195 | auto const count = size.width.as_uint32_t() * size.height.as_uint32_t() * sizeof(uint32_t); | 241 | { |
390 | 196 | write_buffer_data_locked(lg, cursor_image.as_argb_8888(), count); | 242 | pad_and_write_image_data_locked(lg, buffer, cursor_image); |
391 | 243 | } else | ||
392 | 244 | { | ||
393 | 245 | auto const count = size.width.as_uint32_t() * size.height.as_uint32_t() * sizeof(uint32_t); | ||
394 | 246 | write_buffer_data_locked(lg, buffer, cursor_image.as_argb_8888(), count); | ||
395 | 247 | } | ||
396 | 248 | } | ||
397 | 197 | } | 249 | } |
398 | 198 | hotspot = cursor_image.hotspot(); | 250 | hotspot = cursor_image.hotspot(); |
399 | 199 | 251 | ||
400 | @@ -217,9 +269,9 @@ | |||
401 | 217 | void mir::graphics::mesa::Cursor::clear(std::lock_guard<std::mutex> const&) | 269 | void mir::graphics::mesa::Cursor::clear(std::lock_guard<std::mutex> const&) |
402 | 218 | { | 270 | { |
403 | 219 | last_set_failed = false; | 271 | last_set_failed = false; |
405 | 220 | output_container.for_each_output([&](KMSOutput& output) | 272 | output_container.for_each_output([&](std::shared_ptr<KMSOutput> const& output) |
406 | 221 | { | 273 | { |
408 | 222 | if (!output.clear_cursor()) | 274 | if (!output->clear_cursor()) |
409 | 223 | last_set_failed = true; | 275 | last_set_failed = true; |
410 | 224 | }); | 276 | }); |
411 | 225 | } | 277 | } |
412 | @@ -240,14 +292,13 @@ | |||
413 | 240 | std::function<void(KMSOutput&, geom::Rectangle const&, MirOrientation orientation)> const& f) | 292 | std::function<void(KMSOutput&, geom::Rectangle const&, MirOrientation orientation)> const& f) |
414 | 241 | { | 293 | { |
415 | 242 | current_configuration->with_current_configuration_do( | 294 | current_configuration->with_current_configuration_do( |
417 | 243 | [this,&f](KMSDisplayConfiguration const& kms_conf) | 295 | [&f](KMSDisplayConfiguration const& kms_conf) |
418 | 244 | { | 296 | { |
419 | 245 | kms_conf.for_each_output([&](DisplayConfigurationOutput const& conf_output) | 297 | kms_conf.for_each_output([&](DisplayConfigurationOutput const& conf_output) |
420 | 246 | { | 298 | { |
421 | 247 | if (conf_output.used) | 299 | if (conf_output.used) |
422 | 248 | { | 300 | { |
425 | 249 | uint32_t const connector_id = kms_conf.get_kms_connector_id(conf_output.id); | 301 | auto output = kms_conf.get_output_for(conf_output.id); |
424 | 250 | auto output = output_container.get_kms_output_for(connector_id); | ||
426 | 251 | 302 | ||
427 | 252 | f(*output, conf_output.extents(), conf_output.orientation); | 303 | f(*output, conf_output.extents(), conf_output.orientation); |
428 | 253 | } | 304 | } |
429 | @@ -289,7 +340,7 @@ | |||
430 | 289 | output.move_cursor(geom::Point{} + dp - hotspot); | 340 | output.move_cursor(geom::Point{} + dp - hotspot); |
431 | 290 | if (force_state || !output.has_cursor()) // TODO - or if orientation had changed - then set buffer.. | 341 | if (force_state || !output.has_cursor()) // TODO - or if orientation had changed - then set buffer.. |
432 | 291 | { | 342 | { |
434 | 292 | if (!output.set_cursor(buffer) || !output.has_cursor()) | 343 | if (!output.set_cursor(buffer_for_output(output)) || !output.has_cursor()) |
435 | 293 | set_on_all_outputs = false; | 344 | set_on_all_outputs = false; |
436 | 294 | } | 345 | } |
437 | 295 | } | 346 | } |
438 | @@ -304,3 +355,35 @@ | |||
439 | 304 | 355 | ||
440 | 305 | last_set_failed = !set_on_all_outputs; | 356 | last_set_failed = !set_on_all_outputs; |
441 | 306 | } | 357 | } |
442 | 358 | |||
443 | 359 | gbm_bo* mgm::Cursor::buffer_for_output(KMSOutput const& output) | ||
444 | 360 | { | ||
445 | 361 | auto locked_buffers = buffers.lock(); | ||
446 | 362 | |||
447 | 363 | auto buffer_it = std::find_if( | ||
448 | 364 | locked_buffers->begin(), | ||
449 | 365 | locked_buffers->end(), | ||
450 | 366 | [&output](auto const& candidate) | ||
451 | 367 | { | ||
452 | 368 | return candidate.first == output.drm_fd(); | ||
453 | 369 | }); | ||
454 | 370 | |||
455 | 371 | if (buffer_it != locked_buffers->end()) | ||
456 | 372 | { | ||
457 | 373 | return buffer_it->second; | ||
458 | 374 | } | ||
459 | 375 | |||
460 | 376 | locked_buffers->push_back(std::make_pair(output.drm_fd(), GBMBOWrapper(output.drm_fd()))); | ||
461 | 377 | |||
462 | 378 | gbm_bo* bo = locked_buffers->back().second; | ||
463 | 379 | if (gbm_bo_get_width(bo) < min_buffer_width) | ||
464 | 380 | { | ||
465 | 381 | min_buffer_width = gbm_bo_get_width(bo); | ||
466 | 382 | } | ||
467 | 383 | if (gbm_bo_get_height(bo) < min_buffer_height) | ||
468 | 384 | { | ||
469 | 385 | min_buffer_height = gbm_bo_get_height(bo); | ||
470 | 386 | } | ||
471 | 387 | |||
472 | 388 | return bo; | ||
473 | 389 | } | ||
474 | 307 | 390 | ||
475 | === modified file 'src/platforms/mesa/server/kms/cursor.h' | |||
476 | --- src/platforms/mesa/server/kms/cursor.h 2017-02-15 07:38:33 +0000 | |||
477 | +++ src/platforms/mesa/server/kms/cursor.h 2017-03-22 06:55:00 +0000 | |||
478 | @@ -25,11 +25,13 @@ | |||
479 | 25 | #include "mir/geometry/displacement.h" | 25 | #include "mir/geometry/displacement.h" |
480 | 26 | 26 | ||
481 | 27 | #include "mir_toolkit/common.h" | 27 | #include "mir_toolkit/common.h" |
482 | 28 | #include "mutex.h" | ||
483 | 28 | 29 | ||
484 | 29 | #include <gbm.h> | 30 | #include <gbm.h> |
485 | 30 | 31 | ||
486 | 31 | #include <memory> | 32 | #include <memory> |
487 | 32 | #include <mutex> | 33 | #include <mutex> |
488 | 34 | #include <vector> | ||
489 | 33 | 35 | ||
490 | 34 | namespace mir | 36 | namespace mir |
491 | 35 | { | 37 | { |
492 | @@ -66,7 +68,6 @@ | |||
493 | 66 | { | 68 | { |
494 | 67 | public: | 69 | public: |
495 | 68 | Cursor( | 70 | Cursor( |
496 | 69 | gbm_device* device, | ||
497 | 70 | KMSOutputContainer& output_container, | 71 | KMSOutputContainer& output_container, |
498 | 71 | std::shared_ptr<CurrentConfiguration> const& current_configuration); | 72 | std::shared_ptr<CurrentConfiguration> const& current_configuration); |
499 | 72 | 73 | ||
500 | @@ -86,9 +87,18 @@ | |||
501 | 86 | void for_each_used_output(std::function<void(KMSOutput&, geometry::Rectangle const&, MirOrientation orientation)> const& f); | 87 | void for_each_used_output(std::function<void(KMSOutput&, geometry::Rectangle const&, MirOrientation orientation)> const& f); |
502 | 87 | void place_cursor_at(geometry::Point position, ForceCursorState force_state); | 88 | void place_cursor_at(geometry::Point position, ForceCursorState force_state); |
503 | 88 | void place_cursor_at_locked(std::lock_guard<std::mutex> const&, geometry::Point position, ForceCursorState force_state); | 89 | void place_cursor_at_locked(std::lock_guard<std::mutex> const&, geometry::Point position, ForceCursorState force_state); |
506 | 89 | void write_buffer_data_locked(std::lock_guard<std::mutex> const&, void const* data, size_t count); | 90 | void write_buffer_data_locked( |
507 | 90 | void pad_and_write_image_data_locked(std::lock_guard<std::mutex> const&, CursorImage const& image); | 91 | std::lock_guard<std::mutex> const&, |
508 | 92 | gbm_bo* buffer, | ||
509 | 93 | void const* data, | ||
510 | 94 | size_t count); | ||
511 | 95 | void pad_and_write_image_data_locked( | ||
512 | 96 | std::lock_guard<std::mutex> const&, | ||
513 | 97 | gbm_bo* buffer, | ||
514 | 98 | CursorImage const& image); | ||
515 | 91 | void clear(std::lock_guard<std::mutex> const&); | 99 | void clear(std::lock_guard<std::mutex> const&); |
516 | 100 | |||
517 | 101 | gbm_bo* buffer_for_output(KMSOutput const& output); | ||
518 | 92 | 102 | ||
519 | 93 | std::mutex guard; | 103 | std::mutex guard; |
520 | 94 | 104 | ||
521 | @@ -101,17 +111,21 @@ | |||
522 | 101 | 111 | ||
523 | 102 | struct GBMBOWrapper | 112 | struct GBMBOWrapper |
524 | 103 | { | 113 | { |
526 | 104 | GBMBOWrapper(gbm_device* gbm); | 114 | GBMBOWrapper(int fd); |
527 | 105 | operator gbm_bo*(); | 115 | operator gbm_bo*(); |
528 | 106 | ~GBMBOWrapper(); | 116 | ~GBMBOWrapper(); |
529 | 117 | |||
530 | 118 | GBMBOWrapper(GBMBOWrapper&& from); | ||
531 | 107 | private: | 119 | private: |
533 | 108 | gbm_bo* buffer; | 120 | gbm_device* const device; |
534 | 121 | gbm_bo* const buffer; | ||
535 | 109 | GBMBOWrapper(GBMBOWrapper const&) = delete; | 122 | GBMBOWrapper(GBMBOWrapper const&) = delete; |
536 | 110 | GBMBOWrapper& operator=(GBMBOWrapper const&) = delete; | 123 | GBMBOWrapper& operator=(GBMBOWrapper const&) = delete; |
538 | 111 | } buffer; | 124 | }; |
539 | 125 | Mutex<std::vector<std::pair<int, GBMBOWrapper>>> buffers; | ||
540 | 112 | 126 | ||
543 | 113 | uint32_t buffer_width; | 127 | uint32_t min_buffer_width; |
544 | 114 | uint32_t buffer_height; | 128 | uint32_t min_buffer_height; |
545 | 115 | 129 | ||
546 | 116 | std::shared_ptr<CurrentConfiguration> const current_configuration; | 130 | std::shared_ptr<CurrentConfiguration> const current_configuration; |
547 | 117 | }; | 131 | }; |
548 | 118 | 132 | ||
549 | === modified file 'src/platforms/mesa/server/kms/display.cpp' | |||
550 | --- src/platforms/mesa/server/kms/display.cpp 2017-03-13 08:12:52 +0000 | |||
551 | +++ src/platforms/mesa/server/kms/display.cpp 2017-03-22 06:55:00 +0000 | |||
552 | @@ -39,6 +39,7 @@ | |||
553 | 39 | 39 | ||
554 | 40 | #include <stdexcept> | 40 | #include <stdexcept> |
555 | 41 | #include <algorithm> | 41 | #include <algorithm> |
556 | 42 | #include <unordered_map> | ||
557 | 42 | 43 | ||
558 | 43 | namespace mgm = mir::graphics::mesa; | 44 | namespace mgm = mir::graphics::mesa; |
559 | 44 | namespace mg = mir::graphics; | 45 | namespace mg = mir::graphics; |
560 | @@ -78,24 +79,48 @@ | |||
561 | 78 | mgm::helpers::EGLHelper egl; | 79 | mgm::helpers::EGLHelper egl; |
562 | 79 | }; | 80 | }; |
563 | 80 | 81 | ||
567 | 81 | } | 82 | std::vector<int> drm_fds_from_drm_helpers( |
568 | 82 | 83 | std::vector<std::shared_ptr<mgm::helpers::DRMHelper>> const& helpers) | |
569 | 83 | mgm::Display::Display(std::shared_ptr<helpers::DRMHelper> const& drm, | 84 | { |
570 | 85 | std::vector<int> fds; | ||
571 | 86 | for (auto const& helper: helpers) | ||
572 | 87 | { | ||
573 | 88 | fds.push_back(helper->fd); | ||
574 | 89 | } | ||
575 | 90 | return fds; | ||
576 | 91 | } | ||
577 | 92 | |||
578 | 93 | } | ||
579 | 94 | |||
580 | 95 | mgm::Display::Display(std::vector<std::shared_ptr<helpers::DRMHelper>> const& drm, | ||
581 | 84 | std::shared_ptr<helpers::GBMHelper> const& gbm, | 96 | std::shared_ptr<helpers::GBMHelper> const& gbm, |
582 | 85 | std::shared_ptr<VirtualTerminal> const& vt, | 97 | std::shared_ptr<VirtualTerminal> const& vt, |
583 | 86 | mgm::BypassOption bypass_option, | 98 | mgm::BypassOption bypass_option, |
584 | 87 | std::shared_ptr<DisplayConfigurationPolicy> const& initial_conf_policy, | 99 | std::shared_ptr<DisplayConfigurationPolicy> const& initial_conf_policy, |
585 | 88 | std::shared_ptr<GLConfig> const& gl_config, | 100 | std::shared_ptr<GLConfig> const& gl_config, |
586 | 89 | std::shared_ptr<DisplayReport> const& listener) | 101 | std::shared_ptr<DisplayReport> const& listener) |
588 | 90 | : drm(drm), | 102 | : drm{drm}, |
589 | 91 | gbm(gbm), | 103 | gbm(gbm), |
590 | 92 | vt(vt), | 104 | vt(vt), |
591 | 93 | listener(listener), | 105 | listener(listener), |
592 | 94 | monitor(mir::udev::Context()), | 106 | monitor(mir::udev::Context()), |
593 | 95 | shared_egl{*gl_config}, | 107 | shared_egl{*gl_config}, |
597 | 96 | output_container{drm->fd, | 108 | output_container{ |
598 | 97 | std::make_shared<KMSPageFlipper>(drm->fd, listener)}, | 109 | std::make_shared<RealKMSOutputContainer>( |
599 | 98 | current_display_configuration{drm->fd}, | 110 | drm_fds_from_drm_helpers(drm), |
600 | 111 | [ | ||
601 | 112 | listener, | ||
602 | 113 | flippers = std::unordered_map<int, std::shared_ptr<KMSPageFlipper>>{} | ||
603 | 114 | ](int drm_fd) mutable | ||
604 | 115 | { | ||
605 | 116 | auto& flipper = flippers[drm_fd]; | ||
606 | 117 | if (!flipper) | ||
607 | 118 | { | ||
608 | 119 | flipper = std::make_shared<KMSPageFlipper>(drm_fd, listener); | ||
609 | 120 | } | ||
610 | 121 | return flipper; | ||
611 | 122 | })}, | ||
612 | 123 | current_display_configuration{output_container}, | ||
613 | 99 | dirty_configuration{false}, | 124 | dirty_configuration{false}, |
614 | 100 | bypass_option(bypass_option), | 125 | bypass_option(bypass_option), |
615 | 101 | gl_config{gl_config} | 126 | gl_config{gl_config} |
616 | @@ -193,7 +218,8 @@ | |||
617 | 193 | try | 218 | try |
618 | 194 | { | 219 | { |
619 | 195 | if (auto c = cursor.lock()) c->suspend(); | 220 | if (auto c = cursor.lock()) c->suspend(); |
621 | 196 | drm->drop_master(); | 221 | for (auto& helper : drm) |
622 | 222 | helper->drop_master(); | ||
623 | 197 | } | 223 | } |
624 | 198 | catch(std::runtime_error const& e) | 224 | catch(std::runtime_error const& e) |
625 | 199 | { | 225 | { |
626 | @@ -206,7 +232,8 @@ | |||
627 | 206 | { | 232 | { |
628 | 207 | try | 233 | try |
629 | 208 | { | 234 | { |
631 | 209 | drm->set_master(); | 235 | for (auto& helper : drm) |
632 | 236 | helper->set_master(); | ||
633 | 210 | } | 237 | } |
634 | 211 | catch(std::runtime_error const& e) | 238 | catch(std::runtime_error const& e) |
635 | 212 | { | 239 | { |
636 | @@ -260,9 +287,9 @@ | |||
637 | 260 | 287 | ||
638 | 261 | try | 288 | try |
639 | 262 | { | 289 | { |
643 | 263 | locked_cursor = std::make_shared<Cursor>(gbm->device, | 290 | locked_cursor = std::make_shared<Cursor>( |
644 | 264 | output_container, | 291 | *output_container, |
645 | 265 | std::make_shared<KMSCurrentConfiguration>(*this)); | 292 | std::make_shared<KMSCurrentConfiguration>(*this)); |
646 | 266 | } | 293 | } |
647 | 267 | catch (std::runtime_error const&) | 294 | catch (std::runtime_error const&) |
648 | 268 | { | 295 | { |
649 | @@ -287,8 +314,7 @@ | |||
650 | 287 | if (conf_output.connected && | 314 | if (conf_output.connected && |
651 | 288 | (!conf_output.used || (conf_output.power_mode != mir_power_mode_on))) | 315 | (!conf_output.used || (conf_output.power_mode != mir_power_mode_on))) |
652 | 289 | { | 316 | { |
655 | 290 | uint32_t const connector_id = current_display_configuration.get_kms_connector_id(conf_output.id); | 317 | auto kms_output = current_display_configuration.get_output_for(conf_output.id); |
654 | 291 | auto kms_output = output_container.get_kms_output_for(connector_id); | ||
656 | 292 | 318 | ||
657 | 293 | kms_output->clear_crtc(); | 319 | kms_output->clear_crtc(); |
658 | 294 | kms_output->set_power_mode(conf_output.power_mode); | 320 | kms_output->set_power_mode(conf_output.power_mode); |
659 | @@ -327,10 +353,40 @@ | |||
660 | 327 | 353 | ||
661 | 328 | mg::Frame mgm::Display::last_frame_on(unsigned output_id) const | 354 | mg::Frame mgm::Display::last_frame_on(unsigned output_id) const |
662 | 329 | { | 355 | { |
664 | 330 | auto output = output_container.get_kms_output_for(output_id); | 356 | auto output = current_display_configuration.get_output_for( |
665 | 357 | DisplayConfigurationOutputId{static_cast<int>(output_id)}); | ||
666 | 331 | return output->last_frame(); | 358 | return output->last_frame(); |
667 | 332 | } | 359 | } |
668 | 333 | 360 | ||
669 | 361 | namespace | ||
670 | 362 | { | ||
671 | 363 | /* | ||
672 | 364 | * Add output to the grouping, maintaining the invariant that each vector of outputs | ||
673 | 365 | * is a single GPU memory domain. | ||
674 | 366 | */ | ||
675 | 367 | void add_to_drm_device_group( | ||
676 | 368 | std::vector<std::vector<std::shared_ptr<mgm::KMSOutput>>>& grouping, | ||
677 | 369 | std::shared_ptr<mgm::KMSOutput>&& output) | ||
678 | 370 | { | ||
679 | 371 | for (auto &group : grouping) | ||
680 | 372 | { | ||
681 | 373 | /* | ||
682 | 374 | * We could be smarter about this, but being on the same DRM device is guaranteed | ||
683 | 375 | * to be in the same GPU memory domain :). | ||
684 | 376 | */ | ||
685 | 377 | if (group.front()->drm_fd() == output->drm_fd()) | ||
686 | 378 | { | ||
687 | 379 | group.push_back(std::move(output)); | ||
688 | 380 | break; | ||
689 | 381 | } | ||
690 | 382 | } | ||
691 | 383 | if (output) | ||
692 | 384 | { | ||
693 | 385 | grouping.push_back(std::vector<std::shared_ptr<mgm::KMSOutput>>{std::move(output)}); | ||
694 | 386 | } | ||
695 | 387 | } | ||
696 | 388 | } | ||
697 | 389 | |||
698 | 334 | void mgm::Display::configure_locked( | 390 | void mgm::Display::configure_locked( |
699 | 335 | mgm::RealKMSDisplayConfiguration const& kms_conf, | 391 | mgm::RealKMSDisplayConfiguration const& kms_conf, |
700 | 336 | std::lock_guard<std::mutex> const&) | 392 | std::lock_guard<std::mutex> const&) |
701 | @@ -359,8 +415,7 @@ | |||
702 | 359 | kms_conf.for_each_output( | 415 | kms_conf.for_each_output( |
703 | 360 | [&](DisplayConfigurationOutput const& conf_output) | 416 | [&](DisplayConfigurationOutput const& conf_output) |
704 | 361 | { | 417 | { |
707 | 362 | uint32_t const connector_id = current_display_configuration.get_kms_connector_id(conf_output.id); | 418 | auto kms_output = current_display_configuration.get_output_for(conf_output.id); |
706 | 363 | auto kms_output = output_container.get_kms_output_for(connector_id); | ||
708 | 364 | kms_output->clear_cursor(); | 419 | kms_output->clear_cursor(); |
709 | 365 | kms_output->reset(); | 420 | kms_output->reset(); |
710 | 366 | }); | 421 | }); |
711 | @@ -374,14 +429,14 @@ | |||
712 | 374 | [&](OverlappingOutputGroup const& group) | 429 | [&](OverlappingOutputGroup const& group) |
713 | 375 | { | 430 | { |
714 | 376 | auto bounding_rect = group.bounding_rectangle(); | 431 | auto bounding_rect = group.bounding_rectangle(); |
716 | 377 | std::vector<std::shared_ptr<KMSOutput>> kms_outputs; | 432 | // Each vector<KMSOutput> is a single GPU memory domain |
717 | 433 | std::vector<std::vector<std::shared_ptr<KMSOutput>>> kms_output_groups; | ||
718 | 378 | MirOrientation orientation = mir_orientation_normal; | 434 | MirOrientation orientation = mir_orientation_normal; |
719 | 379 | 435 | ||
720 | 380 | group.for_each_output( | 436 | group.for_each_output( |
721 | 381 | [&](DisplayConfigurationOutput const& conf_output) | 437 | [&](DisplayConfigurationOutput const& conf_output) |
722 | 382 | { | 438 | { |
725 | 383 | uint32_t const connector_id = kms_conf.get_kms_connector_id(conf_output.id); | 439 | auto kms_output = current_display_configuration.get_output_for(conf_output.id); |
724 | 384 | auto kms_output = output_container.get_kms_output_for(connector_id); | ||
726 | 385 | 440 | ||
727 | 386 | auto const mode_index = kms_conf.get_kms_mode_index(conf_output.id, | 441 | auto const mode_index = kms_conf.get_kms_mode_index(conf_output.id, |
728 | 387 | conf_output.current_mode_index); | 442 | conf_output.current_mode_index); |
729 | @@ -390,7 +445,7 @@ | |||
730 | 390 | { | 445 | { |
731 | 391 | kms_output->set_power_mode(conf_output.power_mode); | 446 | kms_output->set_power_mode(conf_output.power_mode); |
732 | 392 | kms_output->set_gamma(conf_output.gamma); | 447 | kms_output->set_gamma(conf_output.gamma); |
734 | 393 | kms_outputs.push_back(kms_output); | 448 | add_to_drm_device_group(kms_output_groups, std::move(kms_output)); |
735 | 394 | } | 449 | } |
736 | 395 | 450 | ||
737 | 396 | /* | 451 | /* |
738 | @@ -413,28 +468,38 @@ | |||
739 | 413 | std::swap(width, height); | 468 | std::swap(width, height); |
740 | 414 | } | 469 | } |
741 | 415 | 470 | ||
764 | 416 | auto surface = gbm->create_scanout_surface(width, height); | 471 | for (auto const& group : kms_output_groups) |
765 | 417 | auto const raw_surface = surface.get(); | 472 | { |
766 | 418 | 473 | /* | |
767 | 419 | std::unique_ptr<DisplayBuffer> db{ | 474 | * In a hybrid setup a scanout surface needs to be allocated differently if it |
768 | 420 | new DisplayBuffer{bypass_option, | 475 | * needs to be able to be shared across GPUs. This likely reduces performance. |
769 | 421 | listener, | 476 | * |
770 | 422 | kms_outputs, | 477 | * As a first cut, assume every scanout buffer in a hybrid setup might need |
771 | 423 | GBMOutputSurface{ | 478 | * to be shared. |
772 | 424 | drm->fd, | 479 | */ |
773 | 425 | std::move(surface), | 480 | auto surface = gbm->create_scanout_surface(width, height, drm.size() != 1); |
774 | 426 | width, height, | 481 | auto const raw_surface = surface.get(); |
775 | 427 | helpers::EGLHelper{ | 482 | |
776 | 428 | *gl_config, | 483 | auto db = std::make_unique<DisplayBuffer>( |
777 | 429 | *gbm, | 484 | bypass_option, |
778 | 430 | raw_surface, | 485 | listener, |
779 | 431 | shared_egl.context() | 486 | group, |
780 | 432 | } | 487 | GBMOutputSurface{ |
781 | 433 | }, | 488 | group.front()->drm_fd(), |
782 | 434 | bounding_rect, | 489 | std::move(surface), |
783 | 435 | orientation}}; | 490 | width, height, |
784 | 436 | 491 | helpers::EGLHelper{ | |
785 | 437 | display_buffers_new.push_back(std::move(db)); | 492 | *gl_config, |
786 | 493 | *gbm, | ||
787 | 494 | raw_surface, | ||
788 | 495 | shared_egl.context() | ||
789 | 496 | } | ||
790 | 497 | }, | ||
791 | 498 | bounding_rect, | ||
792 | 499 | orientation); | ||
793 | 500 | |||
794 | 501 | display_buffers_new.push_back(std::move(db)); | ||
795 | 502 | } | ||
796 | 438 | } | 503 | } |
797 | 439 | }); | 504 | }); |
798 | 440 | 505 | ||
799 | 441 | 506 | ||
800 | === modified file 'src/platforms/mesa/server/kms/display.h' | |||
801 | --- src/platforms/mesa/server/kms/display.h 2017-03-16 07:37:11 +0000 | |||
802 | +++ src/platforms/mesa/server/kms/display.h 2017-03-22 06:55:00 +0000 | |||
803 | @@ -65,7 +65,7 @@ | |||
804 | 65 | public renderer::gl::ContextSource | 65 | public renderer::gl::ContextSource |
805 | 66 | { | 66 | { |
806 | 67 | public: | 67 | public: |
808 | 68 | Display(std::shared_ptr<helpers::DRMHelper> const& drm, | 68 | Display(std::vector<std::shared_ptr<helpers::DRMHelper>> const& drm, |
809 | 69 | std::shared_ptr<helpers::GBMHelper> const& gbm, | 69 | std::shared_ptr<helpers::GBMHelper> const& gbm, |
810 | 70 | std::shared_ptr<VirtualTerminal> const& vt, | 70 | std::shared_ptr<VirtualTerminal> const& vt, |
811 | 71 | BypassOption bypass_option, | 71 | BypassOption bypass_option, |
812 | @@ -106,14 +106,14 @@ | |||
813 | 106 | void clear_connected_unused_outputs(); | 106 | void clear_connected_unused_outputs(); |
814 | 107 | 107 | ||
815 | 108 | mutable std::mutex configuration_mutex; | 108 | mutable std::mutex configuration_mutex; |
817 | 109 | std::shared_ptr<helpers::DRMHelper> const drm; | 109 | std::vector<std::shared_ptr<helpers::DRMHelper>> const drm; |
818 | 110 | std::shared_ptr<helpers::GBMHelper> const gbm; | 110 | std::shared_ptr<helpers::GBMHelper> const gbm; |
819 | 111 | std::shared_ptr<VirtualTerminal> const vt; | 111 | std::shared_ptr<VirtualTerminal> const vt; |
820 | 112 | std::shared_ptr<DisplayReport> const listener; | 112 | std::shared_ptr<DisplayReport> const listener; |
821 | 113 | mir::udev::Monitor monitor; | 113 | mir::udev::Monitor monitor; |
822 | 114 | helpers::EGLHelper shared_egl; | 114 | helpers::EGLHelper shared_egl; |
823 | 115 | std::vector<std::unique_ptr<DisplayBuffer>> display_buffers; | 115 | std::vector<std::unique_ptr<DisplayBuffer>> display_buffers; |
825 | 116 | mutable RealKMSOutputContainer output_container; | 116 | std::shared_ptr<KMSOutputContainer> const output_container; |
826 | 117 | mutable RealKMSDisplayConfiguration current_display_configuration; | 117 | mutable RealKMSDisplayConfiguration current_display_configuration; |
827 | 118 | mutable std::atomic<bool> dirty_configuration; | 118 | mutable std::atomic<bool> dirty_configuration; |
828 | 119 | 119 | ||
829 | 120 | 120 | ||
830 | === modified file 'src/platforms/mesa/server/kms/display_buffer.cpp' | |||
831 | --- src/platforms/mesa/server/kms/display_buffer.cpp 2017-03-13 08:12:52 +0000 | |||
832 | +++ src/platforms/mesa/server/kms/display_buffer.cpp 2017-03-22 06:55:00 +0000 | |||
833 | @@ -28,8 +28,13 @@ | |||
834 | 28 | #include "mir/graphics/egl_error.h" | 28 | #include "mir/graphics/egl_error.h" |
835 | 29 | 29 | ||
836 | 30 | #include <boost/throw_exception.hpp> | 30 | #include <boost/throw_exception.hpp> |
837 | 31 | #include <EGL/egl.h> | ||
838 | 32 | #include <EGL/eglext.h> | ||
839 | 31 | #include MIR_SERVER_GL_H | 33 | #include MIR_SERVER_GL_H |
840 | 34 | #include <GLES2/gl2ext.h> | ||
841 | 35 | #include <drm/drm_fourcc.h> | ||
842 | 32 | 36 | ||
843 | 37 | #include <sstream> | ||
844 | 33 | #include <stdexcept> | 38 | #include <stdexcept> |
845 | 34 | #include <chrono> | 39 | #include <chrono> |
846 | 35 | #include <thread> | 40 | #include <thread> |
847 | @@ -104,18 +109,378 @@ | |||
848 | 104 | 109 | ||
849 | 105 | namespace | 110 | namespace |
850 | 106 | { | 111 | { |
863 | 107 | 112 | void require_extensions( | |
864 | 108 | void ensure_egl_image_extensions() | 113 | std::initializer_list<char const*> extensions, |
865 | 109 | { | 114 | std::function<std::string()> const& extension_getter) |
866 | 110 | std::string ext_string; | 115 | { |
867 | 111 | const char* exts = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)); | 116 | std::stringstream missing_extensions; |
868 | 112 | if (exts) | 117 | |
869 | 113 | ext_string = exts; | 118 | std::string const ext_string = extension_getter(); |
870 | 114 | 119 | ||
871 | 115 | if (ext_string.find("GL_OES_EGL_image") == std::string::npos) | 120 | for (auto extension : extensions) |
872 | 116 | BOOST_THROW_EXCEPTION(std::runtime_error("GLES2 implementation doesn't support GL_OES_EGL_image extension")); | 121 | { |
873 | 117 | } | 122 | if (ext_string.find(extension) == std::string::npos) |
874 | 118 | 123 | { | |
875 | 124 | missing_extensions << "Missing " << extension << std::endl; | ||
876 | 125 | } | ||
877 | 126 | } | ||
878 | 127 | |||
879 | 128 | if (!missing_extensions.str().empty()) | ||
880 | 129 | { | ||
881 | 130 | BOOST_THROW_EXCEPTION(std::runtime_error( | ||
882 | 131 | std::string("Missing required extensions:\n") + missing_extensions.str())); | ||
883 | 132 | } | ||
884 | 133 | } | ||
885 | 134 | |||
886 | 135 | void require_egl_extensions(EGLDisplay dpy, std::initializer_list<char const*> extensions) | ||
887 | 136 | { | ||
888 | 137 | require_extensions( | ||
889 | 138 | extensions, | ||
890 | 139 | [dpy]() -> std::string | ||
891 | 140 | { | ||
892 | 141 | char const* maybe_exts = eglQueryString(dpy, EGL_EXTENSIONS); | ||
893 | 142 | if (maybe_exts) | ||
894 | 143 | return maybe_exts; | ||
895 | 144 | return {}; | ||
896 | 145 | }); | ||
897 | 146 | } | ||
898 | 147 | |||
899 | 148 | void require_gl_extensions(std::initializer_list<char const*> extensions) | ||
900 | 149 | { | ||
901 | 150 | require_extensions( | ||
902 | 151 | extensions, | ||
903 | 152 | []() -> std::string | ||
904 | 153 | { | ||
905 | 154 | char const *maybe_exts = | ||
906 | 155 | reinterpret_cast<char const*>(glGetString(GL_EXTENSIONS)); | ||
907 | 156 | if (maybe_exts) | ||
908 | 157 | return maybe_exts; | ||
909 | 158 | return {}; | ||
910 | 159 | }); | ||
911 | 160 | } | ||
912 | 161 | |||
913 | 162 | bool needs_bounce_buffer(mgm::KMSOutput const& destination, gbm_bo* source) | ||
914 | 163 | { | ||
915 | 164 | return destination.buffer_requires_migration(source); | ||
916 | 165 | } | ||
917 | 166 | |||
918 | 167 | const GLchar* const vshader = | ||
919 | 168 | { | ||
920 | 169 | "attribute vec4 position;\n" | ||
921 | 170 | "attribute vec2 texcoord;\n" | ||
922 | 171 | "varying vec2 v_texcoord;\n" | ||
923 | 172 | "void main() {\n" | ||
924 | 173 | " gl_Position = position;\n" | ||
925 | 174 | " v_texcoord = texcoord;\n" | ||
926 | 175 | "}\n" | ||
927 | 176 | }; | ||
928 | 177 | |||
929 | 178 | const GLchar* const fshader = | ||
930 | 179 | { | ||
931 | 180 | "#ifdef GL_ES\n" | ||
932 | 181 | "precision mediump float;\n" | ||
933 | 182 | "#endif\n" | ||
934 | 183 | "uniform sampler2D tex;" | ||
935 | 184 | "varying vec2 v_texcoord;\n" | ||
936 | 185 | "void main() {\n" | ||
937 | 186 | " gl_FragColor = texture2D(tex, v_texcoord);\n" | ||
938 | 187 | "}\n" | ||
939 | 188 | }; | ||
940 | 189 | |||
941 | 190 | class VBO | ||
942 | 191 | { | ||
943 | 192 | public: | ||
944 | 193 | VBO(void const* data, size_t size) | ||
945 | 194 | { | ||
946 | 195 | glGenBuffers(1, &buf_id); | ||
947 | 196 | glBindBuffer(GL_ARRAY_BUFFER, buf_id); | ||
948 | 197 | glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW); | ||
949 | 198 | glBindBuffer(GL_ARRAY_BUFFER, 0); | ||
950 | 199 | } | ||
951 | 200 | |||
952 | 201 | ~VBO() | ||
953 | 202 | { | ||
954 | 203 | glDeleteBuffers(1, &buf_id); | ||
955 | 204 | } | ||
956 | 205 | |||
957 | 206 | void bind() | ||
958 | 207 | { | ||
959 | 208 | glBindBuffer(GL_ARRAY_BUFFER, buf_id); | ||
960 | 209 | } | ||
961 | 210 | |||
962 | 211 | private: | ||
963 | 212 | GLuint buf_id; | ||
964 | 213 | }; | ||
965 | 214 | |||
966 | 215 | class EGLBufferCopier | ||
967 | 216 | { | ||
968 | 217 | public: | ||
969 | 218 | EGLBufferCopier( | ||
970 | 219 | int drm_fd, | ||
971 | 220 | uint32_t width, | ||
972 | 221 | uint32_t height, | ||
973 | 222 | uint32_t format) | ||
974 | 223 | : eglCreateImageKHR{ | ||
975 | 224 | reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"))}, | ||
976 | 225 | eglDestroyImageKHR{ | ||
977 | 226 | reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR"))}, | ||
978 | 227 | glEGLImageTargetTexture2DOES{ | ||
979 | 228 | reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"))}, | ||
980 | 229 | device{gbm_create_device(drm_fd), &gbm_device_destroy}, | ||
981 | 230 | width{width}, | ||
982 | 231 | height{height}, | ||
983 | 232 | surface{create_scanout_surface(*device, width, height, format)} | ||
984 | 233 | { | ||
985 | 234 | require_gl_extensions({ | ||
986 | 235 | "GL_OES_EGL_image" | ||
987 | 236 | }); | ||
988 | 237 | |||
989 | 238 | EGLint const config_attr[] = { | ||
990 | 239 | EGL_SURFACE_TYPE, EGL_WINDOW_BIT, | ||
991 | 240 | EGL_RED_SIZE, 5, | ||
992 | 241 | EGL_GREEN_SIZE, 5, | ||
993 | 242 | EGL_BLUE_SIZE, 5, | ||
994 | 243 | EGL_ALPHA_SIZE, 0, | ||
995 | 244 | EGL_DEPTH_SIZE, 0, | ||
996 | 245 | EGL_STENCIL_SIZE, 0, | ||
997 | 246 | EGL_RENDERABLE_TYPE, MIR_SERVER_EGL_OPENGL_BIT, | ||
998 | 247 | EGL_NONE | ||
999 | 248 | }; | ||
1000 | 249 | |||
1001 | 250 | static const EGLint required_egl_version_major = 1; | ||
1002 | 251 | static const EGLint required_egl_version_minor = 4; | ||
1003 | 252 | |||
1004 | 253 | EGLint num_egl_configs; | ||
1005 | 254 | EGLConfig egl_config; | ||
1006 | 255 | |||
1007 | 256 | display = eglGetDisplay(static_cast<EGLNativeDisplayType>(device.get())); | ||
1008 | 257 | if (display == EGL_NO_DISPLAY) | ||
1009 | 258 | BOOST_THROW_EXCEPTION(mg::egl_error("Failed to get EGL display")); | ||
1010 | 259 | |||
1011 | 260 | EGLint major, minor; | ||
1012 | 261 | |||
1013 | 262 | if (eglInitialize(display, &major, &minor) == EGL_FALSE) | ||
1014 | 263 | BOOST_THROW_EXCEPTION(mg::egl_error("Failed to initialize EGL display")); | ||
1015 | 264 | |||
1016 | 265 | if ((major < required_egl_version_major) || | ||
1017 | 266 | (major == required_egl_version_major && minor < required_egl_version_minor)) | ||
1018 | 267 | { | ||
1019 | 268 | BOOST_THROW_EXCEPTION(std::runtime_error("Incompatible EGL version")); | ||
1020 | 269 | } | ||
1021 | 270 | |||
1022 | 271 | require_egl_extensions( | ||
1023 | 272 | display, | ||
1024 | 273 | { | ||
1025 | 274 | "EGL_KHR_image_base", | ||
1026 | 275 | "EGL_EXT_image_dma_buf_import" | ||
1027 | 276 | }); | ||
1028 | 277 | |||
1029 | 278 | if (eglChooseConfig(display, config_attr, &egl_config, 1, &num_egl_configs) == EGL_FALSE || | ||
1030 | 279 | num_egl_configs != 1) | ||
1031 | 280 | { | ||
1032 | 281 | BOOST_THROW_EXCEPTION(mg::egl_error("Failed to choose ARGB EGL config")); | ||
1033 | 282 | } | ||
1034 | 283 | |||
1035 | 284 | eglBindAPI(MIR_SERVER_EGL_OPENGL_API); | ||
1036 | 285 | static const EGLint context_attr[] = { | ||
1037 | 286 | #if MIR_SERVER_EGL_OPENGL_BIT == EGL_OPENGL_ES2_BIT | ||
1038 | 287 | EGL_CONTEXT_CLIENT_VERSION, 2, | ||
1039 | 288 | #endif | ||
1040 | 289 | EGL_NONE | ||
1041 | 290 | }; | ||
1042 | 291 | |||
1043 | 292 | context = eglCreateContext(display, egl_config, EGL_NO_CONTEXT, context_attr); | ||
1044 | 293 | if (context == EGL_NO_CONTEXT) | ||
1045 | 294 | BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); | ||
1046 | 295 | |||
1047 | 296 | egl_surface = eglCreateWindowSurface(display, egl_config, surface.get(), nullptr); | ||
1048 | 297 | eglMakeCurrent(display, egl_surface, egl_surface, context); | ||
1049 | 298 | |||
1050 | 299 | auto vertex = glCreateShader(GL_VERTEX_SHADER); | ||
1051 | 300 | glShaderSource(vertex, 1, &vshader, nullptr); | ||
1052 | 301 | glCompileShader(vertex); | ||
1053 | 302 | |||
1054 | 303 | int compiled; | ||
1055 | 304 | glGetShaderiv (vertex, GL_COMPILE_STATUS, &compiled); | ||
1056 | 305 | |||
1057 | 306 | if (!compiled) { | ||
1058 | 307 | GLchar log[1024]; | ||
1059 | 308 | |||
1060 | 309 | glGetShaderInfoLog (vertex, sizeof log - 1, NULL, log); | ||
1061 | 310 | log[sizeof log - 1] = '\0'; | ||
1062 | 311 | glDeleteShader (vertex); | ||
1063 | 312 | |||
1064 | 313 | BOOST_THROW_EXCEPTION( | ||
1065 | 314 | std::runtime_error(std::string{"Failed to compile vertex shader:\n"} + log)); | ||
1066 | 315 | } | ||
1067 | 316 | |||
1068 | 317 | |||
1069 | 318 | auto fragment = glCreateShader(GL_FRAGMENT_SHADER); | ||
1070 | 319 | glShaderSource(fragment, 1, &fshader, nullptr); | ||
1071 | 320 | glCompileShader(fragment); | ||
1072 | 321 | |||
1073 | 322 | glGetShaderiv (fragment, GL_COMPILE_STATUS, &compiled); | ||
1074 | 323 | if (!compiled) { | ||
1075 | 324 | GLchar log[1024]; | ||
1076 | 325 | |||
1077 | 326 | glGetShaderInfoLog (fragment, sizeof log - 1, NULL, log); | ||
1078 | 327 | log[sizeof log - 1] = '\0'; | ||
1079 | 328 | glDeleteShader (fragment); | ||
1080 | 329 | |||
1081 | 330 | BOOST_THROW_EXCEPTION( | ||
1082 | 331 | std::runtime_error(std::string{"Failed to compile fragment shader:\n"} + log)); | ||
1083 | 332 | } | ||
1084 | 333 | |||
1085 | 334 | prog = glCreateProgram(); | ||
1086 | 335 | glAttachShader(prog, vertex); | ||
1087 | 336 | glAttachShader(prog, fragment); | ||
1088 | 337 | glLinkProgram(prog); | ||
1089 | 338 | glGetProgramiv (prog, GL_LINK_STATUS, &compiled); | ||
1090 | 339 | if (!compiled) { | ||
1091 | 340 | GLchar log[1024]; | ||
1092 | 341 | |||
1093 | 342 | glGetProgramInfoLog (prog, sizeof log - 1, NULL, log); | ||
1094 | 343 | log[sizeof log - 1] = '\0'; | ||
1095 | 344 | |||
1096 | 345 | BOOST_THROW_EXCEPTION( | ||
1097 | 346 | std::runtime_error(std::string{"Failed to link shader prog:\n"} + log)); | ||
1098 | 347 | } | ||
1099 | 348 | |||
1100 | 349 | glUseProgram(prog); | ||
1101 | 350 | |||
1102 | 351 | attrpos = glGetAttribLocation(prog, "position"); | ||
1103 | 352 | attrtex = glGetAttribLocation(prog, "texcoord"); | ||
1104 | 353 | auto unitex = glGetUniformLocation(prog, "tex"); | ||
1105 | 354 | |||
1106 | 355 | glGenTextures(1, &tex); | ||
1107 | 356 | glActiveTexture(GL_TEXTURE0); | ||
1108 | 357 | glBindTexture(GL_TEXTURE_2D, tex); | ||
1109 | 358 | |||
1110 | 359 | glUniform1i(unitex, 0); | ||
1111 | 360 | |||
1112 | 361 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
1113 | 362 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
1114 | 363 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | ||
1115 | 364 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||
1116 | 365 | |||
1117 | 366 | static GLfloat const dest_vert[4][2] = | ||
1118 | 367 | { { -1.f, 1.f }, { 1.f, 1.f }, { 1.f, -1.f }, { -1.f, -1.f } }; | ||
1119 | 368 | vert_data = std::make_unique<VBO>(dest_vert, sizeof(dest_vert)); | ||
1120 | 369 | |||
1121 | 370 | static GLfloat const tex_vert[4][2] = | ||
1122 | 371 | { | ||
1123 | 372 | { 0.f, 0.f }, { 1.f, 0.f }, { 1.f, 1.f }, { 0.f, 1.f }, | ||
1124 | 373 | }; | ||
1125 | 374 | tex_data = std::make_unique<VBO>(tex_vert, sizeof(tex_vert)); | ||
1126 | 375 | } | ||
1127 | 376 | |||
1128 | 377 | EGLBufferCopier(EGLBufferCopier const&) = delete; | ||
1129 | 378 | EGLBufferCopier& operator==(EGLBufferCopier const&) = delete; | ||
1130 | 379 | |||
1131 | 380 | ~EGLBufferCopier() | ||
1132 | 381 | { | ||
1133 | 382 | eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context); | ||
1134 | 383 | vert_data = nullptr; | ||
1135 | 384 | tex_data = nullptr; | ||
1136 | 385 | eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); | ||
1137 | 386 | eglDestroySurface(display, egl_surface); | ||
1138 | 387 | eglDestroyContext(display, context); | ||
1139 | 388 | eglTerminate(display); | ||
1140 | 389 | } | ||
1141 | 390 | |||
1142 | 391 | mgm::GBMOutputSurface::FrontBuffer copy_front_buffer_from(mgm::GBMOutputSurface::FrontBuffer&& from) | ||
1143 | 392 | { | ||
1144 | 393 | eglMakeCurrent(display, egl_surface, egl_surface, context); | ||
1145 | 394 | mir::Fd const dma_buf{gbm_bo_get_fd(from)}; | ||
1146 | 395 | |||
1147 | 396 | glUseProgram(prog); | ||
1148 | 397 | |||
1149 | 398 | EGLint const image_attrs[] = { | ||
1150 | 399 | EGL_WIDTH, static_cast<EGLint>(width), | ||
1151 | 400 | EGL_HEIGHT, static_cast<EGLint>(height), | ||
1152 | 401 | EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_XRGB8888, | ||
1153 | 402 | EGL_DMA_BUF_PLANE0_FD_EXT, static_cast<int>(dma_buf), | ||
1154 | 403 | EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, | ||
1155 | 404 | EGL_DMA_BUF_PLANE0_PITCH_EXT, static_cast<EGLint>(gbm_bo_get_stride(from)), | ||
1156 | 405 | EGL_NONE | ||
1157 | 406 | }; | ||
1158 | 407 | |||
1159 | 408 | auto image = eglCreateImageKHR( | ||
1160 | 409 | display, | ||
1161 | 410 | EGL_NO_CONTEXT, | ||
1162 | 411 | EGL_LINUX_DMA_BUF_EXT, | ||
1163 | 412 | nullptr, | ||
1164 | 413 | image_attrs); | ||
1165 | 414 | |||
1166 | 415 | if (image == EGL_NO_IMAGE_KHR) | ||
1167 | 416 | { | ||
1168 | 417 | BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGLImage from dma_buf")); | ||
1169 | 418 | } | ||
1170 | 419 | |||
1171 | 420 | glActiveTexture(GL_TEXTURE0); | ||
1172 | 421 | glBindTexture(GL_TEXTURE_2D, tex); | ||
1173 | 422 | glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); | ||
1174 | 423 | |||
1175 | 424 | vert_data->bind(); | ||
1176 | 425 | glVertexAttribPointer (attrpos, 2, GL_FLOAT, GL_FALSE, 0, 0); | ||
1177 | 426 | |||
1178 | 427 | tex_data->bind(); | ||
1179 | 428 | glVertexAttribPointer (attrtex, 2, GL_FLOAT, GL_FALSE, 0, 0); | ||
1180 | 429 | |||
1181 | 430 | glEnableVertexAttribArray(attrpos); | ||
1182 | 431 | glEnableVertexAttribArray(attrtex); | ||
1183 | 432 | |||
1184 | 433 | GLubyte const idx[] = { 0, 1, 3, 2 }; | ||
1185 | 434 | glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, idx); | ||
1186 | 435 | |||
1187 | 436 | if (eglSwapBuffers(display, egl_surface) != EGL_TRUE) | ||
1188 | 437 | { | ||
1189 | 438 | BOOST_THROW_EXCEPTION(mg::egl_error("Failed to swap bounce buffers")); | ||
1190 | 439 | } | ||
1191 | 440 | |||
1192 | 441 | eglDestroyImageKHR(display, image); | ||
1193 | 442 | |||
1194 | 443 | eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); | ||
1195 | 444 | return mgm::GBMOutputSurface::FrontBuffer(surface.get()); | ||
1196 | 445 | } | ||
1197 | 446 | |||
1198 | 447 | private: | ||
1199 | 448 | static mgm::GBMSurfaceUPtr create_scanout_surface( | ||
1200 | 449 | gbm_device& on, | ||
1201 | 450 | uint32_t width, | ||
1202 | 451 | uint32_t height, | ||
1203 | 452 | uint32_t format) | ||
1204 | 453 | { | ||
1205 | 454 | auto* const device = &on; | ||
1206 | 455 | |||
1207 | 456 | return { | ||
1208 | 457 | gbm_surface_create( | ||
1209 | 458 | device, | ||
1210 | 459 | width, | ||
1211 | 460 | height, | ||
1212 | 461 | format, | ||
1213 | 462 | GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING), | ||
1214 | 463 | &gbm_surface_destroy}; | ||
1215 | 464 | } | ||
1216 | 465 | |||
1217 | 466 | PFNEGLCREATEIMAGEKHRPROC const eglCreateImageKHR; | ||
1218 | 467 | PFNEGLDESTROYIMAGEKHRPROC const eglDestroyImageKHR; | ||
1219 | 468 | PFNGLEGLIMAGETARGETTEXTURE2DOESPROC const glEGLImageTargetTexture2DOES; | ||
1220 | 469 | |||
1221 | 470 | std::unique_ptr<gbm_device, decltype(&gbm_device_destroy)> const device; | ||
1222 | 471 | uint32_t const width; | ||
1223 | 472 | uint32_t const height; | ||
1224 | 473 | mgm::GBMSurfaceUPtr const surface; | ||
1225 | 474 | EGLDisplay display; | ||
1226 | 475 | EGLContext context; | ||
1227 | 476 | EGLSurface egl_surface; | ||
1228 | 477 | GLuint prog; | ||
1229 | 478 | GLuint tex; | ||
1230 | 479 | GLint attrtex; | ||
1231 | 480 | GLint attrpos; | ||
1232 | 481 | std::unique_ptr<VBO> vert_data; | ||
1233 | 482 | std::unique_ptr<VBO> tex_data; | ||
1234 | 483 | }; | ||
1235 | 119 | } | 484 | } |
1236 | 120 | 485 | ||
1237 | 121 | mgm::DisplayBuffer::DisplayBuffer( | 486 | mgm::DisplayBuffer::DisplayBuffer( |
1238 | @@ -152,19 +517,48 @@ | |||
1239 | 152 | make_current(); | 517 | make_current(); |
1240 | 153 | 518 | ||
1241 | 154 | listener->report_successful_egl_make_current_on_construction(); | 519 | listener->report_successful_egl_make_current_on_construction(); |
1245 | 155 | 520 | ||
1243 | 156 | ensure_egl_image_extensions(); | ||
1244 | 157 | |||
1246 | 158 | glClear(GL_COLOR_BUFFER_BIT); | 521 | glClear(GL_COLOR_BUFFER_BIT); |
1247 | 159 | 522 | ||
1248 | 160 | surface.swap_buffers(); | 523 | surface.swap_buffers(); |
1249 | 161 | 524 | ||
1250 | 162 | listener->report_successful_egl_buffer_swap_on_construction(); | 525 | listener->report_successful_egl_buffer_swap_on_construction(); |
1251 | 163 | 526 | ||
1254 | 164 | visible_composite_frame = surface.lock_front(); | 527 | auto temporary_front = surface.lock_front(); |
1255 | 165 | if (!visible_composite_frame) | 528 | if (!temporary_front) |
1256 | 166 | fatal_error("Failed to get frontbuffer"); | 529 | fatal_error("Failed to get frontbuffer"); |
1257 | 167 | 530 | ||
1258 | 531 | if (needs_bounce_buffer(*outputs.front(), temporary_front)) | ||
1259 | 532 | { | ||
1260 | 533 | get_front_buffer = std::bind( | ||
1261 | 534 | std::mem_fn(&EGLBufferCopier::copy_front_buffer_from), | ||
1262 | 535 | std::make_shared<EGLBufferCopier>( | ||
1263 | 536 | outputs.front()->drm_fd(), | ||
1264 | 537 | fb_width, | ||
1265 | 538 | fb_height, | ||
1266 | 539 | GBM_BO_FORMAT_XRGB8888), | ||
1267 | 540 | std::placeholders::_1); | ||
1268 | 541 | } | ||
1269 | 542 | else | ||
1270 | 543 | { | ||
1271 | 544 | get_front_buffer = [](auto&& fb) { return std::move(fb); }; | ||
1272 | 545 | } | ||
1273 | 546 | |||
1274 | 547 | visible_composite_frame = get_front_buffer(std::move(temporary_front)); | ||
1275 | 548 | |||
1276 | 549 | /* | ||
1277 | 550 | * Check that our (possibly bounced) front buffer is usable on *all* the | ||
1278 | 551 | * outputs we've been asked to output on. | ||
1279 | 552 | */ | ||
1280 | 553 | for (auto const& output : outputs) | ||
1281 | 554 | { | ||
1282 | 555 | if (output->buffer_requires_migration(visible_composite_frame)) | ||
1283 | 556 | { | ||
1284 | 557 | BOOST_THROW_EXCEPTION(std::invalid_argument( | ||
1285 | 558 | "Attempted to create a DisplayBuffer spanning multiple GPU memory domains")); | ||
1286 | 559 | } | ||
1287 | 560 | } | ||
1288 | 561 | |||
1289 | 168 | set_crtc(*outputs.front()->fb_for(visible_composite_frame, fb_width, fb_height)); | 562 | set_crtc(*outputs.front()->fb_for(visible_composite_frame, fb_width, fb_height)); |
1290 | 169 | 563 | ||
1291 | 170 | release_current(); | 564 | release_current(); |
1292 | @@ -213,7 +607,8 @@ | |||
1293 | 213 | if (!native) | 607 | if (!native) |
1294 | 214 | BOOST_THROW_EXCEPTION(std::invalid_argument("could not convert NativeBuffer")); | 608 | BOOST_THROW_EXCEPTION(std::invalid_argument("could not convert NativeBuffer")); |
1295 | 215 | if (native->flags & mir_buffer_flag_can_scanout && | 609 | if (native->flags & mir_buffer_flag_can_scanout && |
1297 | 216 | bypass_buffer->size() == geom::Size{fb_width,fb_height}) | 610 | bypass_buffer->size() == geom::Size{fb_width,fb_height} && |
1298 | 611 | !needs_bounce_buffer(*outputs.front(), native->bo)) | ||
1299 | 217 | { | 612 | { |
1300 | 218 | if (auto bufobj = outputs.front()->fb_for(native->bo, fb_width, fb_height)) | 613 | if (auto bufobj = outputs.front()->fb_for(native->bo, fb_width, fb_height)) |
1301 | 219 | { | 614 | { |
1302 | @@ -278,7 +673,7 @@ | |||
1303 | 278 | } | 673 | } |
1304 | 279 | else | 674 | else |
1305 | 280 | { | 675 | { |
1307 | 281 | scheduled_composite_frame = surface.lock_front(); | 676 | scheduled_composite_frame = get_front_buffer(surface.lock_front()); |
1308 | 282 | bufobj = outputs.front()->fb_for(scheduled_composite_frame, fb_width, fb_height); | 677 | bufobj = outputs.front()->fb_for(scheduled_composite_frame, fb_width, fb_height); |
1309 | 283 | if (!bufobj) | 678 | if (!bufobj) |
1310 | 284 | fatal_error("Failed to get front buffer object"); | 679 | fatal_error("Failed to get front buffer object"); |
1311 | 285 | 680 | ||
1312 | === modified file 'src/platforms/mesa/server/kms/display_buffer.h' | |||
1313 | --- src/platforms/mesa/server/kms/display_buffer.h 2017-03-16 07:37:11 +0000 | |||
1314 | +++ src/platforms/mesa/server/kms/display_buffer.h 2017-03-22 06:55:00 +0000 | |||
1315 | @@ -53,19 +53,18 @@ | |||
1316 | 53 | { | 53 | { |
1317 | 54 | public: | 54 | public: |
1318 | 55 | FrontBuffer(); | 55 | FrontBuffer(); |
1319 | 56 | FrontBuffer(gbm_surface* surface); | ||
1320 | 57 | FrontBuffer(FrontBuffer&& from); | ||
1321 | 58 | |||
1322 | 56 | ~FrontBuffer(); | 59 | ~FrontBuffer(); |
1323 | 57 | 60 | ||
1324 | 58 | FrontBuffer(FrontBuffer&& from); | ||
1325 | 59 | |||
1326 | 60 | FrontBuffer& operator=(FrontBuffer&& from); | 61 | FrontBuffer& operator=(FrontBuffer&& from); |
1327 | 61 | FrontBuffer& operator=(std::nullptr_t); | 62 | FrontBuffer& operator=(std::nullptr_t); |
1328 | 62 | 63 | ||
1329 | 63 | operator gbm_bo*(); | 64 | operator gbm_bo*(); |
1330 | 64 | operator bool() const; | 65 | operator bool() const; |
1331 | 66 | |||
1332 | 65 | private: | 67 | private: |
1333 | 66 | friend class GBMOutputSurface; | ||
1334 | 67 | FrontBuffer(gbm_surface* surface); | ||
1335 | 68 | |||
1336 | 69 | gbm_surface* const surf; | 68 | gbm_surface* const surf; |
1337 | 70 | gbm_bo* const bo; | 69 | gbm_bo* const bo; |
1338 | 71 | }; | 70 | }; |
1339 | @@ -146,6 +145,8 @@ | |||
1340 | 146 | GBMOutputSurface::FrontBuffer visible_composite_frame; | 145 | GBMOutputSurface::FrontBuffer visible_composite_frame; |
1341 | 147 | GBMOutputSurface::FrontBuffer scheduled_composite_frame; | 146 | GBMOutputSurface::FrontBuffer scheduled_composite_frame; |
1342 | 148 | 147 | ||
1343 | 148 | std::function<GBMOutputSurface::FrontBuffer(GBMOutputSurface::FrontBuffer&&)> get_front_buffer; | ||
1344 | 149 | |||
1345 | 149 | geometry::Rectangle area; | 150 | geometry::Rectangle area; |
1346 | 150 | uint32_t fb_width, fb_height; | 151 | uint32_t fb_width, fb_height; |
1347 | 151 | glm::mat2 transform; | 152 | glm::mat2 transform; |
1348 | 152 | 153 | ||
1349 | === modified file 'src/platforms/mesa/server/kms/kms_display_configuration.h' | |||
1350 | --- src/platforms/mesa/server/kms/kms_display_configuration.h 2016-01-29 08:18:22 +0000 | |||
1351 | +++ src/platforms/mesa/server/kms/kms_display_configuration.h 2017-03-22 06:55:00 +0000 | |||
1352 | @@ -20,6 +20,7 @@ | |||
1353 | 20 | #define MIR_GRAPHICS_MESA_KMS_DISPLAY_CONFIGURATION_H_ | 20 | #define MIR_GRAPHICS_MESA_KMS_DISPLAY_CONFIGURATION_H_ |
1354 | 21 | 21 | ||
1355 | 22 | #include "mir/graphics/display_configuration.h" | 22 | #include "mir/graphics/display_configuration.h" |
1356 | 23 | #include <memory> | ||
1357 | 23 | 24 | ||
1358 | 24 | namespace mir | 25 | namespace mir |
1359 | 25 | { | 26 | { |
1360 | @@ -27,15 +28,17 @@ | |||
1361 | 27 | { | 28 | { |
1362 | 28 | namespace mesa | 29 | namespace mesa |
1363 | 29 | { | 30 | { |
1364 | 31 | class KMSOutput; | ||
1365 | 30 | 32 | ||
1366 | 31 | class DRMModeResources; | 33 | class DRMModeResources; |
1367 | 32 | 34 | ||
1368 | 33 | class KMSDisplayConfiguration : public DisplayConfiguration | 35 | class KMSDisplayConfiguration : public DisplayConfiguration |
1369 | 34 | { | 36 | { |
1370 | 35 | public: | 37 | public: |
1374 | 36 | virtual uint32_t get_kms_connector_id(DisplayConfigurationOutputId id) const = 0; | 38 | virtual std::shared_ptr<KMSOutput> get_output_for(DisplayConfigurationOutputId id) const = 0; |
1375 | 37 | virtual size_t get_kms_mode_index(DisplayConfigurationOutputId id, | 39 | virtual size_t get_kms_mode_index( |
1376 | 38 | size_t conf_mode_index) const = 0; | 40 | DisplayConfigurationOutputId id, |
1377 | 41 | size_t conf_mode_index) const = 0; | ||
1378 | 39 | virtual void update() = 0; | 42 | virtual void update() = 0; |
1379 | 40 | }; | 43 | }; |
1380 | 41 | 44 | ||
1381 | 42 | 45 | ||
1382 | === modified file 'src/platforms/mesa/server/kms/kms_output.h' | |||
1383 | --- src/platforms/mesa/server/kms/kms_output.h 2017-03-13 08:12:52 +0000 | |||
1384 | +++ src/platforms/mesa/server/kms/kms_output.h 2017-03-22 06:55:00 +0000 | |||
1385 | @@ -26,12 +26,16 @@ | |||
1386 | 26 | #include "mir/graphics/frame.h" | 26 | #include "mir/graphics/frame.h" |
1387 | 27 | #include "mir_toolkit/common.h" | 27 | #include "mir_toolkit/common.h" |
1388 | 28 | 28 | ||
1389 | 29 | #include "kms-utils/drm_mode_resources.h" | ||
1390 | 30 | |||
1391 | 29 | #include <gbm.h> | 31 | #include <gbm.h> |
1392 | 30 | 32 | ||
1393 | 31 | namespace mir | 33 | namespace mir |
1394 | 32 | { | 34 | { |
1395 | 33 | namespace graphics | 35 | namespace graphics |
1396 | 34 | { | 36 | { |
1397 | 37 | class DisplayConfigurationOutput; | ||
1398 | 38 | |||
1399 | 35 | namespace mesa | 39 | namespace mesa |
1400 | 36 | { | 40 | { |
1401 | 37 | 41 | ||
1402 | @@ -42,6 +46,12 @@ | |||
1403 | 42 | public: | 46 | public: |
1404 | 43 | virtual ~KMSOutput() = default; | 47 | virtual ~KMSOutput() = default; |
1405 | 44 | 48 | ||
1406 | 49 | /* | ||
1407 | 50 | * I'm not sure that DRM guarantees ID uniqueness in the presence of hotplug/unplug; | ||
1408 | 51 | * this may want to be an opaque class Id + operator== in future. | ||
1409 | 52 | */ | ||
1410 | 53 | virtual uint32_t id() const = 0; | ||
1411 | 54 | |||
1412 | 45 | virtual void reset() = 0; | 55 | virtual void reset() = 0; |
1413 | 46 | virtual void configure(geometry::Displacement fb_offset, size_t kms_mode_index) = 0; | 56 | virtual void configure(geometry::Displacement fb_offset, size_t kms_mode_index) = 0; |
1414 | 47 | virtual geometry::Size size() const = 0; | 57 | virtual geometry::Size size() const = 0; |
1415 | @@ -68,14 +78,39 @@ | |||
1416 | 68 | virtual void set_gamma(GammaCurves const& gamma) = 0; | 78 | virtual void set_gamma(GammaCurves const& gamma) = 0; |
1417 | 69 | virtual Frame last_frame() const = 0; | 79 | virtual Frame last_frame() const = 0; |
1418 | 70 | 80 | ||
1419 | 81 | /** | ||
1420 | 82 | * Re-probe the hardware state of this connector. | ||
1421 | 83 | * | ||
1422 | 84 | * \throws std::system_error if the underlying DRM connector has disappeared. | ||
1423 | 85 | */ | ||
1424 | 86 | virtual void refresh_hardware_state() = 0; | ||
1425 | 87 | /** | ||
1426 | 88 | * Translate and copy the cached hardware state into a Mir display configuration object. | ||
1427 | 89 | * | ||
1428 | 90 | * \param [out] to_update The Mir display configuration object to update with new | ||
1429 | 91 | * hardware state. Only hardware state (modes, dimensions, etc) | ||
1430 | 92 | * is touched. | ||
1431 | 93 | */ | ||
1432 | 94 | virtual void update_from_hardware_state(DisplayConfigurationOutput& to_update) const = 0; | ||
1433 | 71 | virtual FBHandle* fb_for(gbm_bo* bo, uint32_t width, uint32_t height) const = 0; | 95 | virtual FBHandle* fb_for(gbm_bo* bo, uint32_t width, uint32_t height) const = 0; |
1434 | 72 | 96 | ||
1435 | 97 | /** | ||
1436 | 98 | * Check whether buffer need to be migrated to GPU-private memory for display. | ||
1437 | 99 | * | ||
1438 | 100 | * \param [in] bo GBM buffer to test | ||
1439 | 101 | * \return True if buffer must be migrated to display-private memory in order to be displayed. | ||
1440 | 102 | * If this method returns true the caller should probably copy it to a new buffer before | ||
1441 | 103 | * calling fb_for(buffer), as acquiring a FBHandle to the buffer will likely make it | ||
1442 | 104 | * unusable for rendering on the original GPU. | ||
1443 | 105 | */ | ||
1444 | 106 | virtual bool buffer_requires_migration(gbm_bo* bo) const = 0; | ||
1445 | 107 | |||
1446 | 108 | virtual int drm_fd() const = 0; | ||
1447 | 73 | protected: | 109 | protected: |
1448 | 74 | KMSOutput() = default; | 110 | KMSOutput() = default; |
1449 | 75 | KMSOutput(const KMSOutput&) = delete; | 111 | KMSOutput(const KMSOutput&) = delete; |
1450 | 76 | KMSOutput& operator=(const KMSOutput&) = delete; | 112 | KMSOutput& operator=(const KMSOutput&) = delete; |
1451 | 77 | }; | 113 | }; |
1452 | 78 | |||
1453 | 79 | } | 114 | } |
1454 | 80 | } | 115 | } |
1455 | 81 | } | 116 | } |
1456 | 82 | 117 | ||
1457 | === modified file 'src/platforms/mesa/server/kms/kms_output_container.h' | |||
1458 | --- src/platforms/mesa/server/kms/kms_output_container.h 2016-01-29 08:18:22 +0000 | |||
1459 | +++ src/platforms/mesa/server/kms/kms_output_container.h 2017-03-22 06:55:00 +0000 | |||
1460 | @@ -36,9 +36,12 @@ | |||
1461 | 36 | public: | 36 | public: |
1462 | 37 | virtual ~KMSOutputContainer() = default; | 37 | virtual ~KMSOutputContainer() = default; |
1463 | 38 | 38 | ||
1466 | 39 | virtual std::shared_ptr<KMSOutput> get_kms_output_for(uint32_t connector_id) = 0; | 39 | virtual void for_each_output(std::function<void(std::shared_ptr<KMSOutput> const&)> functor) const = 0; |
1465 | 40 | virtual void for_each_output(std::function<void(KMSOutput&)> functor) const = 0; | ||
1467 | 41 | 40 | ||
1468 | 41 | /** | ||
1469 | 42 | * Re-probe hardware state and update output list. | ||
1470 | 43 | */ | ||
1471 | 44 | virtual void update_from_hardware_state() = 0; | ||
1472 | 42 | protected: | 45 | protected: |
1473 | 43 | KMSOutputContainer() = default; | 46 | KMSOutputContainer() = default; |
1474 | 44 | KMSOutputContainer(KMSOutputContainer const&) = delete; | 47 | KMSOutputContainer(KMSOutputContainer const&) = delete; |
1475 | 45 | 48 | ||
1476 | === added file 'src/platforms/mesa/server/kms/mutex.h' | |||
1477 | --- src/platforms/mesa/server/kms/mutex.h 1970-01-01 00:00:00 +0000 | |||
1478 | +++ src/platforms/mesa/server/kms/mutex.h 2017-03-22 06:55:00 +0000 | |||
1479 | @@ -0,0 +1,104 @@ | |||
1480 | 1 | /* | ||
1481 | 2 | * Copyright © 2017 Canonical Ltd. | ||
1482 | 3 | * | ||
1483 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
1484 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
1485 | 6 | * as published by the Free Software Foundation. | ||
1486 | 7 | * | ||
1487 | 8 | * This program is distributed in the hope that it will be useful, | ||
1488 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1489 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1490 | 11 | * GNU Lesser General Public License for more details. | ||
1491 | 12 | * | ||
1492 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
1493 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1494 | 15 | * | ||
1495 | 16 | * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> | ||
1496 | 17 | */ | ||
1497 | 18 | |||
1498 | 19 | #ifndef MIR_MUTEX_H_ | ||
1499 | 20 | #define MIR_MUTEX_H_ | ||
1500 | 21 | |||
1501 | 22 | #include <mutex> | ||
1502 | 23 | |||
1503 | 24 | namespace mir | ||
1504 | 25 | { | ||
1505 | 26 | /** | ||
1506 | 27 | * Smart-pointer-esque accessor for Mutex<> protected data. | ||
1507 | 28 | * | ||
1508 | 29 | * Ensures exclusive access to the referenced data. | ||
1509 | 30 | * | ||
1510 | 31 | * \tparam Guarded Type of data guarded by the mutex. | ||
1511 | 32 | */ | ||
1512 | 33 | template<typename Guarded> | ||
1513 | 34 | class MutexGuard | ||
1514 | 35 | { | ||
1515 | 36 | public: | ||
1516 | 37 | MutexGuard(std::unique_lock<std::mutex>&& lock, Guarded& value) | ||
1517 | 38 | : value{value}, | ||
1518 | 39 | lock{std::move(lock)} | ||
1519 | 40 | { | ||
1520 | 41 | } | ||
1521 | 42 | MutexGuard(MutexGuard&& from) = default; | ||
1522 | 43 | ~MutexGuard() noexcept(false) | ||
1523 | 44 | { | ||
1524 | 45 | if (lock.owns_lock()) | ||
1525 | 46 | { | ||
1526 | 47 | lock.unlock(); | ||
1527 | 48 | } | ||
1528 | 49 | } | ||
1529 | 50 | |||
1530 | 51 | Guarded& operator*() | ||
1531 | 52 | { | ||
1532 | 53 | return value; | ||
1533 | 54 | } | ||
1534 | 55 | Guarded* operator->() | ||
1535 | 56 | { | ||
1536 | 57 | return &value; | ||
1537 | 58 | } | ||
1538 | 59 | private: | ||
1539 | 60 | Guarded& value; | ||
1540 | 61 | std::unique_lock<std::mutex> lock; | ||
1541 | 62 | }; | ||
1542 | 63 | |||
1543 | 64 | /** | ||
1544 | 65 | * A data-locking mutex | ||
1545 | 66 | * | ||
1546 | 67 | * This is a mutex which owns the data it guards, and can give out a | ||
1547 | 68 | * smart-pointer-esque lock to lock and access it. | ||
1548 | 69 | * | ||
1549 | 70 | * \tparam Guarded The type of data guarded by the mutex | ||
1550 | 71 | */ | ||
1551 | 72 | template<typename Guarded> | ||
1552 | 73 | class Mutex | ||
1553 | 74 | { | ||
1554 | 75 | public: | ||
1555 | 76 | Mutex() = default; | ||
1556 | 77 | Mutex(Guarded&& initial_value) | ||
1557 | 78 | : value{std::move(initial_value)} | ||
1558 | 79 | { | ||
1559 | 80 | } | ||
1560 | 81 | |||
1561 | 82 | Mutex(Mutex const&) = delete; | ||
1562 | 83 | Mutex& operator=(Mutex const&) = delete; | ||
1563 | 84 | |||
1564 | 85 | /** | ||
1565 | 86 | * Lock the mutex and return an accessor for the protected data. | ||
1566 | 87 | * | ||
1567 | 88 | * \return A smart-pointer-esque accessor for the contained data. | ||
1568 | 89 | * While code has access to the MutexGuard it is guaranteed to have exclusive | ||
1569 | 90 | * access to the contained data. | ||
1570 | 91 | */ | ||
1571 | 92 | MutexGuard<Guarded> lock() | ||
1572 | 93 | { | ||
1573 | 94 | return MutexGuard<Guarded>{std::unique_lock<std::mutex>{mutex}, value}; | ||
1574 | 95 | } | ||
1575 | 96 | |||
1576 | 97 | private: | ||
1577 | 98 | std::mutex mutex; | ||
1578 | 99 | Guarded value; | ||
1579 | 100 | }; | ||
1580 | 101 | |||
1581 | 102 | } | ||
1582 | 103 | |||
1583 | 104 | #endif //MIR_MUTEX_H_ | ||
1584 | 0 | 105 | ||
1585 | === modified file 'src/platforms/mesa/server/kms/platform.cpp' | |||
1586 | --- src/platforms/mesa/server/kms/platform.cpp 2017-03-16 07:37:11 +0000 | |||
1587 | +++ src/platforms/mesa/server/kms/platform.cpp 2017-03-22 06:55:00 +0000 | |||
1588 | @@ -39,17 +39,25 @@ | |||
1589 | 39 | EmergencyCleanupRegistry& emergency_cleanup_registry, | 39 | EmergencyCleanupRegistry& emergency_cleanup_registry, |
1590 | 40 | BypassOption bypass_option) | 40 | BypassOption bypass_option) |
1591 | 41 | : udev{std::make_shared<mir::udev::Context>()}, | 41 | : udev{std::make_shared<mir::udev::Context>()}, |
1593 | 42 | drm{std::make_shared<mgmh::DRMHelper>(mgmh::DRMNodeToUse::card)}, | 42 | drm{helpers::DRMHelper::open_all_devices(udev)}, |
1594 | 43 | gbm{std::make_shared<mgmh::GBMHelper>()}, | 43 | gbm{std::make_shared<mgmh::GBMHelper>()}, |
1595 | 44 | listener{listener}, | 44 | listener{listener}, |
1596 | 45 | vt{vt}, | 45 | vt{vt}, |
1597 | 46 | bypass_option_{bypass_option} | 46 | bypass_option_{bypass_option} |
1598 | 47 | { | 47 | { |
1601 | 48 | drm->setup(udev); | 48 | // We assume the first DRM device is the boot GPU, and arbitrarily pick it as our |
1602 | 49 | gbm->setup(*drm); | 49 | // shell renderer. |
1603 | 50 | // | ||
1604 | 51 | // TODO: expose multiple rendering GPUs to the shell. | ||
1605 | 52 | gbm->setup(*drm.front()); | ||
1606 | 50 | 53 | ||
1607 | 51 | std::weak_ptr<VirtualTerminal> weak_vt = vt; | 54 | std::weak_ptr<VirtualTerminal> weak_vt = vt; |
1609 | 52 | std::weak_ptr<mgmh::DRMHelper> weak_drm = drm; | 55 | std::vector<std::weak_ptr<mgmh::DRMHelper>> weak_drm; |
1610 | 56 | |||
1611 | 57 | for (auto const &helper : drm) | ||
1612 | 58 | { | ||
1613 | 59 | weak_drm.push_back(helper); | ||
1614 | 60 | } | ||
1615 | 53 | emergency_cleanup_registry.add( | 61 | emergency_cleanup_registry.add( |
1616 | 54 | make_module_ptr<EmergencyCleanupHandler>( | 62 | make_module_ptr<EmergencyCleanupHandler>( |
1617 | 55 | [weak_vt,weak_drm] | 63 | [weak_vt,weak_drm] |
1618 | @@ -57,8 +65,19 @@ | |||
1619 | 57 | if (auto const vt = weak_vt.lock()) | 65 | if (auto const vt = weak_vt.lock()) |
1620 | 58 | try { vt->restore(); } catch (...) {} | 66 | try { vt->restore(); } catch (...) {} |
1621 | 59 | 67 | ||
1624 | 60 | if (auto const drm = weak_drm.lock()) | 68 | for (auto helper : weak_drm) |
1625 | 61 | try { drm->drop_master(); } catch (...) {} | 69 | { |
1626 | 70 | if (auto const drm = helper.lock()) | ||
1627 | 71 | { | ||
1628 | 72 | try | ||
1629 | 73 | { | ||
1630 | 74 | drm->drop_master(); | ||
1631 | 75 | } | ||
1632 | 76 | catch (...) | ||
1633 | 77 | { | ||
1634 | 78 | } | ||
1635 | 79 | } | ||
1636 | 80 | } | ||
1637 | 62 | })); | 81 | })); |
1638 | 63 | 82 | ||
1639 | 64 | } | 83 | } |
1640 | @@ -71,12 +90,19 @@ | |||
1641 | 71 | mir::UniqueModulePtr<mg::Display> mgm::Platform::create_display( | 90 | mir::UniqueModulePtr<mg::Display> mgm::Platform::create_display( |
1642 | 72 | std::shared_ptr<DisplayConfigurationPolicy> const& initial_conf_policy, std::shared_ptr<GLConfig> const& gl_config) | 91 | std::shared_ptr<DisplayConfigurationPolicy> const& initial_conf_policy, std::shared_ptr<GLConfig> const& gl_config) |
1643 | 73 | { | 92 | { |
1645 | 74 | return make_module_ptr<mgm::Display>(drm, gbm, vt, bypass_option_, initial_conf_policy, gl_config, listener); | 93 | return make_module_ptr<mgm::Display>( |
1646 | 94 | drm, | ||
1647 | 95 | gbm, | ||
1648 | 96 | vt, | ||
1649 | 97 | bypass_option_, | ||
1650 | 98 | initial_conf_policy, | ||
1651 | 99 | gl_config, | ||
1652 | 100 | listener); | ||
1653 | 75 | } | 101 | } |
1654 | 76 | 102 | ||
1655 | 77 | mir::UniqueModulePtr<mg::PlatformIpcOperations> mgm::Platform::make_ipc_operations() const | 103 | mir::UniqueModulePtr<mg::PlatformIpcOperations> mgm::Platform::make_ipc_operations() const |
1656 | 78 | { | 104 | { |
1658 | 79 | return make_module_ptr<mgm::IpcOperations>(drm); | 105 | return make_module_ptr<mgm::IpcOperations>(drm.front()); |
1659 | 80 | } | 106 | } |
1660 | 81 | 107 | ||
1661 | 82 | mg::NativePlatform* mgm::Platform::native_platform() | 108 | mg::NativePlatform* mgm::Platform::native_platform() |
1662 | 83 | 109 | ||
1663 | === modified file 'src/platforms/mesa/server/kms/platform.h' | |||
1664 | --- src/platforms/mesa/server/kms/platform.h 2017-03-16 07:37:11 +0000 | |||
1665 | +++ src/platforms/mesa/server/kms/platform.h 2017-03-22 06:55:00 +0000 | |||
1666 | @@ -56,7 +56,7 @@ | |||
1667 | 56 | EGLNativeDisplayType egl_native_display() const override; | 56 | EGLNativeDisplayType egl_native_display() const override; |
1668 | 57 | 57 | ||
1669 | 58 | std::shared_ptr<mir::udev::Context> udev; | 58 | std::shared_ptr<mir::udev::Context> udev; |
1671 | 59 | std::shared_ptr<helpers::DRMHelper> const drm; | 59 | std::vector<std::shared_ptr<helpers::DRMHelper>> const drm; |
1672 | 60 | std::shared_ptr<helpers::GBMHelper> const gbm; | 60 | std::shared_ptr<helpers::GBMHelper> const gbm; |
1673 | 61 | 61 | ||
1674 | 62 | std::shared_ptr<DisplayReport> const listener; | 62 | std::shared_ptr<DisplayReport> const listener; |
1675 | 63 | 63 | ||
1676 | === modified file 'src/platforms/mesa/server/kms/real_kms_display_configuration.cpp' | |||
1677 | --- src/platforms/mesa/server/kms/real_kms_display_configuration.cpp 2017-03-14 02:26:28 +0000 | |||
1678 | +++ src/platforms/mesa/server/kms/real_kms_display_configuration.cpp 2017-03-22 06:55:00 +0000 | |||
1679 | @@ -17,9 +17,10 @@ | |||
1680 | 17 | */ | 17 | */ |
1681 | 18 | 18 | ||
1682 | 19 | #include "real_kms_display_configuration.h" | 19 | #include "real_kms_display_configuration.h" |
1683 | 20 | #include "kms-utils/drm_mode_resources.h" | ||
1684 | 21 | #include "mir/graphics/pixel_format_utils.h" | 20 | #include "mir/graphics/pixel_format_utils.h" |
1685 | 22 | #include "mir/log.h" | 21 | #include "mir/log.h" |
1686 | 22 | #include "kms_output_container.h" | ||
1687 | 23 | #include "kms_output.h" | ||
1688 | 23 | 24 | ||
1689 | 24 | #include <cmath> | 25 | #include <cmath> |
1690 | 25 | #include <limits> | 26 | #include <limits> |
1691 | @@ -35,77 +36,21 @@ | |||
1692 | 35 | namespace mgk = mir::graphics::kms; | 36 | namespace mgk = mir::graphics::kms; |
1693 | 36 | namespace geom = mir::geometry; | 37 | namespace geom = mir::geometry; |
1694 | 37 | 38 | ||
1758 | 38 | namespace | 39 | mgm::RealKMSDisplayConfiguration::RealKMSDisplayConfiguration( |
1759 | 39 | { | 40 | std::shared_ptr<KMSOutputContainer> const& displays) |
1760 | 40 | 41 | : displays{displays}, | |
1761 | 41 | bool kms_modes_are_equal(drmModeModeInfo const& info1, drmModeModeInfo const& info2) | 42 | card{mg::DisplayConfigurationCardId{0}, 0} |
1699 | 42 | { | ||
1700 | 43 | return (info1.clock == info2.clock && | ||
1701 | 44 | info1.hdisplay == info2.hdisplay && | ||
1702 | 45 | info1.hsync_start == info2.hsync_start && | ||
1703 | 46 | info1.hsync_end == info2.hsync_end && | ||
1704 | 47 | info1.htotal == info2.htotal && | ||
1705 | 48 | info1.hskew == info2.hskew && | ||
1706 | 49 | info1.vdisplay == info2.vdisplay && | ||
1707 | 50 | info1.vsync_start == info2.vsync_start && | ||
1708 | 51 | info1.vsync_end == info2.vsync_end && | ||
1709 | 52 | info1.vtotal == info2.vtotal); | ||
1710 | 53 | } | ||
1711 | 54 | |||
1712 | 55 | double calculate_vrefresh_hz(drmModeModeInfo const& mode) | ||
1713 | 56 | { | ||
1714 | 57 | if (mode.htotal == 0 || mode.vtotal == 0) | ||
1715 | 58 | return 0.0; | ||
1716 | 59 | |||
1717 | 60 | /* mode.clock is in KHz */ | ||
1718 | 61 | double hz = (mode.clock * 100000LL / | ||
1719 | 62 | ((long)mode.htotal * (long)mode.vtotal) | ||
1720 | 63 | ) / 100.0; | ||
1721 | 64 | |||
1722 | 65 | // Actually we don't need floating point at all for this... | ||
1723 | 66 | // TODO: Consider converting our structs to fixed-point ints | ||
1724 | 67 | return hz; | ||
1725 | 68 | } | ||
1726 | 69 | |||
1727 | 70 | mg::DisplayConfigurationOutputType | ||
1728 | 71 | kms_connector_type_to_output_type(uint32_t connector_type) | ||
1729 | 72 | { | ||
1730 | 73 | return static_cast<mg::DisplayConfigurationOutputType>(connector_type); | ||
1731 | 74 | } | ||
1732 | 75 | |||
1733 | 76 | MirSubpixelArrangement kms_subpixel_to_mir_subpixel(uint32_t subpixel) | ||
1734 | 77 | { | ||
1735 | 78 | switch (subpixel) | ||
1736 | 79 | { | ||
1737 | 80 | case DRM_MODE_SUBPIXEL_UNKNOWN: | ||
1738 | 81 | return mir_subpixel_arrangement_unknown; | ||
1739 | 82 | case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: | ||
1740 | 83 | return mir_subpixel_arrangement_horizontal_rgb; | ||
1741 | 84 | case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: | ||
1742 | 85 | return mir_subpixel_arrangement_horizontal_bgr; | ||
1743 | 86 | case DRM_MODE_SUBPIXEL_VERTICAL_RGB: | ||
1744 | 87 | return mir_subpixel_arrangement_vertical_rgb; | ||
1745 | 88 | case DRM_MODE_SUBPIXEL_VERTICAL_BGR: | ||
1746 | 89 | return mir_subpixel_arrangement_vertical_bgr; | ||
1747 | 90 | case DRM_MODE_SUBPIXEL_NONE: | ||
1748 | 91 | return mir_subpixel_arrangement_none; | ||
1749 | 92 | default: | ||
1750 | 93 | return mir_subpixel_arrangement_unknown; | ||
1751 | 94 | } | ||
1752 | 95 | } | ||
1753 | 96 | |||
1754 | 97 | } | ||
1755 | 98 | |||
1756 | 99 | mgm::RealKMSDisplayConfiguration::RealKMSDisplayConfiguration(int drm_fd) | ||
1757 | 100 | : drm_fd{drm_fd} | ||
1762 | 101 | { | 43 | { |
1763 | 102 | update(); | 44 | update(); |
1764 | 103 | } | 45 | } |
1765 | 104 | 46 | ||
1766 | 105 | mgm::RealKMSDisplayConfiguration::RealKMSDisplayConfiguration( | 47 | mgm::RealKMSDisplayConfiguration::RealKMSDisplayConfiguration( |
1767 | 106 | RealKMSDisplayConfiguration const& conf) | 48 | RealKMSDisplayConfiguration const& conf) |
1770 | 107 | : KMSDisplayConfiguration(), drm_fd{conf.drm_fd}, | 49 | : KMSDisplayConfiguration(), |
1771 | 108 | card(conf.card), outputs{conf.outputs} | 50 | displays{conf.displays}, |
1772 | 51 | // Vivid GCC is apparently confused by trying to copy-construct card from conf.card? | ||
1773 | 52 | card{conf.card.id, conf.card.max_simultaneous_outputs}, | ||
1774 | 53 | outputs{conf.outputs} | ||
1775 | 109 | { | 54 | { |
1776 | 110 | } | 55 | } |
1777 | 111 | 56 | ||
1778 | @@ -114,7 +59,7 @@ | |||
1779 | 114 | { | 59 | { |
1780 | 115 | if (&conf != this) | 60 | if (&conf != this) |
1781 | 116 | { | 61 | { |
1783 | 117 | drm_fd = conf.drm_fd; | 62 | displays = conf.displays; |
1784 | 118 | card = conf.card; | 63 | card = conf.card; |
1785 | 119 | outputs = conf.outputs; | 64 | outputs = conf.outputs; |
1786 | 120 | } | 65 | } |
1787 | @@ -131,16 +76,16 @@ | |||
1788 | 131 | void mgm::RealKMSDisplayConfiguration::for_each_output( | 76 | void mgm::RealKMSDisplayConfiguration::for_each_output( |
1789 | 132 | std::function<void(DisplayConfigurationOutput const&)> f) const | 77 | std::function<void(DisplayConfigurationOutput const&)> f) const |
1790 | 133 | { | 78 | { |
1793 | 134 | for (auto const& output : outputs) | 79 | for (auto const& output_pair : outputs) |
1794 | 135 | f(output); | 80 | f(output_pair.first); |
1795 | 136 | } | 81 | } |
1796 | 137 | 82 | ||
1797 | 138 | void mgm::RealKMSDisplayConfiguration::for_each_output( | 83 | void mgm::RealKMSDisplayConfiguration::for_each_output( |
1798 | 139 | std::function<void(UserDisplayConfigurationOutput&)> f) | 84 | std::function<void(UserDisplayConfigurationOutput&)> f) |
1799 | 140 | { | 85 | { |
1801 | 141 | for (auto& output : outputs) | 86 | for (auto& output_pair : outputs) |
1802 | 142 | { | 87 | { |
1804 | 143 | UserDisplayConfigurationOutput user(output); | 88 | UserDisplayConfigurationOutput user(output_pair.first); |
1805 | 144 | f(user); | 89 | f(user); |
1806 | 145 | } | 90 | } |
1807 | 146 | } | 91 | } |
1808 | @@ -150,233 +95,87 @@ | |||
1809 | 150 | return std::make_unique<RealKMSDisplayConfiguration>(*this); | 95 | return std::make_unique<RealKMSDisplayConfiguration>(*this); |
1810 | 151 | } | 96 | } |
1811 | 152 | 97 | ||
1813 | 153 | uint32_t mgm::RealKMSDisplayConfiguration::get_kms_connector_id( | 98 | std::shared_ptr<mgm::KMSOutput> mgm::RealKMSDisplayConfiguration::get_output_for( |
1814 | 154 | DisplayConfigurationOutputId id) const | 99 | DisplayConfigurationOutputId id) const |
1815 | 155 | { | 100 | { |
1825 | 156 | auto iter = find_output_with_id(id); | 101 | return outputs.at(id.as_value()).second; |
1817 | 157 | |||
1818 | 158 | if (iter == outputs.end()) | ||
1819 | 159 | { | ||
1820 | 160 | BOOST_THROW_EXCEPTION( | ||
1821 | 161 | std::runtime_error("Failed to find DisplayConfigurationOutput with provided id")); | ||
1822 | 162 | } | ||
1823 | 163 | |||
1824 | 164 | return id.as_value(); | ||
1826 | 165 | } | 102 | } |
1827 | 166 | 103 | ||
1828 | 167 | size_t mgm::RealKMSDisplayConfiguration::get_kms_mode_index( | 104 | size_t mgm::RealKMSDisplayConfiguration::get_kms_mode_index( |
1829 | 168 | DisplayConfigurationOutputId id, | 105 | DisplayConfigurationOutputId id, |
1830 | 169 | size_t conf_mode_index) const | 106 | size_t conf_mode_index) const |
1831 | 170 | { | 107 | { |
1838 | 171 | auto iter = find_output_with_id(id); | 108 | if (static_cast<size_t>(id.as_value()) > outputs.size()) |
1839 | 172 | 109 | { | |
1840 | 173 | if (iter == outputs.end() || conf_mode_index >= iter->modes.size()) | 110 | BOOST_THROW_EXCEPTION(std::invalid_argument("Request for KMS mode index of invalid output ID")); |
1841 | 174 | { | 111 | } |
1842 | 175 | BOOST_THROW_EXCEPTION( | 112 | if (conf_mode_index > outputs[id.as_value()].first.modes.size()) |
1843 | 176 | std::runtime_error("Failed to find valid mode index for DisplayConfigurationOutput with provided id/mode_index")); | 113 | { |
1844 | 114 | BOOST_THROW_EXCEPTION(std::invalid_argument("Request for out-of-bounds KMS mode index")); | ||
1845 | 177 | } | 115 | } |
1846 | 178 | 116 | ||
1847 | 179 | return conf_mode_index; | 117 | return conf_mode_index; |
1848 | 180 | } | 118 | } |
1849 | 181 | 119 | ||
1850 | 120 | namespace | ||
1851 | 121 | { | ||
1852 | 122 | void populate_default_mir_config(mg::DisplayConfigurationOutput& to_populate) | ||
1853 | 123 | { | ||
1854 | 124 | to_populate.card_id = mg::DisplayConfigurationCardId{0}; | ||
1855 | 125 | to_populate.gamma_supported = mir_output_gamma_supported; | ||
1856 | 126 | to_populate.orientation = mir_orientation_normal; | ||
1857 | 127 | to_populate.form_factor = mir_form_factor_monitor; | ||
1858 | 128 | to_populate.scale = 1.0f; | ||
1859 | 129 | to_populate.top_left = geom::Point{}; | ||
1860 | 130 | to_populate.used = false; | ||
1861 | 131 | to_populate.pixel_formats = {mir_pixel_format_xrgb_8888, mir_pixel_format_argb_8888}; | ||
1862 | 132 | to_populate.current_format = mir_pixel_format_xrgb_8888; | ||
1863 | 133 | to_populate.current_mode_index = std::numeric_limits<uint32_t>::max(); | ||
1864 | 134 | } | ||
1865 | 135 | } | ||
1866 | 136 | |||
1867 | 182 | void mgm::RealKMSDisplayConfiguration::update() | 137 | void mgm::RealKMSDisplayConfiguration::update() |
1868 | 183 | { | 138 | { |
2065 | 184 | kms::DRMModeResources resources{drm_fd}; | 139 | decltype(outputs) new_outputs; |
2066 | 185 | 140 | ||
2067 | 186 | size_t max_outputs = std::min(resources.num_crtcs(), resources.num_connectors()); | 141 | int counter = 0; |
2068 | 187 | card = {DisplayConfigurationCardId{0}, max_outputs}; | 142 | displays->update_from_hardware_state(); |
2069 | 188 | 143 | displays->for_each_output( | |
2070 | 189 | resources.for_each_connector([&](kms::DRMModeConnectorUPtr connector) | 144 | [this, &new_outputs, &counter](auto const& output) mutable |
2071 | 190 | { | 145 | { |
2072 | 191 | add_or_update_output(resources, *connector); | 146 | DisplayConfigurationOutput mir_config; |
2073 | 192 | }); | 147 | |
2074 | 193 | } | 148 | auto const existing_output = std::find_if( |
2075 | 194 | 149 | outputs.begin(), | |
2076 | 195 | namespace | 150 | outputs.end(), |
2077 | 196 | { | 151 | [&output](auto const& candidate) |
2078 | 197 | std::vector<uint8_t> edid_for_connector(int drm_fd, uint32_t connector_id) | 152 | { |
2079 | 198 | { | 153 | // Pointer comparison; is this KMSOutput object already present? |
2080 | 199 | std::vector<uint8_t> edid; | 154 | return candidate.second == output; |
2081 | 200 | 155 | }); | |
2082 | 201 | mgk::ObjectProperties connector_props{ | 156 | if (existing_output == outputs.end()) |
2083 | 202 | drm_fd, connector_id, DRM_MODE_OBJECT_CONNECTOR}; | 157 | { |
2084 | 203 | 158 | populate_default_mir_config(mir_config); | |
2085 | 204 | if (connector_props.has_property("EDID")) | 159 | } |
2086 | 205 | { | 160 | else |
2087 | 206 | /* | 161 | { |
2088 | 207 | * We don't technically need the property information here, but query it | 162 | mir_config = existing_output->first; |
2089 | 208 | * anyway so we can detect if our assumptions about DRM behaviour | 163 | } |
2090 | 209 | * become invalid. | 164 | |
2091 | 210 | */ | 165 | output->update_from_hardware_state(mir_config); |
2092 | 211 | auto property = mgk::DRMModePropertyUPtr{ | 166 | mir_config.id = DisplayConfigurationOutputId{counter}; |
2093 | 212 | drmModeGetProperty(drm_fd, connector_props.id_for("EDID")), | 167 | counter++; |
2094 | 213 | &drmModeFreeProperty}; | 168 | |
2095 | 214 | 169 | new_outputs.push_back(std::make_pair(mir_config, output)); | |
2096 | 215 | if (!property) | 170 | }); |
2097 | 216 | { | 171 | |
2098 | 217 | mir::log_warning( | 172 | outputs = new_outputs; |
2099 | 218 | "Failed to get EDID property for connector %u: %i (%s)", | 173 | |
2100 | 219 | connector_id, | 174 | /* |
2101 | 220 | errno, | 175 | * This is not the true max simultaneous outputs, but it's unclear whether it's possible |
2102 | 221 | ::strerror(errno)); | 176 | * to provide a max_simultaneous_outputs value that is useful to clients. |
2103 | 222 | return edid; | 177 | */ |
2104 | 223 | } | 178 | card.max_simultaneous_outputs = outputs.size(); |
1909 | 224 | |||
1910 | 225 | if (!drm_property_type_is(property.get(), DRM_MODE_PROP_BLOB)) | ||
1911 | 226 | { | ||
1912 | 227 | mir::log_warning( | ||
1913 | 228 | "EDID property on connector %u has unexpected type %u", | ||
1914 | 229 | connector_id, | ||
1915 | 230 | property->flags); | ||
1916 | 231 | return edid; | ||
1917 | 232 | } | ||
1918 | 233 | |||
1919 | 234 | // A property ID of 0 means invalid. | ||
1920 | 235 | if (connector_props["EDID"] == 0) | ||
1921 | 236 | { | ||
1922 | 237 | /* | ||
1923 | 238 | * Log a debug message only. This will trigger for broken monitors which | ||
1924 | 239 | * don't provide an EDID, which is not as unusual as you might think... | ||
1925 | 240 | */ | ||
1926 | 241 | mir::log_debug("No EDID data available on connector %u", connector_id); | ||
1927 | 242 | return edid; | ||
1928 | 243 | } | ||
1929 | 244 | |||
1930 | 245 | auto blob = drmModeGetPropertyBlob(drm_fd, connector_props["EDID"]); | ||
1931 | 246 | |||
1932 | 247 | if (!blob) | ||
1933 | 248 | { | ||
1934 | 249 | mir::log_warning( | ||
1935 | 250 | "Failed to get EDID property blob for connector %u: %i (%s)", | ||
1936 | 251 | connector_id, | ||
1937 | 252 | errno, | ||
1938 | 253 | ::strerror(errno)); | ||
1939 | 254 | |||
1940 | 255 | return edid; | ||
1941 | 256 | } | ||
1942 | 257 | |||
1943 | 258 | edid.reserve(blob->length); | ||
1944 | 259 | edid.insert(edid.begin(), | ||
1945 | 260 | reinterpret_cast<uint8_t*>(blob->data), | ||
1946 | 261 | reinterpret_cast<uint8_t*>(blob->data) + blob->length); | ||
1947 | 262 | |||
1948 | 263 | drmModeFreePropertyBlob(blob); | ||
1949 | 264 | |||
1950 | 265 | edid.shrink_to_fit(); | ||
1951 | 266 | } | ||
1952 | 267 | |||
1953 | 268 | return edid; | ||
1954 | 269 | } | ||
1955 | 270 | } | ||
1956 | 271 | |||
1957 | 272 | void mgm::RealKMSDisplayConfiguration::add_or_update_output( | ||
1958 | 273 | kms::DRMModeResources const& resources, | ||
1959 | 274 | drmModeConnector const& connector) | ||
1960 | 275 | { | ||
1961 | 276 | DisplayConfigurationOutputId id{static_cast<int>(connector.connector_id)}; | ||
1962 | 277 | DisplayConfigurationCardId card_id{0}; | ||
1963 | 278 | DisplayConfigurationOutputType const type{ | ||
1964 | 279 | kms_connector_type_to_output_type(connector.connector_type)}; | ||
1965 | 280 | geom::Size physical_size{connector.mmWidth, connector.mmHeight}; | ||
1966 | 281 | bool connected{connector.connection == DRM_MODE_CONNECTED}; | ||
1967 | 282 | uint32_t const invalid_mode_index = std::numeric_limits<uint32_t>::max(); | ||
1968 | 283 | uint32_t current_mode_index{invalid_mode_index}; | ||
1969 | 284 | uint32_t preferred_mode_index{invalid_mode_index}; | ||
1970 | 285 | std::vector<DisplayConfigurationMode> modes; | ||
1971 | 286 | std::vector<MirPixelFormat> formats {mir_pixel_format_argb_8888, | ||
1972 | 287 | mir_pixel_format_xrgb_8888}; | ||
1973 | 288 | |||
1974 | 289 | std::vector<uint8_t> edid; | ||
1975 | 290 | if (connected) | ||
1976 | 291 | { | ||
1977 | 292 | /* Only ask for the EDID on connected outputs. There's obviously no monitor EDID | ||
1978 | 293 | * when there is no monitor connected! | ||
1979 | 294 | */ | ||
1980 | 295 | edid = edid_for_connector(drm_fd, connector.connector_id); | ||
1981 | 296 | } | ||
1982 | 297 | |||
1983 | 298 | drmModeModeInfo current_mode_info = drmModeModeInfo(); | ||
1984 | 299 | GammaCurves gamma; | ||
1985 | 300 | |||
1986 | 301 | /* Get information about the current mode */ | ||
1987 | 302 | if (connector.encoder_id) | ||
1988 | 303 | { | ||
1989 | 304 | auto encoder = resources.encoder(connector.encoder_id); | ||
1990 | 305 | if (encoder->crtc_id) | ||
1991 | 306 | { | ||
1992 | 307 | auto crtc = resources.crtc(encoder->crtc_id); | ||
1993 | 308 | current_mode_info = crtc->mode; | ||
1994 | 309 | |||
1995 | 310 | if (crtc->gamma_size > 0) | ||
1996 | 311 | gamma = mg::LinearGammaLUTs(crtc->gamma_size); | ||
1997 | 312 | } | ||
1998 | 313 | } | ||
1999 | 314 | |||
2000 | 315 | /* Add all the available modes and find the current and preferred one */ | ||
2001 | 316 | for (int m = 0; m < connector.count_modes; m++) | ||
2002 | 317 | { | ||
2003 | 318 | drmModeModeInfo& mode_info = connector.modes[m]; | ||
2004 | 319 | |||
2005 | 320 | geom::Size size{mode_info.hdisplay, mode_info.vdisplay}; | ||
2006 | 321 | |||
2007 | 322 | double vrefresh_hz = calculate_vrefresh_hz(mode_info); | ||
2008 | 323 | |||
2009 | 324 | modes.push_back({size, vrefresh_hz}); | ||
2010 | 325 | |||
2011 | 326 | if (kms_modes_are_equal(mode_info, current_mode_info)) | ||
2012 | 327 | current_mode_index = m; | ||
2013 | 328 | |||
2014 | 329 | if ((mode_info.type & DRM_MODE_TYPE_PREFERRED) == DRM_MODE_TYPE_PREFERRED) | ||
2015 | 330 | preferred_mode_index = m; | ||
2016 | 331 | } | ||
2017 | 332 | |||
2018 | 333 | /* Add or update the output */ | ||
2019 | 334 | auto iter = find_output_with_id(id); | ||
2020 | 335 | |||
2021 | 336 | if (iter == outputs.end()) | ||
2022 | 337 | { | ||
2023 | 338 | outputs.push_back({id, card_id, type, formats, modes, preferred_mode_index, | ||
2024 | 339 | physical_size, connected, false, geom::Point(), | ||
2025 | 340 | current_mode_index, mir_pixel_format_xrgb_8888, | ||
2026 | 341 | mir_power_mode_on, mir_orientation_normal, | ||
2027 | 342 | 1.0f, mir_form_factor_monitor, | ||
2028 | 343 | kms_subpixel_to_mir_subpixel(connector.subpixel), | ||
2029 | 344 | gamma, mir_output_gamma_supported, std::move(edid)}); | ||
2030 | 345 | } | ||
2031 | 346 | else | ||
2032 | 347 | { | ||
2033 | 348 | auto& output = *iter; | ||
2034 | 349 | |||
2035 | 350 | output.current_mode_index = current_mode_index; | ||
2036 | 351 | output.modes = modes; | ||
2037 | 352 | output.preferred_mode_index = preferred_mode_index; | ||
2038 | 353 | output.physical_size_mm = physical_size; | ||
2039 | 354 | output.connected = connected; | ||
2040 | 355 | output.current_format = mir_pixel_format_xrgb_8888; | ||
2041 | 356 | output.subpixel_arrangement = kms_subpixel_to_mir_subpixel(connector.subpixel); | ||
2042 | 357 | output.gamma = gamma; | ||
2043 | 358 | output.edid = edid; | ||
2044 | 359 | } | ||
2045 | 360 | } | ||
2046 | 361 | |||
2047 | 362 | std::vector<mg::DisplayConfigurationOutput>::iterator | ||
2048 | 363 | mgm::RealKMSDisplayConfiguration::find_output_with_id(mg::DisplayConfigurationOutputId id) | ||
2049 | 364 | { | ||
2050 | 365 | return std::find_if(outputs.begin(), outputs.end(), | ||
2051 | 366 | [id](DisplayConfigurationOutput const& output) | ||
2052 | 367 | { | ||
2053 | 368 | return output.id == id; | ||
2054 | 369 | }); | ||
2055 | 370 | } | ||
2056 | 371 | |||
2057 | 372 | std::vector<mg::DisplayConfigurationOutput>::const_iterator | ||
2058 | 373 | mgm::RealKMSDisplayConfiguration::find_output_with_id(mg::DisplayConfigurationOutputId id) const | ||
2059 | 374 | { | ||
2060 | 375 | return std::find_if(outputs.begin(), outputs.end(), | ||
2061 | 376 | [id](DisplayConfigurationOutput const& output) | ||
2062 | 377 | { | ||
2063 | 378 | return output.id == id; | ||
2064 | 379 | }); | ||
2105 | 380 | } | 179 | } |
2106 | 381 | 180 | ||
2107 | 382 | // Compatibility means conf1 can be attained from conf2 (and vice versa) | 181 | // Compatibility means conf1 can be attained from conf2 (and vice versa) |
2108 | @@ -386,9 +185,9 @@ | |||
2109 | 386 | // to be allocated/destroyed, and hence should not be considered compatible. | 185 | // to be allocated/destroyed, and hence should not be considered compatible. |
2110 | 387 | bool mgm::compatible(mgm::RealKMSDisplayConfiguration const& conf1, mgm::RealKMSDisplayConfiguration const& conf2) | 186 | bool mgm::compatible(mgm::RealKMSDisplayConfiguration const& conf1, mgm::RealKMSDisplayConfiguration const& conf2) |
2111 | 388 | { | 187 | { |
2115 | 389 | bool compatible{(conf1.drm_fd == conf2.drm_fd) && | 188 | bool compatible{ |
2116 | 390 | (conf1.card == conf2.card) && | 189 | (conf1.card == conf2.card) && |
2117 | 391 | (conf1.outputs.size() == conf2.outputs.size())}; | 190 | (conf1.outputs.size() == conf2.outputs.size())}; |
2118 | 392 | 191 | ||
2119 | 393 | if (compatible) | 192 | if (compatible) |
2120 | 394 | { | 193 | { |
2121 | @@ -396,17 +195,17 @@ | |||
2122 | 396 | 195 | ||
2123 | 397 | for (unsigned int i = 0; i < count; ++i) | 196 | for (unsigned int i = 0; i < count; ++i) |
2124 | 398 | { | 197 | { |
2126 | 399 | compatible &= (conf1.outputs[i].power_mode == conf2.outputs[i].power_mode); | 198 | compatible &= (conf1.outputs[i].first.power_mode == conf2.outputs[i].first.power_mode); |
2127 | 400 | if (compatible) | 199 | if (compatible) |
2128 | 401 | { | 200 | { |
2130 | 402 | auto clone = conf2.outputs[i]; | 201 | auto clone = conf2.outputs[i].first; |
2131 | 403 | 202 | ||
2132 | 404 | // ignore difference in orientation, scale factor, form factor, subpixel arrangement | 203 | // ignore difference in orientation, scale factor, form factor, subpixel arrangement |
2138 | 405 | clone.orientation = conf1.outputs[i].orientation; | 204 | clone.orientation = conf1.outputs[i].first.orientation; |
2139 | 406 | clone.subpixel_arrangement = conf1.outputs[i].subpixel_arrangement; | 205 | clone.subpixel_arrangement = conf1.outputs[i].first.subpixel_arrangement; |
2140 | 407 | clone.scale = conf1.outputs[i].scale; | 206 | clone.scale = conf1.outputs[i].first.scale; |
2141 | 408 | clone.form_factor = conf1.outputs[i].form_factor; | 207 | clone.form_factor = conf1.outputs[i].first.form_factor; |
2142 | 409 | compatible &= (conf1.outputs[i] == clone); | 208 | compatible &= (conf1.outputs[i].first == clone); |
2143 | 410 | } | 209 | } |
2144 | 411 | else | 210 | else |
2145 | 412 | break; | 211 | break; |
2146 | 413 | 212 | ||
2147 | === modified file 'src/platforms/mesa/server/kms/real_kms_display_configuration.h' | |||
2148 | --- src/platforms/mesa/server/kms/real_kms_display_configuration.h 2017-01-18 02:29:37 +0000 | |||
2149 | +++ src/platforms/mesa/server/kms/real_kms_display_configuration.h 2017-03-22 06:55:00 +0000 | |||
2150 | @@ -30,13 +30,15 @@ | |||
2151 | 30 | { | 30 | { |
2152 | 31 | namespace mesa | 31 | namespace mesa |
2153 | 32 | { | 32 | { |
2154 | 33 | class KMSOutput; | ||
2155 | 34 | class KMSOutputContainer; | ||
2156 | 33 | 35 | ||
2157 | 34 | class RealKMSDisplayConfiguration : public KMSDisplayConfiguration | 36 | class RealKMSDisplayConfiguration : public KMSDisplayConfiguration |
2158 | 35 | { | 37 | { |
2159 | 36 | friend bool compatible(RealKMSDisplayConfiguration const& conf1, RealKMSDisplayConfiguration const& conf2); | 38 | friend bool compatible(RealKMSDisplayConfiguration const& conf1, RealKMSDisplayConfiguration const& conf2); |
2160 | 37 | 39 | ||
2161 | 38 | public: | 40 | public: |
2163 | 39 | RealKMSDisplayConfiguration(int drm_fd); | 41 | RealKMSDisplayConfiguration(std::shared_ptr<KMSOutputContainer> const& displays); |
2164 | 40 | RealKMSDisplayConfiguration(RealKMSDisplayConfiguration const& conf); | 42 | RealKMSDisplayConfiguration(RealKMSDisplayConfiguration const& conf); |
2165 | 41 | RealKMSDisplayConfiguration& operator=(RealKMSDisplayConfiguration const& conf); | 43 | RealKMSDisplayConfiguration& operator=(RealKMSDisplayConfiguration const& conf); |
2166 | 42 | 44 | ||
2167 | @@ -45,18 +47,15 @@ | |||
2168 | 45 | void for_each_output(std::function<void(UserDisplayConfigurationOutput&)> f) override; | 47 | void for_each_output(std::function<void(UserDisplayConfigurationOutput&)> f) override; |
2169 | 46 | std::unique_ptr<DisplayConfiguration> clone() const override; | 48 | std::unique_ptr<DisplayConfiguration> clone() const override; |
2170 | 47 | 49 | ||
2172 | 48 | uint32_t get_kms_connector_id(DisplayConfigurationOutputId id) const override; | 50 | std::shared_ptr<KMSOutput> get_output_for(DisplayConfigurationOutputId id) const override; |
2173 | 49 | size_t get_kms_mode_index(DisplayConfigurationOutputId id, size_t conf_mode_index) const override; | 51 | size_t get_kms_mode_index(DisplayConfigurationOutputId id, size_t conf_mode_index) const override; |
2174 | 50 | void update() override; | 52 | void update() override; |
2175 | 51 | 53 | ||
2176 | 52 | private: | 54 | private: |
2177 | 53 | void add_or_update_output(kms::DRMModeResources const& resources, drmModeConnector const& connector); | ||
2178 | 54 | std::vector<DisplayConfigurationOutput>::iterator find_output_with_id(DisplayConfigurationOutputId id); | ||
2179 | 55 | std::vector<DisplayConfigurationOutput>::const_iterator find_output_with_id(DisplayConfigurationOutputId id) const; | ||
2180 | 56 | 55 | ||
2182 | 57 | int drm_fd; | 56 | std::shared_ptr<KMSOutputContainer> displays; |
2183 | 58 | DisplayConfigurationCard card; | 57 | DisplayConfigurationCard card; |
2185 | 59 | std::vector<DisplayConfigurationOutput> outputs; | 58 | std::vector<std::pair< DisplayConfigurationOutput, std::shared_ptr<KMSOutput>>> outputs; |
2186 | 60 | }; | 59 | }; |
2187 | 61 | 60 | ||
2188 | 62 | bool compatible(RealKMSDisplayConfiguration const& conf1, RealKMSDisplayConfiguration const& conf2); | 61 | bool compatible(RealKMSDisplayConfiguration const& conf1, RealKMSDisplayConfiguration const& conf2); |
2189 | 63 | 62 | ||
2190 | === modified file 'src/platforms/mesa/server/kms/real_kms_output.cpp' | |||
2191 | --- src/platforms/mesa/server/kms/real_kms_output.cpp 2017-03-13 08:12:52 +0000 | |||
2192 | +++ src/platforms/mesa/server/kms/real_kms_output.cpp 2017-03-22 06:55:00 +0000 | |||
2193 | @@ -17,6 +17,7 @@ | |||
2194 | 17 | */ | 17 | */ |
2195 | 18 | 18 | ||
2196 | 19 | #include "real_kms_output.h" | 19 | #include "real_kms_output.h" |
2197 | 20 | #include "mir/graphics/display_configuration.h" | ||
2198 | 20 | #include "page_flipper.h" | 21 | #include "page_flipper.h" |
2199 | 21 | #include "kms-utils/kms_connector.h" | 22 | #include "kms-utils/kms_connector.h" |
2200 | 22 | #include "mir/fatal.h" | 23 | #include "mir/fatal.h" |
2201 | @@ -68,20 +69,27 @@ | |||
2202 | 68 | 69 | ||
2203 | 69 | } | 70 | } |
2204 | 70 | 71 | ||
2210 | 71 | mgm::RealKMSOutput::RealKMSOutput(int drm_fd, uint32_t connector_id, | 72 | mgm::RealKMSOutput::RealKMSOutput( |
2211 | 72 | std::shared_ptr<PageFlipper> const& page_flipper) | 73 | int drm_fd, |
2212 | 73 | : drm_fd{drm_fd}, connector_id{connector_id}, page_flipper{page_flipper}, | 74 | kms::DRMModeConnectorUPtr&& connector, |
2213 | 74 | connector(), mode_index{0}, current_crtc(), saved_crtc(), | 75 | std::shared_ptr<PageFlipper> const& page_flipper) |
2214 | 75 | using_saved_crtc{true}, has_cursor_{false}, | 76 | : drm_fd_{drm_fd}, |
2215 | 77 | page_flipper{page_flipper}, | ||
2216 | 78 | connector{std::move(connector)}, | ||
2217 | 79 | mode_index{0}, | ||
2218 | 80 | current_crtc(), | ||
2219 | 81 | saved_crtc(), | ||
2220 | 82 | using_saved_crtc{true}, | ||
2221 | 83 | has_cursor_{false}, | ||
2222 | 76 | power_mode(mir_power_mode_on) | 84 | power_mode(mir_power_mode_on) |
2223 | 77 | { | 85 | { |
2224 | 78 | reset(); | 86 | reset(); |
2225 | 79 | 87 | ||
2227 | 80 | kms::DRMModeResources resources{drm_fd}; | 88 | kms::DRMModeResources resources{drm_fd_}; |
2228 | 81 | 89 | ||
2230 | 82 | if (connector->encoder_id) | 90 | if (this->connector->encoder_id) |
2231 | 83 | { | 91 | { |
2233 | 84 | auto encoder = resources.encoder(connector->encoder_id); | 92 | auto encoder = resources.encoder(this->connector->encoder_id); |
2234 | 85 | if (encoder->crtc_id) | 93 | if (encoder->crtc_id) |
2235 | 86 | { | 94 | { |
2236 | 87 | saved_crtc = *resources.crtc(encoder->crtc_id); | 95 | saved_crtc = *resources.crtc(encoder->crtc_id); |
2237 | @@ -94,14 +102,19 @@ | |||
2238 | 94 | restore_saved_crtc(); | 102 | restore_saved_crtc(); |
2239 | 95 | } | 103 | } |
2240 | 96 | 104 | ||
2241 | 105 | uint32_t mgm::RealKMSOutput::id() const | ||
2242 | 106 | { | ||
2243 | 107 | return connector->connector_id; | ||
2244 | 108 | } | ||
2245 | 109 | |||
2246 | 97 | void mgm::RealKMSOutput::reset() | 110 | void mgm::RealKMSOutput::reset() |
2247 | 98 | { | 111 | { |
2249 | 99 | kms::DRMModeResources resources{drm_fd}; | 112 | kms::DRMModeResources resources{drm_fd_}; |
2250 | 100 | 113 | ||
2251 | 101 | /* Update the connector to ensure we have the latest information */ | 114 | /* Update the connector to ensure we have the latest information */ |
2252 | 102 | try | 115 | try |
2253 | 103 | { | 116 | { |
2255 | 104 | connector = resources.connector(connector_id); | 117 | connector = resources.connector(connector->connector_id); |
2256 | 105 | } | 118 | } |
2257 | 106 | catch (std::exception const& e) | 119 | catch (std::exception const& e) |
2258 | 107 | { | 120 | { |
2259 | @@ -111,7 +124,7 @@ | |||
2260 | 111 | // TODO: What if we can't locate the DPMS property? | 124 | // TODO: What if we can't locate the DPMS property? |
2261 | 112 | for (int i = 0; i < connector->count_props; i++) | 125 | for (int i = 0; i < connector->count_props; i++) |
2262 | 113 | { | 126 | { |
2264 | 114 | auto prop = drmModeGetProperty(drm_fd, connector->props[i]); | 127 | auto prop = drmModeGetProperty(drm_fd_, connector->props[i]); |
2265 | 115 | if (prop && (prop->flags & DRM_MODE_PROP_ENUM)) { | 128 | if (prop && (prop->flags & DRM_MODE_PROP_ENUM)) { |
2266 | 116 | if (!strcmp(prop->name, "DPMS")) | 129 | if (!strcmp(prop->name, "DPMS")) |
2267 | 117 | { | 130 | { |
2268 | @@ -154,7 +167,7 @@ | |||
2269 | 154 | return false; | 167 | return false; |
2270 | 155 | } | 168 | } |
2271 | 156 | 169 | ||
2273 | 157 | auto ret = drmModeSetCrtc(drm_fd, current_crtc->crtc_id, | 170 | auto ret = drmModeSetCrtc(drm_fd_, current_crtc->crtc_id, |
2274 | 158 | fb.get_drm_fb_id(), fb_offset.dx.as_int(), fb_offset.dy.as_int(), | 171 | fb.get_drm_fb_id(), fb_offset.dx.as_int(), fb_offset.dy.as_int(), |
2275 | 159 | &connector->connector_id, 1, | 172 | &connector->connector_id, 1, |
2276 | 160 | &connector->modes[mode_index]); | 173 | &connector->modes[mode_index]); |
2277 | @@ -185,7 +198,7 @@ | |||
2278 | 185 | return; | 198 | return; |
2279 | 186 | } | 199 | } |
2280 | 187 | 200 | ||
2282 | 188 | auto result = drmModeSetCrtc(drm_fd, current_crtc->crtc_id, | 201 | auto result = drmModeSetCrtc(drm_fd_, current_crtc->crtc_id, |
2283 | 189 | 0, 0, 0, nullptr, 0, nullptr); | 202 | 0, 0, 0, nullptr, 0, nullptr); |
2284 | 190 | if (result) | 203 | if (result) |
2285 | 191 | { | 204 | { |
2286 | @@ -207,7 +220,10 @@ | |||
2287 | 207 | mgk::connector_name(connector).c_str()); | 220 | mgk::connector_name(connector).c_str()); |
2288 | 208 | return false; | 221 | return false; |
2289 | 209 | } | 222 | } |
2291 | 210 | return page_flipper->schedule_flip(current_crtc->crtc_id, fb.get_drm_fb_id(), connector_id); | 223 | return page_flipper->schedule_flip( |
2292 | 224 | current_crtc->crtc_id, | ||
2293 | 225 | fb.get_drm_fb_id(), | ||
2294 | 226 | connector->connector_id); | ||
2295 | 211 | } | 227 | } |
2296 | 212 | 228 | ||
2297 | 213 | void mgm::RealKMSOutput::wait_for_page_flip() | 229 | void mgm::RealKMSOutput::wait_for_page_flip() |
2298 | @@ -236,7 +252,7 @@ | |||
2299 | 236 | { | 252 | { |
2300 | 237 | has_cursor_ = true; | 253 | has_cursor_ = true; |
2301 | 238 | result = drmModeSetCursor( | 254 | result = drmModeSetCursor( |
2303 | 239 | drm_fd, | 255 | drm_fd_, |
2304 | 240 | current_crtc->crtc_id, | 256 | current_crtc->crtc_id, |
2305 | 241 | gbm_bo_get_handle(buffer).u32, | 257 | gbm_bo_get_handle(buffer).u32, |
2306 | 242 | gbm_bo_get_width(buffer), | 258 | gbm_bo_get_width(buffer), |
2307 | @@ -255,7 +271,7 @@ | |||
2308 | 255 | { | 271 | { |
2309 | 256 | if (current_crtc) | 272 | if (current_crtc) |
2310 | 257 | { | 273 | { |
2312 | 258 | if (auto result = drmModeMoveCursor(drm_fd, current_crtc->crtc_id, | 274 | if (auto result = drmModeMoveCursor(drm_fd_, current_crtc->crtc_id, |
2313 | 259 | destination.x.as_uint32_t(), | 275 | destination.x.as_uint32_t(), |
2314 | 260 | destination.y.as_uint32_t())) | 276 | destination.y.as_uint32_t())) |
2315 | 261 | { | 277 | { |
2316 | @@ -270,7 +286,7 @@ | |||
2317 | 270 | int result = 0; | 286 | int result = 0; |
2318 | 271 | if (current_crtc) | 287 | if (current_crtc) |
2319 | 272 | { | 288 | { |
2321 | 273 | result = drmModeSetCursor(drm_fd, current_crtc->crtc_id, 0, 0, 0); | 289 | result = drmModeSetCursor(drm_fd_, current_crtc->crtc_id, 0, 0, 0); |
2322 | 274 | 290 | ||
2323 | 275 | if (result) | 291 | if (result) |
2324 | 276 | mir::log_warning("clear_cursor: drmModeSetCursor failed (%s)", | 292 | mir::log_warning("clear_cursor: drmModeSetCursor failed (%s)", |
2325 | @@ -296,7 +312,7 @@ | |||
2326 | 296 | if (connector->connection != DRM_MODE_CONNECTED) | 312 | if (connector->connection != DRM_MODE_CONNECTED) |
2327 | 297 | return false; | 313 | return false; |
2328 | 298 | 314 | ||
2330 | 299 | current_crtc = mgk::find_crtc_for_connector(drm_fd, connector); | 315 | current_crtc = mgk::find_crtc_for_connector(drm_fd_, connector); |
2331 | 300 | 316 | ||
2332 | 301 | 317 | ||
2333 | 302 | return (current_crtc != nullptr); | 318 | return (current_crtc != nullptr); |
2334 | @@ -306,7 +322,7 @@ | |||
2335 | 306 | { | 322 | { |
2336 | 307 | if (!using_saved_crtc) | 323 | if (!using_saved_crtc) |
2337 | 308 | { | 324 | { |
2339 | 309 | drmModeSetCrtc(drm_fd, saved_crtc.crtc_id, saved_crtc.buffer_id, | 325 | drmModeSetCrtc(drm_fd_, saved_crtc.crtc_id, saved_crtc.buffer_id, |
2340 | 310 | saved_crtc.x, saved_crtc.y, | 326 | saved_crtc.x, saved_crtc.y, |
2341 | 311 | &connector->connector_id, 1, &saved_crtc.mode); | 327 | &connector->connector_id, 1, &saved_crtc.mode); |
2342 | 312 | 328 | ||
2343 | @@ -321,8 +337,11 @@ | |||
2344 | 321 | if (power_mode != mode) | 337 | if (power_mode != mode) |
2345 | 322 | { | 338 | { |
2346 | 323 | power_mode = mode; | 339 | power_mode = mode; |
2349 | 324 | drmModeConnectorSetProperty(drm_fd, connector_id, | 340 | drmModeConnectorSetProperty( |
2350 | 325 | dpms_enum_id, mode); | 341 | drm_fd_, |
2351 | 342 | connector->connector_id, | ||
2352 | 343 | dpms_enum_id, | ||
2353 | 344 | mode); | ||
2354 | 326 | } | 345 | } |
2355 | 327 | } | 346 | } |
2356 | 328 | 347 | ||
2357 | @@ -343,7 +362,7 @@ | |||
2358 | 343 | } | 362 | } |
2359 | 344 | 363 | ||
2360 | 345 | int ret = drmModeCrtcSetGamma( | 364 | int ret = drmModeCrtcSetGamma( |
2362 | 346 | drm_fd, | 365 | drm_fd_, |
2363 | 347 | current_crtc->crtc_id, | 366 | current_crtc->crtc_id, |
2364 | 348 | gamma.red.size(), | 367 | gamma.red.size(), |
2365 | 349 | const_cast<uint16_t*>(gamma.red.data()), | 368 | const_cast<uint16_t*>(gamma.red.data()), |
2366 | @@ -357,6 +376,218 @@ | |||
2367 | 357 | // TODO: return bool in future? Then do what with it? | 376 | // TODO: return bool in future? Then do what with it? |
2368 | 358 | } | 377 | } |
2369 | 359 | 378 | ||
2370 | 379 | void mgm::RealKMSOutput::refresh_hardware_state() | ||
2371 | 380 | { | ||
2372 | 381 | connector = kms::get_connector(drm_fd_, connector->connector_id); | ||
2373 | 382 | current_crtc = nullptr; | ||
2374 | 383 | |||
2375 | 384 | if (connector->encoder_id) | ||
2376 | 385 | { | ||
2377 | 386 | auto encoder = kms::get_encoder(drm_fd_, connector->encoder_id); | ||
2378 | 387 | |||
2379 | 388 | if (encoder->crtc_id) | ||
2380 | 389 | { | ||
2381 | 390 | current_crtc = kms::get_crtc(drm_fd_, encoder->crtc_id); | ||
2382 | 391 | } | ||
2383 | 392 | } | ||
2384 | 393 | } | ||
2385 | 394 | |||
2386 | 395 | namespace | ||
2387 | 396 | { | ||
2388 | 397 | |||
2389 | 398 | bool kms_modes_are_equal(drmModeModeInfo const& info1, drmModeModeInfo const& info2) | ||
2390 | 399 | { | ||
2391 | 400 | return (info1.clock == info2.clock && | ||
2392 | 401 | info1.hdisplay == info2.hdisplay && | ||
2393 | 402 | info1.hsync_start == info2.hsync_start && | ||
2394 | 403 | info1.hsync_end == info2.hsync_end && | ||
2395 | 404 | info1.htotal == info2.htotal && | ||
2396 | 405 | info1.hskew == info2.hskew && | ||
2397 | 406 | info1.vdisplay == info2.vdisplay && | ||
2398 | 407 | info1.vsync_start == info2.vsync_start && | ||
2399 | 408 | info1.vsync_end == info2.vsync_end && | ||
2400 | 409 | info1.vtotal == info2.vtotal); | ||
2401 | 410 | } | ||
2402 | 411 | |||
2403 | 412 | double calculate_vrefresh_hz(drmModeModeInfo const& mode) | ||
2404 | 413 | { | ||
2405 | 414 | if (mode.htotal == 0 || mode.vtotal == 0) | ||
2406 | 415 | return 0.0; | ||
2407 | 416 | |||
2408 | 417 | /* mode.clock is in KHz */ | ||
2409 | 418 | double hz = (mode.clock * 100000LL / | ||
2410 | 419 | ((long)mode.htotal * (long)mode.vtotal) | ||
2411 | 420 | ) / 100.0; | ||
2412 | 421 | |||
2413 | 422 | // Actually we don't need floating point at all for this... | ||
2414 | 423 | // TODO: Consider converting our structs to fixed-point ints | ||
2415 | 424 | return hz; | ||
2416 | 425 | } | ||
2417 | 426 | |||
2418 | 427 | mg::DisplayConfigurationOutputType | ||
2419 | 428 | kms_connector_type_to_output_type(uint32_t connector_type) | ||
2420 | 429 | { | ||
2421 | 430 | return static_cast<mg::DisplayConfigurationOutputType>(connector_type); | ||
2422 | 431 | } | ||
2423 | 432 | |||
2424 | 433 | MirSubpixelArrangement kms_subpixel_to_mir_subpixel(uint32_t subpixel) | ||
2425 | 434 | { | ||
2426 | 435 | switch (subpixel) | ||
2427 | 436 | { | ||
2428 | 437 | case DRM_MODE_SUBPIXEL_UNKNOWN: | ||
2429 | 438 | return mir_subpixel_arrangement_unknown; | ||
2430 | 439 | case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: | ||
2431 | 440 | return mir_subpixel_arrangement_horizontal_rgb; | ||
2432 | 441 | case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: | ||
2433 | 442 | return mir_subpixel_arrangement_horizontal_bgr; | ||
2434 | 443 | case DRM_MODE_SUBPIXEL_VERTICAL_RGB: | ||
2435 | 444 | return mir_subpixel_arrangement_vertical_rgb; | ||
2436 | 445 | case DRM_MODE_SUBPIXEL_VERTICAL_BGR: | ||
2437 | 446 | return mir_subpixel_arrangement_vertical_bgr; | ||
2438 | 447 | case DRM_MODE_SUBPIXEL_NONE: | ||
2439 | 448 | return mir_subpixel_arrangement_none; | ||
2440 | 449 | default: | ||
2441 | 450 | return mir_subpixel_arrangement_unknown; | ||
2442 | 451 | } | ||
2443 | 452 | } | ||
2444 | 453 | |||
2445 | 454 | std::vector<uint8_t> edid_for_connector(int drm_fd, uint32_t connector_id) | ||
2446 | 455 | { | ||
2447 | 456 | std::vector<uint8_t> edid; | ||
2448 | 457 | |||
2449 | 458 | mgk::ObjectProperties connector_props{ | ||
2450 | 459 | drm_fd, connector_id, DRM_MODE_OBJECT_CONNECTOR}; | ||
2451 | 460 | |||
2452 | 461 | if (connector_props.has_property("EDID")) | ||
2453 | 462 | { | ||
2454 | 463 | /* | ||
2455 | 464 | * We don't technically need the property information here, but query it | ||
2456 | 465 | * anyway so we can detect if our assumptions about DRM behaviour | ||
2457 | 466 | * become invalid. | ||
2458 | 467 | */ | ||
2459 | 468 | auto property = mgk::DRMModePropertyUPtr{ | ||
2460 | 469 | drmModeGetProperty(drm_fd, connector_props.id_for("EDID")), | ||
2461 | 470 | &drmModeFreeProperty}; | ||
2462 | 471 | |||
2463 | 472 | if (!property) | ||
2464 | 473 | { | ||
2465 | 474 | mir::log_warning( | ||
2466 | 475 | "Failed to get EDID property for connector %u: %i (%s)", | ||
2467 | 476 | connector_id, | ||
2468 | 477 | errno, | ||
2469 | 478 | ::strerror(errno)); | ||
2470 | 479 | return edid; | ||
2471 | 480 | } | ||
2472 | 481 | |||
2473 | 482 | if (!drm_property_type_is(property.get(), DRM_MODE_PROP_BLOB)) | ||
2474 | 483 | { | ||
2475 | 484 | mir::log_warning( | ||
2476 | 485 | "EDID property on connector %u has unexpected type %u", | ||
2477 | 486 | connector_id, | ||
2478 | 487 | property->flags); | ||
2479 | 488 | return edid; | ||
2480 | 489 | } | ||
2481 | 490 | |||
2482 | 491 | // A property ID of 0 means invalid. | ||
2483 | 492 | if (connector_props["EDID"] == 0) | ||
2484 | 493 | { | ||
2485 | 494 | /* | ||
2486 | 495 | * Log a debug message only. This will trigger for broken monitors which | ||
2487 | 496 | * don't provide an EDID, which is not as unusual as you might think... | ||
2488 | 497 | */ | ||
2489 | 498 | mir::log_debug("No EDID data available on connector %u", connector_id); | ||
2490 | 499 | return edid; | ||
2491 | 500 | } | ||
2492 | 501 | |||
2493 | 502 | auto blob = drmModeGetPropertyBlob(drm_fd, connector_props["EDID"]); | ||
2494 | 503 | |||
2495 | 504 | if (!blob) | ||
2496 | 505 | { | ||
2497 | 506 | mir::log_warning( | ||
2498 | 507 | "Failed to get EDID property blob for connector %u: %i (%s)", | ||
2499 | 508 | connector_id, | ||
2500 | 509 | errno, | ||
2501 | 510 | ::strerror(errno)); | ||
2502 | 511 | |||
2503 | 512 | return edid; | ||
2504 | 513 | } | ||
2505 | 514 | |||
2506 | 515 | edid.reserve(blob->length); | ||
2507 | 516 | edid.insert(edid.begin(), | ||
2508 | 517 | reinterpret_cast<uint8_t*>(blob->data), | ||
2509 | 518 | reinterpret_cast<uint8_t*>(blob->data) + blob->length); | ||
2510 | 519 | |||
2511 | 520 | drmModeFreePropertyBlob(blob); | ||
2512 | 521 | |||
2513 | 522 | edid.shrink_to_fit(); | ||
2514 | 523 | } | ||
2515 | 524 | |||
2516 | 525 | return edid; | ||
2517 | 526 | } | ||
2518 | 527 | } | ||
2519 | 528 | |||
2520 | 529 | void mgm::RealKMSOutput::update_from_hardware_state( | ||
2521 | 530 | DisplayConfigurationOutput& output) const | ||
2522 | 531 | { | ||
2523 | 532 | DisplayConfigurationOutputType const type{ | ||
2524 | 533 | kms_connector_type_to_output_type(connector->connector_type)}; | ||
2525 | 534 | geom::Size physical_size{connector->mmWidth, connector->mmHeight}; | ||
2526 | 535 | bool connected{connector->connection == DRM_MODE_CONNECTED}; | ||
2527 | 536 | uint32_t const invalid_mode_index = std::numeric_limits<uint32_t>::max(); | ||
2528 | 537 | uint32_t current_mode_index{invalid_mode_index}; | ||
2529 | 538 | uint32_t preferred_mode_index{invalid_mode_index}; | ||
2530 | 539 | std::vector<DisplayConfigurationMode> modes; | ||
2531 | 540 | std::vector<MirPixelFormat> formats{mir_pixel_format_argb_8888, | ||
2532 | 541 | mir_pixel_format_xrgb_8888}; | ||
2533 | 542 | |||
2534 | 543 | std::vector<uint8_t> edid; | ||
2535 | 544 | if (connected) { | ||
2536 | 545 | /* Only ask for the EDID on connected outputs. There's obviously no monitor EDID | ||
2537 | 546 | * when there is no monitor connected! | ||
2538 | 547 | */ | ||
2539 | 548 | edid = edid_for_connector(drm_fd_, connector->connector_id); | ||
2540 | 549 | } | ||
2541 | 550 | |||
2542 | 551 | drmModeModeInfo current_mode_info = drmModeModeInfo(); | ||
2543 | 552 | GammaCurves gamma; | ||
2544 | 553 | |||
2545 | 554 | /* Get information about the current mode */ | ||
2546 | 555 | if (current_crtc) { | ||
2547 | 556 | current_mode_info = current_crtc->mode; | ||
2548 | 557 | |||
2549 | 558 | if (current_crtc->gamma_size > 0) | ||
2550 | 559 | gamma = mg::LinearGammaLUTs(current_crtc->gamma_size); | ||
2551 | 560 | } | ||
2552 | 561 | |||
2553 | 562 | /* Add all the available modes and find the current and preferred one */ | ||
2554 | 563 | for (int m = 0; m < connector->count_modes; m++) { | ||
2555 | 564 | drmModeModeInfo &mode_info = connector->modes[m]; | ||
2556 | 565 | |||
2557 | 566 | geom::Size size{mode_info.hdisplay, mode_info.vdisplay}; | ||
2558 | 567 | |||
2559 | 568 | double vrefresh_hz = calculate_vrefresh_hz(mode_info); | ||
2560 | 569 | |||
2561 | 570 | modes.push_back({size, vrefresh_hz}); | ||
2562 | 571 | |||
2563 | 572 | if (kms_modes_are_equal(mode_info, current_mode_info)) | ||
2564 | 573 | current_mode_index = m; | ||
2565 | 574 | |||
2566 | 575 | if ((mode_info.type & DRM_MODE_TYPE_PREFERRED) == DRM_MODE_TYPE_PREFERRED) | ||
2567 | 576 | preferred_mode_index = m; | ||
2568 | 577 | } | ||
2569 | 578 | |||
2570 | 579 | output.type = type; | ||
2571 | 580 | output.modes = modes; | ||
2572 | 581 | output.preferred_mode_index = preferred_mode_index; | ||
2573 | 582 | output.physical_size_mm = physical_size; | ||
2574 | 583 | output.connected = connected; | ||
2575 | 584 | output.current_format = mir_pixel_format_xrgb_8888; | ||
2576 | 585 | output.current_mode_index = current_mode_index; | ||
2577 | 586 | output.subpixel_arrangement = kms_subpixel_to_mir_subpixel(connector->subpixel); | ||
2578 | 587 | output.gamma = gamma; | ||
2579 | 588 | output.edid = edid; | ||
2580 | 589 | } | ||
2581 | 590 | |||
2582 | 360 | mgm::FBHandle* mgm::RealKMSOutput::fb_for(gbm_bo* bo, uint32_t width, uint32_t height) const | 591 | mgm::FBHandle* mgm::RealKMSOutput::fb_for(gbm_bo* bo, uint32_t width, uint32_t height) const |
2583 | 361 | { | 592 | { |
2584 | 362 | if (!bo) | 593 | if (!bo) |
2585 | @@ -386,7 +617,7 @@ | |||
2586 | 386 | format = GBM_FORMAT_ARGB8888; | 617 | format = GBM_FORMAT_ARGB8888; |
2587 | 387 | 618 | ||
2588 | 388 | /* Create a KMS FB object with the gbm_bo attached to it. */ | 619 | /* Create a KMS FB object with the gbm_bo attached to it. */ |
2590 | 389 | auto ret = drmModeAddFB2(drm_fd, width, height, format, | 620 | auto ret = drmModeAddFB2(drm_fd_, width, height, format, |
2591 | 390 | handles, strides, offsets, &fb_id, 0); | 621 | handles, strides, offsets, &fb_id, 0); |
2592 | 391 | if (ret) | 622 | if (ret) |
2593 | 392 | return nullptr; | 623 | return nullptr; |
2594 | @@ -397,3 +628,20 @@ | |||
2595 | 397 | 628 | ||
2596 | 398 | return bufobj; | 629 | return bufobj; |
2597 | 399 | } | 630 | } |
2598 | 631 | |||
2599 | 632 | bool mgm::RealKMSOutput::buffer_requires_migration(gbm_bo* bo) const | ||
2600 | 633 | { | ||
2601 | 634 | /* | ||
2602 | 635 | * It's possible that some devices will not require migration - | ||
2603 | 636 | * Intel GPUs can obviously scanout from main memory, as can USB outputs such as | ||
2604 | 637 | * DisplayLink. | ||
2605 | 638 | * | ||
2606 | 639 | * For a first go, just say that *every* device scans out of GPU-private memory. | ||
2607 | 640 | */ | ||
2608 | 641 | return gbm_device_get_fd(gbm_bo_get_device(bo)) != drm_fd_; | ||
2609 | 642 | } | ||
2610 | 643 | |||
2611 | 644 | int mgm::RealKMSOutput::drm_fd() const | ||
2612 | 645 | { | ||
2613 | 646 | return drm_fd_; | ||
2614 | 647 | } | ||
2615 | 400 | 648 | ||
2616 | === modified file 'src/platforms/mesa/server/kms/real_kms_output.h' | |||
2617 | --- src/platforms/mesa/server/kms/real_kms_output.h 2017-03-13 08:12:52 +0000 | |||
2618 | +++ src/platforms/mesa/server/kms/real_kms_output.h 2017-03-22 06:55:00 +0000 | |||
2619 | @@ -38,10 +38,14 @@ | |||
2620 | 38 | class RealKMSOutput : public KMSOutput | 38 | class RealKMSOutput : public KMSOutput |
2621 | 39 | { | 39 | { |
2622 | 40 | public: | 40 | public: |
2625 | 41 | RealKMSOutput(int drm_fd, uint32_t connector_id, | 41 | RealKMSOutput( |
2626 | 42 | std::shared_ptr<PageFlipper> const& page_flipper); | 42 | int drm_fd, |
2627 | 43 | kms::DRMModeConnectorUPtr&& connector, | ||
2628 | 44 | std::shared_ptr<PageFlipper> const& page_flipper); | ||
2629 | 43 | ~RealKMSOutput(); | 45 | ~RealKMSOutput(); |
2630 | 44 | 46 | ||
2631 | 47 | uint32_t id() const override; | ||
2632 | 48 | |||
2633 | 45 | void reset() override; | 49 | void reset() override; |
2634 | 46 | void configure(geometry::Displacement fb_offset, size_t kms_mode_index) override; | 50 | void configure(geometry::Displacement fb_offset, size_t kms_mode_index) override; |
2635 | 47 | geometry::Size size() const override; | 51 | geometry::Size size() const override; |
2636 | @@ -62,14 +66,18 @@ | |||
2637 | 62 | 66 | ||
2638 | 63 | Frame last_frame() const override; | 67 | Frame last_frame() const override; |
2639 | 64 | 68 | ||
2640 | 69 | void refresh_hardware_state() override; | ||
2641 | 70 | void update_from_hardware_state(DisplayConfigurationOutput& output) const override; | ||
2642 | 71 | |||
2643 | 65 | FBHandle* fb_for(gbm_bo* bo, uint32_t width, uint32_t height) const override; | 72 | FBHandle* fb_for(gbm_bo* bo, uint32_t width, uint32_t height) const override; |
2644 | 66 | 73 | ||
2645 | 74 | bool buffer_requires_migration(gbm_bo* bo) const override; | ||
2646 | 75 | int drm_fd() const override; | ||
2647 | 67 | private: | 76 | private: |
2648 | 68 | bool ensure_crtc(); | 77 | bool ensure_crtc(); |
2649 | 69 | void restore_saved_crtc(); | 78 | void restore_saved_crtc(); |
2650 | 70 | 79 | ||
2653 | 71 | int const drm_fd; | 80 | int const drm_fd_; |
2652 | 72 | uint32_t const connector_id; | ||
2654 | 73 | std::shared_ptr<PageFlipper> const page_flipper; | 81 | std::shared_ptr<PageFlipper> const page_flipper; |
2655 | 74 | 82 | ||
2656 | 75 | kms::DRMModeConnectorUPtr connector; | 83 | kms::DRMModeConnectorUPtr connector; |
2657 | 76 | 84 | ||
2658 | === modified file 'src/platforms/mesa/server/kms/real_kms_output_container.cpp' | |||
2659 | --- src/platforms/mesa/server/kms/real_kms_output_container.cpp 2016-01-29 08:18:22 +0000 | |||
2660 | +++ src/platforms/mesa/server/kms/real_kms_output_container.cpp 2017-03-22 06:55:00 +0000 | |||
2661 | @@ -16,39 +16,68 @@ | |||
2662 | 16 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> | 16 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> |
2663 | 17 | */ | 17 | */ |
2664 | 18 | 18 | ||
2665 | 19 | #include <algorithm> | ||
2666 | 19 | #include "real_kms_output_container.h" | 20 | #include "real_kms_output_container.h" |
2667 | 20 | #include "real_kms_output.h" | 21 | #include "real_kms_output.h" |
2668 | 22 | #include "kms-utils/drm_mode_resources.h" | ||
2669 | 21 | 23 | ||
2670 | 22 | namespace mgm = mir::graphics::mesa; | 24 | namespace mgm = mir::graphics::mesa; |
2671 | 23 | 25 | ||
2672 | 24 | mgm::RealKMSOutputContainer::RealKMSOutputContainer( | 26 | mgm::RealKMSOutputContainer::RealKMSOutputContainer( |
2702 | 25 | int drm_fd, std::shared_ptr<PageFlipper> const& page_flipper) | 27 | std::vector<int> const& drm_fds, |
2703 | 26 | : drm_fd{drm_fd}, | 28 | std::function<std::shared_ptr<PageFlipper>(int)> const& construct_page_flipper) |
2704 | 27 | page_flipper{page_flipper} | 29 | : drm_fds{drm_fds}, |
2705 | 28 | { | 30 | construct_page_flipper{construct_page_flipper} |
2706 | 29 | } | 31 | { |
2707 | 30 | 32 | } | |
2708 | 31 | std::shared_ptr<mgm::KMSOutput> | 33 | |
2709 | 32 | mgm::RealKMSOutputContainer::get_kms_output_for(uint32_t connector_id) | 34 | void mgm::RealKMSOutputContainer::for_each_output(std::function<void(std::shared_ptr<KMSOutput> const&)> functor) const |
2710 | 33 | { | 35 | { |
2711 | 34 | std::shared_ptr<KMSOutput> output; | 36 | for(auto& output: outputs) |
2712 | 35 | 37 | functor(output); | |
2713 | 36 | auto output_iter = outputs.find(connector_id); | 38 | } |
2714 | 37 | if (output_iter == outputs.end()) | 39 | |
2715 | 38 | { | 40 | void mgm::RealKMSOutputContainer::update_from_hardware_state() |
2716 | 39 | output = std::make_shared<RealKMSOutput>(drm_fd, connector_id, page_flipper); | 41 | { |
2717 | 40 | outputs[connector_id] = output; | 42 | decltype(outputs) new_outputs; |
2718 | 41 | } | 43 | |
2719 | 42 | else | 44 | for (auto drm_fd : drm_fds) |
2720 | 43 | { | 45 | { |
2721 | 44 | output = output_iter->second; | 46 | kms::DRMModeResources resources{drm_fd}; |
2722 | 45 | } | 47 | |
2723 | 46 | 48 | ||
2724 | 47 | return output; | 49 | for (auto &&connector : resources.connectors()) |
2725 | 48 | } | 50 | { |
2726 | 49 | 51 | // Caution: O(n²) here, but n is the number of outputs, so should | |
2727 | 50 | void mgm::RealKMSOutputContainer::for_each_output(std::function<void(KMSOutput&)> functor) const | 52 | // conservatively be << 100. |
2728 | 51 | { | 53 | auto existing_output = std::find_if( |
2729 | 52 | for(auto& pair: outputs) | 54 | outputs.begin(), |
2730 | 53 | functor(*pair.second); | 55 | outputs.end(), |
2731 | 56 | [&connector, drm_fd](auto const &candidate) | ||
2732 | 57 | { | ||
2733 | 58 | return | ||
2734 | 59 | connector->connector_id == candidate->id() && | ||
2735 | 60 | drm_fd == candidate->drm_fd(); | ||
2736 | 61 | }); | ||
2737 | 62 | |||
2738 | 63 | if (existing_output != outputs.end()) | ||
2739 | 64 | { | ||
2740 | 65 | // We could drop this down to O(n) by being smarter about moving out | ||
2741 | 66 | // of the outputs vector. | ||
2742 | 67 | // | ||
2743 | 68 | // That's a bit of a faff, so just do the simple thing for now. | ||
2744 | 69 | new_outputs.push_back(*existing_output); | ||
2745 | 70 | new_outputs.back()->refresh_hardware_state(); | ||
2746 | 71 | } | ||
2747 | 72 | else | ||
2748 | 73 | { | ||
2749 | 74 | new_outputs.push_back(std::make_shared<RealKMSOutput>( | ||
2750 | 75 | drm_fd, | ||
2751 | 76 | std::move(connector), | ||
2752 | 77 | construct_page_flipper(drm_fd))); | ||
2753 | 78 | } | ||
2754 | 79 | } | ||
2755 | 80 | |||
2756 | 81 | } | ||
2757 | 82 | outputs = new_outputs; | ||
2758 | 54 | } | 83 | } |
2759 | 55 | 84 | ||
2760 | === modified file 'src/platforms/mesa/server/kms/real_kms_output_container.h' | |||
2761 | --- src/platforms/mesa/server/kms/real_kms_output_container.h 2016-01-29 08:18:22 +0000 | |||
2762 | +++ src/platforms/mesa/server/kms/real_kms_output_container.h 2017-03-22 06:55:00 +0000 | |||
2763 | @@ -20,7 +20,7 @@ | |||
2764 | 20 | #define MIR_GRAPHICS_MESA_REAL_KMS_OUTPUT_CONTAINER_H_ | 20 | #define MIR_GRAPHICS_MESA_REAL_KMS_OUTPUT_CONTAINER_H_ |
2765 | 21 | 21 | ||
2766 | 22 | #include "kms_output_container.h" | 22 | #include "kms_output_container.h" |
2768 | 23 | #include <unordered_map> | 23 | #include <vector> |
2769 | 24 | 24 | ||
2770 | 25 | namespace mir | 25 | namespace mir |
2771 | 26 | { | 26 | { |
2772 | @@ -34,15 +34,17 @@ | |||
2773 | 34 | class RealKMSOutputContainer : public KMSOutputContainer | 34 | class RealKMSOutputContainer : public KMSOutputContainer |
2774 | 35 | { | 35 | { |
2775 | 36 | public: | 36 | public: |
2781 | 37 | RealKMSOutputContainer(int drm_fd, std::shared_ptr<PageFlipper> const& page_flipper); | 37 | RealKMSOutputContainer( |
2782 | 38 | 38 | std::vector<int> const& drm_fds, | |
2783 | 39 | std::shared_ptr<KMSOutput> get_kms_output_for(uint32_t connector_id); | 39 | std::function<std::shared_ptr<PageFlipper>(int drm_fd)> const& construct_page_flipper); |
2784 | 40 | void for_each_output(std::function<void(KMSOutput&)> functor) const; | 40 | |
2785 | 41 | 41 | void for_each_output(std::function<void(std::shared_ptr<KMSOutput> const&)> functor) const override; | |
2786 | 42 | |||
2787 | 43 | void update_from_hardware_state() override; | ||
2788 | 42 | private: | 44 | private: |
2792 | 43 | int const drm_fd; | 45 | std::vector<int> const drm_fds; |
2793 | 44 | std::unordered_map<uint32_t,std::shared_ptr<KMSOutput>> outputs; | 46 | std::vector<std::shared_ptr<KMSOutput>> outputs; |
2794 | 45 | std::shared_ptr<PageFlipper> const page_flipper; | 47 | std::function<std::shared_ptr<PageFlipper>(int drm_fd)> const construct_page_flipper; |
2795 | 46 | }; | 48 | }; |
2796 | 47 | 49 | ||
2797 | 48 | } | 50 | } |
2798 | 49 | 51 | ||
2799 | === modified file 'tests/acceptance-tests/test_client_input.cpp' | |||
2800 | --- tests/acceptance-tests/test_client_input.cpp 2017-03-14 02:26:28 +0000 | |||
2801 | +++ tests/acceptance-tests/test_client_input.cpp 2017-03-22 06:55:00 +0000 | |||
2802 | @@ -1578,7 +1578,7 @@ | |||
2803 | 1578 | 1578 | ||
2804 | 1579 | mt::Signal touchscreen_ready; | 1579 | mt::Signal touchscreen_ready; |
2805 | 1580 | fake_touch_screen->on_new_configuration_do( | 1580 | fake_touch_screen->on_new_configuration_do( |
2807 | 1581 | [&touchscreen_ready, second_output](mi::InputDevice const& dev) | 1581 | [&touchscreen_ready](mi::InputDevice const& dev) |
2808 | 1582 | { | 1582 | { |
2809 | 1583 | auto ts = dev.get_touchscreen_settings(); | 1583 | auto ts = dev.get_touchscreen_settings(); |
2810 | 1584 | if (ts.is_set() && ts.value().output_id == second_output) | 1584 | if (ts.is_set() && ts.value().output_id == second_output) |
2811 | @@ -1650,7 +1650,7 @@ | |||
2812 | 1650 | 1650 | ||
2813 | 1651 | mt::Signal touchscreen_ready; | 1651 | mt::Signal touchscreen_ready; |
2814 | 1652 | fake_touch_screen->on_new_configuration_do( | 1652 | fake_touch_screen->on_new_configuration_do( |
2816 | 1653 | [&touchscreen_ready, second_output](mi::InputDevice const& dev) | 1653 | [&touchscreen_ready](mi::InputDevice const& dev) |
2817 | 1654 | { | 1654 | { |
2818 | 1655 | auto ts = dev.get_touchscreen_settings(); | 1655 | auto ts = dev.get_touchscreen_settings(); |
2819 | 1656 | if (ts.is_set() | 1656 | if (ts.is_set() |
2820 | 1657 | 1657 | ||
2821 | === modified file 'tests/include/mir/test/doubles/mock_drm.h' | |||
2822 | --- tests/include/mir/test/doubles/mock_drm.h 2017-01-18 02:29:37 +0000 | |||
2823 | +++ tests/include/mir/test/doubles/mock_drm.h 2017-03-22 06:55:00 +0000 | |||
2824 | @@ -23,6 +23,7 @@ | |||
2825 | 23 | 23 | ||
2826 | 24 | #include <xf86drm.h> | 24 | #include <xf86drm.h> |
2827 | 25 | #include <xf86drmMode.h> | 25 | #include <xf86drmMode.h> |
2828 | 26 | #include <unordered_map> | ||
2829 | 26 | 27 | ||
2830 | 27 | namespace mir | 28 | namespace mir |
2831 | 28 | { | 29 | { |
2832 | @@ -154,12 +155,42 @@ | |||
2833 | 154 | MOCK_METHOD6(drmModeCrtcSetGamma, int(int fd, uint32_t crtc_id, uint32_t size, | 155 | MOCK_METHOD6(drmModeCrtcSetGamma, int(int fd, uint32_t crtc_id, uint32_t size, |
2834 | 155 | uint16_t* red, uint16_t* green, uint16_t* blue)); | 156 | uint16_t* red, uint16_t* green, uint16_t* blue)); |
2835 | 156 | 157 | ||
2837 | 157 | FakeDRMResources fake_drm; | 158 | |
2838 | 159 | void add_crtc( | ||
2839 | 160 | char const* device, | ||
2840 | 161 | uint32_t id, | ||
2841 | 162 | drmModeModeInfo mode); | ||
2842 | 163 | void add_encoder( | ||
2843 | 164 | char const* device, | ||
2844 | 165 | uint32_t encoder_id, | ||
2845 | 166 | uint32_t crtc_id, | ||
2846 | 167 | uint32_t possible_crtcs_mask); | ||
2847 | 168 | void add_connector( | ||
2848 | 169 | char const* device, | ||
2849 | 170 | uint32_t connector_id, | ||
2850 | 171 | uint32_t type, | ||
2851 | 172 | drmModeConnection connection, | ||
2852 | 173 | uint32_t encoder_id, | ||
2853 | 174 | std::vector<drmModeModeInfo>& modes, | ||
2854 | 175 | std::vector<uint32_t>& possible_encoder_ids, | ||
2855 | 176 | geometry::Size const& physical_size, | ||
2856 | 177 | drmModeSubPixel subpixel_arrangement = DRM_MODE_SUBPIXEL_UNKNOWN); | ||
2857 | 178 | |||
2858 | 179 | void prepare(char const* device); | ||
2859 | 180 | void reset(char const* device); | ||
2860 | 181 | |||
2861 | 182 | void generate_event_on(char const* device); | ||
2862 | 183 | |||
2863 | 184 | class IsFdOfDeviceMatcher; | ||
2864 | 185 | friend class IsFdOfDeviceMatcher; | ||
2865 | 158 | 186 | ||
2866 | 159 | private: | 187 | private: |
2867 | 188 | std::unordered_map<std::string, FakeDRMResources> fake_drms; | ||
2868 | 189 | std::unordered_map<int, FakeDRMResources&> fd_to_drm; | ||
2869 | 160 | drmModeObjectProperties empty_object_props; | 190 | drmModeObjectProperties empty_object_props; |
2870 | 161 | }; | 191 | }; |
2871 | 162 | 192 | ||
2872 | 193 | testing::Matcher<int> IsFdOfDevice(char const* device); | ||
2873 | 163 | } | 194 | } |
2874 | 164 | } | 195 | } |
2875 | 165 | } | 196 | } |
2876 | 166 | 197 | ||
2877 | === modified file 'tests/include/mir/test/doubles/mock_gbm.h' | |||
2878 | --- tests/include/mir/test/doubles/mock_gbm.h 2015-07-16 07:03:19 +0000 | |||
2879 | +++ tests/include/mir/test/doubles/mock_gbm.h 2017-03-22 06:55:00 +0000 | |||
2880 | @@ -80,6 +80,7 @@ | |||
2881 | 80 | MOCK_METHOD3(gbm_bo_write, bool(struct gbm_bo *bo, const void *buf, size_t count)); | 80 | MOCK_METHOD3(gbm_bo_write, bool(struct gbm_bo *bo, const void *buf, size_t count)); |
2882 | 81 | MOCK_METHOD1(gbm_bo_destroy, void(struct gbm_bo *bo)); | 81 | MOCK_METHOD1(gbm_bo_destroy, void(struct gbm_bo *bo)); |
2883 | 82 | MOCK_METHOD4(gbm_bo_import, struct gbm_bo*(struct gbm_device*, uint32_t, void*, uint32_t)); | 82 | MOCK_METHOD4(gbm_bo_import, struct gbm_bo*(struct gbm_device*, uint32_t, void*, uint32_t)); |
2884 | 83 | MOCK_METHOD1(gbm_bo_get_fd, int(gbm_bo*)); | ||
2885 | 83 | 84 | ||
2886 | 84 | FakeGBMResources fake_gbm; | 85 | FakeGBMResources fake_gbm; |
2887 | 85 | 86 | ||
2888 | 86 | 87 | ||
2889 | === modified file 'tests/include/mir/test/doubles/mock_gl.h' | |||
2890 | --- tests/include/mir/test/doubles/mock_gl.h 2016-06-30 13:41:11 +0000 | |||
2891 | +++ tests/include/mir/test/doubles/mock_gl.h 2017-03-22 06:55:00 +0000 | |||
2892 | @@ -110,6 +110,7 @@ | |||
2893 | 110 | const GLvoid *)); | 110 | const GLvoid *)); |
2894 | 111 | MOCK_METHOD4(glViewport, void(GLint, GLint, GLsizei, GLsizei)); | 111 | MOCK_METHOD4(glViewport, void(GLint, GLint, GLsizei, GLsizei)); |
2895 | 112 | MOCK_METHOD1(glGenerateMipmap, void(GLenum target)); | 112 | MOCK_METHOD1(glGenerateMipmap, void(GLenum target)); |
2896 | 113 | MOCK_METHOD4(glDrawElements, void(GLenum, GLsizei, GLenum, const GLvoid*)); | ||
2897 | 113 | }; | 114 | }; |
2898 | 114 | 115 | ||
2899 | 115 | } | 116 | } |
2900 | 116 | 117 | ||
2901 | === modified file 'tests/mir_test_doubles/mock_drm.cpp' | |||
2902 | --- tests/mir_test_doubles/mock_drm.cpp 2017-01-18 02:29:37 +0000 | |||
2903 | +++ tests/mir_test_doubles/mock_drm.cpp 2017-03-22 06:55:00 +0000 | |||
2904 | @@ -24,6 +24,8 @@ | |||
2905 | 24 | #include <stdexcept> | 24 | #include <stdexcept> |
2906 | 25 | #include <unistd.h> | 25 | #include <unistd.h> |
2907 | 26 | #include <dlfcn.h> | 26 | #include <dlfcn.h> |
2908 | 27 | #include <system_error> | ||
2909 | 28 | #include <boost/throw_exception.hpp> | ||
2910 | 27 | 29 | ||
2911 | 28 | namespace mtd=mir::test::doubles; | 30 | namespace mtd=mir::test::doubles; |
2912 | 29 | namespace geom = mir::geometry; | 31 | namespace geom = mir::geometry; |
2913 | @@ -239,25 +241,60 @@ | |||
2914 | 239 | memset(&empty_object_props, 0, sizeof(empty_object_props)); | 241 | memset(&empty_object_props, 0, sizeof(empty_object_props)); |
2915 | 240 | 242 | ||
2916 | 241 | ON_CALL(*this, open(_,_,_)) | 243 | ON_CALL(*this, open(_,_,_)) |
2918 | 242 | .WillByDefault(Return(fake_drm.fd())); | 244 | .WillByDefault( |
2919 | 245 | WithArg<0>( | ||
2920 | 246 | Invoke( | ||
2921 | 247 | [this](char const* device_path) | ||
2922 | 248 | { | ||
2923 | 249 | auto fd = fake_drms[device_path].fd(); | ||
2924 | 250 | fd_to_drm.insert({fd, fake_drms[device_path]}); | ||
2925 | 251 | return fd; | ||
2926 | 252 | }))); | ||
2927 | 243 | 253 | ||
2928 | 244 | ON_CALL(*this, drmOpen(_,_)) | 254 | ON_CALL(*this, drmOpen(_,_)) |
2930 | 245 | .WillByDefault(Return(fake_drm.fd())); | 255 | .WillByDefault( |
2931 | 256 | InvokeWithoutArgs( | ||
2932 | 257 | [this]() | ||
2933 | 258 | { | ||
2934 | 259 | auto fd = fake_drms["/dev/dri/card0"].fd(); | ||
2935 | 260 | fd_to_drm.insert({fd, fake_drms["/dev/dri/card0"]}); | ||
2936 | 261 | return fd; | ||
2937 | 262 | })); | ||
2938 | 246 | 263 | ||
2939 | 247 | ON_CALL(*this, drmClose(_)) | 264 | ON_CALL(*this, drmClose(_)) |
2941 | 248 | .WillByDefault(WithArg<0>(Invoke([&](int fd){ return close(fd); }))); | 265 | .WillByDefault(Invoke([](int fd){ return close(fd); })); |
2942 | 249 | 266 | ||
2943 | 250 | ON_CALL(*this, drmModeGetResources(_)) | 267 | ON_CALL(*this, drmModeGetResources(_)) |
2945 | 251 | .WillByDefault(Return(fake_drm.resources_ptr())); | 268 | .WillByDefault( |
2946 | 269 | Invoke( | ||
2947 | 270 | [this](int fd) | ||
2948 | 271 | { | ||
2949 | 272 | return fd_to_drm.at(fd).resources_ptr(); | ||
2950 | 273 | })); | ||
2951 | 252 | 274 | ||
2952 | 253 | ON_CALL(*this, drmModeGetCrtc(_, _)) | 275 | ON_CALL(*this, drmModeGetCrtc(_, _)) |
2954 | 254 | .WillByDefault(WithArgs<1>(Invoke(&fake_drm, &FakeDRMResources::find_crtc))); | 276 | .WillByDefault( |
2955 | 277 | Invoke( | ||
2956 | 278 | [this](int fd, uint32_t crtc_id) | ||
2957 | 279 | { | ||
2958 | 280 | return fd_to_drm.at(fd).find_crtc(crtc_id); | ||
2959 | 281 | })); | ||
2960 | 255 | 282 | ||
2961 | 256 | ON_CALL(*this, drmModeGetEncoder(_, _)) | 283 | ON_CALL(*this, drmModeGetEncoder(_, _)) |
2963 | 257 | .WillByDefault(WithArgs<1>(Invoke(&fake_drm, &FakeDRMResources::find_encoder))); | 284 | .WillByDefault( |
2964 | 285 | Invoke( | ||
2965 | 286 | [this](int fd, uint32_t encoder_id) | ||
2966 | 287 | { | ||
2967 | 288 | return fd_to_drm.at(fd).find_encoder(encoder_id); | ||
2968 | 289 | })); | ||
2969 | 258 | 290 | ||
2970 | 259 | ON_CALL(*this, drmModeGetConnector(_, _)) | 291 | ON_CALL(*this, drmModeGetConnector(_, _)) |
2972 | 260 | .WillByDefault(WithArgs<1>(Invoke(&fake_drm, &FakeDRMResources::find_connector))); | 292 | .WillByDefault( |
2973 | 293 | Invoke( | ||
2974 | 294 | [this](int fd, uint32_t connector_id) | ||
2975 | 295 | { | ||
2976 | 296 | return fd_to_drm.at(fd).find_connector(connector_id); | ||
2977 | 297 | })); | ||
2978 | 261 | 298 | ||
2979 | 262 | ON_CALL(*this, drmModeObjectGetProperties(_, _, _)) | 299 | ON_CALL(*this, drmModeObjectGetProperties(_, _, _)) |
2980 | 263 | .WillByDefault(Return(&empty_object_props)); | 300 | .WillByDefault(Return(&empty_object_props)); |
2981 | @@ -277,6 +314,118 @@ | |||
2982 | 277 | global_mock = nullptr; | 314 | global_mock = nullptr; |
2983 | 278 | } | 315 | } |
2984 | 279 | 316 | ||
2985 | 317 | void mtd::MockDRM::add_crtc(char const *device, uint32_t id, drmModeModeInfo mode) | ||
2986 | 318 | { | ||
2987 | 319 | fake_drms[device].add_crtc(id, mode); | ||
2988 | 320 | } | ||
2989 | 321 | |||
2990 | 322 | void mtd::MockDRM::add_encoder( | ||
2991 | 323 | char const *device, | ||
2992 | 324 | uint32_t encoder_id, | ||
2993 | 325 | uint32_t crtc_id, | ||
2994 | 326 | uint32_t possible_crtcs_mask) | ||
2995 | 327 | { | ||
2996 | 328 | fake_drms[device].add_encoder(encoder_id, crtc_id, possible_crtcs_mask); | ||
2997 | 329 | } | ||
2998 | 330 | |||
2999 | 331 | void mtd::MockDRM::prepare(char const *device) | ||
3000 | 332 | { | ||
3001 | 333 | fake_drms[device].prepare(); | ||
3002 | 334 | } | ||
3003 | 335 | |||
3004 | 336 | void mtd::MockDRM::reset(char const *device) | ||
3005 | 337 | { | ||
3006 | 338 | fake_drms[device].reset(); | ||
3007 | 339 | } | ||
3008 | 340 | |||
3009 | 341 | void mtd::MockDRM::generate_event_on(char const *device) | ||
3010 | 342 | { | ||
3011 | 343 | auto const fd = fake_drms[device].write_fd(); | ||
3012 | 344 | |||
3013 | 345 | if (write(fd, "a", 1) != 1) | ||
3014 | 346 | { | ||
3015 | 347 | BOOST_THROW_EXCEPTION( | ||
3016 | 348 | std::system_error(errno, std::system_category(), "Failed to make fake DRM event")); | ||
3017 | 349 | } | ||
3018 | 350 | } | ||
3019 | 351 | |||
3020 | 352 | void mtd::MockDRM::add_connector( | ||
3021 | 353 | char const *device, | ||
3022 | 354 | uint32_t connector_id, | ||
3023 | 355 | uint32_t type, | ||
3024 | 356 | drmModeConnection connection, | ||
3025 | 357 | uint32_t encoder_id, | ||
3026 | 358 | std::vector<drmModeModeInfo> &modes, | ||
3027 | 359 | std::vector<uint32_t> &possible_encoder_ids, | ||
3028 | 360 | geometry::Size const &physical_size, | ||
3029 | 361 | drmModeSubPixel subpixel_arrangement) | ||
3030 | 362 | { | ||
3031 | 363 | fake_drms[device].add_connector( | ||
3032 | 364 | connector_id, | ||
3033 | 365 | type, | ||
3034 | 366 | connection, | ||
3035 | 367 | encoder_id, | ||
3036 | 368 | modes, | ||
3037 | 369 | possible_encoder_ids, | ||
3038 | 370 | physical_size, | ||
3039 | 371 | subpixel_arrangement); | ||
3040 | 372 | } | ||
3041 | 373 | |||
3042 | 374 | MATCHER_P2(IsFdOfDevice, devname, fds, "") | ||
3043 | 375 | { | ||
3044 | 376 | return std::find( | ||
3045 | 377 | fds.begin(), | ||
3046 | 378 | fds.end(), | ||
3047 | 379 | arg) != fds.end(); | ||
3048 | 380 | } | ||
3049 | 381 | |||
3050 | 382 | class mtd::MockDRM::IsFdOfDeviceMatcher : public ::testing::MatcherInterface<int> | ||
3051 | 383 | { | ||
3052 | 384 | public: | ||
3053 | 385 | IsFdOfDeviceMatcher(char const* device) | ||
3054 | 386 | : device{device} | ||
3055 | 387 | { | ||
3056 | 388 | } | ||
3057 | 389 | |||
3058 | 390 | void DescribeTo(::std::ostream *os) const override | ||
3059 | 391 | { | ||
3060 | 392 | |||
3061 | 393 | *os | ||
3062 | 394 | << "Is an fd of DRM device " | ||
3063 | 395 | << device | ||
3064 | 396 | << " (one of: " | ||
3065 | 397 | << ::testing::PrintToString(fds_for_device(device.c_str())) | ||
3066 | 398 | << ")"; | ||
3067 | 399 | } | ||
3068 | 400 | |||
3069 | 401 | bool MatchAndExplain(int x, testing::MatchResultListener *listener) const override | ||
3070 | 402 | { | ||
3071 | 403 | testing::Matcher<std::vector<int>> matcher = testing::Contains(x); | ||
3072 | 404 | return matcher.MatchAndExplain(fds_for_device(device.c_str()), listener); | ||
3073 | 405 | } | ||
3074 | 406 | |||
3075 | 407 | private: | ||
3076 | 408 | std::vector<int> fds_for_device(char const* device) const | ||
3077 | 409 | { | ||
3078 | 410 | std::vector<int> device_fds; | ||
3079 | 411 | for (auto const& pair : global_mock->fd_to_drm) | ||
3080 | 412 | { | ||
3081 | 413 | if (&global_mock->fake_drms[device] == &pair.second) | ||
3082 | 414 | { | ||
3083 | 415 | device_fds.push_back(pair.first); | ||
3084 | 416 | } | ||
3085 | 417 | } | ||
3086 | 418 | return device_fds; | ||
3087 | 419 | } | ||
3088 | 420 | |||
3089 | 421 | std::string const device; | ||
3090 | 422 | }; | ||
3091 | 423 | |||
3092 | 424 | testing::Matcher<int> mtd::IsFdOfDevice(char const* device) | ||
3093 | 425 | { | ||
3094 | 426 | return ::testing::MakeMatcher(new mtd::MockDRM::IsFdOfDeviceMatcher(device)); | ||
3095 | 427 | } | ||
3096 | 428 | |||
3097 | 280 | int drmOpen(const char *name, const char *busid) | 429 | int drmOpen(const char *name, const char *busid) |
3098 | 281 | { | 430 | { |
3099 | 282 | return global_mock->drmOpen(name, busid); | 431 | return global_mock->drmOpen(name, busid); |
3100 | 283 | 432 | ||
3101 | === modified file 'tests/mir_test_doubles/mock_egl.cpp' | |||
3102 | --- tests/mir_test_doubles/mock_egl.cpp 2017-01-18 02:29:37 +0000 | |||
3103 | +++ tests/mir_test_doubles/mock_egl.cpp 2017-03-22 06:55:00 +0000 | |||
3104 | @@ -156,7 +156,7 @@ | |||
3105 | 156 | { | 156 | { |
3106 | 157 | using namespace testing; | 157 | using namespace testing; |
3107 | 158 | 158 | ||
3109 | 159 | const char* egl_exts = "EGL_KHR_image EGL_KHR_image_base EGL_KHR_image_pixmap"; | 159 | const char* egl_exts = "EGL_KHR_image EGL_KHR_image_base EGL_KHR_image_pixmap EGL_EXT_image_dma_buf_import"; |
3110 | 160 | ON_CALL(*this, eglQueryString(_,EGL_EXTENSIONS)) | 160 | ON_CALL(*this, eglQueryString(_,EGL_EXTENSIONS)) |
3111 | 161 | .WillByDefault(Return(egl_exts)); | 161 | .WillByDefault(Return(egl_exts)); |
3112 | 162 | } | 162 | } |
3113 | 163 | 163 | ||
3114 | === modified file 'tests/mir_test_doubles/mock_gbm.cpp' | |||
3115 | --- tests/mir_test_doubles/mock_gbm.cpp 2015-07-16 07:03:19 +0000 | |||
3116 | +++ tests/mir_test_doubles/mock_gbm.cpp 2017-03-22 06:55:00 +0000 | |||
3117 | @@ -182,3 +182,8 @@ | |||
3118 | 182 | { | 182 | { |
3119 | 183 | return global_mock->gbm_bo_import(device, type, data, flags); | 183 | return global_mock->gbm_bo_import(device, type, data, flags); |
3120 | 184 | } | 184 | } |
3121 | 185 | |||
3122 | 186 | int gbm_bo_get_fd(gbm_bo* bo) | ||
3123 | 187 | { | ||
3124 | 188 | return global_mock->gbm_bo_get_fd(bo); | ||
3125 | 189 | } | ||
3126 | 185 | 190 | ||
3127 | === modified file 'tests/mir_test_doubles/mock_gl.cpp' | |||
3128 | --- tests/mir_test_doubles/mock_gl.cpp 2016-06-30 13:41:11 +0000 | |||
3129 | +++ tests/mir_test_doubles/mock_gl.cpp 2017-03-22 06:55:00 +0000 | |||
3130 | @@ -20,6 +20,8 @@ | |||
3131 | 20 | #include "mir/test/doubles/mock_gl.h" | 20 | #include "mir/test/doubles/mock_gl.h" |
3132 | 21 | #include <gtest/gtest.h> | 21 | #include <gtest/gtest.h> |
3133 | 22 | 22 | ||
3134 | 23 | #include <GLES2/gl2.h> | ||
3135 | 24 | |||
3136 | 23 | #include <cstring> | 25 | #include <cstring> |
3137 | 24 | 26 | ||
3138 | 25 | namespace mtd = mir::test::doubles; | 27 | namespace mtd = mir::test::doubles; |
3139 | @@ -462,3 +464,9 @@ | |||
3140 | 462 | CHECK_GLOBAL_VOID_MOCK(); | 464 | CHECK_GLOBAL_VOID_MOCK(); |
3141 | 463 | global_mock_gl->glPixelStorei(pname, param); | 465 | global_mock_gl->glPixelStorei(pname, param); |
3142 | 464 | } | 466 | } |
3143 | 467 | |||
3144 | 468 | void glDrawElements(GLenum mode, GLsizei count, GLenum type, const void* indicies) | ||
3145 | 469 | { | ||
3146 | 470 | CHECK_GLOBAL_VOID_MOCK(); | ||
3147 | 471 | global_mock_gl->glDrawElements(mode, count, type, indicies); | ||
3148 | 472 | } | ||
3149 | 465 | 473 | ||
3150 | === modified file 'tests/unit-tests/platforms/mesa/kms-utils/test_connector_utils.cpp' | |||
3151 | --- tests/unit-tests/platforms/mesa/kms-utils/test_connector_utils.cpp 2016-05-20 02:15:41 +0000 | |||
3152 | +++ tests/unit-tests/platforms/mesa/kms-utils/test_connector_utils.cpp 2017-03-22 06:55:00 +0000 | |||
3153 | @@ -25,6 +25,7 @@ | |||
3154 | 25 | 25 | ||
3155 | 26 | #include <gtest/gtest.h> | 26 | #include <gtest/gtest.h> |
3156 | 27 | #include <gmock/gmock.h> | 27 | #include <gmock/gmock.h> |
3157 | 28 | #include <fcntl.h> | ||
3158 | 28 | 29 | ||
3159 | 29 | namespace mtd = mir::test::doubles; | 30 | namespace mtd = mir::test::doubles; |
3160 | 30 | namespace mgk = mir::graphics::kms; | 31 | namespace mgk = mir::graphics::kms; |
3161 | @@ -35,30 +36,37 @@ | |||
3162 | 35 | { | 36 | { |
3163 | 36 | using namespace testing; | 37 | using namespace testing; |
3164 | 37 | NiceMock<mtd::MockDRM> drm; | 38 | NiceMock<mtd::MockDRM> drm; |
3167 | 38 | 39 | char const* const drm_device = "/dev/dri/card0"; | |
3166 | 39 | auto& resources = drm.fake_drm; | ||
3168 | 40 | 40 | ||
3169 | 41 | std::array<uint32_t, 3> const crtc_ids = {{21, 25, 30}}; | 41 | std::array<uint32_t, 3> const crtc_ids = {{21, 25, 30}}; |
3170 | 42 | std::array<uint32_t, 3> const encoder_ids = {{3, 5, 7}}; | 42 | std::array<uint32_t, 3> const encoder_ids = {{3, 5, 7}}; |
3171 | 43 | std::array<uint32_t, 3> const connector_id = {{9, 10, 11}}; | 43 | std::array<uint32_t, 3> const connector_id = {{9, 10, 11}}; |
3172 | 44 | 44 | ||
3174 | 45 | resources.reset(); | 45 | drm.reset(drm_device); |
3175 | 46 | // Add a bunch of CRTCs | 46 | // Add a bunch of CRTCs |
3176 | 47 | for (auto id : crtc_ids) | 47 | for (auto id : crtc_ids) |
3177 | 48 | { | 48 | { |
3179 | 49 | resources.add_crtc(id, drmModeModeInfo()); | 49 | drm.add_crtc(drm_device, id, drmModeModeInfo()); |
3180 | 50 | } | 50 | } |
3181 | 51 | // Add an encoder that can only drive any CRTC... | 51 | // Add an encoder that can only drive any CRTC... |
3183 | 52 | resources.add_encoder(encoder_ids[0], 0, 1 << 0 | 1 << 1 | 1 << 2); | 52 | drm.add_encoder(drm_device, encoder_ids[0], 0, 1 << 0 | 1 << 1 | 1 << 2); |
3184 | 53 | // ...and one that can only drive the third... | 53 | // ...and one that can only drive the third... |
3186 | 54 | resources.add_encoder(encoder_ids[1], 0, 1 << 2); | 54 | drm.add_encoder(drm_device, encoder_ids[1], 0, 1 << 2); |
3187 | 55 | // ...and one that can only drive the second two... | 55 | // ...and one that can only drive the second two... |
3189 | 56 | resources.add_encoder(encoder_ids[2], 0, 1 << 1 | 1 << 2); | 56 | drm.add_encoder(drm_device, encoder_ids[2], 0, 1 << 1 | 1 << 2); |
3190 | 57 | 57 | ||
3192 | 58 | std::vector<drmModeModeInfo> modes{resources.create_mode(1200, 1600, 138500, 1400, 1800, mtd::FakeDRMResources::ModePreference::PreferredMode)}; | 58 | std::vector<drmModeModeInfo> modes{ |
3193 | 59 | mtd::FakeDRMResources::create_mode( | ||
3194 | 60 | 1200, | ||
3195 | 61 | 1600, | ||
3196 | 62 | 138500, | ||
3197 | 63 | 1400, | ||
3198 | 64 | 1800, | ||
3199 | 65 | mtd::FakeDRMResources::ModePreference::PreferredMode)}; | ||
3200 | 59 | // Finally, add a connector that can only be driven by any encoder... | 66 | // Finally, add a connector that can only be driven by any encoder... |
3201 | 60 | std::vector<uint32_t> any_encoder{encoder_ids.begin(), encoder_ids.end()}; | 67 | std::vector<uint32_t> any_encoder{encoder_ids.begin(), encoder_ids.end()}; |
3203 | 61 | resources.add_connector( | 68 | drm.add_connector( |
3204 | 69 | drm_device, | ||
3205 | 62 | connector_id[0], | 70 | connector_id[0], |
3206 | 63 | DRM_MODE_CONNECTOR_VGA, | 71 | DRM_MODE_CONNECTOR_VGA, |
3207 | 64 | DRM_MODE_CONNECTED, | 72 | DRM_MODE_CONNECTED, |
3208 | @@ -69,7 +77,8 @@ | |||
3209 | 69 | 77 | ||
3210 | 70 | // ...then one that can only be driven by the third... | 78 | // ...then one that can only be driven by the third... |
3211 | 71 | std::vector<uint32_t> third_encoder{encoder_ids[2]}; | 79 | std::vector<uint32_t> third_encoder{encoder_ids[2]}; |
3213 | 72 | resources.add_connector( | 80 | drm.add_connector( |
3214 | 81 | drm_device, | ||
3215 | 73 | connector_id[1], | 82 | connector_id[1], |
3216 | 74 | DRM_MODE_CONNECTOR_VGA, | 83 | DRM_MODE_CONNECTOR_VGA, |
3217 | 75 | DRM_MODE_CONNECTED, | 84 | DRM_MODE_CONNECTED, |
3218 | @@ -80,7 +89,8 @@ | |||
3219 | 80 | 89 | ||
3220 | 81 | // ...and finally one that can only be driven by the second... | 90 | // ...and finally one that can only be driven by the second... |
3221 | 82 | std::vector<uint32_t> second_encoder{encoder_ids[1]}; | 91 | std::vector<uint32_t> second_encoder{encoder_ids[1]}; |
3223 | 83 | resources.add_connector( | 92 | drm.add_connector( |
3224 | 93 | drm_device, | ||
3225 | 84 | connector_id[2], | 94 | connector_id[2], |
3226 | 85 | DRM_MODE_CONNECTOR_VGA, | 95 | DRM_MODE_CONNECTOR_VGA, |
3227 | 86 | DRM_MODE_CONNECTED, | 96 | DRM_MODE_CONNECTED, |
3228 | @@ -89,21 +99,23 @@ | |||
3229 | 89 | second_encoder, | 99 | second_encoder, |
3230 | 90 | mir::geometry::Size{300, 200}); | 100 | mir::geometry::Size{300, 200}); |
3231 | 91 | 101 | ||
3233 | 92 | resources.prepare(); | 102 | drm.prepare(drm_device); |
3234 | 103 | |||
3235 | 104 | auto const drm_fd = open(drm_device, 0, 0); | ||
3236 | 93 | 105 | ||
3237 | 94 | auto crtc = mgk::find_crtc_for_connector( | 106 | auto crtc = mgk::find_crtc_for_connector( |
3240 | 95 | resources.fd(), | 107 | drm_fd, |
3241 | 96 | mgk::get_connector(resources.fd(), connector_id[0])); | 108 | mgk::get_connector(drm_fd, connector_id[0])); |
3242 | 97 | EXPECT_THAT(crtc->crtc_id, AnyOf(crtc_ids[0], crtc_ids[1], crtc_ids[2])); | 109 | EXPECT_THAT(crtc->crtc_id, AnyOf(crtc_ids[0], crtc_ids[1], crtc_ids[2])); |
3243 | 98 | 110 | ||
3244 | 99 | crtc = mgk::find_crtc_for_connector( | 111 | crtc = mgk::find_crtc_for_connector( |
3247 | 100 | resources.fd(), | 112 | drm_fd, |
3248 | 101 | mgk::get_connector(resources.fd(), connector_id[1])); | 113 | mgk::get_connector(drm_fd, connector_id[1])); |
3249 | 102 | EXPECT_THAT(crtc->crtc_id, AnyOf(crtc_ids[1], crtc_ids[2])); | 114 | EXPECT_THAT(crtc->crtc_id, AnyOf(crtc_ids[1], crtc_ids[2])); |
3250 | 103 | 115 | ||
3251 | 104 | crtc = mgk::find_crtc_for_connector( | 116 | crtc = mgk::find_crtc_for_connector( |
3254 | 105 | resources.fd(), | 117 | drm_fd, |
3255 | 106 | mgk::get_connector(resources.fd(), connector_id[2])); | 118 | mgk::get_connector(drm_fd, connector_id[2])); |
3256 | 107 | EXPECT_THAT(crtc->crtc_id, Eq(crtc_ids[2])); | 119 | EXPECT_THAT(crtc->crtc_id, Eq(crtc_ids[2])); |
3257 | 108 | } | 120 | } |
3258 | 109 | 121 | ||
3259 | @@ -111,30 +123,38 @@ | |||
3260 | 111 | { | 123 | { |
3261 | 112 | using namespace testing; | 124 | using namespace testing; |
3262 | 113 | NiceMock<mtd::MockDRM> drm; | 125 | NiceMock<mtd::MockDRM> drm; |
3265 | 114 | 126 | char const* const drm_device = "/dev/dri/card0"; | |
3266 | 115 | auto& resources = drm.fake_drm; | 127 | int const drm_fd = open(drm_device, 0, 0); |
3267 | 116 | 128 | ||
3268 | 117 | std::array<uint32_t, 2> const crtc_ids = {{21, 25}}; | 129 | std::array<uint32_t, 2> const crtc_ids = {{21, 25}}; |
3269 | 118 | std::array<uint32_t, 2> const encoder_ids = {{3, 5}}; | 130 | std::array<uint32_t, 2> const encoder_ids = {{3, 5}}; |
3270 | 119 | std::array<uint32_t, 2> const connector_id = {{9, 10}}; | 131 | std::array<uint32_t, 2> const connector_id = {{9, 10}}; |
3271 | 120 | 132 | ||
3274 | 121 | resources.reset(); | 133 | drm.reset(drm_device); |
3275 | 122 | auto boring_mode = resources.create_mode(1200, 1600, 138500, 1400, 1800, mtd::FakeDRMResources::ModePreference::PreferredMode); | 134 | auto boring_mode = |
3276 | 135 | mtd::FakeDRMResources::create_mode( | ||
3277 | 136 | 1200, | ||
3278 | 137 | 1600, | ||
3279 | 138 | 138500, | ||
3280 | 139 | 1400, | ||
3281 | 140 | 1800, | ||
3282 | 141 | mtd::FakeDRMResources::ModePreference::PreferredMode); | ||
3283 | 123 | // Add an active CRTC... | 142 | // Add an active CRTC... |
3285 | 124 | resources.add_crtc(crtc_ids[0], boring_mode); | 143 | drm.add_crtc(drm_device, crtc_ids[0], boring_mode); |
3286 | 125 | // ...and an inactive one. | 144 | // ...and an inactive one. |
3288 | 126 | resources.add_crtc(crtc_ids[1], drmModeModeInfo()); | 145 | drm.add_crtc(drm_device, crtc_ids[1], drmModeModeInfo()); |
3289 | 127 | 146 | ||
3290 | 128 | // Add an encoder hooked up to the active CRTC | 147 | // Add an encoder hooked up to the active CRTC |
3292 | 129 | resources.add_encoder(encoder_ids[0], crtc_ids[0], 1 << 0 | 1 << 1); | 148 | drm.add_encoder(drm_device, encoder_ids[0], crtc_ids[0], 1 << 0 | 1 << 1); |
3293 | 130 | // ...and one not connected to anything | 149 | // ...and one not connected to anything |
3295 | 131 | resources.add_encoder(encoder_ids[1], 0, 1 << 0 | 1 << 1); | 150 | drm.add_encoder(drm_device, encoder_ids[1], 0, 1 << 0 | 1 << 1); |
3296 | 132 | 151 | ||
3297 | 133 | std::vector<drmModeModeInfo> modes{boring_mode}; | 152 | std::vector<drmModeModeInfo> modes{boring_mode}; |
3298 | 134 | std::vector<uint32_t> any_encoder{encoder_ids.begin(), encoder_ids.end()}; | 153 | std::vector<uint32_t> any_encoder{encoder_ids.begin(), encoder_ids.end()}; |
3299 | 135 | 154 | ||
3300 | 136 | // Finally, a connector hooked up to a CRTC-encoder | 155 | // Finally, a connector hooked up to a CRTC-encoder |
3302 | 137 | resources.add_connector( | 156 | drm.add_connector( |
3303 | 157 | drm_device, | ||
3304 | 138 | connector_id[0], | 158 | connector_id[0], |
3305 | 139 | DRM_MODE_CONNECTOR_VGA, | 159 | DRM_MODE_CONNECTOR_VGA, |
3306 | 140 | DRM_MODE_CONNECTED, | 160 | DRM_MODE_CONNECTED, |
3307 | @@ -144,7 +164,8 @@ | |||
3308 | 144 | mir::geometry::Size{300, 200}); | 164 | mir::geometry::Size{300, 200}); |
3309 | 145 | 165 | ||
3310 | 146 | // ... and one not hooked up to anything | 166 | // ... and one not hooked up to anything |
3312 | 147 | resources.add_connector( | 167 | drm.add_connector( |
3313 | 168 | drm_device, | ||
3314 | 148 | connector_id[1], | 169 | connector_id[1], |
3315 | 149 | DRM_MODE_CONNECTOR_VGA, | 170 | DRM_MODE_CONNECTOR_VGA, |
3316 | 150 | DRM_MODE_CONNECTED, | 171 | DRM_MODE_CONNECTED, |
3317 | @@ -153,11 +174,11 @@ | |||
3318 | 153 | any_encoder, | 174 | any_encoder, |
3319 | 154 | mir::geometry::Size{300, 200}); | 175 | mir::geometry::Size{300, 200}); |
3320 | 155 | 176 | ||
3322 | 156 | resources.prepare(); | 177 | drm.prepare(drm_device); |
3323 | 157 | 178 | ||
3324 | 158 | auto crtc = mgk::find_crtc_for_connector( | 179 | auto crtc = mgk::find_crtc_for_connector( |
3327 | 159 | resources.fd(), | 180 | drm_fd, |
3328 | 160 | mgk::get_connector(resources.fd(), connector_id[1])); | 181 | mgk::get_connector(drm_fd, connector_id[1])); |
3329 | 161 | EXPECT_THAT(crtc->crtc_id, Eq(crtc_ids[1])); | 182 | EXPECT_THAT(crtc->crtc_id, Eq(crtc_ids[1])); |
3330 | 162 | } | 183 | } |
3331 | 163 | 184 | ||
3332 | @@ -165,30 +186,39 @@ | |||
3333 | 165 | { | 186 | { |
3334 | 166 | using namespace testing; | 187 | using namespace testing; |
3335 | 167 | NiceMock<mtd::MockDRM> drm; | 188 | NiceMock<mtd::MockDRM> drm; |
3336 | 189 | char const* const drm_device = "/dev/dri/card0"; | ||
3337 | 190 | int const drm_fd = open(drm_device, 0, 0); | ||
3338 | 168 | 191 | ||
3339 | 169 | auto& resources = drm.fake_drm; | ||
3340 | 170 | 192 | ||
3341 | 171 | std::array<uint32_t, 2> const crtc_ids = {{21, 25}}; | 193 | std::array<uint32_t, 2> const crtc_ids = {{21, 25}}; |
3342 | 172 | std::array<uint32_t, 2> const encoder_ids = {{3, 5}}; | 194 | std::array<uint32_t, 2> const encoder_ids = {{3, 5}}; |
3343 | 173 | std::array<uint32_t, 2> const connector_id = {{9, 10}}; | 195 | std::array<uint32_t, 2> const connector_id = {{9, 10}}; |
3344 | 174 | 196 | ||
3347 | 175 | resources.reset(); | 197 | drm.reset(drm_device); |
3348 | 176 | auto boring_mode = resources.create_mode(1200, 1600, 138500, 1400, 1800, mtd::FakeDRMResources::ModePreference::PreferredMode); | 198 | auto boring_mode = |
3349 | 199 | mtd::FakeDRMResources::create_mode( | ||
3350 | 200 | 1200, | ||
3351 | 201 | 1600, | ||
3352 | 202 | 138500, | ||
3353 | 203 | 1400, | ||
3354 | 204 | 1800, | ||
3355 | 205 | mtd::FakeDRMResources::ModePreference::PreferredMode); | ||
3356 | 177 | // Add an active CRTC... | 206 | // Add an active CRTC... |
3358 | 178 | resources.add_crtc(crtc_ids[0], boring_mode); | 207 | drm.add_crtc(drm_device, crtc_ids[0], boring_mode); |
3359 | 179 | // ...and an inactive one. | 208 | // ...and an inactive one. |
3361 | 180 | resources.add_crtc(crtc_ids[1], drmModeModeInfo()); | 209 | drm.add_crtc(drm_device, crtc_ids[1], drmModeModeInfo()); |
3362 | 181 | 210 | ||
3363 | 182 | // Add an encoder hooked up to the active CRTC | 211 | // Add an encoder hooked up to the active CRTC |
3365 | 183 | resources.add_encoder(encoder_ids[0], crtc_ids[0], 1 << 0 | 1 << 1); | 212 | drm.add_encoder(drm_device, encoder_ids[0], crtc_ids[0], 1 << 0 | 1 << 1); |
3366 | 184 | // ...and one not connected to anything | 213 | // ...and one not connected to anything |
3368 | 185 | resources.add_encoder(encoder_ids[1], 0, 1 << 0 | 1 << 1); | 214 | drm.add_encoder(drm_device, encoder_ids[1], 0, 1 << 0 | 1 << 1); |
3369 | 186 | 215 | ||
3370 | 187 | std::vector<drmModeModeInfo> modes{boring_mode}; | 216 | std::vector<drmModeModeInfo> modes{boring_mode}; |
3371 | 188 | std::vector<uint32_t> any_encoder{encoder_ids.begin(), encoder_ids.end()}; | 217 | std::vector<uint32_t> any_encoder{encoder_ids.begin(), encoder_ids.end()}; |
3372 | 189 | 218 | ||
3373 | 190 | // Finally, a connector hooked up to a CRTC-encoder | 219 | // Finally, a connector hooked up to a CRTC-encoder |
3375 | 191 | resources.add_connector( | 220 | drm.add_connector( |
3376 | 221 | drm_device, | ||
3377 | 192 | connector_id[0], | 222 | connector_id[0], |
3378 | 193 | DRM_MODE_CONNECTOR_VGA, | 223 | DRM_MODE_CONNECTOR_VGA, |
3379 | 194 | DRM_MODE_CONNECTED, | 224 | DRM_MODE_CONNECTED, |
3380 | @@ -198,7 +228,8 @@ | |||
3381 | 198 | mir::geometry::Size{300, 200}); | 228 | mir::geometry::Size{300, 200}); |
3382 | 199 | 229 | ||
3383 | 200 | // ... and one not hooked up to anything | 230 | // ... and one not hooked up to anything |
3385 | 201 | resources.add_connector( | 231 | drm.add_connector( |
3386 | 232 | drm_device, | ||
3387 | 202 | connector_id[1], | 233 | connector_id[1], |
3388 | 203 | DRM_MODE_CONNECTOR_VGA, | 234 | DRM_MODE_CONNECTOR_VGA, |
3389 | 204 | DRM_MODE_CONNECTED, | 235 | DRM_MODE_CONNECTED, |
3390 | @@ -207,10 +238,10 @@ | |||
3391 | 207 | any_encoder, | 238 | any_encoder, |
3392 | 208 | mir::geometry::Size{300, 200}); | 239 | mir::geometry::Size{300, 200}); |
3393 | 209 | 240 | ||
3395 | 210 | resources.prepare(); | 241 | drm.prepare(drm_device); |
3396 | 211 | 242 | ||
3397 | 212 | auto crtc = mgk::find_crtc_for_connector( | 243 | auto crtc = mgk::find_crtc_for_connector( |
3400 | 213 | resources.fd(), | 244 | drm_fd, |
3401 | 214 | mgk::get_connector(resources.fd(), connector_id[0])); | 245 | mgk::get_connector(drm_fd, connector_id[0])); |
3402 | 215 | EXPECT_THAT(crtc->crtc_id, Eq(crtc_ids[0])); | 246 | EXPECT_THAT(crtc->crtc_id, Eq(crtc_ids[0])); |
3403 | 216 | } | 247 | } |
3404 | 217 | 248 | ||
3405 | === modified file 'tests/unit-tests/platforms/mesa/kms/mock_kms_output.h' | |||
3406 | --- tests/unit-tests/platforms/mesa/kms/mock_kms_output.h 2017-03-13 08:12:52 +0000 | |||
3407 | +++ tests/unit-tests/platforms/mesa/kms/mock_kms_output.h 2017-03-22 06:55:00 +0000 | |||
3408 | @@ -38,6 +38,7 @@ | |||
3409 | 38 | 38 | ||
3410 | 39 | struct MockKMSOutput : public graphics::mesa::KMSOutput | 39 | struct MockKMSOutput : public graphics::mesa::KMSOutput |
3411 | 40 | { | 40 | { |
3412 | 41 | MOCK_CONST_METHOD0(id, uint32_t()); | ||
3413 | 41 | MOCK_METHOD0(reset, void()); | 42 | MOCK_METHOD0(reset, void()); |
3414 | 42 | MOCK_METHOD2(configure, void(geometry::Displacement, size_t)); | 43 | MOCK_METHOD2(configure, void(geometry::Displacement, size_t)); |
3415 | 43 | MOCK_CONST_METHOD0(size, geometry::Size()); | 44 | MOCK_CONST_METHOD0(size, geometry::Size()); |
3416 | @@ -68,7 +69,12 @@ | |||
3417 | 68 | MOCK_METHOD1(set_power_mode, void(MirPowerMode)); | 69 | MOCK_METHOD1(set_power_mode, void(MirPowerMode)); |
3418 | 69 | MOCK_METHOD1(set_gamma, void(mir::graphics::GammaCurves const&)); | 70 | MOCK_METHOD1(set_gamma, void(mir::graphics::GammaCurves const&)); |
3419 | 70 | 71 | ||
3420 | 72 | MOCK_METHOD0(refresh_hardware_state, void()); | ||
3421 | 73 | MOCK_CONST_METHOD1(update_from_hardware_state, void(graphics::DisplayConfigurationOutput&)); | ||
3422 | 74 | |||
3423 | 71 | MOCK_CONST_METHOD3(fb_for, graphics::mesa::FBHandle*(gbm_bo*, uint32_t, uint32_t)); | 75 | MOCK_CONST_METHOD3(fb_for, graphics::mesa::FBHandle*(gbm_bo*, uint32_t, uint32_t)); |
3424 | 76 | MOCK_CONST_METHOD1(buffer_requires_migration, bool(gbm_bo*)); | ||
3425 | 77 | MOCK_CONST_METHOD0(drm_fd, int()); | ||
3426 | 72 | }; | 78 | }; |
3427 | 73 | 79 | ||
3428 | 74 | } // namespace test | 80 | } // namespace test |
3429 | 75 | 81 | ||
3430 | === modified file 'tests/unit-tests/platforms/mesa/kms/test_cursor.cpp' | |||
3431 | --- tests/unit-tests/platforms/mesa/kms/test_cursor.cpp 2017-02-15 07:38:33 +0000 | |||
3432 | +++ tests/unit-tests/platforms/mesa/kms/test_cursor.cpp 2017-03-22 06:55:00 +0000 | |||
3433 | @@ -56,14 +56,14 @@ | |||
3434 | 56 | { | 56 | { |
3435 | 57 | StubKMSOutputContainer() | 57 | StubKMSOutputContainer() |
3436 | 58 | : outputs{ | 58 | : outputs{ |
3440 | 59 | {10, std::make_shared<testing::NiceMock<MockKMSOutput>>()}, | 59 | std::make_shared<testing::NiceMock<MockKMSOutput>>(), |
3441 | 60 | {11, std::make_shared<testing::NiceMock<MockKMSOutput>>()}, | 60 | std::make_shared<testing::NiceMock<MockKMSOutput>>(), |
3442 | 61 | {12, std::make_shared<testing::NiceMock<MockKMSOutput>>()}} | 61 | std::make_shared<testing::NiceMock<MockKMSOutput>>()} |
3443 | 62 | { | 62 | { |
3444 | 63 | // These need to be established before Cursor construction: | 63 | // These need to be established before Cursor construction: |
3446 | 64 | for (auto& entry : outputs) | 64 | for (auto& output : outputs) |
3447 | 65 | { | 65 | { |
3449 | 66 | auto& out = *entry.second; | 66 | auto& out = *output; |
3450 | 67 | ON_CALL(out, has_cursor()) | 67 | ON_CALL(out, has_cursor()) |
3451 | 68 | .WillByDefault(Return(true)); | 68 | .WillByDefault(Return(true)); |
3452 | 69 | ON_CALL(out, set_cursor(_)) | 69 | ON_CALL(out, set_cursor(_)) |
3453 | @@ -73,33 +73,33 @@ | |||
3454 | 73 | } | 73 | } |
3455 | 74 | } | 74 | } |
3456 | 75 | 75 | ||
3466 | 76 | std::shared_ptr<mgm::KMSOutput> get_kms_output_for(uint32_t connector_id) | 76 | void for_each_output(std::function<void(std::shared_ptr<mgm::KMSOutput> const&)> functor) const |
3467 | 77 | { | 77 | { |
3468 | 78 | return outputs[connector_id]; | 78 | for (auto const& output : outputs) |
3469 | 79 | } | 79 | functor(output); |
3461 | 80 | |||
3462 | 81 | void for_each_output(std::function<void(mgm::KMSOutput&)> functor) const | ||
3463 | 82 | { | ||
3464 | 83 | for (auto const& pair : outputs) | ||
3465 | 84 | functor(*pair.second); | ||
3470 | 85 | } | 80 | } |
3471 | 86 | 81 | ||
3472 | 87 | void verify_and_clear_expectations() | 82 | void verify_and_clear_expectations() |
3473 | 88 | { | 83 | { |
3479 | 89 | for (auto const& pair : outputs) | 84 | for (auto const& output : outputs) |
3480 | 90 | ::testing::Mock::VerifyAndClearExpectations(pair.second.get()); | 85 | ::testing::Mock::VerifyAndClearExpectations(output.get()); |
3481 | 91 | } | 86 | } |
3482 | 92 | 87 | ||
3483 | 93 | std::unordered_map<uint32_t,std::shared_ptr<testing::NiceMock<MockKMSOutput>>> outputs; | 88 | void update_from_hardware_state() |
3484 | 89 | { | ||
3485 | 90 | } | ||
3486 | 91 | |||
3487 | 92 | std::vector<std::shared_ptr<testing::NiceMock<MockKMSOutput>>> outputs; | ||
3488 | 94 | }; | 93 | }; |
3489 | 95 | 94 | ||
3490 | 96 | struct StubKMSDisplayConfiguration : public mgm::KMSDisplayConfiguration | 95 | struct StubKMSDisplayConfiguration : public mgm::KMSDisplayConfiguration |
3491 | 97 | { | 96 | { |
3493 | 98 | StubKMSDisplayConfiguration() | 97 | StubKMSDisplayConfiguration(mgm::KMSOutputContainer& container) |
3494 | 99 | : mgm::KMSDisplayConfiguration(), | 98 | : mgm::KMSDisplayConfiguration(), |
3495 | 99 | container{container}, | ||
3496 | 100 | stub_config{ | 100 | stub_config{ |
3497 | 101 | {{ | 101 | {{ |
3499 | 102 | mg::DisplayConfigurationOutputId{10}, | 102 | mg::DisplayConfigurationOutputId{0}, |
3500 | 103 | mg::DisplayConfigurationCardId{}, | 103 | mg::DisplayConfigurationCardId{}, |
3501 | 104 | mg::DisplayConfigurationOutputType::vga, | 104 | mg::DisplayConfigurationOutputType::vga, |
3502 | 105 | {}, | 105 | {}, |
3503 | @@ -124,7 +124,7 @@ | |||
3504 | 124 | {} | 124 | {} |
3505 | 125 | }, | 125 | }, |
3506 | 126 | { | 126 | { |
3508 | 127 | mg::DisplayConfigurationOutputId{11}, | 127 | mg::DisplayConfigurationOutputId{1}, |
3509 | 128 | mg::DisplayConfigurationCardId{}, | 128 | mg::DisplayConfigurationCardId{}, |
3510 | 129 | mg::DisplayConfigurationOutputType::vga, | 129 | mg::DisplayConfigurationOutputType::vga, |
3511 | 130 | {}, | 130 | {}, |
3512 | @@ -149,7 +149,7 @@ | |||
3513 | 149 | {} | 149 | {} |
3514 | 150 | }, | 150 | }, |
3515 | 151 | { | 151 | { |
3517 | 152 | mg::DisplayConfigurationOutputId{12}, | 152 | mg::DisplayConfigurationOutputId{2}, |
3518 | 153 | mg::DisplayConfigurationCardId{}, | 153 | mg::DisplayConfigurationCardId{}, |
3519 | 154 | mg::DisplayConfigurationOutputType::vga, | 154 | mg::DisplayConfigurationOutputType::vga, |
3520 | 155 | {}, | 155 | {}, |
3521 | @@ -174,6 +174,7 @@ | |||
3522 | 174 | {} | 174 | {} |
3523 | 175 | }}} | 175 | }}} |
3524 | 176 | { | 176 | { |
3525 | 177 | update(); | ||
3526 | 177 | } | 178 | } |
3527 | 178 | 179 | ||
3528 | 179 | void for_each_card(std::function<void(mg::DisplayConfigurationCard const&)> f) const override | 180 | void for_each_card(std::function<void(mg::DisplayConfigurationCard const&)> f) const override |
3529 | @@ -201,9 +202,9 @@ | |||
3530 | 201 | return stub_config.valid(); | 202 | return stub_config.valid(); |
3531 | 202 | } | 203 | } |
3532 | 203 | 204 | ||
3534 | 204 | uint32_t get_kms_connector_id(mg::DisplayConfigurationOutputId id) const override | 205 | std::shared_ptr<mgm::KMSOutput> get_output_for(mg::DisplayConfigurationOutputId id) const override |
3535 | 205 | { | 206 | { |
3537 | 206 | return id.as_value(); | 207 | return outputs[id.as_value()]; |
3538 | 207 | } | 208 | } |
3539 | 208 | 209 | ||
3540 | 209 | size_t get_kms_mode_index(mg::DisplayConfigurationOutputId, size_t conf_mode_index) const override | 210 | size_t get_kms_mode_index(mg::DisplayConfigurationOutputId, size_t conf_mode_index) const override |
3541 | @@ -213,6 +214,11 @@ | |||
3542 | 213 | 214 | ||
3543 | 214 | void update() override | 215 | void update() override |
3544 | 215 | { | 216 | { |
3545 | 217 | container.for_each_output( | ||
3546 | 218 | [this](auto const& output) | ||
3547 | 219 | { | ||
3548 | 220 | outputs.push_back(output); | ||
3549 | 221 | }); | ||
3550 | 216 | } | 222 | } |
3551 | 217 | 223 | ||
3552 | 218 | void set_orentation_of_output(mg::DisplayConfigurationOutputId id, MirOrientation orientation) | 224 | void set_orentation_of_output(mg::DisplayConfigurationOutputId id, MirOrientation orientation) |
3553 | @@ -225,11 +231,18 @@ | |||
3554 | 225 | }); | 231 | }); |
3555 | 226 | } | 232 | } |
3556 | 227 | 233 | ||
3557 | 234 | mgm::KMSOutputContainer& container; | ||
3558 | 228 | mtd::StubDisplayConfig stub_config; | 235 | mtd::StubDisplayConfig stub_config; |
3559 | 236 | std::vector<std::shared_ptr<mgm::KMSOutput>> outputs; | ||
3560 | 229 | }; | 237 | }; |
3561 | 230 | 238 | ||
3562 | 231 | struct StubCurrentConfiguration : public mgm::CurrentConfiguration | 239 | struct StubCurrentConfiguration : public mgm::CurrentConfiguration |
3563 | 232 | { | 240 | { |
3564 | 241 | StubCurrentConfiguration(mgm::KMSOutputContainer& container) | ||
3565 | 242 | : conf(container) | ||
3566 | 243 | { | ||
3567 | 244 | } | ||
3568 | 245 | |||
3569 | 233 | void with_current_configuration_do( | 246 | void with_current_configuration_do( |
3570 | 234 | std::function<void(mgm::KMSDisplayConfiguration const&)> const& exec) | 247 | std::function<void(mgm::KMSDisplayConfiguration const&)> const& exec) |
3571 | 235 | { | 248 | { |
3572 | @@ -284,7 +297,7 @@ | |||
3573 | 284 | 297 | ||
3574 | 285 | size_t const cursor_side{64}; | 298 | size_t const cursor_side{64}; |
3575 | 286 | MesaCursorTest() | 299 | MesaCursorTest() |
3577 | 287 | : cursor{mock_gbm.fake_gbm.device, output_container, | 300 | : cursor{output_container, |
3578 | 288 | mt::fake_shared(current_configuration)} | 301 | mt::fake_shared(current_configuration)} |
3579 | 289 | { | 302 | { |
3580 | 290 | using namespace ::testing; | 303 | using namespace ::testing; |
3581 | @@ -304,9 +317,9 @@ | |||
3582 | 304 | } | 317 | } |
3583 | 305 | 318 | ||
3584 | 306 | testing::NiceMock<mtd::MockDRM> mock_drm; | 319 | testing::NiceMock<mtd::MockDRM> mock_drm; |
3585 | 307 | StubCurrentConfiguration current_configuration; | ||
3586 | 308 | StubCursorImage stub_image; | 320 | StubCursorImage stub_image; |
3587 | 309 | StubKMSOutputContainer output_container; | 321 | StubKMSOutputContainer output_container; |
3588 | 322 | StubCurrentConfiguration current_configuration{output_container}; | ||
3589 | 310 | mgm::Cursor cursor; | 323 | mgm::Cursor cursor; |
3590 | 311 | }; | 324 | }; |
3591 | 312 | 325 | ||
3592 | @@ -335,19 +348,20 @@ | |||
3593 | 335 | GBM_FORMAT_ARGB8888, | 348 | GBM_FORMAT_ARGB8888, |
3594 | 336 | GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)); | 349 | GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)); |
3595 | 337 | 350 | ||
3598 | 338 | mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, | 351 | mgm::Cursor cursor_tmp{output_container, |
3599 | 339 | std::make_shared<StubCurrentConfiguration>()}; | 352 | std::make_shared<StubCurrentConfiguration>(output_container)}; |
3600 | 340 | } | 353 | } |
3601 | 341 | 354 | ||
3602 | 342 | TEST_F(MesaCursorTest, queries_received_cursor_size) | 355 | TEST_F(MesaCursorTest, queries_received_cursor_size) |
3603 | 343 | { | 356 | { |
3604 | 344 | using namespace ::testing; | 357 | using namespace ::testing; |
3605 | 345 | 358 | ||
3608 | 346 | EXPECT_CALL(mock_gbm, gbm_bo_get_width(_)); | 359 | // Our standard mock DRM has 2 GPU devices, and we should construct a cursor on both. |
3609 | 347 | EXPECT_CALL(mock_gbm, gbm_bo_get_height(_)); | 360 | EXPECT_CALL(mock_gbm, gbm_bo_get_width(_)).Times(2); |
3610 | 361 | EXPECT_CALL(mock_gbm, gbm_bo_get_height(_)).Times(2); | ||
3611 | 348 | 362 | ||
3614 | 349 | mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, | 363 | mgm::Cursor cursor_tmp{output_container, |
3615 | 350 | std::make_shared<StubCurrentConfiguration>()}; | 364 | std::make_shared<StubCurrentConfiguration>(output_container)}; |
3616 | 351 | } | 365 | } |
3617 | 352 | 366 | ||
3618 | 353 | TEST_F(MesaCursorTest, respects_drm_cap_cursor) | 367 | TEST_F(MesaCursorTest, respects_drm_cap_cursor) |
3619 | @@ -361,8 +375,8 @@ | |||
3620 | 361 | 375 | ||
3621 | 362 | EXPECT_CALL(mock_gbm, gbm_bo_create(_, drm_buffer_size, drm_buffer_size, _, _)); | 376 | EXPECT_CALL(mock_gbm, gbm_bo_create(_, drm_buffer_size, drm_buffer_size, _, _)); |
3622 | 363 | 377 | ||
3625 | 364 | mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, | 378 | mgm::Cursor cursor_tmp{output_container, |
3626 | 365 | std::make_shared<StubCurrentConfiguration>()}; | 379 | std::make_shared<StubCurrentConfiguration>(output_container)}; |
3627 | 366 | } | 380 | } |
3628 | 367 | 381 | ||
3629 | 368 | TEST_F(MesaCursorTest, can_force_64x64_cursor) | 382 | TEST_F(MesaCursorTest, can_force_64x64_cursor) |
3630 | @@ -378,8 +392,8 @@ | |||
3631 | 378 | 392 | ||
3632 | 379 | EXPECT_CALL(mock_gbm, gbm_bo_create(_, 64, 64, _, _)); | 393 | EXPECT_CALL(mock_gbm, gbm_bo_create(_, 64, 64, _, _)); |
3633 | 380 | 394 | ||
3636 | 381 | mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, | 395 | mgm::Cursor cursor_tmp{output_container, |
3637 | 382 | std::make_shared<StubCurrentConfiguration>()}; | 396 | std::make_shared<StubCurrentConfiguration>(output_container)}; |
3638 | 383 | } | 397 | } |
3639 | 384 | 398 | ||
3640 | 385 | TEST_F(MesaCursorTest, show_cursor_writes_to_bo) | 399 | TEST_F(MesaCursorTest, show_cursor_writes_to_bo) |
3641 | @@ -442,8 +456,8 @@ | |||
3642 | 442 | 456 | ||
3643 | 443 | EXPECT_CALL(mock_gbm, gbm_bo_write(mock_gbm.fake_gbm.bo, ContainsASingleWhitePixel(width*height), buffer_size_bytes)); | 457 | EXPECT_CALL(mock_gbm, gbm_bo_write(mock_gbm.fake_gbm.bo, ContainsASingleWhitePixel(width*height), buffer_size_bytes)); |
3644 | 444 | 458 | ||
3647 | 445 | mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, | 459 | mgm::Cursor cursor_tmp{output_container, |
3648 | 446 | std::make_shared<StubCurrentConfiguration>()}; | 460 | std::make_shared<StubCurrentConfiguration>(output_container)}; |
3649 | 447 | cursor_tmp.show(SinglePixelCursorImage()); | 461 | cursor_tmp.show(SinglePixelCursorImage()); |
3650 | 448 | } | 462 | } |
3651 | 449 | 463 | ||
3652 | @@ -470,17 +484,17 @@ | |||
3653 | 470 | { | 484 | { |
3654 | 471 | using namespace testing; | 485 | using namespace testing; |
3655 | 472 | 486 | ||
3659 | 473 | EXPECT_CALL(*output_container.outputs[10], clear_cursor()); | 487 | EXPECT_CALL(*output_container.outputs[0], clear_cursor()); |
3660 | 474 | EXPECT_CALL(*output_container.outputs[11], clear_cursor()); | 488 | EXPECT_CALL(*output_container.outputs[1], clear_cursor()); |
3661 | 475 | EXPECT_CALL(*output_container.outputs[12], clear_cursor()); | 489 | EXPECT_CALL(*output_container.outputs[2], clear_cursor()); |
3662 | 476 | 490 | ||
3663 | 477 | /* No checking of existing cursor state */ | 491 | /* No checking of existing cursor state */ |
3667 | 478 | EXPECT_CALL(*output_container.outputs[10], has_cursor()).Times(0); | 492 | EXPECT_CALL(*output_container.outputs[0], has_cursor()).Times(0); |
3668 | 479 | EXPECT_CALL(*output_container.outputs[11], has_cursor()).Times(0); | 493 | EXPECT_CALL(*output_container.outputs[1], has_cursor()).Times(0); |
3669 | 480 | EXPECT_CALL(*output_container.outputs[12], has_cursor()).Times(0); | 494 | EXPECT_CALL(*output_container.outputs[2], has_cursor()).Times(0); |
3670 | 481 | 495 | ||
3673 | 482 | mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, | 496 | mgm::Cursor cursor_tmp{output_container, |
3674 | 483 | std::make_shared<StubCurrentConfiguration>()}; | 497 | std::make_shared<StubCurrentConfiguration>(output_container)}; |
3675 | 484 | 498 | ||
3676 | 485 | output_container.verify_and_clear_expectations(); | 499 | output_container.verify_and_clear_expectations(); |
3677 | 486 | } | 500 | } |
3678 | @@ -489,16 +503,16 @@ | |||
3679 | 489 | { | 503 | { |
3680 | 490 | using namespace testing; | 504 | using namespace testing; |
3681 | 491 | 505 | ||
3687 | 492 | ON_CALL(*output_container.outputs[10], clear_cursor()) | 506 | ON_CALL(*output_container.outputs[0], clear_cursor()) |
3688 | 493 | .WillByDefault(Return(false)); | 507 | .WillByDefault(Return(false)); |
3689 | 494 | ON_CALL(*output_container.outputs[10], set_cursor(_)) | 508 | ON_CALL(*output_container.outputs[0], set_cursor(_)) |
3690 | 495 | .WillByDefault(Return(false)); | 509 | .WillByDefault(Return(false)); |
3691 | 496 | ON_CALL(*output_container.outputs[10], has_cursor()) | 510 | ON_CALL(*output_container.outputs[0], has_cursor()) |
3692 | 497 | .WillByDefault(Return(false)); | 511 | .WillByDefault(Return(false)); |
3693 | 498 | 512 | ||
3694 | 499 | EXPECT_THROW( | 513 | EXPECT_THROW( |
3697 | 500 | mgm::Cursor cursor_tmp(mock_gbm.fake_gbm.device, output_container, | 514 | mgm::Cursor cursor_tmp(output_container, |
3698 | 501 | std::make_shared<StubCurrentConfiguration>()); | 515 | std::make_shared<StubCurrentConfiguration>(output_container)); |
3699 | 502 | , std::runtime_error); | 516 | , std::runtime_error); |
3700 | 503 | } | 517 | } |
3701 | 504 | 518 | ||
3702 | @@ -508,14 +522,14 @@ | |||
3703 | 508 | 522 | ||
3704 | 509 | cursor.show(stub_image); | 523 | cursor.show(stub_image); |
3705 | 510 | 524 | ||
3707 | 511 | EXPECT_CALL(*output_container.outputs[10], has_cursor()) | 525 | EXPECT_CALL(*output_container.outputs[0], has_cursor()) |
3708 | 512 | .WillOnce(Return(false)) | 526 | .WillOnce(Return(false)) |
3709 | 513 | .WillOnce(Return(true)); | 527 | .WillOnce(Return(true)); |
3711 | 514 | EXPECT_CALL(*output_container.outputs[10], set_cursor(_)); | 528 | EXPECT_CALL(*output_container.outputs[0], set_cursor(_)); |
3712 | 515 | 529 | ||
3714 | 516 | EXPECT_CALL(*output_container.outputs[11], has_cursor()) | 530 | EXPECT_CALL(*output_container.outputs[1], has_cursor()) |
3715 | 517 | .WillOnce(Return(true)); | 531 | .WillOnce(Return(true)); |
3717 | 518 | EXPECT_CALL(*output_container.outputs[11], clear_cursor()); | 532 | EXPECT_CALL(*output_container.outputs[1], clear_cursor()); |
3718 | 519 | 533 | ||
3719 | 520 | cursor.move_to({10, 10}); | 534 | cursor.move_to({10, 10}); |
3720 | 521 | 535 | ||
3721 | @@ -528,14 +542,14 @@ | |||
3722 | 528 | 542 | ||
3723 | 529 | cursor.show(stub_image); | 543 | cursor.show(stub_image); |
3724 | 530 | 544 | ||
3726 | 531 | EXPECT_CALL(*output_container.outputs[10], has_cursor()) | 545 | EXPECT_CALL(*output_container.outputs[0], has_cursor()) |
3727 | 532 | .WillOnce(Return(true)); | 546 | .WillOnce(Return(true)); |
3729 | 533 | EXPECT_CALL(*output_container.outputs[10], set_cursor(_)) | 547 | EXPECT_CALL(*output_container.outputs[0], set_cursor(_)) |
3730 | 534 | .Times(0); | 548 | .Times(0); |
3731 | 535 | 549 | ||
3733 | 536 | EXPECT_CALL(*output_container.outputs[11], has_cursor()) | 550 | EXPECT_CALL(*output_container.outputs[1], has_cursor()) |
3734 | 537 | .WillOnce(Return(false)); | 551 | .WillOnce(Return(false)); |
3736 | 538 | EXPECT_CALL(*output_container.outputs[11], clear_cursor()) | 552 | EXPECT_CALL(*output_container.outputs[1], clear_cursor()) |
3737 | 539 | .Times(0); | 553 | .Times(0); |
3738 | 540 | 554 | ||
3739 | 541 | cursor.move_to({10, 10}); | 555 | cursor.move_to({10, 10}); |
3740 | @@ -549,32 +563,32 @@ | |||
3741 | 549 | 563 | ||
3742 | 550 | cursor.show(stub_image); | 564 | cursor.show(stub_image); |
3743 | 551 | 565 | ||
3746 | 552 | EXPECT_CALL(*output_container.outputs[10], move_cursor(geom::Point{10,10})); | 566 | EXPECT_CALL(*output_container.outputs[0], move_cursor(geom::Point{10,10})); |
3747 | 553 | EXPECT_CALL(*output_container.outputs[11], move_cursor(_)) | 567 | EXPECT_CALL(*output_container.outputs[1], move_cursor(_)) |
3748 | 554 | .Times(0); | 568 | .Times(0); |
3749 | 555 | 569 | ||
3750 | 556 | cursor.move_to({10, 10}); | 570 | cursor.move_to({10, 10}); |
3751 | 557 | 571 | ||
3752 | 558 | output_container.verify_and_clear_expectations(); | 572 | output_container.verify_and_clear_expectations(); |
3753 | 559 | 573 | ||
3755 | 560 | EXPECT_CALL(*output_container.outputs[10], move_cursor(_)) | 574 | EXPECT_CALL(*output_container.outputs[0], move_cursor(_)) |
3756 | 561 | .Times(0); | 575 | .Times(0); |
3758 | 562 | EXPECT_CALL(*output_container.outputs[11], move_cursor(geom::Point{50,100})); | 576 | EXPECT_CALL(*output_container.outputs[1], move_cursor(geom::Point{50,100})); |
3759 | 563 | 577 | ||
3760 | 564 | cursor.move_to({150, 150}); | 578 | cursor.move_to({150, 150}); |
3761 | 565 | 579 | ||
3762 | 566 | output_container.verify_and_clear_expectations(); | 580 | output_container.verify_and_clear_expectations(); |
3763 | 567 | 581 | ||
3766 | 568 | EXPECT_CALL(*output_container.outputs[10], move_cursor(geom::Point{150,75})); | 582 | EXPECT_CALL(*output_container.outputs[0], move_cursor(geom::Point{150,75})); |
3767 | 569 | EXPECT_CALL(*output_container.outputs[11], move_cursor(geom::Point{50,25})); | 583 | EXPECT_CALL(*output_container.outputs[1], move_cursor(geom::Point{50,25})); |
3768 | 570 | 584 | ||
3769 | 571 | cursor.move_to({150, 75}); | 585 | cursor.move_to({150, 75}); |
3770 | 572 | 586 | ||
3771 | 573 | output_container.verify_and_clear_expectations(); | 587 | output_container.verify_and_clear_expectations(); |
3772 | 574 | 588 | ||
3774 | 575 | EXPECT_CALL(*output_container.outputs[10], move_cursor(_)) | 589 | EXPECT_CALL(*output_container.outputs[0], move_cursor(_)) |
3775 | 576 | .Times(0); | 590 | .Times(0); |
3777 | 577 | EXPECT_CALL(*output_container.outputs[11], move_cursor(_)) | 591 | EXPECT_CALL(*output_container.outputs[1], move_cursor(_)) |
3778 | 578 | .Times(0); | 592 | .Times(0); |
3779 | 579 | 593 | ||
3780 | 580 | cursor.move_to({-1, -1}); | 594 | cursor.move_to({-1, -1}); |
3781 | @@ -588,10 +602,10 @@ | |||
3782 | 588 | 602 | ||
3783 | 589 | cursor.show(stub_image); | 603 | cursor.show(stub_image); |
3784 | 590 | 604 | ||
3786 | 591 | current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{12}, mir_orientation_left); | 605 | current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{2}, mir_orientation_left); |
3787 | 592 | 606 | ||
3790 | 593 | EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{112,100})); | 607 | EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{112,100})); |
3791 | 594 | EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{150,96})); | 608 | EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{150,96})); |
3792 | 595 | 609 | ||
3793 | 596 | cursor.move_to({766, 112}); | 610 | cursor.move_to({766, 112}); |
3794 | 597 | cursor.move_to({770, 150}); | 611 | cursor.move_to({770, 150}); |
3795 | @@ -606,11 +620,11 @@ | |||
3796 | 606 | 620 | ||
3797 | 607 | cursor.show(stub_image); | 621 | cursor.show(stub_image); |
3798 | 608 | 622 | ||
3804 | 609 | current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{12}, mir_orientation_right); | 623 | current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{2}, mir_orientation_right); |
3805 | 610 | 624 | ||
3806 | 611 | 625 | ||
3807 | 612 | EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{688,100})); | 626 | EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{688,100})); |
3808 | 613 | EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{650,104})); | 627 | EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{650,104})); |
3809 | 614 | 628 | ||
3810 | 615 | cursor.move_to({766, 112}); | 629 | cursor.move_to({766, 112}); |
3811 | 616 | cursor.move_to({770, 150}); | 630 | cursor.move_to({770, 150}); |
3812 | @@ -624,10 +638,10 @@ | |||
3813 | 624 | 638 | ||
3814 | 625 | cursor.show(stub_image); | 639 | cursor.show(stub_image); |
3815 | 626 | 640 | ||
3817 | 627 | current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{12}, mir_orientation_inverted); | 641 | current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{2}, mir_orientation_inverted); |
3818 | 628 | 642 | ||
3821 | 629 | EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{700,88})); | 643 | EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{700,88})); |
3822 | 630 | EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{696,50})); | 644 | EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{696,50})); |
3823 | 631 | 645 | ||
3824 | 632 | cursor.move_to({766, 112}); | 646 | cursor.move_to({766, 112}); |
3825 | 633 | cursor.move_to({770, 150}); | 647 | cursor.move_to({770, 150}); |
3826 | @@ -639,9 +653,9 @@ | |||
3827 | 639 | { | 653 | { |
3828 | 640 | using namespace testing; | 654 | using namespace testing; |
3829 | 641 | 655 | ||
3833 | 642 | EXPECT_CALL(*output_container.outputs[10], clear_cursor()); | 656 | EXPECT_CALL(*output_container.outputs[0], clear_cursor()); |
3834 | 643 | EXPECT_CALL(*output_container.outputs[11], clear_cursor()); | 657 | EXPECT_CALL(*output_container.outputs[1], clear_cursor()); |
3835 | 644 | EXPECT_CALL(*output_container.outputs[12], clear_cursor()); | 658 | EXPECT_CALL(*output_container.outputs[2], clear_cursor()); |
3836 | 645 | 659 | ||
3837 | 646 | cursor.hide(); | 660 | cursor.hide(); |
3838 | 647 | 661 | ||
3839 | @@ -652,12 +666,12 @@ | |||
3840 | 652 | { | 666 | { |
3841 | 653 | using namespace testing; | 667 | using namespace testing; |
3842 | 654 | 668 | ||
3849 | 655 | EXPECT_CALL(*output_container.outputs[10], clear_cursor()); | 669 | EXPECT_CALL(*output_container.outputs[0], clear_cursor()); |
3850 | 656 | EXPECT_CALL(*output_container.outputs[11], clear_cursor()); | 670 | EXPECT_CALL(*output_container.outputs[1], clear_cursor()); |
3851 | 657 | EXPECT_CALL(*output_container.outputs[12], clear_cursor()); | 671 | EXPECT_CALL(*output_container.outputs[2], clear_cursor()); |
3852 | 658 | EXPECT_CALL(*output_container.outputs[10], move_cursor(_)).Times(0); | 672 | EXPECT_CALL(*output_container.outputs[0], move_cursor(_)).Times(0); |
3853 | 659 | EXPECT_CALL(*output_container.outputs[11], move_cursor(_)).Times(0); | 673 | EXPECT_CALL(*output_container.outputs[1], move_cursor(_)).Times(0); |
3854 | 660 | EXPECT_CALL(*output_container.outputs[12], move_cursor(_)).Times(0); | 674 | EXPECT_CALL(*output_container.outputs[2], move_cursor(_)).Times(0); |
3855 | 661 | 675 | ||
3856 | 662 | cursor.hide(); | 676 | cursor.hide(); |
3857 | 663 | cursor.move_to({17, 29}); | 677 | cursor.move_to({17, 29}); |
3858 | @@ -669,9 +683,9 @@ | |||
3859 | 669 | { | 683 | { |
3860 | 670 | using namespace testing; | 684 | using namespace testing; |
3861 | 671 | 685 | ||
3865 | 672 | EXPECT_CALL(*output_container.outputs[10], clear_cursor()); | 686 | EXPECT_CALL(*output_container.outputs[0], clear_cursor()); |
3866 | 673 | EXPECT_CALL(*output_container.outputs[11], clear_cursor()); | 687 | EXPECT_CALL(*output_container.outputs[1], clear_cursor()); |
3867 | 674 | EXPECT_CALL(*output_container.outputs[12], clear_cursor()); | 688 | EXPECT_CALL(*output_container.outputs[2], clear_cursor()); |
3868 | 675 | } | 689 | } |
3869 | 676 | 690 | ||
3870 | 677 | TEST_F(MesaCursorTest, cursor_is_shown_at_correct_location_after_suspend_resume) | 691 | TEST_F(MesaCursorTest, cursor_is_shown_at_correct_location_after_suspend_resume) |
3871 | @@ -680,23 +694,23 @@ | |||
3872 | 680 | 694 | ||
3873 | 681 | cursor.show(stub_image); | 695 | cursor.show(stub_image); |
3874 | 682 | 696 | ||
3880 | 683 | EXPECT_CALL(*output_container.outputs[10], move_cursor(geom::Point{150,75})); | 697 | EXPECT_CALL(*output_container.outputs[0], move_cursor(geom::Point{150,75})); |
3881 | 684 | EXPECT_CALL(*output_container.outputs[11], move_cursor(geom::Point{50,25})); | 698 | EXPECT_CALL(*output_container.outputs[1], move_cursor(geom::Point{50,25})); |
3882 | 685 | EXPECT_CALL(*output_container.outputs[10], clear_cursor()); | 699 | EXPECT_CALL(*output_container.outputs[0], clear_cursor()); |
3883 | 686 | EXPECT_CALL(*output_container.outputs[11], clear_cursor()); | 700 | EXPECT_CALL(*output_container.outputs[1], clear_cursor()); |
3884 | 687 | EXPECT_CALL(*output_container.outputs[12], has_cursor()) | 701 | EXPECT_CALL(*output_container.outputs[2], has_cursor()) |
3885 | 688 | .WillRepeatedly(Return(false)); | 702 | .WillRepeatedly(Return(false)); |
3887 | 689 | EXPECT_CALL(*output_container.outputs[12], clear_cursor()); | 703 | EXPECT_CALL(*output_container.outputs[2], clear_cursor()); |
3888 | 690 | 704 | ||
3889 | 691 | cursor.move_to({150, 75}); | 705 | cursor.move_to({150, 75}); |
3890 | 692 | cursor.suspend(); | 706 | cursor.suspend(); |
3891 | 693 | 707 | ||
3892 | 694 | output_container.verify_and_clear_expectations(); | 708 | output_container.verify_and_clear_expectations(); |
3893 | 695 | 709 | ||
3898 | 696 | EXPECT_CALL(*output_container.outputs[10], set_cursor(_)); | 710 | EXPECT_CALL(*output_container.outputs[0], set_cursor(_)); |
3899 | 697 | EXPECT_CALL(*output_container.outputs[11], set_cursor(_)); | 711 | EXPECT_CALL(*output_container.outputs[1], set_cursor(_)); |
3900 | 698 | EXPECT_CALL(*output_container.outputs[10], move_cursor(geom::Point{150,75})); | 712 | EXPECT_CALL(*output_container.outputs[0], move_cursor(geom::Point{150,75})); |
3901 | 699 | EXPECT_CALL(*output_container.outputs[11], move_cursor(geom::Point{50,25})); | 713 | EXPECT_CALL(*output_container.outputs[1], move_cursor(geom::Point{50,25})); |
3902 | 700 | 714 | ||
3903 | 701 | cursor.resume(); | 715 | cursor.resume(); |
3904 | 702 | output_container.verify_and_clear_expectations(); | 716 | output_container.verify_and_clear_expectations(); |
3905 | @@ -706,18 +720,18 @@ | |||
3906 | 706 | { | 720 | { |
3907 | 707 | using namespace testing; | 721 | using namespace testing; |
3908 | 708 | 722 | ||
3912 | 709 | EXPECT_CALL(*output_container.outputs[10], clear_cursor()).Times(AnyNumber()); | 723 | EXPECT_CALL(*output_container.outputs[0], clear_cursor()).Times(AnyNumber()); |
3913 | 710 | EXPECT_CALL(*output_container.outputs[11], clear_cursor()).Times(AnyNumber()); | 724 | EXPECT_CALL(*output_container.outputs[1], clear_cursor()).Times(AnyNumber()); |
3914 | 711 | EXPECT_CALL(*output_container.outputs[12], clear_cursor()).Times(AnyNumber()); | 725 | EXPECT_CALL(*output_container.outputs[2], clear_cursor()).Times(AnyNumber()); |
3915 | 712 | 726 | ||
3916 | 713 | cursor.hide(); | 727 | cursor.hide(); |
3917 | 714 | cursor.suspend(); | 728 | cursor.suspend(); |
3918 | 715 | 729 | ||
3919 | 716 | output_container.verify_and_clear_expectations(); | 730 | output_container.verify_and_clear_expectations(); |
3920 | 717 | 731 | ||
3924 | 718 | EXPECT_CALL(*output_container.outputs[10], set_cursor(_)).Times(0); | 732 | EXPECT_CALL(*output_container.outputs[0], set_cursor(_)).Times(0); |
3925 | 719 | EXPECT_CALL(*output_container.outputs[11], set_cursor(_)).Times(0); | 733 | EXPECT_CALL(*output_container.outputs[1], set_cursor(_)).Times(0); |
3926 | 720 | EXPECT_CALL(*output_container.outputs[12], set_cursor(_)).Times(0); | 734 | EXPECT_CALL(*output_container.outputs[2], set_cursor(_)).Times(0); |
3927 | 721 | 735 | ||
3928 | 722 | cursor.resume(); | 736 | cursor.resume(); |
3929 | 723 | output_container.verify_and_clear_expectations(); | 737 | output_container.verify_and_clear_expectations(); |
3930 | @@ -725,24 +739,24 @@ | |||
3931 | 725 | 739 | ||
3932 | 726 | TEST_F(MesaCursorTest, show_with_param_places_cursor_on_output) | 740 | TEST_F(MesaCursorTest, show_with_param_places_cursor_on_output) |
3933 | 727 | { | 741 | { |
3935 | 728 | EXPECT_CALL(*output_container.outputs[10], clear_cursor()); | 742 | EXPECT_CALL(*output_container.outputs[0], clear_cursor()); |
3936 | 729 | cursor.hide(); | 743 | cursor.hide(); |
3937 | 730 | 744 | ||
3938 | 731 | output_container.verify_and_clear_expectations(); | 745 | output_container.verify_and_clear_expectations(); |
3939 | 732 | 746 | ||
3941 | 733 | EXPECT_CALL(*output_container.outputs[10], set_cursor(_)); | 747 | EXPECT_CALL(*output_container.outputs[0], set_cursor(_)); |
3942 | 734 | cursor.show(stub_image); | 748 | cursor.show(stub_image); |
3943 | 735 | } | 749 | } |
3944 | 736 | 750 | ||
3945 | 737 | TEST_F(MesaCursorTest, show_without_param_places_cursor_on_output_output) | 751 | TEST_F(MesaCursorTest, show_without_param_places_cursor_on_output_output) |
3946 | 738 | { | 752 | { |
3947 | 739 | using namespace testing; | 753 | using namespace testing; |
3949 | 740 | EXPECT_CALL(*output_container.outputs[10], clear_cursor()); | 754 | EXPECT_CALL(*output_container.outputs[0], clear_cursor()); |
3950 | 741 | 755 | ||
3951 | 742 | cursor.hide(); | 756 | cursor.hide(); |
3952 | 743 | output_container.verify_and_clear_expectations(); | 757 | output_container.verify_and_clear_expectations(); |
3953 | 744 | 758 | ||
3955 | 745 | EXPECT_CALL(*output_container.outputs[10], set_cursor(_)); | 759 | EXPECT_CALL(*output_container.outputs[0], set_cursor(_)); |
3956 | 746 | cursor.show(); | 760 | cursor.show(); |
3957 | 747 | } | 761 | } |
3958 | 748 | 762 | ||
3959 | @@ -772,15 +786,15 @@ | |||
3960 | 772 | 786 | ||
3961 | 773 | 787 | ||
3962 | 774 | EXPECT_CALL(mock_gbm, gbm_bo_write(_, _, _)).Times(AnyNumber()); | 788 | EXPECT_CALL(mock_gbm, gbm_bo_write(_, _, _)).Times(AnyNumber()); |
3964 | 775 | EXPECT_CALL(*output_container.outputs[10], set_cursor(_)).Times(AnyNumber()); | 789 | EXPECT_CALL(*output_container.outputs[0], set_cursor(_)).Times(AnyNumber()); |
3965 | 776 | 790 | ||
3966 | 777 | // When we set the image with the hotspot, first we should see the cursor move from its initial | 791 | // When we set the image with the hotspot, first we should see the cursor move from its initial |
3967 | 778 | // location, to account for the displacement. Further movement should be offset by the hotspot. | 792 | // location, to account for the displacement. Further movement should be offset by the hotspot. |
3968 | 779 | { | 793 | { |
3969 | 780 | InSequence seq; | 794 | InSequence seq; |
3973 | 781 | EXPECT_CALL(*output_container.outputs[10], move_cursor(initial_buffer_location)); | 795 | EXPECT_CALL(*output_container.outputs[0], move_cursor(initial_buffer_location)); |
3974 | 782 | EXPECT_CALL(*output_container.outputs[10], move_cursor(expected_buffer_location_1)); | 796 | EXPECT_CALL(*output_container.outputs[0], move_cursor(expected_buffer_location_1)); |
3975 | 783 | EXPECT_CALL(*output_container.outputs[10], move_cursor(expected_buffer_location_2)); | 797 | EXPECT_CALL(*output_container.outputs[0], move_cursor(expected_buffer_location_2)); |
3976 | 784 | } | 798 | } |
3977 | 785 | 799 | ||
3978 | 786 | cursor.show(HotspotCursor()); | 800 | cursor.show(HotspotCursor()); |
3979 | 787 | 801 | ||
3980 | === modified file 'tests/unit-tests/platforms/mesa/kms/test_display.cpp' | |||
3981 | --- tests/unit-tests/platforms/mesa/kms/test_display.cpp 2017-02-28 08:53:57 +0000 | |||
3982 | +++ tests/unit-tests/platforms/mesa/kms/test_display.cpp 2017-03-22 06:55:00 +0000 | |||
3983 | @@ -27,6 +27,7 @@ | |||
3984 | 27 | #include "mir/time/steady_clock.h" | 27 | #include "mir/time/steady_clock.h" |
3985 | 28 | #include "mir/glib_main_loop.h" | 28 | #include "mir/glib_main_loop.h" |
3986 | 29 | #include "mir/fatal.h" | 29 | #include "mir/fatal.h" |
3987 | 30 | #include "src/platforms/common/server/kms-utils/drm_mode_resources.h" | ||
3988 | 30 | 31 | ||
3989 | 31 | #include "mir/test/doubles/mock_egl.h" | 32 | #include "mir/test/doubles/mock_egl.h" |
3990 | 32 | #include "mir/test/doubles/mock_gl.h" | 33 | #include "mir/test/doubles/mock_gl.h" |
3991 | @@ -55,6 +56,7 @@ | |||
3992 | 55 | #include <atomic> | 56 | #include <atomic> |
3993 | 56 | #include <mutex> | 57 | #include <mutex> |
3994 | 57 | #include <condition_variable> | 58 | #include <condition_variable> |
3995 | 59 | #include <fcntl.h> | ||
3996 | 58 | 60 | ||
3997 | 59 | namespace mg=mir::graphics; | 61 | namespace mg=mir::graphics; |
3998 | 60 | namespace mgm=mir::graphics::mesa; | 62 | namespace mgm=mir::graphics::mesa; |
3999 | @@ -80,7 +82,8 @@ | |||
4000 | 80 | public: | 82 | public: |
4001 | 81 | MesaDisplayTest() : | 83 | MesaDisplayTest() : |
4002 | 82 | mock_report{std::make_shared<testing::NiceMock<mtd::MockDisplayReport>>()}, | 84 | mock_report{std::make_shared<testing::NiceMock<mtd::MockDisplayReport>>()}, |
4004 | 83 | null_report{mr::null_display_report()} | 85 | null_report{mr::null_display_report()}, |
4005 | 86 | drm_fd{open(drm_device, 0, 0)} | ||
4006 | 84 | { | 87 | { |
4007 | 85 | using namespace testing; | 88 | using namespace testing; |
4008 | 86 | ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_)) | 89 | ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_)) |
4009 | @@ -95,11 +98,16 @@ | |||
4010 | 95 | * the MockGBM destructor, and which are not handled by NiceMock<>. | 98 | * the MockGBM destructor, and which are not handled by NiceMock<>. |
4011 | 96 | */ | 99 | */ |
4012 | 97 | EXPECT_CALL(mock_gbm, gbm_bo_get_device(_)) | 100 | EXPECT_CALL(mock_gbm, gbm_bo_get_device(_)) |
4014 | 98 | .Times(AtLeast(0)); | 101 | .Times(AtLeast(0)); |
4015 | 99 | EXPECT_CALL(mock_gbm, gbm_device_get_fd(_)) | 102 | EXPECT_CALL(mock_gbm, gbm_device_get_fd(_)) |
4017 | 100 | .Times(AtLeast(0)); | 103 | .Times(AtLeast(0)) |
4018 | 104 | .WillRepeatedly(Return(drm_fd)); | ||
4019 | 101 | 105 | ||
4020 | 102 | fake_devices.add_standard_device("standard-drm-devices"); | 106 | fake_devices.add_standard_device("standard-drm-devices"); |
4021 | 107 | |||
4022 | 108 | // Our standard mock devices have 2 DRM devices; kill all the outputs on | ||
4023 | 109 | // the second one, so we don't try to test hybrid (for now) | ||
4024 | 110 | mock_drm.reset("/dev/dri/card1"); | ||
4025 | 103 | } | 111 | } |
4026 | 104 | 112 | ||
4027 | 105 | std::shared_ptr<mgm::Platform> create_platform() | 113 | std::shared_ptr<mgm::Platform> create_platform() |
4028 | @@ -145,14 +153,14 @@ | |||
4029 | 145 | .Times(Exactly(1)) | 153 | .Times(Exactly(1)) |
4030 | 146 | .WillOnce(Return(fake.bo_handle2)); | 154 | .WillOnce(Return(fake.bo_handle2)); |
4031 | 147 | 155 | ||
4033 | 148 | EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(), | 156 | EXPECT_CALL(mock_drm, drmModeAddFB2(drm_fd, |
4034 | 149 | _, _, _, | 157 | _, _, _, |
4035 | 150 | Pointee(fake.bo_handle1.u32), | 158 | Pointee(fake.bo_handle1.u32), |
4036 | 151 | _, _, _, _)) | 159 | _, _, _, _)) |
4037 | 152 | .Times(Exactly(1)) | 160 | .Times(Exactly(1)) |
4038 | 153 | .WillOnce(DoAll(SetArgPointee<7>(fake.fb_id1), Return(0))); | 161 | .WillOnce(DoAll(SetArgPointee<7>(fake.fb_id1), Return(0))); |
4039 | 154 | 162 | ||
4041 | 155 | EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(), | 163 | EXPECT_CALL(mock_drm, drmModeAddFB2(drm_fd, |
4042 | 156 | _, _, _, | 164 | _, _, _, |
4043 | 157 | Pointee(fake.bo_handle2.u32), | 165 | Pointee(fake.bo_handle2.u32), |
4044 | 158 | _, _, _, _)) | 166 | _, _, _, _)) |
4045 | @@ -162,26 +170,27 @@ | |||
4046 | 162 | 170 | ||
4047 | 163 | uint32_t get_connected_connector_id() | 171 | uint32_t get_connected_connector_id() |
4048 | 164 | { | 172 | { |
4059 | 165 | auto drm_res = mock_drm.fake_drm.resources_ptr(); | 173 | mg::kms::DRMModeResources resources{drm_fd}; |
4060 | 166 | 174 | ||
4061 | 167 | for (int i = 0; i < drm_res->count_connectors; i++) | 175 | int connected_id = 0; |
4062 | 168 | { | 176 | resources.for_each_connector( |
4063 | 169 | auto connector = mock_drm.fake_drm.find_connector(drm_res->connectors[i]); | 177 | [&connected_id](auto const& connector) |
4064 | 170 | if (connector->connection == DRM_MODE_CONNECTED) | 178 | { |
4065 | 171 | return connector->connector_id; | 179 | if (connector->connection == DRM_MODE_CONNECTED) |
4066 | 172 | } | 180 | connected_id = connector->connector_id; |
4067 | 173 | 181 | }); | |
4068 | 174 | return 0; | 182 | |
4069 | 183 | return connected_id; | ||
4070 | 175 | } | 184 | } |
4071 | 176 | 185 | ||
4072 | 177 | uint32_t get_connected_crtc_id() | 186 | uint32_t get_connected_crtc_id() |
4073 | 178 | { | 187 | { |
4074 | 179 | auto connector_id = get_connected_connector_id(); | 188 | auto connector_id = get_connected_connector_id(); |
4076 | 180 | auto connector = mock_drm.fake_drm.find_connector(connector_id); | 189 | auto connector = mg::kms::get_connector(drm_fd, connector_id); |
4077 | 181 | 190 | ||
4078 | 182 | if (connector) | 191 | if (connector) |
4079 | 183 | { | 192 | { |
4081 | 184 | auto encoder = mock_drm.fake_drm.find_encoder(connector->encoder_id); | 193 | auto encoder = mg::kms::get_encoder(drm_fd, connector->encoder_id); |
4082 | 185 | if (encoder) | 194 | if (encoder) |
4083 | 186 | return encoder->crtc_id; | 195 | return encoder->crtc_id; |
4084 | 187 | } | 196 | } |
4085 | @@ -217,6 +226,9 @@ | |||
4086 | 217 | std::shared_ptr<testing::NiceMock<mtd::MockDisplayReport>> const mock_report; | 226 | std::shared_ptr<testing::NiceMock<mtd::MockDisplayReport>> const mock_report; |
4087 | 218 | std::shared_ptr<mg::DisplayReport> const null_report; | 227 | std::shared_ptr<mg::DisplayReport> const null_report; |
4088 | 219 | mtf::UdevEnvironment fake_devices; | 228 | mtf::UdevEnvironment fake_devices; |
4089 | 229 | |||
4090 | 230 | char const* const drm_device = "/dev/dri/card0"; | ||
4091 | 231 | int const drm_fd; | ||
4092 | 220 | }; | 232 | }; |
4093 | 221 | 233 | ||
4094 | 222 | } | 234 | } |
4095 | @@ -256,7 +268,7 @@ | |||
4096 | 256 | .WillOnce(Return(fake.bo_handle1)); | 268 | .WillOnce(Return(fake.bo_handle1)); |
4097 | 257 | 269 | ||
4098 | 258 | /* Create a a DRM FB with the DRM buffer attached */ | 270 | /* Create a a DRM FB with the DRM buffer attached */ |
4100 | 259 | EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(), | 271 | EXPECT_CALL(mock_drm, drmModeAddFB2(drm_fd, |
4101 | 260 | _, _, _, | 272 | _, _, _, |
4102 | 261 | Pointee(fake.bo_handle1.u32), | 273 | Pointee(fake.bo_handle1.u32), |
4103 | 262 | _, _, _, _)) | 274 | _, _, _, _)) |
4104 | @@ -264,14 +276,14 @@ | |||
4105 | 264 | .WillOnce(DoAll(SetArgPointee<7>(fake.fb_id1), Return(0))); | 276 | .WillOnce(DoAll(SetArgPointee<7>(fake.fb_id1), Return(0))); |
4106 | 265 | 277 | ||
4107 | 266 | /* Display the DRM FB (first expectation is for cleanup) */ | 278 | /* Display the DRM FB (first expectation is for cleanup) */ |
4109 | 267 | EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), | 279 | EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd, |
4110 | 268 | crtc_id, Ne(fake.fb_id1), | 280 | crtc_id, Ne(fake.fb_id1), |
4111 | 269 | _, _, | 281 | _, _, |
4112 | 270 | Pointee(connector_id), | 282 | Pointee(connector_id), |
4113 | 271 | _, _)) | 283 | _, _)) |
4114 | 272 | .Times(AtLeast(0)); | 284 | .Times(AtLeast(0)); |
4115 | 273 | 285 | ||
4117 | 274 | EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), | 286 | EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd, |
4118 | 275 | crtc_id, fake.fb_id1, | 287 | crtc_id, fake.fb_id1, |
4119 | 276 | _, _, | 288 | _, _, |
4120 | 277 | Pointee(connector_id), | 289 | Pointee(connector_id), |
4121 | @@ -291,7 +303,7 @@ | |||
4122 | 291 | uint32_t const fb_id{66}; | 303 | uint32_t const fb_id{66}; |
4123 | 292 | 304 | ||
4124 | 293 | /* Create DRM FBs */ | 305 | /* Create DRM FBs */ |
4126 | 294 | EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(), | 306 | EXPECT_CALL(mock_drm, drmModeAddFB2(drm_fd, |
4127 | 295 | _, _, _, _, _, _, _, _)) | 307 | _, _, _, _, _, _, _, _)) |
4128 | 296 | .WillRepeatedly(DoAll(SetArgPointee<7>(fb_id), Return(0))); | 308 | .WillRepeatedly(DoAll(SetArgPointee<7>(fb_id), Return(0))); |
4129 | 297 | 309 | ||
4130 | @@ -300,7 +312,7 @@ | |||
4131 | 300 | InSequence s; | 312 | InSequence s; |
4132 | 301 | 313 | ||
4133 | 302 | /* crtc is set */ | 314 | /* crtc is set */ |
4135 | 303 | EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), | 315 | EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd, |
4136 | 304 | crtc_id, fb_id, | 316 | crtc_id, fb_id, |
4137 | 305 | _, _, | 317 | _, _, |
4138 | 306 | Pointee(connector_id), | 318 | Pointee(connector_id), |
4139 | @@ -308,7 +320,7 @@ | |||
4140 | 308 | .Times(AtLeast(1)); | 320 | .Times(AtLeast(1)); |
4141 | 309 | 321 | ||
4142 | 310 | /* crtc is reset */ | 322 | /* crtc is reset */ |
4144 | 311 | EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), | 323 | EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd, |
4145 | 312 | crtc_id, Ne(fb_id), | 324 | crtc_id, Ne(fb_id), |
4146 | 313 | _, _, | 325 | _, _, |
4147 | 314 | Pointee(connector_id), | 326 | Pointee(connector_id), |
4148 | @@ -325,7 +337,10 @@ | |||
4149 | 325 | 337 | ||
4150 | 326 | EXPECT_CALL(mock_drm, open(_,_,_)) | 338 | EXPECT_CALL(mock_drm, open(_,_,_)) |
4151 | 327 | .Times(AtLeast(1)) | 339 | .Times(AtLeast(1)) |
4153 | 328 | .WillRepeatedly(Return(-1)); | 340 | .WillRepeatedly( |
4154 | 341 | DoAll( | ||
4155 | 342 | InvokeWithoutArgs([]() { errno = ENODEV; }), | ||
4156 | 343 | Return(-1))); | ||
4157 | 329 | 344 | ||
4158 | 330 | EXPECT_CALL(mock_drm, drmClose(_)) | 345 | EXPECT_CALL(mock_drm, drmClose(_)) |
4159 | 331 | .Times(Exactly(0)); | 346 | .Times(Exactly(0)); |
4160 | @@ -351,8 +366,9 @@ | |||
4161 | 351 | EXPECT_CALL(mock_drm, drmModeFreeResources(_)) | 366 | EXPECT_CALL(mock_drm, drmModeFreeResources(_)) |
4162 | 352 | .Times(Exactly(0)); | 367 | .Times(Exactly(0)); |
4163 | 353 | 368 | ||
4164 | 369 | // There are 2 DRM device nodes in our mock environment. | ||
4165 | 354 | EXPECT_CALL(mock_drm, drmClose(_)) | 370 | EXPECT_CALL(mock_drm, drmClose(_)) |
4167 | 355 | .Times(Exactly(1)); | 371 | .Times(Exactly(2)); |
4168 | 356 | 372 | ||
4169 | 357 | EXPECT_THROW({ | 373 | EXPECT_THROW({ |
4170 | 358 | auto display = create_display(platform); | 374 | auto display = create_display(platform); |
4171 | @@ -371,7 +387,7 @@ | |||
4172 | 371 | .Times(Exactly(0)); | 387 | .Times(Exactly(0)); |
4173 | 372 | 388 | ||
4174 | 373 | EXPECT_CALL(mock_drm, drmClose(_)) | 389 | EXPECT_CALL(mock_drm, drmClose(_)) |
4176 | 374 | .Times(Exactly(1)); | 390 | .Times(Exactly(2)); |
4177 | 375 | 391 | ||
4178 | 376 | EXPECT_THROW({ | 392 | EXPECT_THROW({ |
4179 | 377 | auto platform = create_platform(); | 393 | auto platform = create_platform(); |
4180 | @@ -381,9 +397,9 @@ | |||
4181 | 381 | namespace | 397 | namespace |
4182 | 382 | { | 398 | { |
4183 | 383 | 399 | ||
4185 | 384 | ACTION_P(QueuePageFlipEvent, write_drm_fd) | 400 | ACTION_P(QueuePageFlipEvent, mock_drm) |
4186 | 385 | { | 401 | { |
4188 | 386 | EXPECT_EQ(1, write(write_drm_fd, "a", 1)); | 402 | static_cast<mtd::MockDRM&>(mock_drm).generate_event_on("/dev/dri/card0"); |
4189 | 387 | } | 403 | } |
4190 | 388 | 404 | ||
4191 | 389 | ACTION_P(InvokePageFlipHandler, param) | 405 | ACTION_P(InvokePageFlipHandler, param) |
4192 | @@ -410,17 +426,17 @@ | |||
4193 | 410 | InSequence s; | 426 | InSequence s; |
4194 | 411 | 427 | ||
4195 | 412 | /* Flip the new FB */ | 428 | /* Flip the new FB */ |
4197 | 413 | EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(), | 429 | EXPECT_CALL(mock_drm, drmModePageFlip(drm_fd, |
4198 | 414 | crtc_id, | 430 | crtc_id, |
4199 | 415 | fake.fb_id2, | 431 | fake.fb_id2, |
4200 | 416 | _, _)) | 432 | _, _)) |
4201 | 417 | .Times(Exactly(1)) | 433 | .Times(Exactly(1)) |
4203 | 418 | .WillOnce(DoAll(QueuePageFlipEvent(mock_drm.fake_drm.write_fd()), | 434 | .WillOnce(DoAll(QueuePageFlipEvent(std::ref(mock_drm)), |
4204 | 419 | SaveArg<4>(&user_data), | 435 | SaveArg<4>(&user_data), |
4205 | 420 | Return(0))); | 436 | Return(0))); |
4206 | 421 | 437 | ||
4207 | 422 | /* Handle the flip event */ | 438 | /* Handle the flip event */ |
4209 | 423 | EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _)) | 439 | EXPECT_CALL(mock_drm, drmHandleEvent(drm_fd, _)) |
4210 | 424 | .Times(1) | 440 | .Times(1) |
4211 | 425 | .WillOnce(DoAll(InvokePageFlipHandler(&user_data), Return(0))); | 441 | .WillOnce(DoAll(InvokePageFlipHandler(&user_data), Return(0))); |
4212 | 426 | 442 | ||
4213 | @@ -455,7 +471,7 @@ | |||
4214 | 455 | setup_post_update_expectations(); | 471 | setup_post_update_expectations(); |
4215 | 456 | 472 | ||
4216 | 457 | // clear_crtc happens at some stage. Not interesting. | 473 | // clear_crtc happens at some stage. Not interesting. |
4218 | 458 | EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), | 474 | EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd, |
4219 | 459 | crtc_id, 0, | 475 | crtc_id, 0, |
4220 | 460 | _, _, _, _, _)) | 476 | _, _, _, _, _)) |
4221 | 461 | .WillOnce(Return(0)); | 477 | .WillOnce(Return(0)); |
4222 | @@ -465,13 +481,13 @@ | |||
4223 | 465 | 481 | ||
4224 | 466 | // DisplayBuffer construction paints an empty screen. | 482 | // DisplayBuffer construction paints an empty screen. |
4225 | 467 | // That's probably less than ideal but we've always had it that way. | 483 | // That's probably less than ideal but we've always had it that way. |
4227 | 468 | EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), | 484 | EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd, |
4228 | 469 | crtc_id, fake.fb_id1, | 485 | crtc_id, fake.fb_id1, |
4229 | 470 | _, _, _, _, _)) | 486 | _, _, _, _, _)) |
4230 | 471 | .WillOnce(Return(0)); | 487 | .WillOnce(Return(0)); |
4231 | 472 | 488 | ||
4232 | 473 | // New FB flip failure | 489 | // New FB flip failure |
4234 | 474 | EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(), | 490 | EXPECT_CALL(mock_drm, drmModePageFlip(drm_fd, |
4235 | 475 | crtc_id, | 491 | crtc_id, |
4236 | 476 | fake.fb_id2, | 492 | fake.fb_id2, |
4237 | 477 | _, _)) | 493 | _, _)) |
4238 | @@ -479,7 +495,7 @@ | |||
4239 | 479 | .WillOnce(Return(-1)); | 495 | .WillOnce(Return(-1)); |
4240 | 480 | 496 | ||
4241 | 481 | // Expect fallback to blitting | 497 | // Expect fallback to blitting |
4243 | 482 | EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), | 498 | EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd, |
4244 | 483 | crtc_id, fake.fb_id2, | 499 | crtc_id, fake.fb_id2, |
4245 | 484 | _, _, _, _, _)) | 500 | _, _, _, _, _)) |
4246 | 485 | .WillOnce(Return(0)); | 501 | .WillOnce(Return(0)); |
4247 | @@ -623,21 +639,6 @@ | |||
4248 | 623 | }, std::runtime_error); | 639 | }, std::runtime_error); |
4249 | 624 | } | 640 | } |
4250 | 625 | 641 | ||
4251 | 626 | TEST_F(MesaDisplayTest, constructor_throws_if_gl_oes_image_not_supported) | ||
4252 | 627 | { | ||
4253 | 628 | using namespace ::testing; | ||
4254 | 629 | |||
4255 | 630 | const char* gl_exts = "GL_OES_texture_npot GL_OES_blend_func_separate"; | ||
4256 | 631 | |||
4257 | 632 | EXPECT_CALL(mock_gl, glGetString(GL_EXTENSIONS)) | ||
4258 | 633 | .WillOnce(Return(reinterpret_cast<const GLubyte*>(gl_exts))); | ||
4259 | 634 | |||
4260 | 635 | EXPECT_THROW( | ||
4261 | 636 | { | ||
4262 | 637 | auto display = create_display(create_platform()); | ||
4263 | 638 | }, std::runtime_error); | ||
4264 | 639 | } | ||
4265 | 640 | |||
4266 | 641 | TEST_F(MesaDisplayTest, for_each_display_buffer_calls_callback) | 642 | TEST_F(MesaDisplayTest, for_each_display_buffer_calls_callback) |
4267 | 642 | { | 643 | { |
4268 | 643 | using namespace ::testing; | 644 | using namespace ::testing; |
4269 | @@ -677,8 +678,8 @@ | |||
4270 | 677 | { | 678 | { |
4271 | 678 | using namespace testing; | 679 | using namespace testing; |
4272 | 679 | 680 | ||
4275 | 680 | EXPECT_CALL(mock_drm, drmDropMaster(mock_drm.fake_drm.fd())) | 681 | EXPECT_CALL(mock_drm, drmDropMaster(_)) |
4276 | 681 | .Times(1); | 682 | .Times(2); |
4277 | 682 | 683 | ||
4278 | 683 | auto display = create_display(create_platform()); | 684 | auto display = create_display(create_platform()); |
4279 | 684 | 685 | ||
4280 | @@ -689,8 +690,8 @@ | |||
4281 | 689 | { | 690 | { |
4282 | 690 | using namespace testing; | 691 | using namespace testing; |
4283 | 691 | 692 | ||
4286 | 692 | EXPECT_CALL(mock_drm, drmSetMaster(mock_drm.fake_drm.fd())) | 693 | EXPECT_CALL(mock_drm, drmSetMaster(_)) |
4287 | 693 | .Times(1); | 694 | .Times(2); |
4288 | 694 | 695 | ||
4289 | 695 | auto display = create_display(create_platform()); | 696 | auto display = create_display(create_platform()); |
4290 | 696 | 697 | ||
4291 | 697 | 698 | ||
4292 | === modified file 'tests/unit-tests/platforms/mesa/kms/test_display_buffer.cpp' | |||
4293 | --- tests/unit-tests/platforms/mesa/kms/test_display_buffer.cpp 2017-03-13 08:12:52 +0000 | |||
4294 | +++ tests/unit-tests/platforms/mesa/kms/test_display_buffer.cpp 2017-03-22 06:55:00 +0000 | |||
4295 | @@ -93,6 +93,8 @@ | |||
4296 | 93 | .WillByDefault(Return(mock_refresh_rate)); | 93 | .WillByDefault(Return(mock_refresh_rate)); |
4297 | 94 | ON_CALL(*mock_kms_output, fb_for(_,_,_)) | 94 | ON_CALL(*mock_kms_output, fb_for(_,_,_)) |
4298 | 95 | .WillByDefault(Return(reinterpret_cast<FBHandle*>(0x12ad))); | 95 | .WillByDefault(Return(reinterpret_cast<FBHandle*>(0x12ad))); |
4299 | 96 | ON_CALL(*mock_kms_output, buffer_requires_migration(_)) | ||
4300 | 97 | .WillByDefault(Return(false)); | ||
4301 | 96 | 98 | ||
4302 | 97 | ON_CALL(*mock_bypassable_buffer, size()) | 99 | ON_CALL(*mock_bypassable_buffer, size()) |
4303 | 98 | .WillByDefault(Return(display_area.size)); | 100 | .WillByDefault(Return(display_area.size)); |
4304 | @@ -486,3 +488,18 @@ | |||
4305 | 486 | EXPECT_FALSE(db.overlay(list)); | 488 | EXPECT_FALSE(db.overlay(list)); |
4306 | 487 | } | 489 | } |
4307 | 488 | 490 | ||
4308 | 491 | TEST_F(MesaDisplayBufferTest, buffer_requiring_migration_is_ineligable_for_bypass) | ||
4309 | 492 | { | ||
4310 | 493 | ON_CALL(*mock_kms_output, buffer_requires_migration(Eq(stub_gbm_native_buffer->bo))) | ||
4311 | 494 | .WillByDefault(Return(true)); | ||
4312 | 495 | |||
4313 | 496 | graphics::mesa::DisplayBuffer db( | ||
4314 | 497 | graphics::mesa::BypassOption::allowed, | ||
4315 | 498 | null_display_report(), | ||
4316 | 499 | {mock_kms_output}, | ||
4317 | 500 | make_output_surface(), | ||
4318 | 501 | display_area, | ||
4319 | 502 | mir_orientation_normal); | ||
4320 | 503 | |||
4321 | 504 | EXPECT_FALSE(db.overlay(bypassable_list)); | ||
4322 | 505 | } | ||
4323 | 489 | 506 | ||
4324 | === modified file 'tests/unit-tests/platforms/mesa/kms/test_display_configuration.cpp' | |||
4325 | --- tests/unit-tests/platforms/mesa/kms/test_display_configuration.cpp 2017-03-14 02:26:28 +0000 | |||
4326 | +++ tests/unit-tests/platforms/mesa/kms/test_display_configuration.cpp 2017-03-22 06:55:00 +0000 | |||
4327 | @@ -44,6 +44,7 @@ | |||
4328 | 44 | #include <gtest/gtest.h> | 44 | #include <gtest/gtest.h> |
4329 | 45 | 45 | ||
4330 | 46 | #include <stdexcept> | 46 | #include <stdexcept> |
4331 | 47 | #include <fcntl.h> | ||
4332 | 47 | 48 | ||
4333 | 48 | namespace mg = mir::graphics; | 49 | namespace mg = mir::graphics; |
4334 | 49 | namespace mgm = mir::graphics::mesa; | 50 | namespace mgm = mir::graphics::mesa; |
4335 | @@ -92,21 +93,30 @@ | |||
4336 | 92 | { | 93 | { |
4337 | 93 | public: | 94 | public: |
4338 | 94 | MesaDisplayConfigurationTest() | 95 | MesaDisplayConfigurationTest() |
4339 | 96 | : drm_fd{open(drm_device, 0, 0)} | ||
4340 | 95 | { | 97 | { |
4341 | 96 | using namespace testing; | 98 | using namespace testing; |
4342 | 97 | 99 | ||
4343 | 98 | /* Needed for display start-up */ | 100 | /* Needed for display start-up */ |
4348 | 99 | ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_)) | 101 | ON_CALL(mock_egl, eglChooseConfig(_, _, _, 1, _)) |
4349 | 100 | .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), | 102 | .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), |
4350 | 101 | SetArgPointee<4>(1), | 103 | SetArgPointee<4>(1), |
4351 | 102 | Return(EGL_TRUE))); | 104 | Return(EGL_TRUE))); |
4352 | 103 | 105 | ||
4353 | 104 | mock_egl.provide_egl_extensions(); | 106 | mock_egl.provide_egl_extensions(); |
4354 | 105 | mock_gl.provide_gles_extensions(); | 107 | mock_gl.provide_gles_extensions(); |
4355 | 106 | 108 | ||
4356 | 109 | ON_CALL(mock_gbm, gbm_device_get_fd(_)) | ||
4357 | 110 | .WillByDefault(Return(drm_fd)); | ||
4358 | 111 | |||
4359 | 107 | setup_sample_modes(); | 112 | setup_sample_modes(); |
4360 | 108 | 113 | ||
4361 | 109 | fake_devices.add_standard_device("standard-drm-devices"); | 114 | fake_devices.add_standard_device("standard-drm-devices"); |
4362 | 115 | |||
4363 | 116 | // Remove all outputs from all but one of the DRM devices we access; | ||
4364 | 117 | // these tests are not set up to test hybrid. | ||
4365 | 118 | mock_drm.reset("/dev/dri/card1"); | ||
4366 | 119 | mock_drm.reset("/dev/dri/card2"); | ||
4367 | 110 | } | 120 | } |
4368 | 111 | 121 | ||
4369 | 112 | std::shared_ptr<mg::Platform> create_platform() | 122 | std::shared_ptr<mg::Platform> create_platform() |
4370 | @@ -160,8 +170,46 @@ | |||
4371 | 160 | std::vector<drmModeModeInfo> modes_empty; | 170 | std::vector<drmModeModeInfo> modes_empty; |
4372 | 161 | 171 | ||
4373 | 162 | mtf::UdevEnvironment fake_devices; | 172 | mtf::UdevEnvironment fake_devices; |
4374 | 173 | |||
4375 | 174 | char const* const drm_device = "/dev/dri/card0"; | ||
4376 | 175 | int const drm_fd; | ||
4377 | 163 | }; | 176 | }; |
4378 | 164 | 177 | ||
4379 | 178 | MATCHER_P(Unique, scratch_vector, "") | ||
4380 | 179 | { | ||
4381 | 180 | if (std::any_of( | ||
4382 | 181 | scratch_vector.begin(), | ||
4383 | 182 | scratch_vector.end(), | ||
4384 | 183 | [&arg](auto const& candidate) { return arg == candidate; })) | ||
4385 | 184 | { | ||
4386 | 185 | return false; | ||
4387 | 186 | } | ||
4388 | 187 | const_cast<typename std::remove_const<scratch_vector_type>::type&>(scratch_vector).push_back(arg); | ||
4389 | 188 | return true; | ||
4390 | 189 | } | ||
4391 | 190 | |||
4392 | 191 | MATCHER_P(DisplayConfigsAreEquivalent, expected_configs, "") | ||
4393 | 192 | { | ||
4394 | 193 | using namespace testing; | ||
4395 | 194 | |||
4396 | 195 | std::vector<mg::DisplayConfigurationOutput> outputs; | ||
4397 | 196 | std::vector<mg::DisplayConfigurationOutputId> output_ids; | ||
4398 | 197 | arg->for_each_output([&](mg::DisplayConfigurationOutput const& output) | ||
4399 | 198 | { | ||
4400 | 199 | outputs.push_back(output); | ||
4401 | 200 | output_ids.push_back(output.id); | ||
4402 | 201 | outputs.back().id = mg::DisplayConfigurationOutputId{0}; | ||
4403 | 202 | }); | ||
4404 | 203 | |||
4405 | 204 | |||
4406 | 205 | Matcher<decltype(outputs)> config_matcher = UnorderedElementsAreArray(expected_configs); | ||
4407 | 206 | std::vector<mg::DisplayConfigurationOutputId> scratch_space; | ||
4408 | 207 | Matcher<decltype(output_ids)> ids_are_unique = Each(Unique(scratch_space)); | ||
4409 | 208 | |||
4410 | 209 | return | ||
4411 | 210 | config_matcher.MatchAndExplain(outputs, result_listener) && | ||
4412 | 211 | ids_are_unique.MatchAndExplain(output_ids, result_listener); | ||
4413 | 212 | } | ||
4414 | 165 | } | 213 | } |
4415 | 166 | 214 | ||
4416 | 167 | TEST_F(MesaDisplayConfigurationTest, configuration_is_read_correctly) | 215 | TEST_F(MesaDisplayConfigurationTest, configuration_is_read_correctly) |
4417 | @@ -182,34 +230,58 @@ | |||
4418 | 182 | std::vector<uint32_t> possible_encoder_ids_empty; | 230 | std::vector<uint32_t> possible_encoder_ids_empty; |
4419 | 183 | uint32_t const possible_crtcs_mask_empty{0}; | 231 | uint32_t const possible_crtcs_mask_empty{0}; |
4420 | 184 | 232 | ||
4444 | 185 | mtd::FakeDRMResources& resources(mock_drm.fake_drm); | 233 | mock_drm.reset(drm_device); |
4445 | 186 | 234 | ||
4446 | 187 | resources.reset(); | 235 | mock_drm.add_crtc( |
4447 | 188 | 236 | drm_device, | |
4448 | 189 | resources.add_crtc(crtc0_id, modes0[1]); | 237 | crtc0_id, |
4449 | 190 | 238 | modes0[1]); | |
4450 | 191 | resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty); | 239 | |
4451 | 192 | resources.add_encoder(encoder1_id, invalid_id, possible_crtcs_mask_empty); | 240 | mock_drm.add_encoder( |
4452 | 193 | 241 | drm_device, | |
4453 | 194 | resources.add_connector(connector0_id, DRM_MODE_CONNECTOR_HDMIA, | 242 | encoder0_id, |
4454 | 195 | DRM_MODE_CONNECTED, encoder0_id, | 243 | crtc0_id, |
4455 | 196 | modes0, possible_encoder_ids_empty, | 244 | possible_crtcs_mask_empty); |
4456 | 197 | connector0_physical_size_mm); | 245 | mock_drm.add_encoder( |
4457 | 198 | resources.add_connector(connector1_id, DRM_MODE_CONNECTOR_Unknown, | 246 | drm_device, |
4458 | 199 | DRM_MODE_DISCONNECTED, invalid_id, | 247 | encoder1_id, |
4459 | 200 | modes_empty, possible_encoder_ids_empty, | 248 | invalid_id, |
4460 | 201 | connector1_physical_size_mm); | 249 | possible_crtcs_mask_empty); |
4461 | 202 | resources.add_connector(connector2_id, DRM_MODE_CONNECTOR_eDP, | 250 | |
4462 | 203 | DRM_MODE_DISCONNECTED, encoder1_id, | 251 | mock_drm.add_connector( |
4463 | 204 | modes_empty, possible_encoder_ids_empty, | 252 | drm_device, |
4464 | 205 | connector2_physical_size_mm); | 253 | connector0_id, |
4465 | 206 | 254 | DRM_MODE_CONNECTOR_HDMIA, | |
4466 | 207 | resources.prepare(); | 255 | DRM_MODE_CONNECTED, |
4467 | 256 | encoder0_id, | ||
4468 | 257 | modes0, | ||
4469 | 258 | possible_encoder_ids_empty, | ||
4470 | 259 | connector0_physical_size_mm); | ||
4471 | 260 | mock_drm.add_connector( | ||
4472 | 261 | drm_device, | ||
4473 | 262 | connector1_id, | ||
4474 | 263 | DRM_MODE_CONNECTOR_Unknown, | ||
4475 | 264 | DRM_MODE_DISCONNECTED, | ||
4476 | 265 | invalid_id, | ||
4477 | 266 | modes_empty, | ||
4478 | 267 | possible_encoder_ids_empty, | ||
4479 | 268 | connector1_physical_size_mm); | ||
4480 | 269 | mock_drm.add_connector( | ||
4481 | 270 | drm_device, | ||
4482 | 271 | connector2_id, | ||
4483 | 272 | DRM_MODE_CONNECTOR_eDP, | ||
4484 | 273 | DRM_MODE_DISCONNECTED, | ||
4485 | 274 | encoder1_id, | ||
4486 | 275 | modes_empty, | ||
4487 | 276 | possible_encoder_ids_empty, | ||
4488 | 277 | connector2_physical_size_mm); | ||
4489 | 278 | |||
4490 | 279 | mock_drm.prepare(drm_device); | ||
4491 | 208 | 280 | ||
4492 | 209 | std::vector<mg::DisplayConfigurationOutput> const expected_outputs = | 281 | std::vector<mg::DisplayConfigurationOutput> const expected_outputs = |
4493 | 210 | { | 282 | { |
4494 | 211 | { | 283 | { |
4496 | 212 | mg::DisplayConfigurationOutputId{connector0_id}, | 284 | mg::DisplayConfigurationOutputId{0}, |
4497 | 213 | mg::DisplayConfigurationCardId{0}, | 285 | mg::DisplayConfigurationCardId{0}, |
4498 | 214 | mg::DisplayConfigurationOutputType::hdmia, | 286 | mg::DisplayConfigurationOutputType::hdmia, |
4499 | 215 | {}, | 287 | {}, |
4500 | @@ -231,7 +303,7 @@ | |||
4501 | 231 | {} | 303 | {} |
4502 | 232 | }, | 304 | }, |
4503 | 233 | { | 305 | { |
4505 | 234 | mg::DisplayConfigurationOutputId{connector1_id}, | 306 | mg::DisplayConfigurationOutputId{0}, |
4506 | 235 | mg::DisplayConfigurationCardId{0}, | 307 | mg::DisplayConfigurationCardId{0}, |
4507 | 236 | mg::DisplayConfigurationOutputType::unknown, | 308 | mg::DisplayConfigurationOutputType::unknown, |
4508 | 237 | {}, | 309 | {}, |
4509 | @@ -253,7 +325,7 @@ | |||
4510 | 253 | {} | 325 | {} |
4511 | 254 | }, | 326 | }, |
4512 | 255 | { | 327 | { |
4514 | 256 | mg::DisplayConfigurationOutputId{connector2_id}, | 328 | mg::DisplayConfigurationOutputId{0}, |
4515 | 257 | mg::DisplayConfigurationCardId{0}, | 329 | mg::DisplayConfigurationCardId{0}, |
4516 | 258 | mg::DisplayConfigurationOutputType::edp, | 330 | mg::DisplayConfigurationOutputType::edp, |
4517 | 259 | {}, | 331 | {}, |
4518 | @@ -281,16 +353,7 @@ | |||
4519 | 281 | 353 | ||
4520 | 282 | auto conf = display->configuration(); | 354 | auto conf = display->configuration(); |
4521 | 283 | 355 | ||
4532 | 284 | size_t output_count{0}; | 356 | EXPECT_THAT(conf, DisplayConfigsAreEquivalent(expected_outputs)); |
4523 | 285 | |||
4524 | 286 | conf->for_each_output([&](mg::DisplayConfigurationOutput const& output) | ||
4525 | 287 | { | ||
4526 | 288 | ASSERT_LT(output_count, expected_outputs.size()); | ||
4527 | 289 | EXPECT_EQ(expected_outputs[output_count], output) << "output_count: " << output_count; | ||
4528 | 290 | ++output_count; | ||
4529 | 291 | }); | ||
4530 | 292 | |||
4531 | 293 | EXPECT_EQ(expected_outputs.size(), output_count); | ||
4533 | 294 | } | 357 | } |
4534 | 295 | 358 | ||
4535 | 296 | TEST_F(MesaDisplayConfigurationTest, reads_subpixel_information_correctly) | 359 | TEST_F(MesaDisplayConfigurationTest, reads_subpixel_information_correctly) |
4536 | @@ -305,8 +368,6 @@ | |||
4537 | 305 | std::vector<uint32_t> possible_encoder_ids_empty; | 368 | std::vector<uint32_t> possible_encoder_ids_empty; |
4538 | 306 | uint32_t const possible_crtcs_mask_empty{0}; | 369 | uint32_t const possible_crtcs_mask_empty{0}; |
4539 | 307 | 370 | ||
4540 | 308 | mtd::FakeDRMResources& resources(mock_drm.fake_drm); | ||
4541 | 309 | |||
4542 | 310 | struct TestData | 371 | struct TestData |
4543 | 311 | { | 372 | { |
4544 | 312 | drmModeSubPixel drm_subpixel; | 373 | drmModeSubPixel drm_subpixel; |
4545 | @@ -324,19 +385,31 @@ | |||
4546 | 324 | for (auto& data : test_data) | 385 | for (auto& data : test_data) |
4547 | 325 | { | 386 | { |
4548 | 326 | 387 | ||
4562 | 327 | resources.reset(); | 388 | mock_drm.reset(drm_device); |
4563 | 328 | 389 | ||
4564 | 329 | resources.add_crtc(crtc0_id, modes0[1]); | 390 | mock_drm.add_crtc( |
4565 | 330 | 391 | drm_device, | |
4566 | 331 | resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty); | 392 | crtc0_id, |
4567 | 332 | 393 | modes0[1]); | |
4568 | 333 | resources.add_connector(connector0_id, DRM_MODE_CONNECTOR_HDMIA, | 394 | |
4569 | 334 | DRM_MODE_CONNECTED, encoder0_id, | 395 | mock_drm.add_encoder( |
4570 | 335 | modes0, possible_encoder_ids_empty, | 396 | drm_device, |
4571 | 336 | connector0_physical_size_mm, | 397 | encoder0_id, |
4572 | 337 | data.drm_subpixel); | 398 | crtc0_id, |
4573 | 338 | 399 | possible_crtcs_mask_empty); | |
4574 | 339 | resources.prepare(); | 400 | |
4575 | 401 | mock_drm.add_connector( | ||
4576 | 402 | drm_device, | ||
4577 | 403 | connector0_id, | ||
4578 | 404 | DRM_MODE_CONNECTOR_HDMIA, | ||
4579 | 405 | DRM_MODE_CONNECTED, | ||
4580 | 406 | encoder0_id, | ||
4581 | 407 | modes0, | ||
4582 | 408 | possible_encoder_ids_empty, | ||
4583 | 409 | connector0_physical_size_mm, | ||
4584 | 410 | data.drm_subpixel); | ||
4585 | 411 | |||
4586 | 412 | mock_drm.prepare(drm_device); | ||
4587 | 340 | 413 | ||
4588 | 341 | /* Test body */ | 414 | /* Test body */ |
4589 | 342 | auto display = create_display(create_platform()); | 415 | auto display = create_display(create_platform()); |
4590 | @@ -369,21 +442,31 @@ | |||
4591 | 369 | std::vector<uint32_t> possible_encoder_ids_empty; | 442 | std::vector<uint32_t> possible_encoder_ids_empty; |
4592 | 370 | uint32_t const possible_crtcs_mask_empty{0}; | 443 | uint32_t const possible_crtcs_mask_empty{0}; |
4593 | 371 | 444 | ||
4609 | 372 | mtd::FakeDRMResources& resources(mock_drm.fake_drm); | 445 | mock_drm.reset(drm_device); |
4610 | 373 | 446 | ||
4611 | 374 | resources.reset(); | 447 | mock_drm.add_crtc( |
4612 | 375 | 448 | drm_device, | |
4613 | 376 | resources.add_crtc(crtc0_id, modes0[1]); | 449 | crtc0_id, |
4614 | 377 | 450 | modes0[1]); | |
4615 | 378 | resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty); | 451 | |
4616 | 379 | 452 | mock_drm.add_encoder( | |
4617 | 380 | resources.add_connector(connector0_id, DRM_MODE_CONNECTOR_HDMIA, | 453 | drm_device, |
4618 | 381 | DRM_MODE_CONNECTED, encoder0_id, | 454 | encoder0_id, |
4619 | 382 | modes0, possible_encoder_ids_empty, | 455 | crtc0_id, |
4620 | 383 | connector0_physical_size_mm, | 456 | possible_crtcs_mask_empty); |
4621 | 384 | DRM_MODE_SUBPIXEL_NONE); | 457 | |
4622 | 385 | 458 | mock_drm.add_connector( | |
4623 | 386 | resources.prepare(); | 459 | drm_device, |
4624 | 460 | connector0_id, | ||
4625 | 461 | DRM_MODE_CONNECTOR_HDMIA, | ||
4626 | 462 | DRM_MODE_CONNECTED, | ||
4627 | 463 | encoder0_id, | ||
4628 | 464 | modes0, | ||
4629 | 465 | possible_encoder_ids_empty, | ||
4630 | 466 | connector0_physical_size_mm, | ||
4631 | 467 | DRM_MODE_SUBPIXEL_NONE); | ||
4632 | 468 | |||
4633 | 469 | mock_drm.prepare(drm_device); | ||
4634 | 387 | 470 | ||
4635 | 388 | struct TestData | 471 | struct TestData |
4636 | 389 | { | 472 | { |
4637 | @@ -405,19 +488,31 @@ | |||
4638 | 405 | for (auto& data : test_data) | 488 | for (auto& data : test_data) |
4639 | 406 | { | 489 | { |
4640 | 407 | 490 | ||
4654 | 408 | resources.reset(); | 491 | mock_drm.reset(drm_device); |
4655 | 409 | 492 | ||
4656 | 410 | resources.add_crtc(crtc0_id, modes0[1]); | 493 | mock_drm.add_crtc( |
4657 | 411 | 494 | drm_device, | |
4658 | 412 | resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty); | 495 | crtc0_id, |
4659 | 413 | 496 | modes0[1]); | |
4660 | 414 | resources.add_connector(connector0_id, DRM_MODE_CONNECTOR_HDMIA, | 497 | |
4661 | 415 | DRM_MODE_CONNECTED, encoder0_id, | 498 | mock_drm.add_encoder( |
4662 | 416 | modes0, possible_encoder_ids_empty, | 499 | drm_device, |
4663 | 417 | connector0_physical_size_mm, | 500 | encoder0_id, |
4664 | 418 | data.drm_subpixel); | 501 | crtc0_id, |
4665 | 419 | 502 | possible_crtcs_mask_empty); | |
4666 | 420 | resources.prepare(); | 503 | |
4667 | 504 | mock_drm.add_connector( | ||
4668 | 505 | drm_device, | ||
4669 | 506 | connector0_id, | ||
4670 | 507 | DRM_MODE_CONNECTOR_HDMIA, | ||
4671 | 508 | DRM_MODE_CONNECTED, | ||
4672 | 509 | encoder0_id, | ||
4673 | 510 | modes0, | ||
4674 | 511 | possible_encoder_ids_empty, | ||
4675 | 512 | connector0_physical_size_mm, | ||
4676 | 513 | data.drm_subpixel); | ||
4677 | 514 | |||
4678 | 515 | mock_drm.prepare(drm_device); | ||
4679 | 421 | 516 | ||
4680 | 422 | mt::Signal handler_signal; | 517 | mt::Signal handler_signal; |
4681 | 423 | MainLoop ml; | 518 | MainLoop ml; |
4682 | @@ -442,88 +537,6 @@ | |||
4683 | 442 | } | 537 | } |
4684 | 443 | } | 538 | } |
4685 | 444 | 539 | ||
4686 | 445 | TEST_F(MesaDisplayConfigurationTest, get_kms_connector_id_returns_correct_id) | ||
4687 | 446 | { | ||
4688 | 447 | uint32_t const crtc0_id{10}; | ||
4689 | 448 | uint32_t const encoder0_id{20}; | ||
4690 | 449 | uint32_t const possible_crtcs_mask_empty{0}; | ||
4691 | 450 | std::vector<uint32_t> const connector_ids{30, 31}; | ||
4692 | 451 | std::vector<uint32_t> encoder_ids{20}; | ||
4693 | 452 | |||
4694 | 453 | /* Set up DRM resources */ | ||
4695 | 454 | mtd::FakeDRMResources& resources(mock_drm.fake_drm); | ||
4696 | 455 | |||
4697 | 456 | resources.reset(); | ||
4698 | 457 | |||
4699 | 458 | resources.add_crtc(crtc0_id, modes0[1]); | ||
4700 | 459 | resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty); | ||
4701 | 460 | for (auto id : connector_ids) | ||
4702 | 461 | { | ||
4703 | 462 | resources.add_connector(id, DRM_MODE_CONNECTOR_DVID, | ||
4704 | 463 | DRM_MODE_CONNECTED, encoder0_id, | ||
4705 | 464 | modes0, encoder_ids, | ||
4706 | 465 | geom::Size()); | ||
4707 | 466 | } | ||
4708 | 467 | |||
4709 | 468 | resources.prepare(); | ||
4710 | 469 | |||
4711 | 470 | /* Test body */ | ||
4712 | 471 | auto display = create_display(create_platform()); | ||
4713 | 472 | |||
4714 | 473 | std::shared_ptr<mg::DisplayConfiguration> conf = display->configuration(); | ||
4715 | 474 | auto const& kms_conf = std::static_pointer_cast<mgm::KMSDisplayConfiguration>(conf); | ||
4716 | 475 | |||
4717 | 476 | size_t output_count{0}; | ||
4718 | 477 | |||
4719 | 478 | conf->for_each_output([&](mg::DisplayConfigurationOutput const& output) | ||
4720 | 479 | { | ||
4721 | 480 | ASSERT_LT(output_count, connector_ids.size()); | ||
4722 | 481 | |||
4723 | 482 | EXPECT_EQ(connector_ids[output_count], | ||
4724 | 483 | kms_conf->get_kms_connector_id(output.id)); | ||
4725 | 484 | ++output_count; | ||
4726 | 485 | }); | ||
4727 | 486 | } | ||
4728 | 487 | |||
4729 | 488 | TEST_F(MesaDisplayConfigurationTest, get_kms_connector_id_throws_on_invalid_id) | ||
4730 | 489 | { | ||
4731 | 490 | uint32_t const crtc0_id{10}; | ||
4732 | 491 | uint32_t const encoder0_id{20}; | ||
4733 | 492 | uint32_t const possible_crtcs_mask_empty{0}; | ||
4734 | 493 | std::vector<uint32_t> const connector_ids{30, 31}; | ||
4735 | 494 | std::vector<uint32_t> encoder_ids{20}; | ||
4736 | 495 | |||
4737 | 496 | /* Set up DRM resources */ | ||
4738 | 497 | mtd::FakeDRMResources& resources(mock_drm.fake_drm); | ||
4739 | 498 | |||
4740 | 499 | resources.reset(); | ||
4741 | 500 | |||
4742 | 501 | resources.add_crtc(crtc0_id, modes0[1]); | ||
4743 | 502 | resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty); | ||
4744 | 503 | for (auto id : connector_ids) | ||
4745 | 504 | { | ||
4746 | 505 | resources.add_connector(id, DRM_MODE_CONNECTOR_VGA, | ||
4747 | 506 | DRM_MODE_CONNECTED, encoder0_id, | ||
4748 | 507 | modes0, encoder_ids, | ||
4749 | 508 | geom::Size()); | ||
4750 | 509 | } | ||
4751 | 510 | |||
4752 | 511 | resources.prepare(); | ||
4753 | 512 | |||
4754 | 513 | /* Test body */ | ||
4755 | 514 | auto display = create_display(create_platform()); | ||
4756 | 515 | |||
4757 | 516 | std::shared_ptr<mg::DisplayConfiguration> conf = display->configuration(); | ||
4758 | 517 | auto const& kms_conf = std::static_pointer_cast<mgm::KMSDisplayConfiguration>(conf); | ||
4759 | 518 | |||
4760 | 519 | EXPECT_THROW({ | ||
4761 | 520 | kms_conf->get_kms_connector_id(mg::DisplayConfigurationOutputId{29}); | ||
4762 | 521 | }, std::runtime_error); | ||
4763 | 522 | EXPECT_THROW({ | ||
4764 | 523 | kms_conf->get_kms_connector_id(mg::DisplayConfigurationOutputId{32}); | ||
4765 | 524 | }, std::runtime_error); | ||
4766 | 525 | } | ||
4767 | 526 | |||
4768 | 527 | TEST_F(MesaDisplayConfigurationTest, returns_updated_configuration) | 540 | TEST_F(MesaDisplayConfigurationTest, returns_updated_configuration) |
4769 | 528 | { | 541 | { |
4770 | 529 | using namespace ::testing; | 542 | using namespace ::testing; |
4771 | @@ -545,7 +558,7 @@ | |||
4772 | 545 | std::vector<mg::DisplayConfigurationOutput> const expected_outputs_before = | 558 | std::vector<mg::DisplayConfigurationOutput> const expected_outputs_before = |
4773 | 546 | { | 559 | { |
4774 | 547 | { | 560 | { |
4776 | 548 | mg::DisplayConfigurationOutputId(connector_ids[0]), | 561 | mg::DisplayConfigurationOutputId{0}, |
4777 | 549 | mg::DisplayConfigurationCardId{0}, | 562 | mg::DisplayConfigurationCardId{0}, |
4778 | 550 | mg::DisplayConfigurationOutputType::composite, | 563 | mg::DisplayConfigurationOutputType::composite, |
4779 | 551 | {}, | 564 | {}, |
4780 | @@ -567,7 +580,7 @@ | |||
4781 | 567 | {} | 580 | {} |
4782 | 568 | }, | 581 | }, |
4783 | 569 | { | 582 | { |
4785 | 570 | mg::DisplayConfigurationOutputId(connector_ids[1]), | 583 | mg::DisplayConfigurationOutputId{0}, |
4786 | 571 | mg::DisplayConfigurationCardId{0}, | 584 | mg::DisplayConfigurationCardId{0}, |
4787 | 572 | mg::DisplayConfigurationOutputType::vga, | 585 | mg::DisplayConfigurationOutputType::vga, |
4788 | 573 | {}, | 586 | {}, |
4789 | @@ -593,7 +606,7 @@ | |||
4790 | 593 | std::vector<mg::DisplayConfigurationOutput> const expected_outputs_after = | 606 | std::vector<mg::DisplayConfigurationOutput> const expected_outputs_after = |
4791 | 594 | { | 607 | { |
4792 | 595 | { | 608 | { |
4794 | 596 | mg::DisplayConfigurationOutputId(connector_ids[0]), | 609 | mg::DisplayConfigurationOutputId{0}, |
4795 | 597 | mg::DisplayConfigurationCardId{0}, | 610 | mg::DisplayConfigurationCardId{0}, |
4796 | 598 | mg::DisplayConfigurationOutputType::composite, | 611 | mg::DisplayConfigurationOutputType::composite, |
4797 | 599 | {}, | 612 | {}, |
4798 | @@ -615,7 +628,7 @@ | |||
4799 | 615 | {} | 628 | {} |
4800 | 616 | }, | 629 | }, |
4801 | 617 | { | 630 | { |
4803 | 618 | mg::DisplayConfigurationOutputId(connector_ids[1]), | 631 | mg::DisplayConfigurationOutputId{0}, |
4804 | 619 | mg::DisplayConfigurationCardId{0}, | 632 | mg::DisplayConfigurationCardId{0}, |
4805 | 620 | mg::DisplayConfigurationOutputType::vga, | 633 | mg::DisplayConfigurationOutputType::vga, |
4806 | 621 | {}, | 634 | {}, |
4807 | @@ -639,60 +652,100 @@ | |||
4808 | 639 | }; | 652 | }; |
4809 | 640 | 653 | ||
4810 | 641 | /* Set up DRM resources and check */ | 654 | /* Set up DRM resources and check */ |
4831 | 642 | mtd::FakeDRMResources& resources(mock_drm.fake_drm); | 655 | auto const syspath = fake_devices.add_device( |
4832 | 643 | auto const syspath = fake_devices.add_device("drm", "card2", NULL, {}, {"DEVTYPE", "drm_minor"}); | 656 | "drm", |
4833 | 644 | 657 | "card2", | |
4834 | 645 | resources.reset(); | 658 | NULL, |
4835 | 646 | 659 | {}, | |
4836 | 647 | resources.add_crtc(crtc_ids[0], modes0[1]); | 660 | { |
4837 | 648 | 661 | "DEVTYPE", "drm_minor", | |
4838 | 649 | resources.add_encoder(encoder_ids[0], crtc_ids[0], possible_crtcs_mask_empty); | 662 | "DEVNAME", "/dev/dri/card2" |
4839 | 650 | resources.add_encoder(encoder_ids[1], invalid_id, possible_crtcs_mask_empty); | 663 | }); |
4840 | 651 | 664 | ||
4841 | 652 | resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_Composite, | 665 | mock_drm.reset(drm_device); |
4842 | 653 | DRM_MODE_CONNECTED, encoder_ids[0], | 666 | |
4843 | 654 | modes0, possible_encoder_ids_empty, | 667 | mock_drm.add_crtc( |
4844 | 655 | connector_physical_sizes_mm_before[0]); | 668 | drm_device, |
4845 | 656 | resources.add_connector(connector_ids[1], DRM_MODE_CONNECTOR_VGA, | 669 | crtc_ids[0], |
4846 | 657 | DRM_MODE_DISCONNECTED, invalid_id, | 670 | modes0[1]); |
4847 | 658 | modes_empty, possible_encoder_ids_empty, | 671 | |
4848 | 659 | connector_physical_sizes_mm_before[1]); | 672 | mock_drm.add_encoder( |
4849 | 660 | 673 | drm_device, | |
4850 | 661 | resources.prepare(); | 674 | encoder_ids[0], |
4851 | 675 | crtc_ids[0], | ||
4852 | 676 | possible_crtcs_mask_empty); | ||
4853 | 677 | mock_drm.add_encoder( | ||
4854 | 678 | drm_device, | ||
4855 | 679 | encoder_ids[1], | ||
4856 | 680 | invalid_id, | ||
4857 | 681 | possible_crtcs_mask_empty); | ||
4858 | 682 | |||
4859 | 683 | mock_drm.add_connector( | ||
4860 | 684 | drm_device, | ||
4861 | 685 | connector_ids[0], | ||
4862 | 686 | DRM_MODE_CONNECTOR_Composite, | ||
4863 | 687 | DRM_MODE_CONNECTED, | ||
4864 | 688 | encoder_ids[0], | ||
4865 | 689 | modes0, | ||
4866 | 690 | possible_encoder_ids_empty, | ||
4867 | 691 | connector_physical_sizes_mm_before[0]); | ||
4868 | 692 | mock_drm.add_connector( | ||
4869 | 693 | drm_device, | ||
4870 | 694 | connector_ids[1], | ||
4871 | 695 | DRM_MODE_CONNECTOR_VGA, | ||
4872 | 696 | DRM_MODE_DISCONNECTED, | ||
4873 | 697 | invalid_id, | ||
4874 | 698 | modes_empty, | ||
4875 | 699 | possible_encoder_ids_empty, | ||
4876 | 700 | connector_physical_sizes_mm_before[1]); | ||
4877 | 701 | |||
4878 | 702 | mock_drm.prepare(drm_device); | ||
4879 | 662 | 703 | ||
4880 | 663 | auto display = create_display(create_platform()); | 704 | auto display = create_display(create_platform()); |
4881 | 664 | 705 | ||
4882 | 665 | auto conf = display->configuration(); | 706 | auto conf = display->configuration(); |
4883 | 666 | 707 | ||
4894 | 667 | size_t output_count{0}; | 708 | EXPECT_THAT(conf, DisplayConfigsAreEquivalent(expected_outputs_before)); |
4885 | 668 | |||
4886 | 669 | conf->for_each_output([&](mg::DisplayConfigurationOutput const& output) | ||
4887 | 670 | { | ||
4888 | 671 | ASSERT_LT(output_count, expected_outputs_before.size()); | ||
4889 | 672 | EXPECT_EQ(expected_outputs_before[output_count], output) << "output_count: " << output_count; | ||
4890 | 673 | ++output_count; | ||
4891 | 674 | }); | ||
4892 | 675 | |||
4893 | 676 | EXPECT_EQ(expected_outputs_before.size(), output_count); | ||
4895 | 677 | 709 | ||
4896 | 678 | /* Reset DRM resources and check again */ | 710 | /* Reset DRM resources and check again */ |
4914 | 679 | resources.reset(); | 711 | mock_drm.reset(drm_device); |
4915 | 680 | 712 | ||
4916 | 681 | resources.add_crtc(crtc_ids[1], modes0[1]); | 713 | mock_drm.add_crtc( |
4917 | 682 | 714 | drm_device, | |
4918 | 683 | resources.add_encoder(encoder_ids[0], invalid_id, possible_crtcs_mask_empty); | 715 | crtc_ids[1], |
4919 | 684 | resources.add_encoder(encoder_ids[1], crtc_ids[1], possible_crtcs_mask_empty); | 716 | modes0[1]); |
4920 | 685 | 717 | ||
4921 | 686 | resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_Composite, | 718 | mock_drm.add_encoder( |
4922 | 687 | DRM_MODE_DISCONNECTED, invalid_id, | 719 | drm_device, |
4923 | 688 | modes_empty, possible_encoder_ids_empty, | 720 | encoder_ids[0], |
4924 | 689 | connector_physical_sizes_mm_after[0]); | 721 | invalid_id, |
4925 | 690 | resources.add_connector(connector_ids[1], DRM_MODE_CONNECTOR_VGA, | 722 | possible_crtcs_mask_empty); |
4926 | 691 | DRM_MODE_CONNECTED, encoder_ids[1], | 723 | mock_drm.add_encoder( |
4927 | 692 | modes0, possible_encoder_ids_empty, | 724 | drm_device, |
4928 | 693 | connector_physical_sizes_mm_after[1]); | 725 | encoder_ids[1], |
4929 | 694 | 726 | crtc_ids[1], | |
4930 | 695 | resources.prepare(); | 727 | possible_crtcs_mask_empty); |
4931 | 728 | |||
4932 | 729 | mock_drm.add_connector( | ||
4933 | 730 | drm_device, | ||
4934 | 731 | connector_ids[0], | ||
4935 | 732 | DRM_MODE_CONNECTOR_Composite, | ||
4936 | 733 | DRM_MODE_DISCONNECTED, | ||
4937 | 734 | invalid_id, | ||
4938 | 735 | modes_empty, | ||
4939 | 736 | possible_encoder_ids_empty, | ||
4940 | 737 | connector_physical_sizes_mm_after[0]); | ||
4941 | 738 | mock_drm.add_connector( | ||
4942 | 739 | drm_device, | ||
4943 | 740 | connector_ids[1], | ||
4944 | 741 | DRM_MODE_CONNECTOR_VGA, | ||
4945 | 742 | DRM_MODE_CONNECTED, | ||
4946 | 743 | encoder_ids[1], | ||
4947 | 744 | modes0, | ||
4948 | 745 | possible_encoder_ids_empty, | ||
4949 | 746 | connector_physical_sizes_mm_after[1]); | ||
4950 | 747 | |||
4951 | 748 | mock_drm.prepare(drm_device); | ||
4952 | 696 | 749 | ||
4953 | 697 | /* Fake a device change so display can fetch updated configuration*/ | 750 | /* Fake a device change so display can fetch updated configuration*/ |
4954 | 698 | MainLoop ml; | 751 | MainLoop ml; |
4955 | @@ -703,16 +756,7 @@ | |||
4956 | 703 | 756 | ||
4957 | 704 | conf = display->configuration(); | 757 | conf = display->configuration(); |
4958 | 705 | 758 | ||
4969 | 706 | output_count = 0; | 759 | EXPECT_THAT(conf, DisplayConfigsAreEquivalent(expected_outputs_after)); |
4960 | 707 | |||
4961 | 708 | conf->for_each_output([&](mg::DisplayConfigurationOutput const& output) | ||
4962 | 709 | { | ||
4963 | 710 | ASSERT_LT(output_count, expected_outputs_after.size()); | ||
4964 | 711 | EXPECT_EQ(expected_outputs_after[output_count], output) << "output_count: " << output_count; | ||
4965 | 712 | ++output_count; | ||
4966 | 713 | }); | ||
4967 | 714 | |||
4968 | 715 | EXPECT_EQ(expected_outputs_after.size(), output_count); | ||
4970 | 716 | } | 760 | } |
4971 | 717 | 761 | ||
4972 | 718 | TEST_F(MesaDisplayConfigurationTest, new_monitor_matches_hardware_state) | 762 | TEST_F(MesaDisplayConfigurationTest, new_monitor_matches_hardware_state) |
4973 | @@ -727,12 +771,11 @@ | |||
4974 | 727 | geom::Size const connector_physical_sizes_mm_after{512, 642}; | 771 | geom::Size const connector_physical_sizes_mm_after{512, 642}; |
4975 | 728 | std::vector<uint32_t> possible_encoder_ids_empty; | 772 | std::vector<uint32_t> possible_encoder_ids_empty; |
4976 | 729 | uint32_t const possible_crtcs_mask_empty{0}; | 773 | uint32_t const possible_crtcs_mask_empty{0}; |
4977 | 730 | int const noutputs = 1; | ||
4978 | 731 | 774 | ||
4980 | 732 | mg::DisplayConfigurationOutput const expected_outputs_before[noutputs] = | 775 | std::vector<mg::DisplayConfigurationOutput> const expected_outputs_before = |
4981 | 733 | { | 776 | { |
4982 | 734 | { | 777 | { |
4984 | 735 | mg::DisplayConfigurationOutputId(connector_ids[0]), | 778 | mg::DisplayConfigurationOutputId{0}, |
4985 | 736 | mg::DisplayConfigurationCardId{0}, | 779 | mg::DisplayConfigurationCardId{0}, |
4986 | 737 | mg::DisplayConfigurationOutputType::composite, | 780 | mg::DisplayConfigurationOutputType::composite, |
4987 | 738 | {}, | 781 | {}, |
4988 | @@ -755,10 +798,10 @@ | |||
4989 | 755 | }, | 798 | }, |
4990 | 756 | }; | 799 | }; |
4991 | 757 | 800 | ||
4993 | 758 | mg::DisplayConfigurationOutput const expected_outputs_after[noutputs] = | 801 | std::vector<mg::DisplayConfigurationOutput> const expected_outputs_after = |
4994 | 759 | { | 802 | { |
4995 | 760 | { | 803 | { |
4997 | 761 | mg::DisplayConfigurationOutputId(connector_ids[0]), | 804 | mg::DisplayConfigurationOutputId{0}, |
4998 | 762 | mg::DisplayConfigurationCardId{0}, | 805 | mg::DisplayConfigurationCardId{0}, |
4999 | 763 | mg::DisplayConfigurationOutputType::composite, | 806 | mg::DisplayConfigurationOutputType::composite, |
5000 | 764 | {}, | 807 | {}, |
The diff has been truncated for viewing.