summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com>2011-11-18 10:24:32 +0100
committerQt by Nokia <qt-info@nokia.com>2012-01-20 14:17:41 +0100
commitc274ea7cf5e11ade0a5812e3080baa671d012524 (patch)
tree86ebcf918989dc674b0e82700cf0c123ba44e49e
parent47d5d349d8f5bc590ca7cdf30bf13b69bddf3225 (diff)
Introducing QPlatformSharedGraphicsCache
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>
-rw-r--r--src/gui/kernel/kernel.pri6
-rw-r--r--src/gui/kernel/qplatformintegration_qpa.cpp11
-rw-r--r--src/gui/kernel/qplatformintegration_qpa.h6
-rw-r--r--src/gui/kernel/qplatformsharedgraphicscache_qpa.cpp212
-rw-r--r--src/gui/kernel/qplatformsharedgraphicscache_qpa.h97
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.cpp29
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.h8
-rw-r--r--src/plugins/platforms/xcb/qxcbsharedbuffermanager.cpp640
-rw-r--r--src/plugins/platforms/xcb/qxcbsharedbuffermanager.h215
-rw-r--r--src/plugins/platforms/xcb/qxcbsharedgraphicscache.cpp290
-rw-r--r--src/plugins/platforms/xcb/qxcbsharedgraphicscache.h91
-rw-r--r--src/plugins/platforms/xcb/xcb.pro9
12 files changed, 1609 insertions, 5 deletions
diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri
index bf552c9991..12cb42c741 100644
--- a/src/gui/kernel/kernel.pri
+++ b/src/gui/kernel/kernel.pri
@@ -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
diff --git a/src/gui/kernel/qplatformintegration_qpa.cpp b/src/gui/kernel/qplatformintegration_qpa.cpp
index 23ecf3add4..d1e267db37 100644
--- a/src/gui/kernel/qplatformintegration_qpa.cpp
+++ b/src/gui/kernel/qplatformintegration_qpa.cpp
@@ -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.
diff --git a/src/gui/kernel/qplatformintegration_qpa.h b/src/gui/kernel/qplatformintegration_qpa.h
index 3975d82288..3043ba801d 100644
--- a/src/gui/kernel/qplatformintegration_qpa.h
+++ b/src/gui/kernel/qplatformintegration_qpa.h
@@ -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
index 0000000000..67a59b0e44
--- /dev/null
+++ b/src/gui/kernel/qplatformsharedgraphicscache_qpa.cpp
@@ -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
index 0000000000..a0661c0ba7
--- /dev/null
+++ b/src/gui/kernel/qplatformsharedgraphicscache_qpa.h
@@ -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
diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp
index 418915bd15..0bed8c6fbb 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.cpp
+++ b/src/plugins/platforms/xcb/qxcbintegration.cpp
@@ -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
diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h
index 2bb5f1e65e..fc769429cb 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.h
+++ b/src/plugins/platforms/xcb/qxcbintegration.h
@@ -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
index 0000000000..51e3e85f10
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbsharedbuffermanager.cpp
@@ -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
index 0000000000..c815336c05
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbsharedbuffermanager.h
@@ -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
index 0000000000..aa13138ee9
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbsharedgraphicscache.cpp
@@ -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
index 0000000000..2b37c334b7
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbsharedgraphicscache.h
@@ -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
diff --git a/src/plugins/platforms/xcb/xcb.pro b/src/plugins/platforms/xcb/xcb.pro
index 823a12b0f4..d80a6df0b6 100644
--- a/src/plugins/platforms/xcb/xcb.pro
+++ b/src/plugins/platforms/xcb/xcb.pro
@@ -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