Lifetimes#

When to render, when to free data, and what to dispose. Radix does not free anything the app made and does not read an object's data. It tells the app when a render has something new to show (onInvalidate) and when an object is no longer drawn by anything (release).

Code: src/render/SplatRenderer.ts and SplatView.ts.

const splatRenderer = new SplatRenderer({
  renderer,
  collection,
  onInvalidate: () => requestRender(),
});

splatRenderer.collection = new SplatCollection([
  // Freed once nothing draws it
  SplatCollection.arrays(splats, { matrixWorld, release: () => splats.dispose() }),
]);

On-demand rendering#

onInvalidate(view?) is called when work that Radix did, or was asked to do, needs a render to be shown:

It is never called for the app's own changes (a new collection, new params, a moved object), which the app already knows about.

Object releases#

An object's release runs once, when that object is in no renderer's live collection. A renderer's live collections are:

const indices = SplatGenerator.indices(renderer, chosen, pool.pop());
const object = SplatCollection.object({
  generator: listed,
  data: { splats, indices },
  count: chosen.length,
  matrixWorld,
  // Once nothing draws or sorts this object; the splats stay
  release: () => pool.push(indices),
});
splatRenderer.collection = new SplatCollection([object]);

On WebGPU an old collection is retired at the next assignment; on the CPU sort it stays live until a newer order is applied, a few frames later. Code that frees data on assignment instead of in release crashes or draws garbage on the WebGL2 path.

Disposal#

CallWhat it frees
splatRenderer.dispose()the renderer's projected arrays, sorter, material, and its views; objects only it held are released. The shared sort pool (SplatRenderer.sortPool) stays for other renderers. It also hides the renderer (visible = false), and prepare or a render of it does nothing, so one left in the scene costs nothing and makes no GPU resources again. Remove it from the scene too
view.dispose()the view's order and key arrays; takes it off its renderer, releasing objects only it held
SplatProjector.dispose(generator)a generator's compiled programs, once no live collection uses it. Garbage collection does not free GPU pipelines. Apps that make generators once never need this
arrays.dispose()a GpuArrays's textures: an object's release is the place for it
pool.dispose()a WorkerPool's workers; later calls reject instead of making new ones

A renderer that is garbage-collected without dispose() logs a console warning, and its objects are released at that point. Dispose renderers explicitly: the warning means GPU memory was held longer than needed.

Gotchas#

Source: docs/lifetimes.md