LargeIslandSplitter.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. // SPDX-FileCopyrightText: 2023 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <Jolt/Jolt.h>
  4. #include <Jolt/Physics/LargeIslandSplitter.h>
  5. #include <Jolt/Physics/IslandBuilder.h>
  6. #include <Jolt/Physics/Constraints/CalculateSolverSteps.h>
  7. #include <Jolt/Physics/Constraints/Constraint.h>
  8. #include <Jolt/Physics/Constraints/ContactConstraintManager.h>
  9. #include <Jolt/Physics/Body/BodyManager.h>
  10. #include <Jolt/Core/Profiler.h>
  11. #include <Jolt/Core/TempAllocator.h>
  12. //#define JPH_LARGE_ISLAND_SPLITTER_DEBUG
  13. JPH_NAMESPACE_BEGIN
  14. LargeIslandSplitter::EStatus LargeIslandSplitter::Splits::FetchNextBatch(uint32 &outConstraintsBegin, uint32 &outConstraintsEnd, uint32 &outContactsBegin, uint32 &outContactsEnd, bool &outFirstIteration)
  15. {
  16. {
  17. // First check if we can get a new batch (doing a read to avoid hammering an atomic with an atomic subtract)
  18. // Note this also avoids overflowing the status counter if we're done but there's still one thread processing items
  19. uint64 status = mStatus.load(memory_order_acquire);
  20. // Check for special value that indicates that the splits are still being built
  21. // (note we do not check for this condition again below as we reset all splits before kicking off jobs that fetch batches of work)
  22. if (status == StatusItemMask)
  23. return EStatus::WaitingForBatch;
  24. // Next check if all items have been processed. Note that we do this after checking if the job can be started
  25. // as mNumIterations is not initialized until the split is started.
  26. if (sGetIteration(status) >= mNumIterations)
  27. return EStatus::AllBatchesDone;
  28. uint item = sGetItem(status);
  29. uint split_index = sGetSplit(status);
  30. if (split_index == cNonParallelSplitIdx)
  31. {
  32. // Non parallel split needs to be taken as a single batch, only the thread that takes element 0 will do it
  33. if (item != 0)
  34. return EStatus::WaitingForBatch;
  35. }
  36. else
  37. {
  38. // Parallel split is split into batches
  39. JPH_ASSERT(split_index < mNumSplits);
  40. const Split &split = mSplits[split_index];
  41. if (item >= split.GetNumItems())
  42. return EStatus::WaitingForBatch;
  43. }
  44. }
  45. // Then try to actually get the batch
  46. uint64 status = mStatus.fetch_add(cBatchSize, memory_order_acquire);
  47. int iteration = sGetIteration(status);
  48. if (iteration >= mNumIterations)
  49. return EStatus::AllBatchesDone;
  50. uint split_index = sGetSplit(status);
  51. JPH_ASSERT(split_index < mNumSplits || split_index == cNonParallelSplitIdx);
  52. const Split &split = mSplits[split_index];
  53. uint item_begin = sGetItem(status);
  54. if (split_index == cNonParallelSplitIdx)
  55. {
  56. if (item_begin == 0)
  57. {
  58. // Non-parallel split always goes as a single batch
  59. outConstraintsBegin = split.mConstraintBufferBegin;
  60. outConstraintsEnd = split.mConstraintBufferEnd;
  61. outContactsBegin = split.mContactBufferBegin;
  62. outContactsEnd = split.mContactBufferEnd;
  63. outFirstIteration = iteration == 0;
  64. return EStatus::BatchRetrieved;
  65. }
  66. else
  67. {
  68. // Otherwise we're done with this split
  69. return EStatus::WaitingForBatch;
  70. }
  71. }
  72. // Parallel split is split into batches
  73. uint num_constraints = split.GetNumConstraints();
  74. uint num_contacts = split.GetNumContacts();
  75. uint num_items = num_constraints + num_contacts;
  76. if (item_begin >= num_items)
  77. return EStatus::WaitingForBatch;
  78. uint item_end = min(item_begin + cBatchSize, num_items);
  79. if (item_end >= num_constraints)
  80. {
  81. if (item_begin < num_constraints)
  82. {
  83. // Partially from constraints and partially from contacts
  84. outConstraintsBegin = split.mConstraintBufferBegin + item_begin;
  85. outConstraintsEnd = split.mConstraintBufferEnd;
  86. }
  87. else
  88. {
  89. // Only contacts
  90. outConstraintsBegin = 0;
  91. outConstraintsEnd = 0;
  92. }
  93. outContactsBegin = split.mContactBufferBegin + (max(item_begin, num_constraints) - num_constraints);
  94. outContactsEnd = split.mContactBufferBegin + (item_end - num_constraints);
  95. }
  96. else
  97. {
  98. // Only constraints
  99. outConstraintsBegin = split.mConstraintBufferBegin + item_begin;
  100. outConstraintsEnd = split.mConstraintBufferBegin + item_end;
  101. outContactsBegin = 0;
  102. outContactsEnd = 0;
  103. }
  104. outFirstIteration = iteration == 0;
  105. return EStatus::BatchRetrieved;
  106. }
  107. void LargeIslandSplitter::Splits::MarkBatchProcessed(uint inNumProcessed, bool &outLastIteration, bool &outFinalBatch)
  108. {
  109. // We fetched this batch, nobody should change the split and or iteration until we mark the last batch as processed so we can safely get the current status
  110. uint64 status = mStatus.load(memory_order_relaxed);
  111. uint split_index = sGetSplit(status);
  112. JPH_ASSERT(split_index < mNumSplits || split_index == cNonParallelSplitIdx);
  113. const Split &split = mSplits[split_index];
  114. uint num_items_in_split = split.GetNumItems();
  115. // Determine if this is the last iteration before possibly incrementing it
  116. int iteration = sGetIteration(status);
  117. outLastIteration = iteration == mNumIterations - 1;
  118. // Add the number of items we processed to the total number of items processed
  119. // Note: This needs to happen after we read the status as other threads may update the status after we mark items as processed
  120. JPH_ASSERT(inNumProcessed > 0); // Logic will break if we mark a block of 0 items as processed
  121. uint total_items_processed = mItemsProcessed.fetch_add(inNumProcessed, memory_order_acq_rel) + inNumProcessed;
  122. // Check if we're at the end of the split
  123. if (total_items_processed >= num_items_in_split)
  124. {
  125. JPH_ASSERT(total_items_processed == num_items_in_split); // Should not overflow, that means we're retiring more items than we should process
  126. // Set items processed back to 0 for the next split/iteration
  127. mItemsProcessed.store(0, memory_order_release);
  128. // Determine next split
  129. do
  130. {
  131. if (split_index == cNonParallelSplitIdx)
  132. {
  133. // At start of next iteration
  134. split_index = 0;
  135. ++iteration;
  136. }
  137. else
  138. {
  139. // At start of next split
  140. ++split_index;
  141. }
  142. // If we're beyond the end of splits, go to the non-parallel split
  143. if (split_index >= mNumSplits)
  144. split_index = cNonParallelSplitIdx;
  145. }
  146. while (iteration < mNumIterations
  147. && mSplits[split_index].GetNumItems() == 0); // We don't support processing empty splits, skip to the next split in this case
  148. mStatus.store((uint64(iteration) << StatusIterationShift) | (uint64(split_index) << StatusSplitShift), memory_order_release);
  149. }
  150. // Track if this is the final batch
  151. outFinalBatch = iteration >= mNumIterations;
  152. }
  153. LargeIslandSplitter::~LargeIslandSplitter()
  154. {
  155. JPH_ASSERT(mSplitMasks == nullptr);
  156. JPH_ASSERT(mContactAndConstraintsSplitIdx == nullptr);
  157. JPH_ASSERT(mContactAndConstraintIndices == nullptr);
  158. JPH_ASSERT(mSplitIslands == nullptr);
  159. }
  160. void LargeIslandSplitter::Prepare(const IslandBuilder &inIslandBuilder, uint32 inNumActiveBodies, TempAllocator *inTempAllocator)
  161. {
  162. JPH_PROFILE_FUNCTION();
  163. // Count the total number of constraints and contacts that we will be putting in splits
  164. mContactAndConstraintsSize = 0;
  165. for (uint32 island = 0; island < inIslandBuilder.GetNumIslands(); ++island)
  166. {
  167. // Get the contacts in this island
  168. uint32 *contacts_start, *contacts_end;
  169. inIslandBuilder.GetContactsInIsland(island, contacts_start, contacts_end);
  170. uint num_contacts_in_island = uint(contacts_end - contacts_start);
  171. // Get the constraints in this island
  172. uint32 *constraints_start, *constraints_end;
  173. inIslandBuilder.GetConstraintsInIsland(island, constraints_start, constraints_end);
  174. uint num_constraints_in_island = uint(constraints_end - constraints_start);
  175. uint island_size = num_contacts_in_island + num_constraints_in_island;
  176. if (island_size >= cLargeIslandTreshold)
  177. {
  178. mNumSplitIslands++;
  179. mContactAndConstraintsSize += island_size;
  180. }
  181. else
  182. break; // If this island doesn't have enough constraints, the next islands won't either since they're sorted from big to small
  183. }
  184. if (mContactAndConstraintsSize > 0)
  185. {
  186. mNumActiveBodies = inNumActiveBodies;
  187. // Allocate split mask buffer
  188. mSplitMasks = (SplitMask *)inTempAllocator->Allocate(mNumActiveBodies * sizeof(SplitMask));
  189. // Allocate contact and constraint buffer
  190. uint contact_and_constraint_indices_size = mContactAndConstraintsSize * sizeof(uint32);
  191. mContactAndConstraintsSplitIdx = (uint32 *)inTempAllocator->Allocate(contact_and_constraint_indices_size);
  192. mContactAndConstraintIndices = (uint32 *)inTempAllocator->Allocate(contact_and_constraint_indices_size);
  193. // Allocate island split buffer
  194. mSplitIslands = (Splits *)inTempAllocator->Allocate(mNumSplitIslands * sizeof(Splits));
  195. // Prevent any of the splits from being picked up as work
  196. for (uint i = 0; i < mNumSplitIslands; ++i)
  197. mSplitIslands[i].ResetStatus();
  198. }
  199. }
  200. uint LargeIslandSplitter::AssignSplit(const Body *inBody1, const Body *inBody2)
  201. {
  202. uint32 idx1 = inBody1->GetIndexInActiveBodiesInternal();
  203. uint32 idx2 = inBody2->GetIndexInActiveBodiesInternal();
  204. // Test if either index is negative
  205. if (idx1 == Body::cInactiveIndex || !inBody1->IsDynamic())
  206. {
  207. // Body 1 is not active or a kinematic body, so we only need to set 1 body
  208. JPH_ASSERT(idx2 < mNumActiveBodies);
  209. SplitMask &mask = mSplitMasks[idx2];
  210. uint split = min(CountTrailingZeros(~uint32(mask)), cNonParallelSplitIdx);
  211. mask |= SplitMask(1U << split);
  212. return split;
  213. }
  214. else if (idx2 == Body::cInactiveIndex || !inBody2->IsDynamic())
  215. {
  216. // Body 2 is not active or a kinematic body, so we only need to set 1 body
  217. JPH_ASSERT(idx1 < mNumActiveBodies);
  218. SplitMask &mask = mSplitMasks[idx1];
  219. uint split = min(CountTrailingZeros(~uint32(mask)), cNonParallelSplitIdx);
  220. mask |= SplitMask(1U << split);
  221. return split;
  222. }
  223. else
  224. {
  225. // If both bodies are active, we need to set 2 bodies
  226. JPH_ASSERT(idx1 < mNumActiveBodies);
  227. JPH_ASSERT(idx2 < mNumActiveBodies);
  228. SplitMask &mask1 = mSplitMasks[idx1];
  229. SplitMask &mask2 = mSplitMasks[idx2];
  230. uint split = min(CountTrailingZeros((~uint32(mask1)) & (~uint32(mask2))), cNonParallelSplitIdx);
  231. SplitMask mask = SplitMask(1U << split);
  232. mask1 |= mask;
  233. mask2 |= mask;
  234. return split;
  235. }
  236. }
  237. uint LargeIslandSplitter::AssignToNonParallelSplit(const Body *inBody)
  238. {
  239. uint32 idx = inBody->GetIndexInActiveBodiesInternal();
  240. if (idx != Body::cInactiveIndex)
  241. {
  242. JPH_ASSERT(idx < mNumActiveBodies);
  243. mSplitMasks[idx] |= 1U << cNonParallelSplitIdx;
  244. }
  245. return cNonParallelSplitIdx;
  246. }
  247. bool LargeIslandSplitter::SplitIsland(uint32 inIslandIndex, const IslandBuilder &inIslandBuilder, const BodyManager &inBodyManager, const ContactConstraintManager &inContactManager, Constraint **inActiveConstraints, CalculateSolverSteps &ioStepsCalculator)
  248. {
  249. JPH_PROFILE_FUNCTION();
  250. // Get the contacts in this island
  251. uint32 *contacts_start, *contacts_end;
  252. inIslandBuilder.GetContactsInIsland(inIslandIndex, contacts_start, contacts_end);
  253. uint num_contacts_in_island = uint(contacts_end - contacts_start);
  254. // Get the constraints in this island
  255. uint32 *constraints_start, *constraints_end;
  256. inIslandBuilder.GetConstraintsInIsland(inIslandIndex, constraints_start, constraints_end);
  257. uint num_constraints_in_island = uint(constraints_end - constraints_start);
  258. // Check if it exceeds the threshold
  259. uint island_size = num_contacts_in_island + num_constraints_in_island;
  260. if (island_size < cLargeIslandTreshold)
  261. return false;
  262. // Get bodies in this island
  263. BodyID *bodies_start, *bodies_end;
  264. inIslandBuilder.GetBodiesInIsland(inIslandIndex, bodies_start, bodies_end);
  265. // Reset the split mask for all bodies in this island
  266. Body const * const *bodies = inBodyManager.GetBodies().data();
  267. for (const BodyID *b = bodies_start; b < bodies_end; ++b)
  268. mSplitMasks[bodies[b->GetIndex()]->GetIndexInActiveBodiesInternal()] = 0;
  269. // Count the number of contacts and constraints per split
  270. uint num_contacts_in_split[cNumSplits] = { };
  271. uint num_constraints_in_split[cNumSplits] = { };
  272. // Get space to store split indices
  273. uint offset = mContactAndConstraintsNextFree.fetch_add(island_size, memory_order_relaxed);
  274. uint32 *contact_split_idx = mContactAndConstraintsSplitIdx + offset;
  275. uint32 *constraint_split_idx = contact_split_idx + num_contacts_in_island;
  276. // Assign the contacts to a split
  277. uint32 *cur_contact_split_idx = contact_split_idx;
  278. for (const uint32 *c = contacts_start; c < contacts_end; ++c)
  279. {
  280. const Body *body1, *body2;
  281. inContactManager.GetAffectedBodies(*c, body1, body2);
  282. uint split = AssignSplit(body1, body2);
  283. num_contacts_in_split[split]++;
  284. *cur_contact_split_idx++ = split;
  285. if (body1->IsDynamic())
  286. ioStepsCalculator(body1->GetMotionPropertiesUnchecked());
  287. if (body2->IsDynamic())
  288. ioStepsCalculator(body2->GetMotionPropertiesUnchecked());
  289. }
  290. // Assign the constraints to a split
  291. uint32 *cur_constraint_split_idx = constraint_split_idx;
  292. for (const uint32 *c = constraints_start; c < constraints_end; ++c)
  293. {
  294. const Constraint *constraint = inActiveConstraints[*c];
  295. uint split = constraint->BuildIslandSplits(*this);
  296. num_constraints_in_split[split]++;
  297. *cur_constraint_split_idx++ = split;
  298. ioStepsCalculator(constraint);
  299. }
  300. ioStepsCalculator.Finalize();
  301. // Start with 0 splits
  302. uint split_remap_table[cNumSplits];
  303. uint new_split_idx = mNextSplitIsland.fetch_add(1, memory_order_relaxed);
  304. JPH_ASSERT(new_split_idx < mNumSplitIslands);
  305. Splits &splits = mSplitIslands[new_split_idx];
  306. splits.mIslandIndex = inIslandIndex;
  307. splits.mNumSplits = 0;
  308. splits.mNumIterations = ioStepsCalculator.GetNumVelocitySteps() + 1; // Iteration 0 is used for warm starting
  309. splits.mNumVelocitySteps = ioStepsCalculator.GetNumVelocitySteps();
  310. splits.mNumPositionSteps = ioStepsCalculator.GetNumPositionSteps();
  311. splits.mItemsProcessed.store(0, memory_order_release);
  312. // Allocate space to store the sorted constraint and contact indices per split
  313. uint32 *constraint_buffer_cur[cNumSplits], *contact_buffer_cur[cNumSplits];
  314. for (uint s = 0; s < cNumSplits; ++s)
  315. {
  316. // If this split doesn't contain enough constraints and contacts, we will combine it with the non parallel split
  317. if (num_constraints_in_split[s] + num_contacts_in_split[s] < cSplitCombineTreshold
  318. && s < cNonParallelSplitIdx) // The non-parallel split cannot merge into itself
  319. {
  320. // Remap it
  321. split_remap_table[s] = cNonParallelSplitIdx;
  322. // Add the counts to the non parallel split
  323. num_contacts_in_split[cNonParallelSplitIdx] += num_contacts_in_split[s];
  324. num_constraints_in_split[cNonParallelSplitIdx] += num_constraints_in_split[s];
  325. }
  326. else
  327. {
  328. // This split is valid, map it to the next empty slot
  329. uint target_split;
  330. if (s < cNonParallelSplitIdx)
  331. target_split = splits.mNumSplits++;
  332. else
  333. target_split = cNonParallelSplitIdx;
  334. Split &split = splits.mSplits[target_split];
  335. split_remap_table[s] = target_split;
  336. // Allocate space for contacts
  337. split.mContactBufferBegin = offset;
  338. split.mContactBufferEnd = split.mContactBufferBegin + num_contacts_in_split[s];
  339. // Allocate space for constraints
  340. split.mConstraintBufferBegin = split.mContactBufferEnd;
  341. split.mConstraintBufferEnd = split.mConstraintBufferBegin + num_constraints_in_split[s];
  342. // Store start for each split
  343. contact_buffer_cur[target_split] = mContactAndConstraintIndices + split.mContactBufferBegin;
  344. constraint_buffer_cur[target_split] = mContactAndConstraintIndices + split.mConstraintBufferBegin;
  345. // Update offset
  346. offset = split.mConstraintBufferEnd;
  347. }
  348. }
  349. // Split the contacts
  350. for (uint c = 0; c < num_contacts_in_island; ++c)
  351. {
  352. uint split = split_remap_table[contact_split_idx[c]];
  353. *contact_buffer_cur[split]++ = contacts_start[c];
  354. }
  355. // Split the constraints
  356. for (uint c = 0; c < num_constraints_in_island; ++c)
  357. {
  358. uint split = split_remap_table[constraint_split_idx[c]];
  359. *constraint_buffer_cur[split]++ = constraints_start[c];
  360. }
  361. #ifdef JPH_LARGE_ISLAND_SPLITTER_DEBUG
  362. // Trace the size of all splits
  363. uint sum = 0;
  364. String stats;
  365. for (uint s = 0; s < cNumSplits; ++s)
  366. {
  367. // If we've processed all splits, jump to the non-parallel split
  368. if (s >= splits.GetNumSplits())
  369. s = cNonParallelSplitIdx;
  370. const Split &split = splits.mSplits[s];
  371. stats += StringFormat("g:%d:%d:%d, ", s, split.GetNumContacts(), split.GetNumConstraints());
  372. sum += split.GetNumItems();
  373. }
  374. stats += StringFormat("sum: %d", sum);
  375. Trace(stats.c_str());
  376. #endif // JPH_LARGE_ISLAND_SPLITTER_DEBUG
  377. #ifdef JPH_ENABLE_ASSERTS
  378. for (uint s = 0; s < cNumSplits; ++s)
  379. {
  380. // If there are no more splits, process the non-parallel split
  381. if (s >= splits.mNumSplits)
  382. s = cNonParallelSplitIdx;
  383. // Check that we wrote all elements
  384. Split &split = splits.mSplits[s];
  385. JPH_ASSERT(contact_buffer_cur[s] == mContactAndConstraintIndices + split.mContactBufferEnd);
  386. JPH_ASSERT(constraint_buffer_cur[s] == mContactAndConstraintIndices + split.mConstraintBufferEnd);
  387. }
  388. #ifdef JPH_DEBUG
  389. // Validate that the splits are indeed not touching the same body
  390. for (uint s = 0; s < splits.mNumSplits; ++s)
  391. {
  392. Array<bool> body_used(mNumActiveBodies, false);
  393. // Validate contacts
  394. uint32 split_contacts_begin, split_contacts_end;
  395. splits.GetContactsInSplit(s, split_contacts_begin, split_contacts_end);
  396. for (uint32 *c = mContactAndConstraintIndices + split_contacts_begin; c < mContactAndConstraintIndices + split_contacts_end; ++c)
  397. {
  398. const Body *body1, *body2;
  399. inContactManager.GetAffectedBodies(*c, body1, body2);
  400. uint32 idx1 = body1->GetIndexInActiveBodiesInternal();
  401. if (idx1 != Body::cInactiveIndex && body1->IsDynamic())
  402. {
  403. JPH_ASSERT(!body_used[idx1]);
  404. body_used[idx1] = true;
  405. }
  406. uint32 idx2 = body2->GetIndexInActiveBodiesInternal();
  407. if (idx2 != Body::cInactiveIndex && body2->IsDynamic())
  408. {
  409. JPH_ASSERT(!body_used[idx2]);
  410. body_used[idx2] = true;
  411. }
  412. }
  413. }
  414. #endif // JPH_DEBUG
  415. #endif // JPH_ENABLE_ASSERTS
  416. // Allow other threads to pick up this split island now
  417. splits.StartFirstBatch();
  418. return true;
  419. }
  420. LargeIslandSplitter::EStatus LargeIslandSplitter::FetchNextBatch(uint &outSplitIslandIndex, uint32 *&outConstraintsBegin, uint32 *&outConstraintsEnd, uint32 *&outContactsBegin, uint32 *&outContactsEnd, bool &outFirstIteration)
  421. {
  422. // We can't be done when all islands haven't been submitted yet
  423. uint num_splits_created = mNextSplitIsland.load(memory_order_acquire);
  424. bool all_done = num_splits_created == mNumSplitIslands;
  425. // Loop over all split islands to find work
  426. uint32 constraints_begin, constraints_end, contacts_begin, contacts_end;
  427. for (Splits *s = mSplitIslands; s < mSplitIslands + num_splits_created; ++s)
  428. switch (s->FetchNextBatch(constraints_begin, constraints_end, contacts_begin, contacts_end, outFirstIteration))
  429. {
  430. case EStatus::AllBatchesDone:
  431. break;
  432. case EStatus::WaitingForBatch:
  433. all_done = false;
  434. break;
  435. case EStatus::BatchRetrieved:
  436. outSplitIslandIndex = uint(s - mSplitIslands);
  437. outConstraintsBegin = mContactAndConstraintIndices + constraints_begin;
  438. outConstraintsEnd = mContactAndConstraintIndices + constraints_end;
  439. outContactsBegin = mContactAndConstraintIndices + contacts_begin;
  440. outContactsEnd = mContactAndConstraintIndices + contacts_end;
  441. return EStatus::BatchRetrieved;
  442. }
  443. return all_done? EStatus::AllBatchesDone : EStatus::WaitingForBatch;
  444. }
  445. void LargeIslandSplitter::MarkBatchProcessed(uint inSplitIslandIndex, const uint32 *inConstraintsBegin, const uint32 *inConstraintsEnd, const uint32 *inContactsBegin, const uint32 *inContactsEnd, bool &outLastIteration, bool &outFinalBatch)
  446. {
  447. uint num_items_processed = uint(inConstraintsEnd - inConstraintsBegin) + uint(inContactsEnd - inContactsBegin);
  448. JPH_ASSERT(inSplitIslandIndex < mNextSplitIsland.load(memory_order_relaxed));
  449. Splits &splits = mSplitIslands[inSplitIslandIndex];
  450. splits.MarkBatchProcessed(num_items_processed, outLastIteration, outFinalBatch);
  451. }
  452. void LargeIslandSplitter::PrepareForSolvePositions()
  453. {
  454. for (Splits *s = mSplitIslands, *s_end = mSplitIslands + mNumSplitIslands; s < s_end; ++s)
  455. {
  456. // Set the number of iterations to the number of position steps
  457. s->mNumIterations = s->mNumPositionSteps;
  458. // We can start again from the first batch
  459. s->StartFirstBatch();
  460. }
  461. }
  462. void LargeIslandSplitter::Reset(TempAllocator *inTempAllocator)
  463. {
  464. JPH_PROFILE_FUNCTION();
  465. // Everything should have been used
  466. JPH_ASSERT(mContactAndConstraintsNextFree.load(memory_order_relaxed) == mContactAndConstraintsSize);
  467. JPH_ASSERT(mNextSplitIsland.load(memory_order_relaxed) == mNumSplitIslands);
  468. // Free split islands
  469. if (mNumSplitIslands > 0)
  470. {
  471. inTempAllocator->Free(mSplitIslands, mNumSplitIslands * sizeof(Splits));
  472. mSplitIslands = nullptr;
  473. mNumSplitIslands = 0;
  474. mNextSplitIsland.store(0, memory_order_relaxed);
  475. }
  476. // Free contact and constraint buffers
  477. if (mContactAndConstraintsSize > 0)
  478. {
  479. inTempAllocator->Free(mContactAndConstraintIndices, mContactAndConstraintsSize * sizeof(uint32));
  480. mContactAndConstraintIndices = nullptr;
  481. inTempAllocator->Free(mContactAndConstraintsSplitIdx, mContactAndConstraintsSize * sizeof(uint32));
  482. mContactAndConstraintsSplitIdx = nullptr;
  483. mContactAndConstraintsSize = 0;
  484. mContactAndConstraintsNextFree.store(0, memory_order_relaxed);
  485. }
  486. // Free split masks
  487. if (mSplitMasks != nullptr)
  488. {
  489. inTempAllocator->Free(mSplitMasks, mNumActiveBodies * sizeof(SplitMask));
  490. mSplitMasks = nullptr;
  491. mNumActiveBodies = 0;
  492. }
  493. }
  494. JPH_NAMESPACE_END