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
=== modified file 'src/common/CMakeLists.txt'
--- src/common/CMakeLists.txt 2017-02-15 14:45:41 +0000
+++ src/common/CMakeLists.txt 2017-03-22 06:55:00 +0000
@@ -20,7 +20,8 @@
20 $<TARGET_OBJECTS:mirtime>20 $<TARGET_OBJECTS:mirtime>
21 ${CMAKE_CURRENT_SOURCE_DIR}/output_type_names.cpp21 ${CMAKE_CURRENT_SOURCE_DIR}/output_type_names.cpp
22 ${CMAKE_CURRENT_SOURCE_DIR}/log.cpp22 ${CMAKE_CURRENT_SOURCE_DIR}/log.cpp
23 ${CMAKE_CURRENT_SOURCE_DIR}/libname.cpp ${PROJECT_SOURCE_DIR}/include/common/mir/libname.h23 ${CMAKE_CURRENT_SOURCE_DIR}/libname.cpp
24 ${PROJECT_SOURCE_DIR}/include/common/mir/libname.h
24 ${PROJECT_SOURCE_DIR}/include/common/mir/posix_rw_mutex.h25 ${PROJECT_SOURCE_DIR}/include/common/mir/posix_rw_mutex.h
25 posix_rw_mutex.cpp26 posix_rw_mutex.cpp
26 edid.cpp27 edid.cpp
2728
=== modified file 'src/platforms/mesa/server/display_helpers.cpp'
--- src/platforms/mesa/server/display_helpers.cpp 2017-03-16 07:37:11 +0000
+++ src/platforms/mesa/server/display_helpers.cpp 2017-03-22 06:55:00 +0000
@@ -20,9 +20,14 @@
20#include "drm_close_threadsafe.h"20#include "drm_close_threadsafe.h"
2121
22#include "kms-utils/drm_mode_resources.h"22#include "kms-utils/drm_mode_resources.h"
23#include "mir/graphics/gl_config.h"
24#include "mir/graphics/egl_error.h"
2325
24#include "mir/udev/wrapper.h"26#include "mir/udev/wrapper.h"
2527
28#define MIR_LOG_COMPONENT "mesa-kms"
29#include "mir/log.h"
30
26#include <boost/exception/errinfo_errno.hpp>31#include <boost/exception/errinfo_errno.hpp>
27#include <boost/throw_exception.hpp>32#include <boost/throw_exception.hpp>
2833
@@ -31,6 +36,7 @@
31#include <stdexcept>36#include <stdexcept>
32#include <xf86drm.h>37#include <xf86drm.h>
33#include <fcntl.h>38#include <fcntl.h>
39#include <vector>
3440
35namespace mg = mir::graphics;41namespace mg = mir::graphics;
36namespace mgm = mir::graphics::mesa;42namespace mgm = mir::graphics::mesa;
@@ -40,6 +46,69 @@
40 * DRMHelper *46 * DRMHelper *
41 *************/47 *************/
4248
49std::vector<std::shared_ptr<mgmh::DRMHelper>>
50mgmh::DRMHelper::open_all_devices(std::shared_ptr<mir::udev::Context> const& udev)
51{
52 int tmp_fd = -1;
53 int error = ENODEV; //Default error is "there are no DRM devices"
54
55 mir::udev::Enumerator devices(udev);
56 devices.match_subsystem("drm");
57 devices.match_sysname("card[0-9]");
58
59 devices.scan_devices();
60
61 std::vector<std::shared_ptr<DRMHelper>> opened_devices;
62
63 for(auto& device : devices)
64 {
65 // If directly opening the DRM device is good enough for X it's good enough for us!
66 tmp_fd = open(device.devnode(), O_RDWR | O_CLOEXEC);
67 if (tmp_fd < 0)
68 {
69 error = errno;
70 mir::log_warning(
71 "Failed to open DRM device node %s: %i (%s)",
72 device.devnode(),
73 error,
74 strerror(error));
75 continue;
76 }
77
78 // Check that the drm device is usable by setting the interface version we use (1.4)
79 drmSetVersion sv;
80 sv.drm_di_major = 1;
81 sv.drm_di_minor = 4;
82 sv.drm_dd_major = -1; /* Don't care */
83 sv.drm_dd_minor = -1; /* Don't care */
84
85 if ((error = -drmSetInterfaceVersion(tmp_fd, &sv)))
86 {
87 close(tmp_fd);
88 mir::log_warning(
89 "Failed to set DRM interface version on device %s: %i (%s)",
90 device.devnode(),
91 error,
92 strerror(error));
93 tmp_fd = -1;
94 continue;
95 }
96
97 // Can't use make_shared with the private constructor.
98 opened_devices.push_back(std::shared_ptr<DRMHelper>{new DRMHelper{tmp_fd}});
99 mir::log_info("Using DRM device %s", device.devnode());
100 tmp_fd = -1;
101 }
102
103 if (opened_devices.size() == 0)
104 {
105 BOOST_THROW_EXCEPTION((
106 std::system_error{error, std::system_category(), "Error opening DRM device"}));
107 }
108
109 return opened_devices;
110}
111
43void mgmh::DRMHelper::setup(std::shared_ptr<mir::udev::Context> const& udev)112void mgmh::DRMHelper::setup(std::shared_ptr<mir::udev::Context> const& udev)
44{113{
45 fd = open_drm_device(udev);114 fd = open_drm_device(udev);
@@ -160,6 +229,12 @@
160 }229 }
161}230}
162231
232mgmh::DRMHelper::DRMHelper(int fd)
233 : fd{fd},
234 node_to_use{DRMNodeToUse::card}
235{
236}
237
163int mgmh::DRMHelper::is_appropriate_device(std::shared_ptr<mir::udev::Context> const& udev, mir::udev::Device const& drm_device)238int mgmh::DRMHelper::is_appropriate_device(std::shared_ptr<mir::udev::Context> const& udev, mir::udev::Device const& drm_device)
164{239{
165 mir::udev::Enumerator children(udev);240 mir::udev::Enumerator children(udev);
@@ -285,11 +360,29 @@
285 std::runtime_error("Failed to create GBM device"));360 std::runtime_error("Failed to create GBM device"));
286}361}
287362
288mgm::GBMSurfaceUPtr mgmh::GBMHelper::create_scanout_surface(uint32_t width, uint32_t height)363mgm::GBMSurfaceUPtr mgmh::GBMHelper::create_scanout_surface(
364 uint32_t width,
365 uint32_t height,
366 bool sharable)
289{367{
368 auto format_flags = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT;
369
370 if (sharable)
371 {
372#ifdef MIR_NO_HYBRID_SUPPORT
373 BOOST_THROW_EXCEPTION((
374 std::runtime_error{
375 "Mir built without hybrid support, but configuration requries hybrid outputs.\n"
376 "This will not work unless Mir is rebuilt against Mesa >= 11.0"}
377 ));
378#else
379 format_flags |= GBM_BO_USE_LINEAR;
380#endif
381 }
382
290 auto surface_raw = gbm_surface_create(device, width, height,383 auto surface_raw = gbm_surface_create(device, width, height,
291 GBM_BO_FORMAT_XRGB8888,384 GBM_BO_FORMAT_XRGB8888,
292 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);385 format_flags);
293386
294 auto gbm_surface_deleter = [](gbm_surface *p) { if (p) gbm_surface_destroy(p); };387 auto gbm_surface_deleter = [](gbm_surface *p) { if (p) gbm_surface_destroy(p); };
295 GBMSurfaceUPtr surface{surface_raw, gbm_surface_deleter};388 GBMSurfaceUPtr surface{surface_raw, gbm_surface_deleter};
296389
=== modified file 'src/platforms/mesa/server/display_helpers.h'
--- src/platforms/mesa/server/display_helpers.h 2017-03-16 07:37:11 +0000
+++ src/platforms/mesa/server/display_helpers.h 2017-03-22 06:55:00 +0000
@@ -24,6 +24,7 @@
2424
25#include <cstddef>25#include <cstddef>
26#include <memory>26#include <memory>
27#include <vector>
2728
28#pragma GCC diagnostic push29#pragma GCC diagnostic push
29#pragma GCC diagnostic warning "-Wall"30#pragma GCC diagnostic warning "-Wall"
@@ -59,6 +60,9 @@
59 DRMHelper(const DRMHelper &) = delete;60 DRMHelper(const DRMHelper &) = delete;
60 DRMHelper& operator=(const DRMHelper&) = delete;61 DRMHelper& operator=(const DRMHelper&) = delete;
6162
63 static std::vector<std::shared_ptr<DRMHelper>> open_all_devices(
64 std::shared_ptr<mir::udev::Context> const& udev);
65
62 void setup(std::shared_ptr<mir::udev::Context> const& udev);66 void setup(std::shared_ptr<mir::udev::Context> const& udev);
63 mir::Fd authenticated_fd();67 mir::Fd authenticated_fd();
64 void auth_magic(drm_magic_t magic);68 void auth_magic(drm_magic_t magic);
@@ -70,6 +74,8 @@
70 DRMNodeToUse const node_to_use;74 DRMNodeToUse const node_to_use;
7175
72private:76private:
77 DRMHelper(int fd);
78
73 // TODO: This herustic is temporary; should be replaced with79 // TODO: This herustic is temporary; should be replaced with
74 // handling >1 DRM device.80 // handling >1 DRM device.
75 int is_appropriate_device(std::shared_ptr<mir::udev::Context> const& udev, mir::udev::Device const& dev);81 int is_appropriate_device(std::shared_ptr<mir::udev::Context> const& udev, mir::udev::Device const& dev);
@@ -90,7 +96,7 @@
9096
91 void setup(const DRMHelper& drm);97 void setup(const DRMHelper& drm);
92 void setup(int drm_fd);98 void setup(int drm_fd);
93 GBMSurfaceUPtr create_scanout_surface(uint32_t width, uint32_t height);99 GBMSurfaceUPtr create_scanout_surface(uint32_t width, uint32_t height, bool sharable);
94100
95 gbm_device* device;101 gbm_device* device;
96};102};
97103
=== modified file 'src/platforms/mesa/server/kms/CMakeLists.txt'
--- src/platforms/mesa/server/kms/CMakeLists.txt 2017-03-16 07:37:11 +0000
+++ src/platforms/mesa/server/kms/CMakeLists.txt 2017-03-22 06:55:00 +0000
@@ -13,6 +13,11 @@
13 ${PROJECT_SOURCE_DIR}/include/client13 ${PROJECT_SOURCE_DIR}/include/client
14)14)
1515
16if (GBM_VERSION VERSION_LESS 11)
17 message(WARNING "Hybrid support requires libgbm from Mesa 11.0 or greater. Hybrid setups will not work")
18 add_definitions(-DMIR_NO_HYBRID_SUPPORT)
19endif()
20
16# gbm.h and drm.h have trailing commas at the end of enum definitions21# gbm.h and drm.h have trailing commas at the end of enum definitions
17# This is valid C99, but g++ 4.4 flags it as an error with -pedantic22# This is valid C99, but g++ 4.4 flags it as an error with -pedantic
18string(REPLACE "-pedantic" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})23string(REPLACE "-pedantic" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
@@ -38,11 +43,13 @@
38 kms_display_configuration.h43 kms_display_configuration.h
39 real_kms_display_configuration.cpp44 real_kms_display_configuration.cpp
40 kms_output.h45 kms_output.h
46 real_kms_output.h
41 real_kms_output.cpp47 real_kms_output.cpp
42 kms_output_container.h48 kms_output_container.h
43 real_kms_output_container.cpp49 real_kms_output_container.cpp
44 egl_helper.h50 egl_helper.h
45 egl_helper.cpp51 egl_helper.cpp
52 mutex.h
46)53)
4754
48configure_file(${CMAKE_CURRENT_SOURCE_DIR}/symbols.map.in55configure_file(${CMAKE_CURRENT_SOURCE_DIR}/symbols.map.in
4956
=== modified file 'src/platforms/mesa/server/kms/cursor.cpp'
--- src/platforms/mesa/server/kms/cursor.cpp 2017-02-15 07:38:33 +0000
+++ src/platforms/mesa/server/kms/cursor.cpp 2017-03-22 06:55:00 +0000
@@ -88,34 +88,73 @@
88 drmGetCap(fd, DRM_CAP_CURSOR_WIDTH, &width);88 drmGetCap(fd, DRM_CAP_CURSOR_WIDTH, &width);
89 return int(width);89 return int(width);
90}90}
91}91
9292gbm_device* gbm_create_device_checked(int fd)
93mgm::Cursor::GBMBOWrapper::GBMBOWrapper(gbm_device* gbm) :93{
94 buffer(gbm_bo_create(94 auto device = gbm_create_device(fd);
95 gbm,95 if (!device)
96 get_drm_cursor_width(gbm_device_get_fd(gbm)),96 {
97 get_drm_cursor_height(gbm_device_get_fd(gbm)),97 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create gbm device"));
98 GBM_FORMAT_ARGB8888,98 }
99 GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE))99 return device;
100}
101}
102
103mgm::Cursor::GBMBOWrapper::GBMBOWrapper(int fd) :
104 device{gbm_create_device_checked(fd)},
105 buffer{
106 gbm_bo_create(
107 device,
108 get_drm_cursor_width(fd),
109 get_drm_cursor_height(fd),
110 GBM_FORMAT_ARGB8888,
111 GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)}
100{112{
101 if (!buffer) BOOST_THROW_EXCEPTION(std::runtime_error("failed to create gbm buffer"));113 if (!buffer) BOOST_THROW_EXCEPTION(std::runtime_error("failed to create gbm buffer"));
102}114}
103115
104inline mgm::Cursor::GBMBOWrapper::operator gbm_bo*() { return buffer; }116inline mgm::Cursor::GBMBOWrapper::operator gbm_bo*()
105inline mgm::Cursor::GBMBOWrapper::~GBMBOWrapper() { gbm_bo_destroy(buffer); }117{
118 return buffer;
119}
120
121inline mgm::Cursor::GBMBOWrapper::~GBMBOWrapper()
122{
123 if (device)
124 gbm_device_destroy(device);
125 if (buffer)
126 gbm_bo_destroy(buffer);
127}
128
129mgm::Cursor::GBMBOWrapper::GBMBOWrapper(GBMBOWrapper&& from)
130 : device{from.device},
131 buffer{from.buffer}
132{
133 const_cast<gbm_bo*&>(from.buffer) = nullptr;
134 const_cast<gbm_device*&>(from.device) = nullptr;
135}
106136
107mgm::Cursor::Cursor(137mgm::Cursor::Cursor(
108 gbm_device* gbm,
109 KMSOutputContainer& output_container,138 KMSOutputContainer& output_container,
110 std::shared_ptr<CurrentConfiguration> const& current_configuration) :139 std::shared_ptr<CurrentConfiguration> const& current_configuration) :
111 output_container(output_container),140 output_container(output_container),
112 current_position(),141 current_position(),
113 last_set_failed(false),142 last_set_failed(false),
114 buffer(gbm),143 min_buffer_width{std::numeric_limits<uint32_t>::max()},
115 buffer_width(gbm_bo_get_width(buffer)),144 min_buffer_height{std::numeric_limits<uint32_t>::max()},
116 buffer_height(gbm_bo_get_height(buffer)),
117 current_configuration(current_configuration)145 current_configuration(current_configuration)
118{146{
147 // Generate the buffers for the initial configuration.
148 current_configuration->with_current_configuration_do(
149 [this](KMSDisplayConfiguration const& kms_conf)
150 {
151 kms_conf.for_each_output(
152 [this, &kms_conf](auto const& output)
153 {
154 buffer_for_output(*kms_conf.get_output_for(output.id));
155 });
156 });
157
119 hide();158 hide();
120 if (last_set_failed)159 if (last_set_failed)
121 throw std::runtime_error("Initial KMS cursor set failed");160 throw std::runtime_error("Initial KMS cursor set failed");
@@ -126,7 +165,11 @@
126 hide();165 hide();
127}166}
128167
129void mgm::Cursor::write_buffer_data_locked(std::lock_guard<std::mutex> const&, void const* data, size_t count)168void mgm::Cursor::write_buffer_data_locked(
169 std::lock_guard<std::mutex> const&,
170 gbm_bo* buffer,
171 void const* data,
172 size_t count)
130{173{
131 if (auto result = gbm_bo_write(buffer, data, count))174 if (auto result = gbm_bo_write(buffer, data, count))
132 {175 {
@@ -136,20 +179,23 @@
136 }179 }
137}180}
138181
139void mgm::Cursor::pad_and_write_image_data_locked(std::lock_guard<std::mutex> const& lg, CursorImage const& image)182void mgm::Cursor::pad_and_write_image_data_locked(
183 std::lock_guard<std::mutex> const& lg,
184 gbm_bo* buffer,
185 CursorImage const& image)
140{186{
141 auto image_argb = static_cast<uint8_t const*>(image.as_argb_8888());187 auto image_argb = static_cast<uint8_t const*>(image.as_argb_8888());
142 auto image_width = image.size().width.as_uint32_t();188 auto image_width = image.size().width.as_uint32_t();
143 auto image_height = image.size().height.as_uint32_t();189 auto image_height = image.size().height.as_uint32_t();
144 auto image_stride = image_width * 4;190 auto image_stride = image_width * 4;
145191
146 if (image_width > buffer_width || image_height > buffer_height)192 if (image_width > min_buffer_width || image_height > min_buffer_height)
147 {193 {
148 BOOST_THROW_EXCEPTION(std::logic_error("Image is too big for GBM cursor buffer"));194 BOOST_THROW_EXCEPTION(std::logic_error("Image is too big for GBM cursor buffer"));
149 }195 }
150 196
151 size_t buffer_stride = gbm_bo_get_stride(buffer); // in bytes197 size_t buffer_stride = gbm_bo_get_stride(buffer); // in bytes
152 size_t padded_size = buffer_stride * buffer_height;198 size_t padded_size = buffer_stride * gbm_bo_get_height(buffer);
153 auto padded = std::unique_ptr<uint8_t[]>(new uint8_t[padded_size]);199 auto padded = std::unique_ptr<uint8_t[]>(new uint8_t[padded_size]);
154 size_t rhs_padding = buffer_stride - image_stride;200 size_t rhs_padding = buffer_stride - image_stride;
155201
@@ -164,9 +210,9 @@
164 src += image_stride;210 src += image_stride;
165 }211 }
166212
167 memset(dest, 0, buffer_stride * (buffer_height - image_height));213 memset(dest, 0, buffer_stride * (gbm_bo_get_height(buffer) - image_height));
168214
169 write_buffer_data_locked(lg, &padded[0], padded_size);215 write_buffer_data_locked(lg, buffer, &padded[0], padded_size);
170}216}
171217
172void mgm::Cursor::show()218void mgm::Cursor::show()
@@ -186,14 +232,20 @@
186232
187 auto const& size = cursor_image.size();233 auto const& size = cursor_image.size();
188234
189 if (size != geometry::Size{buffer_width, buffer_height})235 {
190 {236 auto locked_buffers = buffers.lock();
191 pad_and_write_image_data_locked(lg, cursor_image);237 for (auto& pair : *locked_buffers)
192 }238 {
193 else239 auto& buffer = pair.second;
194 {240 if (size != geometry::Size{gbm_bo_get_width(buffer), gbm_bo_get_height(buffer)})
195 auto const count = size.width.as_uint32_t() * size.height.as_uint32_t() * sizeof(uint32_t);241 {
196 write_buffer_data_locked(lg, cursor_image.as_argb_8888(), count);242 pad_and_write_image_data_locked(lg, buffer, cursor_image);
243 } else
244 {
245 auto const count = size.width.as_uint32_t() * size.height.as_uint32_t() * sizeof(uint32_t);
246 write_buffer_data_locked(lg, buffer, cursor_image.as_argb_8888(), count);
247 }
248 }
197 }249 }
198 hotspot = cursor_image.hotspot();250 hotspot = cursor_image.hotspot();
199 251
@@ -217,9 +269,9 @@
217void mir::graphics::mesa::Cursor::clear(std::lock_guard<std::mutex> const&)269void mir::graphics::mesa::Cursor::clear(std::lock_guard<std::mutex> const&)
218{270{
219 last_set_failed = false;271 last_set_failed = false;
220 output_container.for_each_output([&](KMSOutput& output)272 output_container.for_each_output([&](std::shared_ptr<KMSOutput> const& output)
221 {273 {
222 if (!output.clear_cursor())274 if (!output->clear_cursor())
223 last_set_failed = true;275 last_set_failed = true;
224 });276 });
225}277}
@@ -240,14 +292,13 @@
240 std::function<void(KMSOutput&, geom::Rectangle const&, MirOrientation orientation)> const& f)292 std::function<void(KMSOutput&, geom::Rectangle const&, MirOrientation orientation)> const& f)
241{293{
242 current_configuration->with_current_configuration_do(294 current_configuration->with_current_configuration_do(
243 [this,&f](KMSDisplayConfiguration const& kms_conf)295 [&f](KMSDisplayConfiguration const& kms_conf)
244 {296 {
245 kms_conf.for_each_output([&](DisplayConfigurationOutput const& conf_output)297 kms_conf.for_each_output([&](DisplayConfigurationOutput const& conf_output)
246 {298 {
247 if (conf_output.used)299 if (conf_output.used)
248 {300 {
249 uint32_t const connector_id = kms_conf.get_kms_connector_id(conf_output.id);301 auto output = kms_conf.get_output_for(conf_output.id);
250 auto output = output_container.get_kms_output_for(connector_id);
251302
252 f(*output, conf_output.extents(), conf_output.orientation);303 f(*output, conf_output.extents(), conf_output.orientation);
253 }304 }
@@ -289,7 +340,7 @@
289 output.move_cursor(geom::Point{} + dp - hotspot);340 output.move_cursor(geom::Point{} + dp - hotspot);
290 if (force_state || !output.has_cursor()) // TODO - or if orientation had changed - then set buffer..341 if (force_state || !output.has_cursor()) // TODO - or if orientation had changed - then set buffer..
291 {342 {
292 if (!output.set_cursor(buffer) || !output.has_cursor())343 if (!output.set_cursor(buffer_for_output(output)) || !output.has_cursor())
293 set_on_all_outputs = false;344 set_on_all_outputs = false;
294 }345 }
295 }346 }
@@ -304,3 +355,35 @@
304355
305 last_set_failed = !set_on_all_outputs;356 last_set_failed = !set_on_all_outputs;
306}357}
358
359gbm_bo* mgm::Cursor::buffer_for_output(KMSOutput const& output)
360{
361 auto locked_buffers = buffers.lock();
362
363 auto buffer_it = std::find_if(
364 locked_buffers->begin(),
365 locked_buffers->end(),
366 [&output](auto const& candidate)
367 {
368 return candidate.first == output.drm_fd();
369 });
370
371 if (buffer_it != locked_buffers->end())
372 {
373 return buffer_it->second;
374 }
375
376 locked_buffers->push_back(std::make_pair(output.drm_fd(), GBMBOWrapper(output.drm_fd())));
377
378 gbm_bo* bo = locked_buffers->back().second;
379 if (gbm_bo_get_width(bo) < min_buffer_width)
380 {
381 min_buffer_width = gbm_bo_get_width(bo);
382 }
383 if (gbm_bo_get_height(bo) < min_buffer_height)
384 {
385 min_buffer_height = gbm_bo_get_height(bo);
386 }
387
388 return bo;
389}
307390
=== modified file 'src/platforms/mesa/server/kms/cursor.h'
--- src/platforms/mesa/server/kms/cursor.h 2017-02-15 07:38:33 +0000
+++ src/platforms/mesa/server/kms/cursor.h 2017-03-22 06:55:00 +0000
@@ -25,11 +25,13 @@
25#include "mir/geometry/displacement.h"25#include "mir/geometry/displacement.h"
2626
27#include "mir_toolkit/common.h"27#include "mir_toolkit/common.h"
28#include "mutex.h"
2829
29#include <gbm.h>30#include <gbm.h>
3031
31#include <memory>32#include <memory>
32#include <mutex>33#include <mutex>
34#include <vector>
3335
34namespace mir36namespace mir
35{37{
@@ -66,7 +68,6 @@
66{68{
67public:69public:
68 Cursor(70 Cursor(
69 gbm_device* device,
70 KMSOutputContainer& output_container,71 KMSOutputContainer& output_container,
71 std::shared_ptr<CurrentConfiguration> const& current_configuration);72 std::shared_ptr<CurrentConfiguration> const& current_configuration);
7273
@@ -86,9 +87,18 @@
86 void for_each_used_output(std::function<void(KMSOutput&, geometry::Rectangle const&, MirOrientation orientation)> const& f);87 void for_each_used_output(std::function<void(KMSOutput&, geometry::Rectangle const&, MirOrientation orientation)> const& f);
87 void place_cursor_at(geometry::Point position, ForceCursorState force_state);88 void place_cursor_at(geometry::Point position, ForceCursorState force_state);
88 void place_cursor_at_locked(std::lock_guard<std::mutex> const&, geometry::Point position, ForceCursorState force_state);89 void place_cursor_at_locked(std::lock_guard<std::mutex> const&, geometry::Point position, ForceCursorState force_state);
89 void write_buffer_data_locked(std::lock_guard<std::mutex> const&, void const* data, size_t count);90 void write_buffer_data_locked(
90 void pad_and_write_image_data_locked(std::lock_guard<std::mutex> const&, CursorImage const& image);91 std::lock_guard<std::mutex> const&,
92 gbm_bo* buffer,
93 void const* data,
94 size_t count);
95 void pad_and_write_image_data_locked(
96 std::lock_guard<std::mutex> const&,
97 gbm_bo* buffer,
98 CursorImage const& image);
91 void clear(std::lock_guard<std::mutex> const&);99 void clear(std::lock_guard<std::mutex> const&);
100
101 gbm_bo* buffer_for_output(KMSOutput const& output);
92 102
93 std::mutex guard;103 std::mutex guard;
94104
@@ -101,17 +111,21 @@
101111
102 struct GBMBOWrapper112 struct GBMBOWrapper
103 {113 {
104 GBMBOWrapper(gbm_device* gbm);114 GBMBOWrapper(int fd);
105 operator gbm_bo*();115 operator gbm_bo*();
106 ~GBMBOWrapper();116 ~GBMBOWrapper();
117
118 GBMBOWrapper(GBMBOWrapper&& from);
107 private:119 private:
108 gbm_bo* buffer;120 gbm_device* const device;
121 gbm_bo* const buffer;
109 GBMBOWrapper(GBMBOWrapper const&) = delete;122 GBMBOWrapper(GBMBOWrapper const&) = delete;
110 GBMBOWrapper& operator=(GBMBOWrapper const&) = delete;123 GBMBOWrapper& operator=(GBMBOWrapper const&) = delete;
111 } buffer;124 };
125 Mutex<std::vector<std::pair<int, GBMBOWrapper>>> buffers;
112126
113 uint32_t buffer_width;127 uint32_t min_buffer_width;
114 uint32_t buffer_height;128 uint32_t min_buffer_height;
115129
116 std::shared_ptr<CurrentConfiguration> const current_configuration;130 std::shared_ptr<CurrentConfiguration> const current_configuration;
117};131};
118132
=== modified file 'src/platforms/mesa/server/kms/display.cpp'
--- src/platforms/mesa/server/kms/display.cpp 2017-03-13 08:12:52 +0000
+++ src/platforms/mesa/server/kms/display.cpp 2017-03-22 06:55:00 +0000
@@ -39,6 +39,7 @@
3939
40#include <stdexcept>40#include <stdexcept>
41#include <algorithm>41#include <algorithm>
42#include <unordered_map>
4243
43namespace mgm = mir::graphics::mesa;44namespace mgm = mir::graphics::mesa;
44namespace mg = mir::graphics;45namespace mg = mir::graphics;
@@ -78,24 +79,48 @@
78 mgm::helpers::EGLHelper egl;79 mgm::helpers::EGLHelper egl;
79};80};
8081
81}82std::vector<int> drm_fds_from_drm_helpers(
8283 std::vector<std::shared_ptr<mgm::helpers::DRMHelper>> const& helpers)
83mgm::Display::Display(std::shared_ptr<helpers::DRMHelper> const& drm,84{
85 std::vector<int> fds;
86 for (auto const& helper: helpers)
87 {
88 fds.push_back(helper->fd);
89 }
90 return fds;
91}
92
93}
94
95mgm::Display::Display(std::vector<std::shared_ptr<helpers::DRMHelper>> const& drm,
84 std::shared_ptr<helpers::GBMHelper> const& gbm,96 std::shared_ptr<helpers::GBMHelper> const& gbm,
85 std::shared_ptr<VirtualTerminal> const& vt,97 std::shared_ptr<VirtualTerminal> const& vt,
86 mgm::BypassOption bypass_option,98 mgm::BypassOption bypass_option,
87 std::shared_ptr<DisplayConfigurationPolicy> const& initial_conf_policy,99 std::shared_ptr<DisplayConfigurationPolicy> const& initial_conf_policy,
88 std::shared_ptr<GLConfig> const& gl_config,100 std::shared_ptr<GLConfig> const& gl_config,
89 std::shared_ptr<DisplayReport> const& listener)101 std::shared_ptr<DisplayReport> const& listener)
90 : drm(drm),102 : drm{drm},
91 gbm(gbm),103 gbm(gbm),
92 vt(vt),104 vt(vt),
93 listener(listener),105 listener(listener),
94 monitor(mir::udev::Context()),106 monitor(mir::udev::Context()),
95 shared_egl{*gl_config},107 shared_egl{*gl_config},
96 output_container{drm->fd,108 output_container{
97 std::make_shared<KMSPageFlipper>(drm->fd, listener)},109 std::make_shared<RealKMSOutputContainer>(
98 current_display_configuration{drm->fd},110 drm_fds_from_drm_helpers(drm),
111 [
112 listener,
113 flippers = std::unordered_map<int, std::shared_ptr<KMSPageFlipper>>{}
114 ](int drm_fd) mutable
115 {
116 auto& flipper = flippers[drm_fd];
117 if (!flipper)
118 {
119 flipper = std::make_shared<KMSPageFlipper>(drm_fd, listener);
120 }
121 return flipper;
122 })},
123 current_display_configuration{output_container},
99 dirty_configuration{false},124 dirty_configuration{false},
100 bypass_option(bypass_option),125 bypass_option(bypass_option),
101 gl_config{gl_config}126 gl_config{gl_config}
@@ -193,7 +218,8 @@
193 try218 try
194 {219 {
195 if (auto c = cursor.lock()) c->suspend();220 if (auto c = cursor.lock()) c->suspend();
196 drm->drop_master();221 for (auto& helper : drm)
222 helper->drop_master();
197 }223 }
198 catch(std::runtime_error const& e)224 catch(std::runtime_error const& e)
199 {225 {
@@ -206,7 +232,8 @@
206{232{
207 try233 try
208 {234 {
209 drm->set_master();235 for (auto& helper : drm)
236 helper->set_master();
210 }237 }
211 catch(std::runtime_error const& e)238 catch(std::runtime_error const& e)
212 {239 {
@@ -260,9 +287,9 @@
260287
261 try288 try
262 {289 {
263 locked_cursor = std::make_shared<Cursor>(gbm->device,290 locked_cursor = std::make_shared<Cursor>(
264 output_container,291 *output_container,
265 std::make_shared<KMSCurrentConfiguration>(*this));292 std::make_shared<KMSCurrentConfiguration>(*this));
266 }293 }
267 catch (std::runtime_error const&)294 catch (std::runtime_error const&)
268 {295 {
@@ -287,8 +314,7 @@
287 if (conf_output.connected &&314 if (conf_output.connected &&
288 (!conf_output.used || (conf_output.power_mode != mir_power_mode_on)))315 (!conf_output.used || (conf_output.power_mode != mir_power_mode_on)))
289 {316 {
290 uint32_t const connector_id = current_display_configuration.get_kms_connector_id(conf_output.id);317 auto kms_output = current_display_configuration.get_output_for(conf_output.id);
291 auto kms_output = output_container.get_kms_output_for(connector_id);
292318
293 kms_output->clear_crtc();319 kms_output->clear_crtc();
294 kms_output->set_power_mode(conf_output.power_mode);320 kms_output->set_power_mode(conf_output.power_mode);
@@ -327,10 +353,40 @@
327353
328mg::Frame mgm::Display::last_frame_on(unsigned output_id) const354mg::Frame mgm::Display::last_frame_on(unsigned output_id) const
329{355{
330 auto output = output_container.get_kms_output_for(output_id);356 auto output = current_display_configuration.get_output_for(
357 DisplayConfigurationOutputId{static_cast<int>(output_id)});
331 return output->last_frame();358 return output->last_frame();
332}359}
333360
361namespace
362{
363/*
364 * Add output to the grouping, maintaining the invariant that each vector of outputs
365 * is a single GPU memory domain.
366 */
367void add_to_drm_device_group(
368 std::vector<std::vector<std::shared_ptr<mgm::KMSOutput>>>& grouping,
369 std::shared_ptr<mgm::KMSOutput>&& output)
370{
371 for (auto &group : grouping)
372 {
373 /*
374 * We could be smarter about this, but being on the same DRM device is guaranteed
375 * to be in the same GPU memory domain :).
376 */
377 if (group.front()->drm_fd() == output->drm_fd())
378 {
379 group.push_back(std::move(output));
380 break;
381 }
382 }
383 if (output)
384 {
385 grouping.push_back(std::vector<std::shared_ptr<mgm::KMSOutput>>{std::move(output)});
386 }
387}
388}
389
334void mgm::Display::configure_locked(390void mgm::Display::configure_locked(
335 mgm::RealKMSDisplayConfiguration const& kms_conf,391 mgm::RealKMSDisplayConfiguration const& kms_conf,
336 std::lock_guard<std::mutex> const&)392 std::lock_guard<std::mutex> const&)
@@ -359,8 +415,7 @@
359 kms_conf.for_each_output(415 kms_conf.for_each_output(
360 [&](DisplayConfigurationOutput const& conf_output)416 [&](DisplayConfigurationOutput const& conf_output)
361 {417 {
362 uint32_t const connector_id = current_display_configuration.get_kms_connector_id(conf_output.id);418 auto kms_output = current_display_configuration.get_output_for(conf_output.id);
363 auto kms_output = output_container.get_kms_output_for(connector_id);
364 kms_output->clear_cursor();419 kms_output->clear_cursor();
365 kms_output->reset();420 kms_output->reset();
366 });421 });
@@ -374,14 +429,14 @@
374 [&](OverlappingOutputGroup const& group)429 [&](OverlappingOutputGroup const& group)
375 {430 {
376 auto bounding_rect = group.bounding_rectangle();431 auto bounding_rect = group.bounding_rectangle();
377 std::vector<std::shared_ptr<KMSOutput>> kms_outputs;432 // Each vector<KMSOutput> is a single GPU memory domain
433 std::vector<std::vector<std::shared_ptr<KMSOutput>>> kms_output_groups;
378 MirOrientation orientation = mir_orientation_normal;434 MirOrientation orientation = mir_orientation_normal;
379435
380 group.for_each_output(436 group.for_each_output(
381 [&](DisplayConfigurationOutput const& conf_output)437 [&](DisplayConfigurationOutput const& conf_output)
382 {438 {
383 uint32_t const connector_id = kms_conf.get_kms_connector_id(conf_output.id);439 auto kms_output = current_display_configuration.get_output_for(conf_output.id);
384 auto kms_output = output_container.get_kms_output_for(connector_id);
385440
386 auto const mode_index = kms_conf.get_kms_mode_index(conf_output.id,441 auto const mode_index = kms_conf.get_kms_mode_index(conf_output.id,
387 conf_output.current_mode_index);442 conf_output.current_mode_index);
@@ -390,7 +445,7 @@
390 {445 {
391 kms_output->set_power_mode(conf_output.power_mode);446 kms_output->set_power_mode(conf_output.power_mode);
392 kms_output->set_gamma(conf_output.gamma);447 kms_output->set_gamma(conf_output.gamma);
393 kms_outputs.push_back(kms_output);448 add_to_drm_device_group(kms_output_groups, std::move(kms_output));
394 }449 }
395450
396 /*451 /*
@@ -413,28 +468,38 @@
413 std::swap(width, height);468 std::swap(width, height);
414 }469 }
415470
416 auto surface = gbm->create_scanout_surface(width, height);471 for (auto const& group : kms_output_groups)
417 auto const raw_surface = surface.get();472 {
418473 /*
419 std::unique_ptr<DisplayBuffer> db{474 * In a hybrid setup a scanout surface needs to be allocated differently if it
420 new DisplayBuffer{bypass_option,475 * needs to be able to be shared across GPUs. This likely reduces performance.
421 listener,476 *
422 kms_outputs,477 * As a first cut, assume every scanout buffer in a hybrid setup might need
423 GBMOutputSurface{478 * to be shared.
424 drm->fd,479 */
425 std::move(surface),480 auto surface = gbm->create_scanout_surface(width, height, drm.size() != 1);
426 width, height,481 auto const raw_surface = surface.get();
427 helpers::EGLHelper{482
428 *gl_config,483 auto db = std::make_unique<DisplayBuffer>(
429 *gbm,484 bypass_option,
430 raw_surface,485 listener,
431 shared_egl.context()486 group,
432 }487 GBMOutputSurface{
433 },488 group.front()->drm_fd(),
434 bounding_rect,489 std::move(surface),
435 orientation}};490 width, height,
436491 helpers::EGLHelper{
437 display_buffers_new.push_back(std::move(db));492 *gl_config,
493 *gbm,
494 raw_surface,
495 shared_egl.context()
496 }
497 },
498 bounding_rect,
499 orientation);
500
501 display_buffers_new.push_back(std::move(db));
502 }
438 }503 }
439 });504 });
440505
441506
=== modified file 'src/platforms/mesa/server/kms/display.h'
--- src/platforms/mesa/server/kms/display.h 2017-03-16 07:37:11 +0000
+++ src/platforms/mesa/server/kms/display.h 2017-03-22 06:55:00 +0000
@@ -65,7 +65,7 @@
65 public renderer::gl::ContextSource65 public renderer::gl::ContextSource
66{66{
67public:67public:
68 Display(std::shared_ptr<helpers::DRMHelper> const& drm,68 Display(std::vector<std::shared_ptr<helpers::DRMHelper>> const& drm,
69 std::shared_ptr<helpers::GBMHelper> const& gbm,69 std::shared_ptr<helpers::GBMHelper> const& gbm,
70 std::shared_ptr<VirtualTerminal> const& vt,70 std::shared_ptr<VirtualTerminal> const& vt,
71 BypassOption bypass_option,71 BypassOption bypass_option,
@@ -106,14 +106,14 @@
106 void clear_connected_unused_outputs();106 void clear_connected_unused_outputs();
107107
108 mutable std::mutex configuration_mutex;108 mutable std::mutex configuration_mutex;
109 std::shared_ptr<helpers::DRMHelper> const drm;109 std::vector<std::shared_ptr<helpers::DRMHelper>> const drm;
110 std::shared_ptr<helpers::GBMHelper> const gbm;110 std::shared_ptr<helpers::GBMHelper> const gbm;
111 std::shared_ptr<VirtualTerminal> const vt;111 std::shared_ptr<VirtualTerminal> const vt;
112 std::shared_ptr<DisplayReport> const listener;112 std::shared_ptr<DisplayReport> const listener;
113 mir::udev::Monitor monitor;113 mir::udev::Monitor monitor;
114 helpers::EGLHelper shared_egl;114 helpers::EGLHelper shared_egl;
115 std::vector<std::unique_ptr<DisplayBuffer>> display_buffers;115 std::vector<std::unique_ptr<DisplayBuffer>> display_buffers;
116 mutable RealKMSOutputContainer output_container;116 std::shared_ptr<KMSOutputContainer> const output_container;
117 mutable RealKMSDisplayConfiguration current_display_configuration;117 mutable RealKMSDisplayConfiguration current_display_configuration;
118 mutable std::atomic<bool> dirty_configuration;118 mutable std::atomic<bool> dirty_configuration;
119119
120120
=== modified file 'src/platforms/mesa/server/kms/display_buffer.cpp'
--- src/platforms/mesa/server/kms/display_buffer.cpp 2017-03-13 08:12:52 +0000
+++ src/platforms/mesa/server/kms/display_buffer.cpp 2017-03-22 06:55:00 +0000
@@ -28,8 +28,13 @@
28#include "mir/graphics/egl_error.h"28#include "mir/graphics/egl_error.h"
2929
30#include <boost/throw_exception.hpp>30#include <boost/throw_exception.hpp>
31#include <EGL/egl.h>
32#include <EGL/eglext.h>
31#include MIR_SERVER_GL_H33#include MIR_SERVER_GL_H
34#include <GLES2/gl2ext.h>
35#include <drm/drm_fourcc.h>
3236
37#include <sstream>
33#include <stdexcept>38#include <stdexcept>
34#include <chrono>39#include <chrono>
35#include <thread>40#include <thread>
@@ -104,18 +109,378 @@
104109
105namespace110namespace
106{111{
107112void require_extensions(
108void ensure_egl_image_extensions()113 std::initializer_list<char const*> extensions,
109{114 std::function<std::string()> const& extension_getter)
110 std::string ext_string;115{
111 const char* exts = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));116 std::stringstream missing_extensions;
112 if (exts)117
113 ext_string = exts;118 std::string const ext_string = extension_getter();
114119
115 if (ext_string.find("GL_OES_EGL_image") == std::string::npos)120 for (auto extension : extensions)
116 BOOST_THROW_EXCEPTION(std::runtime_error("GLES2 implementation doesn't support GL_OES_EGL_image extension"));121 {
117}122 if (ext_string.find(extension) == std::string::npos)
118123 {
124 missing_extensions << "Missing " << extension << std::endl;
125 }
126 }
127
128 if (!missing_extensions.str().empty())
129 {
130 BOOST_THROW_EXCEPTION(std::runtime_error(
131 std::string("Missing required extensions:\n") + missing_extensions.str()));
132 }
133}
134
135void require_egl_extensions(EGLDisplay dpy, std::initializer_list<char const*> extensions)
136{
137 require_extensions(
138 extensions,
139 [dpy]() -> std::string
140 {
141 char const* maybe_exts = eglQueryString(dpy, EGL_EXTENSIONS);
142 if (maybe_exts)
143 return maybe_exts;
144 return {};
145 });
146}
147
148void require_gl_extensions(std::initializer_list<char const*> extensions)
149{
150 require_extensions(
151 extensions,
152 []() -> std::string
153 {
154 char const *maybe_exts =
155 reinterpret_cast<char const*>(glGetString(GL_EXTENSIONS));
156 if (maybe_exts)
157 return maybe_exts;
158 return {};
159 });
160}
161
162bool needs_bounce_buffer(mgm::KMSOutput const& destination, gbm_bo* source)
163{
164 return destination.buffer_requires_migration(source);
165}
166
167const GLchar* const vshader =
168 {
169 "attribute vec4 position;\n"
170 "attribute vec2 texcoord;\n"
171 "varying vec2 v_texcoord;\n"
172 "void main() {\n"
173 " gl_Position = position;\n"
174 " v_texcoord = texcoord;\n"
175 "}\n"
176 };
177
178const GLchar* const fshader =
179 {
180 "#ifdef GL_ES\n"
181 "precision mediump float;\n"
182 "#endif\n"
183 "uniform sampler2D tex;"
184 "varying vec2 v_texcoord;\n"
185 "void main() {\n"
186 " gl_FragColor = texture2D(tex, v_texcoord);\n"
187 "}\n"
188 };
189
190class VBO
191{
192public:
193 VBO(void const* data, size_t size)
194 {
195 glGenBuffers(1, &buf_id);
196 glBindBuffer(GL_ARRAY_BUFFER, buf_id);
197 glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
198 glBindBuffer(GL_ARRAY_BUFFER, 0);
199 }
200
201 ~VBO()
202 {
203 glDeleteBuffers(1, &buf_id);
204 }
205
206 void bind()
207 {
208 glBindBuffer(GL_ARRAY_BUFFER, buf_id);
209 }
210
211private:
212 GLuint buf_id;
213};
214
215class EGLBufferCopier
216{
217public:
218 EGLBufferCopier(
219 int drm_fd,
220 uint32_t width,
221 uint32_t height,
222 uint32_t format)
223 : eglCreateImageKHR{
224 reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"))},
225 eglDestroyImageKHR{
226 reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR"))},
227 glEGLImageTargetTexture2DOES{
228 reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"))},
229 device{gbm_create_device(drm_fd), &gbm_device_destroy},
230 width{width},
231 height{height},
232 surface{create_scanout_surface(*device, width, height, format)}
233 {
234 require_gl_extensions({
235 "GL_OES_EGL_image"
236 });
237
238 EGLint const config_attr[] = {
239 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
240 EGL_RED_SIZE, 5,
241 EGL_GREEN_SIZE, 5,
242 EGL_BLUE_SIZE, 5,
243 EGL_ALPHA_SIZE, 0,
244 EGL_DEPTH_SIZE, 0,
245 EGL_STENCIL_SIZE, 0,
246 EGL_RENDERABLE_TYPE, MIR_SERVER_EGL_OPENGL_BIT,
247 EGL_NONE
248 };
249
250 static const EGLint required_egl_version_major = 1;
251 static const EGLint required_egl_version_minor = 4;
252
253 EGLint num_egl_configs;
254 EGLConfig egl_config;
255
256 display = eglGetDisplay(static_cast<EGLNativeDisplayType>(device.get()));
257 if (display == EGL_NO_DISPLAY)
258 BOOST_THROW_EXCEPTION(mg::egl_error("Failed to get EGL display"));
259
260 EGLint major, minor;
261
262 if (eglInitialize(display, &major, &minor) == EGL_FALSE)
263 BOOST_THROW_EXCEPTION(mg::egl_error("Failed to initialize EGL display"));
264
265 if ((major < required_egl_version_major) ||
266 (major == required_egl_version_major && minor < required_egl_version_minor))
267 {
268 BOOST_THROW_EXCEPTION(std::runtime_error("Incompatible EGL version"));
269 }
270
271 require_egl_extensions(
272 display,
273 {
274 "EGL_KHR_image_base",
275 "EGL_EXT_image_dma_buf_import"
276 });
277
278 if (eglChooseConfig(display, config_attr, &egl_config, 1, &num_egl_configs) == EGL_FALSE ||
279 num_egl_configs != 1)
280 {
281 BOOST_THROW_EXCEPTION(mg::egl_error("Failed to choose ARGB EGL config"));
282 }
283
284 eglBindAPI(MIR_SERVER_EGL_OPENGL_API);
285 static const EGLint context_attr[] = {
286#if MIR_SERVER_EGL_OPENGL_BIT == EGL_OPENGL_ES2_BIT
287 EGL_CONTEXT_CLIENT_VERSION, 2,
288#endif
289 EGL_NONE
290 };
291
292 context = eglCreateContext(display, egl_config, EGL_NO_CONTEXT, context_attr);
293 if (context == EGL_NO_CONTEXT)
294 BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context"));
295
296 egl_surface = eglCreateWindowSurface(display, egl_config, surface.get(), nullptr);
297 eglMakeCurrent(display, egl_surface, egl_surface, context);
298
299 auto vertex = glCreateShader(GL_VERTEX_SHADER);
300 glShaderSource(vertex, 1, &vshader, nullptr);
301 glCompileShader(vertex);
302
303 int compiled;
304 glGetShaderiv (vertex, GL_COMPILE_STATUS, &compiled);
305
306 if (!compiled) {
307 GLchar log[1024];
308
309 glGetShaderInfoLog (vertex, sizeof log - 1, NULL, log);
310 log[sizeof log - 1] = '\0';
311 glDeleteShader (vertex);
312
313 BOOST_THROW_EXCEPTION(
314 std::runtime_error(std::string{"Failed to compile vertex shader:\n"} + log));
315 }
316
317
318 auto fragment = glCreateShader(GL_FRAGMENT_SHADER);
319 glShaderSource(fragment, 1, &fshader, nullptr);
320 glCompileShader(fragment);
321
322 glGetShaderiv (fragment, GL_COMPILE_STATUS, &compiled);
323 if (!compiled) {
324 GLchar log[1024];
325
326 glGetShaderInfoLog (fragment, sizeof log - 1, NULL, log);
327 log[sizeof log - 1] = '\0';
328 glDeleteShader (fragment);
329
330 BOOST_THROW_EXCEPTION(
331 std::runtime_error(std::string{"Failed to compile fragment shader:\n"} + log));
332 }
333
334 prog = glCreateProgram();
335 glAttachShader(prog, vertex);
336 glAttachShader(prog, fragment);
337 glLinkProgram(prog);
338 glGetProgramiv (prog, GL_LINK_STATUS, &compiled);
339 if (!compiled) {
340 GLchar log[1024];
341
342 glGetProgramInfoLog (prog, sizeof log - 1, NULL, log);
343 log[sizeof log - 1] = '\0';
344
345 BOOST_THROW_EXCEPTION(
346 std::runtime_error(std::string{"Failed to link shader prog:\n"} + log));
347 }
348
349 glUseProgram(prog);
350
351 attrpos = glGetAttribLocation(prog, "position");
352 attrtex = glGetAttribLocation(prog, "texcoord");
353 auto unitex = glGetUniformLocation(prog, "tex");
354
355 glGenTextures(1, &tex);
356 glActiveTexture(GL_TEXTURE0);
357 glBindTexture(GL_TEXTURE_2D, tex);
358
359 glUniform1i(unitex, 0);
360
361 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
362 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
363 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
364 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
365
366 static GLfloat const dest_vert[4][2] =
367 { { -1.f, 1.f }, { 1.f, 1.f }, { 1.f, -1.f }, { -1.f, -1.f } };
368 vert_data = std::make_unique<VBO>(dest_vert, sizeof(dest_vert));
369
370 static GLfloat const tex_vert[4][2] =
371 {
372 { 0.f, 0.f }, { 1.f, 0.f }, { 1.f, 1.f }, { 0.f, 1.f },
373 };
374 tex_data = std::make_unique<VBO>(tex_vert, sizeof(tex_vert));
375 }
376
377 EGLBufferCopier(EGLBufferCopier const&) = delete;
378 EGLBufferCopier& operator==(EGLBufferCopier const&) = delete;
379
380 ~EGLBufferCopier()
381 {
382 eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context);
383 vert_data = nullptr;
384 tex_data = nullptr;
385 eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
386 eglDestroySurface(display, egl_surface);
387 eglDestroyContext(display, context);
388 eglTerminate(display);
389 }
390
391 mgm::GBMOutputSurface::FrontBuffer copy_front_buffer_from(mgm::GBMOutputSurface::FrontBuffer&& from)
392 {
393 eglMakeCurrent(display, egl_surface, egl_surface, context);
394 mir::Fd const dma_buf{gbm_bo_get_fd(from)};
395
396 glUseProgram(prog);
397
398 EGLint const image_attrs[] = {
399 EGL_WIDTH, static_cast<EGLint>(width),
400 EGL_HEIGHT, static_cast<EGLint>(height),
401 EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_XRGB8888,
402 EGL_DMA_BUF_PLANE0_FD_EXT, static_cast<int>(dma_buf),
403 EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
404 EGL_DMA_BUF_PLANE0_PITCH_EXT, static_cast<EGLint>(gbm_bo_get_stride(from)),
405 EGL_NONE
406 };
407
408 auto image = eglCreateImageKHR(
409 display,
410 EGL_NO_CONTEXT,
411 EGL_LINUX_DMA_BUF_EXT,
412 nullptr,
413 image_attrs);
414
415 if (image == EGL_NO_IMAGE_KHR)
416 {
417 BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGLImage from dma_buf"));
418 }
419
420 glActiveTexture(GL_TEXTURE0);
421 glBindTexture(GL_TEXTURE_2D, tex);
422 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
423
424 vert_data->bind();
425 glVertexAttribPointer (attrpos, 2, GL_FLOAT, GL_FALSE, 0, 0);
426
427 tex_data->bind();
428 glVertexAttribPointer (attrtex, 2, GL_FLOAT, GL_FALSE, 0, 0);
429
430 glEnableVertexAttribArray(attrpos);
431 glEnableVertexAttribArray(attrtex);
432
433 GLubyte const idx[] = { 0, 1, 3, 2 };
434 glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, idx);
435
436 if (eglSwapBuffers(display, egl_surface) != EGL_TRUE)
437 {
438 BOOST_THROW_EXCEPTION(mg::egl_error("Failed to swap bounce buffers"));
439 }
440
441 eglDestroyImageKHR(display, image);
442
443 eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
444 return mgm::GBMOutputSurface::FrontBuffer(surface.get());
445 }
446
447 private:
448 static mgm::GBMSurfaceUPtr create_scanout_surface(
449 gbm_device& on,
450 uint32_t width,
451 uint32_t height,
452 uint32_t format)
453 {
454 auto* const device = &on;
455
456 return {
457 gbm_surface_create(
458 device,
459 width,
460 height,
461 format,
462 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING),
463 &gbm_surface_destroy};
464 }
465
466 PFNEGLCREATEIMAGEKHRPROC const eglCreateImageKHR;
467 PFNEGLDESTROYIMAGEKHRPROC const eglDestroyImageKHR;
468 PFNGLEGLIMAGETARGETTEXTURE2DOESPROC const glEGLImageTargetTexture2DOES;
469
470 std::unique_ptr<gbm_device, decltype(&gbm_device_destroy)> const device;
471 uint32_t const width;
472 uint32_t const height;
473 mgm::GBMSurfaceUPtr const surface;
474 EGLDisplay display;
475 EGLContext context;
476 EGLSurface egl_surface;
477 GLuint prog;
478 GLuint tex;
479 GLint attrtex;
480 GLint attrpos;
481 std::unique_ptr<VBO> vert_data;
482 std::unique_ptr<VBO> tex_data;
483};
119}484}
120485
121mgm::DisplayBuffer::DisplayBuffer(486mgm::DisplayBuffer::DisplayBuffer(
@@ -152,19 +517,48 @@
152 make_current();517 make_current();
153518
154 listener->report_successful_egl_make_current_on_construction();519 listener->report_successful_egl_make_current_on_construction();
155520
156 ensure_egl_image_extensions();
157
158 glClear(GL_COLOR_BUFFER_BIT);521 glClear(GL_COLOR_BUFFER_BIT);
159522
160 surface.swap_buffers();523 surface.swap_buffers();
161524
162 listener->report_successful_egl_buffer_swap_on_construction();525 listener->report_successful_egl_buffer_swap_on_construction();
163526
164 visible_composite_frame = surface.lock_front();527 auto temporary_front = surface.lock_front();
165 if (!visible_composite_frame)528 if (!temporary_front)
166 fatal_error("Failed to get frontbuffer");529 fatal_error("Failed to get frontbuffer");
167530
531 if (needs_bounce_buffer(*outputs.front(), temporary_front))
532 {
533 get_front_buffer = std::bind(
534 std::mem_fn(&EGLBufferCopier::copy_front_buffer_from),
535 std::make_shared<EGLBufferCopier>(
536 outputs.front()->drm_fd(),
537 fb_width,
538 fb_height,
539 GBM_BO_FORMAT_XRGB8888),
540 std::placeholders::_1);
541 }
542 else
543 {
544 get_front_buffer = [](auto&& fb) { return std::move(fb); };
545 }
546
547 visible_composite_frame = get_front_buffer(std::move(temporary_front));
548
549 /*
550 * Check that our (possibly bounced) front buffer is usable on *all* the
551 * outputs we've been asked to output on.
552 */
553 for (auto const& output : outputs)
554 {
555 if (output->buffer_requires_migration(visible_composite_frame))
556 {
557 BOOST_THROW_EXCEPTION(std::invalid_argument(
558 "Attempted to create a DisplayBuffer spanning multiple GPU memory domains"));
559 }
560 }
561
168 set_crtc(*outputs.front()->fb_for(visible_composite_frame, fb_width, fb_height));562 set_crtc(*outputs.front()->fb_for(visible_composite_frame, fb_width, fb_height));
169563
170 release_current();564 release_current();
@@ -213,7 +607,8 @@
213 if (!native)607 if (!native)
214 BOOST_THROW_EXCEPTION(std::invalid_argument("could not convert NativeBuffer"));608 BOOST_THROW_EXCEPTION(std::invalid_argument("could not convert NativeBuffer"));
215 if (native->flags & mir_buffer_flag_can_scanout &&609 if (native->flags & mir_buffer_flag_can_scanout &&
216 bypass_buffer->size() == geom::Size{fb_width,fb_height})610 bypass_buffer->size() == geom::Size{fb_width,fb_height} &&
611 !needs_bounce_buffer(*outputs.front(), native->bo))
217 {612 {
218 if (auto bufobj = outputs.front()->fb_for(native->bo, fb_width, fb_height))613 if (auto bufobj = outputs.front()->fb_for(native->bo, fb_width, fb_height))
219 {614 {
@@ -278,7 +673,7 @@
278 }673 }
279 else674 else
280 {675 {
281 scheduled_composite_frame = surface.lock_front();676 scheduled_composite_frame = get_front_buffer(surface.lock_front());
282 bufobj = outputs.front()->fb_for(scheduled_composite_frame, fb_width, fb_height);677 bufobj = outputs.front()->fb_for(scheduled_composite_frame, fb_width, fb_height);
283 if (!bufobj)678 if (!bufobj)
284 fatal_error("Failed to get front buffer object");679 fatal_error("Failed to get front buffer object");
285680
=== modified file 'src/platforms/mesa/server/kms/display_buffer.h'
--- src/platforms/mesa/server/kms/display_buffer.h 2017-03-16 07:37:11 +0000
+++ src/platforms/mesa/server/kms/display_buffer.h 2017-03-22 06:55:00 +0000
@@ -53,19 +53,18 @@
53 {53 {
54 public:54 public:
55 FrontBuffer();55 FrontBuffer();
56 FrontBuffer(gbm_surface* surface);
57 FrontBuffer(FrontBuffer&& from);
58
56 ~FrontBuffer();59 ~FrontBuffer();
5760
58 FrontBuffer(FrontBuffer&& from);
59
60 FrontBuffer& operator=(FrontBuffer&& from);61 FrontBuffer& operator=(FrontBuffer&& from);
61 FrontBuffer& operator=(std::nullptr_t);62 FrontBuffer& operator=(std::nullptr_t);
6263
63 operator gbm_bo*();64 operator gbm_bo*();
64 operator bool() const;65 operator bool() const;
66
65 private:67 private:
66 friend class GBMOutputSurface;
67 FrontBuffer(gbm_surface* surface);
68
69 gbm_surface* const surf;68 gbm_surface* const surf;
70 gbm_bo* const bo;69 gbm_bo* const bo;
71 };70 };
@@ -146,6 +145,8 @@
146 GBMOutputSurface::FrontBuffer visible_composite_frame;145 GBMOutputSurface::FrontBuffer visible_composite_frame;
147 GBMOutputSurface::FrontBuffer scheduled_composite_frame;146 GBMOutputSurface::FrontBuffer scheduled_composite_frame;
148147
148 std::function<GBMOutputSurface::FrontBuffer(GBMOutputSurface::FrontBuffer&&)> get_front_buffer;
149
149 geometry::Rectangle area;150 geometry::Rectangle area;
150 uint32_t fb_width, fb_height;151 uint32_t fb_width, fb_height;
151 glm::mat2 transform;152 glm::mat2 transform;
152153
=== modified file 'src/platforms/mesa/server/kms/kms_display_configuration.h'
--- src/platforms/mesa/server/kms/kms_display_configuration.h 2016-01-29 08:18:22 +0000
+++ src/platforms/mesa/server/kms/kms_display_configuration.h 2017-03-22 06:55:00 +0000
@@ -20,6 +20,7 @@
20#define MIR_GRAPHICS_MESA_KMS_DISPLAY_CONFIGURATION_H_20#define MIR_GRAPHICS_MESA_KMS_DISPLAY_CONFIGURATION_H_
2121
22#include "mir/graphics/display_configuration.h"22#include "mir/graphics/display_configuration.h"
23#include <memory>
2324
24namespace mir25namespace mir
25{26{
@@ -27,15 +28,17 @@
27{28{
28namespace mesa29namespace mesa
29{30{
31class KMSOutput;
3032
31class DRMModeResources;33class DRMModeResources;
3234
33class KMSDisplayConfiguration : public DisplayConfiguration35class KMSDisplayConfiguration : public DisplayConfiguration
34{36{
35public:37public:
36 virtual uint32_t get_kms_connector_id(DisplayConfigurationOutputId id) const = 0;38 virtual std::shared_ptr<KMSOutput> get_output_for(DisplayConfigurationOutputId id) const = 0;
37 virtual size_t get_kms_mode_index(DisplayConfigurationOutputId id,39 virtual size_t get_kms_mode_index(
38 size_t conf_mode_index) const = 0;40 DisplayConfigurationOutputId id,
41 size_t conf_mode_index) const = 0;
39 virtual void update() = 0;42 virtual void update() = 0;
40};43};
4144
4245
=== modified file 'src/platforms/mesa/server/kms/kms_output.h'
--- src/platforms/mesa/server/kms/kms_output.h 2017-03-13 08:12:52 +0000
+++ src/platforms/mesa/server/kms/kms_output.h 2017-03-22 06:55:00 +0000
@@ -26,12 +26,16 @@
26#include "mir/graphics/frame.h"26#include "mir/graphics/frame.h"
27#include "mir_toolkit/common.h"27#include "mir_toolkit/common.h"
2828
29#include "kms-utils/drm_mode_resources.h"
30
29#include <gbm.h>31#include <gbm.h>
3032
31namespace mir33namespace mir
32{34{
33namespace graphics35namespace graphics
34{36{
37class DisplayConfigurationOutput;
38
35namespace mesa39namespace mesa
36{40{
3741
@@ -42,6 +46,12 @@
42public:46public:
43 virtual ~KMSOutput() = default;47 virtual ~KMSOutput() = default;
4448
49 /*
50 * I'm not sure that DRM guarantees ID uniqueness in the presence of hotplug/unplug;
51 * this may want to be an opaque class Id + operator== in future.
52 */
53 virtual uint32_t id() const = 0;
54
45 virtual void reset() = 0;55 virtual void reset() = 0;
46 virtual void configure(geometry::Displacement fb_offset, size_t kms_mode_index) = 0;56 virtual void configure(geometry::Displacement fb_offset, size_t kms_mode_index) = 0;
47 virtual geometry::Size size() const = 0;57 virtual geometry::Size size() const = 0;
@@ -68,14 +78,39 @@
68 virtual void set_gamma(GammaCurves const& gamma) = 0;78 virtual void set_gamma(GammaCurves const& gamma) = 0;
69 virtual Frame last_frame() const = 0;79 virtual Frame last_frame() const = 0;
7080
81 /**
82 * Re-probe the hardware state of this connector.
83 *
84 * \throws std::system_error if the underlying DRM connector has disappeared.
85 */
86 virtual void refresh_hardware_state() = 0;
87 /**
88 * Translate and copy the cached hardware state into a Mir display configuration object.
89 *
90 * \param [out] to_update The Mir display configuration object to update with new
91 * hardware state. Only hardware state (modes, dimensions, etc)
92 * is touched.
93 */
94 virtual void update_from_hardware_state(DisplayConfigurationOutput& to_update) const = 0;
71 virtual FBHandle* fb_for(gbm_bo* bo, uint32_t width, uint32_t height) const = 0;95 virtual FBHandle* fb_for(gbm_bo* bo, uint32_t width, uint32_t height) const = 0;
7296
97 /**
98 * Check whether buffer need to be migrated to GPU-private memory for display.
99 *
100 * \param [in] bo GBM buffer to test
101 * \return True if buffer must be migrated to display-private memory in order to be displayed.
102 * If this method returns true the caller should probably copy it to a new buffer before
103 * calling fb_for(buffer), as acquiring a FBHandle to the buffer will likely make it
104 * unusable for rendering on the original GPU.
105 */
106 virtual bool buffer_requires_migration(gbm_bo* bo) const = 0;
107
108 virtual int drm_fd() const = 0;
73protected:109protected:
74 KMSOutput() = default;110 KMSOutput() = default;
75 KMSOutput(const KMSOutput&) = delete;111 KMSOutput(const KMSOutput&) = delete;
76 KMSOutput& operator=(const KMSOutput&) = delete;112 KMSOutput& operator=(const KMSOutput&) = delete;
77};113};
78
79}114}
80}115}
81}116}
82117
=== modified file 'src/platforms/mesa/server/kms/kms_output_container.h'
--- src/platforms/mesa/server/kms/kms_output_container.h 2016-01-29 08:18:22 +0000
+++ src/platforms/mesa/server/kms/kms_output_container.h 2017-03-22 06:55:00 +0000
@@ -36,9 +36,12 @@
36public:36public:
37 virtual ~KMSOutputContainer() = default;37 virtual ~KMSOutputContainer() = default;
3838
39 virtual std::shared_ptr<KMSOutput> get_kms_output_for(uint32_t connector_id) = 0;39 virtual void for_each_output(std::function<void(std::shared_ptr<KMSOutput> const&)> functor) const = 0;
40 virtual void for_each_output(std::function<void(KMSOutput&)> functor) const = 0;
4140
41 /**
42 * Re-probe hardware state and update output list.
43 */
44 virtual void update_from_hardware_state() = 0;
42protected:45protected:
43 KMSOutputContainer() = default;46 KMSOutputContainer() = default;
44 KMSOutputContainer(KMSOutputContainer const&) = delete;47 KMSOutputContainer(KMSOutputContainer const&) = delete;
4548
=== added file 'src/platforms/mesa/server/kms/mutex.h'
--- src/platforms/mesa/server/kms/mutex.h 1970-01-01 00:00:00 +0000
+++ src/platforms/mesa/server/kms/mutex.h 2017-03-22 06:55:00 +0000
@@ -0,0 +1,104 @@
1/*
2 * Copyright © 2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#ifndef MIR_MUTEX_H_
20#define MIR_MUTEX_H_
21
22#include <mutex>
23
24namespace mir
25{
26/**
27 * Smart-pointer-esque accessor for Mutex<> protected data.
28 *
29 * Ensures exclusive access to the referenced data.
30 *
31 * \tparam Guarded Type of data guarded by the mutex.
32 */
33template<typename Guarded>
34class MutexGuard
35{
36public:
37 MutexGuard(std::unique_lock<std::mutex>&& lock, Guarded& value)
38 : value{value},
39 lock{std::move(lock)}
40 {
41 }
42 MutexGuard(MutexGuard&& from) = default;
43 ~MutexGuard() noexcept(false)
44 {
45 if (lock.owns_lock())
46 {
47 lock.unlock();
48 }
49 }
50
51 Guarded& operator*()
52 {
53 return value;
54 }
55 Guarded* operator->()
56 {
57 return &value;
58 }
59private:
60 Guarded& value;
61 std::unique_lock<std::mutex> lock;
62};
63
64/**
65 * A data-locking mutex
66 *
67 * This is a mutex which owns the data it guards, and can give out a
68 * smart-pointer-esque lock to lock and access it.
69 *
70 * \tparam Guarded The type of data guarded by the mutex
71 */
72template<typename Guarded>
73class Mutex
74{
75public:
76 Mutex() = default;
77 Mutex(Guarded&& initial_value)
78 : value{std::move(initial_value)}
79 {
80 }
81
82 Mutex(Mutex const&) = delete;
83 Mutex& operator=(Mutex const&) = delete;
84
85 /**
86 * Lock the mutex and return an accessor for the protected data.
87 *
88 * \return A smart-pointer-esque accessor for the contained data.
89 * While code has access to the MutexGuard it is guaranteed to have exclusive
90 * access to the contained data.
91 */
92 MutexGuard<Guarded> lock()
93 {
94 return MutexGuard<Guarded>{std::unique_lock<std::mutex>{mutex}, value};
95 }
96
97private:
98 std::mutex mutex;
99 Guarded value;
100};
101
102}
103
104#endif //MIR_MUTEX_H_
0105
=== modified file 'src/platforms/mesa/server/kms/platform.cpp'
--- src/platforms/mesa/server/kms/platform.cpp 2017-03-16 07:37:11 +0000
+++ src/platforms/mesa/server/kms/platform.cpp 2017-03-22 06:55:00 +0000
@@ -39,17 +39,25 @@
39 EmergencyCleanupRegistry& emergency_cleanup_registry,39 EmergencyCleanupRegistry& emergency_cleanup_registry,
40 BypassOption bypass_option)40 BypassOption bypass_option)
41 : udev{std::make_shared<mir::udev::Context>()},41 : udev{std::make_shared<mir::udev::Context>()},
42 drm{std::make_shared<mgmh::DRMHelper>(mgmh::DRMNodeToUse::card)},42 drm{helpers::DRMHelper::open_all_devices(udev)},
43 gbm{std::make_shared<mgmh::GBMHelper>()},43 gbm{std::make_shared<mgmh::GBMHelper>()},
44 listener{listener},44 listener{listener},
45 vt{vt},45 vt{vt},
46 bypass_option_{bypass_option}46 bypass_option_{bypass_option}
47{47{
48 drm->setup(udev);48 // We assume the first DRM device is the boot GPU, and arbitrarily pick it as our
49 gbm->setup(*drm);49 // shell renderer.
50 //
51 // TODO: expose multiple rendering GPUs to the shell.
52 gbm->setup(*drm.front());
5053
51 std::weak_ptr<VirtualTerminal> weak_vt = vt;54 std::weak_ptr<VirtualTerminal> weak_vt = vt;
52 std::weak_ptr<mgmh::DRMHelper> weak_drm = drm;55 std::vector<std::weak_ptr<mgmh::DRMHelper>> weak_drm;
56
57 for (auto const &helper : drm)
58 {
59 weak_drm.push_back(helper);
60 }
53 emergency_cleanup_registry.add(61 emergency_cleanup_registry.add(
54 make_module_ptr<EmergencyCleanupHandler>(62 make_module_ptr<EmergencyCleanupHandler>(
55 [weak_vt,weak_drm]63 [weak_vt,weak_drm]
@@ -57,8 +65,19 @@
57 if (auto const vt = weak_vt.lock())65 if (auto const vt = weak_vt.lock())
58 try { vt->restore(); } catch (...) {}66 try { vt->restore(); } catch (...) {}
5967
60 if (auto const drm = weak_drm.lock())68 for (auto helper : weak_drm)
61 try { drm->drop_master(); } catch (...) {}69 {
70 if (auto const drm = helper.lock())
71 {
72 try
73 {
74 drm->drop_master();
75 }
76 catch (...)
77 {
78 }
79 }
80 }
62 }));81 }));
6382
64}83}
@@ -71,12 +90,19 @@
71mir::UniqueModulePtr<mg::Display> mgm::Platform::create_display(90mir::UniqueModulePtr<mg::Display> mgm::Platform::create_display(
72 std::shared_ptr<DisplayConfigurationPolicy> const& initial_conf_policy, std::shared_ptr<GLConfig> const& gl_config)91 std::shared_ptr<DisplayConfigurationPolicy> const& initial_conf_policy, std::shared_ptr<GLConfig> const& gl_config)
73{92{
74 return make_module_ptr<mgm::Display>(drm, gbm, vt, bypass_option_, initial_conf_policy, gl_config, listener);93 return make_module_ptr<mgm::Display>(
94 drm,
95 gbm,
96 vt,
97 bypass_option_,
98 initial_conf_policy,
99 gl_config,
100 listener);
75}101}
76102
77mir::UniqueModulePtr<mg::PlatformIpcOperations> mgm::Platform::make_ipc_operations() const103mir::UniqueModulePtr<mg::PlatformIpcOperations> mgm::Platform::make_ipc_operations() const
78{104{
79 return make_module_ptr<mgm::IpcOperations>(drm);105 return make_module_ptr<mgm::IpcOperations>(drm.front());
80}106}
81107
82mg::NativePlatform* mgm::Platform::native_platform()108mg::NativePlatform* mgm::Platform::native_platform()
83109
=== modified file 'src/platforms/mesa/server/kms/platform.h'
--- src/platforms/mesa/server/kms/platform.h 2017-03-16 07:37:11 +0000
+++ src/platforms/mesa/server/kms/platform.h 2017-03-22 06:55:00 +0000
@@ -56,7 +56,7 @@
56 EGLNativeDisplayType egl_native_display() const override;56 EGLNativeDisplayType egl_native_display() const override;
5757
58 std::shared_ptr<mir::udev::Context> udev;58 std::shared_ptr<mir::udev::Context> udev;
59 std::shared_ptr<helpers::DRMHelper> const drm;59 std::vector<std::shared_ptr<helpers::DRMHelper>> const drm;
60 std::shared_ptr<helpers::GBMHelper> const gbm;60 std::shared_ptr<helpers::GBMHelper> const gbm;
6161
62 std::shared_ptr<DisplayReport> const listener;62 std::shared_ptr<DisplayReport> const listener;
6363
=== modified file 'src/platforms/mesa/server/kms/real_kms_display_configuration.cpp'
--- src/platforms/mesa/server/kms/real_kms_display_configuration.cpp 2017-03-14 02:26:28 +0000
+++ src/platforms/mesa/server/kms/real_kms_display_configuration.cpp 2017-03-22 06:55:00 +0000
@@ -17,9 +17,10 @@
17 */17 */
1818
19#include "real_kms_display_configuration.h"19#include "real_kms_display_configuration.h"
20#include "kms-utils/drm_mode_resources.h"
21#include "mir/graphics/pixel_format_utils.h"20#include "mir/graphics/pixel_format_utils.h"
22#include "mir/log.h"21#include "mir/log.h"
22#include "kms_output_container.h"
23#include "kms_output.h"
2324
24#include <cmath>25#include <cmath>
25#include <limits>26#include <limits>
@@ -35,77 +36,21 @@
35namespace mgk = mir::graphics::kms;36namespace mgk = mir::graphics::kms;
36namespace geom = mir::geometry;37namespace geom = mir::geometry;
3738
38namespace39mgm::RealKMSDisplayConfiguration::RealKMSDisplayConfiguration(
39{40 std::shared_ptr<KMSOutputContainer> const& displays)
4041 : displays{displays},
41bool kms_modes_are_equal(drmModeModeInfo const& info1, drmModeModeInfo const& info2)42 card{mg::DisplayConfigurationCardId{0}, 0}
42{
43 return (info1.clock == info2.clock &&
44 info1.hdisplay == info2.hdisplay &&
45 info1.hsync_start == info2.hsync_start &&
46 info1.hsync_end == info2.hsync_end &&
47 info1.htotal == info2.htotal &&
48 info1.hskew == info2.hskew &&
49 info1.vdisplay == info2.vdisplay &&
50 info1.vsync_start == info2.vsync_start &&
51 info1.vsync_end == info2.vsync_end &&
52 info1.vtotal == info2.vtotal);
53}
54
55double calculate_vrefresh_hz(drmModeModeInfo const& mode)
56{
57 if (mode.htotal == 0 || mode.vtotal == 0)
58 return 0.0;
59
60 /* mode.clock is in KHz */
61 double hz = (mode.clock * 100000LL /
62 ((long)mode.htotal * (long)mode.vtotal)
63 ) / 100.0;
64
65 // Actually we don't need floating point at all for this...
66 // TODO: Consider converting our structs to fixed-point ints
67 return hz;
68}
69
70mg::DisplayConfigurationOutputType
71kms_connector_type_to_output_type(uint32_t connector_type)
72{
73 return static_cast<mg::DisplayConfigurationOutputType>(connector_type);
74}
75
76MirSubpixelArrangement kms_subpixel_to_mir_subpixel(uint32_t subpixel)
77{
78 switch (subpixel)
79 {
80 case DRM_MODE_SUBPIXEL_UNKNOWN:
81 return mir_subpixel_arrangement_unknown;
82 case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
83 return mir_subpixel_arrangement_horizontal_rgb;
84 case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
85 return mir_subpixel_arrangement_horizontal_bgr;
86 case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
87 return mir_subpixel_arrangement_vertical_rgb;
88 case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
89 return mir_subpixel_arrangement_vertical_bgr;
90 case DRM_MODE_SUBPIXEL_NONE:
91 return mir_subpixel_arrangement_none;
92 default:
93 return mir_subpixel_arrangement_unknown;
94 }
95}
96
97}
98
99mgm::RealKMSDisplayConfiguration::RealKMSDisplayConfiguration(int drm_fd)
100 : drm_fd{drm_fd}
101{43{
102 update();44 update();
103}45}
10446
105mgm::RealKMSDisplayConfiguration::RealKMSDisplayConfiguration(47mgm::RealKMSDisplayConfiguration::RealKMSDisplayConfiguration(
106 RealKMSDisplayConfiguration const& conf)48 RealKMSDisplayConfiguration const& conf)
107 : KMSDisplayConfiguration(), drm_fd{conf.drm_fd},49 : KMSDisplayConfiguration(),
108 card(conf.card), outputs{conf.outputs}50 displays{conf.displays},
51 // Vivid GCC is apparently confused by trying to copy-construct card from conf.card?
52 card{conf.card.id, conf.card.max_simultaneous_outputs},
53 outputs{conf.outputs}
109{54{
110}55}
11156
@@ -114,7 +59,7 @@
114{59{
115 if (&conf != this)60 if (&conf != this)
116 {61 {
117 drm_fd = conf.drm_fd;62 displays = conf.displays;
118 card = conf.card;63 card = conf.card;
119 outputs = conf.outputs;64 outputs = conf.outputs;
120 }65 }
@@ -131,16 +76,16 @@
131void mgm::RealKMSDisplayConfiguration::for_each_output(76void mgm::RealKMSDisplayConfiguration::for_each_output(
132 std::function<void(DisplayConfigurationOutput const&)> f) const77 std::function<void(DisplayConfigurationOutput const&)> f) const
133{78{
134 for (auto const& output : outputs)79 for (auto const& output_pair : outputs)
135 f(output);80 f(output_pair.first);
136}81}
13782
138void mgm::RealKMSDisplayConfiguration::for_each_output(83void mgm::RealKMSDisplayConfiguration::for_each_output(
139 std::function<void(UserDisplayConfigurationOutput&)> f)84 std::function<void(UserDisplayConfigurationOutput&)> f)
140{85{
141 for (auto& output : outputs)86 for (auto& output_pair : outputs)
142 {87 {
143 UserDisplayConfigurationOutput user(output);88 UserDisplayConfigurationOutput user(output_pair.first);
144 f(user);89 f(user);
145 }90 }
146}91}
@@ -150,233 +95,87 @@
150 return std::make_unique<RealKMSDisplayConfiguration>(*this);95 return std::make_unique<RealKMSDisplayConfiguration>(*this);
151}96}
15297
153uint32_t mgm::RealKMSDisplayConfiguration::get_kms_connector_id(98std::shared_ptr<mgm::KMSOutput> mgm::RealKMSDisplayConfiguration::get_output_for(
154 DisplayConfigurationOutputId id) const99 DisplayConfigurationOutputId id) const
155{100{
156 auto iter = find_output_with_id(id);101 return outputs.at(id.as_value()).second;
157
158 if (iter == outputs.end())
159 {
160 BOOST_THROW_EXCEPTION(
161 std::runtime_error("Failed to find DisplayConfigurationOutput with provided id"));
162 }
163
164 return id.as_value();
165}102}
166103
167size_t mgm::RealKMSDisplayConfiguration::get_kms_mode_index(104size_t mgm::RealKMSDisplayConfiguration::get_kms_mode_index(
168 DisplayConfigurationOutputId id,105 DisplayConfigurationOutputId id,
169 size_t conf_mode_index) const106 size_t conf_mode_index) const
170{107{
171 auto iter = find_output_with_id(id);108 if (static_cast<size_t>(id.as_value()) > outputs.size())
172109 {
173 if (iter == outputs.end() || conf_mode_index >= iter->modes.size())110 BOOST_THROW_EXCEPTION(std::invalid_argument("Request for KMS mode index of invalid output ID"));
174 {111 }
175 BOOST_THROW_EXCEPTION(112 if (conf_mode_index > outputs[id.as_value()].first.modes.size())
176 std::runtime_error("Failed to find valid mode index for DisplayConfigurationOutput with provided id/mode_index"));113 {
114 BOOST_THROW_EXCEPTION(std::invalid_argument("Request for out-of-bounds KMS mode index"));
177 }115 }
178116
179 return conf_mode_index;117 return conf_mode_index;
180}118}
181119
120namespace
121{
122void populate_default_mir_config(mg::DisplayConfigurationOutput& to_populate)
123{
124 to_populate.card_id = mg::DisplayConfigurationCardId{0};
125 to_populate.gamma_supported = mir_output_gamma_supported;
126 to_populate.orientation = mir_orientation_normal;
127 to_populate.form_factor = mir_form_factor_monitor;
128 to_populate.scale = 1.0f;
129 to_populate.top_left = geom::Point{};
130 to_populate.used = false;
131 to_populate.pixel_formats = {mir_pixel_format_xrgb_8888, mir_pixel_format_argb_8888};
132 to_populate.current_format = mir_pixel_format_xrgb_8888;
133 to_populate.current_mode_index = std::numeric_limits<uint32_t>::max();
134}
135}
136
182void mgm::RealKMSDisplayConfiguration::update()137void mgm::RealKMSDisplayConfiguration::update()
183{138{
184 kms::DRMModeResources resources{drm_fd};139 decltype(outputs) new_outputs;
185140
186 size_t max_outputs = std::min(resources.num_crtcs(), resources.num_connectors());141 int counter = 0;
187 card = {DisplayConfigurationCardId{0}, max_outputs};142 displays->update_from_hardware_state();
188143 displays->for_each_output(
189 resources.for_each_connector([&](kms::DRMModeConnectorUPtr connector)144 [this, &new_outputs, &counter](auto const& output) mutable
190 {145 {
191 add_or_update_output(resources, *connector);146 DisplayConfigurationOutput mir_config;
192 });147
193}148 auto const existing_output = std::find_if(
194149 outputs.begin(),
195namespace150 outputs.end(),
196{151 [&output](auto const& candidate)
197std::vector<uint8_t> edid_for_connector(int drm_fd, uint32_t connector_id)152 {
198{153 // Pointer comparison; is this KMSOutput object already present?
199 std::vector<uint8_t> edid;154 return candidate.second == output;
200155 });
201 mgk::ObjectProperties connector_props{156 if (existing_output == outputs.end())
202 drm_fd, connector_id, DRM_MODE_OBJECT_CONNECTOR};157 {
203158 populate_default_mir_config(mir_config);
204 if (connector_props.has_property("EDID"))159 }
205 {160 else
206 /*161 {
207 * We don't technically need the property information here, but query it162 mir_config = existing_output->first;
208 * anyway so we can detect if our assumptions about DRM behaviour163 }
209 * become invalid.164
210 */165 output->update_from_hardware_state(mir_config);
211 auto property = mgk::DRMModePropertyUPtr{166 mir_config.id = DisplayConfigurationOutputId{counter};
212 drmModeGetProperty(drm_fd, connector_props.id_for("EDID")),167 counter++;
213 &drmModeFreeProperty};168
214169 new_outputs.push_back(std::make_pair(mir_config, output));
215 if (!property)170 });
216 {171
217 mir::log_warning(172 outputs = new_outputs;
218 "Failed to get EDID property for connector %u: %i (%s)",173
219 connector_id,174 /*
220 errno,175 * This is not the true max simultaneous outputs, but it's unclear whether it's possible
221 ::strerror(errno));176 * to provide a max_simultaneous_outputs value that is useful to clients.
222 return edid;177 */
223 }178 card.max_simultaneous_outputs = outputs.size();
224
225 if (!drm_property_type_is(property.get(), DRM_MODE_PROP_BLOB))
226 {
227 mir::log_warning(
228 "EDID property on connector %u has unexpected type %u",
229 connector_id,
230 property->flags);
231 return edid;
232 }
233
234 // A property ID of 0 means invalid.
235 if (connector_props["EDID"] == 0)
236 {
237 /*
238 * Log a debug message only. This will trigger for broken monitors which
239 * don't provide an EDID, which is not as unusual as you might think...
240 */
241 mir::log_debug("No EDID data available on connector %u", connector_id);
242 return edid;
243 }
244
245 auto blob = drmModeGetPropertyBlob(drm_fd, connector_props["EDID"]);
246
247 if (!blob)
248 {
249 mir::log_warning(
250 "Failed to get EDID property blob for connector %u: %i (%s)",
251 connector_id,
252 errno,
253 ::strerror(errno));
254
255 return edid;
256 }
257
258 edid.reserve(blob->length);
259 edid.insert(edid.begin(),
260 reinterpret_cast<uint8_t*>(blob->data),
261 reinterpret_cast<uint8_t*>(blob->data) + blob->length);
262
263 drmModeFreePropertyBlob(blob);
264
265 edid.shrink_to_fit();
266 }
267
268 return edid;
269}
270}
271
272void mgm::RealKMSDisplayConfiguration::add_or_update_output(
273 kms::DRMModeResources const& resources,
274 drmModeConnector const& connector)
275{
276 DisplayConfigurationOutputId id{static_cast<int>(connector.connector_id)};
277 DisplayConfigurationCardId card_id{0};
278 DisplayConfigurationOutputType const type{
279 kms_connector_type_to_output_type(connector.connector_type)};
280 geom::Size physical_size{connector.mmWidth, connector.mmHeight};
281 bool connected{connector.connection == DRM_MODE_CONNECTED};
282 uint32_t const invalid_mode_index = std::numeric_limits<uint32_t>::max();
283 uint32_t current_mode_index{invalid_mode_index};
284 uint32_t preferred_mode_index{invalid_mode_index};
285 std::vector<DisplayConfigurationMode> modes;
286 std::vector<MirPixelFormat> formats {mir_pixel_format_argb_8888,
287 mir_pixel_format_xrgb_8888};
288
289 std::vector<uint8_t> edid;
290 if (connected)
291 {
292 /* Only ask for the EDID on connected outputs. There's obviously no monitor EDID
293 * when there is no monitor connected!
294 */
295 edid = edid_for_connector(drm_fd, connector.connector_id);
296 }
297
298 drmModeModeInfo current_mode_info = drmModeModeInfo();
299 GammaCurves gamma;
300
301 /* Get information about the current mode */
302 if (connector.encoder_id)
303 {
304 auto encoder = resources.encoder(connector.encoder_id);
305 if (encoder->crtc_id)
306 {
307 auto crtc = resources.crtc(encoder->crtc_id);
308 current_mode_info = crtc->mode;
309
310 if (crtc->gamma_size > 0)
311 gamma = mg::LinearGammaLUTs(crtc->gamma_size);
312 }
313 }
314
315 /* Add all the available modes and find the current and preferred one */
316 for (int m = 0; m < connector.count_modes; m++)
317 {
318 drmModeModeInfo& mode_info = connector.modes[m];
319
320 geom::Size size{mode_info.hdisplay, mode_info.vdisplay};
321
322 double vrefresh_hz = calculate_vrefresh_hz(mode_info);
323
324 modes.push_back({size, vrefresh_hz});
325
326 if (kms_modes_are_equal(mode_info, current_mode_info))
327 current_mode_index = m;
328
329 if ((mode_info.type & DRM_MODE_TYPE_PREFERRED) == DRM_MODE_TYPE_PREFERRED)
330 preferred_mode_index = m;
331 }
332
333 /* Add or update the output */
334 auto iter = find_output_with_id(id);
335
336 if (iter == outputs.end())
337 {
338 outputs.push_back({id, card_id, type, formats, modes, preferred_mode_index,
339 physical_size, connected, false, geom::Point(),
340 current_mode_index, mir_pixel_format_xrgb_8888,
341 mir_power_mode_on, mir_orientation_normal,
342 1.0f, mir_form_factor_monitor,
343 kms_subpixel_to_mir_subpixel(connector.subpixel),
344 gamma, mir_output_gamma_supported, std::move(edid)});
345 }
346 else
347 {
348 auto& output = *iter;
349
350 output.current_mode_index = current_mode_index;
351 output.modes = modes;
352 output.preferred_mode_index = preferred_mode_index;
353 output.physical_size_mm = physical_size;
354 output.connected = connected;
355 output.current_format = mir_pixel_format_xrgb_8888;
356 output.subpixel_arrangement = kms_subpixel_to_mir_subpixel(connector.subpixel);
357 output.gamma = gamma;
358 output.edid = edid;
359 }
360}
361
362std::vector<mg::DisplayConfigurationOutput>::iterator
363mgm::RealKMSDisplayConfiguration::find_output_with_id(mg::DisplayConfigurationOutputId id)
364{
365 return std::find_if(outputs.begin(), outputs.end(),
366 [id](DisplayConfigurationOutput const& output)
367 {
368 return output.id == id;
369 });
370}
371
372std::vector<mg::DisplayConfigurationOutput>::const_iterator
373mgm::RealKMSDisplayConfiguration::find_output_with_id(mg::DisplayConfigurationOutputId id) const
374{
375 return std::find_if(outputs.begin(), outputs.end(),
376 [id](DisplayConfigurationOutput const& output)
377 {
378 return output.id == id;
379 });
380}179}
381180
382// Compatibility means conf1 can be attained from conf2 (and vice versa)181// Compatibility means conf1 can be attained from conf2 (and vice versa)
@@ -386,9 +185,9 @@
386// to be allocated/destroyed, and hence should not be considered compatible.185// to be allocated/destroyed, and hence should not be considered compatible.
387bool mgm::compatible(mgm::RealKMSDisplayConfiguration const& conf1, mgm::RealKMSDisplayConfiguration const& conf2)186bool mgm::compatible(mgm::RealKMSDisplayConfiguration const& conf1, mgm::RealKMSDisplayConfiguration const& conf2)
388{187{
389 bool compatible{(conf1.drm_fd == conf2.drm_fd) &&188 bool compatible{
390 (conf1.card == conf2.card) &&189 (conf1.card == conf2.card) &&
391 (conf1.outputs.size() == conf2.outputs.size())};190 (conf1.outputs.size() == conf2.outputs.size())};
392191
393 if (compatible)192 if (compatible)
394 {193 {
@@ -396,17 +195,17 @@
396195
397 for (unsigned int i = 0; i < count; ++i)196 for (unsigned int i = 0; i < count; ++i)
398 {197 {
399 compatible &= (conf1.outputs[i].power_mode == conf2.outputs[i].power_mode);198 compatible &= (conf1.outputs[i].first.power_mode == conf2.outputs[i].first.power_mode);
400 if (compatible)199 if (compatible)
401 {200 {
402 auto clone = conf2.outputs[i];201 auto clone = conf2.outputs[i].first;
403202
404 // ignore difference in orientation, scale factor, form factor, subpixel arrangement203 // ignore difference in orientation, scale factor, form factor, subpixel arrangement
405 clone.orientation = conf1.outputs[i].orientation;204 clone.orientation = conf1.outputs[i].first.orientation;
406 clone.subpixel_arrangement = conf1.outputs[i].subpixel_arrangement;205 clone.subpixel_arrangement = conf1.outputs[i].first.subpixel_arrangement;
407 clone.scale = conf1.outputs[i].scale;206 clone.scale = conf1.outputs[i].first.scale;
408 clone.form_factor = conf1.outputs[i].form_factor;207 clone.form_factor = conf1.outputs[i].first.form_factor;
409 compatible &= (conf1.outputs[i] == clone);208 compatible &= (conf1.outputs[i].first == clone);
410 }209 }
411 else210 else
412 break;211 break;
413212
=== modified file 'src/platforms/mesa/server/kms/real_kms_display_configuration.h'
--- src/platforms/mesa/server/kms/real_kms_display_configuration.h 2017-01-18 02:29:37 +0000
+++ src/platforms/mesa/server/kms/real_kms_display_configuration.h 2017-03-22 06:55:00 +0000
@@ -30,13 +30,15 @@
30{30{
31namespace mesa31namespace mesa
32{32{
33class KMSOutput;
34class KMSOutputContainer;
3335
34class RealKMSDisplayConfiguration : public KMSDisplayConfiguration36class RealKMSDisplayConfiguration : public KMSDisplayConfiguration
35{37{
36friend bool compatible(RealKMSDisplayConfiguration const& conf1, RealKMSDisplayConfiguration const& conf2);38friend bool compatible(RealKMSDisplayConfiguration const& conf1, RealKMSDisplayConfiguration const& conf2);
3739
38public:40public:
39 RealKMSDisplayConfiguration(int drm_fd);41 RealKMSDisplayConfiguration(std::shared_ptr<KMSOutputContainer> const& displays);
40 RealKMSDisplayConfiguration(RealKMSDisplayConfiguration const& conf);42 RealKMSDisplayConfiguration(RealKMSDisplayConfiguration const& conf);
41 RealKMSDisplayConfiguration& operator=(RealKMSDisplayConfiguration const& conf);43 RealKMSDisplayConfiguration& operator=(RealKMSDisplayConfiguration const& conf);
4244
@@ -45,18 +47,15 @@
45 void for_each_output(std::function<void(UserDisplayConfigurationOutput&)> f) override;47 void for_each_output(std::function<void(UserDisplayConfigurationOutput&)> f) override;
46 std::unique_ptr<DisplayConfiguration> clone() const override;48 std::unique_ptr<DisplayConfiguration> clone() const override;
4749
48 uint32_t get_kms_connector_id(DisplayConfigurationOutputId id) const override;50 std::shared_ptr<KMSOutput> get_output_for(DisplayConfigurationOutputId id) const override;
49 size_t get_kms_mode_index(DisplayConfigurationOutputId id, size_t conf_mode_index) const override;51 size_t get_kms_mode_index(DisplayConfigurationOutputId id, size_t conf_mode_index) const override;
50 void update() override;52 void update() override;
5153
52private:54private:
53 void add_or_update_output(kms::DRMModeResources const& resources, drmModeConnector const& connector);
54 std::vector<DisplayConfigurationOutput>::iterator find_output_with_id(DisplayConfigurationOutputId id);
55 std::vector<DisplayConfigurationOutput>::const_iterator find_output_with_id(DisplayConfigurationOutputId id) const;
5655
57 int drm_fd;56 std::shared_ptr<KMSOutputContainer> displays;
58 DisplayConfigurationCard card;57 DisplayConfigurationCard card;
59 std::vector<DisplayConfigurationOutput> outputs;58 std::vector<std::pair< DisplayConfigurationOutput, std::shared_ptr<KMSOutput>>> outputs;
60};59};
6160
62bool compatible(RealKMSDisplayConfiguration const& conf1, RealKMSDisplayConfiguration const& conf2);61bool compatible(RealKMSDisplayConfiguration const& conf1, RealKMSDisplayConfiguration const& conf2);
6362
=== modified file 'src/platforms/mesa/server/kms/real_kms_output.cpp'
--- src/platforms/mesa/server/kms/real_kms_output.cpp 2017-03-13 08:12:52 +0000
+++ src/platforms/mesa/server/kms/real_kms_output.cpp 2017-03-22 06:55:00 +0000
@@ -17,6 +17,7 @@
17 */17 */
1818
19#include "real_kms_output.h"19#include "real_kms_output.h"
20#include "mir/graphics/display_configuration.h"
20#include "page_flipper.h"21#include "page_flipper.h"
21#include "kms-utils/kms_connector.h"22#include "kms-utils/kms_connector.h"
22#include "mir/fatal.h"23#include "mir/fatal.h"
@@ -68,20 +69,27 @@
6869
69}70}
7071
71mgm::RealKMSOutput::RealKMSOutput(int drm_fd, uint32_t connector_id,72mgm::RealKMSOutput::RealKMSOutput(
72 std::shared_ptr<PageFlipper> const& page_flipper)73 int drm_fd,
73 : drm_fd{drm_fd}, connector_id{connector_id}, page_flipper{page_flipper},74 kms::DRMModeConnectorUPtr&& connector,
74 connector(), mode_index{0}, current_crtc(), saved_crtc(),75 std::shared_ptr<PageFlipper> const& page_flipper)
75 using_saved_crtc{true}, has_cursor_{false},76 : drm_fd_{drm_fd},
77 page_flipper{page_flipper},
78 connector{std::move(connector)},
79 mode_index{0},
80 current_crtc(),
81 saved_crtc(),
82 using_saved_crtc{true},
83 has_cursor_{false},
76 power_mode(mir_power_mode_on)84 power_mode(mir_power_mode_on)
77{85{
78 reset();86 reset();
7987
80 kms::DRMModeResources resources{drm_fd};88 kms::DRMModeResources resources{drm_fd_};
8189
82 if (connector->encoder_id)90 if (this->connector->encoder_id)
83 {91 {
84 auto encoder = resources.encoder(connector->encoder_id);92 auto encoder = resources.encoder(this->connector->encoder_id);
85 if (encoder->crtc_id)93 if (encoder->crtc_id)
86 {94 {
87 saved_crtc = *resources.crtc(encoder->crtc_id);95 saved_crtc = *resources.crtc(encoder->crtc_id);
@@ -94,14 +102,19 @@
94 restore_saved_crtc();102 restore_saved_crtc();
95}103}
96104
105uint32_t mgm::RealKMSOutput::id() const
106{
107 return connector->connector_id;
108}
109
97void mgm::RealKMSOutput::reset()110void mgm::RealKMSOutput::reset()
98{111{
99 kms::DRMModeResources resources{drm_fd};112 kms::DRMModeResources resources{drm_fd_};
100113
101 /* Update the connector to ensure we have the latest information */114 /* Update the connector to ensure we have the latest information */
102 try115 try
103 {116 {
104 connector = resources.connector(connector_id);117 connector = resources.connector(connector->connector_id);
105 }118 }
106 catch (std::exception const& e)119 catch (std::exception const& e)
107 {120 {
@@ -111,7 +124,7 @@
111 // TODO: What if we can't locate the DPMS property?124 // TODO: What if we can't locate the DPMS property?
112 for (int i = 0; i < connector->count_props; i++)125 for (int i = 0; i < connector->count_props; i++)
113 {126 {
114 auto prop = drmModeGetProperty(drm_fd, connector->props[i]);127 auto prop = drmModeGetProperty(drm_fd_, connector->props[i]);
115 if (prop && (prop->flags & DRM_MODE_PROP_ENUM)) {128 if (prop && (prop->flags & DRM_MODE_PROP_ENUM)) {
116 if (!strcmp(prop->name, "DPMS"))129 if (!strcmp(prop->name, "DPMS"))
117 {130 {
@@ -154,7 +167,7 @@
154 return false;167 return false;
155 }168 }
156169
157 auto ret = drmModeSetCrtc(drm_fd, current_crtc->crtc_id,170 auto ret = drmModeSetCrtc(drm_fd_, current_crtc->crtc_id,
158 fb.get_drm_fb_id(), fb_offset.dx.as_int(), fb_offset.dy.as_int(),171 fb.get_drm_fb_id(), fb_offset.dx.as_int(), fb_offset.dy.as_int(),
159 &connector->connector_id, 1,172 &connector->connector_id, 1,
160 &connector->modes[mode_index]);173 &connector->modes[mode_index]);
@@ -185,7 +198,7 @@
185 return;198 return;
186 }199 }
187200
188 auto result = drmModeSetCrtc(drm_fd, current_crtc->crtc_id,201 auto result = drmModeSetCrtc(drm_fd_, current_crtc->crtc_id,
189 0, 0, 0, nullptr, 0, nullptr);202 0, 0, 0, nullptr, 0, nullptr);
190 if (result)203 if (result)
191 {204 {
@@ -207,7 +220,10 @@
207 mgk::connector_name(connector).c_str());220 mgk::connector_name(connector).c_str());
208 return false;221 return false;
209 }222 }
210 return page_flipper->schedule_flip(current_crtc->crtc_id, fb.get_drm_fb_id(), connector_id);223 return page_flipper->schedule_flip(
224 current_crtc->crtc_id,
225 fb.get_drm_fb_id(),
226 connector->connector_id);
211}227}
212228
213void mgm::RealKMSOutput::wait_for_page_flip()229void mgm::RealKMSOutput::wait_for_page_flip()
@@ -236,7 +252,7 @@
236 {252 {
237 has_cursor_ = true;253 has_cursor_ = true;
238 result = drmModeSetCursor(254 result = drmModeSetCursor(
239 drm_fd,255 drm_fd_,
240 current_crtc->crtc_id,256 current_crtc->crtc_id,
241 gbm_bo_get_handle(buffer).u32,257 gbm_bo_get_handle(buffer).u32,
242 gbm_bo_get_width(buffer),258 gbm_bo_get_width(buffer),
@@ -255,7 +271,7 @@
255{271{
256 if (current_crtc)272 if (current_crtc)
257 {273 {
258 if (auto result = drmModeMoveCursor(drm_fd, current_crtc->crtc_id,274 if (auto result = drmModeMoveCursor(drm_fd_, current_crtc->crtc_id,
259 destination.x.as_uint32_t(),275 destination.x.as_uint32_t(),
260 destination.y.as_uint32_t()))276 destination.y.as_uint32_t()))
261 {277 {
@@ -270,7 +286,7 @@
270 int result = 0;286 int result = 0;
271 if (current_crtc)287 if (current_crtc)
272 {288 {
273 result = drmModeSetCursor(drm_fd, current_crtc->crtc_id, 0, 0, 0);289 result = drmModeSetCursor(drm_fd_, current_crtc->crtc_id, 0, 0, 0);
274290
275 if (result)291 if (result)
276 mir::log_warning("clear_cursor: drmModeSetCursor failed (%s)",292 mir::log_warning("clear_cursor: drmModeSetCursor failed (%s)",
@@ -296,7 +312,7 @@
296 if (connector->connection != DRM_MODE_CONNECTED)312 if (connector->connection != DRM_MODE_CONNECTED)
297 return false;313 return false;
298314
299 current_crtc = mgk::find_crtc_for_connector(drm_fd, connector);315 current_crtc = mgk::find_crtc_for_connector(drm_fd_, connector);
300316
301317
302 return (current_crtc != nullptr);318 return (current_crtc != nullptr);
@@ -306,7 +322,7 @@
306{322{
307 if (!using_saved_crtc)323 if (!using_saved_crtc)
308 {324 {
309 drmModeSetCrtc(drm_fd, saved_crtc.crtc_id, saved_crtc.buffer_id,325 drmModeSetCrtc(drm_fd_, saved_crtc.crtc_id, saved_crtc.buffer_id,
310 saved_crtc.x, saved_crtc.y,326 saved_crtc.x, saved_crtc.y,
311 &connector->connector_id, 1, &saved_crtc.mode);327 &connector->connector_id, 1, &saved_crtc.mode);
312328
@@ -321,8 +337,11 @@
321 if (power_mode != mode)337 if (power_mode != mode)
322 {338 {
323 power_mode = mode;339 power_mode = mode;
324 drmModeConnectorSetProperty(drm_fd, connector_id,340 drmModeConnectorSetProperty(
325 dpms_enum_id, mode);341 drm_fd_,
342 connector->connector_id,
343 dpms_enum_id,
344 mode);
326 }345 }
327}346}
328347
@@ -343,7 +362,7 @@
343 }362 }
344363
345 int ret = drmModeCrtcSetGamma(364 int ret = drmModeCrtcSetGamma(
346 drm_fd,365 drm_fd_,
347 current_crtc->crtc_id,366 current_crtc->crtc_id,
348 gamma.red.size(),367 gamma.red.size(),
349 const_cast<uint16_t*>(gamma.red.data()),368 const_cast<uint16_t*>(gamma.red.data()),
@@ -357,6 +376,218 @@
357 // TODO: return bool in future? Then do what with it?376 // TODO: return bool in future? Then do what with it?
358}377}
359378
379void mgm::RealKMSOutput::refresh_hardware_state()
380{
381 connector = kms::get_connector(drm_fd_, connector->connector_id);
382 current_crtc = nullptr;
383
384 if (connector->encoder_id)
385 {
386 auto encoder = kms::get_encoder(drm_fd_, connector->encoder_id);
387
388 if (encoder->crtc_id)
389 {
390 current_crtc = kms::get_crtc(drm_fd_, encoder->crtc_id);
391 }
392 }
393}
394
395 namespace
396{
397
398bool kms_modes_are_equal(drmModeModeInfo const& info1, drmModeModeInfo const& info2)
399{
400 return (info1.clock == info2.clock &&
401 info1.hdisplay == info2.hdisplay &&
402 info1.hsync_start == info2.hsync_start &&
403 info1.hsync_end == info2.hsync_end &&
404 info1.htotal == info2.htotal &&
405 info1.hskew == info2.hskew &&
406 info1.vdisplay == info2.vdisplay &&
407 info1.vsync_start == info2.vsync_start &&
408 info1.vsync_end == info2.vsync_end &&
409 info1.vtotal == info2.vtotal);
410}
411
412double calculate_vrefresh_hz(drmModeModeInfo const& mode)
413{
414 if (mode.htotal == 0 || mode.vtotal == 0)
415 return 0.0;
416
417 /* mode.clock is in KHz */
418 double hz = (mode.clock * 100000LL /
419 ((long)mode.htotal * (long)mode.vtotal)
420 ) / 100.0;
421
422 // Actually we don't need floating point at all for this...
423 // TODO: Consider converting our structs to fixed-point ints
424 return hz;
425}
426
427mg::DisplayConfigurationOutputType
428kms_connector_type_to_output_type(uint32_t connector_type)
429{
430 return static_cast<mg::DisplayConfigurationOutputType>(connector_type);
431}
432
433MirSubpixelArrangement kms_subpixel_to_mir_subpixel(uint32_t subpixel)
434{
435 switch (subpixel)
436 {
437 case DRM_MODE_SUBPIXEL_UNKNOWN:
438 return mir_subpixel_arrangement_unknown;
439 case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
440 return mir_subpixel_arrangement_horizontal_rgb;
441 case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
442 return mir_subpixel_arrangement_horizontal_bgr;
443 case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
444 return mir_subpixel_arrangement_vertical_rgb;
445 case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
446 return mir_subpixel_arrangement_vertical_bgr;
447 case DRM_MODE_SUBPIXEL_NONE:
448 return mir_subpixel_arrangement_none;
449 default:
450 return mir_subpixel_arrangement_unknown;
451 }
452}
453
454std::vector<uint8_t> edid_for_connector(int drm_fd, uint32_t connector_id)
455{
456 std::vector<uint8_t> edid;
457
458 mgk::ObjectProperties connector_props{
459 drm_fd, connector_id, DRM_MODE_OBJECT_CONNECTOR};
460
461 if (connector_props.has_property("EDID"))
462 {
463 /*
464 * We don't technically need the property information here, but query it
465 * anyway so we can detect if our assumptions about DRM behaviour
466 * become invalid.
467 */
468 auto property = mgk::DRMModePropertyUPtr{
469 drmModeGetProperty(drm_fd, connector_props.id_for("EDID")),
470 &drmModeFreeProperty};
471
472 if (!property)
473 {
474 mir::log_warning(
475 "Failed to get EDID property for connector %u: %i (%s)",
476 connector_id,
477 errno,
478 ::strerror(errno));
479 return edid;
480 }
481
482 if (!drm_property_type_is(property.get(), DRM_MODE_PROP_BLOB))
483 {
484 mir::log_warning(
485 "EDID property on connector %u has unexpected type %u",
486 connector_id,
487 property->flags);
488 return edid;
489 }
490
491 // A property ID of 0 means invalid.
492 if (connector_props["EDID"] == 0)
493 {
494 /*
495 * Log a debug message only. This will trigger for broken monitors which
496 * don't provide an EDID, which is not as unusual as you might think...
497 */
498 mir::log_debug("No EDID data available on connector %u", connector_id);
499 return edid;
500 }
501
502 auto blob = drmModeGetPropertyBlob(drm_fd, connector_props["EDID"]);
503
504 if (!blob)
505 {
506 mir::log_warning(
507 "Failed to get EDID property blob for connector %u: %i (%s)",
508 connector_id,
509 errno,
510 ::strerror(errno));
511
512 return edid;
513 }
514
515 edid.reserve(blob->length);
516 edid.insert(edid.begin(),
517 reinterpret_cast<uint8_t*>(blob->data),
518 reinterpret_cast<uint8_t*>(blob->data) + blob->length);
519
520 drmModeFreePropertyBlob(blob);
521
522 edid.shrink_to_fit();
523 }
524
525 return edid;
526}
527}
528
529void mgm::RealKMSOutput::update_from_hardware_state(
530 DisplayConfigurationOutput& output) const
531{
532 DisplayConfigurationOutputType const type{
533 kms_connector_type_to_output_type(connector->connector_type)};
534 geom::Size physical_size{connector->mmWidth, connector->mmHeight};
535 bool connected{connector->connection == DRM_MODE_CONNECTED};
536 uint32_t const invalid_mode_index = std::numeric_limits<uint32_t>::max();
537 uint32_t current_mode_index{invalid_mode_index};
538 uint32_t preferred_mode_index{invalid_mode_index};
539 std::vector<DisplayConfigurationMode> modes;
540 std::vector<MirPixelFormat> formats{mir_pixel_format_argb_8888,
541 mir_pixel_format_xrgb_8888};
542
543 std::vector<uint8_t> edid;
544 if (connected) {
545 /* Only ask for the EDID on connected outputs. There's obviously no monitor EDID
546 * when there is no monitor connected!
547 */
548 edid = edid_for_connector(drm_fd_, connector->connector_id);
549 }
550
551 drmModeModeInfo current_mode_info = drmModeModeInfo();
552 GammaCurves gamma;
553
554 /* Get information about the current mode */
555 if (current_crtc) {
556 current_mode_info = current_crtc->mode;
557
558 if (current_crtc->gamma_size > 0)
559 gamma = mg::LinearGammaLUTs(current_crtc->gamma_size);
560 }
561
562 /* Add all the available modes and find the current and preferred one */
563 for (int m = 0; m < connector->count_modes; m++) {
564 drmModeModeInfo &mode_info = connector->modes[m];
565
566 geom::Size size{mode_info.hdisplay, mode_info.vdisplay};
567
568 double vrefresh_hz = calculate_vrefresh_hz(mode_info);
569
570 modes.push_back({size, vrefresh_hz});
571
572 if (kms_modes_are_equal(mode_info, current_mode_info))
573 current_mode_index = m;
574
575 if ((mode_info.type & DRM_MODE_TYPE_PREFERRED) == DRM_MODE_TYPE_PREFERRED)
576 preferred_mode_index = m;
577 }
578
579 output.type = type;
580 output.modes = modes;
581 output.preferred_mode_index = preferred_mode_index;
582 output.physical_size_mm = physical_size;
583 output.connected = connected;
584 output.current_format = mir_pixel_format_xrgb_8888;
585 output.current_mode_index = current_mode_index;
586 output.subpixel_arrangement = kms_subpixel_to_mir_subpixel(connector->subpixel);
587 output.gamma = gamma;
588 output.edid = edid;
589}
590
360mgm::FBHandle* mgm::RealKMSOutput::fb_for(gbm_bo* bo, uint32_t width, uint32_t height) const591mgm::FBHandle* mgm::RealKMSOutput::fb_for(gbm_bo* bo, uint32_t width, uint32_t height) const
361{592{
362 if (!bo)593 if (!bo)
@@ -386,7 +617,7 @@
386 format = GBM_FORMAT_ARGB8888;617 format = GBM_FORMAT_ARGB8888;
387618
388 /* Create a KMS FB object with the gbm_bo attached to it. */619 /* Create a KMS FB object with the gbm_bo attached to it. */
389 auto ret = drmModeAddFB2(drm_fd, width, height, format,620 auto ret = drmModeAddFB2(drm_fd_, width, height, format,
390 handles, strides, offsets, &fb_id, 0);621 handles, strides, offsets, &fb_id, 0);
391 if (ret)622 if (ret)
392 return nullptr;623 return nullptr;
@@ -397,3 +628,20 @@
397628
398 return bufobj;629 return bufobj;
399}630}
631
632bool mgm::RealKMSOutput::buffer_requires_migration(gbm_bo* bo) const
633{
634 /*
635 * It's possible that some devices will not require migration -
636 * Intel GPUs can obviously scanout from main memory, as can USB outputs such as
637 * DisplayLink.
638 *
639 * For a first go, just say that *every* device scans out of GPU-private memory.
640 */
641 return gbm_device_get_fd(gbm_bo_get_device(bo)) != drm_fd_;
642}
643
644int mgm::RealKMSOutput::drm_fd() const
645{
646 return drm_fd_;
647}
400648
=== modified file 'src/platforms/mesa/server/kms/real_kms_output.h'
--- src/platforms/mesa/server/kms/real_kms_output.h 2017-03-13 08:12:52 +0000
+++ src/platforms/mesa/server/kms/real_kms_output.h 2017-03-22 06:55:00 +0000
@@ -38,10 +38,14 @@
38class RealKMSOutput : public KMSOutput38class RealKMSOutput : public KMSOutput
39{39{
40public:40public:
41 RealKMSOutput(int drm_fd, uint32_t connector_id,41 RealKMSOutput(
42 std::shared_ptr<PageFlipper> const& page_flipper);42 int drm_fd,
43 kms::DRMModeConnectorUPtr&& connector,
44 std::shared_ptr<PageFlipper> const& page_flipper);
43 ~RealKMSOutput();45 ~RealKMSOutput();
4446
47 uint32_t id() const override;
48
45 void reset() override;49 void reset() override;
46 void configure(geometry::Displacement fb_offset, size_t kms_mode_index) override;50 void configure(geometry::Displacement fb_offset, size_t kms_mode_index) override;
47 geometry::Size size() const override;51 geometry::Size size() const override;
@@ -62,14 +66,18 @@
6266
63 Frame last_frame() const override;67 Frame last_frame() const override;
6468
69 void refresh_hardware_state() override;
70 void update_from_hardware_state(DisplayConfigurationOutput& output) const override;
71
65 FBHandle* fb_for(gbm_bo* bo, uint32_t width, uint32_t height) const override;72 FBHandle* fb_for(gbm_bo* bo, uint32_t width, uint32_t height) const override;
6673
74 bool buffer_requires_migration(gbm_bo* bo) const override;
75 int drm_fd() const override;
67private:76private:
68 bool ensure_crtc();77 bool ensure_crtc();
69 void restore_saved_crtc();78 void restore_saved_crtc();
7079
71 int const drm_fd;80 int const drm_fd_;
72 uint32_t const connector_id;
73 std::shared_ptr<PageFlipper> const page_flipper;81 std::shared_ptr<PageFlipper> const page_flipper;
7482
75 kms::DRMModeConnectorUPtr connector;83 kms::DRMModeConnectorUPtr connector;
7684
=== modified file 'src/platforms/mesa/server/kms/real_kms_output_container.cpp'
--- src/platforms/mesa/server/kms/real_kms_output_container.cpp 2016-01-29 08:18:22 +0000
+++ src/platforms/mesa/server/kms/real_kms_output_container.cpp 2017-03-22 06:55:00 +0000
@@ -16,39 +16,68 @@
16 * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>16 * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
17 */17 */
1818
19#include <algorithm>
19#include "real_kms_output_container.h"20#include "real_kms_output_container.h"
20#include "real_kms_output.h"21#include "real_kms_output.h"
22#include "kms-utils/drm_mode_resources.h"
2123
22namespace mgm = mir::graphics::mesa;24namespace mgm = mir::graphics::mesa;
2325
24mgm::RealKMSOutputContainer::RealKMSOutputContainer(26mgm::RealKMSOutputContainer::RealKMSOutputContainer(
25 int drm_fd, std::shared_ptr<PageFlipper> const& page_flipper)27 std::vector<int> const& drm_fds,
26 : drm_fd{drm_fd},28 std::function<std::shared_ptr<PageFlipper>(int)> const& construct_page_flipper)
27 page_flipper{page_flipper}29 : drm_fds{drm_fds},
28{30 construct_page_flipper{construct_page_flipper}
29}31{
3032}
31std::shared_ptr<mgm::KMSOutput>33
32mgm::RealKMSOutputContainer::get_kms_output_for(uint32_t connector_id)34void mgm::RealKMSOutputContainer::for_each_output(std::function<void(std::shared_ptr<KMSOutput> const&)> functor) const
33{35{
34 std::shared_ptr<KMSOutput> output;36 for(auto& output: outputs)
3537 functor(output);
36 auto output_iter = outputs.find(connector_id);38}
37 if (output_iter == outputs.end())39
38 {40void mgm::RealKMSOutputContainer::update_from_hardware_state()
39 output = std::make_shared<RealKMSOutput>(drm_fd, connector_id, page_flipper);41{
40 outputs[connector_id] = output;42 decltype(outputs) new_outputs;
41 }43
42 else44 for (auto drm_fd : drm_fds)
43 {45 {
44 output = output_iter->second;46 kms::DRMModeResources resources{drm_fd};
45 }47
4648
47 return output;49 for (auto &&connector : resources.connectors())
48}50 {
4951 // Caution: O(n²) here, but n is the number of outputs, so should
50void mgm::RealKMSOutputContainer::for_each_output(std::function<void(KMSOutput&)> functor) const52 // conservatively be << 100.
51{53 auto existing_output = std::find_if(
52 for(auto& pair: outputs)54 outputs.begin(),
53 functor(*pair.second);55 outputs.end(),
56 [&connector, drm_fd](auto const &candidate)
57 {
58 return
59 connector->connector_id == candidate->id() &&
60 drm_fd == candidate->drm_fd();
61 });
62
63 if (existing_output != outputs.end())
64 {
65 // We could drop this down to O(n) by being smarter about moving out
66 // of the outputs vector.
67 //
68 // That's a bit of a faff, so just do the simple thing for now.
69 new_outputs.push_back(*existing_output);
70 new_outputs.back()->refresh_hardware_state();
71 }
72 else
73 {
74 new_outputs.push_back(std::make_shared<RealKMSOutput>(
75 drm_fd,
76 std::move(connector),
77 construct_page_flipper(drm_fd)));
78 }
79 }
80
81 }
82 outputs = new_outputs;
54}83}
5584
=== modified file 'src/platforms/mesa/server/kms/real_kms_output_container.h'
--- src/platforms/mesa/server/kms/real_kms_output_container.h 2016-01-29 08:18:22 +0000
+++ src/platforms/mesa/server/kms/real_kms_output_container.h 2017-03-22 06:55:00 +0000
@@ -20,7 +20,7 @@
20#define MIR_GRAPHICS_MESA_REAL_KMS_OUTPUT_CONTAINER_H_20#define MIR_GRAPHICS_MESA_REAL_KMS_OUTPUT_CONTAINER_H_
2121
22#include "kms_output_container.h"22#include "kms_output_container.h"
23#include <unordered_map>23#include <vector>
2424
25namespace mir25namespace mir
26{26{
@@ -34,15 +34,17 @@
34class RealKMSOutputContainer : public KMSOutputContainer34class RealKMSOutputContainer : public KMSOutputContainer
35{35{
36public:36public:
37 RealKMSOutputContainer(int drm_fd, std::shared_ptr<PageFlipper> const& page_flipper);37 RealKMSOutputContainer(
3838 std::vector<int> const& drm_fds,
39 std::shared_ptr<KMSOutput> get_kms_output_for(uint32_t connector_id);39 std::function<std::shared_ptr<PageFlipper>(int drm_fd)> const& construct_page_flipper);
40 void for_each_output(std::function<void(KMSOutput&)> functor) const;40
4141 void for_each_output(std::function<void(std::shared_ptr<KMSOutput> const&)> functor) const override;
42
43 void update_from_hardware_state() override;
42private:44private:
43 int const drm_fd;45 std::vector<int> const drm_fds;
44 std::unordered_map<uint32_t,std::shared_ptr<KMSOutput>> outputs;46 std::vector<std::shared_ptr<KMSOutput>> outputs;
45 std::shared_ptr<PageFlipper> const page_flipper;47 std::function<std::shared_ptr<PageFlipper>(int drm_fd)> const construct_page_flipper;
46};48};
4749
48}50}
4951
=== modified file 'tests/acceptance-tests/test_client_input.cpp'
--- tests/acceptance-tests/test_client_input.cpp 2017-03-14 02:26:28 +0000
+++ tests/acceptance-tests/test_client_input.cpp 2017-03-22 06:55:00 +0000
@@ -1578,7 +1578,7 @@
15781578
1579 mt::Signal touchscreen_ready;1579 mt::Signal touchscreen_ready;
1580 fake_touch_screen->on_new_configuration_do(1580 fake_touch_screen->on_new_configuration_do(
1581 [&touchscreen_ready, second_output](mi::InputDevice const& dev)1581 [&touchscreen_ready](mi::InputDevice const& dev)
1582 {1582 {
1583 auto ts = dev.get_touchscreen_settings();1583 auto ts = dev.get_touchscreen_settings();
1584 if (ts.is_set() && ts.value().output_id == second_output)1584 if (ts.is_set() && ts.value().output_id == second_output)
@@ -1650,7 +1650,7 @@
16501650
1651 mt::Signal touchscreen_ready;1651 mt::Signal touchscreen_ready;
1652 fake_touch_screen->on_new_configuration_do(1652 fake_touch_screen->on_new_configuration_do(
1653 [&touchscreen_ready, second_output](mi::InputDevice const& dev)1653 [&touchscreen_ready](mi::InputDevice const& dev)
1654 {1654 {
1655 auto ts = dev.get_touchscreen_settings();1655 auto ts = dev.get_touchscreen_settings();
1656 if (ts.is_set()1656 if (ts.is_set()
16571657
=== modified file 'tests/include/mir/test/doubles/mock_drm.h'
--- tests/include/mir/test/doubles/mock_drm.h 2017-01-18 02:29:37 +0000
+++ tests/include/mir/test/doubles/mock_drm.h 2017-03-22 06:55:00 +0000
@@ -23,6 +23,7 @@
2323
24#include <xf86drm.h>24#include <xf86drm.h>
25#include <xf86drmMode.h>25#include <xf86drmMode.h>
26#include <unordered_map>
2627
27namespace mir28namespace mir
28{29{
@@ -154,12 +155,42 @@
154 MOCK_METHOD6(drmModeCrtcSetGamma, int(int fd, uint32_t crtc_id, uint32_t size,155 MOCK_METHOD6(drmModeCrtcSetGamma, int(int fd, uint32_t crtc_id, uint32_t size,
155 uint16_t* red, uint16_t* green, uint16_t* blue));156 uint16_t* red, uint16_t* green, uint16_t* blue));
156157
157 FakeDRMResources fake_drm;158
159 void add_crtc(
160 char const* device,
161 uint32_t id,
162 drmModeModeInfo mode);
163 void add_encoder(
164 char const* device,
165 uint32_t encoder_id,
166 uint32_t crtc_id,
167 uint32_t possible_crtcs_mask);
168 void add_connector(
169 char const* device,
170 uint32_t connector_id,
171 uint32_t type,
172 drmModeConnection connection,
173 uint32_t encoder_id,
174 std::vector<drmModeModeInfo>& modes,
175 std::vector<uint32_t>& possible_encoder_ids,
176 geometry::Size const& physical_size,
177 drmModeSubPixel subpixel_arrangement = DRM_MODE_SUBPIXEL_UNKNOWN);
178
179 void prepare(char const* device);
180 void reset(char const* device);
181
182 void generate_event_on(char const* device);
183
184 class IsFdOfDeviceMatcher;
185 friend class IsFdOfDeviceMatcher;
158186
159private:187private:
188 std::unordered_map<std::string, FakeDRMResources> fake_drms;
189 std::unordered_map<int, FakeDRMResources&> fd_to_drm;
160 drmModeObjectProperties empty_object_props;190 drmModeObjectProperties empty_object_props;
161};191};
162192
193testing::Matcher<int> IsFdOfDevice(char const* device);
163}194}
164}195}
165}196}
166197
=== modified file 'tests/include/mir/test/doubles/mock_gbm.h'
--- tests/include/mir/test/doubles/mock_gbm.h 2015-07-16 07:03:19 +0000
+++ tests/include/mir/test/doubles/mock_gbm.h 2017-03-22 06:55:00 +0000
@@ -80,6 +80,7 @@
80 MOCK_METHOD3(gbm_bo_write, bool(struct gbm_bo *bo, const void *buf, size_t count));80 MOCK_METHOD3(gbm_bo_write, bool(struct gbm_bo *bo, const void *buf, size_t count));
81 MOCK_METHOD1(gbm_bo_destroy, void(struct gbm_bo *bo));81 MOCK_METHOD1(gbm_bo_destroy, void(struct gbm_bo *bo));
82 MOCK_METHOD4(gbm_bo_import, struct gbm_bo*(struct gbm_device*, uint32_t, void*, uint32_t));82 MOCK_METHOD4(gbm_bo_import, struct gbm_bo*(struct gbm_device*, uint32_t, void*, uint32_t));
83 MOCK_METHOD1(gbm_bo_get_fd, int(gbm_bo*));
8384
84 FakeGBMResources fake_gbm;85 FakeGBMResources fake_gbm;
8586
8687
=== modified file 'tests/include/mir/test/doubles/mock_gl.h'
--- tests/include/mir/test/doubles/mock_gl.h 2016-06-30 13:41:11 +0000
+++ tests/include/mir/test/doubles/mock_gl.h 2017-03-22 06:55:00 +0000
@@ -110,6 +110,7 @@
110 const GLvoid *));110 const GLvoid *));
111 MOCK_METHOD4(glViewport, void(GLint, GLint, GLsizei, GLsizei));111 MOCK_METHOD4(glViewport, void(GLint, GLint, GLsizei, GLsizei));
112 MOCK_METHOD1(glGenerateMipmap, void(GLenum target));112 MOCK_METHOD1(glGenerateMipmap, void(GLenum target));
113 MOCK_METHOD4(glDrawElements, void(GLenum, GLsizei, GLenum, const GLvoid*));
113};114};
114115
115}116}
116117
=== modified file 'tests/mir_test_doubles/mock_drm.cpp'
--- tests/mir_test_doubles/mock_drm.cpp 2017-01-18 02:29:37 +0000
+++ tests/mir_test_doubles/mock_drm.cpp 2017-03-22 06:55:00 +0000
@@ -24,6 +24,8 @@
24#include <stdexcept>24#include <stdexcept>
25#include <unistd.h>25#include <unistd.h>
26#include <dlfcn.h>26#include <dlfcn.h>
27#include <system_error>
28#include <boost/throw_exception.hpp>
2729
28namespace mtd=mir::test::doubles;30namespace mtd=mir::test::doubles;
29namespace geom = mir::geometry;31namespace geom = mir::geometry;
@@ -239,25 +241,60 @@
239 memset(&empty_object_props, 0, sizeof(empty_object_props));241 memset(&empty_object_props, 0, sizeof(empty_object_props));
240242
241 ON_CALL(*this, open(_,_,_))243 ON_CALL(*this, open(_,_,_))
242 .WillByDefault(Return(fake_drm.fd()));244 .WillByDefault(
245 WithArg<0>(
246 Invoke(
247 [this](char const* device_path)
248 {
249 auto fd = fake_drms[device_path].fd();
250 fd_to_drm.insert({fd, fake_drms[device_path]});
251 return fd;
252 })));
243253
244 ON_CALL(*this, drmOpen(_,_))254 ON_CALL(*this, drmOpen(_,_))
245 .WillByDefault(Return(fake_drm.fd()));255 .WillByDefault(
256 InvokeWithoutArgs(
257 [this]()
258 {
259 auto fd = fake_drms["/dev/dri/card0"].fd();
260 fd_to_drm.insert({fd, fake_drms["/dev/dri/card0"]});
261 return fd;
262 }));
246263
247 ON_CALL(*this, drmClose(_))264 ON_CALL(*this, drmClose(_))
248 .WillByDefault(WithArg<0>(Invoke([&](int fd){ return close(fd); })));265 .WillByDefault(Invoke([](int fd){ return close(fd); }));
249266
250 ON_CALL(*this, drmModeGetResources(_))267 ON_CALL(*this, drmModeGetResources(_))
251 .WillByDefault(Return(fake_drm.resources_ptr()));268 .WillByDefault(
269 Invoke(
270 [this](int fd)
271 {
272 return fd_to_drm.at(fd).resources_ptr();
273 }));
252274
253 ON_CALL(*this, drmModeGetCrtc(_, _))275 ON_CALL(*this, drmModeGetCrtc(_, _))
254 .WillByDefault(WithArgs<1>(Invoke(&fake_drm, &FakeDRMResources::find_crtc)));276 .WillByDefault(
277 Invoke(
278 [this](int fd, uint32_t crtc_id)
279 {
280 return fd_to_drm.at(fd).find_crtc(crtc_id);
281 }));
255282
256 ON_CALL(*this, drmModeGetEncoder(_, _))283 ON_CALL(*this, drmModeGetEncoder(_, _))
257 .WillByDefault(WithArgs<1>(Invoke(&fake_drm, &FakeDRMResources::find_encoder)));284 .WillByDefault(
285 Invoke(
286 [this](int fd, uint32_t encoder_id)
287 {
288 return fd_to_drm.at(fd).find_encoder(encoder_id);
289 }));
258290
259 ON_CALL(*this, drmModeGetConnector(_, _))291 ON_CALL(*this, drmModeGetConnector(_, _))
260 .WillByDefault(WithArgs<1>(Invoke(&fake_drm, &FakeDRMResources::find_connector)));292 .WillByDefault(
293 Invoke(
294 [this](int fd, uint32_t connector_id)
295 {
296 return fd_to_drm.at(fd).find_connector(connector_id);
297 }));
261298
262 ON_CALL(*this, drmModeObjectGetProperties(_, _, _))299 ON_CALL(*this, drmModeObjectGetProperties(_, _, _))
263 .WillByDefault(Return(&empty_object_props));300 .WillByDefault(Return(&empty_object_props));
@@ -277,6 +314,118 @@
277 global_mock = nullptr;314 global_mock = nullptr;
278}315}
279316
317void mtd::MockDRM::add_crtc(char const *device, uint32_t id, drmModeModeInfo mode)
318{
319 fake_drms[device].add_crtc(id, mode);
320}
321
322void mtd::MockDRM::add_encoder(
323 char const *device,
324 uint32_t encoder_id,
325 uint32_t crtc_id,
326 uint32_t possible_crtcs_mask)
327{
328 fake_drms[device].add_encoder(encoder_id, crtc_id, possible_crtcs_mask);
329}
330
331void mtd::MockDRM::prepare(char const *device)
332{
333 fake_drms[device].prepare();
334}
335
336void mtd::MockDRM::reset(char const *device)
337{
338 fake_drms[device].reset();
339}
340
341void mtd::MockDRM::generate_event_on(char const *device)
342{
343 auto const fd = fake_drms[device].write_fd();
344
345 if (write(fd, "a", 1) != 1)
346 {
347 BOOST_THROW_EXCEPTION(
348 std::system_error(errno, std::system_category(), "Failed to make fake DRM event"));
349 }
350}
351
352void mtd::MockDRM::add_connector(
353 char const *device,
354 uint32_t connector_id,
355 uint32_t type,
356 drmModeConnection connection,
357 uint32_t encoder_id,
358 std::vector<drmModeModeInfo> &modes,
359 std::vector<uint32_t> &possible_encoder_ids,
360 geometry::Size const &physical_size,
361 drmModeSubPixel subpixel_arrangement)
362{
363 fake_drms[device].add_connector(
364 connector_id,
365 type,
366 connection,
367 encoder_id,
368 modes,
369 possible_encoder_ids,
370 physical_size,
371 subpixel_arrangement);
372}
373
374MATCHER_P2(IsFdOfDevice, devname, fds, "")
375{
376 return std::find(
377 fds.begin(),
378 fds.end(),
379 arg) != fds.end();
380}
381
382class mtd::MockDRM::IsFdOfDeviceMatcher : public ::testing::MatcherInterface<int>
383{
384public:
385 IsFdOfDeviceMatcher(char const* device)
386 : device{device}
387 {
388 }
389
390 void DescribeTo(::std::ostream *os) const override
391 {
392
393 *os
394 << "Is an fd of DRM device "
395 << device
396 << " (one of: "
397 << ::testing::PrintToString(fds_for_device(device.c_str()))
398 << ")";
399 }
400
401 bool MatchAndExplain(int x, testing::MatchResultListener *listener) const override
402 {
403 testing::Matcher<std::vector<int>> matcher = testing::Contains(x);
404 return matcher.MatchAndExplain(fds_for_device(device.c_str()), listener);
405 }
406
407private:
408 std::vector<int> fds_for_device(char const* device) const
409 {
410 std::vector<int> device_fds;
411 for (auto const& pair : global_mock->fd_to_drm)
412 {
413 if (&global_mock->fake_drms[device] == &pair.second)
414 {
415 device_fds.push_back(pair.first);
416 }
417 }
418 return device_fds;
419 }
420
421 std::string const device;
422};
423
424testing::Matcher<int> mtd::IsFdOfDevice(char const* device)
425{
426 return ::testing::MakeMatcher(new mtd::MockDRM::IsFdOfDeviceMatcher(device));
427}
428
280int drmOpen(const char *name, const char *busid)429int drmOpen(const char *name, const char *busid)
281{430{
282 return global_mock->drmOpen(name, busid);431 return global_mock->drmOpen(name, busid);
283432
=== modified file 'tests/mir_test_doubles/mock_egl.cpp'
--- tests/mir_test_doubles/mock_egl.cpp 2017-01-18 02:29:37 +0000
+++ tests/mir_test_doubles/mock_egl.cpp 2017-03-22 06:55:00 +0000
@@ -156,7 +156,7 @@
156{156{
157 using namespace testing;157 using namespace testing;
158158
159 const char* egl_exts = "EGL_KHR_image EGL_KHR_image_base EGL_KHR_image_pixmap";159 const char* egl_exts = "EGL_KHR_image EGL_KHR_image_base EGL_KHR_image_pixmap EGL_EXT_image_dma_buf_import";
160 ON_CALL(*this, eglQueryString(_,EGL_EXTENSIONS))160 ON_CALL(*this, eglQueryString(_,EGL_EXTENSIONS))
161 .WillByDefault(Return(egl_exts));161 .WillByDefault(Return(egl_exts));
162}162}
163163
=== modified file 'tests/mir_test_doubles/mock_gbm.cpp'
--- tests/mir_test_doubles/mock_gbm.cpp 2015-07-16 07:03:19 +0000
+++ tests/mir_test_doubles/mock_gbm.cpp 2017-03-22 06:55:00 +0000
@@ -182,3 +182,8 @@
182{182{
183 return global_mock->gbm_bo_import(device, type, data, flags);183 return global_mock->gbm_bo_import(device, type, data, flags);
184}184}
185
186int gbm_bo_get_fd(gbm_bo* bo)
187{
188 return global_mock->gbm_bo_get_fd(bo);
189}
185190
=== modified file 'tests/mir_test_doubles/mock_gl.cpp'
--- tests/mir_test_doubles/mock_gl.cpp 2016-06-30 13:41:11 +0000
+++ tests/mir_test_doubles/mock_gl.cpp 2017-03-22 06:55:00 +0000
@@ -20,6 +20,8 @@
20#include "mir/test/doubles/mock_gl.h"20#include "mir/test/doubles/mock_gl.h"
21#include <gtest/gtest.h>21#include <gtest/gtest.h>
2222
23#include <GLES2/gl2.h>
24
23#include <cstring>25#include <cstring>
2426
25namespace mtd = mir::test::doubles;27namespace mtd = mir::test::doubles;
@@ -462,3 +464,9 @@
462 CHECK_GLOBAL_VOID_MOCK();464 CHECK_GLOBAL_VOID_MOCK();
463 global_mock_gl->glPixelStorei(pname, param);465 global_mock_gl->glPixelStorei(pname, param);
464}466}
467
468void glDrawElements(GLenum mode, GLsizei count, GLenum type, const void* indicies)
469{
470 CHECK_GLOBAL_VOID_MOCK();
471 global_mock_gl->glDrawElements(mode, count, type, indicies);
472}
465473
=== modified file 'tests/unit-tests/platforms/mesa/kms-utils/test_connector_utils.cpp'
--- tests/unit-tests/platforms/mesa/kms-utils/test_connector_utils.cpp 2016-05-20 02:15:41 +0000
+++ tests/unit-tests/platforms/mesa/kms-utils/test_connector_utils.cpp 2017-03-22 06:55:00 +0000
@@ -25,6 +25,7 @@
2525
26#include <gtest/gtest.h>26#include <gtest/gtest.h>
27#include <gmock/gmock.h>27#include <gmock/gmock.h>
28#include <fcntl.h>
2829
29namespace mtd = mir::test::doubles;30namespace mtd = mir::test::doubles;
30namespace mgk = mir::graphics::kms;31namespace mgk = mir::graphics::kms;
@@ -35,30 +36,37 @@
35{36{
36 using namespace testing;37 using namespace testing;
37 NiceMock<mtd::MockDRM> drm;38 NiceMock<mtd::MockDRM> drm;
3839 char const* const drm_device = "/dev/dri/card0";
39 auto& resources = drm.fake_drm;
4040
41 std::array<uint32_t, 3> const crtc_ids = {{21, 25, 30}};41 std::array<uint32_t, 3> const crtc_ids = {{21, 25, 30}};
42 std::array<uint32_t, 3> const encoder_ids = {{3, 5, 7}};42 std::array<uint32_t, 3> const encoder_ids = {{3, 5, 7}};
43 std::array<uint32_t, 3> const connector_id = {{9, 10, 11}};43 std::array<uint32_t, 3> const connector_id = {{9, 10, 11}};
4444
45 resources.reset();45 drm.reset(drm_device);
46 // Add a bunch of CRTCs46 // Add a bunch of CRTCs
47 for (auto id : crtc_ids)47 for (auto id : crtc_ids)
48 {48 {
49 resources.add_crtc(id, drmModeModeInfo());49 drm.add_crtc(drm_device, id, drmModeModeInfo());
50 }50 }
51 // Add an encoder that can only drive any CRTC...51 // Add an encoder that can only drive any CRTC...
52 resources.add_encoder(encoder_ids[0], 0, 1 << 0 | 1 << 1 | 1 << 2);52 drm.add_encoder(drm_device, encoder_ids[0], 0, 1 << 0 | 1 << 1 | 1 << 2);
53 // ...and one that can only drive the third...53 // ...and one that can only drive the third...
54 resources.add_encoder(encoder_ids[1], 0, 1 << 2);54 drm.add_encoder(drm_device, encoder_ids[1], 0, 1 << 2);
55 // ...and one that can only drive the second two...55 // ...and one that can only drive the second two...
56 resources.add_encoder(encoder_ids[2], 0, 1 << 1 | 1 << 2);56 drm.add_encoder(drm_device, encoder_ids[2], 0, 1 << 1 | 1 << 2);
5757
58 std::vector<drmModeModeInfo> modes{resources.create_mode(1200, 1600, 138500, 1400, 1800, mtd::FakeDRMResources::ModePreference::PreferredMode)};58 std::vector<drmModeModeInfo> modes{
59 mtd::FakeDRMResources::create_mode(
60 1200,
61 1600,
62 138500,
63 1400,
64 1800,
65 mtd::FakeDRMResources::ModePreference::PreferredMode)};
59 // Finally, add a connector that can only be driven by any encoder...66 // Finally, add a connector that can only be driven by any encoder...
60 std::vector<uint32_t> any_encoder{encoder_ids.begin(), encoder_ids.end()};67 std::vector<uint32_t> any_encoder{encoder_ids.begin(), encoder_ids.end()};
61 resources.add_connector(68 drm.add_connector(
69 drm_device,
62 connector_id[0],70 connector_id[0],
63 DRM_MODE_CONNECTOR_VGA,71 DRM_MODE_CONNECTOR_VGA,
64 DRM_MODE_CONNECTED,72 DRM_MODE_CONNECTED,
@@ -69,7 +77,8 @@
6977
70 // ...then one that can only be driven by the third...78 // ...then one that can only be driven by the third...
71 std::vector<uint32_t> third_encoder{encoder_ids[2]};79 std::vector<uint32_t> third_encoder{encoder_ids[2]};
72 resources.add_connector(80 drm.add_connector(
81 drm_device,
73 connector_id[1],82 connector_id[1],
74 DRM_MODE_CONNECTOR_VGA,83 DRM_MODE_CONNECTOR_VGA,
75 DRM_MODE_CONNECTED,84 DRM_MODE_CONNECTED,
@@ -80,7 +89,8 @@
8089
81 // ...and finally one that can only be driven by the second...90 // ...and finally one that can only be driven by the second...
82 std::vector<uint32_t> second_encoder{encoder_ids[1]};91 std::vector<uint32_t> second_encoder{encoder_ids[1]};
83 resources.add_connector(92 drm.add_connector(
93 drm_device,
84 connector_id[2],94 connector_id[2],
85 DRM_MODE_CONNECTOR_VGA,95 DRM_MODE_CONNECTOR_VGA,
86 DRM_MODE_CONNECTED,96 DRM_MODE_CONNECTED,
@@ -89,21 +99,23 @@
89 second_encoder,99 second_encoder,
90 mir::geometry::Size{300, 200});100 mir::geometry::Size{300, 200});
91101
92 resources.prepare();102 drm.prepare(drm_device);
103
104 auto const drm_fd = open(drm_device, 0, 0);
93105
94 auto crtc = mgk::find_crtc_for_connector(106 auto crtc = mgk::find_crtc_for_connector(
95 resources.fd(),107 drm_fd,
96 mgk::get_connector(resources.fd(), connector_id[0]));108 mgk::get_connector(drm_fd, connector_id[0]));
97 EXPECT_THAT(crtc->crtc_id, AnyOf(crtc_ids[0], crtc_ids[1], crtc_ids[2]));109 EXPECT_THAT(crtc->crtc_id, AnyOf(crtc_ids[0], crtc_ids[1], crtc_ids[2]));
98110
99 crtc = mgk::find_crtc_for_connector(111 crtc = mgk::find_crtc_for_connector(
100 resources.fd(),112 drm_fd,
101 mgk::get_connector(resources.fd(), connector_id[1]));113 mgk::get_connector(drm_fd, connector_id[1]));
102 EXPECT_THAT(crtc->crtc_id, AnyOf(crtc_ids[1], crtc_ids[2]));114 EXPECT_THAT(crtc->crtc_id, AnyOf(crtc_ids[1], crtc_ids[2]));
103115
104 crtc = mgk::find_crtc_for_connector(116 crtc = mgk::find_crtc_for_connector(
105 resources.fd(),117 drm_fd,
106 mgk::get_connector(resources.fd(), connector_id[2]));118 mgk::get_connector(drm_fd, connector_id[2]));
107 EXPECT_THAT(crtc->crtc_id, Eq(crtc_ids[2]));119 EXPECT_THAT(crtc->crtc_id, Eq(crtc_ids[2]));
108}120}
109121
@@ -111,30 +123,38 @@
111{123{
112 using namespace testing;124 using namespace testing;
113 NiceMock<mtd::MockDRM> drm;125 NiceMock<mtd::MockDRM> drm;
114126 char const* const drm_device = "/dev/dri/card0";
115 auto& resources = drm.fake_drm;127 int const drm_fd = open(drm_device, 0, 0);
116128
117 std::array<uint32_t, 2> const crtc_ids = {{21, 25}};129 std::array<uint32_t, 2> const crtc_ids = {{21, 25}};
118 std::array<uint32_t, 2> const encoder_ids = {{3, 5}};130 std::array<uint32_t, 2> const encoder_ids = {{3, 5}};
119 std::array<uint32_t, 2> const connector_id = {{9, 10}};131 std::array<uint32_t, 2> const connector_id = {{9, 10}};
120132
121 resources.reset();133 drm.reset(drm_device);
122 auto boring_mode = resources.create_mode(1200, 1600, 138500, 1400, 1800, mtd::FakeDRMResources::ModePreference::PreferredMode);134 auto boring_mode =
135 mtd::FakeDRMResources::create_mode(
136 1200,
137 1600,
138 138500,
139 1400,
140 1800,
141 mtd::FakeDRMResources::ModePreference::PreferredMode);
123 // Add an active CRTC...142 // Add an active CRTC...
124 resources.add_crtc(crtc_ids[0], boring_mode);143 drm.add_crtc(drm_device, crtc_ids[0], boring_mode);
125 // ...and an inactive one.144 // ...and an inactive one.
126 resources.add_crtc(crtc_ids[1], drmModeModeInfo());145 drm.add_crtc(drm_device, crtc_ids[1], drmModeModeInfo());
127146
128 // Add an encoder hooked up to the active CRTC147 // Add an encoder hooked up to the active CRTC
129 resources.add_encoder(encoder_ids[0], crtc_ids[0], 1 << 0 | 1 << 1);148 drm.add_encoder(drm_device, encoder_ids[0], crtc_ids[0], 1 << 0 | 1 << 1);
130 // ...and one not connected to anything149 // ...and one not connected to anything
131 resources.add_encoder(encoder_ids[1], 0, 1 << 0 | 1 << 1);150 drm.add_encoder(drm_device, encoder_ids[1], 0, 1 << 0 | 1 << 1);
132151
133 std::vector<drmModeModeInfo> modes{boring_mode};152 std::vector<drmModeModeInfo> modes{boring_mode};
134 std::vector<uint32_t> any_encoder{encoder_ids.begin(), encoder_ids.end()};153 std::vector<uint32_t> any_encoder{encoder_ids.begin(), encoder_ids.end()};
135154
136 // Finally, a connector hooked up to a CRTC-encoder155 // Finally, a connector hooked up to a CRTC-encoder
137 resources.add_connector(156 drm.add_connector(
157 drm_device,
138 connector_id[0],158 connector_id[0],
139 DRM_MODE_CONNECTOR_VGA,159 DRM_MODE_CONNECTOR_VGA,
140 DRM_MODE_CONNECTED,160 DRM_MODE_CONNECTED,
@@ -144,7 +164,8 @@
144 mir::geometry::Size{300, 200});164 mir::geometry::Size{300, 200});
145165
146 // ... and one not hooked up to anything166 // ... and one not hooked up to anything
147 resources.add_connector(167 drm.add_connector(
168 drm_device,
148 connector_id[1],169 connector_id[1],
149 DRM_MODE_CONNECTOR_VGA,170 DRM_MODE_CONNECTOR_VGA,
150 DRM_MODE_CONNECTED,171 DRM_MODE_CONNECTED,
@@ -153,11 +174,11 @@
153 any_encoder,174 any_encoder,
154 mir::geometry::Size{300, 200});175 mir::geometry::Size{300, 200});
155176
156 resources.prepare();177 drm.prepare(drm_device);
157178
158 auto crtc = mgk::find_crtc_for_connector(179 auto crtc = mgk::find_crtc_for_connector(
159 resources.fd(),180 drm_fd,
160 mgk::get_connector(resources.fd(), connector_id[1]));181 mgk::get_connector(drm_fd, connector_id[1]));
161 EXPECT_THAT(crtc->crtc_id, Eq(crtc_ids[1]));182 EXPECT_THAT(crtc->crtc_id, Eq(crtc_ids[1]));
162}183}
163184
@@ -165,30 +186,39 @@
165{186{
166 using namespace testing;187 using namespace testing;
167 NiceMock<mtd::MockDRM> drm;188 NiceMock<mtd::MockDRM> drm;
189 char const* const drm_device = "/dev/dri/card0";
190 int const drm_fd = open(drm_device, 0, 0);
168191
169 auto& resources = drm.fake_drm;
170192
171 std::array<uint32_t, 2> const crtc_ids = {{21, 25}};193 std::array<uint32_t, 2> const crtc_ids = {{21, 25}};
172 std::array<uint32_t, 2> const encoder_ids = {{3, 5}};194 std::array<uint32_t, 2> const encoder_ids = {{3, 5}};
173 std::array<uint32_t, 2> const connector_id = {{9, 10}};195 std::array<uint32_t, 2> const connector_id = {{9, 10}};
174196
175 resources.reset();197 drm.reset(drm_device);
176 auto boring_mode = resources.create_mode(1200, 1600, 138500, 1400, 1800, mtd::FakeDRMResources::ModePreference::PreferredMode);198 auto boring_mode =
199 mtd::FakeDRMResources::create_mode(
200 1200,
201 1600,
202 138500,
203 1400,
204 1800,
205 mtd::FakeDRMResources::ModePreference::PreferredMode);
177 // Add an active CRTC...206 // Add an active CRTC...
178 resources.add_crtc(crtc_ids[0], boring_mode);207 drm.add_crtc(drm_device, crtc_ids[0], boring_mode);
179 // ...and an inactive one.208 // ...and an inactive one.
180 resources.add_crtc(crtc_ids[1], drmModeModeInfo());209 drm.add_crtc(drm_device, crtc_ids[1], drmModeModeInfo());
181210
182 // Add an encoder hooked up to the active CRTC211 // Add an encoder hooked up to the active CRTC
183 resources.add_encoder(encoder_ids[0], crtc_ids[0], 1 << 0 | 1 << 1);212 drm.add_encoder(drm_device, encoder_ids[0], crtc_ids[0], 1 << 0 | 1 << 1);
184 // ...and one not connected to anything213 // ...and one not connected to anything
185 resources.add_encoder(encoder_ids[1], 0, 1 << 0 | 1 << 1);214 drm.add_encoder(drm_device, encoder_ids[1], 0, 1 << 0 | 1 << 1);
186215
187 std::vector<drmModeModeInfo> modes{boring_mode};216 std::vector<drmModeModeInfo> modes{boring_mode};
188 std::vector<uint32_t> any_encoder{encoder_ids.begin(), encoder_ids.end()};217 std::vector<uint32_t> any_encoder{encoder_ids.begin(), encoder_ids.end()};
189218
190 // Finally, a connector hooked up to a CRTC-encoder219 // Finally, a connector hooked up to a CRTC-encoder
191 resources.add_connector(220 drm.add_connector(
221 drm_device,
192 connector_id[0],222 connector_id[0],
193 DRM_MODE_CONNECTOR_VGA,223 DRM_MODE_CONNECTOR_VGA,
194 DRM_MODE_CONNECTED,224 DRM_MODE_CONNECTED,
@@ -198,7 +228,8 @@
198 mir::geometry::Size{300, 200});228 mir::geometry::Size{300, 200});
199229
200 // ... and one not hooked up to anything230 // ... and one not hooked up to anything
201 resources.add_connector(231 drm.add_connector(
232 drm_device,
202 connector_id[1],233 connector_id[1],
203 DRM_MODE_CONNECTOR_VGA,234 DRM_MODE_CONNECTOR_VGA,
204 DRM_MODE_CONNECTED,235 DRM_MODE_CONNECTED,
@@ -207,10 +238,10 @@
207 any_encoder,238 any_encoder,
208 mir::geometry::Size{300, 200});239 mir::geometry::Size{300, 200});
209240
210 resources.prepare();241 drm.prepare(drm_device);
211242
212 auto crtc = mgk::find_crtc_for_connector(243 auto crtc = mgk::find_crtc_for_connector(
213 resources.fd(),244 drm_fd,
214 mgk::get_connector(resources.fd(), connector_id[0]));245 mgk::get_connector(drm_fd, connector_id[0]));
215 EXPECT_THAT(crtc->crtc_id, Eq(crtc_ids[0]));246 EXPECT_THAT(crtc->crtc_id, Eq(crtc_ids[0]));
216}247}
217248
=== modified file 'tests/unit-tests/platforms/mesa/kms/mock_kms_output.h'
--- tests/unit-tests/platforms/mesa/kms/mock_kms_output.h 2017-03-13 08:12:52 +0000
+++ tests/unit-tests/platforms/mesa/kms/mock_kms_output.h 2017-03-22 06:55:00 +0000
@@ -38,6 +38,7 @@
3838
39struct MockKMSOutput : public graphics::mesa::KMSOutput39struct MockKMSOutput : public graphics::mesa::KMSOutput
40{40{
41 MOCK_CONST_METHOD0(id, uint32_t());
41 MOCK_METHOD0(reset, void());42 MOCK_METHOD0(reset, void());
42 MOCK_METHOD2(configure, void(geometry::Displacement, size_t));43 MOCK_METHOD2(configure, void(geometry::Displacement, size_t));
43 MOCK_CONST_METHOD0(size, geometry::Size());44 MOCK_CONST_METHOD0(size, geometry::Size());
@@ -68,7 +69,12 @@
68 MOCK_METHOD1(set_power_mode, void(MirPowerMode));69 MOCK_METHOD1(set_power_mode, void(MirPowerMode));
69 MOCK_METHOD1(set_gamma, void(mir::graphics::GammaCurves const&));70 MOCK_METHOD1(set_gamma, void(mir::graphics::GammaCurves const&));
7071
72 MOCK_METHOD0(refresh_hardware_state, void());
73 MOCK_CONST_METHOD1(update_from_hardware_state, void(graphics::DisplayConfigurationOutput&));
74
71 MOCK_CONST_METHOD3(fb_for, graphics::mesa::FBHandle*(gbm_bo*, uint32_t, uint32_t));75 MOCK_CONST_METHOD3(fb_for, graphics::mesa::FBHandle*(gbm_bo*, uint32_t, uint32_t));
76 MOCK_CONST_METHOD1(buffer_requires_migration, bool(gbm_bo*));
77 MOCK_CONST_METHOD0(drm_fd, int());
72};78};
7379
74} // namespace test80} // namespace test
7581
=== modified file 'tests/unit-tests/platforms/mesa/kms/test_cursor.cpp'
--- tests/unit-tests/platforms/mesa/kms/test_cursor.cpp 2017-02-15 07:38:33 +0000
+++ tests/unit-tests/platforms/mesa/kms/test_cursor.cpp 2017-03-22 06:55:00 +0000
@@ -56,14 +56,14 @@
56{56{
57 StubKMSOutputContainer()57 StubKMSOutputContainer()
58 : outputs{58 : outputs{
59 {10, std::make_shared<testing::NiceMock<MockKMSOutput>>()},59 std::make_shared<testing::NiceMock<MockKMSOutput>>(),
60 {11, std::make_shared<testing::NiceMock<MockKMSOutput>>()},60 std::make_shared<testing::NiceMock<MockKMSOutput>>(),
61 {12, std::make_shared<testing::NiceMock<MockKMSOutput>>()}}61 std::make_shared<testing::NiceMock<MockKMSOutput>>()}
62 {62 {
63 // These need to be established before Cursor construction:63 // These need to be established before Cursor construction:
64 for (auto& entry : outputs)64 for (auto& output : outputs)
65 {65 {
66 auto& out = *entry.second;66 auto& out = *output;
67 ON_CALL(out, has_cursor())67 ON_CALL(out, has_cursor())
68 .WillByDefault(Return(true));68 .WillByDefault(Return(true));
69 ON_CALL(out, set_cursor(_))69 ON_CALL(out, set_cursor(_))
@@ -73,33 +73,33 @@
73 }73 }
74 }74 }
7575
76 std::shared_ptr<mgm::KMSOutput> get_kms_output_for(uint32_t connector_id)76 void for_each_output(std::function<void(std::shared_ptr<mgm::KMSOutput> const&)> functor) const
77 {77 {
78 return outputs[connector_id];78 for (auto const& output : outputs)
79 }79 functor(output);
80
81 void for_each_output(std::function<void(mgm::KMSOutput&)> functor) const
82 {
83 for (auto const& pair : outputs)
84 functor(*pair.second);
85 }80 }
8681
87 void verify_and_clear_expectations()82 void verify_and_clear_expectations()
88 {83 {
89 for (auto const& pair : outputs)84 for (auto const& output : outputs)
90 ::testing::Mock::VerifyAndClearExpectations(pair.second.get());85 ::testing::Mock::VerifyAndClearExpectations(output.get());
91 }86 }
9287
93 std::unordered_map<uint32_t,std::shared_ptr<testing::NiceMock<MockKMSOutput>>> outputs;88 void update_from_hardware_state()
89 {
90 }
91
92 std::vector<std::shared_ptr<testing::NiceMock<MockKMSOutput>>> outputs;
94};93};
9594
96struct StubKMSDisplayConfiguration : public mgm::KMSDisplayConfiguration95struct StubKMSDisplayConfiguration : public mgm::KMSDisplayConfiguration
97{96{
98 StubKMSDisplayConfiguration()97 StubKMSDisplayConfiguration(mgm::KMSOutputContainer& container)
99 : mgm::KMSDisplayConfiguration(),98 : mgm::KMSDisplayConfiguration(),
99 container{container},
100 stub_config{100 stub_config{
101 {{101 {{
102 mg::DisplayConfigurationOutputId{10},102 mg::DisplayConfigurationOutputId{0},
103 mg::DisplayConfigurationCardId{},103 mg::DisplayConfigurationCardId{},
104 mg::DisplayConfigurationOutputType::vga,104 mg::DisplayConfigurationOutputType::vga,
105 {},105 {},
@@ -124,7 +124,7 @@
124 {}124 {}
125 },125 },
126 {126 {
127 mg::DisplayConfigurationOutputId{11},127 mg::DisplayConfigurationOutputId{1},
128 mg::DisplayConfigurationCardId{},128 mg::DisplayConfigurationCardId{},
129 mg::DisplayConfigurationOutputType::vga,129 mg::DisplayConfigurationOutputType::vga,
130 {},130 {},
@@ -149,7 +149,7 @@
149 {}149 {}
150 },150 },
151 {151 {
152 mg::DisplayConfigurationOutputId{12},152 mg::DisplayConfigurationOutputId{2},
153 mg::DisplayConfigurationCardId{},153 mg::DisplayConfigurationCardId{},
154 mg::DisplayConfigurationOutputType::vga,154 mg::DisplayConfigurationOutputType::vga,
155 {},155 {},
@@ -174,6 +174,7 @@
174 {}174 {}
175 }}}175 }}}
176 {176 {
177 update();
177 }178 }
178179
179 void for_each_card(std::function<void(mg::DisplayConfigurationCard const&)> f) const override180 void for_each_card(std::function<void(mg::DisplayConfigurationCard const&)> f) const override
@@ -201,9 +202,9 @@
201 return stub_config.valid();202 return stub_config.valid();
202 }203 }
203204
204 uint32_t get_kms_connector_id(mg::DisplayConfigurationOutputId id) const override205 std::shared_ptr<mgm::KMSOutput> get_output_for(mg::DisplayConfigurationOutputId id) const override
205 {206 {
206 return id.as_value();207 return outputs[id.as_value()];
207 }208 }
208209
209 size_t get_kms_mode_index(mg::DisplayConfigurationOutputId, size_t conf_mode_index) const override210 size_t get_kms_mode_index(mg::DisplayConfigurationOutputId, size_t conf_mode_index) const override
@@ -213,6 +214,11 @@
213214
214 void update() override215 void update() override
215 {216 {
217 container.for_each_output(
218 [this](auto const& output)
219 {
220 outputs.push_back(output);
221 });
216 }222 }
217223
218 void set_orentation_of_output(mg::DisplayConfigurationOutputId id, MirOrientation orientation)224 void set_orentation_of_output(mg::DisplayConfigurationOutputId id, MirOrientation orientation)
@@ -225,11 +231,18 @@
225 });231 });
226 }232 }
227233
234 mgm::KMSOutputContainer& container;
228 mtd::StubDisplayConfig stub_config;235 mtd::StubDisplayConfig stub_config;
236 std::vector<std::shared_ptr<mgm::KMSOutput>> outputs;
229};237};
230238
231struct StubCurrentConfiguration : public mgm::CurrentConfiguration239struct StubCurrentConfiguration : public mgm::CurrentConfiguration
232{240{
241 StubCurrentConfiguration(mgm::KMSOutputContainer& container)
242 : conf(container)
243 {
244 }
245
233 void with_current_configuration_do(246 void with_current_configuration_do(
234 std::function<void(mgm::KMSDisplayConfiguration const&)> const& exec)247 std::function<void(mgm::KMSDisplayConfiguration const&)> const& exec)
235 {248 {
@@ -284,7 +297,7 @@
284297
285 size_t const cursor_side{64};298 size_t const cursor_side{64};
286 MesaCursorTest()299 MesaCursorTest()
287 : cursor{mock_gbm.fake_gbm.device, output_container,300 : cursor{output_container,
288 mt::fake_shared(current_configuration)}301 mt::fake_shared(current_configuration)}
289 {302 {
290 using namespace ::testing;303 using namespace ::testing;
@@ -304,9 +317,9 @@
304 }317 }
305318
306 testing::NiceMock<mtd::MockDRM> mock_drm;319 testing::NiceMock<mtd::MockDRM> mock_drm;
307 StubCurrentConfiguration current_configuration;
308 StubCursorImage stub_image;320 StubCursorImage stub_image;
309 StubKMSOutputContainer output_container;321 StubKMSOutputContainer output_container;
322 StubCurrentConfiguration current_configuration{output_container};
310 mgm::Cursor cursor;323 mgm::Cursor cursor;
311};324};
312325
@@ -335,19 +348,20 @@
335 GBM_FORMAT_ARGB8888,348 GBM_FORMAT_ARGB8888,
336 GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE));349 GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE));
337350
338 mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container,351 mgm::Cursor cursor_tmp{output_container,
339 std::make_shared<StubCurrentConfiguration>()};352 std::make_shared<StubCurrentConfiguration>(output_container)};
340}353}
341354
342TEST_F(MesaCursorTest, queries_received_cursor_size)355TEST_F(MesaCursorTest, queries_received_cursor_size)
343{356{
344 using namespace ::testing;357 using namespace ::testing;
345358
346 EXPECT_CALL(mock_gbm, gbm_bo_get_width(_));359 // Our standard mock DRM has 2 GPU devices, and we should construct a cursor on both.
347 EXPECT_CALL(mock_gbm, gbm_bo_get_height(_));360 EXPECT_CALL(mock_gbm, gbm_bo_get_width(_)).Times(2);
361 EXPECT_CALL(mock_gbm, gbm_bo_get_height(_)).Times(2);
348362
349 mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container,363 mgm::Cursor cursor_tmp{output_container,
350 std::make_shared<StubCurrentConfiguration>()};364 std::make_shared<StubCurrentConfiguration>(output_container)};
351}365}
352366
353TEST_F(MesaCursorTest, respects_drm_cap_cursor)367TEST_F(MesaCursorTest, respects_drm_cap_cursor)
@@ -361,8 +375,8 @@
361375
362 EXPECT_CALL(mock_gbm, gbm_bo_create(_, drm_buffer_size, drm_buffer_size, _, _));376 EXPECT_CALL(mock_gbm, gbm_bo_create(_, drm_buffer_size, drm_buffer_size, _, _));
363377
364 mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container,378 mgm::Cursor cursor_tmp{output_container,
365 std::make_shared<StubCurrentConfiguration>()};379 std::make_shared<StubCurrentConfiguration>(output_container)};
366}380}
367381
368TEST_F(MesaCursorTest, can_force_64x64_cursor)382TEST_F(MesaCursorTest, can_force_64x64_cursor)
@@ -378,8 +392,8 @@
378392
379 EXPECT_CALL(mock_gbm, gbm_bo_create(_, 64, 64, _, _));393 EXPECT_CALL(mock_gbm, gbm_bo_create(_, 64, 64, _, _));
380394
381 mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container,395 mgm::Cursor cursor_tmp{output_container,
382 std::make_shared<StubCurrentConfiguration>()};396 std::make_shared<StubCurrentConfiguration>(output_container)};
383}397}
384398
385TEST_F(MesaCursorTest, show_cursor_writes_to_bo)399TEST_F(MesaCursorTest, show_cursor_writes_to_bo)
@@ -442,8 +456,8 @@
442456
443 EXPECT_CALL(mock_gbm, gbm_bo_write(mock_gbm.fake_gbm.bo, ContainsASingleWhitePixel(width*height), buffer_size_bytes));457 EXPECT_CALL(mock_gbm, gbm_bo_write(mock_gbm.fake_gbm.bo, ContainsASingleWhitePixel(width*height), buffer_size_bytes));
444458
445 mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container,459 mgm::Cursor cursor_tmp{output_container,
446 std::make_shared<StubCurrentConfiguration>()};460 std::make_shared<StubCurrentConfiguration>(output_container)};
447 cursor_tmp.show(SinglePixelCursorImage());461 cursor_tmp.show(SinglePixelCursorImage());
448}462}
449463
@@ -470,17 +484,17 @@
470{484{
471 using namespace testing;485 using namespace testing;
472486
473 EXPECT_CALL(*output_container.outputs[10], clear_cursor());487 EXPECT_CALL(*output_container.outputs[0], clear_cursor());
474 EXPECT_CALL(*output_container.outputs[11], clear_cursor());488 EXPECT_CALL(*output_container.outputs[1], clear_cursor());
475 EXPECT_CALL(*output_container.outputs[12], clear_cursor());489 EXPECT_CALL(*output_container.outputs[2], clear_cursor());
476490
477 /* No checking of existing cursor state */491 /* No checking of existing cursor state */
478 EXPECT_CALL(*output_container.outputs[10], has_cursor()).Times(0);492 EXPECT_CALL(*output_container.outputs[0], has_cursor()).Times(0);
479 EXPECT_CALL(*output_container.outputs[11], has_cursor()).Times(0);493 EXPECT_CALL(*output_container.outputs[1], has_cursor()).Times(0);
480 EXPECT_CALL(*output_container.outputs[12], has_cursor()).Times(0);494 EXPECT_CALL(*output_container.outputs[2], has_cursor()).Times(0);
481495
482 mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container,496 mgm::Cursor cursor_tmp{output_container,
483 std::make_shared<StubCurrentConfiguration>()};497 std::make_shared<StubCurrentConfiguration>(output_container)};
484498
485 output_container.verify_and_clear_expectations();499 output_container.verify_and_clear_expectations();
486}500}
@@ -489,16 +503,16 @@
489{503{
490 using namespace testing;504 using namespace testing;
491505
492 ON_CALL(*output_container.outputs[10], clear_cursor())506 ON_CALL(*output_container.outputs[0], clear_cursor())
493 .WillByDefault(Return(false));507 .WillByDefault(Return(false));
494 ON_CALL(*output_container.outputs[10], set_cursor(_))508 ON_CALL(*output_container.outputs[0], set_cursor(_))
495 .WillByDefault(Return(false));509 .WillByDefault(Return(false));
496 ON_CALL(*output_container.outputs[10], has_cursor())510 ON_CALL(*output_container.outputs[0], has_cursor())
497 .WillByDefault(Return(false));511 .WillByDefault(Return(false));
498512
499 EXPECT_THROW(513 EXPECT_THROW(
500 mgm::Cursor cursor_tmp(mock_gbm.fake_gbm.device, output_container,514 mgm::Cursor cursor_tmp(output_container,
501 std::make_shared<StubCurrentConfiguration>());515 std::make_shared<StubCurrentConfiguration>(output_container));
502 , std::runtime_error);516 , std::runtime_error);
503}517}
504518
@@ -508,14 +522,14 @@
508522
509 cursor.show(stub_image);523 cursor.show(stub_image);
510524
511 EXPECT_CALL(*output_container.outputs[10], has_cursor())525 EXPECT_CALL(*output_container.outputs[0], has_cursor())
512 .WillOnce(Return(false))526 .WillOnce(Return(false))
513 .WillOnce(Return(true));527 .WillOnce(Return(true));
514 EXPECT_CALL(*output_container.outputs[10], set_cursor(_));528 EXPECT_CALL(*output_container.outputs[0], set_cursor(_));
515529
516 EXPECT_CALL(*output_container.outputs[11], has_cursor())530 EXPECT_CALL(*output_container.outputs[1], has_cursor())
517 .WillOnce(Return(true));531 .WillOnce(Return(true));
518 EXPECT_CALL(*output_container.outputs[11], clear_cursor());532 EXPECT_CALL(*output_container.outputs[1], clear_cursor());
519533
520 cursor.move_to({10, 10});534 cursor.move_to({10, 10});
521535
@@ -528,14 +542,14 @@
528542
529 cursor.show(stub_image);543 cursor.show(stub_image);
530544
531 EXPECT_CALL(*output_container.outputs[10], has_cursor())545 EXPECT_CALL(*output_container.outputs[0], has_cursor())
532 .WillOnce(Return(true));546 .WillOnce(Return(true));
533 EXPECT_CALL(*output_container.outputs[10], set_cursor(_))547 EXPECT_CALL(*output_container.outputs[0], set_cursor(_))
534 .Times(0);548 .Times(0);
535549
536 EXPECT_CALL(*output_container.outputs[11], has_cursor())550 EXPECT_CALL(*output_container.outputs[1], has_cursor())
537 .WillOnce(Return(false));551 .WillOnce(Return(false));
538 EXPECT_CALL(*output_container.outputs[11], clear_cursor())552 EXPECT_CALL(*output_container.outputs[1], clear_cursor())
539 .Times(0);553 .Times(0);
540554
541 cursor.move_to({10, 10});555 cursor.move_to({10, 10});
@@ -549,32 +563,32 @@
549563
550 cursor.show(stub_image);564 cursor.show(stub_image);
551565
552 EXPECT_CALL(*output_container.outputs[10], move_cursor(geom::Point{10,10}));566 EXPECT_CALL(*output_container.outputs[0], move_cursor(geom::Point{10,10}));
553 EXPECT_CALL(*output_container.outputs[11], move_cursor(_))567 EXPECT_CALL(*output_container.outputs[1], move_cursor(_))
554 .Times(0);568 .Times(0);
555569
556 cursor.move_to({10, 10});570 cursor.move_to({10, 10});
557571
558 output_container.verify_and_clear_expectations();572 output_container.verify_and_clear_expectations();
559573
560 EXPECT_CALL(*output_container.outputs[10], move_cursor(_))574 EXPECT_CALL(*output_container.outputs[0], move_cursor(_))
561 .Times(0);575 .Times(0);
562 EXPECT_CALL(*output_container.outputs[11], move_cursor(geom::Point{50,100}));576 EXPECT_CALL(*output_container.outputs[1], move_cursor(geom::Point{50,100}));
563577
564 cursor.move_to({150, 150});578 cursor.move_to({150, 150});
565579
566 output_container.verify_and_clear_expectations();580 output_container.verify_and_clear_expectations();
567581
568 EXPECT_CALL(*output_container.outputs[10], move_cursor(geom::Point{150,75}));582 EXPECT_CALL(*output_container.outputs[0], move_cursor(geom::Point{150,75}));
569 EXPECT_CALL(*output_container.outputs[11], move_cursor(geom::Point{50,25}));583 EXPECT_CALL(*output_container.outputs[1], move_cursor(geom::Point{50,25}));
570584
571 cursor.move_to({150, 75});585 cursor.move_to({150, 75});
572586
573 output_container.verify_and_clear_expectations();587 output_container.verify_and_clear_expectations();
574588
575 EXPECT_CALL(*output_container.outputs[10], move_cursor(_))589 EXPECT_CALL(*output_container.outputs[0], move_cursor(_))
576 .Times(0);590 .Times(0);
577 EXPECT_CALL(*output_container.outputs[11], move_cursor(_))591 EXPECT_CALL(*output_container.outputs[1], move_cursor(_))
578 .Times(0);592 .Times(0);
579593
580 cursor.move_to({-1, -1});594 cursor.move_to({-1, -1});
@@ -588,10 +602,10 @@
588602
589 cursor.show(stub_image);603 cursor.show(stub_image);
590604
591 current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{12}, mir_orientation_left);605 current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{2}, mir_orientation_left);
592606
593 EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{112,100}));607 EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{112,100}));
594 EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{150,96}));608 EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{150,96}));
595609
596 cursor.move_to({766, 112});610 cursor.move_to({766, 112});
597 cursor.move_to({770, 150});611 cursor.move_to({770, 150});
@@ -606,11 +620,11 @@
606620
607 cursor.show(stub_image);621 cursor.show(stub_image);
608622
609 current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{12}, mir_orientation_right);623 current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{2}, mir_orientation_right);
610624
611625
612 EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{688,100}));626 EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{688,100}));
613 EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{650,104}));627 EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{650,104}));
614628
615 cursor.move_to({766, 112});629 cursor.move_to({766, 112});
616 cursor.move_to({770, 150});630 cursor.move_to({770, 150});
@@ -624,10 +638,10 @@
624638
625 cursor.show(stub_image);639 cursor.show(stub_image);
626640
627 current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{12}, mir_orientation_inverted);641 current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{2}, mir_orientation_inverted);
628642
629 EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{700,88}));643 EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{700,88}));
630 EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{696,50}));644 EXPECT_CALL(*output_container.outputs[2], move_cursor(geom::Point{696,50}));
631645
632 cursor.move_to({766, 112});646 cursor.move_to({766, 112});
633 cursor.move_to({770, 150});647 cursor.move_to({770, 150});
@@ -639,9 +653,9 @@
639{653{
640 using namespace testing;654 using namespace testing;
641655
642 EXPECT_CALL(*output_container.outputs[10], clear_cursor());656 EXPECT_CALL(*output_container.outputs[0], clear_cursor());
643 EXPECT_CALL(*output_container.outputs[11], clear_cursor());657 EXPECT_CALL(*output_container.outputs[1], clear_cursor());
644 EXPECT_CALL(*output_container.outputs[12], clear_cursor());658 EXPECT_CALL(*output_container.outputs[2], clear_cursor());
645659
646 cursor.hide();660 cursor.hide();
647661
@@ -652,12 +666,12 @@
652{666{
653 using namespace testing;667 using namespace testing;
654668
655 EXPECT_CALL(*output_container.outputs[10], clear_cursor());669 EXPECT_CALL(*output_container.outputs[0], clear_cursor());
656 EXPECT_CALL(*output_container.outputs[11], clear_cursor());670 EXPECT_CALL(*output_container.outputs[1], clear_cursor());
657 EXPECT_CALL(*output_container.outputs[12], clear_cursor());671 EXPECT_CALL(*output_container.outputs[2], clear_cursor());
658 EXPECT_CALL(*output_container.outputs[10], move_cursor(_)).Times(0);672 EXPECT_CALL(*output_container.outputs[0], move_cursor(_)).Times(0);
659 EXPECT_CALL(*output_container.outputs[11], move_cursor(_)).Times(0);673 EXPECT_CALL(*output_container.outputs[1], move_cursor(_)).Times(0);
660 EXPECT_CALL(*output_container.outputs[12], move_cursor(_)).Times(0);674 EXPECT_CALL(*output_container.outputs[2], move_cursor(_)).Times(0);
661675
662 cursor.hide();676 cursor.hide();
663 cursor.move_to({17, 29});677 cursor.move_to({17, 29});
@@ -669,9 +683,9 @@
669{683{
670 using namespace testing;684 using namespace testing;
671685
672 EXPECT_CALL(*output_container.outputs[10], clear_cursor());686 EXPECT_CALL(*output_container.outputs[0], clear_cursor());
673 EXPECT_CALL(*output_container.outputs[11], clear_cursor());687 EXPECT_CALL(*output_container.outputs[1], clear_cursor());
674 EXPECT_CALL(*output_container.outputs[12], clear_cursor());688 EXPECT_CALL(*output_container.outputs[2], clear_cursor());
675}689}
676690
677TEST_F(MesaCursorTest, cursor_is_shown_at_correct_location_after_suspend_resume)691TEST_F(MesaCursorTest, cursor_is_shown_at_correct_location_after_suspend_resume)
@@ -680,23 +694,23 @@
680694
681 cursor.show(stub_image);695 cursor.show(stub_image);
682696
683 EXPECT_CALL(*output_container.outputs[10], move_cursor(geom::Point{150,75}));697 EXPECT_CALL(*output_container.outputs[0], move_cursor(geom::Point{150,75}));
684 EXPECT_CALL(*output_container.outputs[11], move_cursor(geom::Point{50,25}));698 EXPECT_CALL(*output_container.outputs[1], move_cursor(geom::Point{50,25}));
685 EXPECT_CALL(*output_container.outputs[10], clear_cursor());699 EXPECT_CALL(*output_container.outputs[0], clear_cursor());
686 EXPECT_CALL(*output_container.outputs[11], clear_cursor());700 EXPECT_CALL(*output_container.outputs[1], clear_cursor());
687 EXPECT_CALL(*output_container.outputs[12], has_cursor())701 EXPECT_CALL(*output_container.outputs[2], has_cursor())
688 .WillRepeatedly(Return(false));702 .WillRepeatedly(Return(false));
689 EXPECT_CALL(*output_container.outputs[12], clear_cursor());703 EXPECT_CALL(*output_container.outputs[2], clear_cursor());
690704
691 cursor.move_to({150, 75});705 cursor.move_to({150, 75});
692 cursor.suspend();706 cursor.suspend();
693707
694 output_container.verify_and_clear_expectations();708 output_container.verify_and_clear_expectations();
695709
696 EXPECT_CALL(*output_container.outputs[10], set_cursor(_));710 EXPECT_CALL(*output_container.outputs[0], set_cursor(_));
697 EXPECT_CALL(*output_container.outputs[11], set_cursor(_));711 EXPECT_CALL(*output_container.outputs[1], set_cursor(_));
698 EXPECT_CALL(*output_container.outputs[10], move_cursor(geom::Point{150,75}));712 EXPECT_CALL(*output_container.outputs[0], move_cursor(geom::Point{150,75}));
699 EXPECT_CALL(*output_container.outputs[11], move_cursor(geom::Point{50,25}));713 EXPECT_CALL(*output_container.outputs[1], move_cursor(geom::Point{50,25}));
700714
701 cursor.resume();715 cursor.resume();
702 output_container.verify_and_clear_expectations();716 output_container.verify_and_clear_expectations();
@@ -706,18 +720,18 @@
706{720{
707 using namespace testing;721 using namespace testing;
708722
709 EXPECT_CALL(*output_container.outputs[10], clear_cursor()).Times(AnyNumber());723 EXPECT_CALL(*output_container.outputs[0], clear_cursor()).Times(AnyNumber());
710 EXPECT_CALL(*output_container.outputs[11], clear_cursor()).Times(AnyNumber());724 EXPECT_CALL(*output_container.outputs[1], clear_cursor()).Times(AnyNumber());
711 EXPECT_CALL(*output_container.outputs[12], clear_cursor()).Times(AnyNumber());725 EXPECT_CALL(*output_container.outputs[2], clear_cursor()).Times(AnyNumber());
712726
713 cursor.hide();727 cursor.hide();
714 cursor.suspend();728 cursor.suspend();
715729
716 output_container.verify_and_clear_expectations();730 output_container.verify_and_clear_expectations();
717731
718 EXPECT_CALL(*output_container.outputs[10], set_cursor(_)).Times(0);732 EXPECT_CALL(*output_container.outputs[0], set_cursor(_)).Times(0);
719 EXPECT_CALL(*output_container.outputs[11], set_cursor(_)).Times(0);733 EXPECT_CALL(*output_container.outputs[1], set_cursor(_)).Times(0);
720 EXPECT_CALL(*output_container.outputs[12], set_cursor(_)).Times(0);734 EXPECT_CALL(*output_container.outputs[2], set_cursor(_)).Times(0);
721735
722 cursor.resume();736 cursor.resume();
723 output_container.verify_and_clear_expectations();737 output_container.verify_and_clear_expectations();
@@ -725,24 +739,24 @@
725739
726TEST_F(MesaCursorTest, show_with_param_places_cursor_on_output)740TEST_F(MesaCursorTest, show_with_param_places_cursor_on_output)
727{741{
728 EXPECT_CALL(*output_container.outputs[10], clear_cursor());742 EXPECT_CALL(*output_container.outputs[0], clear_cursor());
729 cursor.hide();743 cursor.hide();
730744
731 output_container.verify_and_clear_expectations();745 output_container.verify_and_clear_expectations();
732746
733 EXPECT_CALL(*output_container.outputs[10], set_cursor(_));747 EXPECT_CALL(*output_container.outputs[0], set_cursor(_));
734 cursor.show(stub_image);748 cursor.show(stub_image);
735}749}
736750
737TEST_F(MesaCursorTest, show_without_param_places_cursor_on_output_output)751TEST_F(MesaCursorTest, show_without_param_places_cursor_on_output_output)
738{752{
739 using namespace testing;753 using namespace testing;
740 EXPECT_CALL(*output_container.outputs[10], clear_cursor());754 EXPECT_CALL(*output_container.outputs[0], clear_cursor());
741755
742 cursor.hide();756 cursor.hide();
743 output_container.verify_and_clear_expectations();757 output_container.verify_and_clear_expectations();
744758
745 EXPECT_CALL(*output_container.outputs[10], set_cursor(_));759 EXPECT_CALL(*output_container.outputs[0], set_cursor(_));
746 cursor.show();760 cursor.show();
747}761}
748762
@@ -772,15 +786,15 @@
772 786
773 787
774 EXPECT_CALL(mock_gbm, gbm_bo_write(_, _, _)).Times(AnyNumber());788 EXPECT_CALL(mock_gbm, gbm_bo_write(_, _, _)).Times(AnyNumber());
775 EXPECT_CALL(*output_container.outputs[10], set_cursor(_)).Times(AnyNumber());789 EXPECT_CALL(*output_container.outputs[0], set_cursor(_)).Times(AnyNumber());
776790
777 // When we set the image with the hotspot, first we should see the cursor move from its initial791 // When we set the image with the hotspot, first we should see the cursor move from its initial
778 // location, to account for the displacement. Further movement should be offset by the hotspot.792 // location, to account for the displacement. Further movement should be offset by the hotspot.
779 {793 {
780 InSequence seq;794 InSequence seq;
781 EXPECT_CALL(*output_container.outputs[10], move_cursor(initial_buffer_location));795 EXPECT_CALL(*output_container.outputs[0], move_cursor(initial_buffer_location));
782 EXPECT_CALL(*output_container.outputs[10], move_cursor(expected_buffer_location_1));796 EXPECT_CALL(*output_container.outputs[0], move_cursor(expected_buffer_location_1));
783 EXPECT_CALL(*output_container.outputs[10], move_cursor(expected_buffer_location_2));797 EXPECT_CALL(*output_container.outputs[0], move_cursor(expected_buffer_location_2));
784 }798 }
785799
786 cursor.show(HotspotCursor());800 cursor.show(HotspotCursor());
787801
=== modified file 'tests/unit-tests/platforms/mesa/kms/test_display.cpp'
--- tests/unit-tests/platforms/mesa/kms/test_display.cpp 2017-02-28 08:53:57 +0000
+++ tests/unit-tests/platforms/mesa/kms/test_display.cpp 2017-03-22 06:55:00 +0000
@@ -27,6 +27,7 @@
27#include "mir/time/steady_clock.h"27#include "mir/time/steady_clock.h"
28#include "mir/glib_main_loop.h"28#include "mir/glib_main_loop.h"
29#include "mir/fatal.h"29#include "mir/fatal.h"
30#include "src/platforms/common/server/kms-utils/drm_mode_resources.h"
3031
31#include "mir/test/doubles/mock_egl.h"32#include "mir/test/doubles/mock_egl.h"
32#include "mir/test/doubles/mock_gl.h"33#include "mir/test/doubles/mock_gl.h"
@@ -55,6 +56,7 @@
55#include <atomic>56#include <atomic>
56#include <mutex>57#include <mutex>
57#include <condition_variable>58#include <condition_variable>
59#include <fcntl.h>
5860
59namespace mg=mir::graphics;61namespace mg=mir::graphics;
60namespace mgm=mir::graphics::mesa;62namespace mgm=mir::graphics::mesa;
@@ -80,7 +82,8 @@
80public:82public:
81 MesaDisplayTest() :83 MesaDisplayTest() :
82 mock_report{std::make_shared<testing::NiceMock<mtd::MockDisplayReport>>()},84 mock_report{std::make_shared<testing::NiceMock<mtd::MockDisplayReport>>()},
83 null_report{mr::null_display_report()}85 null_report{mr::null_display_report()},
86 drm_fd{open(drm_device, 0, 0)}
84 {87 {
85 using namespace testing;88 using namespace testing;
86 ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_))89 ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_))
@@ -95,11 +98,16 @@
95 * the MockGBM destructor, and which are not handled by NiceMock<>.98 * the MockGBM destructor, and which are not handled by NiceMock<>.
96 */99 */
97 EXPECT_CALL(mock_gbm, gbm_bo_get_device(_))100 EXPECT_CALL(mock_gbm, gbm_bo_get_device(_))
98 .Times(AtLeast(0));101 .Times(AtLeast(0));
99 EXPECT_CALL(mock_gbm, gbm_device_get_fd(_))102 EXPECT_CALL(mock_gbm, gbm_device_get_fd(_))
100 .Times(AtLeast(0));103 .Times(AtLeast(0))
104 .WillRepeatedly(Return(drm_fd));
101105
102 fake_devices.add_standard_device("standard-drm-devices");106 fake_devices.add_standard_device("standard-drm-devices");
107
108 // Our standard mock devices have 2 DRM devices; kill all the outputs on
109 // the second one, so we don't try to test hybrid (for now)
110 mock_drm.reset("/dev/dri/card1");
103 }111 }
104112
105 std::shared_ptr<mgm::Platform> create_platform()113 std::shared_ptr<mgm::Platform> create_platform()
@@ -145,14 +153,14 @@
145 .Times(Exactly(1))153 .Times(Exactly(1))
146 .WillOnce(Return(fake.bo_handle2));154 .WillOnce(Return(fake.bo_handle2));
147155
148 EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(),156 EXPECT_CALL(mock_drm, drmModeAddFB2(drm_fd,
149 _, _, _,157 _, _, _,
150 Pointee(fake.bo_handle1.u32),158 Pointee(fake.bo_handle1.u32),
151 _, _, _, _))159 _, _, _, _))
152 .Times(Exactly(1))160 .Times(Exactly(1))
153 .WillOnce(DoAll(SetArgPointee<7>(fake.fb_id1), Return(0)));161 .WillOnce(DoAll(SetArgPointee<7>(fake.fb_id1), Return(0)));
154162
155 EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(),163 EXPECT_CALL(mock_drm, drmModeAddFB2(drm_fd,
156 _, _, _,164 _, _, _,
157 Pointee(fake.bo_handle2.u32),165 Pointee(fake.bo_handle2.u32),
158 _, _, _, _))166 _, _, _, _))
@@ -162,26 +170,27 @@
162170
163 uint32_t get_connected_connector_id()171 uint32_t get_connected_connector_id()
164 {172 {
165 auto drm_res = mock_drm.fake_drm.resources_ptr();173 mg::kms::DRMModeResources resources{drm_fd};
166174
167 for (int i = 0; i < drm_res->count_connectors; i++)175 int connected_id = 0;
168 {176 resources.for_each_connector(
169 auto connector = mock_drm.fake_drm.find_connector(drm_res->connectors[i]);177 [&connected_id](auto const& connector)
170 if (connector->connection == DRM_MODE_CONNECTED)178 {
171 return connector->connector_id;179 if (connector->connection == DRM_MODE_CONNECTED)
172 }180 connected_id = connector->connector_id;
173181 });
174 return 0;182
183 return connected_id;
175 }184 }
176185
177 uint32_t get_connected_crtc_id()186 uint32_t get_connected_crtc_id()
178 {187 {
179 auto connector_id = get_connected_connector_id();188 auto connector_id = get_connected_connector_id();
180 auto connector = mock_drm.fake_drm.find_connector(connector_id);189 auto connector = mg::kms::get_connector(drm_fd, connector_id);
181190
182 if (connector)191 if (connector)
183 {192 {
184 auto encoder = mock_drm.fake_drm.find_encoder(connector->encoder_id);193 auto encoder = mg::kms::get_encoder(drm_fd, connector->encoder_id);
185 if (encoder)194 if (encoder)
186 return encoder->crtc_id;195 return encoder->crtc_id;
187 }196 }
@@ -217,6 +226,9 @@
217 std::shared_ptr<testing::NiceMock<mtd::MockDisplayReport>> const mock_report;226 std::shared_ptr<testing::NiceMock<mtd::MockDisplayReport>> const mock_report;
218 std::shared_ptr<mg::DisplayReport> const null_report;227 std::shared_ptr<mg::DisplayReport> const null_report;
219 mtf::UdevEnvironment fake_devices;228 mtf::UdevEnvironment fake_devices;
229
230 char const* const drm_device = "/dev/dri/card0";
231 int const drm_fd;
220};232};
221233
222}234}
@@ -256,7 +268,7 @@
256 .WillOnce(Return(fake.bo_handle1));268 .WillOnce(Return(fake.bo_handle1));
257269
258 /* Create a a DRM FB with the DRM buffer attached */270 /* Create a a DRM FB with the DRM buffer attached */
259 EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(),271 EXPECT_CALL(mock_drm, drmModeAddFB2(drm_fd,
260 _, _, _,272 _, _, _,
261 Pointee(fake.bo_handle1.u32),273 Pointee(fake.bo_handle1.u32),
262 _, _, _, _))274 _, _, _, _))
@@ -264,14 +276,14 @@
264 .WillOnce(DoAll(SetArgPointee<7>(fake.fb_id1), Return(0)));276 .WillOnce(DoAll(SetArgPointee<7>(fake.fb_id1), Return(0)));
265277
266 /* Display the DRM FB (first expectation is for cleanup) */278 /* Display the DRM FB (first expectation is for cleanup) */
267 EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),279 EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd,
268 crtc_id, Ne(fake.fb_id1),280 crtc_id, Ne(fake.fb_id1),
269 _, _,281 _, _,
270 Pointee(connector_id),282 Pointee(connector_id),
271 _, _))283 _, _))
272 .Times(AtLeast(0));284 .Times(AtLeast(0));
273285
274 EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),286 EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd,
275 crtc_id, fake.fb_id1,287 crtc_id, fake.fb_id1,
276 _, _,288 _, _,
277 Pointee(connector_id),289 Pointee(connector_id),
@@ -291,7 +303,7 @@
291 uint32_t const fb_id{66};303 uint32_t const fb_id{66};
292304
293 /* Create DRM FBs */305 /* Create DRM FBs */
294 EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(),306 EXPECT_CALL(mock_drm, drmModeAddFB2(drm_fd,
295 _, _, _, _, _, _, _, _))307 _, _, _, _, _, _, _, _))
296 .WillRepeatedly(DoAll(SetArgPointee<7>(fb_id), Return(0)));308 .WillRepeatedly(DoAll(SetArgPointee<7>(fb_id), Return(0)));
297309
@@ -300,7 +312,7 @@
300 InSequence s;312 InSequence s;
301313
302 /* crtc is set */314 /* crtc is set */
303 EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),315 EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd,
304 crtc_id, fb_id,316 crtc_id, fb_id,
305 _, _,317 _, _,
306 Pointee(connector_id),318 Pointee(connector_id),
@@ -308,7 +320,7 @@
308 .Times(AtLeast(1));320 .Times(AtLeast(1));
309321
310 /* crtc is reset */322 /* crtc is reset */
311 EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),323 EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd,
312 crtc_id, Ne(fb_id),324 crtc_id, Ne(fb_id),
313 _, _,325 _, _,
314 Pointee(connector_id),326 Pointee(connector_id),
@@ -325,7 +337,10 @@
325337
326 EXPECT_CALL(mock_drm, open(_,_,_))338 EXPECT_CALL(mock_drm, open(_,_,_))
327 .Times(AtLeast(1))339 .Times(AtLeast(1))
328 .WillRepeatedly(Return(-1));340 .WillRepeatedly(
341 DoAll(
342 InvokeWithoutArgs([]() { errno = ENODEV; }),
343 Return(-1)));
329344
330 EXPECT_CALL(mock_drm, drmClose(_))345 EXPECT_CALL(mock_drm, drmClose(_))
331 .Times(Exactly(0));346 .Times(Exactly(0));
@@ -351,8 +366,9 @@
351 EXPECT_CALL(mock_drm, drmModeFreeResources(_))366 EXPECT_CALL(mock_drm, drmModeFreeResources(_))
352 .Times(Exactly(0));367 .Times(Exactly(0));
353368
369 // There are 2 DRM device nodes in our mock environment.
354 EXPECT_CALL(mock_drm, drmClose(_))370 EXPECT_CALL(mock_drm, drmClose(_))
355 .Times(Exactly(1));371 .Times(Exactly(2));
356372
357 EXPECT_THROW({373 EXPECT_THROW({
358 auto display = create_display(platform);374 auto display = create_display(platform);
@@ -371,7 +387,7 @@
371 .Times(Exactly(0));387 .Times(Exactly(0));
372388
373 EXPECT_CALL(mock_drm, drmClose(_))389 EXPECT_CALL(mock_drm, drmClose(_))
374 .Times(Exactly(1));390 .Times(Exactly(2));
375391
376 EXPECT_THROW({392 EXPECT_THROW({
377 auto platform = create_platform();393 auto platform = create_platform();
@@ -381,9 +397,9 @@
381namespace397namespace
382{398{
383399
384ACTION_P(QueuePageFlipEvent, write_drm_fd)400ACTION_P(QueuePageFlipEvent, mock_drm)
385{401{
386 EXPECT_EQ(1, write(write_drm_fd, "a", 1));402 static_cast<mtd::MockDRM&>(mock_drm).generate_event_on("/dev/dri/card0");
387}403}
388404
389ACTION_P(InvokePageFlipHandler, param)405ACTION_P(InvokePageFlipHandler, param)
@@ -410,17 +426,17 @@
410 InSequence s;426 InSequence s;
411427
412 /* Flip the new FB */428 /* Flip the new FB */
413 EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(),429 EXPECT_CALL(mock_drm, drmModePageFlip(drm_fd,
414 crtc_id,430 crtc_id,
415 fake.fb_id2,431 fake.fb_id2,
416 _, _))432 _, _))
417 .Times(Exactly(1))433 .Times(Exactly(1))
418 .WillOnce(DoAll(QueuePageFlipEvent(mock_drm.fake_drm.write_fd()),434 .WillOnce(DoAll(QueuePageFlipEvent(std::ref(mock_drm)),
419 SaveArg<4>(&user_data),435 SaveArg<4>(&user_data),
420 Return(0)));436 Return(0)));
421437
422 /* Handle the flip event */438 /* Handle the flip event */
423 EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _))439 EXPECT_CALL(mock_drm, drmHandleEvent(drm_fd, _))
424 .Times(1)440 .Times(1)
425 .WillOnce(DoAll(InvokePageFlipHandler(&user_data), Return(0)));441 .WillOnce(DoAll(InvokePageFlipHandler(&user_data), Return(0)));
426442
@@ -455,7 +471,7 @@
455 setup_post_update_expectations();471 setup_post_update_expectations();
456472
457 // clear_crtc happens at some stage. Not interesting.473 // clear_crtc happens at some stage. Not interesting.
458 EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),474 EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd,
459 crtc_id, 0,475 crtc_id, 0,
460 _, _, _, _, _))476 _, _, _, _, _))
461 .WillOnce(Return(0));477 .WillOnce(Return(0));
@@ -465,13 +481,13 @@
465481
466 // DisplayBuffer construction paints an empty screen.482 // DisplayBuffer construction paints an empty screen.
467 // That's probably less than ideal but we've always had it that way.483 // That's probably less than ideal but we've always had it that way.
468 EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),484 EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd,
469 crtc_id, fake.fb_id1,485 crtc_id, fake.fb_id1,
470 _, _, _, _, _))486 _, _, _, _, _))
471 .WillOnce(Return(0));487 .WillOnce(Return(0));
472488
473 // New FB flip failure489 // New FB flip failure
474 EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(),490 EXPECT_CALL(mock_drm, drmModePageFlip(drm_fd,
475 crtc_id,491 crtc_id,
476 fake.fb_id2,492 fake.fb_id2,
477 _, _))493 _, _))
@@ -479,7 +495,7 @@
479 .WillOnce(Return(-1));495 .WillOnce(Return(-1));
480496
481 // Expect fallback to blitting497 // Expect fallback to blitting
482 EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),498 EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd,
483 crtc_id, fake.fb_id2,499 crtc_id, fake.fb_id2,
484 _, _, _, _, _))500 _, _, _, _, _))
485 .WillOnce(Return(0));501 .WillOnce(Return(0));
@@ -623,21 +639,6 @@
623 }, std::runtime_error);639 }, std::runtime_error);
624}640}
625641
626TEST_F(MesaDisplayTest, constructor_throws_if_gl_oes_image_not_supported)
627{
628 using namespace ::testing;
629
630 const char* gl_exts = "GL_OES_texture_npot GL_OES_blend_func_separate";
631
632 EXPECT_CALL(mock_gl, glGetString(GL_EXTENSIONS))
633 .WillOnce(Return(reinterpret_cast<const GLubyte*>(gl_exts)));
634
635 EXPECT_THROW(
636 {
637 auto display = create_display(create_platform());
638 }, std::runtime_error);
639}
640
641TEST_F(MesaDisplayTest, for_each_display_buffer_calls_callback)642TEST_F(MesaDisplayTest, for_each_display_buffer_calls_callback)
642{643{
643 using namespace ::testing;644 using namespace ::testing;
@@ -677,8 +678,8 @@
677{678{
678 using namespace testing;679 using namespace testing;
679680
680 EXPECT_CALL(mock_drm, drmDropMaster(mock_drm.fake_drm.fd()))681 EXPECT_CALL(mock_drm, drmDropMaster(_))
681 .Times(1);682 .Times(2);
682683
683 auto display = create_display(create_platform());684 auto display = create_display(create_platform());
684685
@@ -689,8 +690,8 @@
689{690{
690 using namespace testing;691 using namespace testing;
691692
692 EXPECT_CALL(mock_drm, drmSetMaster(mock_drm.fake_drm.fd()))693 EXPECT_CALL(mock_drm, drmSetMaster(_))
693 .Times(1);694 .Times(2);
694695
695 auto display = create_display(create_platform());696 auto display = create_display(create_platform());
696697
697698
=== modified file 'tests/unit-tests/platforms/mesa/kms/test_display_buffer.cpp'
--- tests/unit-tests/platforms/mesa/kms/test_display_buffer.cpp 2017-03-13 08:12:52 +0000
+++ tests/unit-tests/platforms/mesa/kms/test_display_buffer.cpp 2017-03-22 06:55:00 +0000
@@ -93,6 +93,8 @@
93 .WillByDefault(Return(mock_refresh_rate));93 .WillByDefault(Return(mock_refresh_rate));
94 ON_CALL(*mock_kms_output, fb_for(_,_,_))94 ON_CALL(*mock_kms_output, fb_for(_,_,_))
95 .WillByDefault(Return(reinterpret_cast<FBHandle*>(0x12ad)));95 .WillByDefault(Return(reinterpret_cast<FBHandle*>(0x12ad)));
96 ON_CALL(*mock_kms_output, buffer_requires_migration(_))
97 .WillByDefault(Return(false));
9698
97 ON_CALL(*mock_bypassable_buffer, size())99 ON_CALL(*mock_bypassable_buffer, size())
98 .WillByDefault(Return(display_area.size));100 .WillByDefault(Return(display_area.size));
@@ -486,3 +488,18 @@
486 EXPECT_FALSE(db.overlay(list));488 EXPECT_FALSE(db.overlay(list));
487}489}
488490
491TEST_F(MesaDisplayBufferTest, buffer_requiring_migration_is_ineligable_for_bypass)
492{
493 ON_CALL(*mock_kms_output, buffer_requires_migration(Eq(stub_gbm_native_buffer->bo)))
494 .WillByDefault(Return(true));
495
496 graphics::mesa::DisplayBuffer db(
497 graphics::mesa::BypassOption::allowed,
498 null_display_report(),
499 {mock_kms_output},
500 make_output_surface(),
501 display_area,
502 mir_orientation_normal);
503
504 EXPECT_FALSE(db.overlay(bypassable_list));
505}
489506
=== modified file 'tests/unit-tests/platforms/mesa/kms/test_display_configuration.cpp'
--- tests/unit-tests/platforms/mesa/kms/test_display_configuration.cpp 2017-03-14 02:26:28 +0000
+++ tests/unit-tests/platforms/mesa/kms/test_display_configuration.cpp 2017-03-22 06:55:00 +0000
@@ -44,6 +44,7 @@
44#include <gtest/gtest.h>44#include <gtest/gtest.h>
4545
46#include <stdexcept>46#include <stdexcept>
47#include <fcntl.h>
4748
48namespace mg = mir::graphics;49namespace mg = mir::graphics;
49namespace mgm = mir::graphics::mesa;50namespace mgm = mir::graphics::mesa;
@@ -92,21 +93,30 @@
92{93{
93public:94public:
94 MesaDisplayConfigurationTest()95 MesaDisplayConfigurationTest()
96 : drm_fd{open(drm_device, 0, 0)}
95 {97 {
96 using namespace testing;98 using namespace testing;
9799
98 /* Needed for display start-up */100 /* Needed for display start-up */
99 ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_))101 ON_CALL(mock_egl, eglChooseConfig(_, _, _, 1, _))
100 .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]),102 .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]),
101 SetArgPointee<4>(1),103 SetArgPointee<4>(1),
102 Return(EGL_TRUE)));104 Return(EGL_TRUE)));
103105
104 mock_egl.provide_egl_extensions();106 mock_egl.provide_egl_extensions();
105 mock_gl.provide_gles_extensions();107 mock_gl.provide_gles_extensions();
106108
109 ON_CALL(mock_gbm, gbm_device_get_fd(_))
110 .WillByDefault(Return(drm_fd));
111
107 setup_sample_modes();112 setup_sample_modes();
108113
109 fake_devices.add_standard_device("standard-drm-devices");114 fake_devices.add_standard_device("standard-drm-devices");
115
116 // Remove all outputs from all but one of the DRM devices we access;
117 // these tests are not set up to test hybrid.
118 mock_drm.reset("/dev/dri/card1");
119 mock_drm.reset("/dev/dri/card2");
110 }120 }
111121
112 std::shared_ptr<mg::Platform> create_platform()122 std::shared_ptr<mg::Platform> create_platform()
@@ -160,8 +170,46 @@
160 std::vector<drmModeModeInfo> modes_empty;170 std::vector<drmModeModeInfo> modes_empty;
161171
162 mtf::UdevEnvironment fake_devices;172 mtf::UdevEnvironment fake_devices;
173
174 char const* const drm_device = "/dev/dri/card0";
175 int const drm_fd;
163};176};
164177
178MATCHER_P(Unique, scratch_vector, "")
179{
180 if (std::any_of(
181 scratch_vector.begin(),
182 scratch_vector.end(),
183 [&arg](auto const& candidate) { return arg == candidate; }))
184 {
185 return false;
186 }
187 const_cast<typename std::remove_const<scratch_vector_type>::type&>(scratch_vector).push_back(arg);
188 return true;
189}
190
191MATCHER_P(DisplayConfigsAreEquivalent, expected_configs, "")
192{
193 using namespace testing;
194
195 std::vector<mg::DisplayConfigurationOutput> outputs;
196 std::vector<mg::DisplayConfigurationOutputId> output_ids;
197 arg->for_each_output([&](mg::DisplayConfigurationOutput const& output)
198 {
199 outputs.push_back(output);
200 output_ids.push_back(output.id);
201 outputs.back().id = mg::DisplayConfigurationOutputId{0};
202 });
203
204
205 Matcher<decltype(outputs)> config_matcher = UnorderedElementsAreArray(expected_configs);
206 std::vector<mg::DisplayConfigurationOutputId> scratch_space;
207 Matcher<decltype(output_ids)> ids_are_unique = Each(Unique(scratch_space));
208
209 return
210 config_matcher.MatchAndExplain(outputs, result_listener) &&
211 ids_are_unique.MatchAndExplain(output_ids, result_listener);
212}
165}213}
166214
167TEST_F(MesaDisplayConfigurationTest, configuration_is_read_correctly)215TEST_F(MesaDisplayConfigurationTest, configuration_is_read_correctly)
@@ -182,34 +230,58 @@
182 std::vector<uint32_t> possible_encoder_ids_empty;230 std::vector<uint32_t> possible_encoder_ids_empty;
183 uint32_t const possible_crtcs_mask_empty{0};231 uint32_t const possible_crtcs_mask_empty{0};
184232
185 mtd::FakeDRMResources& resources(mock_drm.fake_drm);233 mock_drm.reset(drm_device);
186234
187 resources.reset();235 mock_drm.add_crtc(
188236 drm_device,
189 resources.add_crtc(crtc0_id, modes0[1]);237 crtc0_id,
190238 modes0[1]);
191 resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty);239
192 resources.add_encoder(encoder1_id, invalid_id, possible_crtcs_mask_empty);240 mock_drm.add_encoder(
193241 drm_device,
194 resources.add_connector(connector0_id, DRM_MODE_CONNECTOR_HDMIA,242 encoder0_id,
195 DRM_MODE_CONNECTED, encoder0_id,243 crtc0_id,
196 modes0, possible_encoder_ids_empty,244 possible_crtcs_mask_empty);
197 connector0_physical_size_mm);245 mock_drm.add_encoder(
198 resources.add_connector(connector1_id, DRM_MODE_CONNECTOR_Unknown,246 drm_device,
199 DRM_MODE_DISCONNECTED, invalid_id,247 encoder1_id,
200 modes_empty, possible_encoder_ids_empty,248 invalid_id,
201 connector1_physical_size_mm);249 possible_crtcs_mask_empty);
202 resources.add_connector(connector2_id, DRM_MODE_CONNECTOR_eDP,250
203 DRM_MODE_DISCONNECTED, encoder1_id,251 mock_drm.add_connector(
204 modes_empty, possible_encoder_ids_empty,252 drm_device,
205 connector2_physical_size_mm);253 connector0_id,
206254 DRM_MODE_CONNECTOR_HDMIA,
207 resources.prepare();255 DRM_MODE_CONNECTED,
256 encoder0_id,
257 modes0,
258 possible_encoder_ids_empty,
259 connector0_physical_size_mm);
260 mock_drm.add_connector(
261 drm_device,
262 connector1_id,
263 DRM_MODE_CONNECTOR_Unknown,
264 DRM_MODE_DISCONNECTED,
265 invalid_id,
266 modes_empty,
267 possible_encoder_ids_empty,
268 connector1_physical_size_mm);
269 mock_drm.add_connector(
270 drm_device,
271 connector2_id,
272 DRM_MODE_CONNECTOR_eDP,
273 DRM_MODE_DISCONNECTED,
274 encoder1_id,
275 modes_empty,
276 possible_encoder_ids_empty,
277 connector2_physical_size_mm);
278
279 mock_drm.prepare(drm_device);
208280
209 std::vector<mg::DisplayConfigurationOutput> const expected_outputs =281 std::vector<mg::DisplayConfigurationOutput> const expected_outputs =
210 {282 {
211 {283 {
212 mg::DisplayConfigurationOutputId{connector0_id},284 mg::DisplayConfigurationOutputId{0},
213 mg::DisplayConfigurationCardId{0},285 mg::DisplayConfigurationCardId{0},
214 mg::DisplayConfigurationOutputType::hdmia,286 mg::DisplayConfigurationOutputType::hdmia,
215 {},287 {},
@@ -231,7 +303,7 @@
231 {}303 {}
232 },304 },
233 {305 {
234 mg::DisplayConfigurationOutputId{connector1_id},306 mg::DisplayConfigurationOutputId{0},
235 mg::DisplayConfigurationCardId{0},307 mg::DisplayConfigurationCardId{0},
236 mg::DisplayConfigurationOutputType::unknown,308 mg::DisplayConfigurationOutputType::unknown,
237 {},309 {},
@@ -253,7 +325,7 @@
253 {}325 {}
254 },326 },
255 {327 {
256 mg::DisplayConfigurationOutputId{connector2_id},328 mg::DisplayConfigurationOutputId{0},
257 mg::DisplayConfigurationCardId{0},329 mg::DisplayConfigurationCardId{0},
258 mg::DisplayConfigurationOutputType::edp,330 mg::DisplayConfigurationOutputType::edp,
259 {},331 {},
@@ -281,16 +353,7 @@
281353
282 auto conf = display->configuration();354 auto conf = display->configuration();
283355
284 size_t output_count{0};356 EXPECT_THAT(conf, DisplayConfigsAreEquivalent(expected_outputs));
285
286 conf->for_each_output([&](mg::DisplayConfigurationOutput const& output)
287 {
288 ASSERT_LT(output_count, expected_outputs.size());
289 EXPECT_EQ(expected_outputs[output_count], output) << "output_count: " << output_count;
290 ++output_count;
291 });
292
293 EXPECT_EQ(expected_outputs.size(), output_count);
294}357}
295358
296TEST_F(MesaDisplayConfigurationTest, reads_subpixel_information_correctly)359TEST_F(MesaDisplayConfigurationTest, reads_subpixel_information_correctly)
@@ -305,8 +368,6 @@
305 std::vector<uint32_t> possible_encoder_ids_empty;368 std::vector<uint32_t> possible_encoder_ids_empty;
306 uint32_t const possible_crtcs_mask_empty{0};369 uint32_t const possible_crtcs_mask_empty{0};
307370
308 mtd::FakeDRMResources& resources(mock_drm.fake_drm);
309
310 struct TestData371 struct TestData
311 {372 {
312 drmModeSubPixel drm_subpixel;373 drmModeSubPixel drm_subpixel;
@@ -324,19 +385,31 @@
324 for (auto& data : test_data)385 for (auto& data : test_data)
325 {386 {
326387
327 resources.reset();388 mock_drm.reset(drm_device);
328389
329 resources.add_crtc(crtc0_id, modes0[1]);390 mock_drm.add_crtc(
330391 drm_device,
331 resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty);392 crtc0_id,
332393 modes0[1]);
333 resources.add_connector(connector0_id, DRM_MODE_CONNECTOR_HDMIA,394
334 DRM_MODE_CONNECTED, encoder0_id,395 mock_drm.add_encoder(
335 modes0, possible_encoder_ids_empty,396 drm_device,
336 connector0_physical_size_mm,397 encoder0_id,
337 data.drm_subpixel);398 crtc0_id,
338399 possible_crtcs_mask_empty);
339 resources.prepare();400
401 mock_drm.add_connector(
402 drm_device,
403 connector0_id,
404 DRM_MODE_CONNECTOR_HDMIA,
405 DRM_MODE_CONNECTED,
406 encoder0_id,
407 modes0,
408 possible_encoder_ids_empty,
409 connector0_physical_size_mm,
410 data.drm_subpixel);
411
412 mock_drm.prepare(drm_device);
340413
341 /* Test body */414 /* Test body */
342 auto display = create_display(create_platform());415 auto display = create_display(create_platform());
@@ -369,21 +442,31 @@
369 std::vector<uint32_t> possible_encoder_ids_empty;442 std::vector<uint32_t> possible_encoder_ids_empty;
370 uint32_t const possible_crtcs_mask_empty{0};443 uint32_t const possible_crtcs_mask_empty{0};
371444
372 mtd::FakeDRMResources& resources(mock_drm.fake_drm);445 mock_drm.reset(drm_device);
373446
374 resources.reset();447 mock_drm.add_crtc(
375448 drm_device,
376 resources.add_crtc(crtc0_id, modes0[1]);449 crtc0_id,
377450 modes0[1]);
378 resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty);451
379452 mock_drm.add_encoder(
380 resources.add_connector(connector0_id, DRM_MODE_CONNECTOR_HDMIA,453 drm_device,
381 DRM_MODE_CONNECTED, encoder0_id,454 encoder0_id,
382 modes0, possible_encoder_ids_empty,455 crtc0_id,
383 connector0_physical_size_mm,456 possible_crtcs_mask_empty);
384 DRM_MODE_SUBPIXEL_NONE);457
385458 mock_drm.add_connector(
386 resources.prepare();459 drm_device,
460 connector0_id,
461 DRM_MODE_CONNECTOR_HDMIA,
462 DRM_MODE_CONNECTED,
463 encoder0_id,
464 modes0,
465 possible_encoder_ids_empty,
466 connector0_physical_size_mm,
467 DRM_MODE_SUBPIXEL_NONE);
468
469 mock_drm.prepare(drm_device);
387470
388 struct TestData471 struct TestData
389 {472 {
@@ -405,19 +488,31 @@
405 for (auto& data : test_data)488 for (auto& data : test_data)
406 {489 {
407490
408 resources.reset();491 mock_drm.reset(drm_device);
409492
410 resources.add_crtc(crtc0_id, modes0[1]);493 mock_drm.add_crtc(
411494 drm_device,
412 resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty);495 crtc0_id,
413496 modes0[1]);
414 resources.add_connector(connector0_id, DRM_MODE_CONNECTOR_HDMIA,497
415 DRM_MODE_CONNECTED, encoder0_id,498 mock_drm.add_encoder(
416 modes0, possible_encoder_ids_empty,499 drm_device,
417 connector0_physical_size_mm,500 encoder0_id,
418 data.drm_subpixel);501 crtc0_id,
419502 possible_crtcs_mask_empty);
420 resources.prepare();503
504 mock_drm.add_connector(
505 drm_device,
506 connector0_id,
507 DRM_MODE_CONNECTOR_HDMIA,
508 DRM_MODE_CONNECTED,
509 encoder0_id,
510 modes0,
511 possible_encoder_ids_empty,
512 connector0_physical_size_mm,
513 data.drm_subpixel);
514
515 mock_drm.prepare(drm_device);
421516
422 mt::Signal handler_signal;517 mt::Signal handler_signal;
423 MainLoop ml;518 MainLoop ml;
@@ -442,88 +537,6 @@
442 }537 }
443}538}
444539
445TEST_F(MesaDisplayConfigurationTest, get_kms_connector_id_returns_correct_id)
446{
447 uint32_t const crtc0_id{10};
448 uint32_t const encoder0_id{20};
449 uint32_t const possible_crtcs_mask_empty{0};
450 std::vector<uint32_t> const connector_ids{30, 31};
451 std::vector<uint32_t> encoder_ids{20};
452
453 /* Set up DRM resources */
454 mtd::FakeDRMResources& resources(mock_drm.fake_drm);
455
456 resources.reset();
457
458 resources.add_crtc(crtc0_id, modes0[1]);
459 resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty);
460 for (auto id : connector_ids)
461 {
462 resources.add_connector(id, DRM_MODE_CONNECTOR_DVID,
463 DRM_MODE_CONNECTED, encoder0_id,
464 modes0, encoder_ids,
465 geom::Size());
466 }
467
468 resources.prepare();
469
470 /* Test body */
471 auto display = create_display(create_platform());
472
473 std::shared_ptr<mg::DisplayConfiguration> conf = display->configuration();
474 auto const& kms_conf = std::static_pointer_cast<mgm::KMSDisplayConfiguration>(conf);
475
476 size_t output_count{0};
477
478 conf->for_each_output([&](mg::DisplayConfigurationOutput const& output)
479 {
480 ASSERT_LT(output_count, connector_ids.size());
481
482 EXPECT_EQ(connector_ids[output_count],
483 kms_conf->get_kms_connector_id(output.id));
484 ++output_count;
485 });
486}
487
488TEST_F(MesaDisplayConfigurationTest, get_kms_connector_id_throws_on_invalid_id)
489{
490 uint32_t const crtc0_id{10};
491 uint32_t const encoder0_id{20};
492 uint32_t const possible_crtcs_mask_empty{0};
493 std::vector<uint32_t> const connector_ids{30, 31};
494 std::vector<uint32_t> encoder_ids{20};
495
496 /* Set up DRM resources */
497 mtd::FakeDRMResources& resources(mock_drm.fake_drm);
498
499 resources.reset();
500
501 resources.add_crtc(crtc0_id, modes0[1]);
502 resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty);
503 for (auto id : connector_ids)
504 {
505 resources.add_connector(id, DRM_MODE_CONNECTOR_VGA,
506 DRM_MODE_CONNECTED, encoder0_id,
507 modes0, encoder_ids,
508 geom::Size());
509 }
510
511 resources.prepare();
512
513 /* Test body */
514 auto display = create_display(create_platform());
515
516 std::shared_ptr<mg::DisplayConfiguration> conf = display->configuration();
517 auto const& kms_conf = std::static_pointer_cast<mgm::KMSDisplayConfiguration>(conf);
518
519 EXPECT_THROW({
520 kms_conf->get_kms_connector_id(mg::DisplayConfigurationOutputId{29});
521 }, std::runtime_error);
522 EXPECT_THROW({
523 kms_conf->get_kms_connector_id(mg::DisplayConfigurationOutputId{32});
524 }, std::runtime_error);
525}
526
527TEST_F(MesaDisplayConfigurationTest, returns_updated_configuration)540TEST_F(MesaDisplayConfigurationTest, returns_updated_configuration)
528{541{
529 using namespace ::testing;542 using namespace ::testing;
@@ -545,7 +558,7 @@
545 std::vector<mg::DisplayConfigurationOutput> const expected_outputs_before =558 std::vector<mg::DisplayConfigurationOutput> const expected_outputs_before =
546 {559 {
547 {560 {
548 mg::DisplayConfigurationOutputId(connector_ids[0]),561 mg::DisplayConfigurationOutputId{0},
549 mg::DisplayConfigurationCardId{0},562 mg::DisplayConfigurationCardId{0},
550 mg::DisplayConfigurationOutputType::composite,563 mg::DisplayConfigurationOutputType::composite,
551 {},564 {},
@@ -567,7 +580,7 @@
567 {}580 {}
568 },581 },
569 {582 {
570 mg::DisplayConfigurationOutputId(connector_ids[1]),583 mg::DisplayConfigurationOutputId{0},
571 mg::DisplayConfigurationCardId{0},584 mg::DisplayConfigurationCardId{0},
572 mg::DisplayConfigurationOutputType::vga,585 mg::DisplayConfigurationOutputType::vga,
573 {},586 {},
@@ -593,7 +606,7 @@
593 std::vector<mg::DisplayConfigurationOutput> const expected_outputs_after =606 std::vector<mg::DisplayConfigurationOutput> const expected_outputs_after =
594 {607 {
595 {608 {
596 mg::DisplayConfigurationOutputId(connector_ids[0]),609 mg::DisplayConfigurationOutputId{0},
597 mg::DisplayConfigurationCardId{0},610 mg::DisplayConfigurationCardId{0},
598 mg::DisplayConfigurationOutputType::composite,611 mg::DisplayConfigurationOutputType::composite,
599 {},612 {},
@@ -615,7 +628,7 @@
615 {}628 {}
616 },629 },
617 {630 {
618 mg::DisplayConfigurationOutputId(connector_ids[1]),631 mg::DisplayConfigurationOutputId{0},
619 mg::DisplayConfigurationCardId{0},632 mg::DisplayConfigurationCardId{0},
620 mg::DisplayConfigurationOutputType::vga,633 mg::DisplayConfigurationOutputType::vga,
621 {},634 {},
@@ -639,60 +652,100 @@
639 };652 };
640653
641 /* Set up DRM resources and check */654 /* Set up DRM resources and check */
642 mtd::FakeDRMResources& resources(mock_drm.fake_drm);655 auto const syspath = fake_devices.add_device(
643 auto const syspath = fake_devices.add_device("drm", "card2", NULL, {}, {"DEVTYPE", "drm_minor"});656 "drm",
644657 "card2",
645 resources.reset();658 NULL,
646659 {},
647 resources.add_crtc(crtc_ids[0], modes0[1]);660 {
648661 "DEVTYPE", "drm_minor",
649 resources.add_encoder(encoder_ids[0], crtc_ids[0], possible_crtcs_mask_empty);662 "DEVNAME", "/dev/dri/card2"
650 resources.add_encoder(encoder_ids[1], invalid_id, possible_crtcs_mask_empty);663 });
651664
652 resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_Composite,665 mock_drm.reset(drm_device);
653 DRM_MODE_CONNECTED, encoder_ids[0],666
654 modes0, possible_encoder_ids_empty,667 mock_drm.add_crtc(
655 connector_physical_sizes_mm_before[0]);668 drm_device,
656 resources.add_connector(connector_ids[1], DRM_MODE_CONNECTOR_VGA,669 crtc_ids[0],
657 DRM_MODE_DISCONNECTED, invalid_id,670 modes0[1]);
658 modes_empty, possible_encoder_ids_empty,671
659 connector_physical_sizes_mm_before[1]);672 mock_drm.add_encoder(
660673 drm_device,
661 resources.prepare();674 encoder_ids[0],
675 crtc_ids[0],
676 possible_crtcs_mask_empty);
677 mock_drm.add_encoder(
678 drm_device,
679 encoder_ids[1],
680 invalid_id,
681 possible_crtcs_mask_empty);
682
683 mock_drm.add_connector(
684 drm_device,
685 connector_ids[0],
686 DRM_MODE_CONNECTOR_Composite,
687 DRM_MODE_CONNECTED,
688 encoder_ids[0],
689 modes0,
690 possible_encoder_ids_empty,
691 connector_physical_sizes_mm_before[0]);
692 mock_drm.add_connector(
693 drm_device,
694 connector_ids[1],
695 DRM_MODE_CONNECTOR_VGA,
696 DRM_MODE_DISCONNECTED,
697 invalid_id,
698 modes_empty,
699 possible_encoder_ids_empty,
700 connector_physical_sizes_mm_before[1]);
701
702 mock_drm.prepare(drm_device);
662703
663 auto display = create_display(create_platform());704 auto display = create_display(create_platform());
664705
665 auto conf = display->configuration();706 auto conf = display->configuration();
666707
667 size_t output_count{0};708 EXPECT_THAT(conf, DisplayConfigsAreEquivalent(expected_outputs_before));
668
669 conf->for_each_output([&](mg::DisplayConfigurationOutput const& output)
670 {
671 ASSERT_LT(output_count, expected_outputs_before.size());
672 EXPECT_EQ(expected_outputs_before[output_count], output) << "output_count: " << output_count;
673 ++output_count;
674 });
675
676 EXPECT_EQ(expected_outputs_before.size(), output_count);
677709
678 /* Reset DRM resources and check again */710 /* Reset DRM resources and check again */
679 resources.reset();711 mock_drm.reset(drm_device);
680712
681 resources.add_crtc(crtc_ids[1], modes0[1]);713 mock_drm.add_crtc(
682714 drm_device,
683 resources.add_encoder(encoder_ids[0], invalid_id, possible_crtcs_mask_empty);715 crtc_ids[1],
684 resources.add_encoder(encoder_ids[1], crtc_ids[1], possible_crtcs_mask_empty);716 modes0[1]);
685717
686 resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_Composite,718 mock_drm.add_encoder(
687 DRM_MODE_DISCONNECTED, invalid_id,719 drm_device,
688 modes_empty, possible_encoder_ids_empty,720 encoder_ids[0],
689 connector_physical_sizes_mm_after[0]);721 invalid_id,
690 resources.add_connector(connector_ids[1], DRM_MODE_CONNECTOR_VGA,722 possible_crtcs_mask_empty);
691 DRM_MODE_CONNECTED, encoder_ids[1],723 mock_drm.add_encoder(
692 modes0, possible_encoder_ids_empty,724 drm_device,
693 connector_physical_sizes_mm_after[1]);725 encoder_ids[1],
694726 crtc_ids[1],
695 resources.prepare();727 possible_crtcs_mask_empty);
728
729 mock_drm.add_connector(
730 drm_device,
731 connector_ids[0],
732 DRM_MODE_CONNECTOR_Composite,
733 DRM_MODE_DISCONNECTED,
734 invalid_id,
735 modes_empty,
736 possible_encoder_ids_empty,
737 connector_physical_sizes_mm_after[0]);
738 mock_drm.add_connector(
739 drm_device,
740 connector_ids[1],
741 DRM_MODE_CONNECTOR_VGA,
742 DRM_MODE_CONNECTED,
743 encoder_ids[1],
744 modes0,
745 possible_encoder_ids_empty,
746 connector_physical_sizes_mm_after[1]);
747
748 mock_drm.prepare(drm_device);
696749
697 /* Fake a device change so display can fetch updated configuration*/750 /* Fake a device change so display can fetch updated configuration*/
698 MainLoop ml;751 MainLoop ml;
@@ -703,16 +756,7 @@
703756
704 conf = display->configuration();757 conf = display->configuration();
705758
706 output_count = 0;759 EXPECT_THAT(conf, DisplayConfigsAreEquivalent(expected_outputs_after));
707
708 conf->for_each_output([&](mg::DisplayConfigurationOutput const& output)
709 {
710 ASSERT_LT(output_count, expected_outputs_after.size());
711 EXPECT_EQ(expected_outputs_after[output_count], output) << "output_count: " << output_count;
712 ++output_count;
713 });
714
715 EXPECT_EQ(expected_outputs_after.size(), output_count);
716}760}
717761
718TEST_F(MesaDisplayConfigurationTest, new_monitor_matches_hardware_state)762TEST_F(MesaDisplayConfigurationTest, new_monitor_matches_hardware_state)
@@ -727,12 +771,11 @@
727 geom::Size const connector_physical_sizes_mm_after{512, 642};771 geom::Size const connector_physical_sizes_mm_after{512, 642};
728 std::vector<uint32_t> possible_encoder_ids_empty;772 std::vector<uint32_t> possible_encoder_ids_empty;
729 uint32_t const possible_crtcs_mask_empty{0};773 uint32_t const possible_crtcs_mask_empty{0};
730 int const noutputs = 1;
731774
732 mg::DisplayConfigurationOutput const expected_outputs_before[noutputs] =775 std::vector<mg::DisplayConfigurationOutput> const expected_outputs_before =
733 {776 {
734 {777 {
735 mg::DisplayConfigurationOutputId(connector_ids[0]),778 mg::DisplayConfigurationOutputId{0},
736 mg::DisplayConfigurationCardId{0},779 mg::DisplayConfigurationCardId{0},
737 mg::DisplayConfigurationOutputType::composite,780 mg::DisplayConfigurationOutputType::composite,
738 {},781 {},
@@ -755,10 +798,10 @@
755 },798 },
756 };799 };
757800
758 mg::DisplayConfigurationOutput const expected_outputs_after[noutputs] =801 std::vector<mg::DisplayConfigurationOutput> const expected_outputs_after =
759 {802 {
760 {803 {
761 mg::DisplayConfigurationOutputId(connector_ids[0]),804 mg::DisplayConfigurationOutputId{0},
762 mg::DisplayConfigurationCardId{0},805 mg::DisplayConfigurationCardId{0},
763 mg::DisplayConfigurationOutputType::composite,806 mg::DisplayConfigurationOutputType::composite,
764 {},807 {},
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches