#include #include #include #include "../base/global.h" #include "../plottable/Performance_Shower_p.h" #include "Plot_p.h" 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 std::shared_ptr performance_shower_owner(Plot_Private* data) { if (!data || !data->q) return {}; auto context = data->q->render_context(); if (!context) return {}; std::lock_guard lock(context->mutex); return context->performanceShower; } 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 QString renderable_cache_mode_text(Renderable_Cache_Mode mode) { switch (mode) { case Renderable_Cache_Mode::Direct: return "Direct"; case Renderable_Cache_Mode::Local_Pixel: return "Local_Pixel"; } 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_state("RenderAble", name + ".cache_mode", renderable_cache_mode_text(able->cache_mode())); shower->set_gauge("RenderAble", name + ".render_schedule_max_fps", able->render_schedule_max_fps()); std::uint64_t inputPending = 0; std::uint64_t inputDropped = 0; std::uint64_t inputCapacityDropped = 0; std::uint64_t inputRebuildDropped = 0; std::uint64_t inputConsumerDropped = 0; std::uint64_t inputPushed = 0; for (const Input_Buffer_Slot& slot : able->dPtr->mInputBuffers) { if (!slot.data) continue; inputPending += slot.data->pending_count(); inputDropped += slot.data->dropped_count(); inputCapacityDropped += slot.data->capacity_dropped_count(); inputRebuildDropped += slot.data->rebuild_dropped_count(); inputConsumerDropped += slot.data->consumer_dropped_count(); inputPushed += slot.data->pushed_count(); } shower->set_gauge("RenderAble", name + ".input_pending", static_cast(inputPending)); shower->set_gauge("RenderAble", name + ".input_dropped", static_cast(inputDropped)); shower->set_gauge("RenderAble", name + ".input_capacity_dropped", static_cast(inputCapacityDropped)); shower->set_gauge("RenderAble", name + ".input_rebuild_dropped", static_cast(inputRebuildDropped)); shower->set_gauge("RenderAble", name + ".input_consumer_dropped", static_cast(inputConsumerDropped)); shower->set_gauge("RenderAble", name + ".input_pushed", static_cast(inputPushed)); shower->set_gauge("RenderAble", name + ".pixel_cache_area", static_cast(able->cache_image_area())); shower->set_gauge("RenderAble", name + ".pixel_cache_bytes", static_cast(able->cache_image_bytes())); 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(Plot_Private* data) { auto showerOwner = performance_shower_owner(data); Performance_Shower* shower = showerOwner.get(); 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(Plot_Private* 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(); } Abs_Plot::Abs_Plot() { d = new Plot_Private(this); mRenderContext = std::make_shared(); { std::lock_guard lock(mRenderContext->mutex); mRenderContext->backgroundColor = d->mBackground; mRenderContext->refreshTimesPerSecond = d->mRefreshTimesPreSecond; mRenderContext->plot = this; } setAttribute(Qt::WA_OpaquePaintEvent, true); setMouseTracking(true); setFocusPolicy(Qt::NoFocus); Global::instance()->renderScheduler().register_plot(this); } void Abs_Plot::init() { init_renderable_tree(); } void Abs_Plot::init_renderable_tree() { mRootRenderable = std::make_shared(); mRootRenderable->mObjectName = "RootRenderable"; mRootRenderable->init_root(this); } bool Abs_Plot::use_performance_shower() { auto shower = performance_shower_owner(d); return shower && shower->enabled(); } void Abs_Plot::remove_renderable(const std::shared_ptr& able) { if (!able || able->mPlot != this) return; if (able == mRootRenderable) return; std::shared_ptr parent = able->mParentRenderable.lock(); if (parent) parent->remove_child(able); { std::lock_guard lock(mRenderContext->mutex); if (mRenderContext->performanceShower.get() == able.get()) mRenderContext->performanceShower.reset(); } mark_render_state_dirty(); } void Abs_Plot::set_use_performance_shower(bool use) { auto shower = performance_shower_owner(d); if (!shower && use) { auto createdShower = std::make_shared(); createdShower->init(mRootRenderable); std::lock_guard lock(mRenderContext->mutex); mRenderContext->performanceShower = createdShower; } shower = performance_shower_owner(d); if (shower) shower->setEnabled(use); } Abs_Plot::~Abs_Plot() { { std::lock_guard lock(mRenderContext->mutex); d->mDestroying.store(true, std::memory_order_release); mRenderContext->destroying.store(true, std::memory_order_release); mRenderContext->plot = nullptr; } 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; }); } { std::lock_guard lock(mRenderableMutex); if (mRootRenderable) mRootRenderable->detach_from_plot_tree(); mRootRenderable.reset(); } { std::lock_guard lock(mRenderContext->mutex); mRenderContext->performanceShower.reset(); } mRenderContext.reset(); delete d; } std::shared_ptr Abs_Plot::render_context() const { return mRenderContext; } std::shared_ptr Abs_Plot::root_renderable() const { return mRootRenderable; } std::shared_ptr Abs_Plot::create_renderable_node(const std::shared_ptr& parent, const QString& objectName) { if (!parent || parent->mPlot != this) { ASSERT(parent && parent->mPlot == this, "Abs_Plot create_renderable_node error! parent invalid"); return {}; } auto able = std::make_shared(); able->mObjectName = objectName; able->init(parent); return able; } Abs_Plot::RenderableSnapshot Abs_Plot::renderable_snapshot() const { std::lock_guard lock(mRenderableMutex); RenderableSnapshot snapshot; append_renderable_snapshot(snapshot, mRootRenderable); return snapshot; } void Abs_Plot::append_renderable_snapshot(RenderableSnapshot& snapshot, const std::shared_ptr& able) const { if (!able) return; snapshot.append(able); QVector> children = able->children_snapshot(); for (const auto& child : children) append_renderable_snapshot(snapshot, child); } std::shared_ptr Abs_Plot::render_snapshot() const { auto snapshot = std::make_shared(); snapshot->context = mRenderContext; if (!mRenderContext) return snapshot; std::lock_guard lock(mRenderContext->mutex); snapshot->size = mRenderContext->renderSize; snapshot->backgroundColor = mRenderContext->backgroundColor; snapshot->refreshTimesPerSecond = mRenderContext->refreshTimesPerSecond; snapshot->performanceShower = mRenderContext->performanceShower; return snapshot; } bool Abs_Plot::is_rendering() { return d->mRenderEnabled.load(std::memory_order_acquire); } void Abs_Plot::start_render(int refreshTimesPreSecond) { set_render_check_fps(refreshTimesPreSecond); start_render(); } void Abs_Plot::start_render() const { if (d->mDestroying.load(std::memory_order_acquire)) return; d->mRenderEnabled.store(true, std::memory_order_release); Abs_Plot* self = const_cast(this); Plot_Private* privateData = d; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); Global::instance()->renderScheduler().post([self, privateData]() { Plot_Private* 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 Abs_Plot::pause_render() const { d->mRenderEnabled.store(false, std::memory_order_release); if (d->mDestroying.load(std::memory_order_acquire)) return; Abs_Plot* self = const_cast(this); Plot_Private* privateData = d; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); Global::instance()->renderScheduler().post([self, privateData]() { Plot_Private* 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 Abs_Plot::paintEvent(QPaintEvent* event) { Q_UNUSED(event) auto showerOwner = performance_shower_owner(d); Performance_Shower* shower = showerOwner.get(); Cacl paintData("Pipeline", "paintEvent", shower); std::uint64_t paintNs = steady_now_ns(); std::uint64_t updateRequestNs = d->mLastUpdateRequestNs.exchange(0, std::memory_order_acq_rel); if (updateRequestNs && shower) shower->record_duration("Pipeline", "updateToPaint", elapsed_ms(updateRequestNs, paintNs)); QPainter painter(this); auto mode = to_triple_buffer_mode(d->mPaintBufferAcquireMode.load(std::memory_order_acquire)); bool colorLeaseAcquired = false; { Cacl consumeData("Buffer", "Color.consumeReady", shower); auto colorLease = d->mPipeline.consume_ready_color(mode); colorLeaseAcquired = static_cast(colorLease); Renderable::Color_Buffer* color = colorLease.buffer; if (color && !color->image.isNull()) painter.drawImage(QPoint(0, 0), color->image); else painter.fillRect(rect(), d->mBackground); } painter.end(); if (shower) { if (colorLeaseAcquired) shower->increment_counter("Buffer", "Color.frontLeaseSuccess"); else shower->increment_counter("Buffer", "Color.frontLeaseMiss"); shower->increment_counter("Pipeline", "paint"); publish_pipeline_stats(d); } } void Abs_Plot::render() { request_render(Render_Request_Source::Manual); } Plot_Buffer_Acquire_Mode Abs_Plot::paint_buffer_acquire_mode() const { return d->mPaintBufferAcquireMode.load(std::memory_order_acquire); } void Abs_Plot::set_paint_buffer_acquire_mode(Plot_Buffer_Acquire_Mode mode) { d->mPaintBufferAcquireMode.store(mode, std::memory_order_release); } Plot_Buffer_Acquire_Mode Abs_Plot::render_able_buffer_acquire_mode() const { return d->mRenderAbleBufferAcquireMode.load(std::memory_order_acquire); } void Abs_Plot::set_render_able_buffer_acquire_mode(Plot_Buffer_Acquire_Mode mode) { d->mRenderAbleBufferAcquireMode.store(mode, std::memory_order_release); } Plot_Render_Schedule_Mode Abs_Plot::render_schedule_mode() const { return d->mRenderScheduleMode.load(std::memory_order_acquire); } void Abs_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 Abs_Plot::render_check_fps() const { return static_cast(d->mRefreshTimesPreSecond); } void Abs_Plot::set_render_check_fps(double fps) { d->mRefreshTimesPreSecond = qMax(1, qRound(fps)); std::lock_guard lock(mRenderContext->mutex); mRenderContext->refreshTimesPerSecond = d->mRefreshTimesPreSecond; } double Abs_Plot::max_render_fps() const { return d->mMaxRenderFps.load(std::memory_order_acquire); } void Abs_Plot::set_max_render_fps(double fps) { d->mMaxRenderFps.store(fps, std::memory_order_release); } bool Abs_Plot::submit_render_once() { Render_Scheduler& scheduler = Global::instance()->renderScheduler(); if (!scheduler.is_scheduler_thread()) { Plot_Private* privateData = d; std::uint64_t postNs = steady_now_ns(); privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); scheduler.post([this, privateData, postNs]() { auto shower = performance_shower_owner(privateData); if (shower) shower->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); { auto shower = performance_shower_owner(d); if (requestNs && shower) shower->record_duration("Pipeline", "requestToSubmit", elapsed_ms(requestNs, submitNs)); } if (first_resize) return false; if (!d->mRenderEnabled.load(std::memory_order_acquire)) return false; auto frameSnapshot = render_snapshot(); if (frameSnapshot->destroying()) return false; QSize renderSize = frameSnapshot->size; if (renderSize.width() <= 0 || renderSize.height() <= 0) return false; Renderable::Render_Pipeline& pipeline = d->mPipeline; if (pipeline.jobState != Renderable::JobState::Idle) return false; auto snapshot = renderable_snapshot(); auto root = root_renderable(); if (!root) return false; if (!first_prepare_data && !pipeline.has_new_state()) return false; pipeline.jobState = Renderable::JobState::Queued; pipeline.renderStateVersion = pipeline.editStateVersion.load(std::memory_order_acquire); Performance_Shower* shower = frameSnapshot->performanceShower.get(); if (shower) { shower->increment_counter("Pipeline", "renderFrame"); shower->set_info("interval", QString("渲染:%1, global:%2").arg(qRound(1000.0 / (double)frameSnapshot->refreshTimesPerSecond)).arg(Global::instance()->mInterval)); publish_pipeline_stats(d); } { Cacl prepare_data("Pipeline", "prepare_data", shower); for (const auto& cur : snapshot) { if (!cur || cur->retiring() || !cur->mVisable || !cur->dPtr) continue; if (!render_able_ready_for_submit(cur, source, submitNs)) continue; if (!cur->mPrepared) { { Cacl renderAblePrepare("RenderAble", render_able_name(cur.get()) + ".prepare_data", shower); cur->prepare_data(*frameSnapshot); } mark_render_able_submitted(cur, submitNs); publish_render_able_buffer_stats(shower, cur.get()); cur->mPrepared = true; } } if (first_prepare_data) { first_prepare_data = false; mRenderContext->firstPrepareData.store(false, std::memory_order_release); } } for (const auto& able : snapshot) { if (able) able->mPrepared = false; } Renderable::Color_Buffer_Lease colorLease; { Cacl wait_render_color("Buffer", "Color.waitRenderLease", shower); colorLease = pipeline.wait_render_color(); } Renderable::Color_Buffer* color = colorLease.buffer; auto colorLeaseOwner = std::make_shared(std::move(colorLease)); std::uint64_t version = pipeline.renderStateVersion; pipeline.jobState = Renderable::JobState::Running; if (shower) publish_pipeline_stats(d); Plot_Private* privateData = d; std::uint64_t cpuPostNs = steady_now_ns(); auto renderableSnapshot = std::make_shared(std::move(snapshot)); Abs_Plot* plot = this; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); scheduler.post_cpu([privateData, colorLeaseOwner, color, version, cpuPostNs, root, renderableSnapshot, frameSnapshot, plot]() { if (frameSnapshot->performanceShower) frameSnapshot->performanceShower->record_duration("Pipeline", "cpuQueueWait", elapsed_ms(cpuPostNs, steady_now_ns())); if (privateData->mDestroying.load(std::memory_order_acquire)) { colorLeaseOwner->reset(); notify_render_task_done(privateData); return; } Abs_Plot::render_color(color, version, root, *renderableSnapshot, *frameSnapshot); colorLeaseOwner->reset(); if (privateData->mDestroying.load(std::memory_order_acquire)) { notify_render_task_done(privateData); return; } Global::instance()->renderScheduler().post([plot, privateData]() { if (!privateData->mDestroying.load(std::memory_order_acquire)) plot->finish_render(); notify_render_task_done(privateData); }); }); return true; } void Abs_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; } if (performance_shower_owner(d)) publish_pipeline_stats(d); } bool Abs_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 Abs_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)); Plot_Private* 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 Abs_Plot::render_able_ready_for_submit(const std::shared_ptr& able, Render_Request_Source source, std::uint64_t nowNs) { if (!able || able->retiring()) return false; 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 Abs_Plot::mark_render_able_submitted(const std::shared_ptr& 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 Abs_Plot::finish_render() { if (d->mDestroying.load(std::memory_order_acquire)) return; Renderable::Render_Pipeline& pipeline = d->mPipeline; QRegion updateRegion; auto showerOwner = performance_shower_owner(d); Performance_Shower* shower = showerOwner.get(); { Cacl publishData("Pipeline", "publish_render_color", shower); updateRegion = pipeline.publish_render_color(); } pipeline.jobState = Renderable::JobState::Idle; if (shower) publish_pipeline_stats(d); if (!updateRegion.isEmpty()) request_update(updateRegion); on_render_request(Render_Request_Source::Finish); } void Abs_Plot::render_color(Renderable::Color_Buffer* color, std::uint64_t version, const std::shared_ptr& root, const RenderableSnapshot& snapshot, const Plot_Render_Snapshot& renderSnapshot) { Cacl renderData("Pipeline", "render_color", renderSnapshot.performanceShower.get()); QSize size = renderSnapshot.size; if (color->image.size() != size || color->image.format() != QImage::Format_ARGB32) { color->image = QImage(size, QImage::Format_ARGB32); } color->image.detach(); color->image.fill(renderSnapshot.backgroundColor); QPainter painter(&color->image); int renderableCount = 0; int visibleRenderableCount = 0; int directDrawCount = 0; int localCacheCount = 0; { Cacl drawData("Pipeline", "draw", renderSnapshot.performanceShower.get()); for (const auto& cur : snapshot) { ++renderableCount; if (!cur || cur->retiring() || !cur->mVisable) continue; ++visibleRenderableCount; if (cur->cache_mode() == Renderable_Cache_Mode::Local_Pixel) ++localCacheCount; else ++directDrawCount; } if (root) { Cacl renderAbleDraw("RenderAble", render_able_name(root.get()) + ".render", renderSnapshot.performanceShower.get()); root->render(&painter, renderSnapshot); } } painter.end(); color->version.store(version, std::memory_order_release); color->dirtyRegion = QRegion(QRect(QPoint(0, 0), size)); if (renderSnapshot.performanceShower) { double area = static_cast(size.width()) * static_cast(size.height()); renderSnapshot.performanceShower->set_gauge("Pipeline", "main_render_area", area); renderSnapshot.performanceShower->set_gauge("Pipeline", "main_render_full_ratio", area > 0.0 ? 1.0 : 0.0); renderSnapshot.performanceShower->set_gauge("Pipeline", "main_root_child_count", root ? static_cast(root->children_snapshot().size()) : 0.0); renderSnapshot.performanceShower->set_gauge("Pipeline", "main_renderable_count", static_cast(renderableCount)); renderSnapshot.performanceShower->set_gauge("Pipeline", "main_visible_renderable_count", static_cast(visibleRenderableCount)); renderSnapshot.performanceShower->set_gauge("Pipeline", "main_direct_draw_count", static_cast(directDrawCount)); renderSnapshot.performanceShower->set_gauge("Pipeline", "main_local_cache_count", static_cast(localCacheCount)); renderSnapshot.performanceShower->set_gauge("Pipeline", "main_state_lease_miss_count", 0.0); } } void Abs_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)); Plot_Private* 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)) { auto shower = performance_shower_owner(privateData); if (shower) shower->increment_counter("Pipeline", "timerTick"); on_render_request(Render_Request_Source::Timer); schedule_render_timer(); } notify_render_task_done(privateData); }); } void Abs_Plot::mark_render_state_dirty() { if (d->mDestroying.load(std::memory_order_acquire)) return; d->mPipeline.mark_edit_state(); request_render(); } void Abs_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); Plot_Private* 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 Abs_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 Abs_Plot::background_color() { std::lock_guard lock(mRenderContext->mutex); return mRenderContext->backgroundColor; } void Abs_Plot::set_background_color(const QColor& color) const { d->mBackground = color; std::lock_guard lock(mRenderContext->mutex); mRenderContext->backgroundColor = color; const_cast(this)->mark_render_state_dirty(); } bool Abs_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); Plot_Private* privateData = d; privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel); Global::instance()->renderScheduler().post([this, privateData, renderSize]() { if (!privateData->mDestroying.load(std::memory_order_acquire)) { privateData->mRenderSize = renderSize; { std::lock_guard lock(mRenderContext->mutex); mRenderContext->renderSize = renderSize; } privateData->mPipeline.mark_edit_state(); on_render_request(Render_Request_Source::Dirty); } notify_render_task_done(privateData); }); if (first_resize) { auto snapshot = renderable_snapshot(); for (const auto& able : snapshot) able->first_resize_event(e); } { auto snapshot = renderable_snapshot(); for (const auto& able : snapshot) able->resizeEvent(e); } if (first_resize) first_resize = false; mRenderContext->firstResize.store(false, std::memory_order_release); mark_render_state_dirty(); break; } case QEvent::Show: { start_render(); break; } case QEvent::Hide: { pause_render(); break; } case QEvent::Enter: { break; } case QEvent::Leave: { auto snapshot = renderable_snapshot(); for (const auto& able : snapshot) able->set_hover_state(QPoint(), false); break; } case QEvent::Wheel: { auto e = reinterpret_cast(event); auto snapshot = renderable_snapshot(); for (const auto& able : snapshot) able->wheelEvent(e); break; } case QEvent::MouseMove: { auto e = reinterpret_cast(event); auto snapshot = renderable_snapshot(); auto root = root_renderable(); auto hits = root ? root->hit_renderables(e->pos()) : RenderableSnapshot{}; for (const auto& able : snapshot) { if (able) able->set_hover_state(e->pos(), false); } for (const auto& able : hits) { if (!able) continue; able->set_hover_state(e->pos(), true); able->mouseMoveEvent(e); } break; } case QEvent::MouseButtonPress: { auto e = reinterpret_cast(event); auto root = root_renderable(); auto hits = root ? root->hit_renderables(e->pos()) : RenderableSnapshot{}; for (const auto& able : hits) able->mousePressEvent(e); break; } case QEvent::MouseButtonRelease: { auto e = reinterpret_cast(event); auto root = root_renderable(); auto hits = root ? root->hit_renderables(e->pos()) : RenderableSnapshot{}; for (const auto& able : hits) able->mouseReleaseEvent(e); break; } case QEvent::KeyPress: { auto e = reinterpret_cast(event); auto snapshot = renderable_snapshot(); for (const auto& able : snapshot) able->keyPressEvent(e); break; } case QEvent::KeyRelease: { auto e = reinterpret_cast(event); auto snapshot = renderable_snapshot(); for (const auto& able : snapshot) able->keyReleaseEvent(e); break; } default: break; } auto snapshot = renderable_snapshot(); for (const auto& able : snapshot) able->plot_event(event); return ret; } Plot::Plot() = default; } // namespace YSG