Files
Renderive/YSGraphic_Core/base/Global.cpp
T
2026-07-23 13:41:30 +08:00

166 lines
6.2 KiB
C++

#include "asio.hpp"
#include "Global.h"
#include "Plot_p.h"
#include <QApplication>
#include <algorithm>
namespace YSG {
struct RenderSchedulerPrivate {
asio::io_context mIoContext;
std::unique_ptr<asio::executor_work_guard<asio::io_context::executor_type>> mWorkGuard;
std::unique_ptr<asio::thread_pool> mCpuPool;
std::thread mSchedulerThread;
std::thread::id mSchedulerThreadId;
QList<Plot*> mPlots;
std::atomic_bool mRunning{false};
std::atomic_bool mCpuJoined{true};
};
RenderScheduler::RenderScheduler() : d(std::make_unique<RenderSchedulerPrivate>()) {
QObject::connect(QApplication::instance(), &QApplication::aboutToQuit, [this]() {
shutdown();
});
}
RenderScheduler::~RenderScheduler() {
shutdown();
}
void RenderScheduler::start() {
if(d->mRunning.exchange(true, std::memory_order_acq_rel)) return;
d->mIoContext.restart();
d->mWorkGuard = std::make_unique<asio::executor_work_guard<asio::io_context::executor_type>>(d->mIoContext.get_executor());
unsigned cpuCount = std::max(1u, std::thread::hardware_concurrency() > 1 ? std::thread::hardware_concurrency() - 1 : 1u);
d->mCpuPool = std::make_unique<asio::thread_pool>(cpuCount);
d->mCpuJoined.store(false, std::memory_order_release);
d->mSchedulerThread = std::thread([this]() {
d->mSchedulerThreadId = std::this_thread::get_id();
for(Plot* plot : d->mPlots) {
initializePlotOnScheduler(plot);
}
d->mIoContext.run();
d->mRunning.store(false, std::memory_order_release);
});
}
void RenderScheduler::shutdown() {
if(!d->mRunning.load(std::memory_order_acquire)) {
joinCpuPool();
if(d->mSchedulerThread.joinable() && d->mSchedulerThread.get_id() != std::this_thread::get_id()) d->mSchedulerThread.join();
d->mCpuPool.reset();
d->mWorkGuard.reset();
return;
}
if(isSchedulerThread()) {
shutdownOnScheduler();
d->mWorkGuard.reset();
joinCpuPool();
return;
}
postAndWait([this]() {
shutdownOnScheduler();
});
joinCpuPool();
post([this]() {
d->mWorkGuard.reset();
});
if(d->mSchedulerThread.joinable()) d->mSchedulerThread.join();
d->mCpuPool.reset();
}
void RenderScheduler::post(std::function<void()> task) {
asio::post(d->mIoContext, std::move(task));
}
void RenderScheduler::postCpu(std::function<void()> task) {
if(d->mCpuPool) {
asio::post(*d->mCpuPool, std::move(task));
} else {
post(std::move(task));
}
}
void RenderScheduler::registerPlot(Plot* plot) {
if(d->mRunning.load(std::memory_order_acquire) && !isSchedulerThread()) {
postAndWait([this, plot]() {
registerPlotOnScheduler(plot);
});
return;
}
registerPlotOnScheduler(plot);
}
void RenderScheduler::removePlot(Plot* plot) {
if(d->mRunning.load(std::memory_order_acquire) && !isSchedulerThread()) {
postAndWait([this, plot]() {
removePlotOnScheduler(plot);
});
return;
}
removePlotOnScheduler(plot);
}
bool RenderScheduler::isSchedulerThread() const {
return std::this_thread::get_id() == d->mSchedulerThreadId;
}
bool RenderScheduler::isRunning() const {
return d->mRunning.load(std::memory_order_acquire);
}
asio::io_context& RenderScheduler::ioContext() {
return d->mIoContext;
}
void RenderScheduler::joinCpuPool() {
if(d->mCpuPool && !d->mCpuJoined.exchange(true, std::memory_order_acq_rel)) d->mCpuPool->join();
}
void RenderScheduler::postAndWait(std::function<void()> task) {
if(isSchedulerThread() || !d->mRunning.load(std::memory_order_acquire)) {
task();
return;
}
std::mutex mutex;
std::condition_variable cv;
bool done = false;
post([&task, &mutex, &cv, &done]() {
task();
{
std::lock_guard<std::mutex> lock(mutex);
done = true;
}
cv.notify_one();
});
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [&done]() {
return done;
});
}
void RenderScheduler::registerPlotOnScheduler(Plot* plot) {
if(!d->mPlots.contains(plot)) d->mPlots.append(plot);
if(d->mRunning.load(std::memory_order_acquire)) initializePlotOnScheduler(plot);
}
void RenderScheduler::removePlotOnScheduler(Plot* plot) {
d->mPlots.removeAll(plot);
PlotPrivate* data = plot->d;
data->mRenderEnabled.store(false, std::memory_order_release);
if(data->mRenderTimer) {
data->mRenderTimer->cancel();
data->mRenderTimer.reset();
}
}
void RenderScheduler::shutdownOnScheduler() {
for(Plot* plot : d->mPlots) {
PlotPrivate* data = plot->d;
data->mDestroying.store(true, std::memory_order_release);
data->mRenderEnabled.store(false, std::memory_order_release);
if(data->mRenderTimer) {
data->mRenderTimer->cancel();
data->mRenderTimer.reset();
}
}
d->mPlots.clear();
}
void RenderScheduler::initializePlotOnScheduler(Plot* plot) {
PlotPrivate* data = plot->d;
if(!data->mRenderTimer) data->mRenderTimer = std::make_unique<asio::steady_timer>(d->mIoContext);
data->mRenderSize = QSize(data->mPendingRenderWidth.load(std::memory_order_acquire), data->mPendingRenderHeight.load(std::memory_order_acquire));
if(plot->isVisible()) data->mRenderEnabled.store(true, std::memory_order_release);
if(data->mRenderEnabled.load(std::memory_order_acquire)) plot->scheduleRenderTimer();
plot->submitRender();
}
RenderScheduler& Global::renderScheduler() {
return mRenderScheduler;
}
void Global::startRenderScheduler() {
mRenderScheduler.start();
}
}