提升观测系统

隔离taskflow
This commit is contained in:
2026-08-25 10:15:59 +08:00
parent c0d5a13350
commit 2c3105cc07
37 changed files with 1538 additions and 184 deletions
+16 -16
View File
@@ -21,10 +21,10 @@ struct Afterglow::Private : Prev_Private {
void bind_sources(Frequency_Object* frequency_axis_value, Power_Object* power_axis_value);
/* CRTP 覆盖:构建累加、归一化和着色三阶段 Prepare 子图。 */
template <Attached Object>
[[nodiscard]] tf::Taskflow build_prepare_graph(Object* object, const Prop& state);
[[nodiscard]] Task_Graph build_prepare_graph(Object* object, const Prop& state);
/* CRTP 覆盖:构建消费色块矩阵的 Paint 子图。 */
template <Attached Object>
[[nodiscard]] tf::Taskflow build_paint_graph(Object* object, const Prop& state);
[[nodiscard]] Task_Graph build_paint_graph(Object* object, const Prop& state);
/* CRTP 覆盖:分块数量改变时请求重建 Prepare 子图。 */
template <Attached Object>
[[nodiscard]] bool should_rebuild_prepare_graph(Object* object, const Prop& state);
@@ -85,27 +85,27 @@ bool Afterglow::Private::should_rebuild_prepare_graph(Object* object, const Prop
return graph_partition_count != detail::curve_partition_count(state.partition_mode, state.partition_count, std::max<std::size_t>(1, columns));
}
template <Attached Object>
tf::Taskflow Afterglow::Private::build_prepare_graph(Object* object, const Prop& state) {
Task_Graph Afterglow::Private::build_prepare_graph(Object* object, const Prop& state) {
const std::size_t available = object->template access_query_stream<Afterglow_Stream_Tag>(
[](std::span<const std::shared_ptr<const std::vector<Plot_Value>>> spectra) {
return spectra.empty() ? 0 : spectra.back()->size();
});
const std::size_t columns = state.frequency_point_size ? std::min(state.frequency_point_size, available) : available;
graph_partition_count = detail::curve_partition_count(state.partition_mode, state.partition_count, std::max<std::size_t>(1, columns));
tf::Taskflow graph;
auto begin = graph.emplace([this, object] {
Task_Graph graph{"afterglow.prepare"};
auto begin = graph.add("frame", [this, object] {
prepare_frame(object);
}).name("afterglow.prepare.frame");
auto normalize = graph.emplace([this] {
});
auto normalize = graph.add("normalize", [this] {
normalize_frame();
}).name("afterglow.prepare.normalize");
});
for (Plot_Partition_Count index = 0; index < graph_partition_count; ++index) {
auto accumulate = graph.emplace([this, object, index] {
auto accumulate = graph.add("accumulate", [this, object, index] {
accumulate_partition(object, index);
}).name("afterglow.prepare.accumulate");
auto color = graph.emplace([this, object, index] {
});
auto color = graph.add("color", [this, object, index] {
color_partition(object, index);
}).name("afterglow.prepare.color");
});
begin.precede(accumulate);
accumulate.precede(normalize);
normalize.precede(color);
@@ -113,11 +113,11 @@ tf::Taskflow Afterglow::Private::build_prepare_graph(Object* object, const Prop&
return graph;
}
template <Attached Object>
tf::Taskflow Afterglow::Private::build_paint_graph(Object* object, const Prop&) {
tf::Taskflow graph;
graph.emplace([this, object] {
Task_Graph Afterglow::Private::build_paint_graph(Object* object, const Prop&) {
Task_Graph graph{"afterglow.paint"};
graph.add("frame", [this, object] {
paint_frame(object);
}).name("afterglow.paint.frame");
});
return graph;
}
template <Attached Object>
@@ -15,9 +15,9 @@ struct Frequency_Trace::Private : Prev_Private {
Plot_Partition_Count graph_partition_count{}; /* 当前 Prepare 子图固化的分块数。 */
void bind_sources(Time_Object* time_axis_value, Value_Object* value_axis_value);
/* CRTP 覆盖:按当前样本规模构建分块 Prepare 子图。 */
template <Attached Object> [[nodiscard]] tf::Taskflow build_prepare_graph(Object* object, const Prop& state);
template <Attached Object> [[nodiscard]] Task_Graph build_prepare_graph(Object* object, const Prop& state);
/* CRTP 覆盖:构建消费已准备曲线的 Paint 子图。 */
template <Attached Object> [[nodiscard]] tf::Taskflow build_paint_graph(Object* object, const Prop& state);
template <Attached Object> [[nodiscard]] Task_Graph build_paint_graph(Object* object, const Prop& state);
/* CRTP 覆盖:分块数量改变时请求重建 Prepare 子图。 */
template <Attached Object> [[nodiscard]] bool should_rebuild_prepare_graph(Object* object, const Prop& state);
template <Attached Object> void prepare_frame(Object* object, Plot_Partition_Count partition_count);
@@ -36,14 +36,14 @@ std::expected<std::unique_ptr<Object>, Dependency_Graph_Error> Frequency_Trace::
template <Attached Object>
bool Frequency_Trace::Private::should_rebuild_prepare_graph(Object* object, const Prop& state) { return graph_partition_count != detail::curve_partition_count(state.partition_mode, state.partition_count, object->template access_query_stream<Frequency_Trace_Stream_Tag>([](std::span<const Frequency_Trace_Sample> samples) { return samples.size(); })); }
template <Attached Object>
tf::Taskflow Frequency_Trace::Private::build_prepare_graph(Object* object, const Prop& state) {
graph_partition_count = detail::curve_partition_count(state.partition_mode, state.partition_count, object->template access_query_stream<Frequency_Trace_Stream_Tag>([](std::span<const Frequency_Trace_Sample> samples) { return samples.size(); })); tf::Taskflow graph;
auto begin = graph.emplace([this, object] { prepare_frame(object, graph_partition_count); }).name("frequency_trace.prepare.frame");
for (Plot_Partition_Count index = 0; index < graph_partition_count; ++index) { auto task = graph.emplace([this, object, index] { prepare_partition(object, index); }).name("frequency_trace.prepare.partition"); begin.precede(task); }
Task_Graph Frequency_Trace::Private::build_prepare_graph(Object* object, const Prop& state) {
graph_partition_count = detail::curve_partition_count(state.partition_mode, state.partition_count, object->template access_query_stream<Frequency_Trace_Stream_Tag>([](std::span<const Frequency_Trace_Sample> samples) { return samples.size(); })); Task_Graph graph{"frequency_trace.prepare"};
auto begin = graph.add("frame", [this, object] { prepare_frame(object, graph_partition_count); });
for (Plot_Partition_Count index = 0; index < graph_partition_count; ++index) { auto task = graph.add("partition", [this, object, index] { prepare_partition(object, index); }); begin.precede(task); }
return graph;
}
template <Attached Object>
tf::Taskflow Frequency_Trace::Private::build_paint_graph(Object* object, const Prop&) { tf::Taskflow graph; graph.emplace([this, object] { paint_frame(object); }).name("frequency_trace.paint.frame"); return graph; }
Task_Graph Frequency_Trace::Private::build_paint_graph(Object* object, const Prop&) { Task_Graph graph{"frequency_trace.paint"}; graph.add("frame", [this, object] { paint_frame(object); }); return graph; }
template <Attached Object>
void Frequency_Trace::Private::prepare_frame(Object* object, Plot_Partition_Count partition_count) {
const auto& state = object->template read_prop<Frequency_Trace::Base_Tag>(); const auto& time_layout = time_axis->template read_prop<Abs_Axis::Base_Tag>(); const auto& value_layout = value_axis->template read_prop<Abs_Axis::Base_Tag>();
+9 -9
View File
@@ -42,9 +42,9 @@ struct Spectrum::Private : Prev_Private {
/* CRTP State 钩子:Spectrum 业务状态写入后标记自身 Prepare;其他继承层状态由各自 Private 负责。 */
template <typename Object, typename Owner, typename Member, typename Prop_Type> void after_prop_set(Object* object, Member Owner::* member, Prop_Access<Prop_Type> pending_states);
/* CRTP 子图能力:按当前样本数和 State 分块策略构建并行 Prepare 图。 */
template <Attached Object> [[nodiscard]] tf::Taskflow build_prepare_graph(Object* object, const Prop& state);
template <Attached Object> [[nodiscard]] Task_Graph build_prepare_graph(Object* object, const Prop& state);
/* CRTP 子图能力:构建背景、分块曲线和覆盖标记的 Paint 图。 */
template <Attached Object> [[nodiscard]] tf::Taskflow build_paint_graph(Object* object, const Prop& state);
template <Attached Object> [[nodiscard]] Task_Graph build_paint_graph(Object* object, const Prop& state);
/* CRTP 覆盖:样本规模或分块配置改变时重建 Prepare 子图。 */
template <Attached Object> [[nodiscard]] bool should_rebuild_prepare_graph(Object* object, const Prop& state);
template <Attached Object> [[nodiscard]] std::size_t desired_partition_count(const Object* object, const Prop& state) const;
@@ -95,21 +95,21 @@ bool Spectrum::Private::should_rebuild_prepare_graph(Object* object, const Prop&
return prepare_graph_partition_count != desired_partition_count(object, state);
}
template <Attached Object>
tf::Taskflow Spectrum::Private::build_prepare_graph(Object* object, const Prop& state) {
Task_Graph Spectrum::Private::build_prepare_graph(Object* object, const Prop& state) {
const std::size_t partition_count = desired_partition_count(object, state);
prepare_graph_partition_count = partition_count;
tf::Taskflow graph;
auto begin = graph.emplace([this, object, partition_count] { prepare_frame(object, partition_count); }).name("spectrum.prepare.frame");
Task_Graph graph{"spectrum.prepare"};
auto begin = graph.add("frame", [this, object, partition_count] { prepare_frame(object, partition_count); });
for (std::size_t index = 0; index < partition_count; ++index) {
auto partition = graph.emplace([this, object, index] { prepare_partition(object, index); }).name("spectrum.prepare.partition");
auto partition = graph.add("partition", [this, object, index] { prepare_partition(object, index); });
begin.precede(partition);
}
return graph;
}
template <Attached Object>
tf::Taskflow Spectrum::Private::build_paint_graph(Object* object, const Prop&) {
tf::Taskflow graph;
graph.emplace([this, object] { paint_frame(object); }).name("spectrum.paint.frame");
Task_Graph Spectrum::Private::build_paint_graph(Object* object, const Prop&) {
Task_Graph graph{"spectrum.paint"};
graph.add("frame", [this, object] { paint_frame(object); });
return graph;
}
template <Attached Object>
@@ -27,9 +27,9 @@ struct Sweep_Spectrum::Private : Prev_Private {
Plot_Partition_Count graph_partition_count{}; /* 当前 Prepare 子图分块数。 */
void bind_sources(Frequency_Object* frequency_axis_value, Power_Object* power_axis_value);
/* CRTP 覆盖:按扫描点规模构建分块 Prepare 子图。 */
template <Attached Object> [[nodiscard]] tf::Taskflow build_prepare_graph(Object* object, const Prop& state);
template <Attached Object> [[nodiscard]] Task_Graph build_prepare_graph(Object* object, const Prop& state);
/* CRTP 覆盖:构建消费曲线分块的 Paint 子图。 */
template <Attached Object> [[nodiscard]] tf::Taskflow build_paint_graph(Object* object, const Prop& state);
template <Attached Object> [[nodiscard]] Task_Graph build_paint_graph(Object* object, const Prop& state);
/* CRTP 覆盖:分块数量改变时请求重建 Prepare 子图。 */
template <Attached Object> [[nodiscard]] bool should_rebuild_prepare_graph(Object* object, const Prop& state);
template <Attached Object> void prepare_frame(Object* object);
@@ -48,9 +48,9 @@ std::expected<std::unique_ptr<Object>, Dependency_Graph_Error> Sweep_Spectrum::B
template <Attached Object>
bool Sweep_Spectrum::Private::should_rebuild_prepare_graph(Object*, const Prop& state) { return graph_partition_count != detail::curve_partition_count(state.partition_mode, state.partition_count, stored_point_count()); }
template <Attached Object>
tf::Taskflow Sweep_Spectrum::Private::build_prepare_graph(Object* object, const Prop& state) { graph_partition_count = detail::curve_partition_count(state.partition_mode, state.partition_count, stored_point_count()); tf::Taskflow graph; auto begin = graph.emplace([this, object] { prepare_frame(object); }).name("sweep_spectrum.prepare.frame"); for (Plot_Partition_Count index = 0; index < graph_partition_count; ++index) { auto task = graph.emplace([this, object, index] { prepare_partition(object, index); }).name("sweep_spectrum.prepare.partition"); begin.precede(task); } return graph; }
Task_Graph Sweep_Spectrum::Private::build_prepare_graph(Object* object, const Prop& state) { graph_partition_count = detail::curve_partition_count(state.partition_mode, state.partition_count, stored_point_count()); Task_Graph graph{"sweep_spectrum.prepare"}; auto begin = graph.add("frame", [this, object] { prepare_frame(object); }); for (Plot_Partition_Count index = 0; index < graph_partition_count; ++index) { auto task = graph.add("partition", [this, object, index] { prepare_partition(object, index); }); begin.precede(task); } return graph; }
template <Attached Object>
tf::Taskflow Sweep_Spectrum::Private::build_paint_graph(Object* object, const Prop&) { tf::Taskflow graph; graph.emplace([this, object] { paint_frame(object); }).name("sweep_spectrum.paint.frame"); return graph; }
Task_Graph Sweep_Spectrum::Private::build_paint_graph(Object* object, const Prop&) { Task_Graph graph{"sweep_spectrum.paint"}; graph.add("frame", [this, object] { paint_frame(object); }); return graph; }
template <Attached Object>
void Sweep_Spectrum::Private::prepare_frame(Object* object) {
const auto& state = object->template read_prop<Sweep_Spectrum::Base_Tag>(); const auto& frequency_layout = frequency_axis->template read_prop<Abs_Axis::Base_Tag>(); const auto& power_layout = power_axis->template read_prop<Abs_Axis::Base_Tag>(); const auto& power_state = power_axis->template read_prop<Numeric_Axis::Base_Tag>(); prepared = {}; prepared.canvas = scene->template read_prop<Render_Scene_2D::Base_Tag>().viewport;
+12 -12
View File
@@ -32,10 +32,10 @@ struct Waterfall::Private : Prev_Private {
void bind_sources(Frequency_Object* frequency_axis_value, Time_Object* time_axis_value);
/* CRTP 覆盖:按当前色块工作量构建分块 Prepare 子图。 */
template <Attached Object>
[[nodiscard]] tf::Taskflow build_prepare_graph(Object* object, const Prop& state);
[[nodiscard]] Task_Graph build_prepare_graph(Object* object, const Prop& state);
/* CRTP 覆盖:构建消费色块矩阵和提示信息的 Paint 子图。 */
template <Attached Object>
[[nodiscard]] tf::Taskflow build_paint_graph(Object* object, const Prop& state);
[[nodiscard]] Task_Graph build_paint_graph(Object* object, const Prop& state);
/* CRTP 覆盖:分块数量改变时请求重建 Prepare 子图。 */
template <Attached Object>
[[nodiscard]] bool should_rebuild_prepare_graph(Object* object, const Prop& state);
@@ -96,29 +96,29 @@ bool Waterfall::Private::should_rebuild_prepare_graph(Object* object, const Prop
return graph_partition_count != detail::curve_partition_count(state.partition_mode, state.partition_count, cells);
}
template <Attached Object>
tf::Taskflow Waterfall::Private::build_prepare_graph(Object* object, const Prop& state) {
Task_Graph Waterfall::Private::build_prepare_graph(Object* object, const Prop& state) {
const std::size_t cells = object->template access_query_stream<Waterfall_Stream_Tag>([&](std::span<const std::shared_ptr<const Waterfall_Row>> rows) {
return rows.size() * (state.frequency_bin_count ? state.frequency_bin_count : rows.empty() ? 1 : rows.back()->values.size());
});
graph_partition_count = detail::curve_partition_count(state.partition_mode, state.partition_count, cells);
tf::Taskflow graph;
auto begin = graph.emplace([this, object] {
Task_Graph graph{"waterfall.prepare"};
auto begin = graph.add("frame", [this, object] {
prepare_frame(object);
}).name("waterfall.prepare.frame");
});
for (Plot_Partition_Count index = 0; index < graph_partition_count; ++index) {
auto task = graph.emplace([this, object, index] {
auto task = graph.add("partition", [this, object, index] {
prepare_partition(object, index);
}).name("waterfall.prepare.partition");
});
begin.precede(task);
}
return graph;
}
template <Attached Object>
tf::Taskflow Waterfall::Private::build_paint_graph(Object* object, const Prop&) {
tf::Taskflow graph;
graph.emplace([this, object] {
Task_Graph Waterfall::Private::build_paint_graph(Object* object, const Prop&) {
Task_Graph graph{"waterfall.paint"};
graph.add("frame", [this, object] {
paint_frame(object);
}).name("waterfall.paint.frame");
});
return graph;
}
template <Attached Object>
@@ -9,7 +9,7 @@ Render_Scene_2D::render(Frame_2D* frame) {
return static_cast<Private&>(*d).dispatch->render(this, frame);
}
void Render_Scene_2D::set_frame_callback(Frame_Callback callback) { static_cast<Private&>(*d).dispatch->set_frame_callback(this, std::move(callback)); }
tf::Taskflow& Render_Scene_2D::completion_taskflow() {
Task_Graph& Render_Scene_2D::completion_taskflow() {
return static_cast<Private&>(*d).completion_graph;
}
void Render_Scene_2D::activate_view() {
@@ -48,7 +48,7 @@ struct Render_Scene_2D : Def<Render_Scene_2D, Scene,
* completion Taskflow 完成前调用方必须保持本帧像素所有权;节点若启动更晚结束的异步工作,
* 必须捕获拥有像素所有权的对象,禁止只捕获会被下一帧复用的裸 Frame_2D*。
*/
[[nodiscard]] tf::Taskflow& completion_taskflow();
[[nodiscard]] Task_Graph& completion_taskflow();
/* 激活后 render 才会执行。 */
void activate_view();
/* 停止后续 render 调用,不清除最后一帧。 */
+45 -20
View File
@@ -64,8 +64,8 @@ struct Render_Scene_2D::Private : Prev_Private {
bool frame_in_flight{}; /* render 准入到完成回调返回的唯一状态源。 */
Frame_Callback frame_callback{}; /* 合成完成后的唯一像素发布出口。 */
Frame_Statistics_Accumulator frame_statistics{}; /* Scene 内部增量计算;State 只发布定长统计结果。 */
tf::Taskflow completion_graph{}; /* 最终像素完成后、发布回调前执行的外部续写图。 */
std::unique_ptr<tf::Taskflow> paint_taskflow{}; /* 仅由二维 Paint 图构建的执行图。 */
Task_Graph completion_graph{"render_2d.completion"}; /* 最终像素完成后、发布回调前执行的外部续写图。 */
std::unique_ptr<Task_Graph> paint_taskflow{}; /* 仅由二维 Paint 图构建的执行图。 */
~Private();
Frame_2D* active_frame{}; /* 当前同步 process 借用的外部帧;render 返回前清空。 */
Blend2D_Cache* frame_target{}; /* 当前 render(frame) 所属外部颜色层;调用返回后清空。 */
@@ -104,7 +104,7 @@ void Render_Scene_2D::Private::after_advance(Object* object, Prop*, State_Access
rebuild = true;
}
if (!rebuild) return;
if (!paint_taskflow) paint_taskflow = std::make_unique<tf::Taskflow>();
if (!paint_taskflow) paint_taskflow = std::make_unique<Task_Graph>("render_2d.paint");
auto& taskflow = *paint_taskflow;
taskflow.clear();
const auto dependencies = object->template current_dependency_graph<Paint_Tag>();
@@ -139,14 +139,15 @@ void Render_Scene_2D::Private::after_advance(Object* object, Prop*, State_Access
std::unordered_set<Root*> closed_groups;
for (auto current = paint_order.rbegin(); current != paint_order.rend(); ++current)
if (current->cache_owner && closed_groups.insert(current->cache_owner).second) current->cache_group_last = true;
tf::Task previous;
Task_Node previous;
bool has_previous{};
for (std::size_t index = 0; index < paint_order.size(); ++index) {
auto& paint_node = paint_order[index];
Root* root = paint_node.object;
auto& data = *paint_node.private_data;
auto* dispatch = data.dispatch;
auto paint_if = taskflow.emplace([this, &data, dispatch, root, index] {
const auto task_prefix = std::string(dispatch->business_name) + ".paint";
auto paint_if = taskflow.add_condition(task_prefix + ".condition", [this, &data, dispatch, root, index] {
auto& state = *dispatch->state.pending(root);
state.paint_graph_rebuilt = false;
state.paint_execution_time_ns = 0;
@@ -158,7 +159,7 @@ void Render_Scene_2D::Private::after_advance(Object* object, Prop*, State_Access
state.paint_graph_rebuilt = true;
root->template mark_dirty<Paint_Tag>();
}
state.paint_task_count = data.paint_graph->num_tasks();
state.paint_task_count = data.paint_graph->size();
} else state.paint_task_count = dispatch->paint.run ? 1 : 0;
const bool dirty = root->template dirty<Paint_Tag>();
state.paint_dirty = dirty;
@@ -169,15 +170,14 @@ void Render_Scene_2D::Private::after_advance(Object* object, Prop*, State_Access
std::chrono::steady_clock::now().time_since_epoch()).count());
}
return state.paint_executed ? 0 : 1;
}).name("render_2d.paint.condition");
tf::Task paint_run;
});
Task_Node paint_run;
if (dispatch->paint.builder) {
if (!data.paint_graph) data.paint_graph = std::make_unique<tf::Taskflow>();
paint_run = taskflow.composed_of(*data.paint_graph).name("render_2d.paint.graph");
} else paint_run = taskflow.emplace([dispatch, root] { if (dispatch->paint.run) dispatch->paint.run(root); }).name("render_2d.paint.data");
auto paint_extension = taskflow.composed_of(data.paint_extension)
.name("renderable.paint.extension");
auto paint_done = taskflow.emplace([dispatch, root] {
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) {
root->template take_dirty<Paint_Tag>();
@@ -187,15 +187,16 @@ void Render_Scene_2D::Private::after_advance(Object* object, Prop*, State_Access
state.paint_execution_time_ns = finished - state.paint_execution_time_ns;
}
dispatch->state.publish(root);
}).name("render_2d.paint.complete");
paint_if.precede(paint_run, paint_done);
});
paint_if.precede(paint_run);
paint_if.precede(paint_done);
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) {
Root* cache_owner = paint_node.cache_owner;
auto composite = taskflow.emplace([this, object, cache_owner] {
auto composite = taskflow.add(task_prefix + ".cache_composite", [this, object, cache_owner] {
const auto group = std::find_if(cache_groups.begin(), cache_groups.end(), [cache_owner](const Cache_Group& value) { return value.owner == cache_owner; });
const auto owner_node = std::find_if(paint_order.begin(), paint_order.end(), [cache_owner](const Paint_Node& value) { return value.object == cache_owner; });
if (group == cache_groups.end() || owner_node == paint_order.end()) return;
@@ -204,7 +205,7 @@ void Render_Scene_2D::Private::after_advance(Object* object, Prop*, State_Access
if (!frame_target) throw std::logic_error("2D paint graph has no external frame target");
frame_target->composite(*owner_node->private_data->valid_cache);
}
}).name("render_2d.paint.cache.composite");
});
paint_done.precede(composite);
previous = composite;
}
@@ -295,7 +296,13 @@ void Render_Scene_2D::Private::process(Object* object, Callback&& callback)
Pen{.style = Line_Style::none}, Brush{state.background, Brush_Style::solid});
}
prepare_paint_targets(object, frame, state.viewport);
if (paint_taskflow && !paint_taskflow->empty()) aethera::detail::run_taskflow(*paint_taskflow);
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);
});
std::invoke(std::forward<Callback>(callback));
@@ -322,6 +329,14 @@ Render_Scene_2D::Private::render(Object* object, Frame_2D* frame) {
}
} admission{*this};
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{}; /* 嵌套调用前的帧;析构时恢复。 */
@@ -333,7 +348,17 @@ Render_Scene_2D::Private::render(Object* object, Frame_2D* frame) {
completed = true;
frame->mark(Frame_Trace_Marker::scene_render_finished);
/* 帧准入直到 completion 图和最终回调都结束才释放,因此图运行期间外部帧不会被下一帧复用。 */
if (!completion_graph.empty()) aethera::detail::run_taskflow(completion_graph);
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) {
aethera::detail::finish_taskflow_trace(*frame);
trace_scope.active = false;
}
frame->mark(Frame_Trace_Marker::callback_started);
callback(frame);
frame->mark(Frame_Trace_Marker::callback_finished);
+4 -4
View File
@@ -95,12 +95,12 @@ TEST(axis_render, scene_prepares_and_paints_uncached_axis_into_frame) {
}).has_value()));
int paint_extension_calls{};
int completion_calls{};
axis->paint_taskflow().emplace(
[&] { ++paint_extension_calls; }).name("test.axis.paint.extension");
scene->completion_taskflow().emplace([&] {
axis->paint_taskflow().add(
"test.axis.paint.extension", [&] { ++paint_extension_calls; });
scene->completion_taskflow().add("test.scene_2d.completion", [&] {
EXPECT_EQ(paint_extension_calls, 1);
++completion_calls;
}).name("test.scene_2d.completion");
});
const auto frame = render_frame(scene.get());
EXPECT_EQ(paint_extension_calls, 1);
EXPECT_EQ(completion_calls, 1);