大部分游戏渲染的时候都会构建一个管理Render物体的List,所以简称RenderList,所有要绘制的物体的RT,Shader,Texture,Shader Constant都是保存在这个数据结构里的,构建好RenderList之后才会把这些需求发给GPU执行。
在场景物件很多的时候这个RenderList的构件时间极其夸张,有时候会占到Render线程一半的时间,更让人觉得接受不了的是很多时候Camera基本没怎么动这个开销依然还是那么大。因此降低这个RenderList的构件时间非常重要,因为按照优化准则,总是优化最费的那部分,RenderList这时候觉得算得上NO1了。
我们可以想象,如果Camera不动,对于静态物体来说前一帧和当前帧画的物体一个没多一个没少,那么干嘛不把上一帧的RenderList拿来用呢?嗯,的确可以。如果Camera动起来了就不行了,因为有的物体可能看不见了,不需要再画了,有些物体上一帧没出现,camera转动之后可能当前帧看到了,需要加入新的物体到RenderList。而这时候上一帧看到并且这一帧还看到的物体占了RenderList的一大部分(比例依赖于Camera的转动幅度)。这么分析下来,思路已经很清楚了,对于上一帧出现,当前帧出现的物体继续保留,对于上一帧可见,当前帧不可见的物体垃圾回收掉,对于上一帧不可见当前帧可见的物体新增到RenderList里。
那么怎么来标记在哪一帧可见呢?其实很简单给每一个RenderPack加一个FrameStamp即可
struct RenderPack
{
.......//RenderPack Content
u32 mFrameStamp;
};
每一帧构建RenderList的时候会为每个RenderPack打标记,如果mFrameStamp==LastFrameStamp的话说明上一帧可见,并且当前帧依然可见,只需要更新mFrameStamp即可(这样就把构建一个RenderPack的操作优化为mFrameStamp的更新了),对于mFrameStamp==LastFrameStamp,而这一帧不可见的RenderPack就可以这一帧绘制的时候跳过,或者垃圾回收掉,对于mFrameStamp<LastFrameStamp的RenderPack来说上一帧是不可见的,需要从Pool里拿一个出来。
有了RenderList的Cache机制后,可以为RenderList预留一个Pool,所有的RenderPack都是从这个Pool里去拿的,而不是动态分配和销毁,这样也可以提高性能并且降低内存碎片。这个方法有些时候会带来5倍(节省80%)的RenderList构件时间的提升