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…
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)
Three pools, named because they are different pools — textures and meshes
are the device's, cpu is the set a live script-component context holds. 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.
Scoped to this part · feeds back into the world's score.