#pragma once #include "Core/Statistics/Statistics.h" #include "../RenderAble.h" #include 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 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 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 mInfoMap; QMap mCounter; QMap mInfos; QVector 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 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(std::chrono::system_clock::now() - mStart).count() * 1000; mPerformanceShower->recordCost(mName, useTime); } }; }