Kaydet (Commit) f9028f19 authored tarafından Michael Stahl's avatar Michael Stahl

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::star::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/30669Tested-by: 's avatarJenkins <ci@libreoffice.org>
Reviewed-by: 's avatarMichael Stahl <mstahl@redhat.com>
üst 04644956
...@@ -71,6 +71,12 @@ namespace drawinglayer ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 @@ ...@@ -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 { ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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: ...@@ -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: ...@@ -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,\ ...@@ -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 \
......
/* -*- 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 ...@@ -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 @@ ...@@ -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( ...@@ -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() ...@@ -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) ...@@ -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): ...@@ -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())
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment