include/svx/svdmodel.hxx | 3 ++ include/svx/svdpntv.hxx | 1 svx/source/svdraw/svdmodel.cxx | 1 svx/source/svdraw/svdpntv.cxx | 10 +++++++ sw/qa/extras/tiledrendering/data/3pages-shape.odt |binary sw/qa/extras/tiledrendering/tiledrendering2.cxx | 29 ++++++++++++++++++++++ sw/source/core/layout/layact.cxx | 17 ++++++++++++ 7 files changed, 61 insertions(+)
New commits: commit e60a92164ac2cc35f70efcbf20a7272be1e3f99d Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Thu May 15 09:34:25 2025 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Thu May 15 16:22:00 2025 +0200 cool#11942 svx lok: handle Writer idle status when starting the paint view idle The bugdoc is similar to cool#11785, i.e. a document of 3-400 pages, but the content is reasonably simple, mostly plain text and bullet points. The document has a shape, though. Loading this document in COOL does a full sync layout on load, which is reported to take ~9s on a slower machine and it takes >1s for me locally (after doc load, before the render of the first page). It seems what happens is that idle layout is started, but it's not interrupted, because moving content between pages in SwLayAction::Action(), called from the SwLayIdle ctor calls into SdrPaintView::Notify(), which starts a high priority "idle", so the LOK client thinks it should not interrupt core jobs, so the idle layout first completes, and only then we paint the first tile. Fix the problem by adjusting the priority of SdrPaintView::maComeBackIdle dynamically. Add a flag in the draw model to know when we're inside the Writer idle layout and once the svx/ idle is started, lower the priority in case that's invoked from inside the Writer idle layout. This gives the LOK client a way to interrupt the Writer idle layout (via the anyInput callback), now that it seems core has no high priority jobs in the scheduler. Size of the time window between the end of doc load and the start of the first tile render, before: 1617 ms. After: 182 ms An alternative approach I considered is to interact with the SdrPaintView that belongs to the current Writer view, but that won't work. There are typically 2 draw views active right after document load, one created in SwViewShellImp::MakeDrawView(), but there is also an additional hidden view created in the SvxDrawPage ctor for UNO purposes, so if we don't go via SdrModel, we would have to notify both views that we're entering / leaving the Writer idle layout, which looks more complicated than what this patch does. Change-Id: I115ba2c152fe7283804cde564afd511f9fbff707 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/185360 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/include/svx/svdmodel.hxx b/include/svx/svdmodel.hxx index 2d7118be583f..15b756b1c9e1 100644 --- a/include/svx/svdmodel.hxx +++ b/include/svx/svdmodel.hxx @@ -182,6 +182,7 @@ protected: sal_uInt16 m_nPageNumsDirtyFrom = SAL_MAX_UINT16; sal_uInt16 m_nMasterPageNumsDirtyFrom = SAL_MAX_UINT16; bool m_bIsWriter:1; // to clean up pMyPool from 303a + bool m_bIsWriterIdle:1; bool m_bThemedControls:1; // If false UnoControls should not use theme colors bool mbUndoEnabled:1; // If false no undo is recorded or we are during the execution of an undo action bool mbChanged:1; @@ -567,6 +568,8 @@ public: void disposeOutliner( std::unique_ptr<SdrOutliner> pOutliner ); bool IsWriter() const { return m_bIsWriter; } + void SetWriterIdle(bool bIsWriterIdle) { m_bIsWriterIdle = bIsWriterIdle; } + bool IsWriterIdle() const { return m_bIsWriterIdle; } bool IsPDFDocument() const { return m_bIsPDFDocument; } void setPDFDocument(bool bIsPDFDocument) diff --git a/include/svx/svdpntv.hxx b/include/svx/svdpntv.hxx index e3c9fc1dcc03..4d641de4dc60 100644 --- a/include/svx/svdpntv.hxx +++ b/include/svx/svdpntv.hxx @@ -230,6 +230,7 @@ public: SdrPaintWindow* GetPaintWindow(sal_uInt32 nIndex) const; // Replacement for GetWin(0), may return 0L (!) OutputDevice* GetFirstOutputDevice() const; + const Idle& GetComeBackIdle() const { return maComeBackIdle; }; private: DECL_DLLPRIVATE_LINK(ImpComeBackHdl, Timer*, void); diff --git a/svx/source/svdraw/svdmodel.cxx b/svx/source/svdraw/svdmodel.cxx index c510ec95b6e0..5259ba3aa0f2 100644 --- a/svx/source/svdraw/svdmodel.cxx +++ b/svx/source/svdraw/svdmodel.cxx @@ -129,6 +129,7 @@ SdrModel::SdrModel(SfxItemPool* pPool, comphelper::IEmbeddedHelper* pEmbeddedHel , m_pLinkManager(nullptr) , m_nUndoLevel(0) , m_bIsWriter(true) + , m_bIsWriterIdle(false) , m_bThemedControls(true) , mbUndoEnabled(true) , mbChanged(false) diff --git a/svx/source/svdraw/svdpntv.cxx b/svx/source/svdraw/svdpntv.cxx index 5cee983a0b73..b2327dfddfd0 100644 --- a/svx/source/svdraw/svdpntv.cxx +++ b/svx/source/svdraw/svdpntv.cxx @@ -214,6 +214,16 @@ void SdrPaintView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) if (bObjChg) { mbSomeObjChgdFlag=true; + const SdrModel& rModel = GetModel(); + if (rModel.IsWriterIdle()) + { + // We're inside Writer idle layout: don't pick a high priority. + maComeBackIdle.SetPriority(TaskPriority::DEFAULT_IDLE); + } + else + { + maComeBackIdle.SetPriority(TaskPriority::REPAINT); + } maComeBackIdle.Start(); } } diff --git a/sw/qa/extras/tiledrendering/data/3pages-shape.odt b/sw/qa/extras/tiledrendering/data/3pages-shape.odt new file mode 100644 index 000000000000..27d377887d9a Binary files /dev/null and b/sw/qa/extras/tiledrendering/data/3pages-shape.odt differ diff --git a/sw/qa/extras/tiledrendering/tiledrendering2.cxx b/sw/qa/extras/tiledrendering/tiledrendering2.cxx index 0960b374220b..7258a65b9912 100644 --- a/sw/qa/extras/tiledrendering/tiledrendering2.cxx +++ b/sw/qa/extras/tiledrendering/tiledrendering2.cxx @@ -37,6 +37,8 @@ #include <wrtsh.hxx> #include <swtestviewcallback.hxx> #include <cmdid.h> +#include <viewimp.hxx> +#include <dview.hxx> namespace { @@ -778,6 +780,33 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSpellcheckVisibleArea) CPPUNIT_ASSERT(pPage2->IsInvalidSpelling()); CPPUNIT_ASSERT(pPage3->IsInvalidSpelling()); } + +CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testIdleLayoutShape) +{ + // Given a loaded document with a defined viewport: + awt::Rectangle aVisibleArea{ 0, 0, 12240, 15840 }; + comphelper::LibreOfficeKit::setInitialClientVisibleArea(aVisibleArea); + comphelper::ScopeGuard g([] { comphelper::LibreOfficeKit::setInitialClientVisibleArea({}); }); + OUString aURL = createFileURL(u"3pages-shape.odt"); + UnoApiXmlTest::loadFromURL(aURL); + + // When doing idle layout: + AnyInputCallback aAnyInput; + SwDocShell* pDocShell = getSwDocShell(); + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + pWrtShell->LayoutIdle(); + + // Then make sure that the draw task started during idle layout has no high priority, either: + SdrPaintView* pDrawView = pWrtShell->Imp()->GetDrawView(); + CPPUNIT_ASSERT(pDrawView); + const Idle& rDrawIdle = pDrawView->GetComeBackIdle(); + CPPUNIT_ASSERT(rDrawIdle.IsActive()); + // Without the accompanying fix in place, this test would have failed with: + // - Expected greater than: 4 + // - Actual : 4 + // i.e. the priority was TaskPriority::REPAINT instead of TaskPriority::DEFAULT_IDLE. + CPPUNIT_ASSERT_GREATER(TaskPriority::REPAINT, rDrawIdle.GetPriority()); +} } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/core/layout/layact.cxx b/sw/source/core/layout/layact.cxx index 0d6cdb0842b2..ec87aa49831c 100644 --- a/sw/source/core/layout/layact.cxx +++ b/sw/source/core/layout/layact.cxx @@ -69,6 +69,7 @@ #include <vector> #include <comphelper/diagnose_ex.hxx> #include <comphelper/lok.hxx> +#include <drawdoc.hxx> void SwLayAction::CheckWaitCursor() { @@ -2405,7 +2406,23 @@ SwLayIdle::SwLayIdle( SwRootFrame *pRt, SwViewShellImp *pI ) : aAction.SetInputType( VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER) ); aAction.SetIdle( true ); aAction.SetWaitAllowed( false ); + + SdrModel* pSdrModel = m_pImp->GetShell().getIDocumentDrawModelAccess().GetDrawModel(); + bool bSdrModelIdle{}; + if (pSdrModel) + { + // Let the draw views know that we're inside the idle layout. + bSdrModelIdle = pSdrModel->IsWriterIdle(); + pSdrModel->SetWriterIdle(true); + } + aAction.Action(m_pImp->GetShell().GetOut()); + + if (pSdrModel) + { + pSdrModel->SetWriterIdle(bSdrModelIdle); + } + bInterrupt = aAction.IsInterrupt(); }