Skip to content

Host hooks, capacity, counters and the getters the reference has (#163, first part) - #164

Merged
nicolas-maman merged 1 commit into
mainfrom
api/host-hooks-debug-draw
Oct 9, 2026
Merged

nicolas-maman merged 1 commit into
mainfrom
api/host-hooks-debug-draw

Conversation

@nicolas-maman

Copy link
Copy Markdown
Collaborator

The first part of #163: the public API that e77352c has and no reference test reaches. Debug draw is the second part, so #163 stays open.

Getters and queries

  • dynamics.contact_get_data(id) (b3Contact_GetData). An id that is not a contact's gives an empty result.
  • dynamics.joint_get_constraint_tuning (b3Joint_GetConstraintTuning), returning both values.
  • dynamics.shape_get_mesh_material_count (b3Shape_GetMeshMaterialCount).
  • physics_world.get_world_count and get_max_world_count.
  • physics_world.get_version: the reference version this engine follows, 0.2.0. aephysics has no version scheme of its own, so it does not invent one.
  • physics_world.is_double_precision: true, since world positions are double, as in the reference's large-world build.
  • physics_world.is_avx2_available: the CPU and OS check, as GCC's and Clang's __builtin_cpu_supports read it. This is the query half of SIMD width chosen at run time: AVX2 when the CPU has it, one binary for every CPU (b3SetSIMDWidth) #156.
  • physics_world.get_max_capacity (b3World_GetMaxCapacity):
    • kept at each step's start, never lowered;
    • carried by the snapshot (version 3, with Capacity in its layout hash);
    • Capacity gains the body counts.
  • basics.djb2_hash: the reference's b3Hash, word-wise as it is. The test pins values from the reference's function compiled in C.

Host hooks

  • basics.set_allocator(allocate, release) (b3SetAllocator): every block the engine makes comes from the host's allocator, asked at the reference's 32-byte alignment and zeroed here.
    • The scheduler, its semaphore (now a block the caller allocates, as the reference's b3CreateSemaphore does through b3Alloc) and the ragdoll layer now allocate through it too.
    • The name cache hashes a string's own bytes, so the temporary C copy is gone.
    • set_allocator refuses while blocks from the previous allocator are still out, since they must go back to the allocator that made them.
    • Ragdoll shapes are now freed with human.destroy_shape. Plain free() stays memory-safe while no host allocator is set, so existing callers (ae3d) keep working until they switch.
  • basics.set_log_fn (b3SetLogFcn): the scheduler's overflow message and the solver's stage report go through it.
  • basics.set_assert_fn (b3SetAssertFcn): aephysics checks its inputs and returns instead of asserting. Its one hard stop, the scheduler's task overflow, reports to the host before it aborts.

Diagnostics

  • physics_world.dump_memory_stats (b3World_DumpMemoryStats) and world_byte_count: the reference's parts with its labels, plus ours (fat bounds, owned geometry, names), to the host's log.
  • Counters gains the rest of b3Counters:
    • byte_count, arena_capacity, task_count;
    • manifold_counts: the collide pass's contacts by manifold count, recycled ones included, as the reference counts them;
    • distance_iterations, push_back_iterations, root_iterations: maxima over the world's life, as the reference keeps them.
  • Not ported: b3World_DumpShapeBounds, which e77352c declares but never defines.

Bugs found on the way, filed

  • aether#2623: a module-level var of a void function type rejects a matching function as its initializer. The hooks did not need it, since they are a record a pointer reaches, null until a host sets one, as the reference's are null-checked pointers.
  • aether#2624: &p.field of one struct type passes the checker as an argument of another pointer type, and gcc rejects the C.

Tests

  • test_host_api.ae, 31 checks. A counting allocator that hands out dirty memory is set before anything is made. It takes every block of a world on four workers with a ragdoll, a mesh, a hinge, a bullet, a snapshot and a recording, and gets each back except the scratch the modules keep for the program, which matches the counters exactly. It is asked at 32 bytes every time. The other checks:
    • the log gets the memory part by part, adding up to the counters' byte count;
    • the failure function is called;
    • a contact by id matches the per-body data, and a stale id gives nothing;
    • tuning, material counts, world counts, version, precision and AVX2;
    • capacity: equal to the bodies held, never lowered, carried by a snapshot;
    • counters: tasks, manifold buckets, and the bullet's time-of-impact iterations;
    • b3Hash against the reference's own values.
  • test_human.ae: frees shapes with destroy_shape.
  • design.md: stale lines about tree save/load, compound bytes and the name cache are corrected.

Pins

Aether 0.796.0 and aeb v0.325, both the latest releases.

… first part)

A host's allocator takes every block the engine makes (the scheduler,
its semaphore and the ragdoll layer now allocate through basics too),
its log the engine's messages and its failure function the one hard
stop. The world keeps its maximum capacity at each step's start and a
snapshot carries it; Counters has the rest of b3Counters; the memory
goes to the log part by part. Contact data by id, the joint tuning
getter, the mesh material count, world counts, version, precision,
AVX2 and the reference's b3Hash.
@nicolas-maman
nicolas-maman merged commit b29d498 into main Oct 9, 2026
2 checks passed
@nicolas-maman
nicolas-maman deleted the api/host-hooks-debug-draw branch October 9, 2026 08:41
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.

1 participant