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
[3] http://m.blog.csdn.net/blog/xiaohyy/5309145
D3D9 浮点精度的问题的更多相关文章
- AI中各种浮点精度概念集合:fp16,fp32,bf16,tf32,fp24,pxr24,ef32
常见的浮点类型有fp16,fp32,bf16,tf32,fp24,pxr24,ef32,能表达的数据范围主要看exponent,精度主要看fraction. 可以看出表达的数据范围看fp32,bf16 ...
- PHP中的浮点精度和类型
PHP中的浮点数 精度 在PHP中,浮点数的字长和平台相关,通常最大值是 1.8e308 并具有 14 位十进制数字的精度(64 位 IEEE 格式). 浮点数的精度有限.尽管取决于系统,PHP 通常 ...
- js浮点精度问题
1.先看下图: 2.为什么呢? 3.其实最早自己见过这样的情况,因为不懂得其中的真正道理,每次都是“猜”,结果就可想而知了. ==========原因========== 4.在控制台出现这样的情况在 ...
- js 乘除算法 浮点 精度解决办法
js中进行浮点数运算时容易出现精度问题 1) 除法函数 //说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显.这个函数返回较为精确的除法结果. //调用:accDiv(a ...
- delphi 浮点 精度
double 没有问题, Single有问题 '0.7' 0.69999999999999996 Single; // 4 byte real Double; // 8 byte real
- PHP浮点精度问题
使用php+ - * /计算浮点数的时候,可能会遇到一些计算结果错误的问题,如下: <?php echo intval(0.58 * 100); //输出57 解决办法 <?php ech ...
- SQL中各数据类型的长度、精度
在 Microsoft? SQL Server? 中,每个列.局部变量.表达式和参数都有一个相关的数据类型,这是指定对象可持有的数据类型(整型.字符.money 等等)的特性. SQL Server ...
- 使用Intel编译器获得一致的浮点数值计算结果
使用Intel编译器获得一致的浮点数值计算结果大多数十进制的浮点数, 用二进制表示时不是完全一致的; 与此同时, 大多数与浮点数值相关的计算结果, 存在着固有的不确定性.通常, 编写浮点计算应用软件希 ...
- Unity - 通过降低精度减少动画文件的大小
Animation是Unity中的动画文件,主要内容由一个个关键帧数据构成.通过将Unity的资源序列化方式调整为Text,就可以以文本方式查看动画文件.通过菜单项Edit -> Project ...
随机推荐
- laravel transaction : laravel 的事务是不支持eloquent的, 要用DB::的方式
数据库事务处理# 你可以使用 transaction 方法,去执行一组数据库事务处理的操作: DB::transaction(function() { DB::table('users')->u ...
- 每天php函数 - explode() 使用一个字符串分割另一个字符串
array explode ( string $delimiter , string $string [, int $limit ] ) 此函数返回由字符串组成的数组,每个元素都是 string的一个 ...
- 有两个地方,用到了javabean对象和属性字符串值之间的转换
1.有两个地方,用到了javabean对象和属性字符串值之间的转换 2.一个是接入层spring mvc,将json字符串参数转换为javaBean.通过@RequestBody javaBean方式 ...
- 以http形式启动uwsgi服务
uwsgi yourfile.ini # 配置文件 [uwsgi] http = 127.0.0.1:3106 socket = 127.0.0.1:3006 chdir = /www/student ...
- Yii源码阅读笔记(一)
今天开始阅读yii2的源码,想深入了解一下yii框架的工作原理,同时学习一下优秀的编码规范和风格.在此记录一下阅读中的小心得. 每个框架都有一个入口文件,首先从入口文件开始,yii2的入口文件位于we ...
- wordpress自定义数据库出错页面
wordpress数据连接出错时,会有一个空白页面,有一行字:数据连接错误.这样当然不美观,好在这个页面是可以自定义的. 在/wp-content/目录下创建'db-error.php'文件,当数据库 ...
- P1434 滑雪
水题,记忆化搜索,队列bfs均可 我们定义f[i][j]为到(i, j)的最长路径.然后就不难得出状态转移方程,然后使用无脑dfs,或者有脑递推都是可以的. #include <bits/std ...
- java笔记--关于线程同步(5种同步方式)【转】
为何要使用同步? java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查), 将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完 ...
- ADS报错 Warning : L6301W:Could not find file C:\Program Files . Error : L6218 : Undefined symbol ......
ADS1.2编译时,出现找不到一个不存在目录下的目标文件(*.o) 编译一个COPY到硬盘上的一个工程,出现以下的fatal error message: Error: (Fatal)L6002: C ...
- RocEDU.阅读.写作
RocEDU.阅读.写作 一.选择图书 <黑客大曝光> 二.读书计划 56天内学习完.时间:2016.01.25--2016.03.20 三.承诺 宋宸宁郑重承诺: 1.在56天内(开始时 ...