美文网首页
【GDC2003】Optimizing the Graphics

【GDC2003】Optimizing the Graphics

作者: 离原春草 | 来源:发表于2022-05-14 07:51 被阅读0次

    坚持学习与自我革新不动摇!

    本文是GDC2003上NVidia关于图形管线优化相关的分享文章的学习笔记,原文链接在文末给出。

    AGP:Accelerated Graphics Port (AGP),这是一个高速点对点数据传输channel,用于实现CPU到显卡的数据传输,以实现3D图形渲染的加速,这个东西最开始是作为PCI类连接器的接替者而开发设计的。

    “transform bound” 意味着瓶颈出现在光栅化阶段之前

    “fill bound” 则通常意味着瓶颈发生在setup阶段之后。

    瓶颈的定位可以通过调整各个stage的workload,之后测试性能来得到。

    性能优化在于消除瓶颈,做法是降低瓶颈Stage的workload,或者增加其他非瓶颈Stage的workload。


    定位瓶颈步骤


    1.修改RT或者DS Buffer的格式,比如说从16bit改到32bit,看看帧率是否有变化,如果帧率变化了,那么就说明当前是Framebuffer写入的带宽上存在瓶颈。

    2.如果没有变化,再修改输入采样贴图的分辨率,比如将mipmap强制改到10+或者将点采样改为线性滤波采样,如果改完之后,分辨率有变化,说明当前是贴图读取的带宽存在瓶颈(也就是说,写入跟读取的带宽需要分开考虑)。

    3.如果没变化,再修改RT的分辨率,如果帧率发生变化了,再继续修改pixel shader的复杂度,如果帧率变化了,那就说明当前是PS过于复杂了,否则就说明是光栅化阶段存在瓶颈。

    4.如果修改RT分辨率没有导致帧率变化,就修改VS的复杂度,如果帧率变化了,就说明是VS存在瓶颈。

    5.否则就修改顶点格式,如果帧率变化,就说明当前是AGP上存在瓶颈。

    6.如果上述都没有变化,就说明是CPU阶段存在瓶颈。

    这里给出一些测试上的小tips

    1.在不同的CPU型号跟同一型号的GPU的机器上,如果帧率存在差异,那么就说明是CPU上存在瓶颈。

    2.在bios强制设置AGP = 1x,如果帧率发生变化,那就说明AGP上存在瓶颈。

    3.如果对GPU进行降频(underlock),即降低core的时钟频率,会导致性能下降(帧率下降)的话,那就说明可能是顶点变换、光栅化阶段或者PS上存在瓶颈;而如果降低memory(内存还是显存?从后文来看,倾向于显存)的时钟频率导致了性能下降的话,那就说明可能是贴图读取的带宽或者Framebuffer写入的带宽存在瓶颈。


    优化建议


    1. 尽可能的避免小批次提交,也就是说,一次性提交十万面片,比十次提交一万面片性能要高
    • 降低CPU浪费
    • 合批
    1. 减少不必要的AGP消耗
    • 尽量使用indexbuffer参与的绘制方式,避免过多的顶点数据传输
    • 调整参与渲染的顶点数据的顺序,提高GPU cache的命中率
    1. 对物件按照离相机先后顺序进行排序,通过硬件自带的Early-Z降低需要处理的数据量

    2. 对需要渲染的物件按照渲染状态进行合批,渲染状态即材质包括:

    • RenderStates,如BlendMode,FillMode,StencilOps等
    • shader
    • shader中使用到的参数,如宏,texture以及其他参数
    1. 使用Occlusion Query减少VS/PS/FS的数据处理量
    • 多轮Render
      • pass1,查询object有多少像素被绘制
      • pass2,剔除掉需要绘制的像素数目比较少的物件
      • 优化在于,pass1可以放在上一帧做,不需要进行复杂的shader计算,这一帧直接查询结果,并开始pass2
      • 可以用作粗浅的visibility检测
      • 对于需要使用lens flare特效的app,可以使用包含太阳的一个四边形进行Occlusion Query,并判断有多少像素可见,用以调整lens flares参数
      • 在刚才提到的pass1中,可以直接使用物件的bb进行粗浅的可见性判断

    6.避免资源Lock,如CPU读取贴图内容,如果此时这张贴图正被GPU访问,会导致CPU此时处于空闲状态,敲着二郎腿等待着GPU返回结果


    CPU瓶颈


    1.可能的原因:

    • 应用本身导致的CPU受限

    • 游戏逻辑

    • 游戏AI

    • 网络

    • 文件IO

    • 使用CPU做了除简单裁剪以及排序之外的其他图形学工作

    • 驱动问题导致的CPU受限

    • 频繁提交小数目图元批次

    • 错误的使用API

    大多数的图形应用程序都是CPU受限的

    2.解决方法:

    • 使用CPU Profiler定位问题
    • 通过各种方式增加批次内图元数目
    • 避免各种通过CPU降低GPU负载的“优化”,尽量让图形的工作归于图形(GPU)

    AGP数据传输瓶颈


    1.对于AGP 4x来说就不太可能会存在这类的bottleneck,更何况对于当代的AGP 8x?

    2.原因主要在于传输了过多的数据:

    • 无意义的数据,在GPU中不参与任何计算的,或者参与计算得到的结果对于表现无影响的,或者使用过高精度的数据,如只需要16位即可,却给了32位
    • 过多的动态顶点数据:静态顶点数据是一直存在于GPU Mem中,不需要每帧传输?这就是为什么尽量使用GPU Skin而不要使用CPU Skin的原因之一了?
    • 动态数据渲染时候调用了错误的API,比如之前所说的尽量使用indexbuffer参与的渲染API?
    • video memory过载,使用了过大的framebuffer等导致video memory不够,从而之前传输过的数据,因为年久失修而被排挤出去,导致频繁的数据传输

    3.AGP传输的数据格式也会导致AGP受限:

    • 顶点数据需要与32字节对齐?
    • 为了满足这一条件,可以对顶点数据进行压缩,使之对齐32字节,之后在vs中解压
    • 渲染时候顶点流数据无序性太严重,导致Pre-TnL Cache预处理的顶点数据结果完全浪费,渲染结构尽量保证顶点的处理顺序与顶点buffer中的顺序保持一致,比如使用TriangleStrip等

    4.优化手段:

    • 对于静态物件,创建一个静态只写vertexbuffer,只在初始化的时候写入一次,避免多次传输

    • 对于动态物件,创建一个动态vertexbuffer,在刚创建完vb向其中填入数据的时候,使用DISCARD标志;之后使用NOOVERWRITE标志写入,直到Buffer达到最大值,再重新分配Buffer,循环往复

    • DISCARD标志,使用此标志从CPU向GPU传输数据,实际上此时传输的数据是写入到一个新的Buffer中的,而在传输的过程中,GPU还可以使用原来Buffer中之前的数据进行绘制,当传输完成,调用了UnLock之后,原来Buffer中的数据被释放,GPU就使用新的Buffer数据进行渲染

    • NOOVERWRITE标志,使用此标志从CPU向GPU传输数据,通常是采用追加到buffer的形式(此部分数据并未参与到当前的DrawCall中),不会创建新的Buffer,在传输的过程中GPU还是使用之前buffer中的数据进行绘制

    • 如果不使用任何标志,如果Lock了GPU正在使用的数据,CPU会等待GPU使用完了之后返回结果才能写入,所以,尽量避免不用任何标志的Lock操作

    • 对于半动态物件,将静态部分从动态部分中分离出来,采取上述两种策略的综合->硬件合批


    Vertex Transform瓶颈


    1.这种瓶颈一般比较少见,除非:

    • 每帧需要绘制超过一百万个面片
    • 或者超出了vertex shader的最大指令数目,如单个vs指令数目超出128条

    2.但是如果出现了这种瓶颈

    • 那么就是因为顶点数太多了:
    • 降低顶点数:物件LOD,地形LOD等
    • 一般这种情况确实比较少,所以实际上在CPU中不需要做过度的LOD,一般2~3级静态LOD就完全足够
    • 或者顶点shader太复杂了:
    • 使用了过于复杂的light模型:复杂度排序
    • 方向光模型<点光模型<聚光灯模型
    • 使用了TexGen函数(用作在shader中为顶点数据自动生成对应模式的uv坐标)或者对顶点uv的进行变换的矩阵不是单位矩阵
    • 分支与循环过多
    • Post-TnL Cache使用不当,与Pre-TnL Cache一样,为了提高命中率,最好保持顶点数据的顺序与处理顺序一致

    3.解决方案:

    • 调整顶点顺序,增加Cache命中率,降低顶点数据传输量
    • 将顶点计算尽量精简为每个物件计算一次
    • 降低顶点shader复杂度
    • 考虑使用Shader LOD
    • 如果瓶颈不在FS,可以考虑将计算移动到FS完成

    Triangle Setup瓶颈


    1. 这种瓶颈基本上不太可能出现

    2. 受限因素

    • 三角面片数目
    • 需要插值的顶点属性(一般是有最大值限制的)

    3.解决方案

    • 降低需要插值的顶点属性数目
    • 减少退化面片的数目(当退化面片与有效面片的数目比值超过1的时候,这种处理方法才能起到效果)

    光栅化阶段瓶颈 - Raster Bottleneck


    受限因素:

    • 光栅化的面片的面积越大,速度越慢
    • 光栅化的面片的数量越多,速度越慢

    PS瓶颈


    在固定管线时代,Fragment Shader的设计是能够与其他的Stage匹配得非常良好,之后到了Nvidia 1x时代,虽然Fragment Shader性能因为增加了许多其他的功能而有所下降,但是也不会是瓶颈,Nvdia 3x提高了Fragment Shader的复杂程度,使得DX9的最大PS指令数为512,OpenGL3.0指令数为1024,从而使得复杂的Fragment Shader成为了瓶颈

    1. 具体原因:
    • 像素数目过多
    • 使用Early-Z进行剔除处理
    • 考虑先做一轮Z-Render,之后再做正常Render:增加了Vertex Shader throughoutput,如果PS不是超级复杂,不要考虑这个方法,另外,这个方法也可以缓解一下Framebuffer的带宽压力
    • PS太复杂
    • 在采贴图的时候,最好搭配一条组合指令(combiner,,由于是并行处理的,所以这条指令相当于是白送的?
    • combiner instructions & texture instructions都应该是奇数的(这是什么神奇的规则?)
    • 使用硬件的alphaBlend完成一些神奇的功能:
    • 点乘SRCCOLOR*SRCALPHA
    • 平方SRCCOLOR*SRCCOLOR
    • 使用Shader LOD
    • 移动合适的操作到VS
    • 尽可能的使用lowp

    其他可能的GPU瓶颈原因


    1.贴图尺寸过大

    • 会导致Texture cache miss
    • 需要注意避免dependent texture read
    • 在采样的时候规避使用negative LOD bias来锐化采样效果, Texture cache是针对标准的非负LOD Bias设计的
    • 可以考虑使用 Anistropic Filtering来替代负LOD Bias的锐化效果
    • 注意规避non-power of 2的贴图尺寸,这种也可能会导致贴图缓存性能下降。
    • 会导致AGP负担重
      优化策略
    • 降低贴图尺寸
    • 调整贴图格式,比如从32位调整为16位

    1.1 Shader中贴图采样次数过多

    • 慎用三线性采样,因为需要较多的采样次数,会导致fillrate降低一半
    • 如果再开启了anistropic filtering性能会更差
    • 只在需要的时候开启各向异性采样,同时在开启的时候也要注意尽量缩减maximum ratio of anistropy

    1.2 如何加速贴图上传

    • 尽量使用Managed资源而非采用自己的一套scheme?
    • 对于动态贴图(临时资源吗?):
    • 用d3dusage_dynamic 或者d3dpool_default模式进行创建
    • 使用d3d_discard进行lock
    • 最好不要对这类动态贴图进行读取

    1.3 天空盒大概率会导致贴图读取的瓶颈

    2.FrameBuffer问题

    • read/write操作过多
    • 将Z writes关闭有助于减轻问题
    • Only Shut some Channel Off will slower the performance by reading mask first
    • 浮点格式的framebuffer需要更多的带宽
    • 如果不需要Stencil的话可以考虑使用16bit的Depth
    • Cubemap以及Shadow Map的分辨率调低一点,深度采用16bit可能渲染效果也不会太差
    • 对于反射需要的话,可以考虑将cubemap用半球map来替代,这样贴图尺寸会更小,且在渲染的时候只需要较少的RT切换。
    • 对RT进行重用来避免内存问题
    • Z-Cull针对Z-bias、alpha test关闭,并且stencil buffer没有使用的情况做过特殊优化的
    • 可以考虑使用dx9的constant color blend功能来实现全屏的染色效果,这样比后处理实现的方式更为节省。

    另外需要注意的是,如果PS复杂度较高,那么此时,即使增加了VS的复杂度,也不会有太多额外的代价,但是却可以得到更好的效果。

    参考

    [1] Optimizing the Graphics Pipeline

    相关文章

      网友评论

          本文标题:【GDC2003】Optimizing the Graphics

          本文链接:https://www.haomeiwen.com/subject/ugcpurtx.html