From c3e96d2f865608cccdc63cfa8ce6464c5ff16bba Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Thu, 23 Jul 2026 08:51:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=8F=E7=A8=8B=E6=9E=B6=E6=9E=84=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E6=88=90=E5=8A=9F=EF=BC=8C=20=E4=B8=8B=E4=B8=80?= =?UTF-8?q?=E6=AD=A5=E5=87=86=E5=A4=87=E6=8C=87=E9=92=88=E4=BA=A4=E6=8D=A2?= =?UTF-8?q?=20=E6=97=A0=E9=94=81=E5=8C=96=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- YSGraphic_Core/Axis/AbsAxis.h | 1 + YSGraphic_Core/Axis/AbsAxis_p.h | 27 +- YSGraphic_Core/Axis/Axis_p.h | 5 + YSGraphic_Core/Axis/TimeAxis.cpp | 7 +- YSGraphic_Core/RenderAble.cpp | 56 ++++- YSGraphic_Core/RenderAble.h | 325 +++++++++++++++---------- YSGraphic_Core/base/Global.cpp | 73 ++++-- YSGraphic_Core/base/Global.h | 13 + YSGraphic_Core/base/MutiSelectRect_p.h | 16 +- YSGraphic_Core/base/Plot.cpp | 160 ++++++++---- YSGraphic_Core/base/Plot.h | 7 + YSGraphic_Core/base/Plot_p.h | 13 +- YSGraphic_Core/plottable/Afterglow.cpp | 2 + 13 files changed, 475 insertions(+), 230 deletions(-) diff --git a/YSGraphic_Core/Axis/AbsAxis.h b/YSGraphic_Core/Axis/AbsAxis.h index e695b09..a9bb8c9 100644 --- a/YSGraphic_Core/Axis/AbsAxis.h +++ b/YSGraphic_Core/Axis/AbsAxis.h @@ -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); }; } diff --git a/YSGraphic_Core/Axis/AbsAxis_p.h b/YSGraphic_Core/Axis/AbsAxis_p.h index d537521..be396f8 100644 --- a/YSGraphic_Core/Axis/AbsAxis_p.h +++ b/YSGraphic_Core/Axis/AbsAxis_p.h @@ -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; } } diff --git a/YSGraphic_Core/Axis/Axis_p.h b/YSGraphic_Core/Axis/Axis_p.h index bafadc1..c7e9620 100644 --- a/YSGraphic_Core/Axis/Axis_p.h +++ b/YSGraphic_Core/Axis/Axis_p.h @@ -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()); diff --git a/YSGraphic_Core/Axis/TimeAxis.cpp b/YSGraphic_Core/Axis/TimeAxis.cpp index fb22f91..cd46156 100644 --- a/YSGraphic_Core/Axis/TimeAxis.cpp +++ b/YSGraphic_Core/Axis/TimeAxis.cpp @@ -9,8 +9,11 @@ namespace YSG { PROP_P(TimeAxis, QFont, font) int TimeAxis::giveData(QTime time) { if(!ok()) return -1; - std::pair ret = d()->mTimeTicker.getNewDataTickInfo(); - auto td = d()->tempDataCache(); + TimeAxisPrivate* pd = d(); + SpinLockGuard _guard(&pd->mBufferLock); + RenderEditGuard _editGuard(pd); + std::pair ret = pd->mTimeTicker.getNewDataTickInfo(); + auto td = pd->tempDataCache(); td->mAllTime.push_back(time); if(ret.second) { td->mData.push_front({time, ret.first}); diff --git a/YSGraphic_Core/RenderAble.cpp b/YSGraphic_Core/RenderAble.cpp index 1ba4e79..af32e94 100644 --- a/YSGraphic_Core/RenderAble.cpp +++ b/YSGraphic_Core/RenderAble.cpp @@ -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(); } diff --git a/YSGraphic_Core/RenderAble.h b/YSGraphic_Core/RenderAble.h index 1be75d5..bd1259a 100644 --- a/YSGraphic_Core/RenderAble.h +++ b/YSGraphic_Core/RenderAble.h @@ -1,125 +1,195 @@ #pragma once +#include +#include +#include #include +#include +#include #include - - #include "GlobalTypes.h" - - - namespace YSG { - - #define INIT_GET(className) className##Private* privateData = d(); \ - className##RenderState* renderState = reinterpret_cast(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(src); - return nullptr; +#define INIT_GET(className) className##Private* privateData = d(); \ + className##RenderState* renderState = reinterpret_cast(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(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 mBeRelyList; - bool dectCycleRely(); - private: - bool hasCycleRely(RenderAble* node, QSet& visited, QSet& recursionStack, QVector& currentPath); + struct RenderPipeline { + SpinLock colorLock; + std::array colorBuffers; + ColorBuffer* frontColor{}; + ColorBuffer* readyColor{}; + ColorBuffer* renderColor{}; + std::atomic 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(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 mBeRelyList; + bool dectCycleRely(); +private: + bool hasCycleRely(RenderAble* node, QSet& visited, QSet& recursionStack, QVector& currentPath); +}; +#define Q_Ptr2(ClassName) \ ClassName##Private* d() {return reinterpret_cast(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(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(mRenderState);} \ ClassName##RenderState* renderStateCache() override {return reinterpret_cast(mRenderStateCache);} \ ClassName##TempData* tempData() override {return reinterpret_cast(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(qPtr);} \ ClassName##RenderState* renderState() override {return reinterpret_cast(mRenderState);} \ ClassName##RenderState* renderStateCache() override {return reinterpret_cast(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(qPtr);} \ ClassName##RenderState* renderState() override {return reinterpret_cast(mRenderState);} \ ClassName##RenderState* renderStateCache() override {return reinterpret_cast(mRenderStateCache);} \ ClassName##TempData* tempData() override {return reinterpret_cast(mTempData);} \ ClassName##TempData* tempDataCache() override {return reinterpret_cast(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); \ - - -} \ No newline at end of file + RenderEditGuard _editGuard(pd); +} diff --git a/YSGraphic_Core/base/Global.cpp b/YSGraphic_Core/base/Global.cpp index f260624..37a6a4b 100644 --- a/YSGraphic_Core/base/Global.cpp +++ b/YSGraphic_Core/base/Global.cpp @@ -1,50 +1,81 @@ +#include "asio.hpp" #include "Global.h" #include +#include +#include #include "Plot_p.h" namespace YSG { + struct TimerThreadPrivate { + asio::io_context mIoContext; + std::unique_ptr> mWorkGuard; + std::unique_ptr mCpuPool; + std::thread::id mSchedulerThreadId; + }; - TimerThread::TimerThread() { - connect(QApplication::instance(), &QApplication::aboutToQuit, [&]() { + TimerThread::TimerThread() : d(std::make_unique()) { + 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>(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(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(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 task) { + asio::post(d->mIoContext, std::move(task)); + } + void TimerThread::postCpu(std::function 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) } } diff --git a/YSGraphic_Core/base/Global.h b/YSGraphic_Core/base/Global.h index 47d5f47..318678c 100644 --- a/YSGraphic_Core/base/Global.h +++ b/YSGraphic_Core/base/Global.h @@ -2,17 +2,30 @@ #include "../GlobalTypes.h" #include "Core/Base/global_include.h" +#include +#include #include #include extern int qInitResources_resource(); +namespace asio { + class io_context; +} namespace YSG { + struct TimerThreadPrivate; class LIB_DECL TimerThread : public QThread { Q_OBJECT public: QList mPlots; TimerThread(); + ~TimerThread() override; void run() override; + void post(std::function task); + void postCpu(std::function task); + bool isSchedulerThread() const; + asio::io_context& ioContext(); + private: + std::unique_ptr d; }; diff --git a/YSGraphic_Core/base/MutiSelectRect_p.h b/YSGraphic_Core/base/MutiSelectRect_p.h index 86e9bef..447ea4e 100644 --- a/YSGraphic_Core/base/MutiSelectRect_p.h +++ b/YSGraphic_Core/base/MutiSelectRect_p.h @@ -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 &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 &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 &rectList = sc->mRects; - if (rectList.empty()) return; - rectList.removeLast(); - } + RenderEditGuard _editGuard(this); + mActive = false; + QVector &rectList = sc->mRects; + if (rectList.empty()) return; + rectList.removeLast(); } void keyReleaseEvent(QKeyEvent* event) override { diff --git a/YSGraphic_Core/base/Plot.cpp b/YSGraphic_Core/base/Plot.cpp index ffbd40d..7a8b615 100644 --- a/YSGraphic_Core/base/Plot.cpp +++ b/YSGraphic_Core/base/Plot.cpp @@ -1,6 +1,8 @@ #include "Plot_p.h" #include "PerformanceShower_p.h" #include "Global.h" +#include +#include 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(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(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(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: { diff --git a/YSGraphic_Core/base/Plot.h b/YSGraphic_Core/base/Plot.h index 12df4e5..bfb59b8 100644 --- a/YSGraphic_Core/base/Plot.h +++ b/YSGraphic_Core/base/Plot.h @@ -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 mRenderAbles; @@ -89,6 +91,11 @@ namespace YSG { QHash 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; }; diff --git a/YSGraphic_Core/base/Plot_p.h b/YSGraphic_Core/base/Plot_p.h index ea1f4c2..c2fb497 100644 --- a/YSGraphic_Core/base/Plot_p.h +++ b/YSGraphic_Core/base/Plot_p.h @@ -1,8 +1,10 @@ #pragma once +#include "asio.hpp" #include "Plot.h" #include #include +#include namespace YSG { @@ -11,14 +13,11 @@ namespace YSG { Plot *q{}; int mRefreshTimesPreSecond = 30; TimerThread* mTimerThread{}; - QTimer *mTimer{}; - enum PlotState { NeedToRender, NeedToPaint}; - QAtomicInteger mPlotState = NeedToRender; + std::unique_ptr 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{}; }; diff --git a/YSGraphic_Core/plottable/Afterglow.cpp b/YSGraphic_Core/plottable/Afterglow.cpp index 44474b3..68ca63d 100644 --- a/YSGraphic_Core/plottable/Afterglow.cpp +++ b/YSGraphic_Core/plottable/Afterglow.cpp @@ -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); }