drawinglayer/Library_drawinglayer.mk                                       |   
 2 
 drawinglayer/Library_drawinglayercore.mk                                   |   
 3 
 drawinglayer/source/primitive2d/BufferedDecompositionFlusher.cxx           |  
119 +++++++++
 drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx  |  
132 ++--------
 drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx       |  
131 ++-------
 drawinglayer/source/primitive2d/glowprimitive2d.cxx                        |   
 2 
 drawinglayer/source/primitive2d/graphicprimitive2d.cxx                     |   
 4 
 drawinglayer/source/primitive2d/metafileprimitive2d.cxx                    |   
 2 
 drawinglayer/source/primitive2d/sceneprimitive2d.cxx                       |   
 2 
 drawinglayer/source/primitive2d/shadowprimitive2d.cxx                      |   
 2 
 drawinglayer/source/primitive2d/softedgeprimitive2d.cxx                    |   
 2 
 include/drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx          |   
49 +++
 include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx |   
16 -
 include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx      |   
14 -
 include/drawinglayer/primitive2d/groupprimitive2d.hxx                      |   
 2 
 svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx                          |   
 2 
 16 files changed, 270 insertions(+), 214 deletions(-)

New commits:
commit 8a17b7f0a679ebf21bcfb425186b205d996d129b
Author:     Noel Grandin <noelgran...@gmail.com>
AuthorDate: Thu Mar 13 18:37:54 2025 +0200
Commit:     Noel Grandin <noel.gran...@collabora.co.uk>
CommitDate: Mon Mar 17 07:15:15 2025 +0100

    tdf#131595 Improve drawinglayer flushing mechanism.
    
    Which dramatically speeds up switching between sheets in
    the referenced document.
    
    The problem here is that we have a timer for each buffered primitive.
    And we have a lot of primitives here.
    And the TimerManager does not scale well to lots and lots of timer,
    because it uses a linked list.
    
    So this change modifies the flushing mechanism, trading off some
    precision for some speed.
    
    Change-Id: I66a744f06af9b08d4e9b797d11db8d22f4060789
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182876
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk>

diff --git a/drawinglayer/Library_drawinglayer.mk 
b/drawinglayer/Library_drawinglayer.mk
index 14e40870d7f1..6997bd22457e 100644
--- a/drawinglayer/Library_drawinglayer.mk
+++ b/drawinglayer/Library_drawinglayer.mk
@@ -109,7 +109,6 @@ $(eval $(call 
gb_Library_add_exception_objects,drawinglayer,\
     drawinglayer/source/primitive2d/bitmapprimitive2d \
     drawinglayer/source/primitive2d/BitmapAlphaPrimitive2D \
     drawinglayer/source/primitive2d/borderlineprimitive2d \
-    drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D \
     drawinglayer/source/primitive2d/controlprimitive2d \
     drawinglayer/source/primitive2d/cropprimitive2d \
     drawinglayer/source/primitive2d/discretebitmapprimitive2d \
@@ -125,7 +124,6 @@ $(eval $(call 
gb_Library_add_exception_objects,drawinglayer,\
     drawinglayer/source/primitive2d/graphicprimitivehelper2d \
     drawinglayer/source/primitive2d/graphicprimitive2d \
     drawinglayer/source/primitive2d/gridprimitive2d \
-    drawinglayer/source/primitive2d/groupprimitive2d \
     drawinglayer/source/primitive2d/helplineprimitive2d \
     drawinglayer/source/primitive2d/hiddengeometryprimitive2d \
     drawinglayer/source/primitive2d/invertprimitive2d \
diff --git a/drawinglayer/Library_drawinglayercore.mk 
b/drawinglayer/Library_drawinglayercore.mk
index 7cff1a4b70bd..83d2c0faab6d 100644
--- a/drawinglayer/Library_drawinglayercore.mk
+++ b/drawinglayer/Library_drawinglayercore.mk
@@ -46,7 +46,10 @@ $(eval $(call 
gb_Library_use_custom_headers,drawinglayercore,\
 $(eval $(call gb_Library_add_exception_objects,drawinglayercore,\
     drawinglayer/source/primitive2d/baseprimitive2d \
     drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D \
+    drawinglayer/source/primitive2d/BufferedDecompositionFlusher \
+    drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D \
     drawinglayer/source/primitive2d/Primitive2DContainer \
+    drawinglayer/source/primitive2d/groupprimitive2d \
     drawinglayer/source/primitive2d/Tools \
     drawinglayer/source/geometry/viewinformation2d \
 ))
diff --git a/drawinglayer/source/primitive2d/BufferedDecompositionFlusher.cxx 
b/drawinglayer/source/primitive2d/BufferedDecompositionFlusher.cxx
new file mode 100644
index 000000000000..6bc9312a6f7f
--- /dev/null
+++ b/drawinglayer/source/primitive2d/BufferedDecompositionFlusher.cxx
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx>
+#include <tools/lazydelete.hxx>
+
+namespace drawinglayer::primitive2d
+{
+/**
+    This is a "garbage collection" approach to flushing.
+
+    We store entries in a set. Every 2 seconds, we scan the set for entries 
that have not
+    been used for 10 seconds or more, and if so, we flush the buffer 
primitives in those entries.
+
+    This mechanism is __deliberately__ not perfect.
+    Sometimes things will be flushed a little too soon, sometimes things will 
wait a little too long,
+    since we only have a granularity of 2 seconds.
+    But what is gains from not being perfect, is scalability.
+
+    It is very simple, scales to lots and lots of primitives without needing 
lots of timers, and performs
+    very little work in the common case.
+*/
+
+static BufferedDecompositionFlusher* getInstance()
+{
+    static tools::DeleteRtlReferenceOnDeinit<BufferedDecompositionFlusher> 
gaTimer(
+        new BufferedDecompositionFlusher);
+    return gaTimer.get().get();
+}
+
+// static
+void BufferedDecompositionFlusher::update(const 
BufferedDecompositionPrimitive2D* p)
+{
+    if (auto flusher = getInstance())
+        flusher->updateImpl(p);
+}
+
+// static
+void BufferedDecompositionFlusher::update(const 
BufferedDecompositionGroupPrimitive2D* p)
+{
+    if (auto flusher = getInstance())
+        flusher->updateImpl(p);
+}
+
+BufferedDecompositionFlusher::BufferedDecompositionFlusher()
+{
+    setRemainingTime(salhelper::TTimeValue(2, 0));
+    start();
+}
+
+void BufferedDecompositionFlusher::updateImpl(const 
BufferedDecompositionPrimitive2D* p)
+{
+    std::unique_lock l(maMutex);
+    maRegistered1.insert(const_cast<BufferedDecompositionPrimitive2D*>(p));
+}
+
+void BufferedDecompositionFlusher::updateImpl(const 
BufferedDecompositionGroupPrimitive2D* p)
+{
+    std::unique_lock l(maMutex);
+    
maRegistered2.insert(const_cast<BufferedDecompositionGroupPrimitive2D*>(p));
+}
+
+void SAL_CALL BufferedDecompositionFlusher::onShot()
+{
+    auto aNow = std::chrono::steady_clock::now();
+    std::vector<rtl::Reference<BufferedDecompositionPrimitive2D>> aRemoved1;
+    std::vector<rtl::Reference<BufferedDecompositionGroupPrimitive2D>> 
aRemoved2;
+    {
+        std::unique_lock l(maMutex);
+        for (auto it = maRegistered1.begin(); it != maRegistered1.end();)
+        {
+            if (aNow - (*it)->maLastAccess > std::chrono::seconds(10))
+            {
+                aRemoved1.push_back(*it);
+                it = maRegistered1.erase(it);
+            }
+            else
+                ++it;
+        }
+        for (auto it = maRegistered2.begin(); it != maRegistered2.end();)
+        {
+            if (aNow - (*it)->maLastAccess > std::chrono::seconds(10))
+            {
+                aRemoved2.push_back(*it);
+                it = maRegistered2.erase(it);
+            }
+            else
+                ++it;
+        }
+    }
+    for (const auto& r : aRemoved1)
+        r->setBuffered2DDecomposition(nullptr);
+    for (const auto& r : aRemoved2)
+        r->setBuffered2DDecomposition(Primitive2DContainer{});
+    setRemainingTime(salhelper::TTimeValue(2, 0));
+    start();
+}
+
+} // end of namespace drawinglayer::primitive2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git 
a/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx 
b/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx
index fcdbe8d02d78..304228c1ca81 100644
--- a/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx
+++ b/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx
@@ -21,140 +21,80 @@
 
 #include <drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx>
 #include <drawinglayer/geometry/viewinformation2d.hxx>
-
-namespace
-{
-class LocalCallbackTimer : public salhelper::Timer
-{
-protected:
-    drawinglayer::primitive2d::BufferedDecompositionGroupPrimitive2D* 
pCustomer;
-
-public:
-    explicit LocalCallbackTimer(
-        drawinglayer::primitive2d::BufferedDecompositionGroupPrimitive2D& 
rCustomer)
-        : pCustomer(&rCustomer)
-    {
-    }
-
-    void clearCallback() { pCustomer = nullptr; }
-
-protected:
-    virtual void SAL_CALL onShot() override;
-};
-
-void SAL_CALL LocalCallbackTimer::onShot()
-{
-    if (nullptr != pCustomer)
-        flushBufferedDecomposition(*pCustomer);
-}
-}
+#include <drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx>
 
 namespace drawinglayer::primitive2d
 {
-void flushBufferedDecomposition(BufferedDecompositionGroupPrimitive2D& rTarget)
-{
-    rTarget.acquire();
-    rTarget.setBuffered2DDecomposition(Primitive2DContainer());
-    rTarget.release();
-}
-
 bool BufferedDecompositionGroupPrimitive2D::hasBuffered2DDecomposition() const
 {
-    if (0 != maCallbackSeconds)
-    {
-        std::lock_guard Guard(maCallbackLock);
+    if (!mbFlushOnTimer)
         return !maBuffered2DDecomposition.empty();
-    }
     else
     {
+        std::lock_guard Guard(maCallbackLock);
         return !maBuffered2DDecomposition.empty();
     }
 }
 
 void 
BufferedDecompositionGroupPrimitive2D::setBuffered2DDecomposition(Primitive2DContainer&&
 rNew)
 {
-    if (0 == maCallbackSeconds)
+    if (!mbFlushOnTimer)
     {
         // no flush used, just set
         maBuffered2DDecomposition = std::move(rNew);
-        return;
     }
-
-    if (maCallbackTimer.is())
-    {
-        if (rNew.empty())
-        {
-            // stop timer
-            maCallbackTimer->stop();
-        }
-        else
-        {
-            // decomposition changed, touch
-            
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
-            if (!maCallbackTimer->isTicking())
-                maCallbackTimer->start();
-        }
-    }
-    else if (!rNew.empty())
+    else
     {
-        // decomposition defined/set/changed, init & start timer
-        maCallbackTimer.set(new LocalCallbackTimer(*this));
-        
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
-        maCallbackTimer->start();
-    }
+        // decomposition changed, touch
+        maLastAccess = std::chrono::steady_clock::now();
+        BufferedDecompositionFlusher::update(this);
 
-    // tdf#158913 need to secure change when flush/multithreading is in use
-    std::lock_guard Guard(maCallbackLock);
-    maBuffered2DDecomposition = std::move(rNew);
+        // tdf#158913 need to secure change when flush/multithreading is in use
+        std::lock_guard Guard(maCallbackLock);
+        maBuffered2DDecomposition = std::move(rNew);
+    }
 }
 
 BufferedDecompositionGroupPrimitive2D::BufferedDecompositionGroupPrimitive2D(
     Primitive2DContainer&& aChildren)
     : GroupPrimitive2D(std::move(aChildren))
-    , maCallbackTimer()
     , maCallbackLock()
-    , maCallbackSeconds(0)
+    , mbFlushOnTimer(false)
 {
 }
 
-BufferedDecompositionGroupPrimitive2D::~BufferedDecompositionGroupPrimitive2D()
-{
-    if (maCallbackTimer.is())
-    {
-        // no more decomposition, end callback
-        
static_cast<LocalCallbackTimer*>(maCallbackTimer.get())->clearCallback();
-        maCallbackTimer->stop();
-    }
-}
+BufferedDecompositionGroupPrimitive2D::~BufferedDecompositionGroupPrimitive2D()
 {}
 
 void BufferedDecompositionGroupPrimitive2D::get2DDecomposition(
     Primitive2DDecompositionVisitor& rVisitor,
     const geometry::ViewInformation2D& rViewInformation) const
 {
-    if (!hasBuffered2DDecomposition())
-    {
-        Primitive2DContainer aNewSequence;
-        create2DDecomposition(aNewSequence, rViewInformation);
-        
const_cast<BufferedDecompositionGroupPrimitive2D*>(this)->setBuffered2DDecomposition(
-            std::move(aNewSequence));
-    }
-
-    if (0 == maCallbackSeconds)
+    if (!mbFlushOnTimer)
     {
         // no flush/multithreading is in use, just call
+        if (maBuffered2DDecomposition.empty())
+            create2DDecomposition(maBuffered2DDecomposition, rViewInformation);
         rVisitor.visit(maBuffered2DDecomposition);
-        return;
     }
-
-    // decomposition was used, touch/restart time
-    if (maCallbackTimer)
-        
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
-
-    // tdf#158913 need to secure 'visit' when flush/multithreading is in use,
-    // so that the local non-ref-Counted instance of the decomposition gets not
-    // manipulated (e.g. deleted)
-    std::lock_guard Guard(maCallbackLock);
-    rVisitor.visit(maBuffered2DDecomposition);
+    else
+    {
+        // tdf#158913 need to secure 'visit' when flush/multithreading is in 
use,
+        // so that the local non-ref-Counted instance of the decomposition 
gets not
+        // manipulated (e.g. deleted)
+        Primitive2DContainer xTmp;
+        {
+            // only hold the lock for long enough to get a valid reference
+            std::lock_guard Guard(maCallbackLock);
+            maLastAccess = std::chrono::steady_clock::now();
+            if (maBuffered2DDecomposition.empty())
+            {
+                create2DDecomposition(maBuffered2DDecomposition, 
rViewInformation);
+                BufferedDecompositionFlusher::update(this);
+            }
+            xTmp = maBuffered2DDecomposition;
+        }
+        rVisitor.visit(xTmp);
+    }
 }
 
 } // end of namespace drawinglayer::primitive2d
diff --git 
a/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx 
b/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx
index b04905744b50..f9e476021e44 100644
--- a/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx
+++ b/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx
@@ -21,136 +21,79 @@
 
 #include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx>
 #include <drawinglayer/geometry/viewinformation2d.hxx>
-
-namespace
-{
-class LocalCallbackTimer : public salhelper::Timer
-{
-protected:
-    drawinglayer::primitive2d::BufferedDecompositionPrimitive2D* pCustomer;
-
-public:
-    explicit LocalCallbackTimer(
-        drawinglayer::primitive2d::BufferedDecompositionPrimitive2D& rCustomer)
-        : pCustomer(&rCustomer)
-    {
-    }
-
-    void clearCallback() { pCustomer = nullptr; }
-
-protected:
-    virtual void SAL_CALL onShot() override;
-};
-
-void SAL_CALL LocalCallbackTimer::onShot()
-{
-    if (nullptr != pCustomer)
-        flushBufferedDecomposition(*pCustomer);
-}
-}
+#include <drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx>
 
 namespace drawinglayer::primitive2d
 {
-void flushBufferedDecomposition(BufferedDecompositionPrimitive2D& rTarget)
-{
-    rTarget.acquire();
-    rTarget.setBuffered2DDecomposition(nullptr);
-    rTarget.release();
-}
-
 bool BufferedDecompositionPrimitive2D::hasBuffered2DDecomposition() const
 {
-    if (0 != maCallbackSeconds)
+    if (!mbFlushOnTimer)
+        return maBuffered2DDecomposition.is();
+    else
     {
         std::lock_guard Guard(maCallbackLock);
         return maBuffered2DDecomposition.is();
     }
-    else
-        return maBuffered2DDecomposition.is();
 }
 
 void 
BufferedDecompositionPrimitive2D::setBuffered2DDecomposition(Primitive2DReference
 rNew)
 {
-    if (0 == maCallbackSeconds)
+    if (!mbFlushOnTimer)
     {
         // no flush used, just set
         maBuffered2DDecomposition = std::move(rNew);
-        return;
     }
-
-    if (maCallbackTimer.is())
-    {
-        if (!rNew)
-        {
-            // stop timer
-            maCallbackTimer->stop();
-        }
-        else
-        {
-            // decomposition changed, touch
-            
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
-            if (!maCallbackTimer->isTicking())
-                maCallbackTimer->start();
-        }
-    }
-    else if (rNew)
+    else
     {
-        // decomposition defined/set/changed, init & start timer
-        maCallbackTimer.set(new LocalCallbackTimer(*this));
-        
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
-        maCallbackTimer->start();
-    }
+        // decomposition changed, touch
+        maLastAccess = std::chrono::steady_clock::now();
+        BufferedDecompositionFlusher::update(this);
 
-    // tdf#158913 need to secure change when flush/multithreading is in use
-    std::lock_guard Guard(maCallbackLock);
-    maBuffered2DDecomposition = std::move(rNew);
+        // tdf#158913 need to secure change when flush/multithreading is in use
+        std::lock_guard Guard(maCallbackLock);
+        maBuffered2DDecomposition = std::move(rNew);
+    }
 }
 
 BufferedDecompositionPrimitive2D::BufferedDecompositionPrimitive2D()
     : maBuffered2DDecomposition()
-    , maCallbackTimer()
     , maCallbackLock()
-    , maCallbackSeconds(0)
+    , mbFlushOnTimer(false)
 {
 }
 
-BufferedDecompositionPrimitive2D::~BufferedDecompositionPrimitive2D()
-{
-    if (maCallbackTimer.is())
-    {
-        // no more decomposition, end callback
-        
static_cast<LocalCallbackTimer*>(maCallbackTimer.get())->clearCallback();
-        maCallbackTimer->stop();
-    }
-}
+BufferedDecompositionPrimitive2D::~BufferedDecompositionPrimitive2D() {}
 
 void BufferedDecompositionPrimitive2D::get2DDecomposition(
     Primitive2DDecompositionVisitor& rVisitor,
     const geometry::ViewInformation2D& rViewInformation) const
 {
-    if (!hasBuffered2DDecomposition())
-    {
-        Primitive2DReference aNew = create2DDecomposition(rViewInformation);
-        
const_cast<BufferedDecompositionPrimitive2D*>(this)->setBuffered2DDecomposition(
-            std::move(aNew));
-    }
-
-    if (0 == maCallbackSeconds)
+    if (!mbFlushOnTimer)
     {
         // no flush/multithreading is in use, just call
+        if (!maBuffered2DDecomposition)
+            maBuffered2DDecomposition = 
create2DDecomposition(rViewInformation);
         rVisitor.visit(maBuffered2DDecomposition);
-        return;
     }
-
-    // decomposition was used, touch/restart time
-    if (maCallbackTimer)
-        
maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
-
-    // tdf#158913 need to secure 'visit' when flush/multithreading is in use,
-    // so that the local non-ref-Counted instance of the decomposition gets not
-    // manipulated (e.g. deleted)
-    std::lock_guard Guard(maCallbackLock);
-    rVisitor.visit(maBuffered2DDecomposition);
+    else
+    {
+        // tdf#158913 need to secure 'visit' when flush/multithreading is in 
use,
+        // so that the local non-ref-Counted instance of the decomposition 
gets not
+        // manipulated (e.g. deleted)
+        Primitive2DReference xTmp;
+        {
+            // only hold the lock for long enough to get a valid reference
+            std::lock_guard Guard(maCallbackLock);
+            maLastAccess = std::chrono::steady_clock::now();
+            if (!maBuffered2DDecomposition)
+            {
+                maBuffered2DDecomposition = 
create2DDecomposition(rViewInformation);
+                BufferedDecompositionFlusher::update(this);
+            }
+            xTmp = maBuffered2DDecomposition;
+        }
+        rVisitor.visit(xTmp);
+    }
 }
 
 } // end of namespace drawinglayer::primitive2d
diff --git a/drawinglayer/source/primitive2d/glowprimitive2d.cxx 
b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
index ed212ae4da6d..33a3d59fcc6a 100644
--- a/drawinglayer/source/primitive2d/glowprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
@@ -44,7 +44,7 @@ GlowPrimitive2D::GlowPrimitive2D(const Color& rGlowColor, 
double fRadius,
     , maLastClippedRange()
 {
     // activate callback to flush buffered decomposition content
-    setCallbackSeconds(15);
+    activateFlushOnTimer();
 }
 
 bool GlowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
diff --git a/drawinglayer/source/primitive2d/graphicprimitive2d.cxx 
b/drawinglayer/source/primitive2d/graphicprimitive2d.cxx
index 457bd64c95c6..0a5a5060b0cb 100644
--- a/drawinglayer/source/primitive2d/graphicprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/graphicprimitive2d.cxx
@@ -177,7 +177,7 @@ 
GraphicPrimitive2D::GraphicPrimitive2D(basegfx::B2DHomMatrix aTransform,
     , maGraphicAttr(rGraphicAttr)
 {
     // activate callback to flush buffered decomposition content
-    setCallbackSeconds(20);
+    activateFlushOnTimer();
 }
 
 GraphicPrimitive2D::GraphicPrimitive2D(basegfx::B2DHomMatrix aTransform,
@@ -186,7 +186,7 @@ 
GraphicPrimitive2D::GraphicPrimitive2D(basegfx::B2DHomMatrix aTransform,
     , maGraphicObject(rGraphicObject)
 {
     // activate callback to flush buffered decomposition content
-    setCallbackSeconds(20);
+    activateFlushOnTimer();
 }
 
 bool GraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
diff --git a/drawinglayer/source/primitive2d/metafileprimitive2d.cxx 
b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
index 46ddf6582571..a0a1550d1b66 100644
--- a/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
@@ -91,7 +91,7 @@ namespace drawinglayer::primitive2d
             maMetaFile(rMetaFile)
         {
             // activate callback to flush buffered decomposition content
-            setCallbackSeconds(20);
+            activateFlushOnTimer();
         }
 
         bool MetafilePrimitive2D::operator==(const BasePrimitive2D& 
rPrimitive) const
diff --git a/drawinglayer/source/primitive2d/sceneprimitive2d.cxx 
b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
index 5300e8d82e05..02c51b01af53 100644
--- a/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
@@ -633,7 +633,7 @@ namespace drawinglayer::primitive2d
             mfOldDiscreteSizeY(0.0)
         {
             // activate callback to flush buffered decomposition content
-            setCallbackSeconds(45);
+            activateFlushOnTimer();
         }
 
         bool ScenePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) 
const
diff --git a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx 
b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
index ff5600d1f5cf..3b932b92f8c5 100644
--- a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
@@ -51,7 +51,7 @@ ShadowPrimitive2D::ShadowPrimitive2D(basegfx::B2DHomMatrix 
aShadowTransform,
     , maLastClippedRange()
 {
     // activate callback to flush buffered decomposition content
-    setCallbackSeconds(15);
+    activateFlushOnTimer();
 }
 
 bool ShadowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
diff --git a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx 
b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
index df05771f27a6..ed0348e13465 100644
--- a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
@@ -40,7 +40,7 @@ SoftEdgePrimitive2D::SoftEdgePrimitive2D(double fRadius, 
Primitive2DContainer&&
     , maLastClippedRange()
 {
     // activate callback to flush buffered decomposition content
-    setCallbackSeconds(15);
+    activateFlushOnTimer();
 }
 
 bool SoftEdgePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
diff --git a/include/drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx 
b/include/drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx
new file mode 100644
index 000000000000..26fbea502414
--- /dev/null
+++ b/include/drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx>
+#include <salhelper/timer.hxx>
+#include <unordered_set>
+
+namespace drawinglayer::primitive2d
+{
+class BufferedDecompositionFlusher : public salhelper::Timer
+{
+public:
+    static void update(const BufferedDecompositionPrimitive2D*);
+    static void update(const BufferedDecompositionGroupPrimitive2D*);
+
+    BufferedDecompositionFlusher();
+
+private:
+    virtual void SAL_CALL onShot() override;
+    void updateImpl(const BufferedDecompositionPrimitive2D*);
+    void updateImpl(const BufferedDecompositionGroupPrimitive2D*);
+
+    std::mutex maMutex;
+    std::unordered_set<rtl::Reference<BufferedDecompositionPrimitive2D>> 
maRegistered1;
+    std::unordered_set<rtl::Reference<BufferedDecompositionGroupPrimitive2D>> 
maRegistered2;
+};
+
+} // end of namespace drawinglayer::primitive2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git 
a/include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx 
b/include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx
index 9ea787bb9d38..0956dd51ac81 100644
--- a/include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx
+++ b/include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx
@@ -21,10 +21,12 @@
 
 #include <drawinglayer/drawinglayerdllapi.h>
 #include <drawinglayer/primitive2d/groupprimitive2d.hxx>
-#include <salhelper/timer.hxx>
+#include <chrono>
 
 namespace drawinglayer::primitive2d
 {
+class BufferedDecompositionFlusher;
+
 /** BufferedDecompositionGroupPrimitive2D class
 
     Baseclass for all C++ implementations which are derived from 
GroupPrimitive2D
@@ -33,20 +35,20 @@ namespace drawinglayer::primitive2d
     For discussion please refer to BufferedDecompositionPrimitive2D, this is 
the same
     but for GroupPrimitive2D which want to buffer their decomposition
  */
-class UNLESS_MERGELIBS(DRAWINGLAYER_DLLPUBLIC) 
BufferedDecompositionGroupPrimitive2D
+class UNLESS_MERGELIBS(DRAWINGLAYERCORE_DLLPUBLIC) 
BufferedDecompositionGroupPrimitive2D
     : public GroupPrimitive2D
 {
 private:
     // exclusive helper for Primitive2DFlusher
-    friend void 
flushBufferedDecomposition(BufferedDecompositionGroupPrimitive2D&);
+    friend class BufferedDecompositionFlusher;
 
     /// a sequence used for buffering the last create2DDecomposition() result
-    Primitive2DContainer maBuffered2DDecomposition;
+    mutable Primitive2DContainer maBuffered2DDecomposition;
 
     /// offer callback mechanism to flush buffered content timer-based
-    ::rtl::Reference<::salhelper::Timer> maCallbackTimer;
     mutable std::mutex maCallbackLock;
-    sal_uInt16 maCallbackSeconds;
+    mutable std::chrono::time_point<std::chrono::steady_clock> maLastAccess;
+    bool mbFlushOnTimer;
 
 protected:
     /// identical to BufferedDecompositionPrimitive2D, see there please
@@ -62,7 +64,7 @@ protected:
     // callback mechanism to flush buffered content timer-based will be 
activated.
     // it is protected since the idea is that this gets called in the 
constructor
     // of derived classes.
-    void setCallbackSeconds(sal_uInt16 nNew) { maCallbackSeconds = nNew; }
+    void activateFlushOnTimer() { mbFlushOnTimer = true; }
 
 public:
     /// constructor/destructor. For GroupPrimitive2D we need the child 
parameter, too.
diff --git 
a/include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx 
b/include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx
index 3fbfb1e1e00c..bc24a51a5fb4 100644
--- a/include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx
+++ b/include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx
@@ -22,7 +22,7 @@
 #include <drawinglayer/drawinglayerdllapi.h>
 #include <drawinglayer/primitive2d/Primitive2DContainer.hxx>
 #include <drawinglayer/primitive2d/baseprimitive2d.hxx>
-#include <salhelper/timer.hxx>
+#include <chrono>
 
 namespace drawinglayer::geometry
 {
@@ -31,6 +31,8 @@ class ViewInformation2D;
 
 namespace drawinglayer::primitive2d
 {
+class BufferedDecompositionFlusher;
+
 /** BufferedDecompositionPrimitive2D class
 
     Baseclass for all C++ implementations of css::graphic::XPrimitive2D
@@ -64,15 +66,15 @@ class DRAWINGLAYERCORE_DLLPUBLIC 
BufferedDecompositionPrimitive2D : public BaseP
 {
 private:
     // exclusive helper for Primitive2DFlusher
-    friend void flushBufferedDecomposition(BufferedDecompositionPrimitive2D&);
+    friend class BufferedDecompositionFlusher;
 
     /// a sequence used for buffering the last create2DDecomposition() result
-    Primitive2DReference maBuffered2DDecomposition;
+    mutable Primitive2DReference maBuffered2DDecomposition;
 
     /// offer callback mechanism to flush buffered content timer-based
-    ::rtl::Reference<::salhelper::Timer> maCallbackTimer;
     mutable std::mutex maCallbackLock;
-    sal_uInt16 maCallbackSeconds;
+    mutable std::chrono::time_point<std::chrono::steady_clock> maLastAccess;
+    bool mbFlushOnTimer;
 
 protected:
     /** access methods to maBuffered2DDecomposition. The usage of this methods 
may allow
@@ -90,7 +92,7 @@ protected:
     // callback mechanism to flush buffered content timer-based will be 
activated.
     // it is protected since the idea is that this gets called in the 
constructor
     // of derived classes.
-    void setCallbackSeconds(sal_uInt16 nNew) { maCallbackSeconds = nNew; }
+    void activateFlushOnTimer() { mbFlushOnTimer = true; }
 
 public:
     // constructor/destructor
diff --git a/include/drawinglayer/primitive2d/groupprimitive2d.hxx 
b/include/drawinglayer/primitive2d/groupprimitive2d.hxx
index 1c55a5814743..2ef9d936a3db 100644
--- a/include/drawinglayer/primitive2d/groupprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/groupprimitive2d.hxx
@@ -60,7 +60,7 @@ namespace drawinglayer::primitive2d
             - ModifiedColorPrimitive2D (for a stack of color modifications)
             - TransformPrimitive2D (for a transformation stack)
          */
-        class DRAWINGLAYER_DLLPUBLIC GroupPrimitive2D : public BasePrimitive2D
+        class DRAWINGLAYERCORE_DLLPUBLIC GroupPrimitive2D : public 
BasePrimitive2D
         {
         private:
             /// the children. Declared private since this shall never be 
changed at all after construction
diff --git a/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx 
b/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx
index dadc8d06326e..a6fc701da389 100644
--- a/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx
+++ b/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx
@@ -151,7 +151,7 @@ SdrGrafPrimitive2D::SdrGrafPrimitive2D(
     , mbPlaceholderImage(bPlaceholderImage)
 {
     // activate callback to flush buffered decomposition content
-    setCallbackSeconds(20);
+    activateFlushOnTimer();
 
     // reset some values from GraphicAttr which are part of transformation 
already
     maGraphicAttr.SetRotation(0_deg10);

Reply via email to