KWin
Loading...
Searching...
No Matches
abstract_egl_backend.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: 2015 Martin Gräßlin <mgraesslin@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
10#include "compositor.h"
11#include "core/outputbackend.h"
12#include "main.h"
14#include "utils/common.h"
16#include "wayland_server.h"
17// kwin libs
19#include "opengl/eglutils_p.h"
20#include "opengl/glplatform.h"
21#include "opengl/glutils.h"
23// Qt
24#include <QOpenGLContext>
25
26#include <memory>
27
28#include <drm_fourcc.h>
29#include <xf86drm.h>
30
31namespace KWin
32{
33
34static std::unique_ptr<EglContext> s_globalShareContext;
35
36static bool isOpenGLES_helper()
37{
38 if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) {
39 return true;
40 }
41 return QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES;
42}
43
45 : m_deviceId(deviceId)
46{
48}
49
53
55{
56 if (!s_globalShareContext) {
57 s_globalShareContext = EglContext::create(m_display, config, EGL_NO_CONTEXT);
58 }
59 if (s_globalShareContext) {
60 kwinApp()->outputBackend()->setSceneEglGlobalShareContext(s_globalShareContext->handle());
61 return true;
62 } else {
63 return false;
64 }
65}
66
68{
69 EglDisplay *const eglDisplay = kwinApp()->outputBackend()->sceneEglDisplayObject();
70 if (!eglDisplay || !s_globalShareContext) {
71 return;
72 }
73 s_globalShareContext.reset();
74 kwinApp()->outputBackend()->setSceneEglGlobalShareContext(EGL_NO_CONTEXT);
75}
76
81
83{
84 for (const EGLImageKHR &image : m_importedBuffers) {
85 eglDestroyImageKHR(m_display->handle(), image);
86 }
87
89 cleanupGL();
90 m_context.reset();
91}
92
94{
95 if (m_surface != EGL_NO_SURFACE) {
96 eglDestroySurface(m_display->handle(), m_surface);
97 }
98}
99
107
108typedef void (*eglFuncPtr)();
109static eglFuncPtr getProcAddress(const char *name)
110{
111 return eglGetProcAddress(name);
112}
113
115{
116 GLPlatform *glPlatform = GLPlatform::instance();
117 glPlatform->detect(EglPlatformInterface);
118 glPlatform->printResults();
119 initGL(&getProcAddress);
120}
121
123{
124 if (!WaylandServer::self()) {
125 return;
126 }
127
128 if (m_deviceId) {
129 QString renderNode = m_display->renderNode();
130 if (renderNode.isEmpty()) {
131 drmDevice *device = nullptr;
132 if (drmGetDeviceFromDevId(deviceId(), 0, &device) != 0) {
133 qCWarning(KWIN_OPENGL) << "drmGetDeviceFromDevId() failed:" << strerror(errno);
134 } else {
135 if (device->available_nodes & (1 << DRM_NODE_RENDER)) {
136 renderNode = QString::fromLocal8Bit(device->nodes[DRM_NODE_RENDER]);
137 } else if (device->available_nodes & (1 << DRM_NODE_PRIMARY)) {
138 qCWarning(KWIN_OPENGL) << "No render nodes have been found, falling back to primary node";
139 renderNode = QString::fromLocal8Bit(device->nodes[DRM_NODE_PRIMARY]);
140 }
141 drmFreeDevice(&device);
142 }
143 }
144
145 if (!renderNode.isEmpty()) {
146 waylandServer()->drm()->setDevice(renderNode);
147 } else {
148 qCWarning(KWIN_OPENGL) << "No render node have been found, not initializing wl-drm";
149 }
150 }
151
152 const auto formats = m_display->allSupportedDrmFormats();
153 auto filterFormats = [this, &formats](std::optional<uint32_t> bpc, bool withExternalOnlyYUV) {
154 QHash<uint32_t, QList<uint64_t>> set;
155 for (auto it = formats.constBegin(); it != formats.constEnd(); it++) {
156 const auto info = FormatInfo::get(it.key());
157 if (!info || (bpc && bpc != info->bitsPerColor)) {
158 continue;
159 }
160
161 const bool externalOnlySupported = withExternalOnlyYUV && info->yuvConversion();
162 QList<uint64_t> modifiers = externalOnlySupported ? it->allModifiers : it->nonExternalOnlyModifiers;
163
164 if (externalOnlySupported && !modifiers.isEmpty()) {
165 if (auto yuv = info->yuvConversion()) {
166 for (auto plane : std::as_const(yuv->plane)) {
167 const auto planeModifiers = formats.value(plane.format).allModifiers;
168 modifiers.erase(std::remove_if(modifiers.begin(), modifiers.end(), [&planeModifiers](uint64_t mod) {
169 return !planeModifiers.contains(mod);
170 }),
171 modifiers.end());
172 }
173 }
174 }
175 for (const auto &tranche : std::as_const(m_tranches)) {
176 if (modifiers.isEmpty()) {
177 break;
178 }
179 const auto trancheModifiers = tranche.formatTable.value(it.key());
180 for (auto trancheModifier : trancheModifiers) {
181 modifiers.removeAll(trancheModifier);
182 }
183 }
184 if (modifiers.isEmpty()) {
185 continue;
186 }
187 set.insert(it.key(), modifiers);
188 }
189 return set;
190 };
191
192 auto includeShaderConversions = [](QHash<uint32_t, QList<uint64_t>> &&formats) -> QHash<uint32_t, QList<uint64_t>> {
193 for (auto format : s_drmConversions.keys()) {
194 auto &modifiers = formats[format];
195 if (modifiers.isEmpty()) {
196 modifiers = {DRM_FORMAT_MOD_LINEAR};
197 }
198 }
199 return formats;
200 };
201
202 if (prefer10bpc()) {
203 m_tranches.append({
204 .device = deviceId(),
205 .flags = {},
206 .formatTable = filterFormats(10, false),
207 });
208 }
209 m_tranches.append({
210 .device = deviceId(),
211 .flags = {},
212 .formatTable = filterFormats(8, false),
213 });
214 m_tranches.append({
215 .device = deviceId(),
216 .flags = {},
217 .formatTable = includeShaderConversions(filterFormats({}, true)),
218 });
219
221 dmabuf->setRenderBackend(this);
223}
224
226{
227 // Get the list of client extensions
228 const char *clientExtensionsCString = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
229 const QByteArray clientExtensionsString = QByteArray::fromRawData(clientExtensionsCString, qstrlen(clientExtensionsCString));
230 if (clientExtensionsString.isEmpty()) {
231 // If eglQueryString() returned NULL, the implementation doesn't support
232 // EGL_EXT_client_extensions. Expect an EGL_BAD_DISPLAY error.
233 (void)eglGetError();
234 }
235
236 m_clientExtensions = clientExtensionsString.split(' ');
237}
238
239bool AbstractEglBackend::hasClientExtension(const QByteArray &ext) const
240{
241 return m_clientExtensions.contains(ext);
242}
243
245{
246 if (QOpenGLContext *context = QOpenGLContext::currentContext()) {
247 // Workaround to tell Qt that no QOpenGLContext is current
248 context->doneCurrent();
249 }
250 return m_context->makeCurrent(m_surface);
251}
252
254{
255 m_context->doneCurrent();
256}
257
259{
260 return isOpenGLES_helper();
261}
262
264{
266 return false;
267 }
268 m_context = EglContext::create(m_display, config, s_globalShareContext ? s_globalShareContext->handle() : EGL_NO_CONTEXT);
269 return m_context != nullptr;
270}
271
272void AbstractEglBackend::setSurface(const EGLSurface &surface)
273{
275}
276
277QList<LinuxDmaBufV1Feedback::Tranche> AbstractEglBackend::tranches() const
278{
279 return m_tranches;
280}
281
283{
284 return m_deviceId;
285}
286
288{
289 return false;
290}
291
293{
294 std::pair key(buffer, plane);
295 auto it = m_importedBuffers.constFind(key);
296 if (Q_LIKELY(it != m_importedBuffers.constEnd())) {
297 return *it;
298 }
299
300 Q_ASSERT(buffer->dmabufAttributes());
301 EGLImageKHR image = importDmaBufAsImage(*buffer->dmabufAttributes(), plane, format, size);
302 if (image != EGL_NO_IMAGE_KHR) {
303 m_importedBuffers[key] = image;
304 connect(buffer, &QObject::destroyed, this, [this, key]() {
305 eglDestroyImageKHR(m_display->handle(), m_importedBuffers.take(key));
306 });
307 } else {
308 qCWarning(KWIN_OPENGL) << "failed to import dmabuf" << buffer;
309 }
310
311 return image;
312}
313
315{
316 auto key = std::pair(buffer, 0);
317 auto it = m_importedBuffers.constFind(key);
318 if (Q_LIKELY(it != m_importedBuffers.constEnd())) {
319 return *it;
320 }
321
322 Q_ASSERT(buffer->dmabufAttributes());
324 if (image != EGL_NO_IMAGE_KHR) {
325 m_importedBuffers[key] = image;
326 connect(buffer, &QObject::destroyed, this, [this, key]() {
327 eglDestroyImageKHR(m_display->handle(), m_importedBuffers.take(key));
328 });
329 } else {
330 qCWarning(KWIN_OPENGL) << "failed to import dmabuf" << buffer;
331 }
332
333 return image;
334}
335
340
341EGLImageKHR AbstractEglBackend::importDmaBufAsImage(const DmaBufAttributes &dmabuf, int plane, int format, const QSize &size) const
342{
343 return m_display->importDmaBufAsImage(dmabuf, plane, format, size);
344}
345
346std::shared_ptr<GLTexture> AbstractEglBackend::importDmaBufAsTexture(const DmaBufAttributes &attributes) const
347{
348 return m_context->importDmaBufAsTexture(attributes);
349}
350
352{
353 return importBufferAsImage(buffer) != EGL_NO_IMAGE_KHR;
354}
355
356QHash<uint32_t, QList<uint64_t>> AbstractEglBackend::supportedFormats() const
357{
359}
360
362{
363 return m_surface;
364}
365
367{
368 return m_context->config();
369}
370
375
380}
381
382#include "moc_abstract_egl_backend.cpp"
bool hasClientExtension(const QByteArray &ext) const
EglDisplay * eglDisplayObject() const
QList< LinuxDmaBufV1Feedback::Tranche > tranches() const
void setEglDisplay(EglDisplay *display)
EGLImageKHR importBufferAsImage(GraphicsBuffer *buffer)
QHash< std::pair< GraphicsBuffer *, int >, EGLImageKHR > m_importedBuffers
EGLImageKHR importDmaBufAsImage(const DmaBufAttributes &attributes) const
QList< LinuxDmaBufV1Feedback::Tranche > m_tranches
std::shared_ptr< GLTexture > importDmaBufAsTexture(const DmaBufAttributes &attributes) const
bool ensureGlobalShareContext(EGLConfig config)
QHash< uint32_t, QList< uint64_t > > supportedFormats() const override
void setSurface(const EGLSurface &surface)
bool createContext(EGLConfig config)
std::shared_ptr< EglContext > m_context
bool testImportBuffer(GraphicsBuffer *buffer) override
QList< QByteArray > m_clientExtensions
static Compositor * self()
void setDevice(const QString &node)
static std::unique_ptr< EglContext > create(EglDisplay *display, EGLConfig config, ::EGLContext sharedContext)
QString renderNode() const
bool supportsNativeFence() const
QList< QByteArray > extensions() const
EGLImageKHR importDmaBufAsImage(const DmaBufAttributes &dmabuf) const
QHash< uint32_t, QList< uint64_t > > nonExternalOnlySupportedDrmFormats() const
QHash< uint32_t, DrmFormatInfo > allSupportedDrmFormats() const
bool supportsBufferAge() const
::EGLDisplay handle() const
void printResults() const
void detect(OpenGLPlatformInterface platformInterface)
static GLPlatform * instance()
Definition glplatform.h:394
virtual const DmaBufAttributes * dmabufAttributes() const
void setSupportedFormatsWithModifiers(const QList< LinuxDmaBufV1Feedback::Tranche > &tranches)
void setRenderBackend(RenderBackend *renderBackend)
void setSupportsBufferAge(bool value)
void setSupportsNativeFence(bool value)
void setExtensions(const QList< QByteArray > &extensions)
LinuxDmaBufV1ClientBufferIntegration * linuxDmabuf()
DrmClientBufferIntegration * drm()
void * EGLImageKHR
void KWIN_EXPORT cleanupGL()
Definition glutils.cpp:122
void initGL(const std::function< resolveFuncPtr(const char *)> &resolveFunction)
Definition glutils.cpp:97
void(* eglFuncPtr)()
GLenum format
Definition gltexture.cpp:49
WaylandServer * waylandServer()
@ EglPlatformInterface
Definition globals.h:45
static std::optional< FormatInfo > get(uint32_t drmFormat)