协程架构重构成功, 下一步准备指针交换 无锁化重构

This commit is contained in:
2026-07-23 08:51:05 +08:00
parent 8c8cc8707b
commit c3e96d2f86
13 changed files with 475 additions and 230 deletions
+1
View File
@@ -77,6 +77,7 @@ struct AbsAxis::BuilderT : protected AbsAxis_Prop {
SETTER_T(QString, layerName, "axis")
protected:
Plot *plot{};
size_t pixelSize{};
void set(AbsAxis* ret);
};
}
+14 -13
View File
@@ -135,19 +135,20 @@ namespace YSG {
ret->init(plot, layerName);
AbsAxisPrivate* pd = ret->d();
AbsAxisRenderState* sc = pd->renderStateCache();
PROP_RT(int, x)
PROP_RT(int, y)
PROP_RT(Qt::Orientation, orientation)
PROP_RT(int, tickLength)
PROP_RT(int, subTickLength)
PROP_RT(QColor, color)
PROP_RT(QChar, formatChar)
PROP_RT(QLocale, locale)
PROP_RT(QString, unitText)
PROP_RT(QFont, unitTextFont)
PROP_RT(QPen, unitTextPen)
PROP_RT(QBrush, unitTextBackgroundBrush)
PROP_RT(int, rotate) // 不旋转
sc->x = this->x;
sc->y = this->y;
sc->orientation = this->orientation;
sc->pixelSize = this->pixelSize;
sc->tickLength = this->tickLength;
sc->subTickLength = this->subTickLength;
sc->color = this->color;
sc->formatChar = this->formatChar;
sc->locale = this->locale;
sc->unitText = this->unitText;
sc->unitTextFont = this->unitTextFont;
sc->unitTextPen = this->unitTextPen;
sc->unitTextBackgroundBrush = this->unitTextBackgroundBrush;
sc->rotate = this->rotate;
}
}
+5
View File
@@ -21,6 +21,8 @@ namespace YSG {
bool selectTest(const QPointF& pos) override {return true;}
void wheelEvent(QWheelEvent* event) override {
if(useWheel) {
SpinLockGuard _guard(&mBufferLock);
RenderEditGuard _editGuard(this);
const double delta = event->angleDelta().y();
double wheelSteps = delta / 120.0;
double factor = std::pow(0.8, wheelSteps);
@@ -40,6 +42,7 @@ namespace YSG {
if(!useDrag) return;
if(event->button() != Qt::LeftButton) return;
if(dragging) return;
SpinLockGuard _guard(&mBufferLock);
dragging = true;
startGlobalPos = QCursor::pos();
startPos = event->pos();
@@ -51,6 +54,8 @@ namespace YSG {
if(!useDrag) return;
if(!(event->buttons() & Qt::LeftButton)) return;
if(!dragging) return;
SpinLockGuard _guard(&mBufferLock);
RenderEditGuard _editGuard(this);
QPoint delt = startGlobalPos - QCursor::pos();
auto rc = renderStateCache();
auto coordDelt = pixelPreCoord * (rc->orientation == Qt::Horizontal ? delt.x() :delt.y());
+5 -2
View File
@@ -9,8 +9,11 @@ namespace YSG {
PROP_P(TimeAxis, QFont, font)
int TimeAxis::giveData(QTime time) {
if(!ok()) return -1;
std::pair<int, bool> ret = d()->mTimeTicker.getNewDataTickInfo();
auto td = d()->tempDataCache();
TimeAxisPrivate* pd = d();
SpinLockGuard _guard(&pd->mBufferLock);
RenderEditGuard _editGuard(pd);
std::pair<int, bool> ret = pd->mTimeTicker.getNewDataTickInfo();
auto td = pd->tempDataCache();
td->mAllTime.push_back(time);
if(ret.second) {
td->mData.push_front({time, ret.first});
+55 -1
View File
@@ -44,6 +44,50 @@ namespace YSG {
SpinLockGuard::SpinLockGuard(SpinLock *lock) : mLock(lock) {
if (mLock) mLock->lock();
}
RenderAble::RenderPipeline::RenderPipeline() {
frontColor = &colorBuffers[0];
readyColor = &colorBuffers[1];
renderColor = &colorBuffers[2];
}
void RenderAble::RenderPipeline::markEditState() {
editStateVersion.fetch_add(1, std::memory_order_release);
}
bool RenderAble::RenderPipeline::hasNewState() const {
return RenderAble::version_newer(editStateVersion.load(std::memory_order_acquire), renderStateVersion);
}
RenderAble::ColorBuffer* RenderAble::RenderPipeline::currentRenderColor() const {
return renderColor;
}
QRegion RenderAble::RenderPipeline::publishRenderColor() {
QRegion updateRegion;
SpinLockGuard guard(&colorLock);
if(RenderAble::version_newer(renderColor->version, readyColor->version)) {
std::swap(readyColor, renderColor);
dirtyRegion += readyColor->dirtyRegion;
}
if(RenderAble::version_newer(readyColor->version, frontColor->version)) {
if(dirtyRegion.isEmpty()) dirtyRegion = readyColor->dirtyRegion;
if(!paintRequestPending) {
paintRequestPending = true;
updateRegion = dirtyRegion;
}
}
return updateRegion;
}
RenderAble::ColorBuffer* RenderAble::RenderPipeline::consumeReadyColor() {
SpinLockGuard guard(&colorLock);
if(RenderAble::version_newer(readyColor->version, frontColor->version)) {
std::swap(frontColor, readyColor);
}
paintRequestPending = false;
dirtyRegion = QRegion();
return frontColor;
}
RenderEditGuard::RenderEditGuard(RenderData* data) : mData(data) {
}
RenderEditGuard::~RenderEditGuard() {
if(mData && mData->qPtr) mData->qPtr->markStateDirty();
}
void RenderAble::init(Plot* plot, QString layerName) {
if(layerName.isEmpty()) {
@@ -51,7 +95,11 @@ namespace YSG {
}
plot->mLayerMap[layerName]->mRenderAbles.append(this);
mPlot = plot;
dPtr = createRenderData();
if(!dPtr) dPtr = createRenderData();
delete dPtr->mRenderState;
delete dPtr->mRenderStateCache;
delete dPtr->mTempData;
delete dPtr->mTempDataCache;
dPtr->mRenderState = createRenderState();
dPtr->mRenderStateCache = createRenderState();
dPtr->mTempData = createTempData();
@@ -59,6 +107,12 @@ namespace YSG {
dPtr->mRenderState->dPtr = dPtr;
dPtr->mRenderStateCache->dPtr = dPtr;
dPtr->qPtr = this;
dPtr->mRenderStateCache->version = 1;
if(mPlot) mPlot->markRenderStateDirty();
}
void RenderAble::markStateDirty() {
if(dPtr && dPtr->mRenderStateCache) ++dPtr->mRenderStateCache->version;
if(mPlot) mPlot->markRenderStateDirty();
}
+191 -134
View File
@@ -1,125 +1,195 @@
#pragma once
#include <array>
#include <atomic>
#include <cstdint>
#include <qlinkedlist.h>
#include <QImage>
#include <QRegion>
#include <qscreen.h>
#include "GlobalTypes.h"
namespace YSG {
#define INIT_GET(className) className##Private* privateData = d(); \
className##RenderState* renderState = reinterpret_cast<className##RenderState*>(d()->getState(src)); \
struct TempData {
virtual ~TempData() = default;
};
struct RenderState {
virtual ~RenderState() = default;
RenderData *dPtr{};
SRC src();
};
struct RenderData {
SpinLock mBufferLock;
RenderAble *qPtr{};
bool mRenderStart = false;
RenderState* getState(SRC src) const {
switch (src) {
case SRC::Render: {
return mRenderState;
}
case SRC::Cache: {
return mRenderStateCache;
}
case SRC::Auto: {
return mRenderStart ? mRenderState : mRenderStateCache;
}
}
qDebug() << "RenderState* RenderData::getState(SRC src) error 不存在的枚举值" << static_cast<int>(src);
return nullptr;
#define INIT_GET(className) className##Private* privateData = d(); \
className##RenderState* renderState = reinterpret_cast<className##RenderState*>(d()->getState(src));
struct TempData {
virtual ~TempData() = default;
};
struct RenderState {
virtual ~RenderState() = default;
std::uint64_t version = 0;
RenderData* dPtr{};
SRC src();
};
struct RenderData {
SpinLock mBufferLock;
RenderAble* qPtr{};
bool mRenderStart = false;
RenderState* getState(SRC src) const {
switch (src) {
case SRC::Render: {
return mRenderState;
}
RenderState *mRenderState{}, *mRenderStateCache{};
TempData *mTempData{}, *mTempDataCache{};
protected:
virtual void loadCache() = 0;
virtual RenderState* renderState() = 0;
virtual RenderState* renderStateCache() = 0;
virtual TempData* tempData() = 0;
virtual TempData* tempDataCache() = 0;
virtual ~RenderData() {
delete mTempData;
delete mTempDataCache;
delete mRenderState;
delete mRenderStateCache;
case SRC::Cache: {
return mRenderStateCache;
}
virtual void draw(QPainter* painter){}
virtual void prepareData() {
SpinLockGuard g(&mBufferLock);
//qDebug() << "qPtr->mObjectName " << qPtr->mObjectName;
loadCache();
case SRC::Auto: {
return mRenderStart ? mRenderState : mRenderStateCache;
}
virtual bool selectTest(const QPointF& pos){return false;}
virtual void plotEvent(QEvent* event){}
virtual void mousePressEvent(QMouseEvent* event) {}
virtual void mouseMoveEvent(QMouseEvent* event) {}
virtual void mouseReleaseEvent(QMouseEvent* event) {}
virtual void keyPressEvent(QKeyEvent* event){}
virtual void keyReleaseEvent(QKeyEvent* event){}
virtual void firstResizeEvent(QResizeEvent* event){}
virtual void resizeEvent(QResizeEvent* event){}
virtual void wheelEvent(QWheelEvent* event){}
friend struct RenderAble;
}
qDebug() << "RenderState* RenderData::getState(SRC src) error 不存在的枚举值" << static_cast<int>(src);
return nullptr;
}
RenderState *mRenderState{}, *mRenderStateCache{};
TempData *mTempData{}, *mTempDataCache{};
protected:
virtual void loadCache() = 0;
virtual RenderState* renderState() = 0;
virtual RenderState* renderStateCache() = 0;
virtual TempData* tempData() = 0;
virtual TempData* tempDataCache() = 0;
virtual ~RenderData() {
delete mTempData;
delete mTempDataCache;
delete mRenderState;
delete mRenderStateCache;
}
virtual void draw(QPainter* painter) {}
virtual void prepareData() {
SpinLockGuard g(&mBufferLock);
//qDebug() << "qPtr->mObjectName " << qPtr->mObjectName;
loadCache();
}
virtual bool selectTest(const QPointF& pos) {
return false;
}
virtual void plotEvent(QEvent* event) {}
virtual void mousePressEvent(QMouseEvent* event) {}
virtual void mouseMoveEvent(QMouseEvent* event) {}
virtual void mouseReleaseEvent(QMouseEvent* event) {}
virtual void keyPressEvent(QKeyEvent* event) {}
virtual void keyReleaseEvent(QKeyEvent* event) {}
virtual void firstResizeEvent(QResizeEvent* event) {}
virtual void resizeEvent(QResizeEvent* event) {}
virtual void wheelEvent(QWheelEvent* event) {}
friend struct RenderAble;
};
struct BaseRenderData : RenderData {
RenderState* renderState() override {
return nullptr;
}
RenderState* renderStateCache() override {
return nullptr;
}
TempData* tempData() override {
return nullptr;
}
TempData* tempDataCache() override {
return nullptr;
}
void loadCache() override {}
};
class Plot;
struct LIB_DECL RenderEditGuard {
explicit RenderEditGuard(RenderData* data);
~RenderEditGuard();
RenderData* mData{};
};
struct RenderAble {
enum class JobState {
Idle,
Queued,
Running
};
struct BaseRenderData : RenderData {
RenderState* renderState() override {return nullptr;}
RenderState* renderStateCache() override {return nullptr;}
TempData* tempData() override {return nullptr;}
TempData* tempDataCache() override {return nullptr;}
void loadCache() override{}
struct ColorBuffer {
std::uint64_t version = 0;
QImage image;
QRegion dirtyRegion;
};
class Plot;
struct RenderAble {
virtual RenderData* createRenderData() {return new BaseRenderData;}
virtual RenderState* createRenderState(){return new RenderState;}
virtual TempData* createTempData() {return new TempData;}
virtual void init(Plot *plot, QString layerName = "");
virtual void draw(QPainter* painter){dPtr->draw(painter);}
virtual void prepareData(){dPtr->prepareData();}
virtual bool selectTest(const QPointF& pos){return dPtr->selectTest(pos);}
virtual void plotEvent(QEvent* event){dPtr->plotEvent(event);}
virtual void mousePressEvent(QMouseEvent* event) {dPtr->mousePressEvent(event);}
virtual void mouseMoveEvent(QMouseEvent* event) {dPtr->mouseMoveEvent(event);}
virtual void mouseReleaseEvent(QMouseEvent* event) {dPtr->mouseReleaseEvent(event);}
virtual void keyPressEvent(QKeyEvent* event){dPtr->keyPressEvent(event);}
virtual void keyReleaseEvent(QKeyEvent* event){dPtr->keyReleaseEvent(event);}
virtual void firstResizeEvent(QResizeEvent* event){dPtr->firstResizeEvent(event);}
virtual void resizeEvent(QResizeEvent* event){dPtr->resizeEvent(event);}
virtual void wheelEvent(QWheelEvent* event){dPtr->wheelEvent(event);};
QString mObjectName;
RenderData *dPtr{};
Plot *mPlot{};
[[nodiscard]] bool ok() const;
bool mPrepared = false;
// 这里面可以预先准备数据用来同步
virtual ~RenderAble();
virtual bool select(QPointF pos){return false;}
bool mVisable = true;
RenderAble* mParent{};
QVector<RenderAble*> mBeRelyList;
bool dectCycleRely();
private:
bool hasCycleRely(RenderAble* node, QSet<RenderAble*>& visited, QSet<RenderAble*>& recursionStack, QVector<RenderAble*>& currentPath);
struct RenderPipeline {
SpinLock colorLock;
std::array<ColorBuffer, 3> colorBuffers;
ColorBuffer* frontColor{};
ColorBuffer* readyColor{};
ColorBuffer* renderColor{};
std::atomic<std::uint64_t> editStateVersion{1};
std::uint64_t renderStateVersion = 0;
JobState jobState = JobState::Idle;
bool paintRequestPending = false;
QRegion dirtyRegion;
RenderPipeline();
void markEditState();
bool hasNewState() const;
ColorBuffer* currentRenderColor() const;
QRegion publishRenderColor();
ColorBuffer* consumeReadyColor();
};
#define Q_Ptr2(ClassName) \
virtual RenderData* createRenderData() {
return new BaseRenderData;
}
virtual RenderState* createRenderState() {
return new RenderState;
}
virtual TempData* createTempData() {
return new TempData;
}
virtual void init(Plot* plot, QString layerName = "");
virtual void draw(QPainter* painter) {
dPtr->draw(painter);
}
virtual void prepareData() {
dPtr->prepareData();
}
virtual bool selectTest(const QPointF& pos) {
return dPtr->selectTest(pos);
}
virtual void plotEvent(QEvent* event) {
dPtr->plotEvent(event);
}
virtual void mousePressEvent(QMouseEvent* event) {
dPtr->mousePressEvent(event);
}
virtual void mouseMoveEvent(QMouseEvent* event) {
dPtr->mouseMoveEvent(event);
}
virtual void mouseReleaseEvent(QMouseEvent* event) {
dPtr->mouseReleaseEvent(event);
}
virtual void keyPressEvent(QKeyEvent* event) {
dPtr->keyPressEvent(event);
}
virtual void keyReleaseEvent(QKeyEvent* event) {
dPtr->keyReleaseEvent(event);
}
virtual void firstResizeEvent(QResizeEvent* event) {
dPtr->firstResizeEvent(event);
}
virtual void resizeEvent(QResizeEvent* event) {
dPtr->resizeEvent(event);
}
virtual void wheelEvent(QWheelEvent* event) {
dPtr->wheelEvent(event);
};
static bool version_newer(std::uint64_t a, std::uint64_t b) {
return static_cast<std::int64_t>(a - b) > 0;
}
void markStateDirty();
QString mObjectName;
RenderData* dPtr{};
Plot* mPlot{};
[[nodiscard]] bool ok() const;
bool mPrepared = false;
// 这里面可以预先准备数据用来同步
virtual ~RenderAble();
virtual bool select(QPointF pos) {
return false;
}
bool mVisable = true;
RenderAble* mParent{};
QVector<RenderAble*> mBeRelyList;
bool dectCycleRely();
private:
bool hasCycleRely(RenderAble* node, QSet<RenderAble*>& visited, QSet<RenderAble*>& recursionStack, QVector<RenderAble*>& currentPath);
};
#define Q_Ptr2(ClassName) \
ClassName##Private* d() {return reinterpret_cast<ClassName##Private*>(dPtr);} \
RenderData* createRenderData() override; \
RenderState* createRenderState() override; \
@@ -130,28 +200,21 @@ namespace YSG {
friend struct ClassName##RenderState; \
friend struct ClassName##TempData; \
public:
#define Q_Ptr(ClassName) \
#define Q_Ptr(ClassName) \
ClassName(); \
ClassName##Private* d() {return reinterpret_cast<ClassName##Private*>(dPtr);} \
RenderData* createRenderData() override; \
RenderState* createRenderState() override; \
TempData* createTempData() override; \
#define Q_Ptr_cpp(ClassName) \
TempData* createTempData() override;
#define Q_Ptr_cpp(ClassName) \
ClassName::ClassName() { \
dPtr = new ClassName##Private; \
mObjectName = #ClassName; \
} \
RenderData* ClassName::createRenderData() {return new ClassName##Private;} \
RenderState* ClassName::createRenderState() {return new ClassName##RenderState;} \
TempData* ClassName::createTempData() {return new ClassName##TempData;} \
#define D_Ptr_3(ClassName) \
TempData* ClassName::createTempData() {return new ClassName##TempData;}
#define D_Ptr_3(ClassName) \
ClassName##RenderState* renderState() override {return reinterpret_cast<ClassName##RenderState*>(mRenderState);} \
ClassName##RenderState* renderStateCache() override {return reinterpret_cast<ClassName##RenderState*>(mRenderStateCache);} \
ClassName##TempData* tempData() override {return reinterpret_cast<ClassName##TempData*>(mTempData);} \
@@ -162,10 +225,7 @@ namespace YSG {
std::swap(mTempData, mTempDataCache); \
if(!mRenderStart) mRenderStart = true; \
}
#define D_Ptr(ClassName) \
#define D_Ptr(ClassName) \
ClassName* q() {return reinterpret_cast<ClassName*>(qPtr);} \
ClassName##RenderState* renderState() override {return reinterpret_cast<ClassName##RenderState*>(mRenderState);} \
ClassName##RenderState* renderStateCache() override {return reinterpret_cast<ClassName##RenderState*>(mRenderStateCache);} \
@@ -177,19 +237,16 @@ namespace YSG {
std::swap(mTempData, mTempDataCache); \
if(!mRenderStart) mRenderStart = true; \
}
#define D_Ptr2(ClassName) \
#define D_Ptr2(ClassName) \
ClassName* q() {return reinterpret_cast<ClassName*>(qPtr);} \
ClassName##RenderState* renderState() override {return reinterpret_cast<ClassName##RenderState*>(mRenderState);} \
ClassName##RenderState* renderStateCache() override {return reinterpret_cast<ClassName##RenderState*>(mRenderStateCache);} \
ClassName##TempData* tempData() override {return reinterpret_cast<ClassName##TempData*>(mTempData);} \
ClassName##TempData* tempDataCache() override {return reinterpret_cast<ClassName##TempData*>(mTempDataCache);} \
friend class ClassName;
#define INIT_SET(className) className##Private* pd = d(); \
#define INIT_SET(className) className##Private* pd = d(); \
className##RenderState* sc = pd->renderStateCache();\
className##TempData* td = pd->tempDataCache(); \
SpinLockGuard _guard(&pd->mBufferLock); \
}
RenderEditGuard _editGuard(pd);
}
+52 -21
View File
@@ -1,50 +1,81 @@
#include "asio.hpp"
#include "Global.h"
#include <QApplication>
#include <algorithm>
#include <thread>
#include "Plot_p.h"
namespace YSG {
struct TimerThreadPrivate {
asio::io_context mIoContext;
std::unique_ptr<asio::executor_work_guard<asio::io_context::executor_type>> mWorkGuard;
std::unique_ptr<asio::thread_pool> mCpuPool;
std::thread::id mSchedulerThreadId;
};
TimerThread::TimerThread() {
connect(QApplication::instance(), &QApplication::aboutToQuit, [&]() {
TimerThread::TimerThread() : d(std::make_unique<TimerThreadPrivate>()) {
connect(QApplication::instance(), &QApplication::aboutToQuit, [this]() {
if (isRunning()) {
quit();
d->mIoContext.stop();
if(d->mCpuPool) d->mCpuPool->stop();
wait();
}
delete this;
});
}
TimerThread::~TimerThread() = default;
void TimerThread::run() {
d->mSchedulerThreadId = std::this_thread::get_id();
d->mIoContext.restart();
d->mWorkGuard = std::make_unique<asio::executor_work_guard<asio::io_context::executor_type>>(d->mIoContext.get_executor());
unsigned cpuCount = std::max(1u, std::thread::hardware_concurrency() > 1 ? std::thread::hardware_concurrency() - 1 : 1u);
d->mCpuPool = std::make_unique<asio::thread_pool>(cpuCount);
for (auto &plot: mPlots) {
plot->d->mTimer = new QTimer;
plot->d->mTimer->setTimerType(Qt::PreciseTimer);
plot->d->mTimerThread = this;
connect(plot->d->mTimer, &QTimer::timeout, plot, &Plot::render, Qt::DirectConnection);
// show里的调用可能比这个 线程开启提前, 那是 timer是nullptr
if (plot->isVisible()) plot->startRender();
plot->d->mRenderTimer = std::make_unique<asio::steady_timer>(d->mIoContext);
if(plot->isVisible()) plot->d->mRenderEnabled = true;
if(plot->d->mRenderEnabled) plot->scheduleRenderTimer();
plot->requestRender();
}
exec();
d->mIoContext.run();
for (auto &plot: mPlots) {
plot->d->mTimer->stop();
delete plot->d->mTimer;
if(plot->d->mRenderTimer) {
plot->d->mRenderTimer->cancel();
plot->d->mRenderTimer.reset();
}
}
if(d->mCpuPool) {
d->mCpuPool->stop();
d->mCpuPool->join();
d->mCpuPool.reset();
}
d->mWorkGuard.reset();
}
void TimerThread::post(std::function<void()> task) {
asio::post(d->mIoContext, std::move(task));
}
void TimerThread::postCpu(std::function<void()> task) {
if(d->mCpuPool) {
asio::post(*d->mCpuPool, std::move(task));
} else {
post(std::move(task));
}
}
bool TimerThread::isSchedulerThread() const {
return std::this_thread::get_id() == d->mSchedulerThreadId;
}
asio::io_context& TimerThread::ioContext() {
return d->mIoContext;
}
void Global::startAllTimeThread() {
for (auto &thread: mTimerThreadMap) {
//qDebug() << "startAllTimeThread == " << thread->objectName();
thread->start();
if(!thread->isRunning()) thread->start();
}
mTimerId = startTimer(10);
}
void Global::timerEvent(QTimerEvent* event) {
if (event->timerId() != mTimerId) return;
for (TimerThread *t: mTimerThreadMap) {
for (Plot *plot: t->mPlots) {
if (plot->d->mPlotState.loadAcquire() != PlotPrivate::PlotState::NeedToPaint) continue;
plot->repaint();
}
}
Q_UNUSED(event)
}
}
+13
View File
@@ -2,17 +2,30 @@
#include "../GlobalTypes.h"
#include "Core/Base/global_include.h"
#include <functional>
#include <memory>
#include <QThread>
#include <qrgb.h>
extern int qInitResources_resource();
namespace asio {
class io_context;
}
namespace YSG {
struct TimerThreadPrivate;
class LIB_DECL TimerThread : public QThread {
Q_OBJECT
public:
QList<Plot*> mPlots;
TimerThread();
~TimerThread() override;
void run() override;
void post(std::function<void()> task);
void postCpu(std::function<void()> task);
bool isSchedulerThread() const;
asio::io_context& ioContext();
private:
std::unique_ptr<TimerThreadPrivate> d;
};
+9 -7
View File
@@ -44,9 +44,10 @@ namespace YSG {
// event->button():返回引发该事件的单个鼠标按钮。通常在 mousePressEvent 和 mouseReleaseEvent 中使用。
// event->buttons():返回一个标志,指示当前按下的所有鼠标按钮。可以在任何鼠标事件中使用,特别是 mouseMoveEvent 中。
void mousePressEvent(QMouseEvent* event) override {
if (event->button() != Qt::LeftButton) return;
MutiSelectRectRenderState* sc = renderStateCache();
SpinLockGuard _guard(&mBufferLock);
if (event->button() != Qt::LeftButton) return;
RenderEditGuard _editGuard(this);
QVector<QRectF> &rectList = sc->mRects;
if (!(QApplication::keyboardModifiers() & Qt::ControlModifier)) {
rectList.clear();
@@ -69,6 +70,7 @@ namespace YSG {
if(!(event->buttons() & Qt::LeftButton)) return;
MutiSelectRectRenderState* sc = renderStateCache();
SpinLockGuard _guard(&mBufferLock);
RenderEditGuard _editGuard(this);
QVector<QRectF> &rectList = sc->mRects;
if (rectList.empty()) return;
rectList.last().setBottomRight(event->pos());
@@ -80,14 +82,14 @@ namespace YSG {
void mouseReleaseEvent(QMouseEvent* event) override {}
void keyPressEvent(QKeyEvent* event) override {
if (event->key() != Qt::Key_Escape || !mActive) return;
MutiSelectRectRenderState* sc = renderStateCache();
SpinLockGuard _guard(&mBufferLock);
if (event->key() == Qt::Key_Escape && mActive) {
mActive = false;
QVector<QRectF> &rectList = sc->mRects;
if (rectList.empty()) return;
rectList.removeLast();
}
RenderEditGuard _editGuard(this);
mActive = false;
QVector<QRectF> &rectList = sc->mRects;
if (rectList.empty()) return;
rectList.removeLast();
}
void keyReleaseEvent(QKeyEvent* event) override {
+115 -45
View File
@@ -1,6 +1,8 @@
#include "Plot_p.h"
#include "PerformanceShower_p.h"
#include "Global.h"
#include <algorithm>
#include <chrono>
namespace YSG {
class PrePareMutiDataMutexGuard {
@@ -31,7 +33,9 @@ namespace YSG {
g->mTimerThreadMap[threadName] = newThread;
newThread->setObjectName(threadName);
}
g->mTimerThreadMap[threadName]->mPlots.append(this);
TimerThread* thread = g->mTimerThreadMap[threadName];
thread->mPlots.append(this);
d->mTimerThread = thread;
}
Plot::Plot(){
@@ -57,12 +61,12 @@ namespace YSG {
}
Plot::~Plot() {
delete d->mRenderBuffer;
pauseRender();
delete d;
}
bool Plot::isRendering() {
if(!d->mTimer) return false;
return d->mTimer->isActive();
return d->mRenderEnabled;
}
void Plot::startRender(int refreshTimesPreSecond) {
@@ -71,36 +75,57 @@ namespace YSG {
}
void Plot::startRender() const {
if(d->mTimer) QMetaObject::invokeMethod(d->mTimer, "start", Qt::AutoConnection, Q_ARG(int, qRound(1000.0 / (double) d->mRefreshTimesPreSecond)));
d->mRenderEnabled = true;
if(!d->mTimerThread) return;
Plot* self = const_cast<Plot*>(this);
d->mTimerThread->post([self]() {
PlotPrivate* d = self->d;
if(d->mRenderTimer) {
d->mRenderTimer->cancel();
self->scheduleRenderTimer();
}
self->submitRender();
});
}
void Plot::pauseRender() const {
if(d->mTimer) QMetaObject::invokeMethod(d->mTimer, "stop", Qt::AutoConnection);
d->mRenderEnabled = false;
if(!d->mTimerThread) return;
Plot* self = const_cast<Plot*>(this);
d->mTimerThread->post([self]() {
PlotPrivate* d = self->d;
if(d->mRenderTimer) {
d->mRenderTimer->cancel();
}
});
}
void Plot::paintEvent(QPaintEvent* event) {
SpinLockGuard guard(&d->mBufferLock);
Q_UNUSED(event)
QPainter painter(this);
painter.drawPixmap(QPoint(0, 0), d->mPaintBuffer);
//painter.drawImage(QPoint(0, 0), d->mPaintBuffer);
RenderAble::ColorBuffer* color = d->mPipeline.consumeReadyColor();
if(color && !color->image.isNull()) painter.drawImage(QPoint(0, 0), color->image);
else painter.fillRect(rect(), d->mBackground);
painter.end();
if(d->mShower) d->mShower->counterIncrease("plot paint");
d->mPlotState.storeRelease(PlotPrivate::NeedToRender);
}
void Plot::render() {
if (mFirstResize) return;
if (d->mPlotState.loadAcquire() != PlotPrivate::NeedToRender) return;
if(!d->mRenderBuffer) return;
SpinLockGuard renderGuard(&d->mRenderLock);
if(d->mRenderBuffer->isNull()) return;
submitRender();
}
void Plot::submitRender() {
if(mFirstResize) return;
if(!d->mRenderEnabled) return;
QSize renderSize = d->mRenderSize;
if(renderSize.width() <= 0 || renderSize.height() <= 0) return;
RenderAble::RenderPipeline& pipeline = d->mPipeline;
if(pipeline.jobState != RenderAble::JobState::Idle) return;
if(!pipeline.hasNewState()) return;
pipeline.jobState = RenderAble::JobState::Queued;
pipeline.renderStateVersion = pipeline.editStateVersion.load(std::memory_order_acquire);
if(d->mShower) {
d->mShower->counterIncrease("渲染帧率");
d->mShower->mInfos["interval"] = QString("渲染:%1, global%2").arg(d->mTimer->interval()).arg(Global::instance()->mInterval);
d->mShower->mInfos["interval"] = QString("渲染:%1, global%2").arg(qRound(1000.0 / (double)d->mRefreshTimesPreSecond)).arg(Global::instance()->mInterval);
}
Cacl renderData("整体渲染耗时: %1 avg: %2", d->mShower);
QPainter painter(d->mRenderBuffer);
painter.fillRect(rect(), d->mBackground);
{
Cacl prepareData("准备数据耗时: %1 avg: %2", d->mShower);
SpinLockGuard prepareDataLock(&d->mPrepareDataLock);
@@ -125,10 +150,46 @@ namespace YSG {
}
if(mFirstPrepareData) mFirstPrepareData = false;
}
for(Layer* layer : mLayerList) {
for(RenderAble* able : layer->mRenderAbles) {
able->mPrepared = false;
}
}
RenderAble::ColorBuffer* color = pipeline.currentRenderColor();
QColor background = d->mBackground;
std::uint64_t version = pipeline.renderStateVersion;
TimerThread* thread = d->mTimerThread;
pipeline.jobState = RenderAble::JobState::Running;
if(thread) {
thread->postCpu([this, color, renderSize, background, version, thread]() {
renderColor(color, renderSize, background, version);
thread->post([this]() {
finishRender();
});
});
} else {
renderColor(color, renderSize, background, version);
finishRender();
}
}
void Plot::finishRender() {
RenderAble::RenderPipeline& pipeline = d->mPipeline;
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) {
Cacl renderData("整体渲染耗时: %1 avg: %2", d->mShower);
if(color->image.size() != size || color->image.format() != QImage::Format_ARGB32) {
color->image = QImage(size, QImage::Format_ARGB32);
}
color->image.detach();
color->image.fill(background);
QPainter painter(&color->image);
{
Cacl prepareData("draw耗时: %1 avg: %2", d->mShower);
Cacl drawData("draw耗时: %1 avg: %2", d->mShower);
for(Layer* layer : mLayerList) {
for(RenderAble* cur : layer->mRenderAbles) {
if(!cur->mVisable) continue;
@@ -136,23 +197,35 @@ namespace YSG {
}
}
}
{
SpinLockGuard bufferGuard(&d->mBufferLock);
// d->mPaintBuffer = *d->mRenderBuffer;
d->mPaintBuffer = QPixmap::fromImage(*d->mRenderBuffer);
d->mPlotState.storeRelease(PlotPrivate::NeedToPaint);
}
for(Layer* layer : mLayerList) {
for(RenderAble* able : layer->mRenderAbles) {
able->mPrepared = false;
}
}
if(d->mShower) {
d->mShower->refresh();
}
painter.end();
color->version = version;
color->dirtyRegion = QRegion(QRect(QPoint(0, 0), size));
}
void Plot::scheduleRenderTimer() {
if(!d->mRenderTimer || !d->mRenderEnabled) return;
int interval = std::max(1, qRound(1000.0 / (double)d->mRefreshTimesPreSecond));
d->mRenderTimer->expires_after(std::chrono::milliseconds(interval));
d->mRenderTimer->async_wait([this](const asio::error_code& error) {
if(error) return;
submitRender();
scheduleRenderTimer();
});
}
void Plot::markRenderStateDirty() {
d->mPipeline.markEditState();
requestRender();
}
void Plot::requestRender() {
if(!d->mTimerThread) return;
d->mTimerThread->post([this]() {
submitRender();
});
}
void Plot::requestUpdate(const QRegion& region) {
QTimer::singleShot(0, this, [this, region]() {
if(region.isEmpty()) update();
else update(region);
});
}
QColor Plot::backgroundColor() {
@@ -169,12 +242,8 @@ namespace YSG {
case QEvent::Resize: {
auto e = reinterpret_cast<QResizeEvent*>(event);
{
//qDebug() << resizeEvent->size();
SpinLockGuard guard(&d->mRenderLock);
delete d->mRenderBuffer;
//d->mRenderBuffer = new QPixmap(e->size());
d->mRenderBuffer = new QImage(e->size().width(), e->size().height(), QImage::Format_ARGB32);
d->mRenderSize = e->size();
if(mFirstResize) {
for(Layer* layer : mLayerList) {
for(RenderAble* able : layer->mRenderAbles) {
@@ -189,6 +258,7 @@ namespace YSG {
}
if(mFirstResize) mFirstResize = false;
}
markRenderStateDirty();
break;
}
case QEvent::Show: {
+7
View File
@@ -57,6 +57,8 @@ namespace YSG {
QColor backgroundColor();
bool mFirstResize = true, mFirstPrepareData = true;
void setBackgroundColor(const QColor& color) const;
void markRenderStateDirty();
void requestRender();
struct Layer {
QString mLayerName;
QVector<RenderAble*> mRenderAbles;
@@ -89,6 +91,11 @@ namespace YSG {
QHash<QString, Layer*> mLayerMap;
bool event(QEvent* event) final;
void paintEvent(QPaintEvent* event) final;
void scheduleRenderTimer();
void submitRender();
void finishRender();
void requestUpdate(const QRegion& region);
void renderColor(RenderAble::ColorBuffer* color, QSize size, QColor background, std::uint64_t version);
friend class TimerThread;
friend struct RenderAble;
};
+6 -7
View File
@@ -1,8 +1,10 @@
#pragma once
#include "asio.hpp"
#include "Plot.h"
#include <QApplication>
#include <QThread>
#include <memory>
namespace YSG {
@@ -11,14 +13,11 @@ namespace YSG {
Plot *q{};
int mRefreshTimesPreSecond = 30;
TimerThread* mTimerThread{};
QTimer *mTimer{};
enum PlotState { NeedToRender, NeedToPaint};
QAtomicInteger<int> mPlotState = NeedToRender;
std::unique_ptr<asio::steady_timer> mRenderTimer;
bool mRenderEnabled = false;
QSize mRenderSize;
RenderAble::RenderPipeline mPipeline;
SpinLock mBufferLock, mRenderLock, mPrepareDataLock, mMustPrepareDataLock;
//QPixmap *mRenderBuffer{}, mPaintBuffer;
//QImage *mRenderBuffer{}, mPaintBuffer;
QImage *mRenderBuffer{};
QPixmap mPaintBuffer{};
QColor mBackground = Qt::black;
PerformanceShower *mShower{};
};
+2
View File
@@ -17,6 +17,7 @@ namespace YSG {
AfterglowRenderState* sc = pd->renderStateCache();
AfterglowTempData* td = pd->tempDataCache();
SpinLockGuard _guard(&pd->mBufferLock);
RenderEditGuard _editGuard(pd);
sc->frequentPointSize = std::move(t);
sc->update_PointSize(sc->frequentPointSize, sc->powerPointSize);
}
@@ -25,6 +26,7 @@ namespace YSG {
AfterglowRenderState* sc = pd->renderStateCache();
AfterglowTempData* td = pd->tempDataCache();
SpinLockGuard _guard(&pd->mBufferLock);
RenderEditGuard _editGuard(pd);
sc->powerPointSize = std::move(t);
sc->update_PointSize(sc->frequentPointSize, sc->powerPointSize);
}