Le 07/07/2016 19:06, Pavel Sanda a écrit :
Can you try if you can see difference between 2.1 and master for this
scenario:
1. set high speed rate for your keyboard: xset r rate 300 180
2. load user guide, fullscreen
3. Hold right arrow until you reach the end of the first page and count
the time
(even approximately, the difference is quite noticeable here).
I remember trying that some time ago and being very frustrated with having
a keyboard that I could not use anymore :) I'll try again. For now I am
Use xset r rate 300 20 to get back.
OK, I did some testing with the down arrow from the top to the bottom of
the User Guide, because the right arrow really requires too much time
for some reason. [Rereading your message I notice only now that you
meant first page only. Oh well.]
Test run on non-debug builds:
Qt4.8.7 builds:
2.1.5: 3:57min
2.2.1dev: 5:14min
2.3.0dev (with my new secret patch): 1:16min
Qt5.5.1 builds (with build-type=dev, I'm lazy)
2.3.0dev (with only the breakAt patch): 1:32min
2.3.0dev (with the new secret patch): 1:25min
Two conclusions:
* the secret patch is fast
* Qt5.5 is fast even without it (better tests have to be done with
Qt5), and not really faster with it. For memory sake, the caching
patch should maybe by Qt4 only.
So, what is the secret patch, I hear you ask? Here it is for your
enjoyment. The idea is to keep around the glyph representation of our
row elements and to use them even for drawing. It is not quite ready,
but as you see, it works. In a first test I saw some display glitches
that I have not been able to reproduce, but they are probably lurking
somewhere.
It is simple enough that I might propose its grandson (or something) to
2.2.x.
JMarc
>From 8af238ca4f75100ba919182eef504fa90b0eab98 Mon Sep 17 00:00:00 2001
From: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date: Tue, 5 Jul 2016 14:06:22 +0200
Subject: [PATCH] Add caching for the QTextLayout objects we use
The QTextLayout handling is terribly slow on Qt 4.8.7, but some
caching has been added in Qt5 that makes it much faster. For some
reason, it is not that slow with Qt 4.8.1.
This commit introduces some caching, which should probably only be
active for Qt 4.
Improve the caching of QTextLayout objects used for pos2x and x2pos
and use them for drawing too. We originally used a trivial caching
that the last QTextLayout, but now they are kept in a QHash.
Movoerover, caching is enabled for these QTextLayout object (not sure
what this does).
This patch also adds some caching in the breakAt method, the only user of
QTextLayout which did not have some kind of caching already.
---
src/frontends/qt4/GuiFontMetrics.cpp | 93 ++++++++++++++++++++++--------------
src/frontends/qt4/GuiFontMetrics.h | 24 ++++++----
src/frontends/qt4/GuiPainter.cpp | 13 ++++-
src/support/convert.cpp | 7 +++
4 files changed, 89 insertions(+), 48 deletions(-)
diff --git a/src/frontends/qt4/GuiFontMetrics.cpp b/src/frontends/qt4/GuiFontMetrics.cpp
index eade8cc..816fdac 100644
--- a/src/frontends/qt4/GuiFontMetrics.cpp
+++ b/src/frontends/qt4/GuiFontMetrics.cpp
@@ -21,6 +21,7 @@
#include "insets/Inset.h"
+#include "support/convert.h"
#include "support/lassert.h"
using namespace std;
@@ -51,11 +52,10 @@ inline QChar const ucs4_to_qchar(char_type const ucs4)
} // anon namespace
-// Limit strwidth_cache_ size to 512kB of string data
+// Limit (strwidth|breakat)_cache_ size to 512kB of string data
GuiFontMetrics::GuiFontMetrics(QFont const & font)
: font_(font), metrics_(font, 0),
- strwidth_cache_(1 << 19),
- tl_cache_rtl_(false), tl_cache_wordspacing_(-1.0)
+ strwidth_cache_(1 << 19), breakat_cache_(1 << 19), qtextlayout_cache_(500)
{
}
@@ -170,54 +170,56 @@ int GuiFontMetrics::signedWidth(docstring const & s) const
}
-QTextLayout const &
-GuiFontMetrics::getTextLayout(docstring const & s, QFont font,
- bool const rtl, double const wordspacing) const
+QTextLayout const *
+GuiFontMetrics::getTextLayout(docstring const & s, bool const rtl,
+ double const wordspacing) const
{
- if (s != tl_cache_s_ || font != tl_cache_font_ || rtl != tl_cache_rtl_
- || wordspacing != tl_cache_wordspacing_) {
- tl_cache_.setText(toqstr(s));
- font.setWordSpacing(wordspacing);
- tl_cache_.setFont(font);
+ docstring s_cache = s + (rtl ? "r" : "l") + convert<docstring>(wordspacing);
+ QByteArray qba =
+ QByteArray(reinterpret_cast<char const *>(s_cache.data()),
+ s.size() * sizeof(docstring::value_type));
+ QTextLayout * ptl = qtextlayout_cache_[qba];
+ if (!ptl) {
+ ptl = new QTextLayout();
+ ptl->setCacheEnabled(true);
+ ptl->setText(toqstr(s));
+ QFont copy = font_;
+ copy.setWordSpacing(wordspacing);
+ ptl->setFont(copy);
// Note that both setFlags and the enums are undocumented
- tl_cache_.setFlags(rtl ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight);
- tl_cache_.beginLayout();
- tl_cache_.createLine();
- tl_cache_.endLayout();
- tl_cache_s_ = s;
- tl_cache_font_ = font;
- tl_cache_rtl_ = rtl;
- tl_cache_wordspacing_ = wordspacing;
+ ptl->setFlags(rtl ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight);
+ ptl->beginLayout();
+ ptl->createLine();
+ ptl->endLayout();
+ qtextlayout_cache_.insert(qba, ptl);
}
- return tl_cache_;
+ return ptl;
}
int GuiFontMetrics::pos2x(docstring const & s, int const pos, bool const rtl,
double const wordspacing) const
{
- QFont copy = font_;
- copy.setWordSpacing(wordspacing);
- QTextLayout const & tl = getTextLayout(s, font_, rtl, wordspacing);
- return static_cast<int>(tl.lineForTextPosition(pos).cursorToX(pos));
+ QTextLayout const * tl = getTextLayout(s, rtl, wordspacing);
+ return static_cast<int>(tl->lineForTextPosition(pos).cursorToX(pos));
}
int GuiFontMetrics::x2pos(docstring const & s, int & x, bool const rtl,
double const wordspacing) const
{
- QTextLayout const & tl = getTextLayout(s, font_, rtl, wordspacing);
- int pos = tl.lineForTextPosition(0).xToCursor(x);
+ QTextLayout const * tl = getTextLayout(s, rtl, wordspacing);
+ int pos = tl->lineForTextPosition(0).xToCursor(x);
// correct x value to the actual cursor position.
- x = static_cast<int>(tl.lineForTextPosition(0).cursorToX(pos));
+ x = static_cast<int>(tl->lineForTextPosition(0).cursorToX(pos));
return pos;
}
-bool GuiFontMetrics::breakAt(docstring & s, int & x, bool const rtl, bool const force) const
+pair<int, int> *
+GuiFontMetrics::breakAt_helper(docstring const & s, int const x,
+ bool const rtl, bool const force) const
{
- if (s.empty())
- return false;
QTextLayout tl;
/* Qt will not break at a leading or trailing space, and we need
* that sometimes, see http://www.lyx.org/trac/ticket/9921.
@@ -225,7 +227,7 @@ bool GuiFontMetrics::breakAt(docstring & s, int & x, bool const rtl, bool const
* To work around the problem, we enclose the string between
* zero-width characters so that the QTextLayout algorithm will
* agree to break the text at these extremal spaces.
- */
+ */
// Unicode character ZERO WIDTH NO-BREAK SPACE
QChar const zerow_nbsp(0xfeff);
tl.setText(zerow_nbsp + toqstr(s) + zerow_nbsp);
@@ -240,11 +242,31 @@ bool GuiFontMetrics::breakAt(docstring & s, int & x, bool const rtl, bool const
line.setLineWidth(x);
tl.createLine();
tl.endLayout();
- if ((force && line.textLength() == 1) || int(line.naturalTextWidth()) > x)
- return false;
- x = int(line.naturalTextWidth());
// The -1 is here to account for the leading zerow_nbsp.
- s = s.substr(0, line.textLength() - 1);
+ return new pair<int, int>(line.textLength() - 1,
+ int(line.naturalTextWidth()));
+}
+
+
+bool GuiFontMetrics::breakAt(docstring & s, int & x, bool const rtl, bool const force) const
+{
+ if (s.empty())
+ return false;
+ docstring s_cache = s + (rtl ? "r" : "l") + (force ? "f" : "w");
+
+ QByteArray qba =
+ QByteArray(reinterpret_cast<char const *>(s_cache.data()),
+ s.size() * sizeof(docstring::value_type));
+ pair<int, int> * pp = breakat_cache_[qba];
+ if (!pp) {
+ pp = breakAt_helper(s, x, rtl, force);
+ breakat_cache_.insert(qba, pp, qba.size());
+ }
+
+ if ((force && pp->first == 0) || pp->second > x)
+ return false;
+ s = s.substr(0, pp->first);
+ x = pp->second;
return true;
}
@@ -260,7 +282,6 @@ void GuiFontMetrics::rectText(docstring const & str,
}
-
void GuiFontMetrics::buttonText(docstring const & str,
int & w, int & ascent, int & descent) const
{
diff --git a/src/frontends/qt4/GuiFontMetrics.h b/src/frontends/qt4/GuiFontMetrics.h
index 8c8daab..112612b 100644
--- a/src/frontends/qt4/GuiFontMetrics.h
+++ b/src/frontends/qt4/GuiFontMetrics.h
@@ -63,11 +63,16 @@ public:
///
int width(QString const & str) const;
+ /// Return a pointer to a cached QTextLayout object
+ QTextLayout const *
+ getTextLayout(docstring const & s, bool const rtl,
+ double const wordspacing) const;
+
private:
- QTextLayout const &
- getTextLayout(docstring const & s, QFont font,
- bool const rtl, double const wordspacing) const;
+ std::pair<int, int> *
+ breakAt_helper(docstring const & s, int const x,
+ bool const rtl, bool const force) const;
/// The font
QFont font_;
@@ -81,6 +86,12 @@ private:
/// Cache of string widths
mutable QCache<QByteArray, int> strwidth_cache_;
+ /// Cache for breakAt
+ mutable QCache<QByteArray, std::pair<int, int>> breakat_cache_;
+
+ /// Cache for QTextLayout:s
+ mutable QCache<QByteArray, QTextLayout> qtextlayout_cache_;
+
struct AscendDescend {
int ascent;
int descent;
@@ -93,13 +104,6 @@ private:
/// Cache of char right bearings
mutable QHash<char_type, int> rbearing_cache_;
- // A trivial QTextLayout cache
- mutable QTextLayout tl_cache_;
- mutable docstring tl_cache_s_;
- mutable QFont tl_cache_font_;
- mutable bool tl_cache_rtl_;
- mutable double tl_cache_wordspacing_;
-
};
} // namespace frontend
diff --git a/src/frontends/qt4/GuiPainter.cpp b/src/frontends/qt4/GuiPainter.cpp
index 5762c9b..ce421e0 100644
--- a/src/frontends/qt4/GuiPainter.cpp
+++ b/src/frontends/qt4/GuiPainter.cpp
@@ -474,8 +474,17 @@ void GuiPainter::text(int x, int y, docstring const & s,
return;
}
- // don't use the pixmap cache,
- do_drawText(x, y, str, dir, f, ff);
+ // don't use the pixmap cache
+ setQPainterPen(computeColor(f.realColor()));
+ if (dir != Auto) {
+ QTextLayout const * ptl = fm.getTextLayout(s, dir == RtL, wordspacing);
+ ptl->draw(this, QPointF(x, y - fm.maxAscent()));
+ }
+ else {
+ if (font() != ff)
+ setFont(ff);
+ drawText(x, y, str);
+ }
//LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8())
// << " at " << x << "," << y);
}
diff --git a/src/support/convert.cpp b/src/support/convert.cpp
index 58a5647..076c9d7 100644
--- a/src/support/convert.cpp
+++ b/src/support/convert.cpp
@@ -155,6 +155,13 @@ string convert<string>(double d)
template<>
+docstring convert<docstring>(double d)
+{
+ return from_ascii(convert<string>(d));
+}
+
+
+template<>
int convert<int>(string const s)
{
return strtol(s.c_str(), 0, 10);
--
2.7.4