游戏开发50课 性能优化8

 

4.2 渲染状态优化

4.2.1 状态缓存

在引擎侧,可以使用状态缓存减少渲染管线的切换。伪代码:

class RenderStateCache
{
public:
    void InitRenderStates();
    {
        for (RenderStateType t=RenderStateType.begin; t<RenderStateType.end; ++i)
        {
            _renderStateCache[t] = GetRenderStateFromDevice(t);
        }
    }

    void SetRenderState(RenderStateType state, RenderStateValue value)
    {
        // 如果要设置的状态和当前缓存的一样,则忽略。
        if (_renderStateCache.count(state) > 0 && _renderStateCache[state] == value)
        {
            return;
        }
        _renderStateCache[state] = value;
        SetRenderStateToDevice(state, value);
    }

private:
    std::map<RenderStateType, RenderStateValue> _renderStateCache;
};

4.2.2 渲染状态建议

  • 少用Alpha Blend。开启Alpha Blend了一般会关闭深度测试,无法利用深度测试剔除多余片元,导致片元数量增加,造成过绘制。所以要尽量少用。
  • 禁用Alpha Test。现代部分移动端GPU采用了特殊的渲染优化方式,如PowerVR采用Tile Based Deferred Rendering方式(下图右),而Alpha Test会破坏Early-Z优化技术,可用Alpha Blend代替。更多看这里

  • 开启背面裁剪。背面裁剪可以将背向摄像机的面片剔除,减少顶点和片元的数据量。
  • 开启MipMaps。开启后,渲染时会自动根据画面尺寸选择合适大小的纹理,从而降低带宽,也可以降低锯齿,提高画质效果。但UI界面不能开启,原因见2.2.3。
  • 关闭雾。只在固定管线适用。
  • 少用抗锯齿。图形API内置的抗锯齿通常会增加纹理采样次数数倍之多(下图),所以要慎用。

4.3 控制绘制顺序

控制模型绘制顺序的目的是充分利用深度测试,减少片元后续操作。特别是Early-Z技术的引入,此法效果更明显。绘制顺序是:先绘制已做好排序的不透明物体,再绘制Alpha Tested物体,最后渲染透明物体。伪代码:

void Render()
{
    // 1. 绘制不透明物体
    SortOpaqueObjectsInViewSpace(); // 对不透明物体进行排序,须在相机空间,离相机近的排在前面。
    DrawOpaqueObjects();            // 绘制不透明物体,离相机近的先绘制。

    // 2. 绘制Alpha测试物体
    SortAlphaTestedObjectsInViewSpace(); // 对Alpha测试物体进行排序,须在相机空间,离相机近的排在前面。
    DrawAlphaTestedObjects();          // 绘制Alpha测试物体,离相机近的先绘制。

    // 3. 绘制透明物体(注意:绘制顺序跟不透明物体刚好相反)
    SortTransparentObjectsInViewSpace(); // 对不透明物体进行排序,须在相机空间,离相机远的排在前面。
    DrawTransparentObjects();            // 绘制不透明物体,离相机远的先绘制。
}

4.4 多线程渲染

在单线程渲染架构中,CPU性能消耗过高会影响GPU的渲染帧率,反之,GPU渲染过慢也会让CPU一直处于等待状态。多线程渲染就是为了解决CPU和GPU相互等待的问题。以Metal/Vulkan等架构出现为界限,将它们分成两个阶段。

4.4.1 软件级多线程渲染

早期的图形API和硬件架构都不支持多线程渲染,此阶段多线程渲染能做的优化比较受限,只能将渲染提交独立成一个线程,使之不会卡逻辑线程。开源图形渲染引擎OGRE的多线程渲染实现方式有两种:

  • Middle-level Multithread

    每个渲染物体都有两份实例,主线程改变其中一份数据,在下一帧给渲染线程使用。(下图)

​ 

​ 这种方式实现很复杂,要维护物体的两份实例,也不容易在多核CPU做扩展,不能充分发挥多核CPU的优势。

  • Low-level Multithread

这种实现方式是将渲染物体的顶点等数据拷贝一份,逻辑线程修改其中一份数据,下一帧给渲染线程使用。除了以上两种方案外,可以给逻辑线程的若干逻辑(如Update/粒子/动画)开辟多个线程(下图),并行计算,缩短整体处理时间。

4.4.2 硬件级多线程渲染

近几年,Metal/Vulkan图形架构横空出世,基于硬件级别的多线程渲染的时代终于到来。它们的特点:

  • 轻量化的驱动层。

OpenGL的API和驱动做了很多逻辑封装,用状态机的方式实现渲染(下图左)。而Metal/Vulkan与之不同的是,在驱动层只做少量的工作,为应用程序提供直接访问GPU硬件的接口,属于轻量级封装(下图右)。

 

从API架构上看,Metal/Vulkan的性能已胜出一大筹。

  • 支持硬件级的多线程渲染。

Metal/Vulkan支持并行渲染指令,方便CPU各个线程各自提交渲染指令和数据。下图展示的是其中一种渲染方式,由多个线程创建不同的绘制命令,再由单独的线程管理渲染命令队列,统一提交给GPU绘制。

由于图形API已经支持多线程渲染指令提交,再结合上一节讲到的若干方案,将如虎添翼,渲染性能也会发生质的提升。目前主流商业引擎已经支持Metal/Vulkan,Unity2018.3已经支持Metal/Vulkan:

Unity在Rendering设置面板可以开启多线程渲染:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值