From bab5f16d3c3f25842884c4f2904930061e02d8c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Wed, 15 May 2013 13:47:27 +0200 Subject: [PATCH] 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. --- CMakeLists.txt | 18 ++ compositingprefs.cpp | 3 +- egl_wayland_backend.cpp | 547 ++++++++++++++++++++++++++++++++++++++++ egl_wayland_backend.h | 225 +++++++++++++++++ scene_opengl.cpp | 31 ++- scene_opengl.h | 2 + 6 files changed, 824 insertions(+), 2 deletions(-) create mode 100644 egl_wayland_backend.cpp create mode 100644 egl_wayland_backend.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6570b1aa48..f9d9c21147 100644 --- a/CMakeLists.txt +++ b/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/compositingprefs.cpp b/compositingprefs.cpp index 87a6e91f36..f46dac455e 100644 --- a/compositingprefs.cpp +++ b/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/egl_wayland_backend.cpp b/egl_wayland_backend.cpp new file mode 100644 index 0000000000..1d49f8974c --- /dev/null +++ b/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 + +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 . +*********************************************************************/ +#define WL_EGL_PLATFORM 1 +#include "egl_wayland_backend.h" +// kwin +#include "options.h" +// kwin libs +#include +// KDE +#include +// system +#include + +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(data); + + if (strcmp(interface, "wl_compositor") == 0) { + d->setCompositor(reinterpret_cast(wl_registry_bind(registry, name, &wl_compositor_interface, 1))); + } else if (strcmp(interface, "wl_shell") == 0) { + d->setShell(reinterpret_cast(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(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 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 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 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 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(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 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/egl_wayland_backend.h b/egl_wayland_backend.h new file mode 100644 index 0000000000..711ad1d576 --- /dev/null +++ b/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 + +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 . +*********************************************************************/ +#ifndef KWIN_EGL_WAYLAND_BACKEND_H +#define KWIN_EGL_WAYLAND_BACKEND_H +#include "scene_opengl.h" +// wayland +#include +#include +// xcb +#include + +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 m_wayland; + QScopedPointer 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/scene_opengl.cpp b/scene_opengl.cpp index 4201136d87..2f54321546 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -24,6 +24,11 @@ along with this program. If not, see . #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,7 +201,15 @@ 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: @@ -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/scene_opengl.h b/scene_opengl.h index 043ef114ba..33a59b93fc 100644 --- a/scene_opengl.h +++ b/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();