KWin
Loading...
Searching...
No Matches
drm_virtual_egl_layer.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
10#include "drm_egl_backend.h"
11#include "drm_gpu.h"
12#include "drm_logging.h"
13#include "drm_virtual_output.h"
14#include "opengl/eglswapchain.h"
17#include "wayland/surface.h"
18
19#include <QRegion>
20#include <drm_fourcc.h>
21#include <errno.h>
22#include <gbm.h>
23#include <unistd.h>
24
25namespace KWin
26{
27
29 : m_output(output)
30 , m_eglBackend(eglBackend)
31{
32}
33
35
36std::optional<OutputLayerBeginFrameInfo> VirtualEglGbmLayer::beginFrame()
37{
38 // gbm surface
39 if (doesGbmSwapchainFit(m_gbmSwapchain.get())) {
40 m_oldGbmSwapchain.reset();
41 m_oldDamageJournal.clear();
42 } else {
43 if (doesGbmSwapchainFit(m_oldGbmSwapchain.get())) {
44 m_gbmSwapchain = m_oldGbmSwapchain;
45 m_damageJournal = m_oldDamageJournal;
46 } else {
47 if (const auto swapchain = createGbmSwapchain()) {
48 m_oldGbmSwapchain = m_gbmSwapchain;
49 m_oldDamageJournal = m_damageJournal;
50 m_gbmSwapchain = swapchain;
51 m_damageJournal = DamageJournal();
52 } else {
53 return std::nullopt;
54 }
55 }
56 }
57
58 if (!m_eglBackend->contextObject()->makeCurrent()) {
59 return std::nullopt;
60 }
61
62 auto slot = m_gbmSwapchain->acquire();
63 if (!slot) {
64 return std::nullopt;
65 }
66
67 m_currentSlot = slot;
68 m_scanoutSurface.clear();
69 if (m_scanoutBuffer) {
70 m_scanoutBuffer->unref();
71 m_scanoutBuffer = nullptr;
72 }
73
74 if (!m_query) {
75 m_query = std::make_unique<GLRenderTimeQuery>();
76 }
77 m_query->begin();
78
79 const QRegion repair = m_damageJournal.accumulate(slot->age(), infiniteRegion());
81 .renderTarget = RenderTarget(slot->framebuffer()),
82 .repaint = repair,
83 };
84}
85
86bool VirtualEglGbmLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
87{
88 m_query->end();
89 glFlush();
90 m_currentDamage = damagedRegion;
91 m_damageJournal.add(damagedRegion);
92 m_gbmSwapchain->release(m_currentSlot);
93 return true;
94}
95
97{
98 return m_currentDamage;
99}
100
101std::shared_ptr<EglSwapchain> VirtualEglGbmLayer::createGbmSwapchain() const
102{
103 static bool modifiersEnvSet = false;
104 static const bool modifiersEnv = qEnvironmentVariableIntValue("KWIN_DRM_USE_MODIFIERS", &modifiersEnvSet) != 0;
105 const bool allowModifiers = !modifiersEnvSet || modifiersEnv;
106
107 const auto tranches = m_eglBackend->tranches();
108 for (const auto &tranche : tranches) {
109 for (auto it = tranche.formatTable.constBegin(); it != tranche.formatTable.constEnd(); it++) {
110 const auto size = m_output->modeSize();
111 const auto format = it.key();
112 const auto modifiers = it.value();
113
114 if (allowModifiers && !modifiers.isEmpty()) {
115 if (auto swapchain = EglSwapchain::create(m_eglBackend->gpu()->graphicsBufferAllocator(), m_eglBackend->contextObject(), size, format, modifiers)) {
116 return swapchain;
117 }
118 }
119
120 static const QList<uint64_t> implicitModifier{DRM_FORMAT_MOD_INVALID};
121 if (auto swapchain = EglSwapchain::create(m_eglBackend->gpu()->graphicsBufferAllocator(), m_eglBackend->contextObject(), size, format, implicitModifier)) {
122 return swapchain;
123 }
124 }
125 }
126 qCWarning(KWIN_DRM) << "couldn't create a gbm swapchain for a virtual output!";
127 return nullptr;
128}
129
130bool VirtualEglGbmLayer::doesGbmSwapchainFit(EglSwapchain *swapchain) const
131{
132 return swapchain && swapchain->size() == m_output->modeSize();
133}
134
135std::shared_ptr<GLTexture> VirtualEglGbmLayer::texture() const
136{
137 if (m_scanoutSurface) {
138 return m_eglBackend->importDmaBufAsTexture(*m_scanoutBuffer->dmabufAttributes());
139 } else if (m_currentSlot) {
140 return m_currentSlot->texture();
141 }
142 return nullptr;
143}
144
146{
147 static bool valid;
148 static const bool directScanoutDisabled = qEnvironmentVariableIntValue("KWIN_DRM_NO_DIRECT_SCANOUT", &valid) == 1 && valid;
149 if (directScanoutDisabled) {
150 return false;
151 }
152
153 SurfaceItemWayland *item = qobject_cast<SurfaceItemWayland *>(surfaceItem);
154 if (!item || !item->surface()) {
155 return false;
156 }
157 const auto buffer = item->surface()->buffer();
158 if (!buffer || !buffer->dmabufAttributes() || buffer->size() != m_output->modeSize()) {
159 return false;
160 }
161 buffer->ref();
162 if (m_scanoutBuffer) {
163 m_scanoutBuffer->unref();
164 }
165 m_scanoutBuffer = buffer;
166 // damage tracking for screen casting
167 m_currentDamage = m_scanoutSurface == item->surface() ? surfaceItem->mapFromBuffer(surfaceItem->damage()) : infiniteRegion();
168 surfaceItem->resetDamage();
169 // ensure the pixmap is updated when direct scanout ends
170 surfaceItem->destroyPixmap();
171 m_scanoutSurface = item->surface();
172 return true;
173}
174
176{
177 m_eglBackend->contextObject()->makeCurrent();
178 m_gbmSwapchain.reset();
179 m_oldGbmSwapchain.reset();
180 m_currentSlot.reset();
181 if (m_scanoutBuffer) {
182 m_scanoutBuffer->unref();
183 m_scanoutBuffer = nullptr;
184 }
185}
186
187std::chrono::nanoseconds VirtualEglGbmLayer::queryRenderTime() const
188{
189 m_eglBackend->makeCurrent();
190 return m_query->result();
191}
192}
QList< LinuxDmaBufV1Feedback::Tranche > tranches() const
std::shared_ptr< GLTexture > importDmaBufAsTexture(const DmaBufAttributes &attributes) const
void add(const QRegion &region)
QRegion accumulate(int bufferAge, const QRegion &fallback=QRegion()) const
GraphicsBufferAllocator * graphicsBufferAllocator() const
Definition drm_gpu.cpp:834
bool makeCurrent(EGLSurface surface=EGL_NO_SURFACE) const
OpenGL Backend using Egl on a GBM surface.
static std::shared_ptr< EglSwapchain > create(GraphicsBufferAllocator *allocator, EglContext *context, const QSize &size, uint32_t format, const QList< uint64_t > &modifiers)
QSize modeSize() const
Definition output.cpp:480
QSizeF size() const
GraphicsBuffer * buffer() const
Definition surface.cpp:809
QRegion damage() const
QRegion mapFromBuffer(const QRegion &region) const
SurfaceInterface * surface() const
bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override
QRegion currentDamage() const override
~VirtualEglGbmLayer() override
std::optional< OutputLayerBeginFrameInfo > beginFrame() override
std::shared_ptr< GLTexture > texture() const override
std::chrono::nanoseconds queryRenderTime() const override
bool scanout(SurfaceItem *surfaceItem) override
VirtualEglGbmLayer(EglGbmBackend *eglBackend, DrmVirtualOutput *output)
KWIN_EXPORT QRect infiniteRegion()
Definition globals.h:234
GLenum format
Definition gltexture.cpp:49