KWin
Loading...
Searching...
No Matches
egldisplay.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@gmail.com>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9#include "egldisplay.h"
10#include "core/graphicsbuffer.h"
11#include "opengl/eglutils_p.h"
12#include "opengl/glutils.h"
13#include "utils/common.h"
14
15#include <QOpenGLContext>
16#include <drm_fourcc.h>
18
19#ifndef EGL_DRM_RENDER_NODE_FILE_EXT
20#define EGL_DRM_RENDER_NODE_FILE_EXT 0x3377
21#endif
22
23namespace KWin
24{
25
26static bool isOpenGLES()
27{
28 if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) {
29 return true;
30 }
31 return QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES;
32}
33
34std::unique_ptr<EglDisplay> EglDisplay::create(::EGLDisplay display, bool owning)
35{
36 if (!display) {
37 return nullptr;
38 }
39 EGLint major, minor;
40 if (eglInitialize(display, &major, &minor) == EGL_FALSE) {
41 qCWarning(KWIN_OPENGL) << "eglInitialize failed";
42 EGLint error = eglGetError();
43 if (error != EGL_SUCCESS) {
44 qCWarning(KWIN_OPENGL) << "Error during eglInitialize " << error;
45 }
46 return nullptr;
47 }
48 EGLint error = eglGetError();
49 if (error != EGL_SUCCESS) {
50 qCWarning(KWIN_OPENGL) << "Error during eglInitialize " << error;
51 return nullptr;
52 }
53 qCDebug(KWIN_OPENGL) << "Egl Initialize succeeded";
54 if (eglBindAPI(isOpenGLES() ? EGL_OPENGL_ES_API : EGL_OPENGL_API) == EGL_FALSE) {
55 qCCritical(KWIN_OPENGL) << "bind OpenGL API failed";
56 return nullptr;
57 }
58 qCDebug(KWIN_OPENGL) << "EGL version: " << major << "." << minor;
59
60 const auto extensions = QByteArray(eglQueryString(display, EGL_EXTENSIONS)).split(' ');
61
62 const QByteArray requiredExtensions[] = {
63 QByteArrayLiteral("EGL_KHR_no_config_context"),
64 QByteArrayLiteral("EGL_KHR_surfaceless_context"),
65 };
66 for (const QByteArray &extensionName : requiredExtensions) {
67 if (!extensions.contains(extensionName)) {
68 qCWarning(KWIN_OPENGL) << extensionName << "extension is unsupported";
69 return nullptr;
70 }
71 }
72
73 return std::make_unique<EglDisplay>(display, extensions, owning);
74}
75
76EglDisplay::EglDisplay(::EGLDisplay display, const QList<QByteArray> &extensions, bool owning)
77 : m_handle(display)
78 , m_extensions(extensions)
79 , m_owning(owning)
80 , m_supportsBufferAge(extensions.contains(QByteArrayLiteral("EGL_EXT_buffer_age")) && qgetenv("KWIN_USE_BUFFER_AGE") != "0")
81 , m_supportsNativeFence(extensions.contains(QByteArrayLiteral("EGL_ANDROID_native_fence_sync")))
82 , m_importFormats(queryImportFormats())
83{
84}
85
87{
88 if (m_owning) {
89 eglTerminate(m_handle);
90 }
91}
92
93QList<QByteArray> EglDisplay::extensions() const
94{
95 return m_extensions;
96}
97
98::EGLDisplay EglDisplay::handle() const
99{
100 return m_handle;
101}
102
103bool EglDisplay::hasExtension(const QByteArray &name) const
104{
105 return m_extensions.contains(name);
106}
107
108static bool checkExtension(const QByteArrayView extensions, const QByteArrayView extension)
109{
110 for (int i = 0; i < extensions.size();) {
111 if (extensions[i] == ' ') {
112 i++;
113 continue;
114 }
115 int next = extensions.indexOf(' ', i);
116 if (next == -1) {
117 next = extensions.size();
118 }
119
120 const int size = next - i;
121 if (extension.size() == size && extensions.sliced(i, size) == extension) {
122 return true;
123 }
124
125 i = next;
126 }
127
128 return false;
129}
130
132{
133 const char *clientExtensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
134 if (checkExtension(clientExtensions, "EGL_EXT_device_query")) {
135 EGLAttrib eglDeviceAttrib;
136 if (eglQueryDisplayAttribEXT(m_handle, EGL_DEVICE_EXT, &eglDeviceAttrib)) {
137 EGLDeviceEXT eglDevice = reinterpret_cast<EGLDeviceEXT>(eglDeviceAttrib);
138
139 const char *deviceExtensions = eglQueryDeviceStringEXT(eglDevice, EGL_EXTENSIONS);
140 if (checkExtension(deviceExtensions, "EGL_EXT_device_drm_render_node")) {
141 if (const char *node = eglQueryDeviceStringEXT(eglDevice, EGL_DRM_RENDER_NODE_FILE_EXT)) {
142 return QString::fromLocal8Bit(node);
143 }
144 }
145 if (checkExtension(deviceExtensions, "EGL_EXT_device_drm")) {
146 // Fallback to display device.
147 if (const char *node = eglQueryDeviceStringEXT(eglDevice, EGL_DRM_DEVICE_FILE_EXT)) {
148 return QString::fromLocal8Bit(node);
149 }
150 }
151 }
152 }
153 return QString();
154}
155
157{
158 return m_supportsBufferAge;
159}
160
162{
163 return m_supportsNativeFence;
164}
165
167{
168 QList<EGLint> attribs;
169 attribs.reserve(6 + dmabuf.planeCount * 10 + 1);
170
171 attribs << EGL_WIDTH << dmabuf.width
172 << EGL_HEIGHT << dmabuf.height
173 << EGL_LINUX_DRM_FOURCC_EXT << dmabuf.format;
174
175 attribs << EGL_DMA_BUF_PLANE0_FD_EXT << dmabuf.fd[0].get()
176 << EGL_DMA_BUF_PLANE0_OFFSET_EXT << dmabuf.offset[0]
177 << EGL_DMA_BUF_PLANE0_PITCH_EXT << dmabuf.pitch[0];
178 if (dmabuf.modifier != DRM_FORMAT_MOD_INVALID) {
179 attribs << EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT << EGLint(dmabuf.modifier & 0xffffffff)
180 << EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT << EGLint(dmabuf.modifier >> 32);
181 }
182
183 if (dmabuf.planeCount > 1) {
184 attribs << EGL_DMA_BUF_PLANE1_FD_EXT << dmabuf.fd[1].get()
185 << EGL_DMA_BUF_PLANE1_OFFSET_EXT << dmabuf.offset[1]
186 << EGL_DMA_BUF_PLANE1_PITCH_EXT << dmabuf.pitch[1];
187 if (dmabuf.modifier != DRM_FORMAT_MOD_INVALID) {
188 attribs << EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT << EGLint(dmabuf.modifier & 0xffffffff)
189 << EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT << EGLint(dmabuf.modifier >> 32);
190 }
191 }
192
193 if (dmabuf.planeCount > 2) {
194 attribs << EGL_DMA_BUF_PLANE2_FD_EXT << dmabuf.fd[2].get()
195 << EGL_DMA_BUF_PLANE2_OFFSET_EXT << dmabuf.offset[2]
196 << EGL_DMA_BUF_PLANE2_PITCH_EXT << dmabuf.pitch[2];
197 if (dmabuf.modifier != DRM_FORMAT_MOD_INVALID) {
198 attribs << EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT << EGLint(dmabuf.modifier & 0xffffffff)
199 << EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT << EGLint(dmabuf.modifier >> 32);
200 }
201 }
202
203 if (dmabuf.planeCount > 3) {
204 attribs << EGL_DMA_BUF_PLANE3_FD_EXT << dmabuf.fd[3].get()
205 << EGL_DMA_BUF_PLANE3_OFFSET_EXT << dmabuf.offset[3]
206 << EGL_DMA_BUF_PLANE3_PITCH_EXT << dmabuf.pitch[3];
207 if (dmabuf.modifier != DRM_FORMAT_MOD_INVALID) {
208 attribs << EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT << EGLint(dmabuf.modifier & 0xffffffff)
209 << EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT << EGLint(dmabuf.modifier >> 32);
210 }
211 }
212
213 attribs << EGL_NONE;
214
215 return eglCreateImageKHR(m_handle, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs.data());
216}
217
218EGLImageKHR EglDisplay::importDmaBufAsImage(const DmaBufAttributes &dmabuf, int plane, int format, const QSize &size) const
219{
220 QList<EGLint> attribs;
221 attribs.reserve(6 + 1 * 10 + 1);
222
223 attribs << EGL_WIDTH << size.width()
224 << EGL_HEIGHT << size.height()
225 << EGL_LINUX_DRM_FOURCC_EXT << format;
226
227 attribs << EGL_DMA_BUF_PLANE0_FD_EXT << dmabuf.fd[plane].get()
228 << EGL_DMA_BUF_PLANE0_OFFSET_EXT << dmabuf.offset[plane]
229 << EGL_DMA_BUF_PLANE0_PITCH_EXT << dmabuf.pitch[plane];
230 if (dmabuf.modifier != DRM_FORMAT_MOD_INVALID) {
231 attribs << EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT << EGLint(dmabuf.modifier & 0xffffffff)
232 << EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT << EGLint(dmabuf.modifier >> 32);
233 }
234 attribs << EGL_NONE;
235
236 return eglCreateImageKHR(m_handle, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs.data());
237}
238
239QHash<uint32_t, EglDisplay::DrmFormatInfo> EglDisplay::allSupportedDrmFormats() const
240{
241 return m_importFormats;
242}
243
244QHash<uint32_t, QList<uint64_t>> EglDisplay::nonExternalOnlySupportedDrmFormats() const
245{
246 QHash<uint32_t, QList<uint64_t>> ret;
247 ret.reserve(m_importFormats.size());
248 for (auto it = m_importFormats.constBegin(), itEnd = m_importFormats.constEnd(); it != itEnd; ++it) {
249 ret[it.key()] = it->nonExternalOnlyModifiers;
250 }
251 return ret;
252}
253
254bool EglDisplay::isExternalOnly(uint32_t format, uint64_t modifier) const
255{
256 if (const auto it = m_importFormats.find(format); it != m_importFormats.end()) {
257 return it->externalOnlyModifiers.contains(modifier);
258 } else {
259 return false;
260 }
261}
262
263QHash<uint32_t, EglDisplay::DrmFormatInfo> EglDisplay::queryImportFormats() const
264{
265 if (!hasExtension(QByteArrayLiteral("EGL_EXT_image_dma_buf_import")) || !hasExtension(QByteArrayLiteral("EGL_EXT_image_dma_buf_import_modifiers"))) {
266 return {};
267 }
268
269 typedef EGLBoolean (*eglQueryDmaBufFormatsEXT_func)(EGLDisplay dpy, EGLint max_formats, EGLint *formats, EGLint *num_formats);
270 typedef EGLBoolean (*eglQueryDmaBufModifiersEXT_func)(EGLDisplay dpy, EGLint format, EGLint max_modifiers, EGLuint64KHR *modifiers, EGLBoolean *external_only, EGLint *num_modifiers);
271 eglQueryDmaBufFormatsEXT_func eglQueryDmaBufFormatsEXT = nullptr;
272 eglQueryDmaBufModifiersEXT_func eglQueryDmaBufModifiersEXT = nullptr;
273 eglQueryDmaBufFormatsEXT = (eglQueryDmaBufFormatsEXT_func)eglGetProcAddress("eglQueryDmaBufFormatsEXT");
274 eglQueryDmaBufModifiersEXT = (eglQueryDmaBufModifiersEXT_func)eglGetProcAddress("eglQueryDmaBufModifiersEXT");
275 if (eglQueryDmaBufFormatsEXT == nullptr) {
276 return {};
277 }
278
279 EGLint count = 0;
280 EGLBoolean success = eglQueryDmaBufFormatsEXT(m_handle, 0, nullptr, &count);
281 if (!success || count == 0) {
282 qCCritical(KWIN_OPENGL) << "eglQueryDmaBufFormatsEXT failed!" << getEglErrorString();
283 return {};
284 }
285 QList<uint32_t> formats(count);
286 if (!eglQueryDmaBufFormatsEXT(m_handle, count, (EGLint *)formats.data(), &count)) {
287 qCCritical(KWIN_OPENGL) << "eglQueryDmaBufFormatsEXT with count" << count << "failed!" << getEglErrorString();
288 return {};
289 }
290 QHash<uint32_t, DrmFormatInfo> ret;
291 for (const auto format : std::as_const(formats)) {
292 if (eglQueryDmaBufModifiersEXT != nullptr) {
293 EGLint count = 0;
294 const EGLBoolean success = eglQueryDmaBufModifiersEXT(m_handle, format, 0, nullptr, nullptr, &count);
295 if (success && count > 0) {
296 DrmFormatInfo drmFormatInfo;
297 drmFormatInfo.allModifiers.resize(count);
298 QList<EGLBoolean> externalOnly(count);
299 if (eglQueryDmaBufModifiersEXT(m_handle, format, count, drmFormatInfo.allModifiers.data(), externalOnly.data(), &count)) {
300 drmFormatInfo.externalOnlyModifiers = drmFormatInfo.allModifiers;
301 drmFormatInfo.nonExternalOnlyModifiers = drmFormatInfo.allModifiers;
302 for (int i = drmFormatInfo.allModifiers.size() - 1; i >= 0; i--) {
303 if (externalOnly[i]) {
304 drmFormatInfo.nonExternalOnlyModifiers.removeAll(drmFormatInfo.allModifiers[i]);
305 } else {
306 drmFormatInfo.externalOnlyModifiers.removeAll(drmFormatInfo.allModifiers[i]);
307 }
308 }
309 if (!drmFormatInfo.allModifiers.empty()) {
310 if (!drmFormatInfo.allModifiers.contains(DRM_FORMAT_MOD_INVALID)) {
311 drmFormatInfo.allModifiers.push_back(DRM_FORMAT_MOD_INVALID);
312 drmFormatInfo.nonExternalOnlyModifiers.push_back(DRM_FORMAT_MOD_INVALID);
313 }
314 ret.insert(format, drmFormatInfo);
315 }
316 continue;
317 }
318 }
319 }
320 DrmFormatInfo drmFormat;
321 drmFormat.allModifiers = {DRM_FORMAT_MOD_INVALID, DRM_FORMAT_MOD_LINEAR};
322 drmFormat.nonExternalOnlyModifiers = {DRM_FORMAT_MOD_INVALID, DRM_FORMAT_MOD_LINEAR};
323 ret.insert(format, drmFormat);
324 }
325 return ret;
326}
327
328}
bool hasExtension(const QByteArray &name) const
EglDisplay(::EGLDisplay display, const QList< QByteArray > &extensions, bool owning=true)
QString renderNode() const
bool supportsNativeFence() const
QList< QByteArray > extensions() const
EGLImageKHR importDmaBufAsImage(const DmaBufAttributes &dmabuf) const
static std::unique_ptr< EglDisplay > create(::EGLDisplay display, bool owning=true)
QHash< uint32_t, QList< uint64_t > > nonExternalOnlySupportedDrmFormats() const
QHash< uint32_t, DrmFormatInfo > allSupportedDrmFormats() const
bool supportsBufferAge() const
bool isExternalOnly(uint32_t format, uint64_t modifier) const
::EGLDisplay handle() const
#define EGL_DRM_RENDER_NODE_FILE_EXT
void * EGLImageKHR
GLenum format
Definition gltexture.cpp:49
std::array< uint32_t, 4 > offset
std::array< uint32_t, 4 > pitch
std::array< FileDescriptor, 4 > fd