在 Ruby 中实现 Game Boy 模拟器

这是关于用 Ruby 实现 Game Boy 模拟器并在浏览器中运行的项目总结,主要内容如下:

  • 创建与发布:作者创建了名为 rubyboy 的 Ruby Gem 并在 GitHub 上开源,同时实现了在浏览器中使用 WebAssembly 运行该模拟器,提供了演示链接https://sacckey.github.io/rubyboy/
  • 实现过程

    • UI 实现:使用 SDL2 通过 Ruby-FFI gem 实现屏幕渲染、音频播放和键盘输入的 UI 部分,并创建了封装 SDL2 方法的包装类。
    • ROM 加载:实现了加载和使用游戏数据的功能,可通过特定地址获取游戏标题等信息。
    • MBC 实现:支持 NoMBC 和 MBC1 游戏,通过 Factory 模式返回适当的 MBC 实现,并可添加其他类型 MBC 芯片的支持。
    • CPU 实现:实现了 CPU 执行周期,先运行最小测试 ROM,再逐步实现所有指令和中断处理,过程中遇到并解决了一些指令实现的问题。
    • PPU 实现:实现了 PPU 的剩余渲染过程,包括窗口、精灵渲染、中断处理和 DMA 传输等,使游戏可运行,但速度极慢,开始进行优化。
  • 优化过程

    • 启用 YJIT:Ruby 3.2 之后启用 YJIT 编译器,使 FPS 从 41.138559 提升到 46.733855。
    • 减少 Hash 创建:通过优化 render_sprites 方法,减少创建 Hash 的次数,FPS 从 46.733855 提升到 49.223373。
    • 提前计算地址:将 tile_map_addr 和 tile_index 的计算移到循环外,分别使 FPS 提升到 56.658074 和 60.441401。
    • Ruby 版本升级:更新到 Ruby 3.3 后,性能大幅提升,FPS 达到令人惊讶的速度,同时减少了 GC 发生次数。
    • 减少 Integer#<=>:创建 @read_methods 数组避免频繁的地址比较,使 FPS 从 55.972724 提升到 68.952551,并进一步优化了其他性能瓶颈,最终 FPS 达到 274.688515。
  • 在浏览器中运行:参考 optcarrot.wasm 的实现,通过 ruby_wasm gem 将 Ruby Boy 转换为 Wasm 包并在浏览器中运行,介绍了系统架构和相关处理细节,如 VM 初始化和绘制游戏屏幕等。
  • 总结与展望

    • 积极方面:实现过程充满乐趣,发布了 Ruby Gem,深入了解了底层技术,提升了程序优化经验和大型程序设计实现能力。
    • 对 Ruby 的印象:对 Ruby 的简单语法、丰富方法和众多有用 gems 感到满意,但也对其处理速度慢感到沮丧,庆幸 YJIT 的进化使其速度大幅提升。
    • 未来计划:修复渲染 bug,添加更多 MBC 类型,加强 WebAssembly 支持,改进基准测试系统。
  • 参考资料:作者参考了自己的博客文章、演讲幻灯片、文档以及 ruby.wasm 的相关实现等资料,用于实现和优化过程。
阅读 35
0 条评论