原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- Direct12优化
第一章:向量代数
1、向量计算的时候,使用XMVECTOR(可以利用SIMD优点);类成员变量使用XMFLOAT2 (2D),XMFLOAT3 (3D),和XMFLOAT4 (4D) 。
2、向函数传递参数的时候(XMVECTOR可以直接传递到SSE/SSE2)前三个参数类型要定义为FXMVECTOR; 第四个要定义为GXMVECTOR; 第五个和第六个要定义为HXMVECTOR; 其他参数要定义为CXMVECTOR。
第二章:矩阵代数
1、用以计算的时候使用XMMATRIX类型;类的成员变量使用XMFLOAT4X4;
2、向函数传递参数时,规则和XMVECTOR一样;
3、上述规则对于构造函数是例外,构造函数一直使用CXMMATRIX,并且不要添加XM_CALLCONV。
第三章:变换
1、正交矩阵的逆矩阵和它的转置矩阵相等,所以计算起来更高效;
2、根据矩阵的乘法结合律,我们可以将多个变换矩阵合成为一个变化矩阵。
第四章:Direct 3D初始化
1、为了优化全屏模式下的性能,准确匹配显示模式就很重要,比如刷新频率。
第五章:渲染流水线
1、类成员变量XMCOLOR,计算时使用XMVECTOR;
2、128位颜色用来进行高精度的计算,这样算法上的错误积累就少很多;最终颜色一般保存为32位(在back buffer中也通常是32位),当前的物理显示设备无法体现128位颜色的好处;
第六章:在Direct3D中绘制
1、如果PS上裁切像素,尽可能早clip掉,因为这样像素不会进行后面的PS计算;
2、对于硬件优化,一个像素片段是有可能直接跳过像素着色器,比如(early-z rejection),但是在有些情况下,这个功能会无法使用,比如如果像素着色器中修改了Z值,那么每个像素必须进行像素着色器计算后才能得到最终的Z值;
第七章:在Direct3D中绘制(二)
1、不要在着色器中使用太多的常量缓冲,为了性能[Thibieroz13]建议保持在5个以下;
2、每一个绘制调用都会以当前设置的根参数对象状态来执行。这样可以正常执行,因为硬件会为每一个绘制调用自动保存一份根参数对象的snapshot。也就是说根参数对象会在每次绘制调用中自动进行版本管理。为了性能优化,我们应该尽可能让根签名更小,其中一个原因是每个绘制调用中对根参数对象的自动版本管理,根签名越大,需要的开销就越大。更进一步,SDK文档建议跟参数应该根据改变的频率来排序(从高到低),并且尽可能减少根签名的切换。所以在多个PSO*享一个根签名是一个不错的主意。所以创建一个“super”根签名在多个着色器程序*享,即使部分参数在部分着色器中不需要使用,也可以优化性能。但是如果这个“super”根签名太大就会让减少切换获得的好处变小。
第八章:光照
1、全局光照的计算量无法应用到实时程序,游戏中一般使用局部光照。
第九章:贴图
1、我们可以将多个图片放到一个大纹理中(texture atlas),然后应用于多个物体。这样可以避免多次资源加载,减少DrawCall,优化性能;
2、使用DDS格式文件,GPU可以直接原生使用很多它的文件格式;并且它支持压缩,可以让GPU原生解压缩;
3、根据DX11的文档:定义完整类型的格式,可以进行运行时的优化。也就是说为了性能,只有当你真正需要无类型格式,否则都定义成完整类型的格式;
第十章:混合
1、混合操作是需要更多的逐像素计算,所以是消耗较多的性能,所以只有当需要使用的时候打开它,使用完成后就关闭它;
2、Alpha测试会消耗性能,所以只有当我们需要的时候再开启它;
第十一章:模板测试
1、显卡可能会每帧在同一个点上绘制多次,这种覆盖会影响到性能,因为它在重复绘制看不到的点。所以测量深度复杂度对优化分析就很有用;
2、深度测试时在输出合并阶段执行的(像素着色器之后)。所以即使像素后续会被放弃,但是还是会进行很耗时的像素着色器计算。但是现代的显卡提供了一个叫早期Z测试(early z-test)的技术,可以让Z测试在像素着色器之前进行。为了能够得到这个技术带来的重要的好处,你需要在绘制没有混合的物体的时候,进行从前往后的顺序进行绘制,这样最近的物体会先绘制,然后被遮挡的物体会被这个early z-test技术剔除,它会对具有高深度复杂度的场景带来大量的性能提升。对于early z-test技术我们无法使用D3D API进行控制,它完全是由显卡驱动判定控制的。比如如果你的像素着色器中修改了像素的Z值,那么early z-test将无法进行;
第十三章:计算着色器
1、所以如果你的GPU有16个多处理器,那么你至少要把你的需求划分为16个线程组,这样你所有的多处理器都可以同时计算。为了有更好的性能,你应该为每个多处理器划分2个线程组,这样就可以切换线程组([Fung10]);
2、硬件把这些线程划分为warps(32个线程为一个warp),然后warps被多处理器以SIMD32来处理。每个CUDA core处理一个线程并且回顾“Fermi”多处理器,有32个CUDA cores。在D3D中你可以用一个不是32的倍数的值指定一个线程组的大小,但是出于性能考虑,最好还是指定为warp大小的倍数([Fung10]);
3、尺寸最好是wavefront的倍数(因为同时也是warp的倍数),这样就可以同时兼容两种显卡;
4、做模糊效果时,对于9x9的矩阵,我们需要81个采样。但是分离到2个1D的时候,我们只需要18个采样。尤其我们是在模糊纹理,纹理提取是很消耗性能的,所以通过分离模糊来减少纹理采样可以提高性能;
5、上面的步骤需要我们先进行正常的渲染流水线,然后切换到CS进行计算,然后切换回渲染流水线。这样的切换是由开销的([NVIDIA10])应当尽可能避免这样的切换;
6、模糊是一个很占用性能的操作,它的运算量主要与纹理的大小相关。一般情况下我们渲染到纹理的时候,可以渲染到一张比后置缓冲小的纹理上。这样可以提高渲染的纹理的速度;因为尺寸减小了,所以提高了模糊的速度;最终绘制到后置缓冲的时候,因为用了放大滤波器,又增加一层模糊效果;
7、线程组提供共享内存,访问它跟访问硬件cache一样快,它可以用来优化或者一些算法的实现。在CS中,它的定义如下:groupshared float4 gCache[N]; 数组大小可以是任意数,但是不能超过32kb,出于性能考虑,它的大小应该不超过16kb,否则不能让2个线程组指定到用一个多处理器;
第十四章:曲面细分阶段
1、D3D11硬件支持的最大细分因子是64。如果所有细分因子都是0,那么当前patch就拒绝进入后面的阶段。它可以帮助我们基于patch在背面消除和视锥体裁切上实现优化;
2、具体裁切多少主要基于需求,不要做不需要的裁切来浪费性能;
3、如果细分因子是1(也就是不细分),走一遍细分阶段流程是浪费GPU开销;
4、因为是基于GPU实现的,不要细分一个覆盖小于8个像素的这种太小的三角形;
5、批量调用具有细分的绘制调用(频繁打开和关闭曲面细分非常浪费性能);
6、曲面细分可以优化内存,也可以减少物理和动画运算(在低模上计算),可以实现LOD(以前只能放到CPU);
第十五章:第一人称摄像机和动态索引
1、最小化descriptors可以让我们的根签名更小,这代表每个绘制调用造成更少的性能开销;
第十七章:拾取
1、为了优化考虑,我们先进行物体包围体检测,只有检测通过的物体再遍历每个三角形检测;
第十八章:立方体贴图
1、在以前,应用通常优先绘制天空,然后使用它替换掉渲染目标,和深度/模板缓冲。但是“ATI Radeon HD 2000 Programming Guide”反对这个做法,原因有下:第一,深度/模板缓冲会为了内部硬件优化被明确的清空掉,对于渲染目标也是一样的;第二,因为大部分天空都是被其他物体比如建筑和地形遮挡的,所以如果我们先绘制天空,会导致很多像素需要重新绘制,这样很浪费性能。所以现在推荐最后再清空和绘制天空;
第十九章:法线贴图
1、如果你要使用压缩纹理格式保存法线贴图,使用BC7 (DXGI_FORMAT_BC7_UNORM)格式是最好的效果,它可以减少由压缩法线贴图造成的错误;
第二十章:阴影贴图
1、目前为止,我们使用4次测试的PCF内核,更大的内核会得到更平滑的边缘,但是会更消耗性能。观察上面的例子,其实只需要在边缘进行PCF,内部是不需要的,根据这个需求,衍生出了其他算法。[Isidoro06b]描述了一种方案,在着色器代码中需要动态分支:只有在边缘进行PCF。这种检测边缘又会带来其他性能开销,所以选择方案的时候要做好利弊分析;
2、这里像素着色器没有返回值,是因为我们只输出深度值。像素着色器只用以裁剪透明的像素片段。如果不需要根据透明度进行裁剪,我们可以设置像素着色器为null,这样可以让性能更高;
3、如果渲染阴影贴图的时候包含曲面细分几何体,我们需要曲面细分要和摄像机渲染时的细分保持一致;也就是说相机与物体的距离和光源与物体的距离要差不多,否则阴影会出现错误。一种优化的方案是,渲染阴影贴图的时候不使用曲面细分。(这种优化用准确性交换速度);
4、构造函数通过分辨率和viewport来创建纹理。分辨率影响了阴影的效果,高分辨率会需要更多性能开销和内存;
5、PCF的缺点在于它需要4次采样,在现代显卡上,采样操作是很费性能的操作之一,因为GPU的内存带宽和延时并没有像它计算能力那样得到提高[Möller08]。幸运的是,DX11图形硬件内置了支持PCF的方法SampleCmpLevelZero;
第二十一章:环境光遮蔽
1、实时程序使用屏幕空间环境光遮蔽(SSAO),无法使用光线追踪环境光遮蔽,因为计算量太大;
2、为了性能,SSAP贴图使用半分辨率;