1 /*
2 * Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
3 * Copyright (c) 2018 Andrey Kamakin <a.kamakin@icloud.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 #ifndef KIS_TILE_DATA_STORE_H_
21 #define KIS_TILE_DATA_STORE_H_
22
23 #include "kritaimage_export.h"
24
25 #include <QReadWriteLock>
26 #include "kis_tile_data_interface.h"
27
28 #include "kis_tile_data_pooler.h"
29 #include "swap/kis_tile_data_swapper.h"
30 #include "swap/kis_swapped_data_store.h"
31 #include "3rdparty/lock_free_map/concurrent_map.h"
32
33 class KisTileDataStoreIterator;
34 class KisTileDataStoreReverseIterator;
35 class KisTileDataStoreClockIterator;
36
37 /**
38 * Stores tileData objects. When needed compresses them and swaps.
39 */
40 class KRITAIMAGE_EXPORT KisTileDataStore
41 {
42 public:
43 KisTileDataStore();
44 ~KisTileDataStore();
45 static KisTileDataStore* instance();
46
47 void debugPrintList();
48
49 struct MemoryStatistics {
50 qint64 totalMemorySize;
51 qint64 realMemorySize;
52 qint64 historicalMemorySize;
53
54 qint64 poolSize;
55
56 qint64 swapSize;
57 };
58
59 MemoryStatistics memoryStatistics();
60 void tryForceUpdateMemoryStatisticsWhileIdle();
61
62 /**
63 * Returns total number of tiles present: in memory
64 * or in a swap file
65 */
numTiles()66 inline qint32 numTiles() const
67 {
68 return m_numTiles.loadAcquire() + m_swappedStore.numTiles();
69 }
70
71 /**
72 * Returns the number of tiles present in memory only
73 */
numTilesInMemory()74 inline qint32 numTilesInMemory() const
75 {
76 return m_numTiles.loadAcquire();
77 }
78
checkFreeMemory()79 inline void checkFreeMemory()
80 {
81 m_swapper.checkFreeMemory();
82 }
83
84 /**
85 * \see m_memoryMetric
86 */
memoryMetric()87 inline qint64 memoryMetric() const
88 {
89 return m_memoryMetric.loadAcquire();
90 }
91
92 KisTileDataStoreIterator* beginIteration();
93 void endIteration(KisTileDataStoreIterator* iterator);
94
95 KisTileDataStoreReverseIterator* beginReverseIteration();
96 void endIteration(KisTileDataStoreReverseIterator* iterator);
97
98 KisTileDataStoreClockIterator* beginClockIteration();
99 void endIteration(KisTileDataStoreClockIterator* iterator);
100
createDefaultTileData(qint32 pixelSize,const quint8 * defPixel)101 inline KisTileData* createDefaultTileData(qint32 pixelSize, const quint8 *defPixel)
102 {
103 return allocTileData(pixelSize, defPixel);
104 }
105
106 // Called by The Memento Manager after every commit
kickPooler()107 inline void kickPooler()
108 {
109 m_pooler.kick();
110
111 //FIXME: maybe, rename a function?
112 m_swapper.kick();
113 }
114
115 /**
116 * Try swap out the tile data.
117 * It may fail in case the tile is being accessed
118 * at the same moment of time.
119 */
120 bool trySwapTileData(KisTileData *td);
121
122
123 /**
124 * WARN: The following three method are only for usage
125 * in KisTileData. Do not call them directly!
126 */
127
128 KisTileData *duplicateTileData(KisTileData *rhs);
129
130 void freeTileData(KisTileData *td);
131
132 /**
133 * Ensures that the tile data is totally present in memory
134 * and it's swapping is blocked by holding td->m_swapLock
135 * in a read mode.
136 * PRECONDITIONS: td->m_swapLock is *unlocked*
137 * m_listRWLock is *unlocked*
138 * POSTCONDITIONS: td->m_data is in memory and
139 * td->m_swapLock is locked
140 * m_listRWLock is unlocked
141 */
142 void ensureTileDataLoaded(KisTileData *td);
143
144 void registerTileData(KisTileData *td);
145 void unregisterTileData(KisTileData *td);
146
147 private:
148 KisTileData *allocTileData(qint32 pixelSize, const quint8 *defPixel);
149
150 inline void registerTileDataImp(KisTileData *td);
151 inline void unregisterTileDataImp(KisTileData *td);
152 void freeRegisteredTiles();
153
154 friend class DeadlockyThread;
155 friend class KisLowMemoryTests;
156 void debugSwapAll();
157 void debugClear();
158
159 friend class KisTiledDataManagerTest;
160 void testingSuspendPooler();
161 void testingResumePooler();
162
163 friend class KisLowMemoryBenchmark;
164 void testingRereadConfig();
165 private:
166 KisTileDataPooler m_pooler;
167 KisTileDataSwapper m_swapper;
168
169 friend class KisTileDataStoreTest;
170 friend class KisTileDataPoolerTest;
171 KisSwappedDataStore m_swappedStore;
172
173 /**
174 * This metric is used for computing the volume
175 * of memory occupied by tile data objects.
176 * metric = num_bytes / (KisTileData::WIDTH * KisTileData::HEIGHT)
177 */
178 QAtomicInt m_numTiles;
179 QAtomicInt m_memoryMetric;
180 QAtomicInt m_counter;
181 QAtomicInt m_clockIndex;
182 ConcurrentMap<int, KisTileData*> m_tileDataMap;
183 QReadWriteLock m_iteratorLock;
184 };
185
186 template<typename T>
MiB_TO_METRIC(T value)187 inline T MiB_TO_METRIC(T value)
188 {
189 unsigned long long __MiB = 1ULL << 20;
190 return value * (__MiB / (KisTileData::WIDTH * KisTileData::HEIGHT));
191 }
192
193 #endif /* KIS_TILE_DATA_STORE_H_ */
194
195