#include "global.h" #include #include "Assert.h" #include "Memory.h" #include "../plot/Plot_Core.h" #include "../render/Canvas.h" #include "asio.hpp" #include namespace renderive { struct Render_Scheduler_Private { asio::io_context io_context; std::unique_ptr> work_guard; std::unique_ptr render_executor; std::thread scheduler_thread; std::thread::id scheduler_thread_id; std::vector plots; std::atomic_bool running{false}; std::size_t configured_worker_count{}; }; Render_Scheduler::Render_Scheduler() : d(std::make_unique()) {} Global::Global() {} namespace { Global*& global_instance_storage() { static Global* value = nullptr; return value; } } // namespace Global* Global::instance() { Global*& value = global_instance_storage(); if (!value) value = new Global(); return value; } void Global::destroy() { Global*& value = global_instance_storage(); delete value; value = nullptr; } Render_Scheduler::~Render_Scheduler() { shutdown(); } void Render_Scheduler::start(Render_Runtime_Config config) { memory_resource(); Canvas::diagnose_default_font_once(); std::size_t resolved_worker_count = resolve_render_worker_count(config.worker_count); if (d->running.exchange(true, std::memory_order_acq_rel)) { ASSERT(d->configured_worker_count == resolved_worker_count, "Render scheduler already started with a different Taskflow worker count"); return; } d->io_context.restart(); d->work_guard = std::make_unique>(d->io_context.get_executor()); d->configured_worker_count = resolved_worker_count; d->render_executor = std::make_unique(Render_Runtime_Config{resolved_worker_count}); d->scheduler_thread = std::thread([this]() { d->scheduler_thread_id = std::this_thread::get_id(); for (Plot_Core* plot : d->plots) initialize_plot_on_scheduler(plot); d->io_context.run(); d->running.store(false, std::memory_order_release); }); } void Render_Scheduler::shutdown() { if (!d->running.load(std::memory_order_acquire)) { if (d->scheduler_thread.joinable() && d->scheduler_thread.get_id() != std::this_thread::get_id()) d->scheduler_thread.join(); if (d->render_executor) d->render_executor->shutdown(); d->render_executor.reset(); d->work_guard.reset(); return; } if (is_scheduler_thread()) { shutdown_on_scheduler(); d->work_guard.reset(); if (d->render_executor) d->render_executor->shutdown(); return; } post_and_wait([this]() { shutdown_on_scheduler(); }); post([this]() { d->work_guard.reset(); }); if (d->scheduler_thread.joinable()) d->scheduler_thread.join(); if (d->render_executor) d->render_executor->shutdown(); d->render_executor.reset(); } void Render_Scheduler::post(Task task) { asio::post(d->io_context, std::move(task)); } void Render_Scheduler::post_at(std::uint64_t steady_time_ns, Task task) { if (!task) return; auto timer = std::make_shared(d->io_context); timer->expires_at(std::chrono::steady_clock::time_point(std::chrono::nanoseconds(steady_time_ns))); timer->async_wait([timer, task = std::move(task)](const asio::error_code& error) mutable { if (!error) task(); }); } bool Render_Scheduler::try_submit_render_task(Render_Executor_Task task) { if (!d->render_executor) return false; return d->render_executor->try_submit(std::move(task)); } Render_Executor_Snapshot Render_Scheduler::render_executor_snapshot() const { if (!d->render_executor) return {}; return d->render_executor->snapshot(); } std::size_t Render_Scheduler::render_worker_count() const { return d->configured_worker_count; } void Render_Scheduler::register_plot(Plot_Core* plot) { if (d->running.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(Plot_Core* plot) { if (d->running.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->scheduler_thread_id; } bool Render_Scheduler::is_running() const { return d->running.load(std::memory_order_acquire); } asio::io_context& Render_Scheduler::io_context() { return d->io_context; } void Render_Scheduler::post_and_wait(Task task) { if (is_scheduler_thread() || !d->running.load(std::memory_order_acquire)) { task(); return; } std::mutex mutex; std::condition_variable cv; bool done = false; post([task = std::move(task), &mutex, &cv, &done]() mutable { task(); { std::lock_guard lock(mutex); done = true; } cv.notify_one(); }); std::unique_lock lock(mutex); cv.wait(lock, [&done]() { return done; }); } void Render_Scheduler::register_plot_on_scheduler(Plot_Core* plot) { if (std::find(d->plots.begin(), d->plots.end(), plot) == d->plots.end()) d->plots.push_back(plot); if (d->running.load(std::memory_order_acquire)) initialize_plot_on_scheduler(plot); } void Render_Scheduler::remove_plot_on_scheduler(Plot_Core* plot) { d->plots.erase(std::remove(d->plots.begin(), d->plots.end(), plot), d->plots.end()); plot->deactivate_view(); } void Render_Scheduler::shutdown_on_scheduler() { for (Plot_Core* plot : d->plots) plot->deactivate_view(); d->plots.clear(); } void Render_Scheduler::initialize_plot_on_scheduler(Plot_Core* plot) { plot->set_viewport_size(plot->viewport_size()); if (plot->view_active()) plot->activate_view(); } Render_Scheduler& Global::render_scheduler() { return render_scheduler_value; } void Global::start_render_scheduler(Render_Runtime_Config config) { render_scheduler_value.start(config); } } // namespace renderive