游戏开发论坛

 找回密码
 立即注册
搜索
查看: 3440|回复: 5

发现个问题:若D3D9设备先于Lua创建,则Lua会运行异常.

[复制链接]

13

主题

36

帖子

301

积分

中级会员

Rank: 3Rank: 3

积分
301
发表于 2013-10-15 09:55:12 | 显示全部楼层 |阅读模式
本帖最后由 书径尘 于 2013-10-15 10:09 编辑

为了保证测试正确,我专门用DX9的SDK里的D3D9初始化程序做的测试,在D3D设备创建完后,我初始化lua,然后运行lua脚本的一个函数,该函数又调用应用程序的一个C函数,参数就是1个16进制数:

--script
function Script_Main()
App_Test(0xFFFF00FF)
end

// app
int Appt_Test(lua_State* pLuaState)
{
UINT color = (UINT)lua_tonumber(pLuaState, 1);   // 就是这里,这里读出的颜色却是0xffff0100
return 0;
}

但是呢,如果先初始化lua,再去创建D3D设备,或者,干脆不去创建,读出的颜色是正常的,
非常令人不解...

PS:我用的lua版本是5.2.1

13

主题

36

帖子

301

积分

中级会员

Rank: 3Rank: 3

积分
301
 楼主| 发表于 2013-10-15 10:08:23 | 显示全部楼层
百度了一下,找到了些蛛丝马迹:

首先是一个lua capi和DX共用时一个要注意的地方. 在hge调用System_Initiate后(对D3D9设备初始化后), lua_tointeger,
luaL_checkinteger等从交换栈中取出整型数据的capi都永远返回0了(不管栈中数据是什么).

一开始感到很奇异,lua的capi怎么会和DX扯上关系. 对照lua的源码,发现失效的函数最终都是调用
了lua_tointeger, 而lua_tointeger是这样实现的

  if (tonumber(o, &n)) {
    lua_Integer res;
    lua_Number num = nvalue(o);
    lua_number2integer(res, num);
    return res;
  }
其意思是,首先尝试把n转换为lua_Number(包括n为String的情况),然后在通过lua_number2interger
这一宏来将lua_Number转换为lua_Integer, 即对浮点数取整.

然而, lua_number2interger为了优化效率实现了一些trick,如下(在luaconf.h中)


#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) && !defined(__SSE2__) &&
    (defined(__i386) || defined (_M_IX86) || defined(__i386__))
   

#if defined(_MSC_VER)

#define lua_number2int(i,d)   __asm fld d   __asm fistp i
#define lua_number2integer(i,n)     lua_number2int(i, n)


#else
   
union luai_Cast { double l_d; long l_l; };
#define lua_number2int(i,d)
  { volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; }
#define lua_number2integer(i,n)     lua_number2int(i, n)

#endif


#else
#define lua_number2int(i,d) ((i)=(int)(d))
#define lua_number2integer(i,d) ((i)=(lua_Integer)(d))

#endif

如果在Pentium处理器并且是用Ms的c编译器, 便通过内联asm来完成这操作:
fld d : 加载浮点数
fistp i: 浮点数取整出栈

如果不是Ms的c编译器,作者使用了一种trick
(据说是id Software的约翰·卡马克发明的, Wolfenstein 3D的作者 ^^)

union luai_Cast { double l_d; long l_l; };
{ volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; }

但是注意作者的注释里提示到:可能会于Dx冲突, 因为在D3d中会调节FPU的运算精度,
而在低精度模式下,trick便失效.

...

13

主题

36

帖子

301

积分

中级会员

Rank: 3Rank: 3

积分
301
 楼主| 发表于 2013-10-15 13:42:38 | 显示全部楼层
搜索 D3D sdk ,在 CreateDevice 中发现 D3DCREATE 有这么一个选项

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


很明显了, d3d 在未指定该参数的时候会把 FPU 设置为单精度模式,其原因是因为处理单精度数据比双精度具备更好的性能。由于 D3D 默认修改了 FPU ,影响到了 lua 的精度( lua 里面 number 只有 double ),造成了一定时间的误差。

以上源自:
http://blog.csdn.net/xiaohyy/article/details/5309145

201

主题

1437

帖子

1963

积分

金牌会员

Rank: 6Rank: 6

积分
1963
QQ
发表于 2013-10-18 09:25:45 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

16

主题

154

帖子

309

积分

中级会员

Rank: 3Rank: 3

积分
309
发表于 2013-10-19 23:03:07 | 显示全部楼层
了解一下.

0

主题

2

帖子

6

积分

新手上路

Rank: 1

积分
6
发表于 2013-10-20 09:08:49 | 显示全部楼层
进来学习的
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-2-26 08:23

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表