最近在对我们的渲染引擎进行优化的时候,发现一个奇怪的现象,因为我们做了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 浮点精度的问题的更多相关文章

  1. AI中各种浮点精度概念集合:fp16,fp32,bf16,tf32,fp24,pxr24,ef32

    常见的浮点类型有fp16,fp32,bf16,tf32,fp24,pxr24,ef32,能表达的数据范围主要看exponent,精度主要看fraction. 可以看出表达的数据范围看fp32,bf16 ...

  2. PHP中的浮点精度和类型

    PHP中的浮点数 精度 在PHP中,浮点数的字长和平台相关,通常最大值是 1.8e308 并具有 14 位十进制数字的精度(64 位 IEEE 格式). 浮点数的精度有限.尽管取决于系统,PHP 通常 ...

  3. js浮点精度问题

    1.先看下图: 2.为什么呢? 3.其实最早自己见过这样的情况,因为不懂得其中的真正道理,每次都是“猜”,结果就可想而知了. ==========原因========== 4.在控制台出现这样的情况在 ...

  4. js 乘除算法 浮点 精度解决办法

    js中进行浮点数运算时容易出现精度问题 1) 除法函数 //说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显.这个函数返回较为精确的除法结果. //调用:accDiv(a ...

  5. delphi 浮点 精度

    double 没有问题, Single有问题 '0.7' 0.69999999999999996 Single; // 4 byte real Double; // 8 byte real

  6. PHP浮点精度问题

    使用php+ - * /计算浮点数的时候,可能会遇到一些计算结果错误的问题,如下: <?php echo intval(0.58 * 100); //输出57 解决办法 <?php ech ...

  7. SQL中各数据类型的长度、精度

    在 Microsoft? SQL Server? 中,每个列.局部变量.表达式和参数都有一个相关的数据类型,这是指定对象可持有的数据类型(整型.字符.money 等等)的特性. SQL Server ...

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

    使用Intel编译器获得一致的浮点数值计算结果大多数十进制的浮点数, 用二进制表示时不是完全一致的; 与此同时, 大多数与浮点数值相关的计算结果, 存在着固有的不确定性.通常, 编写浮点计算应用软件希 ...

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

    Animation是Unity中的动画文件,主要内容由一个个关键帧数据构成.通过将Unity的资源序列化方式调整为Text,就可以以文本方式查看动画文件.通过菜单项Edit -> Project ...

随机推荐

  1. laravel transaction : laravel 的事务是不支持eloquent的, 要用DB::的方式

    数据库事务处理# 你可以使用 transaction 方法,去执行一组数据库事务处理的操作: DB::transaction(function() { DB::table('users')->u ...

  2. 每天php函数 - explode() 使用一个字符串分割另一个字符串

    array explode ( string $delimiter , string $string [, int $limit ] ) 此函数返回由字符串组成的数组,每个元素都是 string的一个 ...

  3. 有两个地方,用到了javabean对象和属性字符串值之间的转换

    1.有两个地方,用到了javabean对象和属性字符串值之间的转换 2.一个是接入层spring mvc,将json字符串参数转换为javaBean.通过@RequestBody javaBean方式 ...

  4. 以http形式启动uwsgi服务

    uwsgi yourfile.ini # 配置文件 [uwsgi] http = 127.0.0.1:3106 socket = 127.0.0.1:3006 chdir = /www/student ...

  5. Yii源码阅读笔记(一)

    今天开始阅读yii2的源码,想深入了解一下yii框架的工作原理,同时学习一下优秀的编码规范和风格.在此记录一下阅读中的小心得. 每个框架都有一个入口文件,首先从入口文件开始,yii2的入口文件位于we ...

  6. wordpress自定义数据库出错页面

    wordpress数据连接出错时,会有一个空白页面,有一行字:数据连接错误.这样当然不美观,好在这个页面是可以自定义的. 在/wp-content/目录下创建'db-error.php'文件,当数据库 ...

  7. P1434 滑雪

    水题,记忆化搜索,队列bfs均可 我们定义f[i][j]为到(i, j)的最长路径.然后就不难得出状态转移方程,然后使用无脑dfs,或者有脑递推都是可以的. #include <bits/std ...

  8. java笔记--关于线程同步(5种同步方式)【转】

    为何要使用同步?     java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),      将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完 ...

  9. 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 ...

  10. RocEDU.阅读.写作

    RocEDU.阅读.写作 一.选择图书 <黑客大曝光> 二.读书计划 56天内学习完.时间:2016.01.25--2016.03.20 三.承诺 宋宸宁郑重承诺: 1.在56天内(开始时 ...