#include "Plot_p.h" #include "PerformanceShower_p.h" #include "Global.h" #include #include namespace YSG { static std::uint64_t steadyNowNs() { return static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count()); } static double elapsedMs(std::uint64_t startNs, std::uint64_t endNs) { return static_cast(endNs - startNs) / 1000000.0; } static QString jobStateText(RenderAble::JobState state) { switch(state) { case RenderAble::JobState::Idle: return "Idle"; case RenderAble::JobState::Queued: return "Queued"; case RenderAble::JobState::Running: return "Running"; } return {}; } static QString renderAbleName(RenderAble* able) { if(!able->mObjectName.isEmpty()) return able->mObjectName; return QString::number(reinterpret_cast(able), 16); } static QString acquireModeText(Plot_Buffer_Acquire_Mode mode) { return mode == Plot_Buffer_Acquire_Mode::Wait ? "Wait" : "Try"; } static void publishTripleBufferStats(PerformanceShower* shower, const QString& name, const Triple_Buffer_Stats& stats) { if(!shower || !shower->enabled()) return; shower->setGauge("Buffer", name + ".markSuccess", static_cast(stats.mark_success.load(std::memory_order_relaxed))); shower->setGauge("Buffer", name + ".markBusy", static_cast(stats.mark_busy.load(std::memory_order_relaxed))); shower->setGauge("Buffer", name + ".markStateChanged", static_cast(stats.mark_state_changed.load(std::memory_order_relaxed))); shower->setGauge("Buffer", name + ".swapSuccess", static_cast(stats.swap_success.load(std::memory_order_relaxed))); shower->setGauge("Buffer", name + ".swapBusy", static_cast(stats.swap_busy.load(std::memory_order_relaxed))); shower->setGauge("Buffer", name + ".swapStateChanged", static_cast(stats.swap_state_changed.load(std::memory_order_relaxed))); shower->setGauge("Buffer", name + ".swapNoNeed", static_cast(stats.swap_condition_failed.load(std::memory_order_relaxed))); shower->setGauge("Buffer", name + ".wait", static_cast(stats.wait_count.load(std::memory_order_relaxed))); } static void publishRenderAbleBufferStats(PerformanceShower* shower, RenderAble* able) { if(!shower || !shower->enabled()) return; if(able == shower) return; QString name = renderAbleName(able); shower->setState("RenderAble", name + ".bufferAcquireMode", acquireModeText(able->bufferAcquireMode())); publishTripleBufferStats(shower, name + ".State", able->dPtr->mStateBufferStats); publishTripleBufferStats(shower, name + ".Input", able->dPtr->mInputBufferStats); } static void publishPipelineStats(PlotPrivate* data) { PerformanceShower* shower = data->mShower; if(!shower || !shower->enabled()) return; RenderAble::RenderPipeline& pipeline = data->mPipeline; shower->setState("Pipeline", "jobState", jobStateText(pipeline.jobState)); shower->setState("Pipeline", "paintBufferAcquireMode", acquireModeText(data->mPaintBufferAcquireMode.load(std::memory_order_acquire))); shower->setState("Pipeline", "renderAbleDefaultAcquireMode", acquireModeText(data->mRenderAbleBufferAcquireMode.load(std::memory_order_acquire))); shower->setGauge("Pipeline", "activeRenderTasks", data->mActiveRenderTasks.load(std::memory_order_acquire)); shower->setGauge("Pipeline", "renderEnabled", data->mRenderEnabled.load(std::memory_order_acquire) ? 1.0 : 0.0); shower->setGauge("Pipeline", "paintRequestPending", pipeline.paintRequestPending.load(std::memory_order_acquire) ? 1.0 : 0.0); shower->setGauge("Pipeline", "editStateVersion", static_cast(pipeline.editStateVersion.load(std::memory_order_acquire))); shower->setGauge("Pipeline", "renderStateVersion", static_cast(pipeline.renderStateVersion)); publishTripleBufferStats(shower, "Color", pipeline.colorBufferStats); } static void notifyRenderTaskDone(PlotPrivate* data) { if(data->mActiveRenderTasks.fetch_sub(1, std::memory_order_acq_rel) == 1) data->mRenderTaskDone.notify_all(); } void startRenderScheduler() { Global::instance()->startRenderScheduler(); } Plot::Plot() { d = new PlotPrivate(this); setAttribute(Qt::WA_OpaquePaintEvent, true); setMouseTracking(true); setFocusPolicy(Qt::NoFocus); Global::instance()->renderScheduler().registerPlot(this); } bool Plot::usePerformanceShower() { return d->mShower && d->mShower->enabled(); } void Plot::set_usePerformanceShower(bool use) { if(!d->mShower) { d->mShower = new PerformanceShower; d->mShower->init(this, "legend"); } d->mShower->setEnabled(use); } Plot::~Plot() { d->mDestroying.store(true, std::memory_order_release); d->mRenderEnabled.store(false, std::memory_order_release); Global::instance()->renderScheduler().removePlot(this); { std::unique_lock lock(d->mRenderTaskMutex); d->mRenderTaskDone.wait(lock, [this]() { return d->mActiveRenderTasks.load(std::memory_order_acquire) == 0; }); } delete d->mShower; d->mShower = nullptr; delete d; } bool Plot::isRendering() { return d->mRenderEnabled.load(std::memory_order_acquire); } void Plot::startRender(int refreshTimesPreSecond) { d->mRefreshTimesPreSecond = refreshTimesPreSecond; startRender(); } void Plot::startRender() const { if(d->mDestroying.load(std::memory_order_acquire)) return; d->mRenderEnabled.store(true, std::memory_order_release); Plot* self = const_cast(this); PlotPrivate* privateData = d; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); Global::instance()->renderScheduler().post([self, privateData]() { PlotPrivate* d = self->d; if(!d->mDestroying.load(std::memory_order_acquire)) { if(d->mRenderTimer) { d->mRenderTimer->cancel(); self->scheduleRenderTimer(); } self->submitRender(); } notifyRenderTaskDone(privateData); }); } void Plot::pauseRender() const { d->mRenderEnabled.store(false, std::memory_order_release); if(d->mDestroying.load(std::memory_order_acquire)) return; Plot* self = const_cast(this); PlotPrivate* privateData = d; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); Global::instance()->renderScheduler().post([self, privateData]() { PlotPrivate* d = self->d; if(!d->mDestroying.load(std::memory_order_acquire)) { if(d->mRenderTimer) { d->mRenderTimer->cancel(); } } notifyRenderTaskDone(privateData); }); } void Plot::paintEvent(QPaintEvent* event) { Q_UNUSED(event) Cacl paintData("Pipeline", "paintEvent", d->mShower); std::uint64_t paintNs = steadyNowNs(); std::uint64_t updateRequestNs = d->mLastUpdateRequestNs.exchange(0, std::memory_order_acq_rel); if(updateRequestNs && d->mShower) d->mShower->recordDuration("Pipeline", "updateToPaint", elapsedMs(updateRequestNs, paintNs)); QPainter painter(this); auto mode = to_triple_buffer_mode(d->mPaintBufferAcquireMode.load(std::memory_order_acquire)); RenderAble::ColorBufferLease colorLease; { Cacl consumeData("Buffer", "Color.consumeReady", d->mShower); colorLease = d->mPipeline.consumeReadyColor(mode); } RenderAble::ColorBuffer* color = colorLease.buffer; if(color && !color->image.isNull()) painter.drawImage(QPoint(0, 0), color->image); else painter.fillRect(rect(), d->mBackground); if(colorLease) d->mPipeline.unmarkColor(colorLease); for(Layer* layer : mLayerList) { for(RenderAble* able : layer->mRenderAbles) { if(!able->mVisable || !able->dPtr->independentPixelCache()) continue; auto pixelLease = able->dPtr->consumeReadyPixel(Triple_Buffer_Mode::Try); if(pixelLease && !pixelLease.buffer->image.isNull()) painter.drawImage(QPoint(0, 0), pixelLease.buffer->image); if(pixelLease) able->dPtr->unmarkPixel(pixelLease); } } painter.end(); if(d->mShower) { if(colorLease) d->mShower->incrementCounter("Buffer", "Color.frontLeaseSuccess"); else d->mShower->incrementCounter("Buffer", "Color.frontLeaseMiss"); d->mShower->incrementCounter("Pipeline", "paint"); publishPipelineStats(d); } } void Plot::render() { submitRender(); } Plot_Buffer_Acquire_Mode Plot::paintBufferAcquireMode() const { return d->mPaintBufferAcquireMode.load(std::memory_order_acquire); } void Plot::setPaintBufferAcquireMode(Plot_Buffer_Acquire_Mode mode) { d->mPaintBufferAcquireMode.store(mode, std::memory_order_release); } Plot_Buffer_Acquire_Mode Plot::renderAbleBufferAcquireMode() const { return d->mRenderAbleBufferAcquireMode.load(std::memory_order_acquire); } void Plot::setRenderAbleBufferAcquireMode(Plot_Buffer_Acquire_Mode mode) { d->mRenderAbleBufferAcquireMode.store(mode, std::memory_order_release); } void Plot::submitRender() { RenderScheduler& scheduler = Global::instance()->renderScheduler(); if(!scheduler.isSchedulerThread()) { PlotPrivate* privateData = d; std::uint64_t postNs = steadyNowNs(); privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); scheduler.post([this, privateData, postNs]() { if(privateData->mShower) privateData->mShower->recordDuration("Pipeline", "schedulerQueueWait", elapsedMs(postNs, steadyNowNs())); if(!privateData->mDestroying.load(std::memory_order_acquire)) submitRender(); notifyRenderTaskDone(privateData); }); return; } if(d->mDestroying.load(std::memory_order_acquire)) return; std::uint64_t submitNs = steadyNowNs(); std::uint64_t requestNs = d->mLastRequestNs.exchange(0, std::memory_order_acq_rel); if(requestNs && d->mShower) d->mShower->recordDuration("Pipeline", "requestToSubmit", elapsedMs(requestNs, submitNs)); if(mFirstResize) return; if(!d->mRenderEnabled.load(std::memory_order_acquire)) 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->incrementCounter("Pipeline", "renderFrame"); d->mShower->setInfo("interval", QString("渲染:%1, global:%2").arg(qRound(1000.0 / (double)d->mRefreshTimesPreSecond)).arg(Global::instance()->mInterval)); publishPipelineStats(d); } { Cacl prepareData("Pipeline", "prepareData", d->mShower); for(Layer* layer : mLayerList) { for(RenderAble *cur : layer->mRenderAbles) { for(auto rely : cur->mBeRelyList) { if(rely->dPtr->independentPixelCache()) continue; if(!rely->mPrepared){ { Cacl renderAblePrepare("RenderAble", renderAbleName(rely) + ".prepareData", d->mShower); rely->prepareData(); } publishRenderAbleBufferStats(d->mShower, rely); rely->mPrepared = true; } } if(cur->dPtr->independentPixelCache()) continue; if(!cur->mPrepared){ { Cacl renderAblePrepare("RenderAble", renderAbleName(cur) + ".prepareData", d->mShower); cur->prepareData(); } publishRenderAbleBufferStats(d->mShower, cur); cur->mPrepared = true; } } } if(mFirstPrepareData) mFirstPrepareData = false; } for(Layer* layer : mLayerList) { for(RenderAble* able : layer->mRenderAbles) { able->mPrepared = false; } } RenderAble::ColorBufferLease colorLease; { Cacl waitRenderColor("Buffer", "Color.waitRenderLease", d->mShower); colorLease = pipeline.waitRenderColor(); } RenderAble::ColorBuffer* color = colorLease.buffer; QColor background = d->mBackground; std::uint64_t version = pipeline.renderStateVersion; pipeline.jobState = RenderAble::JobState::Running; if(d->mShower) publishPipelineStats(d); PlotPrivate* privateData = d; std::uint64_t cpuPostNs = steadyNowNs(); privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); scheduler.postCpu([this, privateData, colorLease, color, renderSize, background, version, cpuPostNs]() { if(privateData->mShower) privateData->mShower->recordDuration("Pipeline", "cpuQueueWait", elapsedMs(cpuPostNs, steadyNowNs())); if(privateData->mDestroying.load(std::memory_order_acquire)) { privateData->mPipeline.unmarkColor(colorLease); notifyRenderTaskDone(privateData); return; } renderColor(color, renderSize, background, version); privateData->mPipeline.unmarkColor(colorLease); if(privateData->mDestroying.load(std::memory_order_acquire)) { notifyRenderTaskDone(privateData); return; } Global::instance()->renderScheduler().post([this, privateData]() { if(!privateData->mDestroying.load(std::memory_order_acquire)) finishRender(); notifyRenderTaskDone(privateData); }); }); } void Plot::finishRender() { if(d->mDestroying.load(std::memory_order_acquire)) return; RenderAble::RenderPipeline& pipeline = d->mPipeline; QRegion updateRegion; { Cacl publishData("Pipeline", "publishRenderColor", d->mShower); updateRegion = pipeline.publishRenderColor(); } pipeline.jobState = RenderAble::JobState::Idle; if(d->mShower) publishPipelineStats(d); if(!updateRegion.isEmpty()) requestUpdate(updateRegion); if(pipeline.hasNewState()) submitRender(); } void Plot::renderColor(RenderAble::ColorBuffer* color, QSize size, QColor background, std::uint64_t version) { Cacl renderData("Pipeline", "renderColor", 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); QVector leasedAbles; QVector stateLeases; QPainter painter(&color->image); { Cacl drawData("Pipeline", "draw", d->mShower); for(Layer* layer : mLayerList) { for(RenderAble* cur : layer->mRenderAbles) { if(!cur->mVisable || cur->dPtr->independentPixelCache()) continue; auto lease = cur->dPtr->markStateRole(State_Render); if(!lease) { if(d->mShower) d->mShower->incrementCounter("RenderAble", renderAbleName(cur) + ".stateLeaseMiss"); continue; } leasedAbles.append(cur); stateLeases.append(lease); } } for(RenderAble* able : leasedAbles) { Cacl renderAbleDraw("RenderAble", renderAbleName(able) + ".draw", d->mShower); able->draw(&painter); } } for(int i = 0; i < leasedAbles.size(); ++i) { leasedAbles[i]->dPtr->unmarkState(stateLeases[i]); } painter.end(); color->version.store(version, std::memory_order_release); color->dirtyRegion = QRegion(QRect(QPoint(0, 0), size)); } void Plot::markIndependentRenderDirty(RenderAble* able) { if(d->mDestroying.load(std::memory_order_acquire)) return; PlotPrivate* privateData = d; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); Global::instance()->renderScheduler().post([this, privateData, able]() { if(!privateData->mDestroying.load(std::memory_order_acquire)) submitIndependentRender(able); notifyRenderTaskDone(privateData); }); } void Plot::submitIndependentRender(RenderAble* able) { RenderScheduler& scheduler = Global::instance()->renderScheduler(); if(!scheduler.isSchedulerThread()) { markIndependentRenderDirty(able); return; } if(d->mDestroying.load(std::memory_order_acquire)) return; if(!able || !able->mVisable || !able->dPtr->independentPixelCache()) return; if(able->dPtr->mIndependentRenderQueued.exchange(true, std::memory_order_acq_rel)) return; QSize renderSize = d->mRenderSize; if(renderSize.width() <= 0 || renderSize.height() <= 0) { able->dPtr->mIndependentRenderQueued.store(false, std::memory_order_release); return; } if(!able->dPtr->hasNewPixelState()) { able->dPtr->mIndependentRenderQueued.store(false, std::memory_order_release); return; } able->prepareData(); auto pixelLease = able->dPtr->waitRenderPixel(); PixelBufferSlot* pixel = pixelLease.buffer; std::uint64_t version = able->dPtr->mPixelEditVersion.load(std::memory_order_acquire); PlotPrivate* privateData = d; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); scheduler.postCpu([this, privateData, able, pixelLease, pixel, renderSize, version]() { if(privateData->mDestroying.load(std::memory_order_acquire)) { able->dPtr->unmarkPixel(pixelLease); notifyRenderTaskDone(privateData); return; } renderIndependentColor(able, pixel, renderSize, version); able->dPtr->unmarkPixel(pixelLease); Global::instance()->renderScheduler().post([this, privateData, able]() { if(!privateData->mDestroying.load(std::memory_order_acquire)) finishIndependentRender(able); notifyRenderTaskDone(privateData); }); }); } void Plot::renderIndependentColor(RenderAble* able, PixelBufferSlot* pixel, QSize size, std::uint64_t version) { if(pixel->image.size() != size || pixel->image.format() != QImage::Format_ARGB32) { pixel->image = QImage(size, QImage::Format_ARGB32); } pixel->image.detach(); pixel->image.fill(Qt::transparent); QPainter painter(&pixel->image); auto lease = able->dPtr->markStateRole(State_Render); if(lease) { able->draw(&painter); able->dPtr->unmarkState(lease); } painter.end(); pixel->version.store(version, std::memory_order_release); pixel->dirtyRegion = QRegion(QRect(QPoint(0, 0), size)); able->dPtr->mPixelRenderVersion = version; } void Plot::finishIndependentRender(RenderAble* able) { QRegion updateRegion = able->dPtr->publishRenderPixel(); able->dPtr->mIndependentRenderQueued.store(false, std::memory_order_release); if(!updateRegion.isEmpty()) requestUpdate(updateRegion); if(able->dPtr->hasNewPixelState()) submitIndependentRender(able); } void Plot::scheduleRenderTimer() { if(d->mDestroying.load(std::memory_order_acquire) || !d->mRenderTimer || !d->mRenderEnabled.load(std::memory_order_acquire)) return; int interval = std::max(1, qRound(1000.0 / (double)d->mRefreshTimesPreSecond)); d->mRenderTimer->expires_after(std::chrono::milliseconds(interval)); 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)) { if(d->mShower) d->mShower->incrementCounter("Pipeline", "timerTick"); 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; d->mLastRequestNs.store(steadyNowNs(), std::memory_order_release); PlotPrivate* privateData = d; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); Global::instance()->renderScheduler().post([this, privateData]() { if(!d->mDestroying.load(std::memory_order_acquire)) submitRender(); notifyRenderTaskDone(privateData); }); } void Plot::requestUpdate(const QRegion& region) { d->mLastUpdateRequestNs.store(steadyNowNs(), std::memory_order_release); QTimer::singleShot(0, this, [this, region]() { if(d->mDestroying.load(std::memory_order_acquire)) return; if(region.isEmpty()) update(); else update(region); }); } QColor Plot::backgroundColor() { return d->mBackground; } void Plot::setBackgroundColor(const QColor& color) const { d->mBackground = color; } bool Plot::event(QEvent* event) { bool ret = QWidget::event(event); switch (event->type()){ case QEvent::Resize: { auto e = reinterpret_cast(event); QSize renderSize = e->size(); d->mPendingRenderWidth.store(renderSize.width(), std::memory_order_release); d->mPendingRenderHeight.store(renderSize.height(), std::memory_order_release); PlotPrivate* privateData = d; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); Global::instance()->renderScheduler().post([privateData, renderSize]() { if(!privateData->mDestroying.load(std::memory_order_acquire)) privateData->mRenderSize = renderSize; notifyRenderTaskDone(privateData); }); if(mFirstResize) { for(Layer* layer : mLayerList) { for(RenderAble* able : layer->mRenderAbles) { able->firstResizeEvent(e); } } } for(Layer* layer : mLayerList) { for(RenderAble* able : layer->mRenderAbles) { able->resizeEvent(e); } } if(mFirstResize) mFirstResize = false; markRenderStateDirty(); break; } case QEvent::Show: { startRender(); break; } case QEvent::Hide: { pauseRender(); break; } case QEvent::Enter: { break; } case QEvent::Leave: { for(Layer* layer : mLayerList) { for(RenderAble* able : layer->mRenderAbles) { able->setHoverState(QPoint(), false); } } break; } case QEvent::Wheel: { auto e = reinterpret_cast(event); for(Layer* layer : mLayerList) { for(RenderAble* able : layer->mRenderAbles) { able->wheelEvent(e); } } break; } case QEvent::MouseMove: { auto e = reinterpret_cast(event); for(Layer* layer : mLayerList) { for(RenderAble* able : layer->mRenderAbles) { bool selected = able->selectTest(e->pos()); able->setHoverState(e->pos(), selected); if(selected) { able->mouseMoveEvent(e); } } } break; } case QEvent::MouseButtonPress: { auto e = reinterpret_cast(event); for(Layer* layer : mLayerList) { for(RenderAble* able : layer->mRenderAbles) { if(able->selectTest(e->pos())) { able->mousePressEvent(e); } } } break; } case QEvent::MouseButtonRelease: { auto e = reinterpret_cast(event); for(Layer* layer : mLayerList) { for(RenderAble* able : layer->mRenderAbles) { if(able->selectTest(e->pos())) { able->mouseReleaseEvent(e); } } } break; } case QEvent::KeyPress: { auto e = reinterpret_cast(event); for(Layer* layer : mLayerList) { for(RenderAble* able : layer->mRenderAbles) { able->keyPressEvent(e); } } break; } case QEvent::KeyRelease: { auto e = reinterpret_cast(event); for(Layer* layer : mLayerList) { for(RenderAble* able : layer->mRenderAbles) { able->keyReleaseEvent(e); } } break; } default: break; } for(Layer* layer : mLayerList) { for(RenderAble* able : layer->mRenderAbles) { able->plotEvent(event); } } return ret; } }