117 lines
5.8 KiB
Markdown
117 lines
5.8 KiB
Markdown
# 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 without render errors. 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.
|