Skip to content

BroadPhase

Generated from engine/collision/broad_phase.h at 862e08b. The text under each declaration is the header's own comment, word for word. About the reference says how these pages are made.

Browse the collision module

#include "engine/collision/broad_phase.h" · namespace labrador

Which pairs are worth measuring.

PHILOSOPHY's Collision section promises this. Without it the pair enumeration is all-pairs, O(n^2) in the object count, and bench/ says what that costs: on a release build, an all-pairs Scene::resolve is 25 us at 64 objects, 380 us at 256, 6.1 ms at 1,024 and 106 ms at 4,096 - which is six times the whole 60 Hz frame budget, from one call, at an object count a 2D engine has no business finding difficult.

The same benchmark is why there is no spatial index in front of the *render* cull. That loop is 6.2 ns per object and flat from 64 to 4,096 - 102 us for four views over four thousand objects, well under one percent of a frame. An index there would be ceremony.

WHY A UNIFORM GRID. The world it is built for is a plane of static geometry of broadly similar size, mostly not touching, with characters and projectiles moving through it. That is the case a uniform grid is best at and a tree is worst at: no rebalancing, no per-frame allocation once the buffers are warm, and cell membership is arithmetic rather than a descent. Where it degrades - one object far larger than the rest - it degrades towards the all-pairs sweep rather than towards anything worse, and the large-object list below bounds even that.

WHAT IT GUARANTEES. Exactly the pairs the all-pairs sweep considers, minus ones whose bounding boxes cannot overlap, in the same ascending (a, b) index order. Same order matters: a contact list is a value the game reads, and dispatch re-measures each pair in list order, so a different order is a different resolution. tests/collision asserts the two enumerations agree object-for-object on randomised scenes.

void find_pairs(std::span<CollisionObject* const> objects,
std::vector<std::pair<int, int>>& pairs);

Fills pairs with the index pairs worth measuring, ascending by first index then second, with no duplicates.

pairs is cleared and reused, and so is every buffer inside this object - which is what makes the steady state allocation-free (PHILOSOPHY, Performance: per-frame code performs no heap allocation). Objects flagged for deletion are skipped here rather than by the caller, because a pair that cannot produce a contact should not reach the grid at all.

float cell_size() const;

The cell edge the last call chose, in world units. For tests and for anyone wondering why a scene got slow.

int large_object_count() const;

How many objects the last call put in the large-object list - the ones whose bounding box covers so much of the grid that indexing them costs more than testing them against everything.

std::vector<std::pair<int, int>>& pairs_buffer();

The pair list this object keeps across frames, so find_contacts has somewhere allocation-free to receive one. A caller driving find_pairs itself may pass its own vector instead.

Named in the public declarations above: CollisionObject.

The files that include this header directly. A file can also reach it through another header.

Development documentation (unreleased). Built from Labrador 862e08b of 2026-10-08.