864 lines
37 KiB
C++
864 lines
37 KiB
C++
#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
|