summaryrefslogtreecommitdiff
path: root/src/mtexturepixmapitem_egl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mtexturepixmapitem_egl.cpp')
-rw-r--r--src/mtexturepixmapitem_egl.cpp446
1 files changed, 446 insertions, 0 deletions
diff --git a/src/mtexturepixmapitem_egl.cpp b/src/mtexturepixmapitem_egl.cpp
new file mode 100644
index 0000000..981e70e
--- /dev/null
+++ b/src/mtexturepixmapitem_egl.cpp
@@ -0,0 +1,446 @@
+/***************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (directui@nokia.com)
+**
+** This file is part of mcompositor.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at directui@nokia.com.
+**
+** This library is free software; you can redistribute it and/or
+** modify it under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation
+** and appearing in the file LICENSE.LGPL included in the packaging
+** of this file.
+**
+****************************************************************************/
+
+#include "mtexturepixmapitem.h"
+#include "mtexturepixmapitem_p.h"
+
+#include <QPainterPath>
+#include <QRect>
+#include <QGLContext>
+#include <QX11Info>
+#include <vector>
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/Xrender.h>
+#include <X11/extensions/Xfixes.h>
+
+#include <GLES2/gl2.h>
+
+/* Emulator doesn't support these configurations:
+ - off screen pixmap
+ - bind to texture
+ */
+
+class EglTextureManager
+{
+public:
+ // TODO: this should be dynamic
+ // use QCache instead like Qt's GL backend
+ static const int sz = 20;
+
+ EglTextureManager() {
+ glGenTextures(sz, tex);
+ for (int i = 0; i < sz; i++)
+ textures.push_back(tex[i]);
+ }
+
+ ~EglTextureManager() {
+ glDeleteTextures(sz, tex);
+ }
+
+ GLuint getTexture() {
+ if (textures.empty()) {
+ qWarning("Empty texture stack");
+ return 0;
+ }
+ GLuint ret = textures.back();
+ textures.pop_back();
+ return ret;
+ }
+
+ void closeTexture(GLuint texture) {
+ // clear this texture
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, 0);
+ textures.push_back(texture);
+ }
+
+ GLuint tex[sz+1];
+ std::vector<GLuint> textures;
+};
+
+class EglResourceManager
+{
+public:
+ EglResourceManager()
+ : has_tfp(false) {
+ if (!dpy)
+ dpy = eglGetDisplay(EGLNativeDisplayType(QX11Info::display()));
+
+ QString exts = QLatin1String(eglQueryString(dpy, EGL_EXTENSIONS));
+ if (exts.contains("EGL_NOKIA_texture_from_pixmap") ||
+ exts.contains("EGL_EXT_texture_from_pixmap"))
+ has_tfp = true;
+ texman = new EglTextureManager();
+ }
+
+ bool texturePixmapSupport() {
+ return has_tfp;
+ }
+
+ EglTextureManager *texman;
+ static EGLConfig config;
+ static EGLConfig configAlpha;
+ static EGLDisplay dpy;
+
+ bool has_tfp;
+};
+
+EglResourceManager *MTexturePixmapPrivate::eglresource = 0;
+EGLConfig EglResourceManager::config = 0;
+EGLConfig EglResourceManager::configAlpha = 0;
+EGLDisplay EglResourceManager::dpy = 0;
+
+void MTexturePixmapItem::init()
+{
+ if (!d->viewable) {
+ qWarning("MTexturePixmapItem::init(): Failed getting offscreen pixmap");
+ return;
+ }
+
+ if (!d->eglresource)
+ d->eglresource = new EglResourceManager();
+
+ d->glpixmap = EGL_NO_SURFACE;
+ d->custom_tfp = !d->eglresource->texturePixmapSupport();
+ if (d->custom_tfp) {
+ initCustomTfp();
+ return;
+ }
+ saveBackingStore();
+ EGLint pixmapAttribs[] = {
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
+ EGL_TEXTURE_FORMAT, d->has_alpha ? EGL_TEXTURE_RGBA : EGL_TEXTURE_RGB,
+ EGL_NONE
+ };
+
+ // We cache the config so we dont have to loop thru it again when
+ // rebinding pixmaps
+ if (!d->eglresource->config || !d->eglresource->configAlpha) {
+ EGLint pixmap_config[] = {
+ EGL_SURFACE_TYPE, EGL_PIXMAP_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_DEPTH_SIZE, 0,
+ d->has_alpha ? EGL_BIND_TO_TEXTURE_RGBA : EGL_BIND_TO_TEXTURE_RGB, EGL_TRUE,
+ EGL_NONE
+ };
+ EGLint ecfgs = 0;
+ EGLConfig configs[20];
+ if (!eglChooseConfig(d->eglresource->dpy, pixmap_config, configs,
+ 20, &ecfgs) || !ecfgs) {
+ qWarning("No appropriate EGL configuration for texture from "
+ "pixmap! Falling back to custom texture to pixmap ");
+ d->custom_tfp = true;
+ initCustomTfp();
+ return;
+ }
+
+ // find best matching configuration for offscreen pixmap
+ for (EGLint i = 0; i < ecfgs; i++) {
+ d->glpixmap = eglCreatePixmapSurface(d->eglresource->dpy, configs[i],
+ (EGLNativePixmapType) d->windowp,
+ pixmapAttribs);
+ if (d->glpixmap == EGL_NO_SURFACE)
+ continue;
+ else {
+ if (d->has_alpha)
+ d->eglresource->configAlpha = configs[i];
+ else
+ d->eglresource->config = configs[i];
+ break;
+ }
+ }
+ } else
+ d->glpixmap = eglCreatePixmapSurface(d->eglresource->dpy,
+ d->has_alpha ? d->eglresource->configAlpha : d->eglresource->config,
+ (EGLNativePixmapType) d->windowp,
+ pixmapAttribs);
+ if (d->glpixmap == EGL_NO_SURFACE)
+ qWarning("MTexturePixmapItem: Cannot create EGL surface: 0x%x",
+ eglGetError());
+
+ d->textureId = d->eglresource->texman->getTexture();
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, d->textureId);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+MTexturePixmapItem::MTexturePixmapItem(Window window, QGLWidget *glwidget, QGraphicsItem *parent)
+ : MCompositeWindow(window, parent),
+ d(new MTexturePixmapPrivate(window, glwidget, this))
+{
+ init();
+}
+
+void MTexturePixmapItem::saveBackingStore(bool renew)
+{
+ d->saveBackingStore(renew);
+}
+
+void MTexturePixmapItem::rebindPixmap()
+{
+ EGLint pixmapAttribs[] = {
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
+ EGL_TEXTURE_FORMAT, d->has_alpha ? EGL_TEXTURE_RGBA : EGL_TEXTURE_RGB,
+ EGL_NONE
+ };
+
+ if (d->glpixmap != EGL_NO_SURFACE) {
+ if (eglReleaseTexImage(d->eglresource->dpy, d->glpixmap, EGL_BACK_BUFFER) == EGL_FALSE)
+ qWarning("Destroy surface: Cant release bound texture");
+ glBindTexture(0, 0);
+ eglSwapBuffers(d->eglresource->dpy, d->glpixmap);
+ eglDestroySurface(d->eglresource->dpy, d->glpixmap);
+ }
+ d->glpixmap = eglCreatePixmapSurface(d->eglresource->dpy, d->has_alpha ? d->eglresource->configAlpha :
+ d->eglresource->config,
+ (EGLNativePixmapType) d->windowp,
+ pixmapAttribs);
+ if (d->glpixmap == EGL_NO_SURFACE)
+ qWarning("MTexturePixmapItem: Cannot renew EGL surface: 0x%x",
+ eglGetError());
+}
+
+void MTexturePixmapItem::enableDirectFbRendering()
+{
+ d->damageTracking(false);
+
+ if (d->direct_fb_render || d->glpixmap == EGL_NO_SURFACE)
+ return;
+
+ d->direct_fb_render = true;
+ // Just free the off-screen surface but re-use the
+ // existing texture id, so don't delete it yet.
+ if (eglReleaseTexImage(d->eglresource->dpy, d->glpixmap, EGL_BACK_BUFFER) == EGL_FALSE)
+ qWarning("MTexturePixmapItem::enableDirectFbRender: "
+ "Cant release bound texture: 0x%x", eglGetError());
+
+ glBindTexture(0, 0);
+ eglSwapBuffers(d->eglresource->dpy, d->glpixmap);
+ eglDestroySurface(d->eglresource->dpy, d->glpixmap);
+ d->glpixmap = EGL_NO_SURFACE;
+ if (d->windowp) {
+ XFreePixmap(QX11Info::display(), d->windowp);
+ d->windowp = 0;
+ }
+ XCompositeUnredirectWindow(QX11Info::display(), window(),
+ CompositeRedirectManual);
+ XSync(QX11Info::display(), FALSE);
+}
+
+void MTexturePixmapItem::enableRedirectedRendering()
+{
+ d->damageTracking(true);
+
+ if (!d->direct_fb_render || d->glpixmap != EGL_NO_SURFACE)
+ return;
+
+ d->direct_fb_render = false;
+ XCompositeRedirectWindow(QX11Info::display(), window(),
+ CompositeRedirectManual);
+ XSync(QX11Info::display(), FALSE);
+ saveBackingStore(true);
+ updateWindowPixmap();
+}
+
+bool MTexturePixmapItem::isDirectRendered() const
+{
+ return d->direct_fb_render;
+}
+
+MTexturePixmapItem::~MTexturePixmapItem()
+{
+ cleanup();
+ delete d;
+}
+
+void MTexturePixmapItem::initCustomTfp()
+{
+ d->ctextureId = d->eglresource->texman->getTexture();
+
+ glBindTexture(GL_TEXTURE_2D, d->ctextureId);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+}
+
+void MTexturePixmapItem::cleanup()
+{
+ eglDestroySurface(d->eglresource->dpy, d->glpixmap);
+ d->glpixmap = EGL_NO_SURFACE;
+ XSync(QX11Info::display(), FALSE);
+
+ if (!d->custom_tfp)
+ d->eglresource->texman->closeTexture(d->textureId);
+ else
+ d->eglresource->texman->closeTexture(d->ctextureId);
+
+#if (QT_VERSION < 0x040600)
+ eglMakeCurrent(d->eglresource->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+#endif
+
+ XFreePixmap(QX11Info::display(), d->windowp);
+}
+
+void MTexturePixmapItem::updateWindowPixmap(XRectangle *rects, int num)
+{
+ if (isTransitioning() || d->direct_fb_render || !windowVisible())
+ return;
+
+ QRegion r;
+ for (int i = 0; i < num; ++i)
+ r += QRegion(rects[i].x, rects[i].y, rects[i].width, rects[i].height);
+ d->damageRegion = r;
+
+ // Our very own custom texture from pixmap
+ if (d->custom_tfp) {
+ QPixmap qp = QPixmap::fromX11Pixmap(d->windowp);
+
+ QImage img = d->glwidget->convertToGLFormat(qp.toImage());
+ glBindTexture(GL_TEXTURE_2D, d->ctextureId);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.width(), img.height(), 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
+ update();
+ } else {
+ if (d->glpixmap == EGL_NO_SURFACE)
+ saveBackingStore(true);
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, d->textureId);
+ bool valid = true;
+
+ if (eglReleaseTexImage(d->eglresource->dpy, d->glpixmap, EGL_BACK_BUFFER) == EGL_FALSE) {
+ qWarning("Update: Cant release bound texture 0x%x",
+ eglGetError());
+ valid = false;
+ }
+ if (eglBindTexImage(d->eglresource->dpy, d->glpixmap, EGL_BACK_BUFFER) == EGL_FALSE) {
+ qWarning("Update: Can't bind EGL texture to pixmap: 0x%x",
+ eglGetError());
+ valid = false;
+ }
+ if (!valid)
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, 0);
+ else
+ d->glwidget->update();
+ }
+}
+
+void MTexturePixmapItem::paint(QPainter *painter,
+ const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+ Q_UNUSED(option)
+ Q_UNUSED(widget)
+
+ if (d->direct_fb_render) {
+ glBindTexture(0, 0);
+ return;
+ }
+
+#if QT_VERSION < 0x040600
+ if (painter->paintEngine()->type() != QPaintEngine::OpenGL)
+ return;
+#else
+ if (painter->paintEngine()->type() != QPaintEngine::OpenGL2) {
+ return;
+ }
+#endif
+ QGLWidget *gl = (QGLWidget *) painter->device();
+ if (!d->ctx)
+ d->ctx = const_cast<QGLContext *>(gl->context());
+
+ if (d->has_alpha || opacity() < 1.0f) {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ glBindTexture(GL_TEXTURE_2D, d->custom_tfp ? d->ctextureId : d->textureId);
+
+ if (d->damageRegion.numRects() > 1) {
+ d->drawTexture(painter->combinedTransform(), boundingRect(), opacity());
+ glEnable(GL_SCISSOR_TEST);
+ for (int i = 0; i < d->damageRegion.numRects(); ++i) {
+ glScissor(d->damageRegion.rects().at(i).x(),
+ d->brect.height() -
+ (d->damageRegion.rects().at(i).y() +
+ d->damageRegion.rects().at(i).height()),
+ d->damageRegion.rects().at(i).width(),
+ d->damageRegion.rects().at(i).height());
+ d->drawTexture(painter->combinedTransform(), boundingRect(), opacity());
+ }
+ glDisable(GL_SCISSOR_TEST);
+ } else
+ d->drawTexture(painter->combinedTransform(), boundingRect(), opacity());
+
+ // Explicitly disable blending. for some reason, the latest drivers
+ // still has blending left-over even if we call glDisable(GL_BLEND)
+ glBlendFunc(GL_ONE, GL_ZERO);
+ glDisable(GL_BLEND);
+}
+
+void MTexturePixmapItem::windowRaised()
+{
+ d->windowRaised();
+}
+
+void MTexturePixmapItem::resize(int w, int h)
+{
+ d->resize(w, h);
+}
+
+QSizeF MTexturePixmapItem::sizeHint(Qt::SizeHint, const QSizeF &) const
+{
+ return boundingRect().size();
+}
+
+QRectF MTexturePixmapItem::boundingRect() const
+{
+ return d->brect;
+}
+
+QPainterPath MTexturePixmapItem::shape() const
+{
+ QPainterPath path;
+ path.addRect(boundingRect());
+ return path;
+}
+
+bool MTexturePixmapItem::hasAlpha() const
+{
+ return d->has_alpha;
+}
+
+bool MTexturePixmapItem::isOverrideRedirect() const
+{
+ return d->override_redirect;
+}
+
+void MTexturePixmapItem::clearTexture()
+{
+ glBindTexture(GL_TEXTURE_2D, d->custom_tfp ? d->ctextureId : d->textureId);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, 0);
+}