Attempted to increase performance of chart paint by caching every frame.
This commit is contained in:
parent
c8e95f7297
commit
f473a1a804
|
@ -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>();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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.);
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user