GameFramework: Resource loading, resource loading, dependency loading, task pool, object pool, reference counting

GF resource loading flowchart

insert image description here

Introduction to GF loading resources

ResourceManager holds a function such as ResourceLoader (resource loading), ResourceLoader holds TaskPool, TaskPool holds agent, agent holds helper, and the method of actually performing operations is stored in helper.

ResourceManager:IResourceManager

Cache resource groups and loaded resources. You can get the version number and related path address of the resource, as well as some functions for version update and resource loading, etc.

How to index from asset name to corresponding bundle name

The following information is obtained from the description file, and the bundle, asset, and asset dependencies in the GameFrameworkVersion.dat file are changed from int[] to string[] and saved. Call the
process
GameFramework.Resource.ResourceManager.ResourceChecker.CheckResources
Mainly to solve the problem, you only need to remember the full path of the asset in the project starting from assets to load resources, without needing to remember the resource name

private Dictionary<string, AssetInfo> m_AssetInfos; //用于描述 Unity 中的一个具体资产,如一个预制体、一个材质、一张图片等。
private Dictionary<ResourceName, ResourceInfo> m_ResourceInfos; //用于描述 Unity 中的一个 AssetBundle(一些 Asset 的集合),或者一个 Game Framework 定义下的二进制文件(策划表)
 private readonly Dictionary<string, ResourceGroup> m_ResourceGroups;//资源组用于将资源分类,是资源的一种特性标签,一个资源可以归属于多个资源组。通过资源组可以游戏中构造出类似于“插件”的更新机制,如高清材质包、游戏语音包等。

ResourceLoader

Asset, Bundle is dependent on the number of references

            private readonly Dictionary<object, int> m_AssetDependencyCount; //asset引用计数,key即资源对象,例如texture等类型资源
            private readonly Dictionary<object, int> m_ResourceDependencyCount; //resource引用计数,object即assetbundle

Each time an asset is loaded, it corresponds to all dependencies asset+1, and the dependent bundle is dependent on the number of times its own internal asset is dependent on +1. Each time an asset is
unloaded, it is 0, which means that it is not depended on by others and can be uninstalled, and the corresponding dependency asset-1, Assets that depend on bundle-1
with a reference count of 0 can be released. Resources.UnloadAsset(object)
can release Assets with a reference count of 0. AssetBundle.Unload(true)

Use the task pool mechanism m_TaskPool
LoadAsset
LoadBinary
LoadScene
to load each load task LoadResourceTaskBase into m_TaskPool, the task can be added infinitely, and s_Serial will increase automatically. However, the number of agents, that is, the number of execution tasks is limited.
Add agents, the total number of agents is UnityGameFramework.Runtime.ResourceComponent.m_LoadResourceAgentHelperCount Control
GameFramework.Resource.ResourceManager.ResourceLoader.AddLoadResourceAgentHelper
The final agent is LoadResourceAgent

            private IObjectPool<AssetObject> m_AssetPool; //缓存的asset对象池
            private IObjectPool<ResourceObject> m_ResourcePool;//缓存的resource对象池

load dependencies

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)) //递归调用,加载依赖项,如果有循环依赖如何避免,a引用b,b引用a
                    {
    
    
                        return false;
                    }
                }

                m_TaskPool.AddTask(dependencyTask);
                if (!resourceInfo.Ready)
                {
    
    
                    m_ResourceManager.UpdateResource(resourceInfo.ResourceName);
                }

                return true;
            }

LoadResourceTaskBase: load resource task base class

Creating a task does not execute the task immediately.
Every time a task is added, s_Serial will increase automatically.

LoadAssetTask

Load target asset task

LoadDependencyAssetTask

load dependencies

TaskPool


Driver ProcessWaitingTasks LoadResourceAgent Agent Start in Update

//等待处理的任务,把所有任务,从free中分配working 代理
        private void ProcessWaitingTasks(float elapseSeconds, float realElapseSeconds)
        {
    
    
            LinkedListNode<T> current = m_WaitingTasks.First; //等待任务第一个
            //如果当前有空闲代理
            while (current != null && FreeAgentCount > 0) //当前还有free代理,且会一直循环到wait末尾
            {
    
    
                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 execution, task status

  1. The asset can be instantiated from m_AssetPool, and the task is done Done
  2. The resource can be instantiated in m_ResourcePool, and the task can be followed by CanResume
  3. Call the helper to perform the load
    insert image description here

Assign the idle agent to the working agent in taskPool.update, Start execution

//任务不是场景,说明要实例化
                   if (!m_Task.IsScene)
                   {
    
    
                       //从对象池里拿一个,已经可以从ab里实例出来asset,任务做完了
                       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;
                       }
                   }
                   
                    //从resource对象池中取出,说明任务可以接着执行
                   ResourceObject resourceObject = m_ResourceLoader.m_ResourcePool.Spawn(resourceName);
                   if (resourceObject != null)
                   {
    
    
                       OnResourceObjectReady(resourceObject);
                       return StartTaskStatus.CanResume;
                   }
                   
                   //以上都不满足
                   m_Helper.ReadFile(fullPath);

m_Helper.ReadFile(fullPath); Here we finally call DefaultLoadResourceAgentHelper.ReadFile
m_FileAssetBundleCreateRequest = AssetBundle.LoadFromFileAsync(fullPath);

DefaultLoadResourceAgentHelper

GameObject will be created in the scene
to load the proxy helper, which will be created in the scene
insert image description here

ILoadResourceAgentHelper is the loading resource agent helper interface. Loading resources will be accompanied by six major events (asynchronously loading resource update events, asynchronously reading resource file completion events, asynchronously reading resource binary stream completion events, and asynchronously converting resource binary streams to loading objects. event, asynchronously loaded resource completion event, error event). File and binary asynchronous loading of resources and functions to reset helpers. It is the interface for actually loading resources. After loading, it will be cached in the IObjectPool of ResourceLoader under ResourceManager.

load bundle

  1. After the AssetBundleCreateRequest request in UpdateFileAssetBundleCreateRequest() is polled in Update, a notification is sent.
  2. The final call to GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.OnLoadResourceAgentHelperReadFileComplete
  3. Create a ResourceObject and register it to ResourceLoader.m_ResourcePool. ResourceObject is mainly to increase the last used timestamp, and some information Resource information
  4. And cleared from the loading Resource table, s_LoadingResourceNames
  5. Resource is ready, load m_Task.LoadMain(this, resourceObject);, that is, load the target asset

load assets

UnityGameFramework.Runtime.DefaultLoadResourceAgentHelper.LoadAsset

  1. If it is a scene, enable asynchronous loading: m_AsyncOperation = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
  2. If it is a resource, m_AssetBundleRequest = assetBundle.LoadAssetAsync(assetName);
    poll m_AssetBundleRequest.isDone in Update
    , and throw the event LoadResourceAgentHelperLoadComplete
    to load the completed result
    GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.OnLoadResourceAgentHelperLoadComplete
  3. Create AssetObject, +1 for yourself, +1 for all assets you depend on
  4. Add to m_ResourceLoader.m_AssetPool.Register(assetObject, true);
  5. s_LoadingAssetNames.Remove(m_Task.AssetName); remove the resource being loaded
  6. All dependent Resource references + 1
    reference count
    Every time an asset is loaded, it corresponds to all dependent assets + 1. The dependent bundle is dependent on its own internal asset + 1
    each time an asset is unloaded. It is 0, which means it is not relied on by others. To unload, the Asset that depends on asset-1 and depends on bundle-1
    with a reference count of 0 can be released, and
    the AssetBundle with a reference count of 0 in Resources.UnloadAsset(object) can be released, and AssetBundle.Unload(true)

Load Log process

  1. Create and load asset main task a: Assets/GameMain/UI/UIForms/DialogForm.prefab
    GameFramework.Resource.ResourceManager/ResourceLoader/LoadAssetTask:Create
    initialize loading task 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"]
    here depends on 4 PNGs
    , here we need to load a perfab, and the corresponding dependent resources are also found.
    The function calls
    GameFramework.Resource.ResourceManager.ResourceLoader.LoadAsset
  2. Create dependent resource task 1: Assets/GameMain/UI/UISprites/Common/dialog-title-background.png
  3. Add dependent resource task 1 to waiting list GameFramework.Resource.ResourceManager+ResourceLoader+LoadDependencyAssetTask to m_WaitingTasks.count = 1
  4. Create dependent resource task 2: Assets/GameMain/UI/UISprites/Common/button-outline.png
  5. Add dependent resource task 2 to the waiting list GameFramework.Resource.ResourceManager+ResourceLoader+LoadDependencyAssetTask to m_WaitingTasks.count = 2
  6. Add a total of 4 dependent tasks in sequence
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)) //递归调用,加载依赖项,如果有循环依赖如何避免,a引用b,b引用a,因为加载中,asset,resource都有缓存,不会重复创建任务
                    {
    
    
                        return false;
                    }
                }

                m_TaskPool.AddTask(dependencyTask);
  1. Add the main task to waiting, so as to ensure that the previous dependent tasks are executed, and then execute the main task a
    Add task: GameFramework.Resource.ResourceManager+ResourceLoader+LoadAssetTask to m_WaitingTasks.count = 5
  2. Starting from the waiting task pool, the agent executes GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.Start
    Although the task is to read the asset, it actually reads ab asynchronously
    to read the resource file C:/Users/luoyikun_l/AppData/LocalLow /Game Framework/Star Force/UI/UISprites/Common.dat
  3. UnityGameFramework.Runtime.DefaultLoadResourceAgentHelper.UpdateFileAssetBundleCreateRequest is polled by the agent, and ab is loaded
  4. Resource loading completed UI/UISprites/Common
  5. The Assets/GameMain/UI/UISprites/Common/dialog-title-background.png task in the resource agent assistant ui/uisprites/common (UnityEngine.AssetBundle) in AB is not
    released, but the asset is still being loaded
  6. When other tasks are polling in TaskPool.update, they know that the bundle loaded by the previous task has been loaded
//从resource对象池中取出,说明任务可以接着执行
                    ResourceObject resourceObject = m_ResourceLoader.m_ResourcePool.Spawn(resourceName);
                    if (resourceObject != null)
                    {
    
    
                        OnResourceObjectReady(resourceObject);
                        return StartTaskStatus.CanResume;
                    }

The task directly enters the loading asset stage

//从resource里加载目标asset
               public void LoadMain(LoadResourceAgent agent, ResourceObject resourceObject)
               {
    
    
                   m_ResourceObject = resourceObject;
                   agent.Helper.LoadAsset(resourceObject.Target, AssetName, AssetType, IsScene);
               }
  1. UnityGameFramework.Runtime.DefaultLoadResourceAgentHelper.UpdateAssetBundleRequest polling asset loading completed
    asset loading completed Assets/GameMain/UI/UISprites/Common/dialog-background.png
    loaded assets will be put into the buffer pool
    asset–>Assets/GameMain/UI/ UISprites/Common/button-outline.png is loaded, and the assetObject is created in the m_AssetPool buffer pool
GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.OnLoadResourceAgentHelperLoadComplete 
m_ResourceLoader.m_AssetPool.Register(assetObject, true);
  1. Because the target asset will wait for all dependent assets to be loaded before starting to load its own bundle, and then load the asset
GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.Start 
//遍历依赖
                    foreach (string dependencyAssetName in m_Task.GetDependencyAssetNames())
                    {
    
    
                        //如果依赖asset不能spawn,接着等待
                        if (!m_ResourceLoader.m_AssetPool.CanSpawn(dependencyAssetName))
                        {
    
    
                            GameFrameworkLog.Info("{0}依赖项{1}未加载完成", m_Task.AssetName,dependencyAssetName);
                            m_Task.StartTime = default(DateTime);
                            return StartTaskStatus.HasToWait;
                        }
                    }

Now all dependent assets are loaded, and the target asset is loaded. There may be assets that depend on the project and will depend on other assets again, which will cause the dependent assets to enter the waiting state again

When resources are unloaded

GameFramework.Resource.ResourceManager.ResourceLoader.AssetObject.OnUnspawn
executes all dependent assets

                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;
                                //只会-1,不会对为 0 的Assetbundle进行卸载
                            }
                            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); //AssetBundle.Unload(true) ,也可能是asset 执行释放,也可能是scene执行释放

                }

Guess you like

Origin blog.csdn.net/luoyikun/article/details/125804373