• Unity
  • Load skeletonData asynchronously

Related Discussions
...

Hi,

We are planning for our project (using unity and spine), to have animated loading screens (not just simple pictures).
As we load the skeletondata during loading screens (like everyone i guess), we need to run loading on another thread so animation on screen won't freeze. We are using binary format to reduce skeletondata loading but there is still 3-4s of freeze.

I tried to launch loading on another thread but I get this errors on GetSkeletonData :

It seems that the skeletonData use unity primitives , that can only be executed on main thread.
How do you guys handle that?

Thank in advance

Yeah, the Unity primitives you're talking about is TextAsset for the json/binary and the atlas.txt.

If TextAsset doesn't allow you to get_text on a different thread, that has to be left on the main thread.

Thank for your answer

Ok 'ill try to set everything that use TextAsset in GetSkeletonData in main thread. I just hope this will not be everything that take time... or it will be useless.

I don't think I'm the only one who have this problem, it seems pretty common.
Have you heard of a way to get around this problem? maybe use something else to read txt/binart files?


01 Sep 2016, 07:53


So yes, what takes time is what the TextAsset do. So Set its work in the main thread and the rest in another does not resolve the problem...

Maybe the solution is to use a System class to read files , something thread safe. I'll try later to replace everything that use TextAsset in spine API.

How would you access Unity assets through a system class though?

5 jours plus tard

I don't think the text asset loading is the real problem. The problem is that the Spine runtime is loading the assets and recreating the skeleton data on scene activation. If you're loading a scene asynchronously in Unity then this occurs after your scene load is complete, and will happen on the main thread when you activate the scene. Ideally the Spine runtime would only load the assets and create the skeleton data in the editor, and then the skeleton data would be serialized out. This would allow the skeleton data to be loaded asynchronously. In theory it seems like this should be possible just by marking the appropriate types as [Serializable] but when I made a quick attempt at this it quickly got more complicated. 🙂

Maybe this is something that could be considered when next updating the Unity runtime? We switched over to the binary asset format to reduce the pause we get after the async scene load, and this helped a lot, but it would be even better if there were no pause at all!

@chrishowe The skeleton data can be loaded on a loading scene on command so they don't load lazily when the Spine components get instantiated.
You just need to call GetSkeleton and GetAtlas on the assets when ever you want them to load.

But you are right that it's more complicated than just marking things with the serializable attribute.
SkeletonData has dictionaries, trees and things that involve polymorphism. Adding the layer of indirection in the editor may not be worth it to the user or for load times. We don't know.

Pharan a écrit

@[supprimé] The skeleton data can be loaded on a loading scene on command so they don't load lazily when the Spine components get instantiated.

How do we do that?

One way would be to have a GameObject in a loading scene. And add a MonoBehaviour to it that looks like this:

public class SpineDataPreloader : MonoBehaviour {
     public List<SkeletonDataAsset> skeletonDataAssetsToLoad = new List<SkeletonDataAsset>();

 void Start () {
      foreach (var a in skeletonDataAssetsToLoad) {
           a.GetSkeletonData(true);
           Debug.Log(string.Format("{0} was loaded.", a.name));
      }
 }
}

Obviously, in production, you would put that method in a better place. But the essence of it is, you have a bunch of SkeletonDataAssets, and just call GetSkeletonData on it. That will cause it to load from the json if it hasn't already.

OP knows this. The primary topic was getting it to load on a different thread so the main thread can play a loading animation or do something else.

Okay, I thought from your wording that you knew of a way to make the load part of the scene load process, so just wanted to make sure. I know I can move the load around to somewhere less disconcerting to the player. In our game the loads all occur under a fade-to-black as part of the scene transition, so the user does not see the scene freeze, only that they stay on the black screen for longer. In a VR game though any pause is disconcerting to the player, even under a fade.

I understand that the OP was looking into multithreading to remedy the issue, but I'm not optimistic about that idea. The Unity API is not thread safe at all, so there may well be issues even after the TextAsset loading. I think the better solution would be to use the Unity serialization abilities so we don't have to parse the data at runtime and can make the load entirely part of an async scene load. If I get more time on this project I will investigate that path more thoroughly and get back to you. Thanks. 🙂

10 mois plus tard

hey,

We want to do the same thing. Was there any update to this topic?

A few versions ago, I made some changes to SkeletonDataAsset so a manager can have the threadsafe methods to call. Loading via threads is doable where threads are ok. (resulting speedup was marginal. Like around 10% something, strangely.)
But the threading structure is heavily dependent on how your game's loading is architectured so we didn't include any out-of-the-box solutions.

See the methods:

// Get stuff from your SkeletonDataAsset
byte[] bytes = skeletonDataAsset.skeletonJson.bytes; // for binary
AttachmentLoader attachmentLoader = new AtlasAttachmentLoader(skeletonDataAsset.atlasArray);
float scale = skeletonDataAsset.scale;

SkeletonData loadedSkeletonData = SkeletonData.ReadSkeletonData(bytes, attachmentLoader, scale); // This is pure C#. No Unity APIs. Call this in a thread.

skeletonData.InitializeWithData(loadedSkeletonData); // This one is Unity stuff. Don't call in a thread.

These are currently internal methods so this script needs to be in the same assembly as your Spine-Unity runtime. eg, if you moved that to your Plugins folder, the threaded loader's script in Unity will have to be in that folder too.

There have been some changes since then, but we've tried to make stuff threadsafe where it was obvious that it wasn't.

We are currently doing this:

Atlas[] atlasArray = m_SkeletonDataAssets[ix].GetAtlasArray();
attachmentLoader = new AtlasAttachmentLoader(atlasArray);

Your snippet does:

AttachmentLoader attachmentLoader = new AtlasAttachmentLoader(skeletonDataAsset.atlasArray);

And (with out version of the source at least) the skeletonDataAsset.atlasArray would just return an empty array:

public AtlasAsset[] atlasAssets = new AtlasAsset[0];

And it is specifically the GetAtlasArray() function which is currently causing us problems (as this goes off and starts parsing the atlas text file which ends up being very expensive, doing slow string parsing on quite a large set of files).

8 ans plus tard

Hello!
Any updates on this issue? we would like to load these assets in another thread, hopefully using Unity's job system, however I'm not certain it is possible due to reference types constraints (?)

@CandiGal You've just resurrected an eight year old thread, in general it's better to start a new one with the respective question, as the existing postings above are likely all completely outdated and more confusing than helpful.

See the on-demand loading UPM extension packages, currently supporting Unity Addressables natively:
https://esotericsoftware.com/spine-unity-on-demand-loading