Report the overlap akgl_collide_rectangles could not see
It asked whether either rectangle enclosed one of the other's four corners -- eight akgl_collide_point_rectangle calls, stopping at the first hit. That is a different question from "do these overlap", and it has the wrong answer for one arrangement: a tall thin rectangle crossing a short wide one overlaps in a plus sign with no corner of either inside the other, and all eight tests said no. A long thin platform crossing a tall thin character is exactly that shape, so this is a shape a 2D game produces, not a curiosity. util.h carried an @note describing it and docs/18-utilities.md had a diagram of it, both under the heading of a limitation rather than a defect, and there was no test for it at all -- nor for full containment, nor for a shared edge. It is four comparisons now, on both axes. `<=` rather than `<` because akgl_collide_point_rectangle is inclusive on all four edges and these two have always agreed that touching counts; a span test written with `<` would have silently changed a contract both the header and the manual state. The test was written first and failed on the cross before the fix went in. Two answers change for a caller upgrading, and both are in the header note and the chapter: - The cross reports `true`, which is the point. - The comparison is in float rather than through akgl_Point's int members, so a sub-pixel overlap is no longer truncated away. A pickup test that was accidentally forgiving by up to a pixel is no longer forgiving. Both tutorials use this for coins and hazards; both still pass. Faster as a side effect rather than a goal, and worth recording because the numbers move a documented budget: 24.9 ns -> 6.1 overlapping, 57.9 -> 6.1 disjoint, and the all-pairs sweep over 64 actors 115 us -> 12.2. The disjoint case gained most because it was the one that ran all eight tests before answering. The three moved rows are re-recorded in PERFORMANCE.md and nothing else is. akgl_rectangle_points is untouched by this change and reads 6.1 in the same run against the 4.0 recorded, so 6 ns is this run's floor and the new figure means "too cheap to measure" rather than "exactly 6.1" -- said in the prose so the next reader does not re-baseline the table around it. Co-Authored-By: Claude Code <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -126,9 +126,9 @@ reciprocal.
|
||||
| `akgl_physics_simulate`, empty pool | frame | 63.9 | 15,650,101 |
|
||||
| logic frame: 64 updates + simulate | frame | 5,763.1 | 173,517 |
|
||||
| `akgl_rectangle_points` | call | 4.0 | 248,412,026 |
|
||||
| `akgl_collide_rectangles`, overlapping | call | 24.9 | 40,181,108 |
|
||||
| `akgl_collide_rectangles`, disjoint | call | 57.9 | 17,261,906 |
|
||||
| all-pairs collision sweep, 64 actors (2016 pairs) | sweep | 115,023.6 | 8,694 |
|
||||
| `akgl_collide_rectangles`, overlapping | call | 6.1 | 164,345,776 |
|
||||
| `akgl_collide_rectangles`, disjoint | call | 6.1 | 164,423,708 |
|
||||
| all-pairs collision sweep, 64 actors (2016 pairs) | sweep | 12,241.6 | 81,688 |
|
||||
| `akgl_string_initialize` | call | 32.1 | 31,197,432 |
|
||||
| `akgl_string_copy`, full length | call | 32.2 | 31,075,004 |
|
||||
| `json_load_file`, small document | load | 11,338.8 | 88,193 |
|
||||
@@ -183,7 +183,7 @@ full screen of 16-pixel tiles and 64 actors:
|
||||
| Part of the frame | Cost | Share of 16.67 ms |
|
||||
|---|---:|---:|
|
||||
| Logic: 64 actor updates + one physics sweep | 0.006 ms | 0.03% |
|
||||
| All-pairs collision over 64 actors, if you do it | 0.115 ms | 0.7% |
|
||||
| All-pairs collision over 64 actors, if you do it | 0.012 ms | 0.07% |
|
||||
| Clear + present | 0.023 ms | 0.1% |
|
||||
| 64 actor renders (48x48 blits) | 0.191 ms | 1.1% |
|
||||
| 1200 tile blits | 16.26 ms | 97.6% |
|
||||
@@ -306,15 +306,25 @@ and not a string operation.
|
||||
|
||||
### The collision helpers are fine; the missing broad phase is the problem
|
||||
|
||||
`akgl_collide_rectangles` is 24.9 ns when the rectangles overlap (it returns at
|
||||
the first corner that hits) and 57.9 ns when they do not (all eight corner tests
|
||||
run). Both are fine.
|
||||
`akgl_collide_rectangles` is 6.1 ns whether the rectangles overlap or not. It
|
||||
was 24.9 ns overlapping and 57.9 ns disjoint until 0.8.0, when it stopped being
|
||||
eight `akgl_collide_point_rectangle` calls and became four comparisons -- a
|
||||
change made to fix the cross case it could not see, with the speed as a side
|
||||
effect rather than the goal. The disjoint case improved most because it was the
|
||||
one that ran all eight tests before answering.
|
||||
|
||||
**Read that 6.1 as a floor, not a measurement.** `akgl_rectangle_points` is
|
||||
untouched by that change and reads 6.1 in the same run against the 4.0 recorded
|
||||
above, so this run's resolution is around 6 ns and the collision test is now too
|
||||
cheap to distinguish from a function that does almost nothing. The rows above are
|
||||
not re-baselined for it; only the three rows the change actually moved are.
|
||||
|
||||
What the library does not provide is a broad phase, so a caller that wants
|
||||
collision writes the all-pairs loop: 2016 pairs for 64 actors, 115 µs a frame,
|
||||
0.7% of a 60 fps budget. That is affordable. It is also O(n²): raise
|
||||
`AKGL_MAX_HEAP_ACTOR` to 256 and the same loop is 32,640 pairs and 1.9 ms — over
|
||||
10% of the frame, for a game that has done nothing yet.
|
||||
collision writes the all-pairs loop: 2016 pairs for 64 actors, **12 µs** a frame,
|
||||
0.07% of a 60 fps budget. That is affordable. It is still O(n²): raise
|
||||
`AKGL_MAX_HEAP_ACTOR` to 256 and the same loop is 32,640 pairs, which at this
|
||||
per-pair cost is around 200 µs — 1.2% of the frame, for a game that has done
|
||||
nothing yet.
|
||||
|
||||
### Spawning is cheap, and a third of it is a log line
|
||||
|
||||
|
||||
Reference in New Issue
Block a user