D3D9 浮点精度的问题

  最近在对我们的渲染引擎进行优化的时候,发现一个奇怪的现象,因为我们做了Pre-Z(把比较大的物体先绘制一遍,这个时候关闭颜色写,只开启深度测试和写入,目的是为了减少后面一些不可见像素的计算。),面在绘制另外一遍的时候(这一遍我们是把相同的进行了合批处理,采用硬件实例化技术(Hardware instancing)),会发现某些像素会闪烁,这个就说明了两次计算的深度并不一样才导致了闪烁的问题,知道了问题出现的原因,那就开始查找。

  一开始主要有以下几个猜测:

  1、可能是在把世界位置传给GPU时精度丢失了一部分精度,或者在GPU和CPU上计算的world-view-projection矩阵有差距?这个通过修改微软自带的Instancing实例进行验证,此猜测是错误的。

  2、因为我们是把顶点位置进行了压缩成16位浮点数,是不是这样造成的?同样,我们紧接着修改了下微软自带的示例来进行验证,此猜测也是错误的。

  3、难道两次计算的结果不一样?经过跟踪,发现两次计算相乘的矩阵是完全一样的,但是再次结果某些浮点数后几位确实是不一样的。这时候怀疑是不是多线程引起的,写了个测试程序,多个线程同时跑计算出来的结果是一样的,再次证明猜测是错误的。

  4、也怀疑是编译器对浮点数计算优化的问题,把编译选项改成了/fp:precise结果还是一样,说明跟这个猜测也是错误的。

  5、难道是在计算时浮点运算器x87 FPU运算时状态被修改了?这个时候同事帮忙在网上搜到了D3D9下面浮点精度的问题,这才找到了问题,原来是D3D9为了优化搞的鬼,看来对D3D9还是不太熟悉。D3D9在创建设备时有个BehaviorFlags,用于控制创建设备的。

其中有一个标记就是D3DCREATE_FPU_PRESERVE,D3D9对它的解释是这样的:

  Set the precision for Direct3D floating-point calculations to the precision used by the calling thread. If you do not specify this flag, Direct3D defaults to single-precision round-to-nearest mode for two reasons:

  • Double-precision mode will reduce Direct3D performance.
  • Portions of Direct3D assume floating-point unit exceptions are masked; unmasking these exceptions may result in undefined behavior.

也就是说D3D9为了效率考虑,把使用D3D9的线程强制修改了浮点运算器的标记位,这样在计算的时候就可以使用单精度浮点数进行计算了,这样做主要有两个原因:

  1、双数度模式会降低D3D的性能。

  2、一些函数假设浮点单元异常被标记了,否则会可能会出现未定义的行为。

  在FPU中,存在着三种运算精度:single precision(24bits),double precision(53bits),double extended precision(64bits)。而默认精度是53bits的double precision,也就是双精度浮点。D3D出于性能考虑,会将fpu的计算精度改为单精度。因为fpu线程相关的特性,渲染线程中所有的浮点运算都会保持与D3D一致。这种转变体现在fpu的控制寄存器(CTRL)的变化上,CTRL寄存器的值从007F变成027F。

RC字段,这个字段控制浮点转整型的转换方式,

  00 = 朝最接近或者偶数舍入 01 = 朝负无穷大方向舍入

  10 = 朝正无穷大方向舍入 11 = 超0方向截断

  PC 字段精度控制

  00 = 单精度 01 = 保留 10 = 双精度 11 = 扩展精度[1]

  既然找到了问题的原因,那么就要找一个比较好的解决方案:

  1、创建设备时指定D3DCREATE_FPU_PRESERVE标记,让D3D9采用双精度浮点运算,这样会降低D3D9的运行效率。

  2、让主线程和渲染线程同时使用单精度进行浮点数的计算,可以使用微软提供的函数_controlfp_s[2]来达到目的,这样可以提高所有的效果,但是可能会出现其它未预料到的精度问题,还有一个不好的地方就是需要客户端处理这个事情。比如有些人就遇到了客户端中lua出了问题的现象。[3]

  3、所有需要传给GPU的浮点运算均在渲染线程执行,理论上可以,但是实际操作起来并不是那么现实。

  我比较倾向于使用第一种方法,原因在上面已经说明了,不知道各位会怎么解决这个问题,欢迎留言讨论。

参考文章:


[1] http://www.cnblogs.com/wxxweb/archive/2011/12/13/2285565.html

[2] https://msdn.microsoft.com/en-gb/library/c9676k6h(v=vs.90).aspx

[3] http://m.blog.csdn.net/blog/xiaohyy/5309145

时间: 2025-01-17 18:18:42

D3D9 浮点精度的问题的相关文章

Unity - 通过降低精度减少动画文件的大小

Animation是Unity中的动画文件,主要内容由一个个关键帧数据构成.通过将Unity的资源序列化方式调整为Text,就可以以文本方式查看动画文件.通过菜单项Edit -> Project Settings -> Editor打开Editor Settings窗口,就可以设置资源序列化方式: 下图展示了我对一个Cube制作的动画,动画中包含了若干个关键帧,调整了Cube的坐标位置和旋转方向: 以文本方式打开动画文件,部分内容如下: 动画文件的序列化格式不在我们的讨论范围内,本文我们主要讨

使用Intel编译器获得一致的浮点数值计算结果

大多数十进制的浮点数, 用二进制表示时不是完全一致的; 与此同时, 大多数与浮点数值相关的计算结果, 存在着固有的不确定性. 通常, 编写浮点计算应用软件希望达到如下的目标:  - 准确性:     意味着该产品产生的计算结果,应当"接近"于实际计算的结果; 评判的标准是误差值, 有时候也采用最后几位("units in the last place", ulp)  - 可复制性:    意味着该产品始终产生一致的结果, 无论运行的先后, 采用不同的编译选项, 使用

CCF-CSP 201903-1 小中大(常见浮点错误)

做题时遇到如下几个问题:而从第2开始应该算是比较典型而容易出错的,在此记录. 1. %g占位符: 题目要求“对于整数,直接输出整数”.可能会想到直接浮点运算,结果为整数时,小数点后全为0,省略后,看起来像输出了一个整数.于是利用printf的占位符“%g”输出,但这种做法存在严重的问题:当数据较长或较短时%g相当于%e,即采用了科学计数法,而不是简单地省略小数点后多余的0.  虽然%f不存在该问题,但会输出小数点后多余的0. 而接下来分析的机器相关问题将彻底否定这种利用浮点统一计算的想法: 2.

MSSQL数据类型

数据类型 真数 bigint 从 -2^63 (-9223372036854775808) 到 2^63-1 (9223372036854775807) 的整型数据(所有数字). int 从 -2^31 (-2,147,483,648) 到 2^31 - 1 (2,147,483,647) 的整型数据(所有数字). smallint 从 -2^15 (-32,768) 到 2^15 - 1 (32,767) 的整数数据. tinyint 从 0 到 255 的整数数据. bit 1 或 0 的整

201671010130 2016-2017-2 《Java程序设计》第二周学习小结

学习Java第三章小结 本周我学会了: 首先是解决关于解决运行程序前出现了错误提示"editor dose not contain a main type"程序无法运行"的问题,通过网友的博客http://blog.csdn.net/huazhangena/article/details/7349044,出现这个问题的原因是我们所建的主类文件未放在编译器访问路径下,也就是缺省路径在项目非缺省源程序文件夹下下保存了源程序,解决办法就是重构这个程序的编译路径. 现在的我已经可以静

JDBC规范(转)

公司开发一直用的是ibatis,进来心血来潮想研究一下源码,可是发现自己的JDBC似乎已经忘得差不多了,为了自己能顺利的研读ibatis的源码,于是乎找到了 XIAO_DF的JDBC规范的博客,转到自己博客方便阅读,感谢技术老铁的分享! JDBC接口规范 前言 JDBC(JavaDatabase Connectivity)表示Java查询引擎连接,由一组用Java编程语言编写的类和接口组成.JDBC为Java程序访问关系型查询引擎提供了编程接口,为查询引擎开发人员提供了一个标准的API,使他们能

(转)女生应该找一个玩ACM的男生

1.强烈的事业心 将来,他也一定会有自己热爱的事业.而且,男人最性感的时刻之一,就是他专心致志做事的时候.所以,找一个机会在他全神贯注玩ACM的时候,从侧面好好观察他,你就会发现我说的话没错. 2.永不放弃的精神 在比赛刚开始,神牛队就飘崎岖.玩ACM的男生不会退缩,而是毅然决定一个人继续AC下去,靠着自己对胜利的渴望,拿下一个气球.两个气球.三个气 球……N个气球!即使WA了10+次也始终如一的战斗到最后一刻.如果没有不服输的精神,早就放弃了.所以,想要在玩ACM的男性里找一个容易服输的人很

C专家编程 总结

1 类型转换 当执行算术运算时,操作数的类型如果不同,就会发生转换,数据类型一般朝着浮点精度高.长度更长的方向转换,整数型如果转换为signed不会丢失信息,就转换为signed,否则转换为unsigned. K&R C所采用无房户后保留原著,就是当一个无符号类型与int或更小的整型混合使用时,结果类型是无符号类型. 2 C语言中const并不真正表示常量. 3 switch语句的缺点 1)switch语句最大的缺点是它不会在每个case标签后面的语句执行完毕后自动终止. 2)由于break语句

BestCoder22 1003.NPY and shot 解题报告

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5144 题目意思:有个人抛物体,已知抛的速度和高度,问可以抛到的最远距离是多少.即水平距离. 做的时候是抄公式的,居然过了,幸运幸运............ 1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <cstring> 5 #include <algorit