[SOLVED] Speed up simulation to faster than real-time

benelot
Posts: 350
Joined: Sat Jul 04, 2015 10:33 am
Location: Bern, Switzerland

[SOLVED] Speed up simulation to faster than real-time

Post by benelot »

Hello!

I am writing a simulator that is currently running with bullet physics in real-time. What I now would like to achieve is a slow-down and a speed-up button to run the simulation in slow-motion or faster than real time. How can this be achieved without affecting the simulation accuracy too much?

From what I understand according to the "stepping the world" page (http://bulletphysics.org/mediawiki-1.5. ... _The_World) I need to use the

Code: Select all

btDynamicsWorld::stepSimulation(
   btScalar timeStep,
   int maxSubSteps=1,
   btScalar fixedTimeStep=btScalar(1.)/btScalar(60.));
method. And to speed up, I probably just need to increase the timestep and divide it back down by the max substeps, right? Is there a simple way how I can let this self-optimize itself so that it basically simulates "as fast as it can"? I would then basically only tell it the fixedTimestep to fine-grain the simulation and the rest would be just running as quick as possible. Any ideas on how to speed up or slow down?

Thanks,
Last edited by benelot on Tue Aug 18, 2015 1:30 pm, edited 1 time in total.
Basroil
Posts: 463
Joined: Fri Nov 30, 2012 4:50 am

Re: Speed up simulation to faster than real-time

Post by Basroil »

I suggest you don't change the simulation at all, or else you'll get horrible artifacts like Blender's animation engine constraint glitch. Instead, run the simulation somewhere near your maximum "temporal zoom" level, and just change your graphics polling rate. An easy way is to have your dt (timeStep) be changed (dt*.5 for slowing down by 50%, dt*2 for 100% faster,etc) and maxSubSteps be the maximum dt divided by your step (fixedTimeStep) size (plus a bit for safety). Your simulation will technically still be running 1:1 with itself, but be displayed at a faster or slower rate depending on your choice of dt.
benelot
Posts: 350
Joined: Sat Jul 04, 2015 10:33 am
Location: Bern, Switzerland

Re: Speed up simulation to faster than real-time

Post by benelot »

This is what I basically had in mind! My concern is that at some point you reach such a high dt (timestep) that your maxSubsteps grow that huge that the simulation breaks.

I from what I understand (tell me if I am wrong), the CPU/GPU (wherever it is configured to run) both have a certain limit of substeps/second they can perform. Here I do not mean simulated time but actual time, so a measure of performance of the CPU/GPU. Let me say my processing unit has a limit of 80. So let us say that I set the dt that high that the max time steps reaches a number higher than 80. The PU will not be able to do the steps within the time limit and will lose time, right? A good behavior to me would be to get a return that tells me that it could only step 90% forward given the substeps I request. Otherwise I will always have a fine-grain simulation and then a huge simulation gap where objects jump. How can I detect the performance limit and basically just stay right below it to keep the maximum performance given my accuracy of the simulation?
Basroil
Posts: 463
Joined: Fri Nov 30, 2012 4:50 am

Re: Speed up simulation to faster than real-time

Post by Basroil »

That all depends on how complex your simulation is. A simple two body two constraint simulation I have for motor optimization runs at ~26000FPS (actually using timesteps of just 240, but I can do >100 of those one second dt simulations in one real second including very poorly written secondary code), but a bit more in depth one end up with ~2000FPS.

So yes, your mileage will vary, and you'll need to tune the difference between smooth animation and CPU time accordingly. Just remember that if you're planning on making a game others will use that you'll need to pick a target platform and tune for that.
benelot
Posts: 350
Joined: Sat Jul 04, 2015 10:33 am
Location: Bern, Switzerland

Re: Speed up simulation to faster than real-time

Post by benelot »

Thanks for your answer! So ok, it is not possible to just say that to let it run as fast as possible. Can I ask bullet somehow how many of the maxsubsteps it actually calculated and how many were not lost? Otherwise I will just tune it for my platforms to get my simulations done there as fast as possible. Hopefully I cam shorten down weeks down to days or so. Do you know if the Bullet on GPU is already ready to be used in complex simulations or not? Because that might speed up everything as well.

Edit: Found a way! I just calculate how much time is left for physics in a canonical loop as described in the Bullet physics wiki http://www.bulletphysics.org/mediawiki- ... _Game_Loop. My canonical loop even has an inner loop that triggers when I say I want a headless simulation. Then the inner loop only updates the physics and my model of the simulation and the input, but never goes to the graphics rendering. Only when I turn the headless simulation off via input, it exits that inner loop. SIMULATION_SPEED_09 in the code below is the graphical mode running as fast as possible, SIMULATION_SPEED_10 is the headless mode running as fast as possible. When this method exits, the Graphics rendering takes place. That is why I also measure the time the program is outside this method.

Code: Select all

bool SimulationManager::frameRenderingQueued(const Ogre::FrameEvent& evt) {
	// structure according to the canonical game loop
	// http://www.bulletphysics.org/mediawiki-1.5.8/index.php/Canonical_Game_Loop

	// shutdown the application if the application has initiated shutdown
	if (mWindow->isClosed()
			|| mStateHandler.getCurrentState() == StateHandler::SHUTDOWN) {
		return false;
	}

	//#############
	// Physics handling part
	//#############
	/* This, like the rendering, ticks every time around.
	 Bullet does the interpolation for us. */
	do {
		// update timers
		mPrevious = mNow;
		mNow = boost::posix_time::microsec_clock::local_time();

		mModelStart = boost::posix_time::microsec_clock::local_time();
		mLastGraphicsTick = mModelStart - mGraphicsStart;

		if (mSimulationSpeed == PhysicsConfiguration::SIMULATION_SPEED_09
				|| mSimulationSpeed
						== PhysicsConfiguration::SIMULATION_SPEED_10) {
			mPhysicsTick = boost::posix_time::millisec(
					ApplicationConfiguration::APPLICATION_TICK)
					- mLastGraphicsTick - mLastInputTick;

			mPhysicsStepStart = boost::posix_time::microsec_clock::local_time();
			while (mPhysicsTick > mPhysicsStepEnd - mPhysicsStepStart) {
				// step the physics forward
				mUniverse.setSimulationSpeed(mSimulationSpeed);
				mUniverse.stepPhysics(
						PhysicsConfiguration::SIMULATOR_PHYSICS_FIXED_STEP_SIZE);
				// update the model
				mUniverse.update(
						PhysicsConfiguration::SIMULATOR_PHYSICS_FIXED_STEP_SIZE);
				mPhysicsStepEnd =
						boost::posix_time::microsec_clock::local_time();
			}
		} else {
			// step the physics forward
			mUniverse.setSimulationSpeed(mSimulationSpeed);
			mUniverse.stepPhysics(
					(mNow - mPrevious).total_milliseconds() / 1000.0f);
			// update the universe
			mUniverse.update((mNow - mPrevious).total_milliseconds() / 1000.0f);
		}

		mInputStart = mNow = boost::posix_time::microsec_clock::local_time();
		mLastModelTick = mInputStart - mModelStart;

		//#############
		// Input part
		//#############
		// Game Clock part of the loop
		/*  This ticks once every APPLICATION_TICK milliseconds on average */
		mApplicationDt = mNow - mApplicationClock;
		while (mApplicationDt
				>= boost::posix_time::millisec(
						ApplicationConfiguration::APPLICATION_TICK)) {
			mApplicationDt -= boost::posix_time::millisec(
					ApplicationConfiguration::APPLICATION_TICK);
			mApplicationClock += boost::posix_time::millisec(
					ApplicationConfiguration::APPLICATION_TICK);
			// Inject input into handlers
			mInputHandler.injectInput();

			// update the information in the panels on screen
			updatePanels(ApplicationConfiguration::APPLICATION_TICK / 1000.0f);
		}

		mGraphicsStart = boost::posix_time::microsec_clock::local_time();
		mLastInputTick = mGraphicsStart - mInputStart;

	} while (mStateHandler.getCurrentState()
			== StateHandler::HEADLESS_SIMULATION);

	//#############
	// Graphics part
	//#############
	// reposition the camera
        ...
	// update view
        ...
	// draw the debug output if enabled
        ...
	return true;
}
I then step the bullet physics world like this:

Code: Select all

void PhysicsController::stepBulletPhysics(const double timeStep) {
	//step the simulation
	if (mDynamicsWorld && (!mPhysicsPaused || mPhysicsStepped)) {

		//calculate the number of substeps the simulator needs to take
		int subSteps =
				PhysicsConfiguration::SIMULATOR_SECURITY_MARGIN
						+ ceil(
								pow(2,
										PhysicsConfiguration::SIMULATION_SPEEDS[mSimulationSpeed])
										* timeStep
										/ PhysicsConfiguration::SIMULATOR_PHYSICS_FIXED_STEP_SIZE);
		if (timeStep) {
			mDynamicsWorld->stepSimulation(
					pow(2,
							PhysicsConfiguration::SIMULATION_SPEEDS[mSimulationSpeed])
							* timeStep, subSteps,
					PhysicsConfiguration::SIMULATOR_PHYSICS_FIXED_STEP_SIZE);
		}

		//if step trigger is pressed, we pause the simulation and it steps forward every time we press the step trigger
		if (mPhysicsStepped) {
			mPhysicsStepped = false;
			mPhysicsPaused = true;
		}
	}
}
The constants in my code are those:

Code: Select all

	/**
	 * Step size of the bullet physics simulator (solverAccuracy). Accuracy versus speed.
	 */
	static const double SIMULATOR_PHYSICS_FIXED_STEP_SIZE = 1.0f / 60.0f;

	static const int SIMULATOR_SECURITY_MARGIN = 5;

		const int PhysicsConfiguration::SIMULATION_SPEEDS[] = {
		/*SIMULATION_SPEED_01*/
		-2,
		/*SIMULATION_SPEED_02*/
		-1,
		/*SIMULATION_SPEED_03*/
		0,
		/*SIMULATION_SPEED_04*/
		1,
		/*SIMULATION_SPEED_05*/
		3,
		/*SIMULATION_SPEED_06*/
		5,
		/*SIMULATION_SPEED_07*/
		6,
		/*SIMULATION_SPEED_08*/
		7,
		/*SIMULATION_SPEED_09*/
		0, // run as fast as possible with graphics
		/*SIMULATION_SPEED_10*/
		0 //run as fast as possible headless
		};
I hope this helps somebody that has the same problem. If something is unclear, just ask.