Autopilot · write-up
Native navigation environment (ABI 82)
ABI 83 now supplies this environment through a C-owned persistent map session; see AUTOPILOT_NATIVE_MAP_SESSION.MD. Python map cells and embedded map metadata are lazy compatibility views.
xenon_autopilot_environment.c now prepares the wall raster, cropped/dilated ship
footprints, physical and strategic span graphs, and exact wall queries directly
from XapMapState. The shipped-assets path no longer walks Python map cells to
select tile patterns, uploads pattern indices, exports free/clearance grids as
Python bytes, hashes those bytes and uploads them again to construct C graphs.
The implementation separates raster preparation, ship cropping, graph preparation,
snapshot creation and cache ownership. native_environment_reference.py retains
the former assembly for comparisons and custom atlases. Set
XENON_NATIVE_ENVIRONMENT=0 to select it while retaining the C driver.
Ownership and reusable C interface
- Create/seed native map state with
xap_map_state_create_level(embedded JSON assets) orxap_map_state_create(an explicit restored map). - Apply captured tile draws through
xap_map_state_observe; preserve its existing two-observation destruction proof and static-wall protection. - Create an
XapEnvironmentCache, then callxap_environment_cache_preparewith map identity/revision, level, inclusive tile bounds, player collision sprite and integer reserve. The map must remain alive while the cache uses its identity. - Bind the snapshot's handles into mission/scene headers with
xap_environment_bind, then call the existing C driver. No JSON, atlas parsing, filesystem access or Python geometry callbacks are required by these C APIs. - Release each returned snapshot with
xap_environment_free. Free the cache when its owning pilot/session ends.xap_environment_createalso supports an independent snapshot without a cache.
The cache holds at most eight recurring physical footprints. It compares cropped mask content, not sprite identity: equivalent animation frames reuse geometry. All footprints for the same map revision share one reference-counted wall raster. Level 3 and zero-reserve configurations share the physical/strategic graph. Cache eviction or map changes release only cache references; replay-held snapshots keep their immutable geometry alive. Access is serialized, including graph query scratch buffers; this is not an independently concurrent mutation API.
Span topology needs free cells but never reads the clearance scores. Native grid preparation now accepts NULL clearance and skips its writes and both distance passes. The temporary free grid stays in C and is freed after graph construction. Existing reference callers can still request both outputs.
Collision assets
The manual asset generator now includes xenon_autopilot_collision_data.h, derived
from shared and all five level SATX/SATL atlas pairs. It preserves level-first,
shared-second lookup, deduplicates identical patterns, and stores only low-bit-first
alpha rows cropped to the largest wall/ship query (32x27), not BGRA images.
Runtime lookup uses sorted numeric sprite identities. Unknown wall sprites retain
the conservative solid-tile rule; missing player captures retain the previous
rectangle and four-pixel query rounding. The ship crop is unchanged.
Run python xenon_tools/generate_native_assets.py manually after changing assets;
--check verifies all six generated outputs. Nothing was added to build-time
generation. Custom asset directories/atlas objects and unsupported reserve widths
retain the reference assembler instead of silently using incompatible embedded data.
Validation and measurements
Standalone C build, 282 native tests, 67 planner tests, 575 forced-native reference policy tests, and 24 replay tests pass. New tests compare every embedded sprite mask, all five complete rasters, sampled routes/wall probes, actual ship cropping, destruction updates, snapshot lifetimes and cache/raster sharing. A bypass test rejects Python mask/grid/raster preparation on the shipped-assets driving path.
Fresh preparation with the real shared ship collision sprite, CPU pinned and two repeats in reversed order (milliseconds):
| Level | Reference assembly | Native environment |
|---|---|---|
| 1 | 9.371 | 4.180 |
| 2 | 8.201 | 3.922 |
| 3 | 5.272 | 1.431 |
| 4 | 10.241 | 3.992 |
| 5 | 8.681 | 3.485 |
This is 52–73% less preparation time, not an equivalent speedup on every frame.
Atlas loading and map seeding are outside this measurement. Reproduce with
python xenon_tools/benchmark_native_environment.py --pin-cpu --repeats 2.
Results: run_logs/native-environment-preparation-ship-0913.json under xenon_tools.
Recorded processing comparisons, two fresh-process reverse-order repeats:
| Sample | Previous environment | Native environment |
|---|---|---|
| Level 1, 2367–3367 | 1.029 ms | 1.013 ms |
| Level 3, 49751–50020 | 1.518 ms | 1.492 ms |
| Level 5 barriers, 90160–90459 | 1.490 ms | 1.509 ms |
All compared decisions match. These small timing differences are roughly neutral;
ordinary frames already reuse preparation. Reports are in
xenon_tools/run_logs/native-environment-{level1,level3,level5}-0913-final/.
Use comparator variants c-environment-old,c to isolate this migration.
An initial uncached version rebuilt graphs for recurring sprite frames and regressed
Level 1 processing; the bounded C footprint cache fixes that repeated work.
No live campaign or WASM profiling was run for this geometry-preserving migration.
Remaining migration
Python still orchestrates map observations, maintains compatibility map cells and world views, initializes its runtime atlas for remaining consumers, and fills small captured driver headers. The new C environment removes geometry preparation from that dependency chain; it does not claim the entire pilot is Python-independent. The next substantial boundary is C-owned observation/world/map orchestration with lazy Python views. Lifecycle, shops, fire scheduling and the final in-memory capture/control adapter remain afterwards. Hatari build integration is unchanged.
Current wall geometry (ABI 106, 2026-10-05)
The earlier animated/cropped ship footprint described above is superseded.
Original $387C uses a fixed 25-row footprint from the $3515C table.
generate_native_collision_masks.py now extracts that footprint from
assets/stram.bin and verifies all 16 shifted copies before emitting
ship_wall_rows in the generated header. Sprite alpha remains available for
object interaction and terrain rasterization.
xap_environment_create(map, level, first, last, reserve) no longer takes a
player sprite. xap_environment_cache_prepare(cache, map, level, first, last,
reserve, revision) retains one immutable environment for the current key.
Snapshots retain the environment directly; a separate shared-raster owner
and eight animation entries are unnecessary. The resident session does not
rebuild graphs when the ship tilts. Python inspection mirrors require ABI 106;
they are not part of the live resident control loop.
Per-update wall motion rejects every occupied destination. Strategic span
segments still validate the complete connector; the old approximate
escape-distance exception is removed from the execution path. Actual rollback
completion invalidates the failed connector once. See
AUTOPILOT_NAVIGATION_REVIEW_20261005.MD for the full-prefix validation and
remaining equipment/combat/reward failures.
Manual regeneration remains python xenon_tools/generate_native_assets.py;
--check now verifies eight outputs. The legacy preparation benchmark still
compares preparation cost, but its two footprints differ; its results no
longer establish decision or geometry equivalence. Earlier measurement tables
are historical. No phone/WASM timing is claimed.