Appearance
从硬件到开发者:客户端渲染的系统栈
这不是一张“软件分层图”。它描述一次输入如何穿过真实机器,最后成为一帧画面;也描述开发者写的一行状态更新,最后怎样反向变成 CPU 工作、驱动命令、GPU 任务与显示扫描。
text
用户动作
-> 输入设备与控制器
-> 内核输入子系统 / 中断 / 调度
-> 应用进程与运行时
-> UI 库、状态与渲染引擎
-> 窗口系统 / 浏览器合成器 / 图形 API
-> 驱动、命令缓冲、同步对象
-> GPU、显存、显示控制器
-> 屏幕扫描出一帧
-> 用户看见结果,开始下一次动作反向看同一条链:开发者不是“直接画像素”,而是在约定范围内描述状态、结构、样式和动画;库与引擎把它们编译成图形工作;内核和驱动负责安全地调度 CPU、内存和 GPU;显示硬件按刷新节奏把结果呈现出去。
节点清单
| 节点 | 输入 | 它维护的状态 | 输出给谁 | 典型故障表现 |
|---|---|---|---|---|
| 输入设备 | 触摸、鼠标、键盘、笔 | 设备报告、坐标、按键状态 | 控制器与内核 | 丢事件、坐标漂移、输入延迟 |
| CPU 与内存 | 指令、数据、缺页、中断 | 寄存器、缓存、页表、线程上下文 | 内核、应用、驱动 | 长任务、抖动、换页、响应慢 |
| GPU 与显示硬件 | 命令缓冲、纹理、fence | 队列、显存、管线状态、扫描时序 | 显示控制器 | 掉帧、白块、纹理压力、撕裂 |
| 内核与驱动 | IRQ、系统调用、DMA、图形命令 | 进程、虚拟内存、设备队列、同步对象 | 窗口系统、应用、GPU | 任务排队、阻塞、设备重置、权限失败 |
| 平台图形栈 | 窗口表面、buffer、输入事件 | surface、buffer ownership、合成顺序 | 浏览器/Flutter/原生应用 | 不可见窗口、合成延迟、缩放错误 |
| 引擎与库 | DOM/Widget、样式、状态变更 | 渲染树、布局、显示列表、图层 | 图形 API 与平台 surface | 布局错、重绘大、动画掉帧 |
| 开发者代码 | 业务事件、异步结果 | 状态机、identity、缓存、命令关联 | UI 库与网络/存储 | 假成功、竞态、整页重建、不可访问 |
一次点击的真实路径
以“点击保存,按钮变为提交中,服务端确认后显示已保存”为例:
- 输入设备报告按下与抬起;控制器通过中断或轮询把报告交给内核。
- 内核记录输入事件,调度目标窗口所属线程;窗口系统或浏览器进程完成坐标与目标路由。
- 应用线程运行事件处理器,状态从
editing变为submitting(requestId);UI 库计算最小更新。 - 引擎重新布局或更新图层,生成绘制命令;图形库把命令编码进 command buffer。
- 驱动验证与提交命令,GPU 栅格化或合成;fence 表示哪一批工作已经完成。
- 显示控制器在合适的刷新周期取到新 buffer,扫描到屏幕。此时用户看到“提交中”。
- 网络结果回到应用,重复第 3 到第 6 步。只有服务端确认与本地状态一致时,画面才能显示“已保存”。
前六步解决“如何尽快把本地状态变成可见画面”;第七步才涉及共享事实。画面先变不等于保存已经完成。
分页阅读
- 硬件与显示链:CPU、缓存、内存、PCIe、GPU、显存、显示控制器和刷新。
- 内核与驱动:中断、调度、虚拟内存、系统调用、DMA、图形驱动、命令队列和 fence。
- 平台图形栈、引擎与应用:窗口系统、surface、浏览器/Flutter 引擎、图形库、UI 框架与开发者边界。
- 跨层故障链:把“点了没反应、卡、花屏、假成功”还原成可验证节点。
学习准则
不要按名词背诵。每遇到一个组件,先问:它是否拥有状态?它能否并行?它如何同步?它和谁交换 buffer 或消息?它失败后用户看到了什么?能回答这些,才算真正把它放进图里。