From 93d3f937741989c27a9b839f5701bfd4f8b49125 Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Fri, 28 Aug 2026 14:10:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=80=E6=9C=89=E6=9D=83=E6=8C=87=E9=92=88?= =?UTF-8?q?=E6=98=8E=E7=A1=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 2 + Project_detail_specification.md | 45 +++++-- kernel/src/kernel/Task_Graph.cpp | 119 ++++++++---------- kernel/src/kernel/Task_Graph_Internal.hpp | 2 +- kernel/src/kernel/double_buffer/model.hpp | 3 +- kernel/src/kernel/event.hpp | 2 +- kernel/src/kernel/event.ipp | 2 +- kernel/src/kernel/render_common.cpp | 33 ++--- kernel/src/kernel/scene.ipp | 14 +-- kernel/src/test/Dependency_Graph_Test.cpp | 8 +- kernel/src/test/model_test.cpp | 4 +- mcp/core/runtime/Gallery_Plots.cpp | 6 +- mcp/core/runtime/Gallery_Plots.hpp | 2 +- mcp/core/runtime/Gallery_Plots_3D.cpp | 14 +-- mcp/core/runtime/Plot.cpp | 36 +++--- mcp/tests/Control_Path_Benchmarks.cpp | 27 ++++ render_2D/render_2D/base/Renderable_2D.ipp | 6 +- render_2D/render_2D/plottable/Afterglow.ipp | 20 +-- .../plottable/Constellation_Diagram.ipp | 24 ++-- .../render_2D/plottable/Frequency_Trace.ipp | 28 ++--- .../plottable/Selection_Rectangle_Overlay.ipp | 4 +- render_2D/render_2D/plottable/Spectrum.ipp | 28 ++--- .../render_2D/plottable/Sweep_Spectrum.ipp | 20 +-- render_2D/render_2D/plottable/Waterfall.ipp | 30 ++--- render_2D/render_2D/render/Blend2D_Cache.cpp | 6 +- render_2D/render_2D/render/Blend2D_Cache.hpp | 2 +- render_2D/render_2D/scene/Render_Scene_2D.ipp | 58 ++++----- .../detail/Datoviz_Visual_Backend.cpp | 54 ++++---- .../detail/Datoviz_Visual_Backend.ipp | 14 +-- .../detail/Gpu_Completion_Service.cpp | 32 ++--- .../detail/Gpu_Completion_Service.ipp | 8 +- render_3D/render_3D/detail/Render_Domain.cpp | 2 +- render_3D/render_3D/detail/Render_Domain.hpp | 2 +- render_3D/render_3D/scene/Render_Scene_3D.hpp | 4 +- render_3D/render_3D/scene/Render_Scene_3D.ipp | 118 ++++++++--------- web_server/src/Gallery_Video_Stream.cpp | 4 +- web_server/src/Gallery_Video_Stream.ipp | 2 +- web_server/tests/Gallery_Plots_Tests.cpp | 2 +- 38 files changed, 407 insertions(+), 380 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 3e24619..b71b642 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,6 +21,8 @@ 除非特别说明不允许写任何 快照, 任何线程问题先完了都要给我说明 我来审核。 `` +每次写指针需要明确所有权 T* 表示可空、非拥有借用;not_null 表示必填、非拥有借用;owner才表示裸指针所有权。 + 我使用 里的这个环境 D:\ae\proj\Aethera\CMakePresets.json "toolchain/vs2022.json" ====================[ 构建 | Aethera_Kernel_check | vs2022_debug ]================ "C:\Program Files\JetBrains\CLion 2026.1\bin\cmake\win\x64\bin\cmake.exe" --build D: diff --git a/Project_detail_specification.md b/Project_detail_specification.md index 8d88343..b1797e8 100644 --- a/Project_detail_specification.md +++ b/Project_detail_specification.md @@ -2,30 +2,49 @@ ## 代码组织 -* `.hpp` 只放业务契约和必要前置声明,不允许出现函数体;构造函数、薄壳函数及模板函数也只能声明。模板定义统一放对应 `.ipp`,非模板定义放 `.cpp` 或仅供头文件实例化的 `.ipp`。 +* `.hpp` 只放业务契约和必要前置声明,不允许出现函数体;构造函数、薄壳函数及模板函数也只能声明。模板定义统一放对应 `.ipp` + ,非模板定义放 `.cpp` 或仅供头文件实例化的 `.ipp`。 * 嵌套 `Private` 在 `.hpp` 只写 `struct Private;`,完整定义、内部字段和 CRTP 定制点放在对应 `.ipp`。 * 派生 `Private` 必须继承 `Prev_Private`。可覆盖或必需的 CRTP 能力必须在 `Private` 定义处写清用途、参数、返回值、默认行为、调用时机和编译期选择优先级。 -* 状态处理、算法和内部协作都实现在 `Private`;只有外部消费者需要的业务能力才在原始类声明薄壳,并通过 `d` 调用最终 `Private`。 +* 状态处理、算法和内部协作都实现在 `Private`;只有外部消费者需要的业务能力才在原始类声明薄壳,并通过 `d` 调用最终 + `Private`。 * 替换实现时删除旧实现,不保留重复定义、兼容别名或转发层。 ## 状态与接口 * 每个状态只能有一个权威来源。双缓冲交换后的当前结构就是稳定读面,跨对象直接读取该结构;禁止为无锁访问再复制一份快照、View、镜像字段或同步缓存。 * `Prop` 保存外部可读写的业务属性,`State` 保存实现向外发布的运行结果,`Private` 保存实现细节;三者不得互相复制并手工同步。 -* 每个 `Def` 定义层自动以自身类型生成 `Base_Tag`;Prop、State、Private 分别在隔离的标签空间中复用该标签,禁止再声明 `XXX_Prop_Tag`、`XXX_State_Tag` 或 `XXX_Private_Tag`。 +* 每个 `Def` 定义层自动以自身类型生成 `Base_Tag`;Prop、State、Private 分别在隔离的标签空间中复用该标签,禁止再声明 + `XXX_Prop_Tag`、`XXX_State_Tag` 或 `XXX_Private_Tag`。 * 依赖可以选择 Prop/State 的单字段或整个 `Base_Tag` 层;字段写入必须同时发出字段级和所属层级变更,使用方按实际重建粒度选择一种依赖。 -* 整体对象依赖只表达依赖图中的拓扑顺序,不传播 dirty;准备顺序、绘图顺序等业务含义由各自 Tag 解释。具体字段依赖才用于对应 Tag 的 dirty 传播,例如绘图缓存失效。 -* Kernel `Scene` 只负责 2D/3D 共有的 Prepare 数据阶段;Paint、缓存失效、像素合成和异步后端提交由对应渲染模块自己的 Scene、Tag 与 Taskflow 负责。 -* `Scene::Private` 是输入事件流的唯一所有者:外部转移事件对象所有权,并发提交到 Def 注册的 MPMC 三缓冲;收集、内部渲染、外部查询分别占用一份队列,Prepare 入口只在交换锁下轮换三者并清空过期查询队列。事件不得独立触发帧。2D 按 Renderable 区域与 Paint 顺序形成接受链,区域默认整个 viewport;3D 无等待提交渲染域,满载时保留当前事件供下一次 Prepare 重试。 -* 帧由 Scene 外部创建和持有;每次 `render(frame*)` 只借用该帧并在完成回调返回同一地址。Scene、异步后端和 Web 层只向帧写入固定语义的单调时间点与原始耗时,不保存平均值、分位数或波动等衍生统计。 -* Plot 使用服务端帧时钟调用 `Scene::render(frame*)`;`fixed_rate` 只由 Frame_Scheduler 周期时钟驱动,完成回调不得改变其 deadline。`maximum_rate` 是唯一允许完成回调在释放 Plot 准入后异步投递下一帧请求的特殊模式;回调不得直接重入 `render()`、不得同步等待,并且物理帧槽耗尽时只能由帧退役事件解除背压。系统不存在浏览器逐帧 request/ack。Scene 回调返回完成帧后,Web 层在 Render Domain 之外把 2D 原生 BGRA 或 3D 原生 RGBA 编码为 H.264,经 LibDataChannel WebRTC 视频轨直接发布;WebSocket 只承载 SDP/ICE、输入事件和诊断 JSON。多个订阅者共享同一编码结果,关闭视频时 3D 必须使用 diagnostics 输出并跳过 GPU 像素回读。 -* 帧策略及其时钟实现统一归属 `kernel/src/kernel/Frame_Policy`。配置变更和运行事实通过 MPMC 事件流进入唯一 consumer,由 `double_buffer/model.hpp` 发布 `Frame_Policy::State`;查询只读取已发布 State。手动帧捕获必须把帧创建边界的策略 State 绑定到该帧并沿现有帧捕获协议返回,禁止另建策略历史、轮询累加器或重复诊断字段。 -* 相机是 3D Scene 组件,只允许定义在 `render_3D/camera`;Kernel 和 2D 不得依赖相机类型。Web 层只为已有 `Camera_3D` 增加协议描述,不复制相机配置。Gallery 服务同一时刻只允许一个页面实例持有;该页面的所有 Plot 连接共享页面令牌,其他标签页或浏览器实例必须被拒绝。 -* `Time_Axis` 是时间与 tick 的唯一权威来源;使用层先推进时间轴,再把同一 tick 分发给所有相关数据图元。时间窗口从第一条数据起始终锚定最新 tick,未产生数据的槽位保持背景。 +* 整体对象依赖只表达依赖图中的拓扑顺序,不传播 dirty;准备顺序、绘图顺序等业务含义由各自 Tag 解释。具体字段依赖才用于对应 + Tag 的 dirty 传播,例如绘图缓存失效。 +* Kernel `Scene` 只负责 2D/3D 共有的 Prepare 数据阶段;Paint、缓存失效、像素合成和异步后端提交由对应渲染模块自己的 + Scene、Tag 与 Taskflow 负责。 +* `Scene::Private` 是输入事件流的唯一所有者:外部转移事件对象所有权,并发提交到 Def 注册的 MPMC + 三缓冲;收集、内部渲染、外部查询分别占用一份队列,Prepare 入口只在交换锁下轮换三者并清空过期查询队列。事件不得独立触发帧。2D + 按 Renderable 区域与 Paint 顺序形成接受链,区域默认整个 viewport;3D 无等待提交渲染域,满载时保留当前事件供下一次 Prepare + 重试。 +* 帧由 Scene 外部创建和持有;每次 `render(frame*)` 只借用该帧并在完成回调返回同一地址。Scene、异步后端和 Web + 层只向帧写入固定语义的单调时间点与原始耗时,不保存平均值、分位数或波动等衍生统计。 +* Plot 使用服务端帧时钟调用 `Scene::render(frame*)`;`fixed_rate` 只由 Frame_Scheduler 周期时钟驱动,完成回调不得改变其 + deadline。`maximum_rate` 是唯一允许完成回调在释放 Plot 准入后异步投递下一帧请求的特殊模式;回调不得直接重入 `render()` + 、不得同步等待,并且物理帧槽耗尽时只能由帧退役事件解除背压。系统不存在浏览器逐帧 request/ack。Scene 回调返回完成帧后,Web + 层在 Render Domain 之外把 2D 原生 BGRA 或 3D 原生 RGBA 编码为 H.264,经 LibDataChannel WebRTC 视频轨直接发布;WebSocket + 只承载 SDP/ICE、输入事件和诊断 JSON。多个订阅者共享同一编码结果,关闭视频时 3D 必须使用 diagnostics 输出并跳过 GPU 像素回读。 +* 帧策略及其时钟实现统一归属 `kernel/src/kernel/Frame_Policy`。配置变更和运行事实通过 MPMC 事件流进入唯一 consumer,由 + `double_buffer/model.hpp` 发布 `Frame_Policy::State`;查询只读取已发布 State。手动帧捕获必须把帧创建边界的策略 State + 绑定到该帧并沿现有帧捕获协议返回,禁止另建策略历史、轮询累加器或重复诊断字段。 +* 相机是 3D Scene 组件,只允许定义在 `render_3D/camera`;Kernel 和 2D 不得依赖相机类型。Web 层只为已有 `Camera_3D` + 增加协议描述,不复制相机配置。Gallery 服务同一时刻只允许一个页面实例持有;该页面的所有 Plot 连接共享页面令牌,其他标签页或浏览器实例必须被拒绝。 +* `Time_Axis` 是时间与 tick 的唯一权威来源;使用层先推进时间轴,再把同一 tick 分发给所有相关数据图元。时间窗口从第一条数据起始终锚定最新 + tick,未产生数据的槽位保持背景。 * 图表选区保存两根轴上的数据范围,绘制时才映射为像素;选区作为独立 Renderable 在使用层与图元组合,禁止在各图元内复制选区状态。 -* `Root` 只保存一个最终 `Private` 指针;`Builder::build()` 校验成功后创建并挂接完整 Private,`Root` 通过公共 Private 基类的虚析构统一释放。禁止直接公开该指针。 +* `Root` 只保存一个最终 `Private` 指针;`Builder::build()` 校验成功后创建并挂接完整 Private,`Root` 通过公共 Private + 基类的虚析构统一释放。禁止直接公开该指针。 * 能从权威结构查询或计算的数据即时获取,不保存为成员。类只保存自身职责需要且无法推导的状态,并检查每个新增成员的读写者和生命周期。 -* 公共接口只表达业务语义,不暴露 `Private`、内部指针、线程状态或缓冲区角色;接口保持正交,不增加空配置、未完成接口、无消费者统计或只做转发的 getter/setter。 +* 公共接口只表达业务语义,不暴露 `Private`、内部指针、线程状态或缓冲区角色;接口保持正交,不增加空配置、未完成接口、无消费者统计或只做转发的 + getter/setter。 ## 注释与修改 diff --git a/kernel/src/kernel/Task_Graph.cpp b/kernel/src/kernel/Task_Graph.cpp index 644260f..7619291 100644 --- a/kernel/src/kernel/Task_Graph.cpp +++ b/kernel/src/kernel/Task_Graph.cpp @@ -6,30 +6,29 @@ #include #include #include - namespace aethera { struct Task_Graph::Private { struct Node { - tf::Task task{}; /* 原生节点句柄;只在静态构图期间使用。 */ - std::string name{}; /* 调用方提供的业务节点名。 */ - std::string node_id{}; /* 本图内去重后的业务节点 ID。 */ - std::shared_ptr child{}; /* 模块引用的子图;普通节点为空。 */ + tf::Task task{}; /* 原生节点句柄;只在静态构图期间使用。 */ + std::string name{}; /* 调用方提供的业务节点名。 */ + std::string node_id{}; /* 本图内去重后的业务节点 ID。 */ + std::shared_ptr child{}; /* 模块引用的子图;普通节点为空。 */ std::vector> attributes{}; }; - explicit Private(std::string graph_name) - : taskflow(std::move(graph_name)) {} + explicit Private(std::string graph_name) : taskflow(std::move(graph_name)) {} std::vector> attributes{}; - tf::Taskflow taskflow{}; /* 第三方 Taskflow 仅存在于本实现单元。 */ - std::vector nodes{}; /* 与原生 graph 插入顺序一致的业务节点。 */ + tf::Taskflow taskflow{}; /* 第三方 Taskflow 仅存在于本实现单元。 */ + std::vector nodes{}; /* 与原生 graph 插入顺序一致的业务节点。 */ std::unordered_map name_counts{}; /* 同名业务节点的下一个序号。 */ - std::uint64_t generation{1}; /* clear/重建后使旧 Task_Node 句柄失效。 */ - + std::uint64_t generation{1}; /* clear/重建后使旧 Task_Node 句柄失效。 */ [[nodiscard]] std::string unique_node_id(const std::string& name) { const std::string base = taskflow.name().empty() - ? name : taskflow.name() + "/" + name; + ? name + : taskflow.name() + "/" + name; const auto occurrence = name_counts[name]++; - return occurrence == 0 ? base - : base + "#" + std::to_string(occurrence); + return occurrence == 0 + ? base + : base + "#" + std::to_string(occurrence); } void collect_nodes(std::string_view parent, const std::vector>& inherited, @@ -51,8 +50,9 @@ struct Task_Graph::Private { local_id.size() > graph_name.size() && local_id[graph_name.size()] == '/') local_id.remove_prefix(graph_name.size() + 1); - node.node_id = parent.empty() ? source.node_id - : std::string(parent) + "/" + std::string(local_id); + node.node_id = parent.empty() + ? source.node_id + : std::string(parent) + "/" + std::string(local_id); node.parent_node_id = std::string(parent); node.name = source.name; node.type = std::string(tf::to_string(source.task.type())); @@ -74,53 +74,43 @@ struct Task_Graph::Private { }); const auto module_id = node.node_id; result.push_back(std::move(node)); - if (source.child) - source.child->collect_nodes(module_id, node.attributes, result); + if (source.child) source.child->collect_nodes(module_id, node.attributes, result); } } - - void bind_frame( - std::optional> frame) noexcept { + void bind_frame(Render_Frame* frame) noexcept { for (auto& node : nodes) { - node.task.data(frame ? frame->get() : nullptr); + node.task.data(frame); if (node.child) node.child->bind_frame(frame); } } }; - struct Task_Node::Private { - std::shared_ptr graph{}; /* 节点所属 DAG 及其静态生命周期。 */ - std::size_t index{}; /* DAG 内节点槽位;只在实现单元解释。 */ - std::uint64_t generation{}; /* 防止 clear 后的旧句柄指向复用槽位。 */ + std::shared_ptr graph{}; /* 节点所属 DAG 及其静态生命周期。 */ + std::size_t index{}; /* DAG 内节点槽位;只在实现单元解释。 */ + std::uint64_t generation{}; /* 防止 clear 后的旧句柄指向复用槽位。 */ }; - namespace detail { void* Task_Graph_Access::native_storage(Task_Graph& graph) noexcept { return std::addressof(graph.d->taskflow); } - std::vector Task_Graph_Access::nodes( const Task_Graph& graph) { std::vector result; graph.d->collect_nodes({}, {}, result); return result; } - void Task_Graph_Access::bind_frame(Task_Graph& graph, - std::optional> frame) noexcept { + Render_Frame* frame) noexcept { graph.d->bind_frame(frame); } } - Task_Node::Task_Node() = default; Task_Node::~Task_Node() = default; Task_Node::Task_Node(const Task_Node&) = default; Task_Node::Task_Node(Task_Node&&) noexcept = default; Task_Node& Task_Node::operator=(const Task_Node&) = default; Task_Node& Task_Node::operator=(Task_Node&&) noexcept = default; -Task_Node::Task_Node(std::shared_ptr private_data) - : d(std::move(private_data)) {} - +Task_Node::Task_Node(std::shared_ptr private_data) : d(std::move(private_data)) {} void Task_Node::precede(const Task_Node& after) const { if (!d || !after.d || !d->graph || d->graph != after.d->graph || d->generation != d->graph->generation || @@ -132,25 +122,19 @@ void Task_Node::precede(const Task_Node& after) const { d->graph->nodes[d->index].task.precede( d->graph->nodes[after.d->index].task); } - Task_Node& Task_Node::describe(std::string key, std::string value) { if (!d || !d->graph || d->generation != d->graph->generation || d->index >= d->graph->nodes.size()) throw std::invalid_argument("Task graph node handle is stale"); - if (key.empty()) - throw std::invalid_argument("Task graph node attribute key is empty"); + if (key.empty()) throw std::invalid_argument("Task graph node attribute key is empty"); auto& attributes = d->graph->nodes[d->index].attributes; const auto found = std::ranges::find( attributes, key, &std::pair::first); - if (found == attributes.end()) - attributes.emplace_back(std::move(key), std::move(value)); - else - found->second = std::move(value); + if (found == attributes.end()) attributes.emplace_back(std::move(key), std::move(value)); + else found->second = std::move(value); return *this; } - -Task_Graph::Task_Graph(std::string graph_name) - : d(std::make_shared(std::move(graph_name))) {} +Task_Graph::Task_Graph(std::string graph_name) : d(std::make_shared(std::move(graph_name))) {} Task_Graph::~Task_Graph() = default; Task_Graph::Task_Graph(Task_Graph&&) noexcept = default; Task_Graph& Task_Graph::operator=(Task_Graph&& other) noexcept { @@ -170,13 +154,14 @@ Task_Graph& Task_Graph::operator=(Task_Graph&& other) noexcept { ++d->generation; return *this; } - Task_Node Task_Graph::add(std::string task_name, std::function work) { if (!work) throw std::invalid_argument("Task graph work is empty"); auto task = d->taskflow.emplace( [name = task_name, work = std::move(work)] { - try { work(); } + try { + work(); + } catch (const std::exception& error) { std::fprintf(stderr, "Taskflow node %s failed: %s\n", name.c_str(), error.what()); @@ -193,16 +178,19 @@ Task_Node Task_Graph::add(std::string task_name, detail::register_taskflow_node( static_cast(task.hash_value()), node_id); d->nodes.push_back({task, std::move(task_name), node_id, nullptr}); - return Task_Node{std::make_shared( - Task_Node::Private{d, d->nodes.size() - 1, d->generation})}; + return Task_Node{ + std::make_shared( + Task_Node::Private{d, d->nodes.size() - 1, d->generation}) + }; } - Task_Node Task_Graph::add_condition(std::string task_name, std::function work) { if (!work) throw std::invalid_argument("Task graph condition is empty"); auto task = d->taskflow.emplace( [name = task_name, work = std::move(work)] { - try { return work(); } + try { + return work(); + } catch (const std::exception& error) { std::fprintf(stderr, "Taskflow condition %s failed: %s\n", name.c_str(), error.what()); @@ -219,10 +207,11 @@ Task_Node Task_Graph::add_condition(std::string task_name, detail::register_taskflow_node( static_cast(task.hash_value()), node_id); d->nodes.push_back({task, std::move(task_name), node_id, nullptr}); - return Task_Node{std::make_shared( - Task_Node::Private{d, d->nodes.size() - 1, d->generation})}; + return Task_Node{ + std::make_shared( + Task_Node::Private{d, d->nodes.size() - 1, d->generation}) + }; } - Task_Node Task_Graph::compose(std::string task_name, Task_Graph& child) { auto task = d->taskflow.composed_of(child.d->taskflow); const auto node_id = d->unique_node_id(task_name); @@ -230,29 +219,28 @@ Task_Node Task_Graph::compose(std::string task_name, Task_Graph& child) { detail::register_taskflow_node( static_cast(task.hash_value()), node_id); d->nodes.push_back({task, std::move(task_name), node_id, child.d}); - return Task_Node{std::make_shared( - Task_Node::Private{d, d->nodes.size() - 1, d->generation})}; + return Task_Node{ + std::make_shared( + Task_Node::Private{d, d->nodes.size() - 1, d->generation}) + }; } - Task_Graph& Task_Graph::describe(std::string key, std::string value) { - if (key.empty()) - throw std::invalid_argument("Task graph attribute key is empty"); + if (key.empty()) throw std::invalid_argument("Task graph attribute key is empty"); const auto found = std::ranges::find( d->attributes, key, &std::pair::first); - if (found == d->attributes.end()) - d->attributes.emplace_back(std::move(key), std::move(value)); - else - found->second = std::move(value); + if (found == d->attributes.end()) d->attributes.emplace_back(std::move(key), std::move(value)); + else found->second = std::move(value); return *this; } - void Task_Graph::clear() { d->taskflow.clear(); d->nodes.clear(); d->name_counts.clear(); ++d->generation; } -bool Task_Graph::empty() const noexcept { return d->taskflow.empty(); } +bool Task_Graph::empty() const noexcept { + return d->taskflow.empty(); +} std::size_t Task_Graph::size() const noexcept { return d->taskflow.num_tasks(); } @@ -260,8 +248,7 @@ const std::string& Task_Graph::name() const noexcept { return d->taskflow.name(); } void Task_Graph::corun_until(std::function predicate) { - if (!predicate) - throw std::invalid_argument("Task graph cooperative predicate is empty"); + if (!predicate) throw std::invalid_argument("Task graph cooperative predicate is empty"); detail::corun_taskflow_until(std::move(predicate)); } } diff --git a/kernel/src/kernel/Task_Graph_Internal.hpp b/kernel/src/kernel/Task_Graph_Internal.hpp index c1afaca..674ebfe 100644 --- a/kernel/src/kernel/Task_Graph_Internal.hpp +++ b/kernel/src/kernel/Task_Graph_Internal.hpp @@ -16,7 +16,7 @@ struct Task_Graph_Access { const Task_Graph& graph); static void bind_frame( Task_Graph& graph, - std::optional> frame) noexcept; + Render_Frame* frame) noexcept; }; /* Task_Graph.cpp 到全局 Kernel Executor 的内部桥;不得在模块公共头文件中声明。 */ void corun_taskflow_until(std::function predicate); diff --git a/kernel/src/kernel/double_buffer/model.hpp b/kernel/src/kernel/double_buffer/model.hpp index cc98bf1..1672fcc 100644 --- a/kernel/src/kernel/double_buffer/model.hpp +++ b/kernel/src/kernel/double_buffer/model.hpp @@ -568,7 +568,8 @@ public: template Callback> void process(Callback&& callback) { advance(); - static_cast(*this).process(object, std::forward(callback)); + static_cast(*this).process( + object.get(), std::forward(callback)); } void advance() { before_advance(); diff --git a/kernel/src/kernel/event.hpp b/kernel/src/kernel/event.hpp index bd5127a..da74697 100644 --- a/kernel/src/kernel/event.hpp +++ b/kernel/src/kernel/event.hpp @@ -75,7 +75,7 @@ private: void observe_with( not_null accumulator) noexcept; std::uint64_t created_steady_ns_{}; /* 事件对象进入 Scene 前的内核单调时刻。 */ - std::optional> statistics_{}; /* 所属 Scene 的统计器;不拥有且不得跨 Scene。 */ + Event_Statistics_Accumulator* statistics_{}; /* 所属 Scene 的统计器;不拥有且不得跨 Scene。 */ mutable bool accepted{}; /* 处理链是否已经消费事件。 */ mutable std::atomic_uint64_t dispatch_frame_sequence_{}; mutable std::atomic_uint64_t dispatch_started_steady_ns_{}; diff --git a/kernel/src/kernel/event.ipp b/kernel/src/kernel/event.ipp index 3e8b870..5f57a02 100644 --- a/kernel/src/kernel/event.ipp +++ b/kernel/src/kernel/event.ipp @@ -24,7 +24,7 @@ inline void Event::mark_dispatch_completed() const noexcept { const auto completed = detail::event_steady_time_ns(); dispatch_completed_steady_ns_.store(completed, std::memory_order_release); if (statistics_) - (*statistics_)->submit( + statistics_->submit( type, created_steady_ns_, dispatch_started_steady_ns_.load(std::memory_order_acquire), completed); diff --git a/kernel/src/kernel/render_common.cpp b/kernel/src/kernel/render_common.cpp index bc4f23f..95d6702 100644 --- a/kernel/src/kernel/render_common.cpp +++ b/kernel/src/kernel/render_common.cpp @@ -36,7 +36,7 @@ tf::Taskflow& native_taskflow(Task_Graph& graph) noexcept { return *static_cast( detail::Task_Graph_Access::native_storage(graph)); } -std::optional> task_frame( +Render_Frame* task_frame( const tf::TaskView& task) noexcept { /* * Taskflow v4.1.0 ABI: TaskView 的 offset 0 是唯一的 const Node&,Task 的 @@ -51,9 +51,7 @@ std::optional> task_frame( tf::Task handle{}; *reinterpret_cast(std::addressof(handle)) = node; auto* frame = static_cast(handle.data()); - return frame - ? std::optional>{not_null{frame}} - : std::nullopt; + return frame; } #if defined(_WIN32) std::uint64_t thread_cpu_ns(HANDLE thread) noexcept { @@ -123,7 +121,7 @@ private: std::uint64_t maximum_segment_ns{}; /* 已结束连续独占片段的最大墙钟。 */ Clock::time_point cooperative_wait_started{}; /* 主动 corun 让出 Worker 的墙钟起点。 */ std::uint64_t cooperative_wait_ns{}; /* 已累计 cooperative wait 墙钟。 */ - std::optional> frame{}; /* 进入任务时唯一活动的按帧捕获。 */ + Render_Frame* frame{}; /* 进入任务时唯一活动的按帧捕获。 */ std::size_t queue_size{}; /* 进入任务时 worker 队列深度。 */ std::size_t queue_capacity{}; /* 进入任务时 worker 队列容量。 */ std::uint64_t native_id{}; /* 嵌套 corun 返回外层任务时恢复其原生身份。 */ @@ -434,7 +432,7 @@ public: if (start.frame) { try { trace_task = detail::Taskflow_Frame_Access::append_task( - **start.frame, worker.id(), + *start.frame, worker.id(), static_cast(task.hash_value()), start.queue_size, start.queue_capacity, start.entered, start.started, finished, start.cooperative_wait_ns); @@ -447,7 +445,7 @@ public: if (start.frame) { if (trace_task) detail::Taskflow_Frame_Access::finish_task_observer( - **start.frame, worker.id(), *trace_task, completed); + *start.frame, worker.id(), *trace_task, completed); } if (worker_starts.empty()) { auto busy = static_cast( @@ -802,12 +800,12 @@ public: failed_taskflows.store(0, std::memory_order_relaxed); } std::uint64_t run(Task_Graph& taskflow) { - detail::Task_Graph_Access::bind_frame(taskflow, std::nullopt); + detail::Task_Graph_Access::bind_frame(taskflow, nullptr); return run_synchronous(taskflow, nullptr); } void run(Task_Graph& taskflow, std::function completion) { if (!completion) throw std::invalid_argument("Taskflow completion is empty"); - detail::Task_Graph_Access::bind_frame(taskflow, std::nullopt); + detail::Task_Graph_Access::bind_frame(taskflow, nullptr); run_asynchronous(taskflow, false, [completion = std::move(completion)]( detail::Taskflow_Graph_Timing) mutable { completion(); @@ -824,19 +822,17 @@ public: * 3. 多 worker 会并发附加 trace;Frame 必须按 worker 隔离单写者,不能共享写同一容器。 */ detail::Task_Graph_Access::bind_frame( - taskflow, frame.taskflow_trace_requested() - ? std::optional>{not_null{&frame}} - : std::nullopt); + taskflow, frame.taskflow_trace_requested() ? &frame : nullptr); detail::Taskflow_Graph_Timing timing{}; try { const auto elapsed = run_synchronous(taskflow, &timing); - detail::Task_Graph_Access::bind_frame(taskflow, std::nullopt); + detail::Task_Graph_Access::bind_frame(taskflow, nullptr); timing.observer_unbind_finished = std::chrono::steady_clock::now(); detail::Taskflow_Frame_Access::finish_graph(token, timing); return elapsed; } catch (...) { - detail::Task_Graph_Access::bind_frame(taskflow, std::nullopt); + detail::Task_Graph_Access::bind_frame(taskflow, nullptr); timing.observer_unbind_finished = std::chrono::steady_clock::now(); detail::Taskflow_Frame_Access::finish_graph(token, timing); throw; @@ -849,14 +845,11 @@ public: frame, taskflow, stage); try { detail::Task_Graph_Access::bind_frame( - taskflow, frame.taskflow_trace_requested() - ? std::optional>{ - not_null{&frame}} - : std::nullopt); + taskflow, frame.taskflow_trace_requested() ? &frame : nullptr); run_asynchronous(taskflow, true, [this, &taskflow, &frame, token, completion = std::move(completion)]( detail::Taskflow_Graph_Timing timing) mutable { - detail::Task_Graph_Access::bind_frame(taskflow, std::nullopt); + detail::Task_Graph_Access::bind_frame(taskflow, nullptr); timing.observer_unbind_finished = std::chrono::steady_clock::now(); detail::Taskflow_Frame_Access::finish_graph(token, timing); completion(); @@ -864,7 +857,7 @@ public: } catch (...) { detail::Taskflow_Graph_Timing timing{}; - detail::Task_Graph_Access::bind_frame(taskflow, std::nullopt); + detail::Task_Graph_Access::bind_frame(taskflow, nullptr); timing.observer_unbind_finished = std::chrono::steady_clock::now(); detail::Taskflow_Frame_Access::finish_graph(token, timing); throw; diff --git a/kernel/src/kernel/scene.ipp b/kernel/src/kernel/scene.ipp index 9a1d1ea..0ae6bdb 100644 --- a/kernel/src/kernel/scene.ipp +++ b/kernel/src/kernel/scene.ipp @@ -31,7 +31,7 @@ struct Scene::Private : Prev_Private { template void process( Object* object, - std::optional> frame, + Render_Frame* frame, Callback&& callback) requires std::invocable; /* 派生 Scene 的 Private 还可覆盖 Def::Private 的四个 State 生命周期 hook,并通过 State_Access::get() 访问状态层。 */ @@ -64,22 +64,22 @@ std::shared_ptr Scene::make_event(Arguments&&... arguments) { } template void Scene::Private::process(Object* object, Callback&& callback) requires std::invocable { - process(object, std::nullopt, std::forward(callback)); + process(object, nullptr, std::forward(callback)); } template void Scene::Private::process( Object* object, - std::optional> frame, + Render_Frame* frame, Callback&& callback) requires std::invocable { - if (frame) (*frame)->mark(Frame_Trace_Marker::prepare_started); + if (frame) frame->mark(Frame_Trace_Marker::prepare_started); if (runtime->taskflow && !runtime->taskflow->empty()) - if (frame && (*frame)->taskflow_trace_requested()) + if (frame && frame->taskflow_trace_requested()) (void)detail::run_taskflow( - *runtime->taskflow, **frame, "scene.renderables"); + *runtime->taskflow, *frame, "scene.renderables"); else (void)detail::run_taskflow(*runtime->taskflow); - if (frame) (*frame)->mark(Frame_Trace_Marker::prepare_finished); + if (frame) frame->mark(Frame_Trace_Marker::prepare_finished); double_buffer::detail::Internal_Access::current_dependency_graph(object).for_each_bound( [](Renderable* renderable, Renderable::Private& data) { data.publish_renderable_state(renderable); diff --git a/kernel/src/test/Dependency_Graph_Test.cpp b/kernel/src/test/Dependency_Graph_Test.cpp index c5226c6..eceaf91 100644 --- a/kernel/src/test/Dependency_Graph_Test.cpp +++ b/kernel/src/test/Dependency_Graph_Test.cpp @@ -49,8 +49,8 @@ TEST(dependency_graph_storage, edited_graph_commits_and_stays_synchronized) { ); ASSERT_TRUE(result.has_value()); auto& buffer = graph->data_for_test().dependency_graph_storage.get(); - auto* pending_before = buffer.pending; - auto* current_before = buffer.current; + auto pending_before = buffer.pending; + auto current_before = buffer.current; graph->advance(); EXPECT_EQ(buffer.current, pending_before); EXPECT_EQ(buffer.pending, current_before); @@ -65,8 +65,8 @@ TEST(dependency_graph_storage, unchanged_graph_does_not_advance_again) { ASSERT_TRUE(graph->edit_dependency_graph([&](auto& editor) { editor.add(node.get()); }).has_value()); graph->advance(); auto& buffer = graph->data_for_test().dependency_graph_storage.get(); - auto* pending = buffer.pending; - auto* current = buffer.current; + auto pending = buffer.pending; + auto current = buffer.current; graph->advance(); EXPECT_EQ(buffer.pending, pending); EXPECT_EQ(buffer.current, current); diff --git a/kernel/src/test/model_test.cpp b/kernel/src/test/model_test.cpp index 357265d..72832d8 100644 --- a/kernel/src/test/model_test.cpp +++ b/kernel/src/test/model_test.cpp @@ -11,8 +11,8 @@ TEST(double_buffer, advance_only_swaps_sides) { double_buffer::Double_Buffer buffer; buffer.pending->first = 1; buffer.current->first = 2; - auto* pending = buffer.pending; - auto* current = buffer.current; + auto pending = buffer.pending; + auto current = buffer.current; buffer.advance(); EXPECT_EQ(buffer.pending, current); EXPECT_EQ(buffer.current, pending); diff --git a/mcp/core/runtime/Gallery_Plots.cpp b/mcp/core/runtime/Gallery_Plots.cpp index 53548b8..0ab14fb 100644 --- a/mcp/core/runtime/Gallery_Plots.cpp +++ b/mcp/core/runtime/Gallery_Plots.cpp @@ -36,12 +36,12 @@ std::span gallery_plot_definitions() noexcept { return definitions; } -std::optional> +const Plot_Definition* find_gallery_plot_definition(std::string_view id) noexcept { const auto found = std::ranges::find(definitions, id, &Plot_Definition::id); - if (found == definitions.end()) return std::nullopt; - return not_null{&*found}; + if (found == definitions.end()) return nullptr; + return &*found; } std::string_view plot_dimension_name(Plot_Dimension dimension) noexcept { diff --git a/mcp/core/runtime/Gallery_Plots.hpp b/mcp/core/runtime/Gallery_Plots.hpp index aacb87a..273832a 100644 --- a/mcp/core/runtime/Gallery_Plots.hpp +++ b/mcp/core/runtime/Gallery_Plots.hpp @@ -20,7 +20,7 @@ struct Plot_Definition { [[nodiscard]] std::span gallery_plot_definitions() noexcept; -[[nodiscard]] std::optional> +[[nodiscard]] const Plot_Definition* find_gallery_plot_definition(std::string_view id) noexcept; [[nodiscard]] std::string_view plot_dimension_name( Plot_Dimension dimension) noexcept; diff --git a/mcp/core/runtime/Gallery_Plots_3D.cpp b/mcp/core/runtime/Gallery_Plots_3D.cpp index b3f2afe..fff59fd 100644 --- a/mcp/core/runtime/Gallery_Plots_3D.cpp +++ b/mcp/core/runtime/Gallery_Plots_3D.cpp @@ -438,14 +438,12 @@ public: void update(const Plot_Render_Tick& request) override { if constexpr (requires(Data_Generator& generator, Visual_Object& visual, Axes_Object& axes, - std::optional> markers, + Marker_Object* markers, const Plot_Render_Tick& frame) { generator.update(visual, axes, markers, frame); }) { - const auto markers = markers_ - ? std::optional{not_null{markers_.get()}} - : std::nullopt; - data_generator_.update(*visual_, *axes_, markers, request); + data_generator_.update( + *visual_, *axes_, markers_.get(), request); } } private: @@ -748,7 +746,7 @@ struct Spectrogram_Data_Generator { } } void update(Mesh_Visual& visual, Axes_3D&, - std::optional> markers, + Marker_Visual* markers, const Plot_Render_Tick& request) { if (!parameters.animation_enabled || request.sequence % parameters.update_every_n_frames != 0) @@ -757,14 +755,14 @@ struct Spectrogram_Data_Generator { auto mesh = spectrogram_mesh(parameters, animation_seconds); visual.template set<&Mesh_Visual::Prop::items>(std::move(mesh)); if (!markers) return; - auto items = (*markers)->template read_prop().items; + auto items = markers->template read_prop().items; for (auto& marker : items) { const float time = std::clamp((marker.position.x + 1.0F) * 0.5F, 0.0F, 1.0F); const float frequency = std::clamp((marker.position.y + 1.0F) * 0.5F, 0.0F, 1.0F); marker.position.z = -1.0F + 2.0F * spectrogram_level( time, frequency, animation_seconds, parameters.ridge_count); } - (*markers)->template set<&Marker_Visual::Prop::items>(std::move(items)); + markers->template set<&Marker_Visual::Prop::items>(std::move(items)); } }; } diff --git a/mcp/core/runtime/Plot.cpp b/mcp/core/runtime/Plot.cpp index 8f8b6af..292e11d 100644 --- a/mcp/core/runtime/Plot.cpp +++ b/mcp/core/runtime/Plot.cpp @@ -1162,7 +1162,7 @@ void Plot::Private::render_frame(Plot_Render_Tick tick) { void Plot::Private::publish_completed_frame( not_null completed) { - std::optional> managed; + Managed_Frame* managed{}; for (auto& slot : frame_slots) { const auto frame = std::visit( [](const auto& value) -> not_null { @@ -1175,7 +1175,7 @@ void Plot::Private::publish_completed_frame( } if (!managed) throw std::logic_error("completed frame has no owning Plot policy slot"); - if ((*managed)->state.load(std::memory_order_acquire) != + if (managed->state.load(std::memory_order_acquire) != Frame_State::rendering) throw std::logic_error("completed Plot frame is not rendering"); const auto frame = completed; @@ -1188,7 +1188,7 @@ void Plot::Private::publish_completed_frame( std::uint32_t width{}; std::uint32_t height{}; if (auto *frame_2d = - std::get_if>(&(*managed)->frame)) { + std::get_if>(&managed->frame)) { pixel_layout = Plot_Pixel_Layout::bgra8; const auto image = (*frame_2d)->image(); width = static_cast(image.width); @@ -1201,7 +1201,7 @@ void Plot::Private::publish_completed_frame( height = static_cast(output.height); } } else { - auto &frame_3d = std::get>((*managed)->frame); + auto &frame_3d = std::get>(managed->frame); rendered_identity = frame_3d->rendered_identity(); const auto extent = frame_3d->extent(); width = extent.width; @@ -1211,7 +1211,7 @@ void Plot::Private::publish_completed_frame( } auto pixels = std::make_shared(Plot_Pixel_Frame{ - std::move(pixel_storage), pixel_layout, (*managed)->presentation_time, + std::move(pixel_storage), pixel_layout, managed->presentation_time, identity.sequence, identity.correlation_id, rendered_identity.sequence, rendered_identity.correlation_id, width, height}); @@ -1225,14 +1225,14 @@ void Plot::Private::publish_completed_frame( std::chrono::steady_clock::now() - publish_started) .count()))); auto expected = Frame_State::rendering; - if (!(*managed)->state.compare_exchange_strong( + if (!managed->state.compare_exchange_strong( expected, Frame_State::consuming, std::memory_order_acq_rel, std::memory_order_acquire)) throw std::logic_error("Plot frame left rendering before pixel publish"); } void Plot::Private::consume_completed_frame(not_null frame) { - std::optional> managed; + Managed_Frame* managed{}; for (auto& slot : frame_slots) { const auto address = std::visit( [](const auto& value) -> not_null { @@ -1253,7 +1253,7 @@ void Plot::Private::consume_completed_frame(not_null frame) { * 释放 Plot 的 view/update 门,并立刻唤醒 busy 期间保留的 latest tick。 * 之后外接 DAG 仍在当前 Frame trace 内执行,但不会阻塞下一帧渲染。 */ - if (std::holds_alternative>((*managed)->frame)) + if (std::holds_alternative>(managed->frame)) release_render_admission(lifetime); if (post_publish_graph.empty()) return; @@ -1336,7 +1336,7 @@ void Plot::Private::consume_retired_frames() { } void Plot::Private::retire_completed_frame(not_null frame) { - std::optional> managed; + Managed_Frame* managed{}; for (auto& slot : frame_slots) { const auto address = std::visit( [](const auto& value) -> not_null { @@ -1352,15 +1352,15 @@ void Plot::Private::retire_completed_frame(not_null frame) { auto* head = retired_frames.load(std::memory_order_relaxed); do { - (*managed)->retired_next.store(head, std::memory_order_relaxed); + managed->retired_next.store(head, std::memory_order_relaxed); } while (!retired_frames.compare_exchange_weak( - head, managed->get(), std::memory_order_release, + head, managed, std::memory_order_release, std::memory_order_relaxed)); arm_tick_consumer(lifetime); } void Plot::Private::finalize_retired_frame(not_null frame) { - std::optional> managed; + Managed_Frame* managed{}; for (auto& slot : frame_slots) { const auto address = std::visit( [](const auto& value) -> not_null { @@ -1376,9 +1376,9 @@ void Plot::Private::finalize_retired_frame(not_null frame) { throw std::logic_error("retired frame has no owned Plot slot"); const auto completion_latency = - (*managed)->submitted_at.time_since_epoch().count() == 0 + managed->submitted_at.time_since_epoch().count() == 0 ? std::chrono::steady_clock::duration::zero() - : std::chrono::steady_clock::now() - (*managed)->submitted_at; + : std::chrono::steady_clock::now() - managed->submitted_at; std::optional datoviz_observation; if (auto* frame_3d = dynamic_cast(frame.get())) { datoviz_observation = frame_3d->take_datoviz_observation(); @@ -1400,8 +1400,8 @@ void Plot::Private::finalize_retired_frame(not_null frame) { completed_frame_statistics.reset(); applied_statistics_generation = statistics_generation_value; } - (*managed)->statistics = completed_frame_statistics.submit(*frame); - (*managed)->statistics_generation = statistics_generation_value; + managed->statistics = completed_frame_statistics.submit(*frame); + managed->statistics_generation = statistics_generation_value; std::optional captured_trace; nlohmann::json captured_components; @@ -1430,11 +1430,11 @@ void Plot::Private::finalize_retired_frame(not_null frame) { } auto expected = Frame_State::consuming; - if (!(*managed)->state.compare_exchange_strong( + if (!managed->state.compare_exchange_strong( expected, Frame_State::available, std::memory_order_acq_rel, std::memory_order_acquire)) throw std::logic_error("retired Plot frame is not consuming"); - latest_statistics_frame.store(managed->get(), std::memory_order_release); + latest_statistics_frame.store(managed, std::memory_order_release); /* 物理槽是唯一背压原因;归还任意槽后只解除一次耗尽状态。 */ auto admission = Render_Admission_State::frame_slots_exhausted; diff --git a/mcp/tests/Control_Path_Benchmarks.cpp b/mcp/tests/Control_Path_Benchmarks.cpp index 55678a1..527161d 100644 --- a/mcp/tests/Control_Path_Benchmarks.cpp +++ b/mcp/tests/Control_Path_Benchmarks.cpp @@ -83,6 +83,33 @@ BENCHMARK_DEFINE_F(Control_Path, Timestamped_Drag_Admission)( benchmark::DoNotOptimize(result.result); } state.SetItemsProcessed(state.iterations()); + + const auto runtime = service->call_tool( + "aethera_task_runtime", nlohmann::json::object()); + if (runtime.result != Tool_Call_Result::ok) { + state.SkipWithError("Taskflow runtime diagnostics were rejected"); + return; + } + const auto milliseconds = [](const nlohmann::json& value, + std::string_view key) { + return static_cast(value.at(key).get()) / + 1'000'000.0; + }; + state.counters["taskflow_wall_ms"] = + milliseconds(runtime.content, "observed_wall_time_ns"); + state.counters["worker_busy_ms"] = + milliseconds(runtime.content, "worker_busy_time_ns"); + state.counters["worker_cpu_ms"] = + milliseconds(runtime.content, "worker_cpu_time_ns"); + state.counters["worker_utilization_pct"] = + runtime.content.at("worker_utilization").get(); + state.counters["worker_cpu_utilization_pct"] = + runtime.content.at("worker_cpu_utilization").get(); + state.counters["active_taskflows"] = + static_cast(runtime.content.at("active_taskflows").get()); + state.counters["completed_taskflows"] = + static_cast(runtime.content.at("completed_taskflows").get()); + state.SetLabel(runtime.content.at("longest_task").at("name").get()); } BENCHMARK(decode_timestamped_drag); diff --git a/render_2D/render_2D/base/Renderable_2D.ipp b/render_2D/render_2D/base/Renderable_2D.ipp index 097c9f8..36f6140 100644 --- a/render_2D/render_2D/base/Renderable_2D.ipp +++ b/render_2D/render_2D/base/Renderable_2D.ipp @@ -10,8 +10,8 @@ struct Renderable_2D::Private : Prev_Private { Cache_Access pending_cache{}; /* 非空时返回最终对象本轮可写的缓存物理对象。 */ Cache_Enabled cache_enabled{}; /* 读取最终对象当前发布的运行时缓存策略。 */ Event_Region_Run event_region_run{}; /* 计算最终对象当前事件区域的无虚函数入口。 */ - std::optional> paint_target{}; /* 仅在 Scene Paint 阶段有效的非拥有绘制目标。 */ - std::optional> valid_cache{}; /* 缓存根最近一次完整重绘产生的权威物理缓存。 */ + Blend2D_Cache* paint_target{}; /* 仅在 Scene Paint 阶段有效的非拥有绘制目标。 */ + Blend2D_Cache* valid_cache{}; /* 缓存根最近一次完整重绘产生的权威物理缓存。 */ Scene_Attach scene_attach{}; /* Scene Builder 在构造期执行的所属场景及拓扑绑定。 */ /* Paint 实现使用:返回 Scene 为本轮指定的目标;未进入 Paint 阶段属于契约错误。 */ [[nodiscard]] Blend2D_Cache& paint_surface(); @@ -26,7 +26,7 @@ inline bool Renderable_2D::Prop::operator==(const Prop&) const = default; inline bool Renderable_2D::State::operator==(const State&) const = default; inline Blend2D_Cache& Renderable_2D::Private::paint_surface() { if (!paint_target) throw std::logic_error("2D renderable painted without a scene paint target"); - return **paint_target; + return *paint_target; } inline Rect_F Renderable_2D::Private::event_region(const Attached auto*, Size viewport) const { return {0.0, 0.0, static_cast(viewport.width), static_cast(viewport.height)}; diff --git a/render_2D/render_2D/plottable/Afterglow.ipp b/render_2D/render_2D/plottable/Afterglow.ipp index 370c6db..61aad2d 100644 --- a/render_2D/render_2D/plottable/Afterglow.ipp +++ b/render_2D/render_2D/plottable/Afterglow.ipp @@ -15,9 +15,9 @@ struct Afterglow::Private : Prev_Private { Plot_Ratio maximum{1.0}; /* 本帧强度归一化分母,最小为 1。 */ bool valid{}; /* 轴布局和输入数据是否足以生成色块。 */ }; - std::optional> scene{}; /* 不拥有的所属 Scene。 */ - std::optional> frequency_axis{}; /* 不拥有的频率轴。 */ - std::optional> power_axis{}; /* 不拥有的功率轴。 */ + Scene_Object* scene{}; /* 不拥有的所属 Scene。 */ + Frequency_Object* frequency_axis{}; /* 不拥有的频率轴。 */ + Power_Object* power_axis{}; /* 不拥有的功率轴。 */ Prepared prepared{}; /* 当前权威状态推导出的 Paint 输入。 */ Plot_Partition_Grid graph_partition_grid{}; /* Prepare/Paint 子图当前固化的 framebuffer 列×行网格。 */ void bind_sources(not_null frequency_axis_value, @@ -55,8 +55,8 @@ std::expected, Dependency_Graph_Error> Afterglow::Builde const not_null scene{static_cast(root.get())}; auto& data = detail::Internal_Access::layer(object.get()); data.scene = scene; - const auto frequency_axis = data.frequency_axis.value(); - const auto power_axis = data.power_axis.value(); + const auto frequency_axis = not_null{data.frequency_axis}; + const auto power_axis = not_null{data.power_axis}; return detail::Internal_Access::edit_dependency_graph(scene.get(), [&](auto& graph, auto& cache) { graph.add_dependency(frequency_axis.get(), object.get()); graph.add_dependency(power_axis.get(), object.get()); @@ -119,17 +119,17 @@ inline void Afterglow::Private::prepare_frame(Afterglow* object) { }); detail::Internal_Access::accumulate_stream(object, std::size_t{64}); const auto& frequency_layout = this->template read_current_prop( - frequency_axis.value().get()); + frequency_axis); const auto& power_layout = this->template read_current_prop( - power_axis.value().get()); + power_axis); const std::size_t available = prepared.source_spectra.empty() ? 0 : prepared.source_spectra.back()->size(); const int columns = static_cast(state.frequency_point_size ? std::min(state.frequency_point_size, available) : available); const int rows = static_cast(state.power_point_size ? state.power_point_size : std::max(1.0, std::abs(power_layout.pixel_length))); const Size canvas = this->template read_current_prop( - scene.value().get()).viewport; + scene).viewport; const auto layout = detail::raster_layout( - frequency_axis.value().get(), state.frequency_range, columns, - power_axis.value().get(), state.power_range, rows, + frequency_axis, state.frequency_range, columns, + power_axis, state.power_range, rows, frequency_layout.orientation, power_layout.orientation); if (canvas.empty() || !layout.valid()) { prepared.valid = false; diff --git a/render_2D/render_2D/plottable/Constellation_Diagram.ipp b/render_2D/render_2D/plottable/Constellation_Diagram.ipp index 13fe753..1b39c36 100644 --- a/render_2D/render_2D/plottable/Constellation_Diagram.ipp +++ b/render_2D/render_2D/plottable/Constellation_Diagram.ipp @@ -11,9 +11,9 @@ struct Constellation_Diagram::Private : Prev_Private { Size canvas{}; /* 当前 Scene viewport 的像素尺寸。 */ bool valid{}; /* 两根轴是否正交且画布有效。 */ }; - std::optional> scene{}; /* 不拥有的所属 Scene。 */ - std::optional> i_axis{}; /* 不拥有的同相分量轴。 */ - std::optional> q_axis{}; /* 不拥有的正交分量轴。 */ + Scene_Object* scene{}; /* 不拥有的所属 Scene。 */ + Axis_Object* i_axis{}; /* 不拥有的同相分量轴。 */ + Axis_Object* q_axis{}; /* 不拥有的正交分量轴。 */ Prepared prepared{}; /* 当前权威状态推导出的 Paint 输入。 */ void bind_sources(not_null i_axis_value, not_null q_axis_value); @@ -51,8 +51,8 @@ std::expected, Dependency_Graph_Error> Constellation_Dia const not_null scene{static_cast(root.get())}; auto& data = detail::Internal_Access::layer(object.get()); data.scene = scene; - const auto i_axis = data.i_axis.value(); - const auto q_axis = data.q_axis.value(); + const auto i_axis = not_null{data.i_axis}; + const auto q_axis = not_null{data.q_axis}; return detail::Internal_Access::edit_dependency_graph(scene.get(), [&](auto& graph, auto& cache) { graph.add_dependency(i_axis.get(), object.get()); graph.add_dependency(q_axis.get(), object.get()); @@ -78,12 +78,12 @@ inline void Constellation_Diagram::Private::prepare_data(Constellation_Diagram* return current - point.submitted_at_ms <= state.point_lifetime_ms; }); const auto& i_layout = this->template read_current_prop( - i_axis.value().get()); + i_axis); const auto& q_layout = this->template read_current_prop( - q_axis.value().get()); + q_axis); prepared = {}; prepared.canvas = this->template read_current_prop( - scene.value().get()).viewport; + scene).viewport; if (prepared.canvas.empty() || i_layout.orientation == q_layout.orientation) { return; } @@ -92,8 +92,8 @@ inline void Constellation_Diagram::Private::prepare_data(Constellation_Diagram* for (const auto& value : points) if (current - value.submitted_at_ms <= state.point_lifetime_ms) prepared.points.push_back(detail::map_plot_point( - i_axis.value().get(), value.point.x, - q_axis.value().get(), value.point.y, + i_axis, value.point.x, + q_axis, value.point.y, i_layout.orientation)); }); const int count = static_cast(state.type); @@ -103,8 +103,8 @@ inline void Constellation_Diagram::Private::prepare_data(Constellation_Diagram* for (int index = 0; index < count; ++index) { const Plot_Ratio angle = state.phase_offset_radians + 2.0 * std::numbers::pi * index / count; prepared.anchors.push_back(detail::map_plot_point( - i_axis.value().get(), center_i + std::cos(angle) * radius, - q_axis.value().get(), center_q + std::sin(angle) * radius, + i_axis, center_i + std::cos(angle) * radius, + q_axis, center_q + std::sin(angle) * radius, i_layout.orientation)); } prepared.valid = true; diff --git a/render_2D/render_2D/plottable/Frequency_Trace.ipp b/render_2D/render_2D/plottable/Frequency_Trace.ipp index 880acc3..12a8a31 100644 --- a/render_2D/render_2D/plottable/Frequency_Trace.ipp +++ b/render_2D/render_2D/plottable/Frequency_Trace.ipp @@ -9,9 +9,9 @@ struct Frequency_Trace::Private : Prev_Private { Size canvas{}; /* 当前颜色层尺寸。 */ bool valid{}; /* 两根轴正交且画布有效。 */ }; - std::optional> scene{}; /* 不拥有的所属 Scene。 */ - std::optional> time_axis{}; /* 不拥有的时间轴。 */ - std::optional> value_axis{}; /* 不拥有的数值轴。 */ + Scene_Object* scene{}; /* 不拥有的所属 Scene。 */ + Time_Object* time_axis{}; /* 不拥有的时间轴。 */ + Value_Object* value_axis{}; /* 不拥有的数值轴。 */ Prepared prepared{}; /* 当前状态和轴状态推导出的 Paint 输入。 */ Plot_Partition_Count graph_partition_count{}; /* 当前 Prepare 子图固化的分块数。 */ void bind_sources(not_null time_axis_value, @@ -46,8 +46,8 @@ std::expected, Dependency_Graph_Error> Frequency_Trace:: const not_null scene{static_cast(root.get())}; auto& data = detail::Internal_Access::get(object.get()); data.scene = scene; - const auto time = data.time_axis.value(); - const auto value = data.value_axis.value(); + const auto time = not_null{data.time_axis}; + const auto value = not_null{data.value_axis}; return detail::Internal_Access::edit_dependency_graph< Render_Graph_Tag, Render_Cache_Tag>( scene.get(), [&](auto& graph, auto& cache) { @@ -95,19 +95,19 @@ inline Task_Graph Frequency_Trace::Private::build_graph(Frequency_Trace* object, inline void Frequency_Trace::Private::prepare_frame(Frequency_Trace* object, Plot_Partition_Count partition_count) { const auto& state = static_cast(detail::Internal_Access::current_prop(object)); const auto& time_layout = static_cast( - detail::Internal_Access::current_prop(time_axis.value().get())); + detail::Internal_Access::current_prop(time_axis)); const auto& value_layout = static_cast( - detail::Internal_Access::current_prop(value_axis.value().get())); + detail::Internal_Access::current_prop(value_axis)); const auto visible_count = static_cast(std::max( 2, static_cast( detail::Internal_Access::current_prop( - time_axis.value().get())).visible_count)); + time_axis)).visible_count)); detail::Internal_Access::exchange_stream(object); detail::Internal_Access::accumulate_stream(object, visible_count); prepared = {}; prepared.partitions.resize(partition_count); prepared.canvas = static_cast( - detail::Internal_Access::current_prop(scene.value().get())).viewport; + detail::Internal_Access::current_prop(scene)).viewport; if (prepared.canvas.empty() || time_layout.orientation == value_layout.orientation) return; detail::Internal_Access::access_rendering_stream(object, [&](std::span samples) { prepared.samples.reserve(samples.size()); @@ -120,19 +120,19 @@ inline void Frequency_Trace::Private::prepare_partition(Frequency_Trace* object, if (!prepared.valid) return; const auto& state = static_cast(detail::Internal_Access::current_prop(object)); const auto& time_layout = static_cast( - detail::Internal_Access::current_prop(time_axis.value().get())); + detail::Internal_Access::current_prop(time_axis)); const auto& value_layout = static_cast( - detail::Internal_Access::current_prop(value_axis.value().get())); + detail::Internal_Access::current_prop(value_axis)); const auto& value_state = static_cast( - detail::Internal_Access::current_prop(value_axis.value().get())); + detail::Internal_Access::current_prop(value_axis)); const Axis_Range domain{prepared.samples.front().coordinate, prepared.samples.back().coordinate}; const auto range = detail::curve_partition_range(prepared.samples.size(), partition_index, prepared.partitions.size(), domain); const Axis_Range visible_range = detail::Internal_Access::layer( - time_axis.value().get()).runtime_coordinate_range(); + time_axis).runtime_coordinate_range(); prepared.partitions[partition_index] = detail::prepare_curve( std::span(prepared.samples).subspan( range.first_sample, range.sample_count), true, visible_range, value_state.coordinate_range, - time_axis.value().get(), value_axis.value().get(), + time_axis, value_axis, time_layout.orientation, value_layout.orientation); } inline void Frequency_Trace::Private::paint_partition(Frequency_Trace* object, Plot_Partition_Count partition_index) { diff --git a/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.ipp b/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.ipp index 7101bb2..7ee195d 100644 --- a/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.ipp +++ b/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.ipp @@ -8,7 +8,7 @@ namespace aethera::render_2d { struct Selection_Rectangle_Overlay::Private : Prev_Private { using Axis_Source = std::variant, not_null, not_null>; - std::optional> scene{}; /* 不拥有的所属 Scene。 */ + Scene_Object* scene{}; /* 不拥有的所属 Scene。 */ std::optional horizontal_axis{}; /* 保留最终类型的水平坐标轴。 */ std::optional vertical_axis{}; /* 保留最终类型的垂直坐标轴。 */ Axis_Point drag_origin{}; /* 当前拖动起点的两轴数据坐标。 */ @@ -80,7 +80,7 @@ inline void Selection_Rectangle_Overlay::Private::paint(Selection_Rectangle_Over auto& data = double_buffer::detail::Internal_Access::get(object); const auto& state = static_cast(double_buffer::detail::Internal_Access::current_prop(object)); const Size canvas = this->template read_current_prop< - Render_Scene_2D::Base_Tag>(scene.value().get()).viewport; + Render_Scene_2D::Base_Tag>(scene).viewport; auto& cache = data.paint_surface(); if (canvas.empty()) return; detail::Painter painter(cache, canvas); diff --git a/render_2D/render_2D/plottable/Spectrum.ipp b/render_2D/render_2D/plottable/Spectrum.ipp index 6d1e9f3..1f24f30 100644 --- a/render_2D/render_2D/plottable/Spectrum.ipp +++ b/render_2D/render_2D/plottable/Spectrum.ipp @@ -33,9 +33,9 @@ struct Spectrum::Private : Prev_Private { Size canvas_size{}; /* 所属 Scene viewport 决定的颜色层尺寸。 */ bool valid{}; /* 两根轴与画布是否足以生成绘制数据。 */ }; - std::optional> scene{}; /* 不拥有的所属二维 Scene;Builder 已登记状态层依赖。 */ - std::optional> frequency_axis{}; /* 不拥有的频率轴;Prepare 直接读取其当前状态。 */ - std::optional> power_axis{}; /* 不拥有的功率轴;Prepare 直接读取其当前状态。 */ + Scene_Object* scene{}; /* 不拥有的所属二维 Scene;Builder 已登记状态层依赖。 */ + Frequency_Object* frequency_axis{}; /* 不拥有的频率轴;Prepare 直接读取其当前状态。 */ + Power_Object* power_axis{}; /* 不拥有的功率轴;Prepare 直接读取其当前状态。 */ Prepared prepared{}; /* 当前权威 State、Frame 和轴状态推导出的 Paint 输入。 */ std::vector maxima{}; /* 当前样本历史逐点最大值;只由 Prepare 更新。 */ std::vector minima{}; /* 当前样本历史逐点最小值;只由 Prepare 更新。 */ @@ -79,8 +79,8 @@ std::expected, Dependency_Graph_Error> Spectrum::Builder const not_null scene{static_cast(root.get())}; auto& data = detail::Internal_Access::layer(object.get()); data.scene = scene; - const auto frequency_axis = data.frequency_axis.value(); - const auto power_axis = data.power_axis.value(); + const auto frequency_axis = not_null{data.frequency_axis}; + const auto power_axis = not_null{data.power_axis}; return detail::Internal_Access::edit_dependency_graph(scene.get(), [&](auto& graph, auto& cache) { graph.add_dependency(frequency_axis.get(), object.get()); graph.add_dependency(power_axis.get(), object.get()); @@ -158,8 +158,8 @@ inline Task_Graph Spectrum::Private::build_graph(Spectrum* object, const Prop& s } inline void Spectrum::Private::prepare_frame(Spectrum* object, std::size_t partition_count) { auto& private_data = double_buffer::detail::Internal_Access::get(object); - const auto frequency = frequency_axis.value().get(); - const auto power = power_axis.value().get(); + const auto frequency = frequency_axis; + const auto power = power_axis; const auto& state = static_cast(double_buffer::detail::Internal_Access::current_prop(object)); const auto& frame = double_buffer::detail::Internal_Access::current_buffer(object); hold_history_reset = maxima.size() != frame.samples.size(); @@ -168,7 +168,7 @@ inline void Spectrum::Private::prepare_frame(Spectrum* object, std::size_t parti minima = frame.samples; } const auto& scene_state = this->template read_current_prop( - scene.value().get()); + scene); const auto& frequency_layout = this->template read_current_prop(frequency); const auto& power_layout = this->template read_current_prop(power); const auto& power_state = this->template read_current_prop(power); @@ -182,7 +182,7 @@ inline void Spectrum::Private::prepare_frame(Spectrum* object, std::size_t parti for (std::size_t index = 0; index < state.custom_markers.size(); ++index) { const Spectrum_Frequency frequency = state.custom_markers[index]; const Marker_Style style = static_cast(index) == state.selected_marker ? Marker_Style::selected : Marker_Style::normal; - prepared.markers.push_back({detail::map_plot_point(frequency_axis.value().get(), frequency, power, power_state.coordinate_range.origin, frequency_layout.orientation), detail::map_plot_point(frequency_axis.value().get(), frequency, power, power_state.coordinate_range.target, frequency_layout.orientation), style}); + prepared.markers.push_back({detail::map_plot_point(frequency_axis, frequency, power, power_state.coordinate_range.origin, frequency_layout.orientation), detail::map_plot_point(frequency_axis, frequency, power, power_state.coordinate_range.target, frequency_layout.orientation), style}); } prepared.valid = true; } @@ -227,10 +227,10 @@ inline void Spectrum::Private::prepare_extreme(Spectrum* object, bool maximum) { state.frequency_range.length() * static_cast(index) / denominator; const auto& frequency_layout = this->template read_current_prop< - Abs_Axis::Base_Tag>(frequency_axis.value().get()); + Abs_Axis::Base_Tag>(frequency_axis); prepared.extremes[slot] = Prepared_Extreme{ - detail::map_plot_point(frequency_axis.value().get(), frequency, - power_axis.value().get(), *iterator, + detail::map_plot_point(frequency_axis, frequency, + power_axis, *iterator, frequency_layout.orientation), maximum}; } @@ -240,8 +240,8 @@ inline void Spectrum::Private::prepare_partition(Spectrum* object, std::size_t p const auto& state = static_cast(double_buffer::detail::Internal_Access::current_prop(object)); const auto& frame = double_buffer::detail::Internal_Access::current_buffer(object); if (frame.samples.empty()) return; - const auto frequency = frequency_axis.value().get(); - const auto power = power_axis.value().get(); + const auto frequency = frequency_axis; + const auto power = power_axis; const auto& frequency_layout = this->template read_current_prop(frequency); const auto& power_layout = this->template read_current_prop(power); const auto& frequency_state = this->template read_current_prop(frequency); diff --git a/render_2D/render_2D/plottable/Sweep_Spectrum.ipp b/render_2D/render_2D/plottable/Sweep_Spectrum.ipp index 7c636ca..f9363de 100644 --- a/render_2D/render_2D/plottable/Sweep_Spectrum.ipp +++ b/render_2D/render_2D/plottable/Sweep_Spectrum.ipp @@ -14,9 +14,9 @@ struct Sweep_Spectrum::Private : Prev_Private { Size canvas{}; /* 当前 Scene viewport 的像素尺寸。 */ bool valid{}; /* 两根轴正交、画布及组合折线均有效。 */ }; - std::optional> scene{}; /* 不拥有的所属 Scene。 */ - std::optional> frequency_axis{}; /* 不拥有的频率轴。 */ - std::optional> power_axis{}; /* 不拥有的功率轴。 */ + Scene_Object* scene{}; /* 不拥有的所属 Scene。 */ + Frequency_Object* frequency_axis{}; /* 不拥有的频率轴。 */ + Power_Object* power_axis{}; /* 不拥有的功率轴。 */ /* * 完整扫频的唯一持久状态。每次交换得到一组块更新并原位覆盖相应槽位; * 三缓冲只负责跨线程交付输入组,不承担扫频历史保存职责。 @@ -60,8 +60,8 @@ std::expected, Dependency_Graph_Error> Sweep_Spectrum::B const not_null scene{static_cast(root.get())}; auto& data = detail::Internal_Access::get(object.get()); data.scene = scene; - const auto frequency = data.frequency_axis.value(); - const auto power = data.power_axis.value(); + const auto frequency = not_null{data.frequency_axis}; + const auto power = not_null{data.power_axis}; return detail::Internal_Access::edit_dependency_graph< Render_Graph_Tag, Render_Cache_Tag>( scene.get(), [&](auto& graph, auto& cache) { @@ -111,8 +111,8 @@ inline Task_Graph Sweep_Spectrum::Private::build_graph(Sweep_Spectrum* object, c return graph; } inline void Sweep_Spectrum::Private::prepare_frame(Sweep_Spectrum* object) { - const auto frequency = frequency_axis.value().get(); - const auto power = power_axis.value().get(); + const auto frequency = frequency_axis; + const auto power = power_axis; const auto& state = this->template read_current_prop< Sweep_Spectrum::Base_Tag>(object); const auto& frequency_layout = this->template read_current_prop< @@ -123,7 +123,7 @@ inline void Sweep_Spectrum::Private::prepare_frame(Sweep_Spectrum* object) { Numeric_Axis::Base_Tag>(power); prepared = {}; prepared.canvas = this->template read_current_prop< - Render_Scene_2D::Base_Tag>(scene.value().get()).viewport; + Render_Scene_2D::Base_Tag>(scene).viewport; const std::size_t block_count = std::max(1, state.block_count); if (sweep_blocks.size() != block_count) { sweep_blocks.assign(block_count, {}); @@ -166,8 +166,8 @@ inline void Sweep_Spectrum::Private::prepare_frame(Sweep_Spectrum* object) { } inline void Sweep_Spectrum::Private::prepare_partition(Sweep_Spectrum* object, Plot_Partition_Count index) { if (!prepared.valid) return; - const auto frequency = frequency_axis.value().get(); - const auto power = power_axis.value().get(); + const auto frequency = frequency_axis; + const auto power = power_axis; const auto& state = this->template read_current_prop< Sweep_Spectrum::Base_Tag>(object); const auto& frequency_layout = this->template read_current_prop< diff --git a/render_2D/render_2D/plottable/Waterfall.ipp b/render_2D/render_2D/plottable/Waterfall.ipp index 063ad5c..5ab7aa9 100644 --- a/render_2D/render_2D/plottable/Waterfall.ipp +++ b/render_2D/render_2D/plottable/Waterfall.ipp @@ -24,9 +24,9 @@ struct Waterfall::Private : Prev_Private { int source_first{}; /* 可视频段在源频谱行中的首列。 */ bool valid{}; /* 轴布局和行数据是否足以生成色块。 */ }; - std::optional> scene{}; /* 不拥有的所属 Scene。 */ - std::optional> frequency_axis{}; /* 不拥有的频率轴。 */ - std::optional> time_axis{}; /* 不拥有的时间轴,同时权威决定保留行数。 */ + Scene_Object* scene{}; /* 不拥有的所属 Scene。 */ + Frequency_Object* frequency_axis{}; /* 不拥有的频率轴。 */ + Time_Object* time_axis{}; /* 不拥有的时间轴,同时权威决定保留行数。 */ Prepared prepared{}; /* 当前权威状态推导出的 Paint 输入。 */ detail::Hover_Tooltip_Runtime tooltip{}; /* 事件侧当前 hover 位置。 */ Plot_Partition_Grid graph_partition_grid{}; /* Prepare/Paint 子图当前固化的 framebuffer 列×行网格。 */ @@ -66,8 +66,8 @@ std::expected, Dependency_Graph_Error> Waterfall::Builde const not_null scene{static_cast(root.get())}; auto& data = detail::Internal_Access::layer(object.get()); data.scene = scene; - const auto frequency_axis = data.frequency_axis.value(); - const auto time_axis = data.time_axis.value(); + const auto frequency_axis = not_null{data.frequency_axis}; + const auto time_axis = not_null{data.time_axis}; return detail::Internal_Access::edit_dependency_graph(scene.get(), [&](auto& graph, auto& cache) { graph.add_dependency(frequency_axis.get(), object.get()); graph.add_dependency(time_axis.get(), object.get()); @@ -120,7 +120,7 @@ inline void Waterfall::Private::prepare_frame(Waterfall* object) { detail::Internal_Access::exchange_stream(object); const auto row_limit = static_cast(std::max( 2, this->template read_current_prop( - time_axis.value().get()).visible_count)); + time_axis).visible_count)); detail::Internal_Access::accumulate_stream(object, row_limit); prepared = {}; std::vector> source_rows; @@ -129,11 +129,11 @@ inline void Waterfall::Private::prepare_frame(Waterfall* object) { source_rows.assign(rows.begin(), rows.end()); }); const auto& frequency_layout = this->template read_current_prop( - frequency_axis.value().get()); + frequency_axis); const auto& time_layout = this->template read_current_prop( - time_axis.value().get()); + time_axis); prepared.canvas = this->template read_current_prop( - scene.value().get()).viewport; + scene).viewport; if (source_rows.empty()) { return; } @@ -145,19 +145,19 @@ inline void Waterfall::Private::prepare_frame(Waterfall* object) { const auto selection = detail::raster_axis_selection( state.frequency_range, detail::Internal_Access::layer( - frequency_axis.value().get()).runtime_coordinate_range(), + frequency_axis).runtime_coordinate_range(), source_columns, state.visible_range_only); if (!selection) { return; } const int time_slots = std::max( 2, this->template read_current_prop( - time_axis.value().get()).visible_count); + time_axis).visible_count); const Axis_Range time_range = detail::Internal_Access::layer( - time_axis.value().get()).runtime_coordinate_range(); + time_axis).runtime_coordinate_range(); prepared.layout = detail::raster_layout( - frequency_axis.value().get(), selection->range, selection->count(), - time_axis.value().get(), time_range, time_slots, + frequency_axis, selection->range, selection->count(), + time_axis, time_range, time_slots, frequency_layout.orientation, time_layout.orientation); if (prepared.canvas.empty() || !prepared.layout.valid()) { return; @@ -184,7 +184,7 @@ inline void Waterfall::Private::prepare_frame(Waterfall* object) { std::ostringstream text; text << std::fixed << std::setprecision(2) << detail::Internal_Access::layer( - frequency_axis.value().get()) + frequency_axis) .runtime_point_to_coordinate(tooltip.position) << " Hz"; prepared.tooltip_text = text.str(); diff --git a/render_2D/render_2D/render/Blend2D_Cache.cpp b/render_2D/render_2D/render/Blend2D_Cache.cpp index d90efb3..bfc372f 100644 --- a/render_2D/render_2D/render/Blend2D_Cache.cpp +++ b/render_2D/render_2D/render/Blend2D_Cache.cpp @@ -306,10 +306,10 @@ Painter::Clip_Scope::Clip_Scope(Painter& owner, Rect_F rect) { } Painter::Clip_Scope::~Clip_Scope() { if (!painter) return; - (*painter)->d->context.restore(); + painter->d->context.restore(); /* restore 会回滚绘制状态;同步失效本地样式缓存,避免后续错误跳过状态设置。 */ - (*painter)->d->fill_color.reset(); - (*painter)->d->stroke_pen.reset(); + painter->d->fill_color.reset(); + painter->d->stroke_pen.reset(); } Painter::Clip_Scope::Clip_Scope(Clip_Scope&& other) noexcept : painter(std::exchange(other.painter, {})) {} diff --git a/render_2D/render_2D/render/Blend2D_Cache.hpp b/render_2D/render_2D/render/Blend2D_Cache.hpp index ea052ae..93ecb6e 100644 --- a/render_2D/render_2D/render/Blend2D_Cache.hpp +++ b/render_2D/render_2D/render/Blend2D_Cache.hpp @@ -55,7 +55,7 @@ struct Painter { Clip_Scope(Clip_Scope&& other) noexcept; Clip_Scope& operator=(Clip_Scope&& other) = delete; private: - std::optional> painter{}; /* 非空时析构恢复对应 Painter 的裁剪状态。 */ + Painter* painter{}; /* 非空时析构恢复对应 Painter 的裁剪状态。 */ }; Painter(Blend2D_Cache& cache, Size size); /* diff --git a/render_2D/render_2D/scene/Render_Scene_2D.ipp b/render_2D/render_2D/scene/Render_Scene_2D.ipp index 3c1c897..2e23a1a 100644 --- a/render_2D/render_2D/scene/Render_Scene_2D.ipp +++ b/render_2D/render_2D/scene/Render_Scene_2D.ipp @@ -51,7 +51,7 @@ struct Render_Scene_2D::Private : Prev_Private { struct Paint_Node { not_null object; /* Paint 拓扑位置对应的最终对象;Scene 不拥有。 */ not_null private_data; /* 对象的二维能力层;对象存活期间有效。 */ - std::optional> cache_owner{}; /* 所属缓存根;空值表示直接绘制最终帧。 */ + Root* cache_owner{}; /* 所属缓存根;空值表示直接绘制最终帧。 */ bool paint_requested{}; /* Scene 按本轮组级失效结果计算的执行许可。 */ }; struct Cache_Group { @@ -66,8 +66,8 @@ struct Render_Scene_2D::Private : Prev_Private { std::atomic_bool frame_in_flight{}; /* Scene 当前是否仍借用一个外部 Frame。 */ std::unique_ptr frame_taskflow{}; ~Private(); - std::optional> active_frame{}; /* 异步帧 DAG 借用的外部帧;完成回调前保持存活。 */ - std::optional> frame_target{}; /* 当前异步帧的最终颜色层;帧 DAG 完成后清空。 */ + Frame_2D* active_frame{}; /* 异步帧 DAG 借用的外部帧;完成回调前保持存活。 */ + Blend2D_Cache* frame_target{}; /* 当前异步帧的最终颜色层;帧 DAG 完成后清空。 */ /* 最终 Private 实现:Prepare 完成后按 Paint 图拓扑边并行绘制并合成颜色层。 */ std::vector paint_order{}; /* Paint 图当前拓扑序及 Scene 缓存分组结果。 */ std::vector cache_groups{}; /* 仅保存显式缓存根对应的执行分组。 */ @@ -105,7 +105,7 @@ void Render_Scene_2D::Private::after_advance( const bool enabled = node.private_data->cache_enabled && node.private_data->cache_enabled(node.object); if (enabled == (node.cache_owner == node.object)) continue; - node.private_data->valid_cache.reset(); + node.private_data->valid_cache = nullptr; rebuild = true; } if (!rebuild) return; @@ -114,7 +114,7 @@ void Render_Scene_2D::Private::after_advance( double_buffer::detail::Internal_Access::current_dependency_graph(object); paint_order.clear(); cache_groups.clear(); - std::unordered_map>> + std::unordered_map cache_owner_by_object; std::unordered_map cache_group_by_owner; const auto order_result = dependencies.for_each_topological_view( @@ -123,7 +123,7 @@ void Render_Scene_2D::Private::after_advance( if (!base_data) return; const not_null data{ static_cast(base_data)}; - std::optional> cache_owner{}; + Root* cache_owner{}; if (data->cache_enabled && data->cache_enabled(node.object)) { cache_owner = node.object; } @@ -135,7 +135,7 @@ void Render_Scene_2D::Private::after_advance( cache_owner_by_object.find(dependency->object.get()); const auto dependency_owner = current == cache_owner_by_object.end() - ? std::optional>{} + ? nullptr : current->second; if (!dependency_owner || (cache_owner && cache_owner != dependency_owner)) { @@ -144,7 +144,7 @@ void Render_Scene_2D::Private::after_advance( } cache_owner = dependency_owner; } - if (conflict) cache_owner.reset(); + if (conflict) cache_owner = nullptr; } cache_owner_by_object.emplace(node.object.get(), cache_owner); paint_order.push_back( @@ -152,8 +152,8 @@ void Render_Scene_2D::Private::after_advance( if (!cache_owner) return; auto [group, inserted] = cache_group_by_owner.emplace( - cache_owner->get(), cache_groups.size()); - if (inserted) cache_groups.push_back(Cache_Group{*cache_owner}); + cache_owner, cache_groups.size()); + if (inserted) cache_groups.push_back(Cache_Group{cache_owner}); cache_groups[group->second].members.push_back(node.object); }); if (!order_result) @@ -215,7 +215,7 @@ void Render_Scene_2D::Private::ensure_frame_taskflow(Object* object) { auto begin = graph.add("scene.begin", [this, object] { if (!active_frame) throw std::logic_error("2D frame DAG lost its active frame"); - const auto frame = *active_frame; + const auto frame = not_null{active_frame}; const Prop& prop = double_buffer::detail::Internal_Access::current_prop_layer< Render_Scene_2D::Base_Tag>(object); @@ -244,26 +244,26 @@ void Render_Scene_2D::Private::ensure_frame_taskflow(Object* object) { "scene.frame_target", [this, object] { if (!active_frame || !frame_target) throw std::logic_error("2D frame target lost its frame"); - const auto frame = *active_frame; + const auto frame = not_null{active_frame}; const Prop& prop = double_buffer::detail::Internal_Access::current_prop_layer< Render_Scene_2D::Base_Tag>(object); frame->mark(Frame_Trace_Marker::paint_frame_target_started); - (*frame_target)->ensure_size(prop.viewport); - (*frame_target)->clear(); + frame_target->ensure_size(prop.viewport); + frame_target->clear(); frame->mark(Frame_Trace_Marker::paint_frame_target_finished); }); auto paint_background = graph.add( "scene.background", [this, object] { if (!active_frame || !frame_target) throw std::logic_error("2D background lost its frame target"); - const auto frame = *active_frame; + const auto frame = not_null{active_frame}; const Prop& prop = double_buffer::detail::Internal_Access::current_prop_layer< Render_Scene_2D::Base_Tag>(object); frame->mark(Frame_Trace_Marker::paint_background_started); { - detail::Painter painter(**frame_target, prop.viewport); + detail::Painter painter(*frame_target, prop.viewport); painter.rect( {0.0, 0.0, static_cast(prop.viewport.width), static_cast(prop.viewport.height)}, @@ -276,20 +276,20 @@ void Render_Scene_2D::Private::ensure_frame_taskflow(Object* object) { "scene.cache_targets", [this, object] { if (!active_frame || !frame_target) throw std::logic_error("2D cache setup lost its frame"); - const auto frame = *active_frame; + const auto frame = not_null{active_frame}; const Prop& prop = double_buffer::detail::Internal_Access::current_prop_layer< Render_Scene_2D::Base_Tag>(object); frame->mark(Frame_Trace_Marker::paint_cache_targets_started); - prepare_paint_targets(object, **frame_target, prop.viewport); + prepare_paint_targets(object, *frame_target, prop.viewport); frame->mark(Frame_Trace_Marker::paint_cache_targets_finished); }); auto render_ready = graph.add("scene.render.ready", [this] { if (!active_frame) throw std::logic_error("2D render lost its active frame"); - (*active_frame)->mark(Frame_Trace_Marker::prepare_finished); - (*active_frame)->mark(Frame_Trace_Marker::paint_started); - (*active_frame)->mark(Frame_Trace_Marker::paint_taskflow_started); + active_frame->mark(Frame_Trace_Marker::prepare_finished); + active_frame->mark(Frame_Trace_Marker::paint_started); + active_frame->mark(Frame_Trace_Marker::paint_taskflow_started); }); auto renderables = graph.compose("scene.renderables", *runtime->taskflow); renderables.describe("dimension", "2D").describe("backend", "Blend2D"); @@ -307,16 +307,16 @@ void Render_Scene_2D::Private::ensure_frame_taskflow(Object* object) { owner->private_data->valid_cache = owner->private_data->paint_target; if (owner->private_data->valid_cache) - (*frame_target)->composite(**owner->private_data->valid_cache); + frame_target->composite(*owner->private_data->valid_cache); } }); auto finish = graph.add("scene.render.complete", [this, object] { auto& state = static_cast( double_buffer::detail::Internal_Access::pending_state(object)); state.event_statistics = event_statistics.state(); - (*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); + 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); }); auto completion = graph.compose("scene.completion", completion_graph); completion.describe("owner", "scene").describe("stage", "frame consumers"); @@ -396,8 +396,8 @@ Render_Scene_2D::Private::render( frame->mark(Frame_Trace_Marker::frame_ready); if (trace_started) aethera::detail::finish_taskflow_trace(*frame); - active_frame.reset(); - frame_target.reset(); + active_frame = nullptr; + frame_target = nullptr; frame_in_flight.store(false, std::memory_order_release); callback(frame); }; @@ -410,8 +410,8 @@ Render_Scene_2D::Private::render( catch (...) { if (trace_started) aethera::detail::finish_taskflow_trace(*frame); - active_frame.reset(); - frame_target.reset(); + active_frame = nullptr; + frame_target = nullptr; frame_in_flight.store(false, std::memory_order_release); throw; } diff --git a/render_3D/render_3D/detail/Datoviz_Visual_Backend.cpp b/render_3D/render_3D/detail/Datoviz_Visual_Backend.cpp index 4ac0fd5..867e852 100644 --- a/render_3D/render_3D/detail/Datoviz_Visual_Backend.cpp +++ b/render_3D/render_3D/detail/Datoviz_Visual_Backend.cpp @@ -1,4 +1,4 @@ -#include "Datoviz_Visual_Backend.hpp" +#include "Datoviz_Visual_Backend.hpp" #include "Exception.hpp" #include #include @@ -1154,11 +1154,11 @@ void Datoviz_Visual_Backend::Private::create_scene( dvz_text_set_style(coordinate_text, &style) != DVZ_OK) throw std::runtime_error("failed to create Datoviz marker coordinate labels"); } - std::optional> field_borrow{}; + DvzSampledField* field_borrow{}; if (sampled_field != nullptr) field_borrow = not_null{sampled_field}; - std::optional> font_borrow{}; + DvzFont* font_borrow{}; if (font != nullptr) font_borrow = not_null{font}; - std::optional> coordinate_text_borrow{}; + DvzText* coordinate_text_borrow{}; if (coordinate_text != nullptr) coordinate_text_borrow = not_null{coordinate_text}; visuals_.push_back( @@ -1312,13 +1312,13 @@ void Datoviz_Visual_Backend::Private::apply_axes( {"stroke_width_px", widths.data(), segment_count} } }; - if (dvz_visual_set_data_many(axes_visual_->get(), updates.data(), + if (dvz_visual_set_data_many(axes_visual_, updates.data(), static_cast(updates.size())) != DVZ_OK || - dvz_visual_set_visible(axes_visual_->get(), !starts.empty()) != DVZ_OK) + dvz_visual_set_visible(axes_visual_, !starts.empty()) != DVZ_OK) throw std::runtime_error("failed to upload Datoviz 3D axes geometry"); for (std::size_t index = 0; index < text_items.size(); ++index) text_items[index].string = strings[index].c_str(); if (dvz_text_set_items( - axes_text_->get(), text_items.empty() ? nullptr : text_items.data(), + axes_text_, text_items.empty() ? nullptr : text_items.data(), static_cast(text_items.size())) != DVZ_OK) throw std::runtime_error("failed to upload Datoviz 3D axes labels"); applied_axes_ = descriptors; @@ -1353,7 +1353,7 @@ void Datoviz_Visual_Backend::Private::apply_camera( camera.projection.ortho_height = static_cast( 2.0 * distance * std::tan(source.vertical_field_of_view_degrees * std::numbers::pi / 360.0)); - if (dvz_panel_set_camera_desc(panel_->get(), &camera) != DVZ_OK) throw std::runtime_error("failed to apply Datoviz Camera component"); + if (dvz_panel_set_camera_desc(panel_, &camera) != DVZ_OK) throw std::runtime_error("failed to apply Datoviz Camera component"); DvzDimMask dimensions = DVZ_DIM_MASK_XYZ; switch (source.controller) { case Camera_Controller::turntable: { @@ -1417,7 +1417,7 @@ void Datoviz_Visual_Backend::Private::apply_camera( } } if (camera_controller_ == nullptr || - dvz_panel_bind_controller(panel_->get(), camera_controller_, dimensions) != DVZ_OK) + dvz_panel_bind_controller(panel_, camera_controller_, dimensions) != DVZ_OK) throw std::runtime_error("failed to bind Datoviz camera controller"); applied_camera_ = source; } @@ -1449,7 +1449,7 @@ std::uint64_t Datoviz_Visual_Backend::Private::apply( const Scene_3D_Parameters& scene, const Prepared_Visual_Batch& prepared, std::uint8_t target_index, bool bind_target) { if (figure_extent_ != scene.viewport) { - if (dvz_figure_resize(figure_->get(), scene.viewport.width, + if (dvz_figure_resize(figure_, scene.viewport.width, scene.viewport.height) != DVZ_OK) throw std::runtime_error("failed to resize Datoviz point figure"); DvzInputResizeEvent resize{ @@ -1735,7 +1735,7 @@ std::uint64_t Datoviz_Visual_Backend::Private::apply_visual( auto* visual = target.visual.get(); const bool structure_changed = !target.applied || !same_visual_structure(*target.applied, point); - const bool coordinate_text_changed = target.coordinate_text.has_value() && + const bool coordinate_text_changed = target.coordinate_text != nullptr && (!target.applied || has_coordinate_labels(*target.applied) || has_coordinate_labels(point)); if (coordinate_text_changed) { @@ -1773,7 +1773,7 @@ std::uint64_t Datoviz_Visual_Backend::Private::apply_visual( } } for (std::size_t index = 0; index < items.size(); ++index) items[index].string = strings[index].c_str(); - if (dvz_text_set_items(target.coordinate_text->get(), + if (dvz_text_set_items(target.coordinate_text, items.empty() ? nullptr : items.data(), static_cast(items.size())) != DVZ_OK) throw std::runtime_error("failed to upload Datoviz marker coordinate labels"); @@ -1974,9 +1974,9 @@ std::uint64_t Datoviz_Visual_Backend::Private::apply_visual( view.rows_per_image = data.field_height; const std::array extent{data.field_width, data.field_height, 1U}; result = extent == target.field_extent - ? dvz_sampled_field_set_data(target.field->get(), &view) + ? dvz_sampled_field_set_data(target.field, &view) : dvz_sampled_field_resize( - target.field->get(), extent[0], extent[1], extent[2], &view); + target.field, extent[0], extent[1], extent[2], &view); if (result != DVZ_OK) break; target.field_extent = extent; const std::array updates{ @@ -2000,9 +2000,9 @@ std::uint64_t Datoviz_Visual_Backend::Private::apply_visual( view.rows_per_image = data.field_height; const std::array extent{data.field_width, data.field_height, 1U}; result = extent == target.field_extent - ? dvz_sampled_field_set_data(target.field->get(), &view) + ? dvz_sampled_field_set_data(target.field, &view) : dvz_sampled_field_resize( - target.field->get(), extent[0], extent[1], extent[2], &view); + target.field, extent[0], extent[1], extent[2], &view); if (result != DVZ_OK) break; target.field_extent = extent; const std::array updates{ @@ -2031,7 +2031,7 @@ std::uint64_t Datoviz_Visual_Backend::Private::apply_visual( case Visual_Family::text: { if (!target.font) throw std::logic_error("Datoviz text visual has no font"); - upload_text(visual, target.font->get(), + upload_text(visual, target.font, prepared_data(point)); break; } @@ -2056,9 +2056,9 @@ std::uint64_t Datoviz_Visual_Backend::Private::apply_visual( const std::array extent{ data.field_width, data.field_height, data.field_depth}; result = extent == target.field_extent - ? dvz_sampled_field_set_data(target.field->get(), &view) + ? dvz_sampled_field_set_data(target.field, &view) : dvz_sampled_field_resize( - target.field->get(), extent[0], extent[1], extent[2], &view); + target.field, extent[0], extent[1], extent[2], &view); if (result == DVZ_OK) target.field_extent = extent; break; } @@ -2169,7 +2169,7 @@ DvzSceneFrameArtifact* Datoviz_Visual_Backend::Private::emit( * N-1's resource state. Replaying retained payloads also compensates for the * Scene-wide dirty flags committed by another target's preceding emission. */ auto* artifact = dvz_figure_emit_frame_with_emitter( - figure_->get(), runtime_slots_[target_index].emitter, true, + figure_, runtime_slots_[target_index].emitter, true, &capabilities, &report, &configuration); if (artifact == nullptr) { std::string message = "failed to emit Datoviz visual frame"; @@ -2303,7 +2303,7 @@ Datoviz_Visual_Backend::Private::prepare( scene, visuals, *target_index, bind_target); if (item_interaction_ != nullptr) { static_cast(dvz_figure_process_queries( - figure_->get(), runtime_slots_[*target_index].runtime, nullptr)); + figure_, runtime_slots_[*target_index].runtime, nullptr)); DvzQueryResult query{}; bool resolved{}; while (dvz_scene_poll_query(scene_, &query)) resolved = true; @@ -2330,7 +2330,7 @@ Datoviz_Visual_Backend::Private::prepare( std::ranges::copy(item_label, query.label); } } - hover_readout_ = dvz_pinned_readout_query(panel_->get(), &query); + hover_readout_ = dvz_pinned_readout_query(panel_, &query); } } } @@ -2521,7 +2521,7 @@ void Datoviz_Visual_Backend::Private::destroy() { hover_readout_ = nullptr; } if (panel_ && input_router_ != nullptr) - (void)dvz_panel_connect_input(panel_->get(), nullptr); + (void)dvz_panel_connect_input(panel_, nullptr); if (gesture_handler_ != nullptr) { dvz_pointer_gesture_handler_destroy(gesture_handler_); gesture_handler_ = nullptr; @@ -2531,12 +2531,12 @@ void Datoviz_Visual_Backend::Private::destroy() { input_router_ = nullptr; } visuals_.clear(); - axes_visual_.reset(); - axes_text_.reset(); + axes_visual_ = nullptr; + axes_text_ = nullptr; applied_camera_.reset(); applied_axes_.reset(); - panel_.reset(); - figure_.reset(); + panel_ = nullptr; + figure_ = nullptr; if (scene_ != nullptr) { dvz_scene_destroy(scene_); scene_ = nullptr; diff --git a/render_3D/render_3D/detail/Datoviz_Visual_Backend.ipp b/render_3D/render_3D/detail/Datoviz_Visual_Backend.ipp index 3361a21..3ccb855 100644 --- a/render_3D/render_3D/detail/Datoviz_Visual_Backend.ipp +++ b/render_3D/render_3D/detail/Datoviz_Visual_Backend.ipp @@ -32,9 +32,9 @@ struct Datoviz_Visual_Backend::Private : Prev_Private { Visual_Identity identity{}; /* Scene 注册的稳定身份。 */ Visual_Family family{Visual_Family::point}; /* 原生 Visual 的确定 family。 */ not_null visual; /* 由 Datoviz Scene 拥有。 */ - std::optional> field{}; /* 仅纹理和体数据 family 使用的 Scene 字段资源。 */ - std::optional> font{}; /* Glyph/Text 使用的 Scene 字体与可增长 atlas 来源。 */ - std::optional> coordinate_text{}; /* Marker 的数据坐标 XYZ 标注。 */ + DvzSampledField* field{}; /* 仅纹理和体数据 family 使用的 Scene 字段资源。 */ + DvzFont* font{}; /* Glyph/Text 使用的 Scene 字体与可增长 atlas 来源。 */ + DvzText* coordinate_text{}; /* Marker 的数据坐标 XYZ 标注。 */ std::array field_extent{}; /* field 当前样本尺寸。 */ std::uint64_t applied_revision{}; /* 此 Visual 已上传的 Prepare 版本。 */ std::array uploaded_data_revisions{}; /* 各物理帧区已写入的数据版本。 */ @@ -114,12 +114,12 @@ struct Datoviz_Visual_Backend::Private : Prev_Private { mutable std::mutex target_mutex_{}; /* 当前 Scene 三个目标槽的唯一生命周期同步源。 */ std::array runtime_slots_{}; /* 三套独立 runtime/emitter 状态机。 */ owner scene_{}; /* 本后端唯一拥有的 Datoviz Scene。 */ - std::optional> figure_{}; /* 当前离屏 Figure。 */ - std::optional> panel_{}; /* 承载全部业务 Visual 的全屏 Panel。 */ + DvzFigure* figure_{}; /* 当前离屏 Figure。 */ + DvzPanel* panel_{}; /* 承载全部业务 Visual 的全屏 Panel。 */ std::vector visuals_{}; /* 按 Scene 稳定身份管理的原生 Visual。 */ std::vector> external_buffers_{}; /* 后端拥有;runtime 销毁后释放。 */ - std::optional> axes_visual_{}; /* 三维主轴、刻度和网格 Segment Visual。 */ - std::optional> axes_text_{}; /* 随相机变换的三维刻度与轴标题。 */ + DvzVisual* axes_visual_{}; /* 三维主轴、刻度和网格 Segment Visual。 */ + DvzText* axes_text_{}; /* 随相机变换的三维刻度与轴标题。 */ owner item_interaction_{}; /* 后端显式销毁的图元交互器。 */ owner hover_readout_{}; /* 后端显式销毁的悬停提示。 */ owner camera_controller_{}; /* 后端显式销毁的相机控制器。 */ diff --git a/render_3D/render_3D/detail/Gpu_Completion_Service.cpp b/render_3D/render_3D/detail/Gpu_Completion_Service.cpp index 46606c8..9360a73 100644 --- a/render_3D/render_3D/detail/Gpu_Completion_Service.cpp +++ b/render_3D/render_3D/detail/Gpu_Completion_Service.cpp @@ -67,13 +67,13 @@ void Gpu_Completion_Service::Reservation::watch(VkDevice device, if (!pending_ || device == VK_NULL_HANDLE || fence == VK_NULL_HANDLE) throw std::logic_error( "GPU completion reservation or fence is invalid"); - (*pending_->service)->watch(pending_, device, fence); + pending_->service->watch(pending_, device, fence); pending_.reset(); } void Gpu_Completion_Service::Reservation::cancel() { if (!pending_) return; - (*pending_->service)->cancel(pending_); + pending_->service->cancel(pending_); pending_.reset(); } @@ -113,12 +113,12 @@ Gpu_Completion_Service::Private::prepare( pending->completion = std::move(completion); pending->on_exception = std::move(on_exception); pending->observe = observe; - pending->service = *object; + pending->service = object; Admission_Result admission{Admission_Result::none}; double_buffer::detail::Internal_Access::update_state< &State::in_flight, &State::peak_in_flight, &State::reservation_count, &State::backpressure_count>( - object->get(), + object, [&](State_Access states) { auto& state = states.template get(); if (state.in_flight >= state.capacity) { @@ -133,10 +133,10 @@ Gpu_Completion_Service::Private::prepare( if (admission != Admission_Result::none) return {{}, admission}; try { - double_buffer::detail::Internal_Access::submit_stream(object->get(), pending); + double_buffer::detail::Internal_Access::submit_stream(object, pending); } catch (...) { - double_buffer::detail::Internal_Access::update_state<&State::in_flight>(object->get(), + double_buffer::detail::Internal_Access::update_state<&State::in_flight>(object, [](State_Access states) { auto& state = states.template get(); if (state.in_flight != 0) --state.in_flight; @@ -161,7 +161,7 @@ void Gpu_Completion_Service::Private::watch( pending->watched_at = std::chrono::steady_clock::now(); pending->status = Gpu_Completion_Pending_Fence::Status::watched; } - double_buffer::detail::Internal_Access::update_state<&State::watched, &State::peak_watched>(object->get(), + double_buffer::detail::Internal_Access::update_state<&State::watched, &State::peak_watched>(object, [](State_Access states) { auto& state = states.template get(); ++state.watched; @@ -183,7 +183,7 @@ void Gpu_Completion_Service::Private::cancel( Gpu_Completion_State Gpu_Completion_Service::Private::state() const { Gpu_Completion_State result{}; - double_buffer::detail::Internal_Access::access_state(object->get(), + double_buffer::detail::Internal_Access::access_state(object, [&](const State& state) { result.capacity = state.capacity; result.in_flight = state.in_flight; @@ -220,8 +220,8 @@ void Gpu_Completion_Service::Private::request_poll( void Gpu_Completion_Service::Private::poll() noexcept { if (stopping.load(std::memory_order_acquire)) return; try { - double_buffer::detail::Internal_Access::exchange_stream(object->get()); - double_buffer::detail::Internal_Access::access_rendering_stream(object->get(), + double_buffer::detail::Internal_Access::exchange_stream(object); + double_buffer::detail::Internal_Access::access_rendering_stream(object, [&](std::span> pending) { active.insert(active.end(), pending.begin(), pending.end()); }); @@ -246,7 +246,7 @@ void Gpu_Completion_Service::Private::poll() noexcept { } double_buffer::detail::Internal_Access::update_state< &State::watched, &State::fault_count, &State::abandoned_count>( - object->get(), + object, [error](State_Access states) { auto& state = states.template get(); if (state.watched != 0) --state.watched; @@ -266,7 +266,7 @@ void Gpu_Completion_Service::Private::poll() noexcept { &State::callback_total_ns, &State::callback_max_ns, &State::callback_failure_count, &State::completion_count, &State::in_flight>( - object->get(), + object, [callback_ns, &callback_failure](State_Access states) { auto& state = states.template get(); state.callback_total_ns += callback_ns; @@ -301,7 +301,7 @@ void Gpu_Completion_Service::Private::poll() noexcept { iterator = active.erase(iterator); double_buffer::detail::Internal_Access::update_state< &State::cancellation_count, &State::in_flight>( - object->get(), + object, [](State_Access states) { auto& state = states.template get(); ++state.cancellation_count; @@ -320,7 +320,7 @@ void Gpu_Completion_Service::Private::poll() noexcept { finish(pending, VK_TIMEOUT, Completion_Error::fence_abandoned); continue; } - double_buffer::detail::Internal_Access::update_state<&State::fence_probe_count>(object->get(), + double_buffer::detail::Internal_Access::update_state<&State::fence_probe_count>(object, [](State_Access states) { ++states.template get().fence_probe_count; }); @@ -342,14 +342,14 @@ void Gpu_Completion_Service::Private::poll() noexcept { now - last_state_publication >= state_publication_interval) { double_buffer::detail::Internal_Access::update_state< &State::active_fences, &State::pending_fences>( - object->get(), + object, [active_count = active.size(), reserved_count]( State_Access states) { auto& state = states.template get(); state.active_fences = active_count; state.pending_fences = reserved_count; }); - double_buffer::detail::Internal_Access::publish_state(object->get()); + double_buffer::detail::Internal_Access::publish_state(object); last_state_publication = now; } diff --git a/render_3D/render_3D/detail/Gpu_Completion_Service.ipp b/render_3D/render_3D/detail/Gpu_Completion_Service.ipp index 66150c2..ba38e6f 100644 --- a/render_3D/render_3D/detail/Gpu_Completion_Service.ipp +++ b/render_3D/render_3D/detail/Gpu_Completion_Service.ipp @@ -17,13 +17,13 @@ struct Gpu_Completion_Pending_Fence { Gpu_Completion_Service::Completion completion{}; /* 完成或隔离后的交付回调。 */ Gpu_Completion_Service::Exception_Handler on_exception{}; /* 回调异常的隔离入口。 */ std::chrono::steady_clock::time_point watched_at{}; /* 开始监视的单调时钟时刻。 */ - std::optional> service{}; /* 不拥有的服务实例。 */ + Gpu_Completion_Service* service{}; /* 不拥有的服务实例。 */ Status status{Status::reserved}; /* service_mutex 下的 reservation 状态。 */ bool observe{}; /* 是否采集 fence 等待耗时。 */ }; struct Gpu_Completion_Service::Private : Prev_Private { using Object = Gpu_Completion_Service; - std::optional> object{}; /* Def 机制所属最终对象;bind 后有效。 */ + Object* object{}; /* Def 机制所属最终对象;bind 后有效。 */ std::mutex service_mutex{}; /* 保护 reservation 状态;fence probe 只在全局 Scheduler 控制线程执行。 */ std::atomic_bool stopping{}; /* Private 析构开始后拒绝新准入。 */ std::vector> active{}; /* 仅 Frame_Scheduler 控制线程访问。 */ @@ -35,9 +35,9 @@ struct Gpu_Completion_Service::Private : Prev_Private { void bind_private_crtp(Attached_Object* attached) { Prev_Private::bind_private_crtp(attached); object = not_null{static_cast(attached)}; - double_buffer::detail::Internal_Access::update_state<&State::capacity>(object->get(), + double_buffer::detail::Internal_Access::update_state<&State::capacity>(object, static_cast(default_capacity)); - double_buffer::detail::Internal_Access::publish_state(object->get()); + double_buffer::detail::Internal_Access::publish_state(object); active.reserve(static_cast(default_capacity)); poll_timer = Frame_Scheduler::instance().make_timer( [this](Frame_Scheduler::Tick) { poll(); }); diff --git a/render_3D/render_3D/detail/Render_Domain.cpp b/render_3D/render_3D/detail/Render_Domain.cpp index eb173b6..6df8964 100644 --- a/render_3D/render_3D/detail/Render_Domain.cpp +++ b/render_3D/render_3D/detail/Render_Domain.cpp @@ -102,7 +102,7 @@ void Render_Domain::run() noexcept { // 不能参与线程退出正确性。 if (!received && stopping_.load(std::memory_order_acquire) && active_posters_.load(std::memory_order_acquire) == 0) { - current_domain_.reset(); + current_domain_ = nullptr; if (destroy_on_exit_.load(std::memory_order_acquire)) { if (thread_.joinable()) thread_.detach(); delete this; diff --git a/render_3D/render_3D/detail/Render_Domain.hpp b/render_3D/render_3D/detail/Render_Domain.hpp index 7326bdf..6c2302c 100644 --- a/render_3D/render_3D/detail/Render_Domain.hpp +++ b/render_3D/render_3D/detail/Render_Domain.hpp @@ -34,7 +34,7 @@ private: void request_stop() noexcept; void run() noexcept; static constexpr std::size_t initial_capacity = 64; - inline static thread_local std::optional> + inline static thread_local Render_Domain* current_domain_{}; /* 当前线程正在执行的 GPU 域。 */ moodycamel::BlockingConcurrentQueue> tasks_{initial_capacity}; /* 多生产者闭包入口与唯一消费队列。 */ std::atomic_size_t active_posters_{}; /* 停止准入边界内仍可能完成入队的生产者数量。 */ diff --git a/render_3D/render_3D/scene/Render_Scene_3D.hpp b/render_3D/render_3D/scene/Render_Scene_3D.hpp index eb4bfd7..bed2bbb 100644 --- a/render_3D/render_3D/scene/Render_Scene_3D.hpp +++ b/render_3D/render_3D/scene/Render_Scene_3D.hpp @@ -43,8 +43,8 @@ struct Render_Scene_3D : Def { void (*bind)(not_null, std::shared_ptr){}; /* 绑定 Scene 拥有的发布上下文。 */ }; std::vector visuals{}; /* 本 Scene 的全部 Visual,注册顺序保持稳定。 */ - std::optional> camera{}; /* 不拥有的 Camera 组件;生命周期必须覆盖 Scene。 */ - std::optional> axes{}; /* 不拥有的三轴组件;生命周期必须覆盖 Scene。 */ + Root* camera{}; /* 不拥有的 Camera 组件;生命周期必须覆盖 Scene。 */ + Root* axes{}; /* 不拥有的三轴组件;生命周期必须覆盖 Scene。 */ using Camera_Read = Camera_Descriptor (*)(not_null); using Axes_Read = std::array (*)(not_null); Camera_Read read_camera{}; diff --git a/render_3D/render_3D/scene/Render_Scene_3D.ipp b/render_3D/render_3D/scene/Render_Scene_3D.ipp index e53fd40..737f2a5 100644 --- a/render_3D/render_3D/scene/Render_Scene_3D.ipp +++ b/render_3D/render_3D/scene/Render_Scene_3D.ipp @@ -28,7 +28,7 @@ struct Render_Scene_3D::Private : Prev_Private { }; struct Frame_Context { std::atomic phase{Frame_Phase::available}; - std::optional> frame{}; + Frame_3D* frame{}; Event_Batch events{}; bool trace_started{}; bool overlaps_gpu{}; @@ -60,11 +60,11 @@ struct Render_Scene_3D::Private : Prev_Private { std::atomic> submitted_callback{}; std::shared_ptr backend{}; /* Scene 拥有的异步后端;已入队命令自行延长实现寿命。 */ std::shared_ptr paint_context{}; /* Paint 节点读取 Scene 当前 Prop 的生命周期门闩。 */ - std::optional> camera_component{}; /* Builder 绑定的 Camera 组件。 */ - std::optional> axes_component{}; /* Builder 绑定的三轴组件。 */ + Root* camera_component{}; /* Builder 绑定的 Camera 组件。 */ + Root* axes_component{}; /* Builder 绑定的三轴组件。 */ Camera_Descriptor (*read_camera)(not_null){}; /* 读取 Camera 当前配置。 */ std::array (*read_axes)(not_null){}; /* 读取三轴当前配置。 */ - std::optional> active_context{}; /* 只由唯一 CPU Prepare 图访问。 */ + Frame_Context* active_context{}; /* 只由唯一 CPU Prepare 图访问。 */ std::unique_ptr frame_taskflow{}; /* Builder 内部初始化后端;必须在绑定 Visual Paint 目标之前调用一次。 */ template @@ -74,7 +74,7 @@ struct Render_Scene_3D::Private : Prev_Private { Camera_Descriptor (*camera_reader)(not_null), std::array (*axes_reader)(not_null)); template [[nodiscard]] detail::Scene_3D_Parameters parameters(Object* object) const; - [[nodiscard]] std::optional> context_for( + [[nodiscard]] Frame_Context* context_for( not_null frame) noexcept; template void try_release_prepare( Object* object, not_null context); @@ -189,7 +189,7 @@ std::expected, Dependency_Graph_Error> Render_Scene_3D:: reinterpret_cast(visual.object.get()), visual.family}); private_data.initialize_backend(scene.get(), gpu_index, validation_enabled, std::move(registrations), - camera.value(), axes.value(), read_camera, + camera, axes, read_camera, read_axes); for (const auto& visual : visuals) visual.bind(visual.object, private_data.paint_context); return scene; @@ -198,14 +198,14 @@ template detail::Scene_3D_Parameters Render_Scene_3D::Private::parameters(Object* object) const { const Prop& prop = static_cast( double_buffer::detail::Internal_Access::current_prop(object)); - const auto axis = read_axes(axes_component.value()); + const auto axis = read_axes(axes_component); return {prop.viewport, prop.clear_color, - read_camera(camera_component.value()), axis[0], axis[1], axis[2]}; + read_camera(camera_component), axis[0], axis[1], axis[2]}; } template void Render_Scene_3D::Private::complete_frame( Object*, not_null context) { - const auto frame = context->frame.value(); + const auto frame = context->frame; const auto callback = frame_callback.load(std::memory_order_acquire); frame->mark(Frame_Trace_Marker::frame_ready); if (context->trace_started) @@ -213,7 +213,7 @@ void Render_Scene_3D::Private::complete_frame( /* Scene 仅借用外部 Frame。完成通知前必须先清除所有借用引用并开放 * Frame_Context;回调之后是否发布、统计或复用只由调用方帧策略决定。 */ - context->frame.reset(); + context->frame = nullptr; context->events.clear(); context->failure = {}; context->trace_started = false; @@ -225,13 +225,13 @@ void Render_Scene_3D::Private::complete_frame( if (callback && *callback) (*callback)(frame); } -inline std::optional> +inline Render_Scene_3D::Private::Frame_Context* Render_Scene_3D::Private::context_for(not_null frame) noexcept { - const auto found = std::ranges::find(frame_contexts, frame, + const auto found = std::ranges::find(frame_contexts, frame.get(), &Frame_Context::frame); return found == frame_contexts.end() - ? std::optional>{} - : std::optional>{not_null{&*found}}; + ? nullptr + : &*found; } template @@ -251,7 +251,7 @@ void Render_Scene_3D::Private::try_release_prepare( if (const auto callback = submitted_callback.load(std::memory_order_acquire); callback && *callback) - (*callback)(context->frame.value(), context->overlaps_gpu); + (*callback)(context->frame, context->overlaps_gpu); if (context->gpu_completed.load(std::memory_order_acquire)) arm_completion(object); } @@ -270,15 +270,15 @@ void Render_Scene_3D::Private::arm_completion(Object* object) { template void Render_Scene_3D::Private::consume_completion(Object* object) { - std::optional> selected{}; + Frame_Context* selected{}; for (auto& context : frame_contexts) { if (context.phase.load(std::memory_order_acquire) != Frame_Phase::submitted || !context.gpu_completed.load(std::memory_order_acquire)) continue; - if (!selected || context.frame.value()->identity().sequence < - (*selected)->frame.value()->identity().sequence) - selected = not_null{&context}; + if (!selected || context.frame->identity().sequence < + selected->frame->identity().sequence) + selected = &context; } if (!selected) { completion_busy.store(false, std::memory_order_release); @@ -291,17 +291,17 @@ void Render_Scene_3D::Private::consume_completion(Object* object) { return; } auto expected = Frame_Phase::submitted; - if (!(*selected)->phase.compare_exchange_strong( + if (!selected->phase.compare_exchange_strong( expected, Frame_Phase::completing, std::memory_order_acq_rel, std::memory_order_acquire)) { completion_busy.store(false, std::memory_order_release); arm_completion(object); return; } - const auto frame = (*selected)->frame.value(); + const auto frame = selected->frame; frame->mark(Frame_Trace_Marker::paint_finished); frame->mark(Frame_Trace_Marker::scene_render_finished); - auto done = [this, object, selected = *selected] { + auto done = [this, object, selected] { complete_frame(object, selected); completion_busy.store(false, std::memory_order_release); arm_completion(object); @@ -350,12 +350,12 @@ void Render_Scene_3D::Private::ensure_frame_taskflow(Object* object) { graph.describe("owner_kind", "scene") .describe("owner_component", "scene"); auto begin = graph.add("scene.begin", [this, object] { - if (!active_context || !(*active_context)->frame) + if (!active_context || !active_context->frame) throw std::logic_error("3D frame DAG lost its active frame"); - const auto frame = (*active_context)->frame.value(); + const auto frame = active_context->frame; frame->mark(Frame_Trace_Marker::scene_render_started); frame->mark(Frame_Trace_Marker::event_dispatch_started); - (*active_context)->events = take_events(object); + active_context->events = take_events(object); frame->mark(Frame_Trace_Marker::event_dispatch_finished); auto context = std::static_pointer_cast< @@ -372,8 +372,8 @@ void Render_Scene_3D::Private::ensure_frame_taskflow(Object* object) { if (context->visuals.use_count() != 1) context->visuals = std::make_shared(*context->visuals); - camera_component.value()->advance_object(); - axes_component.value()->advance_object(); + camera_component->advance_object(); + axes_component->advance_object(); frame->mark(Frame_Trace_Marker::prepare_started); frame->mark(Frame_Trace_Marker::prepare_finished); frame->mark(Frame_Trace_Marker::paint_started); @@ -387,7 +387,7 @@ void Render_Scene_3D::Private::ensure_frame_taskflow(Object* object) { auto submit = graph.add("scene.backend.submit", [this, object] { const auto execution = active_context; - if (!execution || !(*execution)->frame) + if (!execution || !execution->frame) throw std::logic_error("3D submit lost its frame context"); auto context = std::static_pointer_cast< detail::Scene_Paint_Context>(paint_context); @@ -399,16 +399,16 @@ void Render_Scene_3D::Private::ensure_frame_taskflow(Object* object) { state.event_statistics = event_statistics.state(); context->backend->render( context->visuals, context->parameters, - (*execution)->frame.value(), std::move((*execution)->events), + execution->frame, std::move(execution->events), [this, object](not_null submitted, bool overlaps_gpu) { const auto frame_context = context_for(submitted); if (!frame_context) throw std::logic_error( "3D backend submitted a frame not borrowed by Scene"); - (*frame_context)->overlaps_gpu = overlaps_gpu; - (*frame_context)->gpu_submitted.store( + frame_context->overlaps_gpu = overlaps_gpu; + frame_context->gpu_submitted.store( true, std::memory_order_release); - try_release_prepare(object, *frame_context); + try_release_prepare(object, frame_context); }, [this, object](not_null completed, std::exception_ptr failure) { @@ -416,18 +416,18 @@ void Render_Scene_3D::Private::ensure_frame_taskflow(Object* object) { if (!frame_context) throw std::logic_error( "3D backend completed a frame not borrowed by Scene"); - (*frame_context)->failure = std::move(failure); - if ((*frame_context)->failure && - !(*frame_context)->gpu_submitted.load( + frame_context->failure = std::move(failure); + if (frame_context->failure && + !frame_context->gpu_submitted.load( std::memory_order_acquire)) { - (*frame_context)->overlaps_gpu = false; - (*frame_context)->gpu_submitted.store( + frame_context->overlaps_gpu = false; + frame_context->gpu_submitted.store( true, std::memory_order_release); } - (*frame_context)->gpu_completed.store( + frame_context->gpu_completed.store( true, std::memory_order_release); - try_release_prepare(object, *frame_context); - if ((*frame_context)->phase.load(std::memory_order_acquire) == + try_release_prepare(object, frame_context); + if (frame_context->phase.load(std::memory_order_acquire) == Frame_Phase::submitted) arm_completion(object); }); @@ -451,13 +451,13 @@ Render_Scene_3D::Render_Result Render_Scene_3D::Private::render( inactive, true, std::memory_order_acq_rel, std::memory_order_acquire)) return Render_Result::frame_pipeline_busy; - std::optional> execution{}; + Frame_Context* execution{}; for (auto& context : frame_contexts) { auto expected = Frame_Phase::available; if (context.phase.compare_exchange_strong( expected, Frame_Phase::preparing, std::memory_order_acq_rel, std::memory_order_acquire)) { - execution = not_null{&context}; + execution = &context; break; } } @@ -465,18 +465,18 @@ Render_Scene_3D::Render_Result Render_Scene_3D::Private::render( prepare_active.store(false, std::memory_order_release); return Render_Result::frame_pipeline_busy; } - (*execution)->frame = frame; - (*execution)->events.clear(); - (*execution)->failure = {}; - (*execution)->trace_started = false; - (*execution)->overlaps_gpu = false; - (*execution)->cpu_finished.store(false, std::memory_order_relaxed); - (*execution)->gpu_submitted.store(false, std::memory_order_relaxed); - (*execution)->gpu_completed.store(false, std::memory_order_relaxed); - active_context = *execution; - const auto reject_frame = [this, execution = *execution](Render_Result result) { - active_context.reset(); - execution->frame.reset(); + execution->frame = frame; + execution->events.clear(); + execution->failure = {}; + execution->trace_started = false; + execution->overlaps_gpu = false; + execution->cpu_finished.store(false, std::memory_order_relaxed); + execution->gpu_submitted.store(false, std::memory_order_relaxed); + execution->gpu_completed.store(false, std::memory_order_relaxed); + active_context = execution; + const auto reject_frame = [this, execution](Render_Result result) { + active_context = nullptr; + execution->frame = nullptr; execution->phase.store(Frame_Phase::available, std::memory_order_release); prepare_active.store(false, std::memory_order_release); return result; @@ -503,17 +503,17 @@ Render_Scene_3D::Render_Result Render_Scene_3D::Private::render( throw; } frame->mark(Frame_Trace_Marker::scene_render_requested); - (*execution)->trace_started = + execution->trace_started = aethera::detail::begin_taskflow_trace(*frame); try { - auto completion = [this, object, execution = *execution] { + auto completion = [this, object, execution] { double_buffer::detail::Internal_Access:: current_dependency_graph(object).for_each_bound( [](Renderable* renderable, Renderable::Private& data) { data.publish_renderable_state(renderable); }); double_buffer::detail::Internal_Access::publish_state(object); - active_context.reset(); + active_context = nullptr; execution->cpu_finished.store(true, std::memory_order_release); try_release_prepare(object, execution); }; @@ -524,9 +524,9 @@ Render_Scene_3D::Render_Result Render_Scene_3D::Private::render( aethera::detail::run_taskflow(*frame_taskflow, std::move(completion)); } catch (...) { - if ((*execution)->trace_started) { + if (execution->trace_started) { aethera::detail::finish_taskflow_trace(*frame); - (*execution)->trace_started = false; + execution->trace_started = false; } reject_frame(Render_Result::backend_unavailable); throw; diff --git a/web_server/src/Gallery_Video_Stream.cpp b/web_server/src/Gallery_Video_Stream.cpp index 8966f96..7d1310a 100644 --- a/web_server/src/Gallery_Video_Stream.cpp +++ b/web_server/src/Gallery_Video_Stream.cpp @@ -284,8 +284,8 @@ void Gallery_Video_Stream::Private::update_metrics( metric_pixel_start = pixel_frame_count; metric_sample_start = sampler_state.sampled_frames; metric_clock_start = clock_total; - (*object)->template update_state<&State::diagnostics>(std::move(output)); - (*object)->template publish_state(); + object->template update_state<&State::diagnostics>(std::move(output)); + object->template publish_state(); } void Gallery_Video_Stream::Private::sample_media_frame() { active_sample.reset(); diff --git a/web_server/src/Gallery_Video_Stream.ipp b/web_server/src/Gallery_Video_Stream.ipp index bc5d156..7b58019 100644 --- a/web_server/src/Gallery_Video_Stream.ipp +++ b/web_server/src/Gallery_Video_Stream.ipp @@ -20,7 +20,7 @@ struct Gallery_Video_Stream::Private : Prev_Private { std::shared_ptr diagnostics{}; /* 连接自己的传输诊断来源。 */ }; using Consumers = std::vector; - std::optional> object{}; /* Def 机制所属最终对象;bind_private_crtp 后有效。 */ + Object* object{}; /* Def 机制所属最终对象;bind_private_crtp 后有效。 */ Sliding_Statistics compose_ms{600}; /* 图集采样与变更槽复制统计计算器。 */ Sliding_Statistics sample_delay_ms{600}; /* 固定 deadline 到实际采样的调度延迟。 */ Sliding_Statistics encode_ms{600}; /* H.264 编码统计计算器。 */ diff --git a/web_server/tests/Gallery_Plots_Tests.cpp b/web_server/tests/Gallery_Plots_Tests.cpp index ee32acb..ee38526 100644 --- a/web_server/tests/Gallery_Plots_Tests.cpp +++ b/web_server/tests/Gallery_Plots_Tests.cpp @@ -21,7 +21,7 @@ TEST(Gallery_Plots, Catalog_Is_Complete_And_Unique) { << "duplicate plot id: " << definition.id; const auto found = find_gallery_plot_definition(definition.id); ASSERT_TRUE(found); - EXPECT_EQ(found->get(), &definition); + EXPECT_EQ(found, &definition); has_2d |= definition.dimension == Plot_Dimension::two_d; has_3d |= definition.dimension == Plot_Dimension::three_d; }