Log inGet started

World detail — terrain, voxels, streaming, LOD

Four systems decide how much of a world stands at any one moment:

  • terrain — a heightfield drawn through a quadtree LOD cut that refines under the camera
  • voxels — a VoxelWorld split into chunks, each meshed on the GPU into its own child entity
  • spatial streaming — a cell store that stands and releases groups of entities as sources move
  • mesh LOD — a level chosen per entity from how much of the viewport it spans

Reading a Terrain, VoxelWorld, StreamingSource or MeshLod component's fields back tells you what was asked for. The streaming namespace answers the other question: what is standing, what it costs, and for a piece that is not on screen, which of a closed set of reasons it is not.

What is resident right now

local reading = streaming.observe()
reading.totals        --> voxelChunks / voxelChunksMeshed / voxelChunksFailed /
                      --  voxelGpuBytes / terrainEntities / terrainBound /
                      --  terrainIndexCount / cellsTotal / cellsResident /
                      --  cellsPending / cellBytesResident / lodReceivers
reading.terrain       --> per Terrain entity: bound, nodeCount, indexCount,
                      --  vertexCapacity, meshGuid, and the eye the cut refined under
reading.voxel         --> per VoxelWorld: chunksTotal / meshed / queued / failed /
                      --  empty, gpuBytes, and one row per chunk
reading.cells         --> the store's config, its counters, and one row per cell
reading.levels        --> per LOD receiver: the level it draws at and the screen
                      --  fraction that selection was measured from

Each axis reads on its own too: streaming.terrain(), streaming.voxel(), streaming.cells(), streaming.levels().

The same document is served at /zero/runtime/observations/streaming. It is built by the call and published as its last act, so the namespace and the VFS node carry one reading rather than two derivations of it:

local served = require("@builtin::modules.json").decode(
    vfs.read("/zero/runtime/observations/streaming"))
served.reading.totals.voxelChunksFailed

Nothing is computed per frame. An engine nobody is observing pays nothing, and streaming.observe() is the only thing that builds the reading.

Which pool a number belongs to

voxelGpuBytes is what the voxel chunk builds reserved on the device — the vertex pool at the renderer's own stride plus one u32 per reserved index. It is that pool alone. The terrain pool is reported as vertexCapacity per terrain entity, the streaming store's bytes as cellBytesResident, and every resource the device holds under any system is renderer.gpuMemory() and asset.observe().

Why a piece is not on screen

streaming.whyNotDrawn(entityOrName)                     -- whichever system holds it
streaming.whyNotDrawn({ entity = "Vox", chunk = "0_0_0" })  -- one voxel chunk
streaming.whyNotDrawn({ entity = rock, level = 0 })         -- one mesh-LOD level
streaming.whyNotDrawn({ cell = "3_-2" })                    -- one streaming cell

The answer is { kind, reason, detail }. reason is one of the set streaming.reasons() enumerates, and detail names what it is about:

reasonwhat it means
drawnthe engine holds this subject's geometry and is drawing it
notManagedno terrain, voxel, streaming or mesh-LOD system holds it
notBuiltnothing has built this subject's geometry
buildQueueda build has been asked for and has not run yet
buildFaileda build ran and the engine did not carry it through — detail is the engine's own message
builtEmptya build ran over a region holding nothing to draw
emittedNothingthe build ran and the shader wrote no geometry into the capacity it reserved
outsideRadiusno streaming source is close enough to want it resident
evictedit was resident, the store released it, and its content now reads from the file the release wrote
coarserLevela coarser LOD level is the one selected, so this level is not the one drawn
finerLevela finer LOD level is the one selected, so this level is not the one drawn
noSuchLevelthe LOD chain holds no level with this number
hiddenthe entity carrying the geometry is switched off

A voxel world that stands and draws nothing

A chunk's state is the engine's verdict on the build, not the component's record of having asked for one. VoxelWorld:dirtyChunks() empties as soon as each chunk has been attempted; VoxelWorld:chunks() says what became of the attempt:

for _, chunk in ipairs(world:chunks()) do
    print(chunk.key, chunk.state, chunk.detail)
end
-- 0_0_0  buildFailed  the compute shader never compiled within 10000ms, so the dispatch was dropped

Each row carries state, detail, dispatches and failures from the engine's dispatch record, the block count the build read (solidVoxels), what it reserved (reservedVertices, reservedIndices, gpuBytes), and the entity and mesh the chunk draws through.

The join is the buffer prefix: every buffer of a build is created under one, and the engine's dispatch record names it — so chunk.buildPrefix is what holds a chunk against compute.program.status(shaderGuid) directly.

Reserved is not emitted, and the row carries both

reservedVertices / reservedIndices are the capacity the build asked the device for, sized off an upper bound before the shader ran. emittedVertices / emittedIndices are what the shader actually wrote, read back off the counters it increments as it emits.

They are different numbers and the difference is a whole class of failure. A dispatch the device carried through that wrote nothing leaves a chunk holding a MeshHandle, a plausible gpuBytes, failures = 0 — and no geometry. That chunk reads emittedNothing, and meshed is only ever reported for a chunk whose counters say geometry is there.

The read is issued once the engine has recorded the dispatch reaching the device — a read issued before that returns the zeros the build wrote into the counters, which is indistinguishable from a shader that emitted nothing. Until it lands, the chunk reads queued with a detail saying the answer is not in yet; emittedVertices and emittedIndices are absent from the row.

Gpu.emitted(chunk.buildPrefix) waits for that read rather than reporting the absence, for a caller that wants the number and not the state.

Publishing a reading of your own

/zero/runtime/observations is generic. Any Luau system can put a reading on the engine's shelf and gain a VFS leaf without a change to the engine:

__observations.publish("mySystem", { resident = 12, pending = 3 })
-- served at /zero/runtime/observations/mySystem as
-- { name = "mySystem", publishedAtMs = ..., reading = { ... } }
__observations.names()   --> { "mySystem", "streaming" }

The document is serialised at the moment of the call, so the bytes the VFS hands out are the bytes of the table the publishing call built.