Skip to content

Commit 169990c

Browse files
committed
perf: optimize broadphase pair management
1 parent a904b1f commit 169990c

File tree

5 files changed

+77
-34
lines changed

5 files changed

+77
-34
lines changed

src/Settings.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export class Settings {
5353
* future position based on the current displacement. This is a dimensionless
5454
* multiplier.
5555
*/
56-
static aabbMultiplier: number = 2.0;
56+
static aabbMultiplier: number = 4.0;
5757

5858
/**
5959
* A small length used as a collision and constraint tolerance. Usually it is

src/collision/BroadPhase.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,8 @@ export class BroadPhase {
188188
this.m_callback = addPairCallback;
189189

190190
// Perform tree queries for all moving proxies.
191-
while (this.m_moveBuffer.length > 0) {
192-
this.m_queryProxyId = this.m_moveBuffer.pop();
191+
for (let i = 0; i < this.m_moveBuffer.length; ++i) {
192+
this.m_queryProxyId = this.m_moveBuffer[i];
193193
if (this.m_queryProxyId === null) {
194194
continue;
195195
}
@@ -202,8 +202,8 @@ export class BroadPhase {
202202
this.m_tree.query(fatAABB, this.queryCallback);
203203
}
204204

205-
// Try to keep the tree balanced.
206-
// this.m_tree.rebalance(4);
205+
// Reset move buffer
206+
this.m_moveBuffer.length = 0;
207207
}
208208

209209
queryCallback = (proxyId: number): boolean => {
@@ -212,11 +212,15 @@ export class BroadPhase {
212212
return true;
213213
}
214214

215+
const moved = this.m_tree.wasMoved(proxyId);
216+
if (moved && proxyId > this.m_queryProxyId) {
217+
// Both proxies are moving. Avoid duplicate pairs.
218+
return true;
219+
}
220+
215221
const proxyIdA = Math.min(proxyId, this.m_queryProxyId);
216222
const proxyIdB = Math.max(proxyId, this.m_queryProxyId);
217223

218-
// TODO: Skip any duplicate pairs.
219-
220224
const userDataA = this.m_tree.getUserData(proxyIdA);
221225
const userDataB = this.m_tree.getUserData(proxyIdB);
222226

src/collision/DynamicTree.ts

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ export class TreeNode<T> {
4848
/** 0: leaf, -1: free node */
4949
height: number = -1;
5050

51+
moved: boolean = false;
52+
5153
constructor(id?: number) {
5254
this.id = id;
5355
}
@@ -104,6 +106,18 @@ export class DynamicTree<T> {
104106
return node.userData;
105107
}
106108

109+
wasMoved(proxyId: number): boolean {
110+
const node = this.m_nodes[proxyId];
111+
_ASSERT && console.assert(!!node);
112+
return node.moved;
113+
}
114+
115+
clearMoved(proxyId: number): void {
116+
const node = this.m_nodes[proxyId];
117+
_ASSERT && console.assert(!!node);
118+
node.moved = false;
119+
}
120+
107121
/**
108122
* Get the fat AABB for a node id.
109123
*
@@ -122,7 +136,8 @@ export class DynamicTree<T> {
122136
node.parent = null;
123137
node.child1 = null;
124138
node.child2 = null;
125-
node.height = -1;
139+
node.height = 0;
140+
node.moved = false;
126141
this.m_nodes[node.id] = node;
127142
return node;
128143
}
@@ -152,6 +167,7 @@ export class DynamicTree<T> {
152167

153168
node.userData = userData;
154169
node.height = 0;
170+
node.moved = true;
155171

156172
this.insertLeaf(node);
157173

@@ -176,48 +192,65 @@ export class DynamicTree<T> {
176192
* fattened AABB, then the proxy is removed from the tree and re-inserted.
177193
* Otherwise the function returns immediately.
178194
*
179-
* @param d Displacement
195+
* @param displacement Displacement
180196
*
181197
* @return true if the proxy was re-inserted.
182198
*/
183-
moveProxy(id: number, aabb: AABB, d: Vec2): boolean {
199+
moveProxy(id: number, aabb: AABB, displacement: Vec2): boolean {
184200
_ASSERT && console.assert(AABB.isValid(aabb));
185-
_ASSERT && console.assert(!d || Vec2.isValid(d));
201+
_ASSERT && console.assert(!displacement || Vec2.isValid(displacement));
186202

187203
const node = this.m_nodes[id];
188204

189205
_ASSERT && console.assert(!!node);
190206
_ASSERT && console.assert(node.isLeaf());
191207

192-
if (node.aabb.contains(aabb)) {
193-
return false;
194-
}
195-
196-
this.removeLeaf(node);
197-
198-
node.aabb.set(aabb);
199-
200208
// Extend AABB.
201-
aabb = node.aabb;
202-
AABB.extend(aabb, Settings.aabbExtension);
209+
const fatAABB = new AABB()
210+
fatAABB.set(aabb);
211+
AABB.extend(fatAABB, Settings.aabbExtension);
203212

204-
// Predict AABB displacement.
213+
// Predict AABB movement
205214
// const d = Vec2.mul(Settings.aabbMultiplier, displacement);
206215

207-
if (d.x < 0.0) {
208-
aabb.lowerBound.x += d.x * Settings.aabbMultiplier;
216+
if (displacement.x < 0.0) {
217+
fatAABB.lowerBound.x += displacement.x * Settings.aabbMultiplier;
209218
} else {
210-
aabb.upperBound.x += d.x * Settings.aabbMultiplier;
219+
fatAABB.upperBound.x += displacement.x * Settings.aabbMultiplier;
211220
}
212221

213-
if (d.y < 0.0) {
214-
aabb.lowerBound.y += d.y * Settings.aabbMultiplier;
222+
if (displacement.y < 0.0) {
223+
fatAABB.lowerBound.y += displacement.y * Settings.aabbMultiplier;
215224
} else {
216-
aabb.upperBound.y += d.y * Settings.aabbMultiplier;
225+
fatAABB.upperBound.y += displacement.y * Settings.aabbMultiplier;
226+
}
227+
228+
const treeAABB = node.aabb;
229+
if (treeAABB.contains(aabb)) {
230+
// The tree AABB still contains the object, but it might be too large.
231+
// Perhaps the object was moving fast but has since gone to sleep.
232+
// The huge AABB is larger than the new fat AABB.
233+
const hugeAABB = new AABB();
234+
hugeAABB.set(fatAABB);
235+
AABB.extend(hugeAABB, 4.0 * Settings.aabbExtension);
236+
237+
if (hugeAABB.contains(treeAABB)) {
238+
// The tree AABB contains the object AABB and the tree AABB is
239+
// not too large. No tree update needed.
240+
return false;
241+
}
242+
243+
// Otherwise the tree AABB is huge and needs to be shrunk
217244
}
218245

246+
this.removeLeaf(node);
247+
248+
node.aabb = fatAABB;
249+
219250
this.insertLeaf(node);
220251

252+
node.moved = true;
253+
221254
return true;
222255
}
223256

src/dynamics/Body.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -590,13 +590,19 @@ export class Body {
590590
* Update fixtures in broad-phase.
591591
*/
592592
synchronizeFixtures(): void {
593-
const xf = Transform.identity();
593+
const broadPhase = this.m_world.m_broadPhase;
594594

595-
this.m_sweep.getTransform(xf, 0);
595+
if (this.m_awakeFlag) {
596+
const xf = Transform.identity();
597+
this.m_sweep.getTransform(xf, 0);
596598

597-
const broadPhase = this.m_world.m_broadPhase;
598-
for (let f = this.m_fixtureList; f; f = f.m_next) {
599-
f.synchronize(broadPhase, xf, this.m_xf);
599+
for (let f = this.m_fixtureList; f; f = f.m_next) {
600+
f.synchronize(broadPhase, xf, this.m_xf);
601+
}
602+
} else {
603+
for (let f = this.m_fixtureList; f; f = f.m_next) {
604+
f.synchronize(broadPhase, this.m_xf, this.m_xf);
605+
}
600606
}
601607
}
602608

src/dynamics/Fixture.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ export class Fixture {
404404

405405
proxy.aabb.combine(aabb1, aabb2);
406406

407-
const displacement = Vec2.sub(xf2.p, xf1.p);
407+
const displacement = Vec2.sub(aabb2.getCenter(), aabb1.getCenter());
408408

409409
broadPhase.moveProxy(proxy.proxyId, proxy.aabb, displacement);
410410
}

0 commit comments

Comments
 (0)