Mir

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

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
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.

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.

Subscribers

People subscribed via source and target branches