Download

Is Asynchronous Asset Loading Still Relevant

I have been reading through the topic of loading assets asynchronously on demand when they are needed, which was driven by the use of TAssetPtr/TSoftObjectPtr.

I am receiving mixed signals after reading these two documentations:

The first link shows how one would manage and query assets using their meta-data; with the ability to request their loading via the FStreamableManager. Keeping in mind I want a way to use assets on demand, I thought this was the way to go.

The second link however introduces this AssetManager object, and it states that it handles the loading and unloading of assets automatically. My questions are:

  1. If my concern is to ensure an asset is not lingering in memory when unused/unreferenced, wouldn’t the first documentation be irrelevant to me?
  2. What does the first documentation achieve that the AssetManager cannot - especially in the context of loading/unloading assets on demand.

Right now, I use TAssetPtr/TSoftObjectPtr when declaring variables such as meshes for an object. I need to ensure that I can use the pointer without having to make up a paradigm for loading/unloading assets explicitly. For context sake, I am using UE 4.17.

Thanks!

Both can be used.

For simpler cases where you just need to reference an asset that you don’t want to be loaded right away, use soft references/asset ptrs. Asset Manager in the other hand is a fairly more complex system.

Using the Asset Manager would be better in cases for bulk loading of game data, such as querying some database (like the Asset Registry) which give you a list of Assets as string references.

While I can see soft references/asset ptrs are useful for not loading to memory initially - I am also concerned if the asset will be unloaded from memory automatically when it is longer used. For example; if a character equips a weapon - the weapon object will have to load the associated mesh asset to memory for use. When the weapon object is de-equipped or destroyed - can I guarantee that the mesh asset will be unloaded and freed? I assume it will be freed due to garbage collection when the owning weapon object is destroyed.

The issue is, there may be times where I want the mesh to be unloaded, without actually destroying the weapon object (something as simple as toggling visibility or storing the temporary state of the object) - I find it hard to believe the asset management system will realize my intention and unload the asset when not needed. In that case, would it be safe to use this Unloadmethod from the StreamableManager?

There may be a slight confusion between “visible” and “referenced”, which are not the same thing at all:
You can change the visibility of your weapon without destroying the associated static mesh component. In that case the object is not visible but it is still fully in-use from the garbage collector’s perspective. So it’s possible to guarantee that the weapon will be unloaded upon destruction of its component, but not if you “de-equip” it (assuming de-equiping means hiding since you made the distinction with destroying it)

Other that, you are correct to assume that as soon as there are no longer any “known hard references” (as in, hard pointers that are UPROPERTIES) to your static mesh, it will indeed end up being garbage collected. Note that we are talking about the UStaticMesh itself, not the UStaticMeshComponent. Though of course the latter will reference the former.

Therefore, if you want to hide the weapon’s static mesh AND unload it, while preserving the weapon static mesh component, your only choice would be to unassign the UStaticMesh in the UStaticMeshComponent, in order to make the UStaticMesh eligible for Garbage Collection. At this point however, you might want to ask yourself why you’d prefer this solution over simply destroying then re-creating the UStaticMeshComponent when needed. All you’re saving is the CPU cycles needed to re-create the component, which is a negligible gain.

Back to a more general usecase, if you want to garbage collect an asset, you might want to verify first that the way your assets are loaded doesn’t create a “hard” reference by itself. If I remember correctly, most asset loading functions create a weak reference that you can then use as a hard refenrece somewhere else, but there are a few exceptions where the function stores the asset as a hard reference, waiting for your explicit command before releasing the asset. For instance, some AssetManager functions. And I’d assume that if the FStreamableManager has an Unload function, it probably does keep hard references as well.

A few tips to test that stuff by yourself (do not include in production):

1- Change the garbage collector’s frequency in your project settings so that it happens every few seconds (mine is always set at 3sec in the editor). This way, if you mess up your asset referencing, you will know real fast!
2- When loading your asset, try to keep a SoftObjectPtr of the asset, or something of the sort (it can be as simple as a UObject* that isn’t a UPROPERTY), accessible by your code. If you want to confirm if/when the asset is garbage collected, simply verify the validity of the SoftObjectPtr at regular intervals (in a tick function for instance), and print a log if the reference becomes invalid.