drawinglayer/Library_drawinglayer.mk | 2 drawinglayer/Library_drawinglayercore.mk | 3 drawinglayer/source/primitive2d/BufferedDecompositionFlusher.cxx | 119 +++++++++ drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx | 132 ++-------- drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx | 131 ++------- drawinglayer/source/primitive2d/glowprimitive2d.cxx | 2 drawinglayer/source/primitive2d/graphicprimitive2d.cxx | 4 drawinglayer/source/primitive2d/metafileprimitive2d.cxx | 2 drawinglayer/source/primitive2d/sceneprimitive2d.cxx | 2 drawinglayer/source/primitive2d/shadowprimitive2d.cxx | 2 drawinglayer/source/primitive2d/softedgeprimitive2d.cxx | 2 include/drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx | 49 +++ include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx | 16 - include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx | 14 - include/drawinglayer/primitive2d/groupprimitive2d.hxx | 2 svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx | 2 16 files changed, 270 insertions(+), 214 deletions(-)
New commits: commit 8a17b7f0a679ebf21bcfb425186b205d996d129b Author: Noel Grandin <noelgran...@gmail.com> AuthorDate: Thu Mar 13 18:37:54 2025 +0200 Commit: Noel Grandin <noel.gran...@collabora.co.uk> CommitDate: Mon Mar 17 07:15:15 2025 +0100 tdf#131595 Improve drawinglayer flushing mechanism. Which dramatically speeds up switching between sheets in the referenced document. The problem here is that we have a timer for each buffered primitive. And we have a lot of primitives here. And the TimerManager does not scale well to lots and lots of timer, because it uses a linked list. So this change modifies the flushing mechanism, trading off some precision for some speed. Change-Id: I66a744f06af9b08d4e9b797d11db8d22f4060789 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182876 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk> diff --git a/drawinglayer/Library_drawinglayer.mk b/drawinglayer/Library_drawinglayer.mk index 14e40870d7f1..6997bd22457e 100644 --- a/drawinglayer/Library_drawinglayer.mk +++ b/drawinglayer/Library_drawinglayer.mk @@ -109,7 +109,6 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\ drawinglayer/source/primitive2d/bitmapprimitive2d \ drawinglayer/source/primitive2d/BitmapAlphaPrimitive2D \ drawinglayer/source/primitive2d/borderlineprimitive2d \ - drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D \ drawinglayer/source/primitive2d/controlprimitive2d \ drawinglayer/source/primitive2d/cropprimitive2d \ drawinglayer/source/primitive2d/discretebitmapprimitive2d \ @@ -125,7 +124,6 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\ drawinglayer/source/primitive2d/graphicprimitivehelper2d \ drawinglayer/source/primitive2d/graphicprimitive2d \ drawinglayer/source/primitive2d/gridprimitive2d \ - drawinglayer/source/primitive2d/groupprimitive2d \ drawinglayer/source/primitive2d/helplineprimitive2d \ drawinglayer/source/primitive2d/hiddengeometryprimitive2d \ drawinglayer/source/primitive2d/invertprimitive2d \ diff --git a/drawinglayer/Library_drawinglayercore.mk b/drawinglayer/Library_drawinglayercore.mk index 7cff1a4b70bd..83d2c0faab6d 100644 --- a/drawinglayer/Library_drawinglayercore.mk +++ b/drawinglayer/Library_drawinglayercore.mk @@ -46,7 +46,10 @@ $(eval $(call gb_Library_use_custom_headers,drawinglayercore,\ $(eval $(call gb_Library_add_exception_objects,drawinglayercore,\ drawinglayer/source/primitive2d/baseprimitive2d \ drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D \ + drawinglayer/source/primitive2d/BufferedDecompositionFlusher \ + drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D \ drawinglayer/source/primitive2d/Primitive2DContainer \ + drawinglayer/source/primitive2d/groupprimitive2d \ drawinglayer/source/primitive2d/Tools \ drawinglayer/source/geometry/viewinformation2d \ )) diff --git a/drawinglayer/source/primitive2d/BufferedDecompositionFlusher.cxx b/drawinglayer/source/primitive2d/BufferedDecompositionFlusher.cxx new file mode 100644 index 000000000000..6bc9312a6f7f --- /dev/null +++ b/drawinglayer/source/primitive2d/BufferedDecompositionFlusher.cxx @@ -0,0 +1,119 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx> +#include <tools/lazydelete.hxx> + +namespace drawinglayer::primitive2d +{ +/** + This is a "garbage collection" approach to flushing. + + We store entries in a set. Every 2 seconds, we scan the set for entries that have not + been used for 10 seconds or more, and if so, we flush the buffer primitives in those entries. + + This mechanism is __deliberately__ not perfect. + Sometimes things will be flushed a little too soon, sometimes things will wait a little too long, + since we only have a granularity of 2 seconds. + But what is gains from not being perfect, is scalability. + + It is very simple, scales to lots and lots of primitives without needing lots of timers, and performs + very little work in the common case. +*/ + +static BufferedDecompositionFlusher* getInstance() +{ + static tools::DeleteRtlReferenceOnDeinit<BufferedDecompositionFlusher> gaTimer( + new BufferedDecompositionFlusher); + return gaTimer.get().get(); +} + +// static +void BufferedDecompositionFlusher::update(const BufferedDecompositionPrimitive2D* p) +{ + if (auto flusher = getInstance()) + flusher->updateImpl(p); +} + +// static +void BufferedDecompositionFlusher::update(const BufferedDecompositionGroupPrimitive2D* p) +{ + if (auto flusher = getInstance()) + flusher->updateImpl(p); +} + +BufferedDecompositionFlusher::BufferedDecompositionFlusher() +{ + setRemainingTime(salhelper::TTimeValue(2, 0)); + start(); +} + +void BufferedDecompositionFlusher::updateImpl(const BufferedDecompositionPrimitive2D* p) +{ + std::unique_lock l(maMutex); + maRegistered1.insert(const_cast<BufferedDecompositionPrimitive2D*>(p)); +} + +void BufferedDecompositionFlusher::updateImpl(const BufferedDecompositionGroupPrimitive2D* p) +{ + std::unique_lock l(maMutex); + maRegistered2.insert(const_cast<BufferedDecompositionGroupPrimitive2D*>(p)); +} + +void SAL_CALL BufferedDecompositionFlusher::onShot() +{ + auto aNow = std::chrono::steady_clock::now(); + std::vector<rtl::Reference<BufferedDecompositionPrimitive2D>> aRemoved1; + std::vector<rtl::Reference<BufferedDecompositionGroupPrimitive2D>> aRemoved2; + { + std::unique_lock l(maMutex); + for (auto it = maRegistered1.begin(); it != maRegistered1.end();) + { + if (aNow - (*it)->maLastAccess > std::chrono::seconds(10)) + { + aRemoved1.push_back(*it); + it = maRegistered1.erase(it); + } + else + ++it; + } + for (auto it = maRegistered2.begin(); it != maRegistered2.end();) + { + if (aNow - (*it)->maLastAccess > std::chrono::seconds(10)) + { + aRemoved2.push_back(*it); + it = maRegistered2.erase(it); + } + else + ++it; + } + } + for (const auto& r : aRemoved1) + r->setBuffered2DDecomposition(nullptr); + for (const auto& r : aRemoved2) + r->setBuffered2DDecomposition(Primitive2DContainer{}); + setRemainingTime(salhelper::TTimeValue(2, 0)); + start(); +} + +} // end of namespace drawinglayer::primitive2d + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx b/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx index fcdbe8d02d78..304228c1ca81 100644 --- a/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx +++ b/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx @@ -21,140 +21,80 @@ #include <drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> - -namespace -{ -class LocalCallbackTimer : public salhelper::Timer -{ -protected: - drawinglayer::primitive2d::BufferedDecompositionGroupPrimitive2D* pCustomer; - -public: - explicit LocalCallbackTimer( - drawinglayer::primitive2d::BufferedDecompositionGroupPrimitive2D& rCustomer) - : pCustomer(&rCustomer) - { - } - - void clearCallback() { pCustomer = nullptr; } - -protected: - virtual void SAL_CALL onShot() override; -}; - -void SAL_CALL LocalCallbackTimer::onShot() -{ - if (nullptr != pCustomer) - flushBufferedDecomposition(*pCustomer); -} -} +#include <drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx> namespace drawinglayer::primitive2d { -void flushBufferedDecomposition(BufferedDecompositionGroupPrimitive2D& rTarget) -{ - rTarget.acquire(); - rTarget.setBuffered2DDecomposition(Primitive2DContainer()); - rTarget.release(); -} - bool BufferedDecompositionGroupPrimitive2D::hasBuffered2DDecomposition() const { - if (0 != maCallbackSeconds) - { - std::lock_guard Guard(maCallbackLock); + if (!mbFlushOnTimer) return !maBuffered2DDecomposition.empty(); - } else { + std::lock_guard Guard(maCallbackLock); return !maBuffered2DDecomposition.empty(); } } void BufferedDecompositionGroupPrimitive2D::setBuffered2DDecomposition(Primitive2DContainer&& rNew) { - if (0 == maCallbackSeconds) + if (!mbFlushOnTimer) { // no flush used, just set maBuffered2DDecomposition = std::move(rNew); - return; } - - if (maCallbackTimer.is()) - { - if (rNew.empty()) - { - // stop timer - maCallbackTimer->stop(); - } - else - { - // decomposition changed, touch - maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0)); - if (!maCallbackTimer->isTicking()) - maCallbackTimer->start(); - } - } - else if (!rNew.empty()) + else { - // decomposition defined/set/changed, init & start timer - maCallbackTimer.set(new LocalCallbackTimer(*this)); - maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0)); - maCallbackTimer->start(); - } + // decomposition changed, touch + maLastAccess = std::chrono::steady_clock::now(); + BufferedDecompositionFlusher::update(this); - // tdf#158913 need to secure change when flush/multithreading is in use - std::lock_guard Guard(maCallbackLock); - maBuffered2DDecomposition = std::move(rNew); + // tdf#158913 need to secure change when flush/multithreading is in use + std::lock_guard Guard(maCallbackLock); + maBuffered2DDecomposition = std::move(rNew); + } } BufferedDecompositionGroupPrimitive2D::BufferedDecompositionGroupPrimitive2D( Primitive2DContainer&& aChildren) : GroupPrimitive2D(std::move(aChildren)) - , maCallbackTimer() , maCallbackLock() - , maCallbackSeconds(0) + , mbFlushOnTimer(false) { } -BufferedDecompositionGroupPrimitive2D::~BufferedDecompositionGroupPrimitive2D() -{ - if (maCallbackTimer.is()) - { - // no more decomposition, end callback - static_cast<LocalCallbackTimer*>(maCallbackTimer.get())->clearCallback(); - maCallbackTimer->stop(); - } -} +BufferedDecompositionGroupPrimitive2D::~BufferedDecompositionGroupPrimitive2D() {} void BufferedDecompositionGroupPrimitive2D::get2DDecomposition( Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const { - if (!hasBuffered2DDecomposition()) - { - Primitive2DContainer aNewSequence; - create2DDecomposition(aNewSequence, rViewInformation); - const_cast<BufferedDecompositionGroupPrimitive2D*>(this)->setBuffered2DDecomposition( - std::move(aNewSequence)); - } - - if (0 == maCallbackSeconds) + if (!mbFlushOnTimer) { // no flush/multithreading is in use, just call + if (maBuffered2DDecomposition.empty()) + create2DDecomposition(maBuffered2DDecomposition, rViewInformation); rVisitor.visit(maBuffered2DDecomposition); - return; } - - // decomposition was used, touch/restart time - if (maCallbackTimer) - maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0)); - - // tdf#158913 need to secure 'visit' when flush/multithreading is in use, - // so that the local non-ref-Counted instance of the decomposition gets not - // manipulated (e.g. deleted) - std::lock_guard Guard(maCallbackLock); - rVisitor.visit(maBuffered2DDecomposition); + else + { + // tdf#158913 need to secure 'visit' when flush/multithreading is in use, + // so that the local non-ref-Counted instance of the decomposition gets not + // manipulated (e.g. deleted) + Primitive2DContainer xTmp; + { + // only hold the lock for long enough to get a valid reference + std::lock_guard Guard(maCallbackLock); + maLastAccess = std::chrono::steady_clock::now(); + if (maBuffered2DDecomposition.empty()) + { + create2DDecomposition(maBuffered2DDecomposition, rViewInformation); + BufferedDecompositionFlusher::update(this); + } + xTmp = maBuffered2DDecomposition; + } + rVisitor.visit(xTmp); + } } } // end of namespace drawinglayer::primitive2d diff --git a/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx b/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx index b04905744b50..f9e476021e44 100644 --- a/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx +++ b/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx @@ -21,136 +21,79 @@ #include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> - -namespace -{ -class LocalCallbackTimer : public salhelper::Timer -{ -protected: - drawinglayer::primitive2d::BufferedDecompositionPrimitive2D* pCustomer; - -public: - explicit LocalCallbackTimer( - drawinglayer::primitive2d::BufferedDecompositionPrimitive2D& rCustomer) - : pCustomer(&rCustomer) - { - } - - void clearCallback() { pCustomer = nullptr; } - -protected: - virtual void SAL_CALL onShot() override; -}; - -void SAL_CALL LocalCallbackTimer::onShot() -{ - if (nullptr != pCustomer) - flushBufferedDecomposition(*pCustomer); -} -} +#include <drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx> namespace drawinglayer::primitive2d { -void flushBufferedDecomposition(BufferedDecompositionPrimitive2D& rTarget) -{ - rTarget.acquire(); - rTarget.setBuffered2DDecomposition(nullptr); - rTarget.release(); -} - bool BufferedDecompositionPrimitive2D::hasBuffered2DDecomposition() const { - if (0 != maCallbackSeconds) + if (!mbFlushOnTimer) + return maBuffered2DDecomposition.is(); + else { std::lock_guard Guard(maCallbackLock); return maBuffered2DDecomposition.is(); } - else - return maBuffered2DDecomposition.is(); } void BufferedDecompositionPrimitive2D::setBuffered2DDecomposition(Primitive2DReference rNew) { - if (0 == maCallbackSeconds) + if (!mbFlushOnTimer) { // no flush used, just set maBuffered2DDecomposition = std::move(rNew); - return; } - - if (maCallbackTimer.is()) - { - if (!rNew) - { - // stop timer - maCallbackTimer->stop(); - } - else - { - // decomposition changed, touch - maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0)); - if (!maCallbackTimer->isTicking()) - maCallbackTimer->start(); - } - } - else if (rNew) + else { - // decomposition defined/set/changed, init & start timer - maCallbackTimer.set(new LocalCallbackTimer(*this)); - maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0)); - maCallbackTimer->start(); - } + // decomposition changed, touch + maLastAccess = std::chrono::steady_clock::now(); + BufferedDecompositionFlusher::update(this); - // tdf#158913 need to secure change when flush/multithreading is in use - std::lock_guard Guard(maCallbackLock); - maBuffered2DDecomposition = std::move(rNew); + // tdf#158913 need to secure change when flush/multithreading is in use + std::lock_guard Guard(maCallbackLock); + maBuffered2DDecomposition = std::move(rNew); + } } BufferedDecompositionPrimitive2D::BufferedDecompositionPrimitive2D() : maBuffered2DDecomposition() - , maCallbackTimer() , maCallbackLock() - , maCallbackSeconds(0) + , mbFlushOnTimer(false) { } -BufferedDecompositionPrimitive2D::~BufferedDecompositionPrimitive2D() -{ - if (maCallbackTimer.is()) - { - // no more decomposition, end callback - static_cast<LocalCallbackTimer*>(maCallbackTimer.get())->clearCallback(); - maCallbackTimer->stop(); - } -} +BufferedDecompositionPrimitive2D::~BufferedDecompositionPrimitive2D() {} void BufferedDecompositionPrimitive2D::get2DDecomposition( Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const { - if (!hasBuffered2DDecomposition()) - { - Primitive2DReference aNew = create2DDecomposition(rViewInformation); - const_cast<BufferedDecompositionPrimitive2D*>(this)->setBuffered2DDecomposition( - std::move(aNew)); - } - - if (0 == maCallbackSeconds) + if (!mbFlushOnTimer) { // no flush/multithreading is in use, just call + if (!maBuffered2DDecomposition) + maBuffered2DDecomposition = create2DDecomposition(rViewInformation); rVisitor.visit(maBuffered2DDecomposition); - return; } - - // decomposition was used, touch/restart time - if (maCallbackTimer) - maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0)); - - // tdf#158913 need to secure 'visit' when flush/multithreading is in use, - // so that the local non-ref-Counted instance of the decomposition gets not - // manipulated (e.g. deleted) - std::lock_guard Guard(maCallbackLock); - rVisitor.visit(maBuffered2DDecomposition); + else + { + // tdf#158913 need to secure 'visit' when flush/multithreading is in use, + // so that the local non-ref-Counted instance of the decomposition gets not + // manipulated (e.g. deleted) + Primitive2DReference xTmp; + { + // only hold the lock for long enough to get a valid reference + std::lock_guard Guard(maCallbackLock); + maLastAccess = std::chrono::steady_clock::now(); + if (!maBuffered2DDecomposition) + { + maBuffered2DDecomposition = create2DDecomposition(rViewInformation); + BufferedDecompositionFlusher::update(this); + } + xTmp = maBuffered2DDecomposition; + } + rVisitor.visit(xTmp); + } } } // end of namespace drawinglayer::primitive2d diff --git a/drawinglayer/source/primitive2d/glowprimitive2d.cxx b/drawinglayer/source/primitive2d/glowprimitive2d.cxx index ed212ae4da6d..33a3d59fcc6a 100644 --- a/drawinglayer/source/primitive2d/glowprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/glowprimitive2d.cxx @@ -44,7 +44,7 @@ GlowPrimitive2D::GlowPrimitive2D(const Color& rGlowColor, double fRadius, , maLastClippedRange() { // activate callback to flush buffered decomposition content - setCallbackSeconds(15); + activateFlushOnTimer(); } bool GlowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const diff --git a/drawinglayer/source/primitive2d/graphicprimitive2d.cxx b/drawinglayer/source/primitive2d/graphicprimitive2d.cxx index 457bd64c95c6..0a5a5060b0cb 100644 --- a/drawinglayer/source/primitive2d/graphicprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/graphicprimitive2d.cxx @@ -177,7 +177,7 @@ GraphicPrimitive2D::GraphicPrimitive2D(basegfx::B2DHomMatrix aTransform, , maGraphicAttr(rGraphicAttr) { // activate callback to flush buffered decomposition content - setCallbackSeconds(20); + activateFlushOnTimer(); } GraphicPrimitive2D::GraphicPrimitive2D(basegfx::B2DHomMatrix aTransform, @@ -186,7 +186,7 @@ GraphicPrimitive2D::GraphicPrimitive2D(basegfx::B2DHomMatrix aTransform, , maGraphicObject(rGraphicObject) { // activate callback to flush buffered decomposition content - setCallbackSeconds(20); + activateFlushOnTimer(); } bool GraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const diff --git a/drawinglayer/source/primitive2d/metafileprimitive2d.cxx b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx index 46ddf6582571..a0a1550d1b66 100644 --- a/drawinglayer/source/primitive2d/metafileprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx @@ -91,7 +91,7 @@ namespace drawinglayer::primitive2d maMetaFile(rMetaFile) { // activate callback to flush buffered decomposition content - setCallbackSeconds(20); + activateFlushOnTimer(); } bool MetafilePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const diff --git a/drawinglayer/source/primitive2d/sceneprimitive2d.cxx b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx index 5300e8d82e05..02c51b01af53 100644 --- a/drawinglayer/source/primitive2d/sceneprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx @@ -633,7 +633,7 @@ namespace drawinglayer::primitive2d mfOldDiscreteSizeY(0.0) { // activate callback to flush buffered decomposition content - setCallbackSeconds(45); + activateFlushOnTimer(); } bool ScenePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const diff --git a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx index ff5600d1f5cf..3b932b92f8c5 100644 --- a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx @@ -51,7 +51,7 @@ ShadowPrimitive2D::ShadowPrimitive2D(basegfx::B2DHomMatrix aShadowTransform, , maLastClippedRange() { // activate callback to flush buffered decomposition content - setCallbackSeconds(15); + activateFlushOnTimer(); } bool ShadowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const diff --git a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx index df05771f27a6..ed0348e13465 100644 --- a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx @@ -40,7 +40,7 @@ SoftEdgePrimitive2D::SoftEdgePrimitive2D(double fRadius, Primitive2DContainer&& , maLastClippedRange() { // activate callback to flush buffered decomposition content - setCallbackSeconds(15); + activateFlushOnTimer(); } bool SoftEdgePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const diff --git a/include/drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx b/include/drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx new file mode 100644 index 000000000000..26fbea502414 --- /dev/null +++ b/include/drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx @@ -0,0 +1,49 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx> +#include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx> +#include <salhelper/timer.hxx> +#include <unordered_set> + +namespace drawinglayer::primitive2d +{ +class BufferedDecompositionFlusher : public salhelper::Timer +{ +public: + static void update(const BufferedDecompositionPrimitive2D*); + static void update(const BufferedDecompositionGroupPrimitive2D*); + + BufferedDecompositionFlusher(); + +private: + virtual void SAL_CALL onShot() override; + void updateImpl(const BufferedDecompositionPrimitive2D*); + void updateImpl(const BufferedDecompositionGroupPrimitive2D*); + + std::mutex maMutex; + std::unordered_set<rtl::Reference<BufferedDecompositionPrimitive2D>> maRegistered1; + std::unordered_set<rtl::Reference<BufferedDecompositionGroupPrimitive2D>> maRegistered2; +}; + +} // end of namespace drawinglayer::primitive2d + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx b/include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx index 9ea787bb9d38..0956dd51ac81 100644 --- a/include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx +++ b/include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx @@ -21,10 +21,12 @@ #include <drawinglayer/drawinglayerdllapi.h> #include <drawinglayer/primitive2d/groupprimitive2d.hxx> -#include <salhelper/timer.hxx> +#include <chrono> namespace drawinglayer::primitive2d { +class BufferedDecompositionFlusher; + /** BufferedDecompositionGroupPrimitive2D class Baseclass for all C++ implementations which are derived from GroupPrimitive2D @@ -33,20 +35,20 @@ namespace drawinglayer::primitive2d For discussion please refer to BufferedDecompositionPrimitive2D, this is the same but for GroupPrimitive2D which want to buffer their decomposition */ -class UNLESS_MERGELIBS(DRAWINGLAYER_DLLPUBLIC) BufferedDecompositionGroupPrimitive2D +class UNLESS_MERGELIBS(DRAWINGLAYERCORE_DLLPUBLIC) BufferedDecompositionGroupPrimitive2D : public GroupPrimitive2D { private: // exclusive helper for Primitive2DFlusher - friend void flushBufferedDecomposition(BufferedDecompositionGroupPrimitive2D&); + friend class BufferedDecompositionFlusher; /// a sequence used for buffering the last create2DDecomposition() result - Primitive2DContainer maBuffered2DDecomposition; + mutable Primitive2DContainer maBuffered2DDecomposition; /// offer callback mechanism to flush buffered content timer-based - ::rtl::Reference<::salhelper::Timer> maCallbackTimer; mutable std::mutex maCallbackLock; - sal_uInt16 maCallbackSeconds; + mutable std::chrono::time_point<std::chrono::steady_clock> maLastAccess; + bool mbFlushOnTimer; protected: /// identical to BufferedDecompositionPrimitive2D, see there please @@ -62,7 +64,7 @@ protected: // callback mechanism to flush buffered content timer-based will be activated. // it is protected since the idea is that this gets called in the constructor // of derived classes. - void setCallbackSeconds(sal_uInt16 nNew) { maCallbackSeconds = nNew; } + void activateFlushOnTimer() { mbFlushOnTimer = true; } public: /// constructor/destructor. For GroupPrimitive2D we need the child parameter, too. diff --git a/include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx b/include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx index 3fbfb1e1e00c..bc24a51a5fb4 100644 --- a/include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx +++ b/include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx @@ -22,7 +22,7 @@ #include <drawinglayer/drawinglayerdllapi.h> #include <drawinglayer/primitive2d/Primitive2DContainer.hxx> #include <drawinglayer/primitive2d/baseprimitive2d.hxx> -#include <salhelper/timer.hxx> +#include <chrono> namespace drawinglayer::geometry { @@ -31,6 +31,8 @@ class ViewInformation2D; namespace drawinglayer::primitive2d { +class BufferedDecompositionFlusher; + /** BufferedDecompositionPrimitive2D class Baseclass for all C++ implementations of css::graphic::XPrimitive2D @@ -64,15 +66,15 @@ class DRAWINGLAYERCORE_DLLPUBLIC BufferedDecompositionPrimitive2D : public BaseP { private: // exclusive helper for Primitive2DFlusher - friend void flushBufferedDecomposition(BufferedDecompositionPrimitive2D&); + friend class BufferedDecompositionFlusher; /// a sequence used for buffering the last create2DDecomposition() result - Primitive2DReference maBuffered2DDecomposition; + mutable Primitive2DReference maBuffered2DDecomposition; /// offer callback mechanism to flush buffered content timer-based - ::rtl::Reference<::salhelper::Timer> maCallbackTimer; mutable std::mutex maCallbackLock; - sal_uInt16 maCallbackSeconds; + mutable std::chrono::time_point<std::chrono::steady_clock> maLastAccess; + bool mbFlushOnTimer; protected: /** access methods to maBuffered2DDecomposition. The usage of this methods may allow @@ -90,7 +92,7 @@ protected: // callback mechanism to flush buffered content timer-based will be activated. // it is protected since the idea is that this gets called in the constructor // of derived classes. - void setCallbackSeconds(sal_uInt16 nNew) { maCallbackSeconds = nNew; } + void activateFlushOnTimer() { mbFlushOnTimer = true; } public: // constructor/destructor diff --git a/include/drawinglayer/primitive2d/groupprimitive2d.hxx b/include/drawinglayer/primitive2d/groupprimitive2d.hxx index 1c55a5814743..2ef9d936a3db 100644 --- a/include/drawinglayer/primitive2d/groupprimitive2d.hxx +++ b/include/drawinglayer/primitive2d/groupprimitive2d.hxx @@ -60,7 +60,7 @@ namespace drawinglayer::primitive2d - ModifiedColorPrimitive2D (for a stack of color modifications) - TransformPrimitive2D (for a transformation stack) */ - class DRAWINGLAYER_DLLPUBLIC GroupPrimitive2D : public BasePrimitive2D + class DRAWINGLAYERCORE_DLLPUBLIC GroupPrimitive2D : public BasePrimitive2D { private: /// the children. Declared private since this shall never be changed at all after construction diff --git a/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx index dadc8d06326e..a6fc701da389 100644 --- a/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx +++ b/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx @@ -151,7 +151,7 @@ SdrGrafPrimitive2D::SdrGrafPrimitive2D( , mbPlaceholderImage(bPlaceholderImage) { // activate callback to flush buffered decomposition content - setCallbackSeconds(20); + activateFlushOnTimer(); // reset some values from GraphicAttr which are part of transformation already maGraphicAttr.SetRotation(0_deg10);