修复 性能测试竞态bug
This commit is contained in:
@@ -1,106 +1,67 @@
|
||||
#include "PerformanceShower_p.h"
|
||||
#include <QPainter>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace YSG {
|
||||
PerformanceShower::PerformanceShower(QWidget *parent) : QWidget(parent) {
|
||||
setAttribute(Qt::WA_OpaquePaintEvent, true);
|
||||
auto layout = new QVBoxLayout(this);
|
||||
layout->setSpacing(0);
|
||||
PerformanceShower::PerformanceShower() {
|
||||
dPtr = new PerformanceShowerPrivate;
|
||||
mObjectName = "PerformanceShower";
|
||||
}
|
||||
PerformanceShowerPrivate* PerformanceShower::d() {
|
||||
return reinterpret_cast<PerformanceShowerPrivate*>(dPtr);
|
||||
}
|
||||
RenderData* PerformanceShower::createRenderData() {
|
||||
return new PerformanceShowerPrivate;
|
||||
}
|
||||
RenderState* PerformanceShower::createRenderState() {
|
||||
return new PerformanceShowerRenderState;
|
||||
}
|
||||
InputData* PerformanceShower::createInputData() {
|
||||
return new PerformanceShowerInputData;
|
||||
}
|
||||
void PerformanceShower::setEnabled(bool enabled) {
|
||||
mEnabled.store(enabled, std::memory_order_release);
|
||||
if(enabled) {
|
||||
appendEvent({PerformanceEventType::Clear});
|
||||
}
|
||||
markRenderDirty();
|
||||
}
|
||||
bool PerformanceShower::enabled() const {
|
||||
return mEnabled.load(std::memory_order_acquire);
|
||||
}
|
||||
void PerformanceShower::refreshCounter() {
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
for(CounterLine& value : mCounter) {
|
||||
value.mStatistics.clear();
|
||||
}
|
||||
appendEvent({PerformanceEventType::Clear});
|
||||
}
|
||||
void PerformanceShower::counterIncrease(const QString& counterName) {
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
if(!mCounter.contains(counterName)) {
|
||||
mCounter[counterName].mName = counterName;
|
||||
}
|
||||
mCounter[counterName].mStatistics.update(1);
|
||||
PerformanceEvent event;
|
||||
event.type = PerformanceEventType::Counter;
|
||||
event.key = counterName;
|
||||
appendEvent(event);
|
||||
}
|
||||
void PerformanceShower::setPerformanceLine(const QString& key, const PerformanceLine& line) {
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
mInfoMap[key] = line;
|
||||
PerformanceEvent event;
|
||||
event.type = PerformanceEventType::Line;
|
||||
event.key = key;
|
||||
event.line = line;
|
||||
appendEvent(event);
|
||||
}
|
||||
void PerformanceShower::setInfo(const QString& key, const QString& value) {
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
mInfos[key] = value;
|
||||
PerformanceEvent event;
|
||||
event.type = PerformanceEventType::Info;
|
||||
event.key = key;
|
||||
event.value = value;
|
||||
appendEvent(event);
|
||||
}
|
||||
void PerformanceShower::paintEvent(QPaintEvent *event) {
|
||||
QPainter painter(this);
|
||||
QPoint topLeft = rect().topLeft();
|
||||
int x = topLeft.x() + mLeftMargin;
|
||||
int y = topLeft.y() + mTopMargin;
|
||||
painter.fillRect(rect(), Qt::white);
|
||||
painter.setFont(mFont);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
QFontMetrics fm(mFont);
|
||||
int n = mAllInfo.size();
|
||||
for(int i = 0; i < n; i++) {
|
||||
painter.drawText(topLeft + QPointF(x, y + i * fm.lineSpacing() + fm.lineSpacing()), mAllInfo[i]);
|
||||
}
|
||||
}
|
||||
painter.end();
|
||||
void PerformanceShower::recordCost(const QString& key, double value) {
|
||||
PerformanceEvent event;
|
||||
event.type = PerformanceEventType::Cost;
|
||||
event.key = key;
|
||||
event.number = value;
|
||||
appendEvent(event);
|
||||
}
|
||||
|
||||
void PerformanceShower::mousePressEvent(QMouseEvent *event) {
|
||||
if(mMoving) return;
|
||||
if(event->button() == Qt::LeftButton) {
|
||||
mMoving = true;
|
||||
mStartPos = pos();
|
||||
mStartGlobalPos = event->globalPos();
|
||||
}
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void PerformanceShower::mouseMoveEvent(QMouseEvent *event) {
|
||||
if(!mMoving) return;
|
||||
move(mStartPos + event->globalPos() - mStartGlobalPos);
|
||||
event->accept();
|
||||
}
|
||||
void PerformanceShower::mouseReleaseEvent(QMouseEvent *event) {
|
||||
if(!mMoving) return;
|
||||
mMoving = false;
|
||||
event->accept();
|
||||
}
|
||||
|
||||
QVector<QString> PerformanceShower::all() {
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
QVector<QString> infoList = mFixedInfo;
|
||||
for(PerformanceLine& l : mInfoMap) infoList.append(l.toString());
|
||||
for(CounterLine& c : mCounter) infoList.append(c.toString());
|
||||
for(auto it = mInfos.cbegin(); it != mInfos.cend(); ++it) {
|
||||
infoList.append(QString("%1: %2").arg(it.key(), it.value()));
|
||||
}
|
||||
return infoList;
|
||||
}
|
||||
|
||||
void PerformanceShower::refresh() {
|
||||
QVector<QString> infoList = all();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
mAllInfo = infoList;
|
||||
int n = mAllInfo.size();
|
||||
if(mOldSize != n) {
|
||||
int maxWidth = 0;
|
||||
QFontMetrics fm(mFont);
|
||||
for(int i = 0; i < n; i++) maxWidth = qMax(maxWidth, fm.horizontalAdvance(mAllInfo[i]));
|
||||
int w = maxWidth + mLeftMargin + mRightMargin;
|
||||
int h = fm.lineSpacing() * n + mTopMargin + mBottomMargin;
|
||||
QMetaObject::invokeMethod(this, "firstResize", Q_ARG(int, w), Q_ARG(int, h));
|
||||
}
|
||||
mOldSize = n;
|
||||
}
|
||||
QMetaObject::invokeMethod(this, "update");
|
||||
}
|
||||
void PerformanceShower::firstResize(int w, int h) {
|
||||
resize(w, h);
|
||||
}
|
||||
void PerformanceShower::resizeEvent(QResizeEvent *event) {
|
||||
event->accept();
|
||||
void PerformanceShower::appendEvent(const PerformanceEvent& event) {
|
||||
if(!enabled() && event.type != PerformanceEventType::Clear) return;
|
||||
auto pd = d();
|
||||
Triple_Buffer_Lease inputLease = pd->mInputControl.wait_mark_use_role(Input_Edit);
|
||||
auto input = reinterpret_cast<PerformanceShowerInputData*>(pd->inputDataByIndex(inputLease.index));
|
||||
input->events.append(event);
|
||||
RenderInputGuard inputGuard(pd, inputLease);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include "Core/Statistics/Statistics.h"
|
||||
#include "../RenderAble.h"
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
namespace YSG {
|
||||
struct PerformanceLine {
|
||||
PerformanceLine() = default;
|
||||
@@ -37,65 +37,141 @@ namespace YSG {
|
||||
struct CounterLine {
|
||||
QString mName;
|
||||
Psc::Speed_Statistics mStatistics;
|
||||
[[nodiscard]] double getRate() const {
|
||||
return mStatistics.instant_speed;
|
||||
}
|
||||
[[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));
|
||||
}
|
||||
};
|
||||
class PerformanceShower : public QWidget {
|
||||
Q_OBJECT
|
||||
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:
|
||||
explicit PerformanceShower(QWidget *parent = nullptr);
|
||||
QFont mFont;
|
||||
int mTopMargin = 4, mLeftMargin = 4, mRightMargin = 28, mBottomMargin = 8;
|
||||
int mOldSize = -1;
|
||||
QVector<QString> mAllInfo;
|
||||
Q_INVOKABLE void firstResize(int w, int h);
|
||||
void refresh();
|
||||
QVector<QString> all();
|
||||
std::mutex mMutex;
|
||||
QMap<QString, PerformanceLine> mInfoMap;
|
||||
QMap<QString, CounterLine> mCounter;
|
||||
QVector<QString> mFixedInfo;
|
||||
QMap<QString, QString> mInfos;
|
||||
|
||||
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);
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
bool mMoving = false;
|
||||
QPoint mStartPos, mStartGlobalPos;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
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) return;
|
||||
if(!performanceShower || !performanceShower->enabled()) return;
|
||||
mStart = std::chrono::system_clock::now();
|
||||
mName = name;
|
||||
mPerformanceShower = performanceShower;
|
||||
}
|
||||
~Cacl() {
|
||||
if(!mPerformanceShower) return;
|
||||
if(!mPerformanceShower || !mPerformanceShower->enabled()) return;
|
||||
double useTime = std::chrono::duration<double>(std::chrono::system_clock::now() - mStart).count() * 1000;
|
||||
std::lock_guard<std::mutex> lock(mPerformanceShower->mMutex);
|
||||
if(!mPerformanceShower->mInfoMap.contains(mName)) {
|
||||
mPerformanceShower->mInfoMap[mName] = PerformanceLine(mName);
|
||||
}
|
||||
PerformanceLine& data = mPerformanceShower->mInfoMap[mName];
|
||||
data.update(useTime);
|
||||
mPerformanceShower->recordCost(mName, useTime);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,18 +20,14 @@ namespace YSG {
|
||||
Global::instance()->renderScheduler().registerPlot(this);
|
||||
}
|
||||
bool Plot::usePerformanceShower() {
|
||||
return d->mShower;
|
||||
return d->mShower && d->mShower->enabled();
|
||||
}
|
||||
void Plot::set_usePerformanceShower(bool use) {
|
||||
if(!use) {
|
||||
PerformanceShower* t = d->mShower;
|
||||
d->mShower = nullptr;
|
||||
delete t;
|
||||
} else {
|
||||
if(d->mShower) return;
|
||||
d->mShower = new PerformanceShower(this);
|
||||
d->mShower->show();
|
||||
if(!d->mShower) {
|
||||
d->mShower = new PerformanceShower;
|
||||
d->mShower->init(this, "legend");
|
||||
}
|
||||
d->mShower->setEnabled(use);
|
||||
}
|
||||
|
||||
Plot::~Plot() {
|
||||
@@ -44,6 +40,8 @@ namespace YSG {
|
||||
return d->mActiveRenderTasks.load(std::memory_order_acquire) == 0;
|
||||
});
|
||||
}
|
||||
delete d->mShower;
|
||||
d->mShower = nullptr;
|
||||
delete d;
|
||||
}
|
||||
|
||||
@@ -195,7 +193,6 @@ namespace YSG {
|
||||
QRegion updateRegion = pipeline.publishRenderColor();
|
||||
pipeline.jobState = RenderAble::JobState::Idle;
|
||||
if(!updateRegion.isEmpty()) requestUpdate(updateRegion);
|
||||
if(d->mShower) d->mShower->refresh();
|
||||
if(pipeline.hasNewState()) submitRender();
|
||||
}
|
||||
void Plot::renderColor(RenderAble::ColorBuffer* color, QSize size, QColor background, std::uint64_t version) {
|
||||
|
||||
Reference in New Issue
Block a user