Builtin Library
README.md
asset_observe
What the engine is holding for content, and why one asset cannot be used.
Backs asset.observe, asset.diagnose, asset.cpuResident,
asset.gpuResident and asset.unusableReasons, composing the readings only
the engine can take (__assetObserve) with the resolution, import and VFS
surfaces already in Luau.
The reading
M.observe() returns the residency document the engine published, with each
device row joined to the asset its guid names:
local r = asset.observe()
for _, t in r.textures do print(t.identity or t.key, t.bytes) end
print(r.totals.textureBytes, r.totals.meshBytes, r.totals.cpuCount)
Four pools, named because they are different pools: textures and meshes
are the device's, cpu is the set a live script-component context holds, and
offDevice is every mesh nothing displays: its GPU buffers were given back,
and its row says what the engine keeps to bring it back (held is
"definition" for the mesh's whole CPU definition, "source" for only the
stored file source it reads the mesh from again) and the CPU bytes that
costs. An asset can be in one and not the others. totals carries the aggregates the
rows sum to, so a listing reconciles against renderer.textureMemory() and the
meshes category of renderer.gpuMemory(). devicePublished and
cpuPublished say whether the engine can answer at all, which reads
differently from an engine answering with nothing resident.
A row carrying an identity reached the device through that asset; a row
without one is held under a guid no asset claims — a camera's own render
target, a glyph atlas a script built.
The diagnosis
M.diagnose(ref) answers for one asset, from the engine's own reading rather
than from what the caller asked for:
local d = asset.diagnose("myTexture")
if not d.usable then print(d.reason, d.detail) end
print(d.primary) -- the file the type's declared `primary` resolved to
reason is one of M.reasons(), and each is a state the engine distinguishes:
| Reason | What the engine read |
|---|---|
noSuchAsset | nothing resolves under that name |
noPrimaryFile | the type's declared primary list matched no file in the asset's folder |
payloadEmpty | the primary resolved to a file holding no bytes |
decodeFailed | the bytes are not the payload the file claims to be — the format's identifying bytes disagree, or the engine's decoder read them and refused |
importFailed | an import ran over this asset's source and failed; the importer's own error text travels alongside |
importInFlight | an import over it is queued or running, so its payload is not settled yet |
Each is the reading at the moment of the call, so a payload being removed is
reported at whichever stage the call catches it: payloadEmpty while the file
is there with nothing in it, noPrimaryFile once it is gone. The verdict is
whether a load would succeed now — a resource already on the device stays
resident under a payload that has since gone, which M.gpuResident reports.
primary is worth reading even when an asset loads: a type declares its payload
as a list tried in order, so an asset that lost its encoded payload keeps
loading from whatever image is left beside it, including a thumbnail. Naming
the file is what makes that visible.
Reading the payload costs a decode wherever the engine has a decoder for the container — a ZTEX header parse, or the image decode the texture loader itself performs, taken at a single texel.