KWin
Loading...
Searching...
No Matches
wayland_egl_backend.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: 2019 Roman Gilg <subdiff@gmail.com>
6 SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
7
8 SPDX-License-Identifier: GPL-2.0-or-later
9*/
10
11#include "wayland_egl_backend.h"
13#include "opengl/eglswapchain.h"
15#include "opengl/glutils.h"
18#include "wayland/surface.h"
19#include "wayland_backend.h"
20#include "wayland_display.h"
21#include "wayland_logging.h"
22#include "wayland_output.h"
23
24#include <KWayland/Client/surface.h>
25
26#include <cmath>
27#include <drm_fourcc.h>
28#include <fcntl.h>
29#include <unistd.h>
30
31namespace KWin
32{
33namespace Wayland
34{
35
36static const bool bufferAgeEnabled = qEnvironmentVariable("KWIN_USE_BUFFER_AGE") != QStringLiteral("0");
37
39 : m_waylandOutput(output)
40 , m_backend(backend)
41{
42}
43
47
49{
50 return m_buffer->framebuffer();
51}
52
53std::shared_ptr<GLTexture> WaylandEglPrimaryLayer::texture() const
54{
55 return m_buffer->texture();
56}
57
58std::optional<OutputLayerBeginFrameInfo> WaylandEglPrimaryLayer::beginFrame()
59{
60 if (!m_backend->contextObject()->makeCurrent()) {
61 qCCritical(KWIN_WAYLAND_BACKEND) << "Make Context Current failed";
62 return std::nullopt;
63 }
64
65 const QSize nativeSize = m_waylandOutput->modeSize();
66 if (!m_swapchain || m_swapchain->size() != nativeSize) {
67 const QHash<uint32_t, QList<uint64_t>> formatTable = m_backend->backend()->display()->linuxDmabuf()->formats();
68 uint32_t format = DRM_FORMAT_INVALID;
69 QList<uint64_t> modifiers;
70 for (const uint32_t &candidateFormat : {DRM_FORMAT_XRGB2101010, DRM_FORMAT_XRGB8888}) {
71 auto it = formatTable.constFind(candidateFormat);
72 if (it != formatTable.constEnd()) {
73 format = it.key();
74 modifiers = it.value();
75 break;
76 }
77 }
78 if (format == DRM_FORMAT_INVALID) {
79 qCWarning(KWIN_WAYLAND_BACKEND) << "Could not find a suitable render format";
80 return std::nullopt;
81 }
82 m_swapchain = EglSwapchain::create(m_backend->graphicsBufferAllocator(), m_backend->contextObject(), nativeSize, format, modifiers);
83 if (!m_swapchain) {
84 return std::nullopt;
85 }
86 }
87
88 m_buffer = m_swapchain->acquire();
89 if (!m_buffer) {
90 return std::nullopt;
91 }
92
93 const QRegion repair = bufferAgeEnabled ? m_damageJournal.accumulate(m_buffer->age(), infiniteRegion()) : infiniteRegion();
94 if (!m_query) {
95 m_query = std::make_unique<GLRenderTimeQuery>();
96 }
97 m_query->begin();
99 .renderTarget = RenderTarget(m_buffer->framebuffer()),
100 .repaint = repair,
101 };
102}
103
104bool WaylandEglPrimaryLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
105{
106 m_query->end();
107 // Flush rendering commands to the dmabuf.
108 glFlush();
109
110 m_damageJournal.add(damagedRegion);
111 return true;
112}
113
115{
116 Q_ASSERT(!m_presentationBuffer);
117 if (surfaceItem->size() != m_waylandOutput->modeSize()) {
118 return false;
119 }
120 SurfaceItemWayland *item = qobject_cast<SurfaceItemWayland *>(surfaceItem);
121 if (!item || !item->surface() || item->surface()->bufferTransform() != OutputTransform::Kind::Normal) {
122 return false;
123 }
124 m_presentationBuffer = m_backend->backend()->importBuffer(item->surface()->buffer());
125 return m_presentationBuffer;
126}
127
129{
130 if (!m_presentationBuffer) {
131 m_presentationBuffer = m_backend->backend()->importBuffer(m_buffer->buffer());
132 Q_ASSERT(m_presentationBuffer);
133 }
134
135 KWayland::Client::Surface *surface = m_waylandOutput->surface();
136 surface->attachBuffer(m_presentationBuffer);
137 surface->damage(m_damageJournal.lastDamage());
138 surface->setScale(std::ceil(m_waylandOutput->scale()));
139 surface->commit();
140 Q_EMIT m_waylandOutput->outputChange(m_damageJournal.lastDamage());
141 m_presentationBuffer = nullptr;
142
143 m_swapchain->release(m_buffer);
144}
145
146std::chrono::nanoseconds WaylandEglPrimaryLayer::queryRenderTime() const
147{
148 m_backend->makeCurrent();
149 return m_query->result();
150}
151
153 : m_output(output)
154 , m_backend(backend)
155{
156}
157
162
163std::optional<OutputLayerBeginFrameInfo> WaylandEglCursorLayer::beginFrame()
164{
165 if (!m_backend->contextObject()->makeCurrent()) {
166 qCCritical(KWIN_WAYLAND_BACKEND) << "Make Context Current failed";
167 return std::nullopt;
168 }
169
170 const auto tmp = size().expandedTo(QSize(64, 64));
171 const QSize bufferSize(std::ceil(tmp.width()), std::ceil(tmp.height()));
172 if (!m_swapchain || m_swapchain->size() != bufferSize) {
173 const QHash<uint32_t, QList<uint64_t>> formatTable = m_backend->backend()->display()->linuxDmabuf()->formats();
174 uint32_t format = DRM_FORMAT_INVALID;
175 QList<uint64_t> modifiers;
176 for (const uint32_t &candidateFormat : {DRM_FORMAT_ARGB2101010, DRM_FORMAT_ARGB8888}) {
177 auto it = formatTable.constFind(candidateFormat);
178 if (it != formatTable.constEnd()) {
179 format = it.key();
180 modifiers = it.value();
181 break;
182 }
183 }
184 if (format == DRM_FORMAT_INVALID) {
185 qCWarning(KWIN_WAYLAND_BACKEND) << "Could not find a suitable render format";
186 return std::nullopt;
187 }
188 m_swapchain = EglSwapchain::create(m_backend->graphicsBufferAllocator(), m_backend->contextObject(), bufferSize, format, modifiers);
189 }
190
191 m_buffer = m_swapchain->acquire();
192 if (!m_query) {
193 m_query = std::make_unique<GLRenderTimeQuery>();
194 }
195 m_query->begin();
197 .renderTarget = RenderTarget(m_buffer->framebuffer()),
198 .repaint = infiniteRegion(),
199 };
200}
201
202bool WaylandEglCursorLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
203{
204 m_query->end();
205 // Flush rendering commands to the dmabuf.
206 glFlush();
207
208 wl_buffer *buffer = m_backend->backend()->importBuffer(m_buffer->buffer());
209 Q_ASSERT(buffer);
210
211 m_output->cursor()->update(buffer, scale(), hotspot().toPoint());
212
213 m_swapchain->release(m_buffer);
214 return true;
215}
216
217std::chrono::nanoseconds WaylandEglCursorLayer::queryRenderTime() const
218{
219 m_backend->makeCurrent();
220 return m_query->result();
221}
222
225 , m_backend(b)
226 , m_allocator(std::make_unique<GbmGraphicsBufferAllocator>(b->gbmDevice()))
227{
228 connect(m_backend, &WaylandBackend::outputAdded, this, &WaylandEglBackend::createEglWaylandOutput);
229 connect(m_backend, &WaylandBackend::outputRemoved, this, [this](Output *output) {
230 m_outputs.erase(output);
231 });
232
233 b->setEglBackend(this);
234}
235
240
242{
243 return m_backend;
244}
245
247{
248 return m_allocator.get();
249}
250
251void WaylandEglBackend::cleanupSurfaces()
252{
253 m_outputs.clear();
254}
255
256bool WaylandEglBackend::createEglWaylandOutput(Output *waylandOutput)
257{
258 m_outputs[waylandOutput] = Layers{
259 .primaryLayer = std::make_unique<WaylandEglPrimaryLayer>(static_cast<WaylandOutput *>(waylandOutput), this),
260 .cursorLayer = std::make_unique<WaylandEglCursorLayer>(static_cast<WaylandOutput *>(waylandOutput), this),
261 };
262 return true;
263}
264
265bool WaylandEglBackend::initializeEgl()
266{
268
269 if (!m_backend->sceneEglDisplayObject()) {
270 for (const QByteArray &extension : {QByteArrayLiteral("EGL_EXT_platform_base"), QByteArrayLiteral("EGL_KHR_platform_gbm")}) {
271 if (!hasClientExtension(extension)) {
272 qCWarning(KWIN_WAYLAND_BACKEND) << extension << "client extension is not supported by the platform";
273 return false;
274 }
275 }
276
277 m_backend->setEglDisplay(EglDisplay::create(eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, m_backend->gbmDevice(), nullptr)));
278 }
279
280 auto display = m_backend->sceneEglDisplayObject();
281 if (!display) {
282 return false;
283 }
284 setEglDisplay(display);
285 return true;
286}
287
289{
290 if (!initializeEgl()) {
291 setFailed("Could not initialize egl");
292 return;
293 }
294 if (!initRenderingContext()) {
295 setFailed("Could not initialize rendering context");
296 return;
297 }
298
299 initKWinGL();
300 initWayland();
301}
302
303bool WaylandEglBackend::initRenderingContext()
304{
305 if (!createContext(EGL_NO_CONFIG_KHR)) {
306 return false;
307 }
308
309 auto waylandOutputs = m_backend->waylandOutputs();
310
311 // we only allow to start with at least one output
312 if (waylandOutputs.isEmpty()) {
313 return false;
314 }
315
316 for (auto *out : waylandOutputs) {
317 if (!createEglWaylandOutput(out)) {
318 return false;
319 }
320 }
321
322 if (m_outputs.empty()) {
323 qCCritical(KWIN_WAYLAND_BACKEND) << "Create Window Surfaces failed";
324 return false;
325 }
326
327 return makeCurrent();
328}
329
330std::pair<std::shared_ptr<KWin::GLTexture>, ColorDescription> WaylandEglBackend::textureForOutput(KWin::Output *output) const
331{
332 return std::make_pair(m_outputs.at(output).primaryLayer->texture(), ColorDescription::sRGB);
333}
334
335std::unique_ptr<SurfaceTexture> WaylandEglBackend::createSurfaceTextureWayland(SurfacePixmap *pixmap)
336{
337 return std::make_unique<BasicEGLSurfaceTextureWayland>(this, pixmap);
338}
339
340void WaylandEglBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame)
341{
342 m_outputs[output].primaryLayer->present();
343 static_cast<WaylandOutput *>(output)->framePending(frame);
344}
345
347{
348 return m_outputs[output].primaryLayer.get();
349}
350
352{
353 return m_outputs[output].cursorLayer.get();
354}
355
356}
357}
358
359#include "moc_wayland_egl_backend.cpp"
bool hasClientExtension(const QByteArray &ext) const
void setEglDisplay(EglDisplay *display)
bool createContext(EGLConfig config)
static const ColorDescription sRGB
Definition colorspace.h:132
void add(const QRegion &region)
QRegion accumulate(int bufferAge, const QRegion &fallback=QRegion()) const
QRegion lastDamage() const
bool makeCurrent(EGLSurface surface=EGL_NO_SURFACE) const
static std::unique_ptr< EglDisplay > create(::EGLDisplay display, bool owning=true)
static std::shared_ptr< EglSwapchain > create(GraphicsBufferAllocator *allocator, EglContext *context, const QSize &size, uint32_t format, const QList< uint64_t > &modifiers)
OpenGL framebuffer object.
QSizeF size() const
Definition item.cpp:135
void setFailed(const QString &reason)
Sets the backend initialization to failed.
void outputAdded(Output *output)
void outputRemoved(Output *output)
void outputChange(const QRegion &damagedRegion)
qreal scale() const
Definition output.cpp:455
QSize modeSize() const
Definition output.cpp:480
qreal scale() const
QSizeF size() const
QPointF hotspot() const
OutputTransform bufferTransform() const
Definition surface.cpp:804
GraphicsBuffer * buffer() const
Definition surface.cpp:809
SurfaceInterface * surface() const
Class encapsulating all Wayland data structures needed by the Egl backend.
void setEglDisplay(std::unique_ptr< EglDisplay > &&display)
void setEglBackend(WaylandEglBackend *eglBackend)
WaylandDisplay * display() const
EglDisplay * sceneEglDisplayObject() const override
QList< WaylandOutput * > waylandOutputs() const
wl_buffer * importBuffer(GraphicsBuffer *graphicsBuffer)
gbm_device * gbmDevice() const
void update(wl_buffer *buffer, qreal scale, const QPoint &hotspot)
WaylandLinuxDmabufV1 * linuxDmabuf() const
OpenGL Backend using Egl on a Wayland surface.
GraphicsBufferAllocator * graphicsBufferAllocator() const override
OutputLayer * cursorLayer(Output *output) override
void present(Output *output, const std::shared_ptr< OutputFrame > &frame) override
OutputLayer * primaryLayer(Output *output) override
std::pair< std::shared_ptr< KWin::GLTexture >, ColorDescription > textureForOutput(KWin::Output *output) const override
std::unique_ptr< SurfaceTexture > createSurfaceTextureWayland(SurfacePixmap *pixmap) override
std::optional< OutputLayerBeginFrameInfo > beginFrame() override
std::chrono::nanoseconds queryRenderTime() const override
bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override
WaylandEglCursorLayer(WaylandOutput *output, WaylandEglBackend *backend)
WaylandEglPrimaryLayer(WaylandOutput *output, WaylandEglBackend *backend)
std::shared_ptr< GLTexture > texture() const
std::optional< OutputLayerBeginFrameInfo > beginFrame() override
bool scanout(SurfaceItem *surfaceItem) override
std::chrono::nanoseconds queryRenderTime() const override
bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override
QHash< uint32_t, QList< uint64_t > > formats() const
WaylandCursor * cursor() const
KWayland::Client::Surface * surface() const
KWIN_EXPORT QRect infiniteRegion()
Definition globals.h:234
GLenum format
Definition gltexture.cpp:49
struct KWin::@10 formatTable[]