vcl: improve accounting of SVG images in graphics cache
The problem is that the graphics cache only counts the size of the SVG text, which is stored in SvgData::maSvgDataArray. However the SvgData::maSequence may use a lot more memory, as it may contain de-compressed bitmaps that are stored as base64-encoded PNGs in the SVG text. For example icon-themes/galaxy/brand/flat_logo.svg is 812 Ko but contains 60 Mo of bitmaps. This may cause excessive memory usage and failure to export documents due to OOM; according to valgrind massif, the bitmap buffers use 90% of the heap. Add a new interface com::sun:⭐:util::XAccounting, and implement it in drawinglayer BasePrimitive2D. VCL SvgData can't access drawinglayer via C++ directly so this looks like the best approach. Change-Id: I5a7c3147733e23473c1decabed24c1f79d951c7d Reviewed-on: https://gerrit.libreoffice.org/30669 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Michael Stahl <mstahl@redhat.com>
This commit is contained in:
@@ -71,6 +71,12 @@ namespace drawinglayer
|
|||||||
const geometry::ViewInformation2D aViewInformation(rViewParameters);
|
const geometry::ViewInformation2D aViewInformation(rViewParameters);
|
||||||
return basegfx::unotools::rectangle2DFromB2DRectangle(getB2DRange(aViewInformation));
|
return basegfx::unotools::rectangle2DFromB2DRectangle(getB2DRange(aViewInformation));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sal_Int64 SAL_CALL BasePrimitive2D::estimateUsage()
|
||||||
|
throw (css::uno::RuntimeException)
|
||||||
|
{
|
||||||
|
return 0; // for now ignore the objects themselves
|
||||||
|
}
|
||||||
} // end of namespace primitive2d
|
} // end of namespace primitive2d
|
||||||
} // end of namespace drawinglayer
|
} // end of namespace drawinglayer
|
||||||
|
|
||||||
|
@@ -58,6 +58,12 @@ namespace drawinglayer
|
|||||||
return aRetval;
|
return aRetval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sal_Int64 SAL_CALL BitmapPrimitive2D::estimateUsage()
|
||||||
|
throw (css::uno::RuntimeException)
|
||||||
|
{
|
||||||
|
return getBitmapEx().GetSizeBytes();
|
||||||
|
}
|
||||||
|
|
||||||
// provide unique ID
|
// provide unique ID
|
||||||
ImplPrimitive2DIDBlock(BitmapPrimitive2D, PRIMITIVE2D_ID_BITMAPPRIMITIVE2D)
|
ImplPrimitive2DIDBlock(BitmapPrimitive2D, PRIMITIVE2D_ID_BITMAPPRIMITIVE2D)
|
||||||
|
|
||||||
|
@@ -56,6 +56,21 @@ namespace drawinglayer
|
|||||||
return getChildren();
|
return getChildren();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sal_Int64 SAL_CALL GroupPrimitive2D::estimateUsage()
|
||||||
|
throw (css::uno::RuntimeException)
|
||||||
|
{
|
||||||
|
size_t nRet(0);
|
||||||
|
for (auto& it : getChildren())
|
||||||
|
{
|
||||||
|
uno::Reference<util::XAccounting> const xAcc(it, uno::UNO_QUERY);
|
||||||
|
if (xAcc.is())
|
||||||
|
{
|
||||||
|
nRet += xAcc->estimateUsage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nRet;
|
||||||
|
}
|
||||||
|
|
||||||
// provide unique ID
|
// provide unique ID
|
||||||
ImplPrimitive2DIDBlock(GroupPrimitive2D, PRIMITIVE2D_ID_GROUPPRIMITIVE2D)
|
ImplPrimitive2DIDBlock(GroupPrimitive2D, PRIMITIVE2D_ID_GROUPPRIMITIVE2D)
|
||||||
|
|
||||||
|
@@ -314,6 +314,21 @@ namespace drawinglayer
|
|||||||
return BufferedDecompositionPrimitive2D::get2DDecomposition(rViewInformation);
|
return BufferedDecompositionPrimitive2D::get2DDecomposition(rViewInformation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sal_Int64 SAL_CALL PatternFillPrimitive2D::estimateUsage()
|
||||||
|
throw (css::uno::RuntimeException)
|
||||||
|
{
|
||||||
|
size_t nRet(0);
|
||||||
|
for (auto& it : getChildren())
|
||||||
|
{
|
||||||
|
uno::Reference<util::XAccounting> const xAcc(it, uno::UNO_QUERY);
|
||||||
|
if (xAcc.is())
|
||||||
|
{
|
||||||
|
nRet += xAcc->estimateUsage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nRet;
|
||||||
|
}
|
||||||
|
|
||||||
// provide unique ID
|
// provide unique ID
|
||||||
ImplPrimitive2DIDBlock(PatternFillPrimitive2D, PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D)
|
ImplPrimitive2DIDBlock(PatternFillPrimitive2D, PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D)
|
||||||
|
|
||||||
|
@@ -22,8 +22,9 @@
|
|||||||
|
|
||||||
#include <drawinglayer/drawinglayerdllapi.h>
|
#include <drawinglayer/drawinglayerdllapi.h>
|
||||||
|
|
||||||
#include <cppuhelper/compbase1.hxx>
|
#include <cppuhelper/compbase.hxx>
|
||||||
#include <com/sun/star/graphic/XPrimitive2D.hpp>
|
#include <com/sun/star/graphic/XPrimitive2D.hpp>
|
||||||
|
#include <com/sun/star/util/XAccounting.hpp>
|
||||||
#include <cppuhelper/basemutex.hxx>
|
#include <cppuhelper/basemutex.hxx>
|
||||||
#include <basegfx/range/b2drange.hxx>
|
#include <basegfx/range/b2drange.hxx>
|
||||||
|
|
||||||
@@ -49,7 +50,10 @@ namespace drawinglayer { namespace geometry {
|
|||||||
|
|
||||||
namespace drawinglayer { namespace primitive2d {
|
namespace drawinglayer { namespace primitive2d {
|
||||||
/// typedefs for basePrimitive2DImplBase, Primitive2DSequence and Primitive2DReference
|
/// typedefs for basePrimitive2DImplBase, Primitive2DSequence and Primitive2DReference
|
||||||
typedef cppu::WeakComponentImplHelper1< css::graphic::XPrimitive2D > BasePrimitive2DImplBase;
|
typedef cppu::WeakComponentImplHelper<
|
||||||
|
css::graphic::XPrimitive2D,
|
||||||
|
css::util::XAccounting
|
||||||
|
> BasePrimitive2DImplBase;
|
||||||
typedef css::uno::Reference< css::graphic::XPrimitive2D > Primitive2DReference;
|
typedef css::uno::Reference< css::graphic::XPrimitive2D > Primitive2DReference;
|
||||||
typedef css::uno::Sequence< Primitive2DReference > Primitive2DSequence;
|
typedef css::uno::Sequence< Primitive2DReference > Primitive2DSequence;
|
||||||
|
|
||||||
@@ -200,6 +204,10 @@ namespace drawinglayer
|
|||||||
will construct a ViewInformation2D from the ViewParameters for that purpose
|
will construct a ViewInformation2D from the ViewParameters for that purpose
|
||||||
*/
|
*/
|
||||||
virtual css::geometry::RealRectangle2D SAL_CALL getRange( const css::uno::Sequence< css::beans::PropertyValue >& rViewParameters ) throw ( css::uno::RuntimeException, std::exception ) override;
|
virtual css::geometry::RealRectangle2D SAL_CALL getRange( const css::uno::Sequence< css::beans::PropertyValue >& rViewParameters ) throw ( css::uno::RuntimeException, std::exception ) override;
|
||||||
|
|
||||||
|
// XAccounting
|
||||||
|
virtual sal_Int64 SAL_CALL estimateUsage() throw (css::uno::RuntimeException) override;
|
||||||
|
|
||||||
};
|
};
|
||||||
} // end of namespace primitive2d
|
} // end of namespace primitive2d
|
||||||
} // end of namespace drawinglayer
|
} // end of namespace drawinglayer
|
||||||
|
@@ -69,6 +69,9 @@ namespace drawinglayer
|
|||||||
|
|
||||||
/// provide unique ID
|
/// provide unique ID
|
||||||
DeclPrimitive2DIDBlock()
|
DeclPrimitive2DIDBlock()
|
||||||
|
|
||||||
|
// XAccounting
|
||||||
|
virtual sal_Int64 SAL_CALL estimateUsage() throw (css::uno::RuntimeException) override;
|
||||||
};
|
};
|
||||||
} // end of namespace primitive2d
|
} // end of namespace primitive2d
|
||||||
} // end of namespace drawinglayer
|
} // end of namespace drawinglayer
|
||||||
|
@@ -84,6 +84,9 @@ namespace drawinglayer
|
|||||||
|
|
||||||
/// provide unique ID
|
/// provide unique ID
|
||||||
DeclPrimitive2DIDBlock()
|
DeclPrimitive2DIDBlock()
|
||||||
|
|
||||||
|
// XAccounting
|
||||||
|
virtual sal_Int64 SAL_CALL estimateUsage() throw (css::uno::RuntimeException) override;
|
||||||
};
|
};
|
||||||
} // end of namespace primitive2d
|
} // end of namespace primitive2d
|
||||||
} // end of namespace drawinglayer
|
} // end of namespace drawinglayer
|
||||||
|
@@ -87,6 +87,9 @@ namespace drawinglayer
|
|||||||
|
|
||||||
/// provide unique ID
|
/// provide unique ID
|
||||||
DeclPrimitive2DIDBlock()
|
DeclPrimitive2DIDBlock()
|
||||||
|
|
||||||
|
// XAccounting
|
||||||
|
virtual sal_Int64 SAL_CALL estimateUsage() throw (css::uno::RuntimeException) override;
|
||||||
};
|
};
|
||||||
} // end of namespace primitive2d
|
} // end of namespace primitive2d
|
||||||
} // end of namespace drawinglayer
|
} // end of namespace drawinglayer
|
||||||
|
@@ -52,6 +52,7 @@ private:
|
|||||||
std::vector< css::uno::Reference< css::graphic::XPrimitive2D > >
|
std::vector< css::uno::Reference< css::graphic::XPrimitive2D > >
|
||||||
maSequence;
|
maSequence;
|
||||||
BitmapEx maReplacement;
|
BitmapEx maReplacement;
|
||||||
|
size_t mNestedBitmapSize;
|
||||||
|
|
||||||
// on demand creators
|
// on demand creators
|
||||||
void ensureReplacement();
|
void ensureReplacement();
|
||||||
@@ -67,6 +68,8 @@ public:
|
|||||||
/// data read
|
/// data read
|
||||||
const SvgDataArray& getSvgDataArray() const { return maSvgDataArray; }
|
const SvgDataArray& getSvgDataArray() const { return maSvgDataArray; }
|
||||||
sal_uInt32 getSvgDataArrayLength() const { return maSvgDataArray.getLength(); }
|
sal_uInt32 getSvgDataArrayLength() const { return maSvgDataArray.getLength(); }
|
||||||
|
enum class State { UNPARSED, PARSED };
|
||||||
|
std::pair<State, size_t> getSizeBytes();
|
||||||
const OUString& getPath() const { return maPath; }
|
const OUString& getPath() const { return maPath; }
|
||||||
|
|
||||||
/// data read and evtl. on demand creation
|
/// data read and evtl. on demand creation
|
||||||
|
@@ -4113,6 +4113,7 @@ $(eval $(call gb_UnoApi_add_idlfiles,offapi,com/sun/star/util,\
|
|||||||
TriState \
|
TriState \
|
||||||
URL \
|
URL \
|
||||||
VetoException \
|
VetoException \
|
||||||
|
XAccounting \
|
||||||
XAtomServer \
|
XAtomServer \
|
||||||
XBroadcaster \
|
XBroadcaster \
|
||||||
XCancellable \
|
XCancellable \
|
||||||
|
34
offapi/com/sun/star/util/XAccounting.idl
Normal file
34
offapi/com/sun/star/util/XAccounting.idl
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/* -*- 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __com_sun_star_util_XAccounting_idl__
|
||||||
|
#define __com_sun_star_util_XAccounting_idl__
|
||||||
|
|
||||||
|
#include <com/sun/star/uno/XInterface.idl>
|
||||||
|
|
||||||
|
module com { module sun { module star { module util {
|
||||||
|
|
||||||
|
/** allows estimating the memory usage of a service.
|
||||||
|
|
||||||
|
@since LibreOffice 5.3
|
||||||
|
*/
|
||||||
|
interface XAccounting : com::sun::star::uno::XInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @returns an estimate of the current memory usage, in octets.
|
||||||
|
*/
|
||||||
|
hyper estimateUsage();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}; }; }; };
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
|
@@ -766,7 +766,12 @@ sal_uLong ImpGraphic::ImplGetSizeBytes() const
|
|||||||
{
|
{
|
||||||
if(maSvgData.get())
|
if(maSvgData.get())
|
||||||
{
|
{
|
||||||
mnSizeBytes = maSvgData->getSvgDataArrayLength();
|
std::pair<SvgData::State, size_t> tmp(maSvgData->getSizeBytes());
|
||||||
|
if (SvgData::State::UNPARSED == tmp.first)
|
||||||
|
{
|
||||||
|
return tmp.second; // don't cache it until SVG is parsed
|
||||||
|
}
|
||||||
|
mnSizeBytes = tmp.second;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@@ -24,6 +24,7 @@
|
|||||||
#include <com/sun/star/graphic/SvgTools.hpp>
|
#include <com/sun/star/graphic/SvgTools.hpp>
|
||||||
#include <com/sun/star/graphic/Primitive2DTools.hpp>
|
#include <com/sun/star/graphic/Primitive2DTools.hpp>
|
||||||
#include <com/sun/star/rendering/XIntegerReadOnlyBitmap.hpp>
|
#include <com/sun/star/rendering/XIntegerReadOnlyBitmap.hpp>
|
||||||
|
#include <com/sun/star/util/XAccounting.hpp>
|
||||||
#include <vcl/canvastools.hxx>
|
#include <vcl/canvastools.hxx>
|
||||||
#include <comphelper/seqstream.hxx>
|
#include <comphelper/seqstream.hxx>
|
||||||
#include <comphelper/sequence.hxx>
|
#include <comphelper/sequence.hxx>
|
||||||
@@ -88,6 +89,19 @@ BitmapEx convertPrimitive2DSequenceToBitmapEx(
|
|||||||
return aRetval;
|
return aRetval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t estimateSize(
|
||||||
|
std::vector<uno::Reference<graphic::XPrimitive2D>> const& rSequence)
|
||||||
|
{
|
||||||
|
size_t nRet(0);
|
||||||
|
for (auto& it : rSequence)
|
||||||
|
{
|
||||||
|
uno::Reference<util::XAccounting> const xAcc(it, uno::UNO_QUERY);
|
||||||
|
assert(xAcc.is()); // we expect only BasePrimitive2D from SVG parser
|
||||||
|
nRet += xAcc->estimateUsage();
|
||||||
|
}
|
||||||
|
return nRet;
|
||||||
|
}
|
||||||
|
|
||||||
void SvgData::ensureReplacement()
|
void SvgData::ensureReplacement()
|
||||||
{
|
{
|
||||||
ensureSequenceAndRange();
|
ensureSequenceAndRange();
|
||||||
@@ -149,6 +163,19 @@ void SvgData::ensureSequenceAndRange()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mNestedBitmapSize = estimateSize(maSequence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SvgData::getSizeBytes() -> std::pair<State, size_t>
|
||||||
|
{
|
||||||
|
if (maSequence.empty() && maSvgDataArray.hasElements())
|
||||||
|
{
|
||||||
|
return std::make_pair(State::UNPARSED, maSvgDataArray.getLength());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return std::make_pair(State::PARSED, maSvgDataArray.getLength() + mNestedBitmapSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,6 +185,7 @@ SvgData::SvgData(const SvgDataArray& rSvgDataArray, const OUString& rPath)
|
|||||||
maRange(),
|
maRange(),
|
||||||
maSequence(),
|
maSequence(),
|
||||||
maReplacement()
|
maReplacement()
|
||||||
|
, mNestedBitmapSize(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,6 +195,7 @@ SvgData::SvgData(const OUString& rPath):
|
|||||||
maRange(),
|
maRange(),
|
||||||
maSequence(),
|
maSequence(),
|
||||||
maReplacement()
|
maReplacement()
|
||||||
|
, mNestedBitmapSize(0)
|
||||||
{
|
{
|
||||||
SvFileStream rIStm(rPath, StreamMode::STD_READ);
|
SvFileStream rIStm(rPath, StreamMode::STD_READ);
|
||||||
if(rIStm.GetError())
|
if(rIStm.GetError())
|
||||||
|
Reference in New Issue
Block a user