Luxar Viewer API Documentation - v2026.9.22
    Preparing search index...

    Function computeOpfsWriteQueueBudgetBytes

    • The L2 (OPFS) write queue's share of the same non-cache heap remainder (computeNonCacheRemainderBytes) — the ceiling on bytes pending background writes may retain.

      NOT max(l1Size, 64 MB), which is what this cap was at first (#2528). A pending write's buffer is the SAME Uint8Array the L1 entry holds (see the enqueue site in multi-level-caching-store.ts), so while the entry is still L1-resident it costs one extra REFERENCE, not one extra buffer; the only genuinely extra retention is the pending-and-since-evicted set. Charging the queue for bytes L1 already owns and bounds made the cap track the wrong quantity — on a machine whose L1 resolves to ~100 MB it bound at roughly half the 214 MB a single pathology scene streams, which re-introduced the write drops #2528 had removed (0 → 3 966, #2561).

      What this bounds is nonetheless TOTAL pending bytes, not that evicted-and- pending subset. Accounting only the subset would be tighter (it is what #2561's option 3 proposed) but needs an L1 eviction hook the queue does not have; total-pending is a strict over-estimate of the extra retention, so it is conservative in the safe direction and costs nothing but a lower ceiling.

      Resolved values (targetHeapUsage 0.8, so remainder = heap × 0.8 × 0.4, of which this takes a quarter — i.e. simply heap × 0.08):

      • 4 GiB Chrome heap: remainder 1310 MiB → 328 MiB. That clears both the 214 MB the cmu1 pathology scene streams before its cap bound (peak pending ≈ cumulative streamed here: its 10 961 writes drain 4-wide at tens of ms each — see the queue module's docstring — which is several times slower than the ~9.7 s arrival) and its 277 MB whole-store ceiling. The margin over that ceiling is 1.18×, not comfortable.
      • 2 GiB heap: remainder 655 MiB → 164 MiB.
      • 8 GiB heap: remainder 2621 MiB → 655 MiB, capped at 512 MiB.
      • ?cacheBudgetMB=2048: remainder 1365 MiB → 341 MiB.
      • mobile device-class pool (384 MiB): remainder 256 MiB → 64 MiB.
      • no memory signal at all: 256 MiB. So for that scene the cap BINDS below roughly a 2.6 GiB heap (214/0.08 = 2675 MiB) and its whole store no longer fits below ~3.4 GiB (277/0.08 = 3462 MiB). That is the intended heap-relative behaviour, not a regression: a small heap SHOULD drop writes rather than retain a third of a gigabyte it does not have. Hence no absolute floor on the measured path either — a fixed 256 MiB floor on a 1 GiB heap (whose whole non-cache remainder is 328 MiB) would be no bound at all. The no-signal 256 MiB is deliberately more generous than a measured mid-size heap resolves to: it is a no-INFORMATION default, sized to clear today's demos rather than to a heap nobody could observe.

      The absent floor has one pathological edge, left visible rather than papered over: a tiny explicit pool resolves below a single chunk — ?cacheBudgetMB=1 gives a 0.67 MiB remainder and a ~170 KiB allowance, under the 256 KiB a chunk can reach — and because overflow drops the ARRIVAL rather than evicting pending work, nothing is ever persisted. L2 is then effectively off for the session. That is self-inflicted by the setting, and a floor would have to exceed the whole non-cache remainder to prevent it.

      Parameters

      • OptionalheapLimitBytes: number

        Override for the device heap limit (tests). When omitted, readHeapLimitBytes is consulted; invalid explicit values skip the heap path without probing browser state.

      • OptionalpoolOverrideBytes: number

        Explicit total cache pool in bytes (?cacheBudgetMB= / the native launcher). Takes precedence over the heap-derived budget.

      • OptionalfallbackPoolBytes: number

        Device-class total cache pool in bytes, used only when neither an explicit pool nor a measurable heap is available.

      Returns number