desktop/qa/desktop_lib/test_desktop_lib.cxx |    3 +-
 desktop/source/lib/init.cxx                 |    8 ++++++
 include/LibreOfficeKit/LibreOfficeKit.h     |    4 +++
 include/LibreOfficeKit/LibreOfficeKit.hxx   |   17 +++++++++++++
 include/vcl/lok.hxx                         |    4 +++
 vcl/inc/graphic/Manager.hxx                 |    8 ++++--
 vcl/inc/impgraph.hxx                        |    2 +
 vcl/inc/svdata.hxx                          |    7 +++++
 vcl/source/app/svapp.cxx                    |   21 ++++++++++++++++
 vcl/source/app/svdata.cxx                   |   23 +++++++++++++++++
 vcl/source/gdi/impgraph.cxx                 |   25 +++++++++++++++++++
 vcl/source/graphic/Manager.cxx              |   36 +++++++++++++++++++++++-----
 12 files changed, 149 insertions(+), 9 deletions(-)

New commits:
commit 4a4602ad7513262a6c0423f17b42791a852b7e23
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Fri Mar 10 10:36:22 2023 +0000
Commit:     Michael Meeks <michael.me...@collabora.com>
CommitDate: Sat Mar 11 21:03:05 2023 +0000

    lok: add trimMemory capability, and expand dumpState to caches.
    
    Being able to trigger some more aggressive memory saving is
    useful in for both online and mobile.
    
    Change-Id: I9b91c9fe9eecec06c75112595deac0bfeb94c144
    Signed-off-by: Michael Meeks <michael.me...@collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/148624
    Tested-by: Jenkins

diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx 
b/desktop/qa/desktop_lib/test_desktop_lib.cxx
index 217b537fa214..a919dbcf4267 100644
--- a/desktop/qa/desktop_lib/test_desktop_lib.cxx
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -3554,10 +3554,11 @@ void DesktopLOKTest::testABI()
     CPPUNIT_ASSERT_EQUAL(classOffset(14), offsetof(struct 
_LibreOfficeKitClass, setOption));
     CPPUNIT_ASSERT_EQUAL(classOffset(15), offsetof(struct 
_LibreOfficeKitClass, dumpState));
     CPPUNIT_ASSERT_EQUAL(classOffset(16), offsetof(struct 
_LibreOfficeKitClass, extractRequest));
+    CPPUNIT_ASSERT_EQUAL(classOffset(17), offsetof(struct 
_LibreOfficeKitClass, trimMemory));
 
     // When extending LibreOfficeKit with a new function pointer,  add new 
assert for the offsetof the
     // new function pointer and bump this assert for the size of the class.
-    CPPUNIT_ASSERT_EQUAL(classOffset(17), sizeof(struct _LibreOfficeKitClass));
+    CPPUNIT_ASSERT_EQUAL(classOffset(18), sizeof(struct _LibreOfficeKitClass));
 
     CPPUNIT_ASSERT_EQUAL(documentClassOffset(0), offsetof(struct 
_LibreOfficeKitDocumentClass, destroy));
     CPPUNIT_ASSERT_EQUAL(documentClassOffset(1), offsetof(struct 
_LibreOfficeKitDocumentClass, saveAs));
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index cf23f09f1413..9722be530db2 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -2523,6 +2523,8 @@ static bool lo_signDocument(LibreOfficeKit* pThis,
 static char* lo_extractRequest(LibreOfficeKit* pThis,
                                    const char* pFilePath);
 
+static void lo_trimMemory(LibreOfficeKit* pThis, int nTarget);
+
 static void lo_runLoop(LibreOfficeKit* pThis,
                        LibreOfficeKitPollCallback pPollCallback,
                        LibreOfficeKitWakeCallback pWakeCallback,
@@ -2564,6 +2566,7 @@ LibLibreOffice_Impl::LibLibreOffice_Impl()
         m_pOfficeClass->setOption = lo_setOption;
         m_pOfficeClass->dumpState = lo_dumpState;
         m_pOfficeClass->extractRequest = lo_extractRequest;
+        m_pOfficeClass->trimMemory = lo_trimMemory;
 
         gOfficeClass = m_pOfficeClass;
     }
@@ -3142,6 +3145,11 @@ static char* lo_extractRequest(LibreOfficeKit* 
/*pThis*/, const char* pFilePath)
     return convertOUString(result);
 }
 
+static void lo_trimMemory(LibreOfficeKit* /* pThis */, int nTarget)
+{
+    vcl::lok::trimMemory(nTarget);
+}
+
 static void lo_registerCallback (LibreOfficeKit* pThis,
                                  LibreOfficeKitCallback pCallback,
                                  void* pData)
diff --git a/include/LibreOfficeKit/LibreOfficeKit.h 
b/include/LibreOfficeKit/LibreOfficeKit.h
index 3887d3d3c412..e98ea6f47f42 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.h
+++ b/include/LibreOfficeKit/LibreOfficeKit.h
@@ -127,6 +127,10 @@ struct _LibreOfficeKitClass
      */
     char* (*extractRequest) (LibreOfficeKit* pThis,
                            const char* pFilePath);
+
+    /// @see lok::Office::trimMemory
+    /// @since LibreOffice 7.6
+    void (*trimMemory) (LibreOfficeKit* pThis, int nTarget);
 };
 
 #define LIBREOFFICEKIT_DOCUMENT_HAS(pDoc,member) 
LIBREOFFICEKIT_HAS_MEMBER(LibreOfficeKitDocumentClass,member,(pDoc)->pClass->nSize)
diff --git a/include/LibreOfficeKit/LibreOfficeKit.hxx 
b/include/LibreOfficeKit/LibreOfficeKit.hxx
index d3c2e5de78aa..bc3bbb98cc10 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.hxx
+++ b/include/LibreOfficeKit/LibreOfficeKit.hxx
@@ -1146,6 +1146,23 @@ public:
     {
         return mpThis->pClass->extractRequest(mpThis, pFilePath);
     }
+
+    /**
+     * Trim memory usage.
+     *
+     * LibreOfficeKit caches lots of information from large pixmaps
+     * to view and calculation results. When a view has not been
+     * used for some time, depending on the load on memory it can
+     * be useful to free up memory.
+     *
+     * @param nTarget - a negative number means the app is back
+     * in active use, and to re-fill caches, a large positive
+     * number (>=1000) encourages immediate maximum memory saving.
+     */
+    void trimMemory (int nTarget)
+    {
+        mpThis->pClass->trimMemory(mpThis, nTarget);
+    }
 };
 
 /// Factory method to create a lok::Office instance.
diff --git a/include/vcl/lok.hxx b/include/vcl/lok.hxx
index 31a4cf1ec494..ea7ffadd437b 100644
--- a/include/vcl/lok.hxx
+++ b/include/vcl/lok.hxx
@@ -26,6 +26,10 @@ void VCL_DLLPUBLIC unregisterPollCallbacks();
 // can adjust e.g. sizes of bitmap caches to scale well with larger number of 
users.
 void VCL_DLLPUBLIC numberOfViewsChanged(int count);
 
+// Trim memory use by wiping various internal caches
+void VCL_DLLPUBLIC trimMemory(int nTarget);
+
+// Dump internal state of VCL windows etc. for debugging
 void VCL_DLLPUBLIC dumpState(rtl::OStringBuffer& rState);
 }
 
diff --git a/vcl/inc/graphic/Manager.hxx b/vcl/inc/graphic/Manager.hxx
index 6324ff89a899..f6791626d841 100644
--- a/vcl/inc/graphic/Manager.hxx
+++ b/vcl/inc/graphic/Manager.hxx
@@ -11,6 +11,7 @@
 #define INCLUDED_VCL_INC_GRAPHIC_MANAGER_HXX
 
 #include <sal/types.h>
+#include <rtl/strbuf.hxx>
 #include <rtl/ustring.hxx>
 #include <vcl/bitmapex.hxx>
 #include <vcl/animate/Animation.hxx>
@@ -43,16 +44,19 @@ private:
     Manager();
 
     void registerGraphic(const std::shared_ptr<ImpGraphic>& rImpGraphic);
-    void loopGraphicsAndSwapOut(std::unique_lock<std::mutex>& rGuard);
+    void loopGraphicsAndSwapOut(std::unique_lock<std::mutex>& rGuard, bool 
bDropAll);
 
     DECL_LINK(SwapOutTimerHandler, Timer*, void);
 
     static sal_Int64 getGraphicSizeBytes(const ImpGraphic* pImpGraphic);
-    void reduceGraphicMemory(std::unique_lock<std::mutex>& rGuard);
+    void reduceGraphicMemory(std::unique_lock<std::mutex>& rGuard, bool 
bDropAll = false);
 
 public:
     static Manager& get();
 
+    void dropCache();
+    void dumpState(rtl::OStringBuffer& rState);
+
     void swappedIn(const ImpGraphic* pImpGraphic, sal_Int64 nSizeBytes);
     void swappedOut(const ImpGraphic* pImpGraphic, sal_Int64 nSizeBytes);
 
diff --git a/vcl/inc/impgraph.hxx b/vcl/inc/impgraph.hxx
index b62cc8e623ec..2e2b00640a20 100644
--- a/vcl/inc/impgraph.hxx
+++ b/vcl/inc/impgraph.hxx
@@ -47,6 +47,7 @@ class GfxLink;
 class ImpSwapFile;
 class GraphicConversionParameters;
 class ImpGraphic;
+namespace rtl { class OStringBuffer; }
 
 enum class GraphicContentType : sal_Int32
 {
@@ -219,6 +220,7 @@ public:
     SvStream* getSwapFileStream() const;
     // public only because of use in GraphicFilter
     void updateFromLoadedGraphic(const ImpGraphic* graphic);
+    void dumpState(rtl::OStringBuffer &rState);
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/svdata.hxx b/vcl/inc/svdata.hxx
index c514df09422c..85579825a23d 100644
--- a/vcl/inc/svdata.hxx
+++ b/vcl/inc/svdata.hxx
@@ -54,6 +54,10 @@ struct ImplPostEventData;
 struct ImplTimerData;
 struct ImplIdleData;
 struct ImplConfigData;
+namespace rtl
+{
+    class OStringBuffer;
+}
 namespace vcl::font
 {
     class DirectFontSubstitution;
@@ -431,6 +435,9 @@ struct ImplSVData
     LibreOfficeKitPollCallback mpPollCallback = nullptr;
     LibreOfficeKitWakeCallback mpWakeCallback = nullptr;
     void *mpPollClosure = nullptr;
+
+    void dropCaches();
+    void dumpState(rtl::OStringBuffer &rState);
 };
 
 css::uno::Reference<css::i18n::XCharacterClassification> const& 
ImplGetCharClass();
diff --git a/vcl/source/app/svapp.cxx b/vcl/source/app/svapp.cxx
index f503310949f2..5359ac139532 100644
--- a/vcl/source/app/svapp.cxx
+++ b/vcl/source/app/svapp.cxx
@@ -54,6 +54,7 @@
 #include <vcl/skia/SkiaHelper.hxx>
 
 #include <salinst.hxx>
+#include <graphic/Manager.hxx>
 #include <salframe.hxx>
 #include <salsys.hxx>
 #include <svdata.hxx>
@@ -69,6 +70,7 @@
 #include <com/sun/star/uno/Reference.h>
 #include <com/sun/star/awt/XToolkit.hpp>
 #include <comphelper/lok.hxx>
+#include <comphelper/threadpool.hxx>
 #include <comphelper/solarmutex.hxx>
 #include <osl/process.h>
 
@@ -1870,6 +1872,25 @@ void dumpState(rtl::OStringBuffer &rState)
 
         pWin = Application::GetNextTopLevelWindow( pWin );
     }
+
+    vcl::graphic::Manager::get().dumpState(rState);
+
+    pSVData->dumpState(rState);
+}
+
+void trimMemory(int nTarget)
+{
+    if (nTarget >= 1000)
+    {
+        ImplSVData* pSVData = ImplGetSVData();
+        if (!pSVData) // shutting down
+            return;
+        pSVData->dropCaches();
+        vcl::graphic::Manager::get().dropCache();
+        // TODO: ideally - free up any deeper dirtied thread stacks.
+        // comphelper::ThreadPool::getSharedOptimalPool().shutdown();
+    }
+    // else for now caches re-fill themselves as/when used.
 }
 
 } // namespace lok, namespace vcl
diff --git a/vcl/source/app/svdata.cxx b/vcl/source/app/svdata.cxx
index 7c9a5378588e..febed41cb98c 100644
--- a/vcl/source/app/svdata.cxx
+++ b/vcl/source/app/svdata.cxx
@@ -417,6 +417,29 @@ ImplSVData::ImplSVData()
     mpWinData = &private_aImplSVWinData::get();
 }
 
+void ImplSVData::dropCaches()
+{
+    maGDIData.maScaleCache.clear();
+    maGDIData.maThemeImageCache.clear();
+    maGDIData.maThemeDrawCommandsCache.clear();
+}
+
+void ImplSVData::dumpState(rtl::OStringBuffer &rState)
+{
+    rState.append("\nScaleCache:\t");
+    rState.append(static_cast<sal_Int32>(maGDIData.maScaleCache.size()));
+    rState.append("\t items:");
+
+    for (auto it = maGDIData.maScaleCache.begin();
+         it != maGDIData.maScaleCache.begin(); ++it)
+    {
+        rState.append("\n\t");
+        rState.append(static_cast<sal_Int32>(it->first.maDestSize.Width()));
+        rState.append("x");
+        rState.append(static_cast<sal_Int32>(it->first.maDestSize.Height()));
+    }
+}
+
 ImplSVHelpData* CreateSVHelpData()
 {
     if (!comphelper::LibreOfficeKit::isActive())
diff --git a/vcl/source/gdi/impgraph.cxx b/vcl/source/gdi/impgraph.cxx
index 67b27461d7d7..8e0dfdd5ad43 100644
--- a/vcl/source/gdi/impgraph.cxx
+++ b/vcl/source/gdi/impgraph.cxx
@@ -1419,6 +1419,31 @@ void ImpGraphic::updateFromLoadedGraphic(const 
ImpGraphic* pGraphic)
     }
 }
 
+void ImpGraphic::dumpState(rtl::OStringBuffer &rState)
+{
+    if (meType == GraphicType::NONE && mnSizeBytes == 0)
+        return; // uninteresting.
+
+    rState.append("\n\t");
+
+    if (mbSwapOut)
+        rState.append("swapped\t");
+    else
+        rState.append("loaded\t");
+
+    rState.append(static_cast<sal_Int32>(meType));
+    rState.append("\tsize:\t");
+    rState.append(static_cast<sal_Int64>(mnSizeBytes));
+    rState.append("\t");
+    rState.append(static_cast<sal_Int32>(maSwapInfo.maSizePixel.Width()));
+    rState.append("x");
+    rState.append(static_cast<sal_Int32>(maSwapInfo.maSizePixel.Height()));
+    rState.append("\t");
+    rState.append(static_cast<sal_Int32>(maExPrefSize.Width()));
+    rState.append("x");
+    rState.append(static_cast<sal_Int32>(maExPrefSize.Height()));
+}
+
 void ImpGraphic::restoreFromSwapInfo()
 {
     setValuesForPrefMapMod(maSwapInfo.maPrefMapMode);
diff --git a/vcl/source/graphic/Manager.cxx b/vcl/source/graphic/Manager.cxx
index bdb5ba7ef432..f77e3e3c4041 100644
--- a/vcl/source/graphic/Manager.cxx
+++ b/vcl/source/graphic/Manager.cxx
@@ -75,7 +75,7 @@ Manager::Manager()
     }
 }
 
-void Manager::loopGraphicsAndSwapOut(std::unique_lock<std::mutex>& rGuard)
+void Manager::loopGraphicsAndSwapOut(std::unique_lock<std::mutex>& rGuard, 
bool bDropAll)
 {
     // make a copy of m_pImpGraphicList because if we swap out a svg, the svg
     // filter may create more temp Graphics which are auto-added to
@@ -85,14 +85,14 @@ void 
Manager::loopGraphicsAndSwapOut(std::unique_lock<std::mutex>& rGuard)
 
     for (ImpGraphic* pEachImpGraphic : aImpGraphicList)
     {
-        if (mnUsedSize < sal_Int64(mnMemoryLimit * 0.7))
+        if (mnUsedSize < sal_Int64(mnMemoryLimit * 0.7) && !bDropAll)
             return;
 
         if (pEachImpGraphic->isSwappedOut())
             continue;
 
         sal_Int64 nCurrentGraphicSize = getGraphicSizeBytes(pEachImpGraphic);
-        if (nCurrentGraphicSize > 100000)
+        if (nCurrentGraphicSize > 100000 || bDropAll)
         {
             if (!pEachImpGraphic->mpContext)
             {
@@ -112,22 +112,23 @@ void 
Manager::loopGraphicsAndSwapOut(std::unique_lock<std::mutex>& rGuard)
     }
 }
 
-void Manager::reduceGraphicMemory(std::unique_lock<std::mutex>& rGuard)
+void Manager::reduceGraphicMemory(std::unique_lock<std::mutex>& rGuard, bool 
bDropAll)
 {
     // maMutex is locked in callers
 
     if (!mbSwapEnabled)
         return;
 
-    if (mnUsedSize < mnMemoryLimit)
+    if (mnUsedSize < mnMemoryLimit && !bDropAll)
         return;
 
     // avoid recursive reduceGraphicMemory on reexport of tdf118346-1.odg to 
odg
     if (mbReducingGraphicMemory)
         return;
+
     mbReducingGraphicMemory = true;
 
-    loopGraphicsAndSwapOut(rGuard);
+    loopGraphicsAndSwapOut(rGuard, bDropAll);
 
     sal_Int64 calculatedSize = 0;
     for (ImpGraphic* pEachImpGraphic : m_pImpGraphicList)
@@ -148,6 +149,29 @@ void 
Manager::reduceGraphicMemory(std::unique_lock<std::mutex>& rGuard)
     mbReducingGraphicMemory = false;
 }
 
+void Manager::dropCache()
+{
+    std::unique_lock aGuard(maMutex);
+
+    reduceGraphicMemory(aGuard, true);
+}
+
+void Manager::dumpState(rtl::OStringBuffer& rState)
+{
+    std::unique_lock aGuard(maMutex);
+
+    rState.append("\nImage Manager items:\t");
+    rState.append(static_cast<sal_Int32>(m_pImpGraphicList.size()));
+    rState.append("\tsize:\t");
+    rState.append(static_cast<sal_Int64>(mnUsedSize / 1024));
+    rState.append("\tkb");
+
+    for (ImpGraphic* pEachImpGraphic : m_pImpGraphicList)
+    {
+        pEachImpGraphic->dumpState(rState);
+    }
+}
+
 sal_Int64 Manager::getGraphicSizeBytes(const ImpGraphic* pImpGraphic)
 {
     if (!pImpGraphic->isAvailable())

Reply via email to