summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomaž Vajngerl <tomaz.vajngerl@collabora.co.uk>2024-03-18 00:17:54 +0900
committerTomaž Vajngerl <quikee@gmail.com>2024-03-30 15:21:10 +0100
commit324f2e135427f2f24cf7eb9a4fab4aa903329ae5 (patch)
treebdcf04a1cd47b22a7612e903f9c68aef17c1dc41
parent174430d7a831eede078b6718d991b506d39180f1 (diff)
vcl: change (graphic) Manager into a general memory manager
Graphic memory manager was changes so that it can work with any object that implements a specific interface (MemoryManaged). With this it will be possible to use other objects (that take a lot of memory) to be managed by the manager. It is also a first step to move memory managin responsibilities away from Graphic and move it into the specific objects instead (BitmapEx, Animation and VectorGraphic). Change-Id: I7638bd89a1c9ece5c4bc95b506d2192492894ef3 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164958 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
-rw-r--r--vcl/CppunitTest_vcl_graphic_test.mk1
-rw-r--r--vcl/inc/graphic/Manager.hxx67
-rw-r--r--vcl/inc/graphic/MemoryManaged.hxx145
-rw-r--r--vcl/inc/impgraph.hxx12
-rw-r--r--vcl/qa/cppunit/GraphicMemoryTest.cxx310
-rw-r--r--vcl/source/app/svapp.cxx4
-rw-r--r--vcl/source/gdi/graph.cxx30
-rw-r--r--vcl/source/gdi/impgraph.cxx93
-rw-r--r--vcl/source/graphic/Manager.cxx286
9 files changed, 658 insertions, 290 deletions
diff --git a/vcl/CppunitTest_vcl_graphic_test.mk b/vcl/CppunitTest_vcl_graphic_test.mk
index 6089512f5b5b..33e818e23f05 100644
--- a/vcl/CppunitTest_vcl_graphic_test.mk
+++ b/vcl/CppunitTest_vcl_graphic_test.mk
@@ -11,6 +11,7 @@ $(eval $(call gb_CppunitTest_CppunitTest,vcl_graphic_test))
$(eval $(call gb_CppunitTest_add_exception_objects,vcl_graphic_test, \
vcl/qa/cppunit/GraphicTest \
+ vcl/qa/cppunit/GraphicMemoryTest \
vcl/qa/cppunit/GraphicDescriptorTest \
vcl/qa/cppunit/GraphicFormatDetectorTest \
vcl/qa/cppunit/GraphicNativeMetadataTest \
diff --git a/vcl/inc/graphic/Manager.hxx b/vcl/inc/graphic/Manager.hxx
index d239f6a8b01d..0a4ee3fbc84d 100644
--- a/vcl/inc/graphic/Manager.hxx
+++ b/vcl/inc/graphic/Manager.hxx
@@ -11,66 +11,51 @@
#include <sal/types.h>
#include <rtl/strbuf.hxx>
-#include <vcl/bitmapex.hxx>
-#include <vcl/animate/Animation.hxx>
-#include <vcl/vectorgraphicdata.hxx>
#include <vcl/timer.hxx>
-#include <vcl/GraphicExternalLink.hxx>
-#include <vcl/gfxlink.hxx>
#include <memory>
#include <mutex>
#include <chrono>
#include <o3tl/sorted_vector.hxx>
-class ImpGraphic;
-
namespace vcl::graphic
{
-class Manager final
+class MemoryManaged;
+
+class VCL_DLLPUBLIC MemoryManager final
{
private:
+ o3tl::sorted_vector<MemoryManaged*> maObjectList;
+ sal_Int64 mnTotalSize = 0;
std::mutex maMutex; // instead of SolarMutex because graphics can live past vcl main
- o3tl::sorted_vector<ImpGraphic*> m_pImpGraphicList;
- std::chrono::seconds mnAllowedIdleTime;
- bool mbSwapEnabled;
- bool mbReducingGraphicMemory;
- sal_Int64 mnMemoryLimit;
- sal_Int64 mnUsedSize;
- Timer maSwapOutTimer;
-
- Manager();
-
- void registerGraphic(const std::shared_ptr<ImpGraphic>& rImpGraphic);
- void loopGraphicsAndSwapOut(std::unique_lock<std::mutex>& rGuard, bool bDropAll);
- DECL_LINK(SwapOutTimerHandler, Timer*, void);
+ std::chrono::seconds mnAllowedIdleTime = std::chrono::seconds(1);
+ bool mbSwapEnabled = true;
+ bool mbReducingGraphicMemory = false;
+ sal_Int64 mnMemoryLimit = 10'000'000;
+ Timer maSwapOutTimer;
+ sal_Int32 mnTimeout = 1'000;
+ sal_Int64 mnSmallFrySize = 100'000;
- static sal_Int64 getGraphicSizeBytes(const ImpGraphic* pImpGraphic);
- void reduceGraphicMemory(std::unique_lock<std::mutex>& rGuard, bool bDropAll = false);
+ DECL_LINK(ReduceMemoryTimerHandler, Timer*, void);
public:
- static Manager& get();
-
- void dropCache();
- void dumpState(rtl::OStringBuffer& rState);
+ MemoryManager();
+ void registerObject(MemoryManaged* pObject);
+ void unregisterObject(MemoryManaged* pObject);
+ void changeExisting(MemoryManaged* pObject, sal_Int64 nNewSize);
- void swappedIn(const ImpGraphic* pImpGraphic, sal_Int64 nSizeBytes);
- void swappedOut(const ImpGraphic* pImpGraphic, sal_Int64 nSizeBytes);
+ void swappedIn(MemoryManaged* pObject, sal_Int64 nNewSize);
+ void swappedOut(MemoryManaged* pObject, sal_Int64 nNewSize);
- void changeExisting(const ImpGraphic* pImpGraphic, sal_Int64 nOldSize);
- void unregisterGraphic(ImpGraphic* pImpGraphic);
+ static MemoryManager& get();
+ o3tl::sorted_vector<MemoryManaged*> const& getManagedObjects() { return maObjectList; }
+ sal_Int64 getTotalSize() { return mnTotalSize; }
- std::shared_ptr<ImpGraphic> copy(std::shared_ptr<ImpGraphic> const& pImpGraphic);
- std::shared_ptr<ImpGraphic> newInstance();
- std::shared_ptr<ImpGraphic> newInstance(const BitmapEx& rBitmapEx);
- std::shared_ptr<ImpGraphic> newInstance(std::shared_ptr<GfxLink> const& rLink,
- sal_Int32 nPageIndex = 0);
- std::shared_ptr<ImpGraphic>
- newInstance(const std::shared_ptr<VectorGraphicData>& rVectorGraphicDataPtr);
- std::shared_ptr<ImpGraphic> newInstance(const Animation& rAnimation);
- std::shared_ptr<ImpGraphic> newInstance(const GDIMetaFile& rMtf);
- std::shared_ptr<ImpGraphic> newInstance(const GraphicExternalLink& rGraphicLink);
+ void reduceMemory(std::unique_lock<std::mutex>& rGuard, bool bDropAll = false);
+ void loopAndReduceMemory(std::unique_lock<std::mutex>& rGuard, bool bDropAll = false);
+ void reduceAllAndNow();
+ void dumpState(rtl::OStringBuffer& rState);
};
} // end namespace vcl::graphic
diff --git a/vcl/inc/graphic/MemoryManaged.hxx b/vcl/inc/graphic/MemoryManaged.hxx
new file mode 100644
index 000000000000..d88e87979ba9
--- /dev/null
+++ b/vcl/inc/graphic/MemoryManaged.hxx
@@ -0,0 +1,145 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <rtl/strbuf.hxx>
+#include <vcl/timer.hxx>
+
+#include <memory>
+#include <mutex>
+#include <chrono>
+#include <o3tl/sorted_vector.hxx>
+
+namespace vcl::graphic
+{
+class VCL_DLLPUBLIC MemoryManaged
+{
+private:
+ sal_Int64 mnCurrentSizeBytes = 0;
+ bool mbIsRegistered = false;
+
+protected:
+ // Copy
+ MemoryManaged(MemoryManaged const& rMemoryManaged)
+ : mbIsRegistered(rMemoryManaged.mbIsRegistered)
+ {
+ // if the original is registered we need to register as well
+ if (mbIsRegistered)
+ registerIntoManager();
+ }
+
+ // Move
+ MemoryManaged(MemoryManaged&& rMemoryManaged)
+ : mbIsRegistered(rMemoryManaged.mbIsRegistered)
+ {
+ // if the original is registered we need to register as well
+ if (mbIsRegistered)
+ registerIntoManager();
+ }
+
+ MemoryManaged(bool bRegister)
+ {
+ if (bRegister)
+ registerIntoManager();
+ }
+
+ virtual ~MemoryManaged() { unregisterFromManager(); }
+
+ void updateCurrentSizeInBytes(sal_Int64 nNewSize)
+ {
+ if (mbIsRegistered)
+ {
+ auto& rManager = vcl::graphic::MemoryManager::get();
+ rManager.changeExisting(this, nNewSize);
+ }
+ else
+ {
+ mnCurrentSizeBytes = nNewSize;
+ }
+ }
+
+ bool isRegistered() const { return mbIsRegistered; }
+
+ void changeExisting(sal_Int64 nNewSize)
+ {
+ if (mbIsRegistered)
+ {
+ auto& rManager = vcl::graphic::MemoryManager::get();
+ rManager.changeExisting(this, nNewSize);
+ }
+ else
+ {
+ mnCurrentSizeBytes = nNewSize;
+ }
+ }
+
+ void swappedIn(sal_Int64 nNewSize)
+ {
+ if (mbIsRegistered)
+ {
+ auto& rManager = vcl::graphic::MemoryManager::get();
+ rManager.swappedIn(this, nNewSize);
+ }
+ else
+ {
+ mnCurrentSizeBytes = nNewSize;
+ }
+ }
+
+ void swappedOut(sal_Int64 nNewSize)
+ {
+ if (mbIsRegistered)
+ {
+ auto& rManager = vcl::graphic::MemoryManager::get();
+ rManager.swappedOut(this, nNewSize);
+ }
+ else
+ {
+ mnCurrentSizeBytes = nNewSize;
+ }
+ }
+
+public:
+ sal_Int64 getCurrentSizeInBytes() const { return mnCurrentSizeBytes; }
+
+ void setCurrentSizeInBytes(sal_Int64 nSize) { mnCurrentSizeBytes = nSize; }
+
+protected:
+ void registerIntoManager()
+ {
+ if (!mbIsRegistered)
+ {
+ auto& rManager = vcl::graphic::MemoryManager::get();
+ rManager.registerObject(this);
+ mbIsRegistered = true;
+ }
+ }
+
+ void unregisterFromManager()
+ {
+ if (mbIsRegistered)
+ {
+ auto& rManager = vcl::graphic::MemoryManager::get();
+ rManager.unregisterObject(this);
+ mbIsRegistered = false;
+ }
+ }
+
+public:
+ virtual bool canReduceMemory() const = 0;
+ virtual bool reduceMemory() = 0;
+ virtual std::chrono::high_resolution_clock::time_point getLastUsed() const = 0;
+ virtual void dumpState(rtl::OStringBuffer& rState) = 0;
+};
+
+} // end namespace vcl::graphic
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/impgraph.hxx b/vcl/inc/impgraph.hxx
index 707810b9de2e..35542db74641 100644
--- a/vcl/inc/impgraph.hxx
+++ b/vcl/inc/impgraph.hxx
@@ -24,6 +24,7 @@
#include <vcl/gdimtf.hxx>
#include <vcl/graph.hxx>
#include "graphic/Manager.hxx"
+#include "graphic/MemoryManaged.hxx"
#include "graphic/GraphicID.hxx"
#include <optional>
@@ -56,11 +57,10 @@ enum class GraphicContentType : sal_Int32
Vector
};
-class ImpGraphic final
+class ImpGraphic final : public vcl::graphic::MemoryManaged
{
friend class Graphic;
friend class GraphicID;
- friend class vcl::graphic::Manager;
private:
@@ -160,6 +160,8 @@ private:
sal_uLong getSizeBytes() const;
+ void ensureCurrentSizeInBytes();
+
void draw(OutputDevice& rOutDev, const Point& rDestPt) const;
void draw(OutputDevice& rOutDev, const Point& rDestPt,
const Size& rDestSize) const;
@@ -212,6 +214,10 @@ private:
// Set the pref map mode, but don't force swap-in
void setValuesForPrefMapMod(const MapMode& rPrefMapMode);
+ bool canReduceMemory() const override;
+ bool reduceMemory() override;
+ std::chrono::high_resolution_clock::time_point getLastUsed() const override;
+
public:
void resetChecksum() { mnChecksum = 0; }
bool swapIn();
@@ -220,7 +226,7 @@ public:
VCL_DLLPUBLIC SvStream* getSwapFileStream() const;
// public only because of use in GraphicFilter
void updateFromLoadedGraphic(const ImpGraphic* graphic);
- void dumpState(rtl::OStringBuffer &rState);
+ void dumpState(rtl::OStringBuffer &rState) override;
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/qa/cppunit/GraphicMemoryTest.cxx b/vcl/qa/cppunit/GraphicMemoryTest.cxx
new file mode 100644
index 000000000000..95e64e471246
--- /dev/null
+++ b/vcl/qa/cppunit/GraphicMemoryTest.cxx
@@ -0,0 +1,310 @@
+/* -*- 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/.
+ */
+
+#include <test/bootstrapfixture.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <tools/stream.hxx>
+
+#include <impgraph.hxx>
+#include <graphic/GraphicFormatDetector.hxx>
+#include <graphic/MemoryManaged.hxx>
+
+using namespace css;
+
+namespace
+{
+BitmapEx createBitmap(Size aSize, bool bAlpha = false)
+{
+ Bitmap aBitmap(aSize, vcl::PixelFormat::N24_BPP);
+ aBitmap.Erase(COL_LIGHTRED);
+
+ aBitmap.SetPrefSize(Size(aSize.Width() * 2, aSize.Height() * 3));
+ aBitmap.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+
+ if (bAlpha)
+ {
+ sal_uInt8 uAlphaValue = 0x80;
+ AlphaMask aAlphaMask(aSize, &uAlphaValue);
+
+ return BitmapEx(aBitmap, aAlphaMask);
+ }
+ else
+ {
+ return BitmapEx(aBitmap);
+ }
+}
+
+void createBitmapAndExportForType(SvStream& rStream, std::u16string_view sType,
+ Size aSize = Size(120, 100), bool bAlpha = false)
+{
+ BitmapEx aBitmapEx = createBitmap(aSize, bAlpha);
+
+ uno::Sequence<beans::PropertyValue> aFilterData;
+ GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+ sal_uInt16 nFilterFormat = rGraphicFilter.GetExportFormatNumberForShortName(sType);
+ rGraphicFilter.ExportGraphic(aBitmapEx, u"none", rStream, nFilterFormat, &aFilterData);
+
+ rStream.Seek(STREAM_SEEK_TO_BEGIN);
+}
+
+Graphic makeUnloadedGraphic(std::u16string_view sType, Size aSize = Size(120, 100),
+ bool bAlpha = false)
+{
+ SvMemoryStream aStream;
+ GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+ createBitmapAndExportForType(aStream, sType, aSize, bAlpha);
+ return rGraphicFilter.ImportUnloadedGraphic(aStream);
+}
+
+constexpr OUString DATA_DIRECTORY = u"/vcl/qa/cppunit/data/"_ustr;
+
+Graphic loadGraphic(std::u16string_view rFilename)
+{
+ test::Directories aDirectories;
+ OUString aFilename = aDirectories.getURLFromSrc(DATA_DIRECTORY) + rFilename;
+ SvFileStream aFileStream(aFilename, StreamMode::READ);
+ GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+
+ Graphic aGraphic;
+ CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, rGraphicFilter.ImportGraphic(aGraphic, u"", aFileStream,
+ GRFILTER_FORMAT_DONTKNOW));
+ return aGraphic;
+}
+
+Graphic loadUnloadedGraphic(std::u16string_view rFilename)
+{
+ test::Directories aDirectories;
+ OUString aFilename = aDirectories.getURLFromSrc(DATA_DIRECTORY) + rFilename;
+ SvFileStream aFileStream(aFilename, StreamMode::READ);
+ GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+ return rGraphicFilter.ImportUnloadedGraphic(aFileStream);
+}
+
+} // end anonymous
+
+class GraphicMemoryTest : public test::BootstrapFixture
+{
+};
+
+CPPUNIT_TEST_FIXTURE(GraphicMemoryTest, testMemoryManager_Empty)
+{
+ auto& rManager = vcl::graphic::MemoryManager::get();
+ CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+ Graphic aGraphic1;
+ CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+ Graphic aGraphic2;
+ CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+ Graphic aGraphic3;
+ CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+ aGraphic1 = aGraphic2;
+ CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+ aGraphic2 = aGraphic3;
+ CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+}
+
+CPPUNIT_TEST_FIXTURE(GraphicMemoryTest, testMemoryManager_Unloaded)
+{
+ auto& rManager = vcl::graphic::MemoryManager::get();
+ CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+ Graphic aGraphic_100 = makeUnloadedGraphic(u"jpg", Size(100, 100));
+ Graphic aGraphic_200 = makeUnloadedGraphic(u"png", Size(200, 100));
+ Graphic aGraphic_300 = makeUnloadedGraphic(u"jpg", Size(300, 100));
+
+ CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+ aGraphic_100.makeAvailable();
+ CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(30000), rManager.getTotalSize());
+ CPPUNIT_ASSERT_EQUAL(sal_uLong(30000), aGraphic_100.GetSizeBytes());
+
+ aGraphic_200.makeAvailable();
+ CPPUNIT_ASSERT_EQUAL(size_t(2), rManager.getManagedObjects().size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(90000), rManager.getTotalSize());
+ CPPUNIT_ASSERT_EQUAL(sal_uLong(30000), aGraphic_100.GetSizeBytes());
+ CPPUNIT_ASSERT_EQUAL(sal_uLong(60000), aGraphic_200.GetSizeBytes());
+
+ aGraphic_300.makeAvailable();
+ CPPUNIT_ASSERT_EQUAL(size_t(3), rManager.getManagedObjects().size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(180000), rManager.getTotalSize());
+ CPPUNIT_ASSERT_EQUAL(sal_uLong(30000), aGraphic_100.GetSizeBytes());
+ CPPUNIT_ASSERT_EQUAL(sal_uLong(60000), aGraphic_200.GetSizeBytes());
+ CPPUNIT_ASSERT_EQUAL(sal_uLong(90000), aGraphic_300.GetSizeBytes());
+}
+
+CPPUNIT_TEST_FIXTURE(GraphicMemoryTest, testMemoryManager_Destrucion)
+{
+ auto& rManager = vcl::graphic::MemoryManager::get();
+ CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+ Graphic aGraphic_100 = makeUnloadedGraphic(u"jpg", Size(100, 100));
+ Graphic aGraphic_200 = makeUnloadedGraphic(u"png", Size(200, 100));
+ Graphic aGraphic_300 = makeUnloadedGraphic(u"jpg", Size(300, 100));
+
+ CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+ aGraphic_100.makeAvailable();
+ aGraphic_200.makeAvailable();
+ aGraphic_300.makeAvailable();
+ CPPUNIT_ASSERT_EQUAL(size_t(3), rManager.getManagedObjects().size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(180000), rManager.getTotalSize());
+
+ aGraphic_200 = Graphic();
+ CPPUNIT_ASSERT_EQUAL(size_t(2), rManager.getManagedObjects().size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(120000), rManager.getTotalSize());
+
+ aGraphic_300 = Graphic();
+ CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(30000), rManager.getTotalSize());
+}
+
+CPPUNIT_TEST_FIXTURE(GraphicMemoryTest, testMemoryManager_Copy)
+{
+ auto& rManager = vcl::graphic::MemoryManager::get();
+ CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+ Graphic aGraphic_100 = makeUnloadedGraphic(u"jpg", Size(100, 100));
+ Graphic aGraphic_200 = makeUnloadedGraphic(u"png", Size(200, 100));
+ Graphic aGraphic_300 = makeUnloadedGraphic(u"jpg", Size(300, 100));
+
+ Graphic aSVG = loadUnloadedGraphic(u"SimpleExample.svg");
+
+ CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+ aGraphic_100.makeAvailable();
+ aGraphic_200.makeAvailable();
+ aGraphic_300.makeAvailable();
+}
+
+CPPUNIT_TEST_FIXTURE(GraphicMemoryTest, testMemoryManager)
+{
+ auto& rManager = vcl::graphic::MemoryManager::get();
+ CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+ Graphic aGraphic;
+ CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+ Graphic aGraphicPng = loadGraphic(u"TypeDetectionExample.png");
+ {
+ CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap, aGraphicPng.GetType());
+ CPPUNIT_ASSERT_EQUAL(sal_uLong(300), aGraphicPng.GetSizeBytes());
+ CPPUNIT_ASSERT_EQUAL(true, aGraphicPng.isAvailable());
+ }
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(300), rManager.getTotalSize());
+
+ CPPUNIT_ASSERT_EQUAL(false, aGraphicPng.ImplGetImpGraphic()->isSwappedOut());
+ CPPUNIT_ASSERT_EQUAL(true, aGraphicPng.ImplGetImpGraphic()->swapOut());
+ CPPUNIT_ASSERT_EQUAL(true, aGraphicPng.ImplGetImpGraphic()->isSwappedOut());
+ CPPUNIT_ASSERT_EQUAL(false, aGraphicPng.isAvailable());
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(0), rManager.getTotalSize());
+
+ Graphic aGraphicJpg = makeUnloadedGraphic(u"jpg");
+ {
+ CPPUNIT_ASSERT_EQUAL(tools::Long(120), aGraphicJpg.GetSizePixel().Width());
+ CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphicJpg.GetSizePixel().Height());
+ CPPUNIT_ASSERT_EQUAL(false, aGraphicJpg.isAvailable());
+ }
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(0), rManager.getTotalSize());
+
+ aGraphicJpg.makeAvailable();
+
+ CPPUNIT_ASSERT_EQUAL(size_t(2), rManager.getManagedObjects().size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(36000), rManager.getTotalSize());
+
+ CPPUNIT_ASSERT_EQUAL(true, aGraphicPng.makeAvailable());
+ CPPUNIT_ASSERT_EQUAL(false, aGraphicPng.ImplGetImpGraphic()->isSwappedOut());
+
+ CPPUNIT_ASSERT_EQUAL(size_t(2), rManager.getManagedObjects().size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(36300), rManager.getTotalSize());
+
+ aGraphicJpg = aGraphic;
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(300), rManager.getTotalSize());
+}
+
+namespace
+{
+class TestManaged : public vcl::graphic::MemoryManaged
+{
+public:
+ std::chrono::high_resolution_clock::time_point maLastUsed
+ = std::chrono::high_resolution_clock::now();
+
+ TestManaged(bool bRegister, sal_Int64 nSize = 0)
+ : MemoryManaged(bRegister)
+ {
+ updateCurrentSizeInBytes(nSize);
+ }
+
+ void setCurrentSize(sal_Int64 nSize) { updateCurrentSizeInBytes(nSize); }
+
+ void callRegister() { registerIntoManager(); }
+
+ void callUnregister() { unregisterFromManager(); }
+
+ bool canReduceMemory() const override { return false; }
+
+ bool reduceMemory() override { return false; }
+
+ std::chrono::high_resolution_clock::time_point getLastUsed() const override
+ {
+ return maLastUsed;
+ }
+
+ void dumpState(rtl::OStringBuffer& /*rState*/) override {}
+};
+}
+
+CPPUNIT_TEST_FIXTURE(GraphicMemoryTest, testMemoryManagerX)
+{
+ auto& rManager = vcl::graphic::MemoryManager::get();
+ CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+ TestManaged aTestManaged(false, 1000);
+ CPPUNIT_ASSERT_EQUAL(size_t(0), rManager.getManagedObjects().size());
+
+ TestManaged aTestManaged2(true, 100);
+ CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(100), rManager.getTotalSize());
+
+ aTestManaged2.setCurrentSize(400);
+ CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(400), rManager.getTotalSize());
+
+ aTestManaged.setCurrentSize(600);
+ CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(400), rManager.getTotalSize());
+
+ aTestManaged.callRegister();
+ CPPUNIT_ASSERT_EQUAL(size_t(2), rManager.getManagedObjects().size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(1000), rManager.getTotalSize());
+
+ aTestManaged.callUnregister();
+ CPPUNIT_ASSERT_EQUAL(size_t(1), rManager.getManagedObjects().size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(400), rManager.getTotalSize());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/app/svapp.cxx b/vcl/source/app/svapp.cxx
index e0995e0793e2..3b150e1bd956 100644
--- a/vcl/source/app/svapp.cxx
+++ b/vcl/source/app/svapp.cxx
@@ -1775,7 +1775,7 @@ void dumpState(rtl::OStringBuffer &rState)
pWin = Application::GetNextTopLevelWindow( pWin );
}
- vcl::graphic::Manager::get().dumpState(rState);
+ vcl::graphic::MemoryManager::get().dumpState(rState);
pSVData->dumpState(rState);
@@ -1792,7 +1792,7 @@ void trimMemory(int nTarget)
if (!pSVData) // shutting down
return;
pSVData->dropCaches();
- vcl::graphic::Manager::get().dropCache();
+ vcl::graphic::MemoryManager::get().reduceAllAndNow();
// TODO: ideally - free up any deeper dirtied thread stacks.
// comphelper::ThreadPool::getSharedOptimalPool().shutdown();
}
diff --git a/vcl/source/gdi/graph.cxx b/vcl/source/gdi/graph.cxx
index 252ee189b892..2c3e05235225 100644
--- a/vcl/source/gdi/graph.cxx
+++ b/vcl/source/gdi/graph.cxx
@@ -159,14 +159,14 @@ void ImplDrawDefault(OutputDevice& rOutDev, const OUString* pText,
} // end anonymous namespace
Graphic::Graphic()
- : mxImpGraphic(vcl::graphic::Manager::get().newInstance())
+ : mxImpGraphic(new ImpGraphic())
{
}
Graphic::Graphic(const Graphic& rGraphic)
{
if( rGraphic.IsAnimated() )
- mxImpGraphic = vcl::graphic::Manager::get().copy(rGraphic.mxImpGraphic);
+ mxImpGraphic = std::make_shared<ImpGraphic>(*rGraphic.mxImpGraphic);
else
mxImpGraphic = rGraphic.mxImpGraphic;
}
@@ -177,17 +177,17 @@ Graphic::Graphic(Graphic&& rGraphic) noexcept
}
Graphic::Graphic(std::shared_ptr<GfxLink> const & rGfxLink, sal_Int32 nPageIndex)
- : mxImpGraphic(vcl::graphic::Manager::get().newInstance(rGfxLink, nPageIndex))
+ : mxImpGraphic(new ImpGraphic(rGfxLink, nPageIndex))
{
}
Graphic::Graphic(GraphicExternalLink const & rGraphicExternalLink)
- : mxImpGraphic(vcl::graphic::Manager::get().newInstance(rGraphicExternalLink))
+ : mxImpGraphic(new ImpGraphic(rGraphicExternalLink))
{
}
-Graphic::Graphic(const BitmapEx& rBmpEx)
- : mxImpGraphic(vcl::graphic::Manager::get().newInstance(rBmpEx))
+Graphic::Graphic(const BitmapEx& rBitmapEx)
+ : mxImpGraphic(new ImpGraphic(rBitmapEx))
{
}
@@ -195,7 +195,7 @@ Graphic::Graphic(const BitmapEx& rBmpEx)
// and we need to be able to see and preserve 'stock' images too.
Graphic::Graphic(const Image& rImage)
// FIXME: should really defer the BitmapEx load.
- : mxImpGraphic(std::make_shared<ImpGraphic>(rImage.GetBitmapEx()))
+ : mxImpGraphic(new ImpGraphic(rImage.GetBitmapEx()))
{
OUString aStock = rImage.GetStock();
if (aStock.getLength())
@@ -203,17 +203,17 @@ Graphic::Graphic(const Image& rImage)
}
Graphic::Graphic(const std::shared_ptr<VectorGraphicData>& rVectorGraphicDataPtr)
- : mxImpGraphic(vcl::graphic::Manager::get().newInstance(rVectorGraphicDataPtr))
+ : mxImpGraphic(new ImpGraphic(rVectorGraphicDataPtr))
{
}
Graphic::Graphic(const Animation& rAnimation)
- : mxImpGraphic(vcl::graphic::Manager::get().newInstance(rAnimation))
+ : mxImpGraphic(new ImpGraphic(rAnimation))
{
}
-Graphic::Graphic(const GDIMetaFile& rMtf)
- : mxImpGraphic(vcl::graphic::Manager::get().newInstance(rMtf))
+Graphic::Graphic(const GDIMetaFile& rMetaFile)
+ : mxImpGraphic(new ImpGraphic(rMetaFile))
{
}
@@ -225,19 +225,19 @@ Graphic::Graphic( const css::uno::Reference< css::graphic::XGraphic >& rxGraphic
if( pGraphic )
{
if (pGraphic->IsAnimated())
- mxImpGraphic = vcl::graphic::Manager::get().copy(pGraphic->mxImpGraphic);
+ mxImpGraphic = std::make_shared<ImpGraphic>(*pGraphic->mxImpGraphic);
else
mxImpGraphic = pGraphic->mxImpGraphic;
}
else
- mxImpGraphic = vcl::graphic::Manager::get().newInstance();
+ mxImpGraphic = std::make_shared<ImpGraphic>();
}
void Graphic::ImplTestRefCount()
{
if (mxImpGraphic.use_count() > 1)
{
- mxImpGraphic = vcl::graphic::Manager::get().copy(mxImpGraphic);
+ mxImpGraphic = std::make_shared<ImpGraphic>(*mxImpGraphic);
}
}
@@ -256,7 +256,7 @@ Graphic& Graphic::operator=( const Graphic& rGraphic )
if( &rGraphic != this )
{
if( rGraphic.IsAnimated() )
- mxImpGraphic = vcl::graphic::Manager::get().copy(rGraphic.mxImpGraphic);
+ mxImpGraphic = std::make_shared<ImpGraphic>(*rGraphic.mxImpGraphic);
else
mxImpGraphic = rGraphic.mxImpGraphic;
}
diff --git a/vcl/source/gdi/impgraph.cxx b/vcl/source/gdi/impgraph.cxx
index ceb6c111cf7b..42ea57c1a820 100644
--- a/vcl/source/gdi/impgraph.cxx
+++ b/vcl/source/gdi/impgraph.cxx
@@ -82,12 +82,14 @@ SvStream* ImpGraphic::getSwapFileStream() const
}
ImpGraphic::ImpGraphic(bool bDefault)
- : meType(bDefault ? GraphicType::Default : GraphicType::NONE)
+ : MemoryManaged(false)
+ , meType(bDefault ? GraphicType::Default : GraphicType::NONE)
{
}
ImpGraphic::ImpGraphic(const ImpGraphic& rImpGraphic)
- : maMetaFile(rImpGraphic.maMetaFile)
+ : MemoryManaged(rImpGraphic)
+ , maMetaFile(rImpGraphic.maMetaFile)
, maBitmapEx(rImpGraphic.maBitmapEx)
, maSwapInfo(rImpGraphic.maSwapInfo)
, mpContext(rImpGraphic.mpContext)
@@ -101,6 +103,8 @@ ImpGraphic::ImpGraphic(const ImpGraphic& rImpGraphic)
, maGraphicExternalLink(rImpGraphic.maGraphicExternalLink)
, mbPrepared(rImpGraphic.mbPrepared)
{
+ updateCurrentSizeInBytes(mnSizeBytes);
+
// Special case for animations
if (rImpGraphic.mpAnimation)
{
@@ -110,7 +114,8 @@ ImpGraphic::ImpGraphic(const ImpGraphic& rImpGraphic)
}
ImpGraphic::ImpGraphic(ImpGraphic&& rImpGraphic) noexcept
- : maMetaFile(std::move(rImpGraphic.maMetaFile))
+ : MemoryManaged(rImpGraphic)
+ , maMetaFile(std::move(rImpGraphic.maMetaFile))
, maBitmapEx(std::move(rImpGraphic.maBitmapEx))
, maSwapInfo(std::move(rImpGraphic.maSwapInfo))
, mpAnimation(std::move(rImpGraphic.mpAnimation))
@@ -125,12 +130,15 @@ ImpGraphic::ImpGraphic(ImpGraphic&& rImpGraphic) noexcept
, maGraphicExternalLink(rImpGraphic.maGraphicExternalLink)
, mbPrepared (rImpGraphic.mbPrepared)
{
+ updateCurrentSizeInBytes(mnSizeBytes);
+
rImpGraphic.clear();
rImpGraphic.mbDummyContext = false;
}
ImpGraphic::ImpGraphic(std::shared_ptr<GfxLink> xGfxLink, sal_Int32 nPageIndex)
- : mpGfxLink(std::move(xGfxLink))
+ : MemoryManaged(true)
+ , mpGfxLink(std::move(xGfxLink))
, meType(GraphicType::Bitmap)
, mbSwapOut(true)
{
@@ -140,53 +148,63 @@ ImpGraphic::ImpGraphic(std::shared_ptr<GfxLink> xGfxLink, sal_Int32 nPageIndex)
maSwapInfo.mbIsAnimated = false;
maSwapInfo.mnAnimationLoopCount = 0;
maSwapInfo.mnPageIndex = nPageIndex;
+
+ ensureCurrentSizeInBytes();
}
ImpGraphic::ImpGraphic(GraphicExternalLink aGraphicExternalLink)
- : meType(GraphicType::Default)
+ : MemoryManaged(true)
+ , meType(GraphicType::Default)
, maGraphicExternalLink(std::move(aGraphicExternalLink))
{
+ ensureCurrentSizeInBytes();
}
ImpGraphic::ImpGraphic(const BitmapEx& rBitmapEx)
- : maBitmapEx(rBitmapEx)
+ : MemoryManaged(!rBitmapEx.IsEmpty())
+ , maBitmapEx(rBitmapEx)
, meType(rBitmapEx.IsEmpty() ? GraphicType::NONE : GraphicType::Bitmap)
{
+ ensureCurrentSizeInBytes();
}
ImpGraphic::ImpGraphic(const std::shared_ptr<VectorGraphicData>& rVectorGraphicDataPtr)
- : maVectorGraphicData(rVectorGraphicDataPtr)
+ : MemoryManaged(bool(rVectorGraphicDataPtr))
+ , maVectorGraphicData(rVectorGraphicDataPtr)
, meType(rVectorGraphicDataPtr ? GraphicType::Bitmap : GraphicType::NONE)
{
+ ensureCurrentSizeInBytes();
}
ImpGraphic::ImpGraphic(const Animation& rAnimation)
- : maBitmapEx(rAnimation.GetBitmapEx())
+ : MemoryManaged(true)
+ , maBitmapEx(rAnimation.GetBitmapEx())
, mpAnimation(std::make_unique<Animation>(rAnimation))
, meType(GraphicType::Bitmap)
{
+ ensureCurrentSizeInBytes();
}
ImpGraphic::ImpGraphic(const GDIMetaFile& rMetafile)
- : maMetaFile(rMetafile)
+ : MemoryManaged(true)
+ , maMetaFile(rMetafile)
, meType(GraphicType::GdiMetafile)
{
+ ensureCurrentSizeInBytes();
}
ImpGraphic::~ImpGraphic()
{
- vcl::graphic::Manager::get().unregisterGraphic(this);
}
ImpGraphic& ImpGraphic::operator=( const ImpGraphic& rImpGraphic )
{
if( &rImpGraphic != this )
{
- sal_Int64 aOldSizeBytes = mnSizeBytes;
-
maMetaFile = rImpGraphic.maMetaFile;
meType = rImpGraphic.meType;
mnSizeBytes = rImpGraphic.mnSizeBytes;
+ updateCurrentSizeInBytes(mnSizeBytes);
maSwapInfo = rImpGraphic.maSwapInfo;
mpContext = rImpGraphic.mpContext;
@@ -214,7 +232,7 @@ ImpGraphic& ImpGraphic::operator=( const ImpGraphic& rImpGraphic )
maVectorGraphicData = rImpGraphic.maVectorGraphicData;
maLastUsed = std::chrono::high_resolution_clock::now();
- vcl::graphic::Manager::get().changeExisting(this, aOldSizeBytes);
+ changeExisting(mnSizeBytes);
}
return *this;
@@ -222,8 +240,6 @@ ImpGraphic& ImpGraphic::operator=( const ImpGraphic& rImpGraphic )
ImpGraphic& ImpGraphic::operator=(ImpGraphic&& rImpGraphic)
{
- sal_Int64 aOldSizeBytes = mnSizeBytes;
-
maMetaFile = std::move(rImpGraphic.maMetaFile);
meType = rImpGraphic.meType;
mnSizeBytes = rImpGraphic.mnSizeBytes;
@@ -243,7 +259,7 @@ ImpGraphic& ImpGraphic::operator=(ImpGraphic&& rImpGraphic)
rImpGraphic.mbDummyContext = false;
maLastUsed = std::chrono::high_resolution_clock::now();
- vcl::graphic::Manager::get().changeExisting(this, aOldSizeBytes);
+ changeExisting(mnSizeBytes);
return *this;
}
@@ -403,9 +419,9 @@ void ImpGraphic::clear()
// cleanup
clearGraphics();
meType = GraphicType::NONE;
- sal_Int64 nOldSize = mnSizeBytes;
mnSizeBytes = 0;
- vcl::graphic::Manager::get().changeExisting(this, nOldSize);
+
+ changeExisting(mnSizeBytes);
maGraphicExternalLink.msURL.clear();
}
@@ -925,6 +941,14 @@ void ImpGraphic::setPrefMapMode(const MapMode& rPrefMapMode)
setValuesForPrefMapMod(rPrefMapMode);
}
+void ImpGraphic::ensureCurrentSizeInBytes()
+{
+ if (isAvailable())
+ changeExisting(getSizeBytes());
+ else
+ changeExisting(0);
+}
+
sal_uLong ImpGraphic::getSizeBytes() const
{
if (mnSizeBytes > 0)
@@ -1266,8 +1290,6 @@ bool ImpGraphic::swapOut()
bool bResult = false;
- sal_Int64 nByteSize = getSizeBytes();
-
// We have GfxLink so we have the source available
if (mpGfxLink && mpGfxLink->IsNative())
{
@@ -1324,7 +1346,7 @@ bool ImpGraphic::swapOut()
if (bResult)
{
// Signal to manager that we have swapped out
- vcl::graphic::Manager::get().swappedOut(this, nByteSize);
+ swappedOut(0);
}
return bResult;
@@ -1332,14 +1354,17 @@ bool ImpGraphic::swapOut()
bool ImpGraphic::ensureAvailable() const
{
- auto pThis = const_cast<ImpGraphic*>(this);
-
bool bResult = true;
if (isSwappedOut())
+ {
+ auto pThis = const_cast<ImpGraphic*>(this);
+ pThis->registerIntoManager();
+
bResult = pThis->swapIn();
+ }
- pThis->maLastUsed = std::chrono::high_resolution_clock::now();
+ maLastUsed = std::chrono::high_resolution_clock::now();
return bResult;
}
@@ -1526,7 +1551,7 @@ bool ImpGraphic::swapIn()
if (bReturn)
{
- vcl::graphic::Manager::get().swappedIn(this, getSizeBytes());
+ swappedIn(getSizeBytes());
}
return bReturn;
@@ -1731,4 +1756,22 @@ sal_Int32 ImpGraphic::getPageNumber() const
return maVectorGraphicData->getPageIndex();
return -1;
}
+
+bool ImpGraphic::canReduceMemory() const
+{
+ if (mpContext)
+ return false;
+ return !isSwappedOut();
+}
+
+bool ImpGraphic::reduceMemory()
+{
+ return swapOut();
+}
+
+std::chrono::high_resolution_clock::time_point ImpGraphic::getLastUsed() const
+{
+ return maLastUsed;
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/Manager.cxx b/vcl/source/graphic/Manager.cxx
index 4bb4ce79534b..fb316aef153d 100644
--- a/vcl/source/graphic/Manager.cxx
+++ b/vcl/source/graphic/Manager.cxx
@@ -26,8 +26,6 @@
using namespace css;
-namespace vcl::graphic
-{
namespace
{
void setupConfigurationValuesIfPossible(sal_Int64& rMemoryLimit,
@@ -51,267 +49,147 @@ void setupConfigurationValuesIfPossible(sal_Int64& rMemoryLimit,
}
}
-Manager& Manager::get()
+namespace vcl::graphic
{
- static Manager gStaticManager;
- return gStaticManager;
-}
-
-Manager::Manager()
- : mnAllowedIdleTime(10)
- , mbSwapEnabled(true)
- , mbReducingGraphicMemory(false)
- , mnMemoryLimit(300000000)
- , mnUsedSize(0)
- , maSwapOutTimer("graphic::Manager maSwapOutTimer")
+MemoryManager::MemoryManager()
+ : maSwapOutTimer("MemoryManager::MemoryManager maSwapOutTimer")
{
setupConfigurationValuesIfPossible(mnMemoryLimit, mnAllowedIdleTime, mbSwapEnabled);
if (mbSwapEnabled)
{
- maSwapOutTimer.SetInvokeHandler(LINK(this, Manager, SwapOutTimerHandler));
- maSwapOutTimer.SetTimeout(10000);
+ maSwapOutTimer.SetInvokeHandler(LINK(this, MemoryManager, ReduceMemoryTimerHandler));
+ maSwapOutTimer.SetTimeout(mnTimeout);
maSwapOutTimer.Start();
}
}
-void Manager::loopGraphicsAndSwapOut(std::unique_lock<std::mutex>& rGuard, bool bDropAll)
+MemoryManager& MemoryManager::get()
{
- // 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
- // m_pImpGraphicList invalidating a loop over m_pImpGraphicList, e.g.
- // reexport of tdf118346-1.odg
- o3tl::sorted_vector<ImpGraphic*> aImpGraphicList = m_pImpGraphicList;
-
- for (ImpGraphic* pEachImpGraphic : aImpGraphicList)
- {
- if (mnUsedSize < sal_Int64(mnMemoryLimit * 0.7) && !bDropAll)
- return;
-
- if (pEachImpGraphic->isSwappedOut())
- continue;
-
- sal_Int64 nCurrentGraphicSize = getGraphicSizeBytes(pEachImpGraphic);
- if (nCurrentGraphicSize > 100000 || bDropAll)
- {
- if (!pEachImpGraphic->mpContext)
- {
- auto aCurrent = std::chrono::high_resolution_clock::now();
- auto aDeltaTime = aCurrent - pEachImpGraphic->maLastUsed;
- auto aSeconds = std::chrono::duration_cast<std::chrono::seconds>(aDeltaTime);
-
- if (aSeconds > mnAllowedIdleTime)
- {
- // unlock because svgio can call back into us
- rGuard.unlock();
- pEachImpGraphic->swapOut();
- rGuard.lock();
- }
- }
- }
- }
+ static MemoryManager gStaticManager;
+ return gStaticManager;
}
-void Manager::reduceGraphicMemory(std::unique_lock<std::mutex>& rGuard, bool bDropAll)
+IMPL_LINK(MemoryManager, ReduceMemoryTimerHandler, Timer*, pTimer, void)
{
- // maMutex is locked in callers
-
- if (!mbSwapEnabled)
- return;
-
- if (mnUsedSize < mnMemoryLimit && !bDropAll)
- return;
-
- // avoid recursive reduceGraphicMemory on reexport of tdf118346-1.odg to odg
- if (mbReducingGraphicMemory)
- return;
-
- mbReducingGraphicMemory = true;
-
- loopGraphicsAndSwapOut(rGuard, bDropAll);
-
- sal_Int64 calculatedSize = 0;
- for (ImpGraphic* pEachImpGraphic : m_pImpGraphicList)
- {
- if (!pEachImpGraphic->isSwappedOut())
- {
- calculatedSize += getGraphicSizeBytes(pEachImpGraphic);
- }
- }
-
- if (calculatedSize != mnUsedSize)
- {
- assert(rGuard.owns_lock() && rGuard.mutex() == &maMutex);
- // coverity[missing_lock: FALSE] - as above assert
- mnUsedSize = calculatedSize;
- }
-
- mbReducingGraphicMemory = false;
+ std::unique_lock aGuard(maMutex);
+ pTimer->Stop();
+ reduceMemory(aGuard);
+ pTimer->Start();
}
-void Manager::dropCache()
+void MemoryManager::registerObject(MemoryManaged* pMemoryManaged)
{
std::unique_lock aGuard(maMutex);
- reduceGraphicMemory(aGuard, true);
+ // Insert and update the used size (bytes)
+ assert(aGuard.owns_lock() && aGuard.mutex() == &maMutex);
+ // coverity[missing_lock: FALSE] - as above assert
+ mnTotalSize += pMemoryManaged->getCurrentSizeInBytes();
+ maObjectList.insert(pMemoryManaged);
}
-void Manager::dumpState(rtl::OStringBuffer& rState)
+void MemoryManager::unregisterObject(MemoryManaged* pMemoryManaged)
{
std::unique_lock aGuard(maMutex);
+ mnTotalSize -= pMemoryManaged->getCurrentSizeInBytes();
+ maObjectList.erase(pMemoryManaged);
+}
- 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");
+void MemoryManager::changeExisting(MemoryManaged* pMemoryManaged, sal_Int64 nNewSize)
+{
+ std::scoped_lock aGuard(maMutex);
+ sal_Int64 nOldSize = pMemoryManaged->getCurrentSizeInBytes();
+ mnTotalSize -= nOldSize;
+ mnTotalSize += nNewSize;
+ pMemoryManaged->setCurrentSizeInBytes(nNewSize);
+}
- for (ImpGraphic* pEachImpGraphic : m_pImpGraphicList)
- {
- pEachImpGraphic->dumpState(rState);
- }
+void MemoryManager::swappedIn(MemoryManaged* pMemoryManaged, sal_Int64 nNewSize)
+{
+ changeExisting(pMemoryManaged, nNewSize);
}
-sal_Int64 Manager::getGraphicSizeBytes(const ImpGraphic* pImpGraphic)
+void MemoryManager::swappedOut(MemoryManaged* pMemoryManaged, sal_Int64 nNewSize)
{
- if (!pImpGraphic->isAvailable())
- return 0;
- return pImpGraphic->getSizeBytes();
+ changeExisting(pMemoryManaged, nNewSize);
}
-IMPL_LINK(Manager, SwapOutTimerHandler, Timer*, pTimer, void)
+void MemoryManager::reduceAllAndNow()
{
std::unique_lock aGuard(maMutex);
-
- pTimer->Stop();
- reduceGraphicMemory(aGuard);
- pTimer->Start();
+ reduceMemory(aGuard, true);
}
-void Manager::registerGraphic(const std::shared_ptr<ImpGraphic>& pImpGraphic)
+void MemoryManager::dumpState(rtl::OStringBuffer& rState)
{
std::unique_lock aGuard(maMutex);
- // make some space first
- if (mnUsedSize > mnMemoryLimit)
- reduceGraphicMemory(aGuard);
-
- // Insert and update the used size (bytes)
- assert(aGuard.owns_lock() && aGuard.mutex() == &maMutex);
- // coverity[missing_lock: FALSE] - as above assert
- mnUsedSize += getGraphicSizeBytes(pImpGraphic.get());
- m_pImpGraphicList.insert(pImpGraphic.get());
-
- // calculate size of the graphic set
- sal_Int64 calculatedSize = 0;
- for (ImpGraphic* pEachImpGraphic : m_pImpGraphicList)
- {
- if (!pEachImpGraphic->isSwappedOut())
- {
- calculatedSize += getGraphicSizeBytes(pEachImpGraphic);
- }
- }
+ rState.append("\nMemory Manager items:\t");
+ rState.append(static_cast<sal_Int32>(maObjectList.size()));
+ rState.append("\tsize:\t");
+ rState.append(static_cast<sal_Int64>(mnTotalSize / 1024));
+ rState.append("\tkb");
- if (calculatedSize != mnUsedSize)
+ for (MemoryManaged* pMemoryManaged : maObjectList)
{
- SAL_INFO_IF(calculatedSize != mnUsedSize, "vcl.gdi",
- "Calculated size mismatch. Variable size is '"
- << mnUsedSize << "' but calculated size is '" << calculatedSize << "'");
- mnUsedSize = calculatedSize;
+ pMemoryManaged->dumpState(rState);
}
}
-void Manager::unregisterGraphic(ImpGraphic* pImpGraphic)
+void MemoryManager::reduceMemory(std::unique_lock<std::mutex>& rGuard, bool bDropAll)
{
- std::scoped_lock aGuard(maMutex);
-
- mnUsedSize -= getGraphicSizeBytes(pImpGraphic);
- m_pImpGraphicList.erase(pImpGraphic);
-}
+ // maMutex is locked in callers
-std::shared_ptr<ImpGraphic> Manager::copy(std::shared_ptr<ImpGraphic> const& rImpGraphicPtr)
-{
- auto pReturn = std::make_shared<ImpGraphic>(*rImpGraphicPtr);
- registerGraphic(pReturn);
- return pReturn;
-}
+ if (!mbSwapEnabled)
+ return;
-std::shared_ptr<ImpGraphic> Manager::newInstance()
-{
- auto pReturn = std::make_shared<ImpGraphic>();
- registerGraphic(pReturn);
- return pReturn;
-}
+ if (mnTotalSize < mnMemoryLimit && !bDropAll)
+ return;
-std::shared_ptr<ImpGraphic> Manager::newInstance(std::shared_ptr<GfxLink> const& rGfxLink,
- sal_Int32 nPageIndex)
-{
- auto pReturn = std::make_shared<ImpGraphic>(rGfxLink, nPageIndex);
- registerGraphic(pReturn);
- return pReturn;
-}
+ // avoid recursive reduceGraphicMemory on reexport of tdf118346-1.odg to odg
+ if (mbReducingGraphicMemory)
+ return;
-std::shared_ptr<ImpGraphic> Manager::newInstance(const BitmapEx& rBitmapEx)
-{
- auto pReturn = std::make_shared<ImpGraphic>(rBitmapEx);
- registerGraphic(pReturn);
- return pReturn;
-}
+ mbReducingGraphicMemory = true;
-std::shared_ptr<ImpGraphic> Manager::newInstance(const Animation& rAnimation)
-{
- auto pReturn = std::make_shared<ImpGraphic>(rAnimation);
- registerGraphic(pReturn);
- return pReturn;
-}
+ loopAndReduceMemory(rGuard, bDropAll);
-std::shared_ptr<ImpGraphic>
-Manager::newInstance(const std::shared_ptr<VectorGraphicData>& rVectorGraphicDataPtr)
-{
- auto pReturn = std::make_shared<ImpGraphic>(rVectorGraphicDataPtr);
- registerGraphic(pReturn);
- return pReturn;
+ mbReducingGraphicMemory = false;
}
-std::shared_ptr<ImpGraphic> Manager::newInstance(const GDIMetaFile& rMetaFile)
+void MemoryManager::loopAndReduceMemory(std::unique_lock<std::mutex>& rGuard, bool bDropAll)
{
- auto pReturn = std::make_shared<ImpGraphic>(rMetaFile);
- registerGraphic(pReturn);
- return pReturn;
-}
+ // 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
+ // m_pImpGraphicList invalidating a loop over m_pImpGraphicList, e.g.
+ // reexport of tdf118346-1.odg
-std::shared_ptr<ImpGraphic> Manager::newInstance(const GraphicExternalLink& rGraphicLink)
-{
- auto pReturn = std::make_shared<ImpGraphic>(rGraphicLink);
- registerGraphic(pReturn);
- return pReturn;
-}
+ o3tl::sorted_vector<MemoryManaged*> aObjectListCopy = maObjectList;
-void Manager::swappedIn(const ImpGraphic* pImpGraphic, sal_Int64 nSizeBytes)
-{
- std::scoped_lock aGuard(maMutex);
- if (pImpGraphic)
+ for (MemoryManaged* pMemoryManaged : aObjectListCopy)
{
- mnUsedSize += nSizeBytes;
- }
-}
+ if (!pMemoryManaged->canReduceMemory())
+ continue;
-void Manager::swappedOut(const ImpGraphic* pImpGraphic, sal_Int64 nSizeBytes)
-{
- std::scoped_lock aGuard(maMutex);
- if (pImpGraphic)
- {
- mnUsedSize -= nSizeBytes;
+ sal_Int64 nCurrentSizeInBytes = pMemoryManaged->getCurrentSizeInBytes();
+ if (nCurrentSizeInBytes > mnSmallFrySize || bDropAll) // ignore small-fry
+ {
+ auto aCurrent = std::chrono::high_resolution_clock::now();
+ auto aDeltaTime = aCurrent - pMemoryManaged->getLastUsed();
+ auto aSeconds = std::chrono::duration_cast<std::chrono::seconds>(aDeltaTime);
+
+ if (aSeconds > mnAllowedIdleTime)
+ {
+ // unlock because svgio can call back into us
+ rGuard.unlock();
+ pMemoryManaged->reduceMemory();
+ rGuard.lock();
+ }
+ }
}
}
-void Manager::changeExisting(const ImpGraphic* pImpGraphic, sal_Int64 nOldSizeBytes)
-{
- std::scoped_lock aGuard(maMutex);
-
- mnUsedSize -= nOldSizeBytes;
- mnUsedSize += getGraphicSizeBytes(pImpGraphic);
-}
} // end vcl::graphic
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */