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


    参考文章::
    一、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;
    }
    对装箱、拆箱不了解的可以参考文章: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还是少用,尽量封装静态方法调用,性能会更好。
  • 相关阅读:
    计算中文或全角字符串的长度
    day25 python学习 继承,钻石继承 多态
    day25 python学习 继承,钻石继承
    day24 python学习 类 画元,命名空间作用域,组合,人狗大战升级
    第四周经典问题收集
    day23 python学习 类 人狗大战
    day20 python sys os time json pickl 正则
    python 常见的内置函数
    encode decode enumerate
    3.易错点和新要掌握的内容
  • 原文地址:https://www.cnblogs.com/wang-jin-fu/p/13508789.html
Copyright © 2020-2023  润新知