2008年6月14日星期六

OGRE D3D & 浮点数精度问题

这两天问鼎客户端运行时出现一个问题,同一个公式在服务器端和客户端计算的结果不同。因为两端使用的都是同一个LPC driver,运行在同一个主机上,理论上不应该出现这种情况。

我关闭了客户端所有启动的代码以后,发现在计算双精度除法1.0/6时,结果并不是双精度的结果,而服务器端则保持是双精度的结果。进一步研究发现,可能是客户端受到了OGRE的影响,OGRE可能通过某个设置修改了FPU的行为。

首先我们确认:FPU的确可以通过指令FLDCW来控制计算只采用单精度的方式。

然后我一步步调整代码,最后确认在OGRE createRenderWindow以后就会变成单精度的工作方式。这时陈拓琳提议用OpenGL的renderer试试,发现果然恢复正常,不会变成单精度的工作方式,那么看来是D3D的renderer导致的结果。

幸运的是,陈拓琳很快在OGRE代码中搜索到了控制D3D的开关,可以控制这种行为,通过:

RenderSystem::setConfigOption("Floating-point mode", "Consistent")

可以让D3D不会修改FPU的工作方式,这个调用需要在构造Renderer以后尽快执行。

注:这个调用会在初始化时设置D3D的相应参数,通知D3D在执行其调用时先执行FSTCW保存状态,调用完毕再执行FLDCW恢复状态,不至于影响外部代码使用FPU的行为。

注:driver采用的是double类型来处理所有的浮点数运算,如果全部double均降格为单精度,计算最终结果是不会有问题的,但是因为driver会在OGRE初始化之前先载入一部分代码,这部分代码记录了一批双精度的值,和单精度混用会出问题。

注:浮点计算结果应该均采用4舍5入方式,这样即使单、双精度混用在日常游戏逻辑中也不会出现问题。但是这样对策划要求会提高,可能没有必要。

没有评论:

发表评论