Merge lp:~raof/mir/eglstream-platform into lp:mir
- eglstream-platform
- Merge into development-branch
Status: | Merged |
---|---|
Merged at revision: | 3537 |
Proposed branch: | lp:~raof/mir/eglstream-platform |
Merge into: | lp:mir |
Diff against target: |
2843 lines (+2556/-12) 32 files modified
.bzrignore (+3/-0) CMakeLists.txt (+9/-2) include/client/mir_toolkit/client_types.h (+2/-1) src/platforms/CMakeLists.txt (+5/-1) src/platforms/eglstream-kms/CMakeLists.txt (+2/-0) src/platforms/eglstream-kms/client/CMakeLists.txt (+39/-0) src/platforms/eglstream-kms/client/client_buffer.cpp (+159/-0) src/platforms/eglstream-kms/client/client_buffer.h (+67/-0) src/platforms/eglstream-kms/client/client_buffer_factory.cpp (+36/-0) src/platforms/eglstream-kms/client/client_buffer_factory.h (+45/-0) src/platforms/eglstream-kms/client/client_platform.cpp (+77/-0) src/platforms/eglstream-kms/client/client_platform.h (+52/-0) src/platforms/eglstream-kms/client/client_platform_factory.cpp (+59/-0) src/platforms/eglstream-kms/client/symbols.map (+7/-0) src/platforms/eglstream-kms/server/CMakeLists.txt (+55/-0) src/platforms/eglstream-kms/server/buffer_allocator.cpp (+69/-0) src/platforms/eglstream-kms/server/buffer_allocator.h (+48/-0) src/platforms/eglstream-kms/server/display.cpp (+363/-0) src/platforms/eglstream-kms/server/display.h (+82/-0) src/platforms/eglstream-kms/server/egl_output.cpp (+359/-0) src/platforms/eglstream-kms/server/egl_output.h (+79/-0) src/platforms/eglstream-kms/server/kms_display_configuration.cpp (+331/-0) src/platforms/eglstream-kms/server/kms_display_configuration.h (+70/-0) src/platforms/eglstream-kms/server/platform.cpp (+162/-0) src/platforms/eglstream-kms/server/platform.h (+66/-0) src/platforms/eglstream-kms/server/platform_symbols.cpp (+189/-0) src/platforms/eglstream-kms/server/symbols.map.in (+10/-0) src/platforms/mesa/server/kms/platform_symbols.cpp (+14/-0) tests/unit-tests/CMakeLists.txt (+4/-0) tests/unit-tests/client/test_probing_client_platform_factory.cpp (+32/-0) tests/unit-tests/graphics/mesa/kms/test_platform.cpp (+47/-2) tests/unit-tests/graphics/test_platform_prober.cpp (+14/-6) |
To merge this branch: | bzr merge lp:~raof/mir/eglstream-platform |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mir CI Bot | continuous-integration | Needs Fixing | |
Alan Griffiths | Abstain | ||
Cemil Azizoglu (community) | Needs Fixing | ||
Daniel van Vugt | Pending | ||
Review via email: mp+296389@code.launchpad.net |
Commit message
Add an EGLStream client platform and KMS-backed EGLStream server platform.
Currently supports only software clients, as hooking into nvidia's EGL is not (yet?) possible.
Description of the change
The actual EGLStream platform
Mir CI Bot (mir-ci-bot) wrote : | # |
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:3540
https:/
Executed test runs:
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:/
Click here to trigger a rebuild:
https:/
Alan Griffiths (alan-griffiths) wrote : | # |
No tests needed?
Cemil Azizoglu (cemil-azizoglu) wrote : | # |
Are there eglstream implementations that don't use kms? Instead of eglstream-kms, can't we call it eglstream?
-------
Couldn't mesa-kms and/or mesa-x11 return mg::PlatformPri
2489 +mg::PlatformPr
-------
Are guest platforms a TODO or will they be unsupported forever? A comment would be good either way?
2587 +mir::UniqueMod
2588 + std::shared_
2589 + std::shared_
2590 +{
2591 + mir::assert_
2592 + return nullptr;
2593 +}
-------
Isn't this going to require root? Can drm render nodes not be used instead?
2238 + if (drmSetMaster(
-------
Any reason why we don't request alpha? I changed mir-on-X on Gerry's request that he needed alpha. May want to do the same here.
907 + EGL_ALPHA_SIZE, 0,
-------
This only supports software rendering but we should still check for
if (buffer_
and throw if hardware is requested.
765 +std::shared_
766
-------
s/falied/failed
+ "Platform claims to support EGL_EXT_
-------
Probably be better handled by returning unsupported as was done to get the device_count.
2533 + if (eglQueryDevice
2534 + {
2535 + BOOST_THROW_
2536 + }
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:3541
https:/
Executed test runs:
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:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:3542
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:3544
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:3544
https:/
Executed test runs:
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:/
Click here to trigger a rebuild:
https:/
Alan Griffiths (alan-griffiths) wrote : | # |
This seems like a lot of code with only the most trivial of tests. But I guess there's a limit to what we can test when it is supplying our abstraction over the drivers.
Chris Halse Rogers (raof) wrote : | # |
There are some more tests that I could usefully write; I'll write some tests that the platform-probe succeeds in appropriate environments and fails in inappropriate ones.
I'll also investigate writing a dummy KMS driver; that would let us usefully test a whole lot more.
Alan Griffiths (alan-griffiths) wrote : | # |
We can land wit ha promise of tests later
Mir CI Bot (mir-ci-bot) : | # |
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:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Chris Halse Rogers (raof) wrote : | # |
Ahem. Post-merge response to Cemil.
> Are there eglstream implementations that don't use kms? Instead of eglstream-kms, can't we call > it eglstream?
nvidia currently have non-KMS platforms that use EGLStream (they've talked about OpenWF things). We don't currently have non-KMS EGLStream platforms, but they're plausible.
> Couldn't mesa-kms and/or mesa-x11 return mg::PlatformPri
This was resolved by commits (and tests!) subsequent to this question; mesa-kms now checks for the EGL_MESA_
> Are guest platforms a TODO or will they be unsupported forever? A comment would be good either > way?
Guest platforms are a TODO, albeit a TODO that I hope to delay until it no longer needs to be done (splitting output from buffer-allocation will remove guest platforms entirely)
> Isn't this going to require root? Can drm render nodes not be used instead?
> 2238 + if (drmSetMaster(
Yes, this requires root (or to already be master, IIRC). You need to be master to do modesetting, so the platform can't work unless it's master.
Render nodes (as their name suggests) can't do modesetting ☺
> Any reason why we don't request alpha? I changed mir-on-X on Gerry's request that he needed
> alpha. May want to do the same here.
> 907 + EGL_ALPHA_SIZE, 0,
Hm, probably. I'll look into this when I get home and can test it.
Preview Diff
1 | === added file '.bzrignore' |
2 | --- .bzrignore 1970-01-01 00:00:00 +0000 |
3 | +++ .bzrignore 2016-06-08 13:28:19 +0000 |
4 | @@ -0,0 +1,3 @@ |
5 | +.idea |
6 | +build |
7 | +include/server/mir/version.h |
8 | |
9 | === modified file 'CMakeLists.txt' |
10 | --- CMakeLists.txt 2016-06-02 05:33:50 +0000 |
11 | +++ CMakeLists.txt 2016-06-08 13:28:19 +0000 |
12 | @@ -163,10 +163,10 @@ |
13 | # Default to KMS backend, but build all of them |
14 | set( |
15 | MIR_PLATFORM |
16 | - mesa-kms;android;mesa-x11 |
17 | + mesa-kms;android;mesa-x11;eglstream-kms |
18 | CACHE |
19 | STRING |
20 | - "a list of graphics backends to build (options are 'mesa-kms', 'android' or 'mesa-x11')" |
21 | + "a list of graphics backends to build (options are 'mesa-kms', 'android', 'mesa-x11', or 'eglstream-kms')" |
22 | ) |
23 | |
24 | list(GET MIR_PLATFORM 0 MIR_TEST_PLATFORM) |
25 | @@ -183,6 +183,9 @@ |
26 | if (platform STREQUAL "mesa-x11") |
27 | set(MIR_BUILD_PLATFORM_MESA_X11 TRUE) |
28 | endif() |
29 | + if (platform STREQUAL "eglstream-kms") |
30 | + set(MIR_BUILD_PLATFORM_EGLSTREAM_KMS TRUE) |
31 | + endif() |
32 | endforeach(platform) |
33 | |
34 | find_package(EGL REQUIRED) |
35 | @@ -245,6 +248,10 @@ |
36 | pkg_check_modules( DRM REQUIRED libdrm ) |
37 | endif() |
38 | |
39 | +if (MIR_BUILD_PLATFORM_EGLSTREAM_KMS) |
40 | + pkg_check_modules(EPOXY REQUIRED epoxy) |
41 | +endif() |
42 | + |
43 | set(MIR_ANDROID_INCLUDE_DIRECTORIES) # to be filled by android-input |
44 | set(MIR_ANDROID_INPUT_COMPILE_FLAGS) # to be filled by android-input |
45 | set(MIR_3RD_PARTY_INCLUDE_DIRECTORIES) |
46 | |
47 | === modified file 'include/client/mir_toolkit/client_types.h' |
48 | --- include/client/mir_toolkit/client_types.h 2016-05-03 06:55:25 +0000 |
49 | +++ include/client/mir_toolkit/client_types.h 2016-06-08 13:28:19 +0000 |
50 | @@ -200,7 +200,8 @@ |
51 | typedef enum MirPlatformType |
52 | { |
53 | mir_platform_type_gbm, |
54 | - mir_platform_type_android |
55 | + mir_platform_type_android, |
56 | + mir_platform_type_eglstream, |
57 | } MirPlatformType; |
58 | |
59 | typedef struct MirPlatformPackage |
60 | |
61 | === modified file 'src/platforms/CMakeLists.txt' |
62 | --- src/platforms/CMakeLists.txt 2016-05-03 06:55:25 +0000 |
63 | +++ src/platforms/CMakeLists.txt 2016-06-08 13:28:19 +0000 |
64 | @@ -71,4 +71,8 @@ |
65 | add_subdirectory(android/) |
66 | endif() |
67 | |
68 | - add_subdirectory(evdev/) |
69 | +if (MIR_BUILD_PLATFORM_EGLSTREAM_KMS) |
70 | + add_subdirectory(eglstream-kms) |
71 | +endif() |
72 | + |
73 | +add_subdirectory(evdev/) |
74 | |
75 | === added directory 'src/platforms/eglstream-kms' |
76 | === added file 'src/platforms/eglstream-kms/CMakeLists.txt' |
77 | --- src/platforms/eglstream-kms/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
78 | +++ src/platforms/eglstream-kms/CMakeLists.txt 2016-06-08 13:28:19 +0000 |
79 | @@ -0,0 +1,2 @@ |
80 | +add_subdirectory(client/) |
81 | +add_subdirectory(server/) |
82 | |
83 | === added directory 'src/platforms/eglstream-kms/client' |
84 | === added file 'src/platforms/eglstream-kms/client/CMakeLists.txt' |
85 | --- src/platforms/eglstream-kms/client/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
86 | +++ src/platforms/eglstream-kms/client/CMakeLists.txt 2016-06-08 13:28:19 +0000 |
87 | @@ -0,0 +1,39 @@ |
88 | +include_directories(${client_common_include_dirs}) |
89 | + |
90 | +include_directories( |
91 | + ${DRM_INCLUDE_DIRS} |
92 | + ${EGL_INCLUDE_DIRS} |
93 | +) |
94 | + |
95 | +set(symbol_map ${CMAKE_CURRENT_SOURCE_DIR}/symbols.map) |
96 | + |
97 | +add_library(mirclientplatformeglstreamobjects OBJECT |
98 | + client_platform_factory.cpp |
99 | + client_platform.cpp |
100 | + client_buffer_factory.cpp |
101 | + client_buffer.cpp |
102 | +) |
103 | + |
104 | +mir_add_library_with_symbols( |
105 | + mirclientplatformeglstream MODULE |
106 | + |
107 | + ${symbol_map} |
108 | + $<TARGET_OBJECTS:mirclientplatformeglstreamobjects> |
109 | +) |
110 | + |
111 | +set_target_properties( |
112 | + mirclientplatformeglstream PROPERTIES |
113 | + OUTPUT_NAME eglstream |
114 | + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/client-modules |
115 | + PREFIX "" |
116 | + SUFFIX ".so.${MIR_CLIENT_PLATFORM_ABI}" |
117 | + LINK_FLAGS "-Wl,--version-script,${symbol_map}" |
118 | +) |
119 | + |
120 | +target_link_libraries(mirclientplatformeglstream |
121 | + mirclient |
122 | + client_platform_common |
123 | + ${DRM_LDFLAGS} ${DRM_LIBRARIES} |
124 | +) |
125 | + |
126 | +install(TARGETS mirclientplatformeglstream LIBRARY DESTINATION ${MIR_CLIENT_PLATFORM_PATH}) |
127 | |
128 | === added file 'src/platforms/eglstream-kms/client/client_buffer.cpp' |
129 | --- src/platforms/eglstream-kms/client/client_buffer.cpp 1970-01-01 00:00:00 +0000 |
130 | +++ src/platforms/eglstream-kms/client/client_buffer.cpp 2016-06-08 13:28:19 +0000 |
131 | @@ -0,0 +1,159 @@ |
132 | +/* |
133 | + * Copyright © 2016 Canonical Ltd. |
134 | + * |
135 | + * This program is free software: you can redistribute it and/or modify it |
136 | + * under the terms of the GNU Lesser General Public License version 3, |
137 | + * as published by the Free Software Foundation. |
138 | + * |
139 | + * This program is distributed in the hope that it will be useful, |
140 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
141 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
142 | + * GNU Lesser General Public License for more details. |
143 | + * |
144 | + * You should have received a copy of the GNU Lesser General Public License |
145 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
146 | + * |
147 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
148 | + */ |
149 | + |
150 | +#include "mir_toolkit/mir_client_library.h" |
151 | +#include "client_buffer.h" |
152 | + |
153 | +#include <boost/throw_exception.hpp> |
154 | + |
155 | +#include <stdexcept> |
156 | +#include <system_error> |
157 | + |
158 | +#include <errno.h> |
159 | +#include <sys/mman.h> |
160 | + |
161 | +namespace mcl=mir::client; |
162 | +namespace mcle=mir::client::eglstream; |
163 | +namespace geom=mir::geometry; |
164 | + |
165 | +namespace |
166 | +{ |
167 | + |
168 | +struct ShmMemoryRegion : mcl::MemoryRegion |
169 | +{ |
170 | + ShmMemoryRegion( |
171 | + int buffer_fd, |
172 | + geom::Size const& size_param, |
173 | + geom::Stride stride_param, |
174 | + MirPixelFormat format_param) |
175 | + : size_in_bytes{size_param.height.as_uint32_t() * stride_param.as_uint32_t()} |
176 | + { |
177 | + static off_t const map_offset = 0; |
178 | + width = size_param.width; |
179 | + height = size_param.height; |
180 | + stride = stride_param; |
181 | + format = format_param; |
182 | + |
183 | + void* map = mmap( |
184 | + nullptr, |
185 | + size_in_bytes, |
186 | + PROT_READ | PROT_WRITE, |
187 | + MAP_SHARED, |
188 | + buffer_fd, |
189 | + map_offset); |
190 | + if (map == MAP_FAILED) |
191 | + { |
192 | + BOOST_THROW_EXCEPTION(( |
193 | + std::system_error{errno, std::system_category(), "Failed to mmap buffer"})); |
194 | + } |
195 | + |
196 | + vaddr = std::shared_ptr<char>(static_cast<char*>(map), [](auto*) noexcept {}); |
197 | + } |
198 | + |
199 | + ~ShmMemoryRegion() |
200 | + { |
201 | + munmap(vaddr.get(), size_in_bytes); |
202 | + } |
203 | + |
204 | + size_t const size_in_bytes; |
205 | +}; |
206 | + |
207 | +} |
208 | + |
209 | +mcle::ClientBuffer::ClientBuffer( |
210 | + std::shared_ptr<MirBufferPackage> const& package, |
211 | + geom::Size size, MirPixelFormat pf) |
212 | + : creation_package{package}, |
213 | + rect({geom::Point{0, 0}, size}), |
214 | + buffer_pf{pf} |
215 | +{ |
216 | + if (package->fd_items != 1) |
217 | + { |
218 | + BOOST_THROW_EXCEPTION(std::runtime_error( |
219 | + "Buffer package does not contain the expected number of fd items")); |
220 | + } |
221 | +} |
222 | + |
223 | +mcle::ClientBuffer::~ClientBuffer() noexcept |
224 | +{ |
225 | + // TODO (@raof): Error reporting? It should not be possible for this to fail; if it does, |
226 | + // something's seriously wrong. |
227 | + close(creation_package->fd[0]); |
228 | +} |
229 | + |
230 | +std::shared_ptr<mcl::MemoryRegion> mcle::ClientBuffer::secure_for_cpu_write() |
231 | +{ |
232 | + int const buffer_fd = creation_package->fd[0]; |
233 | + |
234 | + return std::make_shared<ShmMemoryRegion>( |
235 | + buffer_fd, |
236 | + size(), |
237 | + stride(), |
238 | + pixel_format()); |
239 | +} |
240 | + |
241 | +geom::Size mcle::ClientBuffer::size() const |
242 | +{ |
243 | + return rect.size; |
244 | +} |
245 | + |
246 | +geom::Stride mcle::ClientBuffer::stride() const |
247 | +{ |
248 | + return geom::Stride{creation_package->stride}; |
249 | +} |
250 | + |
251 | +MirPixelFormat mcle::ClientBuffer::pixel_format() const |
252 | +{ |
253 | + return buffer_pf; |
254 | +} |
255 | + |
256 | +std::shared_ptr<MirNativeBuffer> mcle::ClientBuffer::native_buffer_handle() const |
257 | +{ |
258 | + creation_package->age = age(); |
259 | + return creation_package; |
260 | +} |
261 | + |
262 | +void mcle::ClientBuffer::update_from(MirBufferPackage const&) |
263 | +{ |
264 | +} |
265 | + |
266 | +void mcle::ClientBuffer::fill_update_msg(MirBufferPackage& package) |
267 | +{ |
268 | + package.data_items = 0; |
269 | + package.fd_items = 0; |
270 | +} |
271 | + |
272 | +MirNativeBuffer* mcle::ClientBuffer::as_mir_native_buffer() const |
273 | +{ |
274 | + //mesa has a POD native type for now. can return it directly to client API. |
275 | + return native_buffer_handle().get(); |
276 | +} |
277 | + |
278 | +void mcle::ClientBuffer::set_fence(MirNativeFence*, MirBufferAccess) |
279 | +{ |
280 | +} |
281 | + |
282 | +MirNativeFence* mcle::ClientBuffer::get_fence() const |
283 | +{ |
284 | + return nullptr; |
285 | +} |
286 | + |
287 | +bool mcle::ClientBuffer::wait_fence(MirBufferAccess, std::chrono::nanoseconds) |
288 | +{ |
289 | + return true; |
290 | +} |
291 | |
292 | === added file 'src/platforms/eglstream-kms/client/client_buffer.h' |
293 | --- src/platforms/eglstream-kms/client/client_buffer.h 1970-01-01 00:00:00 +0000 |
294 | +++ src/platforms/eglstream-kms/client/client_buffer.h 2016-06-08 13:28:19 +0000 |
295 | @@ -0,0 +1,67 @@ |
296 | +/* |
297 | + * Copyright © 2016 Canonical Ltd. |
298 | + * |
299 | + * This program is free software: you can redistribute it and/or modify it |
300 | + * under the terms of the GNU Lesser General Public License version 3, |
301 | + * as published by the Free Software Foundation. |
302 | + * |
303 | + * This program is distributed in the hope that it will be useful, |
304 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
305 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
306 | + * GNU Lesser General Public License for more details. |
307 | + * |
308 | + * You should have received a copy of the GNU Lesser General Public License |
309 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
310 | + * |
311 | + * Authored by: |
312 | + * Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
313 | + */ |
314 | + |
315 | +#ifndef MIR_CLIENT_EGLSTREAM_CLIENT_BUFFER_H_ |
316 | +#define MIR_CLIENT_EGLSTREAM_CLIENT_BUFFER_H_ |
317 | + |
318 | +#include "mir/aging_buffer.h" |
319 | +#include "mir_toolkit/mir_client_library.h" |
320 | +#include "mir/geometry/rectangle.h" |
321 | + |
322 | +#include <memory> |
323 | + |
324 | +namespace mir |
325 | +{ |
326 | +namespace client |
327 | +{ |
328 | +namespace eglstream |
329 | +{ |
330 | + |
331 | +class ClientBuffer : public AgingBuffer |
332 | +{ |
333 | +public: |
334 | + ClientBuffer( |
335 | + std::shared_ptr<MirBufferPackage> const& buffer_package, |
336 | + geometry::Size size, |
337 | + MirPixelFormat pf); |
338 | + |
339 | + ~ClientBuffer() noexcept; |
340 | + |
341 | + std::shared_ptr<MemoryRegion> secure_for_cpu_write(); |
342 | + geometry::Size size() const; |
343 | + geometry::Stride stride() const; |
344 | + MirPixelFormat pixel_format() const; |
345 | + std::shared_ptr<MirNativeBuffer> native_buffer_handle() const; |
346 | + void update_from(MirBufferPackage const&); |
347 | + void fill_update_msg(MirBufferPackage&); |
348 | + MirNativeBuffer* as_mir_native_buffer() const; |
349 | + void set_fence(MirNativeFence*, MirBufferAccess); |
350 | + MirNativeFence* get_fence() const; |
351 | + bool wait_fence(MirBufferAccess, std::chrono::nanoseconds timeout); |
352 | + |
353 | +private: |
354 | + std::shared_ptr<MirBufferPackage> const creation_package; |
355 | + geometry::Rectangle const rect; |
356 | + MirPixelFormat const buffer_pf; |
357 | +}; |
358 | + |
359 | +} |
360 | +} |
361 | +} |
362 | +#endif /* MIR_CLIENT_EGLSTREAM_CLIENT_BUFFER_H_ */ |
363 | |
364 | === added file 'src/platforms/eglstream-kms/client/client_buffer_factory.cpp' |
365 | --- src/platforms/eglstream-kms/client/client_buffer_factory.cpp 1970-01-01 00:00:00 +0000 |
366 | +++ src/platforms/eglstream-kms/client/client_buffer_factory.cpp 2016-06-08 13:28:19 +0000 |
367 | @@ -0,0 +1,36 @@ |
368 | +/* |
369 | + * Copyright © 2016 Canonical Ltd. |
370 | + * |
371 | + * This program is free software: you can redistribute it and/or modify |
372 | + * it under the terms of the GNU Lesser General Public License version 3 as |
373 | + * published by the Free Software Foundation. |
374 | + * |
375 | + * This program is distributed in the hope that it will be useful, |
376 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
377 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
378 | + * GNU Lesser General Public License for more details. |
379 | + * |
380 | + * You should have received a copy of the GNU Lesser General Public License |
381 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
382 | + * |
383 | + * Authored by: |
384 | + * Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
385 | + */ |
386 | + |
387 | +#include "client_buffer_factory.h" |
388 | +#include "client_buffer.h" |
389 | + |
390 | +namespace mcl=mir::client; |
391 | +namespace mcle=mir::client::eglstream; |
392 | + |
393 | +std::shared_ptr<mcl::ClientBuffer> |
394 | +mcle::ClientBufferFactory::create_buffer( |
395 | + std::shared_ptr<MirBufferPackage> const& package, |
396 | + geometry::Size /*size*/, |
397 | + MirPixelFormat pf) |
398 | +{ |
399 | + return std::make_shared<mcle::ClientBuffer>( |
400 | + package, |
401 | + geometry::Size{package->width, package->height}, |
402 | + pf); |
403 | +} |
404 | |
405 | === added file 'src/platforms/eglstream-kms/client/client_buffer_factory.h' |
406 | --- src/platforms/eglstream-kms/client/client_buffer_factory.h 1970-01-01 00:00:00 +0000 |
407 | +++ src/platforms/eglstream-kms/client/client_buffer_factory.h 2016-06-08 13:28:19 +0000 |
408 | @@ -0,0 +1,45 @@ |
409 | +/* |
410 | + * Copyright © 2016 Canonical Ltd. |
411 | + * |
412 | + * This program is free software: you can redistribute it and/or modify |
413 | + * it under the terms of the GNU Lesser General Public License version 3 as |
414 | + * published by the Free Software Foundation. |
415 | + * |
416 | + * This program is distributed in the hope that it will be useful, |
417 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
418 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
419 | + * GNU Lesser General Public License for more details. |
420 | + * |
421 | + * You should have received a copy of the GNU Lesser General Public License |
422 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
423 | + * |
424 | + * Authored by: |
425 | + * Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
426 | + */ |
427 | + |
428 | +#ifndef MIR_CLIENT_EGLSTREAM_CLIENT_BUFFER_FACTORY_H_ |
429 | +#define MIR_CLIENT_EGLSTREAM_CLIENT_BUFFER_FACTORY_H_ |
430 | + |
431 | +#include "mir/client_buffer_factory.h" |
432 | + |
433 | +namespace mir |
434 | +{ |
435 | +namespace client |
436 | +{ |
437 | +namespace eglstream |
438 | +{ |
439 | + |
440 | +class ClientBufferFactory : public client::ClientBufferFactory |
441 | +{ |
442 | +public: |
443 | + ClientBufferFactory() = default; |
444 | + |
445 | + std::shared_ptr<client::ClientBuffer> create_buffer( |
446 | + std::shared_ptr<MirBufferPackage> const& package, |
447 | + geometry::Size size, MirPixelFormat pf); |
448 | +}; |
449 | + |
450 | +} |
451 | +} |
452 | +} |
453 | +#endif /* MIR_CLIENT_EGLSTREAM_CLIENT_BUFFER_FACTORY_H_ */ |
454 | |
455 | === added file 'src/platforms/eglstream-kms/client/client_platform.cpp' |
456 | --- src/platforms/eglstream-kms/client/client_platform.cpp 1970-01-01 00:00:00 +0000 |
457 | +++ src/platforms/eglstream-kms/client/client_platform.cpp 2016-06-08 13:28:19 +0000 |
458 | @@ -0,0 +1,77 @@ |
459 | +/* |
460 | + * Copyright © 2016 Canonical Ltd. |
461 | + * |
462 | + * This program is free software: you can redistribute it and/or modify it |
463 | + * under the terms of the GNU Lesser General Public License version 3, |
464 | + * as published by the Free Software Foundation. |
465 | + * |
466 | + * This program is distributed in the hope that it will be useful, |
467 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
468 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
469 | + * GNU Lesser General Public License for more details. |
470 | + * |
471 | + * You should have received a copy of the GNU Lesser General Public License |
472 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
473 | + * |
474 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
475 | + */ |
476 | + |
477 | +#include "mir_toolkit/mir_client_library.h" |
478 | +#include "client_platform.h" |
479 | +#include "client_buffer_factory.h" |
480 | +#include "mir/client_buffer_factory.h" |
481 | +#include "mir/client_context.h" |
482 | + |
483 | +#include <cstring> |
484 | +#include <boost/throw_exception.hpp> |
485 | + |
486 | +namespace mcl=mir::client; |
487 | +namespace mcle=mir::client::eglstream; |
488 | +namespace geom=mir::geometry; |
489 | + |
490 | +mcle::ClientPlatform::ClientPlatform(ClientContext* const context) |
491 | + : context{context} |
492 | +{ |
493 | +} |
494 | + |
495 | +std::shared_ptr<mcl::ClientBufferFactory> mcle::ClientPlatform::create_buffer_factory() |
496 | +{ |
497 | + return std::make_shared<mcle::ClientBufferFactory>(); |
498 | +} |
499 | + |
500 | +std::shared_ptr<void> mcle::ClientPlatform::create_egl_native_window(EGLNativeSurface* /*client_surface*/) |
501 | +{ |
502 | + return nullptr; |
503 | +} |
504 | + |
505 | +std::shared_ptr<EGLNativeDisplayType> mcle::ClientPlatform::create_egl_native_display() |
506 | +{ |
507 | + return nullptr; |
508 | +} |
509 | + |
510 | +MirPlatformType mcle::ClientPlatform::platform_type() const |
511 | +{ |
512 | + return mir_platform_type_eglstream; |
513 | +} |
514 | + |
515 | +void mcle::ClientPlatform::populate(MirPlatformPackage& /*package*/) const |
516 | +{ |
517 | +} |
518 | + |
519 | +MirPlatformMessage* mcle::ClientPlatform::platform_operation(MirPlatformMessage const* /*msg*/) |
520 | +{ |
521 | + return nullptr; |
522 | +} |
523 | + |
524 | +MirNativeBuffer* mcle::ClientPlatform::convert_native_buffer(graphics::NativeBuffer* buf) const |
525 | +{ |
526 | + // Only buffers we currently support are ShmBuffers, which are type-compatible |
527 | + return buf; |
528 | +} |
529 | + |
530 | + |
531 | +MirPixelFormat mcle::ClientPlatform::get_egl_pixel_format( |
532 | + EGLDisplay /*disp*/, EGLConfig /*conf*/) const |
533 | +{ |
534 | + BOOST_THROW_EXCEPTION(std::runtime_error{"EGL support unimplemented"}); |
535 | +} |
536 | |
537 | === added file 'src/platforms/eglstream-kms/client/client_platform.h' |
538 | --- src/platforms/eglstream-kms/client/client_platform.h 1970-01-01 00:00:00 +0000 |
539 | +++ src/platforms/eglstream-kms/client/client_platform.h 2016-06-08 13:28:19 +0000 |
540 | @@ -0,0 +1,52 @@ |
541 | +/* |
542 | + * Copyright © 2016 Canonical Ltd. |
543 | + * |
544 | + * This program is free software: you can redistribute it and/or modify it |
545 | + * under the terms of the GNU Lesser General Public License version 3, |
546 | + * as published by the Free Software Foundation. |
547 | + * |
548 | + * This program is distributed in the hope that it will be useful, |
549 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
550 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
551 | + * GNU Lesser General Public License for more details. |
552 | + * |
553 | + * You should have received a copy of the GNU Lesser General Public License |
554 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
555 | + * |
556 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
557 | + */ |
558 | +#ifndef MIR_CLIENT_EGLSTREAM_CLIENT_PLATFORM_H_ |
559 | +#define MIR_CLIENT_EGLSTREAM_CLIENT_PLATFORM_H_ |
560 | + |
561 | +#include "mir/client_platform.h" |
562 | + |
563 | +namespace mir |
564 | +{ |
565 | +namespace client |
566 | +{ |
567 | +namespace eglstream |
568 | +{ |
569 | + |
570 | +class ClientPlatform : public client::ClientPlatform |
571 | +{ |
572 | +public: |
573 | + ClientPlatform(ClientContext* const context); |
574 | + |
575 | + MirPlatformType platform_type() const override; |
576 | + void populate(MirPlatformPackage& package) const override; |
577 | + MirPlatformMessage* platform_operation(MirPlatformMessage const* request) override; |
578 | + std::shared_ptr<ClientBufferFactory> create_buffer_factory() override; |
579 | + std::shared_ptr<void> create_egl_native_window(EGLNativeSurface *surface) override; |
580 | + std::shared_ptr<EGLNativeDisplayType> create_egl_native_display() override; |
581 | + MirNativeBuffer* convert_native_buffer(graphics::NativeBuffer*) const override; |
582 | + MirPixelFormat get_egl_pixel_format(EGLDisplay, EGLConfig) const override; |
583 | + |
584 | +private: |
585 | + ClientContext* const context; |
586 | +}; |
587 | + |
588 | +} |
589 | +} |
590 | +} |
591 | + |
592 | +#endif /* MIR_CLIENT_EGLSTREAM_CLIENT_PLATFORM_H_ */ |
593 | |
594 | === added file 'src/platforms/eglstream-kms/client/client_platform_factory.cpp' |
595 | --- src/platforms/eglstream-kms/client/client_platform_factory.cpp 1970-01-01 00:00:00 +0000 |
596 | +++ src/platforms/eglstream-kms/client/client_platform_factory.cpp 2016-06-08 13:28:19 +0000 |
597 | @@ -0,0 +1,59 @@ |
598 | +/* |
599 | + * Copyright © 2016 Canonical Ltd. |
600 | + * |
601 | + * This program is free software: you can redistribute it and/or modify it |
602 | + * under the terms of the GNU Lesser General Public License version 3, |
603 | + * as published by the Free Software Foundation. |
604 | + * |
605 | + * This program is distributed in the hope that it will be useful, |
606 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
607 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
608 | + * GNU Lesser General Public License for more details. |
609 | + * |
610 | + * You should have received a copy of the GNU Lesser General Public License |
611 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
612 | + * |
613 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
614 | + */ |
615 | + |
616 | +#include "mir/client_platform_factory.h" |
617 | +#include "client_platform.h" |
618 | +#include "mir_toolkit/client_types.h" |
619 | +#include "mir/client_context.h" |
620 | +#include "mir/egl_native_display_container.h" |
621 | +#include "mir/assert_module_entry_point.h" |
622 | +#include "mir/module_deleter.h" |
623 | + |
624 | +#include "mir_toolkit/mir_client_library.h" |
625 | + |
626 | +#include <cstring> |
627 | + |
628 | +namespace mcl = mir::client; |
629 | +namespace mcle = mcl::eglstream; |
630 | + |
631 | +namespace |
632 | +{ |
633 | +bool is_eglstream_server(mcl::ClientContext* context) |
634 | +{ |
635 | + MirModuleProperties module_properties; |
636 | + |
637 | + context->populate_graphics_module(module_properties); |
638 | + |
639 | + return strncmp(module_properties.name, "mir:eglstream", strlen("mir:eglstream")) == 0; |
640 | +} |
641 | +} |
642 | + |
643 | +mir::UniqueModulePtr<mcl::ClientPlatform> create_client_platform(mcl::ClientContext* context) |
644 | +{ |
645 | + mir::assert_entry_point_signature<mcl::CreateClientPlatform>(&create_client_platform); |
646 | + |
647 | + return mir::make_module_ptr<mcle::ClientPlatform>(context); |
648 | +} |
649 | + |
650 | +bool |
651 | +is_appropriate_module(mcl::ClientContext* context) |
652 | +{ |
653 | + mir::assert_entry_point_signature<mcl::ClientPlatformProbe>(&is_appropriate_module); |
654 | + |
655 | + return is_eglstream_server(context); |
656 | +} |
657 | |
658 | === added file 'src/platforms/eglstream-kms/client/symbols.map' |
659 | --- src/platforms/eglstream-kms/client/symbols.map 1970-01-01 00:00:00 +0000 |
660 | +++ src/platforms/eglstream-kms/client/symbols.map 2016-06-08 13:28:19 +0000 |
661 | @@ -0,0 +1,7 @@ |
662 | +MIR_CLIENT_PLATFORM_5 { |
663 | + global: |
664 | + create_client_platform; |
665 | + is_appropriate_module; |
666 | + local: |
667 | + *; |
668 | +}; |
669 | |
670 | === added directory 'src/platforms/eglstream-kms/server' |
671 | === added file 'src/platforms/eglstream-kms/server/CMakeLists.txt' |
672 | --- src/platforms/eglstream-kms/server/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
673 | +++ src/platforms/eglstream-kms/server/CMakeLists.txt 2016-06-08 13:28:19 +0000 |
674 | @@ -0,0 +1,55 @@ |
675 | +include_directories( |
676 | + ${server_common_include_dirs} |
677 | + ${DRM_INCLUDE_DIRS} |
678 | + ${GBM_INCLUDE_DIRS} |
679 | + ${EGL_INCLUDE_DIRS} |
680 | + ${GLESv2_INCLUDE_DIRS} |
681 | + ${EPOXY_INCLUDE_DIRS} |
682 | +) |
683 | + |
684 | +configure_file( |
685 | + ${CMAKE_CURRENT_SOURCE_DIR}/symbols.map.in |
686 | + ${CMAKE_CURRENT_BINARY_DIR}/symbols.map |
687 | +) |
688 | +set(symbol_map ${CMAKE_CURRENT_BINARY_DIR}/symbols.map) |
689 | + |
690 | +mir_add_library_with_symbols( |
691 | + mirplatformgraphicseglstreamkms MODULE |
692 | + |
693 | + ${symbol_map} |
694 | + platform_symbols.cpp |
695 | + platform.cpp |
696 | + buffer_allocator.cpp |
697 | + buffer_allocator.h |
698 | + display.cpp |
699 | + display.h |
700 | + kms_display_configuration.h |
701 | + kms_display_configuration.cpp |
702 | + egl_output.h |
703 | + egl_output.cpp |
704 | +) |
705 | + |
706 | +target_link_libraries( |
707 | + mirplatformgraphicseglstreamkms |
708 | + |
709 | + PRIVATE |
710 | + mirplatform |
711 | + server_platform_common |
712 | + kms_utils |
713 | + ${Boost_PROGRAM_OPTIONS_LIBRARY} |
714 | + ${DRM_LDFLAGS} ${DRM_LIBRARIES} |
715 | + ${EGL_LDFLAGS} ${EGL_LIBRARIES} |
716 | + ${GLESv2_LDFLAGS} ${GLESv2_LIBRARIES} |
717 | + ${EPOXY_LDFLAGS} ${EPOXY_LIBRARIES} |
718 | +) |
719 | + |
720 | +set_target_properties( |
721 | + mirplatformgraphicseglstreamkms PROPERTIES |
722 | + OUTPUT_NAME graphics-eglstream-kms |
723 | + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/server-modules |
724 | + PREFIX "" |
725 | + SUFFIX ".so.${MIR_SERVER_GRAPHICS_PLATFORM_ABI}" |
726 | + LINK_FLAGS "-Wl,--exclude-libs=ALL -Wl,--version-script,${symbol_map}" |
727 | +) |
728 | + |
729 | +install(TARGETS mirplatformgraphicseglstreamkms LIBRARY DESTINATION ${MIR_SERVER_PLATFORM_PATH}) |
730 | |
731 | === added file 'src/platforms/eglstream-kms/server/buffer_allocator.cpp' |
732 | --- src/platforms/eglstream-kms/server/buffer_allocator.cpp 1970-01-01 00:00:00 +0000 |
733 | +++ src/platforms/eglstream-kms/server/buffer_allocator.cpp 2016-06-08 13:28:19 +0000 |
734 | @@ -0,0 +1,69 @@ |
735 | +/* |
736 | + * Copyright © 2016 Canonical Ltd. |
737 | + * |
738 | + * This program is free software: you can redistribute it and/or modify it |
739 | + * under the terms of the GNU Lesser General Public License version 3, |
740 | + * as published by the Free Software Foundation. |
741 | + * |
742 | + * This program is distributed in the hope that it will be useful, |
743 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
744 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
745 | + * GNU Lesser General Public License for more details. |
746 | + * |
747 | + * You should have received a copy of the GNU Lesser General Public License |
748 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
749 | + * |
750 | + * Authored by: |
751 | + * Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
752 | + */ |
753 | + |
754 | +#include "buffer_allocator.h" |
755 | +#include "buffer_texture_binder.h" |
756 | +#include "anonymous_shm_file.h" |
757 | +#include "shm_buffer.h" |
758 | +#include "mir/graphics/buffer_properties.h" |
759 | +#include <boost/throw_exception.hpp> |
760 | +#include <boost/exception/errinfo_errno.hpp> |
761 | + |
762 | +#include <system_error> |
763 | +#include <cassert> |
764 | + |
765 | +namespace mg = mir::graphics; |
766 | +namespace mge = mg::eglstream; |
767 | +namespace mgc = mg::common; |
768 | +namespace geom = mir::geometry; |
769 | + |
770 | +mge::BufferAllocator::BufferAllocator() |
771 | +{ |
772 | +} |
773 | + |
774 | +std::shared_ptr<mg::Buffer> mge::BufferAllocator::alloc_buffer( |
775 | + BufferProperties const& buffer_properties) |
776 | +{ |
777 | + if (!mgc::ShmBuffer::supports(buffer_properties.format)) |
778 | + { |
779 | + BOOST_THROW_EXCEPTION( |
780 | + std::runtime_error( |
781 | + "Trying to create SHM buffer with unsupported pixel format")); |
782 | + } |
783 | + |
784 | + auto const stride = geom::Stride{ |
785 | + MIR_BYTES_PER_PIXEL(buffer_properties.format) * |
786 | + buffer_properties.size.width.as_uint32_t()}; |
787 | + size_t const size_in_bytes = |
788 | + stride.as_int() * buffer_properties.size.height.as_int(); |
789 | + auto shm_file = |
790 | + std::make_unique<mgc::AnonymousShmFile>(size_in_bytes); |
791 | + |
792 | + auto const buffer = |
793 | + std::make_shared<mgc::ShmBuffer>(std::move(shm_file), buffer_properties.size, |
794 | + buffer_properties.format); |
795 | + |
796 | + return buffer; |
797 | +} |
798 | + |
799 | +std::vector<MirPixelFormat> mge::BufferAllocator::supported_pixel_formats() |
800 | +{ |
801 | + // Lazy |
802 | + return {mir_pixel_format_argb_8888, mir_pixel_format_xrgb_8888}; |
803 | +} |
804 | |
805 | === added file 'src/platforms/eglstream-kms/server/buffer_allocator.h' |
806 | --- src/platforms/eglstream-kms/server/buffer_allocator.h 1970-01-01 00:00:00 +0000 |
807 | +++ src/platforms/eglstream-kms/server/buffer_allocator.h 2016-06-08 13:28:19 +0000 |
808 | @@ -0,0 +1,48 @@ |
809 | +/* |
810 | + * Copyright © 2016 Canonical Ltd. |
811 | + * |
812 | + * This program is free software: you can redistribute it and/or modify it |
813 | + * under the terms of the GNU Lesser General Public License version 3, |
814 | + * as published by the Free Software Foundation. |
815 | + * |
816 | + * This program is distributed in the hope that it will be useful, |
817 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
818 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
819 | + * GNU Lesser General Public License for more details. |
820 | + * |
821 | + * You should have received a copy of the GNU Lesser General Public License |
822 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
823 | + * |
824 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
825 | + */ |
826 | + |
827 | +#ifndef MIR_PLATFORMS_EGLSTREAM_BUFFER_ALLOCATOR_ |
828 | +#define MIR_PLATFORMS_EGLSTREAM_BUFFER_ALLOCATOR_ |
829 | + |
830 | +#include "mir/graphics/graphic_buffer_allocator.h" |
831 | +#include "mir/graphics/buffer_id.h" |
832 | + |
833 | +#include <memory> |
834 | + |
835 | +namespace mir |
836 | +{ |
837 | +namespace graphics |
838 | +{ |
839 | + |
840 | +namespace eglstream |
841 | +{ |
842 | + |
843 | +class BufferAllocator: public graphics::GraphicBufferAllocator |
844 | +{ |
845 | +public: |
846 | + BufferAllocator(); |
847 | + |
848 | + std::shared_ptr<Buffer> alloc_buffer(graphics::BufferProperties const& buffer_properties) override; |
849 | + std::vector<MirPixelFormat> supported_pixel_formats() override; |
850 | +}; |
851 | + |
852 | +} |
853 | +} |
854 | +} |
855 | + |
856 | +#endif // MIR_PLATFORMS_EGLSTREAM_BUFFER_ALLOCATOR_ |
857 | |
858 | === added file 'src/platforms/eglstream-kms/server/display.cpp' |
859 | --- src/platforms/eglstream-kms/server/display.cpp 1970-01-01 00:00:00 +0000 |
860 | +++ src/platforms/eglstream-kms/server/display.cpp 2016-06-08 13:28:19 +0000 |
861 | @@ -0,0 +1,363 @@ |
862 | +/* |
863 | + * Copyright © 2016 Canonical Ltd. |
864 | + * |
865 | + * This program is free software: you can redistribute it and/or modify it |
866 | + * under the terms of the GNU Lesser General Public License version 3, |
867 | + * as published by the Free Software Foundation. |
868 | + * |
869 | + * This program is distributed in the hope that it will be useful, |
870 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
871 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
872 | + * GNU Lesser General Public License for more details. |
873 | + * |
874 | + * You should have received a copy of the GNU Lesser General Public License |
875 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
876 | + * |
877 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
878 | + */ |
879 | + |
880 | +#include <epoxy/egl.h> |
881 | + |
882 | +#include "display.h" |
883 | +#include "egl_output.h" |
884 | + |
885 | +#include "kms-utils/drm_mode_resources.h" |
886 | + |
887 | +#include "mir/graphics/display_configuration.h" |
888 | +#include "mir/graphics/display_configuration_policy.h" |
889 | +#include "mir/graphics/overlapping_output_grouping.h" |
890 | +#include "mir/graphics/gl_context.h" |
891 | +#include "mir/graphics/gl_config.h" |
892 | +#include "mir/graphics/virtual_output.h" |
893 | +#include "mir/graphics/egl_error.h" |
894 | +#include "mir/graphics/display_buffer.h" |
895 | +#include "mir/renderer/gl/render_target.h" |
896 | + |
897 | +#include <drm/drm.h> |
898 | +#include <xf86drmMode.h> |
899 | +#include <sys/ioctl.h> |
900 | +#include <system_error> |
901 | +#include <boost/throw_exception.hpp> |
902 | + |
903 | +namespace mg = mir::graphics; |
904 | +namespace mge = mir::graphics::eglstream; |
905 | +namespace mgk = mir::graphics::kms; |
906 | + |
907 | +namespace |
908 | +{ |
909 | +EGLConfig choose_config(EGLDisplay display, mg::GLConfig const& requested_config) |
910 | +{ |
911 | + EGLint const config_attr[] = { |
912 | + EGL_SURFACE_TYPE, EGL_STREAM_BIT_KHR, |
913 | + EGL_RED_SIZE, 8, |
914 | + EGL_GREEN_SIZE, 8, |
915 | + EGL_BLUE_SIZE, 8, |
916 | + EGL_ALPHA_SIZE, 0, |
917 | + EGL_DEPTH_SIZE, requested_config.depth_buffer_bits(), |
918 | + EGL_STENCIL_SIZE, requested_config.stencil_buffer_bits(), |
919 | + EGL_RENDERABLE_TYPE, MIR_SERVER_EGL_OPENGL_BIT, |
920 | + EGL_NONE |
921 | + }; |
922 | + |
923 | + EGLint num_egl_configs; |
924 | + EGLConfig egl_config; |
925 | + if (eglChooseConfig(display, config_attr, &egl_config, 1, &num_egl_configs) != EGL_TRUE) |
926 | + { |
927 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to chose EGL config")); |
928 | + } |
929 | + else if (num_egl_configs != 1) |
930 | + { |
931 | + BOOST_THROW_EXCEPTION(std::runtime_error{"Failed to find compatible EGL config"}); |
932 | + } |
933 | + |
934 | + return egl_config; |
935 | +} |
936 | + |
937 | +EGLContext create_context(EGLDisplay display, EGLConfig config) |
938 | +{ |
939 | + eglBindAPI(MIR_SERVER_EGL_OPENGL_API); |
940 | + |
941 | + EGLint const context_attr[] = { |
942 | +#if MIR_SERVER_EGL_OPENGL_BIT == EGL_OPENGL_ES2_BIT |
943 | + EGL_CONTEXT_CLIENT_VERSION, 2, |
944 | +#endif |
945 | + EGL_NONE |
946 | + }; |
947 | + |
948 | + EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attr); |
949 | + if (context == EGL_NO_CONTEXT) |
950 | + { |
951 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); |
952 | + } |
953 | + |
954 | + return context; |
955 | +} |
956 | + |
957 | +EGLContext create_context(EGLDisplay display, EGLConfig config, EGLContext shared_context) |
958 | +{ |
959 | + eglBindAPI(MIR_SERVER_EGL_OPENGL_API); |
960 | + |
961 | + EGLint const context_attr[] = { |
962 | +#if MIR_SERVER_EGL_OPENGL_BIT == EGL_OPENGL_ES2_BIT |
963 | + EGL_CONTEXT_CLIENT_VERSION, 2, |
964 | +#endif |
965 | + EGL_NONE |
966 | + }; |
967 | + |
968 | + EGLContext context = eglCreateContext(display, config, shared_context, context_attr); |
969 | + if (context == EGL_NO_CONTEXT) |
970 | + { |
971 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); |
972 | + } |
973 | + |
974 | + return context; |
975 | +} |
976 | + |
977 | +class DisplayBuffer |
978 | + : public mg::DisplaySyncGroup, |
979 | + public mg::DisplayBuffer, |
980 | + public mg::NativeDisplayBuffer, |
981 | + public mir::renderer::gl::RenderTarget |
982 | +{ |
983 | +public: |
984 | + DisplayBuffer(EGLDisplay dpy, EGLContext ctx, EGLConfig config, mge::kms::EGLOutput const& output) |
985 | + : dpy{dpy}, |
986 | + ctx{create_context(dpy, config, ctx)}, |
987 | + layer{output.output_layer()}, |
988 | + view_area_{output.top_left, output.size()} |
989 | + { |
990 | + EGLint const stream_attribs[] = { |
991 | + EGL_STREAM_FIFO_LENGTH_KHR, 1, |
992 | + EGL_NONE |
993 | + }; |
994 | + output_stream = eglCreateStreamKHR(dpy, stream_attribs); |
995 | + if (output_stream == EGL_NO_STREAM_KHR) |
996 | + { |
997 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGLStream")); |
998 | + } |
999 | + |
1000 | + EGLAttrib swap_interval; |
1001 | + if (eglQueryOutputLayerAttribEXT(dpy, layer, EGL_SWAP_INTERVAL_EXT, &swap_interval) != EGL_TRUE) |
1002 | + { |
1003 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to query swap interval")); |
1004 | + } |
1005 | + |
1006 | + if (eglOutputLayerAttribEXT(dpy, layer, EGL_SWAP_INTERVAL_EXT, 1) != EGL_TRUE) |
1007 | + { |
1008 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to set swap interval")); |
1009 | + } |
1010 | + |
1011 | + if (eglStreamConsumerOutputEXT(dpy, output_stream, output.output_layer()) == EGL_FALSE) |
1012 | + { |
1013 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to attach EGLStream to output")); |
1014 | + }; |
1015 | + |
1016 | + EGLint const surface_attribs[] = { |
1017 | + EGL_WIDTH, output.size().width.as_int(), |
1018 | + EGL_HEIGHT, output.size().height.as_int(), |
1019 | + EGL_NONE, |
1020 | + }; |
1021 | + surface = eglCreateStreamProducerSurfaceKHR(dpy, config, output_stream, surface_attribs); |
1022 | + if (surface == EGL_NO_SURFACE) |
1023 | + { |
1024 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create StreamProducerSurface")); |
1025 | + } |
1026 | + |
1027 | + } |
1028 | + |
1029 | + /* gl::RenderTarget */ |
1030 | + void make_current() override |
1031 | + { |
1032 | + if (eglMakeCurrent(dpy, surface, surface, ctx) != EGL_TRUE) |
1033 | + { |
1034 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to make context current")); |
1035 | + } |
1036 | + } |
1037 | + |
1038 | + void release_current() override |
1039 | + { |
1040 | + if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) != EGL_TRUE) |
1041 | + { |
1042 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to release context")); |
1043 | + } |
1044 | + } |
1045 | + |
1046 | + void swap_buffers() override |
1047 | + { |
1048 | + if (eglSwapBuffers(dpy, surface) != EGL_TRUE) |
1049 | + { |
1050 | + BOOST_THROW_EXCEPTION(mg::egl_error("eglSwapBuffers failed")); |
1051 | + } |
1052 | + } |
1053 | + |
1054 | + mir::geometry::Rectangle view_area() const override |
1055 | + { |
1056 | + return view_area_; |
1057 | + } |
1058 | + |
1059 | + bool post_renderables_if_optimizable(const mir::graphics::RenderableList& /*renderlist*/) override |
1060 | + { |
1061 | + return false; |
1062 | + } |
1063 | + |
1064 | + MirOrientation orientation() const override |
1065 | + { |
1066 | + return mir_orientation_normal; |
1067 | + } |
1068 | + |
1069 | + MirMirrorMode mirror_mode() const override |
1070 | + { |
1071 | + return mir_mirror_mode_none; |
1072 | + } |
1073 | + |
1074 | + mir::graphics::NativeDisplayBuffer* native_display_buffer() override |
1075 | + { |
1076 | + return this; |
1077 | + } |
1078 | + |
1079 | + void for_each_display_buffer(const std::function<void(mir::graphics::DisplayBuffer&)>& f) override |
1080 | + { |
1081 | + f(*this); |
1082 | + } |
1083 | + |
1084 | + void post() override |
1085 | + { |
1086 | + |
1087 | + } |
1088 | + |
1089 | + void bind() override |
1090 | + { |
1091 | + } |
1092 | + |
1093 | + std::chrono::milliseconds recommended_sleep() const override |
1094 | + { |
1095 | + return std::chrono::milliseconds{0}; |
1096 | + } |
1097 | + |
1098 | +private: |
1099 | + EGLDisplay dpy; |
1100 | + EGLContext ctx; |
1101 | + EGLOutputLayerEXT layer; |
1102 | + mir::geometry::Rectangle const view_area_; |
1103 | + EGLStreamKHR output_stream; |
1104 | + EGLSurface surface; |
1105 | +}; |
1106 | +} |
1107 | + |
1108 | +mge::Display::Display( |
1109 | + mir::Fd drm_node, |
1110 | + EGLDisplay display, |
1111 | + std::shared_ptr<DisplayConfigurationPolicy> const& configuration_policy, |
1112 | + GLConfig const& gl_conf) |
1113 | + : drm_node{drm_node}, |
1114 | + display{display}, |
1115 | + config{choose_config(display, gl_conf)}, |
1116 | + context{create_context(display, config)}, |
1117 | + display_configuration{this->drm_node, display} |
1118 | +{ |
1119 | + auto ret = drmSetClientCap(drm_node, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); |
1120 | + if (ret != 0) |
1121 | + { |
1122 | + BOOST_THROW_EXCEPTION(std::system_error(-ret, std::system_category(), "Request for Universal Planes support failed")); |
1123 | + } |
1124 | + |
1125 | + ret = drmSetClientCap(drm_node, DRM_CLIENT_CAP_ATOMIC, 1); |
1126 | + if (ret != 0) |
1127 | + { |
1128 | + BOOST_THROW_EXCEPTION(std::system_error(-ret, std::system_category(), "Request for Atomic Modesetting support failed")); |
1129 | + } |
1130 | + |
1131 | + configuration_policy->apply_to(display_configuration); |
1132 | + |
1133 | + configure(display_configuration); |
1134 | +} |
1135 | + |
1136 | +void mge::Display::for_each_display_sync_group(const std::function<void(DisplaySyncGroup&)>& f) |
1137 | +{ |
1138 | + for (auto& group : active_sync_groups) |
1139 | + { |
1140 | + f(*group); |
1141 | + } |
1142 | +} |
1143 | + |
1144 | +std::unique_ptr<mg::DisplayConfiguration> mge::Display::configuration() const |
1145 | +{ |
1146 | + return display_configuration.clone(); |
1147 | +} |
1148 | + |
1149 | +void mge::Display::configure(DisplayConfiguration const& conf) |
1150 | +{ |
1151 | + auto kms_conf = dynamic_cast<KMSDisplayConfiguration const&>(conf); |
1152 | + active_sync_groups.clear(); |
1153 | + kms_conf.for_each_output([this, &conf](kms::EGLOutput const& output) |
1154 | + { |
1155 | + if (output.used) |
1156 | + { |
1157 | + const_cast<kms::EGLOutput&>(output).configure(output.current_mode_index); |
1158 | + active_sync_groups.emplace_back(std::make_unique<::DisplayBuffer>(display, context, config, output)); |
1159 | + } |
1160 | + }); |
1161 | +} |
1162 | + |
1163 | +void mge::Display::register_configuration_change_handler( |
1164 | + EventHandlerRegister& /*handlers*/, |
1165 | + DisplayConfigurationChangeHandler const& /*conf_change_handler*/) |
1166 | +{ |
1167 | +} |
1168 | + |
1169 | +void mge::Display::register_pause_resume_handlers( |
1170 | + EventHandlerRegister& /*handlers*/, |
1171 | + DisplayPauseHandler const& /*pause_handler*/, |
1172 | + DisplayResumeHandler const& /*resume_handler*/) |
1173 | +{ |
1174 | +} |
1175 | + |
1176 | +void mge::Display::pause() |
1177 | +{ |
1178 | + |
1179 | +} |
1180 | + |
1181 | +void mge::Display::resume() |
1182 | +{ |
1183 | + |
1184 | +} |
1185 | + |
1186 | +std::shared_ptr<mg::Cursor> mge::Display::create_hardware_cursor( |
1187 | + std::shared_ptr<CursorImage> const& /*initial_image*/) |
1188 | +{ |
1189 | + // TODO: Find the cursor plane, and use it. |
1190 | + return nullptr; |
1191 | +} |
1192 | + |
1193 | +std::unique_ptr<mg::GLContext> mge::Display::create_gl_context() |
1194 | +{ |
1195 | + class GLContext : public mg::GLContext |
1196 | + { |
1197 | + public: |
1198 | + GLContext(EGLDisplay display, EGLContext context) |
1199 | + : display{display}, |
1200 | + context{context} |
1201 | + { |
1202 | + } |
1203 | + |
1204 | + void make_current() const override |
1205 | + { |
1206 | + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context); |
1207 | + } |
1208 | + |
1209 | + void release_current() const override |
1210 | + { |
1211 | + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
1212 | + } |
1213 | + |
1214 | + private: |
1215 | + EGLDisplay display; |
1216 | + EGLContext context; |
1217 | + }; |
1218 | + return std::make_unique<GLContext>(display, context); |
1219 | +} |
1220 | + |
1221 | +std::unique_ptr<mg::VirtualOutput> mge::Display::create_virtual_output(int /*width*/, int /*height*/) |
1222 | +{ |
1223 | + return nullptr; |
1224 | +} |
1225 | |
1226 | === added file 'src/platforms/eglstream-kms/server/display.h' |
1227 | --- src/platforms/eglstream-kms/server/display.h 1970-01-01 00:00:00 +0000 |
1228 | +++ src/platforms/eglstream-kms/server/display.h 2016-06-08 13:28:19 +0000 |
1229 | @@ -0,0 +1,82 @@ |
1230 | +/* |
1231 | + * Copyright © 2016 Canonical Ltd. |
1232 | + * |
1233 | + * This program is free software: you can redistribute it and/or modify it |
1234 | + * under the terms of the GNU Lesser General Public License version 3, |
1235 | + * as published by the Free Software Foundation. |
1236 | + * |
1237 | + * This program is distributed in the hope that it will be useful, |
1238 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1239 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1240 | + * GNU Lesser General Public License for more details. |
1241 | + * |
1242 | + * You should have received a copy of the GNU Lesser General Public License |
1243 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1244 | + * |
1245 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
1246 | + */ |
1247 | + |
1248 | +#ifndef MIR_PLATFORMS_EGLSTREAM_KMS_DISPLAY_H_ |
1249 | +#define MIR_PLATFORMS_EGLSTREAM_KMS_DISPLAY_H_ |
1250 | + |
1251 | +#include "mir/graphics/display.h" |
1252 | +#include "kms_display_configuration.h" |
1253 | +#include "mir/fd.h" |
1254 | + |
1255 | +namespace mir |
1256 | +{ |
1257 | +namespace graphics |
1258 | +{ |
1259 | +class DisplayConfigurationPolicy; |
1260 | +class GLConfig; |
1261 | + |
1262 | +namespace eglstream |
1263 | +{ |
1264 | + |
1265 | +class Display : public mir::graphics::Display |
1266 | +{ |
1267 | +public: |
1268 | + Display( |
1269 | + mir::Fd drm_node, |
1270 | + EGLDisplay display, |
1271 | + std::shared_ptr<DisplayConfigurationPolicy> const& configuration_policy, |
1272 | + GLConfig const& gl_conf); |
1273 | + |
1274 | + void for_each_display_sync_group(const std::function<void(DisplaySyncGroup&)>& f) override; |
1275 | + |
1276 | + std::unique_ptr<DisplayConfiguration> configuration() const override; |
1277 | + |
1278 | + void configure(DisplayConfiguration const& conf) override; |
1279 | + |
1280 | + void register_configuration_change_handler(EventHandlerRegister& handlers, |
1281 | + DisplayConfigurationChangeHandler const& conf_change_handler) override; |
1282 | + |
1283 | + void register_pause_resume_handlers(EventHandlerRegister& handlers, |
1284 | + DisplayPauseHandler const& pause_handler, DisplayResumeHandler const& resume_handler) override; |
1285 | + |
1286 | + void pause() override; |
1287 | + |
1288 | + void resume() override; |
1289 | + |
1290 | + std::shared_ptr<Cursor> create_hardware_cursor(std::shared_ptr<CursorImage> const& initial_image) override; |
1291 | + |
1292 | + std::unique_ptr<GLContext> create_gl_context() override; |
1293 | + |
1294 | + std::unique_ptr<VirtualOutput> create_virtual_output(int width, int height) override; |
1295 | + |
1296 | +private: |
1297 | + mir::Fd const drm_node; |
1298 | + EGLDisplay display; |
1299 | + EGLConfig config; |
1300 | + EGLContext context; |
1301 | + KMSDisplayConfiguration display_configuration; |
1302 | + std::vector<std::unique_ptr<DisplaySyncGroup>> active_sync_groups; |
1303 | + std::shared_ptr<DisplayConfigurationPolicy> const configuration_policy; |
1304 | +}; |
1305 | + |
1306 | +} |
1307 | +} |
1308 | + |
1309 | +} |
1310 | + |
1311 | +#endif // MIR_PLATFORMS_EGLSTREAM_KMS_DISPLAY_H_ |
1312 | |
1313 | === added file 'src/platforms/eglstream-kms/server/egl_output.cpp' |
1314 | --- src/platforms/eglstream-kms/server/egl_output.cpp 1970-01-01 00:00:00 +0000 |
1315 | +++ src/platforms/eglstream-kms/server/egl_output.cpp 2016-06-08 13:28:19 +0000 |
1316 | @@ -0,0 +1,359 @@ |
1317 | +/* |
1318 | + * Copyright © 2016 Canonical Ltd. |
1319 | + * |
1320 | + * This program is free software: you can redistribute it and/or modify it |
1321 | + * under the terms of the GNU Lesser General Public License version 3, |
1322 | + * as published by the Free Software Foundation. |
1323 | + * |
1324 | + * This program is distributed in the hope that it will be useful, |
1325 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1326 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1327 | + * GNU Lesser General Public License for more details. |
1328 | + * |
1329 | + * You should have received a copy of the GNU Lesser General Public License |
1330 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1331 | + * |
1332 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
1333 | + */ |
1334 | + |
1335 | +#include <epoxy/egl.h> |
1336 | + |
1337 | +#include "egl_output.h" |
1338 | +#include "mir/graphics/egl_error.h" |
1339 | +#include "kms-utils/kms_connector.h" |
1340 | + |
1341 | +#include <cstring> |
1342 | +#include <drm/drm.h> |
1343 | +#include <sys/ioctl.h> |
1344 | +#include <vector> |
1345 | +#include <boost/throw_exception.hpp> |
1346 | +#include <unordered_map> |
1347 | +#include <tuple> |
1348 | +#include <sys/mman.h> |
1349 | + |
1350 | +namespace mg = mir::graphics; |
1351 | +namespace mge = mg::eglstream; |
1352 | +namespace mgek = mge::kms; |
1353 | +namespace mgk = mg::kms; |
1354 | +namespace geom = mir::geometry; |
1355 | + |
1356 | +namespace |
1357 | +{ |
1358 | +namespace |
1359 | +{ |
1360 | +class DumbFb |
1361 | +{ |
1362 | +public: |
1363 | + DumbFb(int drm_fd, uint32_t width, uint32_t height) |
1364 | + : drm_fd{drm_fd} |
1365 | + { |
1366 | + struct drm_mode_create_dumb params = {}; |
1367 | + |
1368 | + params.bpp = 32; |
1369 | + params.height = height; |
1370 | + params.width = width; |
1371 | + |
1372 | + if (ioctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, ¶ms) != 0) |
1373 | + { |
1374 | + BOOST_THROW_EXCEPTION((std::system_error{errno, std::system_category(), "Failed to create dumb buffer"})); |
1375 | + } |
1376 | + |
1377 | + gem_handle = params.handle; |
1378 | + pitch_ = params.pitch; |
1379 | + |
1380 | + auto ret = drmModeAddFB(drm_fd, width, height, 24, 32, params.pitch, params.handle, &fb_id); |
1381 | + if (ret) |
1382 | + { |
1383 | + BOOST_THROW_EXCEPTION((std::system_error{-ret, std::system_category(), "Failed to attach dumb buffer to FB"})); |
1384 | + } |
1385 | + |
1386 | + struct drm_mode_map_dumb map_request = {}; |
1387 | + |
1388 | + map_request.handle = gem_handle; |
1389 | + |
1390 | + ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_request); |
1391 | + if (ret) |
1392 | + { |
1393 | + BOOST_THROW_EXCEPTION((std::system_error{-ret, std::system_category(), "Failed to map dumb buffer"})); |
1394 | + } |
1395 | + |
1396 | + auto map = mmap(0, params.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd, map_request.offset); |
1397 | + if (map == MAP_FAILED) |
1398 | + { |
1399 | + BOOST_THROW_EXCEPTION((std::system_error{errno, std::system_category(), "Failed to mmap() buffer"})); |
1400 | + } |
1401 | + } |
1402 | + ~DumbFb() noexcept(false) |
1403 | + { |
1404 | + struct drm_mode_destroy_dumb params = { gem_handle }; |
1405 | + |
1406 | + if (ioctl(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, ¶ms) != 0) |
1407 | + { |
1408 | + if (!std::uncaught_exception()) |
1409 | + { |
1410 | + BOOST_THROW_EXCEPTION((std::system_error{errno, std::system_category(), "Failed to destroy dumb buffer"})); |
1411 | + } |
1412 | + } |
1413 | + } |
1414 | + |
1415 | + uint32_t handle() const |
1416 | + { |
1417 | + return gem_handle; |
1418 | + } |
1419 | + |
1420 | + uint32_t pitch() const |
1421 | + { |
1422 | + return pitch_; |
1423 | + } |
1424 | + |
1425 | + uint32_t id() const |
1426 | + { |
1427 | + return fb_id; |
1428 | + } |
1429 | + |
1430 | +private: |
1431 | + int const drm_fd; |
1432 | + uint32_t fb_id; |
1433 | + uint32_t gem_handle; |
1434 | + uint32_t pitch_; |
1435 | +}; |
1436 | + |
1437 | +} |
1438 | + |
1439 | +uint32_t kms_id_from_port(EGLDisplay dpy, EGLOutputPortEXT port) |
1440 | +{ |
1441 | + EGLAttrib kms_connector_id; |
1442 | + if (eglQueryOutputPortAttribEXT(dpy, port, EGL_DRM_CONNECTOR_EXT, &kms_connector_id) != EGL_TRUE) |
1443 | + { |
1444 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to get EGLOutputPort → KMS connector ID mapping")); |
1445 | + } |
1446 | + return static_cast<uint32_t>(kms_connector_id); |
1447 | +} |
1448 | + |
1449 | +void refresh_connector(int drm_fd, mgk::DRMModeConnectorUPtr& connector) |
1450 | +{ |
1451 | + connector = mgk::get_connector(drm_fd, connector->connector_id); |
1452 | +} |
1453 | +} |
1454 | + |
1455 | +mgek::EGLOutput::EGLOutput( |
1456 | + int drm_fd, |
1457 | + EGLDisplay dpy, |
1458 | + EGLOutputPortEXT connector) |
1459 | + : drm_fd{drm_fd}, |
1460 | + display{dpy}, |
1461 | + port{connector}, |
1462 | + connector{mgk::get_connector(drm_fd, kms_id_from_port(dpy, port))} |
1463 | +{ |
1464 | + |
1465 | +} |
1466 | + |
1467 | +mgek::EGLOutput::~EGLOutput() noexcept(false) |
1468 | +{ |
1469 | + auto const uncaught_exception = std::uncaught_exception(); |
1470 | + try |
1471 | + { |
1472 | + restore_saved_crtc(); |
1473 | + } |
1474 | + catch(...) |
1475 | + { |
1476 | + if (!uncaught_exception) |
1477 | + { |
1478 | + throw; |
1479 | + } |
1480 | + } |
1481 | +} |
1482 | + |
1483 | +void mgek::EGLOutput::reset() |
1484 | +{ |
1485 | + /* Update the connector to ensure we have the latest information */ |
1486 | + refresh_connector(drm_fd, connector); |
1487 | + |
1488 | + // TODO: What if we can't locate the DPMS property? |
1489 | + // TODO: Replace with mgk::ObjectProperties |
1490 | + for (int i = 0; i < connector->count_props; i++) |
1491 | + { |
1492 | + auto prop = drmModeGetProperty(drm_fd, connector->props[i]); |
1493 | + if (prop && (prop->flags & DRM_MODE_PROP_ENUM)) { |
1494 | + if (!strcmp(prop->name, "DPMS")) |
1495 | + { |
1496 | + dpms_enum_id = connector->props[i]; |
1497 | + drmModeFreeProperty(prop); |
1498 | + break; |
1499 | + } |
1500 | + drmModeFreeProperty(prop); |
1501 | + } |
1502 | + } |
1503 | +} |
1504 | + |
1505 | +geom::Size mgek::EGLOutput::size() const |
1506 | +{ |
1507 | + drmModeModeInfo const& mode = connector->modes[mode_index]; |
1508 | + return {mode.hdisplay, mode.vdisplay}; |
1509 | +} |
1510 | + |
1511 | +int mgek::EGLOutput::max_refresh_rate() const |
1512 | +{ |
1513 | + // TODO: In future when DRM exposes FreeSync/Adaptive Sync/G-Sync info |
1514 | + // this value may be calculated differently. |
1515 | + drmModeModeInfo const& current_mode = connector->modes[mode_index]; |
1516 | + return current_mode.vrefresh; |
1517 | +} |
1518 | + |
1519 | +void mgek::EGLOutput::configure(size_t kms_mode_index) |
1520 | +{ |
1521 | + mode_index = kms_mode_index; |
1522 | + auto const width = connector->modes[kms_mode_index].hdisplay; |
1523 | + auto const height = connector->modes[kms_mode_index].vdisplay; |
1524 | + |
1525 | + std::unique_ptr<drmModeAtomicReq, void(*)(drmModeAtomicReqPtr)> |
1526 | + request{drmModeAtomicAlloc(), &drmModeAtomicFree}; |
1527 | + |
1528 | + mgk::DRMModeCrtcUPtr crtc; |
1529 | + mgk::DRMModePlaneUPtr plane; |
1530 | + |
1531 | + std::tie(crtc, plane) = mgk::find_crtc_with_primary_plane(drm_fd, connector); |
1532 | + auto const crtc_id = crtc->crtc_id; |
1533 | + |
1534 | + uint32_t mode_id{0}; |
1535 | + auto ret = drmModeCreatePropertyBlob( |
1536 | + drm_fd, |
1537 | + &connector->modes[kms_mode_index], |
1538 | + sizeof(connector->modes[kms_mode_index]), |
1539 | + &mode_id); |
1540 | + |
1541 | + if (ret != 0) |
1542 | + { |
1543 | + BOOST_THROW_EXCEPTION( |
1544 | + std::system_error(-ret, std::system_category(), "Failed to create DRM Mode property blob")); |
1545 | + } |
1546 | + |
1547 | + DumbFb dummy{drm_fd, width, height}; |
1548 | + |
1549 | + mgk::ObjectProperties crtc_props{drm_fd, crtc_id, DRM_MODE_OBJECT_CRTC}; |
1550 | + |
1551 | + /* Activate the CRTC and set the mode */ |
1552 | + drmModeAtomicAddProperty(request.get(), crtc_id, crtc_props.id_for("MODE_ID"), mode_id); |
1553 | + drmModeAtomicAddProperty(request.get(), crtc_id, crtc_props.id_for("ACTIVE"), 1); |
1554 | + |
1555 | + /* Set CRTC for the output */ |
1556 | + auto const connector_id = connector->connector_id; |
1557 | + mgk::ObjectProperties connector_props{drm_fd, connector_id, DRM_MODE_OBJECT_CONNECTOR}; |
1558 | + drmModeAtomicAddProperty(request.get(), connector_id, connector_props.id_for("CRTC_ID"), crtc_id); |
1559 | + |
1560 | + /* Set up the output plane... */ |
1561 | + auto const plane_id = plane->plane_id; |
1562 | + mgk::ObjectProperties plane_props{drm_fd, plane_id, DRM_MODE_OBJECT_PLANE}; |
1563 | + |
1564 | + /* Source viewport. Coordinates are 16.16 fixed point format */ |
1565 | + drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("SRC_X"), 0); |
1566 | + drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("SRC_Y"), 0); |
1567 | + drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("SRC_W"), width << 16); |
1568 | + drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("SRC_H"), height << 16); |
1569 | + |
1570 | + /* Destination viewport. Coordinates are *not* 16.16 */ |
1571 | + drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("CRTC_X"), 0); |
1572 | + drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("CRTC_Y"), 0); |
1573 | + drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("CRTC_W"), width); |
1574 | + drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("CRTC_H"), height); |
1575 | + |
1576 | + /* Set a surface for the plane, and connect to the CRTC */ |
1577 | + drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("FB_ID"), dummy.id()); |
1578 | + drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("CRTC_ID"), crtc_id); |
1579 | + |
1580 | + /* We don't monitor the DRM events (yet), so have no userdata */ |
1581 | + ret = drmModeAtomicCommit(drm_fd, request.get(), DRM_MODE_ATOMIC_ALLOW_MODESET, nullptr); |
1582 | + |
1583 | + if (ret != 0) |
1584 | + { |
1585 | + BOOST_THROW_EXCEPTION( |
1586 | + std::system_error(-ret, std::system_category(), "Failed to commit atomic KMS configuration change")); |
1587 | + } |
1588 | + |
1589 | + EGLAttrib const crtc_filter[] = { |
1590 | + EGL_DRM_PLANE_EXT, plane_id, |
1591 | + EGL_NONE}; |
1592 | + int found_layers{0}; |
1593 | + if (eglGetOutputLayersEXT(display, crtc_filter, &layer, 1, &found_layers) != EGL_TRUE) |
1594 | + { |
1595 | + BOOST_THROW_EXCEPTION((mg::egl_error("Failed to find EGLOutputEXT corresponding to DRM CRTC"))); |
1596 | + } |
1597 | + if (found_layers != 1) |
1598 | + { |
1599 | + BOOST_THROW_EXCEPTION(std::runtime_error{"Failed to find EGLOutputEXT corresponding to DRM CRTC"}); |
1600 | + } |
1601 | + |
1602 | + using_saved_crtc = false; |
1603 | +} |
1604 | + |
1605 | +EGLOutputLayerEXT mgek::EGLOutput::output_layer() const |
1606 | +{ |
1607 | + return layer; |
1608 | +} |
1609 | + |
1610 | +void mgek::EGLOutput::clear_crtc() |
1611 | +{ |
1612 | + using namespace std::string_literals; |
1613 | + mgk::DRMModeCrtcUPtr crtc; |
1614 | + try |
1615 | + { |
1616 | + crtc = mgk::find_crtc_for_connector(drm_fd, connector); |
1617 | + } |
1618 | + catch (std::runtime_error const&) |
1619 | + { |
1620 | + /* |
1621 | + * In order to actually clear the output, we need to have a crtc |
1622 | + * connected to the output/connector so that we can disconnect |
1623 | + * it. However, not being able to get a crtc is OK, since it means |
1624 | + * that the output cannot be displaying anything anyway. |
1625 | + */ |
1626 | + return; |
1627 | + } |
1628 | + auto const crtc_id = crtc->crtc_id; |
1629 | + |
1630 | + auto result = drmModeSetCrtc(drm_fd, crtc_id, |
1631 | + 0, 0, 0, nullptr, 0, nullptr); |
1632 | + if (result) |
1633 | + { |
1634 | + BOOST_THROW_EXCEPTION(( |
1635 | + std::system_error{ |
1636 | + -result, |
1637 | + std::system_category(), |
1638 | + "Couldn't clear output "s + mgk::connector_name(connector)})); |
1639 | + } |
1640 | +} |
1641 | + |
1642 | +void mgek::EGLOutput::restore_saved_crtc() |
1643 | +{ |
1644 | + if (!using_saved_crtc) |
1645 | + { |
1646 | + auto result = drmModeSetCrtc( |
1647 | + drm_fd, |
1648 | + saved_crtc.crtc_id, |
1649 | + saved_crtc.buffer_id, |
1650 | + saved_crtc.x, saved_crtc.y, |
1651 | + &connector->connector_id, |
1652 | + 1, |
1653 | + &saved_crtc.mode); |
1654 | + |
1655 | + if (result != 0) |
1656 | + { |
1657 | + BOOST_THROW_EXCEPTION((std::system_error{-result, std::system_category(), "Failed to set CRTC"})); |
1658 | + } |
1659 | + |
1660 | + using_saved_crtc = true; |
1661 | + } |
1662 | +} |
1663 | + |
1664 | +void mgek::EGLOutput::set_power_mode(MirPowerMode mode) |
1665 | +{ |
1666 | + auto ret = drmModeConnectorSetProperty( |
1667 | + drm_fd, |
1668 | + connector->connector_id, |
1669 | + dpms_enum_id, |
1670 | + mode); |
1671 | + if (ret) |
1672 | + { |
1673 | + BOOST_THROW_EXCEPTION((std::system_error{-ret, std::system_category(), "Failed to set output power mode"})); |
1674 | + } |
1675 | +} |
1676 | |
1677 | === added file 'src/platforms/eglstream-kms/server/egl_output.h' |
1678 | --- src/platforms/eglstream-kms/server/egl_output.h 1970-01-01 00:00:00 +0000 |
1679 | +++ src/platforms/eglstream-kms/server/egl_output.h 2016-06-08 13:28:19 +0000 |
1680 | @@ -0,0 +1,79 @@ |
1681 | +/* |
1682 | + * Copyright © 2016 Canonical Ltd. |
1683 | + * |
1684 | + * This program is free software: you can redistribute it and/or modify it |
1685 | + * under the terms of the GNU Lesser General Public License version 3, |
1686 | + * as published by the Free Software Foundation. |
1687 | + * |
1688 | + * This program is distributed in the hope that it will be useful, |
1689 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1690 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1691 | + * GNU Lesser General Public License for more details. |
1692 | + * |
1693 | + * You should have received a copy of the GNU Lesser General Public License |
1694 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1695 | + * |
1696 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
1697 | + */ |
1698 | + |
1699 | +#ifndef MIR_GRAPHICS_EGLSTREAM_KMS_OUTPUT_H_ |
1700 | +#define MIR_GRAPHICS_EGLSTREAM_KMS_OUTPUT_H_ |
1701 | + |
1702 | +#include "kms-utils/drm_mode_resources.h" |
1703 | +#include "mir/geometry/size.h" |
1704 | +#include "mir/geometry/point.h" |
1705 | +#include "mir/geometry/displacement.h" |
1706 | +#include "mir/graphics/display_configuration.h" |
1707 | +#include "mir_toolkit/common.h" |
1708 | + |
1709 | +#include <epoxy/egl.h> |
1710 | + |
1711 | +namespace mir |
1712 | +{ |
1713 | +namespace graphics |
1714 | +{ |
1715 | +namespace eglstream |
1716 | +{ |
1717 | + |
1718 | +namespace kms |
1719 | +{ |
1720 | +class EGLOutput : public DisplayConfigurationOutput |
1721 | +{ |
1722 | +public: |
1723 | + EGLOutput(int drm_fd, EGLDisplay dpy, EGLOutputPortEXT connector); |
1724 | + ~EGLOutput() noexcept(false); |
1725 | + |
1726 | + void reset(); |
1727 | + void configure(size_t kms_mode_index); |
1728 | + geometry::Size size() const; |
1729 | + int max_refresh_rate() const; |
1730 | + |
1731 | + EGLOutputLayerEXT output_layer() const; |
1732 | + void clear_crtc(); |
1733 | + |
1734 | + void set_power_mode(MirPowerMode mode); |
1735 | + |
1736 | +private: |
1737 | + void restore_saved_crtc(); |
1738 | + |
1739 | + int const drm_fd; |
1740 | + |
1741 | + EGLDisplay display; |
1742 | + EGLOutputPortEXT port; |
1743 | + EGLOutputLayerEXT layer; |
1744 | + |
1745 | + graphics::kms::DRMModeConnectorUPtr connector; |
1746 | + |
1747 | + size_t mode_index; |
1748 | + drmModeCrtc saved_crtc; |
1749 | + bool using_saved_crtc; |
1750 | + |
1751 | + int dpms_enum_id; |
1752 | +}; |
1753 | + |
1754 | +} |
1755 | +} |
1756 | +} |
1757 | +} |
1758 | + |
1759 | +#endif /* MIR_GRAPHICS_EGLSTREAM_KMS_KMS_OUTPUT_H_ */ |
1760 | |
1761 | === added file 'src/platforms/eglstream-kms/server/kms_display_configuration.cpp' |
1762 | --- src/platforms/eglstream-kms/server/kms_display_configuration.cpp 1970-01-01 00:00:00 +0000 |
1763 | +++ src/platforms/eglstream-kms/server/kms_display_configuration.cpp 2016-06-08 13:28:19 +0000 |
1764 | @@ -0,0 +1,331 @@ |
1765 | +/* |
1766 | + * Copyright © 2016 Canonical Ltd. |
1767 | + * |
1768 | + * This program is free software: you can redistribute it and/or modify it |
1769 | + * under the terms of the GNU Lesser General Public License version 3, |
1770 | + * as published by the Free Software Foundation. |
1771 | + * |
1772 | + * This program is distributed in the hope that it will be useful, |
1773 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1774 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1775 | + * GNU Lesser General Public License for more details. |
1776 | + * |
1777 | + * You should have received a copy of the GNU Lesser General Public License |
1778 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1779 | + * |
1780 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
1781 | + */ |
1782 | + |
1783 | +#include <epoxy/egl.h> |
1784 | + |
1785 | +#include "kms_display_configuration.h" |
1786 | +#include "kms-utils/drm_mode_resources.h" |
1787 | +#include "mir/graphics/pixel_format_utils.h" |
1788 | +#include "mir/graphics/egl_error.h" |
1789 | + |
1790 | +#include <cmath> |
1791 | +#include <limits> |
1792 | + |
1793 | +#include <boost/throw_exception.hpp> |
1794 | +#include <stdexcept> |
1795 | +#include <algorithm> |
1796 | + |
1797 | +namespace mg = mir::graphics; |
1798 | +namespace mge = mir::graphics::eglstream; |
1799 | +namespace mgk = mir::graphics::kms; |
1800 | + |
1801 | +namespace geom = mir::geometry; |
1802 | + |
1803 | +namespace |
1804 | +{ |
1805 | + |
1806 | +bool kms_modes_are_equal(drmModeModeInfo const& info1, drmModeModeInfo const& info2) |
1807 | +{ |
1808 | + return (info1.clock == info2.clock && |
1809 | + info1.hdisplay == info2.hdisplay && |
1810 | + info1.hsync_start == info2.hsync_start && |
1811 | + info1.hsync_end == info2.hsync_end && |
1812 | + info1.htotal == info2.htotal && |
1813 | + info1.hskew == info2.hskew && |
1814 | + info1.vdisplay == info2.vdisplay && |
1815 | + info1.vsync_start == info2.vsync_start && |
1816 | + info1.vsync_end == info2.vsync_end && |
1817 | + info1.vtotal == info2.vtotal); |
1818 | +} |
1819 | + |
1820 | +double calculate_vrefresh_hz(drmModeModeInfo const& mode) |
1821 | +{ |
1822 | + if (mode.htotal == 0 || mode.vtotal == 0) |
1823 | + return 0.0; |
1824 | + |
1825 | + /* mode.clock is in KHz */ |
1826 | + double hz = (mode.clock * 100000LL / |
1827 | + ((long)mode.htotal * (long)mode.vtotal) |
1828 | + ) / 100.0; |
1829 | + |
1830 | + // Actually we don't need floating point at all for this... |
1831 | + // TODO: Consider converting our structs to fixed-point ints |
1832 | + return hz; |
1833 | +} |
1834 | + |
1835 | +mg::DisplayConfigurationOutputType |
1836 | +kms_connector_type_to_output_type(uint32_t connector_type) |
1837 | +{ |
1838 | + return static_cast<mg::DisplayConfigurationOutputType>(connector_type); |
1839 | +} |
1840 | + |
1841 | +std::vector<std::shared_ptr<mge::kms::EGLOutput>> create_outputs(int drm_fd, EGLDisplay display) |
1842 | +{ |
1843 | + mgk::DRMModeResources resources{drm_fd}; |
1844 | + |
1845 | + std::vector<std::shared_ptr<mge::kms::EGLOutput>> outputs; |
1846 | + |
1847 | + for (auto const& connector : resources.connectors()) |
1848 | + { |
1849 | + EGLOutputPortEXT port; |
1850 | + int num_ports; |
1851 | + EGLAttrib const select_connector[] = { |
1852 | + EGL_DRM_CONNECTOR_EXT, connector->connector_id, |
1853 | + EGL_NONE |
1854 | + }; |
1855 | + if (eglGetOutputPortsEXT(display, select_connector, &port, 1, &num_ports) != EGL_TRUE) |
1856 | + { |
1857 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to find EGLOutputPort corresponding to DRM connector")); |
1858 | + } |
1859 | + if (num_ports != 1) |
1860 | + { |
1861 | + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to find EGLOutputPort corresponding to DRM connector")); |
1862 | + } |
1863 | + outputs.push_back(std::make_shared<mge::kms::EGLOutput>(drm_fd, display, port)); |
1864 | + auto& output = outputs.back(); |
1865 | + |
1866 | + // TODO: This should all be owned by mgek::EGLOutput |
1867 | + |
1868 | + output->id = mg::DisplayConfigurationOutputId{static_cast<int>(connector->connector_id)}; |
1869 | + output->card_id = mg::DisplayConfigurationCardId{0}; |
1870 | + output->type = kms_connector_type_to_output_type(connector->connector_type); |
1871 | + output->physical_size_mm = geom::Size{connector->mmWidth, connector->mmHeight}; |
1872 | + output->connected = connector->connection == DRM_MODE_CONNECTED; |
1873 | + output->pixel_formats = { |
1874 | + mir_pixel_format_argb_8888, |
1875 | + mir_pixel_format_xrgb_8888 |
1876 | + }; |
1877 | + |
1878 | + uint32_t const invalid_mode_index = std::numeric_limits<uint32_t>::max(); |
1879 | + output->current_mode_index = invalid_mode_index; |
1880 | + output->preferred_mode_index = invalid_mode_index; |
1881 | + |
1882 | + /* Get information about the current mode */ |
1883 | + mgk::DRMModeCrtcUPtr current_crtc; |
1884 | + if (connector->encoder_id) |
1885 | + { |
1886 | + auto encoder = mgk::get_encoder(drm_fd, connector->encoder_id); |
1887 | + if (encoder->crtc_id) |
1888 | + { |
1889 | + current_crtc = mgk::get_crtc(drm_fd, encoder->crtc_id); |
1890 | + } |
1891 | + } |
1892 | + |
1893 | + /* Add all the available modes and find the current and preferred one */ |
1894 | + for (int m = 0; m < connector->count_modes; m++) |
1895 | + { |
1896 | + drmModeModeInfo &mode_info = connector->modes[m]; |
1897 | + |
1898 | + geom::Size size{mode_info.hdisplay, mode_info.vdisplay}; |
1899 | + |
1900 | + double vrefresh_hz = calculate_vrefresh_hz(mode_info); |
1901 | + |
1902 | + output->modes.push_back({size, vrefresh_hz}); |
1903 | + |
1904 | + if (current_crtc && kms_modes_are_equal(mode_info, current_crtc->mode)) |
1905 | + output->current_mode_index = m; |
1906 | + |
1907 | + if ((mode_info.type & DRM_MODE_TYPE_PREFERRED) == DRM_MODE_TYPE_PREFERRED) |
1908 | + output->preferred_mode_index = m; |
1909 | + } |
1910 | + |
1911 | + |
1912 | + output->current_format = mir_pixel_format_xrgb_8888; |
1913 | + output->power_mode = mir_power_mode_on; |
1914 | + output->orientation = mir_orientation_normal; |
1915 | + output->scale = 1.0f; |
1916 | + output->form_factor = mir_form_factor_monitor; |
1917 | + output->used = false; |
1918 | + } |
1919 | + |
1920 | + return outputs; |
1921 | +} |
1922 | + |
1923 | +mg::DisplayConfigurationCard create_card(int drm_fd) |
1924 | +{ |
1925 | + mgk::DRMModeResources resources{drm_fd}; |
1926 | + |
1927 | + size_t max_outputs = std::min(resources.num_crtcs(), resources.num_connectors()); |
1928 | + return {mg::DisplayConfigurationCardId{0}, max_outputs}; |
1929 | +} |
1930 | + |
1931 | +} |
1932 | + |
1933 | +mge::KMSDisplayConfiguration::KMSDisplayConfiguration(int drm_fd, EGLDisplay dpy) |
1934 | + : drm_fd{drm_fd}, |
1935 | + card{create_card(drm_fd)}, |
1936 | + outputs{create_outputs(drm_fd, dpy)} |
1937 | +{ |
1938 | + update(); |
1939 | +} |
1940 | + |
1941 | +mge::KMSDisplayConfiguration::KMSDisplayConfiguration( |
1942 | + KMSDisplayConfiguration const& conf) |
1943 | + : mg::DisplayConfiguration(), |
1944 | + drm_fd{conf.drm_fd}, |
1945 | + card(conf.card), |
1946 | + outputs{conf.outputs} |
1947 | +{ |
1948 | +} |
1949 | + |
1950 | +void mge::KMSDisplayConfiguration::for_each_card( |
1951 | + std::function<void(DisplayConfigurationCard const&)> f) const |
1952 | +{ |
1953 | + f(card); |
1954 | +} |
1955 | + |
1956 | +void mge::KMSDisplayConfiguration::for_each_output( |
1957 | + std::function<void(DisplayConfigurationOutput const&)> f) const |
1958 | +{ |
1959 | + for (auto const& output : outputs) |
1960 | + f(*output); |
1961 | +} |
1962 | + |
1963 | +void mge::KMSDisplayConfiguration::for_each_output( |
1964 | + std::function<void(UserDisplayConfigurationOutput&)> f) |
1965 | +{ |
1966 | + for (auto& output : outputs) |
1967 | + { |
1968 | + UserDisplayConfigurationOutput user(*output); |
1969 | + f(user); |
1970 | + } |
1971 | +} |
1972 | + |
1973 | +std::unique_ptr<mg::DisplayConfiguration> mge::KMSDisplayConfiguration::clone() const |
1974 | +{ |
1975 | + return std::make_unique<KMSDisplayConfiguration>(*this); |
1976 | +} |
1977 | + |
1978 | +uint32_t mge::KMSDisplayConfiguration::get_kms_connector_id( |
1979 | + DisplayConfigurationOutputId id) const |
1980 | +{ |
1981 | + auto iter = find_output_with_id(id); |
1982 | + |
1983 | + if (iter == outputs.end()) |
1984 | + { |
1985 | + BOOST_THROW_EXCEPTION( |
1986 | + std::out_of_range("Failed to find DisplayConfigurationOutput with provided id")); |
1987 | + } |
1988 | + |
1989 | + return id.as_value(); |
1990 | +} |
1991 | + |
1992 | +size_t mge::KMSDisplayConfiguration::get_kms_mode_index( |
1993 | + DisplayConfigurationOutputId id, |
1994 | + size_t conf_mode_index) const |
1995 | +{ |
1996 | + auto iter = find_output_with_id(id); |
1997 | + |
1998 | + if (iter == outputs.end() || conf_mode_index >= (*iter)->modes.size()) |
1999 | + { |
2000 | + BOOST_THROW_EXCEPTION( |
2001 | + std::out_of_range("Failed to find valid mode index for DisplayConfigurationOutput with provided id/mode_index")); |
2002 | + } |
2003 | + |
2004 | + return conf_mode_index; |
2005 | +} |
2006 | +void mge::KMSDisplayConfiguration::update() |
2007 | +{ |
2008 | + mgk::DRMModeResources resources{drm_fd}; |
2009 | + |
2010 | + for (auto const& connector : resources.connectors()) |
2011 | + { |
2012 | + update_output(resources, connector, **find_output_with_id(DisplayConfigurationOutputId(connector->connector_id))); |
2013 | + }; |
2014 | +} |
2015 | + |
2016 | +void mge::KMSDisplayConfiguration::for_each_output( |
2017 | + std::function<void(kms::EGLOutput const&)> const& f) const |
2018 | +{ |
2019 | + for (auto const& output : outputs) |
2020 | + { |
2021 | + f(*output); |
2022 | + } |
2023 | +} |
2024 | + |
2025 | +void mge::KMSDisplayConfiguration::update_output( |
2026 | + mgk::DRMModeResources const& resources, |
2027 | + mgk::DRMModeConnectorUPtr const& connector, |
2028 | + kms::EGLOutput& output) |
2029 | +{ |
2030 | + output.physical_size_mm = geom::Size{connector->mmWidth, connector->mmHeight}; |
2031 | + output.connected = connector->connection == DRM_MODE_CONNECTED; |
2032 | + |
2033 | + uint32_t const invalid_mode_index = std::numeric_limits<uint32_t>::max(); |
2034 | + |
2035 | + output.modes.clear(); |
2036 | + output.current_mode_index = invalid_mode_index; |
2037 | + |
2038 | + drmModeModeInfo* current_mode_info{nullptr}; |
2039 | + mgk::DRMModeCrtcUPtr current_crtc; |
2040 | + if (connector->encoder_id) |
2041 | + { |
2042 | + auto encoder = resources.encoder(connector->encoder_id); |
2043 | + if (encoder->crtc_id) |
2044 | + { |
2045 | + current_crtc = resources.crtc(encoder->crtc_id); |
2046 | + current_mode_info = ¤t_crtc->mode; |
2047 | + } |
2048 | + } |
2049 | + |
2050 | + /* Add all the available modes and find the current and preferred one */ |
2051 | + for (int m = 0; m < connector->count_modes; m++) |
2052 | + { |
2053 | + drmModeModeInfo& mode_info = connector->modes[m]; |
2054 | + |
2055 | + geom::Size size{mode_info.hdisplay, mode_info.vdisplay}; |
2056 | + |
2057 | + double vrefresh_hz = calculate_vrefresh_hz(mode_info); |
2058 | + |
2059 | + output.modes.push_back({size, vrefresh_hz}); |
2060 | + |
2061 | + if (current_mode_info && kms_modes_are_equal(mode_info, *current_mode_info)) |
2062 | + output.current_mode_index = m; |
2063 | + |
2064 | + if ((mode_info.type & DRM_MODE_TYPE_PREFERRED) == DRM_MODE_TYPE_PREFERRED) |
2065 | + output.preferred_mode_index = m; |
2066 | + } |
2067 | + |
2068 | + if (output.modes.empty()) |
2069 | + { |
2070 | + output.preferred_mode_index = invalid_mode_index; |
2071 | + } |
2072 | + output.physical_size_mm = geom::Size{connector->mmWidth, connector->mmHeight}; |
2073 | +} |
2074 | + |
2075 | +std::vector<std::shared_ptr<mge::kms::EGLOutput>>::iterator |
2076 | +mge::KMSDisplayConfiguration::find_output_with_id(mg::DisplayConfigurationOutputId id) |
2077 | +{ |
2078 | + return std::find_if( |
2079 | + outputs.begin(), outputs.end(), |
2080 | + [id](std::shared_ptr<DisplayConfigurationOutput> const& output) |
2081 | + { |
2082 | + return output->id == id; |
2083 | + }); |
2084 | +} |
2085 | + |
2086 | +std::vector<std::shared_ptr<mge::kms::EGLOutput>>::const_iterator |
2087 | +mge::KMSDisplayConfiguration::find_output_with_id(mg::DisplayConfigurationOutputId id) const |
2088 | +{ |
2089 | + return std::find_if( |
2090 | + outputs.begin(), outputs.end(), |
2091 | + [id](std::shared_ptr<DisplayConfigurationOutput> const& output) |
2092 | + { |
2093 | + return output->id == id; |
2094 | + }); |
2095 | +} |
2096 | |
2097 | === added file 'src/platforms/eglstream-kms/server/kms_display_configuration.h' |
2098 | --- src/platforms/eglstream-kms/server/kms_display_configuration.h 1970-01-01 00:00:00 +0000 |
2099 | +++ src/platforms/eglstream-kms/server/kms_display_configuration.h 2016-06-08 13:28:19 +0000 |
2100 | @@ -0,0 +1,70 @@ |
2101 | +/* |
2102 | + * Copyright © 2016 Canonical Ltd. |
2103 | + * |
2104 | + * This program is free software: you can redistribute it and/or modify it |
2105 | + * under the terms of the GNU Lesser General Public License version 3, |
2106 | + * as published by the Free Software Foundation. |
2107 | + * |
2108 | + * This program is distributed in the hope that it will be useful, |
2109 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2110 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2111 | + * GNU Lesser General Public License for more details. |
2112 | + * |
2113 | + * You should have received a copy of the GNU Lesser General Public License |
2114 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2115 | + * |
2116 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
2117 | + */ |
2118 | + |
2119 | +#ifndef MIR_PLATFORMS_EGLSTREAM_KMS_KMS_DISPLAY_CONFIGURATION_H_ |
2120 | +#define MIR_PLATFORMS_EGLSTREAM_KMS_KMS_DISPLAY_CONFIGURATION_H_ |
2121 | + |
2122 | +#include "egl_output.h" |
2123 | + |
2124 | +#include "mir/graphics/display_configuration.h" |
2125 | + |
2126 | + |
2127 | +#include <xf86drmMode.h> |
2128 | + |
2129 | +namespace mir |
2130 | +{ |
2131 | +namespace graphics |
2132 | +{ |
2133 | +namespace eglstream |
2134 | +{ |
2135 | + |
2136 | +class KMSDisplayConfiguration : public DisplayConfiguration |
2137 | +{ |
2138 | +public: |
2139 | + KMSDisplayConfiguration(int drm_fd, EGLDisplay dpy); |
2140 | + KMSDisplayConfiguration(KMSDisplayConfiguration const& conf); |
2141 | + |
2142 | + void for_each_card(std::function<void(DisplayConfigurationCard const&)> f) const override; |
2143 | + void for_each_output(std::function<void(DisplayConfigurationOutput const&)> f) const override; |
2144 | + void for_each_output(std::function<void(UserDisplayConfigurationOutput&)> f) override; |
2145 | + std::unique_ptr<DisplayConfiguration> clone() const override; |
2146 | + |
2147 | + uint32_t get_kms_connector_id(DisplayConfigurationOutputId id) const; |
2148 | + size_t get_kms_mode_index(DisplayConfigurationOutputId id, size_t conf_mode_index) const; |
2149 | + void update(); |
2150 | + |
2151 | + void for_each_output(std::function<void(kms::EGLOutput const&)> const& f) const; |
2152 | + |
2153 | +private: |
2154 | + void update_output( |
2155 | + graphics::kms::DRMModeResources const& resources, |
2156 | + graphics::kms::DRMModeConnectorUPtr const& connector, |
2157 | + kms::EGLOutput& output); |
2158 | + std::vector<std::shared_ptr<kms::EGLOutput>>::iterator find_output_with_id(DisplayConfigurationOutputId id); |
2159 | + std::vector<std::shared_ptr<kms::EGLOutput>>::const_iterator find_output_with_id(DisplayConfigurationOutputId id) const; |
2160 | + |
2161 | + int drm_fd; |
2162 | + DisplayConfigurationCard const card; |
2163 | + std::vector<std::shared_ptr<kms::EGLOutput>> outputs; |
2164 | +}; |
2165 | + |
2166 | +} |
2167 | +} |
2168 | +} |
2169 | + |
2170 | +#endif // MIR_PLATFORMS_EGLSTREAM_KMS_KMS_DISPLAY_CONFIGURATION_H_ |
2171 | |
2172 | === added file 'src/platforms/eglstream-kms/server/platform.cpp' |
2173 | --- src/platforms/eglstream-kms/server/platform.cpp 1970-01-01 00:00:00 +0000 |
2174 | +++ src/platforms/eglstream-kms/server/platform.cpp 2016-06-08 13:28:19 +0000 |
2175 | @@ -0,0 +1,162 @@ |
2176 | +/* |
2177 | + * Copyright © 2016 Canonical Ltd. |
2178 | + * |
2179 | + * This program is free software: you can redistribute it and/or modify it |
2180 | + * under the terms of the GNU Lesser General Public License version 3, |
2181 | + * as published by the Free Software Foundation. |
2182 | + * |
2183 | + * This program is distributed in the hope that it will be useful, |
2184 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2185 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2186 | + * GNU Lesser General Public License for more details. |
2187 | + * |
2188 | + * You should have received a copy of the GNU Lesser General Public License |
2189 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2190 | + * |
2191 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
2192 | + */ |
2193 | + |
2194 | +#include <epoxy/egl.h> |
2195 | + |
2196 | +#include "platform.h" |
2197 | +#include "buffer_allocator.h" |
2198 | +#include "display.h" |
2199 | +#include "mir/graphics/platform_ipc_operations.h" |
2200 | +#include "mir/graphics/platform_ipc_package.h" |
2201 | +#include "mir/graphics/platform_operation_message.h" |
2202 | +#include "mir/graphics/buffer_ipc_message.h" |
2203 | + |
2204 | +#include "mir/graphics/egl_error.h" |
2205 | + |
2206 | +#include <boost/throw_exception.hpp> |
2207 | +#include <xf86drm.h> |
2208 | +#include <fcntl.h> |
2209 | + |
2210 | +namespace mg = mir::graphics; |
2211 | +namespace mge = mir::graphics::eglstream; |
2212 | + |
2213 | +namespace |
2214 | +{ |
2215 | +// Our copy of eglext.h doesn't have this? |
2216 | +int const EGL_DRM_MASTER_FD_EXT{0x333C}; |
2217 | + |
2218 | +char const* drm_node_for_device(EGLDeviceEXT device) |
2219 | +{ |
2220 | + auto const device_path = eglQueryDeviceStringEXT(device, EGL_DRM_DEVICE_FILE_EXT); |
2221 | + if (!device_path) |
2222 | + { |
2223 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to determine DRM device node path from EGLDevice")); |
2224 | + } |
2225 | + return device_path; |
2226 | +} |
2227 | +} |
2228 | + |
2229 | +mge::Platform::Platform( |
2230 | + EGLDeviceEXT device, |
2231 | + std::shared_ptr<EmergencyCleanupRegistry> const& /*emergency_cleanup_registry*/, |
2232 | + std::shared_ptr<DisplayReport> const& /*report*/) |
2233 | + : device{device}, |
2234 | + display{EGL_NO_DISPLAY}, |
2235 | + drm_node{open(drm_node_for_device(device), O_RDWR | O_CLOEXEC)} |
2236 | +{ |
2237 | + using namespace std::literals; |
2238 | + |
2239 | + if (drm_node == mir::Fd::invalid) |
2240 | + { |
2241 | + BOOST_THROW_EXCEPTION(std::system_error( |
2242 | + errno, |
2243 | + std::system_category(), |
2244 | + "Failed to open DRM device "s + drm_node_for_device(device))); |
2245 | + } |
2246 | + |
2247 | + if (drmSetMaster(drm_node)) |
2248 | + { |
2249 | + BOOST_THROW_EXCEPTION((std::system_error{errno, std::system_category(), "Failed to acquire DRM master"})); |
2250 | + } |
2251 | + |
2252 | + int const drm_node_attrib[] = { |
2253 | + EGL_DRM_MASTER_FD_EXT, static_cast<int>(drm_node), EGL_NONE |
2254 | + }; |
2255 | + display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, device, drm_node_attrib); |
2256 | + |
2257 | + if (display == EGL_NO_DISPLAY) |
2258 | + { |
2259 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGLDisplay on the EGLDeviceEXT")); |
2260 | + } |
2261 | + |
2262 | + EGLint major{1}; |
2263 | + EGLint minor{4}; |
2264 | + if (eglInitialize(display, &major, &minor) != EGL_TRUE) |
2265 | + { |
2266 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to initialise EGL")); |
2267 | + } |
2268 | + if (major != 1 || minor != 4) |
2269 | + { |
2270 | + BOOST_THROW_EXCEPTION((std::runtime_error{ |
2271 | + "Incompatible EGL version"s + |
2272 | + "Wanted 1.4, got " + std::to_string(major) + "." + std::to_string(minor)})); |
2273 | + } |
2274 | +} |
2275 | + |
2276 | +mir::UniqueModulePtr<mg::GraphicBufferAllocator> mge::Platform::create_buffer_allocator() |
2277 | +{ |
2278 | + return mir::make_module_ptr<mge::BufferAllocator>(); |
2279 | +} |
2280 | + |
2281 | +mir::UniqueModulePtr<mg::Display> mge::Platform::create_display( |
2282 | + std::shared_ptr<DisplayConfigurationPolicy> const& configuration_policy, |
2283 | + std::shared_ptr<GLConfig> const& gl_config) |
2284 | +{ |
2285 | + return mir::make_module_ptr<mge::Display>(drm_node, display, configuration_policy, *gl_config); |
2286 | +} |
2287 | + |
2288 | +mir::UniqueModulePtr<mg::PlatformIpcOperations> mge::Platform::make_ipc_operations() const |
2289 | +{ |
2290 | + class NoIPCOperations : public mg::PlatformIpcOperations |
2291 | + { |
2292 | + |
2293 | + public: |
2294 | + void pack_buffer(BufferIpcMessage& packer, Buffer const& buffer, BufferIpcMsgType msg_type) const override |
2295 | + { |
2296 | + if (msg_type == mg::BufferIpcMsgType::full_msg) |
2297 | + { |
2298 | + auto native_handle = buffer.native_buffer_handle(); |
2299 | + for(auto i=0; i<native_handle->data_items; i++) |
2300 | + { |
2301 | + packer.pack_data(native_handle->data[i]); |
2302 | + } |
2303 | + for(auto i=0; i<native_handle->fd_items; i++) |
2304 | + { |
2305 | + packer.pack_fd(mir::Fd(IntOwnedFd{native_handle->fd[i]})); |
2306 | + } |
2307 | + |
2308 | + packer.pack_stride(buffer.stride()); |
2309 | + packer.pack_flags(native_handle->flags); |
2310 | + packer.pack_size(buffer.size()); |
2311 | + } |
2312 | + } |
2313 | + |
2314 | + void unpack_buffer(BufferIpcMessage& /*message*/, Buffer const& /*buffer*/) const override |
2315 | + { |
2316 | + |
2317 | + } |
2318 | + |
2319 | + std::shared_ptr<mg::PlatformIPCPackage> connection_ipc_package() override |
2320 | + { |
2321 | + return std::make_shared<mg::PlatformIPCPackage>(describe_graphics_module()); |
2322 | + } |
2323 | + |
2324 | + PlatformOperationMessage platform_operation(unsigned int const /*opcode*/, |
2325 | + PlatformOperationMessage const& /*message*/) override |
2326 | + { |
2327 | + BOOST_THROW_EXCEPTION(std::runtime_error{"No platform operations implemented"}); |
2328 | + } |
2329 | + }; |
2330 | + |
2331 | + return mir::make_module_ptr<NoIPCOperations>(); |
2332 | +} |
2333 | + |
2334 | +EGLNativeDisplayType mge::Platform::egl_native_display() const |
2335 | +{ |
2336 | + return display; |
2337 | +} |
2338 | |
2339 | === added file 'src/platforms/eglstream-kms/server/platform.h' |
2340 | --- src/platforms/eglstream-kms/server/platform.h 1970-01-01 00:00:00 +0000 |
2341 | +++ src/platforms/eglstream-kms/server/platform.h 2016-06-08 13:28:19 +0000 |
2342 | @@ -0,0 +1,66 @@ |
2343 | +/* |
2344 | + * Copyright © 2016 Canonical Ltd. |
2345 | + * |
2346 | + * This program is free software: you can redistribute it and/or modify it |
2347 | + * under the terms of the GNU Lesser General Public License version 3, |
2348 | + * as published by the Free Software Foundation. |
2349 | + * |
2350 | + * This program is distributed in the hope that it will be useful, |
2351 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2352 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2353 | + * GNU Lesser General Public License for more details. |
2354 | + * |
2355 | + * You should have received a copy of the GNU Lesser General Public License |
2356 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2357 | + * |
2358 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
2359 | + */ |
2360 | + |
2361 | +#ifndef MIR_PLATFORMS_EGLSTREAM_KMS_PLATFORM_H_ |
2362 | +#define MIR_PLATFORMS_EGLSTREAM_KMS_PLATFORM_H_ |
2363 | + |
2364 | +#include "mir/graphics/platform.h" |
2365 | +#include "mir/options/option.h" |
2366 | +#include "mir/graphics/graphic_buffer_allocator.h" |
2367 | +#include "mir/graphics/display.h" |
2368 | +#include "mir/graphics/platform_ipc_operations.h" |
2369 | +#include "mir/fd.h" |
2370 | + |
2371 | +#include <EGL/egl.h> |
2372 | +#include <EGL/eglext.h> |
2373 | + |
2374 | +namespace mir |
2375 | +{ |
2376 | +namespace graphics |
2377 | +{ |
2378 | +namespace eglstream |
2379 | +{ |
2380 | +class Platform : public mir::graphics::Platform |
2381 | +{ |
2382 | +public: |
2383 | + Platform( |
2384 | + EGLDeviceEXT device, |
2385 | + std::shared_ptr<EmergencyCleanupRegistry> const& /*emergency_cleanup_registry*/, |
2386 | + std::shared_ptr<DisplayReport> const& /*report*/); |
2387 | + ~Platform() = default; |
2388 | + |
2389 | + UniqueModulePtr<GraphicBufferAllocator> create_buffer_allocator() override; |
2390 | + |
2391 | + UniqueModulePtr<Display> create_display( |
2392 | + std::shared_ptr<DisplayConfigurationPolicy> const& /*initial_conf_policy*/, |
2393 | + std::shared_ptr<GLConfig> const& /*gl_config*/) override; |
2394 | + |
2395 | + UniqueModulePtr<PlatformIpcOperations> make_ipc_operations() const override; |
2396 | + |
2397 | + EGLNativeDisplayType egl_native_display() const override; |
2398 | + |
2399 | +private: |
2400 | + EGLDeviceEXT device; |
2401 | + EGLDisplay display; |
2402 | + mir::Fd const drm_node; |
2403 | +}; |
2404 | +} |
2405 | +} |
2406 | +} |
2407 | + |
2408 | +#endif // MIR_PLATFORMS_EGLSTREAM_KMS_PLATFORM_H_ |
2409 | |
2410 | === added file 'src/platforms/eglstream-kms/server/platform_symbols.cpp' |
2411 | --- src/platforms/eglstream-kms/server/platform_symbols.cpp 1970-01-01 00:00:00 +0000 |
2412 | +++ src/platforms/eglstream-kms/server/platform_symbols.cpp 2016-06-08 13:28:19 +0000 |
2413 | @@ -0,0 +1,189 @@ |
2414 | +/* |
2415 | + * Copyright © 2016 Canonical Ltd. |
2416 | + * |
2417 | + * This program is free software: you can redistribute it and/or modify it |
2418 | + * under the terms of the GNU Lesser General Public License version 3, |
2419 | + * as published by the Free Software Foundation. |
2420 | + * |
2421 | + * This program is distributed in the hope that it will be useful, |
2422 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2423 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2424 | + * GNU Lesser General Public License for more details. |
2425 | + * |
2426 | + * You should have received a copy of the GNU Lesser General Public License |
2427 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2428 | + * |
2429 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
2430 | + */ |
2431 | + |
2432 | +#include <epoxy/egl.h> |
2433 | + |
2434 | +#include "platform.h" |
2435 | +#include "mir/graphics/platform.h" |
2436 | +#include "mir/options/option.h" |
2437 | +#include "mir/module_deleter.h" |
2438 | +#include "mir/assert_module_entry_point.h" |
2439 | +#include "mir/libname.h" |
2440 | +#include "mir/log.h" |
2441 | +#include "mir/graphics/egl_error.h" |
2442 | + |
2443 | +#include <boost/throw_exception.hpp> |
2444 | +#include <boost/exception/diagnostic_information.hpp> |
2445 | +#include <xf86drm.h> |
2446 | +#include <sstream> |
2447 | + |
2448 | +#include <fcntl.h> |
2449 | + |
2450 | +namespace mg = mir::graphics; |
2451 | +namespace mo = mir::options; |
2452 | +namespace mge = mir::graphics::eglstream; |
2453 | + |
2454 | +mir::UniqueModulePtr<mg::Platform> create_host_platform( |
2455 | + std::shared_ptr<mo::Option> const& /*options*/, |
2456 | + std::shared_ptr<mir::EmergencyCleanupRegistry> const& emergency_cleanup_registry, |
2457 | + std::shared_ptr<mg::DisplayReport> const& report) |
2458 | +{ |
2459 | + mir::assert_entry_point_signature<mg::CreateHostPlatform>(&create_host_platform); |
2460 | + |
2461 | + int device_count{0}; |
2462 | + if (eglQueryDevicesEXT(0, nullptr, &device_count) != EGL_TRUE) |
2463 | + { |
2464 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to query device count with eglQueryDevicesEXT")); |
2465 | + } |
2466 | + |
2467 | + auto devices = std::make_unique<EGLDeviceEXT[]>(device_count); |
2468 | + if (eglQueryDevicesEXT(device_count, devices.get(), &device_count) != EGL_TRUE) |
2469 | + { |
2470 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to get device list with eglQueryDevicesEXT")); |
2471 | + } |
2472 | + |
2473 | + auto device = std::find_if(devices.get(), devices.get() + device_count, |
2474 | + [](EGLDeviceEXT device) |
2475 | + { |
2476 | + auto device_extensions = eglQueryDeviceStringEXT(device, EGL_EXTENSIONS); |
2477 | + if (device_extensions) |
2478 | + { |
2479 | + return strstr(device_extensions, "EGL_EXT_device_drm") != NULL; |
2480 | + } |
2481 | + return false; |
2482 | + }); |
2483 | + |
2484 | + if (device == (devices.get() + device_count)) |
2485 | + { |
2486 | + BOOST_THROW_EXCEPTION(std::runtime_error("Couldn't find EGLDeviceEXT supporting EGL_EXT_device_drm?")); |
2487 | + } |
2488 | + |
2489 | + return mir::make_module_ptr<mge::Platform>(*device, emergency_cleanup_registry, report); |
2490 | +} |
2491 | + |
2492 | +void add_graphics_platform_options(boost::program_options::options_description& /*config*/) |
2493 | +{ |
2494 | + mir::assert_entry_point_signature<mg::AddPlatformOptions>(&add_graphics_platform_options); |
2495 | +} |
2496 | + |
2497 | +mg::PlatformPriority probe_graphics_platform(mo::ProgramOption const& /*options*/) |
2498 | +{ |
2499 | + mir::assert_entry_point_signature<mg::PlatformProbe>(&probe_graphics_platform); |
2500 | + |
2501 | + std::vector<char const*> missing_extensions; |
2502 | + for (char const* extension : { |
2503 | + "EGL_EXT_platform_base", |
2504 | + "EGL_EXT_platform_device", |
2505 | + "EGL_EXT_device_base",}) |
2506 | + { |
2507 | + if (!epoxy_has_egl_extension(EGL_NO_DISPLAY, extension)) |
2508 | + { |
2509 | + missing_extensions.push_back(extension); |
2510 | + } |
2511 | + } |
2512 | + |
2513 | + if (!missing_extensions.empty()) |
2514 | + { |
2515 | + std::stringstream message; |
2516 | + message << "Missing required extension" << (missing_extensions.size() > 1 ? "s:" : ":"); |
2517 | + for (auto missing_extension : missing_extensions) |
2518 | + { |
2519 | + message << " " << missing_extension; |
2520 | + } |
2521 | + |
2522 | + mir::log( |
2523 | + mir::logging::Severity::debug, |
2524 | + "platform-eglstream", |
2525 | + "EGLStream platform is unsupported: %s", |
2526 | + message.str().c_str()); |
2527 | + return mg::PlatformPriority::unsupported; |
2528 | + } |
2529 | + |
2530 | + int device_count{0}; |
2531 | + if (eglQueryDevicesEXT(0, nullptr, &device_count) != EGL_TRUE) |
2532 | + { |
2533 | + mir::log( |
2534 | + mir::logging::Severity::informational, |
2535 | + "platform-eglstream", |
2536 | + "Platform claims to support EGL_EXT_device_base, but eglQueryDevicesEXT falied: %s", |
2537 | + mg::egl_category().message(eglGetError()).c_str()); |
2538 | + return mg::PlatformPriority::unsupported; |
2539 | + } |
2540 | + |
2541 | + auto devices = std::make_unique<EGLDeviceEXT[]>(device_count); |
2542 | + if (eglQueryDevicesEXT(device_count, devices.get(), &device_count) != EGL_TRUE) |
2543 | + { |
2544 | + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to get device list with eglQueryDevicesEXT")); |
2545 | + } |
2546 | + |
2547 | + if (std::none_of(devices.get(), devices.get() + device_count, |
2548 | + [](EGLDeviceEXT device) |
2549 | + { |
2550 | + auto device_extensions = eglQueryDeviceStringEXT(device, EGL_EXTENSIONS); |
2551 | + if (device_extensions) |
2552 | + { |
2553 | + mir::log( |
2554 | + mir::logging::Severity::debug, |
2555 | + "platform-eglstream", |
2556 | + "Found EGLDeviceEXT with device extensions: %s", device_extensions); |
2557 | + return strstr(device_extensions, "EGL_EXT_device_drm") != NULL; |
2558 | + } |
2559 | + else |
2560 | + { |
2561 | + mir::log( |
2562 | + mir::logging::Severity::debug, |
2563 | + "platform-eglstream", |
2564 | + "Found EGLDeviceEXT with no device extensions"); |
2565 | + return false; |
2566 | + } |
2567 | + })) |
2568 | + { |
2569 | + mir::log( |
2570 | + mir::logging::Severity::debug, |
2571 | + "platform-eglstream", |
2572 | + "EGLDeviceEXTs found, but none support required EGL_EXT_device_drm extension"); |
2573 | + return mg::PlatformPriority::unsupported; |
2574 | + } |
2575 | + |
2576 | + return mg::PlatformPriority::best; |
2577 | +} |
2578 | + |
2579 | +namespace |
2580 | +{ |
2581 | +mir::ModuleProperties const description = { |
2582 | + "mir:eglstream-kms", |
2583 | + MIR_VERSION_MAJOR, |
2584 | + MIR_VERSION_MINOR, |
2585 | + MIR_VERSION_MICRO, |
2586 | + mir::libname() |
2587 | +}; |
2588 | +} |
2589 | + |
2590 | +mir::ModuleProperties const* describe_graphics_module() |
2591 | +{ |
2592 | + mir::assert_entry_point_signature<mg::DescribeModule>(&describe_graphics_module); |
2593 | + return &description; |
2594 | +} |
2595 | + |
2596 | +mir::UniqueModulePtr<mg::Platform> create_guest_platform( |
2597 | + std::shared_ptr<mg::DisplayReport> const&, |
2598 | + std::shared_ptr<mg::NestedContext> const& /*nested_context*/) |
2599 | +{ |
2600 | + mir::assert_entry_point_signature<mg::CreateGuestPlatform>(&create_guest_platform); |
2601 | + return nullptr; |
2602 | +} |
2603 | |
2604 | === added file 'src/platforms/eglstream-kms/server/symbols.map.in' |
2605 | --- src/platforms/eglstream-kms/server/symbols.map.in 1970-01-01 00:00:00 +0000 |
2606 | +++ src/platforms/eglstream-kms/server/symbols.map.in 2016-06-08 13:28:19 +0000 |
2607 | @@ -0,0 +1,10 @@ |
2608 | +@MIR_SERVER_GRAPHICS_PLATFORM_VERSION@ { |
2609 | + global: |
2610 | + add_graphics_platform_options; |
2611 | + create_host_platform; |
2612 | + create_guest_platform; |
2613 | + probe_graphics_platform; |
2614 | + describe_graphics_module; |
2615 | + local: |
2616 | + *; |
2617 | +}; |
2618 | |
2619 | === modified file 'src/platforms/mesa/server/kms/platform_symbols.cpp' |
2620 | --- src/platforms/mesa/server/kms/platform_symbols.cpp 2016-01-29 08:18:22 +0000 |
2621 | +++ src/platforms/mesa/server/kms/platform_symbols.cpp 2016-06-08 13:28:19 +0000 |
2622 | @@ -26,6 +26,7 @@ |
2623 | #include "mir/assert_module_entry_point.h" |
2624 | #include "mir/libname.h" |
2625 | |
2626 | +#include <EGL/egl.h> |
2627 | #include <fcntl.h> |
2628 | #include <sys/ioctl.h> |
2629 | |
2630 | @@ -162,6 +163,19 @@ |
2631 | if (drm_devices.begin() == drm_devices.end()) |
2632 | return mg::PlatformPriority::unsupported; |
2633 | |
2634 | + // We also require GBM EGL platform |
2635 | + auto const* client_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); |
2636 | + if (!client_extensions) |
2637 | + { |
2638 | + // Doesn't support EGL client extensions; Mesa does, so this is unlikely to be mesa. |
2639 | + return mg::PlatformPriority::unsupported; |
2640 | + } |
2641 | + if (strstr(client_extensions, "EGL_MESA_platform_gbm") == nullptr) |
2642 | + { |
2643 | + // No platform_gbm support, so we can't work. |
2644 | + return mg::PlatformPriority::unsupported; |
2645 | + } |
2646 | + |
2647 | // Check for master |
2648 | int tmp_fd = -1; |
2649 | for (auto& device : drm_devices) |
2650 | |
2651 | === modified file 'tests/unit-tests/CMakeLists.txt' |
2652 | --- tests/unit-tests/CMakeLists.txt 2016-06-02 05:33:50 +0000 |
2653 | +++ tests/unit-tests/CMakeLists.txt 2016-06-08 13:28:19 +0000 |
2654 | @@ -17,6 +17,10 @@ |
2655 | add_definitions(-DMIR_BUILD_PLATFORM_MESA_X11) |
2656 | endif() |
2657 | |
2658 | +if (MIR_BUILD_PLATFORM_EGLSTREAM_KMS) |
2659 | + add_definitions(-DMIR_BUILD_PLATFORM_EGLSTREAM_KMS) |
2660 | +endif() |
2661 | + |
2662 | include_directories( |
2663 | ${CMAKE_SOURCE_DIR} |
2664 | |
2665 | |
2666 | === modified file 'tests/unit-tests/client/test_probing_client_platform_factory.cpp' |
2667 | --- tests/unit-tests/client/test_probing_client_platform_factory.cpp 2016-05-27 00:47:04 +0000 |
2668 | +++ tests/unit-tests/client/test_probing_client_platform_factory.cpp 2016-06-08 13:28:19 +0000 |
2669 | @@ -55,6 +55,13 @@ |
2670 | memset(&pkg, 0, sizeof(pkg)); |
2671 | } |
2672 | |
2673 | +#if defined(MIR_BUILD_PLATFORM_EGLSTREAM_KMS) |
2674 | +void populate_valid_eglstream_platform_package(MirPlatformPackage& pkg) |
2675 | +{ |
2676 | + memset(&pkg, 0, sizeof(pkg)); |
2677 | +} |
2678 | +#endif |
2679 | + |
2680 | class ModuleContext |
2681 | { |
2682 | public: |
2683 | @@ -115,6 +122,11 @@ |
2684 | std::make_pair<std::string, ModuleContext>( |
2685 | "android", { "android", "graphics-android", &populate_valid_android_platform_package })); |
2686 | #endif |
2687 | +#if defined(MIR_BUILD_PLATFORM_EGLSTREAM_KMS) |
2688 | + modules.emplace( |
2689 | + std::make_pair<std::string, ModuleContext>( |
2690 | + "eglstream-kms", { "eglstream", "graphics-eglstream-kms", &populate_valid_eglstream_platform_package})); |
2691 | +#endif |
2692 | return modules; |
2693 | } |
2694 | |
2695 | @@ -289,6 +301,26 @@ |
2696 | EXPECT_EQ(mir_platform_type_gbm, platform->platform_type()); |
2697 | } |
2698 | |
2699 | +#if defined(MIR_BUILD_PLATFORM_EGLSTREAM_KMS) |
2700 | +TEST(ProbingClientPlatformFactory, CreatesEglstreamPlatformOnEglstreamKMS) |
2701 | +#else |
2702 | +TEST(ProbingClientPlatformFactory, DISABLED_CreatesEglstreamPlatformOnEglstreamKMS) |
2703 | +#endif |
2704 | +{ |
2705 | + using namespace testing; |
2706 | + |
2707 | + mir::client::ProbingClientPlatformFactory factory( |
2708 | + mir::report::null_shared_library_prober_report(), |
2709 | + all_available_modules(), |
2710 | + {}); |
2711 | + |
2712 | + NiceMock<mtd::MockClientContext> context; |
2713 | + all_available_fixtures().at("eglstream-kms").setup_context(context); |
2714 | + |
2715 | + auto platform = factory.create_client_platform(&context); |
2716 | + EXPECT_EQ(mir_platform_type_eglstream, platform->platform_type()); |
2717 | +} |
2718 | + |
2719 | #ifdef MIR_BUILD_PLATFORM_ANDROID |
2720 | TEST(ProbingClientPlatformFactory, CreatesAndroidPlatformWhenAppropriate) |
2721 | #else |
2722 | |
2723 | === modified file 'tests/unit-tests/graphics/mesa/kms/test_platform.cpp' |
2724 | --- tests/unit-tests/graphics/mesa/kms/test_platform.cpp 2016-01-29 08:18:22 +0000 |
2725 | +++ tests/unit-tests/graphics/mesa/kms/test_platform.cpp 2016-06-08 13:28:19 +0000 |
2726 | @@ -40,6 +40,7 @@ |
2727 | |
2728 | #include "mir/test/doubles/mock_drm.h" |
2729 | #include "mir/test/doubles/mock_gbm.h" |
2730 | +#include "mir/test/doubles/mock_egl.h" |
2731 | #include "mir/test/doubles/fd_matcher.h" |
2732 | |
2733 | #include <gtest/gtest.h> |
2734 | @@ -65,8 +66,11 @@ |
2735 | public: |
2736 | void SetUp() |
2737 | { |
2738 | - ::testing::Mock::VerifyAndClearExpectations(&mock_drm); |
2739 | - ::testing::Mock::VerifyAndClearExpectations(&mock_gbm); |
2740 | + using namespace testing; |
2741 | + Mock::VerifyAndClearExpectations(&mock_drm); |
2742 | + Mock::VerifyAndClearExpectations(&mock_gbm); |
2743 | + ON_CALL(mock_egl, eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS)) |
2744 | + .WillByDefault(Return("EGL_AN_extension_string EGL_MESA_platform_gbm")); |
2745 | fake_devices.add_standard_device("standard-drm-devices"); |
2746 | } |
2747 | |
2748 | @@ -81,6 +85,7 @@ |
2749 | |
2750 | ::testing::NiceMock<mtd::MockDRM> mock_drm; |
2751 | ::testing::NiceMock<mtd::MockGBM> mock_gbm; |
2752 | + ::testing::NiceMock<mtd::MockEGL> mock_egl; |
2753 | mtf::UdevEnvironment fake_devices; |
2754 | }; |
2755 | } |
2756 | @@ -366,3 +371,43 @@ |
2757 | auto probe = platform_lib.load_function<mg::PlatformProbe>(probe_platform); |
2758 | EXPECT_EQ(mg::PlatformPriority::best, probe(options)); |
2759 | } |
2760 | + |
2761 | +TEST_F(MesaGraphicsPlatform, probe_returns_unsupported_when_egl_client_extensions_not_supported) |
2762 | +{ |
2763 | + using namespace testing; |
2764 | + |
2765 | + mtf::UdevEnvironment udev_environment; |
2766 | + boost::program_options::options_description po; |
2767 | + mir::options::ProgramOption options; |
2768 | + const char *argv[] = {"dummy", "--vt"}; |
2769 | + options.parse_arguments(po, 2, argv); |
2770 | + |
2771 | + udev_environment.add_standard_device("standard-drm-devices"); |
2772 | + |
2773 | + ON_CALL(mock_egl, eglQueryString(EGL_NO_DISPLAY, _)) |
2774 | + .WillByDefault(Return(nullptr)); |
2775 | + |
2776 | + mir::SharedLibrary platform_lib{mtf::server_platform("graphics-mesa-kms")}; |
2777 | + auto probe = platform_lib.load_function<mg::PlatformProbe>(probe_platform); |
2778 | + EXPECT_EQ(mg::PlatformPriority::unsupported, probe(options)); |
2779 | +} |
2780 | + |
2781 | +TEST_F(MesaGraphicsPlatform, probe_returns_unsupported_when_gbm_platform_not_supported) |
2782 | +{ |
2783 | + using namespace testing; |
2784 | + |
2785 | + mtf::UdevEnvironment udev_environment; |
2786 | + boost::program_options::options_description po; |
2787 | + mir::options::ProgramOption options; |
2788 | + const char *argv[] = {"dummy", "--vt"}; |
2789 | + options.parse_arguments(po, 2, argv); |
2790 | + |
2791 | + udev_environment.add_standard_device("standard-drm-devices"); |
2792 | + |
2793 | + ON_CALL(mock_egl, eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS)) |
2794 | + .WillByDefault(Return("EGL_KHR_not_really_an_extension EGL_EXT_master_of_the_house")); |
2795 | + |
2796 | + mir::SharedLibrary platform_lib{mtf::server_platform("graphics-mesa-kms")}; |
2797 | + auto probe = platform_lib.load_function<mg::PlatformProbe>(probe_platform); |
2798 | + EXPECT_EQ(mg::PlatformPriority::unsupported, probe(options)); |
2799 | +} |
2800 | |
2801 | === modified file 'tests/unit-tests/graphics/test_platform_prober.cpp' |
2802 | --- tests/unit-tests/graphics/test_platform_prober.cpp 2016-05-27 03:32:07 +0000 |
2803 | +++ tests/unit-tests/graphics/test_platform_prober.cpp 2016-06-08 13:28:19 +0000 |
2804 | @@ -27,6 +27,7 @@ |
2805 | #ifdef MIR_BUILD_PLATFORM_MESA_KMS |
2806 | #include "mir/test/doubles/mock_drm.h" |
2807 | #include "mir/test/doubles/mock_gbm.h" |
2808 | +#include "mir/test/doubles/mock_egl.h" |
2809 | #endif |
2810 | |
2811 | #ifdef MIR_BUILD_PLATFORM_ANDROID |
2812 | @@ -81,11 +82,18 @@ |
2813 | |
2814 | std::shared_ptr<void> ensure_mesa_probing_succeeds() |
2815 | { |
2816 | - auto udev = std::make_shared<mtf::UdevEnvironment>(); |
2817 | - |
2818 | - udev->add_standard_device("standard-drm-devices"); |
2819 | - |
2820 | - return udev; |
2821 | + using namespace testing; |
2822 | + struct MockEnvironment { |
2823 | + mtf::UdevEnvironment udev; |
2824 | + mtd::MockEGL egl; |
2825 | + }; |
2826 | + auto env = std::make_shared<MockEnvironment>(); |
2827 | + |
2828 | + env->udev.add_standard_device("standard-drm-devices"); |
2829 | + ON_CALL(env->egl, eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS)) |
2830 | + .WillByDefault(Return("EGL_MESA_platform_gbm")); |
2831 | + |
2832 | + return env; |
2833 | } |
2834 | |
2835 | std::shared_ptr<void> ensure_android_probing_succeeds() |
2836 | @@ -236,7 +244,7 @@ |
2837 | auto descriptor = module->load_function<mir::graphics::DescribeModule>(describe_module); |
2838 | auto description = descriptor(); |
2839 | |
2840 | - EXPECT_THAT(description->name, Not(HasSubstr("dummy"))); |
2841 | + EXPECT_THAT(description->name, Not(HasSubstr("mir:stub-graphics"))); |
2842 | } |
2843 | #endif |
2844 |
FAILED: Continuous integration, rev:3539 /mir-jenkins. ubuntu. com/job/ mir-ci/ 1081/ /mir-jenkins. ubuntu. com/job/ build-mir/ 1197/console /mir-jenkins. ubuntu. com/job/ build-0- fetch/1247/ console /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= vivid+overlay/ 1238/console /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= xenial/ 1238/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= vivid+overlay/ 1207/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= xenial/ 1207/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= android, release= vivid+overlay/ 1207/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= android, release= vivid+overlay/ 1207/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= mesa,release= xenial/ 1207/console
https:/
Executed test runs:
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/ 1081/rebuild
https:/