xlua源码分析(五) struct类型优化

这篇具有很好参考价值的文章主要介绍了xlua源码分析(五) struct类型优化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

上一节我们分析了xlua是如何实现lua层访问C#值类型的,其中我们重点提到了xlua默认实现方式下,struct访问的效率问题。实际上,xlua还提供了两种优化的方式,可以大大提高struct访问的性能。具体例子在Examples 12_ReImplementInLua中。

第一种优化方式就是在lua层改造C#的struct,C# struct push到lua层时仍为userdata,但它的metatable不指向C#层struct,而是lua层自己实现的:

function test_vector3(title, v1, v2)
    print(title)
    v1.x = 100
    print(v1.x, v1.y, v1.z)
    print(v1, v2)
    print(v1 + v2)
    v1:Set(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z)
    print(v1)
    print(CS.UnityEngine.Vector3.Normalize(v1))
end

local get_x, set_x = xlua.genaccessor(0, 8)
local get_y, set_y = xlua.genaccessor(4, 8)
local get_z, set_z = xlua.genaccessor(8, 8)

local fields_getters = {
    x = get_x, y = get_y, z = get_z
}
local fields_setters = {
    x = set_x, y = set_y, z = set_z
}

local ins_methods = {
    Set = function(o, x, y, z)
        set_x(o, x)
        set_y(o, y)
        set_z(o, z)
    end
}

local mt = {
    __index = function(o, k)
        --print('__index', k)
        if ins_methods[k] then return ins_methods[k] end
        return fields_getters[k] and fields_getters[k](o)
    end,

    __newindex = function(o, k, v)
        if fields_setters[k] then fields_setters[k](o, v) else error('no such field ' .. k) end
    end,

    __tostring = function(o)
        return string.format('vector3 { %f, %f, %f}', o.x, o.y, o.z)
    end,

    __add = function(a, b)
        return CS.UnityEngine.Vector3(a.x + b.x, a.y + b.y, a.z + b.z)
    end
}

xlua.setmetatable(CS.UnityEngine.Vector3, mt)
test_vector3('----after change metatable----', CS.UnityEngine.Vector3(1, 2, 3), CS.UnityEngine.Vector3(7, 8, 9))

这里的代码,就是在lua层实现了一下Vector3的get/set属性和方法,然后替换掉原先的metatable,xlua.setmetatable就是做这个工作的,替换的逻辑很简单,就是找到要替换类的type id,重新设置到registry表里:

public static int XLuaMetatableOperation(RealStatePtr L)
{
    try
    {
        ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
        Type type = getType(L, translator, 1);
        if (type == null)
        {
            return LuaAPI.luaL_error(L, "xlua.metatable_operation, can not find c# type");
        }

        bool is_first = false;
        int type_id = translator.getTypeId(L, type, out is_first);

        var param_num = LuaAPI.lua_gettop(L);

        if (param_num == 1) //get
        {
            LuaAPI.xlua_rawgeti(L, LuaIndexes.LUA_REGISTRYINDEX, type_id);
            return 1;
        }
        else if (param_num == 2) //set
        {
            if (LuaAPI.lua_type(L, 2) != LuaTypes.LUA_TTABLE)
            {
                return LuaAPI.luaL_error(L, "argument #2 must be a table");
            }
            LuaAPI.lua_pushnumber(L, type_id);
            LuaAPI.xlua_rawseti(L, 2, 1);
            LuaAPI.xlua_rawseti(L, LuaIndexes.LUA_REGISTRYINDEX, type_id);
            return 0;
        }
        else
        {
            return LuaAPI.luaL_error(L, "invalid argument num for xlua.metatable_operation: " + param_num);
        }
    }
    catch (Exception e)
    {
        return LuaAPI.luaL_error(L, "c# exception in xlua.metatable_operation: " + e);
    }
}

不过,lua层的Vector3依旧是userdata,如何在lua层对userdata设置/获取数据呢?为此,xlua提供了xlua.genaccessor函数,它接受两个参数,第一个参数表示要设置/获取的字段相对于struct的内存偏移,第二个参数表示要设置/获取的字段类型,对于Vector3,x,y,z的偏移分别为0,4,8,而它们的类型均为float,float在xlua预先定义的类型ID为8:

#define T_INT8   0
#define T_UINT8  1
#define T_INT16  2
#define T_UINT16 3
#define T_INT32  4
#define T_UINT32 5
#define T_INT64  6
#define T_UINT64 7
#define T_FLOAT  8
#define T_DOUBLE 9

genaccessor函数是在C层实现的,那其实很简单了,就是把userdata作为要访问内存的首地址,加上偏移量offset,执行memcpy即可,如果是get,就是从userdata拷贝到value,再push到lua栈;如果是set,就先从lua栈上取出value,再拷贝到userdata。

#define DIRECT_ACCESS(type, push_func, to_func) \
int xlua_struct_get_##type(lua_State *L) {\
	CSharpStruct *css = (CSharpStruct *)lua_touserdata(L, 1);\
	int offset = xlua_tointeger(L, lua_upvalueindex(1));\
	type val;\
	if (css == NULL || css->fake_id != -1 || css->len < offset + sizeof(type)) {\
		return luaL_error(L, "invalid c# struct!");\
	} else {\
		memcpy(&val, (&(css->data[0]) + offset), sizeof(type));\
		push_func(L, val);\
		return 1;\
	}\
}\
\
int xlua_struct_set_##type(lua_State *L) { \
	CSharpStruct *css = (CSharpStruct *)lua_touserdata(L, 1);\
	int offset = xlua_tointeger(L, lua_upvalueindex(1));\
	type val;\
	if (css == NULL || css->fake_id != -1 || css->len < offset + sizeof(type)) {\
		return luaL_error(L, "invalid c# struct!");\
	} else {\
	    val = (type)to_func(L, 2);\
		memcpy((&(css->data[0]) + offset), &val, sizeof(type));\
		return 0;\
	}\
}\

上面例子的运行结果如下:

xlua源码分析(五) struct类型优化,xlua源码分析,lua,c#,xlua

第二种优化方式,是将struct映射成table,即C#层push到lua层的struct,不再为userdata,而是一个table,xlua提供了PackAsTable这个attribute指示生成代码时采用映射table的方式:

[GCOptimize(OptimizeFlag.PackAsTable)]
public struct PushAsTableStruct
{
    public int x;
    public int y;
}

然后,lua层也需要实现配套的代码,即struct的object metatable和class metatable,相当于在lua层实现struct:

local mt = {
    __index = {
        SwapXY = function(o) --成员函数
            o.x, o.y = o.y, o.x
        end
    },

    __tostring = function(o) --打印格式化函数
        return string.format('struct { %d, %d}', o.x, o.y)
    end,
}

xlua.setmetatable(CS.XLuaTest.PushAsTableStruct, mt)

local PushAsTableStruct = {
    Print = function(o) --静态函数
        print(o.x, o.y)
    end
}

setmetatable(PushAsTableStruct, {
    __call = function(_, x, y) --构造函数
        return setmetatable({x = x, y = y}, mt)
    end
})

xlua.setclass(CS.XLuaTest, 'PushAsTableStruct', PushAsTableStruct)

在测试代码中,我们先在C#层push一下struct:

PushAsTableStruct test;
test.x = 100;
test.y = 200;
luaenv.Global.Set("from_cs", test);

然后再在lua层进行测试:

print('--------------from csharp---------------------')
assert(type(from_cs) == 'table')
print(from_cs)
CS.XLuaTest.PushAsTableStruct.Print(from_cs)
from_cs:SwapXY()
print(from_cs)

print('--------------from lua---------------------')
local from_lua = CS.XLuaTest.PushAsTableStruct(4, 5)
assert(type(from_lua) == 'table')
print(from_lua)
CS.XLuaTest.PushAsTableStruct.Print(from_lua)
from_lua:SwapXY()
print(from_lua)

此时C#层push时,不会再生成userdata,而是生成一个table,然后设置字段x和字段y:

public void PushXLuaTestPushAsTableStruct(RealStatePtr L, XLuaTest.PushAsTableStruct val)
{
    if (XLuaTestPushAsTableStruct_TypeID == -1)
    {
        bool is_first;
        XLuaTestPushAsTableStruct_TypeID = getTypeId(L, typeof(XLuaTest.PushAsTableStruct), out is_first);
        
    }
    
    
    LuaAPI.xlua_pushcstable(L, 2, XLuaTestPushAsTableStruct_TypeID);
    
    LuaAPI.xlua_pushasciistring(L, "x");
    LuaAPI.xlua_pushinteger(L, val.x);
    LuaAPI.lua_rawset(L, -3);
    
    LuaAPI.xlua_pushasciistring(L, "y");
    LuaAPI.xlua_pushinteger(L, val.y);
    LuaAPI.lua_rawset(L, -3);
    
    
}

同样的道理,要从lua层把struct传递到C#层,就要获取lua层的table,把它的字段x和字段y取出,依次赋值到C#对象上:

public static void UnPack(ObjectTranslator translator, RealStatePtr L, int idx, out XLuaTest.PushAsTableStruct val)
{
    val = new XLuaTest.PushAsTableStruct();
    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);
    
}

例子的输出结果如下:

xlua源码分析(五) struct类型优化,xlua源码分析,lua,c#,xlua

这两种优化方式,各有优劣,第一种方式,userdata比table更加省内存;而第二种方式,使用原始table操作性能上要比使用userdata要好。两种方式都需要额外生成一些代码。与tolua相比,tolua的struct是采用了类似第二种的方式,tolua的struct在lua层就是个table,需要完整按照C#层实现一遍struct。而数据传输的逻辑,稍微不太相同,tolua是使用lua函数进行数据传输,例如Vector3,tolua可以通过一个get函数直接返回3个float*给C#层,也可以通过一个new函数直接使用x,y,z三个参数构造出一个lua层的struct,pack和unpack的逻辑都放在了lua层里。文章来源地址https://www.toymoban.com/news/detail-800235.html

function Vector3.New(x, y, z)				
	local t = {x = x or 0, y = y or 0, z = z or 0}
	setmetatable(t, Vector3)						
	return t
end

function Vector3.Get(v)		
	return v.x, v.y, v.z	
end

到了这里,关于xlua源码分析(五) struct类型优化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Unity框架】XLua中Lua代码注入C#代码操作

    1.游戏框架下载地址:https://github.com/kof123w/gitWorkSpace/tree/main/XLua 2.XLua官方与教程地址:https://github.com/Tencent/xLua I.宏定义:添加 HOTFIX_ENABLE 到 Edit Project Settings Player Other Settings Scripting Define Symbols II.生成代码:执行 ‘XLua Generate Code’ 菜单,等待Unity编译完成 III.注入:执行 ‘XLua

    2024年02月08日
    浏览(59)
  • 使用XLua在Unity中获取lua全局变量和函数

    1、Lua脚本  入口脚本 测试脚本 2、C#脚本 (1)获取全局变量 执行结果 (2)获取全局函数

    2024年02月07日
    浏览(43)
  • 关于Unity在Xlua调用Lua脚本函数时报错This type must add to CSharpCallLua 解决办法

    使用委托来获取xlua中的function是不行的 报错脚本示范 即使全部接口打好标签并且在编辑器中把兼容等级改为4.X 打包出去还是会出问题 建议在lua脚本中建立一个空的table 再把方法塞进去 比如 然后在c#端 就可以正常运作这个方法了

    2024年02月12日
    浏览(61)
  • Golang struct 结构体指针类型 / 结构体值类型

    结构体变量之间的赋值是值拷贝。 当需要通过变量去修改另外一个结构体内容的时候,可以通过传递地址的方式来达到这个效果。 这样使用肯定是不对的,因为.的运算优先级要高,所以先会去执行p2.age,之后又取地址,所以这个就不行。 在代码执行的时候先执行.的操作,

    2024年02月13日
    浏览(42)
  • Rust 数据类型 之 结构体(Struct)

    目录 结构体(Struct) 定义与声明 结构体定义 结构体实例 结构体分类 单元结构体(Unit Struct) 元组结构体(Tuple Struct) 具名结构体(Named Struct) 结构体嵌套 结构体方法 例1:结构体转换为字符串描述 例2:矩形的周长和面积 例3:结构体字段的更新与输出 关联函数 结构体

    2024年02月16日
    浏览(48)
  • 第117篇 remix 中 struct 类型传参

    remix 中,结构体显示为 tuple ,使用\\\'[]\\\'标识一个对象; 合约示例: 在 remix 部署合约,并调用:

    2024年02月13日
    浏览(34)
  • Hive中的复杂数据类型 - array、map、struct

    水善利万物而不争,处众人之所恶,故几于道💦 一、简单数据类型(复习) 官方数据类型详情页 数据类型 描述 范围 tinyint 1byte有符号整数 from -128 to 127 smallint 2byte有符号整数 from -32,768 to 32,767 int 4byte有符号整数 from -2,147,483,648 to 2,147,483,647 bigint 8byte有符号整数 from -9,223,37

    2024年03月12日
    浏览(53)
  • Linux系统struct input_event结构体分类型(鼠标、键盘、触屏)详解与例子

    目录 一、概述 二、结构体字段解析 三、不同类型地解释字段  3.1 鼠标事件  3.2 键盘事件  3.3 触摸屏事件 四、使用 struct input_event 读取设备文件的例子 Linux系统是通过 输入子系统 来管理输入设备(如鼠标、键盘、触摸屏、游戏摇杆)的。配置了内核支持且安装对应驱动

    2024年02月16日
    浏览(42)
  • Postgresql源码(108)不同类型insert在parse阶段的差异分析

    分析三种类型的insert在parse的各个阶段的差异: 不同insert的计划树type 下面三种SQL在语义分析结果来看有什么区别? 语义分析结果来看,insert语句都会构造插入表和数据表两张表(RangeTblEntry),数据表可能是值构造出来的,或者是select查询出来的。 核心流程都是构造数据表

    2024年02月11日
    浏览(36)
  • Swift之struct二进制大小分析

    作者:京东零售 邓立兵 随着Swift的日渐成熟和给开发过程带来的便利性及安全性,京喜App中的原生业务模块和基础模块使用Swift开发占比逐渐增高。本次讨论的是struct对比Class的一些优劣势,重点分析对包体积带来的影响及规避措施。 1、类型对比 引用类型: 将一个对象赋值

    2024年01月20日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包