IslandBuilder.cpp 16 KB

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