事件处理走双缓冲了
This commit is contained in:
@@ -72,6 +72,7 @@ struct Async_Render_Backend::Implementation
|
||||
Shared_Prepared_Visual_Batch visuals{}; /* Scene CPU Prepare 发布的大批量不可变快照。 */
|
||||
Scene_3D_Parameters parameters{}; /* 本帧 Scene、Camera 与轴参数快照。 */
|
||||
std::vector<Completion> completions{}; /* 合并到同一实际画面的逻辑帧。 */
|
||||
Scene::Event_Batch events{}; /* Prepare 边界已交换的完整事件批次。 */
|
||||
};
|
||||
struct Pending {
|
||||
Datoviz_Visual_Backend::Pending_Frame backend_frame{}; /* 三缓冲目标对应的已录制提交。 */
|
||||
@@ -84,11 +85,7 @@ struct Async_Render_Backend::Implementation
|
||||
std::optional<Gpu_Completion_Service::Result> result{}; /* fence 正常交付时的结果。 */
|
||||
std::exception_ptr failure{}; /* 提交域或完成服务的 Unknown Failure。 */
|
||||
};
|
||||
struct Event_Command {
|
||||
std::shared_ptr<const Event> event{}; /* Scene 转移给准备域的输入事件。 */
|
||||
Extent viewport{}; /* 事件产生时的物理像素视口。 */
|
||||
};
|
||||
using Command = std::variant<Submission, Gpu_Completion, Event_Command>;
|
||||
using Command = std::variant<Submission, Gpu_Completion>;
|
||||
|
||||
static constexpr std::size_t command_capacity = 64;
|
||||
std::shared_ptr<Render_Domain> render_domain; /* 同 GPU 唯一的轻量 Queue Submit 域。 */
|
||||
@@ -124,7 +121,7 @@ struct Async_Render_Backend::Implementation
|
||||
void initialize(const Scene_3D_Parameters& parameters);
|
||||
[[nodiscard]] Async_Render_Backend::Submit_Result render(
|
||||
Shared_Prepared_Visual_Batch visuals, Scene_3D_Parameters parameters,
|
||||
Frame_3D* frame);
|
||||
Frame_3D* frame, Scene::Event_Batch events);
|
||||
void accept(Submission submission);
|
||||
void merge_deferred(Submission submission);
|
||||
void prepare(Submission submission);
|
||||
@@ -133,7 +130,7 @@ struct Async_Render_Backend::Implementation
|
||||
void resolve(std::shared_ptr<Pending> pending,
|
||||
std::optional<Datoviz_Visual_Backend::Completed_Frame> completed);
|
||||
void complete(std::vector<Completion> completions);
|
||||
void dispatch(Event_Command command);
|
||||
void dispatch(const std::shared_ptr<Event>& event, Extent viewport);
|
||||
};
|
||||
|
||||
Async_Render_Backend::Async_Render_Backend(
|
||||
@@ -198,7 +195,7 @@ void Async_Render_Backend::Implementation::initialize(
|
||||
Async_Render_Backend::Submit_Result
|
||||
Async_Render_Backend::Implementation::render(
|
||||
Shared_Prepared_Visual_Batch visuals, Scene_3D_Parameters parameters,
|
||||
Frame_3D* frame) {
|
||||
Frame_3D* frame, Scene::Event_Batch events) {
|
||||
if (!frame)
|
||||
throw std::invalid_argument("3D backend requires a non-null external frame");
|
||||
if (!visuals || visuals->empty())
|
||||
@@ -215,7 +212,8 @@ Async_Render_Backend::Implementation::render(
|
||||
frame->mark(Frame_Trace_Marker::backend_queue_entered);
|
||||
try {
|
||||
enqueue(Submission{std::move(visuals), std::move(parameters),
|
||||
{Completion{frame, std::move(callback)}}});
|
||||
{Completion{frame, std::move(callback)}},
|
||||
std::move(events)});
|
||||
}
|
||||
catch (...) {
|
||||
fail(std::current_exception());
|
||||
@@ -226,6 +224,10 @@ Async_Render_Backend::Implementation::render(
|
||||
void Async_Render_Backend::Implementation::merge_deferred(
|
||||
Submission submission) {
|
||||
if (deferred_submission) {
|
||||
submission.events.insert(
|
||||
submission.events.begin(),
|
||||
std::make_move_iterator(deferred_submission->events.begin()),
|
||||
std::make_move_iterator(deferred_submission->events.end()));
|
||||
submission.completions.insert(
|
||||
submission.completions.begin(),
|
||||
std::make_move_iterator(deferred_submission->completions.begin()),
|
||||
@@ -253,6 +255,9 @@ void Async_Render_Backend::Implementation::prepare(Submission submission) {
|
||||
std::optional<Datoviz_Visual_Backend::Pending_Frame> prepared;
|
||||
try {
|
||||
initialize(submission.parameters);
|
||||
for (const auto& event : submission.events)
|
||||
dispatch(event, submission.parameters.viewport);
|
||||
submission.events.clear();
|
||||
for (const auto& completion : submission.completions)
|
||||
completion.output->mark(Frame_Trace_Marker::backend_prepare_started);
|
||||
const auto sequence =
|
||||
@@ -446,14 +451,21 @@ void Async_Render_Backend::Implementation::finish(Gpu_Completion completion) {
|
||||
resolve(std::move(pending), std::nullopt);
|
||||
}
|
||||
}
|
||||
void Async_Render_Backend::Implementation::dispatch(Event_Command command) {
|
||||
if (!backend || !command.event) return;
|
||||
void Async_Render_Backend::Implementation::dispatch(
|
||||
const std::shared_ptr<Event>& event, Extent viewport) {
|
||||
if (!event) return;
|
||||
const auto* pointer =
|
||||
dynamic_cast<const Pointer_Event_Capability*>(command.event.get());
|
||||
dynamic_cast<const Pointer_Event_Capability*>(event.get());
|
||||
const auto* wheel =
|
||||
dynamic_cast<const Wheel_Event_Capability*>(command.event.get());
|
||||
const auto* key = dynamic_cast<const Key_Event*>(command.event.get());
|
||||
if (wheel) {
|
||||
dynamic_cast<const Wheel_Event_Capability*>(event.get());
|
||||
const auto* key = dynamic_cast<const Key_Event*>(event.get());
|
||||
const bool pointer_valid = pointer &&
|
||||
std::isfinite(pointer->position_x()) &&
|
||||
std::isfinite(pointer->position_y()) &&
|
||||
pointer->position_x() >= 0.0 && pointer->position_y() >= 0.0 &&
|
||||
pointer->position_x() <= viewport.width &&
|
||||
pointer->position_y() <= viewport.height;
|
||||
if (wheel && pointer_valid) {
|
||||
backend->dispatch_wheel(
|
||||
static_cast<float>(pointer->position_x()),
|
||||
static_cast<float>(pointer->position_y()),
|
||||
@@ -461,20 +473,24 @@ void Async_Render_Backend::Implementation::dispatch(Event_Command command) {
|
||||
wheel->angle_delta_x_value()),
|
||||
wheel_step(wheel->pixel_delta_y_value(),
|
||||
wheel->angle_delta_y_value()),
|
||||
pointer->keyboard_modifiers(), command.viewport);
|
||||
pointer->keyboard_modifiers(), viewport);
|
||||
}
|
||||
else if (pointer) {
|
||||
else if (pointer_valid &&
|
||||
(event->type == Event_Type::pointer_move ||
|
||||
event->type == Event_Type::pointer_press ||
|
||||
event->type == Event_Type::pointer_release)) {
|
||||
backend->dispatch_pointer(
|
||||
command.event->type,
|
||||
event->type,
|
||||
static_cast<float>(pointer->position_x()),
|
||||
static_cast<float>(pointer->position_y()),
|
||||
command.event->type == Event_Type::pointer_move
|
||||
event->type == Event_Type::pointer_move
|
||||
? held_button(pointer->pointer_buttons())
|
||||
: pointer->pointer_button(),
|
||||
pointer->keyboard_modifiers(), command.viewport);
|
||||
pointer->keyboard_modifiers(), viewport);
|
||||
}
|
||||
else if (key) backend->dispatch_key(*key);
|
||||
command.event->accept();
|
||||
if ((wheel && pointer_valid) || pointer_valid || key) event->accept();
|
||||
event->mark_dispatch_completed();
|
||||
}
|
||||
void Async_Render_Backend::Implementation::run() noexcept {
|
||||
for (;;) {
|
||||
@@ -483,12 +499,11 @@ void Async_Render_Backend::Implementation::run() noexcept {
|
||||
command, std::chrono::milliseconds(10));
|
||||
if (received) {
|
||||
try {
|
||||
if (auto* submission = std::get_if<Submission>(&command))
|
||||
if (auto* submission = std::get_if<Submission>(&command)) {
|
||||
accept(std::move(*submission));
|
||||
}
|
||||
else if (auto* completion = std::get_if<Gpu_Completion>(&command))
|
||||
finish(std::move(*completion));
|
||||
else if (auto* event = std::get_if<Event_Command>(&command))
|
||||
dispatch(std::move(*event));
|
||||
}
|
||||
catch (...) {
|
||||
fail(std::current_exception());
|
||||
@@ -527,8 +542,9 @@ void Async_Render_Backend::Implementation::run() noexcept {
|
||||
|
||||
Async_Render_Backend::Submit_Result Async_Render_Backend::render(
|
||||
Shared_Prepared_Visual_Batch visuals, Scene_3D_Parameters parameters,
|
||||
Frame_3D* frame) {
|
||||
return implementation_->render(std::move(visuals), parameters, frame);
|
||||
Frame_3D* frame, Scene::Event_Batch events) {
|
||||
return implementation_->render(std::move(visuals), parameters, frame,
|
||||
std::move(events));
|
||||
}
|
||||
void Async_Render_Backend::set_frame_callback(Frame_Callback callback) {
|
||||
std::lock_guard lock(implementation_->callback_mutex);
|
||||
@@ -537,39 +553,4 @@ void Async_Render_Backend::set_frame_callback(Frame_Callback callback) {
|
||||
bool Async_Render_Backend::available() const noexcept {
|
||||
return implementation_->available.load(std::memory_order_acquire);
|
||||
}
|
||||
Async_Render_Backend::Dispatch_Event_Result
|
||||
Async_Render_Backend::dispatch_event(std::shared_ptr<const Event> event,
|
||||
Extent viewport) {
|
||||
auto self = implementation_;
|
||||
const auto* pointer = dynamic_cast<const Pointer_Event_Capability*>(event.get());
|
||||
const auto* wheel = dynamic_cast<const Wheel_Event_Capability*>(event.get());
|
||||
const auto* key = dynamic_cast<const Key_Event*>(event.get());
|
||||
if (wheel && !pointer) return Dispatch_Event_Result::invalid_event;
|
||||
if (pointer) {
|
||||
if (!std::isfinite(pointer->position_x()) ||
|
||||
!std::isfinite(pointer->position_y()))
|
||||
return Dispatch_Event_Result::invalid_event;
|
||||
if (pointer->position_x() < 0.0 || pointer->position_y() < 0.0 ||
|
||||
pointer->position_x() > viewport.width ||
|
||||
pointer->position_y() > viewport.height)
|
||||
return Dispatch_Event_Result::ignored;
|
||||
if (!wheel && event->type != Event_Type::pointer_move &&
|
||||
event->type != Event_Type::pointer_press &&
|
||||
event->type != Event_Type::pointer_release)
|
||||
return Dispatch_Event_Result::invalid_event;
|
||||
}
|
||||
else if (!key) return Dispatch_Event_Result::ignored;
|
||||
std::lock_guard admission_lock(self->admission_mutex);
|
||||
if (!self->available.load(std::memory_order_acquire) ||
|
||||
self->stop_requested.load(std::memory_order_acquire))
|
||||
return Dispatch_Event_Result::backend_unavailable;
|
||||
try {
|
||||
self->enqueue(Implementation::Event_Command{std::move(event), viewport});
|
||||
}
|
||||
catch (...) {
|
||||
self->fail(std::current_exception());
|
||||
return Dispatch_Event_Result::backend_unavailable;
|
||||
}
|
||||
return Dispatch_Event_Result::queued;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "../base/Frame_3D.hpp"
|
||||
#include "Backend_Types.hpp"
|
||||
#include <scene.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
namespace aethera::render_3d::detail {
|
||||
@@ -19,19 +20,10 @@ public:
|
||||
/* 将 Visual 快照所有权无等待转移给所属 GPU Render Domain。 */
|
||||
[[nodiscard]] Submit_Result render(
|
||||
Shared_Prepared_Visual_Batch visuals, Scene_3D_Parameters parameters,
|
||||
Frame_3D* frame);
|
||||
Frame_3D* frame, Scene::Event_Batch events);
|
||||
/* 设置后续提交捕获的逻辑帧完成出口。 */
|
||||
void set_frame_callback(Frame_Callback callback);
|
||||
[[nodiscard]] bool available() const noexcept;
|
||||
enum class Dispatch_Event_Result : std::uint8_t {
|
||||
queued,
|
||||
ignored,
|
||||
invalid_event,
|
||||
backend_unavailable
|
||||
};
|
||||
/* 将事件所有权无等待转移到 Render Domain;停止时由 Scene 在下一帧重试。 */
|
||||
[[nodiscard]] Dispatch_Event_Result dispatch_event(
|
||||
std::shared_ptr<const Event> event, Extent viewport);
|
||||
private:
|
||||
struct Implementation;
|
||||
std::shared_ptr<Implementation> implementation_; /* 已入队闭包共享实现生命周期。 */
|
||||
|
||||
@@ -64,7 +64,8 @@ Render_Domain::Post_Result Render_Domain::post(
|
||||
active_posters_.fetch_sub(1, std::memory_order_release);
|
||||
return Post_Result::stopping;
|
||||
}
|
||||
if (!tasks_.enqueue(std::move(task))) {
|
||||
const bool enqueued = tasks_.enqueue(std::move(task));
|
||||
if (!enqueued) {
|
||||
active_posters_.fetch_sub(1, std::memory_order_release);
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
@@ -75,8 +76,14 @@ void Render_Domain::run() noexcept {
|
||||
current_domain_ = this;
|
||||
for (;;) {
|
||||
std::unique_ptr<Task> task;
|
||||
const bool received = tasks_.wait_dequeue_timed(
|
||||
bool received = tasks_.wait_dequeue_timed(
|
||||
task, std::chrono::milliseconds(1));
|
||||
if (!received && stopping_.load(std::memory_order_acquire) &&
|
||||
active_posters_.load(std::memory_order_acquire) == 0) {
|
||||
/* stop 边界关闭生产者后再真实探测队列,不能用
|
||||
* size_approx 决定生命周期。 */
|
||||
received = tasks_.try_dequeue(task);
|
||||
}
|
||||
if (received && task) {
|
||||
try {
|
||||
task->function();
|
||||
@@ -95,8 +102,7 @@ void Render_Domain::run() noexcept {
|
||||
// 已经消费完所有先于停止边界进入的任务。size_approx() 只能诊断,
|
||||
// 不能参与线程退出正确性。
|
||||
if (!received && stopping_.load(std::memory_order_acquire) &&
|
||||
active_posters_.load(std::memory_order_acquire) == 0 &&
|
||||
!task) {
|
||||
active_posters_.load(std::memory_order_acquire) == 0) {
|
||||
current_domain_ = nullptr;
|
||||
if (destroy_on_exit_.load(std::memory_order_acquire)) {
|
||||
if (thread_.joinable()) thread_.detach();
|
||||
|
||||
@@ -4,7 +4,6 @@ bool Render_Scene_3D::Prop::operator==(const Prop&) const = default;
|
||||
bool Render_Scene_3D::State::operator==(const State&) const = default;
|
||||
Render_Scene_3D::Render_Result Render_Scene_3D::render(Frame_3D* frame) { return static_cast<Private&>(*d).dispatch->render(this, frame); }
|
||||
void Render_Scene_3D::set_frame_callback(Frame_Callback callback) { static_cast<Private&>(*d).dispatch->set_frame_callback(this, std::move(callback)); }
|
||||
void Render_Scene_3D::dispatch_event(std::unique_ptr<Event> event) { static_cast<Private&>(*d).dispatch->push_event(this, std::move(event)); }
|
||||
void Render_Scene_3D::activate_view() { static_cast<Private&>(*d).dispatch->set_active(this, true); }
|
||||
void Render_Scene_3D::deactivate_view() { static_cast<Private&>(*d).dispatch->set_active(this, false); }
|
||||
}
|
||||
|
||||
@@ -62,8 +62,6 @@ struct Render_Scene_3D : Def<Render_Scene_3D, Scene, Dependency_Graph_Type<Submi
|
||||
[[nodiscard]] Render_Result render(Frame_3D* frame);
|
||||
/* 安装完成帧回调;回调由 Datoviz Render Domain 线程传回同一外部帧。 */
|
||||
void set_frame_callback(Frame_Callback callback);
|
||||
/* 无锁提交事件所有权;下一次 Prepare 开始时无等待转交 Datoviz 渲染域。 */
|
||||
void dispatch_event(std::unique_ptr<Event> event);
|
||||
void activate_view();
|
||||
void deactivate_view();
|
||||
};
|
||||
|
||||
@@ -17,12 +17,10 @@ struct Scene_Paint_Context {
|
||||
struct Render_Scene_3D::Private : Prev_Private {
|
||||
using Render_Run = Render_Result (*)(Root*, Frame_3D*);
|
||||
using Callback_Run = void (*)(Root*, Frame_Callback);
|
||||
using Event_Run = void (*)(Root*, std::unique_ptr<Event>);
|
||||
using Active_Run = void (*)(Root*, bool);
|
||||
struct Dispatch {
|
||||
Render_Run render; /* 执行 CPU 图并异步提交 Paint。 */
|
||||
Callback_Run set_frame_callback; /* 安装最终完成帧回调。 */
|
||||
Event_Run push_event; /* 向 Kernel Scene 无锁待处理批次转移事件所有权。 */
|
||||
Active_Run set_active; /* 修改最终 Scene 的活动属性。 */
|
||||
};
|
||||
std::shared_ptr<detail::Async_Render_Backend> backend{}; /* Scene 拥有的异步后端;已入队命令自行延长实现寿命。 */
|
||||
@@ -44,8 +42,6 @@ struct Render_Scene_3D::Private : Prev_Private {
|
||||
template <Attached Object> void bind_private_crtp(Object* object);
|
||||
/* CRTP 覆盖:先执行 Kernel Prepare Taskflow,再运行只负责异步入队的三维 Submit 阶段。 */
|
||||
template <Attached Object, typename Callback> void process(Object* object, Callback&& callback) requires std::invocable<Callback>;
|
||||
/* Prepare 开始时交换公共 Scene 事件批次,并无等待提交到 Datoviz 渲染域。 */
|
||||
void dispatch_events(Extent viewport);
|
||||
template <Attached Object> [[nodiscard]] Render_Result render(Object* object, Frame_3D* frame);
|
||||
template <Attached Object> [[nodiscard]] static const Dispatch& dispatch_for();
|
||||
};
|
||||
@@ -164,12 +160,6 @@ void Render_Scene_3D::Private::initialize_backend(
|
||||
backend, object, nullptr, initial,
|
||||
std::make_shared<detail::Prepared_Visual_Batch>()});
|
||||
}
|
||||
inline void Render_Scene_3D::Private::dispatch_events(Extent viewport) {
|
||||
this->consume_events([&](const std::shared_ptr<Event>& event) {
|
||||
return backend->dispatch_event(event, viewport) !=
|
||||
detail::Async_Render_Backend::Dispatch_Event_Result::backend_unavailable;
|
||||
});
|
||||
}
|
||||
template <Attached Object, typename Callback>
|
||||
void Render_Scene_3D::Private::process(Object* object, Callback&& callback) requires std::invocable<Callback> {
|
||||
Frame_3D* frame = active_frame;
|
||||
@@ -178,7 +168,7 @@ void Render_Scene_3D::Private::process(Object* object, Callback&& callback) requ
|
||||
if (!prop.view_active || prop.viewport.empty() || !backend || !backend->available()) return;
|
||||
frame->mark(Frame_Trace_Marker::scene_render_started);
|
||||
frame->mark(Frame_Trace_Marker::event_dispatch_started);
|
||||
dispatch_events(prop.viewport);
|
||||
auto events = this->take_events(frame->identity().sequence);
|
||||
frame->mark(Frame_Trace_Marker::event_dispatch_finished);
|
||||
auto context = std::static_pointer_cast<detail::Scene_Paint_Context<Object>>(paint_context);
|
||||
context->parameters = parameters(object);
|
||||
@@ -202,7 +192,7 @@ void Render_Scene_3D::Private::process(Object* object, Callback&& callback) requ
|
||||
frame->mark(Frame_Trace_Marker::prepare_finished);
|
||||
frame->mark(Frame_Trace_Marker::paint_started);
|
||||
const auto result = context->backend->render(
|
||||
context->visuals, context->parameters, frame);
|
||||
context->visuals, context->parameters, frame, std::move(events));
|
||||
frame->mark(Frame_Trace_Marker::paint_finished);
|
||||
if (result == detail::Async_Render_Backend::Submit_Result::queued)
|
||||
std::invoke(std::forward<Callback>(callback));
|
||||
@@ -254,7 +244,8 @@ void Render_Scene_3D::Private::process(Object* object, Callback&& callback) requ
|
||||
if (context->visuals->empty())
|
||||
throw std::logic_error("render scene submit graph published no Visual snapshots");
|
||||
backend_submitted = context->backend->render(
|
||||
context->visuals, context->parameters, context->frame) ==
|
||||
context->visuals, context->parameters, context->frame,
|
||||
std::move(events)) ==
|
||||
detail::Async_Render_Backend::Submit_Result::queued;
|
||||
frame->mark(Frame_Trace_Marker::paint_finished);
|
||||
});
|
||||
@@ -278,6 +269,6 @@ Render_Scene_3D::Render_Result Render_Scene_3D::Private::render(Object* object,
|
||||
if (prop.viewport.empty()) return Render_Result::empty_viewport;
|
||||
return Render_Result::backend_unavailable;
|
||||
}
|
||||
template <Attached Object> const Render_Scene_3D::Private::Dispatch& Render_Scene_3D::Private::dispatch_for() { static const Dispatch value{[](Root* root, Frame_3D* frame) { auto* object = static_cast<Object*>(root); return static_cast<typename Object::Private&>(*object->d).render(object, frame); }, [](Root* root, Frame_Callback callback) { auto* object = static_cast<Object*>(root); auto& data = static_cast<typename Object::Private&>(*object->d); if (data.backend) data.backend->set_frame_callback(std::move(callback)); }, [](Root* root, std::unique_ptr<Event> event) { auto* object = static_cast<Object*>(root); static_cast<typename Object::Private&>(*object->d).push_event(std::move(event)); }, [](Root* root, bool active) { static_cast<Object*>(root)->template set<&Prop::view_active>(active); }}; return value; }
|
||||
template <Attached Object> const Render_Scene_3D::Private::Dispatch& Render_Scene_3D::Private::dispatch_for() { static const Dispatch value{[](Root* root, Frame_3D* frame) { auto* object = static_cast<Object*>(root); return static_cast<typename Object::Private&>(*object->d).render(object, frame); }, [](Root* root, Frame_Callback callback) { auto* object = static_cast<Object*>(root); auto& data = static_cast<typename Object::Private&>(*object->d); if (data.backend) data.backend->set_frame_callback(std::move(callback)); }, [](Root* root, bool active) { static_cast<Object*>(root)->template set<&Prop::view_active>(active); }}; return value; }
|
||||
template <Attached Object> void Render_Scene_3D::Private::bind_private_crtp(Object* object) { Prev_Private::bind_private_crtp(object); dispatch = &dispatch_for<Object>(); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user