We’ve been having this crash on-and-off for a few months now whereby we have a Metahuman skeletal mesh head with morph targets that receives changes to those morph targets, which causes a crash during the derived data compilation.
Initially we were seeing the first call stack, where we see a failed check due to a worker thread calling FreeUObjectIndex, this is due to the fact that we already have an existing morph target object residing within the SkeletalMesh asset, however the existing list of morph targets does not contain this morph target. Therefore as part of StaticAllocateObject, the engine tries to ‘delete’ the existing object before reallocating. We thought perhaps we can solve this by turning off async compilation, however that gives us the 2nd callstack.
Here it’s a similar issue, you are trying to allocate a new object over the top of an existing named morph target which causes another issue, I attempted to try to remove the existing object inside ‘BuildMorphTargetsInternal’ by removing the check for IsInGameThread, this prevents crashes in the editor during the mesh compilation, however causes cooker errors when attempting to rename the old morph target. So the current solution we have is to just reuse any existing morph target that exists within the SkeletalMesh asset and this now seems to resolve the crashes we see.
From your point of view does this make sense and would be a viable long term solution? Also confused as to how we have dangling morph targets that are not directly referenced by the ‘RegisteredMorphTargets’ in the SkeletalMesh asset?
If I had to hazard a guess here, in the first case, the StaticFindObject doesn’t find any UObjects that were created with the EInternalObjectFlags::Async flag set. So any morph target already created with that flag set, either because it’s still in the async loader thread, will not be found. Adding a call to StaticFindObjectFastSafe(UMorphTarget::StaticClass(), BaseSkelMesh, *ShapeName, RF_NoFlags, EInternalObjectFlags::Async) to see if it’s there. If wanting to delete the object, it’s probably best to clear the Async flag as well via ClearInternalFlags(EInternalObjectFlags::Async).
Not sure why the cooker is complaining when renaming the morph target out though. I’d have to check with the cooker people to see if there’s any sort of scoping I can put around that section to prevent the cooker from freaking out.
Might be worth throwing a FScopedUObjectHashTablesLock around the whole find/rename/newobject machinery to avoid other threads trashing the UObject hash table while twiddling with it.
With the cooker error it’s due to it attempting to remove the LinkerLoad object from the morph target, however the morph target object has not fully loaded at this point with the ‘RF_NeedPostLoad’ flag set so we get the following error
“Detaching from existing linker <Linker> while object <MorphTarget> needs loading. Setting linker to nullptr. See log for more information.”
Which would perhaps suggest that as you say, the asset has not been fully loaded and is still processing on the AsyncLoadingThread.
As we have our current solution of just reusing any existing morph target object we have now bypassed these issues (as far as I can tell), so getting an additional repro is difficult, I will try and look into it further with your suggestions if I can get the time.