MultithreadedTest.cpp 7.2 KB

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