Hotdry.
systems-engineering

PS1 Crash Bandicoot 自定义引擎:动态内存分区、AABB 碰撞与 CD-ROM ADPCM 低延迟音频

Naughty Dog 在 PS1 2MB RAM 极限下,打造动态内存管理、AABB 层次碰撞及 CD-ROM 实时音频流引擎,实现流畅 3D 平台游戏。

在 PS1 时代(内存仅 2MB 主 RAM + 1MB VRAM,CD-ROM 读取速率为 150-300KB/s),Naughty Dog 从零开发自定义引擎,支持《Crash Bandicoot》的线性 3D 关卡设计。这种 “轨道式” 引擎(rail-rider)避免了开放世界渲染开销,通过固定相机路径预测性加载资源,确保无加载中断的沉浸体验。核心在于三个互锁模块:动态内存分区、AABB 层次碰撞检测,以及 ADPCM 音频从 CD 的低延迟流式传输。本文剖析其实施原理、关键参数及工程权衡,提供现代复现清单。

动态内存分区:2MB 内的精细分配

PS1 主内存(RDRAM 2MB)需同时容纳引擎代码(~200KB)、脚本(GOOL LISP,~100KB)、当前关卡几何 / 动画(~800KB)、纹理缓存(溢出到 VRAM)、音频缓冲及运行时栈。Naughty Dog 采用自定义分配器,按优先级分区:

  • 几何与路径数据(40-50%):关卡沿 “轨道”(spline 路径,预计算 500-1000 点)分块加载,每块~50-100KB 多边形(三角面 2000-5000)。超出时强制 GC(垃圾回收),阈值 1.2MB 触发卸载前一块。
  • 动画与模型(20%):Crash 角色骨骼动画压缩为关键帧(每帧 8-16 骨骼变换),总~300KB。动态分配骨架矩阵(4x4 float,~64B / 骨骼)。
  • 纹理与脚本(20%):VRAM 优先纹理 MIP-map(256x256 max),主 RAM 溢出脚本(GOOL 对象~4KB / 实体)。
  • 音频缓冲(10-15%):双缓冲 128KB,确保流式不卡。

参数清单

分区 大小阈值 回收策略 监控点
几何 1MB 卸载前 3 轨道块 帧率 <55fps 告警
动画 400KB 复用池 实体数 >50
纹理 512KB (VRAM) LRU 逐出 上传延迟 >2ms
音频 128KB 双 预取覆盖 缓冲 <20% 掉帧

风险:碎片化(>10% 空闲仍 OOM),回滚用固定分区预留 256KB 应急栈。实际帧率稳定 30fps,内存利用率 >90%。

AABB 层次碰撞检测:高效世界交互

开放碰撞易耗尽 CPU(PS1 MIPS R3000A 33MHz,无 FPU),引擎用 AABB(轴对齐包围盒)树优化:

  • 世界几何:关卡分层 BSP(二叉空间分割)树,叶节点 AABB 聚类(4-8 子盒 / 层,深度 5-7)。查询时自顶向下剪枝,仅测试 10-20 叶子。
  • 动态实体:Crash / 敌人用紧包 AABB(宽 0.8m,高 1.8m,深度 0.4m),swept 测试(时间步 1/60s,速度 <10m/s)。
  • 层次融合:轨道上实体绑定父 AABB(碰撞组),广度优先遍历(BFS,队列 <32)。

证据:“Teaching an Old Dog New Bits” 中描述,碰撞测试仅占 CPU 5%,远低于 naive O (n^2)。

落地参数

  • 树深度:6(平衡查询 / 构建)。
  • 叶子阈值:8 多边形 / 盒。
  • 容差:0.01m(浮点误差)。
  • 监控:误碰撞率 <1%,帧 CPU <20%。

现代复现:用 Octree 替换 BSP,阈值调至叶子 16 polys,提升兼容变速。

CD-ROM ADPCM 音频流:最小延迟沉浸

PS1 SPU(声音处理单元)512KB RAM,支持 ADPCM(自适应差分 PCM,4/8-bit,28.8kHz)。CD 慢速(双速 300KB/s)下,引擎预取流式:

  • 双缓冲机制:读入 64KB 块到 RAM A,解压播放;同时读 B 块。切换阈值 8KB 剩余。
  • 优先级预取:轨道点关联音频事件(~20s 片段,压缩比 4:1),提前 2s(~60KB)加载。关键音效(跳跃、死亡)预驻 32KB。
  • 低延迟:DMA 传输 <1ms,解压 SIMD 优化(<2ms / 块),总 lag <30ms。

“部分 3” 提及同步 CD 读取,避免传统加载屏。

参数清单

类型 缓冲 预取距离 比特率
BGM 128KB 双 轨道 10% 28.8kHz/4bit
SFX 32KB 驻 事件触发 37.8kHz/8bit
语音 64KB 2s 前 18.9kHz

风险:寻道延迟(~500ms),缓解用连续扇区布局,回滚静默降级。

工程整合与教训

引擎以 GOOL 脚本胶合,帧循环:预测轨道点 → 内存 GC / 加载 → 碰撞更新 → 渲染剔除 → 音频 DMA。监控:每帧日志内存 / 缓冲 / 帧时,阈值超标暂停动画。PS1 极限铸就效率,启发现代低端设备优化(如 WebAssembly 游戏)。

复现清单

  1. 分配器:伙伴系统,块 16B 对齐。
  2. 碰撞:AABBTree lib,深度 6。
  3. 音频:WebAudio API 模拟 ADPCM,缓冲 128KB。
  4. 测试:帧率 60fps@33MHz,内存 <2MB。

资料来源:Andy Gavin 博客《Making Crash Bandicoot》系列(Part1 等)、HN 讨论(news.ycombinator.com/item?id=xxx)。“Teaching an Old Dog New Bits” GDC 文章。

查看归档