Page 1 of 1

[Bepu1Int] Issues with determinsim

Posted: Thu Sep 12, 2019 5:45 pm
by cjddmut
I understand this is the forum for normal Bepu but the fixed point port points to this forum so my hope is that maybe some assistance could be given here as well :)

I'm working on a 2D lockstep networked game so I'm looking at using BEPU1Int to be my collision separation solution. However the simulations of two players keeps diverging. I've created a simple in process test that fails due to divergence.

Code: Select all

[Test]
public void TestDeterminismLateJoin()
{
	// Can't use mutlithreading so don't pass in an IParallelLooper 
	Space space1 = new Space();

	space1.TimeStepSettings.TimeStepDuration = ((Fix64) 1 / 60);
	
	Box box11 = CreatePhysicsObject(space1);
	box11.ActivityInformation.IsAlwaysActive = true;
	box11.LinearDamping = ((Fix64) 1 / 2);

	Box box12 = CreatePhysicsObject(space1);
	box11.ActivityInformation.IsAlwaysActive = true;
	box11.LinearDamping = ((Fix64) 1 / 2);
	
	int preIterations = 100;

	for (int i = 0; i < preIterations; i++)
	{
		space1.Update(space1.TimeStepSettings.TimeStepDuration);
	}
	
	Box box13 = CreatePhysicsObject(space1);
	box13.ActivityInformation.IsAlwaysActive = true;
	box13.LinearDamping = ((Fix64) 1 / 2);
	box13.Position = box11.Position;
	
	Space space2 = new Space();
	
	// As far as I can tell this will impact the random(?) solver
	space2.Solver.PermutationMapper.PermutationIndex = space1.Solver.PermutationMapper.PermutationIndex;
	
	space2.TimeStepSettings.TimeStepDuration = ((Fix64) 1 / 60);
	
	Box box21 = CreatePhysicsObject(space2);
	box21.ActivityInformation.IsAlwaysActive = true;
	box21.LinearDamping = ((Fix64) 1 / 2);
	
	// Saw in a forum post that this is what is needed to get full state across
	box21.Position = box11.Position;
	box21.LinearVelocity = box11.LinearVelocity;
	box21.Orientation = box11.Orientation;
	box21.AngularVelocity = box11.AngularVelocity;
	
	Box box22 = CreatePhysicsObject(space2);
	box22.ActivityInformation.IsAlwaysActive = true;
	box22.LinearDamping = ((Fix64) 1 / 2);
	
	box22.Position = box12.Position;
	box22.LinearVelocity = box12.LinearVelocity;
	box22.Orientation = box12.Orientation;
	box22.AngularVelocity = box12.AngularVelocity;

	Box box23 = CreatePhysicsObject(space2);
	box23.ActivityInformation.IsAlwaysActive = true;
	box23.LinearDamping = ((Fix64) 1 / 2);
	box23.Position = box21.Position;
	
	Assert.IsTrue(box11.Position != Vector3.Zero);
	Assert.IsTrue(box12.Position != Vector3.Zero);
	
	Assert.IsTrue(box21.Position != Vector3.Zero);
	Assert.IsTrue(box22.Position != Vector3.Zero);
	
	int iterations = 1000;

	for (int i = 0; i < iterations; i++)
	{
		space1.Update(space1.TimeStepSettings.TimeStepDuration);
		space2.Update(space2.TimeStepSettings.TimeStepDuration);
	}
	
	// THESE ASSERTS FAIL
	Assert.IsTrue(box11.Position == box21.Position);
	Assert.IsTrue(box12.Position == box22.Position);
	Assert.IsTrue(box13.Position == box23.Position);
}
	
private static Box CreatePhysicsObject(Space space)
{
	Box box = new Box(Vector3.Zero, 1, 1, 1, 1);
	box.PositionUpdateMode = PositionUpdateMode.Continuous;
		
	PointOnPlaneJoint pointOnPlaneJoint = new PointOnPlaneJoint(
		null, 
		box, 
		new Vector3(), 
		new Vector3(0, 0, -1), 
		box.Position);
		
	space.Add(pointOnPlaneJoint);
		
	RevoluteAngularJoint angularJoint = new RevoluteAngularJoint(
		null, 
		box, 
		new Vector3(0, 0, -1));
		
	space.Add(angularJoint);
	space.Add(box);

	return box;
}
The test is pretty simple. Two boxes are created on top of each in a space and allowed to simulation for 100 frames. Then a secondary space is created and the two boxes have their state copied over. A new box in both spaces is created to further interact with the two original boxes. This is allowed to run for 1000 more frames.

At the end of this their positions do not match. I'm not sure if I missing state or otherwise haven't set something up correctly. Or if I just messed up the test but I've looked over it numerous times and don't see anything.

If able to help it would be much appreciated! Thanks!

Re: [Bepu1Int] Issues with determinsim

Posted: Fri Sep 13, 2019 6:44 pm
by Norbo
One of the main sources of nondeterminism is constraints being solved in a different order. The permutation index is one piece of state that feeds into that, but it's influenced by everything that comes before- constraints are created by the narrow phase, whose order is defined by the broad phase's execution, which depends on accumulated updates and body insertion order.

In other words, attempting to reconstruct a simulation based on body states alone won't work in general, since there is additional internal state that has diverged.

So there are a couple of options:
1) Exactly mirror the full simulation in the duplicate. This would mean the exact state and order of entity adds, constraint adds, and time steps.
2) Attempt to bring over the internal state of the old simulation. This would require a pretty big chunk of custom work since there no APIs to handle it.
Edit:
3) Introduce explicit sorts inside the engine to guarantee execution order where necessary.

Re: [Bepu1Int] Issues with determinsim

Posted: Fri Sep 13, 2019 6:50 pm
by cjddmut
Yea I was wondering about the internal state of the physics system and was going to ask if there was any easy way to serialize/deserialize that state for the network.

Thanks for the reply!