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