File indexing completed on 2024-11-10 04:56:56
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"