unity 资源管理之AssetBundle

上一篇讲了unity 资源管理的总体结构链接,这里主要讲unity AssetBundle 相关的内存管理内部机制

AssetBundle 由header 和data segment两部分组成

header包含AssetBundle 一些相关信息 唯一标识、压缩类型、清单文件(identifier, compression type, and a manifest)
清单文件是个查找表 key是对象名称,每个条目对一个data segment 字节位置。在大部分平台这个查找表实现的是一个平衡搜索树,具体在pc平台,windows、ios 实现的是红黑树。
data segment 是序列化AssetBundle中资源的数据,根据压缩选择有些不同
LZMA:所有资源被压缩在一起
LZ4 :每一个资源被分别压缩
没压缩:原始的数据资源

note:5.3之前的unity版本 对象不能被单独压缩,当读取一个或者多少的时候会解压整个AssetBundle 为了提高效率unity会缓存解压后的AssetBundle

加载AssetBundle

提供了5个api来加载,每一个都是同步和异步方法,这些api的不同根据两个条件
1. 平台的不同
2. 压缩类型LZMA , LZ4 ,未压缩

这些api可以随意混合使用
它们是:
AssetBundle.LoadFromMemory(Async optional)
AssetBundle.LoadFromFile(Async optional)
AssetBundle.LoadFromStream(Async optional)
UnityWebRequest’s DownloadHandlerAssetBundle
WWW.LoadFromCacheOrDownload (版本5.6或者低于)

AssetBundle.LoadFromMemory(Async)

unity 官方是建议不要使用该api的,因为c# 加载之后会把他拷贝到一块新的连续的内存里面,如果压缩格式是LZMA 那个会拷贝加载解压之后的整个文件,如果是没有压缩的和LZ4 的则会拷贝当前的全部字节。这样会造成内存的峰值至少为AssetBundle的两倍,最后加载一个asset 还需要把该资源拷贝一次,拷贝到gup 或者系统内存里面

AssetBundle.LoadFromFile(Async)

该api 对于未压缩和LZ4 压缩的文件加载时很高效的因为调研该函数时仅仅夹AssetBundle 的header 只有当需要时才会从文件中加载Object 如InstanceID 被引用或者调用AssetBundle.Load()

对于LZMA 格式的AssetBundle 加载整个文件并保存解压的拷贝

在Unity Editor 会直接加载整个文件到内存中,和AssetBundle.LoadFromMemory 类似,真实测试需要在真机上面

UnityWebRequest’s DownloadHandlerAssetBundle

提供了一个下载储存assetBundle 的方法,是否储存和储存位置都配置,最简单的使用
UnityWebRequest.GetAssetBundle. 下载一个资源,如果assetBundle 是LZMA 格式的在下载过程中可以储存为LZ4 格式,根据设置Caching.CompressionEnabled.。下载完成之后可以直接获取到assetBundle 加载过程和AssetBundle.LoadFromFile类似
如果当前文件已经下载那么会直接读取文件,加载过程和AssetBundle.LoadFromFile类似

WWW.LoadFromCacheOrDownload

在2017.1之后改还是弃用了,请使用UnityWebRequest 相关方法

在之前版本中改行为类似UnityWebRequest ,每次调用改方法会创建一个线程来下载。多次调用需要注意

对于下次LoadFromCacheOrDownload 和UnityWebRequest 都需要Dispose 推荐是有using 语法

加载Asset 从AssetBundle中

有3个加载方法都在AssetBundle 对象上,他们是同步异步都存在的
LoadAsset (LoadAssetAsync)
LoadAllAssets (LoadAllAssetsAsync)
LoadAssetWithSubAssets (LoadAssetWithSubAssetsAsync)

同步:同步方法加载会快于异步方法,至少快一帧(异步是调用的下一帧开始的)
异步:会加载多个对象直到达到当前时间限制

LoadAllAssets :会加载该AssetBundle中的全部资源,只要需要该AssetBundle大部分比如超过66%的时候官方才推荐使用
LoadAssets:加载单个资源
LoadAssetWithSubAssets :加载资源及其子类资源:如fbx的模型资源及嵌入的animation或者相关纹理等

加载资源不是在主线程上面的,与线程不敏感的unity系统(scripting, graphics)会转换到工作线程上面,列如VBO从mesh上面创建,textures解压等
5.3之后的版本,是并行反序列化多个Object 的。这些处理和整合是在工作线程上面的,当物体加载完成后会调用Awake ,在下一帧Object会变得可用。
同步方法会暂停主线程直到加载完成

AssetBundle 依赖关系

追踪依赖关系有两种API方法,它们和具体的环境有关
Unity Editor:AssetDatabase
运行时:AssetBundleManifest

object 直接的引用是通过GUID 和 localID 的(具体内容查看上一篇),在运行时是instancID 。当加载AssetBundle时会给每一个Object分配一个instanceID,当instanceID被其他对象引用时会自动创建出来,AssetBundle 之间的加载顺序没有要求,只要被引用的(object所在的AssetBundle)是已经被加载就好。

AssetBundle manifests

使用BuildPipeline.BuildAssetBundles 打包时,unity会序列化AssetBundle的依赖关系到一个独立的文件中,类型是AssetBundleManifest
储存位置在打包文件夹下,以文件夹为名称如:打包路径(projectroot)/build/Client/ 那么这个文件的路径是(projectroot)/build/Client/Client.manifest

AssetBundleManifest 提供了操作该文件的一些方法:如
GetAllAssetBundles:获取所有AssetBundle 的名称
GetAllDependencies :获取给定AssetBundle 的所有依赖关系
GetDirectDependencies 获取直接子对象的依赖关系

卸载AssetBundle

使用AssetBundle.Unload(unloadAllLoadedObjects )api
当unloadAllLoadedObjects 为true 时会卸载所有资源包括在使用的
unloadAllLoadedObjects 为false 时没有被引用到的资源会被卸载,之后重新加载AssetBundle会生成新的instanceID,重新加载Object 会重新生成一份,这种情况会造成内存多占用了一份(一次这样的操作)
有些情况需要重新加载资源如unity失去对设备的控制权(如pc被锁定,app被切换到后台),gpu会清理掉当前内存,需要从新加载资源

如果使用了false 那么之后可以使用Resources.UnloadUnusedAssets.来卸载,当切换场景不是add模式时会自动调用该方法

你可能感兴趣的:(unity)