GameRes游资网

 找回密码
 立即注册
返回列表
查看: 774|回复: 0

流式加载,实现赛车从静止到200英里/时的高速移动

[复制链接]
发表于 2018-12-5 15:22:13 | 显示全部楼层 |阅读模式
游戏程序
平台类型:  
程序设计: 设计思想/框架 算法逻辑/智能AI 2D图形 3D图形 
编程语言:  
引擎/SDK: Uneal/虚幻 
v2-7910b869c9727e8bcdfa645d02d15b15_1200x500.jpg

文/刘源

Turn 10是微软游戏的一个内部工作室,专注于赛车游戏。在 Froza III 中,一条赛道可以长达13英里。开发者希望有良好的加载性能,赛车从静止到200英里/时的高速移动都保持一致的高画质,没有物件突变,同时保持60fps。

本讲座首先介绍了游戏的5层存储层级,阐述资源如何从dvd达到处理器,如何优化Disk IO与CPU缓存。接下来,解释离线计算的遮挡剔除算法,以及其流式加载的工作集管理。

video https://www.gdcvault.com/play/1012335/Streaming-Massive-Environments-from-0

ppt https://www.gdcvault.com/play/1012656/Streaming-Massive-Environments-from-0

Turn 10的全职开发者约70人,会随着开发周期人员增加(主要是美术)。

Forza III 是一个xbox360游戏,赛道数据量极大,无法全部装入xbox360的512m内存。游戏的哲学是,动态加载越多越好,“除了加载本身,其他都是动态加载的”(按:实际上还是做了个别权衡,见后文)。

游戏中典型的赛道Le Mans有约6000模型和3000贴图,赛车奔驰7分钟行驶1.6圈,总加载量为0.98G,远超赛道的总数据量。具体来说为:

  • 0.14G Mesh
  • 0.84G Texture


Steaming的存储层次结构

2.jpg

上图是加载的层次结构,数据从发布介质——硬盘或者光盘,逐级向上达到处理器。我们逐层解释细节。

1.Disk

首先,赛道以zip格式压缩,使用微软自有的 lzx 算法。xbox的专用lzx库提供 20M/s 解压速度,是开发者找到的最快算法。单赛道压缩后90~300MB,压缩率50%。

2.Compressed Cache, 56M

xbox360使用的双层dvd 有 10M/s 的平均读取速度,但是单次 seek 可耗时100ms。

所以这里使用一个缓存,隐藏 IO。每次从zip中连续读出一个固定大小数据块,可包含上百个文件,不解压。

  • 数据块 1/4M~ 1M(默认)
  • 由赛道配置,仅在性能不佳时微调
  • 读取触发,LRU换出


在访谈最后的磁盘文件排序部分,我们将看到文件如何尽量聚集。

3.Decompressed Heap,194M

这一步从缓存解压到内存。解压后资源内存地址连续、对齐,可以直接由CPU、GPU使用。

内存碎片

内存碎片是长期运行堆的一个常见问题。

  • 堆分配算法为地址排序的First-Fit,这是一种公认的碎片极少的算法。
  • 资源按zone加载,每批置换2~20M不等。zone切换时,为减少碎片,要注意先一次性卸载所有待删除资源,再进行加载。
  • LOD管理


和一般游戏不同,物体的各LOD拆散存储,可以只加载所需的LOD

mip分了三档。 后面的工作集管理会提到具体用法。

  • 高:Mip0
  • 中:Mip1~1*1
  • 低:32*32~1*1
  • xbox硬件允许mip链引使用多块不同内存,所以可以免费分开加载。


注意这里的内存预算。层次2加上层次3共250M,为xbox360总内存的1/2。56 : 194 这个比例是长期试验后微调出来的。

4.GPU/CPUCache

主机游戏编程中,很多项目都会关注处理器缓存命中率。密集计算,需要使用缓存友好的算法。
以游戏的视椎体裁剪为例:每个项根据cache大小设计,数据组织为线性,线性遍历,所以缓存命中率非常高。
另外,游戏大量使用了Command Buffer,包括所有的道路、很多渲染子系统。这样只需要提交渲染数据,计算过程全部在GPU上。(按:这个为啥在cache这一节)

5.GPU/CPU

减少不必要的贴图和顶点的传输。(按:细节信息量少,不录了)

预计算遮挡剔除

作者认为,实时的遮挡剔除算法不够好。他们往往使用近似的保守算法,返回偏多的待渲染物体;同时,需要简化的几何数据,带来巨大的数据量压力。所以本作遮挡剔除是离线预计算的。

  • 直接返回LOD
  • 基于贡献的剔除(Contribution Rejection),可见像素过少也被剔除


基于贡献度的剔除
遮挡剔除分为下面几个步骤:

3.jpg

1.采样,得到zone的可见表


  • 美术用spline标记路的内外边界,构成线性路线
  • 等分成zone,等间隔采样,如图


4.jpg

计算可见性:每个采样点,按赛道当前朝向(例如上坡),先以90°视角渲染4张深度图,然后重新渲染一遍,统计各物体像素数。

随后,可见性向外扩散1格,以便消除临近点的跳变。(按:个人感觉,本步扩散无意义)
累加得到:

  • 每个zone

可见物体表、各物体的最大像素数
模型、贴图表
  • 全赛道的模型、贴图表


注意这里过大的模型,会自动切分为小模型,增加剔除概率。


2. 计算工作集

前面提到了贴图的mip分为高、中、低三档视图,高为mip0,中为mip1~1*1,低为32*32~1*1。赛道所有低档(32*32以下)mip常驻内存,总在工作集中,20~60M不等。这是一个权衡,优点是任何模型加载好就可以立即显示。

然后我们计算单个zone的工作集。相邻3个zone可见物体合并,这就是理论上需要加载的物体。下一步通过丢弃贴图的方式,得到合适的工作集。由于贴图数据大概占85%,有很多丢弃空间。算法:

  • 可见像素小于32*32的话,丢弃最高和中间级贴图。
  • 内存限制:按可见像素数从小到大,依次丢弃最高级贴图、中间级贴图,直到工作集可以在Decompressed Heap(194M)中放下。
  • 带宽限制:按最高移动速度计算出 zone n-1 到本zone的时间,乘估计的加载带宽,算出最大加载量。继续丢弃贴图,直到两工作集之加载差小于带宽限制。


最终,zone实际工作集差 2~20M,其中贴图占大概 85%。直路工作集变化很小,最大的变化会发生在转弯处。

3.磁盘文件排序

为了提高磁盘加载的缓存(56M)命中率,需要让数据尽量集中。所以整个地图的模型和贴图,按看见的顺序排序。效果会是:

  • 植被集中在头部
  • 地形块延zone线性分散


(按:所以同一赛道,同一资源只有一份;不同赛道之间,资源会有重复)

性能手工微调

最后,测试中仍然会有出现跳变。跳变主要为下面两种情况,但是他们相互冲突:

  • 带宽不足,加载晚了 —— 需要减少加载量
  • 没加载可见物 —— 需要增加加载量


主要解决方案:

  • 提供工具,人工对模型和贴图做加减权(bias)
  • 如果无效的话,美术需要减少场景复杂度


演示效果和其他


5.jpg

整个流式加载演示过程见54:05。从视频来看:

  • 实际上实现了基于zone的、考虑了遮挡的流式加载。
  • 遮挡剔除完全是依赛道的赛车视角,相当狠。由于赛车很低,所以中距离的个别低矮的田地都会被遮挡不加载。如果镜头抬高,可能这算法不太够用。
  • 其他我觉得有用的细节
  • 自动修正美术的资源冗余
  • 贴图像素比较去重:有的美术就是会复制贴图
  • 逐级比较mip差异,自动缩小贴图:优化各种纯色和渐变贴图
  • 由于用了Instancing,所以认为合并 mesh 的价值不大
  • 资源导出时,会预检查model的结构,拆出公用子模型,保证Command Buffer的draw尽量少。(说得不是太清楚)
  • GPU Instancing的时候附带一个自定数值(颜色;风力之类),让美术用同样的资源产生多样性

来源:GDC2010
知乎专栏:https://zhuanlan.zhihu.com/p/37500153

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|稿件投递|广告合作|关于本站|GameRes游资网 ( 闽ICP备05005107-1 )

GMT+8, 2018-12-15 04:06

快速回复 返回顶部 返回列表