Readonlypoints— points-specific pool state and methods.
Readonlylines— lines-specific pool state and methods.
Readonlygsplats— gsplats-specific pool state and methods.
— shared with the per-type adapters.
— shared with the per-type adapters; increments via beginFrame().
PrivatemaxPrivateevictionPrivateevictMaximum geometries the pool will dispose in a single
evictUnused() call when not over the hard limit. Without this
cap, a single eviction sweep can dispose dozens of buffers
synchronously — each geometry.dispose() is 5–20 ms on slow
GPUs, so a burst stutters visibly. Remaining evictable buffers
are deferred to the next frame's eviction sweep. The
mustEvict (pool-over-limit) path ignores this cap so the pool
never grows unbounded.
PrivategetLive byte-budget getter (the single VRAM authority shared with LOD
retention). The byte-eviction pass targets TOTAL resident bytes
(active + pooled) against this value: pooled buffers are disposed
(largest-first) only while active + pooled exceeds the budget, and
retained for reuse otherwise. Read live (not snapshotted) so the
WebGL-context-loss budget backoff applies immediately. Returns 0
to disable byte-budget eviction (count-only behavior).
— shared mutable stats; adapters bump fields here.
Pooled buffers skipped this evictUnused call due to batch cap.
Evictions attributable to the BYTE-budget pass alone (a subset of
evictions) — see PoolStats.byteBudgetEvictions for why the two
must not be conflated.
— written by adapters from acquire paths.
PrivatelargeOne-shot guard: have we already logged the >100MB pooled-buffer
warning? Re-checked per evictUnused so the noise stays bounded.
Advance the frame counter. Call once per frame before any acquire calls. This ensures eviction timing is based on rendered frames, not acquire calls.
Whether the most recent acquire*Geometry call returned a
geometry whose GPU-visible buffers (lines' interleaved buffer, the
points/gsplat element texture + aSortedIndex) differ from what was
previously in use for that node — the returned geometry is a fresh
allocation or a different pool candidate (growth is release +
reacquire for all three types).
Callers that hold a THREE.Mesh pointing at the previously-active
geometry should call invalidateRenderObjectFor(mesh) (in
data/scene-loader/commit/invalidate-render-object.ts) when this
returns true. That forces Three's WebGPURenderer to discard the
cached RenderObject.vertexBuffers set (a no-op on the classic
WebGL backend, which has no such cache); without it, WebGPU binds
the old GPU buffer next draw and validation fails with "Instance
range … requires a larger buffer than the bound buffer size".
The flag is overwritten on every acquire call, so consume it
immediately after acquirePointsGeometry / acquireLinesGeometry
/ acquireGSplatsGeometry returns.
Acquire Points geometry from pool (capacity-aware; the fixed texel
layout means any pooled points geometry fits any points node).
See PointsBufferAdapter.acquireGeometry for implementation.
Release Points geometry back to pool.
Update Points geometry attributes in-place (zero GPU allocations).
Optionaloptions: { preserveOrdering?: boolean; repairFromCount?: number; fromInstance?: number }
preserveOrdering: keep the geometry's existing
aSortedIndex permutation instead of resetting it to identity
(same-node same-count recommit — the commit path decides; see
commit-points-geometry.ts). fromInstance: append fast path
(Phase 4 Stage 2) — write & upload only the [fromInstance, count)
suffix, preserving the prefix texels + permutation already on the
GPU.
Acquire geometry for Lines (texture-backed per-segment storage — the fixed 6-texel layout means capacity is the only matching criterion; see lines-adapter.ts).
Release Lines geometry back to pool.
Update Lines geometry in place.
Optionaloptions: { preserveOrdering?: boolean; repairFromCount?: number; fromInstance?: number }
preserveOrdering: keep the existing aSortedIndex
permutation instead of resetting to identity (same-count recommit
of an already-sorted node — the commit path decides; see
commit-lines-geometry.ts). fromInstance: append fast path
(Phase 4 Stage 2) — write & upload only the [fromInstance, count)
segment suffix, preserving the prefix already on the GPU.
Acquire geometry for GSplats (splat texture + aSortedIndex).
Release GSplats geometry back to pool.
Update GSplats geometry in place.
Truncation radius in sigmas (defaults to
GSPLAT_DEFAULT_TRUNCATION_RADIUS).
Must match the material's truncationRadius for correct frustum culling.
Optionaloptions: { preserveOrdering?: boolean; repairFromCount?: number; fromInstance?: number }
preserveOrdering: keep the geometry's existing
aSortedIndex permutation instead of resetting it to identity
(same-node same-count recommit — the commit path decides; see
commit-gsplats-geometry.ts). fromInstance: append fast path (Phase 4
Stage 2) — write & upload only the [fromInstance, count) suffix,
preserving the prefix texels while resetting the enlarged ordering to
full identity until the commit-triggered sort lands.
Get size bucket for capacity-based pooling. Buckets: 1K, 5K, 10K, 50K, 100K, 500K, 1M. — called by adapters; public to satisfy PointsAdapterHost.
Evict unused geometries using LRU policy. Returns number of geometries evicted.
Public for testing and manual pool management.
Private_PrivatesumSum of bytes held by active (in-use) buffers. Uses cached per-geometry estimates.
PrivatesumSum of bytes held by pooled (released, retained-for-reuse) buffers.
Total resident VRAM bytes (active + pooled), using real per-geometry capacities. The single source of truth for budget enforcement: the LOD registry queries it to decide when to demote cold levels, and the pool's own byte-eviction pass keeps it under the live budget. Cheaper than getStats (no per-type breakdown), so it is safe to call once per frame.
Get pool statistics with per-type breakdown.
Register a one-time self-invalidation dispose listener on a freshly
created pooled geometry. Called by the per-type adapters at fresh
allocation — the single choke point every pooled geometry passes
through exactly once (adopted/reused geometries were registered at
their original creation).
The scene-graph disposal helpers (scene-disposal.ts) are pure over a
THREE.Scene with NO pool reference, so on a dataset switch / teardown
they call geometry.dispose() directly on meshes that still hold
pool-owned geometries. Without this guard the pool keeps the disposed
geometry recorded in activeBuffers, and a later acquire*Geometry
for the same nodeId hands it back as reusable — a use-after-dispose
that also over-counts resident bytes.
When dispose fires this:
userData.luxarInvalidated (the acquire fast
paths treat a flagged active entry as a miss — defense-in-depth
against an ordering where the entry is somehow still present);activeBuffers entry that still references THIS geometry.
Matching by identity is inherently safe — a geometry appears in
activeBuffers at most once, so a newer entry that replaced it (a
grow moves the old geometry to the free list first) is never
clobbered (the guard the issue asks for);maxPoolSize slot (which can flip the
LRU sweep into aggressive mustEvict mode), and queued for a
second dispose() by the evictors.Resident-byte / stats accounting is DERIVED from activeBuffers +
free-bucket membership (see getStats / getResidentBytes),
so removing the entry is the entire correction — there is no persistent
counter to decrement. On the normal release/evict/dispose paths the
entry (or its free-bucket slot) is already gone before dispose()
runs, so this listener finds nothing and is a safe no-op there — it
never double-counts eviction stats and never mutates a bucket array
those paths are still iterating (they all commit removals BEFORE
disposing). It never calls dispose() again (no re-entrancy) and
removes itself on first fire (idempotent).
— called by the per-type adapters via their host interface.
PrivateremoveRemove the free-bucket entry referencing geometry, if any. A
geometry appears in at most one bucket of one type pool, so the
scan stops at the first identity match. No eviction counter moves:
this is out-of-band disposal bookkeeping, not an eviction.
Dispose all pooled geometries (for cleanup or context loss).
GPU buffer pool for reusing THREE.BufferGeometry objects.
Manages separate pools for Points, Lines, and GSplats geometries, each with different attribute layouts and update patterns.