Files
Renderive/b.md
T
2026-08-13 00:35:00 +08:00

37 KiB
Raw Blame History

按这一版代码继续执行,不改 DAG 主体设计。 后端只处理前面确认的 1~9,原来的第 10 项完全略过;同时重写 Kernel/threading.md ;前端整体迁移到 React + Vite + Material UIDAG 使用 React Flow,但不使用 ELK、Dagre 或其他自动布局库,拓扑解析和布局算法自己实现。

前端职责明确分开:Material UI 负责页面、菜单、表单、Tabs、Dialog、Card、Table、Tooltip、Chip、Button、Input、Select、Accordion 等全部普通 UIReact Flow 只作为 DAG 画布和节点/边交互层。React Flow 本身已经提供节点/边、缩放、平移、选择、自定义节点等基础能力,节点又直接是 React component,适合和 MUI 组合; 拓扑层级、节点坐标、折叠和时序映射全部由我们自己的 parser 决定。 (MUI)


Renderive Frame Publication + Lock Elimination + Web UI 执行计划

0. 最终目标

这轮改造完成以后,后端形成严格边界:

UI / Control / Producer
        │
        ├── 修改 Renderable state
        ├── 修改 RTD
        ├── 修改 viewport
        ├── 修改 visibility
        ├── 修改 configuration
        ├── rebuild Renderable graph
        └── 修改 frame-control
        │
        ▼
┌──────────────────────────────┐
│ Frame Publication Boundary   │
│                              │
│ 完成所有 mutable → immutable │
│ snapshot / ownership 转移    │
└──────────────────────────────┘
        │
════════════════════════════════════
        │  以下禁止访问 mutable
        ▼
    Render Plan
        │
        ▼
    ONE Taskflow
        │
        ├── Prepare
        ├── Paint
        └── Composite
        │
        ▼
    Frame Complete

双线以下必须满足:

不获取 Plot mutex
不获取 Renderable configuration mutex
不获取 Renderable graph mutex
不重新读取 Frame Control mutable state
不修改 RTD
不访问 Scene mutable API
不读取 live visibility
不持有 renderer mutex 跑完整帧

允许的只有:

immutable Frame Snapshot
immutable Renderable Graph Snapshot
Prepare Buffer
Paint Buffer
Frame-local execution slots
Frame ownership

第一阶段:建立统一 Frame_Render_Snapshot

这是 19 所有修改的基础。

不要针对每一个 mutex 单独打补丁。

新增一个明确的 Frame 级只读上下文,名称按当前项目命名规范确定,语义固定为:

Frame_Render_Snapshot

至少承载:

frame_id
render_sequence
scene_state_revision
viewport
frame_control_state
capture state/ticket

per-renderable:
    Renderable_Id
    visibility
    configuration snapshot
    prepare revision
    paint revision
    immutable render graph

注意:

Frame_Render_Snapshot

不是把所有 Renderable 业务数据复制一次。

Renderable 的大块 State / RTD 继续使用已有:

published state
render state
immutable snapshot

Frame Snapshot 保存的是:

这一帧到底引用哪个 published version

以及渲染过程需要的小型 immutable metadata。


1. 删除 Paint 中的 viewport mutex

当前问题

当前路径:

Paint Task
    ↓
Renderable::viewport_size()
    ↓
Plot_Core::viewport_size()
    ↓
Plot_Core::Impl::mutex

多个 Paint worker 会共同碰这个 mutex。

这是第一处直接从 worker 热路径删除的锁。

修改

Frame 发布时:

Plot viewport
    ↓
Frame_Render_Snapshot::viewport

Paint Context

struct Paint_Render_Context {
    ...
    Size viewport;
};

实际代码不要重复保存时,则引用:

const Frame_Render_Snapshot* frame;

Paint

Painter
    ↓
context.frame.viewport

禁止再:

Painter
    ↓
Renderable::viewport_size()
    ↓
Plot_Core

旧接口语义

Plot_Core::viewport_size() 对外 API 不删除。

调用方普通控制线程仍然可以调用它。

但是:

Render DAG 内部不得调用它。

所以不是破坏旧 API,而是把渲染内部依赖切断。

测试

新增:

Frame snapshot 后修改 viewport

验证:

当前 Frame → 使用旧 viewport
下一 Frame → 使用新 viewport

同时保证多个并行 Paint 不进入 Plot_Core::mutex


2. 拆分 Prepare / Paint / Composite Context

这是这轮最重要的架构约束之一。

当前:

Scene_Render_Context

同时给 Prepare 和 Paint 使用,而且带:

Scene_Base* scene;

必须拆。

新结构

Prepare_Render_Context
Paint_Render_Context
Composite_Render_Context

Prepare 可以拥有

Frame snapshot
自身 Renderable identity
prepare buffer
必要的 dependency published views
metrics

Prepare 可以解析已声明 dependency。


Paint 只能拥有

Frame immutable snapshot
自身 Prepare Buffer
自身 Paint Buffer / Color Cache
自身 paint-only immutable config
metrics

Paint Context 禁止存在

Scene_Base* scene;
Renderable_Base* dependency;
Plot_Core* plot;
Frame_Control_Strategy_Base* strategy;

这样用户就算想在 Paint 里面:

context.scene->xxx();

也根本写不出来。


Composite 只能拥有

Frame snapshot
已完成 Paint Buffer
final target
layer/composite metadata
metrics

Composite 不允许重新访问 Renderable 原始状态。

执行

逐个修改:

Renderable
Waterfall
Spectrum
Afterglow
Axis
Overlay
其他内置 Renderable

所有:

prepare callback
paint callback
composite callback

签名。

旧的统一 Scene_Render_Context 实现直接删除。

不保留兼容 overload。


3. visibility 正式进入 Frame Snapshot

当前:

visible_.load(...)

虽然无 mutex,但是仍然是 live state。

这个要改。

新语义

控制侧:

set_visible(false)

修改下一次 publication 使用的状态。

Frame 发布:

Renderable A visible = true
Renderable B visible = false

这一帧之后固定。

DAG 编译也直接基于这份 snapshot。

最好可以进一步:

visible == false

直接裁掉:

prepare
paint
composite

或者按现有缓存/图层语义裁剪所需节点。

禁止

Taskflow worker

is_visible()

不要再 live load。

测试

Frame A publish: visible=true

Taskflow blocked

control thread:
set_visible(false)

release Taskflow

验证:

Frame A 仍绘制
Frame B 不绘制

4. discard_real_time_data() 移出 Prepare Worker

这是现在 Prepare 热路径里最不应该存在的一类操作。

当前:

Prepare
    ↓
discard_real_time_data()
    ↓
Frame Control query
    ↓
RTD state
    ↓
RTD mutation/discard

必须取消。

正确边界

RTD 的:

update
discard
history maintenance
retention

属于:

producer/control/publication side

不属于 Render DAG。

Frame 建立之前:

RTD mutable storage
    ↓
maintenance / discard
    ↓
publish render snapshot
════════════════════════
    ↓
Prepare 只读

Render DAG 内

Prepare 只允许:

read RTD render snapshot

禁止:

clear
discard
update
lock update storage

Frequency

如果 discard policy 需要:

frequency_hz

直接使用 publication 时捕获的:

frame_control_state.frequency

不要从 RTD Prepare 中再返回 Scene 查询。

测试

必须覆盖:

RTD update 与 Frame publication 并发
RTD discard 与 publish
Frame publish 后 RTD 再变化

当前 Frame 只能看到 publication 时确定的 snapshot。


5. configuration_mutex_ 移出每帧 Render Plan 编译

当前 Render Plan 编译为了:

cache_enabled
prepare validity
paint validity
...

会读取:

configuration()

然后拿:

configuration_mutex_

这要取消。

新设计

区分:

mutable/control configuration

和:

published render configuration

Scene 配置修改仍然通过当前:

set_renderable_configuration()
lock_render_idle()

保持原有外部线程安全语义。

Frame publication 时:

configuration
    ↓
Renderable_Frame_State.configuration

Render Plan compiler 之后只读:

snapshot.configuration

configuration_mutex_

如果控制 API 自身仍然需要 mutex,可以保留。

但它不能再进入:

compile_render_plan()
Taskflow

测试

同样使用 Frame A / Frame B 语义:

Frame A publication
改变 cache/config
Frame A 执行
Frame B publication

Frame A 不允许中途改变。


6. Render Graph 改为 immutable published graph

当前:

renderable->render_graph()

每帧经过:

render_graph_mutex_

这个也清掉。

但不能直接删 mutex,因为当前允许:

render
||
rebuild_render_graph()

并发。

因此要改 ownership,不是硬删锁。

新结构

Renderable 控制侧:

mutable graph builder state
       │
       ▼
rebuild
       │
       ▼
shared_ptr<const Renderable_Graph>
       │
       ▼
atomic/publication

Frame publication

Frame
└── shared_ptr<const Renderable_Graph>

当前 Frame 一旦拿到:

Graph v17

哪怕控制线程马上 rebuild

Graph v18

当前 Frame 仍继续读:

Graph v17

不加锁。

下一 Frame 获得:

Graph v18

生命周期

必须依靠 immutable graph ownership

shared_ptr<const ...>

或者现有项目已有等价 ownership。

不要重新做 generation pointer hack。

rebuild_render_graph()

锁只允许保护:

build/publish 新 Graph

绝不保护:

当前 Frame 读取旧 Graph

测试

保留并强化当前:

Scene render
||
rebuild_render_graph()

测试。

新增验证:

Frame A = graph version 10
rebuild → 11
Frame A 仍然完整执行 version 10
Frame B 使用 version 11

7. Frame Control swap + read 合并成一次 publication

现在类似:

swap()
    ↓
unlock
    ↓
frame_control_state()
    ↓
lock

第二次锁没有意义。

改造

Frame Control publication API 必须能够直接返回:

published State snapshot

语义类似:

State publish();

或者符合现有命名:

swap_and_acquire_render_state()

具体函数名按现有接口风格确定。

但语义必须是:

lock
├── publish/swap
├── 得到当前 immutable State
└── unlock

一次完成。

Frame 以后只持:

State value

不做

不要为兼容保留:

swap()
+
swap_and_get()

两套内部实现。

如果原接口属于公开旧语义,需要保留 public API 时:

public swap()

仍可存在。

但是 Renderive 内部新执行路径只使用单 publication operation。


8. Render_Plan_History 从 unchanged-frame 热路径移出

Plan History 是:

历史/分析冷路径

不能每帧:

mutex
compare
unlock

新状态

Scene/Frame compiler 持有:

current_render_plan

同时维护能够判断 effective topology 是否变化的:

plan signature

这个 signature 是明确的结构字段组合。

不要使用字符串序列化。

例如覆盖:

active node IDs
node kind
dependency edges
composite edges

按照 canonical 顺序比较。

没变化

reuse current shared_ptr<const Render_Plan>

完全不进入 History mutex。

有变化

才:

new version
    ↓
new Render_Plan
    ↓
publish current
    ↓
append Render_Plan_History

History lock 只发生在:

plan version changed

这条冷路径。

注意

继续保持当前 a.md 的语义:

cache pruning 导致 active topology 改变,可以生成新的 Render Plan version。

不要因为这次锁优化重新改掉。


9. Render Lease 从“整帧持 mutex”改成短 ownership claim

这个要非常谨慎,必须保留旧函数语义:

多个 renderer caller 并发调用时,同一个 Frame 最多只能被一个 renderer 消费。

不能因为去锁导致重复消费。

当前

acquire_renderer
    ↓
render_mutex LOCK
    ↓
Render Lease 整个生命周期
    ↓
Taskflow
    ↓
wait
    ↓
Render Lease destructor
    ↓
render_mutex UNLOCK

要改成:

acquire_renderer
    ↓
短同步 ownership claim
    ↓
获得独占 Frame
════════════════════════
下面不再持 Frame-Control mutex
    ↓
Taskflow
    ↓
complete

ownership 状态

Frame 必须具有明确状态机,例如语义:

pending
    ↓
claimed
    ↓
rendering
    ↓
completed

ownership claim 可以:

短 mutex

或者符合当前数据结构时使用:

CAS

不要为了“lock-free”强行上复杂 atomic 状态机。

目标不是:

acquire_renderer 本身绝对无锁

目标是:

ownership 确定以后不再持 mutex 执行整帧

Flow / Manual / Low Latency

分别处理,但统一遵守:

Frame pointer/queue ownership transition
允许短同步

Frame 已被 renderer 独占后
不持 mutex

不能破坏各 strategy 原来的语义。

测试

必须继续覆盖:

4/8 renderer callers
同一个 Frame 恰好消费一次
无重复
无撕裂
无 ownership 丢失

同时增加:

一个 renderer 正在执行长 Taskflow
另外 renderer 调用 acquire_renderer

验证:

不会因为第一个 Render Lease 持整帧 mutex
导致无关 Frame Control API 被长时间锁死

原第 10 项

完全略过。

不做:

TSan 执行计划
性能阈值验收计划

这轮不写进去。


11. 最终 Render Worker 的锁规则

完成 1~9 后,建立一条可以机械检查的规则。

进入:

Render Plan execution

以后,下列函数不得出现:

std::mutex::lock
std::recursive_mutex::lock
std::shared_mutex::lock
std::lock_guard
std::unique_lock

指的是 Kernel 自己的执行数据路径。

下面这种 Frame-local worker slot

execution_slots[index]

继续直接独占写。

不增加锁。

最终应该是:

Taskflow worker
├── immutable Frame Snapshot           readonly
├── immutable Render Graph             readonly
├── Prepare Buffer                     owner-defined
├── Paint Buffer                       exclusive by DAG
├── final composite dependency         DAG controlled
└── execution slot[index]              exclusive index

12. 同步重写 Kernel/threading.md

这次不要在现在的 threading.md 上简单补几行。

当前里面很多描述会因为此次改造失效,例如:

Double/Triple State render snapshot 都在 mutex 下读取
render_graph_mutex_ 串行当前 render graph 访问
Low Latency render_mutex_ 整个 Render Lease 生命周期持有

这些都要按新模型重写。


threading.md 必须采用下面的结构

1. 核心线程模型

第一段直接规定:

Renderive 的同步边界分为:

1. Mutable Control Domain
2. Publication Domain
3. Immutable Frame Execution Domain
4. Cold Analysis Domain

2. Mutable Control Domain

明确这里可以使用锁。

包括:

Renderable 属性修改
Scene attach/detach
dependency/layer 修改
RTD update
viewport 修改
configuration 修改
graph rebuild
Frame producer queue 操作

这里的原则:

锁保护 mutable ownership
锁不泄漏到 Render DAG

3. Publication Domain

专门说明:

publication 是 mutable → immutable 的唯一边界

允许:

mutex
short critical section
pointer swap
shared_ptr publication
atomic ownership change

禁止:

持锁进入 Taskflow

描述:

Frame publication 完成以后,
当前 Frame 所需的数据版本必须全部固定。

4. Immutable Frame Execution Domain

这一节要写成最严格的规则。

明确:

Prepare/Paint/Composite worker 不获得 Kernel mutex。

Worker 只允许:

读取 Frame Snapshot
读取 published Render State
读取 immutable Renderable Graph
访问 dependency snapshot
写自己的 Prepare Buffer
写自己的 Paint Buffer
写自己的 execution slot

禁止:

Plot_Core live getter
Scene control API
Frame Control mutable API
RTD mutation
configuration getter requiring lock
render_graph rebuild/getter requiring lock
live visibility

13. threading.md 增加“锁分类表”

必须直接写表。

类似:

锁/同步 所属域 是否允许跨 Frame Execution 用途
Scene topology mutex Control attach/detach/topology mutation
State publish mutex Publication mutable→render state
RTD mutation mutex Control update + observer 顺序
RTD publish mutex Publication render snapshot
Graph rebuild mutex Control/Publication 创建新 immutable graph
Frame queue mutex Ownership claim/publish frame
Render Plan History mutex Cold 不进入 worker 保存历史版本
Capture repository mutex Cold 不进入 worker 保存成功 Snapshot
Taskflow worker data Execution 无锁 immutable/exclusive data

这样以后审代码的时候直接对表查。


14. threading.md 增加锁顺序

虽然 Frame execution 不拿锁,控制侧仍然需要锁顺序。

必须明确规定:

Scene control
    ↓
Renderable control
    ↓
Frame control
    ↓
RTD / observer

但实际顺序需要按代码最后改完后的真实调用链重新核对后写。

不能提前凭猜测写死。

最终 threading.md 必须列:

允许的 nested locking
禁止的 nested locking
callback 前必须释放哪些锁

尤其强调:

用户 observer callback
用户 Renderable callback
Taskflow callback

调用前不得持有可能被 callback 重入的控制锁。


15. threading.md 增加 callback 规则

写明:

Observer callback 是同步 callback

但:

不得在持有 Frame ownership mutex 时调用
不得在持有 Scene topology mutation mutex 时调用会重入 Scene 的 callback
不得在 publication mutex 下执行未知用户代码

需要 state observation 时:

lock
生成 Observation value
unlock
callback(observation)

保持当前已经采用的正确模式。


16. threading.md 增加 Frame Ownership 章节

Flow / Manual / Low Latency 分别说明:

producer ownership
pending ownership
renderer claim
render ownership
completed ownership
recycle ownership

核心规则:

Frame ownership 转移可以同步,Frame ownership 一旦确定,Frame 内容执行不依赖互斥锁。

这句话作为 Frame Strategy 的总规则。


17. threading.md 增加 Snapshot Lifetime 章节

写清:

Frame 持有什么 shared ownership
什么时候释放
graph version 生命周期
RTD snapshot 生命周期
renderable snapshot 生命周期

特别说明:

Renderable rebuild Graph

不会使正在执行 Frame 的 Graph 失效。


18. threading.md 增加 Prepare/Paint 并发规则

保留现在正确的:

不同 Renderable 无依赖即可并发
Renderable 内无 dependency edge 即可并发
Prepare A 可以与 Paint B 并发

并新增:

并发安全不是依赖 mutex 达成
而是依赖:
immutable input
exclusive output
DAG dependency

这个很重要。


19. threading.md 增加“不应该看到的锁”

直接写一个禁止清单。

在:

prepare callback
paint callback
composite callback
Taskflow wrapper

里面如果 Kernel 自己出现:

configuration_mutex_
render_graph_mutex_
Plot_Core::mutex
Frame Control state mutex
RTD mutable mutex
Render Lease mutex

视为架构错误。

不是“性能优化建议”。

是违反线程模型。


20. 前端整体废除当前手写 DOM 架构

当前:

webapp_gallery/
├── index.html
├── app.js
└── styles.css

其中 app.js 自己维护:

GalleryCard class
DOM clone
querySelector
菜单状态
Tabs
表单
DAG SVG
Timeline SVG
选择状态

这一版直接替换。

旧实现删除。

不保留:

legacy app.js
legacy page
React page

双实现。


21. 前端新技术栈

固定:

React
TypeScript
Vite
Material UI
@mui/icons-material
@xyflow/react

不使用:

ELK
elkjs
Dagre
D3 layout
Graphviz
Cytoscape
AMIS
手写 DOM UI
手写完整 DAG SVG

Material UI 是 React component library,可以把普通 UI 全部收敛为一致的组件模型。 (MUI)

React Flow 只承担图画布能力:

node rendering
edge rendering
zoom
pan
selection
fit view
viewport

这些都是其现成能力。 (React Flow)


22. DAG 布局自己解析

这里严格按你刚才的要求:

不要 ELK。

新增自己的纯 TypeScript parser/layout

renderPlanToDagModel()
        ↓
layoutRenderDag()
        ↓
React Flow nodes/edges

23. DAG parser 输入

直接使用当前后端:

render_plan
├── version
├── nodes
└── edges

不要让 React component 自己理解后端 JSON。

首先解析成前端自己的强类型模型:

RenderPlan
RenderNode
RenderEdge
RenderNodeKind
RenderDagModel
RenderDagLevel

所有后端 JSON 检查集中在:

protocol/

React component 不做:

data?.foo?.bar ??
...

满页面 defensive parsing。


24. 自己实现 DAG level parser

布局算法按 DAG 本身语义做。

第一步:

node indegree

然后 Kahn topological traversal。

计算:

level[node] =
max(level[parent] + 1)

无父节点:

level = 0

得到:

Level 0
├── A.prepare
├── B.prepare
└── C.prepare

Level 1
├── A.paint
└── C.paint

Level 2
├── ...

25. 同 level 节点排序

不能随机。

排序规则固定,保证同一 plan 刷新后节点不乱跳。

依次:

owner/renderable order
kind
logical execution order
node_id

如果前一 version 有相同 Node ID

优先保持上一个位置

这样:

Plan v17
→
Plan v18

只新增一个 node 时,不应该全图重排。


26. DAG 布局增加 barycenter pass

基础 level 完成之后,自己实现简单交叉减少。

两遍:

left → right
right → left

对每层节点根据邻接节点平均位置排序。

不引入第三方 layout engine。

复杂度保持可控。

如果数据量增长,最多增加几轮 deterministic pass。

不做无限迭代优化。


27. DAG Node 使用 MUI 组件

React Flow custom node 内部直接用 MUI

Paper
Stack
Typography
Chip
Tooltip
Box

例如:

┌────────────────────────────┐
│ Waterfall                  │
│ chunk.paint.4              │
│                            │
│ PAINT    1.82ms     23.1%  │
└────────────────────────────┘

Node 数据:

owner
name
kind
duration
critical
cache state
worker

React Flow custom node 本身就是普通 React component,因此这种组合是自然的。 (React Flow)


28. DAG Edge 不自己写 SVG Path 算法

这个和“自己写 parser”区分开。

自己写的是:

DAG topology parser
level layout
position
selection mapping

不是重新实现图形库。

Edge 使用 React Flow 自带:

straight
step
smoothstep

当前更适合执行 DAG 的默认使用:

smoothstep

dependency / composite 用不同:

edge data / style

表达。

React Flow 已经提供这些 edge 类型以及自定义 edge 能力,不需要我们重新维护点击命中、SVG 路径等基础设施。 (React Flow)


29. 前端目录重构

改成:

webapp_gallery/
├── index.html
├── package.json
├── tsconfig.json
├── vite.config.ts
└── src/
    ├── main.tsx
    ├── App.tsx
    ├── theme.ts
    ├── protocol/
    │   ├── gallery.ts
    │   ├── renderPlan.ts
    │   └── capture.ts
    ├── websocket/
    │   └── GallerySocket.ts
    ├── state/
    │   └── galleryState.ts
    ├── components/
    │   ├── GalleryCard.tsx
    │   ├── GalleryToolbar.tsx
    │   ├── ControlPanel.tsx
    │   ├── ObserverPanel.tsx
    │   ├── PerformancePanel.tsx
    │   └── capture/
    │       ├── CapturePanel.tsx
    │       ├── CaptureControls.tsx
    │       ├── FrameBrowser.tsx
    │       ├── RenderDag.tsx
    │       ├── RenderDagNode.tsx
    │       ├── WorkerTimeline.tsx
    │       ├── NodeDetail.tsx
    │       └── PlanComparison.tsx
    └── dag/
        ├── model.ts
        ├── parseRenderPlan.ts
        └── layoutRenderDag.ts

不要搞一个:

utils.ts

塞几千行。


30. Gallery 主页面 Material UI 化

现在的:

topbar
hero
mode tabs
category filters
cards
context menu
toast

分别改成 MUI

AppBar
Toolbar
Tabs
ToggleButtonGroup / Chip
Grid
Card
Drawer / Dialog
Snackbar

所有按钮和 input 都不再自己手写 HTML styling。


31. 原右键菜单改成 MUI Drawer

当前性能控制区内容很多。

不要继续使用自己计算定位的 context menu。

右键 Renderable 后打开:

Drawer

PC 端右侧。

里面:

Tabs
├── 属性
├── 专属 API
├── Observer
├── Performance
├── Performance Capture
└── Render DAG

保持现有功能语义。


32. 表单全部 MUI controlled component

后端字段类型解析成:

boolean  → Switch
enum     → Select
number   → TextField type=number
action   → Button
readonly → Typography/TextField readonly

解决之前手写表单容易:

后端刷新覆盖正在输入的值
Enter 后值恢复
dirty state 不明确

的问题。

每个字段维护:

serverValue
draftValue
dirty
submitting
error

后端 telemetry refresh 不允许覆盖 dirty draft。

只有:

submit 成功
manual reset

才同步。


33. Performance Capture 页面重新组件化

布局:

┌────────────────────────────────────────────────────┐
│ Capture Controls                                   │
├──────────────┬─────────────────────────────────────┤
│ Frame List   │ DAG                                 │
│              │                                     │
│              ├─────────────────────────────────────┤
│              │ Worker Timeline                     │
├──────────────┴─────────────────────────────────────┤
│ Selected Node Detail                               │
├────────────────────────────────────────────────────┤
│ Statistics / Plan Comparison                       │
└────────────────────────────────────────────────────┘

使用 MUI

Paper
Stack
Grid
List
ListItemButton
Tabs
Table
Chip
Tooltip
Divider

34. Timeline 不再手写 SVG

Timeline 不需要另外一个图框架。

自己解析:

worker_id
start_offset_ns
duration_ns

然后使用:

MUI Box

做 CSS absolute positioning。

每一个 execution block

Box

位置:

left = start / frame_duration
width = duration / frame_duration

Worker 一行一个:

Stack / Box

这样:

selection
hover
tooltip
critical path

全部是标准 React DOM,而不是手工 createElementNS()


35. DAG 和 Timeline 共享 selection store

当前:

selectedCaptureNodeId

保留这个概念,但放到 React state。

selectedNodeId
selectedFrameId
selectedPlanVersion

点击 DAG

selectedNodeId = X

Timeline 对应 block 高亮。

点击 Timeline

selectedNodeId = X

DAG 对应 Node 高亮。

Detail 自动更新。

不要两套独立选择状态。


36. DAG parser 不关心 Capture

建立:

parseRenderPlan(plan)

只处理 topology。

Capture overlay 单独:

applyFrameExecution(dag, frame)

这样同一套 DAG 可以用于:

Current Render Plan
Capture Frame
Plan Comparison

避免现在 SVG renderer 里:

plan + frame + statistics

全部搅在一起。


37. Plan version 变化的前端处理

利用稳定:

node_id

做增量视觉稳定。

当:

v17 → v18

parser 输出:

same nodes
added nodes
removed nodes
changed edges

UI 可以:

新增节点标记
删除节点列表
边变化

并尽可能保持原 Node 位置。

这非常适合你后面看:

Waterfall partition
cache pruning

到底怎么改变 DAG。


38. 前端不要修改后端协议语义

这一轮:

前端技术实现替换

不是重新设计 Gallery protocol。

当前已有:

render_plan
performance_capture
node_statistics
plan statistics
frame snapshots

继续消费。

只有在 React 强类型化时发现:

数据本身缺关键 identity

才修改协议。

不要为了前端组件化随便改 C++ JSON 字段。


39. Vite 集成方式

webapp_gallery 自己作为 frontend source。

Vite build 输出固定到现有 Web Server 使用的静态目录。

路径计算全部基于:

vite.config.ts 自身目录

不做随机输出目录。

不改变现有构建库的文件路径语义。

CMake 只负责:

调用 frontend build
复制/嵌入确定的 dist

不把前端源文件逻辑塞进 CMake。


40. 前端迁移顺序

严格按:

1. Vite + React + TS 能构建
2. WebSocket protocol 强类型化
3. Gallery 首页
4. Pixel Canvas
5. Controls
6. Actions
7. Observer
8. Performance
9. Performance Capture
10. Render DAG
11. Worker Timeline
12. Plan Comparison
13. 删除 app.js
14. 删除旧 DOM templates
15. 删除不再使用的 styles.css

不要边迁移边留旧页面 fallback。

最后直接一套实现。


41. 后端实际执行顺序

这部分严格按依赖执行:

A. Frame_Render_Snapshot 基础结构
        ↓
B. viewport snapshot
        ↓
C. Prepare/Paint/Composite Context 拆分
        ↓
D. visibility snapshot
        ↓
E. RTD discard 移出 DAG
        ↓
F. configuration snapshot
        ↓
G. immutable Render Graph publication
        ↓
H. Frame Control publish/read 合并
        ↓
I. Render Plan current/history 分离
        ↓
J. Render Lease 短 ownership claim
        ↓
K. threading.md 重写

原因是后面的锁删除都依赖前面的 snapshot ownership。

不要反过来先删 mutex。


42. 每一步测试要求

每一个步骤都采用:

先补对应测试
↓
修改实现
↓
跑对应测试
↓
跑 Kernel/render_2D/web_server 全量相关测试
↓
进入下一项

不是全部改完最后才测。

尤其不能出现:

先把 mutex 全删掉
然后看看有没有 crash

43. 最终锁审计标准

改完以后重新全局搜索:

mutex
lock_guard
unique_lock
shared_lock
recursive_mutex

逐个给它分类:

Control
Publication
Ownership
Cold

不存在第五类。

凡是属于:

Frame Execution

的 Kernel mutex,继续修改,直到为 0。


44. 最终架构

最终形成:

                     MUTABLE DOMAIN
                           │
    ┌──────────────────────┼───────────────────────┐
    │                      │                       │
Renderable State      RTD / Config          Graph Builder
    │                      │                       │
    └──────────────────────┼───────────────────────┘
                           ▼
                  FRAME PUBLICATION
                           │
              ┌────────────┴────────────┐
              │ Frame Render Snapshot   │
              │ Immutable Graphs        │
              │ Published RTD Views     │
              │ Render Plan             │
              └────────────┬────────────┘
                           │
═══════════════════════════╪════════════════════════════
                     NO KERNEL LOCKS
                           │
                           ▼
                     ONE TASKFLOW
              ┌────────────┼────────────┐
              ▼            ▼            ▼
           Prepare       Paint      Composite
              │            │            │
              └────────────┼────────────┘
                           ▼
                    Frame Snapshot
                           │
                 COLD ANALYSIS DOMAIN
                           │
             Repository / Statistics / API
                           │
                           ▼
              React + Material UI
                           │
                React Flow Render DAG
                           │
                 Own DAG Layout Parser

最终验收只有一句话

控制侧允许锁,publication 允许短锁,Frame ownership 转移允许短同步;一旦 Frame 的 immutable execution snapshot 发布完成,直到 Prepare/Paint/Composite 完成,Kernel 不再通过 mutex 获取任何渲染输入,也不持有 Frame-Control mutex 执行整帧。

前端则执行:

所有常规 UI 使用 Material UIDAG 使用 React Flow 的画布/节点/边能力;DAG 拓扑解析、分层、排序、交叉减少、版本位置稳定全部由 Renderive 自己的 TypeScript parser 完成;不引入 ELK/Dagre。 (React Flow)

这版计划不会动你已经完成的全局 DAG 核心,主要是在它外面补上最后一道真正严格的 Frame immutable publication boundary 。完成后,“交换以后渲染执行无锁”就不再是一种实现倾向,而会成为整个 Kernel 明文规定的线程契约。