Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_tile_data_store.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com>
3 * SPDX-FileCopyrightText: 2018 Andrey Kamakin <a.kamakin@icloud.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8// to disable assert when the leak tracker is active
9#include "config-memory-leak-tracker.h"
10
11#include <QGlobalStatic>
12
13#include "kis_tile_data_store.h"
14#include "kis_tile_data.h"
15#include "kis_debug.h"
16
18
20
21//#define DEBUG_PRECLONE
22
23#ifdef DEBUG_PRECLONE
24#include <stdio.h>
25#define DEBUG_PRECLONE_ACTION(action, oldTD, newTD) \
26 printf("!!! %s:\t\t\t 0x%X -> 0x%X \t\t!!!\n", \
27 action, (quintptr)oldTD, (quintptr) newTD)
28#define DEBUG_FREE_ACTION(td) \
29 printf("Tile data free'd \t(0x%X)\n", td)
30#else
31#define DEBUG_PRECLONE_ACTION(action, oldTD, newTD)
32#define DEBUG_FREE_ACTION(td)
33#endif
34
35#ifdef DEBUG_HIT_MISS
36qint64 __preclone_miss = 0;
37qint64 __preclone_hit = 0;
38
39qint64 __preclone_miss_user_count = 0;
40qint64 __preclone_miss_age = 0;
41
42#define DEBUG_COUNT_PRECLONE_HIT(td) __preclone_hit++
43#define DEBUG_COUNT_PRECLONE_MISS(td) __preclone_miss++; __preclone_miss_user_count+=td->numUsers(); __preclone_miss_age+=td->age()
44#define DEBUG_REPORT_PRECLONE_EFFICIENCY() \
45 dbgKrita << "Hits:" << __preclone_hit \
46 << "of" << __preclone_hit + __preclone_miss \
47 << "(" \
48 << qreal(__preclone_hit) / (__preclone_hit + __preclone_miss) \
49 << ")" \
50 << "miss users" << qreal(__preclone_miss_user_count) / __preclone_miss \
51 << "miss age" << qreal(__preclone_miss_age) / __preclone_miss
52#else
53#define DEBUG_COUNT_PRECLONE_HIT(td)
54#define DEBUG_COUNT_PRECLONE_MISS(td)
55#define DEBUG_REPORT_PRECLONE_EFFICIENCY()
56#endif
57
59 : m_pooler(this),
60 m_swapper(this),
61 m_numTiles(0),
62 m_memoryMetric(0),
63 m_counter(1),
64 m_clockIndex(1)
65{
66 m_pooler.start();
67 m_swapper.start();
68}
69
71{
74
75 if (numTiles() > 0) {
76 errKrita << "Warning: some tiles have leaked:";
77 errKrita << "\tTiles in memory:" << numTilesInMemory() << "\n"
78 << "\tTotal tiles:" << numTiles();
79 }
80}
81
83{
84 return s_instance;
85}
86
88{
89 QReadLocker lock(&m_iteratorLock);
90
91 MemoryStatistics stats;
92
93 const qint64 metricCoeff = qint64(KisTileData::WIDTH) * KisTileData::HEIGHT;
94
95 stats.realMemorySize = m_pooler.lastRealMemoryMetric() * metricCoeff;
97 stats.poolSize = m_pooler.lastPoolMemoryMetric() * metricCoeff;
98
99 stats.totalMemorySize = memoryMetric() * metricCoeff + stats.poolSize;
100
102
103 return stats;
104}
105
107{
108 // in case the pooler is disabled, we should force it
109 // to update the stats
110 if (!m_pooler.isRunning()) {
112 }
113}
114
116{
117 int index = m_counter.fetchAndAddOrdered(1);
118 td->m_tileNumber = index;
119
120 // make sure that access to the hash table is guarded by GC block
121 // (it avoids removal of the referenced cells caused by concurrent
122 // migrations)
124 m_tileDataMap.assign(index, td);
127
128 m_numTiles.ref();
129 m_memoryMetric += td->pixelSize();
130}
131
133{
134 QReadLocker lock(&m_iteratorLock);
136}
137
139{
140 // make sure that access to the hash table is guarded by GC block
141 // (it avoids removal of the referenced cells caused by concurrent
142 // migrations)
144
145 if (m_clockIndex == td->m_tileNumber) {
146 do {
147 m_clockIndex.ref();
148 } while (!m_tileDataMap.get(m_clockIndex.loadAcquire()) && m_clockIndex < m_counter);
149 }
150
151 int index = td->m_tileNumber;
152 td->m_tileNumber = -1;
153 m_tileDataMap.erase(index);
155 m_memoryMetric -= td->pixelSize();
156
159}
160
166
167KisTileData *KisTileDataStore::allocTileData(qint32 pixelSize, const quint8 *defPixel)
168{
169 KisTileData *td = new KisTileData(pixelSize, defPixel, this);
171 return td;
172}
173
175{
176 KisTileData *td = 0;
177
178 if (rhs->m_clonesStack.pop(td)) {
179 DEBUG_PRECLONE_ACTION("+ Pre-clone HIT", rhs, td);
181 } else {
182 rhs->blockSwapping();
183 td = new KisTileData(*rhs);
184 rhs->unblockSwapping();
185 DEBUG_PRECLONE_ACTION("- Pre-clone #MISS#", rhs, td);
187 }
188
190 return td;
191}
192
194{
195 Q_ASSERT(td->m_store == this);
196
198
199 m_iteratorLock.lockForRead();
200 td->m_swapLock.lockForWrite();
201
202 if (!td->data()) {
204 } else {
206 }
207
208 td->m_swapLock.unlock();
209 m_iteratorLock.unlock();
210
211 delete td;
212}
213
215{
216// dbgKrita << "#### SWAP MISS! ####" << td << ppVar(td->mementoed()) << ppVar(td->age()) << ppVar(td->numUsers());
218
219 td->m_swapLock.lockForRead();
220
221 while (!td->data()) {
222 td->m_swapLock.unlock();
223
228 m_iteratorLock.lockForWrite();
229
243 if (!td->data()) {
244 td->m_swapLock.lockForWrite();
245
248
249 td->m_swapLock.unlock();
250 }
251
252 m_iteratorLock.unlock();
253
258 td->m_swapLock.lockForRead();
259 }
260}
261
263{
268 bool result = false;
269 if (!td->m_swapLock.tryLockForWrite()) return result;
270
271 if (td->data()) {
274 result = true;
275 }
276 }
277 td->m_swapLock.unlock();
278
279 return result;
280}
281
288{
289 delete iterator;
290 m_iteratorLock.unlock();
291}
292
304
310
312{
313 m_clockIndex = iterator->getFinalPosition();
314 delete iterator;
315 m_iteratorLock.unlock();
316}
317
319{
321 KisTileData *item = 0;
322
323 while (iter->hasNext()) {
324 item = iter->next();
325 dbgTiles << "-------------------------\n"
326 << "TileData:\t\t\t" << item
327 << "\n refCount:\t" << item->m_refCount;
328 }
329
330 endIteration(iter);
331}
332
334{
336 KisTileData *item = 0;
337
338 while (iter->hasNext()) {
339 item = iter->next();
340 iter->trySwapOut(item);
341 }
342
343 endIteration(iter);
344
345// dbgKrita << "Number of tiles:" << numTiles();
346// dbgKrita << "Tiles in memory:" << numTilesInMemory();
347// m_swappedStore.debugStatistics();
348}
349
351{
352 QWriteLocker l(&m_iteratorLock);
354
355 while (iter.isValid()) {
356 delete iter.getValue();
357 iter.next();
358 }
359
360 m_counter = 1;
361 m_clockIndex = 1;
362 m_numTiles = 0;
363 m_memoryMetric = 0;
364}
365
372
377
Q_GLOBAL_STATIC(KisStoragePluginRegistry, s_instance)
Value erase(Key key)
Value assign(Key key, Value desired)
Value get(Key key)
bool trySwapOutTileData(KisTileData *td)
void forgetTileData(KisTileData *td)
void swapInTileData(KisTileData *td)
qint64 lastRealMemoryMetric() const
qint64 lastHistoricalMemoryMetric() const
qint64 lastPoolMemoryMetric() const
void ensureTileDataLoaded(KisTileData *td)
KisTileDataPooler m_pooler
qint32 numTilesInMemory() const
void registerTileData(KisTileData *td)
void freeTileData(KisTileData *td)
void unregisterTileData(KisTileData *td)
KisTileDataStoreClockIterator * beginClockIteration()
KisTileDataSwapper m_swapper
KisTileData * allocTileData(qint32 pixelSize, const quint8 *defPixel)
void endIteration(KisTileDataStoreIterator *iterator)
KisTileDataStoreIterator * beginIteration()
KisSwappedDataStore m_swappedStore
void unregisterTileDataImp(KisTileData *td)
qint32 numTiles() const
QReadWriteLock m_iteratorLock
KisTileDataStoreReverseIterator * beginReverseIteration()
void tryForceUpdateMemoryStatisticsWhileIdle()
bool trySwapTileData(KisTileData *td)
KisTileData * duplicateTileData(KisTileData *rhs)
ConcurrentMap< int, KisTileData * > m_tileDataMap
MemoryStatistics memoryStatistics()
void registerTileDataImp(KisTileData *td)
static KisTileDataStore * instance()
qint64 memoryMetric() const
QReadWriteLock m_swapLock
void unblockSwapping()
quint32 pixelSize() const
void blockSwapping()
quint8 * data() const
KisTileDataStore * m_store
KisTileDataCache m_clonesStack
static const qint32 HEIGHT
static const qint32 WIDTH
void lockRawPointerAccess()
Definition qsbr.h:105
void unlockRawPointerAccess()
Definition qsbr.h:110
void update()
Definition qsbr.h:93
#define errKrita
Definition kis_debug.h:107
#define dbgTiles
Definition kis_debug.h:49
#define DEBUG_FREE_ACTION(td)
#define DEBUG_COUNT_PRECLONE_HIT(td)
#define DEBUG_REPORT_PRECLONE_EFFICIENCY()
#define DEBUG_PRECLONE_ACTION(action, oldTD, newTD)
#define DEBUG_COUNT_PRECLONE_MISS(td)