217 lines
5.9 KiB
C++
217 lines
5.9 KiB
C++
#pragma once
|
|
|
|
#include "app_core/app_thread.h"
|
|
|
|
#include <algorithm>
|
|
#include <condition_variable>
|
|
#include <cstddef>
|
|
#include <deque>
|
|
#include <functional>
|
|
#include <future>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <typeinfo>
|
|
#include <utility>
|
|
#include <thread>
|
|
|
|
class App;
|
|
|
|
struct AppTask : public std::packaged_task<void()>
|
|
{
|
|
size_t task_id;
|
|
#ifdef _DEBUG
|
|
std::string name;
|
|
#endif
|
|
template<typename F> AppTask(F f) : std::packaged_task<void()>(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<typename T>
|
|
std::future<void> 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<std::mutex> 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<typename T>
|
|
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<std::mutex> 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<typename T>
|
|
std::future<void> 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<std::mutex> 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<typename T>
|
|
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<std::mutex> 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<AppTask> 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<AppTask> 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;
|
|
};
|