perf(desktop): Engine Benchmark plate crops decode via the engine codec, not pure-Dart - #545
Merged
Merged
Conversation
The Engine Benchmark's measured client-side bottleneck (#391) was package:image's pure-Dart JPEG decode of the full detection frame, run per newly-mounted row to derive the plate crop: ~130 ms per 1080p frame (~260 ms at 4 MP) against ~18 ms for the engine's native codec. A 10-row screenful spent ~478 ms wall on images, of which only ~43 ms was fetch. cropPlateToBbox now decodes with the engine codec and rasterizes only the bbox region, so the full decoded frame never crosses into Dart; package:image touches just the small crop (near-black guard + JPEG re-encode). The pure-Dart path stays as the reference implementation and decode fallback. Same 10-row screenful now: ~156 ms wall. Crop bytes, dimensions, aspect caching, and the near-black rejection semantics are unchanged (locked by the new region tests). Signed-off-by: badbread <badbread@users.noreply.github.com>
Signed-off-by: badbread <badbread@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The client was finally measured, and the bottleneck is the pure-Dart decode
#391's correction said to start by measuring the client, not the server. Done: a harness that mounts the real benchmark dialog (widget test against an in-process HTTP server serving a 165-pass report + realistic ~150-175 KB 1080p JPEG frames), plus micro-benchmarks of the exact
plate_crop.dartpipeline. All numbers from the same 8-core Windows build machine, Flutter 3.44.6.What the dialog actually does (measured, not assumed)
ListView.separated: only 7 rows mount at the default dialog size, 10 snapshot fetches go out (visible + cache extent) — there is no 165-row build and no 165-request stampede. Two of the issue's three candidates are refuted.package:image's pure-Dart JPEG decode of that frame to derive the plate crop, in a fresh isolate. That decode is the bottleneck.Where the time goes (before)
img.decodeImage, 1080pimg.decodeImage, 4 MPcopyCrop+encodeJpg(the rest of the crop)dart:ui) decode of the same 1080p bytescacheWidth)Isolate.runspawn overheadA 10-row screenful, current pipeline: 478 ms wall — of which fetch-only is 43 ms. ~90% of image latency is the client decode, paid again for every screenful scrolled.
Change
cropPlateToBboxnow decodes with the engine's native codec (instantiateImageCodecWithSize) and rasterizes only the bbox region (drawImageRect→toImage→ small readback), so the full decoded frame never crosses into Dart.package:imagestill handles the near-black guard and the JPEG re-encode, but only ever sees the tiny crop. The pure-Dart path is kept unchanged as the reference implementation and as the fallback when the engine can't decode the bytes.Callers are untouched: same signature, same JPEG bytes out, same
(bytes, w, h)dimensions/aspect contract, same null-on-near-black semantics (issue #179), same crop cache. The Plates gallery/detail crops and the PDF report ride the same helper, so they get the same win.After (same harness, same machine)
cropPlateToBbox)The 43 ms fetch floor is unchanged; thumbs now land ~3x sooner per screenful, and the per-row cost no longer scales with camera resolution nearly as hard (the 4 MP pure-Dart decode was 2x the 1080p one; native decode is a fraction of either).
Tests
test/plate_crop_region_test.dartlocks the fast path: crops the correct region at the correct dimensions (red-box frame), agrees with the pure-Dart reference on clamped dimensions for an out-of-range box, still rejects a near-black crop, and degrades (not throws) on undecodable bytes.plate_crop_aspect_test.dartpasses unchanged on the new path.flutter analyze: no new issues (224 pre-existing infos before and after).flutter test: 157/157 pass (153 existing + 4 new).On #394 (closed server-side derivatives)
Nothing here changes that verdict. With the bottleneck confirmed client-side, the derivatives were solving the wrong problem for LAN use; they remain a legitimate future win for VPN/cellular where 148 KB/row is real transfer cost, and would compose fine with this change if revived.
Fixes #391