Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

README.md

compute_observe

What the compute subsystem is holding on the GPU, and why a resource key names nothing. Backs compute.observe, compute.resources, compute.diagnose and compute.absentReasons, composing the one reading only the engine can take (__computeObserve.inventory()) into the calls content reaches for.

The inventory

local r = compute.observe()
print(r.totals.count, r.totals.bytes)
for _, res in ipairs(r.resources) do
    print(res.key, res.kind, res.bytes, res.owner.shader)
end

One row per live resource across both registries compute owns — the buffer map and the texture manager's volumes, storage targets, history pairs and samplers. Each row carries the key it is filed under verbatim (the same string a dispatch names it by and a dispatch failure prints), its kind, its bytes, its extent and format where it has them, the usage bits it was created with, and the frame it was created on.

owner is read back out of the key: a resource a .computeShader ref made is filed under <shader-guid>::<name>#<serial>, so owner.shader names the shader, owner.name what the caller called it and owner.serial which of that caller's resources under that name it is. A resource created under a bare name carries the whole key as owner.name.

The engine files its own resources under the same grammar: a compiled shader's params uniform is <shader-guid>::__params, so it names that shader as its owner and compute.resources(ref) accounts for it beside the buffers and volumes the shader made.

totals.bytes is what the rows sum to, and it is the compute figure renderer.gpuMemory() reports: both are read off the same registries, so a listing reconciles against the memory call by construction.

published is false when no renderer has published a reading yet — the engine saying it cannot answer, which reads differently from an engine answering with nothing held.

The same document is served at /zero/runtime/compute, and the resources and totals halves are readable on their own there.

Narrowing to one shader

for _, r in ipairs(compute.resources(shaderRef)) do print(r.key, r.bytes) end

Takes a .computeShader ref, a guid, or an asset identity. A guid stands for itself, so a resource stays reachable by its owner once the asset that made it is gone — which is the state a leak is in. A value carrying no shader raises — a blank string among them — so a narrowing that cannot be done reads as an error rather than as the whole inventory or as an owner that holds nothing.

Why a key names nothing

local d = compute.diagnose("705c73a8-...::f_target#6")
if not d.exists then print(d.reason, d.current) end

reason comes from the closed set compute.absentReasons() enumerates, and each entry is a state the inventory distinguishes on its own:

reasonwhat the inventory read
residenta resource is filed under exactly this key; the row is on resource
notPublishedno renderer has published an inventory, so no key can be answered for
replacedthe owner holds a resource under the same name at a different serial — current names it
nameUnusedthe owner holds resources, none under this name
ownerAbsentno live resource carries this key's owner
unownedthe key carries no owner segment, and nothing is filed under it

A reason is the inventory at the moment the call read it. The inventory is republished on a frame where a registry gained or lost an entry, so a resource created and diagnosed inside one script sees the reading from before it was made; wait a frame and ask again.