include/vcl/BinaryDataContainer.hxx | 15 ++- include/vcl/gfxlink.hxx | 3 vcl/source/gdi/impgraph.cxx | 5 + vcl/source/graphic/BinaryDataContainer.cxx | 117 +++++++++++++++++++++++++---- 4 files changed, 123 insertions(+), 17 deletions(-)
New commits: commit 0f2581204a70038ed7ca78089a9bd96d158e02c0 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Mon Apr 3 09:34:54 2023 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Fri May 5 18:47:03 2023 +0200 BinaryDataContainer swap out implementation. We can easily accumulate a large number of in-memory graphic objects, and swapping these as well as the un-compressed images can become important. For now we swap out the container when the image is swapped out, however it seems unlikely it will ever need to be swapped in again. Despite the shared pImpl, we retained the reference counting on the underling vector to keep this immutable while we hand out a MemoryStream reference to it. Change-Id: Ib7ca45afb8499460b1852461f7c11afca3f3cdfa Signed-off-by: Michael Meeks <michael.me...@collabora.com> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151359 Tested-by: Jenkins diff --git a/include/vcl/BinaryDataContainer.hxx b/include/vcl/BinaryDataContainer.hxx index e9e46a04e667..f6f07f0c5ef6 100644 --- a/include/vcl/BinaryDataContainer.hxx +++ b/include/vcl/BinaryDataContainer.hxx @@ -13,6 +13,7 @@ #include <sal/config.h> #include <com/sun/star/uno/Sequence.hxx> +#include <unotools/tempfile.hxx> #include <tools/stream.hxx> #include <vcl/dllapi.h> @@ -26,9 +27,11 @@ */ class VCL_DLLPUBLIC BinaryDataContainer final { -private: - // the binary data - std::shared_ptr<std::vector<sal_uInt8>> mpData; + struct Impl; + + std::shared_ptr<Impl> mpImpl; + + void ensureSwappedIn() const; public: BinaryDataContainer() = default; @@ -53,6 +56,12 @@ public: /// writes the contents to the given stream std::size_t writeToStream(SvStream& rStream) const; + /// return the in-memory size in bytes as of now. + std::size_t getSizeBytes() const; + + /// swap out to disk for now + void swapOut() const; + size_t calculateHash() const; }; diff --git a/include/vcl/gfxlink.hxx b/include/vcl/gfxlink.hxx index 8c0f5fd32b05..531633b3f738 100644 --- a/include/vcl/gfxlink.hxx +++ b/include/vcl/gfxlink.hxx @@ -85,6 +85,9 @@ public: sal_uInt32 GetDataSize() const { return maDataContainer.getSize(); } const sal_uInt8* GetData() const; + /// return the in-memory size as of now. + size_t getSizeBytes() const { return maDataContainer.getSizeBytes(); } + const BinaryDataContainer& getDataContainer() const { return maDataContainer; diff --git a/vcl/source/gdi/impgraph.cxx b/vcl/source/gdi/impgraph.cxx index 84df1765569c..41f921228d34 100644 --- a/vcl/source/gdi/impgraph.cxx +++ b/vcl/source/gdi/impgraph.cxx @@ -1250,6 +1250,9 @@ bool ImpGraphic::swapOutGraphic(SvStream& rStream) break; } + if (mpGfxLink) + mpGfxLink->getDataContainer().swapOut(); + return true; } @@ -1431,6 +1434,8 @@ void ImpGraphic::dumpState(rtl::OStringBuffer &rState) rState.append(static_cast<sal_Int32>(meType)); rState.append("\tsize:\t"); rState.append(static_cast<sal_Int64>(mnSizeBytes)); + rState.append("\tgfxl:\t"); + rState.append(static_cast<sal_Int64>(mpGfxLink ? mpGfxLink->getSizeBytes() : -1)); rState.append("\t"); rState.append(static_cast<sal_Int32>(maSwapInfo.maSizePixel.Width())); rState.append("x"); diff --git a/vcl/source/graphic/BinaryDataContainer.cxx b/vcl/source/graphic/BinaryDataContainer.cxx index b35195b7d27e..f395497f9449 100644 --- a/vcl/source/graphic/BinaryDataContainer.cxx +++ b/vcl/source/graphic/BinaryDataContainer.cxx @@ -10,21 +10,76 @@ #include <vcl/BinaryDataContainer.hxx> #include <o3tl/hash_combine.hxx> +#include <unotools/tempfile.hxx> +#include <comphelper/lok.hxx> +#include <sal/log.hxx> + +struct BinaryDataContainer::Impl +{ + // temp file to store the data out of RAM if necessary + std::unique_ptr<utl::TempFileNamed> mpFile; + // the binary data + std::shared_ptr<std::vector<sal_uInt8>> mpData; + + Impl(SvStream& stream, size_t size) { readData(stream, size); } + + /// Populate mpData from the stream + void readData(SvStream& stream, size_t size) + { + auto pData = std::make_shared<std::vector<sal_uInt8>>(size); + if (stream.ReadBytes(pData->data(), pData->size()) == size) + mpData = std::move(pData); + } + + /// ensure the data is in-RAM + void ensureSwappedIn() + { + if (mpData || !mpFile) + return; + + auto pStream = mpFile->GetStream(StreamMode::READ); + pStream->Seek(0); + readData(*pStream, pStream->remainingSize()); + + // Horrifying data loss ... + SAL_WARN_IF(pStream->GetError(), "vcl", + "Inconsistent system - failed to swap image back in"); + SAL_DEBUG("Swap in: " << pStream->GetError()); + } + + void swapOut() + { + if (mpFile) + { + // we already have it swapped out. + mpData.reset(); + return; + } + + if (!mpData || mpData->empty()) + return; + + mpFile.reset(new utl::TempFileNamed()); + auto pStream = mpFile->GetStream(StreamMode::READWRITE); + + pStream->WriteBytes(mpData->data(), mpData->size()); + + mpData.reset(); + } +}; BinaryDataContainer::BinaryDataContainer(SvStream& stream, size_t size) + : mpImpl(new Impl(stream, size)) { - auto pBuffer = std::make_shared<std::vector<sal_uInt8>>(size); - if (stream.ReadBytes(pBuffer->data(), pBuffer->size()) == size) - mpData = std::move(pBuffer); } size_t BinaryDataContainer::calculateHash() const { size_t nSeed = 0; - if (mpData) + if (mpImpl && mpImpl->mpData && !mpImpl->mpData->empty()) { o3tl::hash_combine(nSeed, getSize()); - for (sal_uInt8 const& rByte : *mpData) + for (sal_uInt8 const& rByte : *mpImpl->mpData) o3tl::hash_combine(nSeed, rByte); } return nSeed; @@ -34,10 +89,11 @@ css::uno::Sequence<sal_Int8> BinaryDataContainer::getCopyAsByteSequence() const { if (isEmpty()) return css::uno::Sequence<sal_Int8>(); + assert(mpImpl); css::uno::Sequence<sal_Int8> aData(getSize()); - std::copy(mpData->cbegin(), mpData->cend(), aData.getArray()); + std::copy(mpImpl->mpData->cbegin(), mpImpl->mpData->cend(), aData.getArray()); return aData; } @@ -53,10 +109,9 @@ class ReferencedMemoryStream : public SvMemoryStream std::shared_ptr<std::vector<sal_uInt8>> mpData; public: - ReferencedMemoryStream(const std::shared_ptr<std::vector<sal_uInt8>>& rData) - : SvMemoryStream(rData ? rData->data() : nullptr, rData ? rData->size() : 0, - StreamMode::READ) - , mpData(rData) + ReferencedMemoryStream(const std::shared_ptr<std::vector<sal_uInt8>>& pData) + : SvMemoryStream(pData->data(), pData->size(), StreamMode::READ) + , mpData(pData) { } }; @@ -64,18 +119,52 @@ public: std::shared_ptr<SvStream> BinaryDataContainer::getAsStream() { - return std::make_shared<ReferencedMemoryStream>(mpData); + ensureSwappedIn(); // TODO: transfer in streamed chunks + return std::make_shared<ReferencedMemoryStream>(mpImpl->mpData); } std::size_t BinaryDataContainer::writeToStream(SvStream& rStream) const { + ensureSwappedIn(); // TODO: transfer in streamed chunks return rStream.WriteBytes(getData(), getSize()); } -size_t BinaryDataContainer::getSize() const { return mpData ? mpData->size() : 0; } +size_t BinaryDataContainer::getSize() const +{ + ensureSwappedIn(); + return mpImpl && mpImpl->mpData ? mpImpl->mpData->size() : 0; +} + +size_t BinaryDataContainer::getSizeBytes() const +{ + return mpImpl && mpImpl->mpData ? mpImpl->mpData->size() : 0; +} + +bool BinaryDataContainer::isEmpty() const +{ + ensureSwappedIn(); + return !mpImpl || !mpImpl->mpData || mpImpl->mpData->empty(); +} + +const sal_uInt8* BinaryDataContainer::getData() const +{ + ensureSwappedIn(); + return mpImpl && mpImpl->mpData ? mpImpl->mpData->data() : nullptr; +} -bool BinaryDataContainer::isEmpty() const { return !mpData || mpData->empty(); } +void BinaryDataContainer::ensureSwappedIn() const +{ + if (mpImpl) + mpImpl->ensureSwappedIn(); +} + +void BinaryDataContainer::swapOut() const +{ + // Only bother reducing memory footprint in kit mode - for mobile/online etc. + if (!mpImpl || !comphelper::LibreOfficeKit::isActive()) + return; -const sal_uInt8* BinaryDataContainer::getData() const { return mpData ? mpData->data() : nullptr; } + mpImpl->swapOut(); +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */