#include "Plot_p.h" #include "Performance_Shower_p.h" #include "Global.h" #include #include namespace YSG { static std::uint64_t steady_now_ns() { return static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count()); } static double elapsed_ms(std::uint64_t startNs, std::uint64_t endNs) { return static_cast(endNs - startNs) / 1000000.0; } static QString job_state_text(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 render_able_name(Renderable* able) { if (!able->mObjectName.isEmpty()) return able->mObjectName; return QString::number(reinterpret_cast(able), 16); } static QString acquire_mode_text(Plot_Buffer_Acquire_Mode mode) { return mode == Plot_Buffer_Acquire_Mode::Wait ? "Wait" : "Try"; } static QString plot_schedule_mode_text(Plot_Render_Schedule_Mode mode) { switch (mode) { case Plot_Render_Schedule_Mode::Timer_Check: return "Timer_Check"; case Plot_Render_Schedule_Mode::Chase_Latest: return "Chase_Latest"; case Plot_Render_Schedule_Mode::Max_Fps: return "Max_Fps"; case Plot_Render_Schedule_Mode::Manual: return "Manual"; } return {}; } static QString render_able_schedule_mode_text(RenderAble_Render_Schedule_Mode mode) { switch (mode) { case RenderAble_Render_Schedule_Mode::Inherit: return "Inherit"; case RenderAble_Render_Schedule_Mode::Chase_Latest: return "Chase_Latest"; case RenderAble_Render_Schedule_Mode::Max_Fps: return "Max_Fps"; case RenderAble_Render_Schedule_Mode::On_Dirty: return "On_Dirty"; case RenderAble_Render_Schedule_Mode::Manual: return "Manual"; } return {}; } static void publish_triple_buffer_stats(Performance_Shower* shower, const QString& name, const Triple_Buffer_Stats& stats) { if (!shower || !shower->enabled()) return; shower->set_gauge("Buffer", name + ".markSuccess", static_cast(stats.mark_success.load(std::memory_order_relaxed))); shower->set_gauge("Buffer", name + ".markBusy", static_cast(stats.mark_busy.load(std::memory_order_relaxed))); shower->set_gauge("Buffer", name + ".markStateChanged", static_cast(stats.mark_state_changed.load(std::memory_order_relaxed))); shower->set_gauge("Buffer", name + ".swapSuccess", static_cast(stats.swap_success.load(std::memory_order_relaxed))); shower->set_gauge("Buffer", name + ".swapBusy", static_cast(stats.swap_busy.load(std::memory_order_relaxed))); shower->set_gauge("Buffer", name + ".swapStateChanged", static_cast(stats.swap_state_changed.load(std::memory_order_relaxed))); shower->set_gauge("Buffer", name + ".swapNoNeed", static_cast(stats.swap_condition_failed.load(std::memory_order_relaxed))); shower->set_gauge("Buffer", name + ".wait", static_cast(stats.wait_count.load(std::memory_order_relaxed))); } static void publish_render_able_buffer_stats(Performance_Shower* shower, Renderable* able) { if (!shower || !shower->enabled()) return; if (able == shower) return; QString name = render_able_name(able); shower->set_state("RenderAble", name + ".buffer_acquire_mode", acquire_mode_text(able->buffer_acquire_mode())); shower->set_state("RenderAble", name + ".render_schedule_mode", render_able_schedule_mode_text(able->render_schedule_mode())); shower->set_gauge("RenderAble", name + ".render_schedule_max_fps", able->render_schedule_max_fps()); publish_triple_buffer_stats(shower, name + ".State", able->dPtr->mStateBufferStats); publish_triple_buffer_stats(shower, name + ".Input", able->dPtr->mInputBufferStats); } static void publish_pipeline_stats(PlotPrivate* data) { Performance_Shower* shower = data->mShower; if (!shower || !shower->enabled()) return; Renderable::Render_Pipeline& pipeline = data->mPipeline; shower->set_state("Pipeline", "jobState", job_state_text(pipeline.jobState)); shower->set_state("Pipeline", "render_schedule_mode", plot_schedule_mode_text(data->mRenderScheduleMode.load(std::memory_order_acquire))); shower->set_state("Pipeline", "paint_buffer_acquire_mode", acquire_mode_text(data->mPaintBufferAcquireMode.load(std::memory_order_acquire))); shower->set_state("Pipeline", "renderAbleDefaultAcquireMode", acquire_mode_text(data->mRenderAbleBufferAcquireMode.load(std::memory_order_acquire))); shower->set_gauge("Pipeline", "render_check_fps", static_cast(data->mRefreshTimesPreSecond)); shower->set_gauge("Pipeline", "max_render_fps", data->mMaxRenderFps.load(std::memory_order_acquire)); shower->set_gauge("Pipeline", "activeRenderTasks", data->mActiveRenderTasks.load(std::memory_order_acquire)); shower->set_gauge("Pipeline", "renderEnabled", data->mRenderEnabled.load(std::memory_order_acquire) ? 1.0 : 0.0); shower->set_gauge("Pipeline", "renderSubmitPending", data->mRenderSubmitPending.load(std::memory_order_acquire) ? 1.0 : 0.0); shower->set_gauge("Pipeline", "paintRequestPending", pipeline.paintRequestPending.load(std::memory_order_acquire) ? 1.0 : 0.0); shower->set_gauge("Pipeline", "editStateVersion", static_cast(pipeline.editStateVersion.load(std::memory_order_acquire))); shower->set_gauge("Pipeline", "renderStateVersion", static_cast(pipeline.renderStateVersion)); publish_triple_buffer_stats(shower, "Color", pipeline.colorBufferStats); } static void notify_render_task_done(PlotPrivate* data) { if (data->mActiveRenderTasks.fetch_sub(1, std::memory_order_acq_rel) == 1) data->mRenderTaskDone.notify_all(); } void start_render_scheduler() { Global::instance()->start_render_scheduler(); } Plot::Plot() { d = new PlotPrivate(this); setAttribute(Qt::WA_OpaquePaintEvent, true); setMouseTracking(true); setFocusPolicy(Qt::NoFocus); Global::instance()->renderScheduler().register_plot(this); } bool Plot::use_performance_shower() { return d->mShower && d->mShower->enabled(); } void Plot::set_use_performance_shower(bool use) { if (!d->mShower) { d->mShower = new Performance_Shower; 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().remove_plot(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::is_rendering() { return d->mRenderEnabled.load(std::memory_order_acquire); } void Plot::start_render(int refreshTimesPreSecond) { set_render_check_fps(refreshTimesPreSecond); start_render(); } void Plot::start_render() 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->schedule_render_timer(); } self->on_render_request(Render_Request_Source::Dirty); } notify_render_task_done(privateData); }); } void Plot::pause_render() 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(); } if (d->mSubmitTimer) { d->mSubmitTimer->cancel(); d->mSubmitTimerTargetNs = 0; d->mRenderSubmitPending.store(false, std::memory_order_release); } } notify_render_task_done(privateData); }); } void Plot::paintEvent(QPaintEvent* event) { Q_UNUSED(event) Cacl paintData("Pipeline", "paintEvent", d->mShower); std::uint64_t paintNs = steady_now_ns(); std::uint64_t updateRequestNs = d->mLastUpdateRequestNs.exchange(0, std::memory_order_acq_rel); if (updateRequestNs && d->mShower) d->mShower->record_duration("Pipeline", "updateToPaint", elapsed_ms(updateRequestNs, paintNs)); QPainter painter(this); auto mode = to_triple_buffer_mode(d->mPaintBufferAcquireMode.load(std::memory_order_acquire)); Renderable::Color_Buffer_Lease colorLease; { Cacl consumeData("Buffer", "Color.consumeReady", d->mShower); colorLease = d->mPipeline.consume_ready_color(mode); } Renderable::Color_Buffer* 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.unmark_color(colorLease); for (Layer* layer : layer_list) { for (Renderable* able : layer->renderable_list) { if (!able->mVisable || !able->dPtr->independent_pixel_cache()) continue; auto pixelLease = able->dPtr->consume_ready_pixel(Triple_Buffer_Mode::Try); if (pixelLease && !pixelLease.buffer->image.isNull()) painter.drawImage(QPoint(0, 0), pixelLease.buffer->image); if (pixelLease) able->dPtr->unmark_pixel(pixelLease); } } painter.end(); if (d->mShower) { if (colorLease) d->mShower->increment_counter("Buffer", "Color.frontLeaseSuccess"); else d->mShower->increment_counter("Buffer", "Color.frontLeaseMiss"); d->mShower->increment_counter("Pipeline", "paint"); publish_pipeline_stats(d); } } void Plot::render() { request_render(Render_Request_Source::Manual); } Plot_Buffer_Acquire_Mode Plot::paint_buffer_acquire_mode() const { return d->mPaintBufferAcquireMode.load(std::memory_order_acquire); } void Plot::set_paint_buffer_acquire_mode(Plot_Buffer_Acquire_Mode mode) { d->mPaintBufferAcquireMode.store(mode, std::memory_order_release); } Plot_Buffer_Acquire_Mode Plot::render_able_buffer_acquire_mode() const { return d->mRenderAbleBufferAcquireMode.load(std::memory_order_acquire); } void Plot::set_render_able_buffer_acquire_mode(Plot_Buffer_Acquire_Mode mode) { d->mRenderAbleBufferAcquireMode.store(mode, std::memory_order_release); } Plot_Render_Schedule_Mode Plot::render_schedule_mode() const { return d->mRenderScheduleMode.load(std::memory_order_acquire); } void Plot::set_render_schedule_mode(Plot_Render_Schedule_Mode mode) { d->mRenderScheduleMode.store(mode, std::memory_order_release); if (mode == Plot_Render_Schedule_Mode::Max_Fps && d->mMaxRenderFps.load(std::memory_order_acquire) <= 0.0) d->mMaxRenderFps.store(render_check_fps(), std::memory_order_release); request_render(Render_Request_Source::Dirty); } double Plot::render_check_fps() const { return static_cast(d->mRefreshTimesPreSecond); } void Plot::set_render_check_fps(double fps) { d->mRefreshTimesPreSecond = qMax(1, qRound(fps)); } double Plot::max_render_fps() const { return d->mMaxRenderFps.load(std::memory_order_acquire); } void Plot::set_max_render_fps(double fps) { d->mMaxRenderFps.store(fps, std::memory_order_release); } bool Plot::submit_render_once() { Render_Scheduler& scheduler = Global::instance()->renderScheduler(); if (!scheduler.is_scheduler_thread()) { PlotPrivate* privateData = d; std::uint64_t postNs = steady_now_ns(); privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); scheduler.post([this, privateData, postNs]() { if (privateData->mShower) privateData->mShower->record_duration("Pipeline", "schedulerQueueWait", elapsed_ms(postNs, steady_now_ns())); if (!privateData->mDestroying.load(std::memory_order_acquire)) submit_render_once(); notify_render_task_done(privateData); }); return false; } if (d->mDestroying.load(std::memory_order_acquire)) return false; std::uint64_t submitNs = steady_now_ns(); Render_Request_Source source = d->mSubmitSource; std::uint64_t requestNs = d->mLastRequestNs.exchange(0, std::memory_order_acq_rel); if (requestNs && d->mShower) d->mShower->record_duration("Pipeline", "requestToSubmit", elapsed_ms(requestNs, submitNs)); if (first_resize) return false; if (!d->mRenderEnabled.load(std::memory_order_acquire)) return false; QSize renderSize = d->mRenderSize; if (renderSize.width() <= 0 || renderSize.height() <= 0) return false; Renderable::Render_Pipeline& pipeline = d->mPipeline; if (pipeline.jobState != Renderable::JobState::Idle) return false; if (!has_submit_candidate(source, submitNs)) return false; pipeline.jobState = Renderable::JobState::Queued; pipeline.renderStateVersion = pipeline.editStateVersion.load(std::memory_order_acquire); if (d->mShower) { d->mShower->increment_counter("Pipeline", "renderFrame"); d->mShower->set_info("interval", QString("渲染:%1, global:%2").arg(qRound(1000.0 / (double)d->mRefreshTimesPreSecond)).arg(Global::instance()->mInterval)); publish_pipeline_stats(d); } { Cacl prepare_data("Pipeline", "prepare_data", d->mShower); for (Layer* layer : layer_list) { for (Renderable* cur : layer->renderable_list) { for (auto rely : cur->mBeRelyList) { if (rely->dPtr->independent_pixel_cache()) continue; if (!render_able_ready_for_submit(rely, source, submitNs, false)) continue; if (!rely->mPrepared) { { Cacl renderAblePrepare("RenderAble", render_able_name(rely) + ".prepare_data", d->mShower); rely->prepare_data(); } mark_render_able_submitted(rely, submitNs); publish_render_able_buffer_stats(d->mShower, rely); rely->mPrepared = true; } } if (cur->dPtr->independent_pixel_cache()) continue; if (!render_able_ready_for_submit(cur, source, submitNs, false)) continue; if (!cur->mPrepared) { { Cacl renderAblePrepare("RenderAble", render_able_name(cur) + ".prepare_data", d->mShower); cur->prepare_data(); } mark_render_able_submitted(cur, submitNs); publish_render_able_buffer_stats(d->mShower, cur); cur->mPrepared = true; } } } if (first_prepare_data) first_prepare_data = false; } for (Layer* layer : layer_list) { for (Renderable* able : layer->renderable_list) { able->mPrepared = false; } } Renderable::Color_Buffer_Lease colorLease; { Cacl wait_render_color("Buffer", "Color.waitRenderLease", d->mShower); colorLease = pipeline.wait_render_color(); } Renderable::Color_Buffer* color = colorLease.buffer; QColor background = d->mBackground; std::uint64_t version = pipeline.renderStateVersion; pipeline.jobState = Renderable::JobState::Running; if (d->mShower) publish_pipeline_stats(d); PlotPrivate* privateData = d; std::uint64_t cpuPostNs = steady_now_ns(); privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); scheduler.post_cpu([this, privateData, colorLease, color, renderSize, background, version, cpuPostNs]() { if (privateData->mShower) privateData->mShower->record_duration("Pipeline", "cpuQueueWait", elapsed_ms(cpuPostNs, steady_now_ns())); if (privateData->mDestroying.load(std::memory_order_acquire)) { privateData->mPipeline.unmark_color(colorLease); notify_render_task_done(privateData); return; } render_color(color, renderSize, background, version); privateData->mPipeline.unmark_color(colorLease); if (privateData->mDestroying.load(std::memory_order_acquire)) { notify_render_task_done(privateData); return; } Global::instance()->renderScheduler().post([this, privateData]() { if (!privateData->mDestroying.load(std::memory_order_acquire)) finish_render(); notify_render_task_done(privateData); }); }); return true; } void Plot::on_render_request(Render_Request_Source source) { if (d->mDestroying.load(std::memory_order_acquire)) return; Plot_Render_Schedule_Mode mode = d->mRenderScheduleMode.load(std::memory_order_acquire); d->mSubmitSource = source; switch (mode) { case Plot_Render_Schedule_Mode::Timer_Check: if (source == Render_Request_Source::Timer) submit_render_once(); break; case Plot_Render_Schedule_Mode::Chase_Latest: submit_render_once(); break; case Plot_Render_Schedule_Mode::Max_Fps: submit_render_with_fps_limit(); break; case Plot_Render_Schedule_Mode::Manual: if (source == Render_Request_Source::Manual) submit_render_once(); break; } for (Layer* layer : layer_list) { for (Renderable* able : layer->renderable_list) { if (able->mVisable && able->dPtr->independent_pixel_cache() && able->dPtr->has_new_pixel_state()) submit_independent_render(able, source); } } if (d->mShower) publish_pipeline_stats(d); } bool Plot::submit_render_with_fps_limit() { double fps = d->mMaxRenderFps.load(std::memory_order_acquire); if (fps <= 0.0) return submit_render_once(); std::uint64_t now = steady_now_ns(); std::uint64_t intervalNs = static_cast(1000000000.0 / fps); if (now < d->mNextSubmitNs) { d->mRenderSubmitPending.store(true, std::memory_order_release); schedule_max_fps_submit(d->mNextSubmitNs); return false; } bool submitted = submit_render_once(); if (submitted) { d->mNextSubmitNs = now + intervalNs; d->mRenderSubmitPending.store(false, std::memory_order_release); } else if (!d->mPipeline.has_new_state()) { d->mRenderSubmitPending.store(false, std::memory_order_release); } return submitted; } void Plot::schedule_max_fps_submit(std::uint64_t targetNs) { if (!d->mSubmitTimer || !d->mRenderEnabled.load(std::memory_order_acquire)) return; if (d->mSubmitTimerTargetNs && d->mSubmitTimerTargetNs <= targetNs) return; d->mSubmitTimerTargetNs = targetNs; std::uint64_t now = steady_now_ns(); std::uint64_t delayNs = targetNs > now ? targetNs - now : 0; d->mSubmitTimer->expires_after(std::chrono::nanoseconds(delayNs)); PlotPrivate* privateData = d; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); d->mSubmitTimer->async_wait([this, privateData, targetNs](const asio::error_code& error) { if (!error && !privateData->mDestroying.load(std::memory_order_acquire) && privateData->mSubmitTimerTargetNs == targetNs) { privateData->mSubmitTimerTargetNs = 0; if (privateData->mRenderSubmitPending.load(std::memory_order_acquire)) on_render_request(privateData->mSubmitSource); } notify_render_task_done(privateData); }); } bool Plot::has_submit_candidate(Render_Request_Source source, std::uint64_t nowNs) { if (first_prepare_data) return true; for (Layer* layer : layer_list) { for (Renderable* able : layer->renderable_list) { if (!able->mVisable || able->dPtr->independent_pixel_cache()) continue; if (able->dPtr->has_pending_render_data() && render_able_ready_for_submit(able, source, nowNs, false)) return true; } } return false; } bool Plot::render_able_ready_for_submit(Renderable* able, Render_Request_Source source, std::uint64_t nowNs, bool independent) { Q_UNUSED(independent) RenderAble_Render_Schedule_Mode mode = able->render_schedule_mode(); if (mode == RenderAble_Render_Schedule_Mode::Inherit) { switch (d->mRenderScheduleMode.load(std::memory_order_acquire)) { case Plot_Render_Schedule_Mode::Timer_Check: return source == Render_Request_Source::Timer; case Plot_Render_Schedule_Mode::Chase_Latest: return true; case Plot_Render_Schedule_Mode::Max_Fps: return true; case Plot_Render_Schedule_Mode::Manual: return source == Render_Request_Source::Manual; } } switch (mode) { case RenderAble_Render_Schedule_Mode::Inherit: return true; case RenderAble_Render_Schedule_Mode::Chase_Latest: return true; case RenderAble_Render_Schedule_Mode::Max_Fps: { double fps = able->render_schedule_max_fps(); if (fps <= 0.0) return true; std::uint64_t nextNs = able->dPtr->mNextRenderSubmitNs.load(std::memory_order_acquire); if (nowNs >= nextNs) return true; d->mRenderSubmitPending.store(true, std::memory_order_release); schedule_max_fps_submit(nextNs); return false; } case RenderAble_Render_Schedule_Mode::On_Dirty: return source == Render_Request_Source::Dirty || source == Render_Request_Source::Finish || source == Render_Request_Source::Manual; case RenderAble_Render_Schedule_Mode::Manual: return source == Render_Request_Source::Manual; } return true; } void Plot::mark_render_able_submitted(Renderable* able, std::uint64_t nowNs) { if (able->render_schedule_mode() != RenderAble_Render_Schedule_Mode::Max_Fps) return; double fps = able->render_schedule_max_fps(); if (fps <= 0.0) return; able->dPtr->mNextRenderSubmitNs.store(nowNs + static_cast(1000000000.0 / fps), std::memory_order_release); } void Plot::finish_render() { if (d->mDestroying.load(std::memory_order_acquire)) return; Renderable::Render_Pipeline& pipeline = d->mPipeline; QRegion updateRegion; { Cacl publishData("Pipeline", "publish_render_color", d->mShower); updateRegion = pipeline.publish_render_color(); } pipeline.jobState = Renderable::JobState::Idle; if (d->mShower) publish_pipeline_stats(d); if (!updateRegion.isEmpty()) request_update(updateRegion); on_render_request(Render_Request_Source::Finish); } void Plot::render_color(Renderable::Color_Buffer* color, QSize size, QColor background, std::uint64_t version) { Cacl renderData("Pipeline", "render_color", 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 : layer_list) { for (Renderable* cur : layer->renderable_list) { if (!cur->mVisable || cur->dPtr->independent_pixel_cache()) continue; auto lease = cur->dPtr->mark_state_role(State_Render); if (!lease) { if (d->mShower) d->mShower->increment_counter("RenderAble", render_able_name(cur) + ".stateLeaseMiss"); continue; } leasedAbles.append(cur); stateLeases.append(lease); } } for (Renderable* able : leasedAbles) { Cacl renderAbleDraw("RenderAble", render_able_name(able) + ".draw", d->mShower); able->draw(&painter); } } for (int i = 0; i < leasedAbles.size(); ++i) { leasedAbles[i]->dPtr->unmark_state(stateLeases[i]); } painter.end(); color->version.store(version, std::memory_order_release); color->dirtyRegion = QRegion(QRect(QPoint(0, 0), size)); } void Plot::mark_independent_render_dirty(Renderable* able, Render_Request_Source source) { 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, source]() { if (!privateData->mDestroying.load(std::memory_order_acquire)) submit_independent_render(able, source); notify_render_task_done(privateData); }); } void Plot::submit_independent_render(Renderable* able, Render_Request_Source source) { Render_Scheduler& scheduler = Global::instance()->renderScheduler(); if (!scheduler.is_scheduler_thread()) { mark_independent_render_dirty(able, source); return; } if (d->mDestroying.load(std::memory_order_acquire)) return; if (!able || !able->mVisable || !able->dPtr->independent_pixel_cache()) return; if (able->dPtr->mIndependentRenderQueued.exchange(true, std::memory_order_acq_rel)) return; std::uint64_t now = steady_now_ns(); if (!render_able_ready_for_submit(able, source, now, true)) { able->dPtr->mIndependentRenderQueued.store(false, std::memory_order_release); 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->has_new_pixel_state()) { able->dPtr->mIndependentRenderQueued.store(false, std::memory_order_release); return; } able->prepare_data(); mark_render_able_submitted(able, now); auto pixelLease = able->dPtr->wait_render_pixel(); Pixel_Buffer_Slot* 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.post_cpu([this, privateData, able, pixelLease, pixel, renderSize, version]() { if (privateData->mDestroying.load(std::memory_order_acquire)) { able->dPtr->unmark_pixel(pixelLease); notify_render_task_done(privateData); return; } render_independent_color(able, pixel, renderSize, version); able->dPtr->unmark_pixel(pixelLease); Global::instance()->renderScheduler().post([this, privateData, able]() { if (!privateData->mDestroying.load(std::memory_order_acquire)) finish_independent_render(able); notify_render_task_done(privateData); }); }); } void Plot::render_independent_color(Renderable* able, Pixel_Buffer_Slot* 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->mark_state_role(State_Render); if (lease) { able->draw(&painter); able->dPtr->unmark_state(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::finish_independent_render(Renderable* able) { QRegion updateRegion = able->dPtr->publish_render_pixel(); able->dPtr->mIndependentRenderQueued.store(false, std::memory_order_release); if (!updateRegion.isEmpty()) request_update(updateRegion); if (able->dPtr->has_new_pixel_state()) submit_independent_render(able, Render_Request_Source::Finish); } void Plot::schedule_render_timer() { 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->increment_counter("Pipeline", "timerTick"); on_render_request(Render_Request_Source::Timer); schedule_render_timer(); } notify_render_task_done(privateData); }); } void Plot::mark_render_state_dirty() { if (d->mDestroying.load(std::memory_order_acquire)) return; d->mPipeline.mark_edit_state(); request_render(); } void Plot::request_render(Render_Request_Source source) { if (d->mDestroying.load(std::memory_order_acquire)) return; d->mLastRequestNs.store(steady_now_ns(), std::memory_order_release); PlotPrivate* privateData = d; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); Global::instance()->renderScheduler().post([this, privateData, source]() { if (!d->mDestroying.load(std::memory_order_acquire)) on_render_request(source); notify_render_task_done(privateData); }); } void Plot::request_update(const QRegion& region) { d->mLastUpdateRequestNs.store(steady_now_ns(), 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::background_color() { return d->mBackground; } void Plot::set_background_color(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; notify_render_task_done(privateData); }); if (first_resize) { for (Layer* layer : layer_list) { for (Renderable* able : layer->renderable_list) { able->first_resize_event(e); } } } for (Layer* layer : layer_list) { for (Renderable* able : layer->renderable_list) { able->resizeEvent(e); } } if (first_resize) first_resize = false; mark_render_state_dirty(); break; } case QEvent::Show: { start_render(); break; } case QEvent::Hide: { pause_render(); break; } case QEvent::Enter: { break; } case QEvent::Leave: { for (Layer* layer : layer_list) { for (Renderable* able : layer->renderable_list) { able->set_hover_state(QPoint(), false); } } break; } case QEvent::Wheel: { auto e = reinterpret_cast(event); for (Layer* layer : layer_list) { for (Renderable* able : layer->renderable_list) { able->wheelEvent(e); } } break; } case QEvent::MouseMove: { auto e = reinterpret_cast(event); for (Layer* layer : layer_list) { for (Renderable* able : layer->renderable_list) { bool selected = able->select_test(e->pos()); able->set_hover_state(e->pos(), selected); if (selected) { able->mouseMoveEvent(e); } } } break; } case QEvent::MouseButtonPress: { auto e = reinterpret_cast(event); for (Layer* layer : layer_list) { for (Renderable* able : layer->renderable_list) { if (able->select_test(e->pos())) { able->mousePressEvent(e); } } } break; } case QEvent::MouseButtonRelease: { auto e = reinterpret_cast(event); for (Layer* layer : layer_list) { for (Renderable* able : layer->renderable_list) { if (able->select_test(e->pos())) { able->mouseReleaseEvent(e); } } } break; } case QEvent::KeyPress: { auto e = reinterpret_cast(event); for (Layer* layer : layer_list) { for (Renderable* able : layer->renderable_list) { able->keyPressEvent(e); } } break; } case QEvent::KeyRelease: { auto e = reinterpret_cast(event); for (Layer* layer : layer_list) { for (Renderable* able : layer->renderable_list) { able->keyReleaseEvent(e); } } break; } default: break; } for (Layer* layer : layer_list) { for (Renderable* able : layer->renderable_list) { able->plot_event(event); } } return ret; } }