9.3 KiB
Advanced performance capture
Windows releases built from this revision include the recorder. No Rust, engine rebuild, profiler installation, network listener or menu setting is needed to record. From a terminal in the installed release directory:
skate3rust.exe --trace university-cpu.json --trace-wait --trace-seconds 30
Load University normally, settle at the area you want to measure, press F9, and skate for 30 seconds. F10 stops and exports early. Continue playing or close normally; the capture does not terminate gameplay. Use a new output filename for each launch: existing files are never overwritten. The destination directory must already exist and be writable. Relative filenames resolve from the working directory.
For GPU pass diagnostics, repeat the same route and settings in a separate launch:
skate3rust.exe --trace university-gpu.json --trace-wait --trace-seconds 30 --trace-gpu
For startup/loading, omit --trace-wait. For unattended delayed recording use
--trace-delay 60 instead of --trace-wait. Delay is measured from recorder
initialization, not map readiness. Duration defaults to 30 seconds (range 1–600);
delay range is 0–600 seconds. There is one capture per process. F9 cannot restart
a completed recording. Trace modifiers without --trace are rejected. Existing
--assets and --map options can select prepared data as usual.
Open the completed JSON using Open trace file in Perfetto.
Select a CPU slice to see its duration; search for physics::advance,
fixed_collision_and_solve, fixed_animation_graphs, prepare_mesh_bind_groups,
prepare_windows, or present_frames. Expand counter tracks for frame intervals
and render pass diagnostics. Load after recording to avoid viewer GPU contention.
Nothing in the recorder uploads captures. Share the JSON file explicitly when needed.
What the measurements mean
- CPU thread slices use Bevy 0.18.1's built-in ECS/system, schedule and render instrumentation plus startup/map/physics subspans. They measure wall time inside a scope, including waits and descheduling, not exclusively CPU execution time. Worker threads have anonymous numeric IDs. Nested slices must not be summed as independent work. Re-entered spans emit separate intervals. Scopes crossing a capture boundary can be absent; partial frames should be excluded from analysis.
frame_interval_msmeasures successive main-appFirstsamples. It includes pacing and scheduling effects; it is not a monitor presentation/scanout measurement.fixed_schedule_iterationscounts fixed schedule loops, not successful physics steps.fixed_period_msreports the existing native-derived timestep. The main sample duration stops at the trace system inLast; unorderedLastsystems, including the FPS pacer, may fall outside it. Use their CPU spans separately.- Render preparation, command encoding, queue submission and
present_framesare CPU intervals. A longprepare_windowscan reflect waiting for the GPU or presentation rather than expensive mesh preparation. Main and render apps are pipelined, so their work need not correspond to the same displayed frame. - With
--trace-gpu,render/.../elapsed_gpucounters are measured GPU timestamp durations in milliseconds, when the device supports them.elapsed_cpuis CPU pass encoding time. Other render counters are pipeline invocation statistics. Results arrive asynchronously and are sampled only when a new diagnostic arrives. They are plotted at CPU receipt time, not on a calibrated GPU timeline; do not infer exact GPU start times or associate them blindly with that main frame. Anonymous series IDs distinguish views; only recognized pass names are retained. - Bevy's instrumented passes are covered, with added diagnostics for the custom retail exposure meter and tone pass. This is not complete GPU-frame coverage: other custom passes may be uninstrumented, and nested pass durations can overlap. Never add all counters to claim total GPU frame time. Missing GPU counters mean unavailable measurements, not zero GPU cost.
- Asset counters show main-world asset entries, not VRAM usage. Map meshes/images
use
RENDER_WORLDownership and can leave those main-world collections after upload. Treat counts as lifecycle clues, not resident memory or draw-call counts. - Metadata includes the existing build ID/revision, engine versions, map fingerprint, initial map counts, timestep, numeric adapter IDs/backend, window dimensions, camera target sizes/MSAA and graphics settings. Map names, source filenames, local asset paths, player names, environment dumps, CLI arguments and log payloads are omitted. The map fingerprint identifies content without publishing its path.
Bounds, overhead and failure behavior
The writer runs on a separate thread with an 8,192-event queue. Full queues drop
events instead of waiting for disk; capture_complete.args.dropped_events reports
loss. A nonzero count makes the timeline incomplete. The event body is limited to
128 MiB, plus a small JSON header/footer, and ends early with size_limit if reached.
The time limit also exports while gameplay continues. A normal return, startup
error or Rust unwind drops the guard and finishes the file. Forced process termination,
native crashes, power loss or disk errors can leave incomplete JSON; this is not a
crash-safe journal. The crash supervisor retains its existing independent behavior.
CPU recording costs timestamps, span bookkeeping, JSON values and queue operations;
export costs CPU and disk bandwidth. GPU queries/readback and pipeline statistics
add overhead. This implementation has not had its overhead benchmarked in-game.
Compare CPU-only and GPU-enabled runs, and treat heavily instrumented results as
diagnostic evidence rather than baseline FPS. GPU instrumentation remains installed
for the entire --trace-gpu launch, including while waiting and after export. CPU
span subscription also remains installed after export, although event creation stops.
Relaunch without tracing for normal play. With no --trace, no recorder, writer,
sampling systems or GPU diagnostics are installed; the log subscriber rejects spans
before recording. Built-in trace callsites remain compiled but disabled.
The application retains its existing Vulkan device configuration. GPU timestamp
and pipeline statistics support depends on adapter features and drivers; the trace
does not force unsupported features or change device limits. Bevy 0.18.1 supports
these diagnostics on Vulkan/DX12, and internally bounds timestamp/query capacity.
CPU capture works when GPU measurements are unavailable. No textures, shaders,
resolution, culling policy, density, animation cadence or simulation timestep are
changed by the launch flags. Do not combine this workflow with the older
SKATE_PERF_REPORT/camera-sweep benchmark environment options: that is a separate
benchmark mode with different controls and collection semantics.
Developer support and tooling choice
The package always enables Bevy's trace feature; the normal static release build
(cargo build --release --locked -p skate-game --bin skate3rust --no-default-features)
therefore supports the CLI. No separate trace_chrome or trace_tracy build feature
is necessary. The custom subscriber retains event logging and the existing panic
hook while filtering timeline and log layers independently; RUST_LOG controls
logs and cannot silently remove CPU trace systems.
Bevy's stock Chrome backend is convenient but its default formatter includes span fields and lacks our capture controls/privacy bounds. Tracy offers live CPU/GPU correlation and richer analysis, but requires matching capture/viewer tooling and an alternative instrumented build. It is a developer escalation, not a requirement for distributed capture. RenderDoc is useful for inspecting actual draws, resources, pipeline state and overdraw on Vulkan; replay/capture overhead means its numbers should not be substituted for an ordinary frame-time profile. Capture only after the user launches the game. wgpu API tracing is primarily reproduction/replay of GPU API traffic, not a replacement for CPU or hardware duration profiling, and may contain asset contents. It is intentionally not enabled by these flags.
Versioned official references: Bevy 0.18.1 profiling guide, Bevy 0.18.1 render diagnostics, wgpu 27 source, and RenderDoc documentation. The pinned local Bevy/wgpu source is the authority for this implementation; newer Bevy documentation uses some different diagnostic names.
For static inventory without starting the game:
cargo run --release --locked -p skate-data --example map-performance-inventory -- MAP.skate
This reports texture payload identity, material bounds and a spatial-cell estimate.
For exact renderer canonicalization, the ignored, data-only
skate_world::performance_inventory::runtime_batches test accepts
SKATE_MAP_INVENTORY and SKATE_MAP_INVENTORY_OUT. Neither initializes Bevy or a GPU.
Keep private inventory paths and personal launchers out of published docs/artifacts.