MultithreadedTest.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <Samples.h>
  5. #include <Tests/General/MultithreadedTest.h>
  6. #include <Jolt/Physics/Collision/Shape/BoxShape.h>
  7. #include <Jolt/Physics/Collision/RayCast.h>
  8. #include <Jolt/Physics/Collision/CastResult.h>
  9. #include <Jolt/Physics/Body/BodyCreationSettings.h>
  10. #include <Jolt/Skeleton/Skeleton.h>
  11. #include <Jolt/Skeleton/SkeletalAnimation.h>
  12. #include <Jolt/Skeleton/SkeletonPose.h>
  13. #include <Jolt/Physics/Ragdoll/Ragdoll.h>
  14. #include <Jolt/ObjectStream/ObjectStreamIn.h>
  15. #include <Layers.h>
  16. #include <Utils/RagdollLoader.h>
  17. #include <Utils/Log.h>
  18. #include <Utils/AssetStream.h>
  19. #include <Renderer/DebugRendererImp.h>
  20. JPH_SUPPRESS_WARNINGS_STD_BEGIN
  21. #include <chrono>
  22. JPH_SUPPRESS_WARNINGS_STD_END
  23. JPH_IMPLEMENT_RTTI_VIRTUAL(MultithreadedTest)
  24. {
  25. JPH_ADD_BASE_CLASS(MultithreadedTest, Test)
  26. }
  27. MultithreadedTest::~MultithreadedTest()
  28. {
  29. // Quit the threads
  30. mIsQuitting = true;
  31. mBoxSpawnerThread.join();
  32. mRagdollSpawnerThread.join();
  33. mCasterThread.join();
  34. }
  35. void MultithreadedTest::Initialize()
  36. {
  37. // Floor
  38. CreateFloor();
  39. // Start threads
  40. mBoxSpawnerThread = thread([this]() { BoxSpawner(); });
  41. mRagdollSpawnerThread = thread([this]() { RagdollSpawner(); });
  42. mCasterThread = thread([this]() { CasterMain(); });
  43. }
  44. void MultithreadedTest::Execute(default_random_engine &ioRandom, const char *inName, function<void()> inFunction)
  45. {
  46. uniform_real_distribution<float> chance(0, 1);
  47. if (chance(ioRandom) < 0.5f)
  48. {
  49. // Execute as a job and wait for it
  50. JobHandle handle = mJobSystem->CreateJob(inName, Color::sGreen, inFunction);
  51. while (!handle.IsDone())
  52. this_thread::sleep_for(1ms);
  53. }
  54. else
  55. {
  56. // Execute in this separate thread (not part of the job system)
  57. JPH_PROFILE(inName);
  58. inFunction();
  59. }
  60. }
  61. void MultithreadedTest::BoxSpawner()
  62. {
  63. JPH_PROFILE_THREAD_START("BoxSpawner");
  64. #ifdef JPH_DEBUG
  65. const int cMaxObjects = 100;
  66. #else
  67. const int cMaxObjects = 1000;
  68. #endif
  69. default_random_engine random;
  70. Array<BodyID> bodies;
  71. while (!mIsQuitting)
  72. {
  73. // Ensure there are enough objects at all times
  74. if (bodies.size() < cMaxObjects)
  75. {
  76. BodyID body_id;
  77. Execute(random, "AddBody", [this, &body_id, &random]() {
  78. uniform_real_distribution<float> from_y(0, 10);
  79. uniform_real_distribution<float> from_xz(-5, 5);
  80. RVec3 position(from_xz(random), 1.0f + from_y(random), from_xz(random));
  81. Quat orientation = Quat::sRandom(random);
  82. Vec3 velocity = Vec3::sRandom(random);
  83. Body &body = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3(0.5f, 0.2f, 0.3f)), position, orientation, EMotionType::Dynamic, Layers::MOVING));
  84. body.SetLinearVelocity(velocity);
  85. body_id = body.GetID();
  86. mBodyInterface->AddBody(body_id, EActivation::Activate);
  87. });
  88. Execute(random, "Remove/AddBody", [this, body_id]() {
  89. // Undo/redo add to trigger more race conditions
  90. mBodyInterface->RemoveBody(body_id);
  91. mBodyInterface->AddBody(body_id, EActivation::Activate);
  92. });
  93. bodies.push_back(body_id);
  94. }
  95. uniform_real_distribution<float> chance(0, 1);
  96. if (bodies.size() > 0 && chance(random) < 0.5f)
  97. {
  98. // Pick random body
  99. uniform_int_distribution<size_t> element(0, bodies.size() - 1);
  100. size_t index = element(random);
  101. BodyID body_id = bodies[index];
  102. bodies.erase(bodies.begin() + index);
  103. Execute(random, "Remove/DestroyBody", [this, body_id]() {
  104. // Remove it
  105. mBodyInterface->RemoveBody(body_id);
  106. mBodyInterface->DestroyBody(body_id);
  107. });
  108. }
  109. this_thread::sleep_for(1ms);
  110. }
  111. JPH_PROFILE_THREAD_END();
  112. }
  113. void MultithreadedTest::RagdollSpawner()
  114. {
  115. JPH_PROFILE_THREAD_START("RagdollSpawner");
  116. #ifdef JPH_DEBUG
  117. const int cMaxRagdolls = 10;
  118. #else
  119. const int cMaxRagdolls = 50;
  120. #endif
  121. #ifdef JPH_OBJECT_STREAM
  122. // Load ragdoll
  123. Ref<RagdollSettings> ragdoll_settings = RagdollLoader::sLoad("Human.tof", EMotionType::Dynamic);
  124. if (ragdoll_settings == nullptr)
  125. FatalError("Could not load ragdoll");
  126. #else
  127. // Create a ragdoll from code
  128. Ref<RagdollSettings> ragdoll_settings = RagdollLoader::sCreate();
  129. #endif // JPH_OBJECT_STREAM
  130. // Create pose
  131. SkeletonPose ragdoll_pose;
  132. ragdoll_pose.SetSkeleton(ragdoll_settings->GetSkeleton());
  133. {
  134. #ifdef JPH_OBJECT_STREAM
  135. Ref<SkeletalAnimation> animation;
  136. AssetStream stream("Human/dead_pose1.tof", std::ios::in);
  137. if (!ObjectStreamIn::sReadObject(stream.Get(), animation))
  138. FatalError("Could not open animation");
  139. animation->Sample(0.0f, ragdoll_pose);
  140. #else
  141. Ref<Ragdoll> temp_ragdoll = ragdoll_settings->CreateRagdoll(0, 0, mPhysicsSystem);
  142. temp_ragdoll->GetPose(ragdoll_pose);
  143. ragdoll_pose.CalculateJointStates();
  144. #endif // JPH_OBJECT_STREAM
  145. }
  146. default_random_engine random;
  147. uniform_real_distribution<float> from_y(0, 10);
  148. uniform_real_distribution<float> from_xz(-5, 5);
  149. CollisionGroup::GroupID group_id = 1;
  150. Array<Ref<Ragdoll>> ragdolls;
  151. while (!mIsQuitting)
  152. {
  153. // Ensure there are enough objects at all times
  154. if (ragdolls.size() < cMaxRagdolls)
  155. {
  156. // Create ragdoll
  157. Ref<Ragdoll> ragdoll = ragdoll_settings->CreateRagdoll(group_id++, 0, mPhysicsSystem);
  158. // Override root
  159. SkeletonPose::JointState &root = ragdoll_pose.GetJoint(0);
  160. root.mRotation = Quat::sRandom(random);
  161. ragdoll_pose.SetRootOffset(RVec3(from_xz(random), 1.0f + from_y(random), from_xz(random)));
  162. ragdoll_pose.CalculateJointMatrices();
  163. // Drive to pose
  164. ragdoll->SetPose(ragdoll_pose);
  165. ragdoll->DriveToPoseUsingMotors(ragdoll_pose);
  166. Execute(random, "Activate", [ragdoll]() {
  167. // Activate the ragdoll
  168. ragdoll->AddToPhysicsSystem(EActivation::Activate);
  169. });
  170. Execute(random, "Deactivate/Activate", [ragdoll]() {
  171. // Undo/redo add to trigger more race conditions
  172. ragdoll->RemoveFromPhysicsSystem();
  173. ragdoll->AddToPhysicsSystem(EActivation::Activate);
  174. });
  175. ragdolls.push_back(ragdoll);
  176. }
  177. uniform_real_distribution<float> chance(0, 1);
  178. if (ragdolls.size() > 0 && chance(random) < 0.1f)
  179. {
  180. // Pick random body
  181. uniform_int_distribution<size_t> element(0, ragdolls.size() - 1);
  182. size_t index = element(random);
  183. Ref<Ragdoll> ragdoll = ragdolls[index];
  184. ragdolls.erase(ragdolls.begin() + index);
  185. Execute(random, "Deactivate", [ragdoll]() {
  186. // Deactivate it
  187. ragdoll->RemoveFromPhysicsSystem();
  188. });
  189. }
  190. this_thread::sleep_for(1ms);
  191. }
  192. for (Ragdoll *r : ragdolls)
  193. r->RemoveFromPhysicsSystem();
  194. JPH_PROFILE_THREAD_END();
  195. }
  196. void MultithreadedTest::CasterMain()
  197. {
  198. JPH_PROFILE_THREAD_START("CasterMain");
  199. default_random_engine random;
  200. Array<BodyID> bodies;
  201. while (!mIsQuitting)
  202. {
  203. Execute(random, "CastRay", [this, &random]() {
  204. // Cast a random ray
  205. uniform_real_distribution<float> from_y(0, 10);
  206. uniform_real_distribution<float> from_xz(-5, 5);
  207. RVec3 from(from_xz(random), from_y(random), from_xz(random));
  208. RVec3 to(from_xz(random), from_y(random), from_xz(random));
  209. RRayCast ray { from, Vec3(to - from) };
  210. RayCastResult hit;
  211. if (mPhysicsSystem->GetNarrowPhaseQuery().CastRay(ray, hit, SpecifiedBroadPhaseLayerFilter(BroadPhaseLayers::MOVING), SpecifiedObjectLayerFilter(Layers::MOVING)))
  212. {
  213. // Draw hit position
  214. RVec3 hit_position_world = ray.GetPointOnRay(hit.mFraction);
  215. mDebugRenderer->DrawMarker(hit_position_world, Color::sYellow, 0.2f);
  216. BodyLockRead lock(mPhysicsSystem->GetBodyLockInterface(), hit.mBodyID);
  217. if (lock.SucceededAndIsInBroadPhase())
  218. {
  219. // Draw normal
  220. const Body &hit_body = lock.GetBody();
  221. RMat44 inv_com = hit_body.GetInverseCenterOfMassTransform();
  222. Vec3 normal = inv_com.Multiply3x3Transposed(hit_body.GetShape()->GetSurfaceNormal(hit.mSubShapeID2, Vec3(inv_com * hit_position_world))).Normalized();
  223. mDebugRenderer->DrawArrow(hit_position_world, hit_position_world + normal, Color::sGreen, 0.1f);
  224. }
  225. }
  226. });
  227. }
  228. JPH_PROFILE_THREAD_END();
  229. }