diff --git a/samples/main.cpp b/samples/main.cpp index 75fcfe97..e0aa05c9 100644 --- a/samples/main.cpp +++ b/samples/main.cpp @@ -372,7 +372,7 @@ static void ScrollCallback(GLFWwindow* window, double dx, double dy) } } -BOX2D_API bool g_parallel; +BOX2D_API bool b2_parallel; static void UpdateUI() { @@ -397,7 +397,7 @@ static void UpdateUI() ImGui::Checkbox("Sleep", &s_settings.m_enableSleep); ImGui::Checkbox("Warm Starting", &s_settings.m_enableWarmStarting); ImGui::Checkbox("Continuous", &s_settings.m_enableContinuous); - ImGui::Checkbox("Parallel", &g_parallel); + ImGui::Checkbox("Parallel", &b2_parallel); ImGui::Separator(); diff --git a/src/atomic.inl b/src/atomic.inl index 8b310155..01aec5e3 100644 --- a/src/atomic.inl +++ b/src/atomic.inl @@ -1,82 +1,9 @@ #include - -#if 0 && defined(_MSC_VER) && !defined(__clang__) - -#include - -// Returns the initial value -static inline long atomic_fetch_add_long(volatile long* obj, long val) -{ - return _InterlockedExchangeAdd(obj, val); -} - -// Returns the initial value -static inline long atomic_fetch_sub_long(volatile long* obj, long val) -{ - return _InterlockedExchangeAdd(obj, -val); -} - -// Returns the initial value -static inline long atomic_store_long(volatile long* obj, long val) -{ - return _InterlockedExchange(obj, val); -} - -static inline long atomic_load_long(const volatile long* obj) -{ - return *obj; -} - -static inline bool atomic_compare_exchange_strong_long(volatile long* obj, long* expected, int32_t desired) -{ - long current = _InterlockedCompareExchange(obj, desired, *expected); - if (current == *expected) - { - return true; - } - - *expected = current; - return false; -} - -static inline bool atomic_compare_exchange_weak_long(volatile long* obj, long* expected, int32_t desired) -{ - long current = _InterlockedCompareExchange(obj, desired, *expected); - if (current == *expected) - { - return true; - } - - *expected = current; - return false; -} - -#else - #include -#define atomic_fetch_add_long atomic_fetch_add -#define atomic_fetch_sub_long atomic_fetch_sub -#define atomic_store_long atomic_store -#define atomic_load_long atomic_load -#define atomic_compare_exchange_strong_long atomic_compare_exchange_strong -#define atomic_compare_exchange_weak_long atomic_compare_exchange_weak - -#endif - -// Atomically store the minimum two values -// *ptr = min(*ptr, b) -static inline void b2AtomicStoreMin(B2_ATOMIC long* ptr, long b) -{ - long a = atomic_load_long(ptr); - while (a > b) - { - bool success = atomic_compare_exchange_weak_long(ptr, &a, b); - if (success) - { - return; - } - - // otherwise `a` now holds the current value stored in `ptr` - } -} +//#define atomic_fetch_add_long atomic_fetch_add +//#define atomic_fetch_sub_long atomic_fetch_sub +//#define atomic_store_long atomic_store +//#define atomic_load_long atomic_load +//#define atomic_compare_exchange_strong_long atomic_compare_exchange_strong +//#define atomic_compare_exchange_weak_long atomic_compare_exchange_weak diff --git a/src/broad_phase.c b/src/broad_phase.c index d0ace27c..10aa7f66 100644 --- a/src/broad_phase.c +++ b/src/broad_phase.c @@ -5,14 +5,20 @@ #include "allocate.h" #include "array.h" +#include "body.h" +#include "contact.h" #include "core.h" +#include "shape.h" +#include "stack_allocator.h" +#include "world.h" #include "box2d/aabb.h" #include "box2d/timer.h" +#include #include -void b2BroadPhase_Create(b2BroadPhase* bp, b2AddPairFcn* fcn, void* fcnContext) +void b2BroadPhase_Create(b2BroadPhase* bp) { bp->proxyCount = 0; @@ -21,16 +27,14 @@ void b2BroadPhase_Create(b2BroadPhase* bp, b2AddPairFcn* fcn, void* fcnContext) bp->moveCount = 0; bp->moveBuffer = (int32_t*)b2Alloc(bp->moveCapacity * sizeof(int32_t)); + bp->moveResults = NULL; + bp->movePairs = NULL; + bp->movePairCapacity = 0; + bp->movePairIndex = 0; + // TODO_ERIN initial size from b2WorldDef bp->pairSet = b2CreateSet(32); - bp->addPairFcn = fcn; - bp->fcnContext = fcnContext; - - bp->queryProxyKey = B2_NULL_INDEX; - bp->queryTreeType = b2_dynamicBody; - bp->queryShapeIndex = B2_NULL_INDEX; - for (int32_t i = 0; i < b2_bodyTypeCount; ++i) { bp->trees[i] = b2DynamicTree_Create(); @@ -105,8 +109,8 @@ void b2BroadPhase_MoveProxy(b2BroadPhase* bp, int32_t proxyKey, b2AABB aabb, b2A B2_ASSERT(typeIndex == b2_dynamicBody || typeIndex == b2_kinematicBody); - bool buffer = b2DynamicTree_MoveProxy(bp->trees + typeIndex, proxyId, aabb, outFatAABB); - if (buffer) + bool shouldBuffer = b2DynamicTree_MoveProxy(bp->trees + typeIndex, proxyId, aabb, outFatAABB); + if (shouldBuffer) { b2BufferMove(bp, proxyKey); } @@ -119,34 +123,57 @@ void b2BroadPhase_EnlargeProxy(b2BroadPhase* bp, int32_t proxyKey, b2AABB aabb, B2_ASSERT(typeIndex == b2_dynamicBody || typeIndex == b2_kinematicBody); - bool buffer = b2DynamicTree_EnlargeProxy(bp->trees + typeIndex, proxyId, aabb, outFatAABB); - if (buffer) + bool shouldBuffer = b2DynamicTree_EnlargeProxy(bp->trees + typeIndex, proxyId, aabb, outFatAABB); + if (shouldBuffer) { b2BufferMove(bp, proxyKey); } } +typedef struct b2MovePair +{ + int32_t shapeIndexA; + int32_t shapeIndexB; + b2MovePair* next; + bool heap; +} b2MovePair; + +typedef struct b2MoveResult +{ + b2MovePair* pairList; +} b2MoveResult; + +typedef struct b2QueryPairContext +{ + b2World* world; + b2MoveResult* moveResult; + b2BodyType queryTreeType; + int32_t queryProxyKey; + int32_t queryShapeIndex; +} b2QueryPairContext; + // This is called from b2DynamicTree::Query when we are gathering pairs. -static bool b2QueryCallback(int32_t proxyId, int32_t shapeIndex, void* context) +static bool b2PairQueryCallback(int32_t proxyId, int32_t shapeIndex, void* context) { - b2BroadPhase* bp = (b2BroadPhase*)context; + b2QueryPairContext* queryContext = context; + b2BroadPhase* bp = &queryContext->world->broadPhase; - int32_t proxyKey = B2_PROXY_KEY(proxyId, bp->queryTreeType); + int32_t proxyKey = B2_PROXY_KEY(proxyId, queryContext->queryTreeType); // A proxy cannot form a pair with itself. - if (proxyKey == bp->queryProxyKey) + if (proxyKey == queryContext->queryProxyKey) { return true; } - bool moved = b2DynamicTree_WasMoved(bp->trees + bp->queryTreeType, proxyId); - if (moved && proxyKey > bp->queryProxyKey) + bool moved = b2DynamicTree_WasMoved(bp->trees + queryContext->queryTreeType, proxyId); + if (moved && proxyKey > queryContext->queryProxyKey) { // Both proxies are moving. Avoid duplicate pairs. return true; } - uint64_t pairKey = B2_SHAPE_PAIR_KEY(shapeIndex, bp->queryShapeIndex); + uint64_t pairKey = B2_SHAPE_PAIR_KEY(shapeIndex, queryContext->queryShapeIndex); if (b2ContainsKey(&bp->pairSet, pairKey)) { // contact exists @@ -154,30 +181,86 @@ static bool b2QueryCallback(int32_t proxyId, int32_t shapeIndex, void* context) } int32_t shapeIndexA, shapeIndexB; - if (proxyKey < bp->queryProxyKey) + if (proxyKey < queryContext->queryProxyKey) { shapeIndexA = shapeIndex; - shapeIndexB = bp->queryShapeIndex; + shapeIndexB = queryContext->queryShapeIndex; } else { - shapeIndexA = bp->queryShapeIndex; + shapeIndexA = queryContext->queryShapeIndex; shapeIndexB = shapeIndex; } - bp->addPairFcn(shapeIndexA, shapeIndexB, bp->fcnContext); + b2World* world = queryContext->world; + + B2_ASSERT(0 <= shapeIndexA && shapeIndexA < world->shapePool.capacity); + B2_ASSERT(0 <= shapeIndexB && shapeIndexB < world->shapePool.capacity); + + b2Shape* shapeA = world->shapes + shapeIndexA; + b2Shape* shapeB = world->shapes + shapeIndexB; + + // Are the fixtures on the same body? + if (shapeA->bodyIndex == shapeB->bodyIndex) + { + return true; + } + + if (b2ShouldShapesCollide(shapeA->filter, shapeB->filter) == false) + { + return true; + } + + int32_t bodyIndexA = shapeA->bodyIndex; + int32_t bodyIndexB = shapeB->bodyIndex; + b2Body* bodyA = world->bodies + bodyIndexA; + b2Body* bodyB = world->bodies + bodyIndexB; + + // Does a joint override collision? Is at least one body dynamic? + // TODO_ERIN this could be a hash set + if (b2ShouldBodiesCollide(world, bodyA, bodyB) == false) + { + return true; + } + + int pairIndex = atomic_fetch_add(&bp->movePairIndex, 1); + + b2MovePair* pair; + if (pairIndex < bp->movePairCapacity) + { + pair = bp->movePairs + pairIndex; + pair->heap = false; + } + else + { + pair = b2Alloc(sizeof(b2MovePair)); + } + + pair->shapeIndexA = shapeIndexA; + pair->shapeIndexB = shapeIndexB; + pair->next = queryContext->moveResult->pairList; + queryContext->moveResult->pairList = pair; // continue the query return true; } -// TODO_ERIN these queries could be done in parallel in the island solver. Use a hash table to skip existing pairs. -// then any truly new pairs can be added in serially when island is completed. -void b2BroadPhase_UpdatePairs(b2BroadPhase* bp) +void b2FindPairsTask(int32_t startIndex, int32_t endIndex, uint32_t threadIndex, void* context) { - // Perform tree queries for all moving proxies. This fills the pair buffer. - for (int32_t i = 0; i < bp->moveCount; ++i) + B2_MAYBE_UNUSED(threadIndex); + + b2World* world = context; + b2BroadPhase* bp = &world->broadPhase; + + b2QueryPairContext queryContext; + queryContext.world = world; + + for (int32_t i = startIndex; i < endIndex; ++i) { + // Initialize move result for this moved proxy + queryContext.moveResult = bp->moveResults + i; + queryContext.moveResult->pairList = NULL; + int32_t proxyKey = bp->moveBuffer[i]; if (proxyKey == B2_NULL_INDEX) { @@ -187,34 +270,69 @@ void b2BroadPhase_UpdatePairs(b2BroadPhase* bp) int32_t proxyType = B2_PROXY_TYPE(proxyKey); int32_t proxyId = B2_PROXY_ID(proxyKey); - bp->queryProxyKey = proxyKey; + queryContext.queryProxyKey = proxyKey; const b2DynamicTree* baseTree = bp->trees + proxyType; // We have to query the tree with the fat AABB so that // we don't fail to create a contact that may touch later. b2AABB fatAABB = b2DynamicTree_GetFatAABB(baseTree, proxyId); - bp->queryShapeIndex = b2DynamicTree_GetUserData(baseTree, proxyId); + queryContext.queryShapeIndex = b2DynamicTree_GetUserData(baseTree, proxyId); - // Query tree and invoke b2AddPairFcn callback. A callback inside a callback. - // TODO_ERIN test with filtering + // Query trees if (proxyType == b2_dynamicBody) { - bp->queryTreeType = b2_dynamicBody; - b2DynamicTree_Query(bp->trees + b2_dynamicBody, fatAABB, b2QueryCallback, bp); - bp->queryTreeType = b2_kinematicBody; - b2DynamicTree_Query(bp->trees + b2_kinematicBody, fatAABB, b2QueryCallback, bp); - bp->queryTreeType = b2_staticBody; - b2DynamicTree_Query(bp->trees + b2_staticBody, fatAABB, b2QueryCallback, bp); + queryContext.queryTreeType = b2_dynamicBody; + b2DynamicTree_Query(bp->trees + b2_dynamicBody, fatAABB, b2PairQueryCallback, &queryContext); + queryContext.queryTreeType = b2_kinematicBody; + b2DynamicTree_Query(bp->trees + b2_kinematicBody, fatAABB, b2PairQueryCallback, &queryContext); + queryContext.queryTreeType = b2_staticBody; + b2DynamicTree_Query(bp->trees + b2_staticBody, fatAABB, b2PairQueryCallback, &queryContext); } else if (proxyType == b2_kinematicBody) { - bp->queryTreeType = b2_dynamicBody; - b2DynamicTree_Query(bp->trees + b2_dynamicBody, fatAABB, b2QueryCallback, bp); + queryContext.queryTreeType = b2_dynamicBody; + b2DynamicTree_Query(bp->trees + b2_dynamicBody, fatAABB, b2PairQueryCallback, &queryContext); } } +} + +extern bool b2_parallel; + +void b2BroadPhase_UpdatePairs(b2World* world) +{ + b2BroadPhase* bp = &world->broadPhase; + + int32_t moveCount = bp->moveCount; + + if (moveCount == 0) + { + return; + } + + b2StackAllocator* alloc = world->stackAllocator; + + bp->moveResults = b2AllocateStackItem(alloc, moveCount * sizeof(b2MoveResult), "move results"); + bp->movePairCapacity = 8 * moveCount; + bp->movePairs = b2AllocateStackItem(alloc, bp->movePairCapacity * sizeof(b2MovePair), "move pairs"); + bp->movePairIndex = 0; + + if (b2_parallel) + { + int32_t minRange = 1; + world->enqueueTask(&b2FindPairsTask, moveCount, minRange, world, world->userTaskContext); + world->finishTasks(world->userTaskContext); + } + else + { + b2FindPairsTask(0, moveCount, 0, world); + } + + // Single-threaded work + // - Clear move flags + // - Create contacts in deterministic order + b2Shape* shapes = world->shapes; - // Clear move flags for (int32_t i = 0; i < bp->moveCount; ++i) { int32_t proxyKey = bp->moveBuffer[i]; @@ -227,10 +345,44 @@ void b2BroadPhase_UpdatePairs(b2BroadPhase* bp) int32_t proxyId = B2_PROXY_ID(proxyKey); b2DynamicTree_ClearMoved(bp->trees + typeIndex, proxyId); + + b2MoveResult* result = bp->moveResults + i; + b2MovePair* pair = result->pairList; + while (pair != NULL) + { + // TODO_ERIN Check user filtering. + // if (m_contactFilter && m_contactFilter->ShouldCollide(fixtureA, fixtureB) == false) + //{ + // return; + //} + + int32_t shapeIndexA = pair->shapeIndexA; + int32_t shapeIndexB = pair->shapeIndexB; + B2_ASSERT(0 <= shapeIndexA && shapeIndexA < world->shapePool.capacity); + B2_ASSERT(0 <= shapeIndexB && shapeIndexB < world->shapePool.capacity); + + b2CreateContact(world, shapes + shapeIndexA, shapes + shapeIndexB); + + if (pair->heap) + { + b2MovePair* temp = pair; + pair = pair->next; + b2Free(temp, sizeof(b2MovePair)); + } + else + { + pair = pair->next; + } + } } // Reset move buffer bp->moveCount = 0; + + b2FreeStackItem(alloc, bp->movePairs); + bp->movePairs = NULL; + b2FreeStackItem(alloc, bp->moveResults); + bp->moveResults = NULL; } bool b2BroadPhase_TestOverlap(const b2BroadPhase* bp, int32_t proxyKeyA, int32_t proxyKeyB) diff --git a/src/broad_phase.h b/src/broad_phase.h index 537486af..612e2fe6 100644 --- a/src/broad_phase.h +++ b/src/broad_phase.h @@ -9,7 +9,10 @@ #define B2_REBUILD_TREE 1 typedef struct b2Shape b2Shape; -typedef void b2AddPairFcn(int32_t shapeIndexA, int32_t shapeIndexB, void* context); +typedef struct b2MovePair b2MovePair; +typedef struct b2MoveResult b2MoveResult; +typedef struct b2StackAllocator b2StackAllocator; +typedef struct b2World b2World; // Store the proxy type in the lower 4 bits of the proxy key. This leaves 28 bits for the id. #define B2_PROXY_TYPE(KEY) ((b2BodyType)((KEY)&0xF)) @@ -28,17 +31,16 @@ typedef struct b2BroadPhase int32_t moveCapacity; int32_t moveCount; - b2Set pairSet; + b2MoveResult* moveResults; + b2MovePair* movePairs; + int32_t movePairCapacity; + _Atomic int movePairIndex; - b2AddPairFcn* addPairFcn; - void* fcnContext; + b2Set pairSet; - b2BodyType queryTreeType; - int32_t queryProxyKey; - int32_t queryShapeIndex; } b2BroadPhase; -void b2BroadPhase_Create(b2BroadPhase* bp, b2AddPairFcn* fcn, void* fcnContext); +void b2BroadPhase_Create(b2BroadPhase* bp); void b2BroadPhase_Destroy(b2BroadPhase* bp); int32_t b2BroadPhase_CreateProxy(b2BroadPhase* bp, b2BodyType bodyType, b2AABB aabb, uint32_t categoryBits, int32_t shapeIndex, b2AABB* outFatAABB); @@ -51,7 +53,7 @@ void b2BroadPhase_RebuildTrees(b2BroadPhase* bp); int32_t b2BroadPhase_GetShapeIndex(b2BroadPhase* bp, int32_t proxyKey); -void b2BroadPhase_UpdatePairs(b2BroadPhase* bp); +void b2BroadPhase_UpdatePairs(b2World* world); bool b2BroadPhase_TestOverlap(const b2BroadPhase* bp, int32_t proxyKeyA, int32_t proxyKeyB); static inline b2AABB b2BroadPhase_GetFatAABB(b2BroadPhase* bp, int32_t proxyKey) diff --git a/src/contact.c b/src/contact.c index 27275514..d7582e53 100644 --- a/src/contact.c +++ b/src/contact.c @@ -258,7 +258,7 @@ void b2DestroyContact(b2World* world, b2Contact* contact) b2FreeObject(&world->contactPool, &contact->object); } -bool b2ShouldCollide(b2Filter filterA, b2Filter filterB) +bool b2ShouldShapesCollide(b2Filter filterA, b2Filter filterB) { if (filterA.groupIndex == filterB.groupIndex && filterA.groupIndex != 0) { diff --git a/src/contact.h b/src/contact.h index 7dabe48f..b0be763f 100644 --- a/src/contact.h +++ b/src/contact.h @@ -93,7 +93,7 @@ void b2InitializeContactRegisters(); void b2CreateContact(b2World* world, b2Shape* shapeA, b2Shape* shapeB); void b2DestroyContact(b2World* world, b2Contact* contact); -bool b2ShouldCollide(b2Filter filterA, b2Filter filterB); +bool b2ShouldShapesCollide(b2Filter filterA, b2Filter filterB); void b2Contact_Update(b2World* world, b2Contact* contact, b2Shape* shapeA, b2Body* bodyA, b2Shape* shapeB, b2Body* bodyB); diff --git a/src/dynamic_tree.c b/src/dynamic_tree.c index 77b5fb43..0f230c74 100644 --- a/src/dynamic_tree.c +++ b/src/dynamic_tree.c @@ -466,7 +466,7 @@ static void b2InsertLeaf(b2DynamicTree* tree, int32_t leaf) index = tree->nodes[index].parent; } - // Validate(); + b2DynamicTree_Validate(tree); } static void b2RemoveLeaf(b2DynamicTree* tree, int32_t leaf) @@ -619,13 +619,13 @@ bool b2DynamicTree_MoveProxy(b2DynamicTree* tree, int32_t proxyId, b2AABB aabb, b2InsertLeaf(tree, proxyId); bool alreadyMoved = tree->nodes[proxyId].moved; - tree->nodes[proxyId].moved = true; if (alreadyMoved) { return false; } + tree->nodes[proxyId].moved = true; return true; } @@ -673,6 +673,14 @@ bool b2DynamicTree_EnlargeProxy(b2DynamicTree* tree, int32_t proxyId, b2AABB aab } *outFatAABB = fatAABB; + + bool alreadyMoved = nodes[proxyId].moved; + if (alreadyMoved) + { + return false; + } + + nodes[proxyId].moved = true; return true; } diff --git a/src/world.c b/src/world.c index 93d4acf3..998f55b2 100644 --- a/src/world.c +++ b/src/world.c @@ -27,8 +27,8 @@ #include -b2World g_worlds[b2_maxWorlds]; -bool g_parallel = true; +b2World b2_worlds[b2_maxWorlds]; +bool b2_parallel = true; // Per thread task storage typedef struct b2TaskContext @@ -41,7 +41,7 @@ typedef struct b2TaskContext b2World* b2GetWorldFromId(b2WorldId id) { B2_ASSERT(0 <= id.index && id.index < b2_maxWorlds); - b2World* world = g_worlds + id.index; + b2World* world = b2_worlds + id.index; B2_ASSERT(id.revision == world->revision); return world; } @@ -49,7 +49,7 @@ b2World* b2GetWorldFromId(b2WorldId id) b2World* b2GetWorldFromIndex(int16_t index) { B2_ASSERT(0 <= index && index < b2_maxWorlds); - b2World* world = g_worlds + index; + b2World* world = b2_worlds + index; B2_ASSERT(world->blockAllocator != NULL); return world; } @@ -66,6 +66,7 @@ static void b2DefaultFinishTasksFcn(void* userContext) B2_MAYBE_UNUSED(userContext); } +#if 0 static void b2AddPair(int32_t shapeIndexA, int32_t shapeIndexB, void* context) { b2World* world = (b2World*)context; @@ -82,7 +83,7 @@ static void b2AddPair(int32_t shapeIndexA, int32_t shapeIndexB, void* context) return; } - if (b2ShouldCollide(shapeA->filter, shapeB->filter) == false) + if (b2ShouldShapesCollide(shapeA->filter, shapeB->filter) == false) { return; } @@ -93,6 +94,7 @@ static void b2AddPair(int32_t shapeIndexA, int32_t shapeIndexB, void* context) b2Body* bodyB = world->bodies + bodyIndexB; // Does a joint override collision? Is at least one body dynamic? + // TODO_ERIN this could be a hash set if (b2ShouldBodiesCollide(world, bodyA, bodyB) == false) { return; @@ -103,17 +105,17 @@ static void b2AddPair(int32_t shapeIndexA, int32_t shapeIndexB, void* context) //{ // return; //} - //_Thread_local int test; - // test = 1; + b2CreateContact(world, shapeA, shapeB); } +#endif b2WorldId b2CreateWorld(const b2WorldDef* def) { b2WorldId id = b2_nullWorldId; for (int16_t i = 0; i < b2_maxWorlds; ++i) { - if (g_worlds[i].blockAllocator == NULL) + if (b2_worlds[i].blockAllocator == NULL) { id.index = i; break; @@ -128,7 +130,7 @@ b2WorldId b2CreateWorld(const b2WorldDef* def) b2InitializeContactRegisters(); b2World empty = {0}; - b2World* world = g_worlds + id.index; + b2World* world = b2_worlds + id.index; *world = empty; world->index = id.index; @@ -136,7 +138,7 @@ b2WorldId b2CreateWorld(const b2WorldDef* def) world->blockAllocator = b2CreateBlockAllocator(); world->stackAllocator = b2CreateStackAllocator(def->stackAllocatorCapacity); - b2BroadPhase_Create(&world->broadPhase, b2AddPair, world); + b2BroadPhase_Create(&world->broadPhase); // pools world->bodyPool = b2CreatePool(sizeof(b2Body), B2_MAX(def->bodyCapacity, 1)); @@ -340,7 +342,7 @@ static void b2Collide(b2World* world) b2SetBitCountAndClear(&world->taskContextArray[i].contactBitSet, awakeContactCount); } - if (g_parallel) + if (b2_parallel) { int32_t minRange = 32; world->enqueueTask(&b2CollideTask, awakeContactCount, minRange, world, world->userTaskContext); @@ -465,7 +467,7 @@ static void b2Solve(b2World* world, b2StepContext* context) b2TracyCZoneNC(island_solver, "Island Solver", b2_colorSeaGreen, true); - if (g_parallel) + if (b2_parallel) { int32_t minRange = 1; world->enqueueTask(&b2IslandParallelForTask, count, minRange, world, world->userTaskContext); @@ -521,9 +523,6 @@ static void b2Solve(b2World* world, b2StepContext* context) b2FreeStackItem(world->stackAllocator, islands); - // Look for new contacts - b2BroadPhase_UpdatePairs(&world->broadPhase); - world->profile.broadphase = b2GetMilliseconds(&timer); b2TracyCZoneEnd(broad_phase); @@ -553,15 +552,11 @@ void b2World_Step(b2WorldId worldId, float timeStep, int32_t velocityIterations, b2Timer stepTimer = b2CreateTimer(); // If new shapes were added, we need to find the new contacts. - if (world->newContacts) - { - b2TracyCZoneNC(new_contacts, "New Contacts", b2_colorFuchsia, true); + b2TracyCZoneNC(new_contacts, "New Contacts", b2_colorFuchsia, true); - b2BroadPhase_UpdatePairs(&world->broadPhase); - world->newContacts = false; + b2BroadPhase_UpdatePairs(world); - b2TracyCZoneEnd(new_contacts); - } + b2TracyCZoneEnd(new_contacts); // TODO_ERIN atomic world->locked = true;