為了解決系統(tǒng)字經(jīng)常打斷合批的問題耘柱,cocos提供了三種緩存模式:官網(wǎng)文檔
CacheMode.NONE: 先用網(wǎng)頁canvas渲染文字,然后用Texture2D.initWithElement(canvas)獲取紋理
CacheMode.BITMAP: 跟CacheMode.NONE差不多斜做,區(qū)別就是會(huì)將實(shí)時(shí)獲取到的紋理放到動(dòng)態(tài)合集里,于是就能跟附近的同類型Label或者使用動(dòng)態(tài)圖集的Sprite進(jìn)行合批略水。
CacheMode.CHAR:會(huì)先將label拆成一個(gè)個(gè)單獨(dú)的字徽诲,每一個(gè)字都用以上的方法獲取生成的紋理,放在一個(gè)專門存放此類字符紋理的大圖內(nèi)牡属,然后渲染的時(shí)候使用多個(gè)頂點(diǎn)票堵,按每個(gè)字符一個(gè)矩形渲染。
我們先來看下幾種緩存模式下運(yùn)行時(shí)的區(qū)別
如上圖逮栅,None模式下悴势,每個(gè)label都占一個(gè)drawcall,即使內(nèi)容相同也不例外措伐,此時(shí)內(nèi)存中三個(gè)label紋理+一個(gè)sprite紋理+FPS label紋理特纤。
Bitmap模式下,三個(gè)Label和Sprite共占一個(gè)drawcall侥加,是因?yàn)槿龔坙abel的紋理和sprite的紋理打到了一個(gè)動(dòng)態(tài)圖集里面捧存。我們可以打印出動(dòng)態(tài)圖集:
let _debugNode = cc.dynamicAtlasManager.showDebug(true);
_debugNode.getComponent(cc.ScrollView).content.children[0].color = cc.Color.RED
_debugNode.scale = 0.5;
紅色的就是動(dòng)態(tài)圖集,可以注意到即使是一模一樣的Label,在自動(dòng)圖集里也不會(huì)共用貼圖昔穴,有多少個(gè)BITMAP緩存模式的Label镰官,就有多少份字體貼圖,所以如果我們的游戲里有大量相同的label吗货,更加適合用CHAR緩存模式泳唠。
再看CHAR模式,CHAR模式下相當(dāng)于是全局共用一個(gè)2048x2048的位圖字體:cc.Label._shareAtlas宙搬,
我們打印一下該字體的紋理:
let shareLabelTex = cc.Label._shareAtlas.getTexture()
let spr = _debugNode.getComponent(cc.ScrollView).content.children[0].getComponent(cc.Sprite)
spr.spriteFrame.setTexture(shareLabelTex)
從上圖順便可以看出警检,系統(tǒng)FPS信息Label就是用的CHAR模式。
下面我們跟著Label源碼來看看害淤,幾種模式分別是怎么裝配渲染數(shù)據(jù)的
我們今天討論的范圍是非native環(huán)境下2d系統(tǒng)字扇雕。
從這段代碼可知,CHAR模式下對(duì)應(yīng)的Assembler是Letter窥摄,NONE模式和BITMAP模式的Assembler都是TTF镶奉。Label組件就是通過這些Assembler將字體數(shù)據(jù)變成紋理數(shù)據(jù)進(jìn)而顯示的,先看看他們是怎么關(guān)聯(lián)的:
每次系統(tǒng)字相關(guān)設(shè)置發(fā)生變化崭放,組件都會(huì)調(diào)用_applyFontTexture重新初始化紋理數(shù)據(jù)哨苛,非CHAR模式下,是直接創(chuàng)建一個(gè)新的Texture2D紋理币砂,通過canvas初始化(紅框部分)建峭。
而每次諸如Label.string發(fā)生改變后,會(huì)更新渲染數(shù)據(jù)决摧,讓canvas重新繪制字體亿蒸,從而使_ttfTexture得到刷新:
NONE模式和BITMAP模式的Assembler都是TTF边锁,唯一的區(qū)別就是BITMAP模式下,會(huì)在生成完紋理后波岛,將該紋理拷貝一份到自動(dòng)圖集茅坛,然后使用自動(dòng)圖集里面的內(nèi)容。所以BITMAP的優(yōu)勢(shì)是能與周圍使用自動(dòng)圖集的碎圖一起合批则拷,但同時(shí)也會(huì)加大自動(dòng)圖集的消耗贡蓖。內(nèi)容或字體大小等頻繁變化的不要使用BITMAP模式,因?yàn)槊克⑿乱淮位筒纾蜁?huì)拷貝一次最新紋理到自動(dòng)圖集斥铺。
關(guān)于CHAR模式,就有點(diǎn)繞了宣旱,生成紋理的方式還是一樣仅父,但不是 每個(gè)Label生成一個(gè)紋理,而是每個(gè)先把Label字符串分開浑吟,按每個(gè)字符去生成紋理笙纤,然后嘗試拷貝紋理到共用的LetterAtlas大圖,然后下次再遇到相同參數(shù)的字符组力,就直接取大圖紋理省容。
上圖中,紅色的就是CHAR LABEL的公共紋理燎字,可以看到腥椒,字體大小不同時(shí),即使內(nèi)容相同候衍,也會(huì)使用不同的紋理內(nèi)容笼蛛。
所謂相同參數(shù),就是上圖的shareLabelInfo的color蛉鹿、fontSize滨砍、fontFamily、outline妖异、margin惋戏,然后再與字符內(nèi)容相加,得到的hash值他膳,然后維護(hù)一個(gè)_letterDefinitions表响逢,每增加一個(gè)新的letterTexture,就拷貝一份到公共大紋理棕孙,然后保存紋理坐標(biāo)舔亭、寬、高等信息蟀俊,hash為鍵值分歇。
其實(shí)跟向動(dòng)態(tài)圖集插入紋理差不多,只不過系統(tǒng)字剛好有一些可以比較衡量的參數(shù)欧漱,所以比動(dòng)態(tài)圖集更方便的是可以判斷某個(gè)小的字符紋理有沒有被加到大圖中职抡,有的話就直接取。
然后再一點(diǎn)就是CHAR模式下误甚,頂點(diǎn)數(shù)據(jù)有所不同缚甩,普通的Sprite或者Label,都是一個(gè)矩形窑邦、4個(gè)頂點(diǎn)擅威、兩個(gè)三角形,CHAR模式下冈钦,每個(gè)字符所用的紋理是不連續(xù)的郊丛,可能在大圖中的任意位置,所以必須每個(gè)字符使用一套紋理坐標(biāo),有n個(gè)字符厉熟,就有n x 4個(gè)頂點(diǎn)导盅。