Mir

Merge lp:~raof/mir/eglstream-platform into lp:mir

Proposed by Chris Halse Rogers
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
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

To post a comment you must log in.
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3540
https://mir-jenkins.ubuntu.com/job/mir-ci/1082/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/1198
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/1248
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1239
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/1239
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1208
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1208/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1208
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1208/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1208
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1208/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1208
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1208/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1208
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1208/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/1082/rebuild

review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

No tests needed?

review: Needs Information
Revision history for this message
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::PlatformPriority::best when eglstream-kms returns that? (unless render nodes and/or dri nodes are unavailable when using eglstream).

2489 +mg::PlatformPriority probe_graphics_platform(mo::ProgramOption const& /*options*/)

------------------------

Are guest platforms a TODO or will they be unsupported forever? A comment would be good either way?

2587 +mir::UniqueModulePtr<mg::Platform> create_guest_platform(
2588 + std::shared_ptr<mg::DisplayReport> const&,
2589 + std::shared_ptr<mg::NestedContext> const& /*nested_context*/)
2590 +{
2591 + mir::assert_entry_point_signature<mg::CreateGuestPlatform>(&create_guest_platform);
2592 + return nullptr;
2593 +}

------------------------
Isn't this going to require root? Can drm render nodes not be used instead?
2238 + if (drmSetMaster(drm_node))

------------------------
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_properties.usage == BufferUsage::software)
and throw if hardware is requested.

765 +std::shared_ptr<mg::Buffer> mge::BufferAllocator::alloc_buffer(
766
------------------------
s/falied/failed
+ "Platform claims to support EGL_EXT_device_base, but eglQueryDevicesEXT falied: %s",

------------------------
Probably be better handled by returning unsupported as was done to get the device_count.
2533 + if (eglQueryDevicesEXT(device_count, devices.get(), &device_count) != EGL_TRUE)
2534 + {
2535 + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to get device list with eglQueryDevicesEXT"));
2536 + }

review: Needs Fixing
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3541
https://mir-jenkins.ubuntu.com/job/mir-ci/1101/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/1220
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/1270
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1261
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/1261
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1230
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1230/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1230
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1230/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1230
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1230/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1230
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1230/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1230
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1230/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/1101/rebuild

review: Approve (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3544
https://mir-jenkins.ubuntu.com/job/mir-ci/1111/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/1230/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/1280
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1271
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/1271
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1240
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1240/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1240
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1240/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1240
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1240/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1240/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1240
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1240/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/1111/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3544
https://mir-jenkins.ubuntu.com/job/mir-ci/1116/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/1235
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/1285
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1276
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/1276
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1245
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1245/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1245
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1245/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1245
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1245/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1245
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1245/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1245
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1245/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/1116/rebuild

review: Approve (continuous-integration)
Revision history for this message
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.

review: Abstain
Revision history for this message
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.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

We can land wit ha promise of tests later

Revision history for this message
Mir CI Bot (mir-ci-bot) :
review: Approve (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) :
review: Approve (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Autolanding.
More details in the following jenkins job:
https://mir-jenkins.ubuntu.com/job/mir-autolanding/311/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/1265
    FAILURE: https://mir-jenkins.ubuntu.com/job/generic-land-mp/336/console
    None: https://mir-jenkins.ubuntu.com/job/generic-land-mp/337/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/1315
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1306
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/1306
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1275
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1275/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1275
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1275/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1275
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1275/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1275
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1275/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1275
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1275/artifact/output/*zip*/output.zip

review: Needs Fixing (continuous-integration)
Revision history for this message
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::PlatformPriority::best when eglstream-kms returns > that? (unless render nodes and/or dri nodes are unavailable when using eglstream).

This was resolved by commits (and tests!) subsequent to this question; mesa-kms now checks for the EGL_MESA_platform_gbm extension (which provides the gbm-import features that we use).

> 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(drm_node))

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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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, &params) != 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, &params) != 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 = &current_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

Subscribers

People subscribed via source and target branches