File indexing completed on 2024-06-09 04:22:22
0001 /* 0002 * SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com> 0003 * SPDX-FileCopyrightText: 2018 Andrey Kamakin <a.kamakin@icloud.com> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 0009 #include "kis_tile_data.h" 0010 #include "kis_tile_data_store.h" 0011 0012 #include <kis_debug.h> 0013 0014 #include <boost/pool/singleton_pool.hpp> 0015 #include "kis_tile_data_store_iterators.h" 0016 0017 // BPP == bytes per pixel 0018 #define TILE_SIZE_4BPP (4 * __TILE_DATA_WIDTH * __TILE_DATA_HEIGHT) 0019 #define TILE_SIZE_8BPP (8 * __TILE_DATA_WIDTH * __TILE_DATA_HEIGHT) 0020 0021 typedef boost::singleton_pool<KisTileData, TILE_SIZE_4BPP, boost::default_user_allocator_new_delete, boost::details::pool::default_mutex, 256, 4096> BoostPool4BPP; 0022 typedef boost::singleton_pool<KisTileData, TILE_SIZE_8BPP, boost::default_user_allocator_new_delete, boost::details::pool::default_mutex, 128, 2048> BoostPool8BPP; 0023 0024 const qint32 KisTileData::WIDTH = __TILE_DATA_WIDTH; 0025 const qint32 KisTileData::HEIGHT = __TILE_DATA_HEIGHT; 0026 0027 SimpleCache KisTileData::m_cache; 0028 0029 SimpleCache::~SimpleCache() 0030 { 0031 clear(); 0032 } 0033 0034 void SimpleCache::clear() 0035 { 0036 QWriteLocker l(&m_cacheLock); 0037 quint8 *ptr = 0; 0038 0039 while (m_4Pool.pop(ptr)) { 0040 BoostPool4BPP::free(ptr); 0041 } 0042 0043 while (m_8Pool.pop(ptr)) { 0044 BoostPool8BPP::free(ptr); 0045 } 0046 0047 while (m_16Pool.pop(ptr)) { 0048 free(ptr); 0049 } 0050 } 0051 0052 0053 KisTileData::KisTileData(qint32 pixelSize, const quint8 *defPixel, KisTileDataStore *store, bool checkFreeMemory) 0054 : m_state(NORMAL), 0055 m_mementoFlag(0), 0056 m_age(0), 0057 m_usersCount(0), 0058 m_refCount(0), 0059 m_pixelSize(pixelSize), 0060 m_store(store) 0061 { 0062 if (checkFreeMemory) { 0063 m_store->checkFreeMemory(); 0064 } 0065 m_data = allocateData(m_pixelSize); 0066 0067 fillWithPixel(defPixel); 0068 } 0069 0070 0071 /** 0072 * Duplicating tiledata 0073 * + new object loaded in memory 0074 * + it's unlocked and has refCount==0 0075 * 0076 * NOTE: the memory allocated by the pooler for clones is not counted 0077 * by the store in memoryHardLimit. The pooler has it's own slice of 0078 * memory and keeps track of the its size itself. So we should be able 0079 * to disable the memory check with checkFreeMemory, otherwise, there 0080 * is a deadlock. 0081 */ 0082 KisTileData::KisTileData(const KisTileData& rhs, bool checkFreeMemory) 0083 : m_state(NORMAL), 0084 m_mementoFlag(0), 0085 m_age(0), 0086 m_usersCount(0), 0087 m_refCount(0), 0088 m_pixelSize(rhs.m_pixelSize), 0089 m_store(rhs.m_store) 0090 { 0091 if (checkFreeMemory) { 0092 m_store->checkFreeMemory(); 0093 } 0094 m_data = allocateData(m_pixelSize); 0095 0096 memcpy(m_data, rhs.data(), m_pixelSize * WIDTH * HEIGHT); 0097 } 0098 0099 0100 KisTileData::~KisTileData() 0101 { 0102 releaseMemory(); 0103 } 0104 0105 void KisTileData::fillWithPixel(const quint8 *defPixel) 0106 { 0107 quint8 *it = m_data; 0108 0109 for (int i = 0; i < WIDTH * HEIGHT; i++, it += m_pixelSize) { 0110 memcpy(it, defPixel, m_pixelSize); 0111 } 0112 } 0113 0114 void KisTileData::releaseMemory() 0115 { 0116 if (m_data) { 0117 freeData(m_data, m_pixelSize); 0118 m_data = 0; 0119 } 0120 0121 KisTileData *clone = 0; 0122 while (m_clonesStack.pop(clone)) { 0123 delete clone; 0124 } 0125 0126 Q_ASSERT(m_clonesStack.isEmpty()); 0127 } 0128 0129 void KisTileData::allocateMemory() 0130 { 0131 Q_ASSERT(!m_data); 0132 m_data = allocateData(m_pixelSize); 0133 } 0134 0135 quint8* KisTileData::allocateData(const qint32 pixelSize) 0136 { 0137 quint8 *ptr = 0; 0138 0139 if (!m_cache.pop(pixelSize, ptr)) { 0140 switch (pixelSize) { 0141 case 4: 0142 ptr = (quint8*)BoostPool4BPP::malloc(); 0143 break; 0144 case 8: 0145 ptr = (quint8*)BoostPool8BPP::malloc(); 0146 break; 0147 default: 0148 ptr = (quint8*) malloc(pixelSize * WIDTH * HEIGHT); 0149 break; 0150 } 0151 } 0152 0153 return ptr; 0154 } 0155 0156 void KisTileData::freeData(quint8* ptr, const qint32 pixelSize) 0157 { 0158 if (!m_cache.push(pixelSize, ptr)) { 0159 switch (pixelSize) { 0160 case 4: 0161 BoostPool4BPP::free(ptr); 0162 break; 0163 case 8: 0164 BoostPool8BPP::free(ptr); 0165 break; 0166 default: 0167 free(ptr); 0168 break; 0169 } 0170 } 0171 } 0172 0173 //#define DEBUG_POOL_RELEASE 0174 0175 #ifdef DEBUG_POOL_RELEASE 0176 #include <unistd.h> 0177 #endif /* DEBUG_POOL_RELEASE */ 0178 0179 void KisTileData::releaseInternalPools() 0180 { 0181 const int maxMigratedTiles = 100; 0182 0183 if (KisTileDataStore::instance()->numTilesInMemory() < maxMigratedTiles) { 0184 0185 QVector<KisTileData*> dataObjects; 0186 QVector<QByteArray> memoryChunks; 0187 bool failedToLock = false; 0188 0189 KisTileDataStoreIterator *iter = KisTileDataStore::instance()->beginIteration(); 0190 0191 while (iter->hasNext()) { 0192 KisTileData *item = iter->next(); 0193 0194 // first release all the clones 0195 KisTileData *clone = 0; 0196 while (item->m_clonesStack.pop(clone)) { 0197 delete clone; 0198 } 0199 0200 // check if the tile data has actually been pooled 0201 if (item->m_pixelSize != 4 && 0202 item->m_pixelSize != 8 && 0203 item->m_pixelSize != 16) { 0204 0205 continue; 0206 } 0207 0208 // check if the tile has been swapped out 0209 if (item->m_data) { 0210 const bool locked = item->m_swapLock.tryLockForWrite(); 0211 if (!locked) { 0212 failedToLock = true; 0213 break; 0214 } 0215 0216 const int chunkSize = item->m_pixelSize * WIDTH * HEIGHT; 0217 dataObjects << item; 0218 memoryChunks << QByteArray((const char*)item->m_data, chunkSize); 0219 } 0220 0221 } 0222 0223 if (!failedToLock) { 0224 // purge the pools memory 0225 m_cache.clear(); 0226 BoostPool4BPP::purge_memory(); 0227 BoostPool8BPP::purge_memory(); 0228 0229 auto it = dataObjects.begin(); 0230 auto chunkIt = memoryChunks.constBegin(); 0231 0232 for (; it != dataObjects.end(); ++it, ++chunkIt) { 0233 KisTileData *item = *it; 0234 const int chunkSize = item->m_pixelSize * WIDTH * HEIGHT; 0235 0236 item->m_data = allocateData(item->m_pixelSize); 0237 memcpy(item->m_data, chunkIt->data(), chunkSize); 0238 0239 item->m_swapLock.unlock(); 0240 } 0241 } else { 0242 Q_FOREACH (KisTileData *item, dataObjects) { 0243 item->m_swapLock.unlock(); 0244 } 0245 0246 warnKrita << "WARNING: Failed to lock the tiles while trying to release the pooled memory"; 0247 } 0248 0249 KisTileDataStore::instance()->endIteration(iter); 0250 0251 #ifdef DEBUG_POOL_RELEASE 0252 dbgKrita << "After purging unused memory:"; 0253 0254 char command[256]; 0255 sprintf(command, "cat /proc/%d/status | grep -i vm", (int)getpid()); 0256 printf("--- %s ---\n", command); 0257 (void)system(command); 0258 #endif /* DEBUG_POOL_RELEASE */ 0259 0260 } else { 0261 dbgKrita << "DEBUG: releasing of the pooled memory has been cancelled:" 0262 << "there are still" 0263 << KisTileDataStore::instance()->numTilesInMemory() 0264 << "tiles in memory"; 0265 } 0266 }