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

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "drm_virtual_egl_layer.h"
0010 #include "drm_egl_backend.h"
0011 #include "drm_gpu.h"
0012 #include "drm_logging.h"
0013 #include "drm_virtual_output.h"
0014 #include "opengl/eglswapchain.h"
0015 #include "opengl/glrendertimequery.h"
0016 #include "scene/surfaceitem_wayland.h"
0017 #include "wayland/surface.h"
0018 
0019 #include <QRegion>
0020 #include <drm_fourcc.h>
0021 #include <errno.h>
0022 #include <gbm.h>
0023 #include <unistd.h>
0024 
0025 namespace KWin
0026 {
0027 
0028 VirtualEglGbmLayer::VirtualEglGbmLayer(EglGbmBackend *eglBackend, DrmVirtualOutput *output)
0029     : m_output(output)
0030     , m_eglBackend(eglBackend)
0031 {
0032 }
0033 
0034 VirtualEglGbmLayer::~VirtualEglGbmLayer() = default;
0035 
0036 std::optional<OutputLayerBeginFrameInfo> VirtualEglGbmLayer::beginFrame()
0037 {
0038     // gbm surface
0039     if (doesGbmSwapchainFit(m_gbmSwapchain.get())) {
0040         m_oldGbmSwapchain.reset();
0041         m_oldDamageJournal.clear();
0042     } else {
0043         if (doesGbmSwapchainFit(m_oldGbmSwapchain.get())) {
0044             m_gbmSwapchain = m_oldGbmSwapchain;
0045             m_damageJournal = m_oldDamageJournal;
0046         } else {
0047             if (const auto swapchain = createGbmSwapchain()) {
0048                 m_oldGbmSwapchain = m_gbmSwapchain;
0049                 m_oldDamageJournal = m_damageJournal;
0050                 m_gbmSwapchain = swapchain;
0051                 m_damageJournal = DamageJournal();
0052             } else {
0053                 return std::nullopt;
0054             }
0055         }
0056     }
0057 
0058     if (!m_eglBackend->contextObject()->makeCurrent()) {
0059         return std::nullopt;
0060     }
0061 
0062     auto slot = m_gbmSwapchain->acquire();
0063     if (!slot) {
0064         return std::nullopt;
0065     }
0066 
0067     m_currentSlot = slot;
0068     m_scanoutSurface.clear();
0069     if (m_scanoutBuffer) {
0070         m_scanoutBuffer->unref();
0071         m_scanoutBuffer = nullptr;
0072     }
0073 
0074     if (!m_query) {
0075         m_query = std::make_unique<GLRenderTimeQuery>();
0076     }
0077     m_query->begin();
0078 
0079     const QRegion repair = m_damageJournal.accumulate(slot->age(), infiniteRegion());
0080     return OutputLayerBeginFrameInfo{
0081         .renderTarget = RenderTarget(slot->framebuffer()),
0082         .repaint = repair,
0083     };
0084 }
0085 
0086 bool VirtualEglGbmLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
0087 {
0088     m_query->end();
0089     glFlush();
0090     m_currentDamage = damagedRegion;
0091     m_damageJournal.add(damagedRegion);
0092     m_gbmSwapchain->release(m_currentSlot);
0093     return true;
0094 }
0095 
0096 QRegion VirtualEglGbmLayer::currentDamage() const
0097 {
0098     return m_currentDamage;
0099 }
0100 
0101 std::shared_ptr<EglSwapchain> VirtualEglGbmLayer::createGbmSwapchain() const
0102 {
0103     static bool modifiersEnvSet = false;
0104     static const bool modifiersEnv = qEnvironmentVariableIntValue("KWIN_DRM_USE_MODIFIERS", &modifiersEnvSet) != 0;
0105     const bool allowModifiers = !modifiersEnvSet || modifiersEnv;
0106 
0107     const auto tranches = m_eglBackend->tranches();
0108     for (const auto &tranche : tranches) {
0109         for (auto it = tranche.formatTable.constBegin(); it != tranche.formatTable.constEnd(); it++) {
0110             const auto size = m_output->modeSize();
0111             const auto format = it.key();
0112             const auto modifiers = it.value();
0113 
0114             if (allowModifiers && !modifiers.isEmpty()) {
0115                 if (auto swapchain = EglSwapchain::create(m_eglBackend->gpu()->graphicsBufferAllocator(), m_eglBackend->contextObject(), size, format, modifiers)) {
0116                     return swapchain;
0117                 }
0118             }
0119 
0120             static const QList<uint64_t> implicitModifier{DRM_FORMAT_MOD_INVALID};
0121             if (auto swapchain = EglSwapchain::create(m_eglBackend->gpu()->graphicsBufferAllocator(), m_eglBackend->contextObject(), size, format, implicitModifier)) {
0122                 return swapchain;
0123             }
0124         }
0125     }
0126     qCWarning(KWIN_DRM) << "couldn't create a gbm swapchain for a virtual output!";
0127     return nullptr;
0128 }
0129 
0130 bool VirtualEglGbmLayer::doesGbmSwapchainFit(EglSwapchain *swapchain) const
0131 {
0132     return swapchain && swapchain->size() == m_output->modeSize();
0133 }
0134 
0135 std::shared_ptr<GLTexture> VirtualEglGbmLayer::texture() const
0136 {
0137     if (m_scanoutSurface) {
0138         return m_eglBackend->importDmaBufAsTexture(*m_scanoutBuffer->dmabufAttributes());
0139     } else if (m_currentSlot) {
0140         return m_currentSlot->texture();
0141     }
0142     return nullptr;
0143 }
0144 
0145 bool VirtualEglGbmLayer::scanout(SurfaceItem *surfaceItem)
0146 {
0147     static bool valid;
0148     static const bool directScanoutDisabled = qEnvironmentVariableIntValue("KWIN_DRM_NO_DIRECT_SCANOUT", &valid) == 1 && valid;
0149     if (directScanoutDisabled) {
0150         return false;
0151     }
0152 
0153     SurfaceItemWayland *item = qobject_cast<SurfaceItemWayland *>(surfaceItem);
0154     if (!item || !item->surface()) {
0155         return false;
0156     }
0157     const auto buffer = item->surface()->buffer();
0158     if (!buffer || !buffer->dmabufAttributes() || buffer->size() != m_output->modeSize()) {
0159         return false;
0160     }
0161     buffer->ref();
0162     if (m_scanoutBuffer) {
0163         m_scanoutBuffer->unref();
0164     }
0165     m_scanoutBuffer = buffer;
0166     // damage tracking for screen casting
0167     m_currentDamage = m_scanoutSurface == item->surface() ? surfaceItem->mapFromBuffer(surfaceItem->damage()) : infiniteRegion();
0168     surfaceItem->resetDamage();
0169     // ensure the pixmap is updated when direct scanout ends
0170     surfaceItem->destroyPixmap();
0171     m_scanoutSurface = item->surface();
0172     return true;
0173 }
0174 
0175 void VirtualEglGbmLayer::releaseBuffers()
0176 {
0177     m_eglBackend->contextObject()->makeCurrent();
0178     m_gbmSwapchain.reset();
0179     m_oldGbmSwapchain.reset();
0180     m_currentSlot.reset();
0181     if (m_scanoutBuffer) {
0182         m_scanoutBuffer->unref();
0183         m_scanoutBuffer = nullptr;
0184     }
0185 }
0186 
0187 std::chrono::nanoseconds VirtualEglGbmLayer::queryRenderTime() const
0188 {
0189     m_eglBackend->makeCurrent();
0190     return m_query->result();
0191 }
0192 }