perf: Only update regions with animated elements (#5043)

This commit is contained in:
nerix 2023-12-27 01:12:14 +01:00 committed by GitHub
parent eb12cfa50b
commit 9612eac966
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 72 additions and 24 deletions

View file

@ -99,7 +99,7 @@
- Dev: BREAKING: Replace custom `import()` with normal Lua `require()`. (#5014) - Dev: BREAKING: Replace custom `import()` with normal Lua `require()`. (#5014)
- Dev: Fixed most compiler warnings. (#5028) - Dev: Fixed most compiler warnings. (#5028)
- Dev: Added the ability to show `ChannelView`s without a `Split`. (#4747) - Dev: Added the ability to show `ChannelView`s without a `Split`. (#4747)
- Dev: Channels without any animated elements on screen will skip updates from the GIF timer. (#5042) - Dev: Channels without any animated elements on screen will skip updates from the GIF timer. (#5042, #5043)
## 2.4.6 ## 2.4.6

View file

@ -350,6 +350,10 @@ ChannelView::ChannelView(InternalCtor /*tag*/, QWidget *parent, Split *split,
auto curve = QEasingCurve(); auto curve = QEasingCurve();
curve.setCustomType(highlightEasingFunction); curve.setCustomType(highlightEasingFunction);
this->highlightAnimation_.setEasingCurve(curve); this->highlightAnimation_.setEasingCurve(curve);
QObject::connect(&this->highlightAnimation_,
&QVariantAnimation::valueChanged, this, [this] {
this->queueUpdate();
});
this->messageColors_.applyTheme(getTheme()); this->messageColors_.applyTheme(getTheme());
this->messagePreferences_.connectSettings(getSettings(), this->messagePreferences_.connectSettings(getSettings(),
@ -404,13 +408,13 @@ void ChannelView::initializeSignals()
}, },
this->signalHolder_); this->signalHolder_);
this->signalHolder_.managedConnect(getApp()->windows->gifRepaintRequested, this->signalHolder_.managedConnect(
[&] { getApp()->windows->gifRepaintRequested, [&] {
if (this->anyAnimationShown_) if (!this->animationArea_.isEmpty())
{ {
this->queueUpdate(); this->queueUpdate(this->animationArea_);
} }
}); });
this->signalHolder_.managedConnect( this->signalHolder_.managedConnect(
getApp()->windows->layoutRequested, [&](Channel *channel) { getApp()->windows->layoutRequested, [&](Channel *channel) {
@ -580,6 +584,11 @@ void ChannelView::queueUpdate()
this->update(); this->update();
} }
void ChannelView::queueUpdate(const QRect &area)
{
this->update(area);
}
void ChannelView::queueLayout() void ChannelView::queueLayout()
{ {
if (this->isVisible()) if (this->isVisible())
@ -1418,7 +1427,7 @@ void ChannelView::scrollToMessageLayout(MessageLayout *layout,
} }
} }
void ChannelView::paintEvent(QPaintEvent * /*event*/) void ChannelView::paintEvent(QPaintEvent *event)
{ {
// BenchmarkGuard benchmark("paint"); // BenchmarkGuard benchmark("paint");
@ -1427,7 +1436,7 @@ void ChannelView::paintEvent(QPaintEvent * /*event*/)
painter.fillRect(rect(), this->theme->splits.background); painter.fillRect(rect(), this->theme->splits.background);
// draw messages // draw messages
this->drawMessages(painter); this->drawMessages(painter, event->rect());
// draw paused sign // draw paused sign
if (this->paused()) if (this->paused())
@ -1441,7 +1450,7 @@ void ChannelView::paintEvent(QPaintEvent * /*event*/)
// if overlays is false then it draws the message, if true then it draws things // if overlays is false then it draws the message, if true then it draws things
// such as the grey overlay when a message is disabled // such as the grey overlay when a message is disabled
void ChannelView::drawMessages(QPainter &painter) void ChannelView::drawMessages(QPainter &painter, const QRect &area)
{ {
auto &messagesSnapshot = this->getMessagesSnapshot(); auto &messagesSnapshot = this->getMessagesSnapshot();
@ -1474,7 +1483,10 @@ void ChannelView::drawMessages(QPainter &painter)
}; };
bool showLastMessageIndicator = getSettings()->showLastMessageIndicator; bool showLastMessageIndicator = getSettings()->showLastMessageIndicator;
bool anyAnimation = false; QRect animationArea;
auto areaContainsY = [&area](auto y) {
return y >= area.y() && y < area.y() + area.height();
};
for (; ctx.messageIndex < messagesSnapshot.size(); ++ctx.messageIndex) for (; ctx.messageIndex < messagesSnapshot.size(); ++ctx.messageIndex)
{ {
@ -1489,16 +1501,34 @@ void ChannelView::drawMessages(QPainter &painter)
ctx.isLastReadMessage = false; ctx.isLastReadMessage = false;
} }
anyAnimation |= layout->paint(ctx).hasAnimatedElements; if (areaContainsY(ctx.y) || areaContainsY(ctx.y + layout->getHeight()))
if (this->highlightedMessage_ == layout)
{ {
painter.fillRect( auto paintResult = layout->paint(ctx);
0, ctx.y, layout->getWidth(), layout->getHeight(), if (paintResult.hasAnimatedElements)
this->highlightAnimation_.currentValue().value<QColor>());
if (this->highlightAnimation_.state() == QVariantAnimation::Stopped)
{ {
this->highlightedMessage_ = nullptr; if (animationArea.isNull())
{
animationArea = QRect{0, ctx.y, layout->getWidth(),
layout->getHeight()};
}
else
{
animationArea.setBottom(ctx.y + layout->getHeight());
animationArea.setWidth(
std::max(layout->getWidth(), animationArea.width()));
}
}
if (this->highlightedMessage_ == layout)
{
painter.fillRect(
0, ctx.y, layout->getWidth(), layout->getHeight(),
this->highlightAnimation_.currentValue().value<QColor>());
if (this->highlightAnimation_.state() ==
QVariantAnimation::Stopped)
{
this->highlightedMessage_ = nullptr;
}
} }
} }
@ -1510,7 +1540,23 @@ void ChannelView::drawMessages(QPainter &painter)
break; break;
} }
} }
this->anyAnimationShown_ = anyAnimation;
// Only update on a full repaint as some messages with animated elements
// might get left out in partial repaints.
// This happens for example when hovering over the go-to-bottom button.
if (this->height() <= area.height())
{
this->animationArea_ = animationArea;
}
#ifdef FOURTF
else
{
// shows the updated area on partial repaints
painter.setPen(Qt::red);
painter.drawRect(area.x(), area.y(), area.width() - 1,
area.height() - 1);
}
#endif
if (end == nullptr) if (end == nullptr)
{ {

View file

@ -96,6 +96,7 @@ public:
size_t messagesLimit = 1000); size_t messagesLimit = 1000);
void queueUpdate(); void queueUpdate();
void queueUpdate(const QRect &area);
Scrollbar &getScrollBar(); Scrollbar &getScrollBar();
QString getSelectedText(); QString getSelectedText();
@ -232,7 +233,7 @@ private:
void updateScrollbar(const LimitedQueueSnapshot<MessageLayoutPtr> &messages, void updateScrollbar(const LimitedQueueSnapshot<MessageLayoutPtr> &messages,
bool causedByScrollbar, bool causedByShow); bool causedByScrollbar, bool causedByShow);
void drawMessages(QPainter &painter); void drawMessages(QPainter &painter, const QRect &area);
void setSelection(const SelectionItem &start, const SelectionItem &end); void setSelection(const SelectionItem &start, const SelectionItem &end);
void setSelection(const Selection &newSelection); void setSelection(const Selection &newSelection);
void selectWholeMessage(MessageLayout *layout, int &messageIndex); void selectWholeMessage(MessageLayout *layout, int &messageIndex);
@ -273,8 +274,9 @@ private:
bool lastMessageHasAlternateBackground_ = false; bool lastMessageHasAlternateBackground_ = false;
bool lastMessageHasAlternateBackgroundReverse_ = true; bool lastMessageHasAlternateBackgroundReverse_ = true;
/// Tracks if this view has painted any animated element in the last #paintEvent(). /// Tracks the area of animated elements in the last full repaint.
bool anyAnimationShown_ = false; /// If this is empty (QRect::isEmpty()), no animated element is shown.
QRect animationArea_;
bool pausable_ = false; bool pausable_ = false;
QTimer pauseTimer_; QTimer pauseTimer_;