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