D3D10移植感想

    折腾了许久,今天终于完成到D3D10的移植了。总体感觉不错,性能提高了不少。基本上,有一种再也不想回到混乱的D3D9时代的感觉。新的API很精简,没有冗余,操作和性能的关系很明了,完全贴合硬件。

    当然,这也意味着,它和D3D8、9的设计方向完全不同,而是一套偏向底层的API。早年,D3D API一直饱受争议,PC开发者大多认为它太底层太难用,而少数来自游戏主机开发背景的人则反而认为它抽象层次太高阻碍优化(游戏主机的API要更加底层)。当然,PC开发者的意见形成压倒性的优势,所以D3D不断向易用性方向前进,在版本8、9达到顶峰,甚至一时间赢得了超过OpenGL的口碑。但出乎意料的是,可编程管线的迅速发展改写了前进的方向,尤其是顶级的游戏厂商,他们对性能和灵活性的追求远远超过易用性。一套API难以同时满足不同需求的用户。此时的OpenGL也遇到了相同的问题,游戏开发者认为GL API已经严重脱离当今的硬件加速模型,需要调整精简,而CAD开发者则认为完全不需要,兼容性才最重要。争论n年之后的结果,就是分成两个profile。

    这种情况下,D3D10被设计成一套全新的底层API,面向高端用户。一般商务3D需求则有高层的WPF来满足,两者间的空白由XNA来填补。WPF和XNA都是基于.NET的,而对D3D10,微软甚至没有官方的.NET包装。这个定位,也导致D3D10的使用难度很高,如果不是追求顶级性能和灵活性,没必要为此浪费青春,退半步就有微软在易用性上下了苦功的XNA,而且XNA本身也为未来过渡到D3D10架构做了充足的铺垫(XNA还是跨平台的,支持Windows、XBox、Windows Phone 7、Zune)。

    作为底层的API,D3D10是不太适合直接用来写应用的——尻,D3D就没有一个版本适合直接用来写应用的,每隔几年API就变得面目全非了,总得包装一层。但因为这次变动太大,包装层都不得不改。同时,D3DX的扩展库大幅缩水,大量的设计时预处理功能不复存在,而D3D11的扩展库几乎就不剩什么东西了。因为高端开发者是看不上D3DX的,他们都有自己的更优秀的实现。既然D3D10已经定位为面向高端开发的底层API,也就没必要费时间去维护庞大的D3DX了。这些功能呢要是自己重头写起,也是要花费不少功夫的。

    新架构下,渲染API核心只有三样东西:输入流(顶点数据)、配置管线中不可编程部分的状态对象、shader。Shader的作用被大幅放大,alpha test、clipping、fog等全由shader实现。所以熟练使用shader是升级到10的前提,最好在升级前就尽可能把这些都转换为shader实现。

    但话说现在D3D11都出了很久了,10的文档却还是那么的单薄。很多东西都要自己试出来。比如现在InputLayout必须和vertex shader的输入参数匹配,但其实只是要位置和semantic匹配,vertex shader的输入参数是可以比InputLayout少的。再比如,当前版本的effect pool含有非shared的cbuffer的话,编译正确,加载依赖于此pool的effect时则出错。再比如,D3DX10FilterTexture()是处理staging的纹理还是显存中的纹理呢,是CPU处理还是GPU处理?API里只有SamplerState,shader里却有一个没有文档只有样例的SamplerComparisonState,而且对于SampleCmp族函数,还必须使用SamplerComparisonState,其中一个文档样例中出现的属性,编译时会报错说该属性不存在……

    最头痛的是Device Removed。虽然没有了Device Lost,但这回不仅仅是暂时丢失控制权,而是设备直接没了。要调试一下也不容易,得卸载显卡驱动,再重新安装;或者带独立显卡的笔记本电脑扩展坞,接驳和断开。而且设备移除发生时,程序又能怎么办呢?等待重新出现一个可用设备。一个可能是可用设备不会再出现了,另一个可能是出现一个不同的设备,设备能力有所不同。以前设备丢失只要Reset就好了,managed资源会自动恢复,现在可得重新创建设备,什么都得重新初始化。当然,设备移除算是小概率事件,谁会在游戏过程中更新显卡驱动呢?当然接驳扩展坞还是可能的,驱动内部错误复位导致设备移除事件也是可能的……

    另一个较大的设计方向变化是,分离设计时与运行时。大量的操作,如状态对象的创建、管线各个stage间传递参数结构的映射链接、shader参数的按修改频率分组等,都要在设计时明确,初始化时执行,而运行时不再校验处理。D3D9的灵活、自由、朦胧的组合匹配不复存在。这对引擎架构有不小的影响,意味着vertex buffer的格式和shader相关,比如一个shadow map的shader不能再和不同格式的vertex buffer组合使用。

    一个有趣的发现是,D3D10 API不仅继承了D3D9的左右手系都支持的特性,而且更加的贴近右手系。D3D10_BOX等体描述都是按右手系的轴方向,D3D10_RASTERIZER_DESC也提供了OpenGL式的基于正反面而非缠绕方向的CullMode和独立的FrontCounterClockwise参数定义面正向的缠绕方向。在WPF、XNA都使用右手系的情况下,D3D转向右手系也是理所当然的。当然,纹理坐标系仍然是top-down而非GL的bottom-up。其实这个和左右手系无关,3DSMAX就是top-down的纹理坐标,2D纹理坐标使用top-down模式还是很符合2D处理习惯的(不如说是GL的bottom-up纹理坐标更另类一点)。3D纹理坐标就是在x向右y向下的2D坐标系基础上,加上向前的深度方向的z,正好是右手系。

    10的debug信息比以前有了大幅的进步,其详细程度超过文档。不过,比较不容易发现的是,要开启debug信息输出,不止要创建debug layer,而且要在DX Control Panel里加入程序的路径才行。debug信息会输出到IDE的Output Window,但是,对于managed project,需要在工程属性里debug页勾选unmanaged code debugging才行。对于免费的Visual Studio Express版,C#、VB无此选项,需要用其它工具,比如DebugViewNT。

    最后,相信大家会有这个疑问,为何不直接升级到D3D11呢。这个确实想过,但考虑到难度,步子迈得太大,会扯着蛋。从10到11会相对容易,9到10则问题重重,在调试很困难的情况下,循序渐进,一个一个地解决问题要容易些。另一方面,11刚出不算久,支持的显卡不多,用的人少,遇到问题时解决方案都难找。而且,微软对11的API的态度都有点实验性质的,还是“不敢为天下先”好了。

你可能感兴趣的:(游戏,windows,api,WPF,phone,shader,Semantic)