MultithreadedTest.cpp 7.7 KB

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