[cgdc12][nvidia]显卡底层知识


原文是由nv的技术总监cem cebenoyan在cgdc12上带来。

非常好的介绍了很多显卡底层,从硬件到driver,到windows os的一些知识,受益匪浅。

nvidia在cgdc上一直保持着高水平的presentation,赞!


5个常常会造成图形性能卡顿的原因:

  • shader编译
  • 显存用光了->使用system memory->paging
  • 资源的管理:create/destroy/update
  • gpu落后cpu太多帧
  • 不好的query:event/occlusion query
cpu/gpu communication:

  • driver层面,每个device维护一个cmd buffer,提交的时候是提交给os
    • driver一般会在present的时候flush,但是其他地方也可能flush(应该是cmd buffer满了,或者强制flush)
    • driver最多会存上3个frame的cmd,也就是gpu最多可能落后4帧
  • os的graphics scheduler统一进行调配,压入GPU hardware queue
  • gpu进行consume
WindowsDisplayDriverModel:
vista以后才引入了(终于明白为何在xp和win7下,driver行为如此不同的原因了)。
像其他os去封装硬件一样,wddm把显存,graphic task等进行了virtualize。
这里提到了两个模式:
  • umd:user mode driver,处理d3d application, 构建和submit cmd buffer
  • kmd:kernel mode driver, 在os的kernel mode运行,管理硬件资源
  • 操作系统则在两种模式之间管理command queue

工具
提到了3个,fraps,nsight,gpuview。
这个GPUView以前还真是没听说过,是微软开发的,开发者的主页:http://graphics.stanford.edu/~mdfisher/GPUView.html,这哥们截止到现在5篇siggraph。
GPUView可以提供大量的详尽的信息。
一些细节:
  • 如果lock的资源在被gpu使用,那么这个lock函数(在某些flag)就会卡住
  • 显存超出比较多的时候,会出现严重的paging

回到典型的5个会造成卡顿的原因:

shader编译:
我们写的hlsl,编译成asm后,在gpu运行的时候会进一步编译gpu需要使用的机器语言,这个语言是我们开发时候offline生成不了的,因为不同的gpu有不同的指令集,这个过程是driver在运行时刻完成。
一般来讲,是在CreateShader时候生成这个机器语言,但是实际中有一些例外:
  • 对于一些很复杂的shader会先生成一个能用的,但是优化的不好,然后后面时间富裕了在生成一个高度优化的
  • 有些state的转换会造成shader机器指令的编译,比如有的gpu会把alpha test变成shader里面的clip语句,进而就不用提供硬件的alpha test,那么这种render state的转换也会造成shader编译,一些会造成shader recompile
    • shadow map bound/unbound
    • 使用的贴图在fp和非fp格式之间转换--看来fp的texture会使用特殊的sampling指令吧
    • 贴图和rendertarget的srgb
    • 同样的pixel shader,但是COLORWRITEENABLE的状态不同
    • 使用一些枚举或者bool来控制static branch,不同的branch的排列都会造成编译
    • d3d9的
      • clipplane
      • mrt的状态
      • fog
  • 一些比较好的建议:
    • loading的时候把shader create出来,并且把各个mesh至少都画一遍,这样保证runtime的时候就不用recompile了
    • streaming的话,就render一个看不见的物体好了,反正保证画一遍
    • 如果都做不到的话,保证在createshader和使用shader之间有0.5s到1s的时间缓冲
    • 把一些严重的state放在一起画,
资源管理
  • 在vista以及后面的os上,资源创建的时候,内存不总是在create的时候分配,很多情况是在第一次被使用的时候
  • 大型资源的创建很耗费
  • 频繁的create/destroy会产生碎片,pool会改善这一点
  • 一些会造成同步的点,本来gpu和cpu是异步运行,一旦同步就会导致系统停顿,直到同步
    • lock系列(在一定的flag下)
    • 在release了一个大资源之后,创建一个大资源(oops)
  • 一些建议
    • lock和map的时候使用discard flag,这个会新创建一个资源,而不是等待gpu使用上一个
    • 经常更新的资源使用dynamic&NOOVERWRITE flag,这样driver会倾向于把资源放到systemmem,一些比较小的vb,ib,tex放到systemmem也ok啦
    • 尽量pool,而不是runtime的去create&destroy
      • 自己管理的pool,在释放和重用资源的时候,记住要query一下gpu使用它的情况,这个不像单独的一个资源有driver帮着管理,自己的pool就要多费些力气
    • 如果一定要runtime的create&destroy,保证先destroy,然后create,一小会的超内存也会让driver管理内存的时候傻眼
    • 显存的allocate策略是先到先得
    • 按照重要性的顺序来alloc显存
      • depth stencil
      • render target
      • frequently used resource : texture
      • less used resource:vb,ib,texture
    • 重要性一样的话,大的优先,float point的优先
  • 超显存的问题并不总是一个严重问题
    • 高频率使用的资源都放在显存里了,所以一般不会有严重的paging,所以前面说的是大幅度超显存才是问题
    • gpuview里面可以看paging的事件

queued frame
这个一般都是限制gpu不会落后cpu(render thread)超过一帧,但是这里看来似乎并不是最好的策略,如果可以的话落后个多达3帧的话,可以对gpu时间不稳定的情况有更好的容错。
也就是会更平滑。
但是副作用也是比较明显的,比如occlusion query就会晚好多帧。

frame落后有api的:
IDirect3DDevice9Ex::SetMaximumFrameLatency
IDXGIDevice1:: SetMaximumFrameLatency

其他一些会造成性能卡顿
  • gpu context switch,比如在graphics pipeline和direct compute pipeline之间切换
  • 多个d3d application同时运行的时候,会造成竞争(contention)
  • driver的paged/non-paged pool超内存了,xp上面这方面能力相对就差





你可能感兴趣的:(Engine&Render)