Files
Renderive/a.md
T
2026-08-12 16:57:03 +08:00

1920 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
下面这份直接作为 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 边界改造一直执行到最终前端性能分析页面。