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 }