[calligra/calligra/2.9] krita: [FEATURE] Added a reporting functional for memory consumption in Krita

Dmitry Kazakov dimula73 at gmail.com
Tue Jun 2 10:55:28 UTC 2015


Git commit 38d77e007d7704795a87d1a1f5d8965260450536 by Dmitry Kazakov.
Committed on 02/06/2015 at 10:55.
Pushed by dkazakov into branch 'calligra/2.9'.

[FEATURE] Added a reporting functional for memory consumption in Krita

Still TODO: add a configuration page to the settings dialog

CCMAIL:kimageshop at kde.org

M  +1    -0    krita/image/CMakeLists.txt
M  +2    -0    krita/image/kis_image.cc
A  +127  -0    krita/image/kis_memory_statistics_server.cpp     [License: GPL (v2+)]
A  +90   -0    krita/image/kis_memory_statistics_server.h     [License: GPL (v2+)]
M  +4    -0    krita/image/tiles3/kis_tile_data.h
M  +7    -0    krita/image/tiles3/kis_tile_data_interface.h
M  +43   -4    krita/image/tiles3/kis_tile_data_pooler.cc
M  +12   -2    krita/image/tiles3/kis_tile_data_pooler.h
M  +19   -0    krita/image/tiles3/kis_tile_data_store.cc
M  +12   -0    krita/image/tiles3/kis_tile_data_store.h
M  +12   -0    krita/image/tiles3/swap/kis_swapped_data_store.cpp
M  +8    -0    krita/image/tiles3/swap/kis_swapped_data_store.h
M  +1    -1    krita/image/tiles3/swap/kis_tile_data_swapper.cpp
M  +121  -13   krita/ui/kis_statusbar.cc
M  +11   -1    krita/ui/kis_statusbar.h

http://commits.kde.org/calligra/38d77e007d7704795a87d1a1f5d8965260450536

diff --git a/krita/image/CMakeLists.txt b/krita/image/CMakeLists.txt
index a351780..2ce5684 100644
--- a/krita/image/CMakeLists.txt
+++ b/krita/image/CMakeLists.txt
@@ -192,6 +192,7 @@ set(kritaimage_LIB_SRCS
    kis_curve_rect_mask_generator.cpp
    kis_math_toolbox.cpp
    kis_memory_leak_tracker.cpp
+   kis_memory_statistics_server.cpp
    kis_name_server.cpp
    kis_node.cpp
    kis_node_facade.cpp
diff --git a/krita/image/kis_image.cc b/krita/image/kis_image.cc
index 35ecf9d..216b97f 100644
--- a/krita/image/kis_image.cc
+++ b/krita/image/kis_image.cc
@@ -59,6 +59,7 @@
 #include "kis_transaction.h"
 #include "kis_types.h"
 #include "kis_meta_data_merge_strategy.h"
+#include "kis_memory_statistics_server.h"
 
 #include "kis_image_config.h"
 #include "kis_update_scheduler.h"
@@ -187,6 +188,7 @@ KisImage::KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const K
         m_d->scheduler->setProgressProxy(m_d->compositeProgressProxy);
     }
 
+    connect(this, SIGNAL(sigImageModified()), KisMemoryStatisticsServer::instance(), SLOT(notifyImageChanged()));
 }
 
 KisImage::~KisImage()
diff --git a/krita/image/kis_memory_statistics_server.cpp b/krita/image/kis_memory_statistics_server.cpp
new file mode 100644
index 0000000..5ee525e
--- /dev/null
+++ b/krita/image/kis_memory_statistics_server.cpp
@@ -0,0 +1,127 @@
+/*
+ *  Copyright (c) 2015 Dmitry Kazakov <dimula73 at gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "kis_memory_statistics_server.h"
+
+#include <kglobal.h>
+
+#include "kis_image.h"
+#include "kis_image_config.h"
+#include "kis_signal_compressor.h"
+
+#include "tiles3/kis_tile_data_store.h"
+
+
+struct KisMemoryStatisticsServer::Private
+{
+    Private()
+        : updateCompressor(1000 /* ms */, KisSignalCompressor::POSTPONE)
+    {
+    }
+
+    KisSignalCompressor updateCompressor;
+};
+
+
+KisMemoryStatisticsServer::KisMemoryStatisticsServer()
+    : m_d(new Private)
+{
+    connect(&m_d->updateCompressor, SIGNAL(timeout()), SIGNAL(sigUpdateMemoryStatistics()));
+}
+
+KisMemoryStatisticsServer::~KisMemoryStatisticsServer()
+{
+}
+
+KisMemoryStatisticsServer* KisMemoryStatisticsServer::instance()
+{
+    K_GLOBAL_STATIC(KisMemoryStatisticsServer, s_instance);
+    return s_instance;
+}
+
+inline void addDevice(KisPaintDeviceSP dev,
+                      QSet<KisPaintDevice*> &devices,
+                      qint64 &memBound)
+{
+    if (dev && !devices.contains(dev.data())) {
+        devices.insert(dev.data());
+        memBound +=
+            dev->extent().width() *
+            dev->extent().height() *
+            dev->pixelSize();
+    }
+}
+
+qint64 calculateNodeMemoryHiBoundStep(KisNodeSP node,
+                                      QSet<KisPaintDevice*> &devices)
+{
+    qint64 memBound = 0;
+
+    addDevice(node->paintDevice(), devices, memBound);
+    addDevice(node->original(), devices, memBound);
+    addDevice(node->projection(), devices, memBound);
+
+    node = node->firstChild();
+    while (node) {
+        memBound += calculateNodeMemoryHiBoundStep(node, devices);
+        node = node->nextSibling();
+    }
+
+    return memBound;
+}
+
+qint64 calculateNodeMemoryHiBound(KisNodeSP node)
+{
+    QSet<KisPaintDevice*> devices;
+    return calculateNodeMemoryHiBoundStep(node, devices);
+}
+
+
+KisMemoryStatisticsServer::Statistics
+KisMemoryStatisticsServer::fetchMemoryStatistics(KisImageSP image) const
+{
+    KisTileDataStore::MemoryStatistics tileStats =
+        KisTileDataStore::instance()->memoryStatistics();
+
+    Statistics stats;
+    if (image) {
+        stats.imageSize = calculateNodeMemoryHiBound(image->root());
+    }
+    stats.totalMemorySize = tileStats.totalMemorySize;
+    stats.realMemorySize = tileStats.realMemorySize;
+    stats.historicalMemorySize = tileStats.historicalMemorySize;
+    stats.poolSize = tileStats.poolSize;
+
+    stats.swapSize = tileStats.swapSize;
+
+    KisImageConfig cfg;
+
+    stats.tilesHardLimit = cfg.tilesHardLimit() * MiB;
+    stats.tilesSoftLimit = cfg.tilesSoftLimit() * MiB;
+    stats.tilesPoolLimit = cfg.poolLimit() * MiB;
+    stats.totalMemoryLimit = stats.tilesHardLimit + stats.tilesPoolLimit;
+
+    return stats;
+}
+
+void KisMemoryStatisticsServer::notifyImageChanged()
+{
+    m_d->updateCompressor.start();
+}
+
+
diff --git a/krita/image/kis_memory_statistics_server.h b/krita/image/kis_memory_statistics_server.h
new file mode 100644
index 0000000..cb7e734
--- /dev/null
+++ b/krita/image/kis_memory_statistics_server.h
@@ -0,0 +1,90 @@
+/*
+ *  Copyright (c) 2015 Dmitry Kazakov <dimula73 at gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KIS_MEMORY_STATISTICS_SERVER_H
+#define __KIS_MEMORY_STATISTICS_SERVER_H
+
+#include <QtGlobal>
+#include <QObject>
+#include <QScopedPointer>
+
+#include "krita_export.h"
+#include "kis_types.h"
+
+
+class KRITAIMAGE_EXPORT KisMemoryStatisticsServer : public QObject
+{
+    Q_OBJECT
+public:
+    struct Statistics
+    {
+        Statistics()
+            : imageSize(0),
+
+              totalMemorySize(0),
+              realMemorySize(0),
+              historicalMemorySize(0),
+              poolSize(0),
+
+              swapSize(0),
+
+              totalMemoryLimit(0),
+              tilesHardLimit(0),
+              tilesSoftLimit(0),
+              tilesPoolLimit(0)
+        {
+        }
+
+        qint64 imageSize;
+
+        qint64 totalMemorySize;
+        qint64 realMemorySize;
+        qint64 historicalMemorySize;
+        qint64 poolSize;
+
+        qint64 swapSize;
+
+        qint64 totalMemoryLimit;
+        qint64 tilesHardLimit;
+        qint64 tilesSoftLimit;
+        qint64 tilesPoolLimit;
+    };
+
+
+
+public:
+    ~KisMemoryStatisticsServer();
+    static KisMemoryStatisticsServer* instance();
+
+    Statistics fetchMemoryStatistics(KisImageSP image) const;
+
+public Q_SLOTS:
+    void notifyImageChanged();
+
+Q_SIGNALS:
+    void sigUpdateMemoryStatistics();
+
+private:
+    KisMemoryStatisticsServer();
+
+private:
+    struct Private;
+    const QScopedPointer<Private> m_d;
+};
+
+#endif /* __KIS_MEMORY_STATISTICS_SERVER_H */
diff --git a/krita/image/tiles3/kis_tile_data.h b/krita/image/tiles3/kis_tile_data.h
index 98c51c5..dee8a37 100644
--- a/krita/image/tiles3/kis_tile_data.h
+++ b/krita/image/tiles3/kis_tile_data.h
@@ -115,6 +115,10 @@ inline void KisTileData::setMementoed(bool value) {
     m_mementoFlag += value ? 1 : -1;
 }
 
+inline bool KisTileData::historical() const {
+    return mementoed() && numUsers() <= 1;
+}
+
 inline int KisTileData::age() const {
     return m_age;
 }
diff --git a/krita/image/tiles3/kis_tile_data_interface.h b/krita/image/tiles3/kis_tile_data_interface.h
index fc2aad4..8ce9c7c 100644
--- a/krita/image/tiles3/kis_tile_data_interface.h
+++ b/krita/image/tiles3/kis_tile_data_interface.h
@@ -132,6 +132,13 @@ public:
      */
     inline qint32 numUsers() const;
 
+    /**
+     * Conveniece method. Returns true iff the tile data is linked to
+     * information only and therefore can be swapped out easily.
+     *
+     * Effectively equivalent to: (mementoed() && numUsers() <= 1)
+     */
+     inline bool historical() const;
 
     /**
      * Used for swapping purposes only.
diff --git a/krita/image/tiles3/kis_tile_data_pooler.cc b/krita/image/tiles3/kis_tile_data_pooler.cc
index 3984526..11f8dfa 100644
--- a/krita/image/tiles3/kis_tile_data_pooler.cc
+++ b/krita/image/tiles3/kis_tile_data_pooler.cc
@@ -91,6 +91,9 @@ KisTileDataPooler::KisTileDataPooler(KisTileDataStore *store, qint32 memoryLimit
     m_store = store;
     m_timeout = MIN_TIMEOUT;
     m_lastCycleHadWork = false;
+    m_lastPoolMemoryMetric = 0;
+    m_lastRealMemoryMetric = 0;
+    m_lastHistoricalMemoryMetric = 0;
 
     if(memoryLimit >= 0) {
         m_memoryLimit = memoryLimit;
@@ -195,19 +198,44 @@ void KisTileDataPooler::run()
         QList<KisTileData*> donors;
         qint32 memoryOccupied;
 
-        getLists(iter, beggers, donors, memoryOccupied);
+        qint32 statRealMemory;
+        qint32 statHistoricalMemory;
+
+
+        getLists(iter, beggers, donors,
+                 memoryOccupied,
+                 statRealMemory,
+                 statHistoricalMemory);
 
         m_lastCycleHadWork =
             processLists(beggers, donors, memoryOccupied);
 
-        m_store->endIteration(iter);
+        m_lastPoolMemoryMetric = memoryOccupied;
+        m_lastRealMemoryMetric = statRealMemory;
+        m_lastHistoricalMemoryMetric = statHistoricalMemory;
 
+        m_store->endIteration(iter);
 
         DEBUG_TILE_STATISTICS();
         DEBUG_SIMPLE_ACTION("cycle finished");
     }
 }
 
+qint64 KisTileDataPooler::lastPoolMemoryMetric() const
+{
+    return m_lastPoolMemoryMetric;
+}
+
+qint64 KisTileDataPooler::lastRealMemoryMetric() const
+{
+    return m_lastRealMemoryMetric;
+}
+
+qint64 KisTileDataPooler::lastHistoricalMemoryMetric() const
+{
+    return m_lastHistoricalMemoryMetric;
+}
+
 inline int KisTileDataPooler::clonesMetric(KisTileData *td, int numClones) {
     return numClones * td->pixelSize();
 }
@@ -240,9 +268,13 @@ template<class Iter>
 void KisTileDataPooler::getLists(Iter *iter,
                                  QList<KisTileData*> &beggers,
                                  QList<KisTileData*> &donors,
-                                 qint32 &memoryOccupied)
+                                 qint32 &memoryOccupied,
+                                 qint32 &statRealMemory,
+                                 qint32 &statHistoricalMemory)
 {
     memoryOccupied = 0;
+    statRealMemory = 0;
+    statHistoricalMemory = 0;
 
     qint32 needMemoryTotal = 0;
     qint32 canDonorMemoryTotal = 0;
@@ -267,6 +299,13 @@ void KisTileDataPooler::getLists(Iter *iter,
         }
 
         memoryOccupied += clonesMetric(item);
+
+        // statistics gathering
+        if (item->historical()) {
+            statHistoricalMemory += item->pixelSize();
+        } else {
+            statRealMemory += item->pixelSize();
+        }
     }
 
     DEBUG_LISTS(memoryOccupied,
@@ -297,7 +336,7 @@ qint32 KisTileDataPooler::tryGetMemory(QList<KisTileData*> &donors,
 
 bool KisTileDataPooler::processLists(QList<KisTileData*> &beggers,
                                      QList<KisTileData*> &donors,
-                                     qint32 memoryOccupied)
+                                     qint32 &memoryOccupied)
 {
     bool hadWork = false;
 
diff --git a/krita/image/tiles3/kis_tile_data_pooler.h b/krita/image/tiles3/kis_tile_data_pooler.h
index e4ef394..4f4ce37 100644
--- a/krita/image/tiles3/kis_tile_data_pooler.h
+++ b/krita/image/tiles3/kis_tile_data_pooler.h
@@ -40,6 +40,10 @@ public:
 
     void testingRereadConfig();
 
+    qint64 lastPoolMemoryMetric() const;
+    qint64 lastRealMemoryMetric() const;
+    qint64 lastHistoricalMemoryMetric() const;
+
 protected:
     static const qint32 MAX_NUM_CLONES;
     static const qint32 MAX_TIMEOUT;
@@ -61,11 +65,14 @@ protected:
 
     template<class Iter>
         void getLists(Iter *iter, QList<KisTileData*> &beggers,
-                      QList<KisTileData*> &donors, qint32 &memoryOccupied);
+                      QList<KisTileData*> &donors,
+                      qint32 &memoryOccupied,
+                      qint32 &statRealMemory,
+                      qint32 &statHistoricalMemory);
 
     bool processLists(QList<KisTileData*> &beggers,
                       QList<KisTileData*> &donors,
-                      qint32 memoryOccupied);
+                      qint32 &memoryOccupied);
 
 private:
     void debugTileStatistics();
@@ -76,6 +83,9 @@ protected:
     qint32 m_timeout;
     bool m_lastCycleHadWork;
     qint32 m_memoryLimit;
+    qint32 m_lastPoolMemoryMetric;
+    qint32 m_lastRealMemoryMetric;
+    qint32 m_lastHistoricalMemoryMetric;
 };
 
 
diff --git a/krita/image/tiles3/kis_tile_data_store.cc b/krita/image/tiles3/kis_tile_data_store.cc
index 06775c9..53d5fcc 100644
--- a/krita/image/tiles3/kis_tile_data_store.cc
+++ b/krita/image/tiles3/kis_tile_data_store.cc
@@ -95,6 +95,25 @@ KisTileDataStore* KisTileDataStore::instance()
     return s_instance;
 }
 
+KisTileDataStore::MemoryStatistics KisTileDataStore::memoryStatistics()
+{
+    QMutexLocker lock(&m_listLock);
+
+    MemoryStatistics stats;
+
+    const qint64 metricCoeff = KisTileData::WIDTH * KisTileData::HEIGHT;
+
+    stats.realMemorySize = m_pooler.lastRealMemoryMetric() * metricCoeff;
+    stats.historicalMemorySize = m_pooler.lastHistoricalMemoryMetric() * metricCoeff;
+    stats.poolSize = m_pooler.lastPoolMemoryMetric() * metricCoeff;
+
+    stats.totalMemorySize = memoryMetric() * metricCoeff + stats.poolSize;
+
+    stats.swapSize = m_swappedStore.totalMemoryMetric() * metricCoeff;
+
+    return stats;
+}
+
 inline void KisTileDataStore::registerTileDataImp(KisTileData *td)
 {
     td->m_listIterator = m_tileDataList.insert(m_tileDataList.end(), td);
diff --git a/krita/image/tiles3/kis_tile_data_store.h b/krita/image/tiles3/kis_tile_data_store.h
index 67efcb9..3968296 100644
--- a/krita/image/tiles3/kis_tile_data_store.h
+++ b/krita/image/tiles3/kis_tile_data_store.h
@@ -46,6 +46,18 @@ public:
 
     void debugPrintList();
 
+    struct MemoryStatistics {
+        qint64 totalMemorySize;
+        qint64 realMemorySize;
+        qint64 historicalMemorySize;
+
+        qint64 poolSize;
+
+        qint64 swapSize;
+    };
+
+    MemoryStatistics memoryStatistics();
+
     /**
      * Returns total number of tiles present: in memory
      * or in a swap file
diff --git a/krita/image/tiles3/swap/kis_swapped_data_store.cpp b/krita/image/tiles3/swap/kis_swapped_data_store.cpp
index 0a0aec4..6e82b6c 100644
--- a/krita/image/tiles3/swap/kis_swapped_data_store.cpp
+++ b/krita/image/tiles3/swap/kis_swapped_data_store.cpp
@@ -26,6 +26,7 @@
 //#define COMPRESSOR_VERSION 2
 
 KisSwappedDataStore::KisSwappedDataStore()
+    : m_memoryMetric(0)
 {
     KisImageConfig config;
     const quint64 maxSwapSize = config.maxSwapSize() * MiB;
@@ -78,6 +79,8 @@ void KisSwappedDataStore::swapOutTileData(KisTileData *td)
 
     td->releaseMemory();
     td->setSwapChunk(chunk);
+
+    m_memoryMetric += td->pixelSize();
 }
 
 void KisSwappedDataStore::swapInTileData(KisTileData *td)
@@ -95,6 +98,8 @@ void KisSwappedDataStore::swapInTileData(KisTileData *td)
     quint8 *ptr = m_swapSpace->getReadChunkPtr(chunk);
     m_compressor->decompressTileData(ptr, chunk.size(), td);
     m_allocator->freeChunk(chunk);
+
+    m_memoryMetric -= td->pixelSize();
 }
 
 void KisSwappedDataStore::forgetTileData(KisTileData *td)
@@ -103,6 +108,13 @@ void KisSwappedDataStore::forgetTileData(KisTileData *td)
 
     m_allocator->freeChunk(td->swapChunk());
     td->setSwapChunk(KisChunk());
+
+    m_memoryMetric -= td->pixelSize();
+}
+
+qint64 KisSwappedDataStore::totalMemoryMetric() const
+{
+    return m_memoryMetric;
 }
 
 void KisSwappedDataStore::debugStatistics()
diff --git a/krita/image/tiles3/swap/kis_swapped_data_store.h b/krita/image/tiles3/swap/kis_swapped_data_store.h
index c980b0e..fd416b8 100644
--- a/krita/image/tiles3/swap/kis_swapped_data_store.h
+++ b/krita/image/tiles3/swap/kis_swapped_data_store.h
@@ -66,6 +66,12 @@ public:
     void forgetTileData(KisTileData *td);
 
     /**
+     * Retorns the metric of the total memory stored in the swap
+     * in *uncompressed* form!
+     */
+    qint64 totalMemoryMetric() const;
+
+    /**
      * Some debugging output
      */
     void debugStatistics();
@@ -78,6 +84,8 @@ private:
     KisMemoryWindow *m_swapSpace;
 
     QMutex m_lock;
+
+    qint64 m_memoryMetric;
 };
 
 #endif /* __KIS_SWAPPED_DATA_STORE_H */
diff --git a/krita/image/tiles3/swap/kis_tile_data_swapper.cpp b/krita/image/tiles3/swap/kis_tile_data_swapper.cpp
index be40734..7623249 100644
--- a/krita/image/tiles3/swap/kis_tile_data_swapper.cpp
+++ b/krita/image/tiles3/swap/kis_tile_data_swapper.cpp
@@ -159,7 +159,7 @@ public:
 
     static inline bool isInteresting(KisTileData *td) {
         // We are working with mementoed tiles only...
-        return td->mementoed() && td->numUsers() <= 1;
+        return td->historical();
     }
 
     static inline bool swapOutFirst(KisTileData *td) {
diff --git a/krita/ui/kis_statusbar.cc b/krita/ui/kis_statusbar.cc
index c7cce56..0e8cfc7 100644
--- a/krita/ui/kis_statusbar.cc
+++ b/krita/ui/kis_statusbar.cc
@@ -22,7 +22,10 @@
 #include <QLabel>
 #include <QFontMetrics>
 #include <QToolButton>
+#include <QPushButton>
 #include <QAction>
+#include <QToolTip>
+
 
 #include <ksqueezedtextlabel.h>
 #include <kstatusbar.h>
@@ -37,6 +40,7 @@
 #include <kis_selection.h>
 #include <kis_paint_device.h>
 #include <kis_selection_manager.h>
+#include "kis_memory_statistics_server.h"
 
 #include <KisView.h>
 #include "KisViewManager.h"
@@ -85,11 +89,13 @@ KisStatusBar::KisStatusBar(KisViewManager * view)
     m_progress = new KisProgressWidget();
     view->addStatusBarItem(m_progress);
 
-    m_imageSizeLabel = new QLabel(QString());
-    m_imageSizeLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
-    m_imageSizeLabel->setContentsMargins(5,5, 5, 5);
-    m_imageSizeLabel->setMinimumWidth(100);
-    view->addStatusBarItem(m_imageSizeLabel);
+    m_memoryReportBox = new QPushButton();
+    m_memoryReportBox->setFlat(true);
+    m_memoryReportBox->setContentsMargins(5, 5, 5, 5);
+    m_memoryReportBox->setMinimumWidth(120);
+    view->addStatusBarItem(m_memoryReportBox);
+
+    connect(m_memoryReportBox, SIGNAL(clicked()), SLOT(showMemoryInfoToolTip()));
 
     m_pointerPositionLabel = new QLabel(QString());
     m_pointerPositionLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
@@ -98,6 +104,9 @@ KisStatusBar::KisStatusBar(KisViewManager * view)
     view->addStatusBarItem(m_pointerPositionLabel);
     m_pointerPositionLabel->setVisible(false);
 
+    connect(KisMemoryStatisticsServer::instance(),
+            SIGNAL(sigUpdateMemoryStatistics()),
+            SLOT(imageSizeChanged()));
 }
 
 KisStatusBar::~KisStatusBar()
@@ -105,7 +114,7 @@ KisStatusBar::~KisStatusBar()
     m_view->removeStatusBarItem(m_selectionStatus);
     m_view->removeStatusBarItem(m_statusBarStatusLabel);
     m_view->removeStatusBarItem(m_statusBarProfileLabel);
-    m_view->removeStatusBarItem(m_imageSizeLabel);
+    m_view->removeStatusBarItem(m_memoryReportBox);
     m_view->removeStatusBarItem(m_pointerPositionLabel);
     m_view->removeStatusBarItem(m_progress);
 }
@@ -118,6 +127,7 @@ void KisStatusBar::setView(QPointer<KisView> imageView)
     if (m_imageView) {
         m_imageView->disconnect(this);
         m_view->removeStatusBarItem(m_imageView->zoomManager()->zoomActionWidget());
+        m_imageView = 0;
     }
     if (imageView) {
         m_imageView = imageView;
@@ -129,14 +139,17 @@ void KisStatusBar::setView(QPointer<KisView> imageView)
         connect(m_imageView, SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)),
                 this, SLOT(imageSizeChanged()));
         updateStatusBarProfileLabel();
-        imageSizeChanged();
         m_view->addStatusBarItem(m_imageView->zoomManager()->zoomActionWidget());
     }
+
+    imageSizeChanged();
 }
 
 void KisStatusBar::documentMousePositionChanged(const QPointF &pos)
 {
-    QPoint pixelPos = m_view->image()->documentToIntPixel(pos);
+    if (!m_imageView) return;
+
+    QPoint pixelPos = m_imageView->image()->documentToIntPixel(pos);
 
     pixelPos.setX(qBound(0, pixelPos.x(), m_view->image()->width() - 1));
     pixelPos.setY(qBound(0, pixelPos.y(), m_view->image()->height() - 1));
@@ -145,11 +158,21 @@ void KisStatusBar::documentMousePositionChanged(const QPointF &pos)
 
 void KisStatusBar::imageSizeChanged()
 {
-    KisImageWSP image = m_view->image();
-    qint32 w = image->width();
-    qint32 h = image->height();
+    updateMemoryStatus();
+
+    QString sizeText;
+    KisImageWSP image = m_imageView ? m_imageView->image() : 0;
+    if (image) {
+        qint32 w = image->width();
+        qint32 h = image->height();
+        sizeText = QString("%1 x %2 (%3)").arg(w).arg(h).arg(m_shortMemoryTag);
+    } else {
+        sizeText = m_shortMemoryTag;
+    }
 
-    m_imageSizeLabel->setText(QString("%1 x %2").arg(w).arg(h));
+    m_memoryReportBox->setIcon(m_memoryStatusIcon);
+    m_memoryReportBox->setText(sizeText);
+    m_memoryReportBox->setToolTip(m_longMemoryTag);
 }
 
 void KisStatusBar::updateSelectionIcon()
@@ -165,6 +188,89 @@ void KisStatusBar::updateSelectionIcon()
     m_selectionStatus->setIcon(icon);
 }
 
+inline QString formatSize(qint64 size)
+{
+    qint64 K = 1024;
+    QString suffix = i18nc("very shortened \'byte\' suffix (for statusbar)", "b");
+
+    if (size > K) {
+        size /= K;
+        suffix = i18nc("very shortened KiB suffix (for statusbar)", "K");
+    }
+
+    if (size > K) {
+        size /= K;
+        suffix = i18nc("very shortened MiB suffix (for statusbar)", "M");
+    }
+
+    if (size > K) {
+        size /= K;
+        suffix = i18nc("very shortened GiB suffix (for statusbar)", "G");
+    }
+
+    if (size > K) {
+        size /= K;
+        suffix = i18nc("very shortened TiB suffix (for statusbar)", "T");
+    }
+
+    return QString("%2%3").arg(size).arg(suffix);
+}
+
+void KisStatusBar::updateMemoryStatus()
+{
+    KisMemoryStatisticsServer::Statistics stats =
+        KisMemoryStatisticsServer::instance()
+        ->fetchMemoryStatistics(m_imageView ? m_imageView->image() : 0);
+
+    QString longStats =
+        i18nc("tooltip on statusbar memory reporting button",
+              "Image size:\t %1\n"
+              "\n"
+              "Memory used:\t %2 / %3\n"
+              "  image data:\t %4 / %5\n"
+              "  pool:\t\t %6 / %7\n"
+              "  undo data:\t %8\n"
+              "\n"
+              "Swap used:\t %9",
+              formatSize(stats.imageSize),
+
+              formatSize(stats.totalMemorySize),
+              formatSize(stats.totalMemoryLimit),
+
+              formatSize(stats.realMemorySize),
+              formatSize(stats.tilesHardLimit),
+
+              formatSize(stats.poolSize),
+              formatSize(stats.tilesPoolLimit),
+
+              formatSize(stats.historicalMemorySize),
+              formatSize(stats.swapSize));
+
+    QString shortStats = formatSize(stats.imageSize);
+    QIcon icon;
+    qint64 warnLevel = stats.tilesHardLimit - stats.tilesHardLimit / 8;
+
+    if (stats.imageSize > warnLevel ||
+        stats.realMemorySize > warnLevel) {
+
+        icon = QIcon::fromTheme("dialog-warning");
+        QString suffix =
+            i18nc("tooltip on statusbar memory reporting button",
+                  "\n\nWARNING:\tOut of memory! Swapping has been started.\n"
+                  "\t\tPlease configure more RAM for Krita in Settings dialog");
+        longStats += suffix;
+    }
+
+    m_shortMemoryTag = shortStats;
+    m_longMemoryTag = longStats;
+    m_memoryStatusIcon = icon;
+}
+
+void KisStatusBar::showMemoryInfoToolTip()
+{
+    QToolTip::showText(QCursor::pos(), m_memoryReportBox->toolTip(), m_memoryReportBox);
+}
+
 void KisStatusBar::updateSelectionToolTip()
 {
     updateSelectionIcon();
@@ -220,7 +326,9 @@ void KisStatusBar::setHelp(const QString &t)
 
 void KisStatusBar::updateStatusBarProfileLabel()
 {
-    setProfile(m_view->image());
+    if (!m_imageView) return;
+
+    setProfile(m_imageView->image());
 }
 
 
diff --git a/krita/ui/kis_statusbar.h b/krita/ui/kis_statusbar.h
index 5ed52dc..92a34af 100644
--- a/krita/ui/kis_statusbar.h
+++ b/krita/ui/kis_statusbar.h
@@ -21,12 +21,14 @@
 
 #include <QObject>
 #include <QPointer>
+#include <QIcon>
 
 #include <kis_types.h>
 #include "KisView.h"
 
 class QLabel;
 class QToolButton;
+class QPushButton;
 class KSqueezedTextLabel;
 class KisViewManager;
 class KisProgressWidget;
@@ -58,6 +60,10 @@ public Q_SLOTS:
 
 private Q_SLOTS:
     void updateSelectionIcon();
+    void showMemoryInfoToolTip();
+
+private:
+   void updateMemoryStatus();
 
 private:
 
@@ -66,12 +72,16 @@ private:
     KisProgressWidget * m_progress;
 
     QToolButton *m_selectionStatus;
-    QLabel *m_imageSizeLabel;
+    QPushButton *m_memoryReportBox;
     QLabel *m_pointerPositionLabel;
 
     KSqueezedTextLabel *m_statusBarStatusLabel;
     KSqueezedTextLabel *m_statusBarProfileLabel;
 
+
+    QString m_shortMemoryTag;
+    QString m_longMemoryTag;
+    QIcon m_memoryStatusIcon;
 };
 
 #endif


More information about the kimageshop mailing list