2
/********************************************************************
3
KWin - the KDE window manager
4
This file is part of the KDE project.
6
Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org>
8
This program is free software; you can redistribute it and/or modify
9
it under the terms of the GNU General Public License as published by
10
the Free Software Foundation; either version 2 of the License, or
11
(at your option) any later version.
13
This program is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
GNU General Public License for more details.
18
You should have received a copy of the GNU General Public License
19
along with this program. If not, see <http://www.gnu.org/licenses/>.
20
*********************************************************************/
21
#define WL_EGL_PLATFORM 1
22
#include "egl_wayland_backend.h"
24
#include "composite.h"
26
#include "wayland_backend.h"
27
#include "wayland_server.h"
28
#include <KWayland/Client/surface.h>
30
#include <kwinglplatform.h>
32
#include <KWayland/Server/buffer_interface.h>
33
#include <KWayland/Server/display.h>
35
#include <QOpenGLContext>
40
EglWaylandBackend::EglWaylandBackend(Wayland::WaylandBackend *b)
42
, AbstractEglBackend()
48
setFailed("Wayland Backend has not been created");
51
qCDebug(KWIN_CORE) << "Connected to Wayland display?" << (m_wayland->display() ? "yes" : "no" );
52
if (!m_wayland->display()) {
53
setFailed("Could not connect to Wayland compositor");
56
connect(m_wayland, SIGNAL(shellSurfaceSizeChanged(QSize)), SLOT(overlaySizeChanged(QSize)));
59
// Egl is always direct rendering
60
setIsDirectRendering(true);
62
qCWarning(KWIN_CORE) << "Using Wayland rendering backend";
63
qCWarning(KWIN_CORE) << "This is a highly experimental backend, do not use for productive usage!";
64
qCWarning(KWIN_CORE) << "Please do not report any issues you might encounter when using this backend!";
67
EglWaylandBackend::~EglWaylandBackend()
71
wl_egl_window_destroy(m_overlay);
75
bool EglWaylandBackend::initializeEgl()
77
initClientExtensions();
78
EGLDisplay display = EGL_NO_DISPLAY;
80
// Use eglGetPlatformDisplayEXT() to get the display pointer
81
// if the implementation supports it.
82
m_havePlatformBase = hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base"));
83
if (m_havePlatformBase) {
84
// Make sure that the wayland platform is supported
85
if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_wayland")))
88
display = eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, m_wayland->display(), nullptr);
90
display = eglGetDisplay(m_wayland->display());
93
if (display == EGL_NO_DISPLAY)
95
setEglDisplay(display);
99
void EglWaylandBackend::init()
101
if (!initRenderingContext()) {
102
setFailed("Could not initialize rendering context");
111
bool EglWaylandBackend::initRenderingContext()
115
EGLContext context = EGL_NO_CONTEXT;
116
#ifdef KWIN_HAVE_OPENGLES
117
const EGLint context_attribs[] = {
118
EGL_CONTEXT_CLIENT_VERSION, 2,
122
context = eglCreateContext(eglDisplay(), config(), EGL_NO_CONTEXT, context_attribs);
124
const EGLint context_attribs_31_core[] = {
125
EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
126
EGL_CONTEXT_MINOR_VERSION_KHR, 1,
127
EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR,
131
const EGLint context_attribs_legacy[] = {
135
const char* eglExtensionsCString = eglQueryString(eglDisplay(), EGL_EXTENSIONS);
136
const QList<QByteArray> extensions = QByteArray::fromRawData(eglExtensionsCString, qstrlen(eglExtensionsCString)).split(' ');
138
// Try to create a 3.1 core context
139
if (options->glCoreProfile() && extensions.contains(QByteArrayLiteral("EGL_KHR_create_context")))
140
context = eglCreateContext(eglDisplay(), config(), EGL_NO_CONTEXT, context_attribs_31_core);
142
if (context == EGL_NO_CONTEXT)
143
context = eglCreateContext(eglDisplay(), config(), EGL_NO_CONTEXT, context_attribs_legacy);
146
if (context == EGL_NO_CONTEXT) {
147
qCCritical(KWIN_CORE) << "Create Context failed";
152
if (!m_wayland->surface()) {
156
const QSize &size = m_wayland->shellSurfaceSize();
157
auto s = m_wayland->surface();
158
connect(s, &KWayland::Client::Surface::frameRendered, Compositor::self(), &Compositor::bufferSwapComplete);
159
m_overlay = wl_egl_window_create(*s, size.width(), size.height());
161
qCCritical(KWIN_CORE) << "Creating Wayland Egl window failed";
165
EGLSurface surface = EGL_NO_SURFACE;
166
if (m_havePlatformBase)
167
surface = eglCreatePlatformWindowSurfaceEXT(eglDisplay(), config(), (void *) m_overlay, nullptr);
169
surface = eglCreateWindowSurface(eglDisplay(), config(), m_overlay, nullptr);
171
if (surface == EGL_NO_SURFACE) {
172
qCCritical(KWIN_CORE) << "Create Window Surface failed";
177
return makeContextCurrent();
180
bool EglWaylandBackend::makeContextCurrent()
182
if (eglMakeCurrent(eglDisplay(), surface(), surface(), context()) == EGL_FALSE) {
183
qCCritical(KWIN_CORE) << "Make Context Current failed";
187
EGLint error = eglGetError();
188
if (error != EGL_SUCCESS) {
189
qCWarning(KWIN_CORE) << "Error occurred while creating context " << error;
195
bool EglWaylandBackend::initBufferConfigs()
197
const EGLint config_attribs[] = {
198
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
203
#ifdef KWIN_HAVE_OPENGLES
204
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
206
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
208
EGL_CONFIG_CAVEAT, EGL_NONE,
213
EGLConfig configs[1024];
214
if (eglChooseConfig(eglDisplay(), config_attribs, configs, 1, &count) == EGL_FALSE) {
215
qCCritical(KWIN_CORE) << "choose config failed";
219
qCCritical(KWIN_CORE) << "choose config did not return a config" << count;
222
setConfig(configs[0]);
227
void EglWaylandBackend::present()
229
m_wayland->surface()->setupFrameCallback();
230
Compositor::self()->aboutToSwapBuffers();
232
if (supportsBufferAge()) {
233
eglSwapBuffers(eglDisplay(), surface());
234
eglQuerySurface(eglDisplay(), surface(), EGL_BUFFER_AGE_EXT, &m_bufferAge);
235
setLastDamage(QRegion());
238
eglSwapBuffers(eglDisplay(), surface());
239
setLastDamage(QRegion());
243
void EglWaylandBackend::screenGeometryChanged(const QSize &size)
246
// no backend specific code needed
247
// TODO: base implementation in OpenGLBackend
249
// The back buffer contents are now undefined
253
SceneOpenGL::TexturePrivate *EglWaylandBackend::createBackendTexture(SceneOpenGL::Texture *texture)
255
return new EglWaylandTexture(texture, this);
258
QRegion EglWaylandBackend::prepareRenderingFrame()
260
if (!lastDamage().isEmpty())
263
if (supportsBufferAge())
264
repaint = accumulatedDamageHistory(m_bufferAge);
265
eglWaitNative(EGL_CORE_NATIVE_ENGINE);
270
void EglWaylandBackend::endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
272
if (damagedRegion.isEmpty()) {
273
setLastDamage(QRegion());
275
// If the damaged region of a window is fully occluded, the only
276
// rendering done, if any, will have been to repair a reused back
277
// buffer, making it identical to the front buffer.
279
// In this case we won't post the back buffer. Instead we'll just
280
// set the buffer age to 1, so the repaired regions won't be
281
// rendered again in the next frame.
282
if (!renderedRegion.isEmpty())
289
setLastDamage(renderedRegion);
291
if (!blocksForRetrace()) {
292
// This also sets lastDamage to empty which prevents the frame from
293
// being posted again when prepareRenderingFrame() is called.
296
// Make sure that the GPU begins processing the command stream
297
// now and not the next time prepareRenderingFrame() is called.
301
// Save the damaged region to history
302
if (supportsBufferAge())
303
addToDamageHistory(damagedRegion);
306
void EglWaylandBackend::overlaySizeChanged(const QSize &size)
308
wl_egl_window_resize(m_overlay, size.width(), size.height(), 0, 0);
311
bool EglWaylandBackend::usesOverlayWindow() const
316
/************************************************
318
************************************************/
320
EglWaylandTexture::EglWaylandTexture(KWin::SceneOpenGL::Texture *texture, KWin::EglWaylandBackend *backend)
321
: AbstractEglTexture(texture, backend)
325
EglWaylandTexture::~EglWaylandTexture() = default;