Files
Renderive/YSGraphic_Core/base/PerformanceShower_p.h
T
2026-07-23 13:54:06 +08:00

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);
}
};
}