Xlua对c#的vector3等结构体的优化

摘要:
Ud={[“fake_id”]=-1[“len”]=12//存储三个浮点值的大小。}catch(System.Exceptiongen_e){returnLuaAPI.loal_error(L;intmeta_ref){CSharpStruct*css=(CSharpSstruct*)lua_neuserdata(L,len=size;
目录:Xlua源码学习 
参考文章::
https://www.jianshu.com/p/07dc38e85923
https://gameinstitute.qq.com/community/detail/111993
https://gameinstitute.qq.com/community/detail/125117
一、lua如何操作Vector3,中间做了什么操作?
1.获取Vector3对象
由于Vector3的方法、属性都是成员方法、属性(如x、y、z、Slerp),那么调用这些方法前需要先获取Vector3对应的对象。比如Vector3()新建、transform.position获取等。
以transform.position为例:_g_get_position方法创建了一个CSharpStruct类型的ud,并把x,y,z存在这个ud里。这个ud的元表指向Vector3的obj_meta,通过这个元表可以访问Vector3Wrap注册的所有方法、属性,进而间接访问到Vector3。
ud = 
{
    ["fake_id"] = -1
    ["len"] = 12 //存储3个float值的大小,的对应xyz,float是4字节32位。
    ["data"][0] = x
    ["data"][1] = y
    ["data"][0] = z
    ["__index"] = obj_meta
}
static int _g_get_position(RealStatePtr L)
{
    try {
        ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
 
        UnityEngine.Transform gen_to_be_invoked = (UnityEngine.Transform)translator.FastGetCSObj(L, 1);
        translator.PushUnityEngineVector3(L, gen_to_be_invoked.position);
    } catch(System.Exception gen_e) {
        return LuaAPI.luaL_error(L, "c# exception:" + gen_e);
    }
    return 1;
}
 
public void PushUnityEngineVector3(RealStatePtr L, UnityEngine.Vector3 val)
{
    if (UnityEngineVector3_TypeID == -1)
    {
        bool is_first;
        UnityEngineVector3_TypeID = getTypeId(L, typeof(UnityEngine.Vector3), out is_first);   
    }
    //创建一个大小为(12+4+4)字节的userdata,元表Vector3的元表
    IntPtr buff = LuaAPI.xlua_pushstruct(L, 12, UnityEngineVector3_TypeID);
    if (!CopyByValue.Pack(buff, 0, val)) //把vector3拆成3个float传入cc,在c的结构体buff存储数据
    {
    throw new Exception("pack fail fail for UnityEngine.Vector3 ,value="+val);
    }
}
xlua.c里的接口:
LUA_API void *xlua_pushstruct(lua_State *L, unsigned int size, int meta_ref) {
   CSharpStruct *css = (CSharpStruct *)lua_newuserdata(L, size + sizeof(int) + sizeof(unsigned int));
   css->fake_id = -1;
   css->len = size;
    lua_rawgeti(L, LUA_REGISTRYINDEX, meta_ref);
   lua_setmetatable(L, -2);
   return css;
}
 
LUALIB_API int xlua_pack_float3(void *p, int offset, float f1, float f2, float f3) {
   CSharpStruct *css = (CSharpStruct *)p;
   if (css->fake_id != -1 || css->len < offset + sizeof(float) * 3) {
      return 0;
   } else {
      float *pos = (float *)(&(css->data[0]) + offset);
      pos[0] = f1;
      pos[1] = f2;
      pos[2] = f3;
      return 1;
   }
}
2.设置transform.position
代码如下:
主要是UnPack方法调用xlua的xlua_unpack_float3方法,从上面的ud结构里取到x,y,c的值压栈并赋值给UnPack的x,y,z参数,再由UnPack组装一个新的Vevtor3返回给_s_set_position进行赋值。
static int _s_set_position(RealStatePtr L)
{
    try {
        ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
 
        UnityEngine.Transform gen_to_be_invoked = (UnityEngine.Transform)translator.FastGetCSObj(L, 1);
        UnityEngine.Vector3 gen_value;
        translator.Get(L, 2, out gen_value);
        gen_to_be_invoked.position = gen_value;      
    } catch(System.Exception gen_e) {
        return LuaAPI.luaL_error(L, "c# exception:" + gen_e);
    }
    return 0;
}
 
public void Get(RealStatePtr L, int index, out UnityEngine.Vector3 val)
{
    LuaTypes type = LuaAPI.lua_type(L, index);
    if (type == LuaTypes.LUA_TUSERDATA )
    {
        IntPtr buff = LuaAPI.lua_touserdata(L, index);
        if (!CopyByValue.UnPack(buff, 0, out val))
        {
            throw new Exception("unpack fail for UnityEngine.Vector3");
        }
    }
    else if (type ==LuaTypes.LUA_TTABLE)
    {
        CopyByValue.UnPack(this, L, index, out val);
    }
    else
    {
        val = (UnityEngine.Vector3)objectCasters.GetCaster(typeof(UnityEngine.Vector3))(L, index, null);
    }
}
 
public static bool UnPack(IntPtr buff, int offset, out UnityEngine.Vector3 field)
{
    field = default(UnityEngine.Vector3);
    
    float x = default(float);
    float y = default(float);
    float z = default(float);
    
    if(!LuaAPI.xlua_unpack_float3(buff, offset, out x, out y, out z))
    {
        return false;
    }
    field.x = x;
    field.y = y;
    field.z = z;
    
    
    return true;
}
当然,如果你传的不是Vector3类型的usreData就更简单了,例如{x = 1, y = 2, z = 3},直接调用lua_rawget就可以取到了。
public static void UnPack(ObjectTranslator translator, RealStatePtr L, int idx, out UnityEngine.Vector3 val)
{
    val = new UnityEngine.Vector3();
          int top = LuaAPI.lua_gettop(L);
   
   if (Utils.LoadField(L, idx, "x"))
          {
       
              translator.Get(L, top + 1, out val.x);
      
          }
          LuaAPI.lua_pop(L, 1);
   
   if (Utils.LoadField(L, idx, "y"))
          {
       
              translator.Get(L, top + 1, out val.y);
      
          }
          LuaAPI.lua_pop(L, 1);
   
   if (Utils.LoadField(L, idx, "z"))
          {
       
              translator.Get(L, top + 1, out val.z);
      
          }
          LuaAPI.lua_pop(L, 1);
   
}
 
public static bool LoadField(RealStatePtr L, int idx, string field_name)
{
   idx = idx > 0 ? idx : LuaAPI.lua_gettop(L) + idx + 1;// abs of index
   LuaAPI.xlua_pushasciistring(L, field_name);
   LuaAPI.lua_rawget(L, idx);
   return !LuaAPI.lua_isnil(L, -1);
}
xlua.c的解包方法:
LUALIB_API int xlua_unpack_float3(void *p, int offset, float *f1, float *f2, float *f3) {
   CSharpStruct *css = (CSharpStruct *)p;
   if (css->fake_id != -1 || css->len < offset + sizeof(float) * 3) {
      return 0;
   } else {
      float *pos = (float *)(&(css->data[0]) + offset);
      *f1 = pos[0];
      *f2 = pos[1];
      *f3 = pos[2];
      return 1;
   }
}
3.Vector.x:取x值
流程其实跟transform.position差不多,先把自身的ud转成Vector3,再把Vector3.x压栈返回给lua。
static int _g_get_x(RealStatePtr L)
{
    try {
        ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
 
        UnityEngine.Vector3 gen_to_be_invoked;
        translator.Get(L, 1, out gen_to_be_invoked);
        LuaAPI.lua_pushnumber(L, gen_to_be_invoked.x);
    } catch(System.Exception gen_e) {
        return LuaAPI.luaL_error(L, "c# exception:" + gen_e);
    }
    return 1;
}
4.调用总结:
lua跟c#的Vector3并没有直接的调用,而是通过中间层ud(CSharpStruct)进行中转。
这里面的PushUnityEngineVector3、Get方法都是在wrap生成时动态生成的。
每个GCOptimize标记的方法都会生成对应的Push、Get、Pack、UnPack方法,这些方法会调用到xlua.c里对应的c方法对结构体进行解包、封包。
二、xlua为什么要这么做?
Vector3Wrap的__CreateInstance方法使用的是translator.PushUnityEngineVector3对Vector对象压栈。那为什么不用正常的translator.Push压栈呢?
static int __CreateInstance(RealStatePtr L)
{        
    try {
        ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
        if(LuaAPI.lua_gettop(L) == 4 && LuaTypes.LUA_TNUMBER == LuaAPI.lua_type(L, 2) && LuaTypes.LUA_TNUMBER == LuaAPI.lua_type(L, 3) && LuaTypes.LUA_TNUMBER == LuaAPI.lua_type(L, 4))
        {
            float _x = (float)LuaAPI.lua_tonumber(L, 2);
            float _y = (float)LuaAPI.lua_tonumber(L, 3);
            float _z = (float)LuaAPI.lua_tonumber(L, 4);
      
            UnityEngine.Vector3 gen_ret = new UnityEngine.Vector3(_x, _y, _z);
            translator.PushUnityEngineVector3(L, gen_ret);
                 
            return 1;
        }
    }
    catch(System.Exception gen_e) {
       return LuaAPI.luaL_error(L, "c# exception:" + gen_e);
    }        
}
translator.Push里会调用addObject把对象实例跟对象下标做一个绑定,这个唯一的下标用于在lua中获取到c#的对象实例。Struct是值类型,值类型和引用类型(object)的转换会涉及装箱、拆箱操作。
int addObject(object obj, bool is_valuetype, bool is_enum)
{
          int index = objects.Add(obj);
          if (is_enum)
          {
              enumMap[obj] = index;
          }
          else if (!is_valuetype)
          {
              reverseMap[obj] = index;
          }
   
   return index;
}
Xlua对c#的vector3等结构体的优化第1张
 
对装箱、拆箱不了解的可以参考文章:https://www.cnblogs.com/yukaizhao/archive/2011/10/18/csharp_box_unbox_1.html
 
所以xlua其实是对Vector3等值类型的结构体做了一个优化,避免了装箱和拆箱优化
但是,虽然xlua对结构体做了优化,但还是会有很多开销,例如:在c#需要push xyz参数,在lua需要创建表保存xyz值,需要查询元表等。
 
那么如何优化呢?直接在函数中传递三个float,要比传递Vector3要更快。
例如void SetPos(GameObject obj, Vector3pos)改为void SetPos(GameObject obj, float x, floaty, float z)。
对于c#的Struct还是少用,尽量封装静态方法调用,性能会更好。

免责声明:文章转载自《Xlua对c#的vector3等结构体的优化》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇第3章 Spring AOP对称加密和分组加密中的四种模式(ECB、CBC、CFB、OFB)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

机器学习中的正则化问题(1)——概念

一般来说,监督学习就是最小化下面函数: [w^* = argmin_w sum_iL(y_i,;f(x_i;w)) + lambdaOmega(w) ] 对于第一部分——Loss函数,一般有如下几种情况: 如果是Square loss,那就是最小二乘了; 如果是Hinge Loss,那就是著名的SVM了; 如果是exp-Loss,那就是牛逼的 Boo...

DevOps专题|Lua引擎打造超轻量级客户端

Lua 作为一门轻量级脚本语言,源码使用标准C语言发布,语法简洁,非常适合嵌入式、客户端、游戏等场景。 Lua引擎语言特点 轻量级 源码简单,以lua最新版5.3.5为例,加上lua自身提供的lib库,仅30多个.c文件,编译后仅200多k,strip后经upx压缩,可控制在100k以下。 语法简洁 小巧灵活,支持闭包,有GC机制,通过语法糖可实现面向对...

【Lua】手游聊天系统客户端完成总结

网上很多例子,我是参考这篇文章:http://www.myexception.cn/operating-system/1591495.html 1.聊天系统难题一:消息需要支持插入表情和换行。 一开始我打算借鉴上面文章中的方法自己用label和image拼接实现自己的富文本,后来同事建议我使用cocos2dx自带的富文本空间RichText,网上找了一些例...

lua API 小记2

1. 创建lua虚拟机 lua_State *lua_newstate (lua_Alloc f, void *ud) 创建一个新的独立的lua虚拟机. 参数指定了内存分配策略及其参数, 注意, 让用户可以定制内存分配策略是十分有用的, 比如在游戏服务器端使用lua, 我做过一次统记lua在运行的时候会大量的分配大小小于128字节的内存块, 在这样的环境下...

lua工具库penlight--02表和数组

类Python的List lua的优美之处在于把数组和关联数组都用table实现了(Python中叫list和dict,C++中叫vector和map)。 一般我们把数字索引的table叫做list。penlight里的List模仿了Python,看起来从Python借鉴是个好主意。 下面是一个List的例子,List实现了__tostirng,因此可以输...

关于 Lua 内存泄漏的检测

前一阵开始和同事一起优化内存,首先是优化 Lua 内存,因为发现每次战斗完后 Lua 内存非常大,从 3M 左右在经过了10次左右的战斗后,会暴增到近 100M,很明显是有内存泄漏。 然后我正式启动该工作,基本思路就是递归遍历内存中所有的数据,表,函数,协程,用户数据,查看未释放和笔误引起的全局变量泄漏;于是通过搜索我参考了以下资料: http://sha...