sw/inc/IDocumentTimerAccess.hxx | 4 ++++ sw/inc/doc.hxx | 2 +- sw/qa/extras/tiledrendering/tiledrendering2.cxx | 11 ++++++++++- sw/source/core/doc/DocumentTimerManager.cxx | 10 ++++++++++ sw/source/core/inc/DocumentTimerManager.hxx | 2 ++ 5 files changed, 27 insertions(+), 2 deletions(-)
New commits: commit 8278384498d4a02559c81884c0ffe763b9facc1b Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue Apr 29 15:04:42 2025 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Wed Apr 30 10:31:25 2025 +0200 cool#11785 sw lok: only start idling after the idle jobs timer stopped Open a 300 pages document with simple content, the load itself only takes ~100 ms, but the time between the end of load and start of rendering tiles is a lot more than that: debug:32053:31994: lo_documentLoadWithOptions: end @ 2092810770 debug:32053:31994: DocumentTimerManager::DoIdleJobs: finished in 3152 ms debug:32053:31994: doc_paintPartTile: start @ 2092814038 -> 3268 ms Looking at the flamegraph in the issue shows that we start processing idle jobs, part of that is sw::DocumentTimerManager::DoIdleJobs(). That used to do ~no layout work since layout was fully done as part of document load before commit bba965b655a9181c07e5cfb6d3a59363e49e650b (cool#11064 sw lok: allow specifying the visible area during doc load, 2025-02-13). Now some layout can be done on idle, and as part of that we call SpellCheckerDispatcher::isValid(), which takes a lot of time, and is not interrupted as a tile request comes in. Fix the problem by extending the delay effort of commit 349748e63c698076bb44f75da9eaa104489e959c (sw lok: delay processing idle jobs to let LOK finish initialization, 2018-12-03), which already had a 1s delay of idle jobs, but it didn't work, because sw::DocumentTimerManager::StartIdling() is also called from e.g. SwRootFrame::SetIdleFlags(), so the idle jobs still started. Now we explicitly don't start idling till m_aFireIdleJobsTimer is active (started, but not yet invoked). With this, the first tiles are rendered much faster after load: debug:14058:13996: lo_documentLoadWithOptions: end @ 2095914924 debug:14058:13996: doc_paintPartTile: start @ 2095915224 -> 300 ms This also means that two tests now don't get idle jobs done after Scheduler::ProcessEventsToIdle() (and this is wanted by default), so explicitly stop m_aFireIdleJobsTimer in those tests to be able to assert what would happen after waiting for long enough. Change-Id: I8398dc7687d8ac0858f804e687593c5cb7c4e47b Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184809 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins diff --git a/sw/inc/IDocumentTimerAccess.hxx b/sw/inc/IDocumentTimerAccess.hxx index 5cfb1a3ecc41..6ccb4c7efc14 100644 --- a/sw/inc/IDocumentTimerAccess.hxx +++ b/sw/inc/IDocumentTimerAccess.hxx @@ -19,6 +19,8 @@ #pragma once +class Timer; + /** * Handle the background jobs of a Writer document. * @@ -69,6 +71,8 @@ public: */ virtual bool IsIdlingBlocked() const = 0; + virtual Timer& GetFireIdleJobsTimer() = 0; + protected: virtual ~IDocumentTimerAccess(){}; }; diff --git a/sw/inc/doc.hxx b/sw/inc/doc.hxx index 8e599232aedb..837359fbffaf 100644 --- a/sw/inc/doc.hxx +++ b/sw/inc/doc.hxx @@ -547,7 +547,7 @@ public: // IDocumentTimerAccess // Our own 'IdleTimer' calls the following method IDocumentTimerAccess const & getIDocumentTimerAccess() const; - IDocumentTimerAccess & getIDocumentTimerAccess(); + SW_DLLPUBLIC IDocumentTimerAccess & getIDocumentTimerAccess(); // IDocumentChartDataProviderAccess IDocumentChartDataProviderAccess const & getIDocumentChartDataProviderAccess() const; diff --git a/sw/qa/extras/tiledrendering/tiledrendering2.cxx b/sw/qa/extras/tiledrendering/tiledrendering2.cxx index e94293d66246..50a3e18a8a68 100644 --- a/sw/qa/extras/tiledrendering/tiledrendering2.cxx +++ b/sw/qa/extras/tiledrendering/tiledrendering2.cxx @@ -191,7 +191,11 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testAsyncLayout) CPPUNIT_ASSERT(pPage2->IsInvalidContent()); CPPUNIT_ASSERT(pPage3->IsInvalidContent()); - // And then processing all idle events: + // And when processing all idle events: + SwDoc* pDoc = pDocShell->GetDoc(); + IDocumentTimerAccess& rIDTA = pDoc->getIDocumentTimerAccess(); + rIDTA.GetFireIdleJobsTimer().Stop(); + rIDTA.StartIdling(); Scheduler::ProcessEventsToIdle(); // Then make sure all pages get an async layout: @@ -731,6 +735,11 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testCommentsOnLoad) // When getting the list of comments from the document + listening for notifications from idle // layout: pXTextDocument->getPostIts(aWriter); + SwDocShell* pDocShell = getSwDocShell(); + SwDoc* pDoc = pDocShell->GetDoc(); + IDocumentTimerAccess& rIDTA = pDoc->getIDocumentTimerAccess(); + rIDTA.GetFireIdleJobsTimer().Stop(); + rIDTA.StartIdling(); Scheduler::ProcessEventsToIdle(); // Then make sure that: diff --git a/sw/source/core/doc/DocumentTimerManager.cxx b/sw/source/core/doc/DocumentTimerManager.cxx index df232238eca0..c24b2204fb66 100644 --- a/sw/source/core/doc/DocumentTimerManager.cxx +++ b/sw/source/core/doc/DocumentTimerManager.cxx @@ -56,6 +56,11 @@ DocumentTimerManager::DocumentTimerManager(SwDoc& i_rSwdoc) void DocumentTimerManager::StartIdling() { + if (m_aFireIdleJobsTimer.IsActive()) + { + return; + } + if (m_bWaitForLokInit && comphelper::LibreOfficeKit::isActive()) { // Start the idle jobs only after a certain delay. @@ -102,6 +107,11 @@ void DocumentTimerManager::UnblockIdling() } } +Timer& DocumentTimerManager::GetFireIdleJobsTimer() +{ + return m_aFireIdleJobsTimer; +} + IMPL_LINK(DocumentTimerManager, FireIdleJobsTimeout, Timer*, , void) { // Now we can run the idle jobs, assuming we finished LOK initialization. diff --git a/sw/source/core/inc/DocumentTimerManager.hxx b/sw/source/core/inc/DocumentTimerManager.hxx index d22852b910bb..20a9cd83a222 100644 --- a/sw/source/core/inc/DocumentTimerManager.hxx +++ b/sw/source/core/inc/DocumentTimerManager.hxx @@ -57,6 +57,8 @@ public: bool IsIdlingBlocked() const override; + Timer& GetFireIdleJobsTimer() override; + private: DocumentTimerManager(DocumentTimerManager const&) = delete; DocumentTimerManager& operator=(DocumentTimerManager const&) = delete;