@pmndrs/bvh
    Preparing search index...

    Interface SceneTreesOptions

    interface SceneTreesOptions {
        deformingLayouts?: "full" | "compact";
        diagnostics?: boolean;
        firstTreeBuilder?: FirstTreeBuilder;
        gpuFirstTrees?: boolean;
        gpuMaxBatchBytes?: number;
        gpuMaxDepth?: number;
        gpuQuantizeOnEmit?: boolean;
        gpuTreeOutputs?: GpuTreeOutputsMode;
        holdRefinements?: boolean;
        label?: string;
        mergeStaticInstances?: boolean;
        nodeFormat?: GpuLayoutNodeFormat;
        onBuild?: (event: SceneBuildEvent) => void;
        onTree?: (event: SceneTreeEvent) => void;
        parents?: "refit" | "always";
        positionLayout?: PositionLayout;
        positionsBuffer?: GPUBuffer;
        quantizeJobs?: "per-tree" | "batched";
        refine?: boolean;
        refitBoxes?: "shared" | "per-mesh";
        timestamps?: boolean;
        topLevel?: "auto" | "cpu";
        transferGeometry?: boolean;
        triangleLeaves?: GpuLayoutTriangleLeaves;
        trimWhenRefined?: boolean;
        warn?: (message: string) => void;
        weld?: boolean;
        workers?: BvhWorkerPoolOptions | BvhWorkerPool;
    }
    Index
    deformingLayouts?: "full" | "compact"

    What a deforming mesh keeps on the CPU for SceneTrees.setPoseCpu once its tree is placed: 'full' keeps the tree's layout as it came (nodes, 48-byte records, parents); 'compact' keeps the nodes and parents and 4 bytes per triangle (its original triangle, and the flags and padding words only where any is non-zero), and rebuilds the records a pose needs (compactPoseLayout). Same poses bit for bit. Default 'compact' since 2026-11-15.

    diagnostics?: boolean

    Read the GPU top level's refit stats back (counters, warnings). Default false: no readbacks frame after frame.

    firstTreeBuilder?: FirstTreeBuilder

    Builds the first trees instead of the GPU H-PLOC, whatever the device's features: for unit tests in Node.

    gpuFirstTrees?: boolean

    First trees from the GPU (H-PLOC) on a device with subgroups. Default true; false: tinybvh only.

    gpuMaxBatchBytes?: number

    Cap each GPU build batch's bytes (inputs, scratch and outputs) and build the batches one after another (see the file comment). Default 256 MiB (DEFAULT_GPU_MAX_BATCH_BYTES). Infinity turns the cap off: one batch for all of an update's meshes, as big as the device allows (the path before the cap). The batches go on between updates (each is copied out in its own submit), but their trees reach rays at the next SceneTrees.update, as any tree does.

    gpuMaxDepth?: number

    Trees deeper than this are refused, with a fallbackReason: a GPU tree leaves the mesh to tinybvh, a tinybvh tree leaves it on the tree it has (or none). Default GPU_TREE_MAX_DEPTH, which keeps every placed tree within the trace stack.

    gpuQuantizeOnEmit?: boolean

    Quantized scenes: H-PLOC writes the first trees' nodes quantized in its emit pass (HplocOptions.quantizeOnEmit), so a batch holds 32-byte nodes and a frame slot per mesh instead of 64-byte nodes, and no quantize pass runs as the trees are copied in. The trees are the same, bit for bit. Deforming meshes' builds stay f32 (their refit needs an f32 copy). Default true (since 2026-11-15: Sponza's build 41.6 to 35.1 ms on the RTX 3060, an uncapped peak 1,482.6 to 1,307.3 MiB); false runs the quantize pass instead. gpuTreeOutputs: 'adopt' needs it. A copying scene still takes f32 trees from a first-tree builder that doesn't write quantized nodes, and packs them on the way in. Ignored by f32 scenes.

    gpuTreeOutputs?: GpuTreeOutputsMode

    How GPU trees reach the scene's buffers: 'copy' (default) or 'adopt' (see the file comment). Any node and triangle format: a quantized scene adopts H-PLOC batches that wrote quantized nodes themselves (gpuQuantizeOnEmit, the default), and an indexed scene's builds leave room for each mesh's header.

    holdRefinements?: boolean

    tinybvh's trees wait instead of replacing a mesh's GPU tree, until SceneTrees.releaseRefinements. A mesh with no tree yet still takes tinybvh's. For tools that check a scene mid-swap. Default false.

    label?: string

    Prefix for GPU object labels.

    mergeStaticInstances?: boolean

    Merge static instances at the first update (see the file comment). Default true. Only instances added with static: true before the first update are merged; every other instance is untouched. false keeps static instances as ordinary ones.

    nodeFormat?: GpuLayoutNodeFormat

    The node format (docs/internals/design/compressed-nodes.md): 'quantized' (the default, DEFAULT_NODE_FORMAT: 32 bytes per node, plus a 32-byte frame slot per mesh before its root) or 'f32' (64 bytes per node). The walk that reads the scene must be built with the same nodeFormat (SceneTrees.nodeFormat; the walk's default is quantized too). tinybvh trees come quantized from the workers (deforming meshes' come f32: their refit keeps an f32 copy, and they are packed when placed); GPU trees are built f32 and packed by a compute pass on their way into the storage (or written quantized by H-PLOC, gpuQuantizeOnEmit). Independent of triangleLeaves. A coordinate of magnitude 2^100 or more, or a non-finite one, makes a quantized box empty on that axis (hits there are lost).

    onBuild?: (event: SceneBuildEvent) => void

    Called as each mesh's builds go: a build requested, started (a GPU batch submitted, a worker taking the tinybvh build) and finished, per mesh and builder (see SceneBuildEvent). onTree says when a finished tree is placed.

    onTree?: (event: SceneTreeEvent) => void

    Called when a tree is placed (a mesh's first, or a swap).

    parents?: "refit" | "always"

    Which trees carry parents (4 bytes per node; only a refit reads them). 'always': every GPU batch writes its parents output and every tinybvh tree comes with its parents array. 'refit' (P9 lever 7): GPU batches are built without the parents output (deforming meshes' read-back layouts get theirs from their nodes, on the CPU), and tinybvh builds of meshes that don't deform leave their parents out. Same trees. Default 'refit' since 2026-11-15.

    positionLayout?: PositionLayout

    Indexed: 'vec4' (the default, 16 bytes per vertex) or 'packed' (12) positions.

    positionsBuffer?: GPUBuffer

    Indexed scenes: the app's own positions buffer, bound as the walk's positions (buffers.positions) instead of the scene's copy, which is then never made (docs/internals/design/indexed-leaves.md, option A). It must hold every mesh's vertices as f32, in the positionLayout stride ('vec4': 16 bytes per vertex, 'packed': 12), each mesh's from the vertexBase given to addMesh, with exactly the bits of the geometry given there (the trees are built from that geometry; the walk reads this buffer), and STORAGE usage. Its vertices are the meshes' own, so the scene neither welds them (weld is false, and true throws), nor merges static instances (mergeStaticInstances is false, and true throws: a merged group's world-space vertices are not in it), nor takes deforming meshes (their refit writes its pose into the scene's copy). The app keeps the buffer alive and unchanged while the scene uses it. Default: none (the scene's copy).

    quantizeJobs?: "per-tree" | "batched"

    Quantized scenes: how GPU trees are packed on their way into the storage. 'per-tree' records one quantize pass per tree, each with its own mesh table and uniform; 'batched' gathers every tree an update (or a capped batch's own copy) places and records one pass, with one job per batch's node buffer. Same nodes bit for bit; fewer buffers, bind groups and passes (P9, docs/internals/design/memory.md). Default 'batched' since 2026-11-15 (Sponza: 7 passes, not 132).

    refine?: boolean

    Default true: every mesh is also built by tinybvh, and its tree replaces the GPU's (the swap). False is the GPU-only mode: with a GPU builder, tinybvh builds only the meshes the GPU can't give a usable tree (too big, too deep, a batch that failed), and the GPU trees stay. Each mesh's geometry is kept until its GPU tree is placed (for that fallback). Without a GPU builder (no subgroups, gpuFirstTrees: false) tinybvh builds every mesh either way: the declared fallback.

    refitBoxes?: "shared" | "per-mesh"

    Deforming meshes' refits: 'per-mesh' gives each its own primitive-box buffer (32 bytes per triangle); 'shared' gives them one, sized for the largest deforming mesh, used in turn (every refit is recorded one after another in the update's pass). Same boxes, same trees. Default 'shared' since 2026-11-15.

    timestamps?: boolean

    Time each GPU build batch's compute pass with timestamp queries, where the device has timestamp-query: counts.gpuBuildTimestampMs sums them (counts.gpuBuildMs is submit-to-stats wall time either way). Default false: one small query set and readback per batch when on.

    topLevel?: "auto" | "cpu"

    'auto' (default): the GPU top level with subgroups, the CPU one without. 'cpu': always the CPU top level.

    transferGeometry?: boolean

    Move each mesh's geometry arrays into the worker instead of copying them (the caller's arrays are empty afterwards). Default false. Merged groups' geometry is the scene's own and always moves.

    triangleLeaves?: GpuLayoutTriangleLeaves

    The triangle format (docs/internals/design/indexed-leaves.md): 'records' (the default, 48 bytes per triangle) or 'indexed' (16 bytes per triangle, plus the scene's own positions, buffers.positions, or the app's, positionsBuffer). The walk that reads the scene must be built with the same triangleLeaves and positionLayout.

    trimWhenRefined?: boolean

    Trim the node buffer whenever the scene is quiet (no build running or waiting, every tree placed) and the buffer holds more than its trees: the trees are packed to the front of a buffer of their exact size (see SceneTrees.trim). Default: true with gpuTreeOutputs: 'adopt' (the batch's one node per triangle goes: Sponza settles at 448.1 MiB, not 613.6), false with 'copy' (the buffer keeps its growth room; a trim would save about 10 MiB for a copy of every node).

    warn?: (message: string) => void

    Where problems are reported once (a GPU build that failed, a refit repaired). Default console.warn.

    weld?: boolean

    Indexed: weld each mesh's vertices by exact bits when it is added (not deforming meshes'). It changes no coordinate, so rays see the same triangles, and saves position memory. Default true.

    The worker pool for tinybvh builds, or options for one this scene makes (and disposes). A pool the scene makes ends workers idle for SCENE_WORKER_IDLE_TIMEOUT_MS (30 s) unless the options set idleTimeoutMs: 256 MiB of tinybvh memory back once a load is over, for a few milliseconds of worker start on the next build.