From c19c54dd5b85726df1b5187616d17d5430028c16 Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Wed, 11 Oct 2017 18:00:48 +0200 Subject: [PATCH] Allow multiple calls to processUpdateFlags before redraw The goal of this commit is to ensure that a processUpdateFlags call that requires no redraw will not override a previous one that did require a redraw. To this end, the semantics of the flag argument is now different: its value is now OR'ed with a private update_flags_ variable. This variable is only reset after the buffer view has actually been redrawn. A new Update::ForceRedraw flag has been added. It requires a full redraw but no metrics computation. It is not used in the main code (yet), but avoids to compute metrics repeatedly in consecutive processUpdateFlags calls. Finally the dubious call to updateMacros in updateMetrics has been removed for performance reasons. --- development/PAINTING_ANALYSIS | 21 ++++++++----- src/BufferView.cpp | 55 +++++++++++++++++++++-------------- src/BufferView.h | 7 +++-- src/update_flags.h | 14 +++++++-- 4 files changed, 61 insertions(+), 36 deletions(-) diff --git a/development/PAINTING_ANALYSIS b/development/PAINTING_ANALYSIS index f734edb3b0..ec3566e06c 100644 --- a/development/PAINTING_ANALYSIS +++ b/development/PAINTING_ANALYSIS @@ -60,12 +60,6 @@ cursor. * Clean-up of drawing code -The goal is to make painting with drawing disable fast enough that it -can be used after every metrics computation. Then we can separate real -drawing from metrics. - -Other changes are only clean-ups. - ** When a paragraph ends with a newline, compute correctly the height of the extra row. ** Merging bv::updateMetrics and tm::metrics @@ -76,7 +70,7 @@ insets. We should re-use the bv::updateMetrics logic: + transfer all the logic of bv::updateMetrics to tm. + Main InsetText should not be special. -The difficuly for a tall table cell for example, is that it may be +The difficulty for a tall table cell for example, is that it may be necessary to break the whole contents to know the width of the cell. @@ -113,11 +107,19 @@ DecorationUpdate). It triggers a recomputation of the metrics when either: existing metrics. Note that the Update::SinglePar flag is *never* taken into account. +If a computation of metrics has taken place, Force is removed from the +flags and ForceDraw is added instead. + +It is OK to call processUptateFlags several times before an update. In +this case, the effects are cumulative.processUpdateFlags execute the +metrics-related actions, but defers the actual drawing to the next +paint event. + The screen is drawn (with appropriate update strategy), except when update flag is Update::None. -** Metrics computation +** Metrics computation (and nodraw drawing phase) This is triggered by bv::updateMetrics, which calls tm::redoParagraph for all visible paragraphs. Some Paragraphs above or below the screen (needed @@ -127,6 +129,9 @@ tm::redoParagraph will call Inset::metrics for each inset. In the case of text insets, this will invoke recursively tm::metrics, which redoes all the paragraphs of the inset. +At the end of the function, bv::updatePosCache is called. It triggers +a repaint of the document with a NullPainter (a painter that does +nothing). This has the effect of caching all insets positions. ** Drawing the work area. diff --git a/src/BufferView.cpp b/src/BufferView.cpp index 53d374f07f..2c79c340c9 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -228,7 +228,8 @@ enum ScreenUpdateStrategy { struct BufferView::Private { - Private(BufferView & bv) : update_strategy_(NoScreenUpdate), + Private(BufferView & bv) : update_strategy_(FullScreenUpdate), + update_flags_(Update::Force), wh_(0), cursor_(bv), anchor_pit_(0), anchor_ypos_(0), inlineCompletionUniqueChars_(0), @@ -245,6 +246,8 @@ struct BufferView::Private /// ScreenUpdateStrategy update_strategy_; /// + Update::flags update_flags_; + /// CoordCache coord_cache_; /// Estimated average par height for scrollbar. @@ -447,16 +450,16 @@ bool BufferView::needsFitCursor() const void BufferView::processUpdateFlags(Update::flags flags) { - // This is close to a hot-path. - LYXERR(Debug::PAINTING, "BufferView::processUpdateFlags()" - << "[fitcursor = " << (flags & Update::FitCursor) - << ", forceupdate = " << (flags & Update::Force) - << ", singlepar = " << (flags & Update::SinglePar) - << "] buffer: " << &buffer_); + // The update flags are reset to None after the redraw is actually done + d->update_flags_ = d->update_flags_ | flags; - // FIXME Does this really need doing here? It's done in updateBuffer, and - // if the Buffer doesn't need updating, then do the macros? - buffer_.updateMacros(); + // This is close to a hot-path. + LYXERR(Debug::PAINTING, "BufferView::processUpdateFlags( " + << ((d->update_flags_ & Update::FitCursor) ? "fitcursor " : "") + << ((d->update_flags_ & Update::Force) ? "forceupdate " : "") + << ((d->update_flags_ & Update::ForceDraw) ? "forcedraw " : "") + << ((d->update_flags_ & Update::SinglePar) ? "singlepar " : "") + << ") buffer: " << &buffer_); // Now do the first drawing step if needed. This consists on updating // the CoordCache in updateMetrics(). @@ -464,26 +467,26 @@ void BufferView::processUpdateFlags(Update::flags flags) // FIXME: is this still true now that Buffer::changed() is used all over? // Case when no explicit update is requested. - if (!flags) { + if (!d->update_flags_) { // no need to redraw anything. d->update_strategy_ = NoScreenUpdate; return; } - if (flags == Update::Decoration) { + if (d->update_flags_ == Update::Decoration) { d->update_strategy_ = DecorationUpdate; buffer_.changed(false); return; } - if (flags == Update::FitCursor - || flags == (Update::Decoration | Update::FitCursor)) { + if (d->update_flags_ == Update::FitCursor + || d->update_flags_ == (Update::Decoration | Update::FitCursor)) { // tell the frontend to update the screen if needed. if (needsFitCursor()) { showCursor(); return; } - if (flags & Update::Decoration) { + if (d->update_flags_ & Update::Decoration) { d->update_strategy_ = DecorationUpdate; buffer_.changed(false); return; @@ -495,22 +498,21 @@ void BufferView::processUpdateFlags(Update::flags flags) return; } + // We test against the flags parameter here to honor explicit metrics requests bool const full_metrics = flags & Update::Force || !singleParUpdate(); if (full_metrics) // We have to update the full screen metrics. updateMetrics(); + if (d->update_flags_ & Update::ForceDraw) + d->update_strategy_ = FullScreenUpdate; - if (!(flags & Update::FitCursor)) { - // Nothing to do anymore. Trigger a redraw and return + if (!(d->update_flags_ & Update::FitCursor)) { + // Nothing to do anymore. Trigger a redraw and return. buffer_.changed(false); return; } - // updateMetrics() does not update paragraph position - // This is done at draw() time. So we need a redraw! - buffer_.changed(false); - if (needsFitCursor()) { // The cursor is off screen so ensure it is visible. // refresh it: @@ -518,6 +520,9 @@ void BufferView::processUpdateFlags(Update::flags flags) } updateHoveredInset(); + + // Trigger a redraw. + buffer_.changed(false); } @@ -2741,7 +2746,8 @@ void BufferView::updateMetrics() << " pit1 = " << pit1 << " pit2 = " << pit2); - d->update_strategy_ = FullScreenUpdate; + // It is not necessary anymore to compute metrics, but a redraw is needed + d->update_flags_ = (d->update_flags_ & ~Update::Force) | Update::ForceDraw; // Now update the positions of insets in the cache. updatePosCache(); @@ -3149,6 +3155,11 @@ void BufferView::draw(frontend::Painter & pain, bool paint_caret) LYXERR(Debug::PAINTING, "Found new anchor pit = " << d->anchor_pit_ << " anchor ypos = " << d->anchor_ypos_); + if (!pain.isNull()) { + // reset the update flags, everything has been done + d->update_flags_ = Update::None; + } + // Remember what has just been done for the next draw() step if (paint_caret) d->caret_slice_ = d->cursor_.top(); diff --git a/src/BufferView.h b/src/BufferView.h index a5222599f2..7f0bcca9e4 100644 --- a/src/BufferView.h +++ b/src/BufferView.h @@ -120,11 +120,12 @@ public: /// \return true if the BufferView is at the bottom of the document. bool isBottomScreen() const; - /// perform pending metrics updates. - /** \c Update::FitCursor means first to do a FitCursor, and to + /// Add \p flags to current updte flags and trigger an update. + /* If this method is invoked several times before the update + * actually takes place, the effect is cumulative. + * \c Update::FitCursor means first to do a FitCursor, and to * force an update if screen position changes. * \c Update::Force means to force an update in any case. - * \retval true if a screen redraw is needed */ void processUpdateFlags(Update::flags flags); diff --git a/src/update_flags.h b/src/update_flags.h index 3e877c1ca1..a40e88c556 100644 --- a/src/update_flags.h +++ b/src/update_flags.h @@ -21,13 +21,16 @@ namespace Update { /// Recenter the screen around the cursor if is found outside the /// visible area. FitCursor = 1, - /// Force a full screen metrics update. + /// Force a full screen metrics update and a full draw. Force = 2, + /// Force a full redraw (but no metrics computations) + ForceDraw = 4, /// Try to rebreak only the current paragraph metrics. - SinglePar = 4, + /// (currently ignored!) + SinglePar = 8, /// Only the inset decorations need to be redrawn, no text metrics /// update is needed. - Decoration = 8 + Decoration = 16 }; inline flags operator|(flags const f, flags const g) @@ -40,6 +43,11 @@ inline flags operator&(flags const f, flags const g) return static_cast(int(f) & int(g)); } +inline flags operator~(flags const f) +{ + return static_cast(~int(f)); +} + } // namespace Update } // namespace lyx -- 2.39.2