Skip to content

fix(acestep): C512 VAE windows on 1 GiB-capped adapters (iPhone memory) - #58

Merged
Alex-Wengg merged 3 commits into
mainfrom
fix/ios-memory-downshift
Aug 24, 2026
Merged

fix(acestep): C512 VAE windows on 1 GiB-capped adapters (iPhone memory)#58
Alex-Wengg merged 3 commits into
mainfrom
fix/ios-memory-downshift

Conversation

@Alex-Wengg

@Alex-Wengg Alex-Wengg commented Aug 24, 2026

Copy link
Copy Markdown
Member

Problem

Real iPhone 17 Safari, after #55 (1 GiB buffer-limit cap) and #57 (portable subgroups): generation now starts and crashes midway through a 2-minute song — the iOS jetsam tab kill under memory pressure, not a WebGPU validation error.

#55 fixed bindability (no single buffer above 2^30) by downshifting the configured C2378 VAE windows to the capped C2176 contract. But bindability was the wrong criterion for what an iPhone-class adapter can hold: the FP16 VAE backend allocates three whole-window workspaces, so C2176 still resides 3 × 1,069,547,520 ≈ 3.21 GB of workspace during the decode phase. #55's own "Remaining iPhone risks" section called this out as untested.

Change

selectAceVaeWindowRuntimeProfileForLimits now resolves a configured C2378 contract by what the adapter's limits betray about the device — still purely limits-derived, never UA-sniffed:

  • At or below the one-GiB cap (every iOS WebGPU adapter reports exactly 2^30 for both maxBufferSize and maxStorageBufferBindingSize): the upstream C512 baseline geometry is selected — 3 × 251,658,240 = 755 MB total workspaces, ~943 MB accounted VAE phase. C512 is upstream-authenticated: OPT-0035's correctness authority proved the overlap-64 discard decode byte-identical across window geometries, so the smaller authenticated window is exact by construction (re-verified below — same WAV sha256).
  • Above one GiB but below the C2378 workspace: the fix(acestep): support 1 GiB-capped WebGPU adapters (iPhone) #55 capped C2176 contract remains selected (desktop-class memory, only bindability short). No behavior change in that region.
  • Below the C512 workspace: the configured contract is returned unchanged so the device request fails closed with its true deficits. Configured C512 contracts never change geometry.

The derived device limit request consequently shrinks on capped adapters from 1,069,547,520 to the C512 workspace, which merges under the base Stage-1 contract (268,435,456) — exactly the limits the whole pipeline (DiT included) ran under before OPT-0070 raised them for the C2378 workspace. A new plan-level test pins that every production DiT arena slot at the 240 s maximum still fits (largest slot: 73,728,000 bytes). The OPT-0080 depth-two VAE policy stays disabled off-C2378 (audited depth-one baseline), diagnostics keep reporting the configured window identity, and the 240 s maximum decodes as 16 bounded windows.

Second fix the e2e itself surfaced: receipt validation against the effective plan

The first capped e2e run of this branch completed generation and then failed with INVALID_GENERATION_RESULT: isVaeSchedulingReceipt reconstructed the expected chunk plan from the configured diagnostics identity (512 or 2378 only). #55's 30 s e2e passed that check by coincidence — 750 latent frames are a single window under both C2176 and C2378 — but under C512 the same song decodes as two windows. The validator now reconstructs the same authenticated selection the pipeline used (requireAceVaeWindowRuntimeProfileselectAceVaeWindowRuntimeProfileForLimits over the adapter limits already present in the diagnostics capability report); nothing is accepted by size alone, and incoherent identities still fail closed. The OPT-0080 protocol fixture now advertises adapter limits that can actually bind C2378 (its previous one-GiB fixture limits made a C2314 depth-two receipt an impossible tuple on the reported adapter).

Per-phase accounted GPU bytes on a capped (2^30) adapter

From the runtime's own memory planners (DiT: resident weights + arena + readback; VAE: weights + 3 workspaces + staging/output/readback/control):

Phase 30 s 120 s 240 s
DiT denoise (unchanged) ~3,312,270,592 ~3,639,069,440 ~4,076,829,440
VAE decode before (C2176) 3,448,065,296 3,457,405,216 3,469,677,616
VAE decode after (C512) 943,155,232 944,958,512 944,667,696

The DiT→VAE release ordering is real, not aspirational: the pipeline drains and destroys the DiT owner (dit-drain-and-destroy, hardBoundaryBeforeVae: true) before the VAE package is even acquired, and the suite asserts vae-acquire orders after dit-destroy. Phases do not stack; the per-generation peak is the max of the rows above.

Plain statement of what this does and does not fix

  • Fixed: the VAE decode phase no longer holds ~3.45 GB on iPhone-class adapters (now ~0.94 GB, a ~2.5 GB reduction). If the mid-song crash happens during decode — or decode-on-top-of-a-heated-process is the last straw — this resolves it.
  • Not fixed, out of scope here: the DiT phase alone holds ~3.3–4.1 GB (dominated by 3,150,917,888 bytes of resident fp16 weights) and plausibly exceeds an iPhone Safari tab budget by itself. If the crash occurs during denoising, no window geometry can help — that requires DiT weight compression/quantization, a separate quality-gated effort. Hamza: please test a 30 s song on the device to isolate. 30 s keeps the DiT phase at its smallest (~3.31 GB); if 30 s completes but 2 min still dies during denoising, the remaining pressure is DiT-resident weights, not the VAE.

Selection matrix (vendored vitest, extended per the #55 pattern)

Adapter min(maxBufferSize, maxStorageBufferBindingSize) Selected geometry
≥ 1,168,834,560 (C2378 binds) C2378 (unchanged)
(2^30, 1,168,834,560), e.g. 1.1 GB capped C2176 (unchanged from #55)
≤ 2^30 and ≥ 251,658,240 (incl. exactly 2^30 — the iOS signature — and 800 MB-class) C512
< 251,658,240 fail closed with configured C2378 deficits
any, configured C512 C512 (never changes)

Verification

  • npm run acestep:test: 216 files, 2025 passed, including new vae-capped-c512-downshift.test.ts (selection resolution, 240 s C512 plan + workspace accounting + per-buffer fit, DiT arena slot fit under the C512-derived device limits) and updated pipeline tests (2^30 → C512 end to end with a 30 s two-window request validated through isAceGenerationResultValue; 1.1 GB → C2176 intermediate; fail-closed threshold below C512).
  • Root gates: npx tsc --noEmit, npm run build, npm run format:check all clean (prettier does not touch the vendored package).
  • Registered source hash for src/runtime/webgpu-pipeline.ts re-pinned in the OPT-0023 browser contract (comment-only change at the deriveRequiredLimits seam); no optimization/ records touched.

E2E on real Chrome + WebGPU, adapter capped to 1,073,741,824 via ?aceTestMaxGpuBufferBytes (#55 seam)

  • 30 s / seed 12345 generation completes in 18.1 s (warm cache); runtime metrics confirm adapterLimits.maxBufferSize=1073741824, requestedLimits.maxBufferSize=268435456 (was 1069547520), deviceLimits.maxBufferSize=268435456; the VAE ran as two C512 windows and the scheduling receipt validated.
  • WAV byte-identical across the geometry change: sha256 095267d7be0317ed9af10c64b8495d573b43207cd86615b6bbe66f27dc17895d — same as the fix(acestep): support 1 GiB-capped WebGPU adapters (iPhone) #55 capped baseline and the uncapped desktop baseline.
  • Peak tracked GPU bytes: 3,448,065,296 (before, the C2176 VAE phase) → 3,277,864,192 (after — the peak is now the DiT phase; the VAE phase accounts ~943 MB).
  • Fail-closed negative control: adapter capped at 200 MB (below the C512 workspace) fails at device request with the configured C2378 deficits (LIMIT_UNAVAILABLE: maxBufferSize must be at-least 1168834560; adapter reports 200000000) — no downshift below the authenticated geometry.

…memory

PR #55's capped C2176 window contract fixed the LIMIT_UNAVAILABLE device
rejection on iPhone (2^30 maxBufferSize adapters), but generation now
dies mid-song: the C2176 VAE phase holds three whole-window FP16
workspaces of 1,069,547,520 bytes each, a 3,448,065,296-byte accounted
GPU phase on a 30 s song (the tracked peak of the capped e2e baseline),
and iOS jetsam kills the Safari tab under that residency. Bindability
was the wrong criterion on iPhone-class adapters; total resident
workspace bytes are the binding constraint.

selectAceVaeWindowRuntimeProfileForLimits now resolves a configured
C2378 contract by what the adapter limits betray about the device,
still purely limits-derived and never UA-sniffed:

- At or below the one-GiB cap (every iOS WebGPU adapter reports exactly
  2^30 for both buffer limits) the upstream C512 baseline geometry is
  selected: 3 x 251,658,240-byte workspaces, ~944 MB accounted VAE
  phase, a ~2.5 GB reduction. OPT-0035's correctness authority proved
  the overlap-64 discard decode byte-identical across window geometries,
  so the smaller authenticated window is exact by construction; the 30 s
  seed-12345 capped e2e WAV stays byte-identical (sha256 095267d7...).
- Above one GiB but below the C2378 workspace the capped C2176 contract
  remains selected (desktop-class memory, only bindability short).
- Below the C512 workspace the configured contract is returned unchanged
  so the device request fails closed with its true deficits; configured
  C512 contracts never change geometry.

The derived device limit request consequently shrinks to the C512
workspace, merging to the base Stage-1 256 MiB contract that the whole
pipeline (DiT included) ran under before OPT-0070 raised it for the
C2378 workspace; a new plan test pins that every production DiT arena
slot at the 240 s maximum still fits (largest: 73,728,000 bytes). The
240 s C512 decode is 16 bounded windows.

The DiT phase itself is untouched and remains the capped-adapter peak
(3.31 GB accounted at 30 s, 3.64 GB at 120 s, dominated by
3,150,917,888 bytes of resident fp16 weights). If the iPhone crash
occurs during denoising rather than decode, only weight compression can
fix it, which is out of scope here.

Tests: selection matrix (one-GiB to C512, 1.1 GB to C2176, at/above
C2378 unchanged, fail-closed below C512), 240 s C512 plan and workspace
accounting, DiT arena slot fit, pipeline downshift and sub-C2378
intermediate tests. The OPT-0023 registered source hash for
webgpu-pipeline.ts is refreshed for the comment change.
…window plan

The capped C512 e2e failed at the last step with
INVALID_GENERATION_RESULT: isVaeSchedulingReceipt reconstructed the
expected chunk plan from the configured diagnostics identity
(vaeMaxWindowFrames 512 or 2378), but the runtime plans windows from the
adapter-derived effective geometry. PR #55's 30 s capped e2e passed this
check only by coincidence: 750 latent frames are a single window under
both C2176 and C2378, so the receipt matched the configured plan. Under
the C512 downshift the same song decodes as two windows and the receipt
was rejected after a fully successful generation.

The validator now reconstructs the same authenticated selection the
pipeline used: requireAceVaeWindowRuntimeProfile on the reported window
identity, then selectAceVaeWindowRuntimeProfileForLimits over the
reported adapter limits from the diagnostics capability report. Nothing
is accepted by size alone, and an incoherent identity still fails
closed inside the existing try/catch.

The opt0080ProductionDiagnostics protocol fixture now advertises
adapter limits that can actually bind the C2378 workspace (the base
one-GiB fixture limits would downshift, making a C2314 depth-two
receipt an impossible tuple on the reported adapter). The pipeline
downshift tests now run a 30 s request (two C512 windows, mirroring the
e2e regression) and assert isAceGenerationResultValue on the result for
both the C512 and intermediate C2176 selections.
A blanket coherence error cost a debugging cycle; the reject message now
lists which identity clause diverged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant