D3D 调试
1. 在Create D3D资源的时候,使用V()宏是非常必要的,因为很多情况下,即使D3D 资源创建失败,他也会继续执行下去,如此以来,程序的bug的定位就麻烦了很多,而使用V()宏会在出错的地方及时停下来,当然也有不好的地方,因为宏定义本身使用了if语句,因此如果V()宏下方有else将会导致else匹配if时出错的问题。
2. Pair函数成对使用,我把Create-Release,Begin-End等等函数称为Pair函数,更多的时候,这应该称为一种习惯,就像括号的匹配一样,前者容易导致内存泄露,而后者产生的bug就更加奇怪,例如release下结果正常,而debug模式下结果却比较奇怪。
3. 使用Get Buffer类的函数要注意D3D的引用计数,这个引用计数搞不好将会导致D3D退出时的警告,有时候这个警告是正确的,有时候这个警告确实错误的,就像很多时候,我会发现自己RT的引用计数有几十之多,原因就是在每一帧里Get Buffer,导致了引用计数的增加,从而最后无法正确的释放资源,而SAFE RELEASE宏的使用是比较方便的,然而也可能出现V()的问题。另外一个值得注意的地方是,如果获得了D3D Device里的Backbuffer和DepthStencil Buffer,那么你需要显示地释放这两个Surface,否则也会导致程序出错。
4. PIX的使用,这些天以来,我觉得PIX很好的解决了图形渲染阶段的Debug问题,但是却发现,PIX同样带来了许多不方便的地方,最大的地方就是导致了帧速的剧烈下滑,因为每一帧Pix都会产生多达数M或者几十M的数据,这些数据的产生过程极大地拖慢了帧速,帧速地减慢导致了调试连续变换的状态的不便,所以土法调试还是有土法调试的优点,例如打印Shader的结果,很多时候,可以把你想需要的信息放在COLOR1里贴到Screen Quad上,虽然需要做很多的前期准备工作,也不可避免地修改了源代码,但是有时候它的确是有效的。
5. 为了配合PIX的使用,DXUT_BeginPerfEvent以及DXUT_EndPerfEvent宏的使用是非常必要的,很多时候,我们也可以用这两个宏来观看效率瓶颈,这是非常好用的一对宏。
6. 由于D3D里定义了很多的宏,而宏名称多为全大写字母,而有些奇怪的bug便存在于此,例如g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);与g_pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE,TRUE);这两个语句乍一看好像是一样的,但是仔细观察之下,却发现用错了宏。
7. Clear的运用,Surface在写入数据之前,必须进行Clear操作,这样才能最大限度的避免错误,有时候的确不Clear也能得到正确的答案,但是这样子的结果是不稳定的,RT Surface的Clear还是DS Buffer的Clear,都是能够造成渲染错误的原因,Clear的值也是比较讲究的,这就要看具体的情况了。
内容提要:手动调试和使用工具PIX调试Direct3D程序。
3D绘图中常见问题:
1.模型消失,没有出现在画面上;
2.模型在画面上失真、变形或者闪烁;
3.贴图出现不正常拉扯;
4.使用了错误的贴图、Shader或者RenderState .
没有固定方法来查找错误的原因,一般尝试改变绘图设置,慢慢缩小问题可能发生的范围。
模型消失时,第一步检查转换矩阵,尤其是投影矩阵。如果投影矩阵的Z near和Z far范围不够大,3D对象就会落在屏幕坐标系之外而被忽略。可以试着改变投影矩阵的可视范围,看看消失的对象是否会重新出现。必要时检查顶点位置和转换矩阵的相乘结果,看看它们是否会落在画面范围中。还可以把透视投影的矩阵暂时改成正交投影,这样可以查看3D对象的位置是否正确。
确认矩阵没有问题后,如果画面仍然不正确,问题应该出在模型数据和纹理的正确性上。这时可以暂时把纹理简化。第一个简化通常是关闭光照功能。如果模型在画面上看起来过暗,甚至是完全黑色时,可能是光照的设置出了问题,这时只有关闭光照功能,就可以让模型重新出现。简化纹理还可以把Alpha Test和Blending功能关闭,有时可能贴图的Alpha层完全是黑色,或者纹理使用了错误的Alpha Test比较值,这些错误都有可能让整个对象消失。
如果关闭Alpha Test和Blending功能后,画面上仍然看不到3D对象,就把纹理做最终的简化,移除所有的贴图,让物表纹理固定地输出单一颜色。如果这个步骤可以让模型出现在画面上,那就可以肯定问题出在贴图设置或Pixel Shader的代码上。
经过前面3个步骤后,如果模型仍然没有出现在正确的位置上,就在检查模型数据的正确性。第1步在画出模型时,把几何图形的种类由三角形Triangle暂时改成点Point,因为创建三角形数据的Index Buffer有可能发生错误,所以无法组合出正确的模型。暂时先把模型用点Point的类型来画,最起码可以在画面上看到模型的大致形状,如果连大致形状都是错误的,那代表最原始的模型数据就已经出现问题了。
模型数据的问题有可能出现在GPU读取数据的部分,也有可能在设置顶点数据字段时出现错误。这个部分比较难确认,比较好的方法是通过工具来检查,在C++部分所能做到的检查工作如下:
1.确定顶点数据的大小size正确,
2.确认顶点数据字段设置,让GPU能够获得所以需要用到的数据。
3.在测试时,也可以先把数据简化,暂时只传入顶点的位置Position,忽略发线Normal、颜色Color,以及贴图坐标等,先只使用顶点位置把整个模型用点的方法画出来,只要看到模型的大致形状,再试着去传入其它数据。
使用上述的方法应该可以把问题范围缩小,这时也许还没有找出真正的原因,但是先把范围缩小后,再通过工具,应该就可以发现问题所在。
Direct3D调试工具PIX(Performance Investigate for Xbox) for Windows.在Direct SDK的工具下可以找到这个程序。PIX可以查看贴图和模型,绘图结果,顶点和像素着色器调试等功能。注意PIX启动要调试的程序路径必须是英文,否则出现不能运行错误“PIX Experiment File Version Mismatch”。
使用PIX分为2大步骤:第1步是通过PIX来启动想要调试的程序,并在程序启动后,按工具设置来截取某个画面所使用的Direct3D指令;第2步是在截取画面后关闭绘图程序,PIX接下来应该会切换到回放Direct3D指令的模式。
PIX先把某个画面中的Direct3D绘图指令截取下来,再把每个指令都视为1个断点,程序员可以从Event窗口中选择其中指令,再通过Render窗口来查看绘图的结果。Render窗口显示的是,从这个画面的起点开始一直运行到说选择断点为止的3D绘图结果。
PIX可以查看程序中所使用的贴图和模型数据。在Event窗口中找到相对应的SetTexture函数调用,双击函数的第2个参数,即蓝色文字的参数,右下方的窗口中就会显示所使用的贴图。如果要查看模型,选择Event窗口中画出模型的DrawPrimitive和DrawIndexedPrimitive函数调用(单击工具Event窗口最上边的按钮D也可以上下移动到函数调用),再在Detail窗口中选择Mesh,窗口中就会显示坐标转换前和转换后的的模型,还会显示出一个列出所有的顶点数据的列表。双击其中1个顶点,就可以进入Vertex Shader的调试模式。其中查看坐标转换前后的模型非常实用,马上可以看出加载模型是否正确,以及VS处理后是否正确,显示是否超出。Mesh中PreVS的顶点为原始输入坐标值,而PostVS的顶点为处理后的坐标值,即原始输入坐标值XWorld_View_Proj矩阵=处理后的坐标值,其中矩阵为世界,视图,投影三个矩阵的乘积。
调试Pixel Shader。在Detail窗口的Render模式,在要调试的像素上右击,弹出一个快捷菜单,选择菜单上的Debug This Pixel选项,进入Pixel Shader的调试模式。
具体的调试操作可以查阅SDK.
Visual Studil也提供了Shader的调试模式,不过不太实用,Microsoft也没有再对它更新。PIX所提供的调试功能比较完整,也比较实用。
OpenGL调试工具有glslDevil,GLIntercept和gDEBugger等。
摘选于彭国伦的《3D绘图程序设计》.