#include "pch.h" #include "app_runtime.h" #include "app.h" 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::render_thread_tick(App& app) { static uint32_t count = 0; render_thread_id_ = std::this_thread::get_id(); std::deque working_list; pp::app::AppQueueDrainPlan drain_plan; { std::unique_lock 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) { BT_SetTerminate(); render_thread_id_ = std::this_thread::get_id(); render_running_ = pp::app::plan_app_thread_start().mark_running; while (render_running_) { std::deque working_list; pp::app::AppQueueDrainPlan drain_plan; { std::unique_lock lock(render_task_mutex_); render_cv_.wait(lock, [this] { return render_tasklist_.empty() && render_running_ ? false : true; }); 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 working_list; pp::app::AppUiTickPlan tick_plan; { std::unique_lock 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) { 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_) { std::deque working_list; { std::unique_lock lock(ui_task_mutex_); ui_cv_.wait_for(lock, std::chrono::milliseconds(app.idle_ms), [this] { return ui_tasklist_.empty() && ui_running_ ? false : true; }); 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(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::thread(&AppRuntime::render_thread_main, this, std::ref(app)); 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.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::thread(&AppRuntime::ui_thread_main, this, std::ref(app)); 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.notify_worker) ui_cv_.notify_all(); if (plan.join_thread) ui_thread_.join(); }