Files
Renderive/YSGraphic_Core/base/Plot.cpp
T
2026-07-23 18:11:07 +08:00

755 lines
34 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 "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;
}
}