Cocoa: reimplement QPlatformBackingStore::scroll()
[profile/ivi/qtbase.git] / src / plugins / platforms / xcb / qxcbsharedbuffermanager.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #if defined(QT_USE_XCB_SHARED_GRAPHICS_CACHE)
43
44 #include "qxcbsharedbuffermanager.h"
45
46 #include <QtCore/quuid.h>
47 #include <QtGui/qimage.h>
48
49 #include <stdio.h>
50
51 #if !defined(SHAREDGRAPHICSCACHE_MAX_MEMORY_USED)
52 #  define SHAREDGRAPHICSCACHE_MAX_MEMORY_USED 16 * 1024 * 1024 // 16 MB limit
53 #endif
54
55 #if !defined(SHAREDGRAPHICSCACHE_MAX_TEXTURES_PER_CACHE)
56 #  define SHAREDGRAPHICSCACHE_MAX_TEXTURES_PER_CACHE 1
57 #endif
58
59 #if !defined(SHAREDGRAPHICSCACHE_TEXTURE_SIZE)
60 #  define SHAREDGRAPHICSCACHE_TEXTURE_SIZE 2048
61 #endif
62
63 #define SHAREDBUFFERMANAGER_DEBUG 1
64
65 QT_BEGIN_NAMESPACE
66
67 QXcbSharedBufferManager::QXcbSharedBufferManager()
68     : m_memoryUsed(0)
69     , m_mostRecentlyUsed(0)
70     , m_leastRecentlyUsed(0)
71 {
72 }
73
74 QXcbSharedBufferManager::~QXcbSharedBufferManager()
75 {
76     {
77         QHash<QByteArray, Buffer *>::const_iterator it = m_buffers.constBegin();
78         while (it != m_buffers.constEnd()) {
79             Buffer *buffer = it.value();
80             delete buffer;
81             ++it;
82         }
83     }
84
85     {
86         QHash<QByteArray, Items *>::const_iterator it = m_items.constBegin();
87         while (it != m_items.constEnd()) {
88             Items *items = it.value();
89             QHash<quint32, Item *>::const_iterator itemIt = items->items.constBegin();
90             while (itemIt != items->items.constEnd()) {
91                 delete itemIt.value();
92                 ++itemIt;
93             }
94             delete it.value();
95             ++it;
96         }
97     }
98 }
99
100 void QXcbSharedBufferManager::getBufferForItem(const QByteArray &cacheId, quint32 itemId,
101                                                       Buffer **buffer, int *x, int *y) const
102 {
103     Q_ASSERT_X(m_currentCacheId.isEmpty(), Q_FUNC_INFO,
104                "Call endSharedBufferAction before accessing data");
105
106     Q_ASSERT(buffer != 0);
107     Q_ASSERT(x != 0);
108     Q_ASSERT(y != 0);
109
110     Items *items = itemsForCache(cacheId);
111     Item *item = items->items.value(itemId);
112     if (item != 0) {
113         *buffer = item->buffer;
114         *x = item->x;
115         *y = item->y;
116     } else {
117         *buffer = 0;
118         *x = -1;
119         *y = -1;
120     }
121 }
122
123 QPair<QByteArray, int> QXcbSharedBufferManager::serializeBuffer(QSharedMemory *buffer) const
124 {
125     Q_ASSERT_X(m_currentCacheId.isEmpty(), Q_FUNC_INFO,
126                "Call endSharedBufferAction before accessing data");
127
128     return qMakePair(buffer->key().toLatin1(), 0);
129 }
130
131 void QXcbSharedBufferManager::beginSharedBufferAction(const QByteArray &cacheId)
132 {
133 #if defined(SHAREDBUFFERMANAGER_DEBUG)
134     qDebug("QXcbSharedBufferManager::beginSharedBufferAction() called for %s", cacheId.constData());
135 #endif
136
137     Q_ASSERT(m_currentCacheId.isEmpty());
138     Q_ASSERT(!cacheId.isEmpty());
139
140     m_pendingInvalidatedItems.clear();
141     m_pendingReadyItems.clear();
142     m_pendingMissingItems.clear();
143
144     m_currentCacheId = cacheId;
145 }
146
147 void QXcbSharedBufferManager::requestItems(const QSet<quint32> &itemIds)
148 {
149 #if defined(SHAREDBUFFERMANAGER_DEBUG)
150     qDebug("QXcbSharedBufferManager::requestItems for %d items", itemIds.size());
151 #endif
152
153     Q_ASSERT_X(!m_currentCacheId.isEmpty(), Q_FUNC_INFO,
154                "Call beginSharedBufferAction before requesting items");
155     Items *items = itemsForCache(m_currentCacheId);
156
157     QSet<quint32>::const_iterator it = itemIds.constBegin();
158     while (it != itemIds.constEnd()) {
159         if (items->items.contains(*it))
160             m_pendingReadyItems[m_currentCacheId].insert(*it);
161         else
162             m_pendingMissingItems[m_currentCacheId].insert(*it);
163         ++it;
164     }
165 }
166
167 void QXcbSharedBufferManager::releaseItems(const QSet<quint32> &itemIds)
168 {
169 #if defined(SHAREDBUFFERMANAGER_DEBUG)
170     qDebug("QXcbSharedBufferManager::releaseItems for %d items", itemIds.size());
171 #endif
172
173     Items *items = itemsForCache(m_currentCacheId);
174
175     QSet<quint32>::const_iterator it;
176     for (it = itemIds.constBegin(); it != itemIds.constEnd(); ++it) {
177         Item *item = items->items.value(*it);
178         if (item != 0)
179             pushItemToBack(items, item);
180
181         m_pendingReadyItems[m_currentCacheId].remove(*it);
182         m_pendingMissingItems[m_currentCacheId].remove(*it);
183     }
184 }
185
186 void QXcbSharedBufferManager::insertItem(quint32 itemId, uchar *data,
187                                                 int itemWidth, int itemHeight)
188 {
189     Q_ASSERT_X(!m_currentCacheId.isEmpty(), Q_FUNC_INFO,
190                "Call beginSharedBufferAction before inserting items");
191     Items *items = itemsForCache(m_currentCacheId);
192
193     if (!items->items.contains(itemId)) {
194         Buffer *sharedBuffer = 0;
195         int x = 0;
196         int y = 0;
197
198         findAvailableBuffer(itemWidth, itemHeight, &sharedBuffer, &x, &y);
199         copyIntoBuffer(sharedBuffer, x, y, itemWidth, itemHeight, data);
200
201 //        static int counter=0;
202 //        QString fileName = QString::fromLatin1("buffer%1.png").arg(counter++);
203 //        saveBuffer(sharedBuffer, fileName);
204
205         Item *item = new Item;
206         item->itemId = itemId;
207         item->buffer = sharedBuffer;
208         item->x = x;
209         item->y = y;
210
211         items->items[itemId] = item;
212
213         touchItem(items, item);
214     }
215 }
216
217 void QXcbSharedBufferManager::endSharedBufferAction()
218 {
219 #if defined(SHAREDBUFFERMANAGER_DEBUG)
220     qDebug("QXcbSharedBufferManager::endSharedBufferAction() called for %s",
221            m_currentCacheId.constData());
222 #endif
223
224     Q_ASSERT(!m_currentCacheId.isEmpty());
225
226     // Do an extra validation pass on the invalidated items since they may have been re-inserted
227     // after they were invalidated
228     if (m_pendingInvalidatedItems.contains(m_currentCacheId)) {
229         QSet<quint32> &invalidatedItems = m_pendingInvalidatedItems[m_currentCacheId];
230         QSet<quint32>::iterator it = invalidatedItems.begin();
231         while (it != invalidatedItems.end()) {
232             Items *items = m_items.value(m_currentCacheId);
233
234             if (items->items.contains(*it)) {
235                 m_pendingReadyItems[m_currentCacheId].insert(*it);
236                 it = invalidatedItems.erase(it);
237             } else {
238                 ++it;
239             }
240         }
241     }
242
243     m_currentCacheId.clear();
244 }
245
246 void QXcbSharedBufferManager::pushItemToBack(Items *items, Item *item)
247 {
248     if (items->leastRecentlyUsed == item)
249         return;
250
251     if (item->next != 0)
252         item->next->prev = item->prev;
253     if (item->prev != 0)
254         item->prev->next = item->next;
255
256     if (items->mostRecentlyUsed == item)
257         items->mostRecentlyUsed = item->prev;
258
259     if (items->leastRecentlyUsed != 0)
260         items->leastRecentlyUsed->prev = item;
261
262     item->prev = 0;
263     item->next = items->leastRecentlyUsed;
264     items->leastRecentlyUsed = item;
265     if (items->mostRecentlyUsed == 0)
266         items->mostRecentlyUsed = item;
267 }
268
269 void QXcbSharedBufferManager::touchItem(Items *items, Item *item)
270 {
271     if (items->mostRecentlyUsed == item)
272         return;
273
274     if (item->next != 0)
275         item->next->prev = item->prev;
276     if (item->prev != 0)
277         item->prev->next = item->next;
278
279     if (items->leastRecentlyUsed == item)
280         items->leastRecentlyUsed = item->next;
281
282     if (items->mostRecentlyUsed != 0)
283         items->mostRecentlyUsed->next = item;
284
285     item->next = 0;
286     item->prev = items->mostRecentlyUsed;
287     items->mostRecentlyUsed = item;
288     if (items->leastRecentlyUsed == 0)
289         items->leastRecentlyUsed = item;
290 }
291
292 void QXcbSharedBufferManager::deleteItem(Items *items, Item *item)
293 {
294     Q_ASSERT(items != 0);
295     Q_ASSERT(item != 0);
296
297     if (items->mostRecentlyUsed == item)
298         items->mostRecentlyUsed = item->prev;
299     if (items->leastRecentlyUsed == item)
300         items->leastRecentlyUsed = item->next;
301
302     if (item->next != 0)
303         item->next->prev = item->prev;
304     if (item->prev != 0)
305         item->prev->next = item->next;
306
307     m_pendingInvalidatedItems[items->cacheId].insert(item->itemId);
308
309     {
310         QHash<quint32, Item *>::iterator it = items->items.find(item->itemId);
311         while (it != items->items.end() && it.value()->itemId == item->itemId)
312             it = items->items.erase(it);
313     }
314
315     delete item;
316 }
317
318 void QXcbSharedBufferManager::recycleItem(Buffer **sharedBuffer, int *glyphX, int *glyphY)
319 {
320 #if defined(SHAREDBUFFERMANAGER_DEBUG)
321     qDebug("QXcbSharedBufferManager::recycleItem() called for %s", m_currentCacheId.constData());
322 #endif
323
324     Items *items = itemsForCache(m_currentCacheId);
325
326     Item *recycledItem = items->leastRecentlyUsed;
327     Q_ASSERT(recycledItem != 0);
328
329     *sharedBuffer = recycledItem->buffer;
330     *glyphX = recycledItem->x;
331     *glyphY = recycledItem->y;
332
333     deleteItem(items, recycledItem);
334 }
335
336 void QXcbSharedBufferManager::touchBuffer(Buffer *buffer)
337 {
338 #if defined(SHAREDBUFFERMANAGER_DEBUG)
339     qDebug("QXcbSharedBufferManager::touchBuffer() called for %s", buffer->cacheId.constData());
340 #endif
341
342     if (buffer == m_mostRecentlyUsed)
343         return;
344
345     if (buffer->next != 0)
346         buffer->next->prev = buffer->prev;
347     if (buffer->prev != 0)
348         buffer->prev->next = buffer->next;
349
350     if (m_leastRecentlyUsed == buffer)
351         m_leastRecentlyUsed = buffer->next;
352
353     buffer->next = 0;
354     buffer->prev = m_mostRecentlyUsed;
355     if (m_mostRecentlyUsed != 0)
356         m_mostRecentlyUsed->next = buffer;
357     if (m_leastRecentlyUsed == 0)
358         m_leastRecentlyUsed = buffer;
359     m_mostRecentlyUsed = buffer;
360 }
361
362 void QXcbSharedBufferManager::deleteLeastRecentlyUsed()
363 {
364 #if defined(SHAREDBUFFERMANAGER_DEBUG)
365     qDebug("QXcbSharedBufferManager::deleteLeastRecentlyUsed() called");
366 #endif
367
368     if (m_leastRecentlyUsed == 0)
369         return;
370
371     Buffer *old = m_leastRecentlyUsed;
372     m_leastRecentlyUsed = old->next;
373     m_leastRecentlyUsed->prev = 0;
374
375     QByteArray cacheId = old->cacheId;
376     Items *items = itemsForCache(cacheId);
377
378     QHash<quint32, Item *>::iterator it = items->items.begin();
379     while (it != items->items.end()) {
380         Item *item = it.value();
381         if (item->buffer == old) {
382             deleteItem(items, item);
383             it = items->items.erase(it);
384         } else {
385             ++it;
386         }
387     }
388
389     m_buffers.remove(cacheId, old);
390     m_memoryUsed -= old->width * old->height * old->bytesPerPixel;
391
392 #if defined(SHAREDBUFFERMANAGER_DEBUG)
393     qDebug("QXcbSharedBufferManager::deleteLeastRecentlyUsed: Memory used: %d / %d (%6.2f %%)",
394            m_memoryUsed, SHAREDGRAPHICSCACHE_MAX_MEMORY_USED,
395            100.0f * float(m_memoryUsed) / float(SHAREDGRAPHICSCACHE_MAX_MEMORY_USED));
396 #endif
397
398     delete old;
399 }
400
401 QXcbSharedBufferManager::Buffer *QXcbSharedBufferManager::createNewBuffer(const QByteArray &cacheId,
402                                                                   int heightRequired)
403 {
404 #if defined(SHAREDBUFFERMANAGER_DEBUG)
405     qDebug("QXcbSharedBufferManager::createNewBuffer() called for %s", cacheId.constData());
406 #endif
407
408     // ###
409     // if (bufferCount of cacheId == SHAREDGRAPHICACHE_MAX_TEXTURES_PER_CACHE)
410     //    deleteLeastRecentlyUsedBufferForCache(cacheId);
411
412     // ### Take pixel format into account
413     while (m_memoryUsed + SHAREDGRAPHICSCACHE_TEXTURE_SIZE * heightRequired >= SHAREDGRAPHICSCACHE_MAX_MEMORY_USED)
414         deleteLeastRecentlyUsed();
415
416     Buffer *buffer = allocateBuffer(SHAREDGRAPHICSCACHE_TEXTURE_SIZE, heightRequired);
417     buffer->cacheId = cacheId;
418
419     buffer->currentLineMaxHeight = 0;
420     m_buffers.insert(cacheId, buffer);
421
422     return buffer;
423 }
424
425 static inline int qt_next_power_of_two(int v)
426 {
427     v--;
428     v |= v >> 1;
429     v |= v >> 2;
430     v |= v >> 4;
431     v |= v >> 8;
432     v |= v >> 16;
433     ++v;
434     return v;
435 }
436
437 QXcbSharedBufferManager::Buffer *QXcbSharedBufferManager::resizeBuffer(Buffer *oldBuffer, const QSize &newSize)
438 {
439 #if defined(SHAREDBUFFERMANAGER_DEBUG)
440     qDebug("QXcbSharedBufferManager::resizeBuffer() called for %s (current size: %dx%d, new size: %dx%d)",
441            oldBuffer->cacheId.constData(), oldBuffer->width, oldBuffer->height,
442            newSize.width(), newSize.height());
443 #endif
444
445     // Remove old buffer from lists to avoid deleting it under our feet
446     if (m_leastRecentlyUsed == oldBuffer)
447         m_leastRecentlyUsed = oldBuffer->next;
448     if (m_mostRecentlyUsed == oldBuffer)
449         m_mostRecentlyUsed = oldBuffer->prev;
450
451     if (oldBuffer->prev != 0)
452         oldBuffer->prev->next = oldBuffer->next;
453     if (oldBuffer->next != 0)
454         oldBuffer->next->prev = oldBuffer->prev;
455
456     m_memoryUsed -= oldBuffer->width * oldBuffer->height * oldBuffer->bytesPerPixel;
457     m_buffers.remove(oldBuffer->cacheId, oldBuffer);
458
459 #if defined(SHAREDBUFFERMANAGER_DEBUG)
460     qDebug("QXcbSharedBufferManager::resizeBuffer:            Memory used: %d / %d (%6.2f %%)",
461            m_memoryUsed, SHAREDGRAPHICSCACHE_MAX_MEMORY_USED,
462            100.0f * float(m_memoryUsed) / float(SHAREDGRAPHICSCACHE_MAX_MEMORY_USED));
463 #endif
464
465     Buffer *resizedBuffer = createNewBuffer(oldBuffer->cacheId, newSize.height());
466     copyIntoBuffer(resizedBuffer, 0, 0, oldBuffer->width, oldBuffer->height,
467                    reinterpret_cast<uchar *>(oldBuffer->buffer->data()));
468
469     resizedBuffer->currentLineMaxHeight = oldBuffer->currentLineMaxHeight;
470
471     Items *items = itemsForCache(oldBuffer->cacheId);
472     QHash<quint32, Item *>::const_iterator it = items->items.constBegin();
473     while (it != items->items.constEnd()) {
474         Item *item = it.value();
475         if (item->buffer == oldBuffer) {
476             m_pendingReadyItems[oldBuffer->cacheId].insert(item->itemId);
477             item->buffer = resizedBuffer;
478         }
479         ++it;
480     }
481
482     resizedBuffer->nextX = oldBuffer->nextX;
483     resizedBuffer->nextY = oldBuffer->nextY;
484     resizedBuffer->currentLineMaxHeight = oldBuffer->currentLineMaxHeight;
485
486     delete oldBuffer;
487     return resizedBuffer;
488 }
489
490 void QXcbSharedBufferManager::findAvailableBuffer(int itemWidth, int itemHeight,
491                                               Buffer **sharedBuffer, int *glyphX, int *glyphY)
492 {
493     Q_ASSERT(sharedBuffer != 0);
494     Q_ASSERT(glyphX != 0);
495     Q_ASSERT(glyphY != 0);
496
497     QMultiHash<QByteArray, Buffer *>::iterator it = m_buffers.find(m_currentCacheId);
498
499     int bufferCount = 0;
500     while (it != m_buffers.end() && it.key() == m_currentCacheId) {
501         Buffer *buffer = it.value();
502
503         int x = buffer->nextX;
504         int y = buffer->nextY;
505         int width = buffer->width;
506         int height = buffer->height;
507
508         if (x + itemWidth <= width && y + itemHeight <= height) {
509             // There is space on the current line, put the item there
510             buffer->currentLineMaxHeight = qMax(buffer->currentLineMaxHeight, itemHeight);
511             *sharedBuffer = buffer;
512             *glyphX = x;
513             *glyphY = y;
514
515             buffer->nextX += itemWidth;
516
517             return;
518         } else if (itemWidth <= width && y + buffer->currentLineMaxHeight + itemHeight <= height) {
519             // There is space for a new line, put the item on the new line
520             buffer->nextX = 0;
521             buffer->nextY += buffer->currentLineMaxHeight;
522             buffer->currentLineMaxHeight = 0;
523
524             *sharedBuffer = buffer;
525             *glyphX = buffer->nextX;
526             *glyphY = buffer->nextY;
527
528             buffer->nextX += itemWidth;
529
530             return;
531         } else if (y + buffer->currentLineMaxHeight + itemHeight <= SHAREDGRAPHICSCACHE_TEXTURE_SIZE) {
532             // There is space if we resize the buffer, so we do that
533             int newHeight = qt_next_power_of_two(y + buffer->currentLineMaxHeight + itemHeight);
534             buffer = resizeBuffer(buffer, QSize(width, newHeight));
535
536             buffer->nextX = 0;
537             buffer->nextY += buffer->currentLineMaxHeight;
538             buffer->currentLineMaxHeight = 0;
539
540             *sharedBuffer = buffer;
541             *glyphX = buffer->nextX;
542             *glyphY = buffer->nextY;
543
544             buffer->nextX += itemWidth;
545             return;
546         }
547
548         bufferCount++;
549         ++it;
550     }
551
552     if (bufferCount == SHAREDGRAPHICSCACHE_MAX_TEXTURES_PER_CACHE) {
553         // There is no space in any buffer, and there is no space for a new buffer
554         // recycle an old item
555         recycleItem(sharedBuffer, glyphX, glyphY);
556     } else {
557         // Create a new buffer for the item
558         *sharedBuffer = createNewBuffer(m_currentCacheId, qt_next_power_of_two(itemHeight));
559         if (*sharedBuffer == 0) {
560             Q_ASSERT(false);
561             return;
562         }
563
564         *glyphX = (*sharedBuffer)->nextX;
565         *glyphY = (*sharedBuffer)->nextY;
566
567         (*sharedBuffer)->nextX += itemWidth;
568     }
569 }
570
571 QXcbSharedBufferManager::Buffer *QXcbSharedBufferManager::allocateBuffer(int width, int height)
572 {
573     Buffer *buffer = new Buffer;
574     buffer->nextX = 0;
575     buffer->nextY = 0;
576     buffer->width = width;
577     buffer->height = height;
578     buffer->bytesPerPixel = 1; // ### Use pixel format here
579
580     buffer->buffer = new QSharedMemory(QUuid::createUuid().toString());
581     bool ok = buffer->buffer->create(buffer->width * buffer->height * buffer->bytesPerPixel,
582                                      QSharedMemory::ReadWrite);
583     if (!ok) {
584         qWarning("QXcbSharedBufferManager::findAvailableBuffer: Can't create new buffer (%s)",
585                  qPrintable(buffer->buffer->errorString()));
586         delete buffer;
587         return 0;
588     }
589     qMemSet(buffer->buffer->data(), 0, buffer->buffer->size());
590
591     m_memoryUsed += buffer->width * buffer->height * buffer->bytesPerPixel;
592
593 #if defined(SHAREDBUFFERMANAGER_DEBUG)
594     qDebug("QXcbSharedBufferManager::allocateBuffer:          Memory used: %d / %d (%6.2f %%)",
595            int(m_memoryUsed), int(SHAREDGRAPHICSCACHE_MAX_MEMORY_USED),
596            100.0f * float(m_memoryUsed) / float(SHAREDGRAPHICSCACHE_MAX_MEMORY_USED));
597 #endif
598
599     return buffer;
600 }
601
602 void QXcbSharedBufferManager::copyIntoBuffer(Buffer *buffer,
603                                                     int bufferX, int bufferY, int width, int height,
604                                                     uchar *data)
605 {
606 #if defined(SHAREDBUFFERMANAGER_DEBUG)
607     qDebug("QXcbSharedBufferManager::copyIntoBuffer() called for %s (coords: %d, %d)",
608            buffer->cacheId.constData(), bufferX, bufferY);
609 #endif
610
611     Q_ASSERT(bufferX >= 0);
612     Q_ASSERT(bufferX + width <= buffer->width);
613     Q_ASSERT(bufferY >= 0);
614     Q_ASSERT(bufferY + height <= buffer->height);
615
616     uchar *dest = reinterpret_cast<uchar *>(buffer->buffer->data());
617     dest += bufferX + bufferY * buffer->width;
618     for (int y=0; y<height; ++y) {
619         qMemCopy(dest, data, width);
620
621         data += width;
622         dest += buffer->width;
623     }
624 }
625
626 QXcbSharedBufferManager::Items *QXcbSharedBufferManager::itemsForCache(const QByteArray &cacheId) const
627 {
628     Items *items = m_items.value(cacheId);
629     if (items == 0) {
630         items = new Items;
631         items->cacheId = cacheId;
632         m_items[cacheId] = items;
633     }
634
635     return items;
636 }
637
638 QT_END_NAMESPACE
639
640 #endif // QT_USE_XCB_SHARED_GRAPHICS_CACHE