[engine] 新 Char 缓存模式实现

This commit is contained in:
SmallMain 2022-06-25 00:54:38 +08:00
parent bfbb3511eb
commit adb32f5c23
5 changed files with 368 additions and 51 deletions

View File

@ -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();
});
},

View File

@ -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;

View File

@ -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() { }
}

View File

@ -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;
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();
this._fontDefDictionary._texture = texture;
},
/**
* 图集数组
*/
atlases = [];
getLetter (key) {
/**
* Char 多纹理材质
*/
material = null;
/**
* 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;

View File

@ -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;