cocos2d源码剖析

1. TextureAtlas

http://www.cocoachina.com/bbs/read.php?tid-311439-keyword-TextureAtlas.html

2. Label,LabelTTF,LabelBMFont等

http://www.jellythink.com/archives/766

http://www.2cto.com/kf/201409/336234.html

 

  • Problem

Currently Cocos2D supports mainly two types of "label" objects: CCLabelTTF ad CCLabelBMFont. CCLabelTTF uses true type font rendering (very accurate and nicely antialiased fonts) into a texture while CCLabelBMFont uses texture atlases based fonts rendering (the atlases must be created offline and rendering quality is poorer that TTF).
CCLabelTTF grants hight quality text rendition, but (almost) impractically slow when it comes to update the label's text (rendering TTF text into a texture is currently really slow). CCLabeBMFont on the other hand is pretty quick when if comes to rendering and update but requires the fonts to be created beforehand (offline) and also rendering quality doesn't match TTF rendering.

  • Requirements (what we want)

We would (ideally) like to improve CCLabelTTF so that updating the label's text at runtime would be (almost) as quick as when using bitmap based fonts while mantaining visual fidelity (and quality) matching the current implementation.
The API for using this "new" CCLabelTTF should not change and have minimal impact (possibly zero impact) on the already existing client (100% backward compatible).
Also, every new API element (new parameter or new method) IF needed, should be optional and (when not provided by the client code) don't alter in any way the expected API's behavior. 
We also want the "new" TTF label to have "attributes" like shadow and colors.

  • Technical road map (how we want to do it)

1) Using a mechanism similar to the one used by CCLabelBM font we could build an Atlas (for the chosen font and size) "on the fly" and use that atlas for composing the string in the label. This would make the first (and one time) label (and Atlas) creation as slow as the current implementation but would make updating the label much faster. 
This new implementation would allow for fast label updates, but could potentially use more memory (for large fonts, the texture Atlas could be pretty large) and not satisfy the rendering quality expected from a "real" TTF font (see areas of risk below).
This solution would be then an "hybrid" solution between the two current ones, allowing for fast updates at the cost of (most probably) worst rendering quality than a real TTF font.

2) We could profile (and analyze) the current implementation of CCLabelTTF and come up with an optimization plan for making it performing better on the platform.

  • Areas of risk and comments

TTF rendering is a pretty complicated (and computationally intensive) process but it generates highly accurate letters aliasing. The aliasing is somehow commanded by the spacial positioning of the letters composing the word (i.e. the string "aaa" would generate different aliasing than the string "a a a"). Generating letter atlases "on the fly" would cause part of this "pixel perfect" aliasing to be lost. This could be noticeable (and perceived as not tolerable) from certain users and could be potentially show really bad when used in combination with very small fonts.
Also, some more research on the topic is still needed on my side.

 

回去慢慢看:

https://github.com/cocos2d/cocos2d-js__old__/issues/15

 

// Trigger an assert if the reference count is 0 but the Ref is still in autorelease pool.
            // This happens when 'autorelease/release' were not used in pairs with 'new/retain'.
            //
            // Wrong usage (1):
            //
            // auto obj = Node::create();   // Ref = 1, but it's an autorelease Ref which means it was in the autorelease pool.
            // obj->autorelease();   // Wrong: If you wish to invoke autorelease several times, you should retain `obj` first.
            //
            // Wrong usage (2):
            //
            // auto obj = Node::create();
            // obj->release();   // Wrong: obj is an autorelease Ref, it will be released when clearing current pool.
            //
            // Correct usage (1):
            //
            // auto obj = Node::create();
            //                     |-   new Node();     // `new` is the pair of the `autorelease` of next line
            //                     |-   autorelease();  // The pair of `new Node`.
            //
            // obj->retain();
            // obj->autorelease();  // This `autorelease` is the pair of `retain` of previous line.
            //
            // Correct usage (2):
            //
            // auto obj = Node::create();
            // obj->retain();
            // obj->release();   // This `release` is the pair of `retain` of previous line.

 

COCOS2D 释放资源的最佳时机   

 
 
有场景A跟场景B,
场景A是当前场景,场景B是将要替换的新场景。

那么A场景的资源释放最佳时机是在什么时候呢?

这是释放资源的代码(注意要按这个顺序释放):
1
2
3
4
CCAnimationCache::purgeSharedAnimationCache();
SpriteFrameCache::getInstance()->removeUnusedSpriteFrame();
CCTextureCache::getInstance()->removeUnusedTextures();
CCTextureCache::getInstance()->getCachedTextureInfo();


1.
最开始我想到的,应该是在场景A的onExit里释放,试验了下,资源并没有被释放,说明当前纹理仍然在
被引用,即场景A的子节点并未remove。
后来查了下,发现每个场景的资源释放都是在一个叫dealloc的方法里,而这个方法是在onExit之后执行的。
也就是说在onExit里释放资源是不合适的做法。

2.
我突然想起来,每个类不都有个析构函数么?析构函数是在该类的对象被delete时调用的,
只要在A场景的析构函数里释放不就可以了?试验了下,发现也没效果,
仔细想想,我创建场景用的方法是Cocos2DX推荐的默认方法,即场景类继承一个Layer,
然后再createScene里创建一个Scene,再把当前类添加到Scene里。替换场景后,
该Layer被释放时,场景中的其他资源并不一定被释放,所以此方法也是行不通的。

3.
竟然场景真正remove子节点是在dealloc方法里,那么在dealloc里释放资源不就好?
可惜的是我根本就找不到有这个方法。于是放弃了。
后来百度了下场景的调用顺序规则:
1
2
3
4
5
6
7
replaceScene : SceneB
init : SceneB
onExitTransitionDidStart : SceneA
onExit : SceneA
dealloc : SceneA
onEnter : SceneB
onEnterTransitionDidFinish : SceneB

由此可看出,B场景的onEnter函数在A场景的dealloc之后执行。
而且
那么只要在B场景的onEnter里释放A场景的资源就可以。
试验了下,资源的确被释放了。

而且,释放资源是在B场景的init之后执行,这样的好处就是,假设场景B中用到场景A中的某些资源,
这些资源就不会被释放再加载,造成不必要的内存高峰。

4.
问题到此结束了吗?我也以为结束了,然后又发现新问题。
一旦我给场景过渡加上效果,以上方法就不灵验了,释放失败。
1
Director::getInstance()->replaceScene(CCTransitionFade::create(0.5f, SceneB::createScene()));

然后调试跟踪下执行顺序,发现问题所在了。
当有过渡效果存在的时候,执行顺序变成这样:
1
2
3
4
5
6
replaceScene : SceneB
init : SceneB
onExitTransitionDidStart : SceneA
onEnter : SceneB
onExit : SceneA
onEnterTransitionDidFinish : SceneB

没错,场景A并不会立刻结束,而是等到动画效果完毕后才结束。
但是我发现不管有没有使用过渡动画,最后执行的总是onEnterTransitionDidFinish,
那么我们只要将资源释放放在这里就行了。
经过试验,发现又失败了。。。
为啥,因为虽然dealloc方法是在onExit之后执行,但并不是紧追其后,也就是说,
有可能在onEnterTransitionDidFinish 之前,也有可能在其后,因此我们直接在
onEnterTransitionDidFinish 里释放资源并不一定可行。那该怎么解决?
这里我使用了延时释放的方法,在onEnterTransitionDidFinish 执行后过一段时间,
再调用释放资源的方法,这样就可以确保资源被释放。
1
scheduleOnce(CC_SCHEDULE_SELECTOR(SceneB::release), 2.0f);



5.
以上方法的确是可以将资源释放掉,但是不是我们所要的”最佳时机”呢?
仔细观察,B场景的init是在A场景的释放之前的,也就是说,在B场景诞生,A场景彻底释放
的短时间内,会存在A,B场景的所有资源共存的现在。这个时候内存达到巅峰。如果A场景
跟B场景的资源所占内存都非常大的时候,或许会造成崩溃。
那么我们应该如何解决?

目前我的解决方法就是在场景过渡的时候增加一个过渡场景,也就是Loading场景,Loading场景
本身所需的资源并不多,在A场景资源释放完毕后,再开启B场景的资源加载。
 
Lable性能问题:
http://www.cocoachina.com/bbs/read.php?tid-300226-keyword-Label.html
 

3.6CCLabel渲染效率问题   

今天测试项目的时候发现的问题,如果使用系统字体生成的ui::Text,调用setString方法会导致游戏卡顿。跟踪代码后确认卡顿是产生在CCLabel的getContentSize方法。继续追踪下去发现如果是使用系统字体,那么当内容发生改变时会释放掉原纹理生成新的纹理,再根据新纹理返回contentsize
我测试了30个汉字的字符串会消耗0.08秒,卡的非常明显,完全不能接受。
我尝试用ttf字体替换了系统字体,卡顿问题顺利解决了。希望引擎组能尽快优化下系统字体的渲染。
 
重要:
3.x里面Label纹理缓存的疑问  
问:问下,3.x里面Label创建字体会把字体纹理缓存起来,供下次使用,比如我用10号大小创建”标题“两字,下次我要使用12号”标题“,是用之前纹理还是重新创建继续然后继续缓存? 
答:会重新创建,然后先生成一张新的大纹理再继续缓存。字号与字体相同的字体纹理会放在一张大纹理中。10号的放在10号的纹理,12号的放在12号的纹理。这样才可以能够批次渲染!  官方的label在效率上相当的不错,改一改做成富文本也是极好的。
 

cocos2d-x V3.x内存管理分析

SpriteBatchNode和CCTextureCache

http://www.cocoachina.com/bbs/read.php?tid=196135

http://www.zaojiahua.com/

 

posted @ 2015-08-05 16:08  wiessharling  阅读(612)  评论(0编辑  收藏  举报