178 lines
6.7 KiB
C++
178 lines
6.7 KiB
C++
#pragma once
|
|
#include "Core/Statistics/Statistics.h"
|
|
#include "../RenderAble.h"
|
|
#include <atomic>
|
|
namespace YSG {
|
|
struct PerformanceLine {
|
|
PerformanceLine() = default;
|
|
explicit PerformanceLine(const QString& data) {
|
|
mData = data;
|
|
int argIndex = 1;
|
|
while(data.contains("%" + QString::number(argIndex))) {
|
|
argIndex++;
|
|
}
|
|
mPlaceHolderNum = argIndex - 1;
|
|
mValues.resize(mPlaceHolderNum);
|
|
}
|
|
QString mData;
|
|
int mPlaceHolderNum{};
|
|
QVector<double> mValues;
|
|
Psc::Value_Statistics mStatistics;
|
|
void update(double value) {
|
|
mStatistics.update(value);
|
|
if(mValues.size() > 0) mValues[0] = mStatistics.instant;
|
|
if(mValues.size() > 1) mValues[1] = mStatistics.smooth;
|
|
if(mValues.size() > 2) mValues[2] = mStatistics.variation;
|
|
}
|
|
QString toString() {
|
|
QString ret = mData;
|
|
int argIndex = 1;
|
|
for(const double& value : mValues) {
|
|
ret = ret.replace("%" + QString::number(argIndex), QString::number(value, 'f', 2));
|
|
argIndex++;
|
|
}
|
|
return ret;
|
|
}
|
|
};
|
|
struct CounterLine {
|
|
QString mName;
|
|
Psc::Speed_Statistics mStatistics;
|
|
[[nodiscard]] QString toString() const {
|
|
return QString("%1 :%2 avg:%3 (次/s)")
|
|
.arg(mName, QString::number(mStatistics.instant_speed, 'f', 2), QString::number(mStatistics.average_speed, 'f', 2));
|
|
}
|
|
};
|
|
enum class PerformanceEventType : std::uint8_t {
|
|
Clear,
|
|
Counter,
|
|
Line,
|
|
Info,
|
|
Cost
|
|
};
|
|
struct PerformanceEvent {
|
|
PerformanceEventType type = PerformanceEventType::Clear;
|
|
QString key;
|
|
QString value;
|
|
PerformanceLine line;
|
|
double number{};
|
|
};
|
|
struct PerformanceShowerRenderState : RenderState {
|
|
QFont font;
|
|
int topMargin = 4;
|
|
int leftMargin = 4;
|
|
int rightMargin = 28;
|
|
int bottomMargin = 8;
|
|
QColor background = Qt::white;
|
|
QColor foreground = Qt::black;
|
|
};
|
|
struct PerformanceShowerInputData : InputData {
|
|
QVector<PerformanceEvent> events;
|
|
void clear() override {
|
|
events.clear();
|
|
}
|
|
};
|
|
struct PerformanceShowerPrivate;
|
|
class PerformanceShower : public RenderAble {
|
|
public:
|
|
PerformanceShower();
|
|
PerformanceShowerPrivate* d();
|
|
RenderData* createRenderData() override;
|
|
RenderState* createRenderState() override;
|
|
InputData* createInputData() override;
|
|
void setEnabled(bool enabled);
|
|
bool enabled() const;
|
|
void refreshCounter();
|
|
void counterIncrease(const QString& counterName);
|
|
void setPerformanceLine(const QString& key, const PerformanceLine& line);
|
|
void setInfo(const QString& key, const QString& value);
|
|
void recordCost(const QString& key, double value);
|
|
private:
|
|
void appendEvent(const PerformanceEvent& event);
|
|
std::atomic_bool mEnabled{false};
|
|
};
|
|
struct PerformanceShowerPrivate : RenderData {
|
|
D_Ptr(PerformanceShower)
|
|
QMap<QString, PerformanceLine> mInfoMap;
|
|
QMap<QString, CounterLine> mCounter;
|
|
QMap<QString, QString> mInfos;
|
|
QVector<QString> mAllInfo;
|
|
void prepareData() override {
|
|
syncStatePipeline();
|
|
auto input = renderInputData();
|
|
for(const PerformanceEvent& event : input->events) {
|
|
applyEvent(event);
|
|
}
|
|
clearRenderInput();
|
|
rebuildLines();
|
|
}
|
|
void draw(QPainter* painter) override {
|
|
if(!q()->enabled() || mAllInfo.isEmpty()) return;
|
|
auto s = renderState();
|
|
painter->save();
|
|
painter->setFont(s->font);
|
|
QFontMetrics fm(s->font);
|
|
int maxWidth = 0;
|
|
for(const QString& line : mAllInfo) maxWidth = qMax(maxWidth, fm.horizontalAdvance(line));
|
|
int width = maxWidth + s->leftMargin + s->rightMargin;
|
|
int height = fm.lineSpacing() * mAllInfo.size() + s->topMargin + s->bottomMargin;
|
|
QRect rect(0, 0, width, height);
|
|
painter->fillRect(rect, s->background);
|
|
painter->setPen(s->foreground);
|
|
for(int i = 0; i < mAllInfo.size(); ++i) {
|
|
painter->drawText(QPointF(s->leftMargin, s->topMargin + i * fm.lineSpacing() + fm.lineSpacing()), mAllInfo[i]);
|
|
}
|
|
painter->restore();
|
|
}
|
|
void applyEvent(const PerformanceEvent& event) {
|
|
switch(event.type) {
|
|
case PerformanceEventType::Clear:
|
|
mInfoMap.clear();
|
|
mCounter.clear();
|
|
mInfos.clear();
|
|
mAllInfo.clear();
|
|
break;
|
|
case PerformanceEventType::Counter:
|
|
if(!mCounter.contains(event.key)) mCounter[event.key].mName = event.key;
|
|
mCounter[event.key].mStatistics.update(1);
|
|
break;
|
|
case PerformanceEventType::Line:
|
|
mInfoMap[event.key] = event.line;
|
|
break;
|
|
case PerformanceEventType::Info:
|
|
mInfos[event.key] = event.value;
|
|
break;
|
|
case PerformanceEventType::Cost:
|
|
if(!mInfoMap.contains(event.key)) mInfoMap[event.key] = PerformanceLine(event.key);
|
|
mInfoMap[event.key].update(event.number);
|
|
break;
|
|
}
|
|
}
|
|
void rebuildLines() {
|
|
QVector<QString> lines;
|
|
for(PerformanceLine& line : mInfoMap) lines.append(line.toString());
|
|
for(CounterLine& counter : mCounter) lines.append(counter.toString());
|
|
for(auto it = mInfos.cbegin(); it != mInfos.cend(); ++it) {
|
|
lines.append(QString("%1: %2").arg(it.key(), it.value()));
|
|
}
|
|
mAllInfo = lines;
|
|
}
|
|
};
|
|
class Cacl {
|
|
public:
|
|
std::chrono::system_clock::time_point mStart;
|
|
QString mName;
|
|
PerformanceShower *mPerformanceShower{};
|
|
Cacl(const QString& name, PerformanceShower* performanceShower) {
|
|
if(!performanceShower || !performanceShower->enabled()) return;
|
|
mStart = std::chrono::system_clock::now();
|
|
mName = name;
|
|
mPerformanceShower = performanceShower;
|
|
}
|
|
~Cacl() {
|
|
if(!mPerformanceShower || !mPerformanceShower->enabled()) return;
|
|
double useTime = std::chrono::duration<double>(std::chrono::system_clock::now() - mStart).count() * 1000;
|
|
mPerformanceShower->recordCost(mName, useTime);
|
|
}
|
|
};
|
|
}
|