File indexing completed on 2025-04-20 10:57:33

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 "drm_egl_backend.h"
0010 #include "basiceglsurfacetexture_internal.h"
0011 #include "basiceglsurfacetexture_wayland.h"
0012 // kwin
0013 #include "core/renderloop_p.h"
0014 #include "drm_abstract_output.h"
0015 #include "drm_backend.h"
0016 #include "drm_buffer_gbm.h"
0017 #include "drm_dumb_swapchain.h"
0018 #include "drm_egl_cursor_layer.h"
0019 #include "drm_egl_layer.h"
0020 #include "drm_gbm_surface.h"
0021 #include "drm_gpu.h"
0022 #include "drm_logging.h"
0023 #include "drm_output.h"
0024 #include "drm_pipeline.h"
0025 #include "drm_shadow_buffer.h"
0026 #include "drm_virtual_egl_layer.h"
0027 #include "egl_dmabuf.h"
0028 #include "gbm_dmabuf.h"
0029 #include "kwineglutils_p.h"
0030 #include "linux_dmabuf.h"
0031 #include "options.h"
0032 #include "scene/surfaceitem_wayland.h"
0033 #include "wayland/clientconnection.h"
0034 #include "wayland/linuxdmabufv1clientbuffer.h"
0035 #include "wayland/surface_interface.h"
0036 // kwin libs
0037 #include <kwineglimagetexture.h>
0038 #include <kwinglplatform.h>
0039 // system
0040 #include <drm_fourcc.h>
0041 #include <errno.h>
0042 #include <gbm.h>
0043 #include <unistd.h>
0044 
0045 namespace KWin
0046 {
0047 
0048 EglGbmBackend::EglGbmBackend(DrmBackend *drmBackend)
0049     : AbstractEglBackend(drmBackend->primaryGpu()->deviceId())
0050     , m_backend(drmBackend)
0051 {
0052     drmBackend->setRenderBackend(this);
0053     setIsDirectRendering(true);
0054 }
0055 
0056 EglGbmBackend::~EglGbmBackend()
0057 {
0058     m_backend->releaseBuffers();
0059     cleanup();
0060     m_backend->setRenderBackend(nullptr);
0061 }
0062 
0063 bool EglGbmBackend::initializeEgl()
0064 {
0065     initClientExtensions();
0066     EGLDisplay display = m_backend->primaryGpu()->eglDisplay();
0067 
0068     // Use eglGetPlatformDisplayEXT() to get the display pointer
0069     // if the implementation supports it.
0070     if (display == EGL_NO_DISPLAY) {
0071         const bool hasMesaGBM = hasClientExtension(QByteArrayLiteral("EGL_MESA_platform_gbm"));
0072         const bool hasKHRGBM = hasClientExtension(QByteArrayLiteral("EGL_KHR_platform_gbm"));
0073         const GLenum platform = hasMesaGBM ? EGL_PLATFORM_GBM_MESA : EGL_PLATFORM_GBM_KHR;
0074 
0075         if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base")) || (!hasMesaGBM && !hasKHRGBM)) {
0076             setFailed("Missing one or more extensions between EGL_EXT_platform_base, "
0077                       "EGL_MESA_platform_gbm, EGL_KHR_platform_gbm");
0078             return false;
0079         }
0080 
0081         if (!m_backend->primaryGpu()->gbmDevice()) {
0082             setFailed("Could not create gbm device");
0083             return false;
0084         }
0085 
0086         display = eglGetPlatformDisplayEXT(platform, m_backend->primaryGpu()->gbmDevice(), nullptr);
0087         m_backend->primaryGpu()->setEglDisplay(display);
0088     }
0089 
0090     if (display == EGL_NO_DISPLAY) {
0091         return false;
0092     }
0093     setEglDisplay(display);
0094     return initEglAPI();
0095 }
0096 
0097 void EglGbmBackend::init()
0098 {
0099     if (!initializeEgl()) {
0100         setFailed("Could not initialize egl");
0101         return;
0102     }
0103 
0104     if (!initRenderingContext()) {
0105         setFailed("Could not initialize rendering context");
0106         return;
0107     }
0108     initBufferAge();
0109     initKWinGL();
0110     initWayland();
0111 }
0112 
0113 bool EglGbmBackend::initRenderingContext()
0114 {
0115     if (!initBufferConfigs()) {
0116         return false;
0117     }
0118     if (!createContext() || !makeCurrent()) {
0119         return false;
0120     }
0121     return true;
0122 }
0123 
0124 bool EglGbmBackend::initBufferConfigs()
0125 {
0126     const EGLint config_attribs[] = {
0127         EGL_SURFACE_TYPE,
0128         EGL_WINDOW_BIT,
0129         EGL_RED_SIZE,
0130         1,
0131         EGL_GREEN_SIZE,
0132         1,
0133         EGL_BLUE_SIZE,
0134         1,
0135         EGL_ALPHA_SIZE,
0136         0,
0137         EGL_RENDERABLE_TYPE,
0138         isOpenGLES() ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT,
0139         EGL_CONFIG_CAVEAT,
0140         EGL_NONE,
0141         EGL_NONE,
0142     };
0143 
0144     EGLint count;
0145     EGLConfig configs[1024];
0146     if (!eglChooseConfig(eglDisplay(), config_attribs, configs,
0147                          sizeof(configs) / sizeof(EGLConfig),
0148                          &count)) {
0149         qCCritical(KWIN_DRM) << "eglChooseConfig failed:" << getEglErrorString();
0150         return false;
0151     }
0152 
0153     setConfig(EGL_NO_CONFIG_KHR);
0154 
0155     // Loop through all configs, choosing the first one that has suitable format.
0156     for (EGLint i = 0; i < count; i++) {
0157         EGLint gbmFormat;
0158         eglGetConfigAttrib(eglDisplay(), configs[i], EGL_NATIVE_VISUAL_ID, &gbmFormat);
0159 
0160         GbmFormat format;
0161         format.drmFormat = gbmFormat;
0162         EGLint red, green, blue;
0163         // Query number of bits for color channel
0164         eglGetConfigAttrib(eglDisplay(), configs[i], EGL_RED_SIZE, &red);
0165         eglGetConfigAttrib(eglDisplay(), configs[i], EGL_GREEN_SIZE, &green);
0166         eglGetConfigAttrib(eglDisplay(), configs[i], EGL_BLUE_SIZE, &blue);
0167         eglGetConfigAttrib(eglDisplay(), configs[i], EGL_ALPHA_SIZE, &format.alphaSize);
0168         format.bpp = red + green + blue;
0169         if (m_formats.contains(gbmFormat)) {
0170             continue;
0171         }
0172         m_formats[gbmFormat] = format;
0173         m_configs[format.drmFormat] = configs[i];
0174     }
0175     if (!m_formats.isEmpty()) {
0176         return true;
0177     }
0178 
0179     qCCritical(KWIN_DRM, "Choosing EGL config did not return a supported config. There were %u configs", count);
0180     for (EGLint i = 0; i < count; i++) {
0181         EGLint gbmFormat, blueSize, redSize, greenSize, alphaSize;
0182         eglGetConfigAttrib(eglDisplay(), configs[i], EGL_NATIVE_VISUAL_ID, &gbmFormat);
0183         eglGetConfigAttrib(eglDisplay(), configs[i], EGL_RED_SIZE, &redSize);
0184         eglGetConfigAttrib(eglDisplay(), configs[i], EGL_GREEN_SIZE, &greenSize);
0185         eglGetConfigAttrib(eglDisplay(), configs[i], EGL_BLUE_SIZE, &blueSize);
0186         eglGetConfigAttrib(eglDisplay(), configs[i], EGL_ALPHA_SIZE, &alphaSize);
0187         gbm_format_name_desc name;
0188         gbm_format_get_name(gbmFormat, &name);
0189         qCCritical(KWIN_DRM, "EGL config %d has format %s with %d,%d,%d,%d bits for r,g,b,a", i, name.name, redSize, greenSize, blueSize, alphaSize);
0190     }
0191     return false;
0192 }
0193 
0194 std::unique_ptr<SurfaceTexture> EglGbmBackend::createSurfaceTextureInternal(SurfacePixmapInternal *pixmap)
0195 {
0196     return std::make_unique<BasicEGLSurfaceTextureInternal>(this, pixmap);
0197 }
0198 
0199 std::unique_ptr<SurfaceTexture> EglGbmBackend::createSurfaceTextureWayland(SurfacePixmapWayland *pixmap)
0200 {
0201     return std::make_unique<BasicEGLSurfaceTextureWayland>(this, pixmap);
0202 }
0203 
0204 void EglGbmBackend::present(Output *output)
0205 {
0206     static_cast<DrmAbstractOutput *>(output)->present();
0207 }
0208 
0209 OutputLayer *EglGbmBackend::primaryLayer(Output *output)
0210 {
0211     return static_cast<DrmAbstractOutput *>(output)->primaryLayer();
0212 }
0213 
0214 std::shared_ptr<GLTexture> EglGbmBackend::textureForOutput(Output *output) const
0215 {
0216     const auto drmOutput = static_cast<DrmAbstractOutput *>(output);
0217     if (const auto virtualLayer = dynamic_cast<VirtualEglGbmLayer *>(drmOutput->primaryLayer())) {
0218         return virtualLayer->texture();
0219     }
0220     return static_cast<EglGbmLayer *>(drmOutput->primaryLayer())->texture();
0221 }
0222 
0223 std::optional<GbmFormat> EglGbmBackend::gbmFormatForDrmFormat(uint32_t format) const
0224 {
0225     // TODO use a hardcoded lookup table where needed instead?
0226     const auto it = m_formats.constFind(format);
0227     return it == m_formats.constEnd() ? std::optional<GbmFormat>() : *it;
0228 }
0229 
0230 bool EglGbmBackend::prefer10bpc() const
0231 {
0232     static bool ok = false;
0233     static const int preferred = qEnvironmentVariableIntValue("KWIN_DRM_PREFER_COLOR_DEPTH", &ok);
0234     return !ok || preferred == 30;
0235 }
0236 
0237 EGLConfig EglGbmBackend::config(uint32_t format) const
0238 {
0239     return m_configs.value(format, EGL_NO_CONFIG_KHR);
0240 }
0241 
0242 std::shared_ptr<DrmPipelineLayer> EglGbmBackend::createPrimaryLayer(DrmPipeline *pipeline)
0243 {
0244     return std::make_shared<EglGbmLayer>(this, pipeline);
0245 }
0246 
0247 std::shared_ptr<DrmOverlayLayer> EglGbmBackend::createCursorLayer(DrmPipeline *pipeline)
0248 {
0249     return std::make_shared<EglGbmCursorLayer>(this, pipeline);
0250 }
0251 
0252 std::shared_ptr<DrmOutputLayer> EglGbmBackend::createLayer(DrmVirtualOutput *output)
0253 {
0254     return std::make_shared<VirtualEglGbmLayer>(this, output);
0255 }
0256 
0257 DrmGpu *EglGbmBackend::gpu() const
0258 {
0259     return m_backend->primaryGpu();
0260 }
0261 
0262 bool operator==(const GbmFormat &lhs, const GbmFormat &rhs)
0263 {
0264     return lhs.drmFormat == rhs.drmFormat;
0265 }
0266 
0267 EGLImageKHR EglGbmBackend::importBufferObjectAsImage(gbm_bo *bo)
0268 {
0269     return importDmaBufAsImage(dmaBufAttributesForBo(bo));
0270 }
0271 
0272 std::shared_ptr<GLTexture> EglGbmBackend::importBufferObjectAsTexture(gbm_bo *bo)
0273 {
0274     EGLImageKHR image = importBufferObjectAsImage(bo);
0275     if (image != EGL_NO_IMAGE_KHR) {
0276         return std::make_shared<EGLImageTexture>(eglDisplay(), image, GL_RGBA8, QSize(gbm_bo_get_width(bo), gbm_bo_get_height(bo)));
0277     } else {
0278         qCWarning(KWIN_DRM) << "Failed to record frame: Error creating EGLImageKHR - " << getEglErrorString();
0279         return nullptr;
0280     }
0281 }
0282 
0283 } // namespace KWin