IslandBuilder.cpp 16 KB

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