Selaa lähdekoodia

Limiting render rate to simulation frequency (#873)

This is to prevent the simulation from running faster when the vsync frequency is higher than the simulation frequency.

Fixes #864
Jorrit Rouwe 1 vuosi sitten
vanhempi
commit
37b10b4782

+ 7 - 1
Samples/SamplesApp.cpp

@@ -392,6 +392,12 @@ static constexpr uint cMaxContactConstraints = 20480;
 
 SamplesApp::SamplesApp()
 {
+	// Limit the render frequency to our simulation frequency so we don't play back the simulation too fast
+	// Note that if the simulation frequency > vsync frequency the simulation will slow down as we want
+	// to visualize every simulation step. When the simulation frequency is lower than the vsync frequency
+	// we will not render a new frame every frame as we want to show the result of the sim and not an interpolated version.
+	SetRenderFrequency(mUpdateFrequency);
+
 	// Allocate temp memory
 #ifdef JPH_DISABLE_TEMP_ALLOCATOR
 	mTempAllocator = new TempAllocatorMalloc();
@@ -439,7 +445,7 @@ SamplesApp::SamplesApp()
 			UIElement *phys_settings = mDebugUI->CreateMenu();
 			mDebugUI->CreateSlider(phys_settings, "Max Concurrent Jobs", float(mMaxConcurrentJobs), 1, float(thread::hardware_concurrency()), 1, [this](float inValue) { mMaxConcurrentJobs = (int)inValue; });
 			mDebugUI->CreateSlider(phys_settings, "Gravity (m/s^2)", -mPhysicsSystem->GetGravity().GetY(), 0.0f, 20.0f, 1.0f, [this](float inValue) { mPhysicsSystem->SetGravity(Vec3(0, -inValue, 0)); });
-			mDebugUI->CreateSlider(phys_settings, "Update Frequency (Hz)", mUpdateFrequency, 7.5f, 300.0f, 2.5f, [this](float inValue) { mUpdateFrequency = inValue; });
+			mDebugUI->CreateSlider(phys_settings, "Update Frequency (Hz)", mUpdateFrequency, 7.5f, 300.0f, 2.5f, [this](float inValue) { mUpdateFrequency = inValue; SetRenderFrequency(mUpdateFrequency); });
 			mDebugUI->CreateSlider(phys_settings, "Num Collision Steps", float(mCollisionSteps), 1.0f, 4.0f, 1.0f, [this](float inValue) { mCollisionSteps = int(inValue); });
 			mDebugUI->CreateSlider(phys_settings, "Num Velocity Steps", float(mPhysicsSettings.mNumVelocitySteps), 0, 30, 1, [this](float inValue) { mPhysicsSettings.mNumVelocitySteps = int(round(inValue)); mPhysicsSystem->SetPhysicsSettings(mPhysicsSettings); });
 			mDebugUI->CreateSlider(phys_settings, "Num Position Steps", float(mPhysicsSettings.mNumPositionSteps), 0, 30, 1, [this](float inValue) { mPhysicsSettings.mNumPositionSteps = int(round(inValue)); mPhysicsSystem->SetPhysicsSettings(mPhysicsSettings); });

+ 33 - 1
TestFramework/Application/Application.cpp

@@ -167,7 +167,39 @@ void Application::Run()
 			chrono::microseconds delta = chrono::duration_cast<chrono::microseconds>(time - mLastUpdateTime);
 			mLastUpdateTime = time;
 			float clock_delta_time = 1.0e-6f * delta.count();
-			float world_delta_time = !mIsPaused || mSingleStep? clock_delta_time : 0.0f;
+			float world_delta_time = 0.0f;
+			if (mRequestedDeltaTime <= 0.0f)
+			{
+				// If no fixed frequency update is requested, update with variable time step
+				world_delta_time = !mIsPaused || mSingleStep? clock_delta_time : 0.0f;
+				mResidualDeltaTime = 0.0f;
+			}
+			else
+			{
+				// Else use fixed time steps
+				if (mSingleStep)
+				{
+					// Single step
+					world_delta_time = mRequestedDeltaTime;
+				}
+				else if (!mIsPaused)
+				{
+					// Calculate how much time has passed since the last render
+					world_delta_time = clock_delta_time + mResidualDeltaTime;
+					if (world_delta_time < mRequestedDeltaTime)
+					{
+						// Too soon, set the residual time and don't update
+						mResidualDeltaTime = world_delta_time;
+						world_delta_time = 0.0f;
+					}
+					else
+					{
+						// Update and clamp the residual time to a full update to avoid spiral of death
+						mResidualDeltaTime = min(mRequestedDeltaTime, world_delta_time - mRequestedDeltaTime);
+						world_delta_time = mRequestedDeltaTime;
+					}
+				}
+			}
 			mSingleStep = false;
 
 			// Clear debug lines if we're going to step

+ 5 - 0
TestFramework/Application/Application.h

@@ -65,6 +65,9 @@ protected:
 	/// Programmatically single step the simulation
 	void						SingleStep()									{ mIsPaused = true; mSingleStep = true; }
 
+	/// Set the frequency at which we want to render frames
+	void						SetRenderFrequency(float inFrequency)			{ mRequestedDeltaTime = 1.0f / inFrequency; }
+
 	/// Will restore camera position to that returned by GetInitialCamera
 	void						ResetCamera();
 
@@ -102,6 +105,8 @@ private:
 	bool						mDebugRendererCleared = true;
 	bool						mLeftMousePressed = false;
 	float						mFPS = 0.0f;
+	float						mRequestedDeltaTime = 0.0f;
+	float						mResidualDeltaTime = 0.0f;
 	float						mTotalDeltaTime = 0.0f;
 	int							mNumFrames = 0;
 };