sw/source/core/text/itrform2.cxx   |   11 ++++++++---
 sw/source/core/text/portxt.cxx     |    6 ++++++
 vcl/source/gdi/CommonSalLayout.cxx |    9 ++++++---
 vcl/source/gdi/impglyphitem.cxx    |    9 ++++++++-
 4 files changed, 28 insertions(+), 7 deletions(-)

New commits:
commit b0908a76d02e7babf23c4287f57f3d6e368e26e8
Author:     Jonathan Clark <jonat...@libreoffice.org>
AuthorDate: Tue Sep 3 05:49:28 2024 -0600
Commit:     Adolfo Jayme Barrientos <fit...@ubuntu.com>
CommitDate: Wed Sep 4 18:16:49 2024 +0200

    tdf#92064 sw: Improve large paragraph layout performance
    
    This change includes the following scalability improvements for
    documents containing extremely large paragraphs:
    
    - Reduces the size of layout contexts to account for LF control chars.
    
    - Due to typical access patterns while laying out paragraphs, VCL was
      making O(n^2) calls to vcl::ScriptRun::next(). VCL now uses an
      existing global LRU cache for script runs, avoiding much of this
      overhead.
    
    Change-Id: Iee03938683c95776a817d4819fe9a43c65a7c3fc
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172801
    Tested-by: Jenkins
    Reviewed-by: Jonathan Clark <jonat...@libreoffice.org>
    Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172815
    Reviewed-by: Ilmari Lauhakangas <ilmari.lauhakan...@libreoffice.org>
    Reviewed-by: Adolfo Jayme Barrientos <fit...@ubuntu.com>

diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx
index 3673b46ba247..788d64fc538f 100644
--- a/sw/source/core/text/itrform2.cxx
+++ b/sw/source/core/text/itrform2.cxx
@@ -1460,6 +1460,7 @@ SwTextPortion *SwTextFormatter::NewTextPortion( 
SwTextFormatInfo &rInf )
                 case CH_TXTATR_BREAKWORD:
                 case CH_TXTATR_INWORD:
                 case CH_TXTATR_TAB:
+                case CH_TXTATR_NEWLINE:
                 case CH_TXT_ATR_INPUTFIELDSTART:
                 case CH_TXT_ATR_INPUTFIELDEND:
                 case CH_TXT_ATR_FORMELEMENT:
diff --git a/vcl/source/gdi/CommonSalLayout.cxx 
b/vcl/source/gdi/CommonSalLayout.cxx
index 0e4e3fe9dc48..115870dc9cf6 100644
--- a/vcl/source/gdi/CommonSalLayout.cxx
+++ b/vcl/source/gdi/CommonSalLayout.cxx
@@ -384,7 +384,7 @@ bool 
GenericSalLayout::LayoutText(vcl::text::ImplLayoutArgs& rArgs, const SalLay
     const int nLength = rArgs.mrStr.getLength();
     const sal_Unicode *pStr = rArgs.mrStr.getStr();
 
-    std::optional<vcl::text::TextLayoutCache> oNewScriptRun;
+    std::shared_ptr<const vcl::text::TextLayoutCache> pNewScriptRun;
     vcl::text::TextLayoutCache const* pTextLayout;
     if (rArgs.m_pTextLayoutCache)
     {
@@ -392,8 +392,11 @@ bool 
GenericSalLayout::LayoutText(vcl::text::ImplLayoutArgs& rArgs, const SalLay
     }
     else
     {
-        oNewScriptRun.emplace(pStr, rArgs.mnEndCharPos);
-        pTextLayout = &*oNewScriptRun;
+        // tdf#92064, tdf#162663:
+        // Also use the global LRU cache for full string script runs.
+        // This obviates O(n^2) calls to vcl::ScriptRun::next() when laying 
out large paragraphs.
+        pNewScriptRun = vcl::text::TextLayoutCache::Create(rArgs.mrStr);
+        pTextLayout = pNewScriptRun.get();
     }
 
     // nBaseOffset is used to align vertical text to the center of rotated
commit ef40759390de4eba93d0a1e9369fc8ba5c1ea534
Author:     Jonathan Clark <jonat...@libreoffice.org>
AuthorDate: Fri Jul 12 13:40:34 2024 -0600
Commit:     Adolfo Jayme Barrientos <fit...@ubuntu.com>
CommitDate: Wed Sep 4 18:16:38 2024 +0200

    tdf#92064 sw: Improve Tibetan layout performance
    
    This change includes the following scalability improvements for
    documents containing extremely long paragraphs:
    
    - Reduces the size of layout contexts to account for line breaks.
    
    - Disables a misbehaving glyph cache performance optimization for long
      strings.
    
    Change-Id: Ie9a3365076c0d112a7a655988d672a9f4609b42b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170420
    Tested-by: Jenkins
    Reviewed-by: Jonathan Clark <jonat...@libreoffice.org>
    Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172858
    Reviewed-by: Adolfo Jayme Barrientos <fit...@ubuntu.com>
    Reviewed-by: Ilmari Lauhakangas <ilmari.lauhakan...@libreoffice.org>

diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx
index 71b78bc0914d..3673b46ba247 100644
--- a/sw/source/core/text/itrform2.cxx
+++ b/sw/source/core/text/itrform2.cxx
@@ -1418,8 +1418,11 @@ SwTextPortion *SwTextFormatter::NewTextPortion( 
SwTextFormatInfo &rInf )
     if (!nCharWidthGuess)
         nCharWidthGuess = 1;
     auto nExpect = rInf.GetIdx() + TextFrameIndex(rInf.GetLineWidth() / 
nCharWidthGuess);
-    if (nExpect > rInf.GetIdx() && nNextChg > nExpect)
-        nNextChg = nExpect;
+    if (nExpect > rInf.GetIdx())
+    {
+        nNextChg = std::min(nNextChg, nExpect);
+        nNextContext = std::min(nNextContext, nExpect);
+    }
 
     // we keep an invariant during method calls:
     // there are no portion ending characters like hard spaces
@@ -1444,7 +1447,8 @@ SwTextPortion *SwTextFormatter::NewTextPortion( 
SwTextFormatInfo &rInf )
     // for the first text portion in a paragraph, or for any successive
     // portions that are outside of the bounds of the previous context.
     if (!rInf.GetLayoutContext().has_value()
-        || rInf.GetLayoutContext()->m_nEnd <= rInf.GetIdx().get())
+        || rInf.GetLayoutContext()->m_nBegin < rInf.GetLineStart().get()
+        || rInf.GetLayoutContext()->m_nEnd < nNextChg.get())
     {
         // The layout context must terminate at special characters
         sal_Int32 nEnd = rInf.GetIdx().get();
diff --git a/sw/source/core/text/portxt.cxx b/sw/source/core/text/portxt.cxx
index ee39f156c24a..ec0303c0665c 100644
--- a/sw/source/core/text/portxt.cxx
+++ b/sw/source/core/text/portxt.cxx
@@ -445,6 +445,12 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
 
             SetLen( pGuess->BreakPos() - rInf.GetIdx() );
 
+            // Clamp layout context to the end of the line
+            if(auto stClampedContext = GetLayoutContext(); 
stClampedContext.has_value()) {
+                stClampedContext->m_nEnd = pGuess->BreakPos().get();
+                SetLayoutContext(stClampedContext);
+            }
+
             OSL_ENSURE( pGuess->BreakStart() >= pGuess->FieldDiff(),
                     "Trouble with expanded field portions during line break" );
             TextFrameIndex const nRealStart = pGuess->BreakStart() - 
pGuess->FieldDiff();
diff --git a/vcl/source/gdi/impglyphitem.cxx b/vcl/source/gdi/impglyphitem.cxx
index 1b40291736b7..946230a51762 100644
--- a/vcl/source/gdi/impglyphitem.cxx
+++ b/vcl/source/gdi/impglyphitem.cxx
@@ -364,6 +364,13 @@ const SalLayoutGlyphs* 
SalLayoutGlyphsCache::GetLayoutGlyphs(
             // part being underlined. Doing this for any two segments allows 
this optimization
             // even when the prefix of the string would use a different font.
             // TODO: Can those font differences be ignored?
+
+            // Shaping performance seems to scale poorly with respect to 
string length. Certain
+            // writing systems involve extremely long strings (for example, 
Tibetan: tdf#92064).
+            // In such cases, this optimization would be a net loss, and must 
be disabled.
+            constexpr sal_Int32 nOptLengthThreshold = 20000;
+            bool bEnableOptimization = (text.getLength() < 
nOptLengthThreshold);
+
             // Writer layouts tests enable SAL_NON_APPLICATION_FONT_USE=abort 
in order
             // to make PrintFontManager::Substitute() abort if font fallback 
happens. When
             // laying out the entire string the chance this happens increases 
(e.g. testAbi11870
@@ -374,7 +381,7 @@ const SalLayoutGlyphs* 
SalLayoutGlyphsCache::GetLayoutGlyphs(
                 const char* pEnv = getenv("SAL_NON_APPLICATION_FONT_USE");
                 return pEnv && strcmp(pEnv, "abort") == 0;
             }();
-            if (mLastSubstringKey.has_value() && !bAbortOnFontSubstitute)
+            if (bEnableOptimization && mLastSubstringKey.has_value() && 
!bAbortOnFontSubstitute)
             {
                 sal_Int32 pos = nIndex;
                 if (mLastSubstringKey->len < pos && text[pos - 1] == nbSpace)

Reply via email to