Hi,
We’re seeing a character’s skeletal mesh animation freeze, sometimes intermittently every few frames, sometimes consistently for hundreds of frames in a row, when the character is stood on a moveable surface. After some investigation this appears to be due to bad tick ordering between the character’s SkeletalMeshComponent and PhysicsControlComponent.
I realise the PhysicsControlComponent is Experimental, but figured you might find it helpful if I documented the issue for you. Also if this is a known issue and you have any recommended workarounds then I’d be grateful to hear them. If the workaround is toggling CVarAnimationDelaysEndGroup then I’d be curious about the expected performance implications of that CVar.
Here’s the detail - a bit dense I’m afraid but I’m happy to clarify if any bits of it aren’t making much sense:
- We have a character using the PhysicsControlComponent from the PhysicsControl plugin.
- The PhysicsControlComponent’s PrimaryTickFunction ticks in PrePhysics by default.
- In the PhysicsControlComponent’s PrimaryTickFunction, FPhysicsControlPoseData::Update() is called which reads from the SkeletalMeshComponent’s editable component space transforms.
- The PhysicsControlComponent’s PrimaryTickFunction has a pre-requisite on the SkeletalMeshComponent’s PrimaryTickFunction.
- The SkeletalMeshComponent’s PrimaryTickFunction has a pre-requisite on the CharacterMovementComponent’s PrimaryTickFunction.
- The SkeletalMeshCompoment also has a EndPhysicsTickFunction.
- In the SkeletalMeshCompoment’s EndPhysicsTickFunction, USkeletalMeshComponent::BlendInPhysicsInternal() is called which, when using ParallelBlend, calls SwapEvaluationContextBuffers().
- SwapEvaluationContextBuffers() swaps the SkeletalMeshComponent’s editable component space transforms with non-GameThread buffer.
- Later, not as part of any tick, USkeletalMeshComponent::CompleteParallelBlendPhysics() swaps those buffers back.
So far, so good. That’s all out-of-the box stuff. Normally FPhysicsControlPoseData::Update() is run in the PrePhysics tick group and runs well before SkeletalMeshComponent::BlendInPhysicsInternal() swaps the buffer it’s reading from so there’s no concern about reading the wrong buffer.
However, now consider that the character moves onto some moveable surface belonging to another “floor” actor:
- The CharacterMovementComponent searches through all components on the floor actor and adds them as pre-requisites for the CharacterMovementComponent’s PrimaryTickFunction.
- One of those components might be a SkeletalMeshComponent.
- The floor’s SkeletalMeshComponent has a tick group of PrePhysics, but its EndTickGroup is set to PostPhysics (search for CVarAnimationDelaysEndGroup in SkeletalMeshComponent.cpp for the why)
- Now the CharacterMovementComponent won’t tick until PostPhysics, and the PhysicsControlComponent is shifted to PostPhysics too, allowing it to run after the character’s SkeletalMeshComponent’s EndPhysicsTickFunction, after the buffer swap and before USkeletalMeshComponent::CompleteParallelBlendPhysics() swaps those buffers back.
- In that case, FPhysicsControlPoseData::Update() is reading from whatever was in the AnimEvaluationContext memory, which seems to be cleared to zeros at least, so TMs.Num() is 0, not-equal to BoneDatas.Num().
The visible effect of this is that animation on the character’s skeletal mesh freezes. There’s a timing element to this, so it’s not always every frame, though it can be for a significant number of frames in a row.
Cheers,
Woody
[Attachment Removed]