[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [calligra/calligra/2.9] krita/ui: Add compressing of the update requests queue in KisCanvas2
From:       Dmitry Kazakov <dimula73 () gmail ! com>
Date:       2015-01-06 6:56:18
Message-ID: E1Y8O3u-0001nZ-PY () scm ! kde ! org
[Download RAW message or body]

Git commit b519932e99079f37f72b97c665de7efade6227dd by Dmitry Kazakov.
Committed on 06/01/2015 at 06:47.
Pushed by dkazakov into branch 'calligra/2.9'.

Add compressing of the update requests queue in KisCanvas2

In some rare circumstances (when there are too many updates pending)
the Qt's queue of sigCanvasCacheUpdated() might be overloaded with events.
In worst case such queue might occupy up to 900MiB in RAM in chunks
of 256x256px. That might shatter not-too-high-end systems, because such
memory request is done almost instantly in multithreaded way.

So now we have a special compressor object that ensures the update requests
are not repeated. It also solves a few performance problems because now
we can upload up to 80% (in special cases only, real numbers much lower)
less memory into GPU memory.

CCBUG:342336

M  +1    -0    krita/ui/CMakeLists.txt
M  +45   -32   krita/ui/canvas/kis_canvas2.cpp
M  +2    -2    krita/ui/canvas/kis_canvas2.h
A  +58   -0    krita/ui/canvas/kis_canvas_updates_compressor.cpp     [License: GPL \
(v2+)] A  +42   -0    krita/ui/canvas/kis_canvas_updates_compressor.h     [License: \
GPL (v2+)] M  +1    -1    krita/ui/canvas/kis_image_pyramid.cpp
M  +3    -3    krita/ui/canvas/kis_prescaled_projection.cpp
M  +13   -0    krita/ui/canvas/kis_update_info.cpp
M  +10   -1    krita/ui/canvas/kis_update_info.h
M  +2    -0    krita/ui/opengl/kis_opengl_image_textures.cpp

http://commits.kde.org/calligra/b519932e99079f37f72b97c665de7efade6227dd

diff --git a/krita/ui/CMakeLists.txt b/krita/ui/CMakeLists.txt
index 60b1783..5aca918 100644
--- a/krita/ui/CMakeLists.txt
+++ b/krita/ui/CMakeLists.txt
@@ -30,6 +30,7 @@ endif (APPLE)
 set(kritaui_LIB_SRCS
     canvas/kis_canvas_widget_base.cpp
     canvas/kis_canvas2.cpp
+    canvas/kis_canvas_updates_compressor.cpp
     canvas/kis_canvas_controller.cpp
     canvas/kis_paintop_transformation_connector.cpp
     canvas/kis_display_color_converter.cpp
diff --git a/krita/ui/canvas/kis_canvas2.cpp b/krita/ui/canvas/kis_canvas2.cpp
index c6cd519..22dd4b3 100644
--- a/krita/ui/canvas/kis_canvas2.cpp
+++ b/krita/ui/canvas/kis_canvas2.cpp
@@ -76,6 +76,8 @@
 #include "input/kis_input_manager.h"
 #include "kis_painting_assistants_decoration.h"
 
+#include "kis_canvas_updates_compressor.h"
+
 class KisCanvas2::KisCanvas2Private
 {
 
@@ -121,6 +123,8 @@ public:
 
     KisPopupPalette *popupPalette;
     KisDisplayColorConverter *displayColorConverter;
+
+    KisCanvasUpdatesCompressor projectionUpdatesCompressor;
 };
 
 KisCanvas2::KisCanvas2(KisCoordinatesConverter *coordConverter, \
KoCanvasResourceManager *resourceManager, QPointer<KisView>view, \
KoShapeBasedDocumentBase *sc) @@ -427,7 +431,7 @@ void KisCanvas2::initializeImage()
     m_d->coordinatesConverter->setImage(image);
 
     connect(image, SIGNAL(sigImageUpdated(QRect)), \
                SLOT(startUpdateCanvasProjection(QRect)), Qt::DirectConnection);
-    connect(this, SIGNAL(sigCanvasCacheUpdated(KisUpdateInfoSP)), \
SLOT(updateCanvasProjection(KisUpdateInfoSP))); +    connect(this, \
                SIGNAL(sigCanvasCacheUpdated()), SLOT(updateCanvasProjection()));
     connect(image, SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), \
                SLOT(startResizingImage()), Qt::DirectConnection);
     connect(this, SIGNAL(sigContinueResizeImage(qint32,qint32)), \
SLOT(finishResizingImage(qint32,qint32)));  
@@ -593,62 +597,71 @@ void KisCanvas2::finishResizingImage(qint32 w, qint32 h)
 
 void KisCanvas2::startUpdateCanvasProjection(const QRect & rc)
 {
+    KisUpdateInfoSP info;
+
     if (m_d->currentCanvasIsOpenGL) {
 #ifdef HAVE_OPENGL
         Q_ASSERT(m_d->openGLImageTextures);
         m_d->openGLImageTextures->setChannelFlags(m_d->channelFlags);
-        KisUpdateInfoSP info = m_d->openGLImageTextures->updateCache(rc);
-
-        emit sigCanvasCacheUpdated(info);
+        info = m_d->openGLImageTextures->updateCache(rc);
 #else
         Q_ASSERT_X(0, "startUpdateCanvasProjection()", "Bad use of \
startUpdateCanvasProjection(). It shouldn't have happened =(");  #endif
     } else {
         Q_ASSERT(m_d->prescaledProjection);
-        KisUpdateInfoSP info = m_d->prescaledProjection->updateCache(rc);
+        info = m_d->prescaledProjection->updateCache(rc);
+    }
 
-        emit sigCanvasCacheUpdated(info);
+    if (m_d->projectionUpdatesCompressor.putUpdateInfo(info)) {
+        emit sigCanvasCacheUpdated();
     }
+
 }
 
-void KisCanvas2::updateCanvasProjection(KisUpdateInfoSP info)
+void KisCanvas2::updateCanvasProjection()
 {
-    /**
-     * It might happen that the canvas type is switched while the
-     * update info is being stuck in the Qt's signals queue. Than a wrong
-     * type of the info may come. So just check it here.
-     */
+    KisUpdateInfoSP info;
+
+    while (info = m_d->projectionUpdatesCompressor.takeUpdateInfo()) {
+
+        /**
+         * It might happen that the canvas type is switched while the
+         * update info is being stuck in the Qt's signals queue. Than a wrong
+         * type of the info may come. So just check it here.
+         */
 #ifdef HAVE_OPENGL
-    bool isOpenGLUpdateInfo = dynamic_cast<KisOpenGLUpdateInfo*>(info.data());
-    if (isOpenGLUpdateInfo != m_d->currentCanvasIsOpenGL)
-        return;
+        bool isOpenGLUpdateInfo = dynamic_cast<KisOpenGLUpdateInfo*>(info.data());
+        if (isOpenGLUpdateInfo != m_d->currentCanvasIsOpenGL)
+            continue;
 #endif
 
-    if (m_d->currentCanvasIsOpenGL) {
+        if (m_d->currentCanvasIsOpenGL) {
 #ifdef HAVE_OPENGL
-        Q_ASSERT(m_d->openGLImageTextures);
-        m_d->openGLImageTextures->recalculateCache(info);
 
-        /**
-         * FIXME: Please not update entire canvas
-         * Implement info->dirtyViewportRect()
-         */
-        updateCanvasWidgetImpl();
+            Q_ASSERT(m_d->openGLImageTextures);
+            m_d->openGLImageTextures->recalculateCache(info);
+
+            /**
+             * FIXME: Please not update entire canvas
+             * Implement info->dirtyViewportRect()
+             */
+            updateCanvasWidgetImpl();
 #else
-        Q_ASSERT_X(0, "updateCanvasProjection()", "Bad use of \
updateCanvasProjection(). It shouldn't have happened =("); +            Q_ASSERT_X(0, \
"updateCanvasProjection()", "Bad use of updateCanvasProjection(). It shouldn't have \
happened =(");  #endif
-    }
-    else {
-        // See comment in startUpdateCanvasProjection()
-        Q_ASSERT(m_d->prescaledProjection);
+        }
+        else {
+            // See comment in startUpdateCanvasProjection()
+            Q_ASSERT(m_d->prescaledProjection);
 
-        m_d->prescaledProjection->recalculateCache(info);
+            m_d->prescaledProjection->recalculateCache(info);
 
-        QRect vRect = m_d->coordinatesConverter->
+            QRect vRect = m_d->coordinatesConverter->
                 viewportToWidget(info->dirtyViewportRect()).toAlignedRect();
 
-        if (!vRect.isEmpty()) {
-            updateCanvasWidgetImpl(vRect);
+            if (!vRect.isEmpty()) {
+                updateCanvasWidgetImpl(vRect);
+            }
         }
     }
 }
diff --git a/krita/ui/canvas/kis_canvas2.h b/krita/ui/canvas/kis_canvas2.h
index f3d6b0a..c0f3550 100644
--- a/krita/ui/canvas/kis_canvas2.h
+++ b/krita/ui/canvas/kis_canvas2.h
@@ -173,7 +173,7 @@ public: // KisCanvas2 methods
 signals:
     void imageChanged(KisImageWSP image);
 
-    void sigCanvasCacheUpdated(KisUpdateInfoSP);
+    void sigCanvasCacheUpdated();
     void sigContinueResizeImage(qint32 w, qint32 h);
 
     void documentOffsetUpdateFinished();
@@ -199,7 +199,7 @@ private slots:
     /// The image projection has changed, now start an update
     /// of the canvas representation.
     void startUpdateCanvasProjection(const QRect & rc);
-    void updateCanvasProjection(KisUpdateInfoSP info);
+    void updateCanvasProjection();
 
     void startUpdateInPatches(QRect imageRect);
 
diff --git a/krita/ui/canvas/kis_canvas_updates_compressor.cpp \
b/krita/ui/canvas/kis_canvas_updates_compressor.cpp new file mode 100644
index 0000000..48d35c9
--- /dev/null
+++ b/krita/ui/canvas/kis_canvas_updates_compressor.cpp
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (c) 2015 Dmitry Kazakov <dimula73@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_canvas_updates_compressor.h"
+
+bool KisCanvasUpdatesCompressor::putUpdateInfo(KisUpdateInfoSP info)
+{
+    const QRect newUpdateRect = info->dirtyImageRect();
+    if (newUpdateRect.isEmpty()) return false;
+
+    QMutexLocker l(&m_mutex);
+    bool updateOverridden = false;
+
+    UpdateInfoList::iterator it = m_updatesList.begin();
+    while (it != m_updatesList.end()) {
+        if (newUpdateRect.contains((*it)->dirtyImageRect())) {
+            if (info) {
+                *it = info;
+                info = 0;
+                ++it;
+            } else {
+                it = m_updatesList.erase(it);
+            }
+
+            updateOverridden = true;
+        } else {
+            ++it;
+        }
+    }
+
+    if (!updateOverridden) {
+        Q_ASSERT(info);
+        m_updatesList.append(info);
+    }
+
+    return !updateOverridden;
+}
+
+KisUpdateInfoSP KisCanvasUpdatesCompressor::takeUpdateInfo()
+{
+    QMutexLocker l(&m_mutex);
+    return !m_updatesList.isEmpty() ? m_updatesList.takeFirst() : 0;
+}
diff --git a/krita/ui/canvas/kis_canvas_updates_compressor.h \
b/krita/ui/canvas/kis_canvas_updates_compressor.h new file mode 100644
index 0000000..b13018f
--- /dev/null
+++ b/krita/ui/canvas/kis_canvas_updates_compressor.h
@@ -0,0 +1,42 @@
+/*
+ *  Copyright (c) 2015 Dmitry Kazakov <dimula73@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_CANVAS_UPDATES_COMPRESSOR_H
+#define __KIS_CANVAS_UPDATES_COMPRESSOR_H
+
+#include <QList>
+#include <QMutex>
+#include <QMutexLocker>
+
+#include "kis_update_info.h"
+
+
+class KisCanvasUpdatesCompressor
+{
+    typedef QList<KisUpdateInfoSP> UpdateInfoList;
+
+public:
+    bool putUpdateInfo(KisUpdateInfoSP info);
+    KisUpdateInfoSP takeUpdateInfo();
+
+private:
+    QMutex m_mutex;
+    UpdateInfoList m_updatesList;
+};
+
+#endif /* __KIS_CANVAS_UPDATES_COMPRESSOR_H */
diff --git a/krita/ui/canvas/kis_image_pyramid.cpp \
b/krita/ui/canvas/kis_image_pyramid.cpp index cf4836d..d2454e5 100644
--- a/krita/ui/canvas/kis_image_pyramid.cpp
+++ b/krita/ui/canvas/kis_image_pyramid.cpp
@@ -307,7 +307,7 @@ void KisImagePyramid::recalculateCache(KisPPUpdateInfoSP info)
 {
     KisPaintDevice *src;
     KisPaintDevice *dst;
-    QRect currentSrcRect = info->dirtyImageRect;
+    QRect currentSrcRect = info->dirtyImageRectVar;
 
     for (int i = FIRST_NOT_ORIGINAL_INDEX; i < m_pyramidHeight; i++) {
         src = m_pyramid[i-1].data();
diff --git a/krita/ui/canvas/kis_prescaled_projection.cpp \
b/krita/ui/canvas/kis_prescaled_projection.cpp index 1985094..1da52f6 100644
--- a/krita/ui/canvas/kis_prescaled_projection.cpp
+++ b/krita/ui/canvas/kis_prescaled_projection.cpp
@@ -227,7 +227,7 @@ void KisPrescaledProjection::recalculateCache(KisUpdateInfoSP \
info)  
     QRect rawViewRect =
         m_d->coordinatesConverter->
-        imageToViewport(ppInfo->dirtyImageRect).toAlignedRect();
+        imageToViewport(ppInfo->dirtyImageRectVar).toAlignedRect();
 
     fillInUpdateInformation(rawViewRect, ppInfo);
 
@@ -317,7 +317,7 @@ KisPPUpdateInfoSP \
                KisPrescaledProjection::getInitialUpdateInformation(const QRec
      */
 
     KisPPUpdateInfoSP info = new KisPPUpdateInfo();
-    info->dirtyImageRect = dirtyImageRect;
+    info->dirtyImageRectVar = dirtyImageRect;
 
     return info;
 }
@@ -369,7 +369,7 @@ void KisPrescaledProjection::fillInUpdateInformation(const QRect \
&viewportRect,  dbgRender << ppVar(info->scaleX) << ppVar(info->scaleY);
     dbgRender << ppVar(info->borderWidth) << ppVar(info->renderHints);
     dbgRender << ppVar(info->transfer);
-    dbgRender << ppVar(info->dirtyImageRect);
+    dbgRender << ppVar(info->dirtyImageRectVar);
     dbgRender << "Not aligned rect of the canvas (raw):\t" << croppedViewRect;
     dbgRender << "Update rect in KisImage's pixels:\t" << info->imageRect;
     dbgRender << "Update rect in canvas' pixels:\t" << info->viewportRect;
diff --git a/krita/ui/canvas/kis_update_info.cpp \
b/krita/ui/canvas/kis_update_info.cpp index 437b7e9..c38b07a 100644
--- a/krita/ui/canvas/kis_update_info.cpp
+++ b/krita/ui/canvas/kis_update_info.cpp
@@ -29,6 +29,9 @@ QRect KisPPUpdateInfo::dirtyViewportRect() {
     return viewportRect.toAlignedRect();
 }
 
+QRect KisPPUpdateInfo::dirtyImageRect() const {
+    return dirtyImageRectVar;
+}
 
 #ifdef HAVE_OPENGL
 
@@ -37,4 +40,14 @@ QRect KisOpenGLUpdateInfo::dirtyViewportRect() {
     return QRect();
 }
 
+void KisOpenGLUpdateInfo::assignDirtyImageRect(const QRect &rect)
+{
+    m_dirtyImageRect = rect;
+}
+
+QRect KisOpenGLUpdateInfo::dirtyImageRect() const
+{
+    return m_dirtyImageRect;
+}
+
 #endif /* HAVE_OPENGL */
diff --git a/krita/ui/canvas/kis_update_info.h b/krita/ui/canvas/kis_update_info.h
index 99d8010..d534f99 100644
--- a/krita/ui/canvas/kis_update_info.h
+++ b/krita/ui/canvas/kis_update_info.h
@@ -34,6 +34,7 @@ public:
     virtual ~KisUpdateInfo();
 
     virtual QRect dirtyViewportRect();
+    virtual QRect dirtyImageRect() const = 0;
 };
 
 Q_DECLARE_METATYPE(KisUpdateInfoSP)
@@ -48,7 +49,14 @@ class KisOpenGLUpdateInfo : public KisUpdateInfo
 {
 public:
     KisTextureTileUpdateInfoSPList tileList;
+
     QRect dirtyViewportRect();
+    QRect dirtyImageRect() const;
+
+    void assignDirtyImageRect(const QRect &rect);
+
+private:
+    QRect m_dirtyImageRect;
 };
 #endif /* HAVE_OPENGL */
 
@@ -61,11 +69,12 @@ public:
     };
 
     QRect dirtyViewportRect();
+    QRect dirtyImageRect() const;
 
     /**
      * The rect that was reported by KisImage as dirty
      */
-    QRect dirtyImageRect;
+    QRect dirtyImageRectVar;
 
     /**
      * Rect of KisImage corresponding to @viewportRect.
diff --git a/krita/ui/opengl/kis_opengl_image_textures.cpp \
b/krita/ui/opengl/kis_opengl_image_textures.cpp index 7fdb2e9..47eaf55 100644
--- a/krita/ui/opengl/kis_opengl_image_textures.cpp
+++ b/krita/ui/opengl/kis_opengl_image_textures.cpp
@@ -260,6 +260,8 @@ KisOpenGLUpdateInfoSP KisOpenGLImageTextures::updateCache(const \
QRect& rect)  }
         }
     }
+
+    info->assignDirtyImageRect(rect);
     return info;
 }
 


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic