[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [kde-workspace] kwin: Egl Backend using a Wayland surface for rendering
From: Martin_Gräßlin <mgraesslin () kde ! org>
Date: 2013-06-05 11:47:37
Message-ID: 20130605114737.5CAB3A605A () git ! kde ! org
[Download RAW message or body]
Git commit 9cc9fa844915db2773486f55c54fbc36cb891bd4 by Martin Gräßlin.
Committed on 15/05/2013 at 13:47.
Pushed by graesslin into branch 'master'.
Egl Backend using a Wayland surface for rendering
This backend is able to composite on a Wayland surface instead of an X11
overlay window. It can be considered as a prototype for a Wayland session
compositor.
For texture from X11 pixmap the backend uses XShm. This is far from
optimal, but the KHR_image_pixmap extension is not available in Mesa's
Wayland backend. It's a temporary solution till we have XWayland and
texture from Wayland buffer.
To use this backend one needs to specify the environment variable
KWIN_OPENGL_INTERFACE with "egl_wayland". In future KWin should probably
use this backend if the Wayland display env variable is defined.
To use this setup:
1. Have a normal X-Server running on e.g. VT7
2. Start Weston on VT1
3. Start a terminal on Weston
4. start KWin with:
DISPLAY=:0 KWIN_OPENGL_INTERFACE=egl_wayland kwin --replace &
This should map a Wayland surface to Weston showing the content of the X
setup. At the moment it's not yet possible to interact with the surface
as input events are not yet recieved in the backend.
There are still a lot of limitations as documented in the code.
M +18 -0 kwin/CMakeLists.txt
M +2 -1 kwin/compositingprefs.cpp
A +547 -0 kwin/egl_wayland_backend.cpp [License: GPL (v2)]
A +225 -0 kwin/egl_wayland_backend.h [License: GPL (v2)]
M +30 -1 kwin/scene_opengl.cpp
M +2 -0 kwin/scene_opengl.h
http://commits.kde.org/kde-workspace/9cc9fa844915db2773486f55c54fbc36cb891bd4
diff --git a/kwin/CMakeLists.txt b/kwin/CMakeLists.txt
index 6570b1a..f9d9c21 100644
--- a/kwin/CMakeLists.txt
+++ b/kwin/CMakeLists.txt
@@ -54,6 +54,10 @@ endif()
include_directories(${XCB_INCLUDE_DIR})
+if(WAYLAND_FOUND)
+ include_directories(${WAYLAND_INCLUDE_DIR})
+endif()
+
# for things that are also used by kwin libraries
configure_file(libkwineffects/kwinconfig.h.cmake \
${CMAKE_CURRENT_BINARY_DIR}/libkwineffects/kwinconfig.h ) # for kwin internal things
@@ -192,6 +196,10 @@ if(KWIN_HAVE_EGL)
set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} eglonxbackend.cpp)
endif()
+if(WAYLAND_FOUND)
+ set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} egl_wayland_backend.cpp)
+endif()
+
kde4_add_kcfg_files(kwin_KDEINIT_SRCS settings.kcfgc)
qt4_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.KWin.xml dbusinterface.h \
KWin::DBusInterface ) @@ -248,6 +256,12 @@ set(kwin_XCB_LIBS
${XCB_ICCCM_LIBRARIES}
)
+set(kwin_WAYLAND_LIBS
+ ${WAYLAND_CLIENT_LIBRARIES}
+ ${WAYLAND_EGL_LIBRARIES}
+ ${XCB_SHM_LIBRARIES}
+)
+
set(kwin_OPENGL_LIBS )
find_library(XF86VM_LIBRARY Xxf86vm)
@@ -278,6 +292,10 @@ set(kwinLibs
${kwin_OPENGL_LIBS}
)
+if(WAYLAND_FOUND)
+ set(kwinLibs ${kwinLibs} ${kwin_WAYLAND_LIBS})
+endif()
+
kde4_add_kdeinit_executable( kwin ${kwin_KDEINIT_SRCS})
target_link_libraries(kdeinit_kwin ${kwinLibs})
diff --git a/kwin/compositingprefs.cpp b/kwin/compositingprefs.cpp
index 87a6e91..f46dac4 100644
--- a/kwin/compositingprefs.cpp
+++ b/kwin/compositingprefs.cpp
@@ -139,7 +139,8 @@ void CompositingPrefs::detect()
#ifndef KWIN_HAVE_OPENGLES
// HACK: This is needed for AIGLX
const bool forceIndirect = qstrcmp(qgetenv("LIBGL_ALWAYS_INDIRECT"), "1") == 0;
- const bool forceEgl = qstrcmp(qgetenv("KWIN_OPENGL_INTERFACE"), "egl") == 0;
+ const bool forceEgl = qstrcmp(qgetenv("KWIN_OPENGL_INTERFACE"), "egl") == 0 ||
+ qstrcmp(qgetenv("KWIN_OPENGL_INTERFACE"), "egl_wayland") == 0;
if (!forceIndirect && !forceEgl && qstrcmp(qgetenv("KWIN_DIRECT_GL"), "1") != 0) \
{
// Start an external helper program that initializes GLX and returns
// 0 if we can use direct rendering, and 1 otherwise.
diff --git a/kwin/egl_wayland_backend.cpp b/kwin/egl_wayland_backend.cpp
new file mode 100644
index 0000000..1d49f89
--- /dev/null
+++ b/kwin/egl_wayland_backend.cpp
@@ -0,0 +1,547 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+#define WL_EGL_PLATFORM 1
+#include "egl_wayland_backend.h"
+// kwin
+#include "options.h"
+// kwin libs
+#include <kwinglplatform.h>
+// KDE
+#include <KDE/KDebug>
+// system
+#include <sys/shm.h>
+
+namespace KWin
+{
+
+namespace Wayland
+{
+
+/**
+ * Callback for announcing global objects in the registry
+ **/
+static void registryHandleGlobal(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t \
version) +{
+ Q_UNUSED(version)
+ WaylandBackend *d = reinterpret_cast<WaylandBackend*>(data);
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ d->setCompositor(reinterpret_cast<wl_compositor*>(wl_registry_bind(registry, \
name, &wl_compositor_interface, 1))); + } else if (strcmp(interface, "wl_shell") \
== 0) { + d->setShell(reinterpret_cast<wl_shell *>(wl_registry_bind(registry, \
name, &wl_shell_interface, 1))); + }
+ kDebug(1212) << "Wayland Interface: " << interface;
+}
+
+/**
+ * Callback for removal of global objects in the registry
+ **/
+static void registryHandleGlobalRemove(void *data, struct wl_registry *registry, \
uint32_t name) +{
+ Q_UNUSED(data)
+ Q_UNUSED(registry)
+ Q_UNUSED(name)
+ // TODO: implement me
+}
+
+/**
+ * Call back for ping from Wayland Shell.
+ **/
+static void handlePing(void *data, struct wl_shell_surface *shellSurface, uint32_t \
serial) +{
+ Q_UNUSED(data)
+ wl_shell_surface_pong(shellSurface, serial);
+}
+
+/**
+ * Callback for a configure request for a shell surface
+ **/
+static void handleConfigure(void *data, struct wl_shell_surface *shellSurface, \
uint32_t edges, int32_t width, int32_t height) +{
+ Q_UNUSED(shellSurface)
+ Q_UNUSED(edges)
+ WaylandBackend *display = reinterpret_cast<WaylandBackend*>(data);
+ wl_egl_window_resize(display->overlay(), width, height, 0, 0);
+ // TODO: this information should probably go into Screens
+}
+
+/**
+ * Callback for popups - not needed, we don't have popups
+ **/
+static void handlePopupDone(void *data, struct wl_shell_surface *shellSurface)
+{
+ Q_UNUSED(data)
+ Q_UNUSED(shellSurface)
+}
+
+// handlers
+static const struct wl_registry_listener s_registryListener = {
+ registryHandleGlobal,
+ registryHandleGlobalRemove
+};
+
+static const struct wl_shell_surface_listener s_shellSurfaceListener = {
+ handlePing,
+ handleConfigure,
+ handlePopupDone
+};
+
+WaylandBackend::WaylandBackend()
+ : m_display(wl_display_connect(NULL))
+ , m_registry(wl_display_get_registry(m_display))
+ , m_compositor(NULL)
+ , m_shell(NULL)
+ , m_surface(NULL)
+ , m_overlay(NULL)
+ , m_shellSurface(NULL)
+{
+ kDebug(1212) << "Created Wayland display";
+ // setup the registry
+ wl_registry_add_listener(m_registry, &s_registryListener, this);
+ wl_display_dispatch(m_display);
+}
+
+WaylandBackend::~WaylandBackend()
+{
+ if (m_overlay) {
+ wl_egl_window_destroy(m_overlay);
+ }
+ if (m_shellSurface) {
+ wl_shell_surface_destroy(m_shellSurface);
+ }
+ if (m_surface) {
+ wl_surface_destroy(m_surface);
+ }
+ if (m_shell) {
+ wl_shell_destroy(m_shell);
+ }
+ if (m_compositor) {
+ wl_compositor_destroy(m_compositor);
+ }
+ if (m_registry) {
+ wl_registry_destroy(m_registry);
+ }
+ if (m_display) {
+ wl_display_flush(m_display);
+ wl_display_disconnect(m_display);
+ }
+ kDebug(1212) << "Destroyed Wayland display";
+}
+
+bool WaylandBackend::createSurface()
+{
+ m_surface = wl_compositor_create_surface(m_compositor);
+ if (!m_surface) {
+ kError(1212) << "Creating Wayland Surface failed";
+ return false;
+ }
+ // map the surface as fullscreen
+ m_shellSurface = wl_shell_get_shell_surface(m_shell, m_surface);
+ wl_shell_surface_add_listener(m_shellSurface, &s_shellSurfaceListener, this);
+
+ // TODO: do something better than displayWidth/displayHeight
+ m_overlay = wl_egl_window_create(m_surface, displayWidth(), displayHeight());
+ if (!m_overlay) {
+ kError(1212) << "Creating Wayland Egl window failed";
+ return false;
+ }
+// wl_shell_surface_set_fullscreen(m_shellSurface, \
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, NULL); + \
wl_shell_surface_set_toplevel(m_shellSurface); + handleConfigure(this, \
m_shellSurface, 0, displayWidth(), displayHeight()); +
+ return true;
+}
+
+}
+
+EglWaylandBackend::EglWaylandBackend()
+ : OpenGLBackend()
+ , m_context(EGL_NO_CONTEXT)
+ , m_wayland(new Wayland::WaylandBackend)
+{
+ kDebug(1212) << "Connected to Wayland display?" << (m_wayland->display() ? "yes" \
: "no" ); + if (!m_wayland->display()) {
+ setFailed("Could not connect to Wayland compositor");
+ return;
+ }
+ initializeEgl();
+ init();
+ // Egl is always direct rendering
+ setIsDirectRendering(true);
+}
+
+EglWaylandBackend::~EglWaylandBackend()
+{
+ cleanupGL();
+ eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroyContext(m_display, m_context);
+ eglDestroySurface(m_display, m_surface);
+ eglTerminate(m_display);
+ eglReleaseThread();
+}
+
+bool EglWaylandBackend::initializeEgl()
+{
+ m_display = eglGetDisplay(m_wayland->display());
+ if (m_display == EGL_NO_DISPLAY)
+ return false;
+
+ EGLint major, minor;
+ if (eglInitialize(m_display, &major, &minor) == EGL_FALSE)
+ return false;
+ EGLint error = eglGetError();
+ if (error != EGL_SUCCESS) {
+ kWarning(1212) << "Error during eglInitialize " << error;
+ return false;
+ }
+ kDebug(1212) << "Egl Initialize succeeded";
+
+#ifdef KWIN_HAVE_OPENGLES
+ eglBindAPI(EGL_OPENGL_ES_API);
+#else
+ if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
+ kError(1212) << "bind OpenGL API failed";
+ return false;
+ }
+#endif
+ kDebug(1212) << "EGL version: " << major << "." << minor;
+ return true;
+}
+
+void EglWaylandBackend::init()
+{
+ if (!initRenderingContext()) {
+ setFailed("Could not initialize rendering context");
+ return;
+ }
+
+ initEGL();
+ GLPlatform *glPlatform = GLPlatform::instance();
+ glPlatform->detect(EglPlatformInterface);
+ glPlatform->printResults();
+ initGL(EglPlatformInterface);
+}
+
+bool EglWaylandBackend::initRenderingContext()
+{
+ initBufferConfigs();
+
+#ifdef KWIN_HAVE_OPENGLES
+ const EGLint context_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, \
context_attribs); +#else
+ const EGLint context_attribs_31_core[] = {
+ EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
+ EGL_CONTEXT_MINOR_VERSION_KHR, 1,
+ EGL_CONTEXT_FLAGS_KHR, \
EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR, + EGL_NONE
+ };
+
+ const EGLint context_attribs_legacy[] = {
+ EGL_NONE
+ };
+
+ const QByteArray eglExtensions = eglQueryString(m_display, EGL_EXTENSIONS);
+ const QList<QByteArray> extensions = eglExtensions.split(' ');
+
+ // Try to create a 3.1 core context
+ if (options->glCoreProfile() && extensions.contains("EGL_KHR_create_context"))
+ m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, \
context_attribs_31_core); +
+ if (m_context == EGL_NO_CONTEXT)
+ m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, \
context_attribs_legacy); +#endif
+
+ if (m_context == EGL_NO_CONTEXT) {
+ kError(1212) << "Create Context failed";
+ return false;
+ }
+
+ if (!m_wayland->createSurface()) {
+ return false;
+ }
+
+ m_surface = eglCreateWindowSurface(m_display, m_config, m_wayland->overlay(), \
NULL); + if (m_surface == EGL_NO_SURFACE) {
+ kError(1212) << "Create Window Surface failed";
+ return false;
+ }
+
+ return makeContextCurrent();
+}
+
+bool EglWaylandBackend::makeContextCurrent()
+{
+ if (eglMakeCurrent(m_display, m_surface, m_surface, m_context) == EGL_FALSE) {
+ kError(1212) << "Make Context Current failed";
+ return false;
+ }
+
+ EGLint error = eglGetError();
+ if (error != EGL_SUCCESS) {
+ kWarning(1212) << "Error occurred while creating context " << error;
+ return false;
+ }
+ return true;
+}
+
+bool EglWaylandBackend::initBufferConfigs()
+{
+ const EGLint config_attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 1,
+ EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1,
+ EGL_ALPHA_SIZE, 0,
+#ifdef KWIN_HAVE_OPENGLES
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+#else
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
+#endif
+ EGL_CONFIG_CAVEAT, EGL_NONE,
+ EGL_NONE,
+ };
+
+ EGLint count;
+ EGLConfig configs[1024];
+ if (eglChooseConfig(m_display, config_attribs, configs, 1, &count) == EGL_FALSE) \
{ + kError(1212) << "choose config failed";
+ return false;
+ }
+ if (count != 1) {
+ kError(1212) << "choose config did not return a config" << count;
+ return false;
+ }
+ m_config = configs[0];
+
+ return true;
+}
+
+void EglWaylandBackend::present()
+{
+ setLastDamage(QRegion());
+ wl_display_dispatch_pending(m_wayland->display());
+ wl_display_flush(m_wayland->display());
+ eglSwapBuffers(m_display, m_surface);
+ eglWaitGL();
+}
+
+void EglWaylandBackend::screenGeometryChanged(const QSize &size)
+{
+ Q_UNUSED(size)
+ // no backend specific code needed
+ // TODO: base implementation in OpenGLBackend
+}
+
+SceneOpenGL::TexturePrivate \
*EglWaylandBackend::createBackendTexture(SceneOpenGL::Texture *texture) +{
+ return new EglWaylandTexture(texture, this);
+}
+
+void EglWaylandBackend::prepareRenderingFrame()
+{
+ if (!lastDamage().isEmpty())
+ present();
+ eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+ startRenderTimer();
+}
+
+void EglWaylandBackend::endRenderingFrame(const QRegion &damage)
+{
+ setLastDamage(damage);
+ glFlush();
+}
+
+Shm *EglWaylandBackend::shm()
+{
+ if (m_shm.isNull()) {
+ m_shm.reset(new Shm);
+ }
+ return m_shm.data();
+}
+
+/************************************************
+ * EglTexture
+ ************************************************/
+
+EglWaylandTexture::EglWaylandTexture(KWin::SceneOpenGL::Texture *texture, \
KWin::EglWaylandBackend *backend) + : SceneOpenGL::TexturePrivate()
+ , q(texture)
+ , m_backend(backend)
+ , m_referencedPixmap(XCB_PIXMAP_NONE)
+{
+ m_target = GL_TEXTURE_2D;
+}
+
+EglWaylandTexture::~EglWaylandTexture()
+{
+}
+
+OpenGLBackend *EglWaylandTexture::backend()
+{
+ return m_backend;
+}
+
+void EglWaylandTexture::findTarget()
+{
+ if (m_target != GL_TEXTURE_2D) {
+ m_target = GL_TEXTURE_2D;
+ }
+}
+
+bool EglWaylandTexture::loadTexture(const Pixmap &pix, const QSize &size, int depth)
+{
+ // HACK: egl wayland platform doesn't support texture from X11 pixmap through \
the KHR_image_pixmap + // extension. To circumvent this problem we copy the pixmap \
content into a SHM image and from there + // to the OpenGL texture. This is a \
temporary solution. In future we won't need to get the content + // from X11 \
pixmaps. That's what we have XWayland for to get the content into a nice Wayland \
buffer. + Q_UNUSED(depth)
+ if (pix == XCB_PIXMAP_NONE)
+ return false;
+ m_referencedPixmap = pix;
+
+ Shm *shm = m_backend->shm();
+ if (!shm->isValid()) {
+ return false;
+ }
+
+ xcb_shm_get_image_cookie_t cookie = xcb_shm_get_image_unchecked(connection(), \
pix, 0, 0, size.width(), + size.height(), ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, \
shm->segment(), 0); +
+ glGenTextures(1, &m_texture);
+ q->setWrapMode(GL_CLAMP_TO_EDGE);
+ q->setFilter(GL_LINEAR);
+ q->bind();
+
+ ScopedCPointer<xcb_shm_get_image_reply_t> \
image(xcb_shm_get_image_reply(connection(), cookie, NULL)); + if (image.isNull()) \
{ + return false;
+ }
+
+ // TODO: other formats
+#ifndef KWIN_HAVE_OPENGLES
+ glTexImage2D(m_target, 0, GL_RGBA8, size.width(), size.height(), 0,
+ GL_BGRA, GL_UNSIGNED_BYTE, shm->buffer());
+#endif
+
+ q->unbind();
+ checkGLError("load texture");
+ q->setYInverted(true);
+ m_size = size;
+ updateMatrix();
+ return true;
+}
+
+bool EglWaylandTexture::update(const QRegion &damage)
+{
+ if (m_referencedPixmap == XCB_PIXMAP_NONE) {
+ return false;
+ }
+
+ Shm *shm = m_backend->shm();
+ if (!shm->isValid()) {
+ return false;
+ }
+
+ // TODO: optimize by only updating the damaged areas
+ const QRect &damagedRect = damage.boundingRect();
+ xcb_shm_get_image_cookie_t cookie = xcb_shm_get_image_unchecked(connection(), \
m_referencedPixmap, + damagedRect.x(), damagedRect.y(), damagedRect.width(), \
damagedRect.height(), + ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, shm->segment(), 0);
+
+ q->bind();
+
+ ScopedCPointer<xcb_shm_get_image_reply_t> \
image(xcb_shm_get_image_reply(connection(), cookie, NULL)); + if (image.isNull()) \
{ + return false;
+ }
+
+ // TODO: other formats
+#ifndef KWIN_HAVE_OPENGLES
+ glTexSubImage2D(m_target, 0, damagedRect.x(), damagedRect.y(), \
damagedRect.width(), damagedRect.height(), GL_BGRA, GL_UNSIGNED_BYTE, shm->buffer()); \
+#endif +
+ q->unbind();
+ checkGLError("update texture");
+ return true;
+}
+
+Shm::Shm()
+ : m_shmId(-1)
+ , m_buffer(NULL)
+ , m_segment(XCB_NONE)
+ , m_valid(false)
+{
+ m_valid = init();
+}
+
+Shm::~Shm()
+{
+ if (m_valid) {
+ xcb_shm_detach(connection(), m_segment);
+ shmdt(m_buffer);
+ }
+}
+
+bool Shm::init()
+{
+ const xcb_query_extension_reply_t *ext = xcb_get_extension_data(connection(), \
&xcb_shm_id); + if (!ext || !ext->present) {
+ kDebug(1212) << "SHM extension not available";
+ return false;
+ }
+ ScopedCPointer<xcb_shm_query_version_reply_t> \
version(xcb_shm_query_version_reply(connection(), + \
xcb_shm_query_version_unchecked(connection()), NULL)); + if (version.isNull()) {
+ kDebug(1212) << "Failed to get SHM extension version information";
+ return false;
+ }
+ const int MAXSIZE = 4096 * 2048 * 4; // TODO check there are not larger windows
+ m_shmId = shmget(IPC_PRIVATE, MAXSIZE, IPC_CREAT | 0600);
+ if (m_shmId < 0) {
+ kDebug(1212) << "Failed to allocate SHM segment";
+ return false;
+ }
+ m_buffer = shmat(m_shmId, NULL, 0 /*read/write*/);
+ if (-1 == reinterpret_cast<long>(m_buffer)) {
+ kDebug(1212) << "Failed to attach SHM segment";
+ shmctl(m_shmId, IPC_RMID, NULL);
+ return false;
+ }
+ shmctl(m_shmId, IPC_RMID, NULL);
+
+ m_segment = xcb_generate_id(connection());
+ const xcb_void_cookie_t cookie = xcb_shm_attach_checked(connection(), m_segment, \
m_shmId, false); + ScopedCPointer<xcb_generic_error_t> \
error(xcb_request_check(connection(), cookie)); + if (!error.isNull()) {
+ kDebug(1212) << "xcb_shm_attach error: " << error->error_code;
+ shmdt(m_buffer);
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
diff --git a/kwin/egl_wayland_backend.h b/kwin/egl_wayland_backend.h
new file mode 100644
index 0000000..711ad1d
--- /dev/null
+++ b/kwin/egl_wayland_backend.h
@@ -0,0 +1,225 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+#ifndef KWIN_EGL_WAYLAND_BACKEND_H
+#define KWIN_EGL_WAYLAND_BACKEND_H
+#include "scene_opengl.h"
+// wayland
+#include <wayland-client.h>
+#include <wayland-egl.h>
+// xcb
+#include <xcb/shm.h>
+
+namespace KWin
+{
+
+namespace Wayland
+{
+/**
+* @brief Class encapsulating all Wayland data structures needed by the Egl backend.
+*
+* It creates the connection to the Wayland Compositor, set's up the registry and \
creates +* the Wayland surface and it's shell and egl mapping.
+*/
+class WaylandBackend
+{
+public:
+ WaylandBackend();
+ virtual ~WaylandBackend();
+ wl_display *display();
+ wl_registry *registry();
+ void setCompositor(wl_compositor *c);
+ wl_compositor *compositor();
+ void setShell(wl_shell *s);
+ wl_shell *shell();
+ wl_egl_window *overlay();
+
+ bool createSurface();
+private:
+ wl_display *m_display;
+ wl_registry *m_registry;
+ wl_compositor *m_compositor;
+ wl_shell *m_shell;
+ wl_surface *m_surface;
+ wl_egl_window *m_overlay;
+ wl_shell_surface *m_shellSurface;
+};
+
+inline
+wl_display *WaylandBackend::display()
+{
+ return m_display;
+}
+
+inline
+wl_registry *WaylandBackend::registry()
+{
+ return m_registry;
+}
+
+inline
+void WaylandBackend::setCompositor(wl_compositor *c)
+{
+ m_compositor = c;
+}
+
+inline
+wl_compositor *WaylandBackend::compositor()
+{
+ return m_compositor;
+}
+
+inline
+wl_egl_window *WaylandBackend::overlay()
+{
+ return m_overlay;
+}
+
+inline
+void WaylandBackend::setShell(wl_shell *s)
+{
+ m_shell = s;
+}
+
+inline
+wl_shell *WaylandBackend::shell()
+{
+ return m_shell;
+}
+
+} // namespace Wayland
+
+class Shm;
+
+/**
+ * @brief OpenGL Backend using Egl on a Wayland surface.
+ *
+ * This Backend is the basis for a session compositor running on top of a Wayland \
system compositor. + * It creates a Surface as large as the screen and maps it as a \
fullscreen shell surface on the + * system compositor. The OpenGL context is created \
on the Wayland surface, so for rendering X11 is + * not involved.
+ *
+ * At the moment the backend is still rather limited. For getting textures from \
pixmap it uses the + * XShm library. This is currently a hack and only as proof of \
concept till we support texture from + * Wayland buffers. From then on we should use \
XWayland for texture mapping. + *
+ * Also in repainting the backend is currently still rather limited. Only supported \
mode is fullscreen + * repaints, which is obviously not optimal. Best solution is \
probably to go for buffer_age extension + * and make it the only available solution \
next to fullscreen repaints. + **/
+class EglWaylandBackend : public OpenGLBackend
+{
+public:
+ EglWaylandBackend();
+ virtual ~EglWaylandBackend();
+ virtual void screenGeometryChanged(const QSize &size);
+ virtual SceneOpenGL::TexturePrivate *createBackendTexture(SceneOpenGL::Texture \
*texture); + virtual void prepareRenderingFrame();
+ virtual void endRenderingFrame(const QRegion &damage);
+ Shm *shm();
+
+protected:
+ virtual void present();
+
+private:
+ void init();
+ bool initializeEgl();
+ bool initBufferConfigs();
+ bool initRenderingContext();
+ bool makeContextCurrent();
+ EGLDisplay m_display;
+ EGLConfig m_config;
+ EGLSurface m_surface;
+ EGLContext m_context;
+ QScopedPointer<Wayland::WaylandBackend> m_wayland;
+ QScopedPointer<Shm> m_shm;
+ friend class EglWaylandTexture;
+};
+
+/**
+ * @brief Texture using an EGLImageKHR.
+ **/
+class EglWaylandTexture : public SceneOpenGL::TexturePrivate
+{
+public:
+ virtual ~EglWaylandTexture();
+ virtual void findTarget();
+ virtual bool loadTexture(const Pixmap& pix, const QSize& size, int depth);
+ virtual OpenGLBackend *backend();
+ virtual bool update(const QRegion &damage);
+
+private:
+ friend class EglWaylandBackend;
+ EglWaylandTexture(SceneOpenGL::Texture *texture, EglWaylandBackend *backend);
+ SceneOpenGL::Texture *q;
+ EglWaylandBackend *m_backend;
+ /**
+ * The Pixmap of the window content. Get's updated in loadTexture.
+ */
+ xcb_pixmap_t m_referencedPixmap;
+};
+
+/**
+ * @brief Small helper class to encapsulate SHM related functionality.
+ *
+ */
+class Shm
+{
+public:
+ Shm();
+ ~Shm();
+ int shmId() const;
+ void *buffer() const;
+ xcb_shm_seg_t segment() const;
+ bool isValid() const;
+private:
+ bool init();
+ int m_shmId;
+ void *m_buffer;
+ xcb_shm_seg_t m_segment;
+ bool m_valid;
+};
+
+inline
+void *Shm::buffer() const
+{
+ return m_buffer;
+}
+
+inline
+bool Shm::isValid() const
+{
+ return m_valid;
+}
+
+inline
+xcb_shm_seg_t Shm::segment() const
+{
+ return m_segment;
+}
+
+inline
+int Shm::shmId() const
+{
+ return m_shmId;
+}
+
+} // namespace
+
+#endif // KWIN_EGL_ON_X_BACKEND_H
diff --git a/kwin/scene_opengl.cpp b/kwin/scene_opengl.cpp
index 4201136..2f54321 100644
--- a/kwin/scene_opengl.cpp
+++ b/kwin/scene_opengl.cpp
@@ -24,6 +24,11 @@ along with this program. If not, see \
<http://www.gnu.org/licenses/>. #include "scene_opengl.h"
#ifdef KWIN_HAVE_EGL
#include "eglonxbackend.h"
+// for Wayland
+#include "config-workspace.h"
+#ifdef WAYLAND_FOUND
+#include "egl_wayland_backend.h"
+#endif
#endif
#ifndef KWIN_HAVE_OPENGLES
#include "glxbackend.h"
@@ -173,13 +178,15 @@ SceneOpenGL *SceneOpenGL::createScene()
platformInterface = GlxPlatformInterface;
#endif
+ const QByteArray envOpenGLInterface(qgetenv("KWIN_OPENGL_INTERFACE"));
#ifdef KWIN_HAVE_EGL
#ifdef KWIN_HAVE_OPENGLES
// for OpenGL ES we need to use the Egl Backend
platformInterface = EglPlatformInterface;
#else
// check environment variable
- if (qstrcmp(qgetenv("KWIN_OPENGL_INTERFACE"), "egl") == 0) {
+ if (qstrcmp(envOpenGLInterface, "egl") == 0 ||
+ qstrcmp(envOpenGLInterface, "egl_wayland") == 0) {
kDebug(1212) << "Forcing EGL native interface through environment variable";
platformInterface = EglPlatformInterface;
}
@@ -194,8 +201,16 @@ SceneOpenGL *SceneOpenGL::createScene()
break;
case EglPlatformInterface:
#ifdef KWIN_HAVE_EGL
+#ifdef WAYLAND_FOUND
+ if (qstrcmp(envOpenGLInterface, "egl_wayland") == 0) {
+ backend = new EglWaylandBackend();
+ } else {
+ backend = new EglOnXBackend();
+ }
+#else
backend = new EglOnXBackend();
#endif
+#endif
break;
default:
// no backend available
@@ -941,6 +956,12 @@ bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& \
size, return d->loadTexture(pix, size, depth);
}
+bool SceneOpenGL::Texture::update(const QRegion &damage)
+{
+ Q_D(Texture);
+ return d->update(damage);
+}
+
//****************************************
// SceneOpenGL::Texture
//****************************************
@@ -952,6 +973,12 @@ SceneOpenGL::TexturePrivate::~TexturePrivate()
{
}
+bool SceneOpenGL::TexturePrivate::update(const QRegion &damage)
+{
+ Q_UNUSED(damage)
+ return true;
+}
+
//****************************************
// SceneOpenGL::Window
//****************************************
@@ -1803,9 +1830,11 @@ bool OpenGLWindowPixmap::bind()
{
if (!m_texture->isNull()) {
if (!toplevel()->damage().isEmpty()) {
+ const bool success = m_texture->update(toplevel()->damage());
// mipmaps need to be updated
m_texture->setDirty();
toplevel()->resetDamage();
+ return success;
}
return true;
}
diff --git a/kwin/scene_opengl.h b/kwin/scene_opengl.h
index 043ef11..33a59b9 100644
--- a/kwin/scene_opengl.h
+++ b/kwin/scene_opengl.h
@@ -173,6 +173,7 @@ public:
virtual void findTarget() = 0;
virtual bool loadTexture(const Pixmap& pix, const QSize& size, int depth) = 0;
virtual OpenGLBackend *backend() = 0;
+ virtual bool update(const QRegion &damage);
protected:
TexturePrivate();
@@ -195,6 +196,7 @@ public:
virtual bool load(const QImage& image, GLenum target = GL_TEXTURE_2D);
virtual bool load(const QPixmap& pixmap, GLenum target = GL_TEXTURE_2D);
virtual void discard();
+ bool update(const QRegion &damage);
protected:
void findTarget();
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic