Introducing QPlatformSharedGraphicsCache
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com>
Fri, 18 Nov 2011 09:24:32 +0000 (10:24 +0100)
committerQt by Nokia <qt-info@nokia.com>
Fri, 20 Jan 2012 13:17:41 +0000 (14:17 +0100)
Interface to provide cross-process caching mechanisms in a platform
plugin. Can be used for shared glyph caches and icon caches etc.

Change-Id: If0d89a0a50bbd6eee05daf908448262ff270fc5b
Reviewed-by: Jiang Jiang <jiang.jiang@nokia.com>
12 files changed:
src/gui/kernel/kernel.pri
src/gui/kernel/qplatformintegration_qpa.cpp
src/gui/kernel/qplatformintegration_qpa.h
src/gui/kernel/qplatformsharedgraphicscache_qpa.cpp [new file with mode: 0644]
src/gui/kernel/qplatformsharedgraphicscache_qpa.h [new file with mode: 0644]
src/plugins/platforms/xcb/qxcbintegration.cpp
src/plugins/platforms/xcb/qxcbintegration.h
src/plugins/platforms/xcb/qxcbsharedbuffermanager.cpp [new file with mode: 0644]
src/plugins/platforms/xcb/qxcbsharedbuffermanager.h [new file with mode: 0644]
src/plugins/platforms/xcb/qxcbsharedgraphicscache.cpp [new file with mode: 0644]
src/plugins/platforms/xcb/qxcbsharedgraphicscache.h [new file with mode: 0644]
src/plugins/platforms/xcb/xcb.pro

index bf552c9..12cb42c 100644 (file)
@@ -54,7 +54,8 @@ HEADERS += \
         kernel/qscreen_p.h \
         kernel/qstylehints.h \
         kernel/qtouchdevice.h \
-        kernel/qtouchdevice_p.h
+        kernel/qtouchdevice_p.h \
+        kernel/qplatformsharedgraphicscache_qpa.h
 
 SOURCES += \
         kernel/qclipboard_qpa.cpp \
@@ -96,6 +97,7 @@ SOURCES += \
         kernel/qscreen.cpp \
         kernel/qshortcutmap.cpp \
         kernel/qstylehints.cpp \
-        kernel/qtouchdevice.cpp
+        kernel/qtouchdevice.cpp \
+        kernel/qplatformsharedgraphicscache_qpa.cpp
 
 win32:HEADERS+=kernel/qwindowdefs_win.h
index 23ecf3a..d1e267d 100644 (file)
@@ -202,6 +202,17 @@ QPlatformOpenGLContext *QPlatformIntegration::createPlatformOpenGLContext(QOpenG
 }
 
 /*!
+   Factory function for QPlatformSharedGraphicsCache. This function will return 0 if the platform
+   integration does not support any shared graphics cache mechanism for the given \a cacheId.
+*/
+QPlatformSharedGraphicsCache *QPlatformIntegration::createPlatformSharedGraphicsCache(const char *cacheId) const
+{
+    qWarning("This plugin does not support createPlatformSharedGraphicsBuffer for cacheId: %s!",
+             cacheId);
+    return 0;
+}
+
+/*!
   Returns the platforms input context.
 
   The default implementation returns 0, implying no input method support.
index 3975d82..3043ba8 100644 (file)
@@ -65,6 +65,8 @@ class QAbstractEventDispatcher;
 class QPlatformInputContext;
 class QPlatformAccessibility;
 class QPlatformTheme;
+class QPlatformDialogHelper;
+class QPlatformSharedGraphicsCache;
 
 class Q_GUI_EXPORT QPlatformIntegration
 {
@@ -72,7 +74,8 @@ public:
     enum Capability {
         ThreadedPixmaps = 1,
         OpenGL = 2,
-        ThreadedOpenGL = 3
+        ThreadedOpenGL = 3,
+        SharedGraphicsCache = 4
     };
 
     virtual ~QPlatformIntegration() { }
@@ -83,6 +86,7 @@ public:
     virtual QPlatformWindow *createPlatformWindow(QWindow *window) const = 0;
     virtual QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const = 0;
     virtual QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const;
+    virtual QPlatformSharedGraphicsCache *createPlatformSharedGraphicsCache(const char *cacheId) const;
 
 // Event dispatcher:
     virtual QAbstractEventDispatcher *guiThreadEventDispatcher() const = 0;
diff --git a/src/gui/kernel/qplatformsharedgraphicscache_qpa.cpp b/src/gui/kernel/qplatformsharedgraphicscache_qpa.cpp
new file mode 100644 (file)
index 0000000..67a59b0
--- /dev/null
@@ -0,0 +1,212 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used 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. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformsharedgraphicscache_qpa.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+    \class QPlatformSharedGraphicsCache
+    \since 5.0
+    \internal
+    \preliminary
+    \ingroup qpa
+    \brief The QPlatformSharedGraphicsCache is an abstraction of a cross-process graphics cache.
+
+    If supported, it is possible to retrieve a QPlatformSharedGraphicsCache object from the
+    platform integration. This is typically used to store graphical items which should be shared
+    between several processes.
+
+    Items are requested from the cache by calling requestItems(). If the cache contains the
+    requested items in the requested cache, the itemsAvailable() signal is emitted with the ID of
+    the graphical buffer and each item's coordinates inside the buffer. Before requesting items
+    from a cache, the user must call ensureCacheInitialized() to set the correct parameters for
+    the cache.
+
+    If the cache does not yet contain the requested items, it will emit a similar itemsMissing()
+    signal. The client can then call updateItems() with rasterizations of the items and they will be
+    entered into the shared cache. As the items are rendered into the cache, itemsAvailable() signals
+    will be emitted for each of the items which have previously been requested and which have not
+    yet been reported as ready.
+*/
+
+/*!
+    \enum BufferType
+
+    Defines how the type of buffer required to contain a cache.
+
+    \value OpenGLTexture The buffer will be allocated in graphics memory, and an OpenGL texture
+                         for a buffer belonging to the cache can be requested using
+                         textureIdForBuffer().
+*/
+
+/*!
+    \enum PixelFormat
+
+    Defines the pixel format of a cache.
+
+    \value Alpha8 The cache will use 8 bits to represent the alpha value of each pixel. If an
+                  OpenGL texture is created for a buffer belong to the cache, it will have the
+                  pixel format GL_ALPHA.
+*/
+
+/*!
+   \fn void ensureCacheInitialized(const QByteArray &cacheId, BufferType bufferType, PixelFormat pixelFormat)
+
+   Initializes a cache named \a cacheId if it has not yet been initialized. The \a bufferType and
+   \a pixelFormat gives the format of the buffers that will be used to contain the items in the
+   cache. If a cache with the same \a cacheId has previously been initialized, the call will be
+   ignored. The cache will keep its previously set buffer type and pixel format.
+*/
+
+/*!
+    \fn void QPlatformSharedGraphicsCache::requestItems(const QByteArray &cacheId, const QVector<quint32> &itemIds)
+
+    Requests all the items in \a itemIds from the cache with the name \a cacheId.
+
+    If any or all of the items are available in the cache, one or more itemsAvailable() signals will be
+    emitted corresponding to the items. If the cache does not contain all of the items in question,
+    then an itemsMissing() signal will be emitted corresponding to the missing items. The user
+    is at this point expected to call insertItems() to insert the missing items into the cache. If
+    the inserted items have previously been requested by the user, at which point an itemsAvailable()
+    signal will be emitted corresponding to the items.
+
+    Before requesting items from a cache, the user must call ensureCacheInitialized() with the
+    correct parameters for the cache.
+*/
+
+/*!
+    \fn void QPlatformSharedGraphicsCache::insertItems(const QByteArray &cacheId, const QVector<quint32> &itemIds, const QVector<QImage> &items)
+
+    Inserts the items in \a itemIds into the cache named \a cacheId. The appearance of
+    each item is stored in \a items. The format of the QImage objects is expected to match the
+    pixel format of the cache as it was initialized in ensureCacheInitialized().
+
+    When the items have been successfully entered into the cache, one or more itemsAvailable() signals
+    will be emitted for the items.
+
+    If the cache already contains the items, the behavior is implementation-specific. The
+    implementation may choose to ignore the items or it may overwrite the existing instances in
+    the cache. Either way, itemsAvailable() signals corresponding to the inserted items will be
+    emitted.
+*/
+
+/*!
+    \fn void QPlatformSharedGraphicsCache::releaseItems(const QByteArray &cacheId, const QVector<quint32> &itemIds)
+
+    Releases the reference to the items in \a itemIds from the cache named \a cacheId. This should
+    only be called when all references to the items have been released by the user, and they are no
+    longer needed.
+*/
+
+/*!
+    \fn void itemsMissing(const QByteArray &cacheId, const QVector<quint32> *itemIds)
+
+    This signal is emitted when requestItems() has been called for one or more items in the
+    cache named \a cacheId which are not yet available in the cache. The user is then expected to
+    call insertItems() to update the cache with the respective items, at which point they will
+    become available to all clients of the shared cache.
+
+    The vector \a itemIds contains the IDs of the items that need to be inserted into the cache.
+
+    \sa itemsAvailable(), insertItems(), requestItems()
+*/
+
+/*!
+    \fn void itemsAvailable(const QByteArray &cacheId, void *bufferId, const QSize &bufferSize, const QVector<quint32> &itemIds, const QVector<QPoint> &positionsInBuffer)
+
+    This signal can be emitted at any time when either requestItems() or insertItems() has been
+    called by the application for one or more items in the cache named \a cacheId, as long as
+    releaseItems() has not subsequently been called for the same items. It instructs the application
+    on where to find the items that have been entered into the cache. When the application receives
+    a buffer, it is expected to reference it using referenceBuffer() on it if it keeps a reference
+    to the buffer.
+
+    The \a bufferId is an ID for the buffer that contains the items. The \a bufferId can be
+    converted to a format usable by the application depending on which format it was given at
+    initialization. If it is a OpenGLTexture, its texture ID can be requested using the
+    textureIdForBuffer() function. The dimensions of the buffer are given by \a bufferSize.
+
+    The items provided by the cache are identified in the \a itemIds vector. The
+    \a positionsInBuffer vector contains the locations inside the buffer of each item. Each entry in
+    \a positionsInBuffer corresponds to an item in \a itemIds.
+
+    The buffer and the items' locations within the buffer can be considered valid until an
+    itemsInvalidated() signal has been emitted for the items, or until releaseItems() is called
+    for the items.
+
+    \sa itemsMissing(), requestItems(), bufferType()
+*/
+
+/*!
+    \fn void itemsUpdated(const QByteArray &cacheId, void *bufferId, const QSize &bufferSize, const QVector<quint32> &itemIds, const QVector<QPoint> &positionsInBuffer)
+
+    This signal is similar in usage to the itemsAvailable() signal, but will be emitted when
+    the location of a previously requested or inserted item has been updated. The application
+    must update its data for the respective items and release any references to old buffers held
+    by the items.
+
+    If the application no longer holds any references to previously referenced items in a given
+    cache, it should call releaseItems() for these items, at which point it will no longer receive
+    any itemsUpdated() signal for these items.
+
+    \sa requestItems(), insertItems(), itemsAvailable()
+*/
+
+/*!
+    \fn void itemsInvalidated(const QByteArray &cacheId, const QVector<quint32> &itemIds)
+
+    This signal is emitted when the items given by \a itemIds in the cache named \a cacheId have
+    been removed from the cache and the previously reported information about them is considered
+    invalid. It will only be emitted for items for which a buffer has previously been identified
+    through the itemsAvailable() signal (either as response to a requestItems() call or an
+    insertItems() call.)
+
+    The application is expected to throw away information about the items in the \a itemIds array
+    and drop any references it might have to the memory held by the buffer. If the items are still
+    required by the application, it can re-commit them to the cache using the insertItems() function.
+
+    If the application no longer holds any references to previously referenced items in a given
+    cache, it should call releaseItems() for these items, at which point it will no longer receive
+    any itemsInvalidated() signal for these items.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qplatformsharedgraphicscache_qpa.h b/src/gui/kernel/qplatformsharedgraphicscache_qpa.h
new file mode 100644 (file)
index 0000000..a0661c0
--- /dev/null
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used 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. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPLATFORMSHAREDGRAPHICSCACHE_QPA_H
+#define QPLATFORMSHAREDGRAPHICSCACHE_QPA_H
+
+#include <QtCore/qobject.h>
+#include <QtGui/qimage.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class Q_GUI_EXPORT QPlatformSharedGraphicsCache: public QObject
+{
+    Q_OBJECT
+public:
+    enum PixelFormat
+    {
+        Alpha8
+    };
+
+    enum BufferType
+    {
+        OpenGLTexture
+    };
+
+    QPlatformSharedGraphicsCache(QObject *parent = 0) : QObject(parent) {}
+
+    Q_INVOKABLE virtual void ensureCacheInitialized(const QByteArray &cacheId, BufferType bufferType,
+                                                    PixelFormat pixelFormat) = 0;
+
+    Q_INVOKABLE virtual void requestItems(const QByteArray &cacheId, const QVector<quint32> &itemIds) = 0;
+    Q_INVOKABLE virtual void insertItems(const QByteArray &cacheId,
+                                         const QVector<quint32> &itemIds,
+                                         const QVector<QImage> &items) = 0;
+    Q_INVOKABLE virtual void releaseItems(const QByteArray &cacheId, const QVector<quint32> &itemIds) = 0;
+
+    virtual void serializeBuffer(void *bufferId, QByteArray *serializedData, int *fileDescriptor) const = 0;
+    virtual uint textureIdForBuffer(void *bufferId) = 0;
+    virtual void referenceBuffer(void *bufferId) = 0;
+    virtual bool dereferenceBuffer(void *bufferId) = 0;
+
+Q_SIGNALS:
+    void itemsMissing(const QByteArray &cacheId, const QVector<quint32> &itemIds);
+    void itemsAvailable(const QByteArray &cacheId, void *bufferId, const QSize &bufferSize,
+                        const QVector<quint32> &itemIds, const QVector<QPoint> &positionsInBuffer);
+    void itemsInvalidated(const QByteArray &cacheId, const QVector<quint32> &itemIds);
+    void itemsUpdated(const QByteArray &cacheId, void *bufferId, const QSize &bufferSize,
+                      const QVector<quint32> &itemIds, const QVector<QPoint> &positionsInBuffer);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPLATFORMSHAREDGRAPHICSCACHE_QPA_H
index 418915b..0bed8c6 100644 (file)
@@ -47,6 +47,7 @@
 #include "qxcbnativeinterface.h"
 #include "qxcbclipboard.h"
 #include "qxcbdrag.h"
+#include "qxcbsharedgraphicscache.h"
 
 #include <xcb/xcb.h>
 
@@ -107,6 +108,10 @@ QXcbIntegration::QXcbIntegration(const QStringList &parameters)
     m_fontDatabase.reset(new QGenericUnixFontDatabase());
     m_inputContext.reset(QPlatformInputContextFactory::create());
     m_accessibility.reset(new QPlatformAccessibility());
+
+#if defined(QT_USE_XCB_SHARED_GRAPHICS_CACHE)
+    m_sharedGraphicsCache.reset(new QXcbSharedGraphicsCache);
+#endif
 }
 
 QXcbIntegration::~QXcbIntegration()
@@ -186,6 +191,10 @@ QPlatformBackingStore *QXcbIntegration::createPlatformBackingStore(QWindow *wind
 bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const
 {
     switch (cap) {
+#if defined(QT_USE_XCB_SHARED_GRAPHICS_CACHE)
+    case SharedGraphicsCache: return true;
+#endif
+
     case ThreadedPixmaps: return true;
     case OpenGL: return true;
     case ThreadedOpenGL:
@@ -239,4 +248,24 @@ QPlatformAccessibility *QXcbIntegration::accessibility() const
     return m_accessibility.data();
 }
 
+#if defined(QT_USE_XCB_SHARED_GRAPHICS_CACHE)
+static bool sharedGraphicsCacheDisabled()
+{
+    static const char *environmentVariable = "QT_DISABLE_SHARED_CACHE";
+    static bool cacheDisabled = !qgetenv(environmentVariable).isEmpty()
+            && qgetenv(environmentVariable).toInt() != 0;
+    return cacheDisabled;
+}
+
+QPlatformSharedGraphicsCache *QXcbIntegration::createPlatformSharedGraphicsCache(const char *cacheId) const
+{
+    Q_UNUSED(cacheId);
+
+    if (sharedGraphicsCacheDisabled())
+        return 0;
+
+    return m_sharedGraphicsCache.data();
+}
+#endif
+
 QT_END_NAMESPACE
index 2bb5f1e..fc76942 100644 (file)
@@ -77,6 +77,10 @@ public:
 
     QPlatformAccessibility *accessibility() const;
 
+#if defined(QT_USE_XCB_SHARED_GRAPHICS_CACHE)
+    QPlatformSharedGraphicsCache *createPlatformSharedGraphicsCache(const char *cacheId) const;
+#endif
+
 private:
     QList<QXcbConnection *> m_connections;
 
@@ -87,6 +91,10 @@ private:
     QAbstractEventDispatcher *m_eventDispatcher;
 
     QScopedPointer<QPlatformAccessibility> m_accessibility;
+
+#if defined(QT_USE_XCB_SHARED_GRAPHICS_CACHE)
+    QScopedPointer<QPlatformSharedGraphicsCache> m_sharedGraphicsCache;
+#endif
 };
 
 QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbsharedbuffermanager.cpp b/src/plugins/platforms/xcb/qxcbsharedbuffermanager.cpp
new file mode 100644 (file)
index 0000000..51e3e85
--- /dev/null
@@ -0,0 +1,640 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used 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. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#if defined(QT_USE_XCB_SHARED_GRAPHICS_CACHE)
+
+#include "qxcbsharedbuffermanager.h"
+
+#include <QtCore/quuid.h>
+#include <QtGui/qimage.h>
+
+#include <stdio.h>
+
+#if !defined(SHAREDGRAPHICSCACHE_MAX_MEMORY_USED)
+#  define SHAREDGRAPHICSCACHE_MAX_MEMORY_USED 16 * 1024 * 1024 // 16 MB limit
+#endif
+
+#if !defined(SHAREDGRAPHICSCACHE_MAX_TEXTURES_PER_CACHE)
+#  define SHAREDGRAPHICSCACHE_MAX_TEXTURES_PER_CACHE 1
+#endif
+
+#if !defined(SHAREDGRAPHICSCACHE_TEXTURE_SIZE)
+#  define SHAREDGRAPHICSCACHE_TEXTURE_SIZE 2048
+#endif
+
+#define SHAREDBUFFERMANAGER_DEBUG 1
+
+QT_BEGIN_NAMESPACE
+
+QXcbSharedBufferManager::QXcbSharedBufferManager()
+    : m_memoryUsed(0)
+    , m_mostRecentlyUsed(0)
+    , m_leastRecentlyUsed(0)
+{
+}
+
+QXcbSharedBufferManager::~QXcbSharedBufferManager()
+{
+    {
+        QHash<QByteArray, Buffer *>::const_iterator it = m_buffers.constBegin();
+        while (it != m_buffers.constEnd()) {
+            Buffer *buffer = it.value();
+            delete buffer;
+            ++it;
+        }
+    }
+
+    {
+        QHash<QByteArray, Items *>::const_iterator it = m_items.constBegin();
+        while (it != m_items.constEnd()) {
+            Items *items = it.value();
+            QHash<quint32, Item *>::const_iterator itemIt = items->items.constBegin();
+            while (itemIt != items->items.constEnd()) {
+                delete itemIt.value();
+                ++itemIt;
+            }
+            delete it.value();
+            ++it;
+        }
+    }
+}
+
+void QXcbSharedBufferManager::getBufferForItem(const QByteArray &cacheId, quint32 itemId,
+                                                      Buffer **buffer, int *x, int *y) const
+{
+    Q_ASSERT_X(m_currentCacheId.isEmpty(), Q_FUNC_INFO,
+               "Call endSharedBufferAction before accessing data");
+
+    Q_ASSERT(buffer != 0);
+    Q_ASSERT(x != 0);
+    Q_ASSERT(y != 0);
+
+    Items *items = itemsForCache(cacheId);
+    Item *item = items->items.value(itemId);
+    if (item != 0) {
+        *buffer = item->buffer;
+        *x = item->x;
+        *y = item->y;
+    } else {
+        *buffer = 0;
+        *x = -1;
+        *y = -1;
+    }
+}
+
+QPair<QByteArray, int> QXcbSharedBufferManager::serializeBuffer(QSharedMemory *buffer) const
+{
+    Q_ASSERT_X(m_currentCacheId.isEmpty(), Q_FUNC_INFO,
+               "Call endSharedBufferAction before accessing data");
+
+    return qMakePair(buffer->key().toLatin1(), 0);
+}
+
+void QXcbSharedBufferManager::beginSharedBufferAction(const QByteArray &cacheId)
+{
+#if defined(SHAREDBUFFERMANAGER_DEBUG)
+    qDebug("QXcbSharedBufferManager::beginSharedBufferAction() called for %s", cacheId.constData());
+#endif
+
+    Q_ASSERT(m_currentCacheId.isEmpty());
+    Q_ASSERT(!cacheId.isEmpty());
+
+    m_pendingInvalidatedItems.clear();
+    m_pendingReadyItems.clear();
+    m_pendingMissingItems.clear();
+
+    m_currentCacheId = cacheId;
+}
+
+void QXcbSharedBufferManager::requestItems(const QSet<quint32> &itemIds)
+{
+#if defined(SHAREDBUFFERMANAGER_DEBUG)
+    qDebug("QXcbSharedBufferManager::requestItems for %d items", itemIds.size());
+#endif
+
+    Q_ASSERT_X(!m_currentCacheId.isEmpty(), Q_FUNC_INFO,
+               "Call beginSharedBufferAction before requesting items");
+    Items *items = itemsForCache(m_currentCacheId);
+
+    QSet<quint32>::const_iterator it = itemIds.constBegin();
+    while (it != itemIds.constEnd()) {
+        if (items->items.contains(*it))
+            m_pendingReadyItems[m_currentCacheId].insert(*it);
+        else
+            m_pendingMissingItems[m_currentCacheId].insert(*it);
+        ++it;
+    }
+}
+
+void QXcbSharedBufferManager::releaseItems(const QSet<quint32> &itemIds)
+{
+#if defined(SHAREDBUFFERMANAGER_DEBUG)
+    qDebug("QXcbSharedBufferManager::releaseItems for %d items", itemIds.size());
+#endif
+
+    Items *items = itemsForCache(m_currentCacheId);
+
+    QSet<quint32>::const_iterator it;
+    for (it = itemIds.constBegin(); it != itemIds.constEnd(); ++it) {
+        Item *item = items->items.value(*it);
+        if (item != 0)
+            pushItemToBack(items, item);
+
+        m_pendingReadyItems[m_currentCacheId].remove(*it);
+        m_pendingMissingItems[m_currentCacheId].remove(*it);
+    }
+}
+
+void QXcbSharedBufferManager::insertItem(quint32 itemId, uchar *data,
+                                                int itemWidth, int itemHeight)
+{
+    Q_ASSERT_X(!m_currentCacheId.isEmpty(), Q_FUNC_INFO,
+               "Call beginSharedBufferAction before inserting items");
+    Items *items = itemsForCache(m_currentCacheId);
+
+    if (!items->items.contains(itemId)) {
+        Buffer *sharedBuffer = 0;
+        int x = 0;
+        int y = 0;
+
+        findAvailableBuffer(itemWidth, itemHeight, &sharedBuffer, &x, &y);
+        copyIntoBuffer(sharedBuffer, x, y, itemWidth, itemHeight, data);
+
+//        static int counter=0;
+//        QString fileName = QString::fromLatin1("buffer%1.png").arg(counter++);
+//        saveBuffer(sharedBuffer, fileName);
+
+        Item *item = new Item;
+        item->itemId = itemId;
+        item->buffer = sharedBuffer;
+        item->x = x;
+        item->y = y;
+
+        items->items[itemId] = item;
+
+        touchItem(items, item);
+    }
+}
+
+void QXcbSharedBufferManager::endSharedBufferAction()
+{
+#if defined(SHAREDBUFFERMANAGER_DEBUG)
+    qDebug("QXcbSharedBufferManager::endSharedBufferAction() called for %s",
+           m_currentCacheId.constData());
+#endif
+
+    Q_ASSERT(!m_currentCacheId.isEmpty());
+
+    // Do an extra validation pass on the invalidated items since they may have been re-inserted
+    // after they were invalidated
+    if (m_pendingInvalidatedItems.contains(m_currentCacheId)) {
+        QSet<quint32> &invalidatedItems = m_pendingInvalidatedItems[m_currentCacheId];
+        QSet<quint32>::iterator it = invalidatedItems.begin();
+        while (it != invalidatedItems.end()) {
+            Items *items = m_items.value(m_currentCacheId);
+
+            if (items->items.contains(*it)) {
+                m_pendingReadyItems[m_currentCacheId].insert(*it);
+                it = invalidatedItems.erase(it);
+            } else {
+                ++it;
+            }
+        }
+    }
+
+    m_currentCacheId.clear();
+}
+
+void QXcbSharedBufferManager::pushItemToBack(Items *items, Item *item)
+{
+    if (items->leastRecentlyUsed == item)
+        return;
+
+    if (item->next != 0)
+        item->next->prev = item->prev;
+    if (item->prev != 0)
+        item->prev->next = item->next;
+
+    if (items->mostRecentlyUsed == item)
+        items->mostRecentlyUsed = item->prev;
+
+    if (items->leastRecentlyUsed != 0)
+        items->leastRecentlyUsed->prev = item;
+
+    item->prev = 0;
+    item->next = items->leastRecentlyUsed;
+    items->leastRecentlyUsed = item;
+    if (items->mostRecentlyUsed == 0)
+        items->mostRecentlyUsed = item;
+}
+
+void QXcbSharedBufferManager::touchItem(Items *items, Item *item)
+{
+    if (items->mostRecentlyUsed == item)
+        return;
+
+    if (item->next != 0)
+        item->next->prev = item->prev;
+    if (item->prev != 0)
+        item->prev->next = item->next;
+
+    if (items->leastRecentlyUsed == item)
+        items->leastRecentlyUsed = item->next;
+
+    if (items->mostRecentlyUsed != 0)
+        items->mostRecentlyUsed->next = item;
+
+    item->next = 0;
+    item->prev = items->mostRecentlyUsed;
+    items->mostRecentlyUsed = item;
+    if (items->leastRecentlyUsed == 0)
+        items->leastRecentlyUsed = item;
+}
+
+void QXcbSharedBufferManager::deleteItem(Items *items, Item *item)
+{
+    Q_ASSERT(items != 0);
+    Q_ASSERT(item != 0);
+
+    if (items->mostRecentlyUsed == item)
+        items->mostRecentlyUsed = item->prev;
+    if (items->leastRecentlyUsed == item)
+        items->leastRecentlyUsed = item->next;
+
+    if (item->next != 0)
+        item->next->prev = item->prev;
+    if (item->prev != 0)
+        item->prev->next = item->next;
+
+    m_pendingInvalidatedItems[items->cacheId].insert(item->itemId);
+
+    {
+        QHash<quint32, Item *>::iterator it = items->items.find(item->itemId);
+        while (it != items->items.end() && it.value()->itemId == item->itemId)
+            it = items->items.erase(it);
+    }
+
+    delete item;
+}
+
+void QXcbSharedBufferManager::recycleItem(Buffer **sharedBuffer, int *glyphX, int *glyphY)
+{
+#if defined(SHAREDBUFFERMANAGER_DEBUG)
+    qDebug("QXcbSharedBufferManager::recycleItem() called for %s", m_currentCacheId.constData());
+#endif
+
+    Items *items = itemsForCache(m_currentCacheId);
+
+    Item *recycledItem = items->leastRecentlyUsed;
+    Q_ASSERT(recycledItem != 0);
+
+    *sharedBuffer = recycledItem->buffer;
+    *glyphX = recycledItem->x;
+    *glyphY = recycledItem->y;
+
+    deleteItem(items, recycledItem);
+}
+
+void QXcbSharedBufferManager::touchBuffer(Buffer *buffer)
+{
+#if defined(SHAREDBUFFERMANAGER_DEBUG)
+    qDebug("QXcbSharedBufferManager::touchBuffer() called for %s", buffer->cacheId.constData());
+#endif
+
+    if (buffer == m_mostRecentlyUsed)
+        return;
+
+    if (buffer->next != 0)
+        buffer->next->prev = buffer->prev;
+    if (buffer->prev != 0)
+        buffer->prev->next = buffer->next;
+
+    if (m_leastRecentlyUsed == buffer)
+        m_leastRecentlyUsed = buffer->next;
+
+    buffer->next = 0;
+    buffer->prev = m_mostRecentlyUsed;
+    if (m_mostRecentlyUsed != 0)
+        m_mostRecentlyUsed->next = buffer;
+    if (m_leastRecentlyUsed == 0)
+        m_leastRecentlyUsed = buffer;
+    m_mostRecentlyUsed = buffer;
+}
+
+void QXcbSharedBufferManager::deleteLeastRecentlyUsed()
+{
+#if defined(SHAREDBUFFERMANAGER_DEBUG)
+    qDebug("QXcbSharedBufferManager::deleteLeastRecentlyUsed() called");
+#endif
+
+    if (m_leastRecentlyUsed == 0)
+        return;
+
+    Buffer *old = m_leastRecentlyUsed;
+    m_leastRecentlyUsed = old->next;
+    m_leastRecentlyUsed->prev = 0;
+
+    QByteArray cacheId = old->cacheId;
+    Items *items = itemsForCache(cacheId);
+
+    QHash<quint32, Item *>::iterator it = items->items.begin();
+    while (it != items->items.end()) {
+        Item *item = it.value();
+        if (item->buffer == old) {
+            deleteItem(items, item);
+            it = items->items.erase(it);
+        } else {
+            ++it;
+        }
+    }
+
+    m_buffers.remove(cacheId, old);
+    m_memoryUsed -= old->width * old->height * old->bytesPerPixel;
+
+#if defined(SHAREDBUFFERMANAGER_DEBUG)
+    qDebug("QXcbSharedBufferManager::deleteLeastRecentlyUsed: Memory used: %d / %d (%6.2f %%)",
+           m_memoryUsed, SHAREDGRAPHICSCACHE_MAX_MEMORY_USED,
+           100.0f * float(m_memoryUsed) / float(SHAREDGRAPHICSCACHE_MAX_MEMORY_USED));
+#endif
+
+    delete old;
+}
+
+QXcbSharedBufferManager::Buffer *QXcbSharedBufferManager::createNewBuffer(const QByteArray &cacheId,
+                                                                  int heightRequired)
+{
+#if defined(SHAREDBUFFERMANAGER_DEBUG)
+    qDebug("QXcbSharedBufferManager::createNewBuffer() called for %s", cacheId.constData());
+#endif
+
+    // ###
+    // if (bufferCount of cacheId == SHAREDGRAPHICACHE_MAX_TEXTURES_PER_CACHE)
+    //    deleteLeastRecentlyUsedBufferForCache(cacheId);
+
+    // ### Take pixel format into account
+    while (m_memoryUsed + SHAREDGRAPHICSCACHE_TEXTURE_SIZE * heightRequired >= SHAREDGRAPHICSCACHE_MAX_MEMORY_USED)
+        deleteLeastRecentlyUsed();
+
+    Buffer *buffer = allocateBuffer(SHAREDGRAPHICSCACHE_TEXTURE_SIZE, heightRequired);
+    buffer->cacheId = cacheId;
+
+    buffer->currentLineMaxHeight = 0;
+    m_buffers.insert(cacheId, buffer);
+
+    return buffer;
+}
+
+static inline int qt_next_power_of_two(int v)
+{
+    v--;
+    v |= v >> 1;
+    v |= v >> 2;
+    v |= v >> 4;
+    v |= v >> 8;
+    v |= v >> 16;
+    ++v;
+    return v;
+}
+
+QXcbSharedBufferManager::Buffer *QXcbSharedBufferManager::resizeBuffer(Buffer *oldBuffer, const QSize &newSize)
+{
+#if defined(SHAREDBUFFERMANAGER_DEBUG)
+    qDebug("QXcbSharedBufferManager::resizeBuffer() called for %s (current size: %dx%d, new size: %dx%d)",
+           oldBuffer->cacheId.constData(), oldBuffer->width, oldBuffer->height,
+           newSize.width(), newSize.height());
+#endif
+
+    // Remove old buffer from lists to avoid deleting it under our feet
+    if (m_leastRecentlyUsed == oldBuffer)
+        m_leastRecentlyUsed = oldBuffer->next;
+    if (m_mostRecentlyUsed == oldBuffer)
+        m_mostRecentlyUsed = oldBuffer->prev;
+
+    if (oldBuffer->prev != 0)
+        oldBuffer->prev->next = oldBuffer->next;
+    if (oldBuffer->next != 0)
+        oldBuffer->next->prev = oldBuffer->prev;
+
+    m_memoryUsed -= oldBuffer->width * oldBuffer->height * oldBuffer->bytesPerPixel;
+    m_buffers.remove(oldBuffer->cacheId, oldBuffer);
+
+#if defined(SHAREDBUFFERMANAGER_DEBUG)
+    qDebug("QXcbSharedBufferManager::resizeBuffer:            Memory used: %d / %d (%6.2f %%)",
+           m_memoryUsed, SHAREDGRAPHICSCACHE_MAX_MEMORY_USED,
+           100.0f * float(m_memoryUsed) / float(SHAREDGRAPHICSCACHE_MAX_MEMORY_USED));
+#endif
+
+    Buffer *resizedBuffer = createNewBuffer(oldBuffer->cacheId, newSize.height());
+    copyIntoBuffer(resizedBuffer, 0, 0, oldBuffer->width, oldBuffer->height,
+                   reinterpret_cast<uchar *>(oldBuffer->buffer->data()));
+
+    resizedBuffer->currentLineMaxHeight = oldBuffer->currentLineMaxHeight;
+
+    Items *items = itemsForCache(oldBuffer->cacheId);
+    QHash<quint32, Item *>::const_iterator it = items->items.constBegin();
+    while (it != items->items.constEnd()) {
+        Item *item = it.value();
+        if (item->buffer == oldBuffer) {
+            m_pendingReadyItems[oldBuffer->cacheId].insert(item->itemId);
+            item->buffer = resizedBuffer;
+        }
+        ++it;
+    }
+
+    resizedBuffer->nextX = oldBuffer->nextX;
+    resizedBuffer->nextY = oldBuffer->nextY;
+    resizedBuffer->currentLineMaxHeight = oldBuffer->currentLineMaxHeight;
+
+    delete oldBuffer;
+    return resizedBuffer;
+}
+
+void QXcbSharedBufferManager::findAvailableBuffer(int itemWidth, int itemHeight,
+                                              Buffer **sharedBuffer, int *glyphX, int *glyphY)
+{
+    Q_ASSERT(sharedBuffer != 0);
+    Q_ASSERT(glyphX != 0);
+    Q_ASSERT(glyphY != 0);
+
+    QMultiHash<QByteArray, Buffer *>::iterator it = m_buffers.find(m_currentCacheId);
+
+    int bufferCount = 0;
+    while (it != m_buffers.end() && it.key() == m_currentCacheId) {
+        Buffer *buffer = it.value();
+
+        int x = buffer->nextX;
+        int y = buffer->nextY;
+        int width = buffer->width;
+        int height = buffer->height;
+
+        if (x + itemWidth <= width && y + itemHeight <= height) {
+            // There is space on the current line, put the item there
+            buffer->currentLineMaxHeight = qMax(buffer->currentLineMaxHeight, itemHeight);
+            *sharedBuffer = buffer;
+            *glyphX = x;
+            *glyphY = y;
+
+            buffer->nextX += itemWidth;
+
+            return;
+        } else if (itemWidth <= width && y + buffer->currentLineMaxHeight + itemHeight <= height) {
+            // There is space for a new line, put the item on the new line
+            buffer->nextX = 0;
+            buffer->nextY += buffer->currentLineMaxHeight;
+            buffer->currentLineMaxHeight = 0;
+
+            *sharedBuffer = buffer;
+            *glyphX = buffer->nextX;
+            *glyphY = buffer->nextY;
+
+            buffer->nextX += itemWidth;
+
+            return;
+        } else if (y + buffer->currentLineMaxHeight + itemHeight <= SHAREDGRAPHICSCACHE_TEXTURE_SIZE) {
+            // There is space if we resize the buffer, so we do that
+            int newHeight = qt_next_power_of_two(y + buffer->currentLineMaxHeight + itemHeight);
+            buffer = resizeBuffer(buffer, QSize(width, newHeight));
+
+            buffer->nextX = 0;
+            buffer->nextY += buffer->currentLineMaxHeight;
+            buffer->currentLineMaxHeight = 0;
+
+            *sharedBuffer = buffer;
+            *glyphX = buffer->nextX;
+            *glyphY = buffer->nextY;
+
+            buffer->nextX += itemWidth;
+            return;
+        }
+
+        bufferCount++;
+        ++it;
+    }
+
+    if (bufferCount == SHAREDGRAPHICSCACHE_MAX_TEXTURES_PER_CACHE) {
+        // There is no space in any buffer, and there is no space for a new buffer
+        // recycle an old item
+        recycleItem(sharedBuffer, glyphX, glyphY);
+    } else {
+        // Create a new buffer for the item
+        *sharedBuffer = createNewBuffer(m_currentCacheId, qt_next_power_of_two(itemHeight));
+        if (*sharedBuffer == 0) {
+            Q_ASSERT(false);
+            return;
+        }
+
+        *glyphX = (*sharedBuffer)->nextX;
+        *glyphY = (*sharedBuffer)->nextY;
+
+        (*sharedBuffer)->nextX += itemWidth;
+    }
+}
+
+QXcbSharedBufferManager::Buffer *QXcbSharedBufferManager::allocateBuffer(int width, int height)
+{
+    Buffer *buffer = new Buffer;
+    buffer->nextX = 0;
+    buffer->nextY = 0;
+    buffer->width = width;
+    buffer->height = height;
+    buffer->bytesPerPixel = 1; // ### Use pixel format here
+
+    buffer->buffer = new QSharedMemory(QUuid::createUuid().toString());
+    bool ok = buffer->buffer->create(buffer->width * buffer->height * buffer->bytesPerPixel,
+                                     QSharedMemory::ReadWrite);
+    if (!ok) {
+        qWarning("SharedBufferManager::findAvailableBuffer: Can't create new buffer (%s)",
+                 qPrintable(buffer->buffer->errorString()));
+        delete buffer;
+        return 0;
+    }
+    qMemSet(buffer->buffer->data(), 0, buffer->buffer->size());
+
+    m_memoryUsed += buffer->width * buffer->height * buffer->bytesPerPixel;
+
+#if defined(SHAREDBUFFERMANAGER_DEBUG)
+    qDebug("QXcbSharedBufferManager::allocateBuffer:          Memory used: %d / %d (%6.2f %%)",
+           int(m_memoryUsed), int(SHAREDGRAPHICSCACHE_MAX_MEMORY_USED),
+           100.0f * float(m_memoryUsed) / float(SHAREDGRAPHICSCACHE_MAX_MEMORY_USED));
+#endif
+
+    return buffer;
+}
+
+void QXcbSharedBufferManager::copyIntoBuffer(Buffer *buffer,
+                                                    int bufferX, int bufferY, int width, int height,
+                                                    uchar *data)
+{
+#if defined(SHAREDBUFFERMANAGER_DEBUG)
+    qDebug("QXcbSharedBufferManager::copyIntoBuffer() called for %s (coords: %d, %d)",
+           buffer->cacheId.constData(), bufferX, bufferY);
+#endif
+
+    Q_ASSERT(bufferX >= 0);
+    Q_ASSERT(bufferX + width <= buffer->width);
+    Q_ASSERT(bufferY >= 0);
+    Q_ASSERT(bufferY + height <= buffer->height);
+
+    uchar *dest = reinterpret_cast<uchar *>(buffer->buffer->data());
+    dest += bufferX + bufferY * buffer->width;
+    for (int y=0; y<height; ++y) {
+        qMemCopy(dest, data, width);
+
+        data += width;
+        dest += buffer->width;
+    }
+}
+
+QXcbSharedBufferManager::Items *QXcbSharedBufferManager::itemsForCache(const QByteArray &cacheId) const
+{
+    Items *items = m_items.value(cacheId);
+    if (items == 0) {
+        items = new Items;
+        items->cacheId = cacheId;
+        m_items[cacheId] = items;
+    }
+
+    return items;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_USE_XCB_SHARED_GRAPHICS_CACHE
diff --git a/src/plugins/platforms/xcb/qxcbsharedbuffermanager.h b/src/plugins/platforms/xcb/qxcbsharedbuffermanager.h
new file mode 100644 (file)
index 0000000..c815336
--- /dev/null
@@ -0,0 +1,215 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used 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. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef XCBSHAREDBUFFERMANAGER_H
+#define XCBSHAREDBUFFERMANAGER_H
+
+#if defined(QT_USE_XCB_SHARED_GRAPHICS_CACHE)
+
+#include <QtCore/qset.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qsharedmemory.h>
+
+#include <GLES2/gl2.h>
+
+#include <EGL/egl.h>
+
+#include <EGL/eglext.h>
+
+class wl_resource;
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QXcbSharedBufferManager
+{
+public:
+    struct Buffer {
+        Buffer()
+            : width(-1)
+            , height(-1)
+            , bytesPerPixel(1)
+            , nextX(-1)
+            , nextY(-1)
+            , currentLineMaxHeight(0)
+            , next(0)
+            , prev(0)
+            , buffer(0)
+            , textureId(0)
+        {
+        }
+
+        ~Buffer()
+        {
+            delete buffer;
+
+            if (textureId != 0)
+                glDeleteTextures(1, &textureId);
+        }
+
+        QByteArray cacheId;
+        int width;
+        int height;
+        int bytesPerPixel;
+        int nextX;
+        int nextY;
+        int currentLineMaxHeight;
+
+        Buffer *next;
+        Buffer *prev;
+
+        QSharedMemory *buffer;
+
+        GLuint textureId;
+
+        QAtomicInt ref;
+    };
+
+    typedef QHash<QByteArray, QSet<quint32> > PendingItemIds;
+
+    QXcbSharedBufferManager();
+    ~QXcbSharedBufferManager();
+
+    void beginSharedBufferAction(const QByteArray &cacheId);
+    void insertItem(quint32 itemId, uchar *data, int itemWidth, int itemHeight);
+    void requestItems(const QSet<quint32> &itemIds);
+    void releaseItems(const QSet<quint32> &itemIds);
+    void endSharedBufferAction();
+
+    void getBufferForItem(const QByteArray &cacheId, quint32 itemId, Buffer **buffer,
+                          int *x, int *y) const;
+    QPair<QByteArray, int> serializeBuffer(QSharedMemory *buffer) const;
+
+    PendingItemIds pendingItemsInvalidated() const
+    {
+        Q_ASSERT_X(m_currentCacheId.isEmpty(), Q_FUNC_INFO,
+                   "Call endSharedBufferAction() before accessing data");
+        return m_pendingInvalidatedItems;
+    }
+
+    PendingItemIds pendingItemsReady() const
+    {
+        Q_ASSERT_X(m_currentCacheId.isEmpty(), Q_FUNC_INFO,
+                   "Call endSharedBufferAction() before accessing data");
+        return m_pendingReadyItems;
+    }
+
+    PendingItemIds pendingItemsMissing() const
+    {
+        Q_ASSERT_X(m_currentCacheId.isEmpty(), Q_FUNC_INFO,
+                   "Call endSharedBufferAction() before accessing data");
+        return m_pendingMissingItems;
+    }
+
+private:
+    struct Item {
+        Item()
+            : next(0)
+            , prev(0)
+            , buffer(0)
+            , itemId(0)
+            , x(-1)
+            , y(-1)
+            , width(-1)
+            , height(-1)
+        {
+        }
+
+        Item *next;
+        Item *prev;
+
+        Buffer *buffer;
+        quint32 itemId;
+        int x;
+        int y;
+        int width;
+        int height;
+    };
+
+    struct Items
+    {
+        Items() : leastRecentlyUsed(0), mostRecentlyUsed(0) {}
+
+        Item *leastRecentlyUsed;
+        Item *mostRecentlyUsed;
+
+        QByteArray cacheId;
+        QHash<quint32, Item *> items;
+    };
+
+    void findAvailableBuffer(int itemWidth, int itemHeight, Buffer **buffer, int *x, int *y);
+    void recycleItem(Buffer **buffer, int *x, int *y);
+    void copyIntoBuffer(Buffer *buffer, int x, int y, int itemWidth, int itemHeight, uchar *data);
+    void touchBuffer(Buffer *buffer);
+    void deleteLeastRecentlyUsed();
+
+    Buffer *createNewBuffer(const QByteArray &cacheId, int heightRequired);
+    Buffer *resizeBuffer(Buffer *buffer, const QSize &newSize);
+    Buffer *allocateBuffer(int width, int height);
+
+    Items *itemsForCache(const QByteArray &cacheId) const;
+    void pushItemToBack(Items *items, Item *item);
+    void touchItem(Items *items, Item *item);
+    void deleteItem(Items *items, Item *item);
+    void recycleItem(const QByteArray &cacheId, Buffer **sharedBuffer, int *glyphX, int *glyphY);
+
+    QByteArray m_currentCacheId;
+
+    quint32 m_memoryUsed;
+    Buffer *m_mostRecentlyUsed;
+    Buffer *m_leastRecentlyUsed;
+
+    mutable QHash<QByteArray, Items *> m_items;
+    QMultiHash<QByteArray, Buffer *> m_buffers;
+
+    PendingItemIds m_pendingInvalidatedItems;
+    PendingItemIds m_pendingReadyItems;
+    PendingItemIds m_pendingMissingItems;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_USE_XCB_SHARED_GRAPHICS_CACHE
+
+#endif // XCBSHAREDBUFFERMANAGER_H
diff --git a/src/plugins/platforms/xcb/qxcbsharedgraphicscache.cpp b/src/plugins/platforms/xcb/qxcbsharedgraphicscache.cpp
new file mode 100644 (file)
index 0000000..aa13138
--- /dev/null
@@ -0,0 +1,290 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used 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. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#if defined(QT_USE_XCB_SHARED_GRAPHICS_CACHE)
+
+#include "qxcbsharedgraphicscache.h"
+#include "qxcbsharedbuffermanager.h"
+
+#include <QtCore/qsharedmemory.h>
+
+#include <QtGui/qopenglcontext.h>
+#include <QtGui/qscreen.h>
+
+#define GL_GLEXT_PROTOTYPES
+#include <GLES2/gl2ext.h>
+
+#define SHAREDGRAPHICSCACHE_DEBUG 1
+
+QT_BEGIN_NAMESPACE
+
+QXcbSharedGraphicsCache::QXcbSharedGraphicsCache(QObject *parent)
+    : QPlatformSharedGraphicsCache(parent)
+    , m_bufferManager(new QXcbSharedBufferManager)
+{
+}
+
+void QXcbSharedGraphicsCache::requestItems(const QByteArray &cacheId,
+                                                  const QVector<quint32> &itemIds)
+{
+    m_bufferManager->beginSharedBufferAction(cacheId);
+
+    QSet<quint32> itemsForRequest;
+    for (int i=0; i<itemIds.size(); ++i)
+        itemsForRequest.insert(itemIds.at(i));
+
+    m_bufferManager->requestItems(itemsForRequest);
+    m_bufferManager->endSharedBufferAction();
+
+    processPendingItems();
+}
+
+void QXcbSharedGraphicsCache::insertItems(const QByteArray &cacheId,
+                                                 const QVector<quint32> &itemIds,
+                                                 const QVector<QImage> &items)
+{
+    m_bufferManager->beginSharedBufferAction(cacheId);
+
+    QSet<quint32> itemsForRequest;
+    for (int i=0; i<itemIds.size(); ++i) {
+        QImage image = items.at(i);
+        m_bufferManager->insertItem(itemIds.at(i), image.bits(), image.width(), image.height());
+        itemsForRequest.insert(itemIds.at(i));
+    }
+
+    // ### To avoid loops, we could check missing items here and notify the client
+    m_bufferManager->requestItems(itemsForRequest);
+
+    m_bufferManager->endSharedBufferAction();
+
+    processPendingItems();
+}
+
+void QXcbSharedGraphicsCache::ensureCacheInitialized(const QByteArray &cacheId,
+                                                            BufferType bufferType,
+                                                            PixelFormat pixelFormat)
+{
+    Q_UNUSED(cacheId);
+    Q_UNUSED(bufferType);
+    Q_UNUSED(pixelFormat);
+}
+
+
+void QXcbSharedGraphicsCache::releaseItems(const QByteArray &cacheId,
+                                                  const QVector<quint32> &itemIds)
+{
+    m_bufferManager->beginSharedBufferAction(cacheId);
+
+    QSet<quint32> itemsToRelease;
+    for (int i=0; i<itemIds.size(); ++i)
+        itemsToRelease.insert(itemIds.at(i));
+
+    m_bufferManager->releaseItems(itemsToRelease);
+
+    m_bufferManager->endSharedBufferAction();
+
+    processPendingItems();
+}
+
+void QXcbSharedGraphicsCache::serializeBuffer(void *bufferId,
+                                                     QByteArray *serializedData,
+                                                     int *fileDescriptor) const
+{
+    QXcbSharedBufferManager::Buffer *buffer =
+            reinterpret_cast<QXcbSharedBufferManager::Buffer *>(bufferId);
+
+    QPair<QByteArray, int> bufferName = m_bufferManager->serializeBuffer(buffer->buffer);
+
+    *serializedData = bufferName.first;
+    *fileDescriptor = bufferName.second;
+}
+
+GLuint QXcbSharedGraphicsCache::textureIdForBuffer(void *bufferId)
+{
+#  if defined(SHAREDGRAPHICSCACHE_DEBUG)
+    qDebug("QXcbSharedGraphicsCache::textureIdForBuffer");
+#  endif
+
+    QXcbSharedBufferManager::Buffer *buffer =
+            reinterpret_cast<QXcbSharedBufferManager::Buffer *>(bufferId);
+
+    if (buffer->textureId == 0) {
+        glGenTextures(1, &buffer->textureId);
+        if (buffer->textureId == 0) {
+            qWarning("QXcbSharedGraphicsCache::textureIdForBuffer: Failed to generate texture (gl error: 0x%x)",
+                     glGetError());
+            return 0;
+        }
+
+        glBindTexture(GL_TEXTURE_2D, buffer->textureId);
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    }
+
+    glBindTexture(GL_TEXTURE_2D, buffer->textureId);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, buffer->width, buffer->height, 0, GL_ALPHA,
+                 GL_UNSIGNED_BYTE, buffer->buffer->data());
+    glBindTexture(GL_TEXTURE_2D, 0);
+
+    return buffer->textureId;
+}
+
+void QXcbSharedGraphicsCache::referenceBuffer(void *bufferId)
+{
+    QXcbSharedBufferManager::Buffer *buffer =
+        reinterpret_cast<QXcbSharedBufferManager::Buffer *>(bufferId);
+
+    buffer->ref.ref();
+}
+
+bool QXcbSharedGraphicsCache::dereferenceBuffer(void *bufferId)
+{
+    QXcbSharedBufferManager::Buffer *buffer =
+            reinterpret_cast<QXcbSharedBufferManager::Buffer *>(bufferId);
+
+    if (buffer->ref.deref())
+        return true;
+
+    if (buffer->textureId != 0) {
+        glDeleteTextures(1, &buffer->textureId);
+        buffer->textureId = 0;
+    }
+
+    return false;
+}
+
+void QXcbSharedGraphicsCache::processPendingItems()
+{
+#  if defined(SHAREDGRAPHICSCACHE_DEBUG)
+        qDebug("QXcbSharedGraphicsCache::processPendingItems");
+#  endif
+
+    {
+        QXcbSharedBufferManager::PendingItemIds pendingMissingItems = m_bufferManager->pendingItemsMissing();
+        QXcbSharedBufferManager::PendingItemIds::const_iterator it;
+
+
+        for (it = pendingMissingItems.constBegin(); it != pendingMissingItems.constEnd(); ++it) {
+            QVector<quint32> missingItems;
+
+            const QSet<quint32> &items = it.value();
+            QSet<quint32>::const_iterator itemIt;
+            for (itemIt = items.constBegin(); itemIt != items.constEnd(); ++itemIt)
+                missingItems.append(*itemIt);
+
+#  if defined(SHAREDGRAPHICSCACHE_DEBUG)
+        qDebug("QXcbSharedGraphicsCache::processPendingItems: %d missing items",
+               missingItems.size());
+#  endif
+
+            if (!missingItems.isEmpty())
+                emit itemsMissing(it.key(), missingItems);
+        }
+    }
+
+    {
+        QXcbSharedBufferManager::PendingItemIds pendingInvalidatedItems = m_bufferManager->pendingItemsInvalidated();
+        QXcbSharedBufferManager::PendingItemIds::const_iterator it;
+
+        for (it = pendingInvalidatedItems.constBegin(); it != pendingInvalidatedItems.constEnd(); ++it) {
+            QVector<quint32> invalidatedItems;
+
+            const QSet<quint32> &items = it.value();
+            QSet<quint32>::const_iterator itemIt;
+            for (itemIt = items.constBegin(); itemIt != items.constEnd(); ++itemIt)
+                invalidatedItems.append(*itemIt);
+
+#  if defined(SHAREDGRAPHICSCACHE_DEBUG)
+            qDebug("QXcbSharedGraphicsCache::processPendingItems: %d invalidated items",
+               invalidatedItems.size());
+#  endif
+
+            if (!invalidatedItems.isEmpty())
+                emit itemsInvalidated(it.key(), invalidatedItems);
+        }
+    }
+
+    {
+        QXcbSharedBufferManager::PendingItemIds pendingReadyItems = m_bufferManager->pendingItemsReady();
+        QXcbSharedBufferManager::PendingItemIds::const_iterator it;
+
+        for (it = pendingReadyItems.constBegin(); it != pendingReadyItems.constEnd(); ++it) {
+            QHash<QXcbSharedBufferManager::Buffer *, ReadyItem> readyItemsForBuffer;
+            const QSet<quint32> &items = it.value();
+
+            QByteArray cacheId = it.key();
+
+            QSet<quint32>::const_iterator itemIt;
+            for (itemIt = items.constBegin(); itemIt != items.constEnd(); ++itemIt) {
+                QXcbSharedBufferManager::Buffer *buffer;
+                int x = -1;
+                int y = -1;
+
+                m_bufferManager->getBufferForItem(cacheId, *itemIt, &buffer, &x, &y);
+
+                readyItemsForBuffer[buffer].itemIds.append(*itemIt);
+                readyItemsForBuffer[buffer].positions.append(QPoint(x, y));
+            }
+
+            QHash<QXcbSharedBufferManager::Buffer*, ReadyItem>::iterator readyItemIt
+                    = readyItemsForBuffer.begin();
+            while (readyItemIt != readyItemsForBuffer.end()) {
+                QXcbSharedBufferManager::Buffer *buffer = readyItemIt.key();
+                if (!readyItemIt.value().itemIds.isEmpty()) {
+#  if defined(SHAREDGRAPHICSCACHE_DEBUG)
+                    qDebug("QXcbSharedGraphicsCache::processPendingItems: %d ready items",
+                            readyItemIt.value().itemIds.size());
+#  endif
+
+                    emit itemsAvailable(cacheId, buffer, QSize(buffer->width, buffer->height),
+                                        readyItemIt.value().itemIds, readyItemIt.value().positions);
+                }
+                ++readyItemIt;
+            }
+        }
+    }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_USE_XCB_SHARED_GRAPHICS_CACHE
diff --git a/src/plugins/platforms/xcb/qxcbsharedgraphicscache.h b/src/plugins/platforms/xcb/qxcbsharedgraphicscache.h
new file mode 100644 (file)
index 0000000..2b37c33
--- /dev/null
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used 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. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QXCBSHAREDGRAPHICSCACHE
+#define QXCBSHAREDGRAPHICSCACHE
+
+#if defined(QT_USE_XCB_SHARED_GRAPHICS_CACHE)
+
+#include <QtGui/qplatformsharedgraphicscache_qpa.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QXcbSharedBufferManager;
+class QXcbSharedGraphicsCache : public QPlatformSharedGraphicsCache
+{
+    Q_OBJECT
+public:
+    explicit QXcbSharedGraphicsCache(QObject *parent = 0);
+
+    virtual void ensureCacheInitialized(const QByteArray &cacheId, BufferType bufferType,
+                                        PixelFormat pixelFormat);
+
+    virtual void requestItems(const QByteArray &cacheId, const QVector<quint32> &itemIds);
+    virtual void insertItems(const QByteArray &cacheId,
+                             const QVector<quint32> &itemIds,
+                             const QVector<QImage> &items);
+    virtual void releaseItems(const QByteArray &cacheId, const QVector<quint32> &itemIds);
+
+    virtual void serializeBuffer(void *bufferId, QByteArray *serializedData, int *fileDescriptor) const;
+    virtual uint textureIdForBuffer(void *bufferId);
+    virtual void referenceBuffer(void *bufferId);
+    virtual bool dereferenceBuffer(void *bufferId);
+
+private:
+    struct ReadyItem {
+        QVector<quint32> itemIds;
+        QVector<QPoint> positions;
+    };
+
+    void processPendingItems();
+
+    QXcbSharedBufferManager *m_bufferManager;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_USE_XCB_SHARED_GRAPHICS_CACHE
+
+#endif // QXCBSHAREDGRAPHICSCACHE
index 823a12b..d80a6df 100644 (file)
@@ -5,6 +5,7 @@ QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/platforms
 
 QT += core-private gui-private platformsupport-private
 
+
 SOURCES = \
         qxcbclipboard.cpp \
         qxcbconnection.cpp \
@@ -19,7 +20,9 @@ SOURCES = \
         main.cpp \
         qxcbnativeinterface.cpp \
         qxcbcursor.cpp \
-        qxcbimage.cpp
+        qxcbimage.cpp \
+        qxcbsharedbuffermanager.cpp \
+        qxcbsharedgraphicscache.cpp
 
 HEADERS = \
         qxcbclipboard.h \
@@ -35,7 +38,9 @@ HEADERS = \
         qxcbwmsupport.h \
         qxcbnativeinterface.h \
         qxcbcursor.h \
-        qxcbimage.h
+        qxcbimage.h \
+        qxcbsharedbuffermanager.h \
+        qxcbsharedgraphicscache.h
 
 contains(QT_CONFIG, xcb-poll-for-queued-event) {
     DEFINES += XCB_POLL_FOR_QUEUED_EVENT