Attempted to increase performance of chart paint by caching every frame.

This commit is contained in:
23rd 2023-07-20 21:06:30 +03:00 committed by John Preston
parent c8e95f7297
commit f473a1a804
5 changed files with 156 additions and 18 deletions

View File

@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Statistic { namespace Statistic {
namespace { namespace {
constexpr auto kAlphaDuration = float64(200); constexpr auto kAlphaDuration = float64(350);
} // namespace } // namespace
void ChartLineViewContext::setEnabled(int id, bool enabled, crl::time now) { void ChartLineViewContext::setEnabled(int id, bool enabled, crl::time now) {
@ -39,6 +39,23 @@ float64 ChartLineViewContext::alpha(int id) const {
return (it == end(_entries)) ? 1. : it->second.alpha; return (it == end(_entries)) ? 1. : it->second.alpha;
} }
void ChartLineViewContext::setCacheImage(int id, QImage &&image) {
(_isFooter ? _cachesFooter : _caches)[id].image = std::move(image);
}
void ChartLineViewContext::setCacheLastToken(int id, CacheToken token) {
(_isFooter ? _cachesFooter : _caches)[id].lastToken = token;
}
void ChartLineViewContext::setCacheHQ(int id, bool value) {
(_isFooter ? _cachesFooter : _caches)[id].hq = value;
}
const ChartLineViewContext::Cache &ChartLineViewContext::cache(int id) {
[[maybe_unused]] auto unused = (_isFooter ? _cachesFooter : _caches)[id];
return (_isFooter ? _cachesFooter : _caches).find(id)->second;
}
void ChartLineViewContext::tick(crl::time now) { void ChartLineViewContext::tick(crl::time now) {
auto finishedCount = 0; auto finishedCount = 0;
auto idsToRemove = std::vector<int>(); auto idsToRemove = std::vector<int>();

View File

@ -7,19 +7,70 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
#include <statistics/statistics_common.h>
namespace Statistic { namespace Statistic {
class ChartLineViewContext final { class ChartLineViewContext final {
public: public:
ChartLineViewContext() = default; ChartLineViewContext() = default;
struct CacheToken final {
explicit CacheToken() = default;
explicit CacheToken(
Limits xIndices,
Limits xPercentageLimits,
Limits heightLimits,
QSize rectSize)
: xIndices(std::move(xIndices))
, xPercentageLimits(std::move(xPercentageLimits))
, heightLimits(std::move(heightLimits))
, rectSize(std::move(rectSize)) {
}
bool operator==(const CacheToken &other) const {
return (rectSize == other.rectSize)
&& (xIndices.min == other.xIndices.min)
&& (xIndices.max == other.xIndices.max)
&& (xPercentageLimits.min == other.xPercentageLimits.min)
&& (xPercentageLimits.max == other.xPercentageLimits.max)
&& (heightLimits.min == other.heightLimits.min)
&& (heightLimits.max == other.heightLimits.max);
}
bool operator!=(const CacheToken &other) const {
return !(*this == other);
}
Limits xIndices;
Limits xPercentageLimits;
Limits heightLimits;
QSize rectSize;
};
struct Cache final {
QImage image;
CacheToken lastToken;
bool hq = false;
};
void setEnabled(int id, bool enabled, crl::time now); void setEnabled(int id, bool enabled, crl::time now);
[[nodiscard]] bool isFinished() const;
[[nodiscard]] bool isEnabled(int id) const; [[nodiscard]] bool isEnabled(int id) const;
[[nodiscard]] bool isFinished() const;
[[nodiscard]] float64 alpha(int id) const; [[nodiscard]] float64 alpha(int id) const;
void setCacheFooter(bool value) {
_isFooter = value;
}
void setCacheImage(int id, QImage &&image);
void setCacheLastToken(int id, CacheToken token);
void setCacheHQ(int id, bool value);
[[nodiscard]] const Cache &cache(int id);
void tick(crl::time now); void tick(crl::time now);
float64 factor = 1.;
private: private:
struct Entry final { struct Entry final {
bool enabled = false; bool enabled = false;
@ -28,8 +79,12 @@ private:
}; };
base::flat_map<int, Entry> _entries; base::flat_map<int, Entry> _entries;
base::flat_map<int, Cache> _caches;
base::flat_map<int, Cache> _cachesFooter;
bool _isFinished = true; bool _isFinished = true;
bool _isFooter = false;
}; };
} // namespace Statistic } // namespace Statistic

View File

@ -945,10 +945,11 @@ void ChartWidget::setupChartArea() {
: -1, : -1,
}; };
if (_chartData) { if (_chartData) {
p.setRenderHint( // p.setRenderHint(
QPainter::Antialiasing, // QPainter::Antialiasing,
!_animationController.isFPSSlow() // !_animationController.isFPSSlow()
|| !_animationController.animating()); // || !_animationController.animating());
PainterHighQualityEnabler hp(p);
Statistic::PaintLinearChartView( Statistic::PaintLinearChartView(
p, p,
_chartData, _chartData,
@ -1069,10 +1070,12 @@ void ChartWidget::setupFooter() {
if (_chartData) { if (_chartData) {
auto detailsPaintContext = DetailsPaintContext{ .xIndex = -1 }; auto detailsPaintContext = DetailsPaintContext{ .xIndex = -1 };
p.fillRect(r, st::boxBg); p.fillRect(r, st::boxBg);
p.setRenderHint( // p.setRenderHint(
QPainter::Antialiasing, // QPainter::Antialiasing,
!_animationController.isFPSSlow() // !_animationController.isFPSSlow()
|| !_animationController.animating()); // || !_animationController.animating());
PainterHighQualityEnabler hp(p);
_animatedChartLines.setCacheFooter(true);
Statistic::PaintLinearChartView( Statistic::PaintLinearChartView(
p, p,
_chartData, _chartData,
@ -1082,6 +1085,7 @@ void ChartWidget::setupFooter() {
r, r,
_animatedChartLines, _animatedChartLines,
detailsPaintContext); detailsPaintContext);
_animatedChartLines.setCacheFooter(false);
} }
}); });
@ -1185,17 +1189,37 @@ void ChartWidget::setupDetails() {
} }
void ChartWidget::setupFilterButtons() { void ChartWidget::setupFilterButtons() {
if (!_chartData) { if (!_chartData || (_chartData.lines.size() <= 1)) {
_filterButtons = nullptr; _filterButtons = nullptr;
_chartArea->update(); _chartArea->update();
return; return;
} }
_filterButtons = base::make_unique_q<ChartLinesFilterWidget>(this); _filterButtons = base::make_unique_q<ChartLinesFilterWidget>(this);
const auto asd = Ui::CreateChild<Ui::AbstractButton>(_filterButtons.get());
asd->paintRequest(
) | rpl::start_with_next([=](QRect r) {
auto p = QPainter(asd);
p.setOpacity(0.3);
p.fillRect(r, Qt::darkRed);
p.setOpacity(1.0);
p.setFont(st::statisticsDetailsBottomCaptionStyle.font);
p.setPen(st::boxTextFg);
p.drawText(asd->rect(), QString::number(_animatedChartLines.factor * 100) + "%", style::al_center);
}, asd->lifetime());
asd->setClickedCallback([=] {
_animatedChartLines.factor -= 0.1;
if (_animatedChartLines.factor <= 0) {
_animatedChartLines.factor = 1.0;
}
asd->update();
});
asd->resize(50, 50);
sizeValue( sizeValue(
) | rpl::filter([=](const QSize &s) { ) | rpl::filter([](const QSize &s) {
return s.width() > 0; return s.width() > 0;
}) | rpl::take(1) | rpl::start_with_next([=](const QSize &s) { }) | rpl::take(2) | rpl::start_with_next([=](const QSize &s) {
auto texts = std::vector<QString>(); auto texts = std::vector<QString>();
auto colors = std::vector<QColor>(); auto colors = std::vector<QColor>();
auto ids = std::vector<int>(); auto ids = std::vector<int>();
@ -1210,6 +1234,8 @@ void ChartWidget::setupFilterButtons() {
_filterButtons->fillButtons(texts, colors, ids, s.width()); _filterButtons->fillButtons(texts, colors, ids, s.width());
resizeHeight(); resizeHeight();
asd->raise();
asd->moveToRight(0, 0);
}, _filterButtons->lifetime()); }, _filterButtons->lifetime());
_filterButtons->buttonEnabledChanges( _filterButtons->buttonEnabledChanges(

View File

@ -24,11 +24,17 @@ void PaintLinearChartView(
const Limits &xPercentageLimits, const Limits &xPercentageLimits,
const Limits &heightLimits, const Limits &heightLimits,
const QRect &rect, const QRect &rect,
const ChartLineViewContext &lineViewContext, ChartLineViewContext &lineViewContext,
DetailsPaintContext &detailsPaintContext) { DetailsPaintContext &detailsPaintContext) {
const auto currentMinHeight = rect.y(); // const auto currentMinHeight = rect.y(); //
const auto currentMaxHeight = rect.height() + rect.y(); // const auto currentMaxHeight = rect.height() + rect.y(); //
const auto cacheToken = ChartLineViewContext::CacheToken(
xIndices,
xPercentageLimits,
heightLimits,
rect.size());
for (const auto &line : chartData.lines) { for (const auto &line : chartData.lines) {
p.setOpacity(lineViewContext.alpha(line.id)); p.setOpacity(lineViewContext.alpha(line.id));
if (!p.opacity()) { if (!p.opacity()) {
@ -38,6 +44,32 @@ void PaintLinearChartView(
? 0. ? 0.
: (chartData.xPercentage.front() * rect.width()); : (chartData.xPercentage.front() * rect.width());
////
const auto &cache = lineViewContext.cache(line.id);
const auto isSameToken = (cache.lastToken == cacheToken);
if (isSameToken && cache.hq) {
p.drawImage(rect.topLeft(), cache.image);
continue;
}
const auto kRatio = lineViewContext.factor;//0.5;
lineViewContext.setCacheHQ(line.id, isSameToken);
auto image = QImage();
image = QImage(
rect.size() * style::DevicePixelRatio() * (isSameToken ? 1. : kRatio),
QImage::Format_ARGB32_Premultiplied);
image.setDevicePixelRatio(style::DevicePixelRatio());
image.fill(Qt::transparent);
// image.fill(Qt::darkRed);
auto imagePainter = QPainter(&image);
imagePainter.setRenderHint(QPainter::Antialiasing, true);
if (isSameToken) {
// PainterHighQualityEnabler hp(imagePainter);
} else {
imagePainter.scale(kRatio, kRatio);
}
////
auto first = true; auto first = true;
auto chartPath = QPainterPath(); auto chartPath = QPainterPath();
@ -69,9 +101,17 @@ void PaintLinearChartView(
} }
chartPath.lineTo(xPoint, yPoint); chartPath.lineTo(xPoint, yPoint);
} }
p.setPen(QPen(line.color, st::statisticsChartLineWidth)); imagePainter.translate(-rect.topLeft());
p.setBrush(Qt::NoBrush); imagePainter.setPen(QPen(line.color, st::statisticsChartLineWidth));
p.drawPath(chartPath); imagePainter.setBrush(Qt::NoBrush);
imagePainter.drawPath(chartPath);
if (!isSameToken) {
image = image.scaled(rect.size() * style::DevicePixelRatio(), Qt::IgnoreAspectRatio, Qt::FastTransformation);
}
p.drawImage(rect.topLeft(), image);
lineViewContext.setCacheImage(line.id, std::move(image));
lineViewContext.setCacheLastToken(line.id, cacheToken);
} }
p.setPen(st::boxTextFg); p.setPen(st::boxTextFg);
p.setOpacity(1.); p.setOpacity(1.);

View File

@ -24,7 +24,7 @@ void PaintLinearChartView(
const Limits &xPercentageLimits, const Limits &xPercentageLimits,
const Limits &heightLimits, const Limits &heightLimits,
const QRect &rect, const QRect &rect,
const ChartLineViewContext &lineViewContext, ChartLineViewContext &lineViewContext,
DetailsPaintContext &detailsPaintContext); DetailsPaintContext &detailsPaintContext);
} // namespace Statistic } // namespace Statistic