IslandBuilder.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <Jolt/Jolt.h>
  5. #include <Jolt/Physics/IslandBuilder.h>
  6. #include <Jolt/Physics/Body/Body.h>
  7. #include <Jolt/Physics/PhysicsSettings.h>
  8. #include <Jolt/Core/Profiler.h>
  9. #include <Jolt/Core/Atomics.h>
  10. #include <Jolt/Core/TempAllocator.h>
  11. #include <Jolt/Core/QuickSort.h>
  12. JPH_NAMESPACE_BEGIN
  13. IslandBuilder::~IslandBuilder()
  14. {
  15. JPH_ASSERT(mConstraintLinks == nullptr);
  16. JPH_ASSERT(mContactLinks == nullptr);
  17. JPH_ASSERT(mBodyIslands == nullptr);
  18. JPH_ASSERT(mBodyIslandEnds == nullptr);
  19. JPH_ASSERT(mConstraintIslands == nullptr);
  20. JPH_ASSERT(mConstraintIslandEnds == nullptr);
  21. JPH_ASSERT(mContactIslands == nullptr);
  22. JPH_ASSERT(mContactIslandEnds == nullptr);
  23. JPH_ASSERT(mIslandsSorted == nullptr);
  24. delete [] mBodyLinks;
  25. }
  26. void IslandBuilder::Init(uint32 inMaxActiveBodies)
  27. {
  28. mMaxActiveBodies = inMaxActiveBodies;
  29. // Link each body to itself, BuildBodyIslands() will restore this so that we don't need to do this each step
  30. JPH_ASSERT(mBodyLinks == nullptr);
  31. mBodyLinks = new BodyLink [mMaxActiveBodies];
  32. for (uint32 i = 0; i < mMaxActiveBodies; ++i)
  33. mBodyLinks[i].mLinkedTo.store(i, memory_order_relaxed);
  34. }
  35. void IslandBuilder::PrepareContactConstraints(uint32 inMaxContacts, TempAllocator *inTempAllocator)
  36. {
  37. JPH_PROFILE_FUNCTION();
  38. // Need to call Init first
  39. JPH_ASSERT(mBodyLinks != nullptr);
  40. // Check that the builder has been reset
  41. JPH_ASSERT(mNumContacts == 0);
  42. JPH_ASSERT(mNumIslands == 0);
  43. // Create contact link buffer, not initialized so each contact needs to be explicitly set
  44. JPH_ASSERT(mContactLinks == nullptr);
  45. mContactLinks = (uint32 *)inTempAllocator->Allocate(inMaxContacts * sizeof(uint32));
  46. mMaxContacts = inMaxContacts;
  47. #ifdef JPH_VALIDATE_ISLAND_BUILDER
  48. // Create validation structures
  49. JPH_ASSERT(mLinkValidation == nullptr);
  50. mLinkValidation = (LinkValidation *)inTempAllocator->Allocate(inMaxContacts * sizeof(LinkValidation));
  51. mNumLinkValidation = 0;
  52. #endif
  53. }
  54. void IslandBuilder::PrepareNonContactConstraints(uint32 inNumConstraints, TempAllocator *inTempAllocator)
  55. {
  56. JPH_PROFILE_FUNCTION();
  57. // Need to call Init first
  58. JPH_ASSERT(mBodyLinks != nullptr);
  59. // Check that the builder has been reset
  60. JPH_ASSERT(mNumIslands == 0);
  61. // Store number of constraints
  62. mNumConstraints = inNumConstraints;
  63. // Create constraint link buffer, not initialized so each constraint needs to be explicitly set
  64. JPH_ASSERT(mConstraintLinks == nullptr);
  65. mConstraintLinks = (uint32 *)inTempAllocator->Allocate(inNumConstraints * sizeof(uint32));
  66. }
  67. uint32 IslandBuilder::GetLowestBodyIndex(uint32 inActiveBodyIndex) const
  68. {
  69. uint32 index = inActiveBodyIndex;
  70. for (;;)
  71. {
  72. uint32 link_to = mBodyLinks[index].mLinkedTo.load(memory_order_relaxed);
  73. if (link_to == index)
  74. break;
  75. index = link_to;
  76. }
  77. return index;
  78. }
  79. void IslandBuilder::LinkBodies(uint32 inFirst, uint32 inSecond)
  80. {
  81. JPH_PROFILE_FUNCTION();
  82. // Both need to be active, we don't want to create an island with static objects
  83. if (inFirst >= mMaxActiveBodies || inSecond >= mMaxActiveBodies)
  84. return;
  85. #ifdef JPH_VALIDATE_ISLAND_BUILDER
  86. // Add link to the validation list
  87. if (mNumLinkValidation < uint32(mMaxContacts))
  88. mLinkValidation[mNumLinkValidation++] = { inFirst, inSecond };
  89. else
  90. JPH_ASSERT(false, "Out of links");
  91. #endif
  92. // Start the algorithm with the two bodies
  93. uint32 first_link_to = inFirst;
  94. uint32 second_link_to = inSecond;
  95. for (;;)
  96. {
  97. // Follow the chain until we get to the body with lowest index
  98. // If the swap compare below fails, we'll keep searching from the lowest index for the new lowest index
  99. first_link_to = GetLowestBodyIndex(first_link_to);
  100. second_link_to = GetLowestBodyIndex(second_link_to);
  101. // If the targets are the same, the bodies are already connected
  102. if (first_link_to != second_link_to)
  103. {
  104. // We always link the highest to the lowest
  105. if (first_link_to < second_link_to)
  106. {
  107. // Attempt to link the second to the first
  108. // Since we found this body to be at the end of the chain it must point to itself, and if it
  109. // doesn't it has been reparented and we need to retry the algorithm
  110. if (!mBodyLinks[second_link_to].mLinkedTo.compare_exchange_weak(second_link_to, first_link_to, memory_order_relaxed))
  111. continue;
  112. }
  113. else
  114. {
  115. // Attempt to link the first to the second
  116. // Since we found this body to be at the end of the chain it must point to itself, and if it
  117. // doesn't it has been reparented and we need to retry the algorithm
  118. if (!mBodyLinks[first_link_to].mLinkedTo.compare_exchange_weak(first_link_to, second_link_to, memory_order_relaxed))
  119. continue;
  120. }
  121. }
  122. // Linking succeeded!
  123. // Chains of bodies can become really long, resulting in an O(N) loop to find the lowest body index
  124. // to prevent this we attempt to update the link of the bodies that were passed in to directly point
  125. // to the lowest index that we found. If the value became lower than our lowest link, some other
  126. // thread must have relinked these bodies in the mean time so we won't update the value.
  127. uint32 lowest_link_to = min(first_link_to, second_link_to);
  128. AtomicMin(mBodyLinks[inFirst].mLinkedTo, lowest_link_to, memory_order_relaxed);
  129. AtomicMin(mBodyLinks[inSecond].mLinkedTo, lowest_link_to, memory_order_relaxed);
  130. break;
  131. }
  132. }
  133. void IslandBuilder::LinkConstraint(uint32 inConstraintIndex, uint32 inFirst, uint32 inSecond)
  134. {
  135. LinkBodies(inFirst, inSecond);
  136. JPH_ASSERT(inConstraintIndex < mNumConstraints);
  137. uint32 min_value = min(inFirst, inSecond); // Use fact that invalid index is 0xffffffff, we want the active body of two
  138. JPH_ASSERT(min_value != Body::cInactiveIndex); // At least one of the bodies must be active
  139. mConstraintLinks[inConstraintIndex] = min_value;
  140. }
  141. void IslandBuilder::LinkContact(uint32 inContactIndex, uint32 inFirst, uint32 inSecond)
  142. {
  143. JPH_ASSERT(inContactIndex < mMaxContacts);
  144. mContactLinks[inContactIndex] = min(inFirst, inSecond); // Use fact that invalid index is 0xffffffff, we want the active body of two
  145. }
  146. #ifdef JPH_VALIDATE_ISLAND_BUILDER
  147. void IslandBuilder::ValidateIslands(uint32 inNumActiveBodies) const
  148. {
  149. JPH_PROFILE_FUNCTION();
  150. // Go through all links so far
  151. for (uint32 i = 0; i < mNumLinkValidation; ++i)
  152. {
  153. // If the bodies in this link ended up in different groups we have a problem
  154. if (mBodyLinks[mLinkValidation[i].mFirst].mIslandIndex != mBodyLinks[mLinkValidation[i].mSecond].mIslandIndex)
  155. {
  156. Trace("Fail: %u, %u", mLinkValidation[i].mFirst, mLinkValidation[i].mSecond);
  157. Trace("Num Active: %u", inNumActiveBodies);
  158. for (uint32 j = 0; j < mNumLinkValidation; ++j)
  159. Trace("builder.Link(%u, %u);", mLinkValidation[j].mFirst, mLinkValidation[j].mSecond);
  160. IslandBuilder tmp;
  161. tmp.Init(inNumActiveBodies);
  162. for (uint32 j = 0; j < mNumLinkValidation; ++j)
  163. {
  164. Trace("Link %u -> %u", mLinkValidation[j].mFirst, mLinkValidation[j].mSecond);
  165. tmp.LinkBodies(mLinkValidation[j].mFirst, mLinkValidation[j].mSecond);
  166. for (uint32 t = 0; t < inNumActiveBodies; ++t)
  167. Trace("%u -> %u", t, (uint32)tmp.mBodyLinks[t].mLinkedTo);
  168. }
  169. JPH_ASSERT(false, "IslandBuilder validation failed");
  170. }
  171. }
  172. }
  173. #endif
  174. void IslandBuilder::BuildBodyIslands(const BodyID *inActiveBodies, uint32 inNumActiveBodies, TempAllocator *inTempAllocator)
  175. {
  176. JPH_PROFILE_FUNCTION();
  177. // Store the amount of active bodies
  178. mNumActiveBodies = inNumActiveBodies;
  179. // Create output arrays for body ID's, don't call constructors
  180. JPH_ASSERT(mBodyIslands == nullptr);
  181. mBodyIslands = (BodyID *)inTempAllocator->Allocate(inNumActiveBodies * sizeof(BodyID));
  182. // Create output array for start index of each island. At this point we don't know how many islands there will be, but we know it cannot be more than inNumActiveBodies.
  183. // Note: We allocate 1 extra entry because we always increment the count of the next island.
  184. uint32 *body_island_starts = (uint32 *)inTempAllocator->Allocate((inNumActiveBodies + 1) * sizeof(uint32));
  185. // First island always starts at 0
  186. body_island_starts[0] = 0;
  187. // Calculate island index for all bodies
  188. JPH_ASSERT(mNumIslands == 0);
  189. for (uint32 i = 0; i < inNumActiveBodies; ++i)
  190. {
  191. BodyLink &link = mBodyLinks[i];
  192. uint32 s = link.mLinkedTo.load(memory_order_relaxed);
  193. if (s != i)
  194. {
  195. // Links to another body, take island index from other body (this must have been filled in already since we're looping from low to high)
  196. JPH_ASSERT(s < uint32(i));
  197. uint32 island_index = mBodyLinks[s].mIslandIndex;
  198. link.mIslandIndex = island_index;
  199. // Increment the start of the next island
  200. body_island_starts[island_index + 1]++;
  201. }
  202. else
  203. {
  204. // Does not link to other body, this is the start of a new island
  205. link.mIslandIndex = mNumIslands;
  206. ++mNumIslands;
  207. // Set the start of the next island to 1
  208. body_island_starts[mNumIslands] = 1;
  209. }
  210. }
  211. #ifdef JPH_VALIDATE_ISLAND_BUILDER
  212. ValidateIslands(inNumActiveBodies);
  213. #endif
  214. // Make the start array absolute (so far we only counted)
  215. for (uint32 island = 1; island < mNumIslands; ++island)
  216. body_island_starts[island] += body_island_starts[island - 1];
  217. // Convert the to a linear list grouped by island
  218. for (uint32 i = 0; i < inNumActiveBodies; ++i)
  219. {
  220. BodyLink &link = mBodyLinks[i];
  221. // Copy the body to the correct location in the array and increment it
  222. uint32 &start = body_island_starts[link.mIslandIndex];
  223. mBodyIslands[start] = inActiveBodies[i];
  224. start++;
  225. // Reset linked to field for the next update
  226. link.mLinkedTo.store(i, memory_order_relaxed);
  227. }
  228. // We should now have a full array
  229. JPH_ASSERT(mNumIslands == 0 || body_island_starts[mNumIslands - 1] == inNumActiveBodies);
  230. // We've incremented all body indices so that they now point at the end instead of the starts
  231. JPH_ASSERT(mBodyIslandEnds == nullptr);
  232. mBodyIslandEnds = body_island_starts;
  233. }
  234. void IslandBuilder::BuildConstraintIslands(const uint32 *inConstraintToBody, uint32 inNumConstraints, uint32 *&outConstraints, uint32 *&outConstraintsEnd, TempAllocator *inTempAllocator) const
  235. {
  236. JPH_PROFILE_FUNCTION();
  237. // Check if there's anything to do
  238. if (inNumConstraints == 0)
  239. return;
  240. // Create output arrays for constraints
  241. // Note: For the end indices we allocate 1 extra entry so we don't have to do an if in the inner loop
  242. uint32 *constraints = (uint32 *)inTempAllocator->Allocate(inNumConstraints * sizeof(uint32));
  243. uint32 *constraint_ends = (uint32 *)inTempAllocator->Allocate((mNumIslands + 1) * sizeof(uint32));
  244. // Reset sizes
  245. for (uint32 island = 0; island < mNumIslands; ++island)
  246. constraint_ends[island] = 0;
  247. // Loop over array and increment start relative position for the next island
  248. for (uint32 constraint = 0; constraint < inNumConstraints; ++constraint)
  249. {
  250. uint32 body_idx = inConstraintToBody[constraint];
  251. uint32 next_island_idx = mBodyLinks[body_idx].mIslandIndex + 1;
  252. JPH_ASSERT(next_island_idx <= mNumIslands);
  253. constraint_ends[next_island_idx]++;
  254. }
  255. // Make start positions absolute
  256. for (uint32 island = 1; island < mNumIslands; ++island)
  257. constraint_ends[island] += constraint_ends[island - 1];
  258. // Loop over array and collect constraints
  259. for (uint32 constraint = 0; constraint < inNumConstraints; ++constraint)
  260. {
  261. uint32 body_idx = inConstraintToBody[constraint];
  262. uint32 island_idx = mBodyLinks[body_idx].mIslandIndex;
  263. constraints[constraint_ends[island_idx]++] = constraint;
  264. }
  265. JPH_ASSERT(outConstraints == nullptr);
  266. outConstraints = constraints;
  267. JPH_ASSERT(outConstraintsEnd == nullptr);
  268. outConstraintsEnd = constraint_ends;
  269. }
  270. void IslandBuilder::SortIslands(TempAllocator *inTempAllocator)
  271. {
  272. JPH_PROFILE_FUNCTION();
  273. if (mNumContacts > 0 || mNumConstraints > 0)
  274. {
  275. // Allocate mapping table
  276. JPH_ASSERT(mIslandsSorted == nullptr);
  277. mIslandsSorted = (uint32 *)inTempAllocator->Allocate(mNumIslands * sizeof(uint32));
  278. // Initialize index
  279. for (uint32 island = 0; island < mNumIslands; ++island)
  280. mIslandsSorted[island] = island;
  281. // Determine the sum of contact constraints / constraints per island
  282. uint32 *num_constraints = (uint32 *)inTempAllocator->Allocate(mNumIslands * sizeof(uint32));
  283. if (mNumContacts > 0 && mNumConstraints > 0)
  284. {
  285. num_constraints[0] = mConstraintIslandEnds[0] + mContactIslandEnds[0];
  286. for (uint32 island = 1; island < mNumIslands; ++island)
  287. num_constraints[island] = mConstraintIslandEnds[island] - mConstraintIslandEnds[island - 1]
  288. + mContactIslandEnds[island] - mContactIslandEnds[island - 1];
  289. }
  290. else if (mNumContacts > 0)
  291. {
  292. num_constraints[0] = mContactIslandEnds[0];
  293. for (uint32 island = 1; island < mNumIslands; ++island)
  294. num_constraints[island] = mContactIslandEnds[island] - mContactIslandEnds[island - 1];
  295. }
  296. else
  297. {
  298. num_constraints[0] = mConstraintIslandEnds[0];
  299. for (uint32 island = 1; island < mNumIslands; ++island)
  300. num_constraints[island] = mConstraintIslandEnds[island] - mConstraintIslandEnds[island - 1];
  301. }
  302. // Sort so the biggest islands go first, this means that the jobs that take longest will be running
  303. // first which improves the chance that all jobs finish at the same time.
  304. QuickSort(mIslandsSorted, mIslandsSorted + mNumIslands, [num_constraints](uint32 inLHS, uint32 inRHS) {
  305. return num_constraints[inLHS] > num_constraints[inRHS];
  306. });
  307. inTempAllocator->Free(num_constraints, mNumIslands * sizeof(uint32));
  308. }
  309. }
  310. void IslandBuilder::Finalize(const BodyID *inActiveBodies, uint32 inNumActiveBodies, uint32 inNumContacts, TempAllocator *inTempAllocator)
  311. {
  312. JPH_PROFILE_FUNCTION();
  313. mNumContacts = inNumContacts;
  314. BuildBodyIslands(inActiveBodies, inNumActiveBodies, inTempAllocator);
  315. BuildConstraintIslands(mConstraintLinks, mNumConstraints, mConstraintIslands, mConstraintIslandEnds, inTempAllocator);
  316. BuildConstraintIslands(mContactLinks, mNumContacts, mContactIslands, mContactIslandEnds, inTempAllocator);
  317. SortIslands(inTempAllocator);
  318. }
  319. void IslandBuilder::GetBodiesInIsland(uint32 inIslandIndex, BodyID *&outBodiesBegin, BodyID *&outBodiesEnd) const
  320. {
  321. JPH_ASSERT(inIslandIndex < mNumIslands);
  322. uint32 sorted_index = mIslandsSorted != nullptr? mIslandsSorted[inIslandIndex] : inIslandIndex;
  323. outBodiesBegin = sorted_index > 0? mBodyIslands + mBodyIslandEnds[sorted_index - 1] : mBodyIslands;
  324. outBodiesEnd = mBodyIslands + mBodyIslandEnds[sorted_index];
  325. }
  326. bool IslandBuilder::GetConstraintsInIsland(uint32 inIslandIndex, uint32 *&outConstraintsBegin, uint32 *&outConstraintsEnd) const
  327. {
  328. JPH_ASSERT(inIslandIndex < mNumIslands);
  329. if (mNumConstraints == 0)
  330. {
  331. outConstraintsBegin = nullptr;
  332. outConstraintsEnd = nullptr;
  333. return false;
  334. }
  335. else
  336. {
  337. uint32 sorted_index = mIslandsSorted[inIslandIndex];
  338. outConstraintsBegin = sorted_index > 0? mConstraintIslands + mConstraintIslandEnds[sorted_index - 1] : mConstraintIslands;
  339. outConstraintsEnd = mConstraintIslands + mConstraintIslandEnds[sorted_index];
  340. return outConstraintsBegin != outConstraintsEnd;
  341. }
  342. }
  343. bool IslandBuilder::GetContactsInIsland(uint32 inIslandIndex, uint32 *&outContactsBegin, uint32 *&outContactsEnd) const
  344. {
  345. JPH_ASSERT(inIslandIndex < mNumIslands);
  346. if (mNumContacts == 0)
  347. {
  348. outContactsBegin = nullptr;
  349. outContactsEnd = nullptr;
  350. return false;
  351. }
  352. else
  353. {
  354. uint32 sorted_index = mIslandsSorted[inIslandIndex];
  355. outContactsBegin = sorted_index > 0? mContactIslands + mContactIslandEnds[sorted_index - 1] : mContactIslands;
  356. outContactsEnd = mContactIslands + mContactIslandEnds[sorted_index];
  357. return outContactsBegin != outContactsEnd;
  358. }
  359. }
  360. void IslandBuilder::ResetIslands(TempAllocator *inTempAllocator)
  361. {
  362. JPH_PROFILE_FUNCTION();
  363. if (mIslandsSorted != nullptr)
  364. {
  365. inTempAllocator->Free(mIslandsSorted, mNumIslands * sizeof(uint32));
  366. mIslandsSorted = nullptr;
  367. }
  368. if (mContactIslands != nullptr)
  369. {
  370. inTempAllocator->Free(mContactIslandEnds, (mNumIslands + 1) * sizeof(uint32));
  371. mContactIslandEnds = nullptr;
  372. inTempAllocator->Free(mContactIslands, mNumContacts * sizeof(uint32));
  373. mContactIslands = nullptr;
  374. }
  375. if (mConstraintIslands != nullptr)
  376. {
  377. inTempAllocator->Free(mConstraintIslandEnds, (mNumIslands + 1) * sizeof(uint32));
  378. mConstraintIslandEnds = nullptr;
  379. inTempAllocator->Free(mConstraintIslands, mNumConstraints * sizeof(uint32));
  380. mConstraintIslands = nullptr;
  381. }
  382. inTempAllocator->Free(mBodyIslandEnds, (mNumActiveBodies + 1) * sizeof(uint32));
  383. mBodyIslandEnds = nullptr;
  384. inTempAllocator->Free(mBodyIslands, mNumActiveBodies * sizeof(uint32));
  385. mBodyIslands = nullptr;
  386. inTempAllocator->Free(mConstraintLinks, mNumConstraints * sizeof(uint32));
  387. mConstraintLinks = nullptr;
  388. #ifdef JPH_VALIDATE_ISLAND_BUILDER
  389. inTempAllocator->Free(mLinkValidation, mMaxContacts * sizeof(LinkValidation));
  390. mLinkValidation = nullptr;
  391. #endif
  392. inTempAllocator->Free(mContactLinks, mMaxContacts * sizeof(uint32));
  393. mContactLinks = nullptr;
  394. mNumActiveBodies = 0;
  395. mNumConstraints = 0;
  396. mMaxContacts = 0;
  397. mNumContacts = 0;
  398. mNumIslands = 0;
  399. }
  400. JPH_NAMESPACE_END