[wayland] Support creating Texture from Wayland shm buffer

EglWaylandBackend gains support for creating textures from a
BufferInterface. At the same time it loses the possibility to use
the Xcb shm extension to load the texture. That is Xwayland is
required.

In order to support it in a better way the WindowPixmap is passed
to the Texture for loading and updating. Which is then passed to the
backend specific implementation.
icc-effect-5.14.5
Martin Gräßlin 2015-02-11 11:00:12 +01:00
parent 19d90e4e0e
commit a0b2a938aa
9 changed files with 90 additions and 90 deletions

View File

@ -24,10 +24,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "options.h"
#include "wayland_backend.h"
#include <KWayland/Client/surface.h>
#include "xcbutils.h"
// kwin libs
#include <kwinglplatform.h>
// KDE
#include <KWayland/Server/buffer_interface.h>
// Qt
#include <QOpenGLContext>
@ -355,14 +355,6 @@ void EglWaylandBackend::doneCurrent()
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
Xcb::Shm *EglWaylandBackend::shm()
{
if (m_shm.isNull()) {
m_shm.reset(new Xcb::Shm);
}
return m_shm.data();
}
void EglWaylandBackend::overlaySizeChanged(const QSize &size)
{
wl_egl_window_resize(m_overlay, size.width(), size.height(), 0, 0);
@ -381,7 +373,6 @@ EglWaylandTexture::EglWaylandTexture(KWin::SceneOpenGL::Texture *texture, KWin::
: SceneOpenGL::TexturePrivate()
, q(texture)
, m_backend(backend)
, m_referencedPixmap(XCB_PIXMAP_NONE)
{
m_target = GL_TEXTURE_2D;
}
@ -395,42 +386,42 @@ OpenGLBackend *EglWaylandTexture::backend()
return m_backend;
}
bool EglWaylandTexture::loadTexture(xcb_pixmap_t pix, const QSize &size, xcb_visualid_t visual)
bool EglWaylandTexture::loadTexture(WindowPixmap *pixmap)
{
Q_UNUSED(visual)
// 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.
if (pix == XCB_PIXMAP_NONE)
return false;
m_referencedPixmap = pix;
Xcb::Shm *shm = m_backend->shm();
if (!shm->isValid()) {
if (GLPlatform::instance()->isGLES()) {
// FIXME
return false;
}
const auto &buffer = pixmap->buffer();
if (buffer.isNull()) {
return false;
}
const QImage &image = buffer->data();
if (image.isNull()) {
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()) {
const QSize &size = image.size();
// TODO: this should be shared with GLTexture(const QImage&, GLenum)
GLenum format = 0;
switch (image.format()) {
case QImage::Format_ARGB32:
case QImage::Format_ARGB32_Premultiplied:
format = GL_RGBA8;
break;
case QImage::Format_RGB32:
format = GL_RGB8;
break;
default:
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
glTexImage2D(m_target, 0, format, size.width(), size.height(), 0,
GL_BGRA, GL_UNSIGNED_BYTE, image.bits());
q->unbind();
q->setYInverted(true);
@ -439,37 +430,32 @@ bool EglWaylandTexture::loadTexture(xcb_pixmap_t pix, const QSize &size, xcb_vis
return true;
}
bool EglWaylandTexture::update(const QRegion &damage)
void EglWaylandTexture::updateTexture(WindowPixmap *pixmap)
{
if (m_referencedPixmap == XCB_PIXMAP_NONE) {
return false;
if (GLPlatform::instance()->isGLES()) {
// FIXME
return;
}
Xcb::Shm *shm = m_backend->shm();
if (!shm->isValid()) {
return false;
const auto &buffer = pixmap->buffer();
if (buffer.isNull()) {
return;
}
// 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));
const QImage &image = buffer->data();
if (image.isNull()) {
return false;
return;
}
Q_ASSERT(image.size() == m_size);
q->bind();
const QRegion &damage = pixmap->toplevel()->damage();
// 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
// TODO: this should be shared with GLTexture::update
const QImage im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
for (const QRect &rect : damage.rects()) {
glTexSubImage2D(m_target, 0, rect.x(), rect.y(), rect.width(), rect.height(),
GL_BGRA, GL_UNSIGNED_BYTE, im.copy(rect).bits());
}
q->unbind();
return true;
}
} // namespace

View File

@ -34,10 +34,6 @@ namespace Wayland {
class WaylandBackend;
}
namespace Xcb {
class Shm;
}
/**
* @brief OpenGL Backend using Egl on a Wayland surface.
*
@ -46,10 +42,6 @@ namespace Xcb {
* 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.
@ -66,7 +58,6 @@ public:
virtual void endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion);
virtual bool makeCurrent() override;
virtual void doneCurrent() override;
Xcb::Shm *shm();
virtual bool usesOverlayWindow() const override;
protected:
@ -88,7 +79,6 @@ private:
int m_bufferAge;
Wayland::WaylandBackend *m_wayland;
wl_egl_window *m_overlay;
QScopedPointer<Xcb::Shm> m_shm;
bool m_havePlatformBase;
friend class EglWaylandTexture;
};
@ -100,19 +90,15 @@ class EglWaylandTexture : public SceneOpenGL::TexturePrivate
{
public:
virtual ~EglWaylandTexture();
virtual bool loadTexture(xcb_pixmap_t pix, const QSize &size, xcb_visualid_t visual) override;
virtual bool loadTexture(WindowPixmap *pixmap) override;
virtual void updateTexture(WindowPixmap *pixmap) override;
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;
};
} // namespace

View File

@ -496,10 +496,8 @@ OpenGLBackend *EglTexture::backend()
return m_backend;
}
bool EglTexture::loadTexture(xcb_pixmap_t pix, const QSize &size, xcb_visualid_t visual)
bool EglTexture::loadTexture(xcb_pixmap_t pix, const QSize &size)
{
Q_UNUSED(visual)
if (pix == XCB_NONE)
return false;
@ -528,6 +526,11 @@ bool EglTexture::loadTexture(xcb_pixmap_t pix, const QSize &size, xcb_visualid_t
return true;
}
bool EglTexture::loadTexture(WindowPixmap *pixmap)
{
return loadTexture(pixmap->pixmap(), pixmap->toplevel()->size());
}
void KWin::EglTexture::onDamage()
{
if (options->isGlStrictBinding()) {

View File

@ -69,12 +69,13 @@ class EglTexture : public SceneOpenGL::TexturePrivate
public:
virtual ~EglTexture();
virtual void onDamage();
virtual bool loadTexture(xcb_pixmap_t pix, const QSize &size, xcb_visualid_t visual) override;
virtual bool loadTexture(WindowPixmap *pixmap) override;
virtual OpenGLBackend *backend();
private:
friend class EglOnXBackend;
EglTexture(SceneOpenGL::Texture *texture, EglOnXBackend *backend);
bool loadTexture(xcb_pixmap_t pix, const QSize &size);
SceneOpenGL::Texture *q;
EglOnXBackend *m_backend;
EGLImageKHR m_image;

View File

@ -858,6 +858,12 @@ bool GlxTexture::loadTexture(xcb_pixmap_t pixmap, const QSize &size, xcb_visuali
return true;
}
bool GlxTexture::loadTexture(WindowPixmap *pixmap)
{
Toplevel *t = pixmap->toplevel();
return loadTexture(pixmap->pixmap(), t->size(), t->visual());
}
OpenGLBackend *GlxTexture::backend()
{
return m_backend;

View File

@ -116,12 +116,13 @@ class GlxTexture : public SceneOpenGL::TexturePrivate
public:
virtual ~GlxTexture();
virtual void onDamage();
virtual bool loadTexture(xcb_pixmap_t pix, const QSize &size, xcb_visualid_t visual) override;
virtual bool loadTexture(WindowPixmap *pixmap) override;
virtual OpenGLBackend *backend();
private:
friend class GlxBackend;
GlxTexture(SceneOpenGL::Texture *texture, GlxBackend *backend);
bool loadTexture(xcb_pixmap_t pix, const QSize &size, xcb_visualid_t visual);
SceneOpenGL::Texture *q;
GlxBackend *m_backend;
GLXPixmap m_glxpixmap; // the glx pixmap the texture is bound to

View File

@ -386,14 +386,14 @@ public:
* contentsRect tells where inside the complete pixmap the real content is.
*/
const QRect &contentsRect() const;
protected:
explicit WindowPixmap(Scene::Window *window);
/**
* @brief Returns the Toplevel this WindowPixmap belongs to.
* Note: the Toplevel can change over the lifetime of the WindowPixmap in case the Toplevel is copied to Deleted.
*/
Toplevel *toplevel();
protected:
explicit WindowPixmap(Scene::Window *window);
/**
* @return The Window this WindowPixmap belongs to
*/

View File

@ -1140,17 +1140,23 @@ void SceneOpenGL::Texture::discard()
d_ptr = d_func()->backend()->createBackendTexture(this);
}
bool SceneOpenGL::Texture::load(xcb_pixmap_t pix, const QSize &size,
xcb_visualid_t visual)
bool SceneOpenGL::Texture::load(WindowPixmap *pixmap)
{
if (pix == XCB_NONE)
if (!pixmap->isValid()) {
return false;
}
// decrease the reference counter for the old texture
d_ptr = d_func()->backend()->createBackendTexture(this); //new TexturePrivate();
Q_D(Texture);
return d->loadTexture(pix, size, visual);
return d->loadTexture(pixmap);
}
void SceneOpenGL::Texture::updateFromPixmap(WindowPixmap *pixmap)
{
Q_D(Texture);
d->updateTexture(pixmap);
}
//****************************************
@ -1164,6 +1170,11 @@ SceneOpenGL::TexturePrivate::~TexturePrivate()
{
}
void SceneOpenGL::TexturePrivate::updateTexture(WindowPixmap *pixmap)
{
Q_UNUSED(pixmap)
}
//****************************************
// SceneOpenGL::Window
//****************************************
@ -1579,6 +1590,10 @@ bool OpenGLWindowPixmap::bind()
{
if (!m_texture->isNull()) {
if (!toplevel()->damage().isEmpty()) {
#if HAVE_WAYLAND
updateBuffer();
m_texture->updateFromPixmap(this);
#endif
// mipmaps need to be updated
m_texture->setDirty();
toplevel()->resetDamage();
@ -1589,7 +1604,7 @@ bool OpenGLWindowPixmap::bind()
return false;
}
bool success = m_texture->load(pixmap(), toplevel()->size(), toplevel()->visual());
bool success = m_texture->load(this);
if (success)
toplevel()->resetDamage();

View File

@ -157,7 +157,8 @@ class SceneOpenGL::TexturePrivate
public:
virtual ~TexturePrivate();
virtual bool loadTexture(xcb_pixmap_t pix, const QSize &size, xcb_visualid_t visual) = 0;
virtual bool loadTexture(WindowPixmap *pixmap) = 0;
virtual void updateTexture(WindowPixmap *pixmap);
virtual OpenGLBackend *backend() = 0;
protected:
@ -179,7 +180,8 @@ public:
void discard() override final;
protected:
bool load(xcb_pixmap_t pix, const QSize &size, xcb_visualid_t);
bool load(WindowPixmap *pixmap);
void updateFromPixmap(WindowPixmap *pixmap);
Texture(TexturePrivate& dd);