Hello,
we are recently experiencing some crashes when using Chaos::TSimCallbackObject for Contact Modification in UE 5.6, probably caused by a thread race condition, though we are not 100% sure about that.
Our current use-case is modifying collisions to get the correct desired interaction between specific types of Actors in our project; we use it to make colliding particles extremely light or heavy depending on which Actor is colliding.
This system has been working fine so far, but we have been rarely experiencing some crashes we can’t reproduce reliably.
We are aware a liability of our architecture is the fact we access AActor instances in order to check their type and decide how we should handle it, eventually reading some data from the Actor to modify the collision accordingly. For instance, for TypeA we may want to make the collision particularly heavy, for TypeB we need to read some data to know if we should ignore the collision or modify it, …
We access the Actor from the Physics Thread through the BodyInstance (consider that returning nullptr here is not a problem for our implementation):
AActor* FGameAsyncCallback::GetParticleOwningActor(const Chaos::FGeometryParticleHandle* Particle) const
{
FBodyInstance* BodyInstance = FChaosUserData::Get<FBodyInstance>(Particle->GTGeometryParticle()->UserData());
return (BodyInstance && BodyInstance->OwnerComponent.IsValid()) ? BodyInstance->OwnerComponent->GetOwner() : nullptr;
}
To my understanding, we most probably are facing a race condition between the Game Thread and the Physics Thread. Only part of our Actors are actually destroyed, while we apply a pooling system to most of our other Actors.
To circumvent this problem, we tried applying a ‘ghosting’ solution on the Actors we need to destroy:
1. When we need to destroy them, we first call:
Component->SetSimulatePhysics(false);
Component->SetCollisionEnabled(ECollisionEnabled::NoCollision);
on each UPrimitiveComponent;
2. then we call:
SetLifeSpan(Delay);to destroy them shortly after, with a delay big enough that they have been excluded from Contact Modifications.
The crashes have not improved much this way. From our research it’s unclear whether setting ECollisionEnabled::NoCollision may be causing the crash as well, destroying the physics state before Contact Modification is able to complete its cycle. Our next tentative solution is using the same approach, but just disabling the collision response to all Collision Channels instead of setting ECollisionEnabled::NoCollision.
We are aware this is most probably not the best practice and using the Chaos::TSimCallbackObject Input would probably be better, but, at the current time, changing the architecture drastically is not really feasible for us. In order to still be able to access the Actor instance without causing crashes, is there any other workaround we might try? What would the best practices be for such usage of Contact Modifications?
The most important data we need to pass down to Contact modifications are:
- a Particle’s owner Actor’s type (or another way to identify that)
- payload data that depend on the Actor’s type
Thank you for your time,
Michelangelo
[Attachment Removed]