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