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())