Skip to content

Recorded queries and the name cache (#149, third part) - #162

Merged
nicolas-maman merged 2 commits into
mainfrom
recording/queries
Oct 9, 2026
Merged

nicolas-maman merged 2 commits into
mainfrom
recording/queries

Conversation

@nicolas-maman

Copy link
Copy Markdown
Collaborator

The third and last part of #149: the recorded world queries and the name cache, as the reference's recording*.c and name_cache.c have them. With #160 (snapshot) and #161 (recording and replay), this covers all of #149.

Closes #149.

Queries

  • All seven world queries record themselves while the world records: overlap_aabb, overlap_shape, cast_ray, cast_ray_closest, cast_shape, cast_mover, collide_mover. Each record carries its arguments, every visit with the caller's answer, and the tree's work.
  • Thread safety. A query writes into a buffer of its own while it runs, and its record goes in whole under the recording's lock, since queries may run on several threads at once. Ordinary records now take the same lock.
  • Untouched hot path. Each query is a thin wrapper over its _internal body, so a world that is not recording runs exactly the code it ran before.
  • Tags. QueryFilter gains the reference's id and name. A tagged query is preceded by a tag record holding the key: the reference's 64-bit FNV of the id and name. The id and name go once each into a tag table after the geometry.
  • Replay. The player asks each recorded query again through a trampoline that compares every visit and returns the recorded answer, so a visitor that stops or clips a query replays exactly. Any difference marks the player diverged:
    • a shape, point, normal, fraction or plane;
    • the number of visits;
    • the tree's node and leaf visits (the reference does not compare these, but our snapshots keep the trees bit for bit, so they must match).
  • Frame store. The player keeps the queries of the frame it last played, with their hits: player_frame_query_count, player_frame_query and player_frame_query_hit, the reference's b3RecQueryInfo and b3RecQueryHit. Each query carries its caller's key, id and name, its bounds, and a cast mover's fraction.
  • Version. The recording's minor version goes to 1, since query records are an additive change that older players skip.

Name cache

  • A body or shape now holds a name_id, and the world keeps each name once in a basics.NameCache.
  • Ids are the reference's b3Hash32; the test pins the values from the reference's own function compiled in C.
  • Collisions. Where the reference gives a second name on the same hash the first name's id (so it reads back the wrong name), the second name here takes the next free id. The cache stays deterministic and never answers with another name.
  • Snapshot. Version 2: the cache is written once, records copy their name_id whole, and loading refuses any id the cache does not hold.

Tests

  • test_recording.ae, 152 checks. The reference's tests:
    • QueryReplay: every query kind, with an overlap visitor that stops at its second shape, a ray that clips at each hit, a cast mover, and a mover sunk into the ground so planes are recorded. The ray's kept hits match what the host's visitor saw. A changed recorded hit is caught at the frame it was asked in, without stopping the replay.
    • TaggedQuery: two ids under one name, an id without a name, and an untagged query, through a file round trip.
    • NameRoundTrip, and RollbackNames scrubbed across keyframes with no leak.
    • GeometryHashCollision, and ReservedHeaderBytes.
  • test_threads.ae. Four threads querying a recording world: every record whole (18,000 of them) and the replay asking them all again alike.
  • test_basics.ae. The reference's CacheUnit, plus collisions, loading and refusals.

Not ported: the reference's DebugShapeCallbacks subtest, since aephysics has no debug-shape callbacks on its world def.

Pins

Aether 0.796.0 and aeb v0.325, both the latest releases. The whole gate passed locally, golden trace included.

The seven world queries record each visit with the caller's answer and
are asked again on replay with the same answers, every visit compared;
a query filter's id and name tag its caller; the player keeps the
frame's queries and hits. Records go in under the recording's lock, so
queries from several threads stay whole.

Names are interned once per world under the reference's 32-bit hash
(a collision takes the next free id); bodies and shapes hold ids, and
the snapshot (version 2) carries the cache once.
…ing it

A macOS runner's calling thread finished all 10,000 items alone in
eight runs in a row: the check asked whether a pool thread wakes before
the calling thread could finish, which is the system's scheduling. The
calling thread's first block now gives the pool up to two seconds to
take a block, so the check holds what it means: the pool joins in.
@nicolas-maman
nicolas-maman merged commit 6ef5686 into main Oct 9, 2026
2 checks passed
@nicolas-maman
nicolas-maman deleted the recording/queries branch October 9, 2026 07:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Recording and replay, world snapshot and the name cache (design item 15)

1 participant