unreal引擎中动态vb和ib的使用策略改进(一)
unreal引擎中有大量动态vb和ib的使用场景,例如hud的界面元素绘制,填充cpu端模拟的粒子渲染数据等。它们每帧都会lock大量的vb和ib资源,因此这就在一定程度上造成了渲染的性能瓶颈。我们的项目最先碰到的麻烦是hud的界面元素绘制,它让帧率经常处于不稳定的状态。由于需要在窗口里绘制大量的战斗单位的提示信息(例如单位名称,hp条等),所以逻辑层在一帧内调用了很多次FCanvas::DrawItem的接口,这就使得引擎内部会反复申请新的FCanvasBatchedElementRenderItem。你可能要问,那不是可合批的渲染buffer吗?按道理通过这个类应该不会产生很多drawcall才对,如果材质一致且渲染状态没有改变的话,原本的多个drawcall能够被合并成一个,从而降低运行时的开销。但很可惜,实际情况很复杂,我们的hud上的元素要求以严格的顺序来绘制,不可以按照材质的相似度进行排序。因为hud元素间的遮挡关系是逻辑层决定的,它可不管什么材质的相似度。而且即便hp条和体力条使用了相同的纹理,可文本的字形库被单独存放在引擎内部的动态纹理图集中,很显然它没有办法事先与其他的静态纹理合并在一起。所以这就造成一帧内有可能出现大量界面元素的绘制指令,每条指令均需要填充vb和ib,而且这些vb和ib竟然还是临时创建的,当看到这部分代码时,我都不敢相信自己的眼睛。
我们的游戏在同一屏幕内最多时会有上百个单位,也就是说单纯绘制hud信息就需要生成几百个图元。这个数量级别的vb和ib更新会直接导致帧率从六十帧以上瞬间掉到四十帧以下,我没有料想到开销居然如此之巨大。除此之外,我还发现vb和ib的map操作并不是立即执行的,它会被延迟处理。所以引擎会为此临时从堆中分配一块内存,好让hud模块进行数据填充,等到渲染命令真正执行时才被从堆内存里拷贝到d3d对象中。显然堆内存的分配和释放,以及内存间的拷贝看来都有点冗余,理论上应该可以完全避免。但由于unreal引擎使用了并行渲染技术(多个抽象指令队列同时收集),所以immediate command list的drawcall会缓存后延迟执行。假设想让command list里的命令立即执行就需要调用flush接口,但这样做的后果是导致渲染线程出现空等待的情况,令cpu的利用率下降。因此要彻底解决这些问题还得另辟蹊径才行,不能硬来。
页:
[1]