Warning, file /graphics/krita/libs/image/tiles3/kis_tile_data_pooler.cc was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 0008 #include <stdio.h> 0009 #include "kis_tile_data.h" 0010 #include "kis_tile_data_store.h" 0011 #include "kis_tile_data_store_iterators.h" 0012 #include "kis_debug.h" 0013 #include "kis_tile_data_pooler.h" 0014 #include "kis_image_config.h" 0015 0016 0017 const qint32 KisTileDataPooler::MAX_NUM_CLONES = 16; 0018 const qint32 KisTileDataPooler::MAX_TIMEOUT = 60000; // 01m00s 0019 const qint32 KisTileDataPooler::MIN_TIMEOUT = 100; // 00m00.100s 0020 const qint32 KisTileDataPooler::TIMEOUT_FACTOR = 2; 0021 0022 //#define DEBUG_POOLER 0023 0024 #ifdef DEBUG_POOLER 0025 #define DEBUG_CLONE_ACTION(td, numClones) \ 0026 printf("Cloned (%d):\t\t\t\t0x%X (clones: %d, users: %d, refs: %d)\n", \ 0027 numClones, td, td->m_clonesStack.size(), \ 0028 (int)td->m_usersCount, (int)td->m_refCount) 0029 #define DEBUG_SIMPLE_ACTION(action) \ 0030 printf("pooler: %s\n", action) 0031 0032 #define RUNTIME_SANITY_CHECK(td) do { \ 0033 if(td->m_usersCount < td->m_refCount) { \ 0034 qInfo("**** Suspicious tiledata: 0x%X (clones: %d, users: %d, refs: %d) ****", \ 0035 td, td->m_clonesStack.size(), \ 0036 (int)td->m_usersCount, (int)td->m_refCount); \ 0037 } \ 0038 if(td->m_usersCount <= 0) { \ 0039 qFatal("pooler: Tiledata 0x%X has zero users counter. Crashing...", td); \ 0040 } \ 0041 } while(0) \ 0042 0043 #define DEBUG_TILE_STATISTICS() debugTileStatistics() 0044 0045 #define DEBUG_LISTS(mem, beggars, beggarsMem, donors, donorsMem) \ 0046 do { \ 0047 dbgKrita << "--- getLists finished ---"; \ 0048 dbgKrita << " memoryOccupied:" << mem << "/" << m_memoryLimit; \ 0049 dbgKrita << " donors:" << donors.size() \ 0050 << "(mem:" << donorsMem << ")"; \ 0051 dbgKrita << " beggars:" << beggars.size() \ 0052 << "(mem:" << beggarsMem << ")"; \ 0053 dbgKrita << "--- ----------------- ---"; \ 0054 } while(0) 0055 0056 #define DEBUG_ALLOC_CLONE(mem, totalMem) \ 0057 dbgKrita << "Alloc mem for clones:" << mem \ 0058 << "\tMem usage:" << totalMem << "/" << m_memoryLimit 0059 0060 #define DEBUG_FREE_CLONE(freed, demanded) \ 0061 dbgKrita << "Freed mem for clones:" << freed \ 0062 << "/" << qAbs(demanded) 0063 0064 #else 0065 #define DEBUG_CLONE_ACTION(td, numClones) 0066 #define DEBUG_SIMPLE_ACTION(action) 0067 #define RUNTIME_SANITY_CHECK(td) 0068 #define DEBUG_TILE_STATISTICS() 0069 #define DEBUG_LISTS(mem, beggars, beggarsMem, donors, donorsMem) \ 0070 Q_UNUSED(mem); \ 0071 Q_UNUSED(beggars); \ 0072 Q_UNUSED(beggarsMem); \ 0073 Q_UNUSED(donors); \ 0074 Q_UNUSED(donorsMem); 0075 #define DEBUG_ALLOC_CLONE(mem, totalMem) 0076 #define DEBUG_FREE_CLONE(freed, demanded) 0077 #endif 0078 0079 0080 KisTileDataPooler::KisTileDataPooler(KisTileDataStore *store, qint32 memoryLimit) 0081 : QThread() 0082 { 0083 m_shouldExitFlag = 0; 0084 m_store = store; 0085 m_timeout = MIN_TIMEOUT; 0086 m_lastCycleHadWork = false; 0087 m_lastPoolMemoryMetric = 0; 0088 m_lastRealMemoryMetric = 0; 0089 m_lastHistoricalMemoryMetric = 0; 0090 0091 if(memoryLimit >= 0) { 0092 m_memoryLimit = memoryLimit; 0093 } 0094 else { 0095 m_memoryLimit = MiB_TO_METRIC(KisImageConfig(true).poolLimit()); 0096 } 0097 } 0098 0099 KisTileDataPooler::~KisTileDataPooler() 0100 { 0101 } 0102 0103 void KisTileDataPooler::kick() 0104 { 0105 m_semaphore.release(); 0106 } 0107 0108 void KisTileDataPooler::terminatePooler() 0109 { 0110 unsigned long exitTimeout = 100; 0111 do { 0112 m_shouldExitFlag = true; 0113 kick(); 0114 } while(!wait(exitTimeout)); 0115 } 0116 0117 qint32 KisTileDataPooler::numClonesNeeded(KisTileData *td) const 0118 { 0119 RUNTIME_SANITY_CHECK(td); 0120 qint32 numUsers = td->m_usersCount; 0121 qint32 numPresentClones = td->m_clonesStack.size(); 0122 qint32 totalClones = qMin(numUsers - 1, MAX_NUM_CLONES); 0123 0124 return totalClones - numPresentClones; 0125 } 0126 0127 void KisTileDataPooler::cloneTileData(KisTileData *td, qint32 numClones) const 0128 { 0129 if (numClones > 0) { 0130 td->blockSwapping(); 0131 for (qint32 i = 0; i < numClones; i++) { 0132 td->m_clonesStack.push(new KisTileData(*td, false)); 0133 } 0134 td->unblockSwapping(); 0135 } else { 0136 qint32 numUnneededClones = qAbs(numClones); 0137 for (qint32 i = 0; i < numUnneededClones; i++) { 0138 KisTileData *clone = 0; 0139 0140 bool result = td->m_clonesStack.pop(clone); 0141 if(!result) break; 0142 0143 delete clone; 0144 } 0145 } 0146 0147 DEBUG_CLONE_ACTION(td, numClones); 0148 } 0149 0150 void KisTileDataPooler::waitForWork() 0151 { 0152 bool success; 0153 0154 if (m_lastCycleHadWork) 0155 success = m_semaphore.tryAcquire(1, m_timeout); 0156 else { 0157 m_semaphore.acquire(); 0158 success = true; 0159 } 0160 0161 m_lastCycleHadWork = false; 0162 if (success) { 0163 m_timeout = MIN_TIMEOUT; 0164 } else { 0165 m_timeout *= TIMEOUT_FACTOR; 0166 m_timeout = qMin(m_timeout, MAX_TIMEOUT); 0167 } 0168 } 0169 0170 void KisTileDataPooler::run() 0171 { 0172 if(!m_memoryLimit) return; 0173 0174 m_shouldExitFlag = false; 0175 0176 while (1) { 0177 DEBUG_SIMPLE_ACTION("went to bed... Zzz..."); 0178 0179 waitForWork(); 0180 0181 if (m_shouldExitFlag) 0182 break; 0183 0184 QThread::msleep(0); 0185 DEBUG_SIMPLE_ACTION("cycle started"); 0186 0187 0188 KisTileDataStoreReverseIterator *iter = m_store->beginReverseIteration(); 0189 QList<KisTileData*> beggars; 0190 QList<KisTileData*> donors; 0191 qint32 memoryOccupied; 0192 0193 qint32 statRealMemory; 0194 qint32 statHistoricalMemory; 0195 0196 0197 getLists(iter, beggars, donors, 0198 memoryOccupied, 0199 statRealMemory, 0200 statHistoricalMemory); 0201 0202 m_lastCycleHadWork = 0203 processLists(beggars, donors, memoryOccupied); 0204 0205 m_lastPoolMemoryMetric = memoryOccupied; 0206 m_lastRealMemoryMetric = statRealMemory; 0207 m_lastHistoricalMemoryMetric = statHistoricalMemory; 0208 0209 m_store->endIteration(iter); 0210 0211 DEBUG_TILE_STATISTICS(); 0212 DEBUG_SIMPLE_ACTION("cycle finished"); 0213 } 0214 } 0215 0216 void KisTileDataPooler::forceUpdateMemoryStats() 0217 { 0218 KIS_SAFE_ASSERT_RECOVER_RETURN(!isRunning()); 0219 0220 KisTileDataStoreReverseIterator *iter = m_store->beginReverseIteration(); 0221 QList<KisTileData*> beggars; 0222 QList<KisTileData*> donors; 0223 qint32 memoryOccupied; 0224 0225 qint32 statRealMemory; 0226 qint32 statHistoricalMemory; 0227 0228 0229 getLists(iter, beggars, donors, 0230 memoryOccupied, 0231 statRealMemory, 0232 statHistoricalMemory); 0233 0234 m_lastPoolMemoryMetric = memoryOccupied; 0235 m_lastRealMemoryMetric = statRealMemory; 0236 m_lastHistoricalMemoryMetric = statHistoricalMemory; 0237 0238 m_store->endIteration(iter); 0239 } 0240 0241 qint64 KisTileDataPooler::lastPoolMemoryMetric() const 0242 { 0243 return m_lastPoolMemoryMetric; 0244 } 0245 0246 qint64 KisTileDataPooler::lastRealMemoryMetric() const 0247 { 0248 return m_lastRealMemoryMetric; 0249 } 0250 0251 qint64 KisTileDataPooler::lastHistoricalMemoryMetric() const 0252 { 0253 return m_lastHistoricalMemoryMetric; 0254 } 0255 0256 inline int KisTileDataPooler::clonesMetric(KisTileData *td, int numClones) { 0257 return numClones * td->pixelSize(); 0258 } 0259 0260 inline int KisTileDataPooler::clonesMetric(KisTileData *td) { 0261 return td->m_clonesStack.size() * td->pixelSize(); 0262 } 0263 0264 inline void KisTileDataPooler::tryFreeOrphanedClones(KisTileData *td) 0265 { 0266 qint32 extraClones = -numClonesNeeded(td); 0267 0268 if(extraClones > 0) { 0269 cloneTileData(td, -extraClones); 0270 } 0271 } 0272 0273 inline qint32 KisTileDataPooler::needMemory(KisTileData *td) 0274 { 0275 qint32 clonesNeeded = !td->age() ? qMax(0, numClonesNeeded(td)) : 0; 0276 return clonesMetric(td, clonesNeeded); 0277 } 0278 0279 inline qint32 KisTileDataPooler::canDonorMemory(KisTileData *td) 0280 { 0281 return td->age() && clonesMetric(td); 0282 } 0283 0284 template<class Iter> 0285 void KisTileDataPooler::getLists(Iter *iter, 0286 QList<KisTileData*> &beggars, 0287 QList<KisTileData*> &donors, 0288 qint32 &memoryOccupied, 0289 qint32 &statRealMemory, 0290 qint32 &statHistoricalMemory) 0291 { 0292 memoryOccupied = 0; 0293 statRealMemory = 0; 0294 statHistoricalMemory = 0; 0295 0296 qint32 needMemoryTotal = 0; 0297 qint32 canDonorMemoryTotal = 0; 0298 0299 qint32 neededMemory; 0300 qint32 donoredMemory; 0301 0302 KisTileData *item; 0303 0304 while(iter->hasNext()) { 0305 item = iter->next(); 0306 0307 tryFreeOrphanedClones(item); 0308 0309 if((neededMemory = needMemory(item))) { 0310 needMemoryTotal += neededMemory; 0311 beggars.append(item); 0312 } 0313 else if((donoredMemory = canDonorMemory(item))) { 0314 canDonorMemoryTotal += donoredMemory; 0315 donors.append(item); 0316 } 0317 0318 memoryOccupied += clonesMetric(item); 0319 0320 // statistics gathering 0321 if (item->historical()) { 0322 statHistoricalMemory += item->pixelSize(); 0323 } else { 0324 statRealMemory += item->pixelSize(); 0325 } 0326 } 0327 0328 DEBUG_LISTS(memoryOccupied, 0329 beggars, needMemoryTotal, 0330 donors, canDonorMemoryTotal); 0331 } 0332 0333 qint32 KisTileDataPooler::tryGetMemory(QList<KisTileData*> &donors, 0334 qint32 memoryMetric) 0335 { 0336 qint32 memoryFreed = 0; 0337 0338 QMutableListIterator<KisTileData*> iter(donors); 0339 iter.toBack(); 0340 0341 while(iter.hasPrevious() && memoryFreed < memoryMetric) { 0342 KisTileData *item = iter.previous(); 0343 0344 qint32 numClones = item->m_clonesStack.size(); 0345 cloneTileData(item, -numClones); 0346 memoryFreed += clonesMetric(item, numClones); 0347 0348 iter.remove(); 0349 } 0350 0351 return memoryFreed; 0352 } 0353 0354 bool KisTileDataPooler::processLists(QList<KisTileData*> &beggars, 0355 QList<KisTileData*> &donors, 0356 qint32 &memoryOccupied) 0357 { 0358 bool hadWork = false; 0359 0360 0361 Q_FOREACH (KisTileData *item, beggars) { 0362 qint32 clonesNeeded = numClonesNeeded(item); 0363 qint32 clonesMemory = clonesMetric(item, clonesNeeded); 0364 0365 qint32 memoryLeft = 0366 m_memoryLimit - (memoryOccupied + clonesMemory); 0367 0368 if(memoryLeft < 0) { 0369 qint32 freedMemory = tryGetMemory(donors, -memoryLeft); 0370 memoryOccupied -= freedMemory; 0371 0372 DEBUG_FREE_CLONE(freedMemory, memoryLeft); 0373 0374 if(m_memoryLimit < memoryOccupied + clonesMemory) 0375 break; 0376 } 0377 0378 cloneTileData(item, clonesNeeded); 0379 DEBUG_ALLOC_CLONE(clonesMemory, memoryOccupied); 0380 0381 memoryOccupied += clonesMemory; 0382 hadWork = true; 0383 } 0384 0385 return hadWork; 0386 } 0387 0388 void KisTileDataPooler::debugTileStatistics() 0389 { 0390 /** 0391 * Assume we are called from the inside of the loop. 0392 * This means m_store is already locked 0393 */ 0394 0395 qint64 preallocatedTiles=0; 0396 0397 KisTileDataStoreIterator *iter = m_store->beginIteration(); 0398 KisTileData *item; 0399 0400 while(iter->hasNext()) { 0401 item = iter->next(); 0402 preallocatedTiles += item->m_clonesStack.size(); 0403 } 0404 0405 m_store->endIteration(iter); 0406 0407 dbgKrita << "Tiles statistics:\t total:" << m_store->numTiles() << "\t preallocated:"<< preallocatedTiles; 0408 } 0409 0410 void KisTileDataPooler::testingRereadConfig() 0411 { 0412 m_memoryLimit = MiB_TO_METRIC(KisImageConfig(true).poolLimit()); 0413 }