GF资源加载流程图
GF加载资源简介
ResourceManager持有某个功能例如ResourceLoader(资源加载),ResourceLoader持有TaskPool,TaskPool持有agent,agent持有helper,真正执行操作的方法是存在helper中。
ResourceManager:IResourceManager
缓存资源组和已经加载过的资源。可以获取资源的版本编号和相关的路径地址,还有一些版本更新和资源加载的函数等等。
如何从asset名索引到对应bundle名
下面信息从描述文件获得,把GameFrameworkVersion.dat 文件中的 bundle ,asset ,asset的依赖项的关系,从文件中int[]变为string[],保存起来 调用过程 GameFramework.Resource.ResourceManager.ResourceChecker.CheckResources 主要解决问题,只需要记住工程中asset从assets开始的全路径就可以加载资源,而不需要记忆resource名字
private Dictionary<string, AssetInfo> m_AssetInfos;
private Dictionary<ResourceName, ResourceInfo> m_ResourceInfos;
private readonly Dictionary<string, ResourceGroup> m_ResourceGroups;
ResourceLoader
Asset,Bundle被依赖引用次数
private readonly Dictionary<object, int> m_AssetDependencyCount;
private readonly Dictionary<object, int> m_ResourceDependencyCount;
每次加载asset,对应所有依赖项asset+1,依赖的bundle根据自己内部的asset被依赖次数 +1 每次卸载asset,为0,说没被别人依赖,可以卸载,把对应依赖asset-1,依赖bundle-1 引用计数为0的Asset,即可被释放,Resources.UnloadAsset(object) 引用计数为0的AssetBundle,即可被释放,AssetBundle.Unload(true)
使用任务池机制m_TaskPool LoadAsset LoadBinary LoadScene 每来个加载任务LoadResourceTaskBase,放入到m_TaskPool中,任务可以无限加,s_Serial会自增。但是代理,即执行任务的数量为有限个数 增加代理,代理总数为UnityGameFramework.Runtime.ResourceComponent.m_LoadResourceAgentHelperCount 控制 GameFramework.Resource.ResourceManager.ResourceLoader.AddLoadResourceAgentHelper 最终代理为LoadResourceAgent
private IObjectPool<AssetObject> m_AssetPool;
private IObjectPool<ResourceObject> m_ResourcePool;
加载依赖项
GameFramework.Resource.ResourceManager.ResourceLoader.LoadAsset
foreach (string dependencyAssetName in dependencyAssetNames)
{
if (!LoadDependencyAsset(dependencyAssetName, priority, mainTask, userData))
GameFramework.Resource.ResourceManager.ResourceLoader.LoadDependencyAsset
private bool LoadDependencyAsset(string assetName, int priority, LoadResourceTaskBase mainTask, object userData)
{
ResourceInfo resourceInfo = null;
string[] dependencyAssetNames = null;
if (!CheckAsset(assetName, out resourceInfo, out dependencyAssetNames))
{
return false;
}
LoadDependencyAssetTask dependencyTask = LoadDependencyAssetTask.Create(assetName, priority, resourceInfo, dependencyAssetNames, mainTask, userData);
foreach (string dependencyAssetName in dependencyAssetNames)
{
if (!LoadDependencyAsset(dependencyAssetName, priority, dependencyTask, userData))
{
return false;
}
}
m_TaskPool.AddTask(dependencyTask);
if (!resourceInfo.Ready)
{
m_ResourceManager.UpdateResource(resourceInfo.ResourceName);
}
return true;
}
LoadResourceTaskBase:加载资源任务基类
创建任务并不是会立即执行任务 每增加任务,s_Serial会自增
LoadAssetTask
加载目标asset任务
LoadDependencyAssetTask
加载依赖任务
TaskPool
Update中驱动ProcessWaitingTasks LoadResourceAgent代理Start
private void ProcessWaitingTasks(float elapseSeconds, float realElapseSeconds)
{
LinkedListNode<T> current = m_WaitingTasks.First;
while (current != null && FreeAgentCount > 0)
{
ITaskAgent<T> agent = m_FreeAgents.Pop();
LinkedListNode<ITaskAgent<T>> agentNode = m_WorkingAgents.AddLast(agent);
T task = current.Value;
LinkedListNode<T> next = current.Next;
StartTaskStatus status = agent.Start(task);
if (status == StartTaskStatus.Done || status == StartTaskStatus.HasToWait || status == StartTaskStatus.UnknownError)
{
agent.Reset();
m_FreeAgents.Push(agent);
m_WorkingAgents.Remove(agentNode);
}
if (status == StartTaskStatus.Done || status == StartTaskStatus.CanResume || status == StartTaskStatus.UnknownError)
{
m_WaitingTasks.Remove(current);
}
if (status == StartTaskStatus.Done || status == StartTaskStatus.UnknownError)
{
ReferencePool.Release(task);
}
current = next;
}
}
LoadResourceAgent
GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.Start Start执行,任务状态
- 可以从m_AssetPool中实例出asset,任务做完了Done
- m_ResourcePool中可以实例出resource,任务可以接着做CanResume
- 调用辅助器执行加载
在taskPool.update中把空闲代理分配给到工作代理中,Start执行
if (!m_Task.IsScene)
{
AssetObject assetObject = m_ResourceLoader.m_AssetPool.Spawn(m_Task.AssetName);
if (assetObject != null)
{
OnAssetObjectReady(assetObject);
return StartTaskStatus.Done;
}
}
foreach (string dependencyAssetName in m_Task.GetDependencyAssetNames())
{
if (!m_ResourceLoader.m_AssetPool.CanSpawn(dependencyAssetName))
{
m_Task.StartTime = default(DateTime);
return StartTaskStatus.HasToWait;
}
}
ResourceObject resourceObject = m_ResourceLoader.m_ResourcePool.Spawn(resourceName);
if (resourceObject != null)
{
OnResourceObjectReady(resourceObject);
return StartTaskStatus.CanResume;
}
m_Helper.ReadFile(fullPath);
m_Helper.ReadFile(fullPath);这里最终调用DefaultLoadResourceAgentHelper.ReadFile m_FileAssetBundleCreateRequest = AssetBundle.LoadFromFileAsync(fullPath);
DefaultLoadResourceAgentHelper
会创建GameObject在场景中 加载代理辅助器,会在场景中创建
ILoadResourceAgentHelper是加载资源代理辅助器接口,加载资源会伴随六大事件(异步加载资源更新事件,异步读取资源文件完成事件,异步读取资源二进制流完成事件,异步将资源二进制流转换为加载对象完成事件,异步加载资源完成事件,错误事件)。资源的文件和二进制异步加载还有重置辅助器的函数。是实际加载资源的接口,加载完成以后会缓存到ResourceManager下ResourceLoader的IObjectPool。
加载bundle
- Update中轮询UpdateFileAssetBundleCreateRequest()里AssetBundleCreateRequest 请求完成后,发送通知,
- 最终调用到GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.OnLoadResourceAgentHelperReadFileComplete
- 创建出ResourceObject,注册到ResourceLoader.m_ResourcePool。ResourceObject主要是增加上次使用时间戳,和一些信息Resource信息
- 并且从正在加载Resource表中清除,s_LoadingResourceNames
- Resource准备好了,加载 m_Task.LoadMain(this, resourceObject);,即加载目标asset
加载asset
UnityGameFramework.Runtime.DefaultLoadResourceAgentHelper.LoadAsset
- 如果是场景,开启异步加载:m_AsyncOperation = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
- 如果是资源,m_AssetBundleRequest = assetBundle.LoadAssetAsync(assetName);
在Update轮询 m_AssetBundleRequest.isDone,抛出事件LoadResourceAgentHelperLoadComplete 加载完成结果 GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.OnLoadResourceAgentHelperLoadComplete - 创建AssetObject,自己+1,依赖的所有asset+1
- 加入到m_ResourceLoader.m_AssetPool.Register(assetObject, true);
- s_LoadingAssetNames.Remove(m_Task.AssetName);中移除正在加载资源
- 所有被依赖的Resource引用+1
引用计数 每次加载asset,对应所有依赖项asset+1,依赖的bundle根据自己内部的asset被依赖 +1 每次卸载asset,为0,说没被别人依赖,可以卸载,把对应依赖asset-1,依赖bundle-1 引用计数为0的Asset,即可被释放,Resources.UnloadAsset(object) 引用计数为0的AssetBundle,即可被释放,AssetBundle.Unload(true)
加载Log流程
- 创建加载asset主任务a:Assets/GameMain/UI/UIForms/DialogForm.prefab
GameFramework.Resource.ResourceManager/ResourceLoader/LoadAssetTask:Create 初始化加载任务Assets/GameMain/UI/UIForms/DialogForm.prefab–>[“Assets/GameMain/UI/UISprites/Common/dialog-title-background.png”,“Assets/GameMain/UI/UISprites/Common/button-outline.png”,“Assets/GameMain/UI/UISprites/Common/dialog-background.png”,“Assets/GameMain/UI/UISprites/Common/background.png”] 这里依赖4张PNG 这里要加载个perfab,对应依赖的资源也找出来 函数调用 GameFramework.Resource.ResourceManager.ResourceLoader.LoadAsset - 创建依赖资源任务1:Assets/GameMain/UI/UISprites/Common/dialog-title-background.png
- 增加依赖资源任务1到等待列表中GameFramework.Resource.ResourceManager+ResourceLoader+LoadDependencyAssetTask到m_WaitingTasks.count = 1
- 创建依赖资源任务2:Assets/GameMain/UI/UISprites/Common/button-outline.png
- 增加依赖资源任务2到等待列表中GameFramework.Resource.ResourceManager+ResourceLoader+LoadDependencyAssetTask到m_WaitingTasks.count = 2
- 依次增加总共4个依赖任务
GameFramework.Resource.ResourceManager.ResourceLoader.LoadDependencyAsset
private bool LoadDependencyAsset(string assetName, int priority, LoadResourceTaskBase mainTask, object userData)
{
ResourceInfo resourceInfo = null;
string[] dependencyAssetNames = null;
if (!CheckAsset(assetName, out resourceInfo, out dependencyAssetNames))
{
return false;
}
if (resourceInfo.IsLoadFromBinary)
{
return false;
}
LoadDependencyAssetTask dependencyTask = LoadDependencyAssetTask.Create(assetName, priority, resourceInfo, dependencyAssetNames, mainTask, userData);
foreach (string dependencyAssetName in dependencyAssetNames)
{
if (!LoadDependencyAsset(dependencyAssetName, priority, dependencyTask, userData))
{
return false;
}
}
m_TaskPool.AddTask(dependencyTask);
- 增加主任务到等待中,这样能确保是前面的依赖任务执行完毕,再会执行主任务a
增加任务:GameFramework.Resource.ResourceManager+ResourceLoader+LoadAssetTask到m_WaitingTasks.count = 5 - 从等待任务池里取一个开始,代理执行GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.Start
虽然任务是读取asset,实际上还是先读ab 异步读取资源文件C:/Users/luoyikun_l/AppData/LocalLow/Game Framework/Star Force/UI/UISprites/Common.dat - UnityGameFramework.Runtime.DefaultLoadResourceAgentHelper.UpdateFileAssetBundleCreateRequest 代理中轮询出来,ab加载完毕
- resource加载完成UI/UISprites/Common
- AB加载资源代理辅助器ui/uisprites/common (UnityEngine.AssetBundle)里面Assets/GameMain/UI/UISprites/Common/dialog-title-background.png
task并未释放,还是在加载asset - 其他task 在 TaskPool.update 中轮询时,知道上一个任务共同加载的bundle已加载好
ResourceObject resourceObject = m_ResourceLoader.m_ResourcePool.Spawn(resourceName);
if (resourceObject != null)
{
OnResourceObjectReady(resourceObject);
return StartTaskStatus.CanResume;
}
task直接进入 加载asset阶段
public void LoadMain(LoadResourceAgent agent, ResourceObject resourceObject)
{
m_ResourceObject = resourceObject;
agent.Helper.LoadAsset(resourceObject.Target, AssetName, AssetType, IsScene);
}
- UnityGameFramework.Runtime.DefaultLoadResourceAgentHelper.UpdateAssetBundleRequest 轮询中asset加载完毕
asset加载完成Assets/GameMain/UI/UISprites/Common/dialog-background.png 加载完毕的asset都会放入缓冲池中 asset–>Assets/GameMain/UI/UISprites/Common/button-outline.png加载完成,并且创建assetObject到m_AssetPool缓冲池中
GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.OnLoadResourceAgentHelperLoadComplete
m_ResourceLoader.m_AssetPool.Register(assetObject, true);
- 因为目标asset会一直等待所有依赖的asset加载完毕,才开始自身bundle加载,然后asset加载
GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.Start
foreach (string dependencyAssetName in m_Task.GetDependencyAssetNames())
{
if (!m_ResourceLoader.m_AssetPool.CanSpawn(dependencyAssetName))
{
GameFrameworkLog.Info("{0}依赖项{1}未加载完成", m_Task.AssetName,dependencyAssetName);
m_Task.StartTime = default(DateTime);
return StartTaskStatus.HasToWait;
}
}
现在所有依赖asset全部加载完毕,加载目标asset。可能存在依赖项目的asset还会再次依赖别的asset,会导致依赖项的asset再次进入等待状态
资源卸载时
GameFramework.Resource.ResourceManager.ResourceLoader.AssetObject.OnUnspawn 把所有依赖asset都执行遍
protected internal override void OnUnspawn()
{
base.OnUnspawn();
foreach (object dependencyAsset in m_DependencyAssets)
{
m_ResourceLoader.m_AssetPool.Unspawn(dependencyAsset);
}
}
AssetObject : ObjectBase
protected internal override void Release(bool isShutdown)
{
if (!isShutdown)
{
int targetReferenceCount = 0;
if (m_ResourceLoader.m_ResourceDependencyCount.TryGetValue(Target, out targetReferenceCount) && targetReferenceCount > 0)
{
throw new GameFrameworkException(Utility.Text.Format("Resource target '{0}' reference count is '{1}' larger than 0.", Name, targetReferenceCount));
}
foreach (object dependencyResource in m_DependencyResources)
{
int referenceCount = 0;
if (m_ResourceLoader.m_ResourceDependencyCount.TryGetValue(dependencyResource, out referenceCount))
{
m_ResourceLoader.m_ResourceDependencyCount[dependencyResource] = referenceCount - 1;
}
else
{
throw new GameFrameworkException(Utility.Text.Format("Resource target '{0}' dependency asset reference count is invalid.", Name));
}
}
}
m_ResourceLoader.m_ResourceDependencyCount.Remove(Target);
m_ResourceHelper.Release(Target);
}
|