- name
- performance-profiling
- description
- Diagnose or improve LunCoSim FPS, physics time, periodic stalls, Builder versus View differences, terrain/render cost, or Tracy captures. Use when performance must improve architecturally without changing BigSpace, substeps, shadows, terrain quality, or image fidelity.
# Performance profiling: measure the owner, then remove avoidable work
Read the current handover in
[`docs/reviews/open-400fps-performance-handover.md`](../../docs/reviews/open-400fps-performance-handover.md)
before changing code. A status-bar FPS number is a symptom, not an attribution.
## Required separation
- Run one production session that you own for the product FPS number, launched
from the task checkout on a verified free API port. Do this even if another
user's or agent's session is active; never control, stop, restart, or change
the scene in a pre-existing session. Report concurrent GPU/CPU workloads and
classify affected numbers as contention-affected, not clean acceptance. Do
not use Tracy's instrumented number as acceptance evidence.
Record the admitted Twin source revision and require its expected authored
verdict before comparing completed-mission windows. A tick threshold or zero
process exit does not prove acceptance. Exclude source/API errors, and require
readiness and diagnostics to pass for the measured cohort.
Native presentation failure appears in `RuntimeDiagnostics` under producer
`render-recovery`; `presentation-stopped` is an error even when physics and
the API remain usable. Application-frame FPS can keep advancing without
rendered scene output. Reject such a cohort; do not interpret its frame rate
as graphics performance or cure it by lowering quality implicitly.
Compare the recorded Twin commits explicitly; two individually clean checkouts
can have different revisions. Such runs are different source cohorts, even if
their scenario name and application binary match. Initialization-source and
milestone equality alone do not prove exact native-state replay.
- Run a separate Tracy build/capture using the adjacent `../tracy` checkout;
start the matching `tracy-capture` before the production binary and inspect
the settled window, not only startup. Bevy 0.20 uses Tracy 0.14.1; the matching
official tools are `../tracy/capture/build/tracy-capture-0.14.1` and
`../tracy/csvexport/build/tracy-csvexport-0.14.1`. Older capture tools cannot
read the new client protocol.
On Linux, the adjacent Tracy client supports `TRACY_SAMPLING_HZ`; record the
requested rate and verify the saved capture's sampling period. Its default
10 kHz CPU sampling can add substantial overhead. A lower sampling rate is
useful for a separate zone-attribution run; it is still not FPS acceptance.
Select native physics zones for broad phase, narrow phase, solver preparation,
substeps, integration and CCD. Keep `lunco_physics::order_audit` disabled
unless deterministic ordering itself is the measurement subject.
Check the profiler listener and connection against the owned PID with
`ss -ltnp`/`ss -tnp`. A concurrent Tracy client can occupy 8086 and move the
owned app to the next port; pass that port with `tracy-capture -p`. A capture
from another process is not evidence for the task scene.
A zero collector exit does not prove capture integrity. Compare the saved
file size with the collector's reported compressed payload plus native file
framing, and require a successful native CSV export before using its zones.
`FileWrite::GetCompressionStatistics` excludes the six-byte file header and
four-byte length per compressed block; these bytes are present on disk. A size
mismatch,
parser failure or stalled export leaves that capture unusable. A reported
instrumentation failure also makes the requested window incomplete. Keep hot
zone names bounded: step ids, timestamps, and measured durations must not be
span fields when the Tracy layer uses those values as source-location identity. Inspect the
exact owned exporter process before terminating an unsuccessful read; do not
restart a still-running export solely because an observation timed out.
Check disk space while the owned app is live as well as after it exits.
Native capture writing can overlap runtime allocations, and the collector
can report success despite a truncated file.
Native unwrapped CSV rows are grouped by source location; sort by
`ns_since_start` before reconstructing chronological phases or transitions.
Create the ignored capture directory and check that the collector is still
alive before launching the app. A failed collector must not leave an
unconnected instrumented client accumulating events. If fine executor
bookkeeping dominates the diagnostic, a separately labelled cohort may use
`RUST_LOG=info,bevy_ecs::schedule::executor=warn`; retain system, schedule and
native physics spans and never compare that cohort as an isolated speedup
against a different filter. Exact-zone CPU selection matches the zone's own
thread: a FixedMain main-thread selection excludes concurrent Bevy workers.
For sampled CPU stacks within a slow zone, use the read-only
[`scripts/perf/tracy_samples.cpp`](../../scripts/perf/tracy_samples.cpp) tool
and its [build/interpretation instructions](../../scripts/perf/README.md#saved-cpu-samples).
Match the capture's Tracy release when building its server library, verify
the owned PID, and select the zone's exact thread and time interval. Samples
can be narrowed to complete instances of one exact zone by appending its name
to the sample export command; inspect `matched_zones` and the terminal result.
Frame-wide samples include unrelated work outside the selected child zone.
The native exporter supports `-e -u` for per-event self times. Match those
rows to the inclusive export by source, thread and start before selecting a
bounded window: self duration does not provide the event's original end.
For one complete schedule subtree, require the selected self-time sum to
equal its inclusive duration. Main-thread schedule self time includes waits
for worker systems; it does not by itself prove executor overhead.
Profiling data stays in ignored `scripts/perf/captures/`, `.cache/` or
`target/perf/`; commit
reusable tool sources and documentation only.
- Compare Builder and View with the same scene, camera, rendering-quality
settings, physics substeps, shadow settings, and terrain assets. A Builder-
only cost usually means an editor observer, rebuild, projection, or UI path,
not that physics needs a different global timestep.
- For a settled DEM quality window, require `TerrainLodStatus.stream.wanted ==
resident`, `stream.pending == 0`, `derived.active == false`,
`derived.pending == 0`, and `derived.ready == derived.total`. Record those
owner facts before and throughout the window alongside camera and quality
inputs. Simulation readiness alone does not certify optional visual-map
preparation. Keep this measurement gate out of simulation admission.
Audit a surface camera with `world_pos` and `world_rotation` in the active
simulation frame; `visual_foci` reports composed root coordinates, which move
with celestial ancestors even when the camera is stationary over the terrain.
- Assert the intended fixture before timing an owner-resolution benchmark.
`PortRegistry::default()` installs the generic input owner first; registering
other backends afterward does not make them precede it. A synthetic
precedence fixture must verify its resolved owner index.
The ignored `lunco-port-core` `multi_port_transaction_diagnostic` measures
2,048 complete atomic transactions over 32 entities with 1, 8 or 32 inputs
each, behind five registered but absent component owners. It asserts the
winning backend and exact final scalar bits. Report every width, including
regressions, and qualify the actual scene separately: a multi-input fixture
gain does not establish a faster production exchange.
- Attribute CPU, GPU, physics, terrain, and UI separately. Never disable
shadows, lower authored terrain quality, change BigSpace, or change the
standard substep count to make a graph look better.
For typed hook conversion, `lunco-hooks-rhai`'s ignored
`map_conversion_diagnostic` measures complete nested-map conversion at 8,
128 and 2,048 entries in sorted, reverse and permuted order. Compare every
case with independent executable snapshots and alternating runs, without a
concurrent build. This fixture uses the test allocator; pair it with the
production UI-preparation gate and an owned scene before claiming runtime
effects. A fixture improvement is not a whole-frame or loading improvement.
Snapshot compiler-managed executables with independent copies, for example
`cp --reflink=auto`, never hard links. Rebuilding can overwrite every hard link
to a test artifact. Verify separate device/inode identities and record hashes
before and after the comparison; reject results if a snapshot changed or a
supposed code variant resolved to the same executable.
Before grouping static shapes, inspect native child-pose invalidation and
material inheritance. Preserve mutable global material defaults; a lower
body count alone does not prove a speedup or equivalent physical behavior.
For pose-bridge selection, the ignored `pose_invalidation_diagnostic` in
`lunco-usd-avian-core` measures 512 bodies over 256 persistent-system runs.
Preserve its separate leaf, render-branch, above-frame and physical-parent
cases; require exact native poses, velocities and leaf representation history.
Pair it with a normal production window before claiming scene-level savings.
`collider_composition_diagnostic` in the same owner runs 512 colliders over
256 iterations for ancestor translation, rotation, scale, physical-parent
motion and an irrelevant leaf. Require exact transform fingerprints across
independent variants and retain the cached-prefix hierarchy-depth check.
In a diagnostics-enabled render host, `lunco-render-bevy` adds
`lunco_shadow_shared_early`/`late` and `lunco_shadow_camera_early`/`late`
timestamps around the native shadow passes. Shared events correspond to
individual point/spot views; camera events include that camera's directional
cascades. Check all four names and their event counts before attributing total
shadow cost. Late spans can contain only timestamp overhead. Use the
existing Tracy build for native GPU export: Bevy 0.20's `trace_tracy` enables
the render diagnostics recorder through `bevy_render/tracing-tracy`.
Inspect timestamp support and recorded pass coverage before claiming a
complete GPU total; a sum of recorded passes excludes uninstrumented work
and inter-pass gaps. Use the
early shared child spans `lunco_light_depth/<pass_name>` when attributing
individual maps; native exports prefix them with `lunco_shadow_shared_early/`.
Do not sum child and parent times. `lunco_shadow_view_work` supplies admitted
viewport dimensions, optional phase emptiness and native bin/set counts.
This brief CPU snapshot is not pass execution time; bins are not draw counts
and stable counts do not prove static caster inputs. Native labels identify
current render allocations, not authored lights. Close each child before its
parent. Retain native dependency ordering, native-parameter admission before
begin and render-context buffer order when changing this adapter. Reapply
PBR's system-local early/late
ordering rules after removing the original system; retaining its implicit
type set alone preserves only dependencies targeting that set. Check the
actual shadow image before admitting a capture as performance evidence.
Qualify point/spot roots with the `shared_shadow_allocation` graphics fixture
before comparing native builders. Its positive and negative pixel probes
cover the separate shared-light schedule; camera shadows alone do not.
`SharedShadowView` uses the native single-threaded executor for its serial
GPU chain or two CPU-mode shadow passes. Native render preparation clears
indirect parameter buffers once per frame before view work. Attribute its inclusive
and self costs separately; frame-level Render and camera Core3d still use
their existing executors. Verify native pass inventory and final buffer order
before comparing executor costs. This render adapter does not change solver
cadence or authoritative simulation ordering.
Unsupported timestamp features or
exhausted per-frame queries leave incomplete GPU evidence, not zero cost.
- For intermittent long frames, export individual CPU zones and correlate them
with perspective/window markers. Exclude screenshot delivery, authored test
verdicts and perspective transitions from settled statistics. Inclusive
render/extraction schedules can contain a native `queue_submit` wait; report
the submission boundary separately from owner computation. A long submission
span alone does not establish its driver, GPU or scheduling cause. Retain
periodic GPU clocks/utilization, CPU pressure and concurrent process evidence,
then run the same exact uninstrumented artifact separately. Failure to reproduce
an earlier tail in a later run does not prove a fix. Record visible popup/UI
state alongside camera and graphics settings.
For JSON-scope stalls, distinguish authored source catalogs from completed
experiment histories. The asset/Twin owner publishes
`JsonAssetContent::PersistedResult` as identity-only metadata, projecting the
`persisted_result` Rhai fact without loading or copying result bodies. These
entries own no Bevy text handle and never delay JSON-scope readiness; loading
counts cover authored reads only. The existing
application asset policy skips those records before JSON decoding. Confirm the
`application_json_catalogs` production gate and measure the same admitted source
cohort. Do not hide malformed authored catalogs, change
Rhai limits or classify sources by guessed catalog filenames.
For coupled startup, distinguish the one-shot solve from participant
certification. Pending constructors supply no certifiable observation; the owner
waits on their outcomes and revalidates current source, wiring and physical
samples at final admission. Include coordinator counts and main-thread time
when measuring startup; worker timing alone misses repeated certification work.
Bootstrap API readiness does not certify the requested Twin. Record the exact
active Twin and its physics admission. An interactive replacement retires the
startup failure guard on `TwinClosed`; a late canceled asset result must not
terminate the replacement. The focused production gate is
`scripts/api/test_startup_scene_replacement.py --port <free-port> --log-dir <ignored-evidence-directory>`.
Record prepared-solver hits and native attempts/hits/compilations alongside
startup time; tests and other Twins can legitimately evict bounded artifacts.
An immediate repeat with unchanged inputs distinguishes cold-cache work from
recurring constructor cost without changing retention limits.
For Modelica native-construction costs, distinguish selected application work
from whole-source preparation. Projection reuse storage is needed only for
selected source programs and is shared across that application's issued colors.
Preserve checked identity, input validation and evaluation order. Compare the
same prepared component's complete f64 states alongside construction timing;
include its first complete observation. Zero native compilations do not prove
faster admission: reference evaluation can dominate a large coupled initializer
even when it avoids JIT work. Keep an evaluator change only when the actual
scene improves with its numerical contract intact.
Before changing an artifact-cache codec, sample its complete lookup zone.
Checked deserialization may spend most of its time reconstructing native
structural contracts; parsing and decompression must be attributed separately.
Do not bypass those checks to improve a disk-hit number.
Shared-value construction also pays for source discovery and symbolic proofs.
Its read collector shares the exhaustive Solve operand checks in one traversal;
preserve range/context checks and whole-program equivalence when optimizing it.
The symbolic term table interns complete operation keys once; each scalar output
uses the table-local ID and output ordinal. Retain full structural equality and
the shared table lifetime across segments and both sides of a check.
Scalar terms carry the original slot or operation/output identity directly;
there is no separate scalar-term interning table. Preserve disjoint identity
domains and the independent checker's complete walks when changing storage.
Operation shapes use the complete typed IR encoding, including floating-point
bits. A changed constant's bits must fail the checker; hash equality alone
cannot establish operation identity.
Private proof lookup tables use Rumoca's existing fast hasher. Preserve full
key equality and program-order ID assignment. Qualify constructor costs with
recorded prepared/native-cache hits and the fixed MSL canary; isolated proof
benchmarks do not establish faster scene admission.
Exclude overlapping builds/apps and record local dependency patches separately
from the portable lockfile. Dependency changes require the repository's explicit
authorization before edits.
For fixed RK4, verify that its ME component receives the opaque execution
backend issued by `rumoca_sim::fmi_execution_backend`. Selecting a fixed
integrator does not itself install compiled evaluation. Keep the same lattice,
input transaction and complete checked observation when comparing native and
reference execution. The nonlinear-array differential test qualifies numerical
agreement; its small diagnostic timing does not certify whole coupled ticks.
Use actual compiler-cache accounting on nontrivial production models to verify
native work. A local dependency path patch can disable prepared-model disk
caching; record that state and reject it as final loading acceptance for a
portable Git pin.
For terrain certificate lookups, whole-cell identity is the fenced surface and
the two raster indices. Refined rectangles also retain exact coordinate bits.
Keep one FIFO capacity and the same cache-independent interval and refinement
admission. Use the certified occlusion mechanism checks and production solar
visibility, night and invalid-admission gates. Compare the actual solar zones
in matched settled captures; a smaller synthetic lookup time is not whole-tick
or graphics acceptance.
Measure first scene readiness against the requested active Twin and spawned
physical root. Bootstrap readiness precedes Twin admission; do not use a minimum
elapsed-time filter to distinguish them. Record landing as a separate mission
milestone. Preserve source hashes and native construction/cache accounting.
For USD startup snapshots, attribute native path/field resolution separately from
file loading and ECS publication. Share the library's `BindingCache` only within
one immutable stage snapshot; preserve purpose resolution, native collection
membership and invalid material-target errors. Qualify an SDK update against the
same Twin revision and a regular production readiness/landing run before claiming
a loading gain. Dependency source edits still require explicit permission.
## Architecture checks
Frame-conversion and collider-admission kernels are owned by
`lunco-usd-avian-core`; its regular development build uses optimization level
three with development checks retained. Follow the actual owning package when
profiling moved kernels. Collider propagation must independently drain parent,
transform and body removals before comparing performance; a stale relative
transform invalidates a faster result.
Shader-port writes are fixed-step consumers even though their downstream GPU
binding runs at presentation cadence. Measure the actual backend with the ignored
`shader_input_transaction_benchmark` owner diagnostic, including both unchanged
and changing values. Its declaration and material-intent packages use regular
development optimization level three. Preserve live validation and no-op change
detection; a large visual binding count alone does not authorize changing clocks
or skipping writes.
The shader metadata callback uses the already-selected live slot. Preserve
authored-type precedence over a differently typed live override, rejection of
untyped drives, and current drive membership before accepting a faster result.
When comparing native CPU/GPU instance preparation, retain the same PBR,
shadows, meshes and quality. The desktop composition selects native CPU
preparation; qualify changes with both `shared_shadow_allocation` and
`vertex_shadow_alignment` pixel gates and source-matched normal Twin windows.
Keep the per-scene tradeoffs visible: the GPU path can have a lower median in
some scenes. Device capability is not active-path evidence:
camera `NoCpuCulling` requires native `RenderMeshInstances::GpuBuilding` and
indirect drawing. `NoIndirectDrawing` views need CPU frustum tests. Preserve
foreign-owned markers when reconciling these structural changes.
Native camera and shadow batch modes must also match the active builder. CPU
preparation admits effective `GpuPreprocessingMode::None` after native device
support initialization on every `RenderStartup`, including recovery. A faster
cohort with missing geometry is a failed visual comparison, regardless of FPS
or readiness diagnostics.
For GPU-only buffer layout, preserve deterministic scalar math and validate
shader array stride at the native allocator. `UninitBufferVec` uses encase's
`<[T; 1]>::SHADER_SIZE`: a shader element's minimum size alone does not include
all array alignment. Owner regressions cover both structure padding and `vec3`
array stride. Verify the actual terrain image and retained render diagnostics
when admitting native GPU preparation.
For co-simulation input writes, preserve the port owner's live metadata and
precedence checks. Initial handoff and live exchange use `input_transaction`:
immutable staging and commit share one exclusive world borrow, so each eligible
target is validated once. Returning an error rejects the staged batch. A
retained command preparation has no such borrow guarantee and must still be
revalidated at commit. Do not replace either proof with a revision-only cache.
The scoped transaction's Bevy hash table stores indexes into its staged writes;
duplicate checks compare full identities without cloning the port names again.
Its compact records retain only the admitted callback, entity, name, slot and
value. Preserve full metadata snapshots for independently retained preparations;
only the uninterrupted transaction borrow permits releasing admission metadata.
Unit-reference clones share immutable definitions, coordinate-frame clones share
immutable text, and Avian reuses its fixed built-in unit references. Preserve
exact text/hash/serialization behavior and reject blank frame identities. This
storage sharing never caches the live port
owner, bounds, frame or permission. The bounded debug spans
`port_input_transaction_stage` and `port_input_transaction_commit` separate
validation from application when `lunco_port_core::ports=debug` is enabled.
When reducing shader commit lookups, retain Bevy change detection: borrow the
component once, inspect through its shared dereference and mutate only a changed
slot. The owner diagnostic measures both constant and changing transactions;
`lander_plume_activity` verifies connected shader delivery and rejected scalar
range writes through the production scene-test host.
Before a wheel-shape comparison, verify the coordinate contract in
`docs/architecture/21-domain-usd.md`. Compare the native collider's axial and
radial bounds with its authored geometry and mechanical axle. Preserve the
admitted geometry through a restart: disposable view edits are discarded when
the clean source document refreshes. For an isolated unsaved comparison, use
the normal document journal, do not save it, and verify source hashes after exit.
For synchronous USD edit stalls, inspect compound operation count and native
composition lifetime. The document owner's `UsdEditSession` shares one current
authoring stage across an ordered sequence; direct layer changes invalidate it.
Preserve the private candidate and atomic rejection boundary described in
[architecture 21](../../docs/architecture/21-domain-usd.md). Measure the actual
command in a production run; a faster edit still occupies its calling thread.
Also inspect `usd_native_reference_discovery`: native property resyncs prepare
only their asset values, while prim resyncs scan the affected subtree. Asset
types gate provenance and time-sample resolution. Domain live-edit owners consume
one complete notice batch in place; label changes must preserve live child state.
Modelica coupling holds `FixedMain`, while the pacing clock retains running
demand in `Time<Fixed>::overstep`. Compare completed simulation time against
monotonic wall time and inspect pending demand through `SimulationTimingProfile`;
a short native physics service time does not certify realtime co-simulation.
When a coupled sensor changes with host pacing, compare its full input values
before attributing the difference to solver math. Contact impulses belong to
the completed master interval; `lunco-physics::contact_of` owns load normalization
for ports and telemetry. Native admission delta may be zero while that sample
remains valid. Exercise `scripts/api/test_contact_force_hold.py` through the
production scene-test host when changing this boundary; its authored observer
checks loaded contact, held time, retained force and an unavailable surface.
Physical render easing consumes the time owner's bounded
`SimulationPresentationTime.interpolation` snapshot; full queued cycles are
never a render extrapolation factor. Compare successive sample ticks across coupling waits and
pause/resume: they must not decrease within one mission. Celestial and
interaction clocks retain their separate owners. The common application plugin
uses native endpoint capture/restore with public custom-easing markers. Do not remove queued time or
loosen quaternion admission to conceal an invalid render pose.
For scene-test diagnostics, `--tick-hz` varies manual update pacing; use the
runtime's `Time<Fixed>` quantum and elapsed time when interpreting physics cost.
Completed Modelica receipts have one reader in `SimulationCompletions`, serviced
before clock projection, before admission of a held pending fixed cycle, and by
the native driver on response readiness between application updates. A ready
receipt can release another cycle in the same update; unfinished work yields
without a wait. Full presentation isolation remains a separate requirement.
Native headless readiness is shared through `SimulationCompletionSources` for
participant workers. Its worker leases control registration lifetime;
readiness does not consume a receipt. Windowed hosts never block on this wait.
Complete-cycle continuation checks both coupling and generic progress holds.
For runtime UI policy work, compare actual immutable input snapshots as well as
producer change ticks. Equal facts must not schedule another projection merely
because a presentation component received an equal-value write. Preserve hook
generation, source lifetime, caller identity and derived vessel invalidation.
Attribute `exposure_runtime_surface_facts` separately from its publisher and
worker policy spans. Public output selection uses the port owner's name index
and live slot order; preserve complete output facts and unsampled-port semantics.
The ignored `runtime_output_facts_diagnostic` in `lunco-luncosim-exposures`
measures complete output projection, including all-value allocation. Compare
independent binaries and qualify the authored public-output consumer in a
production session; fixture timing alone does not establish a scene speedup.
The default communication period is one physics tick; longer periods must be
authored explicitly.
Physical celestial sampling uses `CelestialClock::epoch_at_tick`, with
`celestial_physical_inputs` at interval start before environment computation and
`celestial_physical_endpoint` after native writeback. The component's tick is
the admitted cycle, and the sampler owns its phase. Presentation's angular
error gate never chooses physical inputs. Directions, gravity and solar
visibility share `SimulationPoseQuery`; do not restore raw hierarchy readers
for moving probes. Reuse the ephemeris provider's exact-epoch cache. Attribute
sampling separately from native physics and from the rendered celestial chain;
its cost must not be hidden inside a physics-only timing number.
For deliberately frozen captures use `StepSimulation` and presentation-timed
sequencing. Manual demand uses the same fixed runner as paced execution and
still respects preparation and solver barriers. A native hold gates the entire
simulation; compare `clock_snapshot().physics_elapsed_s` and `fixed_elapsed_s`
at completed-cycle boundaries before interpreting throughput measurements.
Attribute numerical service separately from transaction dispatch and result
admission. Live workers pass the exact communication point to the existing
`LiveStepper::advance_to` boundary; adaptive Rumoca sessions and fixed
prediction own their internal steps. Preserve the fixed prediction lattice,
event handling, input sampling and finite/completion validation when optimizing
that path. Do not add another worker-side integration grid or catch-up planner.
For replay divergence, enable
`RUST_LOG=info,lunco_modelica_worker::step_audit=debug` in a separate diagnostic
run. Compare session-relative step ids, communication times and ordered input/
output fingerprints for each exact participant identity. These bounded log
records locate differences; hash equality cannot prove state equality. Use
`trace` for complete accepted samples, or `ReadModelicaStepSamples` for selected
values in the last accepted transaction. Full traces can produce large logs
and perturb scheduling; check storage and retain an uninstrumented acceptance
run. Never infer a numerical repair solely because tracing hides a divergence.
Keep communication-clock checks at floating point roundoff scale, with finite
timestamps required at both request and completion admission. A solver's state
tolerance must not loosen its communication endpoint validation. Validate the
endpoint using `start + dt`; subtracting large absolute timestamps can reject
a valid short interval through cancellation.
UI exposure publication is paced by `Time<Real>`, independent of simulation
pause and rate. Its dirty-state gate still coalesces unchanged facts. Attribute
main-thread snapshot capture separately from `runtime_ui_policy_preparation`
and owner publication. Native pure policy evaluation uses shared bounded worker
admission, retaining captured hooks and caller context; one job plus one latest
pending snapshot per surface bounds work. Source and identity changes fence
completion. The wasm kernel is currently cooperative, not an isolated worker.
Wait for ready surfaces and inspect preparation failures before taking a UI
performance cohort. This boundary does not isolate the entire fixed runner
from the presentation thread. Keep
mission/timeline waits and telemetry deadlines on their declared simulation
clocks; changing their timers to wall time changes simulation semantics.
For script cost, attribute `rhai_native_task_tick` by stable actor gid and
compare the same task-invocation range or simulation phase. Equal wall-time
windows need not contain the same mission work when realtime pacing differs.
Task callbacks borrow the persistent map through Rhai's bound receiver; see
[the callback contract](../../docs/architecture/rhai-task-tree.md#leaf-callback-contract).
Preserve captures, imports, state/error semantics and fixed cadence while
removing copies at that boundary.
For cost or memory that grows with runtime, check interpreter scope lifetime:
initialized globals persist, while per-call constants and function locals
must be released on both success and error. Use Rhai's scope-rewinding option
at every scenario call boundary; do not periodically clear live scenario state.
For Rhai presentation cost, inspect helper arguments and array iteration for
copies of nested facts. Project the fields a helper consumes before calling
it; indexed access can count participant statuses without copying their model
input/output maps. Preserve the public hook facts and compare complete property
outputs, including empty and unavailable states. Use paired real-owner spans
for attribution; synthetic large-payload timings alone do not establish FPS.
For typed Rhai boundary cost, use the shared `lunco-hooks-rhai` converter.
It borrows input containers; embedding owners lower native leaves through
`dynamic_to_hook_with_native`. Do not build a normalized Dynamic tree before
calling it, clone containers to inspect their type, or add another recursive
converter. Preserve native f64 math, unsigned integers, shared values and
visible rejection of unsupported types. Check the interpreter resource seam
with `typed_conversion_rejects_nested_native_values_without_cloning_them`, then
exercise production typed command/query consumers, including a negative case.
For port-exchange cost, inspect `BindingStatus.connections` and join targets to
`ListPorts` owner metadata. The causal-state sink count is not the number of
compiled wire targets: scene/shader inputs also participate in the exchange.
Separate `cosim_exchange_write` from accumulation before changing routing.
Preserve live metadata, bounds, ownership and stale-slot checks. For shader-name
cost, use the shared borrowed converter described in
[shader parameters](../../docs/architecture/shader-layers-and-params.md);
canonical-name lookup must not allocate, and entities without `ShaderLook` must
reject before conversion. Allocation probes and kernel timings do not certify
whole-scene FPS.
For native physics ordering investigations, enable
`lunco_physics::order_audit=debug` in an owned diagnostic session. The continuous
owner records native body and collider identities, poses, mass/inertia and
velocities, plus contact-graph IDs, colours, manifold indices and prepared
point geometry at the physics clock. Compare the first divergence before
attributing a difference to pool size. This logging is diagnostic and must be
disabled for performance acceptance. Partial pose checkpoints do not prove
full-state replay.
Compound audit rows include native scale and local part mass properties.
If the first divergence is already in COM, compare admitted geometry and
mass ownership before attributing later motion to the query dispatcher.
For time-zero mass/inertia, inspect `PhysicsGeometryAdmission` before changing
the authored checkpoint or reference. The shared native geometry schedule must
finish before USD publishes physics-ready; advancing a solver tick to prepare
geometry would change the initial state. Qualify preparation changes with the
rootless scale/mass mechanism check and the unchanged four-rover production
gate, including its deliberately altered-row negative.
`PhysicsGeometryPreparation` uses the native single-thread executor for its
short ordered chain. Compare the same source cohort in normal A/B/A windows
and require exact replay against the maintained reference before changing its
executor. Main-thread schedule self time includes worker waits; do not label
the whole self span dispatch overhead. Attribute that child schedule separately
from the complete fixed cycle and the native solver.
The shared application also configures `FixedPostUpdate` with the native
single-thread executor for the surrounding physical commit chain. Attribute
that outer schedule separately from `PhysicsSchedule` and geometry preparation.
Keep native parallel queries, Modelica worker pools and authored replay checks
when comparing executor configurations.
When attributing native GPU preprocessing, trace allocation identity rather
than only buffer capacity. Bevy's `collect_buffers_for_phase` exchanges
per-phase and frame-level buffer cohorts each frame; equal sizes do not mean
consecutive bindings agree. Native view groups must be present on current
extracted views. Compare actual buffer identities, binding offsets/ranges,
layout descriptors, occlusion textures, work modes/counts and bin-job
parameters, and measure admission overhead and native rebuild frequency.
Pipeline-cache change ticks alone are unsuitable: native queue processing
mutably accesses that resource every frame. Preserve native deferred publication
and every GPU preprocessing/draw pass. See the current handover for the owned
Ver en GitHub