Skip to content

`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:

IdName
0cpu
1metal
2nnapi
3opencl
4vulkan

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 cost

totalMs 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_interval frames, 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 frame
stats.add(report);
// after a run
debugPrint(stats.summary());