为 cocos2d-x 选择新的 Luabinding (1)

前端之家收集整理的这篇文章主要介绍了为 cocos2d-x 选择新的 Luabinding (1)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

最近正在做 Cocos Creator 的 Lua 支持。因为 Creator 使用了一个精简的 cocos2d-x 引擎,所以我也基于这个引擎来做 Cocos Creator 的 Lua 支持

cocos2d-x 目前使用一套基于 tolua++ 的 Luabinding 层。而 tolua++ 这货已经停止维护快十年了,所以换掉 tolua++ 势在必行。另外我一直认为 tolua++ 存在性能问题,替换为更高效的 luabinding 可以充分发挥 Lua 的性能优势。

这篇文章的重点就是选择新的 Luabinding。


性能测试

既然以提高性能为目的,必须用数据来说话。所以我做了一个性能测试工程,放在 github 的 cocos2dx_benchmark 仓库里。

在 iPhone 6 上,这个 Lua 测试例可以在保证 55fps+ 帧率的基础上跑 4500 个星星。

为了看到 Lua 和 C++ 的性能差距到底有多大,我又在 quick2d-engine 仓库中做了一个新的测试例。

C++ 版的测试结果是可以跑到 12000 个星星。可以看出 cocos2d-x Lua 的综合性能只有 C++ 的 37.5%


选择新 Luabinding

找了好些 Luabinding 库,根据这个性能测试选择了 Sol2 。但经过一天多的试验,这个库缺少一个及其重要的特性 concatenate the metatables together。而且作者刚刚发布 2.5 版后就说他不玩了。。因为太累 -_-#

好吧,我换成第二快的 kaguya

这个库采用大量 C++11 的新特性,采用模板的形式来导出 C++ 接口,代码如下:

  1. auto cc = _lua["cc"] = _lua.newTable();
  2.  
  3. cc["Ref"].setClass(kaguya::ClassMetatable<Ref>()
  4. .addMemberFunction("retain",&Ref::retain)
  5. .addMemberFunction("release",&Ref::release)
  6. .addMemberFunction("getReferenceCount",&Ref::getReferenceCount));
  7.  
  8. cc["Scheduler"].setClass(kaguya::ClassMetatable<Scheduler,Ref>());
  9.  
  10. cc["Director"].setClass(kaguya::ClassMetatable<Director,Ref>()
  11. .addStaticFunction("getInstance",&Director::getInstance)
  12. .addMemberFunction("endDirector",&Director::end)
  13. .addMemberFunction("getScheduler",&Director::getScheduler));
  14.  
  15. cc["Node"].setClass(kaguya::ClassMetatable<Node,Ref>()
  16. .addStaticFunction("create",&Node::create)
  17. .addMemberFunction("addChild",static_cast<void(Node::*)(Node*)>(&Node::addChild))
  18. .addMemberFunction("addChild",static_cast<void(Node::*)(Node*,int)>(&Node::addChild))
  19. .addMemberFunction("addChild",int,const std::string&)>(&Node::addChild))
  20. .addMemberFunction("removeChild",&Node::removeChild)
  21. .addMemberFunction("setPosition",static_cast<void(Node::*)(const Vec2&)>(&Node::setPosition))
  22. .addMemberFunction("setPosition",static_cast<void(Node::*)(float,float)>(&Node::setPosition))
  23. .addMemberFunction("setColor",&Node::setColor)
  24. .addMemberFunction("setOpacity",&Node::setOpacity)
  25. .addMemberFunction("schedule",static_cast<void(Node::*)(const std::function<void(float)>&,float,const std::string &)>(&Node::schedule)));

代码看上去是相当整洁的。

但经过性能测试,却发现在超过 3000 个星星后,性能会出现暴跌,而不是线性降低。

又反复测试了一下,估计问题应该出在 Lua call C++ member function 这里。所以又写了一段代码

  1. static int lgetNode(lua_State *L)
  2. {
  3. // node
  4. kaguya::ObjectPointerWrapper<Sprite*> *wrap = static_cast<kaguya::ObjectPointerWrapper<Sprite*>*>(lua_touserdata(L,-1));
  5. Node *node = static_cast<Node*>(wrap->get());
  6. lua_pushlightuserdata(L,node);
  7. return 1;
  8. }
  9.  
  10. static int lsetPosition(lua_State *L)
  11. {
  12. // node,x,y
  13. Node *node = static_cast<Node*>(lua_touserdata(L,-3));
  14. node->setPosition(lua_tonumber(L,-2),lua_tonumber(L,-1));
  15. return 0;
  16. }
  17.  
  18. static int lsetOpacity(lua_State *L)
  19. {
  20. // node,opacity
  21. Node *node = static_cast<Node*>(lua_touserdata(L,-2));
  22. node->setOpacity(lua_tointeger(L,-1));
  23. return 0;
  24. }
  25.  
  26. lua_State *L = _lua.state();
  27.  
  28. lua_pushcfunction(L,&lgetNode);
  29. lua_setglobal(L,"lgetNode");
  30.  
  31. lua_pushcfunction(L,&lsetPosition);
  32. lua_setglobal(L,"lsetPosition");
  33.  
  34. lua_pushcfunction(L,&lsetOpacity);
  35. lua_setglobal(L,"lsetOpacity");

在 Lua 里,就不再使用对象方法调用了,而是改成这三个全局函数。测试结果暴涨到 8200 个星星,接近 C++ 70% 的性能了。


总结

cocos2d-x 现在使用的 Luabinding 只能达到 C++ 37.5% 的性能。在更换新 Luabinding 并做相应调整后,可以获得高达 C++ 70% 的性能表现。而这损耗的 30%,其中还有大量的数值运算。要知道拼计算,脚本是肯定比不过 C++ 的。

虽然新的 Luabinding 方案还有很多工作要做,但我有信心最终搞一个比现在快一倍的 Luabinding 出来。

猜你在找的Cocos2d-x相关文章