Texture Manager 功能模块:
小游戏有同时运行在手机和桌面设备的需求。使用单一的 ASTC 压缩格式在桌面端运行时,ASTC 格式的纹理需要先解压成对应未压缩的纹理格式然后再上传 GPU 。解压过程在主线程执行,会带来严重的卡顿问题;上传未压缩的纹理到 GPU , 也会导致显存的浪费(显存占用超过4倍)。
TextureManager 可以批量生成多种压缩格式的纹理数据,例如 ASTC4x4 + ASTC8x8 + DXT。 游戏运行时,TextureManager 模块开始初始化,贴图对象会根据以后信息和用户平台设定的纹理格式信息初始化 Metadata 信息,但是不会分配存储纹理数据。而后当纹理需要加载上传纹理数据时,引擎可以自动根据当前平台、设备性能选择合适的压缩格式进行下载使用。
启用 TextureManager 后,Assetbundle 只保留纹理的 metadata 信息,纹理数据被剥离出去。在 Assetbundle 打包好后,假如需要调整支持的纹理压缩格式,无需重新打包 AssetBundle 。
TextureManager 支持设置纹理显存用量上限。当显存用量达到预设时,会根据策略计算纹理的分值并排序,优先从 GPU 上踢出低分值纹理。计算分值时会考虑:
之前,在调用 Assetbundle.LoadAsset(Async)
、SceneManager.LoadScene(Async)
、Resource.Load(Async)
等接口加载资产后,引擎会依次:
创建纹理对象;
读纹理内容进内存;
Assetbundle.Unload(true)
、Resource.UnloadUnusedAssets()
时,执行:
从GPU上释放纹理显存
销毁纹理对象
为了控制显存占用,需要用户管理好纹理的生命周期(通常是在用户代码中做好引用计数):
Resource.UnloadUnusedAssets()
会带来严重卡顿问题;AssetBundle.Unload(true)
对 AssetBundle 的组织结构要求较高;启用 TextureManager 后,操作序列将变为:
其中步骤 2~4 将延迟至引用纹理的 Renderer 可见时(或引用纹理的UI被绘制时)再执行。
为避免同时处理大量纹理带来的卡顿与内存峰值,TextureManager 会进行分帧平滑调度,将这些操作分配到多帧中。
TextureManager 仅对纹理的 GPU 显存进行管理,不改变纹理对象的生命周期,纹理对象本身仍由原有的逻辑控制。 如下图所示,当纹理闲置,即当纹理 Renderer 不可见或者当前绘制 UI 对纹理的引用为0时,Texture Manager 标记纹理为可卸载,超出显存预算时将从 GPU 显存中卸载。当纹理重新处于渲染状态时,TextureManager 会再次执行步骤 2~4 ,上传纹理内容到 GPU 用于绘制。
为了解决小游戏中的纹理冗余问题,TextureManager 在上传纹理到 GPU 前会先查看是否已有同样的纹理。发现重复纹理的情况下,会在底层进行映射,使其共用 GPU 上的同一张纹理,从而避免纹理冗余带来的显存开销。