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.
This commit is contained in:
Martin Gräßlin 2013-05-15 13:47:27 +02:00
parent 0063e2ed2d
commit bab5f16d3c
6 changed files with 824 additions and 2 deletions

View file

@ -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})

View file

@ -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.

547
egl_wayland_backend.cpp Normal file
View file

@ -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

225
egl_wayland_backend.h Normal file
View file

@ -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

View file

@ -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,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;
}

View file

@ -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();