From d1aac0e67932f8efc920e0fdee1517b349cb2ad2 Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Thu, 23 Jul 2026 09:35:29 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=85=B3=E9=97=AD=E6=97=B6?= =?UTF-8?q?=E5=80=99=E7=9A=84=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- YSGraphic_Core/RenderAble.cpp | 3 - YSGraphic_Core/RenderAble.h | 4 + YSGraphic_Core/base/Global.cpp | 98 +++++++++++++++++++++--- YSGraphic_Core/base/Global.h | 10 ++- YSGraphic_Core/base/Plot.cpp | 100 +++++++++++++++++++------ YSGraphic_Core/base/Plot_p.h | 12 +-- YSGraphic_Core/plottable/HoverInfo.cpp | 5 +- YSGraphic_Core/plottable/HoverInfo.h | 29 +++---- YSGraphic_Core/plottable/Spectrum_p.h | 7 ++ YSGraphic_Core/plottable/WaterFall_p.h | 10 +-- 10 files changed, 207 insertions(+), 71 deletions(-) diff --git a/YSGraphic_Core/RenderAble.cpp b/YSGraphic_Core/RenderAble.cpp index af32e94..4141943 100644 --- a/YSGraphic_Core/RenderAble.cpp +++ b/YSGraphic_Core/RenderAble.cpp @@ -88,7 +88,6 @@ namespace YSG { RenderEditGuard::~RenderEditGuard() { if(mData && mData->qPtr) mData->qPtr->markStateDirty(); } - void RenderAble::init(Plot* plot, QString layerName) { if(layerName.isEmpty()) { layerName = plot->mDefaultLayerName; @@ -114,8 +113,6 @@ namespace YSG { if(dPtr && dPtr->mRenderStateCache) ++dPtr->mRenderStateCache->version; if(mPlot) mPlot->markRenderStateDirty(); } - - bool RenderAble::ok() const { return dPtr->mRenderStart && mPlot && !mPlot->mFirstResize && !mPlot->mFirstPrepareData; } diff --git a/YSGraphic_Core/RenderAble.h b/YSGraphic_Core/RenderAble.h index bd1259a..ab7ab5d 100644 --- a/YSGraphic_Core/RenderAble.h +++ b/YSGraphic_Core/RenderAble.h @@ -65,6 +65,7 @@ protected: virtual void mousePressEvent(QMouseEvent* event) {} virtual void mouseMoveEvent(QMouseEvent* event) {} virtual void mouseReleaseEvent(QMouseEvent* event) {} + virtual void setHoverState(const QPoint& pos, bool active) {} virtual void keyPressEvent(QKeyEvent* event) {} virtual void keyReleaseEvent(QKeyEvent* event) {} virtual void firstResizeEvent(QResizeEvent* event) {} @@ -153,6 +154,9 @@ struct RenderAble { virtual void mouseReleaseEvent(QMouseEvent* event) { dPtr->mouseReleaseEvent(event); } + virtual void setHoverState(const QPoint& pos, bool active) { + dPtr->setHoverState(pos, active); + } virtual void keyPressEvent(QKeyEvent* event) { dPtr->keyPressEvent(event); } diff --git a/YSGraphic_Core/base/Global.cpp b/YSGraphic_Core/base/Global.cpp index 37a6a4b..69afc68 100644 --- a/YSGraphic_Core/base/Global.cpp +++ b/YSGraphic_Core/base/Global.cpp @@ -1,7 +1,10 @@ #include "asio.hpp" #include "Global.h" #include +#include #include +#include +#include #include #include "Plot_p.h" @@ -11,15 +14,13 @@ namespace YSG { std::unique_ptr> mWorkGuard; std::unique_ptr mCpuPool; std::thread::id mSchedulerThreadId; + std::atomic_bool mIoActive{false}; + std::atomic_bool mCpuJoined{false}; }; TimerThread::TimerThread() : d(std::make_unique()) { connect(QApplication::instance(), &QApplication::aboutToQuit, [this]() { - if (isRunning()) { - d->mIoContext.stop(); - if(d->mCpuPool) d->mCpuPool->stop(); - wait(); - } + shutdown(); delete this; }); } @@ -31,6 +32,8 @@ namespace YSG { 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); + d->mCpuJoined.store(false, std::memory_order_release); + d->mIoActive.store(true, std::memory_order_release); for (auto &plot: mPlots) { plot->d->mTimerThread = this; plot->d->mRenderTimer = std::make_unique(d->mIoContext); @@ -39,17 +42,15 @@ namespace YSG { plot->requestRender(); } d->mIoContext.run(); + d->mIoActive.store(false, std::memory_order_release); for (auto &plot: mPlots) { if(plot->d->mRenderTimer) { plot->d->mRenderTimer->cancel(); plot->d->mRenderTimer.reset(); } } - if(d->mCpuPool) { - d->mCpuPool->stop(); - d->mCpuPool->join(); - d->mCpuPool.reset(); - } + joinCpuPool(); + d->mCpuPool.reset(); d->mWorkGuard.reset(); } void TimerThread::post(std::function task) { @@ -68,6 +69,83 @@ namespace YSG { asio::io_context& TimerThread::ioContext() { return d->mIoContext; } + void TimerThread::removePlot(Plot* plot) { + postAndWait([this, plot]() { + removePlotOnScheduler(plot); + }); + } + void TimerThread::shutdown() { + if(!isRunning()) { + shutdownOnScheduler(); + stopSchedulerOnScheduler(); + joinCpuPool(); + d->mCpuPool.reset(); + return; + } + if(isSchedulerThread()) { + shutdownOnScheduler(); + stopSchedulerOnScheduler(); + return; + } + postAndWait([this]() { + shutdownOnScheduler(); + }); + joinCpuPool(); + postAndWait([this]() { + stopSchedulerOnScheduler(); + }); + wait(); + } + void TimerThread::joinCpuPool() { + if(d->mCpuPool && !d->mCpuJoined.exchange(true, std::memory_order_acq_rel)) d->mCpuPool->join(); + } + void TimerThread::postAndWait(std::function task) { + if(isSchedulerThread() || !isRunning() || !d->mIoActive.load(std::memory_order_acquire)) { + task(); + return; + } + std::mutex mutex; + std::condition_variable cv; + bool done = false; + post([&task, &mutex, &cv, &done]() { + task(); + { + std::lock_guard lock(mutex); + done = true; + } + cv.notify_one(); + }); + std::unique_lock lock(mutex); + cv.wait(lock, [&done]() { + return done; + }); + } + void TimerThread::removePlotOnScheduler(Plot* plot) { + mPlots.removeAll(plot); + PlotPrivate* data = plot->d; + data->mRenderEnabled = false; + if(data->mRenderTimer) { + data->mRenderTimer->cancel(); + data->mRenderTimer.reset(); + } + data->mTimerThread = nullptr; + } + void TimerThread::shutdownOnScheduler() { + for(Plot* plot : mPlots) { + PlotPrivate* data = plot->d; + data->mDestroying.store(true, std::memory_order_release); + data->mRenderEnabled = false; + if(data->mRenderTimer) { + data->mRenderTimer->cancel(); + data->mRenderTimer.reset(); + } + data->mTimerThread = nullptr; + } + mPlots.clear(); + } + void TimerThread::stopSchedulerOnScheduler() { + d->mWorkGuard.reset(); + } void Global::startAllTimeThread() { for (auto &thread: mTimerThreadMap) { diff --git a/YSGraphic_Core/base/Global.h b/YSGraphic_Core/base/Global.h index 318678c..9cc3af9 100644 --- a/YSGraphic_Core/base/Global.h +++ b/YSGraphic_Core/base/Global.h @@ -22,14 +22,18 @@ namespace YSG { void run() override; void post(std::function task); void postCpu(std::function task); + void removePlot(Plot* plot); + void shutdown(); bool isSchedulerThread() const; asio::io_context& ioContext(); private: + void joinCpuPool(); + void postAndWait(std::function task); + void removePlotOnScheduler(Plot* plot); + void shutdownOnScheduler(); + void stopSchedulerOnScheduler(); std::unique_ptr d; }; - - - class LIB_DECL Global : public QObject, public Psc::Singleton { public: int r = qInitResources_resource(); diff --git a/YSGraphic_Core/base/Plot.cpp b/YSGraphic_Core/base/Plot.cpp index 7a8b615..0fd4126 100644 --- a/YSGraphic_Core/base/Plot.cpp +++ b/YSGraphic_Core/base/Plot.cpp @@ -20,8 +20,9 @@ namespace YSG { for(PrePareMutiDataMutex* curLock : mLockList) curLock->unlock(); } }; - - + static void notifyRenderTaskDone(PlotPrivate* data) { + if(data->mActiveRenderTasks.fetch_sub(1, std::memory_order_acq_rel) == 1) data->mRenderTaskDone.notify_all(); + } void startAllRenderThread() { Global::instance()->startAllTimeThread(); } @@ -38,9 +39,10 @@ namespace YSG { d->mTimerThread = thread; } - Plot::Plot(){ + Plot::Plot() { d = new PlotPrivate(this); setAttribute(Qt::WA_OpaquePaintEvent, true); + setMouseTracking(true); setFocusPolicy(Qt::NoFocus); } bool Plot::usePerformanceShower() { @@ -61,7 +63,15 @@ namespace YSG { } Plot::~Plot() { - pauseRender(); + d->mDestroying.store(true, std::memory_order_release); + d->mRenderEnabled = false; + if(d->mTimerThread) d->mTimerThread->removePlot(this); + { + std::unique_lock lock(d->mRenderTaskMutex); + d->mRenderTaskDone.wait(lock, [this]() { + return d->mActiveRenderTasks.load(std::memory_order_acquire) == 0; + }); + } delete d; } @@ -75,28 +85,40 @@ namespace YSG { } void Plot::startRender() const { + if(d->mDestroying.load(std::memory_order_acquire)) return; d->mRenderEnabled = true; if(!d->mTimerThread) return; Plot* self = const_cast(this); - d->mTimerThread->post([self]() { + PlotPrivate* privateData = d; + privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); + d->mTimerThread->post([self, privateData]() { PlotPrivate* d = self->d; - if(d->mRenderTimer) { - d->mRenderTimer->cancel(); - self->scheduleRenderTimer(); + if(!d->mDestroying.load(std::memory_order_acquire)) { + if(d->mRenderTimer) { + d->mRenderTimer->cancel(); + self->scheduleRenderTimer(); + } + self->submitRender(); } - self->submitRender(); + notifyRenderTaskDone(privateData); }); } void Plot::pauseRender() const { d->mRenderEnabled = false; + if(d->mDestroying.load(std::memory_order_acquire)) return; if(!d->mTimerThread) return; Plot* self = const_cast(this); - d->mTimerThread->post([self]() { + PlotPrivate* privateData = d; + privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); + d->mTimerThread->post([self, privateData]() { PlotPrivate* d = self->d; - if(d->mRenderTimer) { - d->mRenderTimer->cancel(); + if(!d->mDestroying.load(std::memory_order_acquire)) { + if(d->mRenderTimer) { + d->mRenderTimer->cancel(); + } } + notifyRenderTaskDone(privateData); }); } @@ -113,6 +135,7 @@ namespace YSG { submitRender(); } void Plot::submitRender() { + if(d->mDestroying.load(std::memory_order_acquire)) return; if(mFirstResize) return; if(!d->mRenderEnabled) return; QSize renderSize = d->mRenderSize; @@ -161,10 +184,21 @@ namespace YSG { TimerThread* thread = d->mTimerThread; pipeline.jobState = RenderAble::JobState::Running; if(thread) { - thread->postCpu([this, color, renderSize, background, version, thread]() { + PlotPrivate* privateData = d; + privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); + thread->postCpu([this, privateData, color, renderSize, background, version, thread]() { + if(privateData->mDestroying.load(std::memory_order_acquire)) { + notifyRenderTaskDone(privateData); + return; + } renderColor(color, renderSize, background, version); - thread->post([this]() { - finishRender(); + if(privateData->mDestroying.load(std::memory_order_acquire)) { + notifyRenderTaskDone(privateData); + return; + } + thread->post([this, privateData]() { + if(!privateData->mDestroying.load(std::memory_order_acquire)) finishRender(); + notifyRenderTaskDone(privateData); }); }); } else { @@ -173,6 +207,7 @@ namespace YSG { } } void Plot::finishRender() { + if(d->mDestroying.load(std::memory_order_acquire)) return; RenderAble::RenderPipeline& pipeline = d->mPipeline; QRegion updateRegion = pipeline.publishRenderColor(); pipeline.jobState = RenderAble::JobState::Idle; @@ -202,27 +237,37 @@ namespace YSG { color->dirtyRegion = QRegion(QRect(QPoint(0, 0), size)); } void Plot::scheduleRenderTimer() { - if(!d->mRenderTimer || !d->mRenderEnabled) return; + if(d->mDestroying.load(std::memory_order_acquire) || !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(); + PlotPrivate* privateData = d; + privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); + d->mRenderTimer->async_wait([this, privateData](const asio::error_code& error) { + if(!error && !d->mDestroying.load(std::memory_order_acquire)) { + submitRender(); + scheduleRenderTimer(); + } + notifyRenderTaskDone(privateData); }); } void Plot::markRenderStateDirty() { + if(d->mDestroying.load(std::memory_order_acquire)) return; d->mPipeline.markEditState(); requestRender(); } void Plot::requestRender() { + if(d->mDestroying.load(std::memory_order_acquire)) return; if(!d->mTimerThread) return; - d->mTimerThread->post([this]() { - submitRender(); + PlotPrivate* privateData = d; + privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); + d->mTimerThread->post([this, privateData]() { + if(!d->mDestroying.load(std::memory_order_acquire)) submitRender(); + notifyRenderTaskDone(privateData); }); } void Plot::requestUpdate(const QRegion& region) { QTimer::singleShot(0, this, [this, region]() { + if(d->mDestroying.load(std::memory_order_acquire)) return; if(region.isEmpty()) update(); else update(region); }); @@ -273,7 +318,11 @@ namespace YSG { break; } case QEvent::Leave: { - + for(Layer* layer : mLayerList) { + for(RenderAble* able : layer->mRenderAbles) { + able->setHoverState(QPoint(), false); + } + } break; } case QEvent::Wheel: { @@ -283,12 +332,15 @@ namespace YSG { able->wheelEvent(e); } } + break; } case QEvent::MouseMove: { auto e = reinterpret_cast(event); for(Layer* layer : mLayerList) { for(RenderAble* able : layer->mRenderAbles) { - if(able->selectTest(e->pos())) { + bool selected = able->selectTest(e->pos()); + able->setHoverState(e->pos(), selected); + if(selected) { able->mouseMoveEvent(e); } } diff --git a/YSGraphic_Core/base/Plot_p.h b/YSGraphic_Core/base/Plot_p.h index c2fb497..c5dde08 100644 --- a/YSGraphic_Core/base/Plot_p.h +++ b/YSGraphic_Core/base/Plot_p.h @@ -2,11 +2,12 @@ #include "asio.hpp" #include "Plot.h" +#include +#include #include #include #include - - +#include namespace YSG { struct PlotPrivate { explicit PlotPrivate(Plot* plot) : q(plot) {}; @@ -15,14 +16,15 @@ namespace YSG { TimerThread* mTimerThread{}; std::unique_ptr mRenderTimer; bool mRenderEnabled = false; + std::atomic_bool mDestroying{false}; + std::atomic_int mActiveRenderTasks{0}; + std::condition_variable mRenderTaskDone; + std::mutex mRenderTaskMutex; QSize mRenderSize; RenderAble::RenderPipeline mPipeline; SpinLock mBufferLock, mRenderLock, mPrepareDataLock, mMustPrepareDataLock; QColor mBackground = Qt::black; PerformanceShower *mShower{}; }; - - } - diff --git a/YSGraphic_Core/plottable/HoverInfo.cpp b/YSGraphic_Core/plottable/HoverInfo.cpp index 01cc5d0..054763e 100644 --- a/YSGraphic_Core/plottable/HoverInfo.cpp +++ b/YSGraphic_Core/plottable/HoverInfo.cpp @@ -4,7 +4,7 @@ namespace YSG { void HoverInfoRenderState::drawHover(QPainter* painter, AbsAxis* h, AbsAxis* v) { - QPoint pos = QCursor::pos() - h->mPlot->mapToGlobal(QPoint(0, 0)); + QPoint pos = hoverInfoPos; if(!renderAble) { auto that = dynamic_cast(this); renderAble = dynamic_cast(that->dPtr->qPtr); @@ -26,9 +26,8 @@ namespace YSG { } bool HoverInfoRenderState::hoverOK(RenderAble* able) const { - QPoint pos = QCursor::pos() - able->mPlot->mapToGlobal(QPoint(0, 0)); auto tmp = dynamic_cast(able); - return useHoverInfo && able->mPlot->underMouse() && tmp->hoverTest(pos); + return useHoverInfo && hoverInfoActive && tmp->hoverTest(hoverInfoPos); } QVector HoverInfoRenderAbleInterFace::createHoverString(AbsAxis* h, AbsAxis* v, QPoint pos) { diff --git a/YSGraphic_Core/plottable/HoverInfo.h b/YSGraphic_Core/plottable/HoverInfo.h index cf126b5..0e09ee7 100644 --- a/YSGraphic_Core/plottable/HoverInfo.h +++ b/YSGraphic_Core/plottable/HoverInfo.h @@ -5,7 +5,6 @@ namespace YSG { struct HoverInfoRenderState; struct HoverInfoRenderAbleInterFace; - struct LIB_DECL HoverInfoRenderState { virtual ~HoverInfoRenderState() = default; HoverInfoRenderState() { @@ -16,12 +15,12 @@ namespace YSG { QFont hoverInfoFont; QPen hoverInfoPen; bool useHoverInfo = true; + bool hoverInfoActive = false; + QPoint hoverInfoPos; HoverInfoRenderAbleInterFace *renderAble{}; void drawHover(QPainter* painter, AbsAxis *h, AbsAxis *v); bool hoverOK(RenderAble* able) const; }; - - struct LIB_DECL HoverInfoRenderAbleInterFace { virtual QVector createHoverString(AbsAxis* h, AbsAxis* v, QPoint pos); virtual ~HoverInfoRenderAbleInterFace() = default; @@ -34,42 +33,38 @@ namespace YSG { virtual bool hoverTest(QPoint pos) { return true; } - bool useHoverInfo(SRC src = SRC::Auto) { return getState(src)->useHoverInfo; } void setUseHoverInfo(bool use) { SpinLockGuard g(lock()); getState(SRC::Cache)->useHoverInfo = use; + markHoverInfoDirty(); } - void setHoverInfoFont(const QFont& font) { SpinLockGuard g(lock()); getState(SRC::Cache)->hoverInfoFont = font; + markHoverInfoDirty(); } - QFont HoverInfoFont(SRC src = SRC::Auto) { return getState(src)->hoverInfoFont; } - QBrush hoverInfoBackgroundBrush(SRC src = SRC::Auto) { return getState(src)->hoverInfoBrush; } - void setHoverInfoBackgroundBrush(const QBrush& brush) { SpinLockGuard g(lock()); getState(SRC::Cache)->hoverInfoBrush = brush.color().isValid() ? brush : Qt::NoBrush; + markHoverInfoDirty(); } - QPen hoverInfoPen(SRC src = SRC::Auto) { return getState(src)->hoverInfoPen; } - void setHoverInfoPen(const QPen& pen) { SpinLockGuard g(lock()); getState(SRC::Cache)->hoverInfoPen = pen.color().isValid() ? pen : Qt::NoPen; + markHoverInfoDirty(); } - void getHoverInfoContentsMargins(int& left, int& top, int& right, int& bottom, SRC src) { auto s = getState(src); left = s->hoverInfoLeft; @@ -85,13 +80,13 @@ namespace YSG { sc->hoverInfoTop = top; sc->hoverInfoRight = right; sc->hoverInfoBottom = bottom; + markHoverInfoDirty(); } protected: virtual HoverInfoRenderState* getState(SRC src) = 0; virtual SpinLock* lock() = 0; + virtual void markHoverInfoDirty() {} }; - - struct LIB_DECL HoverInfoIndependRenderAble : HoverInfoRenderAbleInterFace { private: SpinLock mtx; @@ -116,8 +111,6 @@ namespace YSG { HoverInfoRenderAbleInterFace::drawHover(painter, h, v); } }; - - template struct LIB_DECL HoverInfoRenderAble : HoverInfoRenderAbleInterFace { private: @@ -131,8 +124,8 @@ namespace YSG { auto t = that(); return dynamic_cast(t->dPtr->getState(src)); } + void markHoverInfoDirty() override { + that()->markStateDirty(); + } }; - - } - diff --git a/YSGraphic_Core/plottable/Spectrum_p.h b/YSGraphic_Core/plottable/Spectrum_p.h index 73b0f3e..02030e8 100644 --- a/YSGraphic_Core/plottable/Spectrum_p.h +++ b/YSGraphic_Core/plottable/Spectrum_p.h @@ -222,5 +222,12 @@ namespace YSG { if(!ok) return false; return qAbs(power - sc->powerAxis->pixelToCoord(pos.y(), SRC::Render)) < 4; } + void setHoverState(const QPoint& pos, bool active) override { + SpinLockGuard guard(&mBufferLock); + RenderEditGuard editGuard(this); + SpectrumRenderState* sc = renderStateCache(); + sc->hoverInfoPos = pos; + sc->hoverInfoActive = active; + } }; } diff --git a/YSGraphic_Core/plottable/WaterFall_p.h b/YSGraphic_Core/plottable/WaterFall_p.h index a64ec77..075c5ac 100644 --- a/YSGraphic_Core/plottable/WaterFall_p.h +++ b/YSGraphic_Core/plottable/WaterFall_p.h @@ -35,12 +35,12 @@ namespace YSG { QImage image; int listByteSize{}; bool selectTest(const QPointF& pos) override {return true;} - void mouseMoveEvent(QMouseEvent* event) override { + void setHoverState(const QPoint& pos, bool active) override { + SpinLockGuard guard(&mBufferLock); + RenderEditGuard editGuard(this); WaterFallRenderState* sc = renderStateCache(); - { - SpinLockGuard guard(&mBufferLock); - - } + sc->hoverInfoPos = pos; + sc->hoverInfoActive = active; } void prepareData() override {