UAssetManager::ChangeBundleStateForPrimaryAssets allows adding new BundleNames to the list of BundleNames loaded for a PrimaryAssetId. Each BundleName specifies a BundleEntry with a list of assets. Each BundleEntry for a PrimaryAsset can be individually specified for whether it should be loaded.

ChangeBundleStateForPrimaryAssets is more flexible than LoadPrimaryAssets; it can add or remove BundleNames while keeping others unchanged. LoadPrimaryAssets always completely removes the existing BundleNames and specifies the loading of the input BundleNames.

Whether called externally or through LoadPrimaryAssets, it is possible that ChangeBundleStateForPrimaryAssets will indicate some BundleNames should be loaded that are already finished loading (they are present in the PrimaryAsset's FPrimaryAssetData.CurrentState), and it is possible it will indicate some BundleNames should be loaded that have a load pending (they are present in the PrimaryAsset's FPrimaryAssetData.PendingState).

In the case of already-requested bundles in the CurrentState, the system behaves with good performance: the already-loaded bundles are added to the FStreamableHandle in the PendingState, and a load request is sent for them. The load request exits immediately because the assets are already loaded. When all of the new assets are loaded for the PendingState, the FStreamableHandle from the old CurrentState is dropped and the PendingState is copied onto the CurrentState.

But for already-requested bundles in the PendingState, the performance is suboptimal. The bundles that were previously requested but are not yet completed loading and are in the PendingState are dropped immediately during the function, by a call to NameData->PendingState.Reset(true). They are then requested again and their load restarts.

Avoid the wasted performance cost of canceling and restarting the loads for the bundles that have already been requested.

Note that this change is risky, because we have a single streamable handle that holds all of the requested bundles, and it will need to be changed. We attempted a fix for this issue twice before, but in each case had to revert it because it caused memory leaks.

Record of pervious attempts:

  • 5188931 in February 2019
    • If UAssetManager::ChangeBundleStateForPrimaryAsset is called on an asset id that is currently streaming and the new bundle state is a superset of the current one, just create a combined handle on top of it
    • Reverted with comment "Back out changelist 5188921. Possible cause for EdTest ensure"
  • 13055225 in April 2020
    • Redo undone streamable handle optimization with a fix to bulk load handles being inadvertently cancelled. Pending bulk load requests would get cancelled when individual load request changed bundle state of a single asset.
    • Reverted due to [Link Removed], [Link Removed], which mention menus being kept in memory and sync loads.

If we cannot fix the performance for a while, we should make an easier change sooner: we should change the API for FStreamableHandle so that the DelegateToCall is informed of the cancellation state. It should be told whether the set of package loads completed fully or was fully cancelled or was partially cancelled, and if cancelled whether there is a new streaming handle that should be subscribed to.

Have Comments or More Details?

There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-208804 in the post.

Login to Vote

CreatedMar 4, 2024
UpdatedApr 10, 2024
View Jira Issue