Files
Renderive/YSGraphic_Core/architecture/Plot.cpp
T
2026-07-28 17:55:29 +08:00

864 lines
37 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <algorithm>
#include <chrono>
#include <memory>
#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::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count());
}
static double elapsed_ms(std::uint64_t startNs, std::uint64_t endNs) {
return static_cast<double>(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<std::uintptr_t>(able), 16);
}
static std::shared_ptr<Performance_Shower> performance_shower_owner(Plot_Private* data) {
if (!data || !data->q)
return {};
auto context = data->q->render_context();
if (!context)
return {};
std::lock_guard<std::mutex> 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<double>(stats.mark_success.load(std::memory_order_relaxed)));
shower->set_gauge("Buffer", name + ".markBusy", static_cast<double>(stats.mark_busy.load(std::memory_order_relaxed)));
shower->set_gauge("Buffer", name + ".markStateChanged", static_cast<double>(stats.mark_state_changed.load(std::memory_order_relaxed)));
shower->set_gauge("Buffer", name + ".swapSuccess", static_cast<double>(stats.swap_success.load(std::memory_order_relaxed)));
shower->set_gauge("Buffer", name + ".swapBusy", static_cast<double>(stats.swap_busy.load(std::memory_order_relaxed)));
shower->set_gauge("Buffer", name + ".swapStateChanged", static_cast<double>(stats.swap_state_changed.load(std::memory_order_relaxed)));
shower->set_gauge("Buffer", name + ".swapNoNeed", static_cast<double>(stats.swap_condition_failed.load(std::memory_order_relaxed)));
shower->set_gauge("Buffer", name + ".wait", static_cast<double>(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<double>(inputPending));
shower->set_gauge("RenderAble", name + ".input_dropped", static_cast<double>(inputDropped));
shower->set_gauge("RenderAble", name + ".input_capacity_dropped", static_cast<double>(inputCapacityDropped));
shower->set_gauge("RenderAble", name + ".input_rebuild_dropped", static_cast<double>(inputRebuildDropped));
shower->set_gauge("RenderAble", name + ".input_consumer_dropped", static_cast<double>(inputConsumerDropped));
shower->set_gauge("RenderAble", name + ".input_pushed", static_cast<double>(inputPushed));
shower->set_gauge("RenderAble", name + ".pixel_cache_area", static_cast<double>(able->cache_image_area()));
shower->set_gauge("RenderAble", name + ".pixel_cache_bytes", static_cast<double>(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<double>(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<double>(pipeline.editStateVersion.load(std::memory_order_acquire)));
shower->set_gauge("Pipeline", "renderStateVersion", static_cast<double>(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<Plot_Render_Context>();
{
std::lock_guard<std::mutex> 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<Renderable>();
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<Renderable>& able) {
if (!able || able->mPlot != this)
return;
if (able == mRootRenderable)
return;
std::shared_ptr<Renderable> parent = able->mParentRenderable.lock();
if (parent)
parent->remove_child(able);
{
std::lock_guard<std::mutex> 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<Performance_Shower>();
createdShower->init(mRootRenderable);
std::lock_guard<std::mutex> lock(mRenderContext->mutex);
mRenderContext->performanceShower = createdShower;
}
shower = performance_shower_owner(d);
if (shower)
shower->setEnabled(use);
}
Abs_Plot::~Abs_Plot() {
{
std::lock_guard<std::mutex> 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<std::mutex> lock(d->mRenderTaskMutex);
d->mRenderTaskDone.wait(lock, [this]() {
return d->mActiveRenderTasks.load(std::memory_order_acquire) == 0;
});
}
{
std::lock_guard<std::mutex> lock(mRenderableMutex);
if (mRootRenderable)
mRootRenderable->detach_from_plot_tree();
mRootRenderable.reset();
}
{
std::lock_guard<std::mutex> lock(mRenderContext->mutex);
mRenderContext->performanceShower.reset();
}
mRenderContext.reset();
delete d;
}
std::shared_ptr<Plot_Render_Context> Abs_Plot::render_context() const {
return mRenderContext;
}
std::shared_ptr<Renderable> Abs_Plot::root_renderable() const {
return mRootRenderable;
}
std::shared_ptr<Renderable> Abs_Plot::create_renderable_node(const std::shared_ptr<Renderable>& 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<Renderable>();
able->mObjectName = objectName;
able->init(parent);
return able;
}
Abs_Plot::RenderableSnapshot Abs_Plot::renderable_snapshot() const {
std::lock_guard<std::mutex> lock(mRenderableMutex);
RenderableSnapshot snapshot;
append_renderable_snapshot(snapshot, mRootRenderable);
return snapshot;
}
void Abs_Plot::append_renderable_snapshot(RenderableSnapshot& snapshot, const std::shared_ptr<Renderable>& able) const {
if (!able)
return;
snapshot.append(able);
QVector<std::shared_ptr<Renderable>> children = able->children_snapshot();
for (const auto& child : children)
append_renderable_snapshot(snapshot, child);
}
std::shared_ptr<const Plot_Render_Snapshot> Abs_Plot::render_snapshot() const {
auto snapshot = std::make_shared<Plot_Render_Snapshot>();
snapshot->context = mRenderContext;
if (!mRenderContext)
return snapshot;
std::lock_guard<std::mutex> 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<Abs_Plot*>(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<Abs_Plot*>(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<bool>(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<double>(d->mRefreshTimesPreSecond);
}
void Abs_Plot::set_render_check_fps(double fps) {
d->mRefreshTimesPreSecond = qMax(1, qRound(fps));
std::lock_guard<std::mutex> 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<Renderable::Color_Buffer_Lease>(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<RenderableSnapshot>(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<std::uint64_t>(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<Renderable>& 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<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<std::uint64_t>(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<Renderable>& 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<double>(size.width()) * static_cast<double>(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<double>(root->children_snapshot().size()) : 0.0);
renderSnapshot.performanceShower->set_gauge("Pipeline", "main_renderable_count", static_cast<double>(renderableCount));
renderSnapshot.performanceShower->set_gauge("Pipeline", "main_visible_renderable_count", static_cast<double>(visibleRenderableCount));
renderSnapshot.performanceShower->set_gauge("Pipeline", "main_direct_draw_count", static_cast<double>(directDrawCount));
renderSnapshot.performanceShower->set_gauge("Pipeline", "main_local_cache_count", static_cast<double>(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<std::mutex> lock(mRenderContext->mutex);
return mRenderContext->backgroundColor;
}
void Abs_Plot::set_background_color(const QColor& color) const {
d->mBackground = color;
std::lock_guard<std::mutex> lock(mRenderContext->mutex);
mRenderContext->backgroundColor = color;
const_cast<Abs_Plot*>(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<QResizeEvent*>(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<std::mutex> 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<QWheelEvent*>(event);
auto snapshot = renderable_snapshot();
for (const auto& able : snapshot)
able->wheelEvent(e);
break;
}
case QEvent::MouseMove: {
auto e = reinterpret_cast<QMouseEvent*>(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<QMouseEvent*>(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<QMouseEvent*>(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<QKeyEvent*>(event);
auto snapshot = renderable_snapshot();
for (const auto& able : snapshot)
able->keyPressEvent(e);
break;
}
case QEvent::KeyRelease: {
auto e = reinterpret_cast<QKeyEvent*>(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