开启动态合图的情况下,将 Label 的 CacheMode 设置为 BITMAT,生成的位图无法从动态合图中移除

重现方式:

  1. 开启动态合图
  2. 场景中添加一个 Label,并将 CacheMode 设置为 BITMAP
  3. 通过 cc.internal.dynamicAtlasManager._atlases[0]._innerSpriteFrames 可以看到动态合图中 Label 对应的位图信息
  4. 将 Label 对象 destroy
  5. Label 对应的位图信息仍然存在于 cc.internal.dynamicAtlasManager._atlases[0]._innerSpriteFrames 中无法移除

问题分析:
label.ts::onDestroy 中的处理为:

public onDestroy () {
    ...
    if (this._ttfSpriteFrame) {
        this._ttfSpriteFrame._resetDynamicAtlasFrame();
        ...
        this._ttfSpriteFrame.destroy();
        ...
    }
}

由于在 _resetDynamicAtlasFrame() 时已经将 spriteFrame 的 _original 重置为 null,之后在尝试将 spriteFrame 从动态合图中移除时会失败(见 dynamicAtlasManager.deleteAtlasSpriteFrame 中关于 _original 的判断),表现为字体位图无法从动态合图中移除。

感谢反馈,目前我们的动态合图比较简单,尚未添加释放能力,内部已经建立 issue 跟踪,会排期进行增强和优化。有更多的建议也欢迎反馈: Optimize dynamic atlas packing strategy

很高兴看到你们关于动态合图优化的计划,目前使用动态合图最大的痛点就是纹理回收时对应合图无法释放,以及 Label 的 BITMAP 模式下占用的合图空间不能释放,这导致很多时候不得不放弃使用动态合图,但实际这是非常好的优化手段,期待以后能看到完善且可靠的动态合图能力,看到你们5月已经计划对此进行优化,不知道这部分排期会到什么时候?

另外有一点不成熟小建议,我看到目前对于 spriteFrame 资源是可以设置 Packable 属性,用于控制是否参与动态合图以及自动图集的构建处理;首先或许应该将动态合图和自动图集的控制分开,另外除了 Packable 属性之外还可以针对动态合图增加优先级的设置,或许还能增加一些其他的设置,能够丰富开发者对资源和优化的控制能力。

这个问题在2.x时已经有了, 动态合图里的释放只是释放了合图的位置数据, 而合图png中的图像是没有删除的。
分享一个我自己的方法(仅限web, 原生你要在c++层再重写处理),其实你可以参考动态合图的代码, 重新建一新动态合图,然后新建一个MySprite和MyLabel(分别继承Sprite与Label), 复写动态合图的处理部分, 这个新动态合图管理器是按k-v形式处理, 然把模块作为key, 把散图都打到这个key的合图上, 当模块释放时, 把这个key的合图删掉
打一个比方,有一个alert弹窗, 那里面的sprite/lable 都打图打到key=alert_dialog 的合图中, 在弹窗关闭时, 直接释放alert_dialog 合图
image

2赞

:+1:确实是很好的方案,不过项目目前暂时还不打算定制引擎,如果引擎始终没有完善的机制,以后有机会就尝试这个方案

动态申请和回收纹理区域,得有一套高效智能管理算法

是的,所以楼上兄弟提出的方案确实很不错

如果能够像像项目 Layers 设置一样设置几个合图名称,并且支持在 spriteFrame 资源的属性设置面板和 BITMAP 模式下的 Label 里指定生成到哪张合图,并且提供按照名称回收的接口,那所需要的算法似乎就不用那么智能了,不用考虑如何在一张合图里面进行高效智能的动态申请和回收

@tkhoi01281 老哥,可以详细说说这个怎么改吗,2.4

参考一下engine/cocos2d/core/renderer/utils/dynamic-atlas/manager.js, 整个动态合图实现方法都有了, 只是ta 是用数组来保存合图, 你建一个新的manager在自己项目并改为用map 就好啦,同时接口的参数多加支持一个key:string来确保写到哪张合图里

最后再看看engine/cocos2d/core/renderer/assembler-2d.js里哪里是调用dynamicAtlasManager的
自己建一个新sprite继承老sprite, 然后在_resetAssembler函数super._resetAssembler(),后对this._assembler[‘那个调用dynamicAtlasManager函数名’] = (sprite)=>{ 改为调用你那个新的合图manager }
label也是一様处理方式

同,引擎的动态合图方案太简单了。还存在以下几个问题:

  1. 图集中的碎图不能单独打图集。
  2. 回收后的空间难以复用。
  3. 尤其不适合单场景使用。
  4. 不缓存图片数据时无法正常使用。

所以根据需求,自己实现调整可以得到适合自己的方案。当然这必须定制引擎。

可以重构他们的合图的。这引擎提供的,根本没法用。

先测测,实时动态合图的性能消耗再说,
不做实时复用是有原因,除非你有更智能方案。

大佬能细说什么原因么