diff --git a/a.md b/a.md new file mode 100644 index 0000000..adc6fae --- /dev/null +++ b/a.md @@ -0,0 +1,1919 @@ +下面这份直接作为 Codex 的完整执行计划使用。所有条目都按强制要求执行,不保留旧实现,不增加兼容层,不引入独立 PerfTrace 库。 + +# Renderive Kernel Render DAG 完整改造执行计划 + +## 0. 总目标 + +将 Plot 内部渲染执行统一改造成: + +```text +Renderable 状态 + + +Renderable 数据依赖 + + +Renderable 图层依赖 + + +prepare_buffer 状态 + + +paint_buffer 状态 + + +Renderable 内部任务 DAG + │ + ▼ + render_plan compiler + │ + ▼ + ONE Render DAG + │ + ▼ + ONE Taskflow + │ + ▼ + Frame 执行 + │ + ├── 普通运行:不记录详细性能 + │ + └── Capture:直接写 Frame execution slots + │ + ▼ + 三缓冲完成交换 + │ + ▼ + immutable frame_snapshot + │ + ┌─────┴────────┐ + ▼ ▼ +frame_analysis node_statistics + │ │ + └──────┬───────┘ + ▼ + 后端 API + │ + ▼ +前端 DAG + Worker Timeline + 多帧统计 +``` + +Kernel Render DAG 必须同时成为: + +```text +执行依赖的唯一事实来源 +Taskflow 编译的唯一事实来源 +单帧性能分析的唯一事实来源 +前端 DAG 展示的唯一事实来源 +``` + +--- + +# 1. 锁死 Renderable 三阶段语义 + +Renderable 的执行语义必须明确分成: + +```text +prepare + ↓ +paint + ↓ +composite +``` + +这三个阶段不是全局阶段,不允许建立全局 barrier。 + +它们是每个 Renderable 以及 Renderable 内部任务自己的局部依赖关系。 + +必须严格执行以下语义。 + +### `prepare` + +`prepare` 必须完成: + +```text +读取自身业务数据 +读取其他 Renderable 提供的数据 +处理所有跨 Renderable dependency +坐标转换 +数据转换 +几何计算 +可见范围计算 +裁剪信息计算 +布局结果准备 +分片 +索引生成 +raster 前置数据生成 +paint 所需全部输入生成 +``` + +`prepare` 最终必须只产生当前 Renderable 自己的: + +```text +prepare_buffer +``` + +### `paint` + +`paint` 必须满足: + +```text +禁止读取其他 Renderable +禁止解析 dependency +禁止查询其他 Renderable 当前状态 +禁止重新执行跨 Renderable 数据计算 +禁止临时从 Axis/Series/其他对象读取绘制输入 +``` + +`paint` 只能读取: + +```text +自身 prepare_buffer +自身 paint-only 配置 +自身 paint target +``` + +`paint` 只能写: + +```text +自身 paint_buffer +``` + +必须满足这个验证原则: + +> 将其他 Renderable 全部从 `paint` 的可访问环境中移除,只保留当前 Renderable 的 `prepare_buffer`,`paint` 仍必须能够完整正确执行。 + +### `composite` + +`composite` 必须: + +```text +只读取已经完成的 paint_buffer +按照 layer 关系完成图层组合 +不读取 Renderable 原始业务数据 +不执行 prepare 计算 +``` + +--- + +# 2. 建立强类型 `prepare_buffer` + +每一个需要 prepare 数据的 Renderable 必须拥有自己的强类型 Prepare Buffer。 + +禁止实现: + +```cpp +std::unordered_map +``` + +禁止通过通用字符串字段传递 Renderable 绘制输入。 + +例如: + +```text +Axis +└── axis_prepare_buffer + +Spectrum +└── spectrum_prepare_buffer + +Waterfall +└── waterfall_prepare_buffer + +Afterglow +└── afterglow_prepare_buffer +``` + +具体字段由各 Renderable 自己定义。 + +例如 Waterfall: + +```text +waterfall_prepare_buffer +├── transformed_data +├── visible_range +├── partition_description +├── chunk_metadata +├── raster_parameters +└── 其他 paint 必需数据 +``` + +Paint 不得绕过这些结构重新访问数据源。 + +--- + +# 3. 审计并拆除所有混合 Prepare/Paint 实现 + +逐个审计所有 Renderable。 + +重点处理: + +```text +Waterfall +Spectrum +Afterglow +``` + +同时扫描其他 Renderable 中: + +```text +paint() +build_paint_task_graph() +render() +raster() +``` + +内部是否仍包含: + +```text +数据变换 +跨 Renderable 查询 +坐标计算 +布局计算 +分片 +输入数据准备 +``` + +全部迁移到 Prepare。 + +原来的混合实现直接删除。 + +不得保留: + +```text +旧 paint path ++ +新 prepare path +``` + +两套兼容逻辑。 + +绘制结果必须保持一致。 + +--- + +# 4. Prepare Buffer 与 Paint Buffer 完全独立缓存 + +每个 Renderable 必须独立维护: + +```text +prepare_buffer validity + +paint_buffer validity +``` + +不得继续使用一个 Renderable 整体缓存状态同时代表两者。 + +缓存关系必须严格满足: + +```text +prepare invalid + ↓ +paint 必须 invalid +``` + +但: + +```text +paint invalid +``` + +不得无条件导致: + +```text +prepare invalid +``` + +--- + +# 5. 建立独立 Prepare/Paint Revision + +现有 revision/cache revision 体系必须拆到能够分别回答: + +```text +当前 prepare_buffer 是否仍有效 + +当前 paint_buffer 是否仍有效 +``` + +至少在语义上存在: + +```text +prepare input revision +prepare output revision + +paint input revision +paint output revision +``` + +具体字段名称保持现有项目命名风格。 + +### Prepare 有效性必须依赖 + +```text +自身数据 revision +依赖 Renderable 提供的数据 revision +会影响 prepare 的尺寸/范围/坐标参数 +prepare-only properties +``` + +### Paint 有效性必须依赖 + +```text +当前 prepare_buffer revision +paint-only properties +影响 raster/output 的尺寸和配置 +``` + +例如: + +```text +仅颜色变化 +``` + +必须能够得到: + +```text +prepare cache = valid +paint cache = invalid +``` + +例如: + +```text +依赖 Axis 范围变化 +``` + +必须得到: + +```text +prepare cache = invalid +paint cache = invalid +``` + +--- + +# 6. 修正 Dependency Invalidations + +跨 Renderable dependency 必须只描述 Prepare 数据依赖。 + +例如: + +```text +Series depends on Axis +``` + +必须编译成: + +```text +Axis Prepare + ↓ +Series Prepare +``` + +不得编译成: + +```text +Axis Paint + ↓ +Series Prepare +``` + +不得继续使用: + +```text +整个 Renderable A + ↓ +整个 Renderable B +``` + +这种粗粒度执行约束。 + +Prepare 数据变化向 dependency 后继传播: + +```text +A prepare invalid + ↓ +B prepare invalid + ↓ +B paint invalid +``` + +Paint-only 状态变化不得传播到其他 Renderable 的 Prepare: + +```text +A paint invalid +``` + +不得导致: + +```text +B prepare invalid +``` + +--- + +# 7. 建立 Renderable DAG Builder 接口 + +Renderable 必须正式提供建图能力。 + +保留运行时多态。 + +建立与现有类体系匹配的虚函数,语义必须等价于: + +```cpp +virtual void build_prepare_graph(render_graph_builder &builder); +virtual void build_paint_graph(render_graph_builder &builder); +``` + +两个函数必须写入同一个: + +```text +render_graph_builder +``` + +不得分别生成两张最终 Graph。 + +简单 Renderable: + +```text +Prepare + ↓ +Paint +``` + +复杂 Renderable 可以展开自己的内部 DAG。 + +--- + +# 8. Waterfall 必须展开真实内部 DAG + +Waterfall 分片必须全部成为正式 Node。 + +不得把分片隐藏为一个 Waterfall Task 内部的匿名并行执行。 + +例如: + +```text +Waterfall Prepare + │ + ├── Chunk 0 Prepare -> Chunk 0 Paint ─┐ + ├── Chunk 1 Prepare -> Chunk 1 Paint ─┤ + ├── Chunk 2 Prepare -> Chunk 2 Paint ─┤ + └── Chunk N Prepare -> Chunk N Paint ─┤ + ▼ + Merge +``` + +如果某些分片只需要单独的 Prepare 或单独 Paint,则按真实依赖建立节点。 + +不得为了统一结构制造不存在的任务。 + +动态 Chunk 必须拥有稳定逻辑 Node ID。 + +--- + +# 9. `prepare -> paint` 只能是局部依赖 + +禁止建立: + +```text +所有 Prepare 完成 + ↓ +所有 Paint 开始 +``` + +这种全局 Barrier。 + +必须形成流水式 DAG: + +```text +Prepare A -> Paint A + +Prepare B -------------> Paint B + +Prepare C -> Paint C +``` + +因此实际运行允许: + +```text +Prepare A 完成 + ↓ +Paint A 开始 +``` + +同时: + +```text +Prepare B +Prepare C +``` + +仍在执行。 + +这条必须通过并发测试明确验证。 + +--- + +# 10. Paint 必须最大程度并行 + +所有 Renderable 的 Paint 必须只写自己的独立: + +```text +paint_buffer +``` + +因此: + +```text +Axis Paint +Series Paint +Waterfall Paint +Legend Paint +``` + +在不存在真实 Paint 依赖的情况下必须允许完全并行。 + +不得直接将: + +```text +layer_node_ +``` + +转换为: + +```text +Paint A -> Paint B +``` + +从而人为串行化 Paint。 + +--- + +# 11. Layer 关系必须编译到 Composite + +图层顺序必须主要落实到 Composite DAG。 + +例如: + +```text +Background Paint +Series Paint +Axis Paint +``` + +可以并行。 + +最终: + +```text +Background Paint + ↓ +Composite Background + │ +Series Paint + └────> Composite Series + │ +Axis Paint + └────> Composite Axis +``` + +具体依赖按照现有 Layer Graph 的真实顺序生成。 + +必须保持最终像素结果与原实现一致。 + +--- + +# 12. 最终只有一张 Render DAG + +最终 Compiler 输入必须为: + +```text +Renderable internal graph ++ +dependency_node_ ++ +layer_node_ ++ +prepare_buffer validity ++ +paint_buffer validity ++ +Renderable 当前状态 +``` + +输出必须只有: + +```text +render_plan +└── graph +``` + +Graph 内统一存在: + +```text +prepare node +paint node +composite node +``` + +不得生成: + +```text +prepare_graph +paint_graph +``` + +两张最终执行图。 + +--- + +# 13. Buffer 必须在建图阶段裁剪任务 + +缓存命中的工作必须直接从本帧有效 DAG 中删除。 + +禁止继续生成: + +```text +cache_hit +``` + +空 Task。 + +完整路径: + +```text +Prepare + ↓ +Paint + ↓ +Composite +``` + +### Prepare Cache Hit + +本帧必须生成: + +```text +Paint + ↓ +Composite +``` + +Paint 直接读取已有 Prepare Buffer。 + +### Paint Cache Hit + +本帧必须生成: + +```text +Composite +``` + +Composite 直接读取已有 Paint Buffer。 + +### Prepare/Paint 均失效 + +生成: + +```text +Prepare + ↓ +Paint + ↓ +Composite +``` + +--- + +# 14. 建立稳定 `node_id` + +Kernel/Plot 必须统一分配 DAG Node 身份。 + +所有逻辑任务必须拥有: + +```text +node_id +``` + +包括: + +```text +Prepare +Paint +Composite +Waterfall dynamic chunks +内部 merge +内部 geometry task +``` + +规则严格保持: + +```text +逻辑任务身份不变 +=> node_id 不变 +``` + +```text +逻辑任务语义改变 +=> 分配新 node_id +``` + +```text +node 退休 +=> node_id 永不复用 +``` + +--- + +# 15. Node 必须带最小元数据 + +每个 Render Node 至少包含: + +```text +node_id +owner/renderable id +name +kind +execution_index +``` + +`kind` 至少有: + +```text +prepare +paint +composite +``` + +不提前添加无实际使用场景的分类。 + +--- + +# 16. 建立单一 `render_plan` + +建立: + +```text +render_plan +├── version +└── graph + ├── nodes + └── edges +``` + +`render_plan` 必须代表: + +> 当前 Plot 在当前 Renderable、Dependency、Layer、Prepare Cache、Paint Cache 状态下真正会执行的完整 Render DAG。 + +--- + +# 17. 建立 `render_plan_version` + +以下任一情况发生时必须产生新 Version: + +```text +active node set 改变 +Node 增加 +Node 删除 +Edge 改变 +Waterfall 分片拓扑变化 +dependency topology 变化 +layer/composite topology 变化 +prepare cache 裁剪结果变化 +paint cache 裁剪结果变化 +``` + +仅以下变化不得产生新 Version: + +```text +Task 实际耗时变化 +Task Worker 改变 +调度顺序不同 +实际并行程度不同 +``` + +--- + +# 18. Render Plan 编译成一个 Taskflow + +必须满足: + +```text +1 Render Node += +1 Taskflow Task +``` + +以及: + +```text +1 Render Edge += +1 Taskflow dependency +``` + +最终: + +```text +render_plan.graph + ↓ +Taskflow + ↓ +Taskflow Executor +``` + +不得生成两个前后 Taskflow。 + +--- + +# 19. Taskflow 只遵守偏序关系 + +DAG Edge 只表示: + +```text +B 不能在 A 完成以前执行 +``` + +没有 Edge 的 Node 必须交由 Taskflow 自由调度。 + +不得写: + +```text +固定 Worker +固定执行次序 +手工串行队列 +``` + +除非任务本身存在真实依赖。 + +--- + +# 20. 性能采集必须请求式开启 + +正常运行: + +```text +capture = disabled +``` + +不得常驻记录完整 Node 时序。 + +必须实现: + +```text +capture_next_frame() +``` + +以及: + +```text +capture_frames(count) +``` + +例如: + +```text +capture_frames(20) +``` + +必须从后续完整 Render Frame 开始连续捕获 20 帧,然后自动停止。 + +--- + +# 21. Capture Controller 必须保持极简 + +Plot/Kernel 内维护: + +```text +capture enabled +remaining frame count +capture session id +``` + +使用必要的原子状态实现。 + +不得增加: + +```text +Event system +Record system +MPSC performance queue +TLS trace system +collector thread +``` + +--- + +# 22. Frame 必须持有 Capture 状态 + +每个正在 Render 的 Frame 在开始执行时必须确定: + +```text +capture_this_frame +``` + +该状态在本 Frame 生命周期内保持不变。 + +不得执行到一半再打开 Capture。 + +--- + +# 23. Frame 性能数据使用预分配 List + +不得在 Task 热路径使用: + +```text +map +unordered_map +``` + +Render Plan 编译时给每个 Node 分配: + +```text +execution_index +``` + +例如: + +```text +Node #100 -> slot 0 +Node #101 -> slot 1 +Node #203 -> slot 2 +``` + +Capture Frame 创建: + +```text +node_execution[] +``` + +长度直接根据 Plan 确定。 + +--- + +# 24. `node_execution` 必须记录真实时序 + +至少记录: + +```text +node_id +start_time +end_time +worker_id +status +``` + +`duration` 不重复存储: + +```text +duration = end_time - start_time +``` + +时间源必须保持单调并能在 Worker 间比较。 + +--- + +# 25. Taskflow Worker 只能写自己的 Execution Slot + +编译 Taskflow 时将: + +```text +Task +``` + +直接绑定: + +```text +execution_index +``` + +Capture 开启: + +```text +execution.start = now +execution.worker = worker + +执行真实任务 + +execution.end = now +execution.status = complete +``` + +不同 Task 不得写同一个 slot。 + +热路径不得用 mutex 保护整个性能数据结构。 + +--- + +# 26. Capture 关闭时不得采集详细时间 + +Capture 关闭时 Task 必须直接进入正常执行路径。 + +不得仍然执行: + +```text +now() +start record +end record +statistics aggregation +``` + +只保留最小 capture branch。 + +--- + +# 27. Renderable 自定义指标直接写 Execution Slot + +Renderable 实现允许对自己当前 Node 补充固定性能指标。 + +例如 Waterfall Prepare: + +```text +input_count +chunk_size +prepared_cells +``` + +Paint: + +```text +pixel_count +primitive_count +``` + +不得在 Worker 热路径实现: + +```cpp +unordered_map +``` + +使用固定结构或固定 metric slot。 + +--- + +# 28. Frame Snapshot 必须绑定 `render_plan_version` + +Capture Frame 至少包含: + +```text +frame_id +render_plan_version +render_start +render_end +node_execution[] +``` + +不再创建: + +```text +Trace +Span +Record +Event +``` + +等额外运行层。 + +一帧自己的执行数据就是唯一运行快照。 + +--- + +# 29. 三缓冲交换必须成为 Snapshot 发布边界 + +运行阶段: + +```text +working frame + │ +Taskflow 写 execution slots + │ +render complete + │ +triple-buffer exchange + │ +completed frame +``` + +进入 Completed 后: + +```text +frame performance +``` + +必须不可再修改。 + +外部读取 Completed Frame 时不得需要锁住 Taskflow Worker。 + +--- + +# 30. 建立 Capture Repository + +三缓冲不负责保存历史性能帧。 + +Capture 请求的 Completed Frame 必须在冷路径复制/移动到: + +```text +capture_repository +``` + +结构: + +```text +capture_session +├── session_id +├── requested_count +├── captured_count +├── active +└── frame_snapshots[] +``` + +抓取达到请求数量以后必须自动结束 Session。 + +--- + +# 31. 保存 Render Plan 历史结构 + +必须能够: + +```text +render_plan_version + ↓ +找到当时对应的 Render DAG +``` + +连续 20 帧过程中允许: + +```text +Frame 100 -> Plan 30 +Frame 101 -> Plan 30 +Frame 102 -> Plan 31 +Frame 103 -> Plan 32 +``` + +每帧必须绑定自己真实执行的 Plan Version。 + +不得假定一个 Capture Session 只有一个 DAG Version。 + +--- + +# 32. 实现 `frame_analysis` + +所有重分析必须发生在 Render 热路径之外。 + +输入: + +```text +render_plan ++ +frame_snapshot +``` + +输出: + +```text +frame_analysis +``` + +至少计算: + +```text +total_render_duration +node_duration +node_start_offset +node_end_offset +dependency_ready_time +scheduler_wait +critical_path +critical_path_duration +critical_path_contribution +total_work_duration +peak_parallelism +average_parallelism +parallel_overlap +worker utilization +bottleneck nodes +``` + +--- + +# 33. Scheduler Wait 必须从 DAG 和实际时间推导 + +对于: + +```text +A ─┐ + ├──> C +B ─┘ +``` + +计算: + +```text +dependency_ready(C) += +max(A.end, B.end) +``` + +然后: + +```text +scheduler_wait(C) += +C.start - dependency_ready(C) +``` + +Source Node: + +```text +dependency_ready += +render_start +``` + +不得为了获得 Wait 再增加事件采集系统。 + +--- + +# 34. 正确处理并行节点的耗时占比 + +不得简单计算: + +```text +node_duration / sum(all node durations) +``` + +作为“帧耗时占比”。 + +必须明确输出不同指标: + +```text +work_contribution +critical_path_contribution +``` + +并行节点允许 Work Duration 总和远大于 Wall Duration。 + +--- + +# 35. 实现 `node_statistics` + +按稳定: + +```text +node_id +``` + +聚合多帧数据。 + +至少维护: + +```text +execution_count +average +moving_average +p50 +p95 +p99 +min +max +critical_path_frequency +average_scheduler_wait +``` + +短生命周期 Waterfall Node 同样统计。 + +--- + +# 36. 支持按 `render_plan_version` 聚合 + +必须支持: + +```text +Plan v30 +├── frame... +└── statistics + +Plan v31 +├── frame... +└── statistics +``` + +用于直接比较 DAG 编译策略变化后的性能。 + +例如: + +```text +Plan v30 P95 render = 8.4 ms +Plan v31 P95 render = 6.1 ms +``` + +--- + +# 37. 后端增加 Capture API + +按照当前后端/Adminive API 风格接入。 + +必须提供等价能力: + +```text +请求下一帧 +请求连续 N 帧 +查看 capture 状态 +查看 capture session +查看 captured frame 列表 +读取单帧 snapshot +读取单帧 analysis +读取 render plan topology +读取 node statistics +读取 plan version statistics +``` + +禁止为了这些 API 重新实时读取 Worker 数据。 + +接口只读取: + +```text +completed snapshots +analysis +statistics +``` + +--- + +# 38. 前端增加 Performance Capture 页面 + +前端必须提供: + +```text +Capture next frame + +Capture N frames +``` + +默认 N 可输入: + +```text +20 +``` + +Capture 时显示: + +```text +captured 7 / 20 +``` + +完成后显示捕获到的 Frame 列表。 + +--- + +# 39. Frame 列表必须显示 Version + +例如: + +```text +Frame #10381 Plan v57 6.81 ms +Frame #10382 Plan v57 7.02 ms +Frame #10383 Plan v58 5.93 ms +``` + +点击 Frame 后必须加载: + +```text +frame_snapshot ++ +对应 render_plan_version ++ +frame_analysis +``` + +--- + +# 40. 前端主视图必须是一张完整 Render DAG + +不得拆成: + +```text +Prepare DAG +Paint DAG +``` + +两个主图。 + +必须显示完整: + +```text +Prepare + ↓ +Paint + ↓ +Composite +``` + +以及跨 Renderable 数据依赖。 + +例如: + +```text +Axis Prepare ────────────────┐ + │ │ + ▼ ▼ +Axis Paint Series Prepare + │ │ + │ ▼ + │ Series Paint + │ │ + │ ▼ + │ Composite Series + │ │ + └──────────────────────┼──> Composite Axis +``` + +Waterfall 内部 Chunk 必须可以展开。 + +--- + +# 41. 前端 DAG Node 必须按 `kind` 区分 + +至少区分: + +```text +prepare +paint +composite +``` + +节点上直接显示: + +```text +当前 duration +scheduler_wait +worker +critical contribution +historical p95 +``` + +不得只有一个模糊的百分比字段。 + +--- + +# 42. 前端必须显示 Prepare/Paint Cache 独立状态 + +Renderable Detail 必须显示: + +```text +prepare cache: HIT/MISS +paint cache: HIT/MISS +``` + +例如: + +```text +Spectrum + +prepare cache: HIT +paint cache: MISS +``` + +用户必须能够直接理解为什么本 Frame DAG 中: + +```text +Spectrum Prepare +``` + +不存在,而: + +```text +Spectrum Paint +``` + +存在。 + +--- + +# 43. 前端必须实现真实 Worker Timeline + +同一个 Frame 必须能够按真实时间显示: + +```text +time ─────────────────────────────────────────> + +Worker 0 +[Axis Prepare][Axis Paint ] + +Worker 1 +[Series Prepare ][Series Paint] + +Worker 2 +[WF Prepare][Chunk0 Prep][Chunk0 Paint] + +Worker 3 + [Chunk1 Prep][Chunk1 Paint] + +Worker 4 + [Composite] +``` + +这张 Timeline 使用: + +```text +start_time +end_time +worker_id +``` + +直接生成。 + +--- + +# 44. DAG 与 Timeline 必须双向联动 + +点击 DAG Node: + +```text +Chunk 7 Paint +``` + +必须在 Timeline 高亮它的真实执行区间。 + +点击 Timeline 区间: + +```text +Worker 3 / 2.3ms ~ 3.7ms +``` + +必须高亮对应 DAG Node。 + +--- + +# 45. 前端 Node Detail 必须展示当前与历史对比 + +例如: + +```text +Waterfall / Chunk 7 / Paint + +current duration 1.42 ms +scheduler wait 0.18 ms +worker 3 + +moving average 1.13 ms +p95 1.67 ms +p99 2.10 ms + +critical contribution 12.4% +critical path frequency 14 / 20 +``` + +Renderable 自定义指标同时显示。 + +--- + +# 46. 连续 N 帧必须提供统计视图 + +Capture 20 帧以后必须提供: + +```text +total render +avg +p50 +p95 +max + +parallelism +average +peak + +scheduler wait +average +p95 + +critical path +node frequency +``` + +不得只提供 20 个单帧链接。 + +--- + +# 47. 不同 Plan Version 必须支持直接比较 + +如果 Capture 中存在: + +```text +Plan v57 +Plan v58 +``` + +必须能够比较: + +```text +frame count +render avg +render p95 +peak parallelism +average parallelism +scheduler wait +critical path nodes +``` + +并展示拓扑变化: + +```text +新增 node +删除 node +新增 edge +删除 edge +缓存裁剪变化 +``` + +--- + +# 48. 测试执行顺序 + +所有阶段必须遵守: + +```text +先补测试 +↓ +确认测试失败 +↓ +修改实现 +↓ +相关测试通过 +↓ +全量测试通过 +↓ +再进入下一阶段 +``` + +不得先整体改完再补测试。 + +--- + +# 49. 必须增加的核心测试 + +覆盖: + +```text +prepare 禁止依赖 paint 结果 + +paint 不访问其他 Renderable + +prepare_buffer 独立有效 + +paint_buffer 独立有效 + +prepare invalid -> paint invalid + +paint invalid 不反向 invalid prepare + +dependency 只约束 prepare + +prepare 完成后对应 paint 可立即执行 + +不存在全局 prepare barrier + +无依赖 Renderable prepare 真并行 + +独立 paint_buffer 的 paint 真并行 + +layer 顺序只正确约束 composite + +cache hit 节点真正不进入 DAG + +node_id 跨 plan version 稳定 + +retired node_id 不复用 + +Waterfall 动态分片 node 正确 + +render_plan_version 正确变化 + +一张 DAG 正确编译一个 Taskflow + +Capture off 不生成 execution timing + +Capture next frame 只抓一帧 + +Capture 20 frames 正好抓 20 帧 + +多 worker 写不同 execution slot 无竞争 + +Frame A/B 不串 performance 数据 + +三缓冲交换以后 snapshot 不再变化 + +Plan version 与 Frame 正确绑定 + +scheduler_wait 推导正确 + +critical path 正确 + +并行时间重叠计算正确 + +前端 API 返回 Plan + Frame 完整数据 +``` + +--- + +# 50. 并发与 Sanitizer 验证 + +必须执行: + +```text +高并发 Taskflow 测试 +Waterfall 大量动态 Chunk +持续 Render +Capture 连续多帧 +Capture 开关反复操作 +Plan 高频变化 +缓存高频命中/失效 +``` + +并运行项目当前环境可用的: + +```text +ASan +UBSan +TSan +``` + +TSan 重点检查: + +```text +Frame execution slots +capture controller +triple-buffer publish +capture repository +plan history +``` + +--- + +# 51. 性能回归验证 + +必须单独比较: + +```text +Capture OFF +``` + +情况下改造前后的 Render 性能。 + +Capture OFF 不得因为性能分析系统引入明显: + +```text +锁竞争 +动态内存分配 +hash lookup +时间采样 +队列 push +observer 聚合 +``` + +Capture ON 的开销单独测量。 + +--- + +# 52. 清理旧性能统计路径 + +当 Frame Capture + `node_statistics` 完成以后,审计现有 Renderable Task Observer / Task Execution Snapshot / Rolling +Statistics 等机制。 + +如果职责已经完全被新结构替换: + +```text +直接删除旧实现 +``` + +不得保留双统计路径。 + +仅保留仍有独立语义、没有被新系统替代的统计能力。 + +--- + +# 53. 最终代码结构必须满足 + +```text +Plot +│ +├── Renderable graph relationships +│ +├── render_plan compiler +│ └── ONE Render DAG +│ +├── Taskflow executor +│ +├── frame buffers +│ ├── prepare_buffer state +│ ├── paint_buffer state +│ └── optional performance snapshot +│ +├── capture controller +│ +├── capture repository +│ +├── frame_analysis +│ +└── node_statistics +``` + +前端: + +```text +Performance +│ +├── Capture Control +├── Captured Frames +├── Render DAG +├── Worker Timeline +├── Node Detail +├── Multi-frame Statistics +└── Plan Version Comparison +``` + +--- + +# 54. 最终不可破坏的设计规则 + +这部分直接放到 Codex 任务最顶部: + +> **所有跨 Renderable 数据访问必须在 `prepare` 完成。** +> +> **`paint` 禁止访问其他 Renderable,只能消费自身 `prepare_buffer`。** +> +> **`prepare_buffer` 与 `paint_buffer` 必须是完全独立的两层缓存。** +> +> **`prepare` 缓存失效必须使 `paint` 缓存失效;`paint` 缓存失效不得反向污染 `prepare`。** +> +> **Prepare、Paint、Composite 最终必须构成同一张 Render DAG。** +> +> **禁止建立全局 Prepare→Paint Barrier。** +> +> **某个 Prepare 完成后,只要其真实依赖已经满足,对应 Paint 必须立即允许进入 Taskflow 调度。** +> +> **独立 `paint_buffer` 的 Paint 必须尽可能并行,Layer 顺序落实到 Composite。** +> +> **缓存命中的工作必须在 Render DAG 构建阶段直接裁剪。** +> +> **Kernel Render DAG 是执行、Taskflow 编译和性能分析的唯一拓扑事实来源。** +> +> **性能时序完全绑定 Frame,不建立 Trace/Event/Record/MPSC 性能事件体系。** +> +> **Capture 必须请求式开启,支持下一帧和连续 N 帧。** +> +> **Capture 开启时 Taskflow Task 直接写 Frame 预分配 Execution Slot。** +> +> **三缓冲交换完成后 Frame Snapshot 必须不可变。** +> +> **性能分析必须在完成帧冷路径执行。** +> +> **前端必须同时展示 Render DAG 偏序关系和本 Frame 的真实 Worker 时序。** +> +> **不得触碰 git。** +> +> **所有代码保持 K&R 风格,不增加无意义空行。** +> +> **被替换的旧实现直接删除,不做兼容实现。** + +执行完成后的最终数据流必须严格为: + +```text +Renderable / Dependency / Layer / Cache + │ + ▼ + render_plan compiler + │ + ▼ + ONE Render DAG + │ + ▼ + Taskflow + │ + ┌──────┴──────┐ + │ │ + Capture OFF Capture ON + │ │ + 正常执行 Frame slots + │ + ▼ + completed Frame + │ + ▼ + triple-buffer publish + │ + ▼ + immutable snapshot + │ + ┌──────────┴──────────┐ + ▼ ▼ + frame_analysis node_statistics + │ │ + └──────────┬──────────┘ + ▼ + Backend API + │ + ▼ + DAG + Worker Timeline + + Multi-frame Analysis + + Plan Version Compare +``` + +这份计划直接从 Renderable 边界改造一直执行到最终前端性能分析页面。 diff --git a/cmake/RenderivePackage.cmake b/cmake/RenderivePackage.cmake index bed1390..94638eb 100644 --- a/cmake/RenderivePackage.cmake +++ b/cmake/RenderivePackage.cmake @@ -1,27 +1,28 @@ get_filename_component(renderive_package_root "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE) set(renderive_package_includes "^.*$") set(renderive_package_excludes - "^data/.*$" - "^performance/.*$" - "^cmake-build-[^/]*/.*$" - "^(.*/)?node_modules/.*$" - "^(.*/)?.git/.*$" - "^(.*/)?wwwroot/.*$" - "^(.*/)?dist/.*$" - "^(.*/)?htdocs/.*$" - "^(.*/)?model/.*$" - "^(.*/)?.idea/.*$" - "^(.*/)?.playwright-cli/.*$" - "^(.*/)?performance/.*$" - "^(.*/)?dll_source_lzy/.*$" - "^.*[.]tsbuildinfo$" - "^.*[.]zip$" - "^.*[.]otf$" + "^data/.*$" + "^performance/.*$" + "^third_party/datoviz/.*$" + "^cmake-build-[^/]*/.*$" + "^(.*/)?node_modules/.*$" + "^(.*/)?.git/.*$" + "^(.*/)?wwwroot/.*$" + "^(.*/)?dist/.*$" + "^(.*/)?htdocs/.*$" + "^(.*/)?model/.*$" + "^(.*/)?.idea/.*$" + "^(.*/)?.playwright-cli/.*$" + "^(.*/)?performance/.*$" + "^(.*/)?dll_source_lzy/.*$" + "^.*[.]tsbuildinfo$" + "^.*[.]zip$" + "^.*[.]otf$" ) _add_project_zip_target( - renderive_package_zip - "${renderive_package_root}/renderive_package.zip" - "${renderive_package_root}" - renderive_package_includes - renderive_package_excludes + renderive_package_zip + "${renderive_package_root}/renderive_package.zip" + "${renderive_package_root}" + renderive_package_includes + renderive_package_excludes ) diff --git a/renderive_package.zip b/renderive_package.zip index 7354e60..a5a65f6 100644 Binary files a/renderive_package.zip and b/renderive_package.zip differ