Hi there,
We are calling `Link Anim Graph By Tag` on initialization of certain character actors in our game. (These character actors are pooled, which I believe may be contributing to the issue here.) We are getting a small but consistent stream of crash reports (access violation at `0x0`) from our live game pointing to FAnimNode_LinkedAnimGraph::CacheBonesSubGraph_AnyThread, on a ForegroundWorkerThread, while initializing these characters from the GameThread. It seems that when we call `Link Anim Graph By Tag` while there is work in progress on another thread (possibly left over from previous usage of the same character actor?), we are getting into trouble.
We have not been able to reproduce the issue locally, but I do think I see a potential bug in the code:
void FAnimNode_LinkedAnimGraph::CacheBonesSubGraph_AnyThread(const FAnimationCacheBonesContext& Context)
{
UAnimInstance* InstanceToRun = GetTargetInstance<UAnimInstance>();
if(InstanceToRun && LinkedRoot)
{
...
LinkedRoot->CacheBones_AnyThread(LinkedContext);
}
}
`LinkedRoot` is checked at the start of this function, but there is nothing preventing it from going null during the “then” block. If the game thread calls `UAnimInstance::LinkAnimGraphByTag` while this function is running, we go through `FAnimNode_LinkedAnimGraph::DynamicUnlink`, which sets `LinkedRoot` to `nullptr`. If we are already past the check in `CacheBonesSubgraph_AnyThread` at that point, but before the call to `LinkedRoot->CacheBones_AnyThread()`, we will dereference null when we reach that call.
Note that the LinkAnimGraphByTag call tree eventually ends up in `UAnimInstance::UninitializeAnimation()`, which calls `GetProxyOnGameThread` which effectively flushes any parallel tasks. This happens AFTER `LinkedRoot` is set to nullptr. That means CacheBonesSubgraph is potentially already corrupted and then forced to run to completion.
As a local workaround, I considered putting something like this at the top of `FAnimNode_LinkedAnimGraph::DynamicUnlink`, before `LinkedRoot` is set to nullptr:
if (UAnimInstance* InstanceToRun = GetTargetInstance<UAnimInstance>())
{
// force tasks on other threads to finish
(void)InstanceToRun->GetProxyOnAnyThread<FAnimInstanceProxy>();
}
However, I’m not very familiar with this code and I have no idea if this will cause other problems or undesirable effects.
Any guidance would be much appreciated. Thanks very much in advance.
Logan Smith