Files
Renderive/YSGraphic_Core/base/Plot.cpp
T
2026-07-23 15:31:30 +08:00

767 lines
36 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 "PerformanceShower_p.h"
#include "Global.h"
#include <algorithm>
#include <chrono>
namespace YSG {
static std::uint64_t steadyNowNs() {
return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count());
}
static double elapsedMs(std::uint64_t startNs, std::uint64_t endNs) {
return static_cast<double>(endNs - startNs) / 1000000.0;
}
static QString jobStateText(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 renderAbleName(RenderAble* able) {
if(!able->mObjectName.isEmpty()) return able->mObjectName;
return QString::number(reinterpret_cast<std::uintptr_t>(able), 16);
}
static QString acquireModeText(Plot_Buffer_Acquire_Mode mode) {
return mode == Plot_Buffer_Acquire_Mode::Wait ? "Wait" : "Try";
}
static QString plotScheduleModeText(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 renderAbleScheduleModeText(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 publishTripleBufferStats(PerformanceShower* shower, const QString& name, const Triple_Buffer_Stats& stats) {
if(!shower || !shower->enabled()) return;
shower->setGauge("Buffer", name + ".markSuccess", static_cast<double>(stats.mark_success.load(std::memory_order_relaxed)));
shower->setGauge("Buffer", name + ".markBusy", static_cast<double>(stats.mark_busy.load(std::memory_order_relaxed)));
shower->setGauge("Buffer", name + ".markStateChanged", static_cast<double>(stats.mark_state_changed.load(std::memory_order_relaxed)));
shower->setGauge("Buffer", name + ".swapSuccess", static_cast<double>(stats.swap_success.load(std::memory_order_relaxed)));
shower->setGauge("Buffer", name + ".swapBusy", static_cast<double>(stats.swap_busy.load(std::memory_order_relaxed)));
shower->setGauge("Buffer", name + ".swapStateChanged", static_cast<double>(stats.swap_state_changed.load(std::memory_order_relaxed)));
shower->setGauge("Buffer", name + ".swapNoNeed", static_cast<double>(stats.swap_condition_failed.load(std::memory_order_relaxed)));
shower->setGauge("Buffer", name + ".wait", static_cast<double>(stats.wait_count.load(std::memory_order_relaxed)));
}
static void publishRenderAbleBufferStats(PerformanceShower* shower, RenderAble* able) {
if(!shower || !shower->enabled()) return;
if(able == shower) return;
QString name = renderAbleName(able);
shower->setState("RenderAble", name + ".bufferAcquireMode", acquireModeText(able->bufferAcquireMode()));
shower->setState("RenderAble", name + ".renderScheduleMode", renderAbleScheduleModeText(able->renderScheduleMode()));
shower->setGauge("RenderAble", name + ".renderScheduleMaxFps", able->renderScheduleMaxFps());
publishTripleBufferStats(shower, name + ".State", able->dPtr->mStateBufferStats);
publishTripleBufferStats(shower, name + ".Input", able->dPtr->mInputBufferStats);
}
static void publishPipelineStats(PlotPrivate* data) {
PerformanceShower* shower = data->mShower;
if(!shower || !shower->enabled()) return;
RenderAble::RenderPipeline& pipeline = data->mPipeline;
shower->setState("Pipeline", "jobState", jobStateText(pipeline.jobState));
shower->setState("Pipeline", "renderScheduleMode", plotScheduleModeText(data->mRenderScheduleMode.load(std::memory_order_acquire)));
shower->setState("Pipeline", "paintBufferAcquireMode", acquireModeText(data->mPaintBufferAcquireMode.load(std::memory_order_acquire)));
shower->setState("Pipeline", "renderAbleDefaultAcquireMode", acquireModeText(data->mRenderAbleBufferAcquireMode.load(std::memory_order_acquire)));
shower->setGauge("Pipeline", "renderCheckFps", static_cast<double>(data->mRefreshTimesPreSecond));
shower->setGauge("Pipeline", "maxRenderFps", data->mMaxRenderFps.load(std::memory_order_acquire));
shower->setGauge("Pipeline", "activeRenderTasks", data->mActiveRenderTasks.load(std::memory_order_acquire));
shower->setGauge("Pipeline", "renderEnabled", data->mRenderEnabled.load(std::memory_order_acquire) ? 1.0 : 0.0);
shower->setGauge("Pipeline", "renderSubmitPending", data->mRenderSubmitPending.load(std::memory_order_acquire) ? 1.0 : 0.0);
shower->setGauge("Pipeline", "paintRequestPending", pipeline.paintRequestPending.load(std::memory_order_acquire) ? 1.0 : 0.0);
shower->setGauge("Pipeline", "editStateVersion", static_cast<double>(pipeline.editStateVersion.load(std::memory_order_acquire)));
shower->setGauge("Pipeline", "renderStateVersion", static_cast<double>(pipeline.renderStateVersion));
publishTripleBufferStats(shower, "Color", pipeline.colorBufferStats);
}
static void notifyRenderTaskDone(PlotPrivate* data) {
if(data->mActiveRenderTasks.fetch_sub(1, std::memory_order_acq_rel) == 1) data->mRenderTaskDone.notify_all();
}
void startRenderScheduler() {
Global::instance()->startRenderScheduler();
}
Plot::Plot() {
d = new PlotPrivate(this);
setAttribute(Qt::WA_OpaquePaintEvent, true);
setMouseTracking(true);
setFocusPolicy(Qt::NoFocus);
Global::instance()->renderScheduler().registerPlot(this);
}
bool Plot::usePerformanceShower() {
return d->mShower && d->mShower->enabled();
}
void Plot::set_usePerformanceShower(bool use) {
if(!d->mShower) {
d->mShower = new PerformanceShower;
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().removePlot(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::isRendering() {
return d->mRenderEnabled.load(std::memory_order_acquire);
}
void Plot::startRender(int refreshTimesPreSecond) {
setRenderCheckFps(refreshTimesPreSecond);
startRender();
}
void Plot::startRender() 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->scheduleRenderTimer();
}
self->onRenderRequest(Render_Request_Source::Dirty);
}
notifyRenderTaskDone(privateData);
});
}
void Plot::pauseRender() 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);
}
}
notifyRenderTaskDone(privateData);
});
}
void Plot::paintEvent(QPaintEvent* event) {
Q_UNUSED(event)
Cacl paintData("Pipeline", "paintEvent", d->mShower);
std::uint64_t paintNs = steadyNowNs();
std::uint64_t updateRequestNs = d->mLastUpdateRequestNs.exchange(0, std::memory_order_acq_rel);
if(updateRequestNs && d->mShower) d->mShower->recordDuration("Pipeline", "updateToPaint", elapsedMs(updateRequestNs, paintNs));
QPainter painter(this);
auto mode = to_triple_buffer_mode(d->mPaintBufferAcquireMode.load(std::memory_order_acquire));
RenderAble::ColorBufferLease colorLease;
{
Cacl consumeData("Buffer", "Color.consumeReady", d->mShower);
colorLease = d->mPipeline.consumeReadyColor(mode);
}
RenderAble::ColorBuffer* 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.unmarkColor(colorLease);
for(Layer* layer : mLayerList) {
for(RenderAble* able : layer->mRenderAbles) {
if(!able->mVisable || !able->dPtr->independentPixelCache()) continue;
auto pixelLease = able->dPtr->consumeReadyPixel(Triple_Buffer_Mode::Try);
if(pixelLease && !pixelLease.buffer->image.isNull()) painter.drawImage(QPoint(0, 0), pixelLease.buffer->image);
if(pixelLease) able->dPtr->unmarkPixel(pixelLease);
}
}
painter.end();
if(d->mShower) {
if(colorLease) d->mShower->incrementCounter("Buffer", "Color.frontLeaseSuccess");
else d->mShower->incrementCounter("Buffer", "Color.frontLeaseMiss");
d->mShower->incrementCounter("Pipeline", "paint");
publishPipelineStats(d);
}
}
void Plot::render() {
requestRender(Render_Request_Source::Manual);
}
Plot_Buffer_Acquire_Mode Plot::paintBufferAcquireMode() const {
return d->mPaintBufferAcquireMode.load(std::memory_order_acquire);
}
void Plot::setPaintBufferAcquireMode(Plot_Buffer_Acquire_Mode mode) {
d->mPaintBufferAcquireMode.store(mode, std::memory_order_release);
}
Plot_Buffer_Acquire_Mode Plot::renderAbleBufferAcquireMode() const {
return d->mRenderAbleBufferAcquireMode.load(std::memory_order_acquire);
}
void Plot::setRenderAbleBufferAcquireMode(Plot_Buffer_Acquire_Mode mode) {
d->mRenderAbleBufferAcquireMode.store(mode, std::memory_order_release);
}
Plot_Render_Schedule_Mode Plot::renderScheduleMode() const {
return d->mRenderScheduleMode.load(std::memory_order_acquire);
}
void Plot::setRenderScheduleMode(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(renderCheckFps(), std::memory_order_release);
requestRender(Render_Request_Source::Dirty);
}
double Plot::renderCheckFps() const {
return static_cast<double>(d->mRefreshTimesPreSecond);
}
void Plot::setRenderCheckFps(double fps) {
d->mRefreshTimesPreSecond = qMax(1, qRound(fps));
}
double Plot::maxRenderFps() const {
return d->mMaxRenderFps.load(std::memory_order_acquire);
}
void Plot::setMaxRenderFps(double fps) {
d->mMaxRenderFps.store(fps, std::memory_order_release);
}
bool Plot::submitRenderOnce() {
RenderScheduler& scheduler = Global::instance()->renderScheduler();
if(!scheduler.isSchedulerThread()) {
PlotPrivate* privateData = d;
std::uint64_t postNs = steadyNowNs();
privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel);
scheduler.post([this, privateData, postNs]() {
if(privateData->mShower) privateData->mShower->recordDuration("Pipeline", "schedulerQueueWait", elapsedMs(postNs, steadyNowNs()));
if(!privateData->mDestroying.load(std::memory_order_acquire)) submitRenderOnce();
notifyRenderTaskDone(privateData);
});
return false;
}
if(d->mDestroying.load(std::memory_order_acquire)) return false;
std::uint64_t submitNs = steadyNowNs();
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->recordDuration("Pipeline", "requestToSubmit", elapsedMs(requestNs, submitNs));
if(mFirstResize) 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::RenderPipeline& pipeline = d->mPipeline;
if(pipeline.jobState != RenderAble::JobState::Idle) return false;
if(!hasSubmitCandidate(source, submitNs)) return false;
pipeline.jobState = RenderAble::JobState::Queued;
pipeline.renderStateVersion = pipeline.editStateVersion.load(std::memory_order_acquire);
if(d->mShower) {
d->mShower->incrementCounter("Pipeline", "renderFrame");
d->mShower->setInfo("interval", QString("渲染:%1, global%2").arg(qRound(1000.0 / (double)d->mRefreshTimesPreSecond)).arg(Global::instance()->mInterval));
publishPipelineStats(d);
}
{
Cacl prepareData("Pipeline", "prepareData", d->mShower);
for(Layer* layer : mLayerList) {
for(RenderAble *cur : layer->mRenderAbles) {
for(auto rely : cur->mBeRelyList) {
if(rely->dPtr->independentPixelCache()) continue;
if(!renderAbleReadyForSubmit(rely, source, submitNs, false)) continue;
if(!rely->mPrepared){
{
Cacl renderAblePrepare("RenderAble", renderAbleName(rely) + ".prepareData", d->mShower);
rely->prepareData();
}
markRenderAbleSubmitted(rely, submitNs);
publishRenderAbleBufferStats(d->mShower, rely);
rely->mPrepared = true;
}
}
if(cur->dPtr->independentPixelCache()) continue;
if(!renderAbleReadyForSubmit(cur, source, submitNs, false)) continue;
if(!cur->mPrepared){
{
Cacl renderAblePrepare("RenderAble", renderAbleName(cur) + ".prepareData", d->mShower);
cur->prepareData();
}
markRenderAbleSubmitted(cur, submitNs);
publishRenderAbleBufferStats(d->mShower, cur);
cur->mPrepared = true;
}
}
}
if(mFirstPrepareData) mFirstPrepareData = false;
}
for(Layer* layer : mLayerList) {
for(RenderAble* able : layer->mRenderAbles) {
able->mPrepared = false;
}
}
RenderAble::ColorBufferLease colorLease;
{
Cacl waitRenderColor("Buffer", "Color.waitRenderLease", d->mShower);
colorLease = pipeline.waitRenderColor();
}
RenderAble::ColorBuffer* color = colorLease.buffer;
QColor background = d->mBackground;
std::uint64_t version = pipeline.renderStateVersion;
pipeline.jobState = RenderAble::JobState::Running;
if(d->mShower) publishPipelineStats(d);
PlotPrivate* privateData = d;
std::uint64_t cpuPostNs = steadyNowNs();
privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel);
scheduler.postCpu([this, privateData, colorLease, color, renderSize, background, version, cpuPostNs]() {
if(privateData->mShower) privateData->mShower->recordDuration("Pipeline", "cpuQueueWait", elapsedMs(cpuPostNs, steadyNowNs()));
if(privateData->mDestroying.load(std::memory_order_acquire)) {
privateData->mPipeline.unmarkColor(colorLease);
notifyRenderTaskDone(privateData);
return;
}
renderColor(color, renderSize, background, version);
privateData->mPipeline.unmarkColor(colorLease);
if(privateData->mDestroying.load(std::memory_order_acquire)) {
notifyRenderTaskDone(privateData);
return;
}
Global::instance()->renderScheduler().post([this, privateData]() {
if(!privateData->mDestroying.load(std::memory_order_acquire)) finishRender();
notifyRenderTaskDone(privateData);
});
});
return true;
}
void Plot::onRenderRequest(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) submitRenderOnce();
break;
case Plot_Render_Schedule_Mode::Chase_Latest:
submitRenderOnce();
break;
case Plot_Render_Schedule_Mode::Max_Fps:
submitRenderWithFpsLimit();
break;
case Plot_Render_Schedule_Mode::Manual:
if(source == Render_Request_Source::Manual) submitRenderOnce();
break;
}
for(Layer* layer : mLayerList) {
for(RenderAble* able : layer->mRenderAbles) {
if(able->mVisable && able->dPtr->independentPixelCache() && able->dPtr->hasNewPixelState()) submitIndependentRender(able, source);
}
}
if(d->mShower) publishPipelineStats(d);
}
bool Plot::submitRenderWithFpsLimit() {
double fps = d->mMaxRenderFps.load(std::memory_order_acquire);
if(fps <= 0.0) return submitRenderOnce();
std::uint64_t now = steadyNowNs();
std::uint64_t intervalNs = static_cast<std::uint64_t>(1000000000.0 / fps);
if(now < d->mNextSubmitNs) {
d->mRenderSubmitPending.store(true, std::memory_order_release);
scheduleMaxFpsSubmit(d->mNextSubmitNs);
return false;
}
bool submitted = submitRenderOnce();
if(submitted) {
d->mNextSubmitNs = now + intervalNs;
d->mRenderSubmitPending.store(false, std::memory_order_release);
} else if(!d->mPipeline.hasNewState()) {
d->mRenderSubmitPending.store(false, std::memory_order_release);
}
return submitted;
}
void Plot::scheduleMaxFpsSubmit(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 = steadyNowNs();
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)) onRenderRequest(privateData->mSubmitSource);
}
notifyRenderTaskDone(privateData);
});
}
bool Plot::hasSubmitCandidate(Render_Request_Source source, std::uint64_t nowNs) {
if(mFirstPrepareData) return true;
for(Layer* layer : mLayerList) {
for(RenderAble* able : layer->mRenderAbles) {
if(!able->mVisable || able->dPtr->independentPixelCache()) continue;
if(able->dPtr->hasPendingRenderData() && renderAbleReadyForSubmit(able, source, nowNs, false)) return true;
}
}
return false;
}
bool Plot::renderAbleReadyForSubmit(RenderAble* able, Render_Request_Source source, std::uint64_t nowNs, bool independent) {
Q_UNUSED(independent)
RenderAble_Render_Schedule_Mode mode = able->renderScheduleMode();
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->renderScheduleMaxFps();
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);
scheduleMaxFpsSubmit(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::markRenderAbleSubmitted(RenderAble* able, std::uint64_t nowNs) {
if(able->renderScheduleMode() != RenderAble_Render_Schedule_Mode::Max_Fps) return;
double fps = able->renderScheduleMaxFps();
if(fps <= 0.0) return;
able->dPtr->mNextRenderSubmitNs.store(nowNs + static_cast<std::uint64_t>(1000000000.0 / fps), std::memory_order_release);
}
void Plot::finishRender() {
if(d->mDestroying.load(std::memory_order_acquire)) return;
RenderAble::RenderPipeline& pipeline = d->mPipeline;
QRegion updateRegion;
{
Cacl publishData("Pipeline", "publishRenderColor", d->mShower);
updateRegion = pipeline.publishRenderColor();
}
pipeline.jobState = RenderAble::JobState::Idle;
if(d->mShower) publishPipelineStats(d);
if(!updateRegion.isEmpty()) requestUpdate(updateRegion);
onRenderRequest(Render_Request_Source::Finish);
}
void Plot::renderColor(RenderAble::ColorBuffer* color, QSize size, QColor background, std::uint64_t version) {
Cacl renderData("Pipeline", "renderColor", 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 : mLayerList) {
for(RenderAble* cur : layer->mRenderAbles) {
if(!cur->mVisable || cur->dPtr->independentPixelCache()) continue;
auto lease = cur->dPtr->markStateRole(State_Render);
if(!lease) {
if(d->mShower) d->mShower->incrementCounter("RenderAble", renderAbleName(cur) + ".stateLeaseMiss");
continue;
}
leasedAbles.append(cur);
stateLeases.append(lease);
}
}
for(RenderAble* able : leasedAbles) {
Cacl renderAbleDraw("RenderAble", renderAbleName(able) + ".draw", d->mShower);
able->draw(&painter);
}
}
for(int i = 0; i < leasedAbles.size(); ++i) {
leasedAbles[i]->dPtr->unmarkState(stateLeases[i]);
}
painter.end();
color->version.store(version, std::memory_order_release);
color->dirtyRegion = QRegion(QRect(QPoint(0, 0), size));
}
void Plot::markIndependentRenderDirty(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)) submitIndependentRender(able, source);
notifyRenderTaskDone(privateData);
});
}
void Plot::submitIndependentRender(RenderAble* able, Render_Request_Source source) {
RenderScheduler& scheduler = Global::instance()->renderScheduler();
if(!scheduler.isSchedulerThread()) {
markIndependentRenderDirty(able, source);
return;
}
if(d->mDestroying.load(std::memory_order_acquire)) return;
if(!able || !able->mVisable || !able->dPtr->independentPixelCache()) return;
if(able->dPtr->mIndependentRenderQueued.exchange(true, std::memory_order_acq_rel)) return;
std::uint64_t now = steadyNowNs();
if(!renderAbleReadyForSubmit(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->hasNewPixelState()) {
able->dPtr->mIndependentRenderQueued.store(false, std::memory_order_release);
return;
}
able->prepareData();
markRenderAbleSubmitted(able, now);
auto pixelLease = able->dPtr->waitRenderPixel();
PixelBufferSlot* 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.postCpu([this, privateData, able, pixelLease, pixel, renderSize, version]() {
if(privateData->mDestroying.load(std::memory_order_acquire)) {
able->dPtr->unmarkPixel(pixelLease);
notifyRenderTaskDone(privateData);
return;
}
renderIndependentColor(able, pixel, renderSize, version);
able->dPtr->unmarkPixel(pixelLease);
Global::instance()->renderScheduler().post([this, privateData, able]() {
if(!privateData->mDestroying.load(std::memory_order_acquire)) finishIndependentRender(able);
notifyRenderTaskDone(privateData);
});
});
}
void Plot::renderIndependentColor(RenderAble* able, PixelBufferSlot* 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->markStateRole(State_Render);
if(lease) {
able->draw(&painter);
able->dPtr->unmarkState(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::finishIndependentRender(RenderAble* able) {
QRegion updateRegion = able->dPtr->publishRenderPixel();
able->dPtr->mIndependentRenderQueued.store(false, std::memory_order_release);
if(!updateRegion.isEmpty()) requestUpdate(updateRegion);
if(able->dPtr->hasNewPixelState()) submitIndependentRender(able, Render_Request_Source::Finish);
}
void Plot::scheduleRenderTimer() {
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->incrementCounter("Pipeline", "timerTick");
onRenderRequest(Render_Request_Source::Timer);
scheduleRenderTimer();
}
notifyRenderTaskDone(privateData);
});
}
void Plot::markRenderStateDirty() {
if(d->mDestroying.load(std::memory_order_acquire)) return;
d->mPipeline.markEditState();
requestRender();
}
void Plot::requestRender(Render_Request_Source source) {
if(d->mDestroying.load(std::memory_order_acquire)) return;
d->mLastRequestNs.store(steadyNowNs(), 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)) onRenderRequest(source);
notifyRenderTaskDone(privateData);
});
}
void Plot::requestUpdate(const QRegion& region) {
d->mLastUpdateRequestNs.store(steadyNowNs(), 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::backgroundColor() {
return d->mBackground;
}
void Plot::setBackgroundColor(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;
notifyRenderTaskDone(privateData);
});
if(mFirstResize) {
for(Layer* layer : mLayerList) {
for(RenderAble* able : layer->mRenderAbles) {
able->firstResizeEvent(e);
}
}
}
for(Layer* layer : mLayerList) {
for(RenderAble* able : layer->mRenderAbles) {
able->resizeEvent(e);
}
}
if(mFirstResize) mFirstResize = false;
markRenderStateDirty();
break;
}
case QEvent::Show: {
startRender();
break;
}
case QEvent::Hide: {
pauseRender();
break;
}
case QEvent::Enter: {
break;
}
case QEvent::Leave: {
for(Layer* layer : mLayerList) {
for(RenderAble* able : layer->mRenderAbles) {
able->setHoverState(QPoint(), false);
}
}
break;
}
case QEvent::Wheel: {
auto e = reinterpret_cast<QWheelEvent*>(event);
for(Layer* layer : mLayerList) {
for(RenderAble* able : layer->mRenderAbles) {
able->wheelEvent(e);
}
}
break;
}
case QEvent::MouseMove: {
auto e = reinterpret_cast<QMouseEvent*>(event);
for(Layer* layer : mLayerList) {
for(RenderAble* able : layer->mRenderAbles) {
bool selected = able->selectTest(e->pos());
able->setHoverState(e->pos(), selected);
if(selected) {
able->mouseMoveEvent(e);
}
}
}
break;
}
case QEvent::MouseButtonPress: {
auto e = reinterpret_cast<QMouseEvent*>(event);
for(Layer* layer : mLayerList) {
for(RenderAble* able : layer->mRenderAbles) {
if(able->selectTest(e->pos())) {
able->mousePressEvent(e);
}
}
}
break;
}
case QEvent::MouseButtonRelease: {
auto e = reinterpret_cast<QMouseEvent*>(event);
for(Layer* layer : mLayerList) {
for(RenderAble* able : layer->mRenderAbles) {
if(able->selectTest(e->pos())) {
able->mouseReleaseEvent(e);
}
}
}
break;
}
case QEvent::KeyPress: {
auto e = reinterpret_cast<QKeyEvent*>(event);
for(Layer* layer : mLayerList) {
for(RenderAble* able : layer->mRenderAbles) {
able->keyPressEvent(e);
}
}
break;
}
case QEvent::KeyRelease: {
auto e = reinterpret_cast<QKeyEvent*>(event);
for(Layer* layer : mLayerList) {
for(RenderAble* able : layer->mRenderAbles) {
able->keyReleaseEvent(e);
}
}
break;
}
default: break;
}
for(Layer* layer : mLayerList) {
for(RenderAble* able : layer->mRenderAbles) {
able->plotEvent(event);
}
}
return ret;
}
}