用 Windows 窗口播放 Bad Apple:Rust 与一次批量窗口实验

把每个窗口当成像素,用 DeferWindowPos、WS_EX_TOOLWINDOW 和帧预处理,做一个高吞吐的 Windows 窗口动画。

本文目录

bad_apple_virus 是一个“用 Windows 窗口播放 Bad Apple”的实验。每个窗口是一个像素,窗口的显示、隐藏、移动和缩放组成帧动画。

它不是病毒,只是借用了老式恶作剧程序的视觉语言。项目已经归档,但里面的性能取舍仍然值得看。

把窗口当像素

普通动画操作一个画布;这个项目操作的是大量真实窗口。每个窗口有位置、大小、可见性,还要走 Windows 的窗口管理器。瓶颈通常不在“画”,而在系统调用和重绘。

README 里列出的关键优化:

  • DeferWindowPos:批量提交窗口位置/大小变更,而不是逐窗口 SetWindowPos。
  • WS_EX_TOOLWINDOW:让窗口不出现在任务栏。
  • SWP_NOREDRAW:移动/缩放时避免不必要的重绘。
  • 只显示/隐藏/移动需要变化的窗口。
  • Rust 负责调度与数据处理。

这些点合起来,才把“窗口动画”从幻灯片变成可实时播放的实验。

帧数据不是实时算的

项目包含一个 Python 预处理脚本,把输入视频转换成 boxes.bin:每一帧的窗口边界被压缩成空间优化的表示。

这个分工很合理:

  1. 视频解码和边界提取放在离线脚本。
  2. Rust 运行时只读帧数据,负责窗口操作。
  3. 不需要在每一帧里做图像识别。

对实时程序来说,把能离线做的事情移出主循环,往往比换更快的语言更有效。

为什么 DeferWindowPos 重要

窗口位置调整不是纯内存操作。每次 SetWindowPos 都可能触发窗口管理器的布局、重绘和消息处理。大量窗口逐条调整时,开销会累积。

DeferWindowPos 的思路是先收集一组变更,再一次性提交。它减少的是往返次数,而不是单次调用的成本。对于“每一帧几百个窗口都要动”的场景,这比 micro-optimize 单个函数更有意义。

同样,SWP_NOREDRAW 避免在移动过程中反复绘制;窗口最终位置由下一帧统一处理。

边界与代价

  • 平台绑定:完全依赖 Windows 窗口 API,不能在 Linux/macOS 直接运行。
  • 窗口数量有限:分辨率越高,需要的窗口越多,系统压力越大。
  • 交互会被干扰:大量窗口会抢占焦点/遮挡桌面,只适合演示。
  • 项目已归档:作者明确表示没有带宽继续维护;fork 是预期路径。
  • 不是病毒:README 明确说明它只是视觉上像老式恶作剧程序。

可以复用的经验

  • 批量 API 往往比循环调用单个 API 更值得优化。
  • 把解码/预处理移出实时循环。
  • 只更新发生变化的实体,不要每帧重建全部状态。
  • 先测量瓶颈,再决定是否换语言;Rust 很快,但错误的调用方式仍然会慢。

小结

这是一个“用错工具做对事情”的项目:用窗口系统做像素缓冲区,听起来荒唐,但它把批量提交、最小更新和离线预处理都用上了。作为性能实验,它比很多正经动画 demo 更有信息量。

讨论

评论由 GitHub Discussions 提供,点击后加载。未加载前不会产生第三方请求。