Xlua對c#的vector3等結構體的優化
阿新 • • 發佈:2020-08-15
目錄:Xlua原始碼學習
參考文章::
一、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) {xlua.c裡的介面: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); } }
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 x\y\z引數,在lua需要建立表儲存x\y\z值,需要查詢元表等。 那麼如何優化呢?直接在函式中傳遞三個float,要比傳遞Vector3要更快。 例如void SetPos(GameObject obj, Vector3pos)改為void SetPos(GameObject obj, float x, floaty, float z)。 對於c#的Struct還是少用,儘量封裝靜態方法呼叫,效能會更好。