# Agent runtime and viewport performance Measured on 2026-08-23 with the primary account in Broceliande, a 1024 m x 1024 m varregion, using the default 64 m view distance. The rendered scene had 211 objects, 427,253 triangles, and five unavailable grid textures. All timing numbers below are optimized release builds. ## Runtime ownership - `metacrate-grid-world` owns native OpenSim/Second Life event subscriptions, distance filtering, attachment world transforms, terse movement application, event-drop accounting, and immutable published world snapshots. - `metacrate-game-loop` owns the fixed update schedule, renderer lifetime, persistent GPU scene, fixed-rate viewport loop, completed-frame buffers, and timing signals. Readers receive an immutable `Arc<[u8]>`; they never read a render target while Bevy writes it. - `metacrate-rendering-wgpu` owns Bevy/wgpu resources and supports separate scene loading and camera/frame rendering. Shared immutable scene arrays cross the renderer thread without copying their contents. - `metacrate-grid-agent` supplies policy, conversation, tools, and lifecycle triggers. It consumes published world/viewport state instead of synchronizing render buffers or individual native event channels. Grid callbacks never perform asset I/O or render work. The world loop drains a bounded 4,096-event queue in batches, coalesces dirty IDs, and publishes only complete snapshots. Object, avatar, attachment, and terrain admission uses the configured view distance; 64 m is the default. The live reference contained 280 visible prims, one avatar, and 51 nearby terrain patches, with zero dropped events. ## Readiness Transport connection is not agent readiness. A generation remains degraded and chat, tools, movement, and autonomous behavior remain fenced while the terminal reports: ```text INITIALIZING generation=N waiting_for=world_state,viewport target_fps=10 ``` The framework gate opens only after the region-scoped world state exists, the first scene/assets have converged, and at least two complete viewport frames have been published after the last missed deadline, within the frame budget, and without render errors. One-time warm-up misses are reported separately and do not contaminate the post-`READY` stability window. The terminal then emits `INITIALIZED ...` followed by one unambiguous `READY ...` line when the session and agent services are released. The default readiness allowance is two minutes; the measured scene convergence window was about 30.05 seconds. ## Measured steady state | Phase | Measured time | |---|---:| | Bevy/wgpu initialization | 476 ms | | First persistent GPU scene load and frame | 1.02 s | | Cached complete scene refresh | 86 ms | | Stable warm render plus coherent CPU readback | 31–36 ms | | JPEG encoding on demand | 37 ms | | Repeated capture using the already published viewport, including JPEG | 45 ms | The viewport uses a 90 ms scheduling interval, providing margin above the minimum 10 FPS requirement. The live two-second soak completed 22 frames at 10.99 FPS with zero missed deadlines and zero render errors. JPEG encoding and LLM transfer are not in this loop. A cached full-scene refresh completed in 86 ms, inside the 90 ms interval: validation 1.08 ms, shared command handoff 0.001 ms, texture cache sync 30.47 ms, mesh cache sync 15.21 ms, scene setup 0.47 ms, and render/readback 35.70 ms. Unchanged frames skip validation, command transfer, texture sync, and mesh sync. ## Startup work | Phase | Reference time | Scheduling | |---|---:|---| | Client owner initialization | 2 ms | Startup thread | | Grid login | 3.37 s | Parallel with renderer initialization | | Bevy/wgpu initialization | 0.49 s | Completed before agent readiness | | Initial world/asset convergence | 30.05 s | Asynchronous Grid and asset workers | | First persistent GPU scene and frame | 1.02 s | Before the readiness gate opens | The slower first start is intentional: the agent does not claim readiness until it can provide stable world and visual state. Raw immutable assets persist in the configured 2 GiB LRU under the platform cache directory (`$HOME/.cache/metacrate` on Linux), so later starts reuse them. ## Frame publication and future GUI path The current agent path continuously renders and publishes the latest complete RGBA frame. A requested snapshot reads that immutable frame and performs JPEG encoding independently. Camera or scene submissions replace stale pending input; consumers never block the viewport loop. The current Bevy screenshot API still performs synchronous CPU readback, which accounts for most of the 31–36 ms warm frame cost. This meets the current headless-agent target, but a GUI viewer should present the GPU target directly. For higher-rate CPU capture, use a ring of three staging buffers: render, enqueue GPU copy, map asynchronously, and publish only after completion. If all staging buffers are occupied, skip that readback instead of stalling rendering. ## Instrumentation and observed limits Hot paths record monotonic timestamps and fixed-cardinality counters only. World event drops, completed frames, missed deadlines, render errors, and last render duration are available without formatting or I/O on the loop threads. Observed limiting factors: - Initial scene/asset convergence, about 30 seconds, dominates readiness. - First GPU admission is about one second; it occurs before `READY`. - A cached full-scene refresh is 86 ms and is currently the closest operation to the 90 ms frame budget. Incremental per-entity GPU updates are the next useful optimization if denser or highly animated regions exceed that budget. - Synchronous readback costs roughly 31–36 ms per frame. A direct GUI path or asynchronous staging ring removes it from presentation. - Five referenced textures were unavailable from the test grid. Missing assets do not deadlock readiness; completeness remains explicit. - The successful live run used 280 visible prims, one avatar, 51 terrain patches, 427,253 render triangles, 4,096 event slots, and dropped zero events.