404 lines
11 KiB
C++
404 lines
11 KiB
C++
#include "pch.h"
|
|
#include "app_runtime.h"
|
|
|
|
#include "app.h"
|
|
|
|
AppRuntime::AppRuntime()
|
|
: prepared_file_worker_([this](std::stop_token stop_token)
|
|
{
|
|
prepared_file_worker_main(stop_token);
|
|
})
|
|
, canvas_async_worker_([this](std::stop_token stop_token)
|
|
{
|
|
canvas_async_worker_main(stop_token);
|
|
})
|
|
{
|
|
}
|
|
|
|
AppRuntime::~AppRuntime()
|
|
{
|
|
canvas_async_worker_stop();
|
|
prepared_file_worker_stop();
|
|
}
|
|
|
|
bool AppRuntime::is_render_thread() const noexcept
|
|
{
|
|
return std::this_thread::get_id() == render_thread_id_;
|
|
}
|
|
|
|
bool AppRuntime::is_ui_thread() const noexcept
|
|
{
|
|
return std::this_thread::get_id() == ui_thread_id_;
|
|
}
|
|
|
|
void AppRuntime::notify_render_worker() noexcept
|
|
{
|
|
render_cv_.notify_all();
|
|
}
|
|
|
|
void AppRuntime::notify_ui_worker() noexcept
|
|
{
|
|
ui_cv_.notify_all();
|
|
}
|
|
|
|
void AppRuntime::prepared_file_task(std::function<void()> task)
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> lock(prepared_file_task_mutex_);
|
|
if (!prepared_file_running_)
|
|
return;
|
|
prepared_file_tasklist_.push_back(std::move(task));
|
|
}
|
|
prepared_file_cv_.notify_one();
|
|
}
|
|
|
|
void AppRuntime::canvas_async_task(std::function<void()> task)
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> lock(canvas_async_task_mutex_);
|
|
if (!canvas_async_running_)
|
|
return;
|
|
canvas_async_tasklist_.push_back(std::move(task));
|
|
}
|
|
canvas_async_cv_.notify_one();
|
|
}
|
|
|
|
void AppRuntime::prepared_file_worker_main(std::stop_token stop_token)
|
|
{
|
|
for (;;)
|
|
{
|
|
std::function<void()> task;
|
|
{
|
|
std::unique_lock<std::mutex> lock(prepared_file_task_mutex_);
|
|
prepared_file_cv_.wait(lock, [this, &stop_token]
|
|
{
|
|
return stop_token.stop_requested() || !prepared_file_running_ || !prepared_file_tasklist_.empty();
|
|
});
|
|
if ((stop_token.stop_requested() || !prepared_file_running_) && prepared_file_tasklist_.empty())
|
|
break;
|
|
task = std::move(prepared_file_tasklist_.front());
|
|
prepared_file_tasklist_.pop_front();
|
|
}
|
|
|
|
if (task)
|
|
{
|
|
try
|
|
{
|
|
task();
|
|
}
|
|
catch (...)
|
|
{
|
|
LOG("prepared file worker task failed");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AppRuntime::canvas_async_worker_main(std::stop_token stop_token)
|
|
{
|
|
for (;;)
|
|
{
|
|
std::function<void()> task;
|
|
{
|
|
std::unique_lock<std::mutex> lock(canvas_async_task_mutex_);
|
|
canvas_async_cv_.wait(lock, [this, &stop_token]
|
|
{
|
|
return stop_token.stop_requested() || !canvas_async_running_ || !canvas_async_tasklist_.empty();
|
|
});
|
|
if ((stop_token.stop_requested() || !canvas_async_running_) && canvas_async_tasklist_.empty())
|
|
break;
|
|
task = std::move(canvas_async_tasklist_.front());
|
|
canvas_async_tasklist_.pop_front();
|
|
}
|
|
|
|
if (task)
|
|
{
|
|
try
|
|
{
|
|
task();
|
|
}
|
|
catch (...)
|
|
{
|
|
LOG("canvas async worker task failed");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AppRuntime::prepared_file_worker_stop()
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> lock(prepared_file_task_mutex_);
|
|
prepared_file_running_ = false;
|
|
}
|
|
prepared_file_cv_.notify_all();
|
|
if (prepared_file_worker_.joinable())
|
|
{
|
|
prepared_file_worker_.request_stop();
|
|
prepared_file_worker_.join();
|
|
}
|
|
}
|
|
|
|
void AppRuntime::canvas_async_worker_stop()
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> lock(canvas_async_task_mutex_);
|
|
canvas_async_running_ = false;
|
|
}
|
|
canvas_async_cv_.notify_all();
|
|
if (canvas_async_worker_.joinable())
|
|
{
|
|
canvas_async_worker_.request_stop();
|
|
canvas_async_worker_.join();
|
|
}
|
|
}
|
|
|
|
void AppRuntime::render_thread_tick(App& app)
|
|
{
|
|
static uint32_t count = 0;
|
|
render_thread_id_ = std::this_thread::get_id();
|
|
std::deque<AppTask> working_list;
|
|
pp::app::AppQueueDrainPlan drain_plan;
|
|
|
|
{
|
|
std::unique_lock<std::mutex> lock(render_task_mutex_);
|
|
drain_plan = pp::app::plan_app_render_queue_drain(render_tasklist_.size());
|
|
render_running_ = drain_plan.mark_running;
|
|
if (!drain_plan.drain_tasks)
|
|
return;
|
|
working_list = std::move(render_tasklist_);
|
|
}
|
|
|
|
if (drain_plan.wrap_in_render_context)
|
|
{
|
|
app.async_start();
|
|
while (!working_list.empty())
|
|
{
|
|
count++;
|
|
working_list.front()();
|
|
working_list.pop_front();
|
|
}
|
|
app.async_end();
|
|
}
|
|
}
|
|
|
|
void AppRuntime::render_thread_main(App& app, std::stop_token stop_token)
|
|
{
|
|
BT_SetTerminate();
|
|
|
|
render_thread_id_ = std::this_thread::get_id();
|
|
render_running_ = pp::app::plan_app_thread_start().mark_running;
|
|
while (render_running_ && !stop_token.stop_requested())
|
|
{
|
|
std::deque<AppTask> working_list;
|
|
pp::app::AppQueueDrainPlan drain_plan;
|
|
|
|
{
|
|
std::unique_lock<std::mutex> lock(render_task_mutex_);
|
|
render_cv_.wait(lock, [this, &stop_token]
|
|
{
|
|
return stop_token.stop_requested() || !render_running_ || !render_tasklist_.empty();
|
|
});
|
|
drain_plan = pp::app::plan_app_render_queue_drain(render_tasklist_.size());
|
|
working_list = std::move(render_tasklist_);
|
|
}
|
|
|
|
if (drain_plan.wrap_in_render_context)
|
|
{
|
|
app.async_start();
|
|
while (!working_list.empty())
|
|
{
|
|
working_list.front()();
|
|
working_list.pop_front();
|
|
}
|
|
app.async_end();
|
|
}
|
|
}
|
|
}
|
|
|
|
void AppRuntime::ui_thread_tick(App& app)
|
|
{
|
|
ui_thread_id_ = std::this_thread::get_id();
|
|
|
|
std::deque<AppTask> working_list;
|
|
pp::app::AppUiTickPlan tick_plan;
|
|
|
|
{
|
|
std::unique_lock<std::mutex> lock(ui_task_mutex_);
|
|
tick_plan = pp::app::plan_app_ui_thread_tick(ui_tasklist_.size(), app.redraw);
|
|
ui_running_ = tick_plan.mark_running;
|
|
working_list = std::move(ui_tasklist_);
|
|
}
|
|
|
|
if (tick_plan.execute_tasks)
|
|
{
|
|
while (!working_list.empty())
|
|
{
|
|
working_list.front()();
|
|
working_list.pop_front();
|
|
}
|
|
}
|
|
|
|
if (tick_plan.tick_app)
|
|
app.tick(0);
|
|
|
|
const auto redraw_plan = pp::app::plan_app_ui_loop_redraw(app.redraw, 0);
|
|
if (redraw_plan.enqueue_render_frame)
|
|
{
|
|
if (redraw_plan.update_before_render)
|
|
app.update(0);
|
|
app.render_task([&app]
|
|
{
|
|
app.bind_default_render_target();
|
|
app.clear();
|
|
app.draw(0);
|
|
app.async_swap();
|
|
});
|
|
}
|
|
}
|
|
|
|
void AppRuntime::ui_thread_main(App& app, std::stop_token stop_token)
|
|
{
|
|
BT_SetTerminate();
|
|
|
|
ui_thread_id_ = std::this_thread::get_id();
|
|
ui_running_ = pp::app::plan_app_thread_start().mark_running;
|
|
|
|
app.attach_ui_thread();
|
|
|
|
LOG("ui thread init()");
|
|
app.init();
|
|
|
|
auto t_start = std::chrono::high_resolution_clock::now();
|
|
float t_frame = 0;
|
|
float t_fps_counter = 0;
|
|
float t_reloader = 0;
|
|
int rendered_frames = 0;
|
|
while (ui_running_ && !stop_token.stop_requested())
|
|
{
|
|
std::deque<AppTask> working_list;
|
|
|
|
{
|
|
std::unique_lock<std::mutex> lock(ui_task_mutex_);
|
|
ui_cv_.wait_for(lock, std::chrono::milliseconds(app.idle_ms), [this, &stop_token]
|
|
{
|
|
return stop_token.stop_requested() || !ui_running_ || !ui_tasklist_.empty();
|
|
});
|
|
working_list = std::move(ui_tasklist_);
|
|
}
|
|
|
|
if (!working_list.empty())
|
|
{
|
|
while (!working_list.empty())
|
|
{
|
|
working_list.front()();
|
|
working_list.pop_front();
|
|
}
|
|
}
|
|
|
|
auto t_now = std::chrono::high_resolution_clock::now();
|
|
float dt = std::chrono::duration<float>(t_now - t_start).count();
|
|
t_start = t_now;
|
|
|
|
const auto timer_plan = pp::app::plan_app_ui_loop_timers(
|
|
dt,
|
|
t_frame,
|
|
t_fps_counter,
|
|
t_reloader,
|
|
rendered_frames,
|
|
app.platform_enables_live_asset_reloading());
|
|
if (timer_plan) {
|
|
if (timer_plan.value().update_platform_frame)
|
|
app.update_platform_frame(dt);
|
|
t_frame = timer_plan.value().frame_accumulator;
|
|
t_fps_counter = timer_plan.value().fps_accumulator;
|
|
t_reloader = timer_plan.value().reload_accumulator;
|
|
rendered_frames = timer_plan.value().rendered_frames_after_report;
|
|
|
|
if (timer_plan.value().report_rendered_frames)
|
|
app.report_rendered_frames(timer_plan.value().reported_frame_count);
|
|
|
|
if (timer_plan.value().check_live_asset_reload) {
|
|
if (ShaderManager::reload())
|
|
{
|
|
app.stroke->update_controls();
|
|
app.redraw = true;
|
|
}
|
|
if (app.layout.reload())
|
|
app.redraw = true;
|
|
if (app.layout_designer.reload())
|
|
app.redraw = true;
|
|
}
|
|
}
|
|
|
|
const auto redraw_plan = pp::app::plan_app_ui_loop_redraw(app.redraw, rendered_frames);
|
|
if (redraw_plan.tick_app)
|
|
app.tick(dt);
|
|
|
|
if (redraw_plan.enqueue_render_frame)
|
|
{
|
|
if (redraw_plan.update_before_render)
|
|
app.update(t_frame);
|
|
app.render_task([&app, t_frame]
|
|
{
|
|
app.bind_default_render_target();
|
|
app.clear();
|
|
app.draw(t_frame);
|
|
app.async_swap();
|
|
});
|
|
if (redraw_plan.reset_frame_accumulator)
|
|
t_frame = 0;
|
|
rendered_frames = redraw_plan.rendered_frames;
|
|
}
|
|
}
|
|
app.detach_ui_thread();
|
|
}
|
|
|
|
void AppRuntime::render_thread_start(App& app)
|
|
{
|
|
const auto plan = pp::app::plan_app_thread_start();
|
|
if (plan.start_thread)
|
|
render_thread_ = std::jthread([this, &app](std::stop_token stop_token)
|
|
{
|
|
render_thread_main(app, stop_token);
|
|
});
|
|
render_running_ = plan.mark_running;
|
|
}
|
|
|
|
void AppRuntime::render_thread_stop()
|
|
{
|
|
const auto plan = pp::app::plan_app_thread_stop(render_thread_.joinable());
|
|
if (plan.mark_not_running)
|
|
render_running_ = false;
|
|
if (plan.join_thread)
|
|
render_thread_.request_stop();
|
|
if (plan.notify_worker)
|
|
render_cv_.notify_all();
|
|
if (plan.join_thread)
|
|
render_thread_.join();
|
|
}
|
|
|
|
void AppRuntime::ui_thread_start(App& app)
|
|
{
|
|
const auto plan = pp::app::plan_app_thread_start();
|
|
if (plan.start_thread)
|
|
ui_thread_ = std::jthread([this, &app](std::stop_token stop_token)
|
|
{
|
|
ui_thread_main(app, stop_token);
|
|
});
|
|
ui_running_ = plan.mark_running;
|
|
}
|
|
|
|
void AppRuntime::ui_thread_stop()
|
|
{
|
|
const auto plan = pp::app::plan_app_thread_stop(ui_thread_.joinable());
|
|
if (plan.mark_not_running)
|
|
ui_running_ = false;
|
|
if (plan.join_thread)
|
|
ui_thread_.request_stop();
|
|
if (plan.notify_worker)
|
|
ui_cv_.notify_all();
|
|
if (plan.join_thread)
|
|
ui_thread_.join();
|
|
}
|