核心机制:重编译而非模拟

PS2 Web Recomp 项目实现了在浏览器中直接运行 PlayStation 2 游戏,无需传统模拟器。其核心在于将游戏的 Emotion Engine 机器码预先翻译为 C++,编译为 WebAssembly,并链接到对 PS2 操作系统和硬件的重实现上,最终通过 WebGPU 进行渲染。

重新编译流程: eerecomp 工具读取游戏可执行文件,追踪控制流以定位函数,并以 16 KiB 为单位生成 C++ 代码。输出代码保留了 R5900 处理器的关键特性,包括 128 位寄存器、多媒体指令、分支延迟槽及非 IEEE 标准的浮点运算。线程切换通过展开主机栈并在客户机地址恢复来实现,无需为每个线程维护独立栈。

系统层模拟: EE 端的操作系统功能(如线程、信号量、DMA 控制器、中断处理等)由高级模拟处理。I/O 处理器(IOP)在其 R3000 CPU 解释器上运行游戏自带的 IRX 模块、驱动及光盘流式传输代码,而 IOP 内核及库则用 C++ 重实现。光盘数据通过 HTTP Range 请求读取,仅需预先下载可执行文件。

图形与向量单元: PS2 的 3D 工作主要依赖向量单元 1 的微程序。这些微程序以位精确的 PS2 浮点数学运算运行,并模拟流水线延迟。已知微程序预编译为 SIMD C++,新遇到的微程序则即时翻译为 WebAssembly 模块。Graphics Synthesizer (GS) 的 4 MB 显存位于 GPU 上,通过 WebGPU 计算着色器完成光栅化,支持原生 1 倍至 3 倍内部分辨率。

性能优化:从 3 FPS 到稳定 50 FPS

初版测试中,赛车场景在使用软件光栅化器和解释器时仅获 3 FPS。通过将 GS 迁移至 WebGPU 计算着色器并编译向量单元程序,原生速度提升至约 70 FPS。后续优化聚焦于降低浏览器开销:

  • 减少绘制调用: 通过提前上传不依赖纹理的操作,将每帧计算分发次数从 650 次降至 80 次。
  • GPU 缓存策略: 针对《GT赛车4》每帧上传相同 3 MB 纹理的情况,在 GPU 设置 16 MB 缓存,减少 60% 的每帧数据传输量。
  • 着色器优化: 统一加载设置并将每帧数据写入单一块,解决线程分支发散问题,使集成显卡在 3 倍分辨率下达到 48 至 50 FPS。
  • 线程调度: 将模拟移至 Worker,通过 MessageChannel 让步,使页面线程专责 GPU 数据输送,将波动帧率稳定在 50 FPS。

兼容性突破:《战神2》成功运行

继《GT赛车4》后,《战神2》也通过 port.sh 成功移植。过程中修复了多个首款游戏未触发的 Bug:

  • 音频缺失: 实现了 IOP 硬件定时器,解决音频驱动无法混合样本的问题。
  • 渲染崩溃: 修复重编译器在处理 16 KiB 单元末尾分支时的逻辑错误,防止指令重复执行或跳过。
  • 多边形异常: 调整向量单元模型,使裁剪标志在正确指令周期后可见,解决巨大多边形未剔除问题。

目前仍需类似 BIOS 的启动路径、内存加载模块及 Scratchpad DMA 目标链模式等支持。

部署与使用指南

环境要求: 需 Git、CMake、Ninja、C++20 编译器(推荐 clang)、Python 3 及 7-Zip(如需处理 .7z 格式)。所有环境在 Linux x86-64 上测试。每个游戏构建约占用 300 MB 磁盘空间。

git clone https://github.com/snuri00/ps2-web-recomp.git
cd ps2-web-recomp
scripts/port.sh mygame "My Game.iso"
python3 scripts/serve.py build/web-mygame games/mygame/disc.iso

port.sh 自动处理从 ISO 到网页的转换,包括静态分析、入口点添加及微程序预编译。在 8 核笔记本上,《战神2》转换耗时约 3.5 分钟。

浏览器支持: 必须启用 WebGPU。Chrome 和 Edge 在 Windows/macOS 上默认支持;Linux 用户需手动启用 chrome://flags/#enable-unsafe-webgpu 和 Vulkan。无 WebGPU 时将回退至缓慢的软件渲染。键盘映射已预设,支持手柄及移动端屏幕控件。

存档与托管

游戏进度保存在浏览器存储中的虚拟记忆卡(插槽 1),支持导出为 PCSX2 格式的 .ps2 文件以实现跨浏览器或从 PCSX2 迁移。托管时需配置 Cross-Origin-Opener-Policy: same-origin 和 Cross-Origin-Embedder-Policy: require-corp 头以支持 SharedArrayBuffer。

法律声明

本项目代码采用 MIT 许可证,不包含任何游戏代码或数据。生成的 C++ 和 WebAssembly 文件属于游戏所有者,请仅在本地保留并使用你拥有的游戏光盘镜像。本项目与 Sony Interactive Entertainment 及其子公司无任何关联或背书。