Search Unity

can not do late atlas binding after reload spriteAtlas

Discussion in '2D' started by ranye, Aug 30, 2019.

  1. ranye

    ranye

    Joined:
    Sep 10, 2012
    Posts:
    8
    i want to cache lots of game objects and sprites,and load/unload spriteAtlas dynamically to save memory, atlas texture is huge.

    the first late binding is successful, the experiment is following:
    1, i keep gameObjs and sprite ,
    2, unload spriteAtlas and atlas texture using Resources.UnloadAsset(),
    3, load spriteAtlas and atlas texture again from assetBundle
    4, call previous saved System.Action<SpriteAtlas> callback in SpriteAtlasManager.atlasRequested

    this time late binding doesn't work for the sprites already existed.
    only new sprite cloned from spriteAtlas has atlas texture.
    unity version is 2018.4.4f1

    i hope old sprite can be bind again.
    the experiment is done using assetBundle, sprite and spriteAtlas asset is one bundle, gameObjs is in another,
    bundle isn't unloaded during experiment.
    in memory profiler spriteAtlas and atlas texture is unloaded/loaded correctly.

    btw, now spriteAtlas can only be power of 2 , multiple of 4 will comsume less memory , and is supported by dxt/etc/astc, mipmap is not used in this case
     
    Last edited: Aug 31, 2019
  2. MikePage_Artrix

    MikePage_Artrix

    Joined:
    Mar 18, 2016
    Posts:
    11
    Have you possibly managed to find a solution to this?

    I have almost an identical issue on 2019.2.0

    1. handle sprite atlas requested by saving the callback
    2. load sprite atlas from asset bundle
    3. callback action with sprite atlas - everything is fine here
    4. unload scene and call assetbundle.unload(true) on the sprite atlas bundle
    5. reload scene

    once I reload the scene and we repeat the cycle, not late binding will take place. I get the request in 1. and return the newly loaded atlas in 3. but the sprites simply don't show.
     
  3. ranye

    ranye

    Joined:
    Sep 10, 2012
    Posts:
    8
    i found a workaround, that is to use cloned sprites from spriteAtals to replace old sprites.
    but that is not that good for performance, especially for ugui, lots of ui components and sprite need to be handled.
     
  4. Venkify

    Venkify

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    185
    @MikePage_Artrix @ranye Please submit a bug report with a simple repro project please. We will take a look.
     
  5. MikePage_Artrix

    MikePage_Artrix

    Joined:
    Mar 18, 2016
    Posts:
    11
    Last edited: Sep 6, 2019
  6. MikePage_Artrix

    MikePage_Artrix

    Joined:
    Mar 18, 2016
    Posts:
    11
    @Venkify To add this, I think I've found the issue. This snipping shows the difference between my test scene after I've unloaded the sprite atlas (and unload the scene with the sprites that referenced it) and reloaded it.

    You can see that two sprites are left over from the unloading of the sprite atlas, even though I also unloaded an additive scene which contained the sprites. These two sprites have a ref count of 0.

    upload_2019-9-9_10-9-0.png

    If I forcibly call Resources.UnloadUnsedAssets() before reload it actually solves the problem by cleaning up these two orphaned sprites.

    However, this solution only works in my most basic test bed and does not work in our currently released Android application.
     
  7. MikePage_Artrix

    MikePage_Artrix

    Joined:
    Mar 18, 2016
    Posts:
    11
    @Venkify further to this, after some testing I replaced the UI images with this component,

    Code (CSharp):
    1. public class ManagedImage : Image
    2. {    protected override void OnDestroy()
    3.     {
    4.         Resources.UnloadAsset(sprite);
    5.         base.OnDestroy();
    6.     }
    7. }
    This then works as expected when the additive scene is reloaded.

    Although it also throws up a few:

    UnloadAsset can only be used on assets;
    UnityEngine.Resources:UnloadAsset(Object)

    Errors during runtime...

    OK, these errors are caused by trying to unload a sprite returned by SpriteAtlas.GetSprite() which are clones, once I wrapped these so the don't try to unload everything is fine.
     
    Last edited: Sep 9, 2019
  8. Venkify

    Venkify

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    185
  9. ranye

    ranye

    Joined:
    Sep 10, 2012
    Posts:
    8
  10. MikePage_Artrix

    MikePage_Artrix

    Joined:
    Mar 18, 2016
    Posts:
    11
    @Venkify Just a little more of an update. Unfortunately though everything seemed to be working, eventually the game/editor will crash with the following stack trace:

    Code (CSharp):
    1. ========== OUTPUTTING STACK TRACE ==================
    2.  
    3. 0x00007FF7B41730DC (Unity) SpriteAtlas::GetRuntimeRenderData
    4. 0x00007FF7B4171729 (Unity) SpriteAtlas::CanBindTo
    5. 0x00007FF7B50F3C61 (Unity) SpriteAtlas_CUSTOM_CanBindTo
    6. 0x0000017DA1AB78E9 (Mono JIT Code) (wrapper managed-to-native) UnityEngine.U2D.SpriteAtlas:CanBindTo (UnityEngine.U2D.SpriteAtlas,UnityEngine.Sprite)
    7. 0x0000017DA1AB75CB (Mono JIT Code) [C:\Program Files\Unity\Hub\Editor\2019.2.0f1\Editor\Data\Resources\PackageManager\BuiltInPackages\com.unity.ugui\Runtime\UI\Core\Image.cs:1864] UnityEngine.UI.Image:RebuildImage (UnityEngine.U2D.SpriteAtlas)
    8. 0x0000017DA1AB73C4 (Mono JIT Code) [C:\buildslave\unity\build\Runtime\2D\SpriteAtlas\ScriptBindings\SpriteAtlas.bindings.cs:31] UnityEngine.U2D.SpriteAtlasManager:PostRegisteredAtlas (UnityEngine.U2D.SpriteAtlas)
    9. 0x0000017DB5EAA59B (Mono JIT Code) (wrapper runtime-invoke) <Module>:runtime_invoke_void_object (object,intptr,intptr,intptr)
    10. 0x00007FF8B01BBC80 (mono-2.0-bdwgc) [c:\users\bokken\builds\vm\mono\mono\mini\mini-runtime.c:2809] mono_jit_runtime_invoke
    11. 0x00007FF8B0141D92 (mono-2.0-bdwgc) [c:\users\bokken\builds\vm\mono\mono\metadata\object.c:2921] do_runtime_invoke
    12. 0x00007FF8B014AD8F (mono-2.0-bdwgc) [c:\users\bokken\builds\vm\mono\mono\metadata\object.c:2968] mono_runtime_invoke
    13. 0x00007FF7B4FB9AF2 (Unity) scripting_method_invoke
    14. 0x00007FF7B4FB37B1 (Unity) ScriptingInvocation::Invoke
    15. 0x00007FF7B4173E41 (Unity) SpriteAtlasManager::Register
    16. 0x00007FF7B50F3AC5 (Unity) SpriteAtlasManager_CUSTOM_Register
    17. 0x0000017DA1AB71CE (Mono JIT Code) (wrapper managed-to-native) UnityEngine.U2D.SpriteAtlasManager:Register (UnityEngine.U2D.SpriteAtlas)
    18. 0x0000017D9E25845B (Mono JIT Code) [C:\Development\Depots\ManaMonsters\up_6\mc_client\Assets\Scripts\System\SpriteAtlasLateBinder.cs:97] SpriteAtlasLateBinder:SpriteAtlasRequested (string,System.Action`1<UnityEngine.U2D.SpriteAtlas>)
    19. 0x0000017D9E257D75 (Mono JIT Code) [C:\buildslave\unity\build\Runtime\2D\SpriteAtlas\ScriptBindings\SpriteAtlas.bindings.cs:20] UnityEngine.U2D.SpriteAtlasManager:RequestAtlas (string)
    20. 0x0000017D9E257F8B (Mono JIT Code) (wrapper runtime-invoke) <Module>:runtime_invoke_bool_object (object,intptr,intptr,intptr)
    21. 0x00007FF8B01BBC80 (mono-2.0-bdwgc) [c:\users\bokken\builds\vm\mono\mono\mini\mini-runtime.c:2809] mono_jit_runtime_invoke
    22. 0x00007FF8B0141D92 (mono-2.0-bdwgc) [c:\users\bokken\builds\vm\mono\mono\metadata\object.c:2921] do_runtime_invoke
    23. 0x00007FF8B014AD8F (mono-2.0-bdwgc) [c:\users\bokken\builds\vm\mono\mono\metadata\object.c:2968] mono_runtime_invoke
    24. 0x00007FF7B4FB9AF2 (Unity) scripting_method_invoke
    25. 0x00007FF7B4FB37B1 (Unity) ScriptingInvocation::Invoke
    26. 0x00007FF7B4FADA0A (Unity) ScriptingInvocation::Invoke<bool>
    27. 0x00007FF7B4173F6A (Unity) SpriteAtlasManager::RequestAtlasViaScript
    28. 0x00007FF7B417272F (Unity) `SpriteAtlasManager::SpriteAtlasManager'::`2'::EarlyUpdateSpriteAtlasManagerUpdateRegistrator::Forward
    29. 0x00007FF7B4A7D2A8 (Unity) ExecutePlayerLoop
    30. 0x00007FF7B4A7D389 (Unity) ExecutePlayerLoop
    31. 0x00007FF7B4A821FB (Unity) PlayerLoop
    32. 0x00007FF7B304EA9B (Unity) PlayerLoopController::UpdateScene
    33. 0x00007FF7B304C59C (Unity) Application::TickTimer
    34. 0x00007FF7B38DBE50 (Unity) MainMessageLoop
    35. 0x00007FF7B38DE86F (Unity) WinMain
    36. 0x00007FF7B6575792 (Unity) __scrt_common_main_seh
    37. 0x00007FF8F90F7BD4 (KERNEL32) BaseThreadInitThunk
    38. 0x00007FF8F9CECE71 (ntdll) RtlUserThreadStart
    39.  
    40. ========== END OF STACKTRACE ===========
     
  11. MikePage_Artrix

    MikePage_Artrix

    Joined:
    Mar 18, 2016
    Posts:
    11
    @Venkify

    So I've tracked down the problem causing the crash.

    This occurs when you have a coroutine / async function running waiting to Invoke the action from the sprite atlas late binding.

    Basically if an Image has been loaded which causes the late binder to start one if these async functions and then the Image is removed, the callback action is still valid.

    So when the sprite atlas is loaded, it calls the action, the sprite atlas manager gets the callback and promptly tries to see if a now destroy sprite can bind to the sprite atlas. Causing the crash.