Skip to content

feat(webv2): surface Ref2VA image-reference cost, tier the detail defaults - #209

Open
lstein wants to merge 1 commit into
mainfrom
feat/webv2-reference-detail-cost
Open

feat(webv2): surface Ref2VA image-reference cost, tier the detail defaults#209
lstein wants to merge 1 commit into
mainfrom
feat/webv2-reference-detail-cost

Conversation

@lstein

@lstein lstein commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Why

The image-reference detail choice ("Max detail (2048px)" vs "Match generation size") is a much bigger performance lever than it looks, and the panel gave no hint of that.

An image reference is patchified like any frame — width × height / 1024 rows at H3's 16× spatial compression and 2×2 patch — and those rows are re-attended at every denoising step, with attention quadratic in the sequence length. max pins the short edge to 2048 regardless of the generation size, so the cost is worst exactly when the output is smallest.

Measured with the real code paths (124 frames, the UI default; VRAM from the denoise node's own 0.25 MiB/row model):

Canvas Source max match
1344×768 — 37,710 generated rows 1920×1080 3648×2048 → 7,296 rows (+19%, +1.78 GiB, ~1.4× attention) 1344×768 → 1,008 rows (+3%)
1344×768 4032×3024 2720×2048 → 5,440 rows (+14%, +1.33 GiB) 1184×896 → 1,036 rows (+3%)
768×768 — 21,726 generated rows 1920×1080 7,296 rows (+34%, +1.78 GiB, ~1.8× attention) 576 rows (+3%)

Three max references add ~16k rows — roughly 2× the attention work and ~+4 GiB.

What

1. Each image reference card shows its cost. The size the graph will encode plus the rows it adds, under the detail selector, updating live as the detail or the canvas changes. resolveMiniMaxH3ReferenceImage (in features/video/core/dimensions.ts, beside the existing canvas ports) mirrors the backend's resolve_reference_image_short_edge + normalize_reference_image — banker's rounding included, via the module's existing roundHalfToEven — so this is the real size, not an approximation. Null (and nothing rendered) when match has no canvas to match.

2. Tiered detail defaults. The first image reference still starts at max — upstream's rule, and the primary subject is where the extra detail earns its cost — while later ones start at match. The backend node's own default is unchanged at max; this is a panel convenience for the multi-reference case, and both the help text and the new per-card row count make it visible rather than surprising.

Testing

  • resolveMiniMaxH3ReferenceImage: an 8-row table cross-checked against the Python implementation's output for the same inputs, plus the 2048 clamp, the no-target-area cases, and degenerate input.
  • getDefaultReferenceImageDetail: first-image / later-image / video-only-list cases.
  • pnpm run check:architecture green locally (509 unit + 158 browser files, perf and project-file gates included).

Independent of #206; both touch VideoReferenceListField.tsx, so whichever lands second will want a trivial merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_01S4B5exWsbC2z2Uu2tA167A

…48px defaults

An image reference is patchified like any frame -- width * height / 1024
rows at H3's 16x spatial compression and 2x2 patch -- and those rows are
re-attended at EVERY denoising step, with attention quadratic in the
sequence length. "Max detail" pins the short edge to 2048 whatever the
generation size is, so a 1920x1080 reference normalizes to 3648x2048 and
adds 7,296 rows: +19% on a 1344x768 x 124-frame request (~1.8 GiB by the
denoise node's 0.25 MiB/row model, ~1.4x the attention work), and +34% on
a 768x768 one. "Match generation size" costs ~1,000 rows for the same
image. Nothing in the panel said any of this before the queue.

Two changes:

- Each image reference card now shows the size the graph will encode and
  the rows it adds, live as the detail or the canvas changes.
  `resolveMiniMaxH3ReferenceImage` mirrors the backend's
  `resolve_reference_image_short_edge` + `normalize_reference_image`,
  banker's rounding included, so the estimate is the real size; its table
  test is cross-checked against the Python implementation.

- The FIRST image reference still defaults to "max" -- upstream's rule,
  and the primary subject is where the detail earns its cost -- while
  later ones default to "match". Three 2048px references are what turn a
  modest surcharge into a doubling, and the marginal value of a supporting
  reference at seven times the output's pixel density is small. The help
  text says so, and the per-card row count makes it visible.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S4B5exWsbC2z2Uu2tA167A
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant