PerformanceTest.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. // Jolt includes
  4. #include <Jolt.h>
  5. #include <RegisterTypes.h>
  6. #include <Core/TempAllocator.h>
  7. #include <Core/JobSystemThreadPool.h>
  8. #include <Physics/PhysicsSettings.h>
  9. #include <Physics/PhysicsSystem.h>
  10. #ifdef JPH_DEBUG_RENDERER
  11. #include <Renderer/DebugRendererRecorder.h>
  12. #include <Core/StreamWrapper.h>
  13. #endif // JPH_DEBUG_RENDERER
  14. // STL includes
  15. #include <iostream>
  16. #include <thread>
  17. #include <chrono>
  18. #include <memory>
  19. using namespace JPH;
  20. using namespace std;
  21. // Local includes
  22. #include "RagdollScene.h"
  23. #include "ConvexVsMeshScene.h"
  24. // Time step for physics
  25. constexpr float cDeltaTime = 1.0f / 60.0f;
  26. // Number of iterations to run the test
  27. constexpr uint cMaxIterations = 500;
  28. // Program entry point
  29. int main(int argc, char** argv)
  30. {
  31. // Parse command line parameters
  32. int specified_quality = -1;
  33. int specified_threads = -1;
  34. bool enable_profiler = false;
  35. bool enable_debug_renderer = false;
  36. unique_ptr<PerformanceTestScene> scene;
  37. for (int argidx = 1; argidx < argc; ++argidx)
  38. {
  39. const char *arg = argv[argidx];
  40. if (strncmp(arg, "-s=", 3) == 0)
  41. {
  42. // Parse scene
  43. if (strcmp(arg + 3, "Ragdoll") == 0)
  44. scene = unique_ptr<PerformanceTestScene>(new RagdollScene);
  45. else if (strcmp(arg + 3, "ConvexVsMesh") == 0)
  46. scene = unique_ptr<PerformanceTestScene>(new ConvexVsMeshScene);
  47. else
  48. {
  49. cerr << "Invalid scene" << endl;
  50. return 1;
  51. }
  52. }
  53. else if (strncmp(arg, "-q=", 3) == 0)
  54. {
  55. // Parse quality
  56. if (strcmp(arg + 3, "Discrete") == 0)
  57. specified_quality = 0;
  58. else if (strcmp(arg + 3, "LinearCast") == 0)
  59. specified_quality = 1;
  60. else
  61. {
  62. cerr << "Invalid quality" << endl;
  63. return 1;
  64. }
  65. }
  66. else if (strncmp(arg, "-t=", 3) == 0)
  67. {
  68. // Parse threads
  69. specified_threads = atoi(arg + 3);
  70. }
  71. else if (strcmp(arg, "-p") == 0)
  72. {
  73. enable_profiler = true;
  74. }
  75. else if (strcmp(arg, "-r") == 0)
  76. {
  77. enable_debug_renderer = true;
  78. }
  79. else if (strcmp(arg, "-h") == 0)
  80. {
  81. // Print usage
  82. cerr << "Usage: PerformanceTest [-s=<scene>] [-q=<quality>] [-t=<threads>] [-p] [-r]" << endl
  83. << "-s: Select scene (Ragdoll, ConvexVsMesh)" << endl
  84. << "-q: Test only with specified quality (Discrete, LinearCast)" << endl
  85. << "-t: Test only with N threads" << endl
  86. << "-p: Write out profiles" << endl
  87. << "-r: Record debug renderer output for JoltViewer" << endl;
  88. return 0;
  89. }
  90. }
  91. // Register all Jolt physics types
  92. RegisterTypes();
  93. // Create temp allocator
  94. TempAllocatorImpl temp_allocator(10 * 1024 * 1024);
  95. // Load the scene
  96. if (scene == nullptr)
  97. scene = unique_ptr<PerformanceTestScene>(new RagdollScene);
  98. if (!scene->Load())
  99. return 1;
  100. // Output scene we're running
  101. cout << "Running scene: " << scene->GetName() << endl;
  102. // Create mapping table from object layer to broadphase layer
  103. ObjectToBroadPhaseLayer object_to_broadphase = GetObjectToBroadPhaseLayer();
  104. // Start profiling this thread
  105. JPH_PROFILE_THREAD_START("Main");
  106. // Trace header
  107. cout << "Motion Quality, Thread Count, Steps / Second, Hash" << endl;
  108. // Iterate motion qualities
  109. for (uint mq = 0; mq < 2; ++mq)
  110. {
  111. // Skip quality if another was specified
  112. if (specified_quality != -1 && mq != (uint)specified_quality)
  113. continue;
  114. // Determine motion quality
  115. EMotionQuality motion_quality = mq == 0? EMotionQuality::Discrete : EMotionQuality::LinearCast;
  116. string motion_quality_str = mq == 0? "Discrete" : "LinearCast";
  117. // Determine which thread counts to test
  118. vector<uint> thread_permutations;
  119. if (specified_threads > 0)
  120. thread_permutations.push_back((uint)specified_threads - 1);
  121. else
  122. for (uint num_threads = 0; num_threads < thread::hardware_concurrency(); ++num_threads)
  123. thread_permutations.push_back(num_threads);
  124. // Test thread permutations
  125. for (uint num_threads : thread_permutations)
  126. {
  127. // Create job system with desired number of threads
  128. JobSystemThreadPool job_system(cMaxPhysicsJobs, cMaxPhysicsBarriers, num_threads);
  129. // Create physics system
  130. PhysicsSystem physics_system;
  131. physics_system.Init(10240, 0, 65536, 10240, object_to_broadphase, BroadPhaseCanCollide, ObjectCanCollide);
  132. // Start test scene
  133. scene->StartTest(physics_system, motion_quality);
  134. #ifdef JPH_DEBUG_RENDERER
  135. // Open output
  136. ofstream renderer_file;
  137. if (enable_debug_renderer)
  138. renderer_file.open(("performance_test_" + ToLower(motion_quality_str) + "_th" + ConvertToString(num_threads + 1) + ".JoltRecording").c_str(), ofstream::out | ofstream::binary | ofstream::trunc);
  139. StreamOutWrapper renderer_stream(renderer_file);
  140. DebugRendererRecorder renderer(renderer_stream);
  141. #endif // JPH_DEBUG_RENDERER
  142. chrono::nanoseconds total_duration(0);
  143. // Step the world for a fixed amount of iterations
  144. for (uint iterations = 0; iterations < cMaxIterations; ++iterations)
  145. {
  146. JPH_PROFILE_NEXTFRAME();
  147. // Start measuring
  148. chrono::high_resolution_clock::time_point clock_start = chrono::high_resolution_clock::now();
  149. // Do a physics step
  150. physics_system.Update(cDeltaTime, 1, 1, &temp_allocator, &job_system);
  151. // Stop measuring
  152. chrono::high_resolution_clock::time_point clock_end = chrono::high_resolution_clock::now();
  153. total_duration += chrono::duration_cast<chrono::nanoseconds>(clock_end - clock_start);
  154. #ifdef JPH_DEBUG_RENDERER
  155. if (enable_debug_renderer)
  156. {
  157. // Draw the state of the world
  158. BodyManager::DrawSettings settings;
  159. physics_system.DrawBodies(settings, &renderer);
  160. // Mark end of frame
  161. renderer.EndFrame();
  162. }
  163. #endif // JPH_DEBUG_RENDERER
  164. // Dump profile information every 100 iterations
  165. if (enable_profiler && iterations % 100 == 0)
  166. {
  167. JPH_PROFILE_DUMP(ToLower(motion_quality_str) + "_th" + ConvertToString(num_threads + 1) + "_it" + ConvertToString(iterations));
  168. }
  169. }
  170. // Calculate hash of all positions and rotations of the bodies
  171. size_t hash = 0;
  172. BodyInterface &bi = physics_system.GetBodyInterfaceNoLock();
  173. BodyIDVector body_ids;
  174. physics_system.GetBodies(body_ids);
  175. for (BodyID id : body_ids)
  176. {
  177. Vec3 pos = bi.GetPosition(id);
  178. Quat rot = bi.GetRotation(id);
  179. hash_combine(hash, pos.GetX(), pos.GetY(), pos.GetZ(), rot.GetX(), rot.GetY(), rot.GetZ(), rot.GetW());
  180. }
  181. // Stop test scene
  182. scene->StopTest(physics_system);
  183. // Trace stat line
  184. cout << motion_quality_str << ", " << num_threads + 1 << ", " << double(cMaxIterations) / (1.0e-9 * total_duration.count()) << ", " << hash << endl;
  185. }
  186. }
  187. // End profiling this thread
  188. JPH_PROFILE_THREAD_END();
  189. return 0;
  190. }