Readonlypoints— points-specific pool state and methods.
Readonlylines— lines-specific pool state and methods.
Readonlygsplats— gsplats-specific pool state and methods.
ReadonlyactivenodeId → active geometry, keeping a running byte total (see
gpu-buffer-pool/byte-tracked-maps.ts). — shared with the
per-type adapters.
PrivatemeshCommitted, non-pooled mesh geometries. The scene graph owns their lifetime.
PrivatetrackedAtomic-commit counter — the pool's unit of age (#2939). — shared with the per-type adapters; increments via beginCommit.
PrivatemaxPrivateevictionLRU age limit, in atomic commits (see beginCommit).
PrivateevictMaximum 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 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 commit counter. Call once per ATOMIC COMMIT (a non-discarded
update pass), before its acquire calls — the only production caller is
data/scene-loader/update-view/atomic-commit.ts.
The pool therefore ages pooled buffers in commits, not rendered frames
(#2939): evictionCommits (default 300) is 300 commits, and the
over-limit mustEvict grace below is OVER_LIMIT_GRACE_COMMITS (60).
That is deliberate —
buffers are only acquired and released on commits — but it has two
consequences worth knowing when tuning it:
maxPoolSize;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: InstancedOrderingOptions
The commit ordering the commit path chose
(writeInstancedCommitOrdering, shared by all three types):
preserveOrdering / repairFromCount keep or rebuild the existing
permutation; fromInstance (append fast path) writes & uploads only the
[fromInstance, count) suffix and HOLDS the draw at the previous
population until the grown population's ordering lands; seedOrdering
holds a GROWN geometry on the previous geometry's drawn permutation.
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: InstancedOrderingOptions
The commit ordering, as for updatePointsGeometry (counted in segments).
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: InstancedOrderingOptions
The commit ordering, as for updatePointsGeometry.
Get size bucket for capacity-based pooling. Buckets: 1K, 5K, 10K, 50K, 100K, 500K, 1M. — called by adapters; public to satisfy PoolAdapterHost.
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. O(1): the map keeps the total incrementally (B9b) — it used to walk every active buffer.
Track a committed mesh without making it eligible for buffer reuse.
PrivatesumSum of bytes held by pooled (released, retained-for-reuse) buffers. O(1).
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 follows activeBuffers + free-bucket
membership (see getStats / getResidentBytes; the byte
totals are kept incrementally BY those containers), so removing the
entry is the entire correction — there is no separate 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.