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 }