File indexing completed on 2024-05-12 15:58:28
0001 /* 0002 * SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kis_memory_statistics_server.h" 0008 0009 #include <QGlobalStatic> 0010 #include <QApplication> 0011 0012 #include "kis_image.h" 0013 #include "kis_image_config.h" 0014 #include "kis_signal_compressor.h" 0015 0016 #include "tiles3/kis_tile_data_store.h" 0017 0018 Q_GLOBAL_STATIC(KisMemoryStatisticsServer, s_instance) 0019 0020 struct Q_DECL_HIDDEN KisMemoryStatisticsServer::Private 0021 { 0022 Private(KisMemoryStatisticsServer *q) 0023 : updateCompressor(1000 /* ms */, KisSignalCompressor::POSTPONE, q) 0024 { 0025 } 0026 0027 KisSignalCompressor updateCompressor; 0028 }; 0029 0030 0031 KisMemoryStatisticsServer::KisMemoryStatisticsServer() 0032 : m_d(new Private(this)) 0033 { 0034 /** 0035 * The first instance() call may happen from non-gui thread, 0036 * so we should ensure the signals and timers are running in the 0037 * correct (GUI) thread. 0038 */ 0039 moveToThread(qApp->thread()); 0040 connect(&m_d->updateCompressor, SIGNAL(timeout()), SIGNAL(sigUpdateMemoryStatistics())); 0041 } 0042 0043 KisMemoryStatisticsServer::~KisMemoryStatisticsServer() 0044 { 0045 } 0046 0047 KisMemoryStatisticsServer* KisMemoryStatisticsServer::instance() 0048 { 0049 return s_instance; 0050 } 0051 0052 inline void addDevice(KisPaintDeviceSP dev, 0053 bool isProjection, 0054 QSet<KisPaintDevice*> &devices, 0055 qint64 &memBound, 0056 qint64 &layersSize, 0057 qint64 &projectionsSize, 0058 qint64 &lodSize) 0059 { 0060 if (dev && !devices.contains(dev.data())) { 0061 devices.insert(dev.data()); 0062 0063 qint64 imageData = 0; 0064 qint64 temporaryData = 0; 0065 qint64 lodData = 0; 0066 0067 dev->estimateMemoryStats(imageData, temporaryData, lodData); 0068 memBound += imageData + temporaryData + lodData; 0069 0070 KIS_SAFE_ASSERT_RECOVER_NOOP(!temporaryData || isProjection); 0071 0072 if (!isProjection) { 0073 layersSize += imageData + temporaryData; 0074 } else { 0075 projectionsSize += imageData + temporaryData; 0076 } 0077 0078 lodSize += lodData; 0079 } 0080 } 0081 0082 qint64 calculateNodeMemoryHiBoundStep(KisNodeSP node, 0083 QSet<KisPaintDevice*> &devices, 0084 qint64 &layersSize, 0085 qint64 &projectionsSize, 0086 qint64 &lodSize) 0087 { 0088 qint64 memBound = 0; 0089 0090 const bool originalIsProjection = 0091 node->inherits("KisGroupLayer") || 0092 node->inherits("KisAdjustmentLayer"); 0093 0094 0095 addDevice(node->paintDevice(), false, devices, memBound, layersSize, projectionsSize, lodSize); 0096 addDevice(node->original(), originalIsProjection, devices, memBound, layersSize, projectionsSize, lodSize); 0097 addDevice(node->projection(), true, devices, memBound, layersSize, projectionsSize, lodSize); 0098 0099 node = node->firstChild(); 0100 while (node) { 0101 memBound += calculateNodeMemoryHiBoundStep(node, devices, 0102 layersSize, projectionsSize, lodSize); 0103 node = node->nextSibling(); 0104 } 0105 0106 return memBound; 0107 } 0108 0109 qint64 calculateNodeMemoryHiBound(KisNodeSP node, 0110 qint64 &layersSize, 0111 qint64 &projectionsSize, 0112 qint64 &lodSize) 0113 { 0114 layersSize = 0; 0115 projectionsSize = 0; 0116 lodSize = 0; 0117 0118 QSet<KisPaintDevice*> devices; 0119 return calculateNodeMemoryHiBoundStep(node, 0120 devices, 0121 layersSize, 0122 projectionsSize, 0123 lodSize); 0124 } 0125 0126 0127 KisMemoryStatisticsServer::Statistics 0128 KisMemoryStatisticsServer::fetchMemoryStatistics(KisImageSP image) const 0129 { 0130 KisTileDataStore::MemoryStatistics tileStats = 0131 KisTileDataStore::instance()->memoryStatistics(); 0132 0133 Statistics stats; 0134 if (image) { 0135 stats.imageSize = 0136 calculateNodeMemoryHiBound(image->root(), 0137 stats.layersSize, 0138 stats.projectionsSize, 0139 stats.lodSize); 0140 } 0141 stats.totalMemorySize = tileStats.totalMemorySize; 0142 stats.realMemorySize = tileStats.realMemorySize; 0143 stats.historicalMemorySize = tileStats.historicalMemorySize; 0144 stats.poolSize = tileStats.poolSize; 0145 0146 stats.swapSize = tileStats.swapSize; 0147 0148 KisImageConfig cfg(true); 0149 0150 stats.tilesHardLimit = cfg.tilesHardLimit() * MiB; 0151 stats.tilesSoftLimit = cfg.tilesSoftLimit() * MiB; 0152 stats.tilesPoolLimit = cfg.poolLimit() * MiB; 0153 stats.totalMemoryLimit = stats.tilesHardLimit + stats.tilesPoolLimit; 0154 0155 return stats; 0156 } 0157 0158 void KisMemoryStatisticsServer::tryForceUpdateMemoryStatisticsWhileIdle() 0159 { 0160 KisTileDataStore::instance()->tryForceUpdateMemoryStatisticsWhileIdle(); 0161 notifyImageChanged(); 0162 } 0163 0164 void KisMemoryStatisticsServer::notifyImageChanged() 0165 { 0166 m_d->updateCompressor.start(); 0167 } 0168 0169