File indexing completed on 2024-11-10 04:56:33

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 "core/gbmgraphicsbufferallocator.h"
0013 #include "opengl/eglswapchain.h"
0014 #include "opengl/glrendertimequery.h"
0015 #include "opengl/glutils.h"
0016 #include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h"
0017 #include "wayland_backend.h"
0018 #include "wayland_display.h"
0019 #include "wayland_logging.h"
0020 #include "wayland_output.h"
0021 
0022 #include <KWayland/Client/surface.h>
0023 
0024 #include <cmath>
0025 #include <drm_fourcc.h>
0026 #include <fcntl.h>
0027 #include <unistd.h>
0028 
0029 namespace KWin
0030 {
0031 namespace Wayland
0032 {
0033 
0034 static const bool bufferAgeEnabled = qEnvironmentVariable("KWIN_USE_BUFFER_AGE") != QStringLiteral("0");
0035 
0036 WaylandEglPrimaryLayer::WaylandEglPrimaryLayer(WaylandOutput *output, WaylandEglBackend *backend)
0037     : m_waylandOutput(output)
0038     , m_backend(backend)
0039 {
0040 }
0041 
0042 WaylandEglPrimaryLayer::~WaylandEglPrimaryLayer()
0043 {
0044 }
0045 
0046 GLFramebuffer *WaylandEglPrimaryLayer::fbo() const
0047 {
0048     return m_buffer->framebuffer();
0049 }
0050 
0051 std::shared_ptr<GLTexture> WaylandEglPrimaryLayer::texture() const
0052 {
0053     return m_buffer->texture();
0054 }
0055 
0056 std::optional<OutputLayerBeginFrameInfo> WaylandEglPrimaryLayer::beginFrame()
0057 {
0058     if (!m_backend->contextObject()->makeCurrent()) {
0059         qCCritical(KWIN_WAYLAND_BACKEND) << "Make Context Current failed";
0060         return std::nullopt;
0061     }
0062 
0063     const QSize nativeSize = m_waylandOutput->modeSize();
0064     if (!m_swapchain || m_swapchain->size() != nativeSize) {
0065         const QHash<uint32_t, QList<uint64_t>> formatTable = m_backend->backend()->display()->linuxDmabuf()->formats();
0066         uint32_t format = DRM_FORMAT_INVALID;
0067         QList<uint64_t> modifiers;
0068         for (const uint32_t &candidateFormat : {DRM_FORMAT_XRGB2101010, DRM_FORMAT_XRGB8888}) {
0069             auto it = formatTable.constFind(candidateFormat);
0070             if (it != formatTable.constEnd()) {
0071                 format = it.key();
0072                 modifiers = it.value();
0073                 break;
0074             }
0075         }
0076         if (format == DRM_FORMAT_INVALID) {
0077             qCWarning(KWIN_WAYLAND_BACKEND) << "Could not find a suitable render format";
0078             return std::nullopt;
0079         }
0080         m_swapchain = EglSwapchain::create(m_backend->graphicsBufferAllocator(), m_backend->contextObject(), nativeSize, format, modifiers);
0081         if (!m_swapchain) {
0082             return std::nullopt;
0083         }
0084     }
0085 
0086     m_buffer = m_swapchain->acquire();
0087     if (!m_buffer) {
0088         return std::nullopt;
0089     }
0090 
0091     const QRegion repair = bufferAgeEnabled ? m_damageJournal.accumulate(m_buffer->age(), infiniteRegion()) : infiniteRegion();
0092     if (!m_query) {
0093         m_query = std::make_unique<GLRenderTimeQuery>();
0094     }
0095     m_query->begin();
0096     return OutputLayerBeginFrameInfo{
0097         .renderTarget = RenderTarget(m_buffer->framebuffer()),
0098         .repaint = repair,
0099     };
0100 }
0101 
0102 bool WaylandEglPrimaryLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
0103 {
0104     m_query->end();
0105     // Flush rendering commands to the dmabuf.
0106     glFlush();
0107 
0108     m_damageJournal.add(damagedRegion);
0109     return true;
0110 }
0111 
0112 void WaylandEglPrimaryLayer::present()
0113 {
0114     wl_buffer *buffer = m_backend->backend()->importBuffer(m_buffer->buffer());
0115     Q_ASSERT(buffer);
0116 
0117     KWayland::Client::Surface *surface = m_waylandOutput->surface();
0118     surface->attachBuffer(buffer);
0119     surface->damage(m_damageJournal.lastDamage());
0120     surface->setScale(std::ceil(m_waylandOutput->scale()));
0121     surface->commit();
0122     Q_EMIT m_waylandOutput->outputChange(m_damageJournal.lastDamage());
0123 
0124     m_swapchain->release(m_buffer);
0125 }
0126 
0127 std::chrono::nanoseconds WaylandEglPrimaryLayer::queryRenderTime() const
0128 {
0129     m_backend->makeCurrent();
0130     return m_query->result();
0131 }
0132 
0133 WaylandEglCursorLayer::WaylandEglCursorLayer(WaylandOutput *output, WaylandEglBackend *backend)
0134     : m_output(output)
0135     , m_backend(backend)
0136 {
0137 }
0138 
0139 WaylandEglCursorLayer::~WaylandEglCursorLayer()
0140 {
0141     m_backend->contextObject()->makeCurrent();
0142 }
0143 
0144 std::optional<OutputLayerBeginFrameInfo> WaylandEglCursorLayer::beginFrame()
0145 {
0146     if (!m_backend->contextObject()->makeCurrent()) {
0147         qCCritical(KWIN_WAYLAND_BACKEND) << "Make Context Current failed";
0148         return std::nullopt;
0149     }
0150 
0151     const auto tmp = size().expandedTo(QSize(64, 64));
0152     const QSize bufferSize(std::ceil(tmp.width()), std::ceil(tmp.height()));
0153     if (!m_swapchain || m_swapchain->size() != bufferSize) {
0154         const QHash<uint32_t, QList<uint64_t>> formatTable = m_backend->backend()->display()->linuxDmabuf()->formats();
0155         uint32_t format = DRM_FORMAT_INVALID;
0156         QList<uint64_t> modifiers;
0157         for (const uint32_t &candidateFormat : {DRM_FORMAT_ARGB2101010, DRM_FORMAT_ARGB8888}) {
0158             auto it = formatTable.constFind(candidateFormat);
0159             if (it != formatTable.constEnd()) {
0160                 format = it.key();
0161                 modifiers = it.value();
0162                 break;
0163             }
0164         }
0165         if (format == DRM_FORMAT_INVALID) {
0166             qCWarning(KWIN_WAYLAND_BACKEND) << "Could not find a suitable render format";
0167             return std::nullopt;
0168         }
0169         m_swapchain = EglSwapchain::create(m_backend->graphicsBufferAllocator(), m_backend->contextObject(), bufferSize, format, modifiers);
0170     }
0171 
0172     m_buffer = m_swapchain->acquire();
0173     if (!m_query) {
0174         m_query = std::make_unique<GLRenderTimeQuery>();
0175     }
0176     m_query->begin();
0177     return OutputLayerBeginFrameInfo{
0178         .renderTarget = RenderTarget(m_buffer->framebuffer()),
0179         .repaint = infiniteRegion(),
0180     };
0181 }
0182 
0183 bool WaylandEglCursorLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
0184 {
0185     m_query->end();
0186     // Flush rendering commands to the dmabuf.
0187     glFlush();
0188 
0189     wl_buffer *buffer = m_backend->backend()->importBuffer(m_buffer->buffer());
0190     Q_ASSERT(buffer);
0191 
0192     m_output->cursor()->update(buffer, scale(), hotspot().toPoint());
0193 
0194     m_swapchain->release(m_buffer);
0195     return true;
0196 }
0197 
0198 std::chrono::nanoseconds WaylandEglCursorLayer::queryRenderTime() const
0199 {
0200     m_backend->makeCurrent();
0201     return m_query->result();
0202 }
0203 
0204 WaylandEglBackend::WaylandEglBackend(WaylandBackend *b)
0205     : AbstractEglBackend()
0206     , m_backend(b)
0207     , m_allocator(std::make_unique<GbmGraphicsBufferAllocator>(b->gbmDevice()))
0208 {
0209     connect(m_backend, &WaylandBackend::outputAdded, this, &WaylandEglBackend::createEglWaylandOutput);
0210     connect(m_backend, &WaylandBackend::outputRemoved, this, [this](Output *output) {
0211         m_outputs.erase(output);
0212     });
0213 
0214     b->setEglBackend(this);
0215 }
0216 
0217 WaylandEglBackend::~WaylandEglBackend()
0218 {
0219     cleanup();
0220 }
0221 
0222 WaylandBackend *WaylandEglBackend::backend() const
0223 {
0224     return m_backend;
0225 }
0226 
0227 GraphicsBufferAllocator *WaylandEglBackend::graphicsBufferAllocator() const
0228 {
0229     return m_allocator.get();
0230 }
0231 
0232 void WaylandEglBackend::cleanupSurfaces()
0233 {
0234     m_outputs.clear();
0235 }
0236 
0237 bool WaylandEglBackend::createEglWaylandOutput(Output *waylandOutput)
0238 {
0239     m_outputs[waylandOutput] = Layers{
0240         .primaryLayer = std::make_unique<WaylandEglPrimaryLayer>(static_cast<WaylandOutput *>(waylandOutput), this),
0241         .cursorLayer = std::make_unique<WaylandEglCursorLayer>(static_cast<WaylandOutput *>(waylandOutput), this),
0242     };
0243     return true;
0244 }
0245 
0246 bool WaylandEglBackend::initializeEgl()
0247 {
0248     initClientExtensions();
0249 
0250     if (!m_backend->sceneEglDisplayObject()) {
0251         for (const QByteArray &extension : {QByteArrayLiteral("EGL_EXT_platform_base"), QByteArrayLiteral("EGL_KHR_platform_gbm")}) {
0252             if (!hasClientExtension(extension)) {
0253                 qCWarning(KWIN_WAYLAND_BACKEND) << extension << "client extension is not supported by the platform";
0254                 return false;
0255             }
0256         }
0257 
0258         m_backend->setEglDisplay(EglDisplay::create(eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, m_backend->gbmDevice(), nullptr)));
0259     }
0260 
0261     auto display = m_backend->sceneEglDisplayObject();
0262     if (!display) {
0263         return false;
0264     }
0265     setEglDisplay(display);
0266     return true;
0267 }
0268 
0269 void WaylandEglBackend::init()
0270 {
0271     if (!initializeEgl()) {
0272         setFailed("Could not initialize egl");
0273         return;
0274     }
0275     if (!initRenderingContext()) {
0276         setFailed("Could not initialize rendering context");
0277         return;
0278     }
0279 
0280     initKWinGL();
0281     initWayland();
0282 }
0283 
0284 bool WaylandEglBackend::initRenderingContext()
0285 {
0286     if (!createContext(EGL_NO_CONFIG_KHR)) {
0287         return false;
0288     }
0289 
0290     auto waylandOutputs = m_backend->waylandOutputs();
0291 
0292     // we only allow to start with at least one output
0293     if (waylandOutputs.isEmpty()) {
0294         return false;
0295     }
0296 
0297     for (auto *out : waylandOutputs) {
0298         if (!createEglWaylandOutput(out)) {
0299             return false;
0300         }
0301     }
0302 
0303     if (m_outputs.empty()) {
0304         qCCritical(KWIN_WAYLAND_BACKEND) << "Create Window Surfaces failed";
0305         return false;
0306     }
0307 
0308     return makeCurrent();
0309 }
0310 
0311 std::pair<std::shared_ptr<KWin::GLTexture>, ColorDescription> WaylandEglBackend::textureForOutput(KWin::Output *output) const
0312 {
0313     return std::make_pair(m_outputs.at(output).primaryLayer->texture(), ColorDescription::sRGB);
0314 }
0315 
0316 std::unique_ptr<SurfaceTexture> WaylandEglBackend::createSurfaceTextureWayland(SurfacePixmap *pixmap)
0317 {
0318     return std::make_unique<BasicEGLSurfaceTextureWayland>(this, pixmap);
0319 }
0320 
0321 void WaylandEglBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame)
0322 {
0323     m_outputs[output].primaryLayer->present();
0324     static_cast<WaylandOutput *>(output)->framePending(frame);
0325 }
0326 
0327 OutputLayer *WaylandEglBackend::primaryLayer(Output *output)
0328 {
0329     return m_outputs[output].primaryLayer.get();
0330 }
0331 
0332 OutputLayer *WaylandEglBackend::cursorLayer(Output *output)
0333 {
0334     return m_outputs[output].cursorLayer.get();
0335 }
0336 
0337 }
0338 }
0339 
0340 #include "moc_wayland_egl_backend.cpp"