#pragma once #include "app_core/app_thread.h" #include #include #include #include #include #include #include #include #include #include #include class App; struct AppTask : public std::packaged_task { size_t task_id; #ifdef _DEBUG std::string name; #endif template AppTask(F f) : std::packaged_task(f) { task_id = typeid(f).hash_code(); #ifdef _DEBUG name = typeid(f).name(); #endif } }; class AppRuntime { public: [[nodiscard]] bool is_render_thread() const noexcept; [[nodiscard]] bool is_ui_thread() const noexcept; [[nodiscard]] bool request_redraw() const noexcept { return request_redraw_; } void clear_request_redraw() noexcept { request_redraw_ = false; } void notify_render_worker() noexcept; void notify_ui_worker() noexcept; void render_thread_tick(App& app); void render_thread_main(App& app, std::stop_token stop_token); void render_thread_start(App& app); void render_thread_stop(); void ui_thread_tick(App& app); void ui_thread_main(App& app, std::stop_token stop_token); void ui_thread_start(App& app); void ui_thread_stop(); template std::future render_task_async(T task, bool unique = false) { AppTask pt(task); auto f = pt.get_future(); const auto dispatch = pp::app::plan_app_task_dispatch( is_render_thread(), unique, 0U, render_running_, false, false); if (dispatch.execute_immediately) { pt(); } else if (dispatch.queue_task) { { std::lock_guard lock(render_task_mutex_); const auto queue_dispatch = pp::app::plan_app_task_dispatch( false, unique, render_tasklist_.size(), render_running_, false, false); if (queue_dispatch.remove_matching_unique_task) render_tasklist_.erase(std::remove_if(render_tasklist_.begin(), render_tasklist_.end(), [id = pt.task_id](AppTask const& t){ return t.task_id == id; }), render_tasklist_.end()); render_tasklist_.push_back(std::move(pt)); } if (dispatch.notify_worker) render_cv_.notify_all(); } return f; } template void render_task(T task) { AppTask pt(task); auto f = pt.get_future(); const auto dispatch = pp::app::plan_app_task_dispatch( is_render_thread(), false, 0U, render_running_, true, false); if (dispatch.execute_immediately) { pt(); } else if (dispatch.queue_task) { { std::lock_guard lock(render_task_mutex_); render_tasklist_.push_back(std::move(pt)); } if (dispatch.notify_worker) render_cv_.notify_all(); } if (dispatch.wait_for_completion) f.get(); } void render_sync() { render_task([] {}); } template std::future ui_task_async(T task, bool unique = false) { AppTask pt(task); auto f = pt.get_future(); const auto dispatch = pp::app::plan_app_task_dispatch( is_ui_thread(), unique, 0U, ui_running_, false, false); if (dispatch.execute_immediately) { pt(); } else if (dispatch.queue_task) { { std::lock_guard lock(ui_task_mutex_); const auto queue_dispatch = pp::app::plan_app_task_dispatch( false, unique, ui_tasklist_.size(), ui_running_, false, false); if (queue_dispatch.remove_matching_unique_task) ui_tasklist_.erase(std::remove_if(ui_tasklist_.begin(), ui_tasklist_.end(), [id = pt.task_id](AppTask const& t){ return t.task_id == id; }), ui_tasklist_.end()); ui_tasklist_.push_back(std::move(pt)); } if (dispatch.notify_worker) ui_cv_.notify_all(); } return f; } template void ui_task(T task) { AppTask pt(task); auto f = pt.get_future(); const auto dispatch = pp::app::plan_app_task_dispatch( is_ui_thread(), false, 0U, ui_running_, true, true); if (dispatch.execute_immediately) { pt(); } else if (dispatch.queue_task) { { std::lock_guard lock(ui_task_mutex_); ui_tasklist_.push_back(std::move(pt)); } if (dispatch.notify_worker) ui_cv_.notify_all(); } if (dispatch.wait_for_completion) f.get(); if (dispatch.request_redraw) request_redraw_ = true; } void ui_sync() { ui_task([] {}); } private: std::deque render_tasklist_; std::mutex render_task_mutex_; std::condition_variable render_cv_; std::jthread render_thread_; std::thread::id render_thread_id_; bool render_running_ = false; std::deque ui_tasklist_; std::mutex ui_task_mutex_; std::condition_variable ui_cv_; std::jthread ui_thread_; std::thread::id ui_thread_id_; bool ui_running_ = false; bool request_redraw_ = false; };