If execution is within UpdateModuleHierarchyFromCDO -> ForEachModule and LoadStrongReference is called, a PostLoad call may cause the ModularRigModel to re-cache the CachedChildren property on ModuleReferences. The problem is these arrays are being iterated on in FModularRigModel::TraverseModules when they are modified. My solution is to preload all modules like so:
// the CDO owns the model - when we ask for the model we'll always
// get the model from the CDO. we'll now add UObject module instances
// for each module (data only) reference in the model.
// Note: The CDO does not contain any UObject module instances itself.
const FModularRigModel& Model = GetModularRigModel();
// Begin #ControlRig_ArrayReallocCrash
// preload as this can cause traversal transient begin iterator pointers to be invalid ( see ForEachModule -> TraverseModules )
TMap<FName, TStrongObjectPtr<UObject>> LoadedReferences;
for (const FRigModuleReference& ModuleReference : Model.Modules)
{
FControlRigAssetStrongReference AssetReference = ModuleReference.ControlRigAssetReference.Get();
if (IsInGameThread() && !AssetReference.IsValid())
{
AssetReference = ModuleReference.ControlRigAssetReference.LoadStrongReference(/*bForceLoad=*/true);
}
LoadedReferences.Add(ModuleReference.Name, TStrongObjectPtr(AssetReference.Get()));
}
Model.ForEachModule([this, &Model, &LoadedReferences](const FRigModuleReference* InModuleReference) -> bool
// End #ControlRig_ArrayReallocCrash
{
check(InModuleReference);
FControlRigAssetStrongReference AssetReference = InModuleReference->ControlRigAssetReference.Get();
// Begin #ControlRig_ArrayReallocCrash
TStrongObjectPtr<UObject>* ModuleObject = LoadedReferences.Find(InModuleReference->Name);
const bool bModuleWasPreloaded = ModuleObject != nullptr && ModuleObject->Get() == AssetReference.Get();
checkf(bModuleWasPreloaded, TEXT("ControlRig Preload Failure - Name Found (%s)"), *LexToString(ModuleObject != nullptr));
// if (IsInGameThread() && !AssetReference.IsValid())
// {
// AssetReference = InModuleReference->ControlRigAssetReference.LoadStrongReference(/*bForceLoad=*/true);
// }
// End #ControlRig_ArrayReallocCrash
Hopefully, that’s enough to go on? I considered instead of preloading, just iterating on Model.Modules instead of using ForEachModule. That might be the cleaner solution but I’m not familiar enough with this code.
Looking to see if this is on Epic’s radar or a fix is incoming? I could not find anything in perforce.
[Attachment Removed]