jrouwe/JoltPhysics

UNDETERMINISTIC report!!!Regarding ContactManifold!

Closed this issue · 3 comments

Hello, I would like to report an issue of non-determinism. I have set up a scene with many Kinematic objects that are driven by external logic. I can guarantee that this part of the code is executed in a specific order. Then, I registered a callback function.

class CustomContactListener : public ContactListener {
    public:
        std::vector<ContactTuple> contactAddVector;
        std::vector<ContactTuple> contactPersistVector;
        std::vector<ContactTuple> contactRemoveVector;

        CustomContactListener() {
            contactAddVector.reserve(2048);
            contactPersistVector.reserve(40960);
            contactRemoveVector.reserve(2048);
        }

        std::vector<ContactTuple> FilterDuplicateContactTuples(const std::vector<ContactTuple>& contactTuples)
        {
            std::vector<ContactTuple> filteredTuples = contactTuples;
            auto uniqueEnd = std::unique(filteredTuples.begin(), filteredTuples.end(), [](const ContactTuple& tuple1, const ContactTuple& tuple2) {
                return tuple1.body1 == tuple2.body1 && tuple1.body2 == tuple2.body2;
            });
            filteredTuples.erase(uniqueEnd, filteredTuples.end());
            return filteredTuples;
        }

        bool IsTupleInVector(const ContactTuple& tuple, const std::vector<ContactTuple>& vector)
        {
            for (const auto& element : vector)
            {
                if (element.body1 == tuple.body1 && element.body2 == tuple.body2)
                {
                    return true;
                }
            }
            return false;
        }

        static bool CompareContactTuples(const ContactTuple& tuple1, const ContactTuple& tuple2)
        {
            if (tuple1.body1 < tuple2.body1)
                return true;
            else if (tuple1.body1 > tuple2.body1)
                return false;
            else
                return tuple1.body2 < tuple2.body2;
        }

        static bool CompareContactPoints(const Vec3& point1, const Vec3& point2) {
            return point1.GetX() < point2.GetX();
        }

        virtual ValidateResult OnContactValidate(const Body &inBody1, const Body &inBody2, RVec3Arg inBaseOffset,
                                                 const CollideShapeResult &inCollisionResult) override {

            // Allows you to ignore a contact before it is created (using layers to not make objects collide is cheaper!)
            //return ValidateResult::AcceptContact;
            return ValidateResult::AcceptAllContactsForThisBodyPair;
        }

        virtual void OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold,
                                    ContactSettings &ioSettings) override {
            ContactTuple tuple;
            tuple.body1 = inBody1.GetID().GetIndexAndSequenceNumber();
            tuple.body2 = inBody2.GetID().GetIndexAndSequenceNumber();
            if(inManifold.mRelativeContactPointsOn1.size()>0)
            {
                ContactPoints contactPoints = inManifold.mRelativeContactPointsOn1;
                std::vector<Vec3> contactPointsVector(contactPoints.begin(), contactPoints.end());
                QuickSort(contactPointsVector.begin(), contactPointsVector.end(), CompareContactPoints);
                tuple.contactPoint = toVec3(inManifold.mBaseOffset+contactPointsVector[0]);
                tuple.num=inManifold.mRelativeContactPointsOn1.size();
                tuple.BaseOffset=toVec3(inManifold.mBaseOffset);
                tuple.PenetrationDepth=inManifold.mPenetrationDepth;
            }
            if (!IsTupleInVector(tuple, contactAddVector))
            {
                contactAddVector.push_back(tuple);
            }
        }

        virtual void OnContactPersisted(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold,
                                        ContactSettings &ioSettings) override {
            ContactTuple tuple;
            tuple.body1 = inBody1.GetID().GetIndexAndSequenceNumber();
            tuple.body2 = inBody2.GetID().GetIndexAndSequenceNumber();
            if(inManifold.mRelativeContactPointsOn1.size()>0)
            {
                ContactPoints contactPoints = inManifold.mRelativeContactPointsOn1;
                std::vector<Vec3> contactPointsVector(contactPoints.begin(), contactPoints.end());
                QuickSort(contactPointsVector.begin(), contactPointsVector.end(), CompareContactPoints);

                tuple.contactPoint = toVec3(inManifold.mBaseOffset+contactPointsVector[0]);
                tuple.num=inManifold.mRelativeContactPointsOn1.size();
                tuple.BaseOffset=toVec3(inManifold.mBaseOffset);
                tuple.PenetrationDepth=inManifold.mPenetrationDepth;
            }
            if (!IsTupleInVector(tuple, contactPersistVector))
            {
                contactPersistVector.push_back(tuple);
            }
        }

        std::vector<ContactTuple> GetContactPersistTuples()
        {
            std::vector<ContactTuple> contactTuples = contactPersistVector;
            QuickSort(contactTuples.begin(), contactTuples.end(), CompareContactTuples);
            contactPersistVector.clear();
            std::vector<ContactTuple> filteredTuples = FilterDuplicateContactTuples(contactTuples);
            return filteredTuples;
        }

        std::vector<ContactTuple> GetContactAddTuples()
        {
            std::vector<ContactTuple> contactTuples = contactAddVector;
            QuickSort(contactTuples.begin(), contactTuples.end(), CompareContactTuples);
            contactAddVector.clear();
            std::vector<ContactTuple> filteredTuples = FilterDuplicateContactTuples(contactTuples);
            return filteredTuples;
        }
    };

There is occasional inconsistency observed in the values returned for inManifold.mRelativeContactPointsOn1 and inManifold.mBaseOffset between two instances of the program running on the same computer while executing the same code segment. The evidence is as follows:

企业微信截图_170858533590

Can you take a look at this issue and fix it, please?Thank you very much

What's even more concerning is that different instances are able to detect a varying number of collisions.For example, one instance detects 29 collisions persist while another detects 30.

The contact listener can receive callbacks from multiple threads at the same time. Your contact listener doesn't have any protection against that, so it is quite possible that this is just caused by a race condition. Try adding a mutex lock around OnContactAdded and OnContactPersisted.

This works, Thanks, and please accept my sincere respect.