File indexing completed on 2024-06-09 05:25:53

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "platformsupport/scenes/opengl/abstract_egl_backend.h"
0010 #include "compositor.h"
0011 #include "core/outputbackend.h"
0012 #include "main.h"
0013 #include "opengl/egl_context_attribute_builder.h"
0014 #include "utils/common.h"
0015 #include "wayland/drmclientbuffer.h"
0016 #include "wayland_server.h"
0017 // kwin libs
0018 #include "opengl/eglimagetexture.h"
0019 #include "opengl/eglutils_p.h"
0020 #include "opengl/glplatform.h"
0021 #include "opengl/glutils.h"
0022 #include "utils/drm_format_helper.h"
0023 // Qt
0024 #include <QOpenGLContext>
0025 
0026 #include <memory>
0027 
0028 #include <drm_fourcc.h>
0029 #include <xf86drm.h>
0030 
0031 namespace KWin
0032 {
0033 
0034 static std::unique_ptr<EglContext> s_globalShareContext;
0035 
0036 static bool isOpenGLES_helper()
0037 {
0038     if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) {
0039         return true;
0040     }
0041     return QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES;
0042 }
0043 
0044 AbstractEglBackend::AbstractEglBackend(dev_t deviceId)
0045     : m_deviceId(deviceId)
0046 {
0047     connect(Compositor::self(), &Compositor::aboutToDestroy, this, &AbstractEglBackend::teardown);
0048 }
0049 
0050 AbstractEglBackend::~AbstractEglBackend()
0051 {
0052 }
0053 
0054 bool AbstractEglBackend::ensureGlobalShareContext(EGLConfig config)
0055 {
0056     if (!s_globalShareContext) {
0057         s_globalShareContext = EglContext::create(m_display, config, EGL_NO_CONTEXT);
0058     }
0059     if (s_globalShareContext) {
0060         kwinApp()->outputBackend()->setSceneEglGlobalShareContext(s_globalShareContext->handle());
0061         return true;
0062     } else {
0063         return false;
0064     }
0065 }
0066 
0067 void AbstractEglBackend::destroyGlobalShareContext()
0068 {
0069     EglDisplay *const eglDisplay = kwinApp()->outputBackend()->sceneEglDisplayObject();
0070     if (!eglDisplay || !s_globalShareContext) {
0071         return;
0072     }
0073     s_globalShareContext.reset();
0074     kwinApp()->outputBackend()->setSceneEglGlobalShareContext(EGL_NO_CONTEXT);
0075 }
0076 
0077 void AbstractEglBackend::teardown()
0078 {
0079     destroyGlobalShareContext();
0080 }
0081 
0082 void AbstractEglBackend::cleanup()
0083 {
0084     for (const EGLImageKHR &image : m_importedBuffers) {
0085         eglDestroyImageKHR(m_display->handle(), image);
0086     }
0087 
0088     cleanupSurfaces();
0089     cleanupGL();
0090     m_context.reset();
0091 }
0092 
0093 void AbstractEglBackend::cleanupSurfaces()
0094 {
0095     if (m_surface != EGL_NO_SURFACE) {
0096         eglDestroySurface(m_display->handle(), m_surface);
0097     }
0098 }
0099 
0100 void AbstractEglBackend::setEglDisplay(EglDisplay *display)
0101 {
0102     m_display = display;
0103     setExtensions(m_display->extensions());
0104     setSupportsNativeFence(m_display->supportsNativeFence());
0105     setSupportsBufferAge(m_display->supportsBufferAge());
0106 }
0107 
0108 typedef void (*eglFuncPtr)();
0109 static eglFuncPtr getProcAddress(const char *name)
0110 {
0111     return eglGetProcAddress(name);
0112 }
0113 
0114 void AbstractEglBackend::initKWinGL()
0115 {
0116     GLPlatform *glPlatform = GLPlatform::instance();
0117     glPlatform->detect(EglPlatformInterface);
0118     glPlatform->printResults();
0119     initGL(&getProcAddress);
0120 }
0121 
0122 void AbstractEglBackend::initWayland()
0123 {
0124     if (!WaylandServer::self()) {
0125         return;
0126     }
0127 
0128     if (m_deviceId) {
0129         QString renderNode = m_display->renderNode();
0130         if (renderNode.isEmpty()) {
0131             drmDevice *device = nullptr;
0132             if (drmGetDeviceFromDevId(deviceId(), 0, &device) != 0) {
0133                 qCWarning(KWIN_OPENGL) << "drmGetDeviceFromDevId() failed:" << strerror(errno);
0134             } else {
0135                 if (device->available_nodes & (1 << DRM_NODE_RENDER)) {
0136                     renderNode = QString::fromLocal8Bit(device->nodes[DRM_NODE_RENDER]);
0137                 } else if (device->available_nodes & (1 << DRM_NODE_PRIMARY)) {
0138                     qCWarning(KWIN_OPENGL) << "No render nodes have been found, falling back to primary node";
0139                     renderNode = QString::fromLocal8Bit(device->nodes[DRM_NODE_PRIMARY]);
0140                 }
0141                 drmFreeDevice(&device);
0142             }
0143         }
0144 
0145         if (!renderNode.isEmpty()) {
0146             waylandServer()->drm()->setDevice(renderNode);
0147         } else {
0148             qCWarning(KWIN_OPENGL) << "No render node have been found, not initializing wl-drm";
0149         }
0150     }
0151 
0152     const auto formats = m_display->allSupportedDrmFormats();
0153     auto filterFormats = [this, &formats](std::optional<uint32_t> bpc, bool withExternalOnlyYUV) {
0154         QHash<uint32_t, QList<uint64_t>> set;
0155         for (auto it = formats.constBegin(); it != formats.constEnd(); it++) {
0156             const auto info = FormatInfo::get(it.key());
0157             if (!info || (bpc && bpc != info->bitsPerColor)) {
0158                 continue;
0159             }
0160 
0161             const bool externalOnlySupported = withExternalOnlyYUV && info->yuvConversion();
0162             QList<uint64_t> modifiers = externalOnlySupported ? it->allModifiers : it->nonExternalOnlyModifiers;
0163 
0164             if (externalOnlySupported && !modifiers.isEmpty()) {
0165                 if (auto yuv = info->yuvConversion()) {
0166                     for (auto plane : std::as_const(yuv->plane)) {
0167                         const auto planeModifiers = formats.value(plane.format).allModifiers;
0168                         modifiers.erase(std::remove_if(modifiers.begin(), modifiers.end(), [&planeModifiers](uint64_t mod) {
0169                                             return !planeModifiers.contains(mod);
0170                                         }),
0171                                         modifiers.end());
0172                     }
0173                 }
0174             }
0175             for (const auto &tranche : std::as_const(m_tranches)) {
0176                 if (modifiers.isEmpty()) {
0177                     break;
0178                 }
0179                 const auto trancheModifiers = tranche.formatTable.value(it.key());
0180                 for (auto trancheModifier : trancheModifiers) {
0181                     modifiers.removeAll(trancheModifier);
0182                 }
0183             }
0184             if (modifiers.isEmpty()) {
0185                 continue;
0186             }
0187             set.insert(it.key(), modifiers);
0188         }
0189         return set;
0190     };
0191 
0192     auto includeShaderConversions = [](QHash<uint32_t, QList<uint64_t>> &&formats) -> QHash<uint32_t, QList<uint64_t>> {
0193         for (auto format : s_drmConversions.keys()) {
0194             auto &modifiers = formats[format];
0195             if (modifiers.isEmpty()) {
0196                 modifiers = {DRM_FORMAT_MOD_LINEAR};
0197             }
0198         }
0199         return formats;
0200     };
0201 
0202     if (prefer10bpc()) {
0203         m_tranches.append({
0204             .device = deviceId(),
0205             .flags = {},
0206             .formatTable = filterFormats(10, false),
0207         });
0208     }
0209     m_tranches.append({
0210         .device = deviceId(),
0211         .flags = {},
0212         .formatTable = filterFormats(8, false),
0213     });
0214     m_tranches.append({
0215         .device = deviceId(),
0216         .flags = {},
0217         .formatTable = includeShaderConversions(filterFormats({}, true)),
0218     });
0219 
0220     LinuxDmaBufV1ClientBufferIntegration *dmabuf = waylandServer()->linuxDmabuf();
0221     dmabuf->setRenderBackend(this);
0222     dmabuf->setSupportedFormatsWithModifiers(m_tranches);
0223 }
0224 
0225 void AbstractEglBackend::initClientExtensions()
0226 {
0227     // Get the list of client extensions
0228     const char *clientExtensionsCString = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
0229     const QByteArray clientExtensionsString = QByteArray::fromRawData(clientExtensionsCString, qstrlen(clientExtensionsCString));
0230     if (clientExtensionsString.isEmpty()) {
0231         // If eglQueryString() returned NULL, the implementation doesn't support
0232         // EGL_EXT_client_extensions. Expect an EGL_BAD_DISPLAY error.
0233         (void)eglGetError();
0234     }
0235 
0236     m_clientExtensions = clientExtensionsString.split(' ');
0237 }
0238 
0239 bool AbstractEglBackend::hasClientExtension(const QByteArray &ext) const
0240 {
0241     return m_clientExtensions.contains(ext);
0242 }
0243 
0244 bool AbstractEglBackend::makeCurrent()
0245 {
0246     if (QOpenGLContext *context = QOpenGLContext::currentContext()) {
0247         // Workaround to tell Qt that no QOpenGLContext is current
0248         context->doneCurrent();
0249     }
0250     return m_context->makeCurrent(m_surface);
0251 }
0252 
0253 void AbstractEglBackend::doneCurrent()
0254 {
0255     m_context->doneCurrent();
0256 }
0257 
0258 bool AbstractEglBackend::isOpenGLES() const
0259 {
0260     return isOpenGLES_helper();
0261 }
0262 
0263 bool AbstractEglBackend::createContext(EGLConfig config)
0264 {
0265     if (!ensureGlobalShareContext(config)) {
0266         return false;
0267     }
0268     m_context = EglContext::create(m_display, config, s_globalShareContext ? s_globalShareContext->handle() : EGL_NO_CONTEXT);
0269     return m_context != nullptr;
0270 }
0271 
0272 void AbstractEglBackend::setSurface(const EGLSurface &surface)
0273 {
0274     m_surface = surface;
0275 }
0276 
0277 QList<LinuxDmaBufV1Feedback::Tranche> AbstractEglBackend::tranches() const
0278 {
0279     return m_tranches;
0280 }
0281 
0282 dev_t AbstractEglBackend::deviceId() const
0283 {
0284     return m_deviceId;
0285 }
0286 
0287 bool AbstractEglBackend::prefer10bpc() const
0288 {
0289     return false;
0290 }
0291 
0292 EGLImageKHR AbstractEglBackend::importBufferAsImage(GraphicsBuffer *buffer, int plane, int format, const QSize &size)
0293 {
0294     std::pair key(buffer, plane);
0295     auto it = m_importedBuffers.constFind(key);
0296     if (Q_LIKELY(it != m_importedBuffers.constEnd())) {
0297         return *it;
0298     }
0299 
0300     Q_ASSERT(buffer->dmabufAttributes());
0301     EGLImageKHR image = importDmaBufAsImage(*buffer->dmabufAttributes(), plane, format, size);
0302     if (image != EGL_NO_IMAGE_KHR) {
0303         m_importedBuffers[key] = image;
0304         connect(buffer, &QObject::destroyed, this, [this, key]() {
0305             eglDestroyImageKHR(m_display->handle(), m_importedBuffers.take(key));
0306         });
0307     } else {
0308         qCWarning(KWIN_OPENGL) << "failed to import dmabuf" << buffer;
0309     }
0310 
0311     return image;
0312 }
0313 
0314 EGLImageKHR AbstractEglBackend::importBufferAsImage(GraphicsBuffer *buffer)
0315 {
0316     auto key = std::pair(buffer, 0);
0317     auto it = m_importedBuffers.constFind(key);
0318     if (Q_LIKELY(it != m_importedBuffers.constEnd())) {
0319         return *it;
0320     }
0321 
0322     Q_ASSERT(buffer->dmabufAttributes());
0323     EGLImageKHR image = importDmaBufAsImage(*buffer->dmabufAttributes());
0324     if (image != EGL_NO_IMAGE_KHR) {
0325         m_importedBuffers[key] = image;
0326         connect(buffer, &QObject::destroyed, this, [this, key]() {
0327             eglDestroyImageKHR(m_display->handle(), m_importedBuffers.take(key));
0328         });
0329     } else {
0330         qCWarning(KWIN_OPENGL) << "failed to import dmabuf" << buffer;
0331     }
0332 
0333     return image;
0334 }
0335 
0336 EGLImageKHR AbstractEglBackend::importDmaBufAsImage(const DmaBufAttributes &dmabuf) const
0337 {
0338     return m_display->importDmaBufAsImage(dmabuf);
0339 }
0340 
0341 EGLImageKHR AbstractEglBackend::importDmaBufAsImage(const DmaBufAttributes &dmabuf, int plane, int format, const QSize &size) const
0342 {
0343     return m_display->importDmaBufAsImage(dmabuf, plane, format, size);
0344 }
0345 
0346 std::shared_ptr<GLTexture> AbstractEglBackend::importDmaBufAsTexture(const DmaBufAttributes &attributes) const
0347 {
0348     return m_context->importDmaBufAsTexture(attributes);
0349 }
0350 
0351 bool AbstractEglBackend::testImportBuffer(GraphicsBuffer *buffer)
0352 {
0353     return importBufferAsImage(buffer) != EGL_NO_IMAGE_KHR;
0354 }
0355 
0356 QHash<uint32_t, QList<uint64_t>> AbstractEglBackend::supportedFormats() const
0357 {
0358     return m_display->nonExternalOnlySupportedDrmFormats();
0359 }
0360 
0361 EGLSurface AbstractEglBackend::surface() const
0362 {
0363     return m_surface;
0364 }
0365 
0366 EGLConfig AbstractEglBackend::config() const
0367 {
0368     return m_context->config();
0369 }
0370 
0371 EglDisplay *AbstractEglBackend::eglDisplayObject() const
0372 {
0373     return m_display;
0374 }
0375 
0376 EglContext *AbstractEglBackend::contextObject()
0377 {
0378     return m_context.get();
0379 }
0380 }
0381 
0382 #include "moc_abstract_egl_backend.cpp"