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