Changeset View
Changeset View
Standalone View
Standalone View
platformsupport/scenes/opengl/linux_dmabuf.cpp
- This file was added.
1 | /******************************************************************** | ||||
---|---|---|---|---|---|
2 | KWin - the KDE window manager | ||||
3 | This file is part of the KDE project. | ||||
4 | | ||||
5 | Copyright © 2019 Roman Gilg <subdiff@gmail.com> | ||||
6 | Copyright © 2018 Fredrik Höglund <fredrik@kde.org> | ||||
7 | | ||||
8 | This program is free software; you can redistribute it and/or modify | ||||
9 | it under the terms of the GNU General Public License as published by | ||||
10 | the Free Software Foundation; either version 2 of the License, or | ||||
11 | (at your option) any later version. | ||||
12 | | ||||
13 | This program is distributed in the hope that it will be useful, | ||||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
16 | GNU General Public License for more details. | ||||
17 | | ||||
18 | You should have received a copy of the GNU General Public License | ||||
19 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
20 | *********************************************************************/ | ||||
21 | #include "linux_dmabuf.h" | ||||
22 | | ||||
23 | #include "drm_fourcc.h" | ||||
24 | #include "../../../wayland_server.h" | ||||
25 | | ||||
26 | #include <KWayland/Server/display.h> | ||||
27 | | ||||
28 | #include <unistd.h> | ||||
29 | | ||||
30 | namespace KWin | ||||
31 | { | ||||
32 | | ||||
33 | typedef EGLBoolean (*eglQueryDmaBufFormatsEXT_func) (EGLDisplay dpy, EGLint max_formats, EGLint *formats, EGLint *num_formats); | ||||
34 | typedef EGLBoolean (*eglQueryDmaBufModifiersEXT_func) (EGLDisplay dpy, EGLint format, EGLint max_modifiers, EGLuint64KHR *modifiers, EGLBoolean *external_only, EGLint *num_modifiers); | ||||
35 | eglQueryDmaBufFormatsEXT_func eglQueryDmaBufFormatsEXT = nullptr; | ||||
36 | eglQueryDmaBufModifiersEXT_func eglQueryDmaBufModifiersEXT = nullptr; | ||||
37 | | ||||
38 | #ifndef EGL_EXT_image_dma_buf_import | ||||
39 | #define EGL_LINUX_DMA_BUF_EXT 0x3270 | ||||
40 | #define EGL_LINUX_DRM_FOURCC_EXT 0x3271 | ||||
41 | #define EGL_DMA_BUF_PLANE0_FD_EXT 0x3272 | ||||
42 | #define EGL_DMA_BUF_PLANE0_OFFSET_EXT 0x3273 | ||||
43 | #define EGL_DMA_BUF_PLANE0_PITCH_EXT 0x3274 | ||||
44 | #define EGL_DMA_BUF_PLANE1_FD_EXT 0x3275 | ||||
45 | #define EGL_DMA_BUF_PLANE1_OFFSET_EXT 0x3276 | ||||
46 | #define EGL_DMA_BUF_PLANE1_PITCH_EXT 0x3277 | ||||
47 | #define EGL_DMA_BUF_PLANE2_FD_EXT 0x3278 | ||||
48 | #define EGL_DMA_BUF_PLANE2_OFFSET_EXT 0x3279 | ||||
49 | #define EGL_DMA_BUF_PLANE2_PITCH_EXT 0x327A | ||||
50 | #define EGL_YUV_COLOR_SPACE_HINT_EXT 0x327B | ||||
51 | #define EGL_SAMPLE_RANGE_HINT_EXT 0x327C | ||||
52 | #define EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT 0x327D | ||||
53 | #define EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT 0x327E | ||||
54 | #define EGL_ITU_REC601_EXT 0x327F | ||||
55 | #define EGL_ITU_REC709_EXT 0x3280 | ||||
56 | #define EGL_ITU_REC2020_EXT 0x3281 | ||||
57 | #define EGL_YUV_FULL_RANGE_EXT 0x3282 | ||||
58 | #define EGL_YUV_NARROW_RANGE_EXT 0x3283 | ||||
59 | #define EGL_YUV_CHROMA_SITING_0_EXT 0x3284 | ||||
60 | #define EGL_YUV_CHROMA_SITING_0_5_EXT 0x3285 | ||||
61 | #endif // EGL_EXT_image_dma_buf_import | ||||
62 | | ||||
63 | #ifndef EGL_EXT_image_dma_buf_import_modifiers | ||||
64 | #define EGL_DMA_BUF_PLANE3_FD_EXT 0x3440 | ||||
65 | #define EGL_DMA_BUF_PLANE3_OFFSET_EXT 0x3441 | ||||
66 | #define EGL_DMA_BUF_PLANE3_PITCH_EXT 0x3442 | ||||
67 | #define EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT 0x3443 | ||||
68 | #define EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT 0x3444 | ||||
69 | #define EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT 0x3445 | ||||
70 | #define EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT 0x3446 | ||||
71 | #define EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT 0x3447 | ||||
72 | #define EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT 0x3448 | ||||
73 | #define EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT 0x3449 | ||||
74 | #define EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT 0x344A | ||||
75 | #endif // EGL_EXT_image_dma_buf_import_modifiers | ||||
76 | | ||||
77 | DmabufBuffer::DmabufBuffer(EGLImage image, | ||||
78 | const QVector<Plane> &planes, | ||||
79 | uint32_t format, | ||||
80 | const QSize &size, | ||||
81 | Flags flags, | ||||
82 | LinuxDmabuf *interfaceImpl) | ||||
83 | : KWayland::Server::LinuxDmabufUnstableV1Buffer(format, size), | ||||
84 | m_image(image), | ||||
85 | m_planes(planes), | ||||
86 | m_flags(flags), | ||||
87 | m_interfaceImpl(interfaceImpl) | ||||
88 | { | ||||
89 | } | ||||
90 | | ||||
91 | DmabufBuffer::~DmabufBuffer() | ||||
92 | { | ||||
93 | if (m_interfaceImpl) { | ||||
94 | m_interfaceImpl->removeBuffer(this); | ||||
95 | | ||||
96 | assert(m_image != EGL_NO_IMAGE_KHR); | ||||
97 | eglDestroyImageKHR(m_interfaceImpl->m_backend->eglDisplay(), m_image); | ||||
98 | } | ||||
99 | | ||||
100 | // Close all open file descriptors | ||||
101 | for (int i = 0; i < m_planes.count(); i++) { | ||||
102 | if (m_planes[i].fd != -1) | ||||
103 | ::close(m_planes[i].fd); | ||||
104 | m_planes[i].fd = -1; | ||||
105 | } | ||||
106 | } | ||||
107 | | ||||
108 | void DmabufBuffer::destroyImage() | ||||
109 | { | ||||
110 | assert(m_image != EGL_NO_IMAGE_KHR); | ||||
111 | eglDestroyImageKHR(m_interfaceImpl->m_backend->eglDisplay(), m_image); | ||||
112 | m_image = EGL_NO_IMAGE_KHR; | ||||
113 | m_interfaceImpl = nullptr; | ||||
114 | } | ||||
115 | | ||||
116 | using Plane = KWayland::Server::LinuxDmabufUnstableV1Interface::Plane; | ||||
117 | using Flags = KWayland::Server::LinuxDmabufUnstableV1Interface::Flags; | ||||
118 | | ||||
119 | KWayland::Server::LinuxDmabufUnstableV1Buffer *LinuxDmabuf::importBuffer(const QVector<Plane> &planes, | ||||
120 | uint32_t format, | ||||
121 | const QSize &size, | ||||
122 | Flags flags) | ||||
123 | { | ||||
124 | // FIXME: Add support for multi-planar images | ||||
125 | if (planes.count() != 1) | ||||
126 | return nullptr; | ||||
127 | | ||||
128 | const EGLint attribs[] = { | ||||
129 | EGL_WIDTH, size.width(), | ||||
130 | EGL_HEIGHT, size.height(), | ||||
131 | EGL_LINUX_DRM_FOURCC_EXT, EGLint(format), | ||||
132 | EGL_DMA_BUF_PLANE0_FD_EXT, planes[0].fd, | ||||
133 | EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGLint(planes[0].offset), | ||||
134 | EGL_DMA_BUF_PLANE0_PITCH_EXT, EGLint(planes[0].stride), | ||||
135 | EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, EGLint(planes[0].modifier & 0xffffffff), | ||||
136 | EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, EGLint(planes[0].modifier >> 32), | ||||
137 | EGL_NONE | ||||
138 | }; | ||||
139 | | ||||
140 | // Note that the EGLImage does NOT take onwership of the file descriptors | ||||
141 | EGLImage image = eglCreateImageKHR(m_backend->eglDisplay(), EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, (EGLClientBuffer) nullptr, attribs); | ||||
142 | if (image == EGL_NO_IMAGE_KHR) | ||||
143 | return nullptr; | ||||
144 | | ||||
145 | auto *buf = new DmabufBuffer(image, planes, format, size, flags, this); | ||||
146 | m_buffers << buf; | ||||
147 | return buf; | ||||
148 | } | ||||
149 | | ||||
150 | LinuxDmabuf* LinuxDmabuf::factory(AbstractEglBackend *backend) | ||||
151 | { | ||||
152 | if (!backend->hasExtension(QByteArrayLiteral("EGL_EXT_image_dma_buf_import"))) { | ||||
153 | return nullptr; | ||||
154 | } | ||||
155 | | ||||
156 | if (backend->hasExtension(QByteArrayLiteral("EGL_EXT_image_dma_buf_import_modifiers"))) { | ||||
157 | eglQueryDmaBufFormatsEXT = (eglQueryDmaBufFormatsEXT_func) eglGetProcAddress("eglQueryDmaBufFormatsEXT"); | ||||
158 | eglQueryDmaBufModifiersEXT = (eglQueryDmaBufModifiersEXT_func) eglGetProcAddress("eglQueryDmaBufModifiersEXT"); | ||||
159 | } | ||||
160 | | ||||
161 | if (eglQueryDmaBufFormatsEXT == nullptr) { | ||||
162 | return nullptr; | ||||
163 | } | ||||
164 | | ||||
165 | return new LinuxDmabuf(backend); | ||||
166 | } | ||||
167 | | ||||
168 | LinuxDmabuf::LinuxDmabuf(AbstractEglBackend *backend) | ||||
169 | : KWayland::Server::LinuxDmabufUnstableV1Interface::Impl() | ||||
170 | , m_backend(backend) | ||||
171 | { | ||||
172 | Q_ASSERT(waylandServer()); | ||||
173 | m_interface = waylandServer()->display()->createLinuxDmabufInterface(backend); | ||||
174 | setSupportedFormatsAndModifiers(); | ||||
175 | m_interface->setImpl(this); | ||||
176 | m_interface->create(); | ||||
177 | } | ||||
178 | | ||||
179 | LinuxDmabuf::~LinuxDmabuf() | ||||
180 | { | ||||
181 | for (auto *dmabuf : qAsConst(m_buffers)) { | ||||
182 | dmabuf->destroyImage(); | ||||
183 | } | ||||
184 | } | ||||
185 | | ||||
186 | void LinuxDmabuf::removeBuffer(DmabufBuffer *buffer) | ||||
187 | { | ||||
188 | m_buffers.remove(buffer); | ||||
189 | } | ||||
190 | | ||||
191 | const uint32_t s_multiPlaneFormats[] = { | ||||
192 | DRM_FORMAT_XRGB8888_A8, | ||||
193 | DRM_FORMAT_XBGR8888_A8, | ||||
194 | DRM_FORMAT_RGBX8888_A8, | ||||
195 | DRM_FORMAT_BGRX8888_A8, | ||||
196 | DRM_FORMAT_RGB888_A8, | ||||
197 | DRM_FORMAT_BGR888_A8, | ||||
198 | DRM_FORMAT_RGB565_A8, | ||||
199 | DRM_FORMAT_BGR565_A8, | ||||
200 | | ||||
201 | DRM_FORMAT_NV12, | ||||
202 | DRM_FORMAT_NV21, | ||||
203 | DRM_FORMAT_NV16, | ||||
204 | DRM_FORMAT_NV61, | ||||
205 | DRM_FORMAT_NV24, | ||||
206 | DRM_FORMAT_NV42, | ||||
207 | | ||||
208 | DRM_FORMAT_YUV410, | ||||
209 | DRM_FORMAT_YVU410, | ||||
210 | DRM_FORMAT_YUV411, | ||||
211 | DRM_FORMAT_YVU411, | ||||
212 | DRM_FORMAT_YUV420, | ||||
213 | DRM_FORMAT_YVU420, | ||||
214 | DRM_FORMAT_YUV422, | ||||
215 | DRM_FORMAT_YVU422, | ||||
216 | DRM_FORMAT_YUV444, | ||||
217 | DRM_FORMAT_YVU444 | ||||
218 | }; | ||||
219 | | ||||
220 | void filterFormatsWithMultiplePlanes(QVector<uint32_t> &formats) | ||||
221 | { | ||||
222 | QVector<uint32_t>::iterator it = formats.begin(); | ||||
223 | while (it != formats.end()) { | ||||
224 | for (auto linuxFormat : s_multiPlaneFormats) { | ||||
225 | if (*it == linuxFormat) { | ||||
226 | qDebug() << "Filter multi-plane format" << *it; | ||||
227 | it = formats.erase(it); | ||||
228 | it--; | ||||
229 | break; | ||||
230 | } | ||||
231 | } | ||||
232 | it++; | ||||
233 | } | ||||
234 | } | ||||
235 | | ||||
236 | void LinuxDmabuf::setSupportedFormatsAndModifiers() | ||||
237 | { | ||||
238 | const EGLDisplay eglDisplay = m_backend->eglDisplay(); | ||||
239 | EGLint count = 0; | ||||
240 | EGLBoolean success = eglQueryDmaBufFormatsEXT(eglDisplay, 0, NULL, &count); | ||||
241 | | ||||
242 | if (!success || count == 0) { | ||||
243 | return; | ||||
244 | } | ||||
245 | | ||||
246 | QVector<uint32_t> formats(count); | ||||
247 | if (!eglQueryDmaBufFormatsEXT(eglDisplay, count, (EGLint *) formats.data(), &count)) { | ||||
248 | return; | ||||
249 | } | ||||
250 | | ||||
251 | // TODO: this is not sufficient. a modifier might add additional planes | ||||
252 | // to an otherwise 1-plane format. | ||||
253 | filterFormatsWithMultiplePlanes(formats); | ||||
254 | | ||||
255 | QHash<uint32_t, QSet<uint64_t> > set; | ||||
256 | | ||||
257 | for (auto format : qAsConst(formats)) { | ||||
258 | if (eglQueryDmaBufModifiersEXT != nullptr) { | ||||
259 | count = 0; | ||||
260 | success = eglQueryDmaBufModifiersEXT(eglDisplay, format, 0, NULL, NULL, &count); | ||||
261 | | ||||
262 | if (success && count > 0) { | ||||
263 | QVector<uint64_t> modifiers(count); | ||||
264 | if (eglQueryDmaBufModifiersEXT(eglDisplay, | ||||
265 | format, count, modifiers.data(), | ||||
266 | NULL, &count)) { | ||||
267 | QSet<uint64_t> modifiersSet; | ||||
268 | for (auto mod : qAsConst(modifiers)) { | ||||
269 | modifiersSet.insert(mod); | ||||
270 | } | ||||
271 | set.insert(format, modifiersSet); | ||||
272 | continue; | ||||
273 | } | ||||
274 | } | ||||
275 | } | ||||
276 | set.insert(format, QSet<uint64_t>()); | ||||
277 | } | ||||
278 | | ||||
279 | m_interface->setSupportedFormatsWithModifiers(set); | ||||
280 | } | ||||
281 | | ||||
282 | } |