事件处理走双缓冲了
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
@@ -17,13 +18,24 @@ enum class Event_Type : std::uint8_t {
|
||||
};
|
||||
/* 所有输入事件的公共业务基类。 */
|
||||
struct Event {
|
||||
struct Dispatch_Timing {
|
||||
std::uint64_t frame_sequence{};
|
||||
std::uint64_t started_steady_ns{};
|
||||
std::uint64_t completed_steady_ns{};
|
||||
};
|
||||
explicit Event(Event_Type value);
|
||||
virtual ~Event();
|
||||
void accept() const noexcept;
|
||||
[[nodiscard]] bool is_accepted() const noexcept;
|
||||
void mark_dispatch_started(std::uint64_t frame_sequence) const noexcept;
|
||||
void mark_dispatch_completed() const noexcept;
|
||||
[[nodiscard]] Dispatch_Timing dispatch_timing() const noexcept;
|
||||
Event_Type type; /* 事件种类;构造后保持不变。 */
|
||||
private:
|
||||
mutable bool accepted{}; /* 处理链是否已经消费事件。 */
|
||||
mutable std::atomic_uint64_t dispatch_frame_sequence_{};
|
||||
mutable std::atomic_uint64_t dispatch_started_steady_ns_{};
|
||||
mutable std::atomic_uint64_t dispatch_completed_steady_ns_{};
|
||||
};
|
||||
template <typename T>
|
||||
concept Event_Object = std::derived_from<std::remove_cvref_t<T>, Event>;
|
||||
|
||||
@@ -1,9 +1,29 @@
|
||||
#pragma once
|
||||
#include <chrono>
|
||||
namespace aethera {
|
||||
namespace detail {
|
||||
inline std::uint64_t event_steady_time_ns() noexcept {
|
||||
return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()).count());
|
||||
}
|
||||
}
|
||||
inline Event::Event(Event_Type value) : type(value) {}
|
||||
inline Event::~Event() = default;
|
||||
inline void Event::accept() const noexcept { accepted = true; }
|
||||
inline bool Event::is_accepted() const noexcept { return accepted; }
|
||||
inline void Event::mark_dispatch_started(std::uint64_t frame_sequence) const noexcept {
|
||||
dispatch_frame_sequence_.store(frame_sequence, std::memory_order_relaxed);
|
||||
dispatch_completed_steady_ns_.store(0, std::memory_order_relaxed);
|
||||
dispatch_started_steady_ns_.store(detail::event_steady_time_ns(), std::memory_order_release);
|
||||
}
|
||||
inline void Event::mark_dispatch_completed() const noexcept {
|
||||
dispatch_completed_steady_ns_.store(detail::event_steady_time_ns(), std::memory_order_release);
|
||||
}
|
||||
inline Event::Dispatch_Timing Event::dispatch_timing() const noexcept {
|
||||
return {dispatch_frame_sequence_.load(std::memory_order_relaxed),
|
||||
dispatch_started_steady_ns_.load(std::memory_order_acquire),
|
||||
dispatch_completed_steady_ns_.load(std::memory_order_acquire)};
|
||||
}
|
||||
constexpr Keyboard_Modifier operator|(Keyboard_Modifier left, Keyboard_Modifier right) noexcept { return static_cast<Keyboard_Modifier>(static_cast<std::uint8_t>(left) | static_cast<std::uint8_t>(right)); }
|
||||
template <Event_Point Point> Basic_Pointer_Event<Point>::Basic_Pointer_Event(Event_Type value) : Event(value) {}
|
||||
template <Event_Point Point> double Basic_Pointer_Event<Point>::position_x() const noexcept { return static_cast<double>(position.x); }
|
||||
|
||||
@@ -1,16 +1,43 @@
|
||||
#include "scene.hpp" /* 后端共有 Prepare Scene 实现。 */
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
namespace aethera {
|
||||
void Scene::Private::Event_Queue::push(std::shared_ptr<Event> event) {
|
||||
if (!pending.enqueue(std::move(event))) throw std::bad_alloc{};
|
||||
}
|
||||
Scene::Private::Private() : runtime(std::make_unique<Runtime>()) {}
|
||||
Scene::Private::Private() = default;
|
||||
Scene::Private::~Private() {
|
||||
if (runtime->taskflow) detail::clear_stage_observers(runtime->taskflow.get());
|
||||
if (runtime && runtime->taskflow) detail::clear_stage_observers(runtime->taskflow.get());
|
||||
}
|
||||
void Scene::Private::push_event(std::unique_ptr<Event> event) {
|
||||
void Scene::Private::push_event(Event_Pointer event) {
|
||||
if (!event) throw std::invalid_argument("scene event ownership must not be empty");
|
||||
event_queue.push(std::shared_ptr<Event>{std::move(event)});
|
||||
std::lock_guard lock(runtime->event_mutex);
|
||||
runtime->events.pending->push_back(std::move(event));
|
||||
}
|
||||
Scene::Event_Batch Scene::Private::take_events(std::uint64_t frame_sequence) {
|
||||
Event_Batch result{runtime->events.current->get_allocator()};
|
||||
{
|
||||
std::lock_guard lock(runtime->event_mutex);
|
||||
runtime->events.advance();
|
||||
runtime->events.pending->clear();
|
||||
result = std::move(*runtime->events.current);
|
||||
}
|
||||
for (const auto& event : result) event->mark_dispatch_started(frame_sequence);
|
||||
if (!result.empty()) {
|
||||
std::lock_guard lock(runtime->report_mutex);
|
||||
runtime->reports.pending->insert(runtime->reports.pending->end(),
|
||||
result.begin(), result.end());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Scene::Event_Report_Batch Scene::Private::take_event_reports() {
|
||||
Event_Report_Batch result{runtime->reports.current->get_allocator()};
|
||||
std::lock_guard lock(runtime->report_mutex);
|
||||
runtime->reports.advance();
|
||||
runtime->reports.pending->clear();
|
||||
result = std::move(*runtime->reports.current);
|
||||
return result;
|
||||
}
|
||||
void Scene::dispatch_event(Event_Pointer event) {
|
||||
static_cast<Private&>(*d).push_event(std::move(event));
|
||||
}
|
||||
Scene::Event_Report_Batch Scene::take_event_reports() {
|
||||
return static_cast<Private&>(*d).take_event_reports();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#pragma once
|
||||
#include "frame.hpp"
|
||||
#include "renderable.hpp"
|
||||
#include <memory>
|
||||
#include <memory_resource>
|
||||
#include <vector>
|
||||
namespace aethera {
|
||||
/* Scene 状态标签,用于访问和订阅 Scene::State。 */
|
||||
/*
|
||||
@@ -8,6 +11,10 @@ namespace aethera {
|
||||
* 用户最终通过 Impl<Scene> 创建可使用实例;编辑 Dependency_Graph 后调用 advance() 提交结构变化,再调用 process(...) 执行当前场景。
|
||||
*/
|
||||
struct Scene : Def<Scene, Root, Dependency_Graph_Type<Prepare_Data_Tag, Renderable>> {
|
||||
using Event_Pointer = std::shared_ptr<Event>;
|
||||
using Event_Batch = std::pmr::vector<Event_Pointer>;
|
||||
using Event_Report_Pointer = std::shared_ptr<const Event>;
|
||||
using Event_Report_Batch = std::pmr::vector<Event_Report_Pointer>;
|
||||
/* Scene 当前没有额外发布属性;派生定义可在自己的 Prop 中继续追加字段。 */
|
||||
struct Prop : Prev_Prop {};
|
||||
/* Scene 每次 process(...) 后发布的总图结构与执行统计。 */
|
||||
@@ -24,6 +31,10 @@ struct Scene : Def<Scene, Root, Dependency_Graph_Type<Prepare_Data_Tag, Renderab
|
||||
};
|
||||
/* 完整声明、字段及可覆盖 CRTP hook 见 scene.ipp 中的 Scene::Private。 */
|
||||
struct Private;
|
||||
template <Event_Object Event_Object_Type, typename... Arguments>
|
||||
[[nodiscard]] std::shared_ptr<Event_Object_Type> make_event(Arguments&&... arguments);
|
||||
void dispatch_event(Event_Pointer event);
|
||||
[[nodiscard]] Event_Report_Batch take_event_reports();
|
||||
};
|
||||
}
|
||||
#include "scene.ipp"
|
||||
|
||||
+20
-26
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include <concurrentqueue-1.0.5/concurrentqueue.h>
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
@@ -8,23 +8,13 @@ namespace aethera {
|
||||
struct Scene::Private : Prev_Private {
|
||||
struct Result {}; /* process(...) 完成回调的结果类型;当前仅表示完成。 */
|
||||
struct Runtime; /* Scene 的 Taskflow 构建产物;完整定义位于本文件下方。 */
|
||||
/* Scene 唯一事件流:第三方 MPMC 队列负责跨线程所有权转移,当前事件仅供单消费者重试。 */
|
||||
struct Event_Queue : Pinned {
|
||||
moodycamel::ConcurrentQueue<std::shared_ptr<Event>> pending{}; /* 外部线程提交、Prepare 线程消费的事件队列。 */
|
||||
std::shared_ptr<Event> current{}; /* 后端暂不可用时留到下一帧重试的事件。 */
|
||||
void push(std::shared_ptr<Event> event);
|
||||
template <typename Callback>
|
||||
void consume(Callback&& callback) requires std::predicate<Callback, const std::shared_ptr<Event>&>;
|
||||
};
|
||||
std::unique_ptr<Runtime> runtime; /* Scene 唯一运行时构建产物的所有权。 */
|
||||
Event_Queue event_queue; /* 跨输入线程与 Scene Prepare 边界交换的唯一事件队列。 */
|
||||
Private();
|
||||
~Private();
|
||||
/* 将事件所有权无锁提交到外部待处理批次;空所有权违反调用契约。 */
|
||||
void push_event(std::unique_ptr<Event> event);
|
||||
/* 在 Scene 执行线程交换并按提交顺序消费一个完整批次。 */
|
||||
template <typename Callback>
|
||||
void consume_events(Callback&& callback) requires std::predicate<Callback, const std::shared_ptr<Event>&>;
|
||||
void push_event(Event_Pointer event);
|
||||
[[nodiscard]] Event_Batch take_events(std::uint64_t frame_sequence);
|
||||
[[nodiscard]] Event_Report_Batch take_event_reports();
|
||||
template <Attached Object> void bind_private_crtp(Object* object);
|
||||
/* Def CRTP hook:所有缓冲推进后重建必要的总 Taskflow,并更新 Scene_State_Tag 状态层。 */
|
||||
template <Attached Object>
|
||||
void after_advance(Object* object,
|
||||
@@ -42,19 +32,23 @@ struct Scene::Private : Prev_Private {
|
||||
};
|
||||
struct Scene::Private::Runtime {
|
||||
std::unique_ptr<tf::Taskflow> taskflow; /* 当前已构建的总 Taskflow;为空表示尚未构建。 */
|
||||
std::mutex event_mutex;
|
||||
double_buffer::Double_Buffer<Event_Batch> events;
|
||||
std::mutex report_mutex;
|
||||
double_buffer::Double_Buffer<Event_Report_Batch> reports;
|
||||
explicit Runtime(std::pmr::memory_resource* resource)
|
||||
: events(std::allocator_arg, typename decltype(events)::allocator_type{resource}),
|
||||
reports(std::allocator_arg, typename decltype(reports)::allocator_type{resource}) {}
|
||||
};
|
||||
template <typename Callback>
|
||||
void Scene::Private::Event_Queue::consume(Callback&& callback) requires std::predicate<Callback, const std::shared_ptr<Event>&> {
|
||||
if (current && !std::invoke(callback, std::as_const(current))) return;
|
||||
current.reset();
|
||||
while (pending.try_dequeue(current)) {
|
||||
if (!std::invoke(callback, std::as_const(current))) return;
|
||||
current.reset();
|
||||
}
|
||||
template <Attached Object>
|
||||
void Scene::Private::bind_private_crtp(Object* object) {
|
||||
Prev_Private::bind_private_crtp(object);
|
||||
runtime = std::make_unique<Runtime>(object->memory_resource());
|
||||
}
|
||||
template <typename Callback>
|
||||
void Scene::Private::consume_events(Callback&& callback) requires std::predicate<Callback, const std::shared_ptr<Event>&> {
|
||||
event_queue.consume(std::forward<Callback>(callback));
|
||||
template <Event_Object Event_Object_Type, typename... Arguments>
|
||||
std::shared_ptr<Event_Object_Type> Scene::make_event(Arguments&&... arguments) {
|
||||
return std::allocate_shared<Event_Object_Type>(
|
||||
allocator<Event_Object_Type>(), std::forward<Arguments>(arguments)...);
|
||||
}
|
||||
template <Attached Object, typename Callback>
|
||||
void Scene::Private::process(Object* object, Callback&& callback) requires std::invocable<Callback, const Result&> {
|
||||
|
||||
Reference in New Issue
Block a user