Hi *, please find attached[1] a patch (diff between master and the words-layout_performance-sebsauer branch) that improves layouting-performance of Calligra Words.
This is done by getting right of the eventloop-reentry that happened quit a lot before during layouting [2]. This is the change done to KWTextDocumentLayout::layout . This now has the effect that we do layout way more thinks at once before going to the eventloop (and updating what is displayed). All problems that showed up cause of this changed behavior where fixed and fixes where merged already into master [3] what means, that after applying the patch there should be no regressions, no changed behavior [3]. Ok to merge? [1] I still have some problems with reviewboard and seems it will take a bit longer till they are sorted out, therefore I wasn't able to use reviewboard. [2] Before loading the first 100 pages of ODF 1.1 specs took ~22 seconds on my quad-core while now it takes ~8 seconds. [3] Crashes or even infinite update-loops () which are very visible with the new no-QTimer::singleShot's design and where before very likely only hit in an unreproducable, random way. [4] As you can imagine it would rock if we could unittest this. At the moment it's not possible or at least canot be easily done without lot of work. The plan here is that such tests will be added once we refactor that code to make it a) easier to read and understand and b) allow proper unittesting of single parts/cases.
diff --git a/libs/kotext/KoTextDocumentLayout.h b/libs/kotext/KoTextDocumentLayout.h index 01e6123..c0f216e 100644 --- a/libs/kotext/KoTextDocumentLayout.h +++ b/libs/kotext/KoTextDocumentLayout.h @@ -1,5 +1,6 @@ /* This file is part of the KDE project * Copyright (C) 2006-2007, 2009 Thomas Zander <zan...@kde.org> + * Copyright (C) 2006, 2011 Sebastian Sauer <m...@dipe.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -139,7 +140,7 @@ public: /// return the list of shapes that will be used to run all the text into. virtual QList<KoShape*> shapes() const; - // reset all inline object which document position is bigger or equal to resetPosition + /// reset all inline object which document position is bigger or equal to resetPosition virtual void resetInlineObject(int resetPosition); /** @@ -159,10 +160,13 @@ public: virtual void reset() = 0; /// returns true if reset has been called. virtual bool isInterrupted() const = 0; - /// return the number of columns of the line to be layouted + /** + * return the number of columns of the line to be layouted. + * if numColumns() returns 0, use width() instead. + */ virtual int numColumns() { return 0; - } // if numColumns() returns 0, use width() instead + } /// return the width of the line to be layouted virtual qreal width() = 0; /// return the x position of the line to be layouted @@ -219,11 +223,11 @@ public: virtual QTextLine createLine() = 0; /// Fiddle with the width of the current textline until it fits virtual void fitLineForRunAround(bool resetHorizontalPosition) = 0; - // add inline object + /// add inline object virtual void insertInlineObject(KoTextAnchor * textAnchor) = 0; - // reset all inline object which document position is bigger or equal to resetPosition + /// reset all inline object which document position is bigger or equal to resetPosition virtual void resetInlineObject(int resetPosition) = 0; - // remove inline object + /// remove inline object virtual void removeInlineObject(KoTextAnchor * textAnchor) = 0; /// the index in the list of shapes (or frameset) of the shape we are currently layouting. int shapeNumber; diff --git a/plugins/textshape/TextShape.cpp b/plugins/textshape/TextShape.cpp index bd019f9..5806329 100644 --- a/plugins/textshape/TextShape.cpp +++ b/plugins/textshape/TextShape.cpp @@ -101,7 +101,7 @@ TextShape::TextShape(KoInlineTextObjectManager *inlineTextObjectManager) KoTextDocument(m_textShapeData->document()).setInlineTextObjectManager(inlineTextObjectManager); setCollisionDetection(true); - lay->connect(m_textShapeData, SIGNAL(relayout()), SLOT(scheduleLayout())); + QObject::connect(m_textShapeData, SIGNAL(relayout()), lay, SLOT(scheduleLayout()), Qt::QueuedConnection); } TextShape::~TextShape() diff --git a/words/part/KWDocument.cpp b/words/part/KWDocument.cpp index dd95b1a..3d2590e 100644 --- a/words/part/KWDocument.cpp +++ b/words/part/KWDocument.cpp @@ -84,6 +84,7 @@ #include <QThread> #include <QCoreApplication> #include <QTextBlock> +#include <QTime> /// \internal // this class will be added to all views and be hidden by default. @@ -806,6 +807,8 @@ void KWDocument::requestMoreSpace(KWTextFrameSet *fs) if (page.pageSide() == KWPage::PageSpread) pageDiff--; if (pageDiff >= (lastFrame->frameOnBothSheets() ? 1 : 2)) { + //kDebug() << "frameSet=" << fs << "pageDiff=" << pageDiff << "pageCount=" << m_pageManager.pageCount() << "pageNumber=" << page.pageNumber(); + // its enough to just create a new frame. m_frameLayout.createNewFrameForPage(fs, page.pageNumber() + (lastFrame->frameOnBothSheets() ? 1 : 2)); @@ -814,8 +817,11 @@ void KWDocument::requestMoreSpace(KWTextFrameSet *fs) KWPage last = m_pageManager.last(); if (last.isValid()) afterPageNum = last.pageNumber(); + + //kDebug() << "frameSet=" << fs << "pageDiff=" << pageDiff << "pageCount=" << m_pageManager.pageCount() << "pageNumber=" << page.pageNumber() << "afterPageNum=" << afterPageNum; + KWPageInsertCommand cmd(this, afterPageNum, masterPageName); - cmd.redo(); + cmd.redo(); // does also schedule an update using the PageProcessingQueue KWPage newPage = cmd.page(); Q_ASSERT(newPage.isValid()); newPage.setAutoGenerated(true); @@ -987,19 +993,25 @@ void PageProcessingQueue::addPage(KWPage page) void PageProcessingQueue::process() { - m_triggered = false; const bool docIsEmpty = m_document->isEmpty(); const bool docIsModified = m_document->isModified(); - foreach (int pageNumber, m_pages) { + QList<int> pages = m_pages; + m_triggered = false; + m_pages.clear(); + + QTime timer; + timer.start(); + qSort(pages.begin(), pages.end()); + foreach (int pageNumber, pages) { m_document->m_frameLayout.createNewFramesForPage(pageNumber); } - + kDebug(32001) << "pages=" << pages << "elapsed=" << timer.elapsed(); + if (docIsEmpty) m_document->setEmpty(); if (!docIsModified) m_document->setModified(false); if (m_deleteLater) deleteLater(); - m_pages.clear(); emit m_document->pageSetupChanged(); } diff --git a/words/part/frames/KWFrameLayout.cpp b/words/part/frames/KWFrameLayout.cpp index 76764f3..6e6a45a 100644 --- a/words/part/frames/KWFrameLayout.cpp +++ b/words/part/frames/KWFrameLayout.cpp @@ -1,6 +1,7 @@ /* This file is part of the KDE project * Copyright (C) 2006-2010 Thomas Zander <zan...@kde.org> * Copyright (C) 2008 Pierre Ducroquet <pina...@pinaraf.info> + * Copyright (C) 2008,2011 Sebastian Sauer <m...@dipe.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public diff --git a/words/part/frames/KWTextDocumentLayout.cpp b/words/part/frames/KWTextDocumentLayout.cpp index 87eb991..cc6b7f7 100644 --- a/words/part/frames/KWTextDocumentLayout.cpp +++ b/words/part/frames/KWTextDocumentLayout.cpp @@ -226,6 +226,10 @@ void KWTextDocumentLayout::positionInlineObject(QTextInlineObject item, int posi void KWTextDocumentLayout::layout() { +#ifdef TDEBUG + TDEBUG << "starting layout pass, document=" << ((void*)document()) << "frameSet=" << m_frameSet << "headerFooter=" << KWord::isHeaderFooter(m_frameSet); +#endif + class End { public: @@ -244,8 +248,10 @@ void KWTextDocumentLayout::layout() }; End ender(m_frameSet, m_state); // poor mans finally{} - if (! m_state->start()) + if (!m_state->start()) { + kDebug() << "start layouting failed"; return; + } qreal endPos = 1E9; qreal bottomOfText = 0.0; @@ -256,6 +262,14 @@ void KWTextDocumentLayout::layout() KoShape *currentShape = 0; while (m_state->shape) { +#ifdef DEBUG_TEXT + TDEBUG << "> loop.... layout has" << m_state->layout->lineCount() << "lines"; + for (int i = 0; i < m_state->layout->lineCount(); ++i) { + QTextLine line = m_state->layout->lineAt(i); + TDEBUG << i << "]" << (line.isValid() ? QString("%1 - %2").arg(line.textStart()).arg(line.textLength()) : QString("invalid")); + } +#endif + if (m_state->shape != currentShape) { // next shape TDEBUG << "New shape"; currentShape = m_state->shape; @@ -359,7 +373,7 @@ void KWTextDocumentLayout::layout() } if (m_state->isInterrupted() || (newParagraph && m_state->y() > endPos)) { // enough for now. Try again later. - kDebug() << "schedule a next layout due to having done a layout of quite some space, interrupted="<<m_state->isInterrupted()<<"m_state->y()="<<m_state->y()<<"endPos="<<endPos; + TDEBUG << "schedule a next layout due to having done a layout of quite some space, interrupted="<<m_state->isInterrupted()<<"m_state->y()="<<m_state->y()<<"endPos="<<endPos; scheduleLayoutWithoutInterrupt(); return; } diff --git a/words/part/frames/KWTextFrameSet.cpp b/words/part/frames/KWTextFrameSet.cpp index 3eeba00..8bf98cf 100644 --- a/words/part/frames/KWTextFrameSet.cpp +++ b/words/part/frames/KWTextFrameSet.cpp @@ -1,6 +1,7 @@ /* This file is part of the KDE project * Copyright (C) 2006-2010 Thomas Zander <zan...@kde.org> * Copyright (C) 2008 Pierre Ducroquet <pina...@pinaraf.info> + * Copyright (C) 2006,2008 Sebastian Sauer <m...@dipe.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -175,25 +176,29 @@ void KWTextFrameSet::updateTextLayout() return; } KWTextDocumentLayout *lay = dynamic_cast<KWTextDocumentLayout*>(m_document->documentLayout()); - if (lay) - lay->scheduleLayout(); + if (lay) { + // Don't schedule the layout what would wait with the layout till the eventloop kicks + // in what sucks performance-wise. So, start the layouting right away. + //lay->scheduleLayout(); + lay->relayout(); + } } void KWTextFrameSet::requestMoreFrames(qreal textHeight) { - //kDebug() <<"KWTextFrameSet::requestMoreFrames" << textHeight; if (frameCount() == 0) return; // there is no way we can get more frames anyway. KWTextFrame *lastFrame = static_cast<KWTextFrame*>(frames()[frameCount()-1]); - if (!lastFrame) - return; + Q_ASSERT(lastFrame); if (KWord::isHeaderFooter(this)) { KWTextFrame *frame = static_cast<KWTextFrame*>(frames().first()); frame->setMinimumFrameHeight(frame->minimumFrameHeight() + textHeight + 1E-6); + //kDebug(32001)<<"Header/Footer frameSet="<<this<<"lastFrame="<<lastFrame<<"allowLayout="<<allowLayout()<<"textHeight="<<textHeight; if (allowLayout()) emit decorationFrameResize(this); } else if (textHeight == 0.0 || lastFrame->frameBehavior() == KWord::AutoCreateNewFrameBehavior) { + //kDebug(32001)<<"AutoCreateNewFrameBehavior frameSet="<<this<<"lastFrame="<<lastFrame<<"ReconnectNewFrame="<<(lastFrame->newFrameBehavior() == KWord::ReconnectNewFrame)<<"textHeight="<<textHeight; if (lastFrame->newFrameBehavior() == KWord::ReconnectNewFrame) emit moreFramesNeeded(this); } else if (lastFrame->frameBehavior() == KWord::AutoExtendFrameBehavior @@ -204,27 +209,31 @@ void KWTextFrameSet::requestMoreFrames(qreal textHeight) requestMoreFrames(0); return; } + QSizeF size = shape->size(); QPointF orig = shape->absolutePosition(KoFlake::TopLeftCorner); shape->setSize(QSizeF(size.width(), size.height() + textHeight + 1E-6)); shape->setAbsolutePosition(orig, KoFlake::TopLeftCorner); shape->update(QRectF(0.0, size.height(), size.width(), textHeight + 1E-6)); lastFrame->allowToGrow(); + + //kDebug(32001)<<"AutoExtendFrameBehavior frameSet="<<this<<"lastFrame="<<lastFrame<<"size="<<size<<"orig="<<orig<<"textHeight="<<textHeight;; } } void KWTextFrameSet::spaceLeft(qreal excessHeight) { -//kDebug() <<"KWTextFrameSet::spaceLeft" << excessHeight; Q_ASSERT(excessHeight >= 0); if (m_frames.count() == 0) return; if (KWord::isHeaderFooter(this)) { KWTextFrame *frame = static_cast<KWTextFrame*>(frames().first()); + kDebug(32001) <<"KWTextFrameSet::spaceLeft" << frame->minimumFrameHeight() << excessHeight; frame->setMinimumFrameHeight(frame->minimumFrameHeight() - excessHeight); emit decorationFrameResize(this); return; } + //kDebug(32001) <<"KWTextFrameSet::spaceLeft" << excessHeight; QList<KWFrame*>::Iterator iter = --m_frames.end(); do { KWTextFrame *tf = dynamic_cast<KWTextFrame*>(*(iter)); @@ -241,7 +250,7 @@ void KWTextFrameSet::spaceLeft(qreal excessHeight) void KWTextFrameSet::framesEmpty(int emptyFrames) { - //kDebug() <<"KWTextFrameSet::framesEmpty" << emptyFrames; + //kDebug(32001) <<"KWTextFrameSet::framesEmpty" << emptyFrames; if (m_pageManager == 0) // be lazy; just refuse to delete frames if we don't know which are on which page return; if (KWord::isHeaderFooter(this)) // then we are deleted by the frameManager
_______________________________________________ calligra-devel mailing list calligra-devel@kde.org https://mail.kde.org/mailman/listinfo/calligra-devel