Merge lp:~alan-griffiths/mir/mesa-hybrid-cursor into lp:mir
- mesa-hybrid-cursor
- Merge into development-branch
Status: | Merged |
---|---|
Approved by: | Alan Griffiths |
Approved revision: | no longer in the source branch. |
Merged at revision: | 4193 |
Proposed branch: | lp:~alan-griffiths/mir/mesa-hybrid-cursor |
Merge into: | lp:mir |
Diff against target: |
547 lines (+259/-63) 7 files modified
src/common/CMakeLists.txt (+2/-1) src/platforms/mesa/server/kms/CMakeLists.txt (+1/-0) src/platforms/mesa/server/kms/cursor.cpp (+117/-31) src/platforms/mesa/server/kms/cursor.h (+21/-7) src/platforms/mesa/server/kms/display.cpp (+3/-14) src/platforms/mesa/server/kms/mutex.h (+104/-0) tests/unit-tests/platforms/mesa/kms/test_cursor.cpp (+11/-10) |
To merge this branch: | bzr merge lp:~alan-griffiths/mir/mesa-hybrid-cursor |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mir CI Bot | continuous-integration | Approve | |
Chris Halse Rogers | Approve | ||
Alberto Aguirre (community) | Approve | ||
Alan Griffiths | Pending | ||
Review via email: mp+325215@code.launchpad.net |
This proposal supersedes a proposal from 2017-03-22.
Commit message
mesa-kms: Support hardware cursors in hybrid setups.
Description of the change
Mir CI Bot (mir-ci-bot) wrote : Posted in a previous version of this proposal | # |
Mir CI Bot (mir-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:4087
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:4087
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:4088
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:4088
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal | # |
It isn't clear why this failed CI (and too much time has passed to see any logs).
It still merges and doesn't obviously break things - so is it still worth landing?
Mir CI Bot (mir-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:4088
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Chris Halse Rogers (raof) wrote : Posted in a previous version of this proposal | # |
It's still worth merging. Once it builds, it seems!
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4191
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Alberto Aguirre (albaguirre) wrote : | # |
+{
87 + const_cast<
88 + const_cast<
Seems like undefined behavior.
Use a unique_ptr with custom deleter instead and move the unique_ptrs on move constructor.
165 + memset(dest, 0, buffer_stride * (gbm_bo_
"gbm_bo_
Alan Griffiths (alan-griffiths) wrote : | # |
> +{
> 87 + const_cast<
> 88 + const_cast<
>
> Seems like undefined behavior.
Fixed
> 165 + memset(dest, 0, buffer_stride * (gbm_bo_
> image_height));
>
> "gbm_bo_
> converted to size_t (so a huge number - Needs a check.
Unless I'm missing something (always possible) there is a check Vis:
if (image_width > min_buffer_width || image_height > min_buffer_height)
{
}
AIUI min_buffer_height ought to be <= gbm_bo_
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4192
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Alberto Aguirre (albaguirre) wrote : | # |
Oh right... it was just a convoluted way of checking yeah.
Mir CI Bot (mir-ci-bot) : | # |
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
SUCCESS: https:/
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Chris Halse Rogers (raof) wrote : | # |
LGTM.
FWIW, this seems to be against our style guide:
191 + {
192 + pad_and_
193 + } else
194 + {
Mir CI Bot (mir-ci-bot) : | # |
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Autolanding.
Unapproved changes made after approval.
https:/
Executed test runs:
SUCCESS: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
SUCCESS: https:/
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4193
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) : | # |
Preview Diff
1 | === modified file 'src/common/CMakeLists.txt' | |||
2 | --- src/common/CMakeLists.txt 2017-05-08 03:04:26 +0000 | |||
3 | +++ src/common/CMakeLists.txt 2017-06-09 10:22:51 +0000 | |||
4 | @@ -20,7 +20,8 @@ | |||
5 | 20 | $<TARGET_OBJECTS:mirtime> | 20 | $<TARGET_OBJECTS:mirtime> |
6 | 21 | ${CMAKE_CURRENT_SOURCE_DIR}/output_type_names.cpp | 21 | ${CMAKE_CURRENT_SOURCE_DIR}/output_type_names.cpp |
7 | 22 | ${CMAKE_CURRENT_SOURCE_DIR}/log.cpp | 22 | ${CMAKE_CURRENT_SOURCE_DIR}/log.cpp |
9 | 23 | ${CMAKE_CURRENT_SOURCE_DIR}/libname.cpp ${PROJECT_SOURCE_DIR}/include/common/mir/libname.h | 23 | ${CMAKE_CURRENT_SOURCE_DIR}/libname.cpp |
10 | 24 | ${PROJECT_SOURCE_DIR}/include/common/mir/libname.h | ||
11 | 24 | ${PROJECT_SOURCE_DIR}/include/common/mir/posix_rw_mutex.h | 25 | ${PROJECT_SOURCE_DIR}/include/common/mir/posix_rw_mutex.h |
12 | 25 | posix_rw_mutex.cpp | 26 | posix_rw_mutex.cpp |
13 | 26 | edid.cpp | 27 | edid.cpp |
14 | 27 | 28 | ||
15 | === modified file 'src/platforms/mesa/server/kms/CMakeLists.txt' | |||
16 | --- src/platforms/mesa/server/kms/CMakeLists.txt 2017-05-17 04:48:46 +0000 | |||
17 | +++ src/platforms/mesa/server/kms/CMakeLists.txt 2017-06-09 10:22:51 +0000 | |||
18 | @@ -43,6 +43,7 @@ | |||
19 | 43 | real_kms_output_container.cpp | 43 | real_kms_output_container.cpp |
20 | 44 | egl_helper.h | 44 | egl_helper.h |
21 | 45 | egl_helper.cpp | 45 | egl_helper.cpp |
22 | 46 | mutex.h | ||
23 | 46 | ) | 47 | ) |
24 | 47 | 48 | ||
25 | 48 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/symbols.map.in | 49 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/symbols.map.in |
26 | 49 | 50 | ||
27 | === modified file 'src/platforms/mesa/server/kms/cursor.cpp' | |||
28 | --- src/platforms/mesa/server/kms/cursor.cpp 2017-05-08 03:04:26 +0000 | |||
29 | +++ src/platforms/mesa/server/kms/cursor.cpp 2017-06-09 10:22:51 +0000 | |||
30 | @@ -88,34 +88,74 @@ | |||
31 | 88 | drmGetCap(fd, DRM_CAP_CURSOR_WIDTH, &width); | 88 | drmGetCap(fd, DRM_CAP_CURSOR_WIDTH, &width); |
32 | 89 | return int(width); | 89 | return int(width); |
33 | 90 | } | 90 | } |
43 | 91 | } | 91 | |
44 | 92 | 92 | gbm_device* gbm_create_device_checked(int fd) | |
45 | 93 | mgm::Cursor::GBMBOWrapper::GBMBOWrapper(gbm_device* gbm) : | 93 | { |
46 | 94 | buffer(gbm_bo_create( | 94 | auto device = gbm_create_device(fd); |
47 | 95 | gbm, | 95 | if (!device) |
48 | 96 | get_drm_cursor_width(gbm_device_get_fd(gbm)), | 96 | { |
49 | 97 | get_drm_cursor_height(gbm_device_get_fd(gbm)), | 97 | BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create gbm device")); |
50 | 98 | GBM_FORMAT_ARGB8888, | 98 | } |
51 | 99 | GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) | 99 | return device; |
52 | 100 | } | ||
53 | 101 | } | ||
54 | 102 | |||
55 | 103 | mgm::Cursor::GBMBOWrapper::GBMBOWrapper(int fd) : | ||
56 | 104 | device{gbm_create_device_checked(fd)}, | ||
57 | 105 | buffer{ | ||
58 | 106 | gbm_bo_create( | ||
59 | 107 | device, | ||
60 | 108 | get_drm_cursor_width(fd), | ||
61 | 109 | get_drm_cursor_height(fd), | ||
62 | 110 | GBM_FORMAT_ARGB8888, | ||
63 | 111 | GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)} | ||
64 | 100 | { | 112 | { |
65 | 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")); |
66 | 102 | } | 114 | } |
67 | 103 | 115 | ||
70 | 104 | inline mgm::Cursor::GBMBOWrapper::operator gbm_bo*() { return buffer; } | 116 | inline mgm::Cursor::GBMBOWrapper::operator gbm_bo*() |
71 | 105 | inline mgm::Cursor::GBMBOWrapper::~GBMBOWrapper() { gbm_bo_destroy(buffer); } | 117 | { |
72 | 118 | return buffer; | ||
73 | 119 | } | ||
74 | 120 | |||
75 | 121 | inline mgm::Cursor::GBMBOWrapper::~GBMBOWrapper() | ||
76 | 122 | { | ||
77 | 123 | if (device) | ||
78 | 124 | gbm_device_destroy(device); | ||
79 | 125 | if (buffer) | ||
80 | 126 | gbm_bo_destroy(buffer); | ||
81 | 127 | } | ||
82 | 128 | |||
83 | 129 | mgm::Cursor::GBMBOWrapper::GBMBOWrapper(GBMBOWrapper&& from) | ||
84 | 130 | : device{from.device}, | ||
85 | 131 | buffer{from.buffer} | ||
86 | 132 | { | ||
87 | 133 | from.buffer = nullptr; | ||
88 | 134 | from.device = nullptr; | ||
89 | 135 | } | ||
90 | 106 | 136 | ||
91 | 107 | mgm::Cursor::Cursor( | 137 | mgm::Cursor::Cursor( |
92 | 108 | gbm_device* gbm, | ||
93 | 109 | KMSOutputContainer& output_container, | 138 | KMSOutputContainer& output_container, |
94 | 110 | std::shared_ptr<CurrentConfiguration> const& current_configuration) : | 139 | std::shared_ptr<CurrentConfiguration> const& current_configuration) : |
95 | 111 | output_container(output_container), | 140 | output_container(output_container), |
96 | 112 | current_position(), | 141 | current_position(), |
97 | 113 | last_set_failed(false), | 142 | last_set_failed(false), |
101 | 114 | buffer(gbm), | 143 | min_buffer_width{std::numeric_limits<uint32_t>::max()}, |
102 | 115 | buffer_width(gbm_bo_get_width(buffer)), | 144 | min_buffer_height{std::numeric_limits<uint32_t>::max()}, |
100 | 116 | buffer_height(gbm_bo_get_height(buffer)), | ||
103 | 117 | current_configuration(current_configuration) | 145 | current_configuration(current_configuration) |
104 | 118 | { | 146 | { |
105 | 147 | // Generate the buffers for the initial configuration. | ||
106 | 148 | current_configuration->with_current_configuration_do( | ||
107 | 149 | [this](KMSDisplayConfiguration const& kms_conf) | ||
108 | 150 | { | ||
109 | 151 | kms_conf.for_each_output( | ||
110 | 152 | [this, &kms_conf](auto const& output) | ||
111 | 153 | { | ||
112 | 154 | // I'm not sure why g++ needs the explicit "this->" but it does - alan_g | ||
113 | 155 | this->buffer_for_output(*kms_conf.get_output_for(output.id)); | ||
114 | 156 | }); | ||
115 | 157 | }); | ||
116 | 158 | |||
117 | 119 | hide(); | 159 | hide(); |
118 | 120 | if (last_set_failed) | 160 | if (last_set_failed) |
119 | 121 | throw std::runtime_error("Initial KMS cursor set failed"); | 161 | throw std::runtime_error("Initial KMS cursor set failed"); |
120 | @@ -126,7 +166,11 @@ | |||
121 | 126 | hide(); | 166 | hide(); |
122 | 127 | } | 167 | } |
123 | 128 | 168 | ||
125 | 129 | void mgm::Cursor::write_buffer_data_locked(std::lock_guard<std::mutex> const&, void const* data, size_t count) | 169 | void mgm::Cursor::write_buffer_data_locked( |
126 | 170 | std::lock_guard<std::mutex> const&, | ||
127 | 171 | gbm_bo* buffer, | ||
128 | 172 | void const* data, | ||
129 | 173 | size_t count) | ||
130 | 130 | { | 174 | { |
131 | 131 | if (auto result = gbm_bo_write(buffer, data, count)) | 175 | if (auto result = gbm_bo_write(buffer, data, count)) |
132 | 132 | { | 176 | { |
133 | @@ -136,20 +180,23 @@ | |||
134 | 136 | } | 180 | } |
135 | 137 | } | 181 | } |
136 | 138 | 182 | ||
138 | 139 | void mgm::Cursor::pad_and_write_image_data_locked(std::lock_guard<std::mutex> const& lg, CursorImage const& image) | 183 | void mgm::Cursor::pad_and_write_image_data_locked( |
139 | 184 | std::lock_guard<std::mutex> const& lg, | ||
140 | 185 | gbm_bo* buffer, | ||
141 | 186 | CursorImage const& image) | ||
142 | 140 | { | 187 | { |
143 | 141 | auto image_argb = static_cast<uint8_t const*>(image.as_argb_8888()); | 188 | auto image_argb = static_cast<uint8_t const*>(image.as_argb_8888()); |
144 | 142 | auto image_width = image.size().width.as_uint32_t(); | 189 | auto image_width = image.size().width.as_uint32_t(); |
145 | 143 | auto image_height = image.size().height.as_uint32_t(); | 190 | auto image_height = image.size().height.as_uint32_t(); |
146 | 144 | auto image_stride = image_width * 4; | 191 | auto image_stride = image_width * 4; |
147 | 145 | 192 | ||
149 | 146 | if (image_width > buffer_width || image_height > buffer_height) | 193 | if (image_width > min_buffer_width || image_height > min_buffer_height) |
150 | 147 | { | 194 | { |
151 | 148 | BOOST_THROW_EXCEPTION(std::logic_error("Image is too big for GBM cursor buffer")); | 195 | BOOST_THROW_EXCEPTION(std::logic_error("Image is too big for GBM cursor buffer")); |
152 | 149 | } | 196 | } |
154 | 150 | 197 | ||
155 | 151 | size_t buffer_stride = gbm_bo_get_stride(buffer); // in bytes | 198 | size_t buffer_stride = gbm_bo_get_stride(buffer); // in bytes |
157 | 152 | size_t padded_size = buffer_stride * buffer_height; | 199 | size_t padded_size = buffer_stride * gbm_bo_get_height(buffer); |
158 | 153 | auto padded = std::unique_ptr<uint8_t[]>(new uint8_t[padded_size]); | 200 | auto padded = std::unique_ptr<uint8_t[]>(new uint8_t[padded_size]); |
159 | 154 | size_t rhs_padding = buffer_stride - image_stride; | 201 | size_t rhs_padding = buffer_stride - image_stride; |
160 | 155 | 202 | ||
161 | @@ -164,9 +211,9 @@ | |||
162 | 164 | src += image_stride; | 211 | src += image_stride; |
163 | 165 | } | 212 | } |
164 | 166 | 213 | ||
166 | 167 | memset(dest, 0, buffer_stride * (buffer_height - image_height)); | 214 | memset(dest, 0, buffer_stride * (gbm_bo_get_height(buffer) - image_height)); |
167 | 168 | 215 | ||
169 | 169 | write_buffer_data_locked(lg, &padded[0], padded_size); | 216 | write_buffer_data_locked(lg, buffer, &padded[0], padded_size); |
170 | 170 | } | 217 | } |
171 | 171 | 218 | ||
172 | 172 | void mgm::Cursor::show() | 219 | void mgm::Cursor::show() |
173 | @@ -186,14 +233,21 @@ | |||
174 | 186 | 233 | ||
175 | 187 | auto const& size = cursor_image.size(); | 234 | auto const& size = cursor_image.size(); |
176 | 188 | 235 | ||
185 | 189 | if (size != geometry::Size{buffer_width, buffer_height}) | 236 | { |
186 | 190 | { | 237 | auto locked_buffers = buffers.lock(); |
187 | 191 | pad_and_write_image_data_locked(lg, cursor_image); | 238 | for (auto& pair : *locked_buffers) |
188 | 192 | } | 239 | { |
189 | 193 | else | 240 | auto& buffer = pair.second; |
190 | 194 | { | 241 | if (size != geometry::Size{gbm_bo_get_width(buffer), gbm_bo_get_height(buffer)}) |
191 | 195 | auto const count = size.width.as_uint32_t() * size.height.as_uint32_t() * sizeof(uint32_t); | 242 | { |
192 | 196 | write_buffer_data_locked(lg, cursor_image.as_argb_8888(), count); | 243 | pad_and_write_image_data_locked(lg, buffer, cursor_image); |
193 | 244 | } | ||
194 | 245 | else | ||
195 | 246 | { | ||
196 | 247 | auto const count = size.width.as_uint32_t() * size.height.as_uint32_t() * sizeof(uint32_t); | ||
197 | 248 | write_buffer_data_locked(lg, buffer, cursor_image.as_argb_8888(), count); | ||
198 | 249 | } | ||
199 | 250 | } | ||
200 | 197 | } | 251 | } |
201 | 198 | hotspot = cursor_image.hotspot(); | 252 | hotspot = cursor_image.hotspot(); |
202 | 199 | 253 | ||
203 | @@ -288,7 +342,7 @@ | |||
204 | 288 | output.move_cursor(geom::Point{} + dp - hotspot); | 342 | output.move_cursor(geom::Point{} + dp - hotspot); |
205 | 289 | if (force_state || !output.has_cursor()) // TODO - or if orientation had changed - then set buffer.. | 343 | if (force_state || !output.has_cursor()) // TODO - or if orientation had changed - then set buffer.. |
206 | 290 | { | 344 | { |
208 | 291 | if (!output.set_cursor(buffer) || !output.has_cursor()) | 345 | if (!output.set_cursor(buffer_for_output(output)) || !output.has_cursor()) |
209 | 292 | set_on_all_outputs = false; | 346 | set_on_all_outputs = false; |
210 | 293 | } | 347 | } |
211 | 294 | } | 348 | } |
212 | @@ -303,3 +357,35 @@ | |||
213 | 303 | 357 | ||
214 | 304 | last_set_failed = !set_on_all_outputs; | 358 | last_set_failed = !set_on_all_outputs; |
215 | 305 | } | 359 | } |
216 | 360 | |||
217 | 361 | gbm_bo* mgm::Cursor::buffer_for_output(KMSOutput const& output) | ||
218 | 362 | { | ||
219 | 363 | auto locked_buffers = buffers.lock(); | ||
220 | 364 | |||
221 | 365 | auto buffer_it = std::find_if( | ||
222 | 366 | locked_buffers->begin(), | ||
223 | 367 | locked_buffers->end(), | ||
224 | 368 | [&output](auto const& candidate) | ||
225 | 369 | { | ||
226 | 370 | return candidate.first == output.drm_fd(); | ||
227 | 371 | }); | ||
228 | 372 | |||
229 | 373 | if (buffer_it != locked_buffers->end()) | ||
230 | 374 | { | ||
231 | 375 | return buffer_it->second; | ||
232 | 376 | } | ||
233 | 377 | |||
234 | 378 | locked_buffers->push_back(std::make_pair(output.drm_fd(), GBMBOWrapper(output.drm_fd()))); | ||
235 | 379 | |||
236 | 380 | gbm_bo* bo = locked_buffers->back().second; | ||
237 | 381 | if (gbm_bo_get_width(bo) < min_buffer_width) | ||
238 | 382 | { | ||
239 | 383 | min_buffer_width = gbm_bo_get_width(bo); | ||
240 | 384 | } | ||
241 | 385 | if (gbm_bo_get_height(bo) < min_buffer_height) | ||
242 | 386 | { | ||
243 | 387 | min_buffer_height = gbm_bo_get_height(bo); | ||
244 | 388 | } | ||
245 | 389 | |||
246 | 390 | return bo; | ||
247 | 391 | } | ||
248 | 306 | 392 | ||
249 | === modified file 'src/platforms/mesa/server/kms/cursor.h' | |||
250 | --- src/platforms/mesa/server/kms/cursor.h 2017-05-25 02:47:48 +0000 | |||
251 | +++ src/platforms/mesa/server/kms/cursor.h 2017-06-09 10:22:51 +0000 | |||
252 | @@ -25,12 +25,14 @@ | |||
253 | 25 | #include "mir/geometry/displacement.h" | 25 | #include "mir/geometry/displacement.h" |
254 | 26 | 26 | ||
255 | 27 | #include "mir_toolkit/common.h" | 27 | #include "mir_toolkit/common.h" |
256 | 28 | #include "mutex.h" | ||
257 | 28 | 29 | ||
258 | 29 | #include <gbm.h> | 30 | #include <gbm.h> |
259 | 30 | 31 | ||
260 | 31 | #include <functional> | 32 | #include <functional> |
261 | 32 | #include <memory> | 33 | #include <memory> |
262 | 33 | #include <mutex> | 34 | #include <mutex> |
263 | 35 | #include <vector> | ||
264 | 34 | 36 | ||
265 | 35 | namespace mir | 37 | namespace mir |
266 | 36 | { | 38 | { |
267 | @@ -67,7 +69,6 @@ | |||
268 | 67 | { | 69 | { |
269 | 68 | public: | 70 | public: |
270 | 69 | Cursor( | 71 | Cursor( |
271 | 70 | gbm_device* device, | ||
272 | 71 | KMSOutputContainer& output_container, | 72 | KMSOutputContainer& output_container, |
273 | 72 | std::shared_ptr<CurrentConfiguration> const& current_configuration); | 73 | std::shared_ptr<CurrentConfiguration> const& current_configuration); |
274 | 73 | 74 | ||
275 | @@ -87,9 +88,18 @@ | |||
276 | 87 | void for_each_used_output(std::function<void(KMSOutput&, geometry::Rectangle const&, MirOrientation orientation)> const& f); | 88 | void for_each_used_output(std::function<void(KMSOutput&, geometry::Rectangle const&, MirOrientation orientation)> const& f); |
277 | 88 | void place_cursor_at(geometry::Point position, ForceCursorState force_state); | 89 | void place_cursor_at(geometry::Point position, ForceCursorState force_state); |
278 | 89 | void place_cursor_at_locked(std::lock_guard<std::mutex> const&, geometry::Point position, ForceCursorState force_state); | 90 | void place_cursor_at_locked(std::lock_guard<std::mutex> const&, geometry::Point position, ForceCursorState force_state); |
281 | 90 | void write_buffer_data_locked(std::lock_guard<std::mutex> const&, void const* data, size_t count); | 91 | void write_buffer_data_locked( |
282 | 91 | void pad_and_write_image_data_locked(std::lock_guard<std::mutex> const&, CursorImage const& image); | 92 | std::lock_guard<std::mutex> const&, |
283 | 93 | gbm_bo* buffer, | ||
284 | 94 | void const* data, | ||
285 | 95 | size_t count); | ||
286 | 96 | void pad_and_write_image_data_locked( | ||
287 | 97 | std::lock_guard<std::mutex> const&, | ||
288 | 98 | gbm_bo* buffer, | ||
289 | 99 | CursorImage const& image); | ||
290 | 92 | void clear(std::lock_guard<std::mutex> const&); | 100 | void clear(std::lock_guard<std::mutex> const&); |
291 | 101 | |||
292 | 102 | gbm_bo* buffer_for_output(KMSOutput const& output); | ||
293 | 93 | 103 | ||
294 | 94 | std::mutex guard; | 104 | std::mutex guard; |
295 | 95 | 105 | ||
296 | @@ -102,17 +112,21 @@ | |||
297 | 102 | 112 | ||
298 | 103 | struct GBMBOWrapper | 113 | struct GBMBOWrapper |
299 | 104 | { | 114 | { |
301 | 105 | GBMBOWrapper(gbm_device* gbm); | 115 | GBMBOWrapper(int fd); |
302 | 106 | operator gbm_bo*(); | 116 | operator gbm_bo*(); |
303 | 107 | ~GBMBOWrapper(); | 117 | ~GBMBOWrapper(); |
304 | 118 | |||
305 | 119 | GBMBOWrapper(GBMBOWrapper&& from); | ||
306 | 108 | private: | 120 | private: |
307 | 121 | gbm_device* device; | ||
308 | 109 | gbm_bo* buffer; | 122 | gbm_bo* buffer; |
309 | 110 | GBMBOWrapper(GBMBOWrapper const&) = delete; | 123 | GBMBOWrapper(GBMBOWrapper const&) = delete; |
310 | 111 | GBMBOWrapper& operator=(GBMBOWrapper const&) = delete; | 124 | GBMBOWrapper& operator=(GBMBOWrapper const&) = delete; |
312 | 112 | } buffer; | 125 | }; |
313 | 126 | Mutex<std::vector<std::pair<int, GBMBOWrapper>>> buffers; | ||
314 | 113 | 127 | ||
317 | 114 | uint32_t buffer_width; | 128 | uint32_t min_buffer_width; |
318 | 115 | uint32_t buffer_height; | 129 | uint32_t min_buffer_height; |
319 | 116 | 130 | ||
320 | 117 | std::shared_ptr<CurrentConfiguration> const current_configuration; | 131 | std::shared_ptr<CurrentConfiguration> const current_configuration; |
321 | 118 | }; | 132 | }; |
322 | 119 | 133 | ||
323 | === modified file 'src/platforms/mesa/server/kms/display.cpp' | |||
324 | --- src/platforms/mesa/server/kms/display.cpp 2017-05-08 03:04:26 +0000 | |||
325 | +++ src/platforms/mesa/server/kms/display.cpp 2017-06-09 10:22:51 +0000 | |||
326 | @@ -339,17 +339,6 @@ | |||
327 | 339 | 339 | ||
328 | 340 | auto mgm::Display::create_hardware_cursor() -> std::shared_ptr<graphics::Cursor> | 340 | auto mgm::Display::create_hardware_cursor() -> std::shared_ptr<graphics::Cursor> |
329 | 341 | { | 341 | { |
330 | 342 | /* | ||
331 | 343 | * TODO: Using the hardware cursor in a hybrid-output situation requires making | ||
332 | 344 | * mgm::Cursor hybrid-aware so it can create a cursor bo on each GPU. | ||
333 | 345 | * | ||
334 | 346 | * For a first cut, just disable the hardware cursor on hybrid systems. | ||
335 | 347 | */ | ||
336 | 348 | if (drm.size() > 1) | ||
337 | 349 | { | ||
338 | 350 | return nullptr; | ||
339 | 351 | } | ||
340 | 352 | |||
341 | 353 | // There is only one hardware cursor. We do not keep a strong reference to it in the display though, | 342 | // There is only one hardware cursor. We do not keep a strong reference to it in the display though, |
342 | 354 | // if no other component of Mir is interested (i.e. the input stack does not keep a reference to send | 343 | // if no other component of Mir is interested (i.e. the input stack does not keep a reference to send |
343 | 355 | // position updates) we must be configured not to use a cursor and thusly let it deallocate. | 344 | // position updates) we must be configured not to use a cursor and thusly let it deallocate. |
344 | @@ -377,9 +366,9 @@ | |||
345 | 377 | 366 | ||
346 | 378 | try | 367 | try |
347 | 379 | { | 368 | { |
351 | 380 | locked_cursor = std::make_shared<Cursor>(gbm->device, | 369 | locked_cursor = std::make_shared<Cursor>( |
352 | 381 | *output_container, | 370 | *output_container, |
353 | 382 | std::make_shared<KMSCurrentConfiguration>(*this)); | 371 | std::make_shared<KMSCurrentConfiguration>(*this)); |
354 | 383 | } | 372 | } |
355 | 384 | catch (std::runtime_error const&) | 373 | catch (std::runtime_error const&) |
356 | 385 | { | 374 | { |
357 | 386 | 375 | ||
358 | === added file 'src/platforms/mesa/server/kms/mutex.h' | |||
359 | --- src/platforms/mesa/server/kms/mutex.h 1970-01-01 00:00:00 +0000 | |||
360 | +++ src/platforms/mesa/server/kms/mutex.h 2017-06-09 10:22:51 +0000 | |||
361 | @@ -0,0 +1,104 @@ | |||
362 | 1 | /* | ||
363 | 2 | * Copyright © 2017 Canonical Ltd. | ||
364 | 3 | * | ||
365 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
366 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
367 | 6 | * as published by the Free Software Foundation. | ||
368 | 7 | * | ||
369 | 8 | * This program is distributed in the hope that it will be useful, | ||
370 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
371 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
372 | 11 | * GNU Lesser General Public License for more details. | ||
373 | 12 | * | ||
374 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
375 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
376 | 15 | * | ||
377 | 16 | * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> | ||
378 | 17 | */ | ||
379 | 18 | |||
380 | 19 | #ifndef MIR_MUTEX_H_ | ||
381 | 20 | #define MIR_MUTEX_H_ | ||
382 | 21 | |||
383 | 22 | #include <mutex> | ||
384 | 23 | |||
385 | 24 | namespace mir | ||
386 | 25 | { | ||
387 | 26 | /** | ||
388 | 27 | * Smart-pointer-esque accessor for Mutex<> protected data. | ||
389 | 28 | * | ||
390 | 29 | * Ensures exclusive access to the referenced data. | ||
391 | 30 | * | ||
392 | 31 | * \tparam Guarded Type of data guarded by the mutex. | ||
393 | 32 | */ | ||
394 | 33 | template<typename Guarded> | ||
395 | 34 | class MutexGuard | ||
396 | 35 | { | ||
397 | 36 | public: | ||
398 | 37 | MutexGuard(std::unique_lock<std::mutex>&& lock, Guarded& value) | ||
399 | 38 | : value{value}, | ||
400 | 39 | lock{std::move(lock)} | ||
401 | 40 | { | ||
402 | 41 | } | ||
403 | 42 | MutexGuard(MutexGuard&& from) = default; | ||
404 | 43 | ~MutexGuard() noexcept(false) | ||
405 | 44 | { | ||
406 | 45 | if (lock.owns_lock()) | ||
407 | 46 | { | ||
408 | 47 | lock.unlock(); | ||
409 | 48 | } | ||
410 | 49 | } | ||
411 | 50 | |||
412 | 51 | Guarded& operator*() | ||
413 | 52 | { | ||
414 | 53 | return value; | ||
415 | 54 | } | ||
416 | 55 | Guarded* operator->() | ||
417 | 56 | { | ||
418 | 57 | return &value; | ||
419 | 58 | } | ||
420 | 59 | private: | ||
421 | 60 | Guarded& value; | ||
422 | 61 | std::unique_lock<std::mutex> lock; | ||
423 | 62 | }; | ||
424 | 63 | |||
425 | 64 | /** | ||
426 | 65 | * A data-locking mutex | ||
427 | 66 | * | ||
428 | 67 | * This is a mutex which owns the data it guards, and can give out a | ||
429 | 68 | * smart-pointer-esque lock to lock and access it. | ||
430 | 69 | * | ||
431 | 70 | * \tparam Guarded The type of data guarded by the mutex | ||
432 | 71 | */ | ||
433 | 72 | template<typename Guarded> | ||
434 | 73 | class Mutex | ||
435 | 74 | { | ||
436 | 75 | public: | ||
437 | 76 | Mutex() = default; | ||
438 | 77 | Mutex(Guarded&& initial_value) | ||
439 | 78 | : value{std::move(initial_value)} | ||
440 | 79 | { | ||
441 | 80 | } | ||
442 | 81 | |||
443 | 82 | Mutex(Mutex const&) = delete; | ||
444 | 83 | Mutex& operator=(Mutex const&) = delete; | ||
445 | 84 | |||
446 | 85 | /** | ||
447 | 86 | * Lock the mutex and return an accessor for the protected data. | ||
448 | 87 | * | ||
449 | 88 | * \return A smart-pointer-esque accessor for the contained data. | ||
450 | 89 | * While code has access to the MutexGuard it is guaranteed to have exclusive | ||
451 | 90 | * access to the contained data. | ||
452 | 91 | */ | ||
453 | 92 | MutexGuard<Guarded> lock() | ||
454 | 93 | { | ||
455 | 94 | return MutexGuard<Guarded>{std::unique_lock<std::mutex>{mutex}, value}; | ||
456 | 95 | } | ||
457 | 96 | |||
458 | 97 | private: | ||
459 | 98 | std::mutex mutex; | ||
460 | 99 | Guarded value; | ||
461 | 100 | }; | ||
462 | 101 | |||
463 | 102 | } | ||
464 | 103 | |||
465 | 104 | #endif //MIR_MUTEX_H_ | ||
466 | 0 | 105 | ||
467 | === modified file 'tests/unit-tests/platforms/mesa/kms/test_cursor.cpp' | |||
468 | --- tests/unit-tests/platforms/mesa/kms/test_cursor.cpp 2017-05-08 03:04:26 +0000 | |||
469 | +++ tests/unit-tests/platforms/mesa/kms/test_cursor.cpp 2017-06-09 10:22:51 +0000 | |||
470 | @@ -300,7 +300,7 @@ | |||
471 | 300 | 300 | ||
472 | 301 | size_t const cursor_side{64}; | 301 | size_t const cursor_side{64}; |
473 | 302 | MesaCursorTest() | 302 | MesaCursorTest() |
475 | 303 | : cursor{mock_gbm.fake_gbm.device, output_container, | 303 | : cursor{output_container, |
476 | 304 | mt::fake_shared(current_configuration)} | 304 | mt::fake_shared(current_configuration)} |
477 | 305 | { | 305 | { |
478 | 306 | using namespace ::testing; | 306 | using namespace ::testing; |
479 | @@ -351,7 +351,7 @@ | |||
480 | 351 | GBM_FORMAT_ARGB8888, | 351 | GBM_FORMAT_ARGB8888, |
481 | 352 | GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)); | 352 | GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)); |
482 | 353 | 353 | ||
484 | 354 | mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, | 354 | mgm::Cursor cursor_tmp{output_container, |
485 | 355 | std::make_shared<StubCurrentConfiguration>(output_container)}; | 355 | std::make_shared<StubCurrentConfiguration>(output_container)}; |
486 | 356 | } | 356 | } |
487 | 357 | 357 | ||
488 | @@ -359,10 +359,11 @@ | |||
489 | 359 | { | 359 | { |
490 | 360 | using namespace ::testing; | 360 | using namespace ::testing; |
491 | 361 | 361 | ||
494 | 362 | EXPECT_CALL(mock_gbm, gbm_bo_get_width(_)); | 362 | // Our standard mock DRM has 2 GPU devices, and we should construct a cursor on both. |
495 | 363 | EXPECT_CALL(mock_gbm, gbm_bo_get_height(_)); | 363 | EXPECT_CALL(mock_gbm, gbm_bo_get_width(_)).Times(2); |
496 | 364 | EXPECT_CALL(mock_gbm, gbm_bo_get_height(_)).Times(2); | ||
497 | 364 | 365 | ||
499 | 365 | mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, | 366 | mgm::Cursor cursor_tmp{output_container, |
500 | 366 | std::make_shared<StubCurrentConfiguration>(output_container)}; | 367 | std::make_shared<StubCurrentConfiguration>(output_container)}; |
501 | 367 | } | 368 | } |
502 | 368 | 369 | ||
503 | @@ -377,7 +378,7 @@ | |||
504 | 377 | 378 | ||
505 | 378 | EXPECT_CALL(mock_gbm, gbm_bo_create(_, drm_buffer_size, drm_buffer_size, _, _)); | 379 | EXPECT_CALL(mock_gbm, gbm_bo_create(_, drm_buffer_size, drm_buffer_size, _, _)); |
506 | 379 | 380 | ||
508 | 380 | mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, | 381 | mgm::Cursor cursor_tmp{output_container, |
509 | 381 | std::make_shared<StubCurrentConfiguration>(output_container)}; | 382 | std::make_shared<StubCurrentConfiguration>(output_container)}; |
510 | 382 | } | 383 | } |
511 | 383 | 384 | ||
512 | @@ -394,7 +395,7 @@ | |||
513 | 394 | 395 | ||
514 | 395 | EXPECT_CALL(mock_gbm, gbm_bo_create(_, 64, 64, _, _)); | 396 | EXPECT_CALL(mock_gbm, gbm_bo_create(_, 64, 64, _, _)); |
515 | 396 | 397 | ||
517 | 397 | mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, | 398 | mgm::Cursor cursor_tmp{output_container, |
518 | 398 | std::make_shared<StubCurrentConfiguration>(output_container)}; | 399 | std::make_shared<StubCurrentConfiguration>(output_container)}; |
519 | 399 | } | 400 | } |
520 | 400 | 401 | ||
521 | @@ -458,7 +459,7 @@ | |||
522 | 458 | 459 | ||
523 | 459 | EXPECT_CALL(mock_gbm, gbm_bo_write(mock_gbm.fake_gbm.bo, ContainsASingleWhitePixel(width*height), buffer_size_bytes)); | 460 | EXPECT_CALL(mock_gbm, gbm_bo_write(mock_gbm.fake_gbm.bo, ContainsASingleWhitePixel(width*height), buffer_size_bytes)); |
524 | 460 | 461 | ||
526 | 461 | mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, | 462 | mgm::Cursor cursor_tmp{output_container, |
527 | 462 | std::make_shared<StubCurrentConfiguration>(output_container)}; | 463 | std::make_shared<StubCurrentConfiguration>(output_container)}; |
528 | 463 | cursor_tmp.show(SinglePixelCursorImage()); | 464 | cursor_tmp.show(SinglePixelCursorImage()); |
529 | 464 | } | 465 | } |
530 | @@ -495,7 +496,7 @@ | |||
531 | 495 | EXPECT_CALL(*output_container.outputs[1], has_cursor()).Times(0); | 496 | EXPECT_CALL(*output_container.outputs[1], has_cursor()).Times(0); |
532 | 496 | EXPECT_CALL(*output_container.outputs[2], has_cursor()).Times(0); | 497 | EXPECT_CALL(*output_container.outputs[2], has_cursor()).Times(0); |
533 | 497 | 498 | ||
535 | 498 | mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, | 499 | mgm::Cursor cursor_tmp{output_container, |
536 | 499 | std::make_shared<StubCurrentConfiguration>(output_container)}; | 500 | std::make_shared<StubCurrentConfiguration>(output_container)}; |
537 | 500 | 501 | ||
538 | 501 | output_container.verify_and_clear_expectations(); | 502 | output_container.verify_and_clear_expectations(); |
539 | @@ -513,7 +514,7 @@ | |||
540 | 513 | .WillByDefault(Return(false)); | 514 | .WillByDefault(Return(false)); |
541 | 514 | 515 | ||
542 | 515 | EXPECT_THROW( | 516 | EXPECT_THROW( |
544 | 516 | mgm::Cursor cursor_tmp(mock_gbm.fake_gbm.device, output_container, | 517 | mgm::Cursor cursor_tmp(output_container, |
545 | 517 | std::make_shared<StubCurrentConfiguration>(output_container)); | 518 | std::make_shared<StubCurrentConfiguration>(output_container)); |
546 | 518 | , std::runtime_error); | 519 | , std::runtime_error); |
547 | 519 | } | 520 | } |
FAILED: Continuous integration, rev:4086 /mir-jenkins. ubuntu. com/job/ mir-ci/ 3208/ /mir-jenkins. ubuntu. com/job/ build-mir/ 4317/console /mir-jenkins. ubuntu. com/job/ build-0- fetch/4404/ console /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= vivid+overlay/ 4394/console /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= xenial+ overlay/ 4394/console /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= zesty/4394/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= zesty/4349/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= xenial+ overlay/ 4349/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= zesty/4349/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= android, release= vivid+overlay/ 4349/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= android, release= vivid+overlay/ 4349/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= mesa,release= xenial+ overlay/ 4349/console
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /mir-jenkins. ubuntu. com/job/ mir-ci/ 3208/rebuild
https:/