IslandBuilder.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  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. // Both need to be active, we don't want to create an island with static objects
  82. if (inFirst >= mMaxActiveBodies || inSecond >= mMaxActiveBodies)
  83. return;
  84. #ifdef JPH_VALIDATE_ISLAND_BUILDER
  85. // Add link to the validation list
  86. if (mNumLinkValidation < uint32(mMaxContacts))
  87. mLinkValidation[mNumLinkValidation++] = { inFirst, inSecond };
  88. else
  89. JPH_ASSERT(false, "Out of links");
  90. #endif
  91. // Start the algorithm with the two bodies
  92. uint32 first_link_to = inFirst;
  93. uint32 second_link_to = inSecond;
  94. for (;;)
  95. {
  96. // Follow the chain until we get to the body with lowest index
  97. // If the swap compare below fails, we'll keep searching from the lowest index for the new lowest index
  98. first_link_to = GetLowestBodyIndex(first_link_to);
  99. second_link_to = GetLowestBodyIndex(second_link_to);
  100. // If the targets are the same, the bodies are already connected
  101. if (first_link_to != second_link_to)
  102. {
  103. // We always link the highest to the lowest
  104. if (first_link_to < second_link_to)
  105. {
  106. // Attempt to link the second to the first
  107. // Since we found this body to be at the end of the chain it must point to itself, and if it
  108. // doesn't it has been reparented and we need to retry the algorithm
  109. if (!mBodyLinks[second_link_to].mLinkedTo.compare_exchange_weak(second_link_to, first_link_to, memory_order_relaxed))
  110. continue;
  111. }
  112. else
  113. {
  114. // Attempt to link the first to the second
  115. // Since we found this body to be at the end of the chain it must point to itself, and if it
  116. // doesn't it has been reparented and we need to retry the algorithm
  117. if (!mBodyLinks[first_link_to].mLinkedTo.compare_exchange_weak(first_link_to, second_link_to, memory_order_relaxed))
  118. continue;
  119. }
  120. }
  121. // Linking succeeded!
  122. // Chains of bodies can become really long, resulting in an O(N) loop to find the lowest body index
  123. // to prevent this we attempt to update the link of the bodies that were passed in to directly point
  124. // to the lowest index that we found. If the value became lower than our lowest link, some other
  125. // thread must have relinked these bodies in the mean time so we won't update the value.
  126. uint32 lowest_link_to = min(first_link_to, second_link_to);
  127. AtomicMin(mBodyLinks[inFirst].mLinkedTo, lowest_link_to, memory_order_relaxed);
  128. AtomicMin(mBodyLinks[inSecond].mLinkedTo, lowest_link_to, memory_order_relaxed);
  129. break;
  130. }
  131. }
  132. void IslandBuilder::LinkConstraint(uint32 inConstraintIndex, uint32 inFirst, uint32 inSecond)
  133. {
  134. LinkBodies(inFirst, inSecond);
  135. JPH_ASSERT(inConstraintIndex < mNumConstraints);
  136. uint32 min_value = min(inFirst, inSecond); // Use fact that invalid index is 0xffffffff, we want the active body of two
  137. JPH_ASSERT(min_value != Body::cInactiveIndex); // At least one of the bodies must be active
  138. mConstraintLinks[inConstraintIndex] = min_value;
  139. }
  140. void IslandBuilder::LinkContact(uint32 inContactIndex, uint32 inFirst, uint32 inSecond)
  141. {
  142. JPH_ASSERT(inContactIndex < mMaxContacts);
  143. mContactLinks[inContactIndex] = min(inFirst, inSecond); // Use fact that invalid index is 0xffffffff, we want the active body of two
  144. }
  145. #ifdef JPH_VALIDATE_ISLAND_BUILDER
  146. void IslandBuilder::ValidateIslands(uint32 inNumActiveBodies) const
  147. {
  148. JPH_PROFILE_FUNCTION();
  149. // Go through all links so far
  150. for (uint32 i = 0; i < mNumLinkValidation; ++i)
  151. {
  152. // If the bodies in this link ended up in different groups we have a problem
  153. if (mBodyLinks[mLinkValidation[i].mFirst].mIslandIndex != mBodyLinks[mLinkValidation[i].mSecond].mIslandIndex)
  154. {
  155. Trace("Fail: %u, %u", mLinkValidation[i].mFirst, mLinkValidation[i].mSecond);
  156. Trace("Num Active: %u", inNumActiveBodies);
  157. for (uint32 j = 0; j < mNumLinkValidation; ++j)
  158. Trace("builder.Link(%u, %u);", mLinkValidation[j].mFirst, mLinkValidation[j].mSecond);
  159. IslandBuilder tmp;
  160. tmp.Init(inNumActiveBodies);
  161. for (uint32 j = 0; j < mNumLinkValidation; ++j)
  162. {
  163. Trace("Link %u -> %u", mLinkValidation[j].mFirst, mLinkValidation[j].mSecond);
  164. tmp.LinkBodies(mLinkValidation[j].mFirst, mLinkValidation[j].mSecond);
  165. for (uint32 t = 0; t < inNumActiveBodies; ++t)
  166. Trace("%u -> %u", t, (uint32)tmp.mBodyLinks[t].mLinkedTo);
  167. }
  168. JPH_ASSERT(false, "IslandBuilder validation failed");
  169. }
  170. }
  171. }
  172. #endif
  173. void IslandBuilder::BuildBodyIslands(const BodyID *inActiveBodies, uint32 inNumActiveBodies, TempAllocator *inTempAllocator)
  174. {
  175. JPH_PROFILE_FUNCTION();
  176. // Store the amount of active bodies
  177. mNumActiveBodies = inNumActiveBodies;
  178. // Create output arrays for body ID's, don't call constructors
  179. JPH_ASSERT(mBodyIslands == nullptr);
  180. mBodyIslands = (BodyID *)inTempAllocator->Allocate(inNumActiveBodies * sizeof(BodyID));
  181. // 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.
  182. // Note: We allocate 1 extra entry because we always increment the count of the next island.
  183. uint32 *body_island_starts = (uint32 *)inTempAllocator->Allocate((inNumActiveBodies + 1) * sizeof(uint32));
  184. // First island always starts at 0
  185. body_island_starts[0] = 0;
  186. // Calculate island index for all bodies
  187. JPH_ASSERT(mNumIslands == 0);
  188. for (uint32 i = 0; i < inNumActiveBodies; ++i)
  189. {
  190. BodyLink &link = mBodyLinks[i];
  191. uint32 s = link.mLinkedTo.load(memory_order_relaxed);
  192. if (s != i)
  193. {
  194. // 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)
  195. JPH_ASSERT(s < uint32(i));
  196. uint32 island_index = mBodyLinks[s].mIslandIndex;
  197. link.mIslandIndex = island_index;
  198. // Increment the start of the next island
  199. body_island_starts[island_index + 1]++;
  200. }
  201. else
  202. {
  203. // Does not link to other body, this is the start of a new island
  204. link.mIslandIndex = mNumIslands;
  205. ++mNumIslands;
  206. // Set the start of the next island to 1
  207. body_island_starts[mNumIslands] = 1;
  208. }
  209. }
  210. #ifdef JPH_VALIDATE_ISLAND_BUILDER
  211. ValidateIslands(inNumActiveBodies);
  212. #endif
  213. // Make the start array absolute (so far we only counted)
  214. for (uint32 island = 1; island < mNumIslands; ++island)
  215. body_island_starts[island] += body_island_starts[island - 1];
  216. // Convert the to a linear list grouped by island
  217. for (uint32 i = 0; i < inNumActiveBodies; ++i)
  218. {
  219. BodyLink &link = mBodyLinks[i];
  220. // Copy the body to the correct location in the array and increment it
  221. uint32 &start = body_island_starts[link.mIslandIndex];
  222. mBodyIslands[start] = inActiveBodies[i];
  223. start++;
  224. // Reset linked to field for the next update
  225. link.mLinkedTo.store(i, memory_order_relaxed);
  226. }
  227. // We should now have a full array
  228. JPH_ASSERT(mNumIslands == 0 || body_island_starts[mNumIslands - 1] == inNumActiveBodies);
  229. // We've incremented all body indices so that they now point at the end instead of the starts
  230. JPH_ASSERT(mBodyIslandEnds == nullptr);
  231. mBodyIslandEnds = body_island_starts;
  232. }
  233. void IslandBuilder::BuildConstraintIslands(const uint32 *inConstraintToBody, uint32 inNumConstraints, uint32 *&outConstraints, uint32 *&outConstraintsEnd, TempAllocator *inTempAllocator) const
  234. {
  235. JPH_PROFILE_FUNCTION();
  236. // Check if there's anything to do
  237. if (inNumConstraints == 0)
  238. return;
  239. // Create output arrays for constraints
  240. // Note: For the end indices we allocate 1 extra entry so we don't have to do an if in the inner loop
  241. uint32 *constraints = (uint32 *)inTempAllocator->Allocate(inNumConstraints * sizeof(uint32));
  242. uint32 *constraint_ends = (uint32 *)inTempAllocator->Allocate((mNumIslands + 1) * sizeof(uint32));
  243. // Reset sizes
  244. for (uint32 island = 0; island < mNumIslands; ++island)
  245. constraint_ends[island] = 0;
  246. // Loop over array and increment start relative position for the next island
  247. for (uint32 constraint = 0; constraint < inNumConstraints; ++constraint)
  248. {
  249. uint32 body_idx = inConstraintToBody[constraint];
  250. uint32 next_island_idx = mBodyLinks[body_idx].mIslandIndex + 1;
  251. JPH_ASSERT(next_island_idx <= mNumIslands);
  252. constraint_ends[next_island_idx]++;
  253. }
  254. // Make start positions absolute
  255. for (uint32 island = 1; island < mNumIslands; ++island)
  256. constraint_ends[island] += constraint_ends[island - 1];
  257. // Loop over array and collect constraints
  258. for (uint32 constraint = 0; constraint < inNumConstraints; ++constraint)
  259. {
  260. uint32 body_idx = inConstraintToBody[constraint];
  261. uint32 island_idx = mBodyLinks[body_idx].mIslandIndex;
  262. constraints[constraint_ends[island_idx]++] = constraint;
  263. }
  264. JPH_ASSERT(outConstraints == nullptr);
  265. outConstraints = constraints;
  266. JPH_ASSERT(outConstraintsEnd == nullptr);
  267. outConstraintsEnd = constraint_ends;
  268. }
  269. void IslandBuilder::SortIslands(TempAllocator *inTempAllocator)
  270. {
  271. JPH_PROFILE_FUNCTION();
  272. if (mNumContacts > 0 || mNumConstraints > 0)
  273. {
  274. // Allocate mapping table
  275. JPH_ASSERT(mIslandsSorted == nullptr);
  276. mIslandsSorted = (uint32 *)inTempAllocator->Allocate(mNumIslands * sizeof(uint32));
  277. // Initialize index
  278. for (uint32 island = 0; island < mNumIslands; ++island)
  279. mIslandsSorted[island] = island;
  280. // Determine the sum of contact constraints / constraints per island
  281. uint32 *num_constraints = (uint32 *)inTempAllocator->Allocate(mNumIslands * sizeof(uint32));
  282. if (mNumContacts > 0 && mNumConstraints > 0)
  283. {
  284. num_constraints[0] = mConstraintIslandEnds[0] + mContactIslandEnds[0];
  285. for (uint32 island = 1; island < mNumIslands; ++island)
  286. num_constraints[island] = mConstraintIslandEnds[island] - mConstraintIslandEnds[island - 1]
  287. + mContactIslandEnds[island] - mContactIslandEnds[island - 1];
  288. }
  289. else if (mNumContacts > 0)
  290. {
  291. num_constraints[0] = mContactIslandEnds[0];
  292. for (uint32 island = 1; island < mNumIslands; ++island)
  293. num_constraints[island] = mContactIslandEnds[island] - mContactIslandEnds[island - 1];
  294. }
  295. else
  296. {
  297. num_constraints[0] = mConstraintIslandEnds[0];
  298. for (uint32 island = 1; island < mNumIslands; ++island)
  299. num_constraints[island] = mConstraintIslandEnds[island] - mConstraintIslandEnds[island - 1];
  300. }
  301. // Sort so the biggest islands go first, this means that the jobs that take longest will be running
  302. // first which improves the chance that all jobs finish at the same time.
  303. QuickSort(mIslandsSorted, mIslandsSorted + mNumIslands, [num_constraints](uint32 inLHS, uint32 inRHS) {
  304. return num_constraints[inLHS] > num_constraints[inRHS];
  305. });
  306. inTempAllocator->Free(num_constraints, mNumIslands * sizeof(uint32));
  307. }
  308. }
  309. void IslandBuilder::Finalize(const BodyID *inActiveBodies, uint32 inNumActiveBodies, uint32 inNumContacts, TempAllocator *inTempAllocator)
  310. {
  311. JPH_PROFILE_FUNCTION();
  312. mNumContacts = inNumContacts;
  313. BuildBodyIslands(inActiveBodies, inNumActiveBodies, inTempAllocator);
  314. BuildConstraintIslands(mConstraintLinks, mNumConstraints, mConstraintIslands, mConstraintIslandEnds, inTempAllocator);
  315. BuildConstraintIslands(mContactLinks, mNumContacts, mContactIslands, mContactIslandEnds, inTempAllocator);
  316. SortIslands(inTempAllocator);
  317. mNumPositionSteps = (uint8 *)inTempAllocator->Allocate(mNumIslands * sizeof(uint8));
  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. inTempAllocator->Free(mNumPositionSteps, mNumIslands * sizeof(uint8));
  364. if (mIslandsSorted != nullptr)
  365. {
  366. inTempAllocator->Free(mIslandsSorted, mNumIslands * sizeof(uint32));
  367. mIslandsSorted = nullptr;
  368. }
  369. if (mContactIslands != nullptr)
  370. {
  371. inTempAllocator->Free(mContactIslandEnds, (mNumIslands + 1) * sizeof(uint32));
  372. mContactIslandEnds = nullptr;
  373. inTempAllocator->Free(mContactIslands, mNumContacts * sizeof(uint32));
  374. mContactIslands = nullptr;
  375. }
  376. if (mConstraintIslands != nullptr)
  377. {
  378. inTempAllocator->Free(mConstraintIslandEnds, (mNumIslands + 1) * sizeof(uint32));
  379. mConstraintIslandEnds = nullptr;
  380. inTempAllocator->Free(mConstraintIslands, mNumConstraints * sizeof(uint32));
  381. mConstraintIslands = nullptr;
  382. }
  383. inTempAllocator->Free(mBodyIslandEnds, (mNumActiveBodies + 1) * sizeof(uint32));
  384. mBodyIslandEnds = nullptr;
  385. inTempAllocator->Free(mBodyIslands, mNumActiveBodies * sizeof(uint32));
  386. mBodyIslands = nullptr;
  387. inTempAllocator->Free(mConstraintLinks, mNumConstraints * sizeof(uint32));
  388. mConstraintLinks = nullptr;
  389. #ifdef JPH_VALIDATE_ISLAND_BUILDER
  390. inTempAllocator->Free(mLinkValidation, mMaxContacts * sizeof(LinkValidation));
  391. mLinkValidation = nullptr;
  392. #endif
  393. inTempAllocator->Free(mContactLinks, mMaxContacts * sizeof(uint32));
  394. mContactLinks = nullptr;
  395. mNumActiveBodies = 0;
  396. mNumConstraints = 0;
  397. mMaxContacts = 0;
  398. mNumContacts = 0;
  399. mNumIslands = 0;
  400. }
  401. JPH_NAMESPACE_END