事件处理走双缓冲了

This commit is contained in:
2026-08-24 00:55:34 +08:00
parent 933ff2ec08
commit db251b5abf
21 changed files with 497 additions and 211 deletions
+12
View File
@@ -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>;
+20
View File
@@ -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); }
+35 -8
View File
@@ -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();
}
}
+11
View File
@@ -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
View File
@@ -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&> {