[kgraphviewer-devel] [kgraphviewer] src/part: optimize: cache slow QFontMetrics calculations over paint events

Milian Wolff mail at milianw.de
Thu Sep 22 20:57:02 UTC 2011


Git commit a12378611d1137d300a826e785a0e7270f8f2208 by Milian Wolff.
Committed on 15/07/2011 at 19:19.
Pushed by mwolff into branch 'master'.

optimize: cache slow QFontMetrics calculations over paint events

the introductions of the renderOperationsRevision should allow more optimizations
across paint events when nothing actually changed (i.e. only scrolling occurred)

M  +12   -8    src/part/DotGraphParsingHelper.cpp
M  +36   -12   src/part/canvaselement.cpp
M  +4    -1    src/part/canvaselement.h
M  +11   -6    src/part/dotgraph.cpp
M  +17   -13   src/part/graphedge.cpp
M  +8    -1    src/part/graphelement.cpp
M  +8    -3    src/part/graphelement.h
M  +9    -5    src/part/graphnode.cpp
M  +11   -7    src/part/graphsubgraph.cpp

http://commits.kde.org/kgraphviewer/a12378611d1137d300a826e785a0e7270f8f2208

diff --git a/src/part/DotGraphParsingHelper.cpp b/src/part/DotGraphParsingHelper.cpp
index 0be9e81..69cac49 100644
--- a/src/part/DotGraphParsingHelper.cpp
+++ b/src/part/DotGraphParsingHelper.cpp
@@ -90,26 +90,28 @@ void DotGraphParsingHelper::setgraphelementattributes(GraphElement* ge, const At
     }
   }
   
+  DotRenderOpVec ops = ge->renderOperations();
   if (attributes.find("_draw_") != attributes.end())
   {
-    parse_renderop((attributes.find("_draw_"))->second, ge->renderOperations());
+    parse_renderop((attributes.find("_draw_"))->second, ops);
 //     kDebug() << "element renderOperations size is now " << ge->renderOperations().size();
   }
   if (attributes.find("_ldraw_") != attributes.end())
   {
-    parse_renderop(attributes.find("_ldraw_")->second, ge->renderOperations());
+    parse_renderop(attributes.find("_ldraw_")->second, ops);
 //     kDebug() << "element renderOperations size is now " << ge->renderOperations().size();
   }
   if (attributes.find("_hldraw_") != attributes.end())
   {
-    parse_renderop(attributes.find("_hldraw_")->second, ge->renderOperations());
+    parse_renderop(attributes.find("_hldraw_")->second, ops);
 //     kDebug() << "element renderOperations size is now " << ge->renderOperations().size();
   }
   if (attributes.find("_tldraw_") != attributes.end())
   {
-    parse_renderop(attributes.find("_tldraw_")->second, ge->renderOperations());
+    parse_renderop(attributes.find("_tldraw_")->second, ops);
 //     kDebug() << "element renderOperations size is now " << ge->renderOperations().size();
   }
+  ge->setRenderOperations(ops);
 }
 
 void DotGraphParsingHelper::setgraphattributes()
@@ -150,24 +152,26 @@ void DotGraphParsingHelper::setedgeattributes()
 //   kDebug() << "z="<<ge->z();
   setgraphelementattributes(ge, edgesAttributes);
   
+  DotRenderOpVec ops = ge->renderOperations();
   if (edgesAttributes.find("_tdraw_") != edgesAttributes.end())
   {
-    parse_renderop(edgesAttributes["_tdraw_"], ge->renderOperations());
+    parse_renderop(edgesAttributes["_tdraw_"], ops);
 //     kDebug() << "edge renderOperations size is now " << ge->renderOperations().size();
     DotRenderOpVec::const_iterator it, it_end;
-    it = ge->renderOperations().constBegin(); it_end = ge->renderOperations().constEnd();
+    it = ops.constBegin(); it_end = ops.constEnd();
     for (; it != it_end; it++)
       ge->arrowheads().push_back(*it);
   }
   if (edgesAttributes.find("_hdraw_") != edgesAttributes.end())
   {
-    parse_renderop(edgesAttributes["_hdraw_"], ge->renderOperations());
+    parse_renderop(edgesAttributes["_hdraw_"], ops);
 //     kDebug() << "edge renderOperations size is now " << ge->renderOperations().size();
     DotRenderOpVec::const_iterator it, it_end;
-    it = ge->renderOperations().constBegin(); it_end = ge->renderOperations().constEnd();
+    it = ops.constBegin(); it_end = ops.constEnd();
     for (; it != it_end; it++)
       ge->arrowheads().push_back(*it);
   }
+  ge->setRenderOperations(ops);
 }
 
 void DotGraphParsingHelper::setattributedlist()
diff --git a/src/part/canvaselement.cpp b/src/part/canvaselement.cpp
index dcbb28e..57f1b1d 100644
--- a/src/part/canvaselement.cpp
+++ b/src/part/canvaselement.cpp
@@ -54,10 +54,12 @@ CanvasElement::CanvasElement(
     m_font(0),
     m_pen(Dot2QtConsts::componentData().qtColor(gelement->fontColor())),
     m_popup(new QMenu()),
-    m_hovered(false)
+    m_hovered(false),
+    m_lastRenderOpRev(0)
 {
 //   kDebug();
   m_font = FontsCache::changeable().fromName(gelement->fontName());
+
 /*  kDebug() << "Creating CanvasElement for "<<gelement->id();
   kDebug() << "    data: " << wdhcf << "," << hdvcf << "," << gh << "," 
     << scaleX << "," << scaleY << "," << xMargin << "," << yMargin << endl;*/
@@ -206,9 +208,14 @@ void CanvasElement::computeBoundingRect()
   setPos(0,0);
 }
 
+///TODO: optimize more!
 void CanvasElement::paint(QPainter* p, const QStyleOptionGraphicsItem *option,
 QWidget *widget)
 {
+  if (m_lastRenderOpRev != element()->renderOperationsRevision()) {
+    m_fontSizeCache.clear();
+  }
+
   Q_UNUSED(option)
   Q_UNUSED(widget)
   /// computes the scaling of line width
@@ -422,6 +429,7 @@ QWidget *widget)
 //   kDebug() << "Drawing" << element()->id() << "labels";
   QString color = lineColor.name();
   it.toFront();
+  uint num_T = 0;
   while (it.hasNext())
   {
     const DotRenderOp& dro = it.next();
@@ -438,6 +446,7 @@ QWidget *widget)
     }
     else if ( dro.renderop == "T" )
     {
+      ++num_T;
       // we suppose here that the color has been set just before
       element()->setFontColor(color);
       // draw a label
@@ -447,20 +456,33 @@ QWidget *widget)
 //         << " (" << element()->fontName() << ", " << element()->fontSize()
 //         << ", " << element()->fontColor() << ")";
 
-      int stringWidthGoal = int(dro.integers[3] * m_scaleX);
-      int fontSize = element()->fontSize();
+      int fontWidth = 0;
+      bool cacheValid = false;
 //       kDebug() << element()->id() << " initial fontSize " << fontSize;
-      m_font->setPointSize(fontSize);
-
-      QFontMetrics fm(*m_font);
-      int fontWidth = fm.width(dro.str);
-      while (fontWidth > stringWidthGoal && fontSize > 1)
-      {
-        // use floor'ed extrapolated font size
-        fontSize = double(stringWidthGoal) / fontWidth * fontSize;
+      if (m_lastRenderOpRev == element()->renderOperationsRevision()) {
+        FontSizeCache::iterator cacheIt = m_fontSizeCache.find(num_T);
+        if (cacheIt != m_fontSizeCache.end()) {
+            m_font->setPointSize(cacheIt->first);
+            fontWidth = cacheIt->second;
+            cacheValid = true;
+        }
+      }
+      if (!cacheValid) {
+        int stringWidthGoal = int(dro.integers[3] * m_scaleX);
+        int fontSize = element()->fontSize();
         m_font->setPointSize(fontSize);
-        fm = QFontMetrics(*m_font);
+
+        QFontMetrics fm(*m_font);
         fontWidth = fm.width(dro.str);
+        while (fontWidth > stringWidthGoal && fontSize > 1)
+        {
+            // use floor'ed extrapolated font size
+            fontSize = double(stringWidthGoal) / fontWidth * fontSize;
+            m_font->setPointSize(fontSize);
+            fm = QFontMetrics(*m_font);
+            fontWidth = fm.width(dro.str);
+        }
+        m_fontSizeCache[num_T] = qMakePair(fontSize, fontWidth);
       }
 
       p->save();
@@ -495,6 +517,8 @@ QWidget *widget)
     p->drawRect(QRectF(m_boundingRect.bottomRight()-QPointF(6,6),QSizeF(6,6)));
     p->restore();
   }
+
+  m_lastRenderOpRev = element()->renderOperationsRevision();
 }
 
 void CanvasElement::mousePressEvent(QGraphicsSceneMouseEvent* event)
diff --git a/src/part/canvaselement.h b/src/part/canvaselement.h
index 40aedb3..5a79576 100644
--- a/src/part/canvaselement.h
+++ b/src/part/canvaselement.h
@@ -83,7 +83,10 @@ public:
   QMenu* m_popup;
 
   bool m_hovered;
-  
+
+  quint32 m_lastRenderOpRev;
+  typedef QHash<int, QPair<int, int> > FontSizeCache;
+  FontSizeCache m_fontSizeCache;
 Q_SIGNALS:
   void selected(CanvasElement*, Qt::KeyboardModifiers);
   void elementContextMenuEvent(const QString&, const QPoint&);
diff --git a/src/part/dotgraph.cpp b/src/part/dotgraph.cpp
index fdb53ad..b9f2b9b 100644
--- a/src/part/dotgraph.cpp
+++ b/src/part/dotgraph.cpp
@@ -399,18 +399,23 @@ void DotGraph::updateWithGraph(graph_t* newGraph)
   kDebug();
 
   // copy global graph render operations and attributes
-  renderOperations().clear();
+  DotRenderOpVec ops;
+  // decrease mem peak
+  setRenderOperations(ops);
+
   if (agget(newGraph, (char*)"_draw_") != NULL)
   {
-    parse_renderop(agget(newGraph, (char*)"_draw_"), renderOperations());
-    kDebug() << "_draw_: element renderOperations size is now " << renderOperations().size();
+    parse_renderop(agget(newGraph, (char*)"_draw_"), ops);
+    kDebug() << "_draw_: element renderOperations size is now " << ops.size();
   }
   if (agget(newGraph, (char*)"_ldraw_") != NULL)
   {
-    parse_renderop(agget(newGraph, (char*)"_ldraw_"), renderOperations());
-    kDebug() << "_ldraw_: element renderOperations size is now " << renderOperations().size();
+    parse_renderop(agget(newGraph, (char*)"_ldraw_"), ops);
+    kDebug() << "_ldraw_: element renderOperations size is now " << ops.size();
   }
-  
+
+  setRenderOperations(ops);
+
   Agsym_t *attr = agfstattr(newGraph);
   while(attr)
   {
diff --git a/src/part/graphedge.cpp b/src/part/graphedge.cpp
index bfe5665..3492f99 100644
--- a/src/part/graphedge.cpp
+++ b/src/part/graphedge.cpp
@@ -106,37 +106,41 @@ void GraphEdge::updateWithEdge(const GraphEdge& edge)
 void GraphEdge::updateWithEdge(edge_t* edge)
 {
   kDebug();
-  renderOperations().clear();
+  DotRenderOpVec ops;
+  // decrease mem peak
+  setRenderOperations(ops);
+
   if (agget(edge, (char*)"_draw_") != NULL)
   {
-    parse_renderop(agget(edge, (char*)"_draw_"), renderOperations());
-    kDebug() << "element renderOperations size is now " << renderOperations().size();
+    parse_renderop(agget(edge, (char*)"_draw_"), ops);
+    kDebug() << "element renderOperations size is now " << ops.size();
   }
   if (agget(edge, (char*)"_ldraw_") != NULL)
   {
-    parse_renderop(agget(edge, (char*)"_ldraw_"), renderOperations());
-    kDebug() << "element renderOperations size is now " << renderOperations().size();
+    parse_renderop(agget(edge, (char*)"_ldraw_"), ops);
+    kDebug() << "element renderOperations size is now " << ops.size();
   }
   if (agget(edge, (char*)"_hdraw_") != NULL)
   {
-    parse_renderop(agget(edge, (char*)"_hdraw_"), renderOperations());
-    kDebug() << "element renderOperations size is now " << renderOperations().size();
+    parse_renderop(agget(edge, (char*)"_hdraw_"), ops);
+    kDebug() << "element renderOperations size is now " << ops.size();
   }
   if (agget(edge, (char*)"_tdraw_") != NULL)
   {
-    parse_renderop(agget(edge, (char*)"_tdraw_"), renderOperations());
-    kDebug() << "element renderOperations size is now " << renderOperations().size();
+    parse_renderop(agget(edge, (char*)"_tdraw_"), ops);
+    kDebug() << "element renderOperations size is now " << ops.size();
   }
   if (agget(edge, (char*)"_hldraw_") != NULL)
   {
-    parse_renderop(agget(edge, (char*)"_hldraw_"), renderOperations());
-    kDebug() << "element renderOperations size is now " << renderOperations().size();
+    parse_renderop(agget(edge, (char*)"_hldraw_"), ops);
+    kDebug() << "element renderOperations size is now " << ops.size();
   }
   if (agget(edge, (char*)"_tldraw_") != NULL)
   {
-    parse_renderop(agget(edge, (char*)"_tldraw_"), renderOperations());
-    kDebug() << "element renderOperations size is now " << renderOperations().size();
+    parse_renderop(agget(edge, (char*)"_tldraw_"), ops);
+    kDebug() << "element renderOperations size is now " << ops.size();
   }
+  setRenderOperations(ops);
   Agsym_t *attr = agfstattr(edge);
   while(attr)
   {
diff --git a/src/part/graphelement.cpp b/src/part/graphelement.cpp
index 52d9fa8..bc409f8 100644
--- a/src/part/graphelement.cpp
+++ b/src/part/graphelement.cpp
@@ -58,12 +58,19 @@ GraphElement::GraphElement(const GraphElement& element) : QObject(),
   m_ce(element.m_ce),
   m_z(element.m_z),
   m_renderOperations(),
+  m_renderOperationsRevision(0),
   m_selected(element.m_selected)
 {
   kDebug() ;
   updateWithElement(element);
 }
 
+void GraphElement::setRenderOperations(const DotRenderOpVec& drov)
+{
+    m_renderOperations = drov;
+    ++m_renderOperationsRevision;
+}
+
 void GraphElement::updateWithElement(const GraphElement& element)
 {
   kDebug() << element.id();
@@ -91,7 +98,7 @@ void GraphElement::updateWithElement(const GraphElement& element)
   if (modified)
   {
     kDebug() << "modified: update render operations";
-    m_renderOperations = element.m_renderOperations;
+    setRenderOperations(element.m_renderOperations);
 /*    foreach (DotRenderOp op, m_renderOperations)
     {
       QString msg;
diff --git a/src/part/graphelement.h b/src/part/graphelement.h
index 5a65574..a840300 100644
--- a/src/part/graphelement.h
+++ b/src/part/graphelement.h
@@ -68,10 +68,14 @@ public:
   inline QString fontColor() const {return m_attributes["fontcolor"];}
   inline void setFontColor(const QString& fc) {m_attributes["fontcolor"] = fc;}
 
-  inline DotRenderOpVec& renderOperations() {return m_renderOperations;};
   inline const DotRenderOpVec& renderOperations() const {return m_renderOperations;};
-  inline void setRenderOperations(DotRenderOpVec& drov) {m_renderOperations = drov;};
-  
+  void setRenderOperations(const DotRenderOpVec& drov);
+  /**
+   * indicates the version of the render operations, gets increased everytime
+   * @c setRenderOperations gets called.
+   */
+  inline quint32 renderOperationsRevision() const {return m_renderOperationsRevision;};
+
   inline double z() const {return m_z;}
   inline void setZ(double thez) {m_z = thez;}
   
@@ -118,6 +122,7 @@ private:
   bool m_visible;
 
   DotRenderOpVec m_renderOperations;
+  quint32 m_renderOperationsRevision;
 
   bool m_selected;
 };
diff --git a/src/part/graphnode.cpp b/src/part/graphnode.cpp
index 60d7ed1..cc283aa 100644
--- a/src/part/graphnode.cpp
+++ b/src/part/graphnode.cpp
@@ -82,19 +82,23 @@ void GraphNode::updateWithNode(node_t* node)
   m_attributes["id"] = node->name;
   m_attributes["label"] = ND_label(node)->text;
 
+  DotRenderOpVec ops;
+  // decrease mem peak
+  setRenderOperations(ops);
 
-  renderOperations().clear();
   if (agget(node, (char*)"_draw_") != NULL)
   {
-    parse_renderop(agget(node, (char*)"_draw_"), renderOperations());
-    kDebug() << "_draw_: element renderOperations size is now " << renderOperations().size();
+    parse_renderop(agget(node, (char*)"_draw_"), ops);
+    kDebug() << "_draw_: element renderOperations size is now " << ops.size();
   }
   if (agget(node, (char*)"_ldraw_") != NULL)
   {
-    parse_renderop(agget(node, (char*)"_ldraw_"), renderOperations());
-    kDebug() << "_ldraw_: element renderOperations size is now " << renderOperations().size();
+    parse_renderop(agget(node, (char*)"_ldraw_"), ops);
+    kDebug() << "_ldraw_: element renderOperations size is now " << ops.size();
   }
 
+  setRenderOperations(ops);
+
   Agsym_t *attr = agfstattr(node);
   while(attr)
   {
diff --git a/src/part/graphsubgraph.cpp b/src/part/graphsubgraph.cpp
index d664dec..07bc8db 100644
--- a/src/part/graphsubgraph.cpp
+++ b/src/part/graphsubgraph.cpp
@@ -107,19 +107,23 @@ void GraphSubgraph::updateWithSubgraph(graph_t* subgraph)
   if (GD_label(subgraph))
     m_attributes["label"] = GD_label(subgraph)->text;
   
-  
-  renderOperations().clear();
+  DotRenderOpVec ops;
+  // decrease mem peak
+  setRenderOperations(ops);
+
   if (agget(subgraph, (char*)"_draw_") != NULL)
   {
-    parse_renderop(agget(subgraph, (char*)"_draw_"), renderOperations());
-    kDebug() << "_draw_: element renderOperations size is now " << renderOperations().size();
+    parse_renderop(agget(subgraph, (char*)"_draw_"), ops);
+    kDebug() << "_draw_: element renderOperations size is now " << ops.size();
   }
   if (agget(subgraph, (char*)"_ldraw_") != NULL)
   {
-    parse_renderop(agget(subgraph, (char*)"_ldraw_"), renderOperations());
-    kDebug() << "_ldraw_: element renderOperations size is now " << renderOperations().size();
+    parse_renderop(agget(subgraph, (char*)"_ldraw_"), ops);
+    kDebug() << "_ldraw_: element renderOperations size is now " << ops.size();
   }
-  
+
+  setRenderOperations(ops);
+
   Agsym_t *attr = agfstattr(subgraph);
   while(attr)
   {



More information about the kgraphviewer-devel mailing list