Skip to content

Commit 6e219a2

Browse files
committed
Update to "Release memory in the Sweep-And-Prune algorithm when several objects are removed from the world"
DanielChappuis/reactphysics3d@d2f7f6e28c093394 36c4aba05180343ff6794c29
1 parent 7d8dab9 commit 6e219a2

File tree

2 files changed

+107
-17
lines changed

2 files changed

+107
-17
lines changed

src/main/java/org/spout/physics/collision/broadphase/PairManager.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,6 @@ private boolean isDifferentPair(BodyPair pair1, int pair2ID1, int pair2ID2) {
7373
return pair2ID1 != pair1.getFirstBody().getID() || pair2ID2 != pair1.getSecondBody().getID();
7474
}
7575

76-
// Return the next power of two of a 32bits integer using a SWAR algorithm
77-
private int computeNextPowerOfTwo(int number) {
78-
number |= number >> 1;
79-
number |= number >> 2;
80-
number |= number >> 4;
81-
number |= number >> 8;
82-
number |= number >> 16;
83-
return number + 1;
84-
}
85-
8676
// Sort the bodies according to their IDs (smallest ID first)
8777
private BodyPair sortBodiesUsingID(CollisionBody body1, CollisionBody body2) {
8878
if (body1.getID() > body2.getID()) {
@@ -332,6 +322,21 @@ private void reallocatePairs() {
332322
mOffsetNextPair = newOffsetNextPair;
333323
}
334324

325+
/**
326+
* Returns the next power of two of a 32bit integer using a SWAR algorithm.
327+
*
328+
* @param number The number
329+
* @return The next power of two
330+
*/
331+
public static int computeNextPowerOfTwo(int number) {
332+
number |= number >> 1;
333+
number |= number >> 2;
334+
number |= number >> 4;
335+
number |= number >> 8;
336+
number |= number >> 16;
337+
return number + 1;
338+
}
339+
335340
/**
336341
* Represents a pair of bodies during the broad-phase collision detection.
337342
*/

src/main/java/org/spout/physics/collision/broadphase/SweepAndPruneAlgorithm.java

Lines changed: 92 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@
2626
*/
2727
package org.spout.physics.collision.broadphase;
2828

29+
import gnu.trove.iterator.TObjectIntIterator;
30+
import gnu.trove.list.TIntList;
31+
import gnu.trove.list.array.TIntArrayList;
2932
import gnu.trove.map.TObjectIntMap;
3033
import gnu.trove.map.hash.TObjectIntHashMap;
31-
import gnu.trove.stack.TIntStack;
32-
import gnu.trove.stack.array.TIntArrayStack;
3334

3435
import org.spout.physics.body.CollisionBody;
3536
import org.spout.physics.collision.CollisionDetection;
@@ -47,7 +48,7 @@ public class SweepAndPruneAlgorithm extends BroadPhaseAlgorithm {
4748
private final EndPoint[][] mEndPoints = {null, null, null};
4849
private int mNbBoxes = 0;
4950
private int mNbMaxBoxes = 0;
50-
private final TIntStack mFreeBoxIndices = new TIntArrayStack();
51+
private final TIntList mFreeBoxIndices = new TIntArrayList();
5152
private final TObjectIntMap<CollisionBody> mMapBodyToBoxIndex = new TObjectIntHashMap<>();
5253

5354
/**
@@ -82,7 +83,7 @@ public void addObject(CollisionBody body, AABB aabb) {
8283
}
8384
final int boxIndex;
8485
if (mFreeBoxIndices.size() != 0) {
85-
boxIndex = mFreeBoxIndices.pop();
86+
boxIndex = mFreeBoxIndices.removeAt(mFreeBoxIndices.size() - 1);
8687
} else {
8788
if (mNbBoxes == mNbMaxBoxes) {
8889
resizeArrays();
@@ -151,12 +152,15 @@ public void removeObject(CollisionBody body) {
151152
final EndPoint newMaxLimitEndPoint = mEndPoints[axis][indexLimitEndPoint - 2];
152153
newMaxLimitEndPoint.setValues(maxLimitEndPoint.getBoxID(), maxLimitEndPoint.isMin(), maxLimitEndPoint.getValue());
153154
}
154-
mFreeBoxIndices.push(boxIndex);
155+
mFreeBoxIndices.add(boxIndex);
155156
mMapBodyToBoxIndex.remove(body);
156157
mNbBoxes--;
158+
final int nextPowerOf2 = PairManager.computeNextPowerOfTwo((mNbBoxes - 1) / 100);
159+
if (nextPowerOf2 * 100 < mNbMaxBoxes) {
160+
shrinkArrays();
161+
}
157162
}
158163

159-
160164
@Override
161165
public void updateObject(CollisionBody body, AABB aabb) {
162166
final AABBInt aabbInt = new AABBInt(aabb);
@@ -340,6 +344,75 @@ private void resizeArrays() {
340344
mNbMaxBoxes = newNbMaxBoxes;
341345
}
342346

347+
// Shrinks the boxes and end-points arrays when too much memory is allocated.
348+
private void shrinkArrays() {
349+
final int nextPowerOf2 = PairManager.computeNextPowerOfTwo((mNbBoxes - 1) / 100);
350+
final int newNbMaxBoxes = mNbBoxes > 100 ? nextPowerOf2 * 100 : 100;
351+
final int nbEndPoints = mNbBoxes * 2 + NB_SENTINELS;
352+
final int newNbEndPoints = newNbMaxBoxes * 2 + NB_SENTINELS;
353+
if (newNbMaxBoxes >= mNbMaxBoxes) {
354+
throw new IllegalStateException("The new maximum number of boxes can't be greater or equal to the old one");
355+
}
356+
mFreeBoxIndices.sort();
357+
final TObjectIntMap<CollisionBody> newMapBodyToBoxIndex = new TObjectIntHashMap<>();
358+
final TObjectIntIterator<CollisionBody> it = mMapBodyToBoxIndex.iterator();
359+
while (it.hasNext()) {
360+
it.advance();
361+
final CollisionBody body = it.key();
362+
final int boxIndex = it.value();
363+
if (boxIndex >= mNbBoxes) {
364+
if (mFreeBoxIndices.isEmpty()) {
365+
throw new IllegalStateException("The list of free box indices can't be empty");
366+
}
367+
final int newBoxIndex = mFreeBoxIndices.removeAt(0);
368+
if (newBoxIndex >= mNbBoxes) {
369+
throw new IllegalStateException("The new box index can't be greater or equal to number of boxes");
370+
}
371+
final BoxAABB oldBox = mBoxes[boxIndex];
372+
final BoxAABB newBox = mBoxes[newBoxIndex];
373+
if (oldBox.getBody().getID() != body.getID()) {
374+
throw new IllegalStateException("The old box body ID can't be equal to body ID");
375+
}
376+
newBox.setBody(oldBox.getBody());
377+
for (int axis = 0; axis < 3; axis++) {
378+
newBox.setMin(axis, oldBox.getMin()[axis]);
379+
newBox.setMax(axis, oldBox.getMax()[axis]);
380+
final EndPoint minimumEndPoint = mEndPoints[axis][newBox.getMin()[axis]];
381+
final EndPoint maximumEndPoint = mEndPoints[axis][newBox.getMax()[axis]];
382+
if (minimumEndPoint.getBoxID() != boxIndex) {
383+
throw new IllegalStateException("The minimum end point box ID can't be equal to box index");
384+
}
385+
if (maximumEndPoint.getBoxID() != boxIndex) {
386+
throw new IllegalStateException("The maximum end point box ID can't be equal to box index");
387+
}
388+
minimumEndPoint.setBoxID(newBoxIndex);
389+
maximumEndPoint.setBoxID(newBoxIndex);
390+
}
391+
newMapBodyToBoxIndex.put(body, newBoxIndex);
392+
} else {
393+
newMapBodyToBoxIndex.put(body, boxIndex);
394+
}
395+
}
396+
if (newMapBodyToBoxIndex.size() != mMapBodyToBoxIndex.size()) {
397+
throw new IllegalStateException("The size of the new map from body to box index must be the same as the old one");
398+
}
399+
mMapBodyToBoxIndex.clear();
400+
mMapBodyToBoxIndex.putAll(newMapBodyToBoxIndex);
401+
final BoxAABB[] newBoxesArray = new BoxAABB[newNbMaxBoxes];
402+
final EndPoint[] newEndPointsXArray = new EndPoint[newNbEndPoints];
403+
final EndPoint[] newEndPointsYArray = new EndPoint[newNbEndPoints];
404+
final EndPoint[] newEndPointsZArray = new EndPoint[newNbEndPoints];
405+
System.arraycopy(mBoxes, 0, newBoxesArray, 0, mNbBoxes);
406+
System.arraycopy(mEndPoints[0], 0, newEndPointsXArray, 0, nbEndPoints);
407+
System.arraycopy(mEndPoints[1], 0, newEndPointsYArray, 0, nbEndPoints);
408+
System.arraycopy(mEndPoints[2], 0, newEndPointsZArray, 0, nbEndPoints);
409+
mBoxes = newBoxesArray;
410+
mEndPoints[0] = newEndPointsXArray;
411+
mEndPoints[1] = newEndPointsYArray;
412+
mEndPoints[2] = newEndPointsZArray;
413+
mNbMaxBoxes = newNbMaxBoxes;
414+
}
415+
343416
// Encodes a floating value into a integer value in order to work with integer
344417
// comparisons in the Sweep-And-Prune algorithm, for performance.
345418
// The main issue when encoding a floating number into an integer is keeping
@@ -380,6 +453,10 @@ private int getBoxID() {
380453
return boxID;
381454
}
382455

456+
private void setBoxID(int boxID) {
457+
this.boxID = boxID;
458+
}
459+
383460
private boolean isMin() {
384461
return isMin;
385462
}
@@ -404,7 +481,7 @@ public String toString() {
404481
}
405482
}
406483

407-
// Represents an AABB in the Sweep-And-Prune algorithm
484+
// Represents an AABB in the Sweep-And-Prune algorithm.
408485
private static class BoxAABB {
409486
private final int[] min = new int[3];
410487
private final int[] max = new int[3];
@@ -414,10 +491,18 @@ private int[] getMin() {
414491
return min;
415492
}
416493

494+
private void setMin(int i, int v) {
495+
min[i] = v;
496+
}
497+
417498
private int[] getMax() {
418499
return max;
419500
}
420501

502+
private void setMax(int i, int v) {
503+
max[i] = v;
504+
}
505+
421506
private CollisionBody getBody() {
422507
return body;
423508
}

0 commit comments

Comments
 (0)