优化了 还是卡
This commit is contained in:
@@ -40,7 +40,10 @@ struct Render_Scene_2D : Def<Render_Scene_2D, Scene,
|
||||
using Frame_Callback = std::function<void(Frame_2D*)>;
|
||||
/* 向调用方拥有的帧合成一次;必须先安装回调。回调返回前的并发请求返回 frame_in_flight。 */
|
||||
[[nodiscard]] std::expected<void, Render_Result> render(Frame_2D* frame);
|
||||
/* 安装完成帧回调;回调收到对应 render(frame) 的对象,返回时 Scene 才释放下一帧准入。 */
|
||||
/*
|
||||
* 安装完成帧回调;回调收到对应 render(frame) 的对象,返回时 Scene 才释放下一帧准入。
|
||||
* Scene 和调用方拥有的 Frame 必须存活到回调返回;析构不会用同步等待隐藏错误的生命周期。
|
||||
*/
|
||||
void set_frame_callback(Frame_Callback callback);
|
||||
/*
|
||||
* 返回最终像素完成后、帧回调前执行的直接 Taskflow。
|
||||
|
||||
@@ -31,6 +31,17 @@ std::expected<std::unique_ptr<Object>, Dependency_Graph_Error> Render_Scene_2D::
|
||||
auto attach_result = attach(scene.get());
|
||||
if (!attach_result) return std::unexpected(attach_result.error());
|
||||
}
|
||||
/*
|
||||
* Renderable attachment changes the Scene dependency graphs after the base
|
||||
* Builder has performed its initial commit. Commit those attachments here,
|
||||
* while no frame topology can be running, so the first render observes a
|
||||
* fully built Prepare/Paint DAG instead of publishing a background-only
|
||||
* bootstrap frame.
|
||||
*/
|
||||
scene->advance();
|
||||
/* after_advance writes derived DAG diagnostics into the State write side;
|
||||
* publish that already-computed result before handing the Scene to Plot. */
|
||||
scene->advance();
|
||||
return scene;
|
||||
}
|
||||
struct Render_Scene_2D::Private : Prev_Private {
|
||||
@@ -66,14 +77,18 @@ struct Render_Scene_2D::Private : Prev_Private {
|
||||
Frame_Statistics_Accumulator frame_statistics{}; /* Scene 内部增量计算;State 只发布定长统计结果。 */
|
||||
Task_Graph completion_graph{"render_2d.completion"}; /* 最终像素完成后、发布回调前执行的外部续写图。 */
|
||||
std::unique_ptr<Task_Graph> paint_taskflow{}; /* 仅由二维 Paint 图构建的执行图。 */
|
||||
std::unique_ptr<Task_Graph> frame_taskflow{};
|
||||
Frame_Callback active_callback{};
|
||||
bool active_trace{};
|
||||
std::vector<Renderable*> active_prepare_executions{};
|
||||
std::chrono::steady_clock::time_point active_prepare_started{};
|
||||
~Private();
|
||||
Frame_2D* active_frame{}; /* 当前同步 process 借用的外部帧;render 返回前清空。 */
|
||||
Blend2D_Cache* frame_target{}; /* 当前 render(frame) 所属外部颜色层;调用返回后清空。 */
|
||||
/* Impl CRTP 实现:在对象锁内执行 Kernel Scene,再按 Paint 图拓扑顺序合成颜色层。 */
|
||||
std::vector<Paint_Node> paint_order{}; /* Paint 图当前拓扑序及 Scene 缓存分组结果。 */
|
||||
std::vector<Cache_Group> cache_groups{}; /* 仅保存显式缓存根对应的执行分组。 */
|
||||
template <Attached Object, typename Callback>
|
||||
void process(Object* object, Callback&& callback) requires std::invocable<Callback>;
|
||||
template <Attached Object> void ensure_frame_taskflow(Object* object);
|
||||
/* Def CRTP hook:二维 Paint 图结构变化后重建其独立 Taskflow。 */
|
||||
template <Attached Object>
|
||||
void after_advance(Object* object, Prop* pending_prop, State_Access<State> pending_states, const Prop* current_prop, State_Access<State> current_states);
|
||||
@@ -176,7 +191,6 @@ void Render_Scene_2D::Private::after_advance(Object* object, Prop*, State_Access
|
||||
if (!data.paint_graph) data.paint_graph = std::make_unique<Task_Graph>(task_prefix + ".graph");
|
||||
paint_run = taskflow.compose(task_prefix + ".graph", *data.paint_graph);
|
||||
} else paint_run = taskflow.add(task_prefix + ".data", [dispatch, root] { if (dispatch->paint.run) dispatch->paint.run(root); });
|
||||
auto paint_extension = taskflow.compose(task_prefix + ".extension", data.paint_extension);
|
||||
auto paint_done = taskflow.add(task_prefix + ".complete", [dispatch, root] {
|
||||
auto& state = *dispatch->state.pending(root);
|
||||
if (state.paint_executed) {
|
||||
@@ -190,8 +204,15 @@ void Render_Scene_2D::Private::after_advance(Object* object, Prop*, State_Access
|
||||
});
|
||||
paint_if.precede(paint_run);
|
||||
paint_if.precede(paint_done);
|
||||
paint_run.precede(paint_extension);
|
||||
paint_extension.precede(paint_done);
|
||||
if (data.paint_extension.empty()) {
|
||||
paint_run.precede(paint_done);
|
||||
}
|
||||
else {
|
||||
auto paint_extension = taskflow.compose(
|
||||
task_prefix + ".extension", data.paint_extension);
|
||||
paint_run.precede(paint_extension);
|
||||
paint_extension.precede(paint_done);
|
||||
}
|
||||
if (has_previous) previous.precede(paint_if);
|
||||
previous = paint_done;
|
||||
if (paint_node.cache_group_last) {
|
||||
@@ -254,63 +275,102 @@ void Render_Scene_2D::Private::prepare_paint_targets(Object*, Blend2D_Cache& fra
|
||||
node.object->template mark_dirty<Paint_Tag>();
|
||||
}
|
||||
}
|
||||
template <Attached Object, typename Callback>
|
||||
void Render_Scene_2D::Private::process(Object* object, Callback&& callback)
|
||||
requires std::invocable<Callback> {
|
||||
Frame_2D* frame_object = active_frame;
|
||||
if (!frame_object) throw std::logic_error("2D Scene process has no external frame");
|
||||
const auto& state = static_cast<const Prop&>(*static_cast<typename Object::Private&>(*this).current);
|
||||
if (!state.view_active || state.viewport.empty()) return;
|
||||
frame_object->mark(Frame_Trace_Marker::scene_render_started);
|
||||
frame_object->mark(Frame_Trace_Marker::event_dispatch_started);
|
||||
dispatch_events(object, state.viewport, frame_object->identity().sequence);
|
||||
frame_object->mark(Frame_Trace_Marker::event_dispatch_finished);
|
||||
object->template current_dependency_graph<Paint_Cache_Tag>().for_each([](const Dependency_Graph::Node& node) {
|
||||
if (!node.object->template take_dirty<Paint_Cache_Tag>()) return;
|
||||
node.object->template mark_dirty<Prepare_Data_Tag>();
|
||||
node.object->template mark_dirty<Paint_Tag>();
|
||||
template <Attached Object>
|
||||
void Render_Scene_2D::Private::ensure_frame_taskflow(Object* object) {
|
||||
if (frame_taskflow) return;
|
||||
if (!runtime->taskflow)
|
||||
runtime->taskflow = std::make_unique<Task_Graph>("scene.prepare");
|
||||
if (!paint_taskflow)
|
||||
paint_taskflow = std::make_unique<Task_Graph>("render_2d.paint");
|
||||
frame_taskflow = std::make_unique<Task_Graph>("render_2d.frame");
|
||||
auto& graph = *frame_taskflow;
|
||||
auto begin = graph.add("scene.begin", [this, object] {
|
||||
auto* frame = active_frame;
|
||||
if (!frame) throw std::logic_error("2D frame DAG lost its active frame");
|
||||
const auto& prop = object->template read_prop<Render_Scene_2D::Base_Tag>();
|
||||
frame->mark(Frame_Trace_Marker::scene_render_started);
|
||||
frame->mark(Frame_Trace_Marker::event_dispatch_started);
|
||||
dispatch_events(object, prop.viewport, frame->identity().sequence);
|
||||
frame->mark(Frame_Trace_Marker::event_dispatch_finished);
|
||||
object->template current_dependency_graph<Paint_Cache_Tag>().for_each(
|
||||
[](const Dependency_Graph::Node& node) {
|
||||
if (!node.object->template take_dirty<Paint_Cache_Tag>()) return;
|
||||
node.object->template mark_dirty<Prepare_Data_Tag>();
|
||||
node.object->template mark_dirty<Paint_Tag>();
|
||||
});
|
||||
active_prepare_executions.clear();
|
||||
object->template current_dependency_graph<Prepare_Data_Tag>().for_each_bound(
|
||||
[this](Renderable* renderable, Renderable::Private& data) {
|
||||
bool execute = renderable->template dirty<Prepare_Data_Tag>();
|
||||
if (data.dispatch->prepare.builder)
|
||||
execute = execute || !data.prepare_graph_built ||
|
||||
data.dispatch->prepare.rebuild_predicate(renderable);
|
||||
if (execute) active_prepare_executions.push_back(renderable);
|
||||
});
|
||||
auto& private_data = static_cast<typename Object::Private&>(*this);
|
||||
auto& state = static_cast<State&>(*private_data.state.pending);
|
||||
state.taskflow_execution_time_ns = 0;
|
||||
active_prepare_started = std::chrono::steady_clock::now();
|
||||
frame->mark(Frame_Trace_Marker::prepare_started);
|
||||
});
|
||||
std::vector<Renderable*> prepare_executions;
|
||||
object->template current_dependency_graph<Prepare_Data_Tag>().for_each_bound([&](Renderable* renderable, Renderable::Private& data) {
|
||||
bool execute = renderable->template dirty<Prepare_Data_Tag>();
|
||||
if (data.dispatch->prepare.builder)
|
||||
execute = execute || !data.prepare_graph_built || data.dispatch->prepare.rebuild_predicate(renderable);
|
||||
if (execute) prepare_executions.push_back(renderable);
|
||||
});
|
||||
Prev_Private::process(object, frame_object, [&](const Scene::Private::Result&) {
|
||||
begin.describe("dimension", "2D").describe("stage", "event dispatch");
|
||||
auto prepare = graph.compose("scene.prepare", *runtime->taskflow);
|
||||
prepare.describe("dimension", "2D").describe("stage", "data prepare");
|
||||
auto setup_paint = graph.add("scene.paint.setup", [this, object] {
|
||||
auto* frame_object = active_frame;
|
||||
if (!frame_object) throw std::logic_error("2D Paint lost its active frame");
|
||||
auto& private_data = static_cast<typename Object::Private&>(*this);
|
||||
auto& scene_state = static_cast<State&>(*private_data.state.pending);
|
||||
scene_state.taskflow_execution_time_ns = static_cast<std::uint64_t>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now() - active_prepare_started).count());
|
||||
scene_state.event_statistics = event_statistics.state();
|
||||
frame_object->mark(Frame_Trace_Marker::prepare_finished);
|
||||
frame_object->mark(Frame_Trace_Marker::paint_started);
|
||||
for (auto* renderable : prepare_executions) renderable->template mark_dirty<Paint_Tag>();
|
||||
for (auto* renderable : active_prepare_executions)
|
||||
renderable->template mark_dirty<Paint_Tag>();
|
||||
auto& frame = detail::Frame_2D_Access::render_target(frame_object);
|
||||
struct Frame_Target_Scope {
|
||||
Blend2D_Cache*& target; /* Scene 当前帧目标槽位。 */
|
||||
Blend2D_Cache* previous{}; /* 嵌套调用前的目标;析构时恢复。 */
|
||||
~Frame_Target_Scope() { target = previous; }
|
||||
} frame_target_scope{frame_target, frame_target};
|
||||
frame_target = &frame;
|
||||
frame.ensure_size(state.viewport);
|
||||
const auto& prop = object->template read_prop<Render_Scene_2D::Base_Tag>();
|
||||
frame_object->mark(Frame_Trace_Marker::paint_frame_target_started);
|
||||
frame.ensure_size(prop.viewport);
|
||||
frame.clear();
|
||||
frame_object->mark(Frame_Trace_Marker::paint_frame_target_finished);
|
||||
frame_object->mark(Frame_Trace_Marker::paint_background_started);
|
||||
{
|
||||
detail::Painter painter(frame, state.viewport);
|
||||
painter.rect({0.0, 0.0, static_cast<double>(state.viewport.width),
|
||||
static_cast<double>(state.viewport.height)},
|
||||
Pen{.style = Line_Style::none}, Brush{state.background, Brush_Style::solid});
|
||||
detail::Painter painter(frame, prop.viewport);
|
||||
painter.rect({0.0, 0.0, static_cast<double>(prop.viewport.width),
|
||||
static_cast<double>(prop.viewport.height)},
|
||||
Pen{.style = Line_Style::none},
|
||||
Brush{prop.background, Brush_Style::solid});
|
||||
}
|
||||
prepare_paint_targets(object, frame, state.viewport);
|
||||
if (paint_taskflow && !paint_taskflow->empty()) {
|
||||
if (frame_object->taskflow_trace_requested())
|
||||
aethera::detail::run_taskflow(*paint_taskflow, *frame_object,
|
||||
"render_2d.paint");
|
||||
else
|
||||
aethera::detail::run_taskflow(*paint_taskflow);
|
||||
}
|
||||
frame_object->mark(Frame_Trace_Marker::paint_finished);
|
||||
frame_object->mark(Frame_Trace_Marker::paint_background_finished);
|
||||
frame_object->mark(Frame_Trace_Marker::paint_cache_targets_started);
|
||||
prepare_paint_targets(object, frame, prop.viewport);
|
||||
frame_object->mark(Frame_Trace_Marker::paint_cache_targets_finished);
|
||||
frame_object->mark(Frame_Trace_Marker::paint_taskflow_started);
|
||||
});
|
||||
std::invoke(std::forward<Callback>(callback));
|
||||
setup_paint.describe("dimension", "2D").describe("stage", "frame target");
|
||||
auto paint = graph.compose("scene.paint", *paint_taskflow);
|
||||
paint.describe("dimension", "2D").describe("backend", "Blend2D");
|
||||
auto finish_paint = graph.add("scene.paint.complete", [this] {
|
||||
active_frame->mark(Frame_Trace_Marker::paint_taskflow_finished);
|
||||
active_frame->mark(Frame_Trace_Marker::paint_finished);
|
||||
active_frame->mark(Frame_Trace_Marker::scene_render_finished);
|
||||
});
|
||||
finish_paint.describe("dimension", "2D").describe("stage", "pixel complete");
|
||||
auto completion = graph.compose("scene.completion", completion_graph);
|
||||
completion.describe("owner", "scene").describe("stage", "frame consumers");
|
||||
begin.precede(prepare);
|
||||
prepare.precede(setup_paint);
|
||||
setup_paint.precede(paint);
|
||||
paint.precede(finish_paint);
|
||||
finish_paint.precede(completion);
|
||||
}
|
||||
|
||||
template <Attached Object>
|
||||
std::expected<void, Render_Scene_2D::Render_Result>
|
||||
Render_Scene_2D::Private::render(Object* object, Frame_2D* frame) {
|
||||
static_cast<void>(detail::Frame_2D_Access::render_target(frame));
|
||||
Frame_Callback callback;
|
||||
{
|
||||
std::lock_guard lock(render_mutex);
|
||||
@@ -321,59 +381,88 @@ Render_Scene_2D::Private::render(Object* object, Frame_2D* frame) {
|
||||
frame_in_flight = true;
|
||||
callback = frame_callback;
|
||||
}
|
||||
struct Admission_Scope {
|
||||
Private& data;
|
||||
~Admission_Scope() {
|
||||
std::lock_guard lock(data.render_mutex);
|
||||
data.frame_in_flight = false;
|
||||
const auto release_admission = [this] {
|
||||
std::lock_guard lock(render_mutex);
|
||||
frame_in_flight = false;
|
||||
};
|
||||
try {
|
||||
/*
|
||||
* Plot writes properties and tagged input buffers before render().
|
||||
* Commit the complete Scene object here, before submitting the frame
|
||||
* topology: this advances every attached Renderable, publishes the
|
||||
* current input buffers, and rebuilds dirty Prepare/Paint graphs while
|
||||
* no Taskflow is running. The completion callback only publishes
|
||||
* results; it must not modify a graph that is still executing.
|
||||
*/
|
||||
object->advance();
|
||||
const auto& prop =
|
||||
object->template read_prop<Render_Scene_2D::Base_Tag>();
|
||||
if (!prop.view_active) {
|
||||
release_admission();
|
||||
return std::unexpected(Render_Result::view_inactive);
|
||||
}
|
||||
} admission{*this};
|
||||
if (prop.viewport.empty()) {
|
||||
release_admission();
|
||||
return std::unexpected(Render_Result::empty_viewport);
|
||||
}
|
||||
ensure_frame_taskflow(object);
|
||||
active_frame = frame;
|
||||
active_callback = callback;
|
||||
}
|
||||
catch (...) {
|
||||
release_admission();
|
||||
throw;
|
||||
}
|
||||
frame->mark(Frame_Trace_Marker::scene_render_requested);
|
||||
const bool trace_started = aethera::detail::begin_taskflow_trace(*frame);
|
||||
struct Trace_Scope {
|
||||
Frame_2D* frame{}; /* 异常路径仍需关闭本帧 Observer 写入窗口。 */
|
||||
bool active{}; /* 只有成功取得全局捕获槽位才负责关闭。 */
|
||||
~Trace_Scope() {
|
||||
if (active) aethera::detail::finish_taskflow_trace(*frame);
|
||||
}
|
||||
} trace_scope{frame, trace_started};
|
||||
struct Active_Frame_Scope {
|
||||
Frame_2D*& target; /* 最终 Private 的同步 process 帧槽位。 */
|
||||
Frame_2D* previous{}; /* 嵌套调用前的帧;析构时恢复。 */
|
||||
~Active_Frame_Scope() { target = previous; }
|
||||
} active_frame_scope{active_frame, active_frame};
|
||||
active_frame = frame;
|
||||
bool completed{};
|
||||
object->process([&] {
|
||||
completed = true;
|
||||
frame->mark(Frame_Trace_Marker::scene_render_finished);
|
||||
/* 帧准入直到 completion 图和最终回调都结束才释放,因此图运行期间外部帧不会被下一帧复用。 */
|
||||
if (!completion_graph.empty()) {
|
||||
if (frame->taskflow_trace_requested())
|
||||
aethera::detail::run_taskflow(completion_graph, *frame,
|
||||
"render_2d.completion");
|
||||
else
|
||||
aethera::detail::run_taskflow(completion_graph);
|
||||
}
|
||||
if (trace_started) {
|
||||
active_trace = aethera::detail::begin_taskflow_trace(*frame);
|
||||
try {
|
||||
auto completion = [this, object, frame] {
|
||||
struct Release {
|
||||
Private* data;
|
||||
~Release() {
|
||||
data->active_frame = nullptr;
|
||||
data->frame_target = nullptr;
|
||||
data->active_callback = {};
|
||||
data->active_prepare_executions.clear();
|
||||
std::lock_guard lock(data->render_mutex);
|
||||
data->frame_in_flight = false;
|
||||
}
|
||||
} release{this};
|
||||
if (active_trace) {
|
||||
aethera::detail::finish_taskflow_trace(*frame);
|
||||
active_trace = false;
|
||||
}
|
||||
frame->mark(Frame_Trace_Marker::callback_started);
|
||||
active_callback(frame);
|
||||
frame->mark(Frame_Trace_Marker::callback_finished);
|
||||
frame->mark(Frame_Trace_Marker::frame_ready);
|
||||
auto& private_data = static_cast<typename Object::Private&>(*this);
|
||||
auto& scene_state = static_cast<State&>(*private_data.state.pending);
|
||||
scene_state.frame_statistics = frame_statistics.submit(
|
||||
*frame, Frame_Dimension::two_dimensional);
|
||||
private_data.state.advance();
|
||||
object->template notify_state<Render_Scene_2D::Base_Tag>();
|
||||
};
|
||||
if (frame->taskflow_trace_requested())
|
||||
aethera::detail::run_taskflow(
|
||||
*frame_taskflow, *frame, "render_2d.frame", std::move(completion));
|
||||
else
|
||||
aethera::detail::run_taskflow(*frame_taskflow, std::move(completion));
|
||||
}
|
||||
catch (...) {
|
||||
if (active_trace) {
|
||||
aethera::detail::finish_taskflow_trace(*frame);
|
||||
trace_scope.active = false;
|
||||
active_trace = false;
|
||||
}
|
||||
frame->mark(Frame_Trace_Marker::callback_started);
|
||||
callback(frame);
|
||||
frame->mark(Frame_Trace_Marker::callback_finished);
|
||||
frame->mark(Frame_Trace_Marker::frame_ready);
|
||||
auto& private_data = static_cast<typename Object::Private&>(*this);
|
||||
auto& scene_state = static_cast<State&>(*private_data.state.pending);
|
||||
scene_state.frame_statistics = frame_statistics.submit(
|
||||
*frame, Frame_Dimension::two_dimensional);
|
||||
private_data.state.advance();
|
||||
object->template notify_state<Render_Scene_2D::Base_Tag>();
|
||||
});
|
||||
if (completed) return {};
|
||||
const auto& prop = object->template read_prop<Render_Scene_2D::Base_Tag>();
|
||||
return std::unexpected(prop.view_active ? Render_Result::empty_viewport
|
||||
: Render_Result::view_inactive);
|
||||
active_frame = nullptr;
|
||||
frame_target = nullptr;
|
||||
active_callback = {};
|
||||
active_prepare_executions.clear();
|
||||
std::lock_guard lock(render_mutex);
|
||||
frame_in_flight = false;
|
||||
throw;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
template <Attached Object>
|
||||
void Render_Scene_2D::Private::dispatch_events(Object* object, Size viewport,
|
||||
|
||||
Reference in New Issue
Block a user