最近在对我们的渲染引擎进行优化的时候,发现一个奇怪的现象,因为我们做了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. php 递归创建目录、递归删除非空目录、迭代创建目录

    递归创建目录 方法一 function mk_dir($path){ if(is_dir($path)){ //参数本身是一个目录 return true; } if(is_dir(dirname($ ...

  2. artDialog ( v 6.0.2 ) content 参数引入页面 html 内容

    /*! artDialog v6.0.2 | https://github.com/aui/artDialog */ 将页面某一隐藏的 div 的 html 内容传到 artdialog 的弹窗中,并 ...

  3. 判断返回数据是否为 null

    判断是否为null //尖括号                if ([regeocode.city isEqual:[NSNull class]]) {                    NSL ...

  4. TCP/IP简介

    TCP/IP协议分层 提到协议分层,我们很容易联想到ISO-OSI的七层协议经典架构,但是TCP/IP协议族的结构则稍有不同.如图所示 TCP/IP协议族按照层次由上到下,层层包装.最上面的就是应用层 ...

  5. Java的访问控制

       类内部  本包(实例.类变量和方法)  子类(任何位置) 外部包(实例.类变量和方法) public    √  √  √  √ protected   √  √  √  × default  ...

  6. python的变量

    Python变量 在Python中,变量的概念基本上和初中代数的方程变量是一致的.例如,对于方程式 y=x*x ,x就是变量.当x=2时,计算结果是4,当x=5时,计算结果是25. 只是在计算机程序中 ...

  7. 超简单的处理JSON格式和JSON数组格式的String

    现在网站上有不少处理JSON格式的工具类,但是我找了一天,发现大都是需要编写相应对象类来进行处理,比较麻烦,比如:Gson,json-lib.Gson,json-lib这些处理那些接口之类的参数名字和 ...

  8. docker sonaqube

    postgresql:   image: orchardup/postgresql:latest   environment:   - POSTGRESQL_USER=sonar   - POSTGR ...

  9. 002_base64的编码实现

    一.先贴一张ASCII码的图 二.再贴一张base64转换规则的图 二.python代码实现.

  10. android 直接启动其他应用的Service

    最近在做一个小插件,没有图标没有activity,利用其他APK启动它的service. 直奔主题,插件A,安装插件的应用B. B安装A后,由于A刚被安装,没有注册广播接收器,这里不考虑AIDL.需求 ...