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

    Variable MESH_DECODE_BUDGET_BYTESConst

    MESH_DECODE_BUDGET_BYTES: number = ...

    Per-node ceiling, in bytes, on what a mesh node is allowed to declare before the loader will fetch any of it. Default 512 MiB.

    This value is the viewer's own policy: the write side cannot know what a tab can survive, and this gate's primary job is one the writer can never help with. The viewer loads arbitrary ?src= URLs, and the mesh loader is whole-node (docs/specs/MESH_NODE_SPEC.md §7) — it fetches and decodes every array in full — so a hostile or corrupt store could otherwise exhaust tab memory before the per-node LoaderError containment is ever reachable.

    MIRROR: MESH_DECODE_BUDGET_BYTES in packages/luxar/src/luxar/typing_utils/constants.py holds the same value. That is a PARTIAL write-time twin, added because the two purposes above are separable: policing hostile stores is this gate's alone, but refusing to AUTHOR a store that provably exceeds the ceiling is something add_mesh can do, and should — a bound enforced only here surfaces in a browser, far from the call that caused it. The twin is deliberately weaker: it charges only the decoded term, so it is a strict lower bound on the accounting below and can never refuse a node this gate would admit. See validate_mesh_decode_budget. If you change this value, change that one.

    Three quantities are checked against this, all from .zarray metadata alone: every array's stored footprint, what each one DECODES to, and each array's single largest per-chunk allocation.

    Charging the decoded term is not belt-and-braces — the stored side can be arbitrarily smaller than what gets allocated, so a stored-only budget is wrong in the dangerous direction. A broadcast array stores one row and expands to n_elements rows, so a ~12-byte declaration can materialize gigabytes; every decoder-routed array yields a Float32Array, so a uint8 store decodes at 4x its stored bytes; and faces widens to u32 whatever narrow dtype the INDEX encoder chose. The decoded term is also the only thing bounding ndim, which has no cap of its own.

    The per-chunk term is likewise not redundant: zarr v2 does not require chunks <= shape, so a "shape": [100, 3] array declaring "chunks": [268435456, 3] would slip a multi-gigabyte first-chunk allocation past a shape-only budget.

    Be precise about what this bounds: the SUM of stored bytes, decoded bytes, and the largest single chunk buffer — the three are added together, not checked independently, because a chunk buffer exists during decode alongside the arrays. (An oversized chunk is ALSO rejected on its own, so a single array cannot exceed the ceiling even where the sum would fit.) This is still not the loader's whole transient peak: the admission path also holds the extracted display-space position and the driver-side GPU upload, so the real peak is a modest multiple of this value. 512 MiB keeps that comfortably inside a 64-bit tab — which is why this must never be raised toward "what a tab survives": the tab has to survive the multiple, not the ceiling.

    The ceiling is per NODE. N nodes can still sum to N x budget; the guarantee it buys is "one node lost, not the scene", not an aggregate cap.