219 lines
6.4 KiB
C++
219 lines
6.4 KiB
C++
#include "global.h"
|
|
#include <algorithm>
|
|
#include "Assert.h"
|
|
#include "Memory.h"
|
|
#include "../plot/Plot_Core.h"
|
|
#include "../render/Canvas.h"
|
|
#include "asio.hpp"
|
|
#include <chrono>
|
|
|
|
namespace renderive {
|
|
|
|
struct Render_Scheduler_Private {
|
|
asio::io_context io_context;
|
|
std::unique_ptr<asio::executor_work_guard<asio::io_context::executor_type>> work_guard;
|
|
std::unique_ptr<Render_Executor> render_executor;
|
|
std::thread scheduler_thread;
|
|
std::thread::id scheduler_thread_id;
|
|
std::vector<Plot_Core*> plots;
|
|
std::atomic_bool running{false};
|
|
std::size_t configured_worker_count{};
|
|
};
|
|
|
|
Render_Scheduler::Render_Scheduler() : d(std::make_unique<Render_Scheduler_Private>()) {}
|
|
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<asio::executor_work_guard<asio::io_context::executor_type>>(d->io_context.get_executor());
|
|
d->configured_worker_count = resolved_worker_count;
|
|
d->render_executor = std::make_unique<Render_Executor>(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<asio::steady_timer>(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<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(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
|