186 lines
6.2 KiB
C++
186 lines
6.2 KiB
C++
#include "global.h"
|
|
#include <QApplication>
|
|
#include <algorithm>
|
|
#include "../architecture/Plot_p.h"
|
|
#include "asio.hpp"
|
|
namespace YSG {
|
|
struct Render_Scheduler_Private {
|
|
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<Abs_Plot*> mPlots;
|
|
std::atomic_bool mRunning{false};
|
|
std::atomic_bool mCpuJoined{true};
|
|
};
|
|
Render_Scheduler::Render_Scheduler() : d(std::make_unique<Render_Scheduler_Private>()) {
|
|
QObject::connect(QApplication::instance(), &QApplication::aboutToQuit, [this]() {
|
|
shutdown();
|
|
});
|
|
}
|
|
Render_Scheduler::~Render_Scheduler() {
|
|
shutdown();
|
|
}
|
|
void Render_Scheduler::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 (Abs_Plot* plot : d->mPlots) {
|
|
initialize_plot_on_scheduler(plot);
|
|
}
|
|
d->mIoContext.run();
|
|
d->mRunning.store(false, std::memory_order_release);
|
|
});
|
|
}
|
|
void Render_Scheduler::shutdown() {
|
|
if (!d->mRunning.load(std::memory_order_acquire)) {
|
|
join_cpu_Pool();
|
|
if (d->mSchedulerThread.joinable() && d->mSchedulerThread.get_id() != std::this_thread::get_id())
|
|
d->mSchedulerThread.join();
|
|
d->mCpuPool.reset();
|
|
d->mWorkGuard.reset();
|
|
return;
|
|
}
|
|
if (is_scheduler_thread()) {
|
|
shutdown_on_scheduler();
|
|
d->mWorkGuard.reset();
|
|
join_cpu_Pool();
|
|
return;
|
|
}
|
|
post_and_wait([this]() {
|
|
shutdown_on_scheduler();
|
|
});
|
|
join_cpu_Pool();
|
|
post([this]() {
|
|
d->mWorkGuard.reset();
|
|
});
|
|
if (d->mSchedulerThread.joinable())
|
|
d->mSchedulerThread.join();
|
|
d->mCpuPool.reset();
|
|
}
|
|
void Render_Scheduler::post(std::function<void()> task) {
|
|
asio::post(d->mIoContext, std::move(task));
|
|
}
|
|
void Render_Scheduler::post_cpu(std::function<void()> task) {
|
|
if (d->mCpuPool) {
|
|
asio::post(*d->mCpuPool, std::move(task));
|
|
}
|
|
else {
|
|
post(std::move(task));
|
|
}
|
|
}
|
|
void Render_Scheduler::register_plot(Abs_Plot* plot) {
|
|
if (d->mRunning.load(std::memory_order_acquire) && !is_scheduler_thread()) {
|
|
post_and_wait([this, plot]() {
|
|
register_plot_on_scheduler(plot);
|
|
});
|
|
return;
|
|
}
|
|
register_plot_on_scheduler(plot);
|
|
}
|
|
void Render_Scheduler::remove_plot(Abs_Plot* plot) {
|
|
if (d->mRunning.load(std::memory_order_acquire) && !is_scheduler_thread()) {
|
|
post_and_wait([this, plot]() {
|
|
remove_plot_on_scheduler(plot);
|
|
});
|
|
return;
|
|
}
|
|
remove_plot_on_scheduler(plot);
|
|
}
|
|
bool Render_Scheduler::is_scheduler_thread() const {
|
|
return std::this_thread::get_id() == d->mSchedulerThreadId;
|
|
}
|
|
bool Render_Scheduler::is_running() const {
|
|
return d->mRunning.load(std::memory_order_acquire);
|
|
}
|
|
asio::io_context& Render_Scheduler::io_context() {
|
|
return d->mIoContext;
|
|
}
|
|
void Render_Scheduler::join_cpu_Pool() {
|
|
if (d->mCpuPool && !d->mCpuJoined.exchange(true, std::memory_order_acq_rel))
|
|
d->mCpuPool->join();
|
|
}
|
|
void Render_Scheduler::post_and_wait(std::function<void()> task) {
|
|
if (is_scheduler_thread() || !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 Render_Scheduler::register_plot_on_scheduler(Abs_Plot* plot) {
|
|
if (!d->mPlots.contains(plot))
|
|
d->mPlots.append(plot);
|
|
if (d->mRunning.load(std::memory_order_acquire))
|
|
initialize_plot_on_scheduler(plot);
|
|
}
|
|
void Render_Scheduler::remove_plot_on_scheduler(Abs_Plot* plot) {
|
|
d->mPlots.removeAll(plot);
|
|
Plot_Private* data = plot->d;
|
|
data->mRenderEnabled.store(false, std::memory_order_release);
|
|
if (data->mRenderTimer) {
|
|
data->mRenderTimer->cancel();
|
|
data->mRenderTimer.reset();
|
|
}
|
|
if (data->mSubmitTimer) {
|
|
data->mSubmitTimer->cancel();
|
|
data->mSubmitTimer.reset();
|
|
}
|
|
}
|
|
void Render_Scheduler::shutdown_on_scheduler() {
|
|
for (Abs_Plot* plot : d->mPlots) {
|
|
Plot_Private* 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();
|
|
}
|
|
if (data->mSubmitTimer) {
|
|
data->mSubmitTimer->cancel();
|
|
data->mSubmitTimer.reset();
|
|
}
|
|
}
|
|
d->mPlots.clear();
|
|
}
|
|
void Render_Scheduler::initialize_plot_on_scheduler(Abs_Plot* plot) {
|
|
Plot_Private* data = plot->d;
|
|
if (!data->mRenderTimer)
|
|
data->mRenderTimer = std::make_unique<asio::steady_timer>(d->mIoContext);
|
|
if (!data->mSubmitTimer)
|
|
data->mSubmitTimer = 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->schedule_render_timer();
|
|
plot->on_render_request(Render_Request_Source::Dirty);
|
|
}
|
|
Render_Scheduler& Global::renderScheduler() {
|
|
return mRenderScheduler;
|
|
}
|
|
void Global::start_render_scheduler() {
|
|
mRenderScheduler.start();
|
|
}
|
|
} // namespace YSG
|