Phase 1 MVP implementation of wl-webrtc: Wayland screen capture tool with hardware-accelerated VAAPI H.264 encoding and WebTransport output. Includes all 9 runtime bug fixes from code audit (fix-audit-issues plan): CRITICAL: - C2: h264_metadata BSF with repeat_sps/repeat_pps in encode pipeline - C4: FpsLimit wired as timing gate in on_copy_complete HIGH: - C3+A2: DRM device discovery via dmabuf feedback MainDevice event, unified resolve_drm_path() helper (CLI > compositor > auto > fallback) - H2: Separate physical_size (mm) from mode_size (pixels) in wl_output - H1+A3: Multi-output warning + named-output-not-found error MEDIUM: - M5: tv_sec u32->u64 to avoid Y2106 timestamp truncation - M4: Guard against SHM Buffer event (DMA-BUF only) Key components: - src/avhw.rs: FFmpeg VAAPI encoder + filter graph + BSF pipeline - src/state.rs: Wayland event loop + output negotiation + screencopy - src/cap_wlr_screencopy.rs: wlr-screencopy capture source - src/fps_limit.rs: Frame rate limiting with configurable target - src/transform.rs: Frame format conversion utilities
26 KiB
wl-screenrec 源码分析
1. 项目概述
wl-screenrec 是一个高性能 Wayland 屏幕录制器,核心特性:原始视频数据不经过 CPU,全程在 GPU 上完成捕获、格式转换和编码。
技术栈:Rust + FFmpeg(硬件加速)+ Wayland 协议(wayland-client crate)+ mio 事件循环
9 个源文件:
| 文件 | 行数 | 职责 |
|---|---|---|
main.rs |
~2430 | 状态机、事件循环、编码管线编排 |
avhw.rs |
~444 | FFmpeg 硬件设备/帧上下文(VAAPI/Vulkan) |
audio.rs |
~405 | 音频捕获→解码→重采样→编码(独立线程) |
cap_ext_image_copy.rs |
~237 | ext-image-copy-capture 协议后端 |
filter.rs |
~194 | FFmpeg 视频滤镜图(crop+scale+transpose) |
transform.rs |
~215 | 坐标系变换(处理旋转/翻转) |
cap_wlr_screencopy.rs |
~165 | wlr-screencopy 协议后端 |
fps_limit.rs |
~130 | 帧率限制器(VRR 感知) |
fifo.rs |
~60 | FFmpeg AVAudioFifo 安全封装 |
2. 架构概览
2.1 模块依赖
┌──────────────────────────────┐
│ main.rs │
└──┬──┬──┬──┬──┬──┬──┬─────────┘
│ │ │ │ │ │ │
┌───────────┘ │ │ │ │ │ └──────────┐
▼ ▼ │ ▼ │ ▼ ▼
┌──────────┐ ┌────────┐│┌───────┐ ┌────────┐ ┌──────────┐
│ avhw.rs │ │filter.rs│││audio.rs│ │fps_limit│ │transform │
└──────────┘ └───┬────┘│└───┬───┘ └────────┘ └──────────┘
│ │ │
┌───────┤ │ ▼
▼ ▼ │ ┌──────┐
┌────────────┐┌─────┴┐│fifo.rs│
│cap_wlr_ ││cap_ │└──────┘
│screencopy ││ext_* │
└────────────┘└──────┘
依赖层次:fifo/fps_limit/transform(叶节点)→ avhw/audio → filter → cap_* → main.rs(核心)。main.rs 与 cap_.rs 之间存在双向依赖:main 定义 CaptureSource trait 和 State<S>,cap_ 为 State<Cap*> 实现 Dispatch。
2.2 State<S> 全局状态
核心状态结构体持有以下关键字段:
- in_flight_surface:帧在途状态,跟踪当前帧的捕获生命周期
- dma:DMA-BUF 协议对象,用于 GPU 缓冲区共享
- enc:编码器构造状态机(
EncConstructionStage),管理从探测到就绪的全过程 - starting_timestamp:首帧时间戳(纳秒),用于音视频同步
- args:命令行参数
- errored:致命错误标志
- gm:Wayland 全局对象列表
- xdg_output_manager:输出几何信息管理器
泛型 S: CaptureSource 使同一个 State 支持两种截屏后端,无需运行时动态分发。
2.3 CaptureSource Trait
定义了屏幕捕获后端的统一接口契约:
- 关联类型 Frame:每种后端有自己的帧类型
- new():从 Wayland 全局对象和输出创建后端实例
- alloc_frame():分配捕获帧,返回
Option<Frame>统一同步/异步两种模式 - queue_copy():提交 DMA-BUF 缓冲区给合成器,请求捕获
- on_done_with_frame():帧使用完毕后的回收回调
| 实现者 | 协议 | 文件 |
|---|---|---|
CapWlrScreencopy |
zwlr-screencopy-unstable-v1 | cap_wlr_screencopy.rs |
CapExtImageCopy |
ext-image-copy-capture-v1 | cap_ext_image_copy.rs |
关键差异:wlr-screencopy 的 alloc_frame() 返回 None(异步),ext-image-copy 直接返回 Some(frame)(同步)。
2.4 事件循环
采用 mio polling + Wayland 事件队列 + Unix 信号三合一架构:
- Token(0) = 信号(SIGINT/SIGTERM/SIGHUP 退出,SIGUSR1 触发 history flush)
- Token(1) = Wayland 连接 fd →
queue.dispatch_pending(&mut state) - 超时由 FPS 报告周期驱动
- 退出时仅
Complete状态才 flush 编码器
后端自动选择:探测全局列表,优先 ext-image-copy-capture(跨桌面标准),否则回退 wlr-screencopy。
3. Wayland 协议交互层
3.1 两个后端的事件流对比
wlr-screencopy:
capture_output() [异步]
→ LinuxDmabuf { format, w, h } × N → 收集格式(仅 LINEAR)
→ BufferDone → negotiate_format() + on_frame_allocd()
queue_copy(WlBuffer)
→ Ready { timestamp } → on_copy_complete()
ext-image-copy-capture:
[会话初始化]
→ BufferSize / DmabufDevice / DmabufFormat × N → 收集约束
→ Done → negotiate_format()
[每帧]
create_frame() [同步,直接返回 Some]
queue_copy(WlBuffer)
→ PresentationTime { timestamp } → 暂存
→ Ready → on_copy_complete()
核心差异:wlr 每帧触发格式协商(帧级别),ext 在会话建立时完成(会话级别)。ext 提供真实 modifier 列表,wlr 硬编码 DrmModifier::LINEAR。
3.2 DMA-BUF 缓冲区创建
零拷贝路径:
AV HW Surface → av_hwframe_map → DRM PRIME 描述符 (DMA-BUF fd)
→ zwp_linux_dmabuf.create_params → add(planes) → create_immed → WlBuffer
→ cap.queue_copy(WlBuffer)
→ 合成器直接写入 GPU Surface(零拷贝)
3.3 格式协商
格式优先级:XRGB8888 > XBGR8888 > XRGB2101010。VAAPI 模式仅接受 LINEAR modifier,Vulkan 模式接受任意 modifier。
DRM 设备发现:两条路径(wlr 的 MainDevice / ext 的 DmabufDevice),核心逻辑相同:dev_t → DrmNode → Render 节点路径。回退 /dev/dri/renderD128。
3.4 Dispatch 泛型分发模式
三种模式:
- A. 完全泛型:
Dispatch<ZwpLinuxDmabufV1, ()> for State<S>— 共享协议,通常空实现 - B. 带状态回调的泛型:
Dispatch<WlOutput, ()> for State<S>— 需要'static,含实质逻辑 - C. 后端专用:
Dispatch<ZwlrScreencopyFrameV1, ()> for State<CapWlrScreencopy>— 非泛型,含后端特有逻辑
输出探测通过 WlOutput + ZxdgOutputV1 协作完成,PartialOutputInfo 增量收集直到所有字段填充。每个输出收到两次 Done 事件,忽略第一次。
4. GPU 编码管道
4.1 零拷贝数据流
GPU 帧池 ─alloc()→ HW Surface
↓
av_hwframe_map → DMA-BUF fd
↓
zwp_linux_dmabuf → WlBuffer (fd 共享)
↓
合成器直接写入 GPU Surface
↓
buffersrc → GPU 滤镜 (crop/scale/transpose)
↓
buffersink → 编码器 (send_frame)
↓
receive_packet → Muxer → 文件
整条链路中原始帧数据始终在 GPU 内存,不经过 CPU。
4.2 硬件设备上下文
两种硬件加速路径:
- VAAPI:一步创建,直接从 DRM 设备创建 VAAPI 硬件设备上下文
- Vulkan:两步创建,先创建 DRM 上下文,再派生 Vulkan 上下文,中间 DRM 上下文立即释放
帧上下文两种用途:
- Capture:Vulkan flags =
SAMPLED | TRANSFER_DST,tiling =Drm(modifiers) - Enc:Vulkan flags =
VIDEO_ENCODE_SRC_KHR | TRANSFER_DST,tiling =Optimal
4.3 Vulkan 自引用 Pin 模式
AvHwDevCtxVulkanBuffers 包含自引用 C 指针链(drm_info → image_fmt_list_info → 内部数组),通过 Pin<Box<Self>> 解决。PhantomPinned 标记 !Unpin,'static 是对 FFmpeg C API 的"善意谎言"。
4.4 FFmpeg 滤镜图
buffersrc (HW) → crop → scale → [transpose] → [hwdownload] → buffersink
hw_frames_ctx绑定是零拷贝的关键- crop 使用
exact=1workaround - scale/transpose 按硬件类型选择:
scale_vaapi/scale_vulkan,transpose_vaapi/transpose_vulkan hwdownload仅在软件编码路径添加
4.5 编码器选择
| Codec | VAAPI | Vulkan |
|---|---|---|
| H.264 | h264_vaapi |
h264_vulkan |
| HEVC | hevc_vaapi |
hevc_vulkan |
| VP8/VP9 | vp8/vp9_vaapi |
不支持 |
| AV1 | av1_vaapi |
av1_vulkan |
选择优先级:--ffmpeg-encoder 显式指定 > 硬件编码器(尝试 low_power=1 后回退)> 通用编码器。
4.6 EncodePixelFormat 三路派发
Vaapi(Pixel) / Vulkan(Pixel) / Sw(Pixel) 在编码器格式设置、硬件上下文绑定、滤镜图构建、帧上下文创建四处做三路匹配。
--no-hw 路径:捕获仍用 GPU(DMA-BUF),编码前 hwdownload 到 CPU,软件编码器(x264 自动 ultrafast)。
5. 状态机与帧生命周期
5.1 EncConstructionStage 状态机
┌──────────────────┐
应用启动 │ ProbingOutputs │
│ └────────┬─────────┘
▼ │ 所有输出探测完毕
┌───────────────┐ ▼
│ ProbingOutputs├──→ ┌──────────────────┐
└───────────────┘ │EverythingButFmt │
└────────┬─────────┘
│ negotiate_format()
▼
┌───────────┐ 输出断开
┌─────→│ Complete │──────────┐
│ └─────┬─────┘ │
│ │ ▼
│ 格式变化 │ ┌──────────────┐
│ on_new_ │ │OutputWentAway│
│ capture_ │ └──────┬───────┘
│ format() │ │ 同名输出重连
└────────────┘ │
←───────────────────────┘
Intermediate 瞬态存在于所有转换箭头处(mem::replace)
关键转换点:
- ProbingOutputs → EverythingButFormat:所有输出探测完毕
- EverythingButFormat → Complete:
negotiate_format()创建 EncState - Complete → OutputWentAway:
on_copy_fail()检测到输出断开,保留 enc 丢弃 cap - OutputWentAway → Complete:同名输出重新出现时创建新 cap 复用旧 enc
Intermediate 瞬态通过 mem::replace + take_enc() 实现安全所有权转移。take_enc() 只允许从 Complete/OutputWentAway 提取编码器。
5.2 InFlightSurface 帧生命周期
┌──────┐ queue_alloc_frame() ┌─────────────┐
│ None │ ───────────────────→ │ AllocQueued │
└──────┘ └──────┬───────┘
↑ │ on_frame_allocd()
│ ▼
│ ┌───────────┐
│ │ Allocd │
│ └─────┬─────┘
│ │ queue_frame_capture()
│ ▼
│ ┌──────────────┐
└────── on_copy_complete ─│ CopyQueued │
/ on_copy_fail └──────────────┘
帧级串行化:同一时间只有一帧在途,通过 assert! 强制执行。CopyQueued 持有 GPU surface、DRM 映射、Wayland 帧和 buffer 四个资源的所有权,拷贝完成后全部释放并启动下一帧。
5.3 HistoryState 双模式
- RecordingHistory(Duration, VecDeque<Packet>):环形缓冲,以关键帧为边界裁剪,确保回放可解码
- Recording(i64):正常写入,PTS 减去偏移量保证起始对齐
SIGUSR1 触发 RecordingHistory → Recording 转换:先转换状态,再将历史包通过正常录制路径写出。
5.4 错误恢复
on_copy_fail() 三个分支按优先级判断:
output_went_away == true→ 保留编码器,进入OutputWentAway等待重连format_change == true→ 预期失败,重置标志后重新分配帧- 其他 → 未知错误,记录日志后重试
5.5 动态格式切换
捕获格式变化时重建 frames_rgb、video_filter、enc_video、frames_yuv,但保留 octx、hw_device_ctx、audio、history_state。
6. 音频管道与辅助模块
6.1 音频管道
独立线程运行,三阶段构造:
- IncompleteAudioState:完成编码器选择、设备打开、解码/编码器创建
- AudioHandle:主线程句柄,含
Receiver<Packet>+AtomicBool控制标志 - AudioState:音频线程内部状态,move 到独立线程
数据流:音频设备 → 解码 → audio_filter(aformat) → AudioFifo(可选) → 编码 → mpsc → 主线程
同步机制:started 原子标志在视频首帧时间戳获得后才置 true,确保音视频起点对齐。
AudioFifo:解决变长帧编码器需要固定 frame_size 的问题。条件判断:编码器不支持 VARIABLE_FRAME_SIZE 时创建。
6.2 帧率限制器
VRR 感知设计:引入一帧缓冲延迟判定,避免在 VRR 场景下丢弃"更长时间显示"的帧。
on_new_frame(frame, ts):
第1帧 → 直接通过
第2帧 → 存入 on_deck 缓冲
第N帧 → 比较缓冲帧与新帧时间戳:
新帧太近 → 丢弃缓冲帧
间隔足够 → 输出缓冲帧,新帧存入 on_deck
6.3 坐标变换
处理 Wayland 输出变换(旋转/翻转)对坐标系的影响:
- transform_basis():8 种变换的基矩阵映射
- screen_to_frame():矩形从屏幕空间到帧空间
- transpose_if_transform_transposed():90° 旋转时交换宽高
- fit_inside_bounds():ROI 越界时安全裁剪
6.4 主线程事件循环集成
主循环中音频包在视频帧处理间隙通过 try_recv 非阻塞收取,无需额外事件源。
退出时 EncState::flush() 依次:刷出 FPS 限制器缓冲帧 → flush 音频线程 → 刷视频过滤器 → 发送编码器 EOF → 写容器 trailer。
7. 可移植设计模式
从代码库中提取的 10 个可复用设计模式,按复杂度从低到高排列。
7.1 策略 Trait + 泛型状态(CaptureSource)
问题:多后端系统如何在避免运行时动态分发(dyn Trait)开销的同时保持类型安全和可扩展性?
方案:定义 CaptureSource trait 带关联类型 Frame,将整个状态 State<S: CaptureSource> 泛型参数化。State<CapWlrScreencopy> 和 State<CapExtImageCopy> 编译为两个独立单态化类型,后端选择在启动时确定。alloc_frame() 返回 Option<Self::Frame> 统一了同步和异步两种帧分配模式。
移植要点:
- 适用后端数量有限(2-5 个)且进程生命周期内不变的场景;需运行时热切换则改用 trait object
State<S>中Sized约束必须,因为S作为字段存储;编译膨胀需注意大型 State 的泛型实例化- main.rs 定义 trait 而 cap_*.rs 实现它,形成双向依赖,大型项目应将 trait 提取到独立模块
7.2 多态枚举状态机(EncConstructionStage)
问题:Rust 中如何以零开销实现状态机,同时保证状态转换的类型安全?
方案:EncConstructionStage<S> 有 5 个枚举变体(ProbingOutputs、EverythingButFormat、Complete、OutputWentAway、Intermediate),每个携带该状态所需的数据载荷。Intermediate 瞬态 + mem::replace 组合解决了部分借用限制:match 解构 &mut self.enc 同时给 self.enc 赋新值。take_enc() 通过消费 self 确保只有含编码器的状态才能被提取。
移植要点:
- 3-7 个状态是 enum 状态机甜蜜点;优势是编译期穷尽检查,添加新状态时所有 match 报编译错误
Intermediate瞬态必须存在,否则mem::replace无法满足类型系统要求Complete和OutputWentAway都持有EncState但后者丢弃cap,体现"保留昂贵资源、丢弃可重建资源"
7.3 类型安全帧生命周期(InFlightSurface)
问题:异步 DMA-BUF 传输涉及多个阶段,如何防止在错误阶段执行操作?
方案:InFlightSurface<S> 是 4 状态枚举 None → AllocQueued → Allocd(S::Frame) → CopyQueued{...} → None。每个状态携带该阶段特有的资源(CopyQueued 持有 GPU surface、DRM 映射、Wayland 帧和 buffer)。状态转换通过 assert!(matches!(...)) 运行时守护,take() 方法(mem::replace)提供安全取出并自动重置为 None。同一时间只有一帧在途。
移植要点:
- 适用任何"请求→资源就绪→提交操作→操作完成"的异步 I/O 管道
- 运行时 assert 而非编译期 typestate 是合理权衡:回调驱动的异步场景中编译期类型状态过于复杂
- RAII 确保
CopyQueued → None路径释放所有资源(DRM 映射、Wayland buffer、帧对象)
7.4 Pin<Box> 自引用结构(Vulkan Buffers)
问题:C 库中的链式结构体(Vulkan pNext 链)内部指针指向同结构其他字段,Rust 中移动会使指针失效,如何安全构建?
方案:AvHwDevCtxVulkanBuffers 通过 PhantomPinned 标记 !Unpin,Box::pin 在堆上分配并固定,get_unchecked_mut 设置自引用指针。Vulkan 结构体的生命周期标记为 'static 作为对 C API 的"善意谎言",实际受 Pin<Box<>> 控制。unsafe 代码集中在 new() 中,使用方完全安全。chain_ptr() 根据有无 DRM modifier 返回不同链头。
移植要点:
- 通用模式,适用于 Vulkan、FFmpeg 硬件加速、内核 IOCTL 等涉及自引用 C 结构的场景
'static不是真正静态生命周期,而是向 C API 表达"指针在使用期间有效";确保持有者比 C API 使用时间更长- 优于
ouroboroscrate:手写Pin<Box<>>逻辑清晰可控,生成的代码可调试
7.5 独立线程管道 + 原子标志(音频线程)
问题:音频需要持续低延迟处理,视频帧率不固定且受 VRR 影响,如何设计无锁跨线程协作?
方案:音频处理完全隔离在独立线程。mpsc::channel 传递已编码 Packet,主线程在视频帧处理间隙通过 try_recv() 非阻塞收取。Arc<AtomicBool> + SeqCst 实现两个控制信号:started(视频首帧时间戳获得后置 true,音视频起点对齐)、flush_flag(退出通知)。音频线程主循环为 pull 模型,生命周期由输入设备驱动。
移植要点:
AtomicBool适用于简单布尔信号,比Mutex<bool>高效且不死锁;不适用于需要等待/通知的场景- 主循环间隙调用
try_recv是经典的"顺便收取"模式,避免为音频注册额外事件源 - 适用任何生产者-消费者跨线程场景:传感器采集、网络 I/O 卸载、日志异步写入
7.6 VRR 感知帧率控制(FpsLimit)
问题:VRR 显示器上帧时间戳极不规则,简单"距上帧太近就丢弃"会产生错误决策,如何在不确定的时间戳流中做出正确帧选择?
方案:FpsLimit<T> 引入一帧延迟:第一帧直接通过,第二帧存入 on_deck 缓冲,从第三帧起用新帧时间戳判断旧帧是否保留。新帧太近则丢弃缓冲帧,间隔足够则输出缓冲帧。目标时间计算中使用 max 防止回退,正确处理帧跳跃后恢复。零项目内依赖,可直接复制使用。
移植要点:
- 泛型
T无约束,只做保留/丢弃决策,调用者完全控制帧生命周期 - 结束时必须调用
flush()取出缓冲中的最后一帧,否则丢帧 - 一帧延迟对录屏/编码场景可接受,实时交互场景(如游戏输入)需评估
7.7 泛型 Dispatch 三层分发(Wayland 协议)
问题:多后端 Wayland 客户端如何组织 Dispatch 实现,使共享协议代码只写一次、后端专用代码各自独立?
方案:三层分发模式。A. 完全泛型:impl<S: CaptureSource> Dispatch<ZwpLinuxDmabufV1, ()> for State<S>,共享协议,通常空实现。B. 带状态回调泛型:impl<S: CaptureSource + 'static> Dispatch<WlOutput, ()> for State<S>,需 'static 约束,含实质状态更新逻辑。C. 后端专用:impl Dispatch<ZwlrScreencopyFrameV1, ()> for State<CapWlrScreencopy>,非泛型,在各自后端文件中。Rust trait 系统根据代理类型 × 状态泛型参数 × UserData 自动路由。
移植要点:
- 适用于所有 wayland-client 项目;共享 Dispatch 放 main.rs,专用 Dispatch 放各自后端文件
- 需关联信息时(如 xdg-output 关联 WlOutput)用
TypedObjectId<T>作为 UserData 'static约束源自事件循环要求,状态类型必须满足因为回调可能在任意时刻触发
7.8 三阶段安全构造(IncompleteAudioState)
问题:对象需分多阶段初始化且后续阶段依赖前阶段资源,如何在类型系统中安全表达?
方案:三个不同类型表示三个阶段。IncompleteAudioState 持有输入设备、解码器、编码器(完成 FFmpeg 流创建)。finish(self) 消费不完整状态,创建过滤器、FIFO、通道,组装 AudioState 并启动线程,返回 AudioHandle(主线程句柄,含 Receiver<Packet> + AtomicBool)。AudioState 通过 move 语义进入线程。finish(self) 而非 finish(&mut self) 保证不完整状态被消费后不再存在。
移植要点:
- typestate pattern 变体,用不同类型(非泛型参数)编码状态,优势是不需要泛型
- 每阶段恰好分配该阶段所需资源;第一阶段打开设备(可能失败)不浪费线程资源
- 适用 FFmpeg 管线构建、数据库连接池、GPU 资源分配等"先收集信息、再一次性创建"的场景
7.9 显示器热插拔自动恢复(OutputWentAway)
问题:长时间录屏中显示器断连/重连,如何保持编码上下文不丢失并自动恢复录制?
方案:OutputWentAway 状态机变体实现完整断连恢复。wl_registry 的 GlobalRemove 设置 output_went_away 标志(延迟到 on_copy_fail 时再切换,避免事件处理中途转状态)。转换时通过 Intermediate 取出 enc(保留编码器),丢弃 cap(协议对象已失效),记录等待的输出名称并重新探测。重连时按名称匹配创建新 CaptureSource,复用旧编码器继续录制。
移植要点:
- 核心策略:保留昂贵资源(编码器、文件句柄)、丢弃可重建资源(协议对象、设备句柄)
- 名称匹配(如 "DP-1")而非序号或指针,因为重连后 Wayland 对象 ID 会变化;稳定标识符是热插拔场景关键
- 适用 USB 摄像头、音频设备、网络连接等可热插拔设备的应用
7.10 零拷贝 GPU 管道(DMA-BUF → HW Frame → Filter → Encoder)
问题:传统录屏将 GPU 帧下载到 CPU 再上传回 GPU 编码,如何实现全程不离开 GPU 内存的零拷贝管线?
方案:GPU 帧池分配硬件表面,av_hwframe_map 映射为 DRM PRIME 描述符获取 DMA-BUF fd,注册为 WlBuffer 后合成器直接写入 GPU 表面。滤镜图 buffersrc → crop → scale → [transpose] → buffersink 全部在 GPU 执行,hw_frames_ctx 绑定确保 FFmpeg 识别 GPU 帧。编码器(VAAPI/Vulkan)直接消费 GPU 帧。EncodePixelFormat 三路枚举在编码器选择、滤镜构建、帧上下文创建处统一派发,仅 Sw 路径添加 hwdownload。
移植要点:
- DMA-BUF 桥接是 Linux 特有的;Windows/macOS 需用 D3D11 共享句柄或 IOSurface
- 捕获帧上下文用
Drm(modifiers)匹配合成器,编码帧上下文用Optimal获最佳性能,滤镜图做格式转换 - 零拷贝路径失败应降级到 CPU 路径或重试,而非直接崩溃
模式总结与关联
| # | 模式 | 核心机制 | 复杂度 |
|---|---|---|---|
| 1 | 策略 Trait + 泛型状态 | trait + State<S> 单态化 |
中 |
| 2 | 多态枚举状态机 | enum + mem::replace + Intermediate |
中高 |
| 3 | 类型安全帧生命周期 | 4 状态 enum + assert 守护 | 低中 |
| 4 | Pin<Box> 自引用结构 | PhantomPinned + Box::pin + unsafe |
高 |
| 5 | 独立线程管道 + 原子标志 | mpsc::channel + AtomicBool |
低 |
| 6 | VRR 感知帧率控制 | 一帧缓冲延迟决策 | 低 |
| 7 | 泛型 Dispatch 三层分发 | impl<S: Trait> Dispatch for State<S> |
中 |
| 8 | 三阶段安全构造 | 不同类型 × 消费 self | 低中 |
| 9 | 显示器热插拔恢复 | 标志延迟 + 资源分类 + 名称匹配 | 中 |
| 10 | 零拷贝 GPU 管道 | DMA-BUF + HW Frame + GPU Filter | 高 |
模式围绕"GPU 加速屏幕录制"协同工作:模式 1(策略 Trait)是架构骨架,模式 2(状态机)是运行时驱动核心,模式 10(零拷贝管道)是性能关键路径。模式 1 被模式 2/3/7/9 使用,模式 5(音频线程)使用模式 8(三阶段构造),模式 10 使用模式 4(Pin<Box>)并被模式 6(帧率控制)调节。