`PipelineTimingReport`
PipelineTimingReport is the per-frame performance breakdown surfaced
by PoseCameraView.onPipelineTiming
(and the same callback on TrackedMovementView).
It’s what the LiveDiagnosticsSheet
renders as the per-stage waterfall.
TrackedMovementView( // ... onPipelineTiming: (report) { debugPrint( 'the pose engine: ${report.cInferenceMs.toStringAsFixed(1)}ms ' '(bb=${report.cBackboneMs.toStringAsFixed(1)} ' 'lm=${report.cLandmarkHeadMs.toStringAsFixed(1)})', ); },);Shape
class PipelineTimingReport { // Dart-side stages final double colorConvertMs; final double isolateSendMs; final double isolateReceiveMs; final double dartTrackingMs;
// C-side the pose engine rollups final double cInferenceMs; // total C-side time final double cDetectorMs; // SSD detector segment final double cCropMs; // ROI crop segment final double cLandmarksMs; // total landmark stage final double cPostprocessMs; // landmark post-processing
// C-side per-segment breakdowns final double cBackboneMs; final double cDecoderMs; final double cLandmarkHeadMs;
// Frame summary final double totalMs; // wall-clock total final bool usedDetector; // heavy frame vs tracking reuse
// Backend ids the pipeline negotiated this frame final int backboneBackend; // BF_BACKEND_* id final int decoderBackend; final int landmarkBackend;
final int frameNumber; final DateTime timestamp;}Backend ids map to short names via:
| Id | Name |
|---|---|
| 0 | cpu |
| 1 | metal |
| 2 | nnapi |
| 3 | opencl |
| 4 | vulkan |
onBackends on PoseCameraView
delivers the human-readable names once after init, use that for
one-shot display rather than mapping the id every frame.
Reading the stages
A per-frame waterfall fits roughly into three groups:
1. Dart-side pre-inference colorConvertMs , YUV → BGR (usually 0 now that C handles it) isolateSendMs , Pigeon marshalling + TransferableTypedData send
2. C-side inference cBackboneMs , backbone CNN (big) cDecoderMs , pose decoder (medium) cLandmarkHeadMs , landmark head (medium) cDetectorMs , SSD detector (only on usedDetector=true frames)
3. Dart-side post-inference isolateReceiveMs , result deserialisation dartTrackingMs , MovementTracker.processFrame Dart-side costtotalMs is the wall-clock total, it should equal the sum of the
above within a few hundred microseconds (the difference is overhead
the report doesn’t itemise).
When usedDetector is true
The pose pipeline runs the heavy SSD detector either:
- Periodically (every
redetect_intervalframes, typically every ~30 frames). - On demand when tracking is lost.
Detector frames are roughly 2–3× the cost of a tracking-reuse frame.
Use usedDetector to separate the two when computing FPS averages ,
otherwise the detector cadence makes the per-frame timing look more
variable than it is.
PipelineTimingStats aggregator
pose_flow ships PipelineTimingStats for accumulating reports over
a window. Call add(report) per frame and summary() to dump a
formatted string with mean / median / p95 / min / max / count per
field, useful for benchmarking from a debug shell or perf test.
final stats = PipelineTimingStats();
// per framestats.add(report);
// after a rundebugPrint(stats.summary());Read next
PoseCameraView, the producer (onPipelineTiming).LiveDiagnosticsSheet, the canonical consumer, renders the waterfall.- Tracking pipeline, explains what each stage actually does.