Просмотр исходного кода

Added an #ifdef to turn on traces to help find determinism problems (#200)

Jorrit Rouwe 3 лет назад
Родитель
Сommit
59058fc38a

+ 1 - 0
.gitignore

@@ -5,3 +5,4 @@
 /stats*.html
 /snapshot.bin
 /*.jor
+/detlog.txt

+ 2 - 0
Jolt/Jolt.cmake

@@ -319,6 +319,8 @@ set(JOLT_PHYSICS_SRC_FILES
 	${JOLT_PHYSICS_ROOT}/Physics/Constraints/SwingTwistConstraint.h
 	${JOLT_PHYSICS_ROOT}/Physics/Constraints/TwoBodyConstraint.cpp
 	${JOLT_PHYSICS_ROOT}/Physics/Constraints/TwoBodyConstraint.h
+	${JOLT_PHYSICS_ROOT}/Physics/DeterminismLog.cpp
+	${JOLT_PHYSICS_ROOT}/Physics/DeterminismLog.h
 	${JOLT_PHYSICS_ROOT}/Physics/EActivation.h
 	${JOLT_PHYSICS_ROOT}/Physics/IslandBuilder.cpp
 	${JOLT_PHYSICS_ROOT}/Physics/IslandBuilder.h

+ 5 - 4
Jolt/Physics/Body/MotionProperties.h

@@ -8,6 +8,7 @@
 #include <Jolt/Physics/Body/BodyAccess.h>
 #include <Jolt/Physics/Body/MotionType.h>
 #include <Jolt/Physics/Body/MassProperties.h>
+#include <Jolt/Physics/DeterminismLog.h>
 
 JPH_NAMESPACE_BEGIN
 
@@ -116,10 +117,10 @@ public:
 
 	///@name Update linear and angular velocity (used during constraint solving)
 	///@{
-	inline void				AddLinearVelocityStep(Vec3Arg inLinearVelocityChange)			{ JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mLinearVelocity += inLinearVelocityChange; JPH_ASSERT(!mLinearVelocity.IsNaN()); }
-	inline void				SubLinearVelocityStep(Vec3Arg inLinearVelocityChange)			{ JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mLinearVelocity -= inLinearVelocityChange; JPH_ASSERT(!mLinearVelocity.IsNaN()); }
-	inline void				AddAngularVelocityStep(Vec3Arg inAngularVelocityChange)			{ JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mAngularVelocity += inAngularVelocityChange; JPH_ASSERT(!mAngularVelocity.IsNaN()); }
-	inline void				SubAngularVelocityStep(Vec3Arg inAngularVelocityChange) 		{ JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mAngularVelocity -= inAngularVelocityChange; JPH_ASSERT(!mAngularVelocity.IsNaN()); }
+	inline void				AddLinearVelocityStep(Vec3Arg inLinearVelocityChange)			{ JPH_DET_LOG("AddLinearVelocityStep: " << inLinearVelocityChange); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mLinearVelocity += inLinearVelocityChange; JPH_ASSERT(!mLinearVelocity.IsNaN()); }
+	inline void				SubLinearVelocityStep(Vec3Arg inLinearVelocityChange)			{ JPH_DET_LOG("SubLinearVelocityStep: " << inLinearVelocityChange); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mLinearVelocity -= inLinearVelocityChange; JPH_ASSERT(!mLinearVelocity.IsNaN()); }
+	inline void				AddAngularVelocityStep(Vec3Arg inAngularVelocityChange)			{ JPH_DET_LOG("AddAngularVelocityStep: " << inAngularVelocityChange); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mAngularVelocity += inAngularVelocityChange; JPH_ASSERT(!mAngularVelocity.IsNaN()); }
+	inline void				SubAngularVelocityStep(Vec3Arg inAngularVelocityChange) 		{ JPH_DET_LOG("SubAngularVelocityStep: " << inAngularVelocityChange); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mAngularVelocity -= inAngularVelocityChange; JPH_ASSERT(!mAngularVelocity.IsNaN()); }
 	///@}
 
 	/// Apply all accumulated forces, torques and drag (should only be called by the PhysicsSystem)

+ 3 - 0
Jolt/Physics/Constraints/ConstraintPart/AxisConstraintPart.h

@@ -6,6 +6,7 @@
 #include <Jolt/Physics/Body/Body.h>
 #include <Jolt/Physics/Constraints/ConstraintPart/SpringPart.h>
 #include <Jolt/Physics/StateRecorder.h>
+#include <Jolt/Physics/DeterminismLog.h>
 
 JPH_NAMESPACE_BEGIN
 
@@ -124,6 +125,8 @@ public:
 
 		// Calculate effective mass and spring properties
 		mSpringPart.CalculateSpringProperties(inDeltaTime, inv_effective_mass, inBias, inC, inFrequency, inDamping, mEffectiveMass);
+
+		JPH_DET_LOG("TemplatedCalculateConstraintProperties: dt: " << inDeltaTime << " invI1: " << inInvI1 << " r1PlusU: " << inR1PlusU << " invI2: " << inInvI2 << " r2: " << inR2 << " bias: " << inBias << ", c: " << inC << ", frequency: " << inFrequency << ", damping: " << inDamping << " r1PlusUxAxis: " << mR1PlusUxAxis << " r2xAxis: " << mR2xAxis << " invI1_R1PlusUxAxis: " << mInvI1_R1PlusUxAxis << " invI2_R2xAxis: " << mInvI2_R2xAxis << " effectiveMass: " << mEffectiveMass << " totalLambda: " << mTotalLambda);
 	}
 
 	/// Calculate properties used during the functions below

+ 13 - 0
Jolt/Physics/Constraints/ContactConstraintManager.cpp

@@ -8,6 +8,7 @@
 #include <Jolt/Physics/PhysicsUpdateContext.h>
 #include <Jolt/Physics/PhysicsSettings.h>
 #include <Jolt/Physics/IslandBuilder.h>
+#include <Jolt/Physics/DeterminismLog.h>
 #include <Jolt/Core/TempAllocator.h>
 #include <Jolt/Core/QuickSort.h>
 #ifdef JPH_DEBUG_RENDERER
@@ -42,6 +43,10 @@ void ContactConstraintManager::WorldContactPoint::CalculateNonPenetrationConstra
 template <EMotionType Type1, EMotionType Type2>
 JPH_INLINE void ContactConstraintManager::WorldContactPoint::CalculateFrictionAndNonPenetrationConstraintProperties(float inDeltaTime, const Body &inBody1, const Body &inBody2, Mat44Arg inInvI1, Mat44Arg inInvI2, Vec3Arg inWorldSpacePosition1, Vec3Arg inWorldSpacePosition2, Vec3Arg inWorldSpaceNormal, Vec3Arg inWorldSpaceTangent1, Vec3Arg inWorldSpaceTangent2, float inCombinedRestitution, float inCombinedFriction, float inMinVelocityForRestitution)
 {
+	JPH_DET_LOG("CalculateFrictionAndNonPenetrationConstraintProperties: p1: " << inWorldSpacePosition1 << " p2: " << inWorldSpacePosition2
+		<< " normal: " << inWorldSpaceNormal << " tangent1: " << inWorldSpaceTangent1 << " tangent2: " << inWorldSpaceTangent2
+		<< " restitution: " << inCombinedRestitution << " friction: " << inCombinedFriction << " minv: " << inMinVelocityForRestitution);
+
 	// Calculate collision points relative to body
 	Vec3 p = 0.5f * (inWorldSpacePosition1 + inWorldSpacePosition2);
 	Vec3 r1 = p - inBody1.GetCenterOfMassPosition();
@@ -840,6 +845,8 @@ void ContactConstraintManager::GetContactsFromCache(ContactAllocator &ioContactA
 				wcp.mContactPoint = &ccp;
 			}
 
+			JPH_DET_LOG("GetContactsFromCache: id1: " << constraint.mBody1->GetID() << " id2: " << constraint.mBody2->GetID() << " key: " << constraint.mSortKey);
+
 			// Calculate friction and non-penetration constraint properties for all contact points
 			CalculateFrictionAndNonPenetrationConstraintProperties(constraint, delta_time, transform_body1, transform_body2, *body1, *body2);
 
@@ -1022,6 +1029,8 @@ bool ContactConstraintManager::TemplatedAddContactConstraint(ContactAllocator &i
 		constraint.mCombinedFriction = settings.mCombinedFriction;
 		constraint.mCombinedRestitution = settings.mCombinedRestitution;
 
+		JPH_DET_LOG("TemplatedAddContactConstraint: id1: " << constraint.mBody1->GetID() << " id2: " << constraint.mBody2->GetID() << " key: " << constraint.mSortKey);
+
 		// Notify island builder
 		mUpdateContext->mIslandBuilder->LinkContact(constraint_idx, inBody1.GetIndexInActiveBodiesInternal(), inBody2.GetIndexInActiveBodiesInternal());
 
@@ -1094,6 +1103,10 @@ bool ContactConstraintManager::AddContactConstraint(ContactAllocator &ioContactA
 {
 	JPH_PROFILE_FUNCTION();
 
+	JPH_DET_LOG("AddContactConstraint: id1: " << inBody1.GetID() << " id2: " << inBody2.GetID()
+		<< " subshape1: " << inManifold.mSubShapeID1 << " subshape2: " << inManifold.mSubShapeID2
+		<< " normal: " << inManifold.mWorldSpaceNormal << " pendepth: " << inManifold.mPenetrationDepth);
+	
 	JPH_ASSERT(inManifold.mWorldSpaceNormal.IsNormalized());
 
 	// Swap bodies so that body 1 id < body 2 id

+ 16 - 0
Jolt/Physics/DeterminismLog.cpp

@@ -0,0 +1,16 @@
+// SPDX-FileCopyrightText: 2022 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt/Jolt.h>
+
+#include <Jolt/Physics/DeterminismLog.h>
+
+#ifdef JPH_ENABLE_DETERMINISM_LOG
+
+JPH_NAMESPACE_BEGIN
+
+DeterminismLog DeterminismLog::sLog;
+
+JPH_NAMESPACE_END
+
+#endif // JPH_ENABLE_DETERMINISM_LOG

+ 134 - 0
Jolt/Physics/DeterminismLog.h

@@ -0,0 +1,134 @@
+// SPDX-FileCopyrightText: 2022 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+//#define JPH_ENABLE_DETERMINISM_LOG
+#ifdef JPH_ENABLE_DETERMINISM_LOG
+
+#include <Jolt/Physics/Body/BodyID.h>
+#include <Jolt/Physics/Collision/Shape/SubShapeID.h>
+
+#include <iomanip>
+#include <fstream>
+
+JPH_NAMESPACE_BEGIN
+
+/// A simple class that logs the state of the simulation. The resulting text file can be used to diff between platforms and find issues in determinism.
+class DeterminismLog
+{
+private:
+	JPH_INLINE uint32		Convert(float inValue) const
+	{
+		return *(uint32 *)&inValue;
+	}
+
+public:
+							DeterminismLog()
+	{
+		mLog.open("detlog.txt", ios::out | ios::trunc | ios::binary); // Binary because we don't want a difference between Unix and Windows line endings.
+		mLog.fill('0');
+	}
+
+	DeterminismLog &		operator << (char inValue)
+	{
+		mLog << inValue;
+		return *this;
+	}
+
+	DeterminismLog &		operator << (const char *inValue)
+	{
+		mLog << dec << inValue;
+		return *this;
+	}
+
+	DeterminismLog &		operator << (const string &inValue)
+	{
+		mLog << dec << inValue;
+		return *this;
+	}
+
+	DeterminismLog &		operator << (const BodyID &inValue)
+	{
+		mLog << hex << setw(8) << inValue.GetIndexAndSequenceNumber();
+		return *this;
+	}
+
+	DeterminismLog &		operator << (const SubShapeID &inValue)
+	{
+		mLog << hex << setw(8) << inValue.GetValue();
+		return *this;
+	}
+
+	DeterminismLog &		operator << (float inValue)
+	{
+		mLog << hex << setw(8) << Convert(inValue);
+		return *this;
+	}
+
+	DeterminismLog &		operator << (int inValue)
+	{
+		mLog << inValue;
+		return *this;
+	}
+
+	DeterminismLog &		operator << (uint32 inValue)
+	{
+		mLog << hex << setw(8) << inValue;
+		return *this;
+	}
+
+	DeterminismLog &		operator << (uint64 inValue)
+	{
+		mLog << hex << setw(16) << inValue;
+		return *this;
+	}
+
+	DeterminismLog &		operator << (Vec3Arg inValue)
+	{
+		mLog << hex << setw(8) << Convert(inValue.GetX()) << " " << setw(8) << Convert(inValue.GetY()) << " " << setw(8) << Convert(inValue.GetZ());
+		return *this;
+	}
+	
+	DeterminismLog &		operator << (Vec4Arg inValue)
+	{
+		mLog << hex << setw(8) << Convert(inValue.GetX()) << " " << setw(8) << Convert(inValue.GetY()) << " " << setw(8) << Convert(inValue.GetZ()) << " " << setw(8) << Convert(inValue.GetW());
+		return *this;
+	}
+	
+	DeterminismLog &		operator << (const Float3 &inValue)
+	{
+		mLog << hex << setw(8) << Convert(inValue.x) << " " << setw(8) << Convert(inValue.y) << " " << setw(8) << Convert(inValue.z);
+		return *this;
+	}
+	
+	DeterminismLog &		operator << (Mat44Arg inValue)
+	{
+		*this << inValue.GetColumn4(0) << " " << inValue.GetColumn4(1) << " " << inValue.GetColumn4(2) << " " << inValue.GetColumn4(3);
+		return *this;
+	}
+
+	DeterminismLog &		operator << (QuatArg inValue)
+	{
+		*this << inValue.GetXYZW();
+		return *this;
+	}
+
+	// Singleton instance
+	static DeterminismLog	sLog;
+
+private:
+	ofstream				mLog;
+};
+
+/// Will log something to the determinism log, usage: JPH_DET_LOG("label " << value);
+#define JPH_DET_LOG(...)	DeterminismLog::sLog << __VA_ARGS__ << '\n'
+
+JPH_NAMESPACE_END
+
+#else
+
+/// By default we log nothing
+#define JPH_DET_LOG(...)
+
+#endif // JPH_ENABLE_DETERMINISM_LOG

+ 5 - 0
Jolt/Physics/PhysicsSystem.cpp

@@ -19,6 +19,7 @@
 #include <Jolt/Physics/Collision/ManifoldBetweenTwoFaces.h>
 #include <Jolt/Physics/Collision/Shape/ConvexShape.h>
 #include <Jolt/Physics/Constraints/ConstraintPart/AxisConstraintPart.h>
+#include <Jolt/Physics/DeterminismLog.h>
 #include <Jolt/Geometry/RayAABox.h>
 #include <Jolt/Core/JobSystem.h>
 #include <Jolt/Core/TempAllocator.h>
@@ -944,6 +945,8 @@ void PhysicsSystem::ProcessBodyPair(ContactAllocator &ioContactAllocator, const
 	Body *body2 = &mBodyManager.GetBody(inBodyPair.mBodyB);
 	JPH_ASSERT(body1->IsActive());
 
+	JPH_DET_LOG("ProcessBodyPair: id1: " << inBodyPair.mBodyA << " id2: " << inBodyPair.mBodyB << " p1: " << body1->GetCenterOfMassPosition() << " p2: " << body2->GetCenterOfMassPosition() << " r1: " << body1->GetRotation() << " r2: " << body2->GetRotation());
+
 	// Ensure that body1 is dynamic, this ensures that we do the collision detection in the space of a moving body, which avoids accuracy problems when testing a very large static object against a small dynamic object
 	// Ensure that body1 id < body2 id for dynamic vs dynamic
 	// Keep body order unchanged when colliding with a sensor
@@ -1391,6 +1394,8 @@ void PhysicsSystem::JobIntegrateVelocity(const PhysicsUpdateContext *ioContext,
 			Body &body = mBodyManager.GetBody(body_id);
 			MotionProperties *mp = body.GetMotionProperties();
 
+			JPH_DET_LOG("JobIntegrateVelocity: id: " << body_id << " v: " << body.GetLinearVelocity() << " w: " << body.GetAngularVelocity());
+
 			// Clamp velocities (not for kinematic bodies)
 			if (body.IsDynamic())
 			{

+ 15 - 0
PerformanceTest/PerformanceTest.cpp

@@ -11,6 +11,7 @@
 #include <Jolt/Physics/PhysicsSystem.h>
 #include <Jolt/Physics/Collision/NarrowPhaseStats.h>
 #include <Jolt/Physics/StateRecorderImpl.h>
+#include <Jolt/Physics/DeterminismLog.h>
 #ifdef JPH_DEBUG_RENDERER
 	#include <Jolt/Renderer/DebugRendererRecorder.h>
 	#include <Jolt/Core/StreamWrapper.h>
@@ -273,6 +274,7 @@ int main(int argc, char** argv)
 			for (uint iterations = 0; iterations < max_iterations; ++iterations)
 			{
 				JPH_PROFILE_NEXTFRAME();
+				JPH_DET_LOG("Iteration: " << iterations);
 
 				// Start measuring
 				chrono::high_resolution_clock::time_point clock_start = chrono::high_resolution_clock::now();
@@ -336,6 +338,19 @@ int main(int argc, char** argv)
 					validator.SetValidating(true);
 					physics_system.RestoreState(validator);
 				}
+
+			#ifdef JPH_ENABLE_DETERMINISM_LOG
+				const BodyLockInterface &bli = physics_system.GetBodyLockInterfaceNoLock();
+				BodyIDVector body_ids;
+				physics_system.GetBodies(body_ids);
+				for (BodyID id : body_ids)
+				{
+					BodyLockRead lock(bli, id);
+					const Body &body = lock.GetBody();
+					if (!body.IsStatic())
+						JPH_DET_LOG(id << ": p: " << body.GetPosition() << " r: " << body.GetRotation() << " v: " << body.GetLinearVelocity() << " w: " << body.GetAngularVelocity());
+				}
+			#endif // JPH_ENABLE_DETERMINISM_LOG
 			}
 
 			// Calculate hash of all positions and rotations of the bodies