浏览代码

Made GetProcessorTicksPerSecond more reliable and removed some dependencies on it (#706)

GetProcessorTicksPerSecond was using /proc/cpuinfo on Linux. The returned value is not accurate on ARM and apparently also not on new x86 AMD CPU's. Using std::chrono now to measure the number of ticks per second and updating it every frame to ensure that it stays reasonably accurate if the ticks/second value changes with e.g. CPU power states. Note that this function is only used for profiling information, so even if the value is wrong, the simulation will not be affected.

See: https://x.com/phys_ballsocket/status/1701933316654383164?s=20
Jorrit Rouwe 1 年之前
父节点
当前提交
b90eea5895

+ 2 - 0
Jolt/Core/Profiler.cpp

@@ -53,6 +53,8 @@ void Profiler::NextFrame()
 
 	for (ProfileThread *t : mThreads)
 		t->mCurrentSample = 0;
+
+	UpdateReferenceTime();
 }
 
 void Profiler::Dump(const string_view &inTag)

+ 16 - 81
Jolt/Core/TickCounter.cpp

@@ -16,15 +16,17 @@
 	#include <windows.h>
 #endif
 	JPH_SUPPRESS_WARNING_POP
-#elif defined(JPH_PLATFORM_LINUX) || defined(JPH_PLATFORM_ANDROID)
-	#include <fstream>
-#elif defined(JPH_PLATFORM_MACOS) || defined(JPH_PLATFORM_IOS)
-	#include <sys/types.h>
-	#include <sys/sysctl.h>
 #endif
 
+JPH_SUPPRESS_WARNINGS_STD_BEGIN
+#include <chrono>
+JPH_SUPPRESS_WARNINGS_STD_END
+
 JPH_NAMESPACE_BEGIN
 
+static uint64 sReferenceTick;
+static std::chrono::high_resolution_clock::time_point sReferenceTime;
+
 #if defined(JPH_PLATFORM_WINDOWS_UWP) || (defined(JPH_PLATFORM_WINDOWS) && defined(JPH_CPU_ARM))
 
 uint64 GetProcessorTickCount()
@@ -36,85 +38,18 @@ uint64 GetProcessorTickCount()
 
 #endif // JPH_PLATFORM_WINDOWS_UWP || (JPH_PLATFORM_WINDOWS && JPH_CPU_ARM)
 
-static const uint64 sProcessorTicksPerSecond = []() {
-#if defined(JPH_PLATFORM_WINDOWS_UWP) || (defined(JPH_PLATFORM_WINDOWS) && defined(JPH_CPU_ARM))
-	LARGE_INTEGER frequency { };
-	QueryPerformanceFrequency(&frequency);
-	return uint64(frequency.QuadPart);
-#elif defined(JPH_PLATFORM_WINDOWS)
-	// Open the key where the processor speed is stored
-	HKEY hkey;
-	RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, 1, &hkey);
-
-	// Query the speed in MHz
-	uint mhz = 0;
-	DWORD mhz_size = sizeof(uint);
-	RegQueryValueExA(hkey, "~MHz", nullptr, nullptr, (LPBYTE)&mhz, &mhz_size);
-
-	// Close key
-	RegCloseKey(hkey);
-
-	// Initialize amount of cycles per second
-	return uint64(mhz) * 1000000UL;
-#elif defined(JPH_PLATFORM_BLUE)
-	return JPH_PLATFORM_BLUE_GET_TICK_FREQUENCY();
-#elif defined(JPH_PLATFORM_LINUX) || defined(JPH_PLATFORM_ANDROID)
-	// Open /proc/cpuinfo
-    std::ifstream ifs("/proc/cpuinfo");
-    if (ifs.is_open())
-	{
-		// Read all lines
-		while (ifs.good())
-		{
-			// Get next line
-			string line;
-			getline(ifs, line);
-
-		#if defined(JPH_CPU_X86)
-			const char *cpu_str = "cpu MHz";
-		#elif defined(JPH_CPU_ARM)
-			const char *cpu_str = "BogoMIPS";
-		#else
-			#error Unsupported CPU architecture
-		#endif
-
-			// Check if line starts with correct string
-			const size_t num_chars = strlen(cpu_str);
-			if (strncmp(line.c_str(), cpu_str, num_chars) == 0)
-			{
-				// Find ':'
-				string::size_type pos = line.find(':', num_chars);
-				if (pos != String::npos)
-				{
-					// Convert to number
-					string freq = line.substr(pos + 1);
-					return uint64(stod(freq) * 1000000.0);
-				}
-			}
-		}
-	}
-
-	JPH_ASSERT(false);
-    return uint64(0);
-#elif defined(JPH_PLATFORM_MACOS) || defined(JPH_PLATFORM_IOS)
-	// Use sysctl to get the processor frequency
-	int mib[2];
-    mib[0] = CTL_HW;
-    mib[1] = HW_CPU_FREQ;
-    uint64 freq = 1;
-    size_t len = sizeof(freq);
-    sysctl(mib, 2, &freq, &len, nullptr, 0);
-	return freq;
-#elif defined(JPH_PLATFORM_WASM)
-	return 1; // Not supported
-#else
-	#error Undefined
-#endif
-}();
+void UpdateReferenceTime()
+{
+	sReferenceTick = GetProcessorTickCount();
+	sReferenceTime = std::chrono::high_resolution_clock::now();
+}
 
 uint64 GetProcessorTicksPerSecond()
 {
-	return sProcessorTicksPerSecond;
+	uint64 ticks = GetProcessorTickCount();
+	std::chrono::high_resolution_clock::time_point time = std::chrono::high_resolution_clock::now();
+
+	return (ticks - sReferenceTick) * 1000000000ULL / std::chrono::duration_cast<std::chrono::nanoseconds>(time - sReferenceTime).count();
 }
 
 JPH_NAMESPACE_END

+ 3 - 0
Jolt/Core/TickCounter.h

@@ -42,6 +42,9 @@ JPH_INLINE uint64 GetProcessorTickCount()
 
 #endif // JPH_PLATFORM_WINDOWS_UWP || (JPH_PLATFORM_WINDOWS && JPH_CPU_ARM)
 
+/// We measure the amount of ticks per second, this function resets the reference time point
+JPH_EXPORT void UpdateReferenceTime();
+
 /// Get the amount of ticks per second, note that this number will never be fully accurate as the amound of ticks per second may vary with CPU load, so this number is only to be used to give an indication of time for profiling purposes
 JPH_EXPORT uint64 GetProcessorTicksPerSecond();
 

+ 4 - 0
Jolt/RegisterTypes.cpp

@@ -7,6 +7,7 @@
 #include <Jolt/RegisterTypes.h>
 #include <Jolt/Core/Factory.h>
 #include <Jolt/Core/RTTI.h>
+#include <Jolt/Core/TickCounter.h>
 #include <Jolt/Physics/Collision/CollisionDispatch.h>
 #include <Jolt/Physics/Collision/Shape/TriangleShape.h>
 #include <Jolt/Physics/Collision/Shape/SphereShape.h>
@@ -89,6 +90,9 @@ void RegisterTypesInternal(uint64 inVersionID)
 
 	JPH_ASSERT(Factory::sInstance != nullptr, "Need to create a factory first!");
 
+	// Set the initial reference time
+	UpdateReferenceTime();
+
 	// Initialize dispatcher
 	CollisionDispatch::sInit();
 

+ 7 - 6
Samples/SamplesApp.cpp

@@ -2347,8 +2347,8 @@ void SamplesApp::StepPhysics(JobSystem *inJobSystem)
 		mTest->PrePhysicsUpdate(pre_update);
 	}
 
-	// Remember start tick
-	uint64 start_tick = GetProcessorTickCount();
+	// Remember start time
+	chrono::high_resolution_clock::time_point clock_start = chrono::high_resolution_clock::now();
 
 	// Step the world (with fixed frequency)
 	mPhysicsSystem->Update(delta_time, mCollisionSteps, mTempAllocator, inJobSystem);
@@ -2357,16 +2357,17 @@ void SamplesApp::StepPhysics(JobSystem *inJobSystem)
 #endif // JPH_DISABLE_TEMP_ALLOCATOR
 
 	// Accumulate time
-	mTotalTime += GetProcessorTickCount() - start_tick;
+	chrono::high_resolution_clock::time_point clock_end = chrono::high_resolution_clock::now();
+	chrono::microseconds duration = chrono::duration_cast<chrono::microseconds>(clock_end - clock_start);
+	mTotalTime += duration;
 	mStepNumber++;
 
 	// Print timing information
 	constexpr int cNumSteps = 60;
 	if (mStepNumber % cNumSteps == 0)
 	{
-		double us_per_step = double(mTotalTime / cNumSteps) / double(GetProcessorTicksPerSecond()) * 1.0e6;
-		Trace("Timing: %d, %.0f", mStepNumber / cNumSteps, us_per_step);
-		mTotalTime = 0;
+		Trace("Timing: %d, %d", mStepNumber / cNumSteps, mTotalTime.count() / cNumSteps);
+		mTotalTime = chrono::microseconds(0);
 	}
 
 #ifdef JPH_TRACK_BROADPHASE_STATS

+ 1 - 1
Samples/SamplesApp.h

@@ -222,5 +222,5 @@ private:
 
 	// Timing
 	uint					mStepNumber = 0;											// Which step number we're accumulating
-	uint64					mTotalTime = 0;												// How many tick we spent
+	chrono::microseconds	mTotalTime { 0 };											// How many nano seconds we spent simulating
 };

+ 5 - 5
TestFramework/Application/Application.cpp

@@ -80,7 +80,7 @@ Application::Application() :
 	}
 
 	// Get initial time
-	mLastUpdateTicks = GetProcessorTickCount();
+	mLastUpdateTime = chrono::high_resolution_clock::now();
 }
 
 // Destructor
@@ -163,10 +163,10 @@ void Application::Run()
 				}
 
 			// Calculate delta time
-			uint64 ticks = GetProcessorTickCount();
-			uint64 delta = ticks - mLastUpdateTicks;
-			mLastUpdateTicks = ticks;
-			float clock_delta_time = float(delta) / float(GetProcessorTicksPerSecond());
+			chrono::high_resolution_clock::time_point time = chrono::high_resolution_clock::now();
+			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;
 			mSingleStep = false;
 

+ 6 - 1
TestFramework/Application/Application.h

@@ -8,6 +8,11 @@
 #include <Input/Mouse.h>
 #include <Jolt/Core/Reference.h>
 
+// STL includes
+JPH_SUPPRESS_WARNINGS_STD_BEGIN
+#include <chrono>
+JPH_SUPPRESS_WARNINGS_STD_END
+
 class UIManager;
 class DebugUI;
 namespace JPH {
@@ -91,7 +96,7 @@ private:
 	/// Draw the frame rate counter
 	void						DrawFPS(float inDeltaTime);
 
-	uint64						mLastUpdateTicks;
+	chrono::high_resolution_clock::time_point mLastUpdateTime;
 	bool						mIsPaused = false;
 	bool						mSingleStep = false;
 	bool						mDebugRendererCleared = true;