755 lines
34 KiB
C++
755 lines
34 KiB
C++
#include "Plot_p.h"
|
||
#include "Performance_Shower_p.h"
|
||
#include "Global.h"
|
||
#include <algorithm>
|
||
#include <chrono>
|
||
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 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<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_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<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(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<std::mutex> 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<Plot*>(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<Plot*>(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<double>(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<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 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<std::uint64_t>(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<Renderable*> leasedAbles;
|
||
QVector<Triple_Buffer_Lease> 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<QResizeEvent*>(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<QWheelEvent*>(event);
|
||
for (Layer* layer : layer_list) {
|
||
for (Renderable* able : layer->renderable_list) {
|
||
able->wheelEvent(e);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case QEvent::MouseMove: {
|
||
auto e = reinterpret_cast<QMouseEvent*>(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<QMouseEvent*>(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<QMouseEvent*>(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<QKeyEvent*>(event);
|
||
for (Layer* layer : layer_list) {
|
||
for (Renderable* able : layer->renderable_list) {
|
||
able->keyPressEvent(e);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case QEvent::KeyRelease: {
|
||
auto e = reinterpret_cast<QKeyEvent*>(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;
|
||
}
|
||
}
|