You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: close the gaps the widened mask union and open producer opened
Widening `DetectionMask` and opening the live producer contract both moved a
boundary, and each left a consumer on the far side that had never been asked to
handle what now crosses it.
**A dense mask crashed the editable annotation session.** Routing every reader
through `decodeDetectionMask()` was necessary and not sufficient:
`createEditableAnnotationFrameSession()` never decodes, it deep freezes, and
`Object.freeze()` throws on an array buffer view with elements. Every mask had
been a `counts` string, so nothing in core had met a typed array nested in a
`Detection`. The freeze now steps over views; the buffer is documented immutable
and shared, so there was nothing there to protect. Rebasing onto main surfaced
the same class of bug from the other direction — #90's `regionCoverageMask` is
typed `DetectionMask` and assigned straight from `detection.mask`, so it decodes
through the union now too.
**The producer contract accepted more than it drew.** Only dense masks reach the
fill loops, which is the right call — decoding RLE per detection per frame is
the cost the dense encoding exists to avoid — but a producer publishing cold
storage got a blank overlay and no signal. Skipped masks are now counted and
reported through the readout. Keypoint edge indices are bounds-checked rather
than asserted, since the skeleton is no longer a constant this package owns, and
keypoint color resolves per detection from `className` the way boxes already
did; one frame-wide color was an artifact of pose having had exactly one class.
`createExecutorchPoseKeypointInstructions` returns as a deprecated forwarding
alias, so the rename breaks no import.
**The media clock paid twice for held frames.** It re-reported identical
detections to React on every presented frame, turning a ~1.4Hz cross-thread hop
under `analysis` into ~30Hz under `media`, and rebuilt the ID-mask artifact from
scratch each time — ~15ms per frame with the JS builder on a Pixel 10 Pro.
Those milliseconds come out of the wait time the pacing budget banks, so
refilling an identical artifact was lowering the inference cadence it existed to
protect. `skia.ts` now splits the fill from the upload; the pump caches the
build keyed on the detections array identity held frames already share.
Uniforms and the `SkImage` stay per packet, so `PreparedFrameStore` ownership is
unchanged and a reused artifact reports `fillMs: 0` rather than a cost it did
not pay. Under `analysis`, `shouldInfer` is always true, so that lane is
bit-identical.
Also corrects the record on the spin loop. The pump runtime does have
`setTimeout` — `createWorkletRuntimeForThread()` delegates to worklets'
`createWorkletRuntime()`, whose `enableEventLoop` defaults to true. What it
cannot do is fire one from inside `runPump`, a synchronous loop that never
returns to the run loop. That makes the spin a structural choice with a known
exit at Phase 3, not a missing primitive to go hunting for.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
0 commit comments