Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handle not release when AssetManager.Unload #2

Open
DumoeDss opened this issue Jul 17, 2020 · 5 comments
Open

handle not release when AssetManager.Unload #2

DumoeDss opened this issue Jul 17, 2020 · 5 comments

Comments

@DumoeDss
Copy link

For example:
public static bool TryGetOrLoadComponentAsync<TComponentType>(AssetReference aRef, out AsyncOperationHandle<TComponentType> handle) where TComponentType : Component

the handle should be release, or the asset won't be unload.

@akauper
Copy link
Owner

akauper commented Jul 19, 2020

Good find. I'll fix this and post an update

@SujanDuttaMishra
Copy link

SujanDuttaMishra commented Jul 21, 2020

hello, tried looking into this , but in code i see handle is being released with Unload Method ? may be its in the pool so ? try untick use pool and see ? in mine i see it releases everything whether i use pool or not.

try doing 👍
preload & pool and lunch app then you would see all asset loaded ... then do UnloadAllAsset without instantiating any particle or audio ? does it releases everything ? *** in mine Yes
do the same but this time instantiate audio and particle ! does it releases everything ? *** in mine Yes
try without pool ! does it releases everything ? *** in mine Yes

only issue so far for me is if i don't do preload event viewer don't show anything! even if i use preload with blank field it works! as in Example UI it utilizes LoadAssetsByLabelAsync ... i am trying to figure out where it it getting asset from when not using preload ... its difficult to follow thru codes especially when its not yours and your style ... i had to break the code in many smaller files .. so i took the manager and moved it to different classes for load ,unload,instantiate,destroy,utilities .. just makes it easier to follow the code ... So may be i added Addressables.Release(handle); at the end of unload method ? i don't remember just check.

@DumoeDss
Copy link
Author

DumoeDss commented Jul 22, 2020

preload & pool and lunch app then you would see all asset loaded ... then do UnloadAllAsset without instantiating any particle or audio ? does it releases everything ?
in mine No, particle doesn't releases
do the same but this time instantiate audio and particle ! does it releases everything ?
in mine No, particle doesn't releases
try without pool ! does it releases everything ?
in mine No, particle doesn't releases

The asset(Audio) can be release, but the gameobject can't.
I think what cause this is you just manager the asset handle when the gameobject instantiate.
In the function TryInstantiateOrLoadAsync(AssetReference aRef, Vector3 position, Quaternion rotation, Transform parent,
out AsyncOperationHandle handle)
, it will call TryGetOrLoadObjectAsync(aRef, out AsyncOperationHandle loadHandle), the loadHandle is an asset handle, and it was managed by _loadedAssets.
And then the instantiate handle->handle has be created by Addressables.ResourceManager.CreateChainOperation(), see what will do when it have been created:

 if (AssetManager.TryInstantiateOrLoadAsync(reference, Vector3.zero, Quaternion.identity, null, out AsyncOperationHandle<ParticleSystem> **handle**))
            {
                //The particle system has already been loaded.
                Destroy(handle.Result.gameObject, 5f);
            }
            else
            {
                //The particle system was not yet loaded.
                handle.Completed += op =>
                {
                    Destroy(op.Result.gameObject, 5f);
                };
            }

When Unload:

 static void Unload(object key)
        {
            CheckRuntimeKey(key);
            
            AsyncOperationHandle handle;
            if (_loadingAssets.ContainsKey(key))
            {
                handle = _loadingAssets[key];
                _loadingAssets.Remove(key);
            }
            else if (_loadedAssets.ContainsKey(key))
            {
                handle = _loadedAssets[key];
                _loadedAssets.Remove(key);
            }
            else
            {
                Debug.LogWarning($"{_baseErr}Cannot {nameof(Unload)} RuntimeKey '{key}': It is not loading or loaded.");
                return;
            }

            if (IsInstantiated(key))
                DestroyAllInstances(key);

            Addressables.Release(handle);
            
            OnAssetUnloaded?.Invoke(key);
        }

static void DestroyAllInstances(object key)
        {
            var instanceList = _instantiatedObjects[key];
            for (int i = instanceList.Count - 1; i >= 0; i--)
            {
                DestroyInternal(instanceList[i]);
            }
            _instantiatedObjects[key].Clear();
            _instantiatedObjects.Remove(key);
        }

Hey, the handle of TryInstantiateOrLoadAsync->out was not be released!

It use the Object.Instantiate in the InstantiateInternal(AssetReference aRef, TComponentType loadedAsset, Vector3 position, Quaternion rotation, Transform parent).
See this thread about release asset and instance .

Two bits of context before I get to the answer. First, ignoring Addressables and looking a little lower-level. If you load an asset from an asset bundle, instantiate it, then unload the asset and the bundle, you are in an uncertain situation. Some things are fine after being instantiated and some are not. Say your prefab references a texture from that bundle. Unloading it will break the live-link to that texture. So unloading a bundle post-instantiation is does not guarantee disaster, but is unlikely to be a good idea.
Second bit of context, you have two choices for your instantiating:

  1. If you do Addressables.LoadAsset("x"), then take the result and Instantiate it 100 times, our ref count on "x" will be 1.
  2. If you do Addressables.LoadAsset("x"), then Addressables.Instantiate("x") 100 times, our ref count on "x" will be 101.
    Now, to answer your question. If you do Addressables.ReleaseAsset("x"), you will decrement the ref count by one. In the first scenario, this means the count goes to 0, and the bundle will be unloaded if there's no other still-referenced asset in it. If you are in the second scenario, our ref count will still be at 100, and won't go down until you've called Addressables.ReleaseInstance 100 times.
    Of note, if you put 100 instances into a scene, then close the scene, this is the same as calling ReleaseInstance on those 100 items. We detect the close and react properly.

@DumoeDss
Copy link
Author

DumoeDss commented Jul 22, 2020

I define a List to collect instantiate handle, and add handle to List when handle has been created, then release all handle at UnloadAllAssets(). It work fine with GameObject, but not with ParticleSystem, I think there has some other problem with instantiate component.

public List<AsyncOperationHandle<GameObject>> handleList;

public void InstantiateRandomParticleSystem(bool usePooling){
    ...
    if(usePooling) {
    ...
    } else {
if (AssetManager.TryInstantiateOrLoadAsync(reference, Vector3.zero, Quaternion.identity, null, out AsyncOperationHandle<GameObject> handle))
            {
                //The particle system has already been loaded.
                Destroy(handle.Result.gameObject, 5f);
                if (handleList == null)
                {
                    handleList = new List<AsyncOperationHandle<GameObject>>();
                }
                if (!handleList.Contains(handle)){
                    handleList.Add(handle);
                }
            }
            else
            {
                //The particle system was not yet loaded.
                handle.Completed += op =>
                {
                    Destroy(op.Result.gameObject, 5f);
                    if (handleList == null)
                    {
                        handleList = new List<AsyncOperationHandle<GameObject>>();
                    }
                    if (!handleList.Contains(op))
                    {
                        handleList.Add(op);
                    }
                };
            }
    }
}
 public void UnloadAllAssets()
    {
       ...
        if (handleList != null)
            foreach (var handle in handleList)
            {
                Addressables.Release(handle);
            }
    } 

@DumoeDss
Copy link
Author

hello, tried looking into this , but in code i see handle is being released with Unload Method ? may be its in the pool so ? try untick use pool and see ? in mine i see it releases everything whether i use pool or not.

try doing 👍
preload & pool and lunch app then you would see all asset loaded ... then do UnloadAllAsset without instantiating any particle or audio ? does it releases everything ? *** in mine Yes
do the same but this time instantiate audio and particle ! does it releases everything ? *** in mine Yes
try without pool ! does it releases everything ? *** in mine Yes

only issue so far for me is if i don't do preload event viewer don't show anything! even if i use preload with blank field it works! as in Example UI it utilizes LoadAssetsByLabelAsync ... i am trying to figure out where it it getting asset from when not using preload ... its difficult to follow thru codes especially when its not yours and your style ... i had to break the code in many smaller files .. so i took the manager and moved it to different classes for load ,unload,instantiate,destroy,utilities .. just makes it easier to follow the code ... So may be i added Addressables.Release(handle); at the end of unload method ? i don't remember just check.

There has some problem with addressable event viewer, please try to use Addressable ver 1.8.4 or ver 1.9.2 ( My unity version is 2019.4.1). If it still not work, try to click "Record" and "Clear" button when editor play, maybe click twice button, it would be work.

With the ver 1.10.0 to ver 1.12.0, you can try to edit the function RegisterEventHandler of EventViewerWindow class which the file is in "Packages\com.unity.addressables@1.12.0\Editor\Diagnostics\GUI\EventViewerWindow.cs"
from

     void RegisterEventHandler(bool reg)
        {
            if (ProjectConfigData.postProfilerEvents)
                DiagnosticEventCollectorSingleton.RegisterEventHandler(OnEditorPlayModeEvent, reg, true);
        }

to

     void RegisterEventHandler(bool reg)
        {
                DiagnosticEventCollectorSingleton.RegisterEventHandler(OnEditorPlayModeEvent, reg, EditorApplication.isPlaying);
        }

Sometimes the bug would be fixed, sometimes not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants