hi ..

doing some profiling with valgrind/massif, i noticed that a large amount of 
start up processing and memory usage is spent in parsing SVGs. surprise! ;)

i introduced a KPixmapCache for on-disk caching to help prevent the need to 
render from the source SVG over and over.

i was able to squeeze ~1MB of RAM usage out by doing that. but we can be even 
better ....

right now what is kicking us in the teeth is accessing embedded hints in the 
SVG file. turns out they are SUCH a great idea after all ;) at least not when 
we don't cache them, resulting in unecessary parsing of the SVG.

in particular, those include:

hint-apply-color-scheme - checked every time setImagePath is called

hint-*-margin - checked whenever PanelSvg::updateSizes is called

additionally, the following methods kill us:

Svg::hasElement (mostly called from PanelSvg::hasElementPrefix)
Svg::elementSize
Svg::elementRect

i decided to try tackling the first one, hint-apply-color-scheme, by caching 
that in the global config file.. turns out this help us avoid creating the 
renderer in a huge number of cases. in fact, with the attached patch applied, 
after the first run only PanelSvg cause us to hit createRenderer method.

just getting rid of the embedded hints isn't going to help us any: 
elementSize, elementRect and hasElement will still trigger unecessary 
createRenderer() calls.

so i'm thinking that it might make sense to keep a cache of some sort for all 
these parameters so that we only end up opening the SVG files to do new 
rendering.

one outstanding issue will be clearing the disk and parameter caches when the 
theme is updated on disk but not changed in the config.

before i go any further with this approach: any thoughts, ideas, inspirations?

-- 
Aaron J. Seigo
humru othro a kohnu se
GPG Fingerprint: 8B8B 2209 0C6F 7C47 B1EA  EE75 D6B7 2EB1 A7F1 DB43

KDE core developer sponsored by Qt Software

Index: theme.h
===================================================================
--- theme.h	(revision 871491)
+++ theme.h	(working copy)
@@ -179,6 +179,19 @@
          */
         bool useGlobalSettings() const;
 
+        /**
+         * Tries to load pixmap with the specified key from cache.
+         * @return true when pixmap was found and loaded from cache, false otherwise
+         **/
+        bool findInCache(const QString &key, QPixmap &pix);
+
+        /**
+         * Insert specified pixmap into the cache.
+         * If the cache already contains pixmap with the specified key then it is
+         *  overwritten.
+         **/
+        void insertIntoCache(const QString& key, const QPixmap& pix);
+
     Q_SIGNALS:
         /**
          * Emitted when the user changes the theme. SVGs should be reloaded at
Index: svg.cpp
===================================================================
--- svg.cpp	(revision 871491)
+++ svg.cpp	(working copy)
@@ -22,15 +22,15 @@
 #include <QDir>
 #include <QMatrix>
 #include <QPainter>
-#include <QPixmapCache>
 #include <QSharedData>
 
+#include <KColorScheme>
+#include <KConfigGroup>
 #include <KDebug>
+#include <KIconEffect>
+#include <KGlobalSettings>
 #include <KSharedPtr>
 #include <KSvgRenderer>
-#include <KColorScheme>
-#include <KIconEffect>
-#include <KGlobalSettings>
 
 #include "theme.h"
 
@@ -77,8 +77,17 @@
             eraseRenderer();
         }
 
-        void setImagePath(const QString &imagePath, Svg *q)
+        bool setImagePath(const QString &imagePath, Svg *q)
         {
+            bool isThemed = !QDir::isAbsolutePath(imagePath);
+            if (isThemed == themed &&
+                ((themed && themePath == imagePath) ||
+                 (!themed && path == imagePath))) {
+                return false;
+            }
+
+            bool updateNeeded = !path.isEmpty() || !themePath.isEmpty();
+
             if (themed) {
                 QObject::disconnect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()),
                                     q, SLOT(themeChanged()));
@@ -86,7 +95,7 @@
                                     q, SLOT(colorsChanged()));
             }
 
-            themed = !QDir::isAbsolutePath(imagePath);
+            themed = isThemed;
             path.clear();
             themePath.clear();
 
@@ -96,38 +105,22 @@
                                  q, SLOT(themeChanged()));
 
                 // check if svg wants colorscheme applied
-                createRenderer();
-                applyColors = renderer->elementExists("hint-apply-color-scheme");
+                checkApplyColorHint();
                 if (applyColors && !Theme::defaultTheme()->colorScheme()) {
                     QObject::connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
                                      q, SLOT(colorsChanged()));
                 }
-
+            } else if (!QFile::exists(path)) {
+                path = imagePath;
             } else {
-                path = imagePath;
-
-                if (!QFile::exists(path)) {
-                    kDebug() << "file '" << path << "' does not exist!";
-                }
+                kDebug() << "file '" << path << "' does not exist!";
             }
-        }
 
-        void removeFromCache() {
-            if (ids.isEmpty()) {
-                return;
-            }
-
-            foreach (const QString &id, ids) {
-                QPixmapCache::remove(id);
-            }
-
-            ids.clear();
+            return updateNeeded;
         }
 
         QPixmap findInCache(const QString &elementId, const QSizeF &s = QSizeF())
         {
-            createRenderer();
-
             QSize size;
             if (elementId.isEmpty() || multipleImages) {
                 size = s.toSize();
@@ -153,7 +146,8 @@
 
             QPixmap p;
 
-            if (QPixmapCache::find(id, p)) {
+            Theme *theme = Theme::defaultTheme();
+            if (theme->findInCache(id, p)) {
                 //kDebug() << "found cached version of " << id;
                 return p;
             } else {
@@ -168,6 +162,7 @@
             p.fill(Qt::transparent);
             QPainter renderPainter(&p);
 
+            createRenderer();
             if (elementId.isEmpty()) {
                 renderer->render(&renderPainter);
             } else {
@@ -179,15 +174,11 @@
             // Apply current color scheme if the svg asks for it
             if (applyColors) {
                 QImage itmp = p.toImage();
-                KIconEffect::colorize(
-                    itmp, Theme::defaultTheme()->color(Theme::BackgroundColor), 1.0);
+                KIconEffect::colorize(itmp, theme->color(Theme::BackgroundColor), 1.0);
                 p = p.fromImage(itmp);
             }
 
-            if (!QPixmapCache::insert(id, p)) {
-                //kDebug() << "pixmap cache is too small for inserting" << id << "of size" << s;
-            }
-
+            theme->insertIntoCache(id, p);
             return p;
         }
 
@@ -201,24 +192,26 @@
                 path = Plasma::Theme::defaultTheme()->imagePath(themePath);
             }
 
-            QHash<QString, SharedSvgRenderer::Ptr>::const_iterator it = renderers.find(path);
+            QHash<QString, SharedSvgRenderer::Ptr>::const_iterator it = s_renderers.find(path);
 
-            if (it != renderers.end()) {
+            if (it != s_renderers.end()) {
                 //kDebug() << "gots us an existing one!";
                 renderer = it.value();
             } else {
                 renderer = new SharedSvgRenderer(path);
-                renderers[path] = renderer;
+                s_renderers[path] = renderer;
             }
 
-            size = renderer->defaultSize();
+            if (size.isNull()) {
+                size = renderer->defaultSize();
+            }
         }
 
         void eraseRenderer()
         {
             if (renderer && renderer.count() == 2) {
                 // this and the cache reference it; and boy is this not thread safe ;)
-                renderers.erase(renderers.find(path));
+                s_renderers.erase(s_renderers.find(path));
             }
 
             renderer = 0;
@@ -260,6 +253,19 @@
             return renderer->matrixForElement(elementId);
         }
 
+        void checkApplyColorHint()
+        {
+            KConfigGroup cg(KGlobal::config(), "SvgHints");
+            QString cgKey = themePath + "-hint-apply-color-scheme";
+            if (cg.hasKey(cgKey)) {
+                applyColors = cg.readEntry(cgKey, false);
+            } else {
+                createRenderer();
+                applyColors = renderer->elementExists("hint-apply-color-scheme");
+                cg.writeEntry(cgKey, applyColors);
+            }
+        }
+
         void themeChanged()
         {
             if (!themed) {
@@ -272,14 +278,12 @@
                 return;
             }
 
-            removeFromCache();
             path = newPath;
             //delete d->renderer; we're a KSharedPtr
             eraseRenderer();
 
             // check if new theme svg wants colorscheme applied
-            createRenderer();
-            applyColors = renderer->elementExists("hint-apply-color-scheme");
+            checkApplyColorHint();
             if (applyColors && !Theme::defaultTheme()->colorScheme()) {
                 QObject::connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
                                  q, SLOT(colorsChanged()));
@@ -297,13 +301,12 @@
                 return;
             }
 
-            removeFromCache();
             eraseRenderer();
             emit q->repaintNeeded();
         }
 
         Svg *q;
-        static QHash<QString, SharedSvgRenderer::Ptr> renderers;
+        static QHash<QString, SharedSvgRenderer::Ptr> s_renderers;
         SharedSvgRenderer::Ptr renderer;
         QString themePath;
         QString path;
@@ -314,7 +317,7 @@
         bool applyColors;
 };
 
-QHash<QString, SharedSvgRenderer::Ptr> SvgPrivate::renderers;
+QHash<QString, SharedSvgRenderer::Ptr> SvgPrivate::s_renderers;
 
 Svg::Svg(QObject *parent)
     : QObject(parent),
@@ -371,14 +374,16 @@
 
 void Svg::resize(const QSizeF &size)
 {
-    d->createRenderer();
     d->size = size;
 }
 
 void Svg::resize()
 {
-    d->createRenderer();
-    d->size = d->renderer->defaultSize();
+    if (d->renderer) {
+        d->size = d->renderer->defaultSize();
+    } else {
+        d->size = QSizeF();
+    }
 }
 
 QSize Svg::elementSize(const QString &elementId) const
@@ -393,6 +398,10 @@
 
 bool Svg::hasElement(const QString &elementId) const
 {
+    if (d->path.isNull() && d->themePath.isNull()) {
+        return false;
+    }
+
     d->createRenderer();
     return d->renderer->elementExists(elementId);
 }
@@ -415,6 +424,10 @@
 
 bool Svg::isValid() const
 {
+    if (d->path.isNull() && d->themePath.isNull()) {
+        return false;
+    }
+
     d->createRenderer();
     return d->renderer->isValid();
 }
@@ -431,9 +444,10 @@
 
 void Svg::setImagePath(const QString &svgFilePath)
 {
-   d->setImagePath(svgFilePath, this);
-   d->eraseRenderer();
-   emit repaintNeeded();
+    if (d->setImagePath(svgFilePath, this)) {
+        d->eraseRenderer();
+        emit repaintNeeded();
+    }
 }
 
 QString Svg::imagePath() const
Index: theme.cpp
===================================================================
--- theme.cpp	(revision 871491)
+++ theme.cpp	(working copy)
@@ -25,15 +25,17 @@
 #include <QX11Info>
 #endif
 
-#include <KWindowSystem>
 #include <KColorScheme>
+#include <KComponentData>
 #include <KConfigGroup>
 #include <KDebug>
 #include <KGlobal>
+#include <KGlobalSettings>
+#include <KPixmapCache>
 #include <KSelectionWatcher>
 #include <KSharedConfig>
 #include <KStandardDirs>
-#include <KGlobalSettings>
+#include <KWindowSystem>
 
 #include "private/packages_p.h"
 
@@ -56,6 +58,7 @@
           defaultWallpaperSuffix(DEFAULT_WALLPAPER_SUFFIX),
           defaultWallpaperWidth(DEFAULT_WALLPAPER_WIDTH),
           defaultWallpaperHeight(DEFAULT_WALLPAPER_HEIGHT),
+          pixmapCache(0),
           locolor(false),
           compositingActive(KWindowSystem::compositingActive()),
           isDefault(false),
@@ -102,6 +105,7 @@
     QString defaultWallpaperSuffix;
     int defaultWallpaperWidth;
     int defaultWallpaperHeight;
+    KPixmapCache *pixmapCache;
 
 #ifdef Q_WS_X11
     KSelectionWatcher *compositeWatch;
@@ -466,6 +470,24 @@
     return d->useGlobal;
 }
 
+bool Theme::findInCache(const QString &key, QPixmap &pix)
+{
+    if (!d->pixmapCache) {
+        d->pixmapCache = new KPixmapCache(KGlobal::mainComponent().componentName());
+    }
+
+    return d->pixmapCache->find(key, pix);
 }
 
+void Theme::insertIntoCache(const QString& key, const QPixmap& pix)
+{
+    if (!d->pixmapCache) {
+        d->pixmapCache = new KPixmapCache(KGlobal::mainComponent().componentName());
+    }
+
+    d->pixmapCache->insert(key, pix);
+}
+
+}
+
 #include <theme.moc>

Attachment: signature.asc
Description: This is a digitally signed message part.

_______________________________________________
Plasma-devel mailing list
Plasma-devel@kde.org
https://mail.kde.org/mailman/listinfo/plasma-devel

Reply via email to