How to Asynchronously Load Global/Singleton Assets

I am trying to store some commonly used, constant data, in a singleton object. These data are in the from of TAssetPtr. I am not sure if I really want all my singleton data to be persistent, raw pointers, so I am sticking with asset pointers for now, and loading them asynchronously. My issue is, I cannot find a good time to do the asynchronous loading.

I tried doing it in the constructor of the singleton or the class containing the data, but that causes a lot of crashes - I assume mostly because the order of instantiation of the singleton might propose some challenges. (i.e I couldn’t use the AssetManager when the singleton is constructed).

My current solution is such that whenever data is requested via the singleton, the singleton instance first verifies if all the assets were preloaded. If not, then it forces an async load before returning the request. This solution is rather hacky, because the async load will never finish on the same frame the request for the data was made. So I am left with few seconds where I receive null data before things fully initialize.

So the question becomes, what is a good place or entry point to do all loading I want?

Here is an example. Assume a singleton that contains a sound manager. The sound manager contains the actual asset pointers that require loading. The singleton’s get method looks as follows:


class USoundMgr : public UObject
{
    void                    PreloadSoundAssets();
    bool                    IsLoaded() const;
    ...
    UPROPERTY(EditDefaultsOnly)
    TAssetPtr<DataType> Data;
}



UBaseSingleton* UBaseSingleton::Get()
{
    if(!GEngine)
    {
        return nullptr;
    }

    UBaseSingleton* singleton = Cast<UBaseSingleton>(GEngine->GameSingleton);

    if(!singleton || !singleton->IsValidLowLevel())
    {
        // ("No Valid Singleton found!");
    }

    if(!singleton->SoundMgr->IsLoaded())
    {
        // ("Force Preloading Sound Manager!");
        singleton->SoundMgr->PreloadSoundAssets();
    }

    return singleton;
}


GameMode.StartPlay() or BeginPlay() seems like a reasonable spot. Map is loaded but game has not yet started for all actors.

https://api.unrealengine.com/INT/API…ode/index.html

If the Singleton only needs to be loaded once and is persistent then GameInstance might be better.

If you want to force all sound assets to load at startup then there’s no reason to use async loading. Using raw pointers will do the same.

Yep, that is true. But it was good to learn how to do async loading as well. Perhaps I want these assets to load as part of a loading menu that doesn’t freeze or stutter (Assuming that is how loading the game works, havn’t reached that far yet). But yeah, using raw pointers here would have made life so much easier.

I am thinking of how a loading screen works in a game - how things load without freezing the game as the level is populated. GameMode::StartPlay()/BeginPlay() sounds about right I think. Or even GameMode::InitGame?

You may want to wrap your singleton into the new Subsystem stuff that came in with 4.22. They have better lifecycle management/access than traditional singletons.

I would personally avoid singletons, there is GameInstance class you can subclass for that purpose as it will always be only one instance of it for duration of game running. On top of that you are free to subclass AssetManager to extend it for your project specific need as detailed in Unreal documentation. If you want to keep some data around you can hang them on GameInstance or add them to “root set”, that way they won’t be GC collected. If you want to do some loading like loading screen you should check this loading screen implementation plugin which uses it’s own module for this purpose. You can create as many modules as you like (it is very easy to reuse them later on for different projects).