mirror of
https://github.com/smallmain/cocos-enhance-kit.git
synced 2024-12-25 19:28:28 +00:00
[engine] 新 Char 缓存模式实现
This commit is contained in:
parent
bfbb3511eb
commit
adb32f5c23
@ -95,7 +95,7 @@ var builtins = {
|
||||
effect.addRef();
|
||||
cc.sp.inited = true;
|
||||
cc.sp.multiBatcher.init();
|
||||
if (cc.dynamicAtlasManager.maxAtlasCount === -1) cc.dynamicAtlasManager.maxAtlasCount = cc.sp.MAX_MULTITEXTURE_NUM;
|
||||
if (cc.dynamicAtlasManager.maxAtlasCount === -1) cc.dynamicAtlasManager.maxAtlasCount = Math.max(0, cc.sp.MAX_MULTITEXTURE_NUM - cc.sp.charAtlasAutoBatchCount);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
|
@ -483,6 +483,10 @@ let Label = cc.Class({
|
||||
|
||||
if (oldValue === CacheMode.CHAR) {
|
||||
this._ttfTexture = null;
|
||||
const material = this._materials[0];
|
||||
if (material && material.material && cc.Label._shareAtlas.material === material.material) {
|
||||
this.setMaterial(0, this._getDefaultMaterial());
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.enabledInHierarchy) return;
|
||||
|
@ -339,6 +339,8 @@ export default class BmfontAssembler extends Assembler2D {
|
||||
index += tokenLen;
|
||||
} //end of for loop
|
||||
|
||||
this._finishMultilineTextWrap();
|
||||
|
||||
_linesWidth.push(letterRight);
|
||||
|
||||
_numberOfLines = lineIndex + 1;
|
||||
@ -638,7 +640,7 @@ export default class BmfontAssembler extends Assembler2D {
|
||||
if (_tmpRect.height > 0 && _tmpRect.width > 0) {
|
||||
let isRotated = this._determineRect(_tmpRect);
|
||||
let letterPositionX = letterInfo.x + _linesOffsetX[letterInfo.line];
|
||||
this.appendQuad(_comp, texture, _tmpRect, isRotated, letterPositionX - appx, py - appy, _bmfontScale);
|
||||
this.appendQuad(_comp, texture, _tmpRect, isRotated, letterPositionX - appx, py - appy, _bmfontScale, letterDef);
|
||||
}
|
||||
}
|
||||
this._quadsUpdated(_comp);
|
||||
@ -727,8 +729,9 @@ export default class BmfontAssembler extends Assembler2D {
|
||||
|
||||
updateWorldVerts() {}
|
||||
|
||||
appendQuad (comp, texture, rect, rotated, x, y, scale) {}
|
||||
appendQuad(comp, texture, rect, rotated, x, y, scale, letter) {}
|
||||
_quadsUpdated (comp) {}
|
||||
|
||||
_reserveQuads () {}
|
||||
_finishMultilineTextWrap() { }
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
****************************************************************************/
|
||||
|
||||
import WebglBmfontAssembler from '../../webgl/assemblers/label/2d/bmfont';
|
||||
import { vfmtPosUvColorTexId } from '../../webgl/vertex-format';
|
||||
|
||||
const Label = require('../../../components/CCLabel');
|
||||
const LabelOutline = require('../../../components/CCLabelOutline');
|
||||
@ -118,36 +119,90 @@ LetterTexture.prototype = {
|
||||
this._texture.handleLoadedTexture();
|
||||
},
|
||||
|
||||
destroy () {
|
||||
destroy() {
|
||||
this._texture._packable = false;
|
||||
this._texture.destroy();
|
||||
this._texture = null;
|
||||
Label._canvasPool.put(this._data);
|
||||
},
|
||||
}
|
||||
|
||||
function LetterAtlas (width, height) {
|
||||
function LetterAtlas(atlases, width, height) {
|
||||
let texture = new RenderTexture();
|
||||
texture.initWithSize(width, height);
|
||||
texture.update();
|
||||
|
||||
this._fontDefDictionary = new FontAtlas(texture);
|
||||
this._atlases = atlases;
|
||||
this._texture = texture;
|
||||
this._id = 0;
|
||||
this._tmpId = -1;
|
||||
|
||||
this._x = space;
|
||||
this._y = space;
|
||||
this._nexty = space;
|
||||
|
||||
this.frees = [];
|
||||
this.waitCleans = [];
|
||||
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
|
||||
cc.director.on(cc.Director.EVENT_BEFORE_SCENE_LAUNCH, this.beforeSceneLoad, this);
|
||||
}
|
||||
|
||||
cc.js.mixin(LetterAtlas.prototype, {
|
||||
insertLetterTexture (letterTexture) {
|
||||
insertLetterTexture(letterTexture) {
|
||||
let texture = letterTexture._texture;
|
||||
let width = texture.width, height = texture.height;
|
||||
|
||||
// 先寻找是否有可用的被回收的区域
|
||||
if (this.frees.length > 0) {
|
||||
let score = Number.MAX_VALUE;
|
||||
let areaFit = 0;
|
||||
let original = null;
|
||||
let originalIndex = 0;
|
||||
|
||||
for (let i = 0; i < this.frees.length; i++) {
|
||||
const freeLetter = this.frees[i];
|
||||
if (freeLetter._width === width && freeLetter._height === height) {
|
||||
areaFit = freeLetter._width * freeLetter._height - width * height;
|
||||
if (areaFit < score) {
|
||||
original = freeLetter;
|
||||
originalIndex = i;
|
||||
score = areaFit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (original) {
|
||||
original._hash = letterTexture._hash;
|
||||
original.w = letterTexture._width - bleed;
|
||||
original.h = letterTexture._height - bleed;
|
||||
original.xAdvance = original.w;
|
||||
original.offsetY = letterTexture._offsetY;
|
||||
|
||||
this._texture.drawTextureAt(texture, original.u - bleed / 2, original.v - bleed / 2);
|
||||
|
||||
this._dirty = true;
|
||||
|
||||
this.removeFreeLetter(originalIndex);
|
||||
|
||||
this._atlases._fontDefDictionary.addLetterDefinitions(letterTexture._hash, original);
|
||||
return original;
|
||||
}
|
||||
}
|
||||
|
||||
// 有 bleed 问题,暂时不能复用不同高宽的空间
|
||||
// 矫正宽度为三档: <0.75x height <1x height >1x height
|
||||
// if (width <= height * 0.75) {
|
||||
// width = height * 0.75;
|
||||
// } else if (width <= height) {
|
||||
// width = height;
|
||||
// }
|
||||
|
||||
// 没有可用的被回收区域,尝试直接插入
|
||||
const oldx = this._x, oldy = this._y, oldnexty = this._nexty;
|
||||
|
||||
if ((this._x + width + space) > this._width) {
|
||||
// TODO 跳到下一行之前将这行的剩余区域切成多个正方形并放入 frees,避免浪费
|
||||
this._x = space;
|
||||
this._y = this._nexty;
|
||||
}
|
||||
@ -157,33 +212,69 @@ cc.js.mixin(LetterAtlas.prototype, {
|
||||
}
|
||||
|
||||
if (this._nexty > this._height) {
|
||||
return null;
|
||||
this._x = oldx;
|
||||
this._y = oldy;
|
||||
this._nexty = oldnexty;
|
||||
|
||||
// 回收 waitCleans
|
||||
if (this.waitCleans.length > 0) {
|
||||
for (const letter of this.waitCleans) {
|
||||
letter._inCleans = false;
|
||||
if (letter.ref === 0) {
|
||||
delete this._atlases._fontDefDictionary._letterDefinitions[letter._hash];
|
||||
this.frees.push(letter);
|
||||
}
|
||||
}
|
||||
this.waitCleans.length = 0;
|
||||
return this.insertLetterTexture(letterTexture);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
this._fontDefDictionary._texture.drawTextureAt(texture, this._x, this._y);
|
||||
this._texture.drawTextureAt(texture, this._x, this._y);
|
||||
|
||||
this._dirty = true;
|
||||
|
||||
let letter = new FontLetterDefinition();
|
||||
letter.u = this._x + bleed/2;
|
||||
letter.v = this._y + bleed/2;
|
||||
letter.texture = this._fontDefDictionary._texture;
|
||||
letter.u = this._x + bleed / 2;
|
||||
letter.v = this._y + bleed / 2;
|
||||
letter.texture = this._texture;
|
||||
letter.atlas = this;
|
||||
letter.ref = 0;
|
||||
letter.valid = true;
|
||||
letter.w = letterTexture._width - bleed;
|
||||
letter.h = letterTexture._height - bleed;
|
||||
letter._inCleans = false;
|
||||
letter._hash = letterTexture._hash;
|
||||
letter._width = width;
|
||||
letter._height = height;
|
||||
letter.xAdvance = letter.w;
|
||||
letter.offsetY = letterTexture._offsetY;
|
||||
|
||||
this._x += width + space;
|
||||
|
||||
this._fontDefDictionary.addLetterDefinitions(letterTexture._hash, letter);
|
||||
this._atlases._fontDefDictionary.addLetterDefinitions(letterTexture._hash, letter);
|
||||
|
||||
return letter
|
||||
},
|
||||
|
||||
pushFreeLetter(letter) {
|
||||
const i = this.frees.push(letter) - 1;
|
||||
},
|
||||
|
||||
removeFreeLetter(index) {
|
||||
const temp = this.frees[index];
|
||||
const temp2 = this.frees[this.frees.length - 1];
|
||||
// temp2.cacheIndex = index;
|
||||
// temp.cacheIndex = -1;
|
||||
this.frees[index] = temp2;
|
||||
this.frees.pop();
|
||||
},
|
||||
|
||||
update () {
|
||||
if (!this._dirty) return;
|
||||
this._fontDefDictionary._texture.update();
|
||||
this._texture.update();
|
||||
this._dirty = false;
|
||||
},
|
||||
|
||||
@ -192,47 +283,148 @@ cc.js.mixin(LetterAtlas.prototype, {
|
||||
this._y = space;
|
||||
this._nexty = space;
|
||||
|
||||
let chars = this._fontDefDictionary._letterDefinitions;
|
||||
for (let i = 0, l = chars.length; i < l; i++) {
|
||||
let char = chars[i];
|
||||
if (!char.isValid) {
|
||||
continue;
|
||||
const defs = this._atlases._fontDefDictionary._letterDefinitions;
|
||||
for (const key in defs) {
|
||||
const def = defs[key];
|
||||
if (def.atlas === this) {
|
||||
delete defs[key];
|
||||
}
|
||||
char.destroy();
|
||||
}
|
||||
|
||||
this._fontDefDictionary.clear();
|
||||
this.frees.length = 0;
|
||||
this.waitCleans.length = 0;
|
||||
},
|
||||
|
||||
destroy () {
|
||||
this.reset();
|
||||
this._fontDefDictionary._texture.destroy();
|
||||
this._fontDefDictionary._texture = null;
|
||||
const handler = this._atlases.material.getMultiHandler();
|
||||
handler.removeTexture(this._texture);
|
||||
this._texture.destroy();
|
||||
this._texture = null;
|
||||
},
|
||||
|
||||
beforeSceneLoad () {
|
||||
this.clearAllCache();
|
||||
},
|
||||
});
|
||||
|
||||
clearAllCache () {
|
||||
this.destroy();
|
||||
class LetterAtlases {
|
||||
|
||||
let texture = new RenderTexture();
|
||||
texture.initWithSize(this._width, this._height);
|
||||
texture.update();
|
||||
/**
|
||||
* 图集数组
|
||||
*/
|
||||
atlases = [];
|
||||
|
||||
this._fontDefDictionary._texture = texture;
|
||||
},
|
||||
/**
|
||||
* Char 多纹理材质
|
||||
*/
|
||||
material = null;
|
||||
|
||||
getLetter (key) {
|
||||
/**
|
||||
* Fake MaterialVariant
|
||||
*/
|
||||
fakeMaterial = { material: null };
|
||||
|
||||
/**
|
||||
* 抽象图集
|
||||
*/
|
||||
_fontDefDictionary = new FontAtlas(null);
|
||||
|
||||
|
||||
constructor() {
|
||||
const handler = new cc.sp.MultiHandler();
|
||||
this.material = handler.material;
|
||||
this.fakeMaterial.material = this.material;
|
||||
|
||||
cc.director.on(cc.Director.EVENT_BEFORE_SCENE_LAUNCH, this.beforeSceneLoad, this);
|
||||
}
|
||||
|
||||
|
||||
insertLetterTexture(letterTexture) {
|
||||
for (const atlas of this.atlases) {
|
||||
const letter = atlas.insertLetterTexture(letterTexture);
|
||||
if (letter) {
|
||||
return letter;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.atlases.length >= 8) {
|
||||
return null;
|
||||
} else {
|
||||
const atlas = new LetterAtlas(this, _atlasWidth, _atlasHeight);
|
||||
const len = this.atlases.push(atlas);
|
||||
atlas._id = len - 1;
|
||||
const handler = this.material.getMultiHandler();
|
||||
handler.setTexture(atlas._id, atlas._texture);
|
||||
if (!CC_EDITOR && cc.sp.charAtlasAutoBatchCount >= len) {
|
||||
cc.sp.multiBatcher.requsetMaterial(atlas._texture);
|
||||
}
|
||||
return atlas.insertLetterTexture(letterTexture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
deleteLetter(letter) {
|
||||
letter.ref--;
|
||||
if (letter.ref === 0 && !letter._inCleans) {
|
||||
letter._inCleans = true;
|
||||
letter.atlas.waitCleans.push(letter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update() {
|
||||
for (const atlas of this.atlases) {
|
||||
atlas.update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
reset() {
|
||||
this._fontDefDictionary.clear();
|
||||
|
||||
for (const atlas of this.atlases) {
|
||||
atlas.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
destroy() {
|
||||
this._fontDefDictionary.clear();
|
||||
|
||||
for (const atlas of this.atlases) {
|
||||
atlas.destroy();
|
||||
}
|
||||
|
||||
this.atlases.length = 0;
|
||||
}
|
||||
|
||||
|
||||
beforeSceneLoad() {
|
||||
if (cc.sp.charAtlasAutoResetBeforeSceneLoad) {
|
||||
this.clearAllCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
clearAllCache() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
|
||||
getTexture() {
|
||||
if (!_emptyTexture) {
|
||||
_emptyTexture = new RenderTexture();
|
||||
_emptyTexture.initWithSize(_atlasWidth, _atlasHeight);
|
||||
_emptyTexture.update();
|
||||
}
|
||||
return _emptyTexture;
|
||||
}
|
||||
|
||||
|
||||
getLetter(key) {
|
||||
return this._fontDefDictionary._letterDefinitions[key];
|
||||
},
|
||||
}
|
||||
|
||||
getTexture () {
|
||||
return this._fontDefDictionary.getTexture();
|
||||
},
|
||||
|
||||
getLetterDefinitionForChar: function(char, labelInfo) {
|
||||
getLetterDefinitionForChar(char, labelInfo) {
|
||||
let hash = char.charCodeAt(0) + labelInfo.hash;
|
||||
let letter = this._fontDefDictionary._letterDefinitions[hash];
|
||||
if (!letter) {
|
||||
@ -242,12 +434,50 @@ cc.js.mixin(LetterAtlas.prototype, {
|
||||
temp.destroy();
|
||||
}
|
||||
|
||||
if (letter && _firstTraverse) {
|
||||
letter.ref++;
|
||||
_assembler._letterRefs.push(letter);
|
||||
this.checkMaterialAndUpdateTexId(letter);
|
||||
}
|
||||
|
||||
return letter;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
checkMaterialAndUpdateTexId(letter) {
|
||||
const atlas = letter.atlas;
|
||||
const comp = _assembler._renderComp;
|
||||
|
||||
// 检查是否需要自动切换材质
|
||||
if (_needCheckMaterial) {
|
||||
_needCheckMaterial = false;
|
||||
if (_usedMaterial.material !== _shareAtlas.material) {
|
||||
_assembler.checkAndSwitchMaterial(comp, atlas._texture, _usedMaterial);
|
||||
_usedMaterial = comp._materials[0];
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否需要更新 atlas tmpId,使用内置材质则不检查
|
||||
if (_usedMaterial.material !== _shareAtlas.material && atlas._tmpId === -1) {
|
||||
const handler = _usedMaterial.material.getMultiHandler();
|
||||
if (handler) {
|
||||
const index = handler.getIndex(atlas._texture.getImpl());
|
||||
if (index !== -1) {
|
||||
atlas._tmpId = index;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果无法在材质中找到 texture,则切换至内置材质
|
||||
comp.setMaterial(0, _shareAtlas.material);
|
||||
_usedMaterial = _shareAtlas.fakeMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function computeHash (labelInfo) {
|
||||
let hashData = '';
|
||||
let hashData = '|';
|
||||
let color = labelInfo.color.toHEX();
|
||||
let out = '';
|
||||
if (labelInfo.isOutlined && labelInfo.margin > 0) {
|
||||
@ -262,11 +492,31 @@ let _shareAtlas = null;
|
||||
let _atlasWidth = 2048;
|
||||
let _atlasHeight = 2048;
|
||||
let _isBold = false;
|
||||
let _usedMaterial = null;
|
||||
let _needCheckMaterial = false;
|
||||
let _firstTraverse = false;
|
||||
let _assembler = null;
|
||||
let _emptyTexture = null;
|
||||
|
||||
export default class LetterFontAssembler extends WebglBmfontAssembler {
|
||||
_letterRefs = [];
|
||||
|
||||
initData() {
|
||||
let data = this._renderData;
|
||||
data.createFlexData(0, this.verticesCount, this.indicesCount, this.getVfmt());
|
||||
}
|
||||
|
||||
getVfmt() {
|
||||
return vfmtPosUvColorTexId;
|
||||
}
|
||||
|
||||
getBuffer() {
|
||||
return cc.renderer._handle.getBuffer("mesh", this.getVfmt());
|
||||
}
|
||||
|
||||
_getAssemblerData () {
|
||||
if (!_shareAtlas) {
|
||||
_shareAtlas = new LetterAtlas(_atlasWidth, _atlasHeight);
|
||||
_shareAtlas = new LetterAtlases();
|
||||
cc.Label._shareAtlas = _shareAtlas;
|
||||
}
|
||||
|
||||
@ -310,4 +560,64 @@ export default class LetterFontAssembler extends WebglBmfontAssembler {
|
||||
_determineRect (tempRect) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_aftUpdateRenderData(comp) {
|
||||
// 还原 tex id 与当前使用材质
|
||||
_assembler = this;
|
||||
_usedMaterial = _assembler._renderComp._materials[0];
|
||||
_needCheckMaterial = true;
|
||||
_firstTraverse = true;
|
||||
for (const atlas of _shareAtlas.atlases) {
|
||||
atlas._tmpId = -1;
|
||||
}
|
||||
|
||||
// 还原 letterRef
|
||||
this._recycleLetterRef();
|
||||
|
||||
super._aftUpdateRenderData(comp);
|
||||
|
||||
_usedMaterial = null;
|
||||
_assembler = null;
|
||||
}
|
||||
|
||||
_finishMultilineTextWrap() {
|
||||
_firstTraverse = false;
|
||||
}
|
||||
|
||||
_recycleLetterRef() {
|
||||
for (const letter of this._letterRefs) {
|
||||
_shareAtlas.deleteLetter(letter);
|
||||
}
|
||||
this._letterRefs.length = 0;
|
||||
}
|
||||
|
||||
_resetAssemblerData(assemblerData) {
|
||||
if (this._letterRefs.length !== 0) {
|
||||
this._recycleLetterRef();
|
||||
}
|
||||
}
|
||||
|
||||
appendVerts(comp, offset, l, r, b, t, letter) {
|
||||
super.appendVerts(comp, offset, l, r, b, t, letter);
|
||||
|
||||
// update texId
|
||||
const renderData = this._renderData;
|
||||
const verts = renderData.vDatas[0];
|
||||
const floatsPerVert = this.floatsPerVert;
|
||||
let texIdOffset = offset + this.texIdOffset;
|
||||
const id = _usedMaterial.material !== _shareAtlas.material ? letter.atlas._tmpId : letter.atlas._id;
|
||||
|
||||
verts[texIdOffset] = id;
|
||||
texIdOffset += floatsPerVert;
|
||||
verts[texIdOffset] = id;
|
||||
texIdOffset += floatsPerVert;
|
||||
verts[texIdOffset] = id;
|
||||
texIdOffset += floatsPerVert;
|
||||
verts[texIdOffset] = id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LetterFontAssembler.prototype.floatsPerVert = 6;
|
||||
LetterFontAssembler.prototype.texIdOffset = 5;
|
||||
LetterFontAssembler.prototype.isMulti = true;
|
||||
|
@ -65,7 +65,7 @@ export default class WebglBmfontAssembler extends BmfontAssembler {
|
||||
return comp.node._color._val;
|
||||
}
|
||||
|
||||
appendQuad (comp, texture, rect, rotated, x, y, scale) {
|
||||
appendQuad(comp, texture, rect, rotated, x, y, scale, letter) {
|
||||
let renderData = this._renderData;
|
||||
let verts = renderData.vDatas[0],
|
||||
uintVerts = renderData.uintVDatas[0];
|
||||
@ -131,7 +131,7 @@ export default class WebglBmfontAssembler extends BmfontAssembler {
|
||||
b = y - rectHeight * scale;
|
||||
t = y;
|
||||
|
||||
this.appendVerts(comp, _dataOffset, l, r, b, t);
|
||||
this.appendVerts(comp, _dataOffset, l, r, b, t, letter);
|
||||
|
||||
// colors
|
||||
let colorOffset = _dataOffset + this.colorOffset;
|
||||
@ -143,7 +143,7 @@ export default class WebglBmfontAssembler extends BmfontAssembler {
|
||||
_dataOffset += this.floatsPerVert * 4;
|
||||
}
|
||||
|
||||
appendVerts (comp, offset, l, r, b, t) {
|
||||
appendVerts(comp, offset, l, r, b, t, letter) {
|
||||
let local = this._local;
|
||||
let floatsPerVert = this.floatsPerVert;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user