Continuing discussion from this thread:
[Content removed]
And calling [mention removed]
The crash returned, but I think I may have finally figured it out.
To recap, FCollisionConstraintAllocator has an array, ActiveConstraints, that it iterates, visiting any constraints that it has pointers to. Very occasionally, for us, it crashes because it visits a constraint that has been deleted and overwritten with the freed memory fill pattern 0xDD.
I saw a constraint was destroyed that still had an index in its ContainerCookie.ConstraintIndex, indicating it was still emplaced in ActiveConstraints. But this was in FCollisionConstraintAllocator::RemoveParticle(). RemoveActiveConstraint() should have cleaned this up before the constraint’s destruction. How is this possible?
Here is my prospective fix. We need to pass ECollisionVisitorFlags::VisitAllCurrentAndExpired, because the default flags will not visit everything. If there are any disabled or expired constraints, by default they won’t be visited, and won’t be removed from ActiveConstraints.
void FCollisionConstraintAllocator::RemoveParticle(FGeometryParticleHandle* Particle)
{
// Removal not supported during the (parallel) collision detection phase
check(!bInCollisionDetectionPhase);
// Loop over all particle pairs involving this particle and tell each MidPhase
// that one of its particles is gone. It will get pruned at the next collision detection phase.
// Also remove its collisions from the Active lists.
Particle->ParticleCollisions().VisitMidPhases([this, Particle](FParticlePairMidPhase& MidPhase)
{
MidPhase.VisitCollisions([this](FPBDCollisionConstraint& Constraint)
{
RemoveActiveConstraint(Constraint);
return ECollisionVisitorResult::Continue;
}, ECollisionVisitorFlags::VisitAllCurrentAndExpired); // TRS CHANGE: visit EVERYTHING because next we'll delete everything
MidPhase.DetachParticle(Particle);
[Attachment Removed]