1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <graphic/Manager.hxx>
21 #include <impgraph.hxx>
22 #include <sal/log.hxx>
24 #include <officecfg/Office/Common.hxx>
25 #include <unotools/configmgr.hxx>
29 namespace vcl::graphic
33 void setupConfigurationValuesIfPossible(sal_Int64
& rMemoryLimit
,
34 std::chrono::seconds
& rAllowedIdleTime
, bool& bSwapEnabled
)
36 if (utl::ConfigManager::IsFuzzing())
41 using officecfg::Office::Common::Cache
;
43 rMemoryLimit
= Cache::GraphicManager::GraphicMemoryLimit::get();
45 = std::chrono::seconds(Cache::GraphicManager::GraphicAllowedIdleTime::get());
46 bSwapEnabled
= Cache::GraphicManager::GraphicSwappingEnabled::get();
54 Manager
& Manager::get()
56 static Manager gStaticManager
;
57 return gStaticManager
;
61 : mnAllowedIdleTime(10)
63 , mbReducingGraphicMemory(false)
64 , mnMemoryLimit(300000000)
66 , maSwapOutTimer("graphic::Manager maSwapOutTimer")
68 setupConfigurationValuesIfPossible(mnMemoryLimit
, mnAllowedIdleTime
, mbSwapEnabled
);
72 maSwapOutTimer
.SetInvokeHandler(LINK(this, Manager
, SwapOutTimerHandler
));
73 maSwapOutTimer
.SetTimeout(10000);
74 maSwapOutTimer
.Start();
78 void Manager::loopGraphicsAndSwapOut(std::unique_lock
<std::mutex
>& rGuard
, bool bDropAll
)
80 // make a copy of m_pImpGraphicList because if we swap out a svg, the svg
81 // filter may create more temp Graphics which are auto-added to
82 // m_pImpGraphicList invalidating a loop over m_pImpGraphicList, e.g.
83 // reexport of tdf118346-1.odg
84 o3tl::sorted_vector
<ImpGraphic
*> aImpGraphicList
= m_pImpGraphicList
;
86 for (ImpGraphic
* pEachImpGraphic
: aImpGraphicList
)
88 if (mnUsedSize
< sal_Int64(mnMemoryLimit
* 0.7) && !bDropAll
)
91 if (pEachImpGraphic
->isSwappedOut())
94 sal_Int64 nCurrentGraphicSize
= getGraphicSizeBytes(pEachImpGraphic
);
95 if (nCurrentGraphicSize
> 100000 || bDropAll
)
97 if (!pEachImpGraphic
->mpContext
)
99 auto aCurrent
= std::chrono::high_resolution_clock::now();
100 auto aDeltaTime
= aCurrent
- pEachImpGraphic
->maLastUsed
;
101 auto aSeconds
= std::chrono::duration_cast
<std::chrono::seconds
>(aDeltaTime
);
103 if (aSeconds
> mnAllowedIdleTime
)
105 // unlock because svgio can call back into us
107 pEachImpGraphic
->swapOut();
115 void Manager::reduceGraphicMemory(std::unique_lock
<std::mutex
>& rGuard
, bool bDropAll
)
117 // maMutex is locked in callers
122 if (mnUsedSize
< mnMemoryLimit
&& !bDropAll
)
125 // avoid recursive reduceGraphicMemory on reexport of tdf118346-1.odg to odg
126 if (mbReducingGraphicMemory
)
129 mbReducingGraphicMemory
= true;
131 loopGraphicsAndSwapOut(rGuard
, bDropAll
);
133 sal_Int64 calculatedSize
= 0;
134 for (ImpGraphic
* pEachImpGraphic
: m_pImpGraphicList
)
136 if (!pEachImpGraphic
->isSwappedOut())
138 calculatedSize
+= getGraphicSizeBytes(pEachImpGraphic
);
142 if (calculatedSize
!= mnUsedSize
)
144 assert(rGuard
.owns_lock() && rGuard
.mutex() == &maMutex
);
145 // coverity[missing_lock: FALSE] - as above assert
146 mnUsedSize
= calculatedSize
;
149 mbReducingGraphicMemory
= false;
152 void Manager::dropCache()
154 std::unique_lock
aGuard(maMutex
);
156 reduceGraphicMemory(aGuard
, true);
159 void Manager::dumpState(rtl::OStringBuffer
& rState
)
161 std::unique_lock
aGuard(maMutex
);
163 rState
.append("\nImage Manager items:\t");
164 rState
.append(static_cast<sal_Int32
>(m_pImpGraphicList
.size()));
165 rState
.append("\tsize:\t");
166 rState
.append(static_cast<sal_Int64
>(mnUsedSize
/ 1024));
167 rState
.append("\tkb");
169 for (ImpGraphic
* pEachImpGraphic
: m_pImpGraphicList
)
171 pEachImpGraphic
->dumpState(rState
);
175 sal_Int64
Manager::getGraphicSizeBytes(const ImpGraphic
* pImpGraphic
)
177 if (!pImpGraphic
->isAvailable())
179 return pImpGraphic
->getSizeBytes();
182 IMPL_LINK(Manager
, SwapOutTimerHandler
, Timer
*, pTimer
, void)
184 std::unique_lock
aGuard(maMutex
);
187 reduceGraphicMemory(aGuard
);
191 void Manager::registerGraphic(const std::shared_ptr
<ImpGraphic
>& pImpGraphic
)
193 std::unique_lock
aGuard(maMutex
);
195 // make some space first
196 if (mnUsedSize
> mnMemoryLimit
)
197 reduceGraphicMemory(aGuard
);
199 // Insert and update the used size (bytes)
200 assert(aGuard
.owns_lock() && aGuard
.mutex() == &maMutex
);
201 // coverity[missing_lock: FALSE] - as above assert
202 mnUsedSize
+= getGraphicSizeBytes(pImpGraphic
.get());
203 m_pImpGraphicList
.insert(pImpGraphic
.get());
205 // calculate size of the graphic set
206 sal_Int64 calculatedSize
= 0;
207 for (ImpGraphic
* pEachImpGraphic
: m_pImpGraphicList
)
209 if (!pEachImpGraphic
->isSwappedOut())
211 calculatedSize
+= getGraphicSizeBytes(pEachImpGraphic
);
215 if (calculatedSize
!= mnUsedSize
)
217 SAL_INFO_IF(calculatedSize
!= mnUsedSize
, "vcl.gdi",
218 "Calculated size mismatch. Variable size is '"
219 << mnUsedSize
<< "' but calculated size is '" << calculatedSize
<< "'");
220 mnUsedSize
= calculatedSize
;
224 void Manager::unregisterGraphic(ImpGraphic
* pImpGraphic
)
226 std::scoped_lock
aGuard(maMutex
);
228 mnUsedSize
-= getGraphicSizeBytes(pImpGraphic
);
229 m_pImpGraphicList
.erase(pImpGraphic
);
232 std::shared_ptr
<ImpGraphic
> Manager::copy(std::shared_ptr
<ImpGraphic
> const& rImpGraphicPtr
)
234 auto pReturn
= std::make_shared
<ImpGraphic
>(*rImpGraphicPtr
);
235 registerGraphic(pReturn
);
239 std::shared_ptr
<ImpGraphic
> Manager::newInstance()
241 auto pReturn
= std::make_shared
<ImpGraphic
>();
242 registerGraphic(pReturn
);
246 std::shared_ptr
<ImpGraphic
> Manager::newInstance(std::shared_ptr
<GfxLink
> const& rGfxLink
,
247 sal_Int32 nPageIndex
)
249 auto pReturn
= std::make_shared
<ImpGraphic
>(rGfxLink
, nPageIndex
);
250 registerGraphic(pReturn
);
254 std::shared_ptr
<ImpGraphic
> Manager::newInstance(const BitmapEx
& rBitmapEx
)
256 auto pReturn
= std::make_shared
<ImpGraphic
>(rBitmapEx
);
257 registerGraphic(pReturn
);
261 std::shared_ptr
<ImpGraphic
> Manager::newInstance(const Animation
& rAnimation
)
263 auto pReturn
= std::make_shared
<ImpGraphic
>(rAnimation
);
264 registerGraphic(pReturn
);
268 std::shared_ptr
<ImpGraphic
>
269 Manager::newInstance(const std::shared_ptr
<VectorGraphicData
>& rVectorGraphicDataPtr
)
271 auto pReturn
= std::make_shared
<ImpGraphic
>(rVectorGraphicDataPtr
);
272 registerGraphic(pReturn
);
276 std::shared_ptr
<ImpGraphic
> Manager::newInstance(const GDIMetaFile
& rMetaFile
)
278 auto pReturn
= std::make_shared
<ImpGraphic
>(rMetaFile
);
279 registerGraphic(pReturn
);
283 std::shared_ptr
<ImpGraphic
> Manager::newInstance(const GraphicExternalLink
& rGraphicLink
)
285 auto pReturn
= std::make_shared
<ImpGraphic
>(rGraphicLink
);
286 registerGraphic(pReturn
);
290 void Manager::swappedIn(const ImpGraphic
* pImpGraphic
, sal_Int64 nSizeBytes
)
292 std::scoped_lock
aGuard(maMutex
);
295 mnUsedSize
+= nSizeBytes
;
299 void Manager::swappedOut(const ImpGraphic
* pImpGraphic
, sal_Int64 nSizeBytes
)
301 std::scoped_lock
aGuard(maMutex
);
304 mnUsedSize
-= nSizeBytes
;
308 void Manager::changeExisting(const ImpGraphic
* pImpGraphic
, sal_Int64 nOldSizeBytes
)
310 std::scoped_lock
aGuard(maMutex
);
312 mnUsedSize
-= nOldSizeBytes
;
313 mnUsedSize
+= getGraphicSizeBytes(pImpGraphic
);
315 } // end vcl::graphic
317 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */