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