1920 lines
28 KiB
Markdown
1920 lines
28 KiB
Markdown
下面这份直接作为 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<std::string, std::any>
|
||
```
|
||
|
||
禁止通过通用字符串字段传递 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<node_id, execution>
|
||
unordered_map<node_id, execution>
|
||
```
|
||
|
||
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<string, any>
|
||
```
|
||
|
||
使用固定结构或固定 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 边界改造一直执行到最终前端性能分析页面。
|