Files
BadNote/docs/plans/phase1-perf-results.md
Akiba So 2afc126f30 docs(plan): phase 1 rebuild plan + M1 gate results
pdfrx render core, static/live ink layers, RenderProxyBox pen/touch
arbitration, infinite board, text boxes. Planner-Architect-Critic
consensus APPROVE. MUST #1 (API source-pin) and MUST #2 (coordinate
assertion) verified on pdfium; MUST #3/#4/#5 pending on Surface Pen.
2026-06-21 20:15:13 +08:00

66 lines
6.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# BadNote Phase 1 — Performance Gate Results
**Status:** IN PROGRESS — MUST #1 (API source-pin) GREEN on pdfrx **2.4.4** (Dart-only verification, done). MUST #2/#4/#5 require a device/profile run; MUST #3 requires the physical Surface Pen. Spike code compiles clean (`flutter analyze`: no issues).
---
## M1 Gate Results
| Gate | Target | Result | Device | Commit | PASS/FAIL |
|------|--------|--------|--------|--------|-----------|
| MUST #1 — pdfrx API source-pin | All `[VERIFY-IN-M1]` tags replaced with pinned signatures from `~/.pub-cache/…/pdfrx-2.4.4/lib/src/` | All APIs confirmed; match plan (see table below) | Dev box (Linux) | feat/m1-pdfrx-spike | **PASS** |
| MUST #2 — Coordinate assertion | Stroke at normalized (0.5, 0.5) lands at visual page center at fit / 2× / 4× zoom | **PASS** on real pdfium (`integration_test/coordinate_assertion_test.dart`, "All tests passed", PDF load 74ms). Re-confirm on Windows. | Linux desktop (WSL2) | feat/m1-pdfrx-spike | **PASS (Linux)** |
| MUST #3 — Pen/touch arbitration on Surface Pen | Pen draws AND single-finger touch scrolls AND pinch zooms simultaneously in same overlay; no mode switching | PENDING | Surface Pro (physical device) | — | PENDING |
| MUST #4 — pdfrx-alone perf (300-page fling-scroll) | Median frame (build+raster) ≤ 16.6 ms, p95 ≤ 22 ms; N ≥ 120 frames; profile mode | Harness ready (`integration_test/perf_scroll_bench.dart`). NOT measured — WSL2 dev box was under heavy concurrent load → invalid signal, discarded. | — | — | PENDING (run on Surface, profile, idle machine) |
| MUST #5 — ink-overlay build cost (300 synthetic strokes/page) | Median BUILD time ≤ 16.6 ms during fling-scroll; N ≥ 120 frames; profile mode | Harness ready (same bench, ink-load toggle). NOT measured (same reason). | — | — | PENDING (run on Surface, profile, idle machine) |
---
## Pinned pdfrx APIs
To be filled during M1 sub-task 1 (`flutter pub add pdfrx` + read `~/.pub-cache/…/pdfrx-*/lib/src/`).
All signatures verified against `~/.pub-cache/hosted/pub.dev/pdfrx-2.4.4/` on 2026-06-21.
| API | Pinned signature / notes | Source file:line |
|-----|--------------------------|-----------------|
| `pageOverlaysBuilder` | `typedef PdfPageOverlaysBuilder = List<Widget> Function(BuildContext context, Rect pageRectInViewer, PdfPage page)`. `pageRectInViewer` is **already scrolled+zoomed** (viewer coords). Children laid out in a per-page `Stack`. Matches plan §2.1. | `lib/src/widgets/pdf_viewer_params.dart:1623`, field `:554` |
| `viewerOverlayBuilder` | `typedef PdfViewerOverlaysBuilder = List<Widget> Function(BuildContext context, Size size, PdfViewerHandleLinkTap handleLinkTap)`. Docs: a `GestureDetector` here must use `HitTestBehavior.translucent` + `IgnorePointer` child to let events reach the viewer. | `:1572`, field `:513` |
| `PdfViewerController.goToPage` | `Future<void> goToPage({required int pageNumber, PdfPageAnchor? anchor, Duration duration = 200ms})`. (Also `goToRectInsidePage({required int pageNumber, required PdfRect rect, ...})`.) | `lib/src/widgets/pdf_viewer.dart:4140` |
| `PdfViewerController.currentZoom` | `double get currentZoom => value.zoom;` | `:4318` |
| `PdfViewerController.layout` | `PdfPageLayout get layout;``layout.pageLayouts` is `List<Rect>` in **document** coords (index by page). | `:4029`; pageLayouts usage `:895,1068` |
| `PdfViewerController.globalToDocument` | `Offset? globalToDocument(Offset global)` (public). Also `Offset documentToGlobal`, `Offset? localToGlobal`. | `:4403` |
| `PdfViewerController.documentToLocal` | `Offset documentToLocal(Offset document)` (public). | `:4412` |
| `PdfRect.toRect` | Extension in pdfrx_flutter: `Rect toRect({required PdfPage page, Size? scaledPageSize, int? rotation})`**handles rotation** (removes our bespoke rotate math). Also `toRectInDocument({required PdfPage page, required Rect pageRect})`. | `lib/src/pdfrx_flutter.dart:107` |
| Gesture recognizer (pan/zoom) | pdfrx pan+zoom is an **internal InteractiveViewer** using `GestureDetector(onScaleStart/Update/End)` (scale gesture covers pan+pinch). Default `GestureDetector` accepts all device kinds incl. stylus → **a stylus drag WOULD be claimed by the scale recognizer unless intercepted above**. Confirms R1 and justifies the `PenCaptureRegion` arena-bypass. | `lib/src/widgets/interactive_viewer.dart:1560-1564, 839-948` |
| `PdfOverlayInteractionRegion` (fallback) | Exists (`class PdfOverlayInteractionRegion extends StatefulWidget`), but pdfrx docs state it is **tap-oriented only** (tap/double-tap/long-press/secondary) — **NOT a freehand-drag stream**. ∴ it cannot be the primary draw transport; `PenCaptureRegion` (RenderProxyBox per-kind hit-test) is THE path, not just a preference. | `lib/src/widgets/pdf_viewer.dart:4659`; docs `pdf_viewer_params.dart:483-538` |
| `PdfPage.render` (thumbnails) | To confirm in M6 prep — pdfrx exposes page render via `PdfPage` (pdfrx_engine); verify exact RGBA path before removing syncfusion-viewer dep. | (deferred to M6 prep) |
| `panAxis` | Still **[VERIFY-IN-M1 on device]** whether `PanAxis.vertical` blocks the horizontal component of a pinch-zoom pan; spike uses it — confirm on tablet whether `PanAxis.free` is needed when zoomed. | `pdf_viewer_params.dart` (panAxis field) |
**Transport finding (R1):** Flutter hit-tests a pointer only on its DOWN and caches the path; `RenderBox.hitTest` receives only the position, **not** the pointer kind. The spike resolves this with `PenCaptureBinding` (overrides `GestureBinding.handlePointerEvent` to stash the in-flight `event.kind` before the synchronous hit-test) which `_RenderPenCapture.hitTest` reads — capturing stylus outside the gesture arena while touch/mouse fall through to pdfrx. Compiles clean; **empirical pen-vs-touch behavior remains MUST #3 (physical Surface Pen).**
---
## Sample Protocol (§7.1)
- **Minimum sample size:** N ≥ 120 frames per measurement.
- **Mode:** profile build (`flutter run --profile`), **not** debug.
- **Cache:** warm cache — scroll once end-to-end before recording.
- **Runs:** 3 independent runs; report median across runs.
- **Asset:** `test/assets/large_300p.pdf` (300 pages, non-blank content).
- **Stroke asset:** `test/assets/dense_strokes.json` key `"2000"` for MUST #5 (~300 strokes/page spread across visible pages).
- **Reporting:** paste Flutter DevTools frame chart screenshot + numeric summary (median, p95, p99) into this doc with commit hash and device spec.
---
## Device Specs
| Field | Value |
|-------|-------|
| Device | PENDING |
| OS | PENDING |
| Flutter version | PENDING |
| Dart version | PENDING |
| pdfrx version (resolved) | PENDING |
| Surface Pen model | PENDING |