Cocos2d-x 3.0 新的渲染架构
?
?在单线程架构中,游戏逻辑和渲染操作是顺序执行的。两者消耗的时间加起来如果超过 1/60 秒,就会导致游戏出现卡顿现象。
?
分成多线程后,main thread 有更多的时间来执行游戏逻辑。而 draw thread 也有更多的时间做优化和渲染操作,例如实现 automatic batching。
?
?
新渲染架构可能的实现方案
Zynga 的设计文档并没有给出细节实现原理,我就以大家讨论的内容整理了一个可能的方案。
?
main thread 会每 1/60 秒的频率调用 Scene::drawScene() 方法。这个方法有如下步骤:
?
发送一个 begin command 到一个 command queue
update() 执行所有的 schedule selector 和 event listener,游戏逻辑大部分就在其中。
扫描整个 Node Tree,构造一系列 draw command,并发送到 queue
发送一个 end command 到 queue,形成一个完整的 commands list
如果某次 drawScene() 消耗的时间超过 1/60 秒,则无需等待,立即开始下一次 drawScene() 调用。这样确保游戏的逻辑能够以最高 60fps 运行,但又不会超过 60fps。
?
draw thread 按照如下步骤以最快 1/60 秒的频率执行:
?
从 queue 中找出最后一组 list(如果 draw thread 每次绘图消耗时间超过 1/60 秒,queue 中就可能包含多组 list,放弃之前的 list 可以确保画面符合最近的游戏状态)。
对 list 进行处理,执行画面渲染。
更新 queue,删除已绘制的 list 及其之前的所有 list。
Texture 对象不再保存材质的实际数据,改为保存一个 texture id。材质的实际数据由 draw thread 进行管理。
?
当加载一个材质文件时,main thread 中构造一个新的 Texture 对象,并获得一个新的 texture id。然后发送 load texture command,并将材质文件名和 texture id 作为 command 的参数传递给 draw thread。draw thread 可以在自己线程里载入材质数据,或新开线程载入数据。
?
当一个 Texture 被 Sprite 使用时,增加 Texture 对象的引用计数。而使用该 Texture 的 Sprite 删除时,则减小引用计数。如果 TextureCache 需要释放不再引用计数为 0 的 Texture 对象,就发送 release texture command。
?
在 cocos2d-x 2.x 版中,CCNode 包含了位置、大小、缩放、旋转等信息,以及 OpenGL 绘图需要的数据。
?
在新架构里,main thread 维护的 Node Tree 中,Node 不再包含 OpenGL 绘图需要的数据。而是在构造 Node、改变 Node 状态时,才将 Node 的状态数据发送给 draw thread。
?
draw thread 根据 node id 维护 node 的绘图数据。
?
如果 main thread 执行后,Node 的状态没有发生改变,就无需发送该 Node 的 draw command,也就避免了 draw thread 对没有发生变化的 Node 重新计算绘图数据。
?
?
cocos2d-x 3.0 必须要实现线程安全?
在整个设计里,main thread 不停的往 command queue 追加数据,而 draw thread 每完成一次画面渲染就清理一次 command queue。因此 queue 的实现必须是线程安全的,主要的操作都应该满足原子性要求。
?
但从这个设计看,只要保证两点,那么 main thread 里的对象是不是线程安全根本无所谓:
?
queue 是线程安全的
draw thread 不使用自动释放的引用计数对象
所以这样一番分析后,感觉 cocos2d-x 3.0 要多线程化,就不一定要做大改动。
?
但实际上 cocos2d-x 3.0 里还需要多线程完成异步 IO(例如网络)、异步计算等等。而且提供一个可靠的线程安全架构,也方便开发者创建线程来优化游戏体验(比如把 AI 算法放入单独的线程)。
?
所以从整体架构上看,cocos2d-x 3.0 实现线程安全还是很有必要的。但实现线程安全是否就一定要用智能指针吗?这个我会另外写一篇文章来探讨此问题。
?
最后,欢迎大家多多提意见。
?
- END -