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

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 "x11_windowed_egl_backend.h"
0010 #include "core/gbmgraphicsbufferallocator.h"
0011 #include "opengl/eglswapchain.h"
0012 #include "opengl/glrendertimequery.h"
0013 #include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h"
0014 #include "x11_windowed_backend.h"
0015 #include "x11_windowed_logging.h"
0016 #include "x11_windowed_output.h"
0017 
0018 #include <drm_fourcc.h>
0019 
0020 namespace KWin
0021 {
0022 
0023 X11WindowedEglPrimaryLayer::X11WindowedEglPrimaryLayer(X11WindowedEglBackend *backend, X11WindowedOutput *output)
0024     : m_output(output)
0025     , m_backend(backend)
0026 {
0027 }
0028 
0029 X11WindowedEglPrimaryLayer::~X11WindowedEglPrimaryLayer()
0030 {
0031 }
0032 
0033 std::optional<OutputLayerBeginFrameInfo> X11WindowedEglPrimaryLayer::beginFrame()
0034 {
0035     if (!m_backend->contextObject()->makeCurrent()) {
0036         return std::nullopt;
0037     }
0038 
0039     const QSize bufferSize = m_output->modeSize();
0040     if (!m_swapchain || m_swapchain->size() != bufferSize) {
0041         const uint32_t format = DRM_FORMAT_XRGB8888;
0042         const QHash<uint32_t, QList<uint64_t>> formatTable = m_backend->backend()->driFormats();
0043         if (!formatTable.contains(format)) {
0044             return std::nullopt;
0045         }
0046         m_swapchain = EglSwapchain::create(m_backend->graphicsBufferAllocator(), m_backend->contextObject(), bufferSize, format, formatTable[format]);
0047         if (!m_swapchain) {
0048             return std::nullopt;
0049         }
0050     }
0051 
0052     m_buffer = m_swapchain->acquire();
0053     if (!m_buffer) {
0054         return std::nullopt;
0055     }
0056 
0057     QRegion repaint = m_output->exposedArea() + m_output->rect();
0058     m_output->clearExposedArea();
0059 
0060     if (!m_query) {
0061         m_query = std::make_unique<GLRenderTimeQuery>();
0062     }
0063     m_query->begin();
0064     return OutputLayerBeginFrameInfo{
0065         .renderTarget = RenderTarget(m_buffer->framebuffer()),
0066         .repaint = repaint,
0067     };
0068 }
0069 
0070 bool X11WindowedEglPrimaryLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
0071 {
0072     m_query->end();
0073     return true;
0074 }
0075 
0076 void X11WindowedEglPrimaryLayer::present()
0077 {
0078     xcb_pixmap_t pixmap = m_output->importBuffer(m_buffer->buffer());
0079     Q_ASSERT(pixmap != XCB_PIXMAP_NONE);
0080 
0081     xcb_xfixes_region_t valid = 0;
0082     xcb_xfixes_region_t update = 0;
0083     uint32_t serial = 0;
0084     uint32_t options = 0;
0085     uint64_t targetMsc = 0;
0086 
0087     xcb_present_pixmap(m_output->backend()->connection(),
0088                        m_output->window(),
0089                        pixmap,
0090                        serial,
0091                        valid,
0092                        update,
0093                        0,
0094                        0,
0095                        XCB_NONE,
0096                        XCB_NONE,
0097                        XCB_NONE,
0098                        options,
0099                        targetMsc,
0100                        0,
0101                        0,
0102                        0,
0103                        nullptr);
0104 
0105     Q_EMIT m_output->outputChange(infiniteRegion());
0106 
0107     m_swapchain->release(m_buffer);
0108 }
0109 
0110 std::shared_ptr<GLTexture> X11WindowedEglPrimaryLayer::texture() const
0111 {
0112     return m_buffer->texture();
0113 }
0114 
0115 std::chrono::nanoseconds X11WindowedEglPrimaryLayer::queryRenderTime() const
0116 {
0117     m_backend->makeCurrent();
0118     return m_query->result();
0119 }
0120 
0121 X11WindowedEglCursorLayer::X11WindowedEglCursorLayer(X11WindowedEglBackend *backend, X11WindowedOutput *output)
0122     : m_output(output)
0123     , m_backend(backend)
0124 {
0125 }
0126 
0127 X11WindowedEglCursorLayer::~X11WindowedEglCursorLayer()
0128 {
0129     m_backend->contextObject()->makeCurrent();
0130     m_framebuffer.reset();
0131     m_texture.reset();
0132 }
0133 
0134 std::optional<OutputLayerBeginFrameInfo> X11WindowedEglCursorLayer::beginFrame()
0135 {
0136     if (!m_backend->contextObject()->makeCurrent()) {
0137         return std::nullopt;
0138     }
0139 
0140     const auto tmp = size().expandedTo(QSize(64, 64));
0141     const QSize bufferSize(std::ceil(tmp.width()), std::ceil(tmp.height()));
0142     if (!m_texture || m_texture->size() != bufferSize) {
0143         m_texture = GLTexture::allocate(GL_RGBA8, bufferSize);
0144         if (!m_texture) {
0145             return std::nullopt;
0146         }
0147         m_framebuffer = std::make_unique<GLFramebuffer>(m_texture.get());
0148     }
0149     if (!m_query) {
0150         m_query = std::make_unique<GLRenderTimeQuery>();
0151     }
0152     m_query->begin();
0153     return OutputLayerBeginFrameInfo{
0154         .renderTarget = RenderTarget(m_framebuffer.get()),
0155         .repaint = infiniteRegion(),
0156     };
0157 }
0158 
0159 bool X11WindowedEglCursorLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
0160 {
0161     QImage buffer(m_framebuffer->size(), QImage::Format_RGBA8888_Premultiplied);
0162 
0163     GLFramebuffer::pushFramebuffer(m_framebuffer.get());
0164     glReadPixels(0, 0, buffer.width(), buffer.height(), GL_RGBA, GL_UNSIGNED_BYTE, buffer.bits());
0165     GLFramebuffer::popFramebuffer();
0166 
0167     m_output->cursor()->update(buffer.mirrored(false, true), hotspot());
0168     m_query->end();
0169 
0170     return true;
0171 }
0172 
0173 std::chrono::nanoseconds X11WindowedEglCursorLayer::queryRenderTime() const
0174 {
0175     m_backend->makeCurrent();
0176     return m_query->result();
0177 }
0178 
0179 X11WindowedEglBackend::X11WindowedEglBackend(X11WindowedBackend *backend)
0180     : m_allocator(std::make_unique<GbmGraphicsBufferAllocator>(backend->gbmDevice()))
0181     , m_backend(backend)
0182 {
0183 }
0184 
0185 X11WindowedEglBackend::~X11WindowedEglBackend()
0186 {
0187     cleanup();
0188 }
0189 
0190 X11WindowedBackend *X11WindowedEglBackend::backend() const
0191 {
0192     return m_backend;
0193 }
0194 
0195 GraphicsBufferAllocator *X11WindowedEglBackend::graphicsBufferAllocator() const
0196 {
0197     return m_allocator.get();
0198 }
0199 
0200 bool X11WindowedEglBackend::initializeEgl()
0201 {
0202     initClientExtensions();
0203 
0204     if (!m_backend->sceneEglDisplayObject()) {
0205         for (const QByteArray &extension : {QByteArrayLiteral("EGL_EXT_platform_base"), QByteArrayLiteral("EGL_KHR_platform_gbm")}) {
0206             if (!hasClientExtension(extension)) {
0207                 qCWarning(KWIN_X11WINDOWED) << extension << "client extension is not supported by the platform";
0208                 return false;
0209             }
0210         }
0211 
0212         m_backend->setEglDisplay(EglDisplay::create(eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, m_backend->gbmDevice(), nullptr)));
0213     }
0214 
0215     auto display = m_backend->sceneEglDisplayObject();
0216     if (!display) {
0217         return false;
0218     }
0219     setEglDisplay(display);
0220     return true;
0221 }
0222 
0223 bool X11WindowedEglBackend::initRenderingContext()
0224 {
0225     if (!createContext(EGL_NO_CONFIG_KHR)) {
0226         return false;
0227     }
0228 
0229     return makeCurrent();
0230 }
0231 
0232 void X11WindowedEglBackend::init()
0233 {
0234     qputenv("EGL_PLATFORM", "x11");
0235 
0236     if (!initializeEgl()) {
0237         setFailed(QStringLiteral("Could not initialize egl"));
0238         return;
0239     }
0240     if (!initRenderingContext()) {
0241         setFailed(QStringLiteral("Could not initialize rendering context"));
0242         return;
0243     }
0244 
0245     initKWinGL();
0246     initWayland();
0247 
0248     const auto &outputs = m_backend->outputs();
0249     for (const auto &output : outputs) {
0250         X11WindowedOutput *x11Output = static_cast<X11WindowedOutput *>(output);
0251         m_outputs[output] = Layers{
0252             .primaryLayer = std::make_unique<X11WindowedEglPrimaryLayer>(this, x11Output),
0253             .cursorLayer = std::make_unique<X11WindowedEglCursorLayer>(this, x11Output),
0254         };
0255     }
0256 }
0257 
0258 void X11WindowedEglBackend::cleanupSurfaces()
0259 {
0260     m_outputs.clear();
0261 }
0262 
0263 void X11WindowedEglBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame)
0264 {
0265     m_outputs[output].primaryLayer->present();
0266     static_cast<X11WindowedOutput *>(output)->framePending(frame);
0267 }
0268 
0269 OutputLayer *X11WindowedEglBackend::primaryLayer(Output *output)
0270 {
0271     return m_outputs[output].primaryLayer.get();
0272 }
0273 
0274 OutputLayer *X11WindowedEglBackend::cursorLayer(Output *output)
0275 {
0276     return m_outputs[output].cursorLayer.get();
0277 }
0278 
0279 std::unique_ptr<SurfaceTexture> X11WindowedEglBackend::createSurfaceTextureWayland(SurfacePixmap *pixmap)
0280 {
0281     return std::make_unique<BasicEGLSurfaceTextureWayland>(this, pixmap);
0282 }
0283 
0284 std::pair<std::shared_ptr<GLTexture>, ColorDescription> X11WindowedEglBackend::textureForOutput(Output *output) const
0285 {
0286     auto it = m_outputs.find(output);
0287     if (it == m_outputs.end()) {
0288         return {nullptr, ColorDescription::sRGB};
0289     }
0290     return std::make_pair(it->second.primaryLayer->texture(), ColorDescription::sRGB);
0291 }
0292 
0293 } // namespace
0294 
0295 #include "moc_x11_windowed_egl_backend.cpp"