优化了 还是卡

This commit is contained in:
2026-08-25 15:03:33 +08:00
parent 2c3105cc07
commit fa93927d81
23 changed files with 1517 additions and 619 deletions
@@ -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。
+188 -99
View File
@@ -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,