File indexing completed on 2026-06-07 16:15:41
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com> 0006 SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include "wayland_egl_backend.h" 0012 #include "basiceglsurfacetexture_internal.h" 0013 #include "basiceglsurfacetexture_wayland.h" 0014 #include "../drm/gbm_dmabuf.h" 0015 0016 #include "wayland_backend.h" 0017 #include "wayland_display.h" 0018 #include "wayland_logging.h" 0019 #include "wayland_output.h" 0020 0021 #include <fcntl.h> 0022 #include <unistd.h> 0023 0024 // kwin libs 0025 #include <kwinglplatform.h> 0026 #include <kwinglutils.h> 0027 0028 // KDE 0029 #include <KWayland/Client/shm_pool.h> 0030 #include <KWayland/Client/surface.h> 0031 0032 // Qt 0033 #include <QFile> 0034 #include <QOpenGLContext> 0035 0036 #include <cmath> 0037 #include <drm_fourcc.h> 0038 #include <gbm.h> 0039 0040 #include "wayland-linux-dmabuf-unstable-v1-client-protocol.h" 0041 0042 namespace KWin 0043 { 0044 namespace Wayland 0045 { 0046 0047 WaylandEglLayerBuffer::WaylandEglLayerBuffer(const QSize &size, uint32_t format, const QVector<uint64_t> &modifiers, WaylandEglBackend *backend) 0048 : m_backend(backend) 0049 { 0050 gbm_device *gbmDevice = backend->backend()->gbmDevice(); 0051 0052 if (!modifiers.isEmpty()) { 0053 m_bo = gbm_bo_create_with_modifiers(gbmDevice, 0054 size.width(), 0055 size.height(), 0056 format, 0057 modifiers.constData(), 0058 modifiers.size()); 0059 } 0060 0061 if (!m_bo) { 0062 m_bo = gbm_bo_create(gbmDevice, 0063 size.width(), 0064 size.height(), 0065 format, 0066 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); 0067 } 0068 0069 if (!m_bo) { 0070 qCCritical(KWIN_WAYLAND_BACKEND) << "Failed to allocate a buffer for an output layer"; 0071 return; 0072 } 0073 0074 DmaBufAttributes attributes = dmaBufAttributesForBo(m_bo); 0075 0076 zwp_linux_buffer_params_v1 *params = zwp_linux_dmabuf_v1_create_params(backend->backend()->display()->linuxDmabuf()->handle()); 0077 for (int i = 0; i < attributes.planeCount; ++i) { 0078 zwp_linux_buffer_params_v1_add(params, 0079 attributes.fd[i].get(), 0080 i, 0081 attributes.offset[i], 0082 attributes.pitch[i], 0083 attributes.modifier >> 32, 0084 attributes.modifier & 0xffffffff); 0085 } 0086 0087 m_buffer = zwp_linux_buffer_params_v1_create_immed(params, size.width(), size.height(), format, 0); 0088 zwp_linux_buffer_params_v1_destroy(params); 0089 0090 m_texture = backend->importDmaBufAsTexture(std::move(attributes)); 0091 m_shadowTexture = std::make_unique<GLTexture>(m_texture->internalFormat(), m_texture->size()); 0092 m_framebuffer = std::make_unique<GLFramebuffer>(m_texture.get()); 0093 m_shadowFramebuffer = std::make_unique<GLFramebuffer>(m_shadowTexture.get()); 0094 } 0095 0096 WaylandEglLayerBuffer::~WaylandEglLayerBuffer() 0097 { 0098 m_texture.reset(); 0099 m_framebuffer.reset(); 0100 m_shadowTexture.reset(); 0101 m_shadowFramebuffer.reset(); 0102 0103 if (m_buffer) { 0104 wl_buffer_destroy(m_buffer); 0105 } 0106 if (m_bo) { 0107 gbm_bo_destroy(m_bo); 0108 } 0109 } 0110 0111 wl_buffer *WaylandEglLayerBuffer::buffer() const 0112 { 0113 return m_buffer; 0114 } 0115 0116 GLFramebuffer *WaylandEglLayerBuffer::framebuffer() const 0117 { 0118 return m_framebuffer.get(); 0119 } 0120 0121 int WaylandEglLayerBuffer::age() const 0122 { 0123 return m_age; 0124 } 0125 0126 GLFramebuffer *WaylandEglLayerBuffer::shadowFramebuffer() const 0127 { 0128 return m_shadowFramebuffer.get(); 0129 } 0130 0131 GLTexture *WaylandEglLayerBuffer::shadowTexture() const 0132 { 0133 return m_shadowTexture.get(); 0134 } 0135 0136 WaylandEglLayerSwapchain::WaylandEglLayerSwapchain(const QSize &size, uint32_t format, const QVector<uint64_t> &modifiers, WaylandEglBackend *backend) 0137 : m_backend(backend) 0138 , m_size(size) 0139 { 0140 for (int i = 0; i < 2; ++i) { 0141 m_buffers.append(std::make_shared<WaylandEglLayerBuffer>(size, format, modifiers, backend)); 0142 } 0143 } 0144 0145 WaylandEglLayerSwapchain::~WaylandEglLayerSwapchain() 0146 { 0147 } 0148 0149 QSize WaylandEglLayerSwapchain::size() const 0150 { 0151 return m_size; 0152 } 0153 0154 std::shared_ptr<WaylandEglLayerBuffer> WaylandEglLayerSwapchain::acquire() 0155 { 0156 m_index = (m_index + 1) % m_buffers.count(); 0157 return m_buffers[m_index]; 0158 } 0159 0160 void WaylandEglLayerSwapchain::release(std::shared_ptr<WaylandEglLayerBuffer> buffer) 0161 { 0162 Q_ASSERT(m_buffers[m_index] == buffer); 0163 0164 for (qsizetype i = 0; i < m_buffers.count(); ++i) { 0165 if (m_buffers[i] == buffer) { 0166 m_buffers[i]->m_age = 1; 0167 } else if (m_buffers[i]->m_age > 0) { 0168 m_buffers[i]->m_age++; 0169 } 0170 } 0171 } 0172 0173 WaylandEglPrimaryLayer::WaylandEglPrimaryLayer(WaylandOutput *output, WaylandEglBackend *backend) 0174 : m_waylandOutput(output) 0175 , m_backend(backend) 0176 { 0177 } 0178 0179 WaylandEglPrimaryLayer::~WaylandEglPrimaryLayer() 0180 { 0181 } 0182 0183 GLFramebuffer *WaylandEglPrimaryLayer::fbo() const 0184 { 0185 return m_buffer->framebuffer(); 0186 } 0187 0188 std::optional<OutputLayerBeginFrameInfo> WaylandEglPrimaryLayer::beginFrame() 0189 { 0190 if (eglMakeCurrent(m_backend->eglDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE, m_backend->context()) == EGL_FALSE) { 0191 qCCritical(KWIN_WAYLAND_BACKEND) << "Make Context Current failed"; 0192 return std::nullopt; 0193 } 0194 0195 const QSize nativeSize = m_waylandOutput->pixelSize(); 0196 if (!m_swapchain || m_swapchain->size() != nativeSize) { 0197 const WaylandLinuxDmabufV1 *dmabuf = m_backend->backend()->display()->linuxDmabuf(); 0198 const uint32_t format = DRM_FORMAT_XRGB8888; 0199 if (!dmabuf->formats().contains(format)) { 0200 qCCritical(KWIN_WAYLAND_BACKEND) << "DRM_FORMAT_XRGB8888 is unsupported"; 0201 return std::nullopt; 0202 } 0203 const QVector<uint64_t> modifiers = dmabuf->formats().value(format); 0204 m_swapchain = std::make_unique<WaylandEglLayerSwapchain>(nativeSize, format, modifiers, m_backend); 0205 } 0206 0207 m_buffer = m_swapchain->acquire(); 0208 0209 QRegion repair; 0210 if (m_backend->supportsBufferAge()) { 0211 repair = m_damageJournal.accumulate(m_buffer->age(), infiniteRegion()); 0212 } 0213 0214 return OutputLayerBeginFrameInfo{ 0215 .renderTarget = RenderTarget(m_buffer->shadowFramebuffer()), 0216 .repaint = repair, 0217 }; 0218 } 0219 0220 bool WaylandEglPrimaryLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) 0221 { 0222 GLFramebuffer::pushFramebuffer(m_buffer->framebuffer()); 0223 auto texture = m_buffer->shadowTexture(); 0224 QRect outputGeometry(0, 0, texture->size().width(), texture->size().height()); 0225 0226 ShaderBinder shaderBinder(ShaderTrait::MapTexture); 0227 QMatrix4x4 projectionMatrix; 0228 projectionMatrix.scale(1, -1); 0229 projectionMatrix.ortho(outputGeometry); 0230 shaderBinder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, projectionMatrix); 0231 0232 texture->bind(); 0233 texture->render(outputGeometry, 1); 0234 texture->unbind(); 0235 GLFramebuffer::popFramebuffer(); 0236 // Flush rendering commands to the dmabuf. 0237 glFlush(); 0238 0239 m_damageJournal.add(damagedRegion); 0240 return true; 0241 } 0242 0243 void WaylandEglPrimaryLayer::present() 0244 { 0245 KWayland::Client::Surface *surface = m_waylandOutput->surface(); 0246 surface->attachBuffer(m_buffer->buffer()); 0247 surface->damage(m_damageJournal.lastDamage()); 0248 surface->setScale(std::ceil(m_waylandOutput->scale())); 0249 surface->commit(); 0250 Q_EMIT m_waylandOutput->outputChange(m_damageJournal.lastDamage()); 0251 0252 m_swapchain->release(m_buffer); 0253 } 0254 0255 WaylandEglCursorLayer::WaylandEglCursorLayer(WaylandOutput *output, WaylandEglBackend *backend) 0256 : m_output(output) 0257 , m_backend(backend) 0258 { 0259 } 0260 0261 WaylandEglCursorLayer::~WaylandEglCursorLayer() 0262 { 0263 eglMakeCurrent(m_backend->eglDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE, m_backend->context()); 0264 } 0265 0266 qreal WaylandEglCursorLayer::scale() const 0267 { 0268 return m_scale; 0269 } 0270 0271 void WaylandEglCursorLayer::setScale(qreal scale) 0272 { 0273 m_scale = scale; 0274 } 0275 0276 QPoint WaylandEglCursorLayer::hotspot() const 0277 { 0278 return m_hotspot; 0279 } 0280 0281 void WaylandEglCursorLayer::setHotspot(const QPoint &hotspot) 0282 { 0283 m_hotspot = hotspot; 0284 } 0285 0286 QSize WaylandEglCursorLayer::size() const 0287 { 0288 return m_size; 0289 } 0290 0291 void WaylandEglCursorLayer::setSize(const QSize &size) 0292 { 0293 m_size = size; 0294 } 0295 0296 std::optional<OutputLayerBeginFrameInfo> WaylandEglCursorLayer::beginFrame() 0297 { 0298 if (eglMakeCurrent(m_backend->eglDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE, m_backend->context()) == EGL_FALSE) { 0299 qCCritical(KWIN_WAYLAND_BACKEND) << "Make Context Current failed"; 0300 return std::nullopt; 0301 } 0302 0303 const QSize bufferSize = m_size.expandedTo(QSize(64, 64)); 0304 if (!m_swapchain || m_swapchain->size() != bufferSize) { 0305 const WaylandLinuxDmabufV1 *dmabuf = m_backend->backend()->display()->linuxDmabuf(); 0306 const uint32_t format = DRM_FORMAT_ARGB8888; 0307 if (!dmabuf->formats().contains(format)) { 0308 qCCritical(KWIN_WAYLAND_BACKEND) << "DRM_FORMAT_ARGB8888 is unsupported"; 0309 return std::nullopt; 0310 } 0311 const QVector<uint64_t> modifiers = dmabuf->formats().value(format); 0312 m_swapchain = std::make_unique<WaylandEglLayerSwapchain>(bufferSize, format, modifiers, m_backend); 0313 } 0314 0315 m_buffer = m_swapchain->acquire(); 0316 return OutputLayerBeginFrameInfo{ 0317 .renderTarget = RenderTarget(m_buffer->shadowFramebuffer()), 0318 .repaint = infiniteRegion(), 0319 }; 0320 } 0321 0322 bool WaylandEglCursorLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) 0323 { 0324 GLFramebuffer::pushFramebuffer(m_buffer->framebuffer()); 0325 auto texture = m_buffer->shadowTexture(); 0326 QRect outputGeometry(0, 0, texture->size().width(), texture->size().height()); 0327 0328 ShaderBinder shaderBinder(ShaderTrait::MapTexture); 0329 QMatrix4x4 projectionMatrix; 0330 projectionMatrix.scale(1, -1); 0331 projectionMatrix.ortho(outputGeometry); 0332 shaderBinder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, projectionMatrix); 0333 0334 texture->bind(); 0335 texture->render(outputGeometry, 1); 0336 texture->unbind(); 0337 GLFramebuffer::popFramebuffer(); 0338 0339 // Flush rendering commands to the dmabuf. 0340 glFlush(); 0341 0342 m_output->cursor()->update(m_buffer->buffer(), m_scale, m_hotspot); 0343 0344 m_swapchain->release(m_buffer); 0345 return true; 0346 } 0347 0348 WaylandEglBackend::WaylandEglBackend(WaylandBackend *b) 0349 : AbstractEglBackend() 0350 , m_backend(b) 0351 { 0352 // Egl is always direct rendering 0353 setIsDirectRendering(true); 0354 0355 connect(m_backend, &WaylandBackend::outputAdded, this, &WaylandEglBackend::createEglWaylandOutput); 0356 connect(m_backend, &WaylandBackend::outputRemoved, this, [this](Output *output) { 0357 m_outputs.erase(output); 0358 }); 0359 0360 b->setEglBackend(this); 0361 } 0362 0363 WaylandEglBackend::~WaylandEglBackend() 0364 { 0365 cleanup(); 0366 } 0367 0368 WaylandBackend *WaylandEglBackend::backend() const 0369 { 0370 return m_backend; 0371 } 0372 0373 void WaylandEglBackend::cleanupSurfaces() 0374 { 0375 m_outputs.clear(); 0376 } 0377 0378 bool WaylandEglBackend::createEglWaylandOutput(Output *waylandOutput) 0379 { 0380 m_outputs[waylandOutput] = Layers{ 0381 .primaryLayer = std::make_unique<WaylandEglPrimaryLayer>(static_cast<WaylandOutput *>(waylandOutput), this), 0382 .cursorLayer = std::make_unique<WaylandEglCursorLayer>(static_cast<WaylandOutput *>(waylandOutput), this), 0383 }; 0384 return true; 0385 } 0386 0387 bool WaylandEglBackend::initializeEgl() 0388 { 0389 initClientExtensions(); 0390 EGLDisplay display = m_backend->sceneEglDisplay(); 0391 0392 // Use eglGetPlatformDisplayEXT() to get the display pointer 0393 // if the implementation supports it. 0394 if (display == EGL_NO_DISPLAY) { 0395 m_havePlatformBase = hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base")); 0396 if (m_havePlatformBase) { 0397 // Make sure that the wayland platform is supported 0398 if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_wayland"))) { 0399 return false; 0400 } 0401 0402 display = eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, m_backend->display()->nativeDisplay(), nullptr); 0403 } else { 0404 display = eglGetDisplay(m_backend->display()->nativeDisplay()); 0405 } 0406 } 0407 0408 if (display == EGL_NO_DISPLAY) { 0409 return false; 0410 } 0411 setEglDisplay(display); 0412 return initEglAPI(); 0413 } 0414 0415 void WaylandEglBackend::init() 0416 { 0417 if (!initializeEgl()) { 0418 setFailed("Could not initialize egl"); 0419 return; 0420 } 0421 if (!initRenderingContext()) { 0422 setFailed("Could not initialize rendering context"); 0423 return; 0424 } 0425 0426 initKWinGL(); 0427 initBufferAge(); 0428 initWayland(); 0429 } 0430 0431 bool WaylandEglBackend::initRenderingContext() 0432 { 0433 initBufferConfigs(); 0434 0435 if (!createContext()) { 0436 return false; 0437 } 0438 0439 auto waylandOutputs = m_backend->waylandOutputs(); 0440 0441 // we only allow to start with at least one output 0442 if (waylandOutputs.isEmpty()) { 0443 return false; 0444 } 0445 0446 for (auto *out : waylandOutputs) { 0447 if (!createEglWaylandOutput(out)) { 0448 return false; 0449 } 0450 } 0451 0452 if (m_outputs.empty()) { 0453 qCCritical(KWIN_WAYLAND_BACKEND) << "Create Window Surfaces failed"; 0454 return false; 0455 } 0456 0457 return makeCurrent(); 0458 } 0459 0460 bool WaylandEglBackend::initBufferConfigs() 0461 { 0462 const EGLint config_attribs[] = { 0463 EGL_SURFACE_TYPE, 0464 EGL_WINDOW_BIT, 0465 EGL_RED_SIZE, 0466 1, 0467 EGL_GREEN_SIZE, 0468 1, 0469 EGL_BLUE_SIZE, 0470 1, 0471 EGL_ALPHA_SIZE, 0472 0, 0473 EGL_RENDERABLE_TYPE, 0474 isOpenGLES() ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT, 0475 EGL_CONFIG_CAVEAT, 0476 EGL_NONE, 0477 EGL_NONE, 0478 }; 0479 0480 EGLint count; 0481 EGLConfig configs[1024]; 0482 if (eglChooseConfig(eglDisplay(), config_attribs, configs, 1, &count) == EGL_FALSE) { 0483 qCCritical(KWIN_WAYLAND_BACKEND) << "choose config failed"; 0484 return false; 0485 } 0486 if (count != 1) { 0487 qCCritical(KWIN_WAYLAND_BACKEND) << "choose config did not return a config" << count; 0488 return false; 0489 } 0490 setConfig(configs[0]); 0491 0492 return true; 0493 } 0494 0495 std::shared_ptr<KWin::GLTexture> WaylandEglBackend::textureForOutput(KWin::Output *output) const 0496 { 0497 auto texture = std::make_unique<GLTexture>(GL_RGBA8, output->pixelSize()); 0498 GLFramebuffer::pushFramebuffer(m_outputs.at(output).primaryLayer->fbo()); 0499 GLFramebuffer renderTarget(texture.get()); 0500 renderTarget.blitFromFramebuffer(QRect(0, texture->height(), texture->width(), -texture->height())); 0501 GLFramebuffer::popFramebuffer(); 0502 return texture; 0503 } 0504 0505 std::unique_ptr<SurfaceTexture> WaylandEglBackend::createSurfaceTextureInternal(SurfacePixmapInternal *pixmap) 0506 { 0507 return std::make_unique<BasicEGLSurfaceTextureInternal>(this, pixmap); 0508 } 0509 0510 std::unique_ptr<SurfaceTexture> WaylandEglBackend::createSurfaceTextureWayland(SurfacePixmapWayland *pixmap) 0511 { 0512 return std::make_unique<BasicEGLSurfaceTextureWayland>(this, pixmap); 0513 } 0514 0515 void WaylandEglBackend::present(Output *output) 0516 { 0517 m_outputs[output].primaryLayer->present(); 0518 } 0519 0520 OutputLayer *WaylandEglBackend::primaryLayer(Output *output) 0521 { 0522 return m_outputs[output].primaryLayer.get(); 0523 } 0524 0525 WaylandEglCursorLayer *WaylandEglBackend::cursorLayer(Output *output) 0526 { 0527 return m_outputs[output].cursorLayer.get(); 0528 } 0529 0530 } 0531 }