A store is opened from a Files.ReadDir, so it can access exactly what
that handle can access: the bundle beside the executable needs no permission, and
any other directory needs one. Asset paths are relative to the store; a path
that is not plainly relative, or that meets a symbolic link, is refused as
PathInvalid rather than rewritten.
Textures are platform-owned values, also named Assets.Texture and
Draw.Texture. Releasing the final reference
to one unloads the native texture automatically, so there is no unload to
remember.
The two effects that read the disk -- open! and load_texture! --
wait: each is legal in init!, where it blocks startup, and in tasks, where
it parks the task; both are refused in update! and render!. Everything
else here builds a texture from bytes the app already holds --
texture_from_bytes!, generate_color_texture!,
generate_checked_texture!, update_texture! -- and is legal in init!,
update!, and tasks, and refused in render!, where a decode or an upload
would land in the middle of drawing a frame.
ResourceLimit on any of them means the host's fixed texture table is full.
Release textures the app no longer needs before loading more.
Open the directory a handle names as an asset store, checking its
manifest if the policy requires one.
bundle = io.files().beside_executable!()?
store = Assets.open!(bundle.subdir("assets")?, IgnoreManifest)?
Legal in init!, where it blocks startup, and in tasks, where it parks
the task; refused in update! and render!. Opening the directory and
reading the manifest are filesystem work, so the host does both off the
frame thread and returns when they are done.
A Sha256 expectation compares against the manifest's declaration
only. Nothing walks or hashes the loose files, so opening a store stays
constant-time in the number of assets.
How many bytes an RGBA pixel list occupies on the wire: four per pixel.
An upload borrows the list rather than copying it, so this is what an app
measures a update_texture! or update_texture_region! against when it
is budgeting per-frame pixel traffic.
Legal in init!, where it blocks startup, and in tasks, where it parks
the task; refused in update! and render!. The file is read off the
frame thread; the decode and the GPU upload happen when the bytes are
back. To load after startup, call this inside Task.spawn!, or read the
bytes on a task and call texture_from_bytes! from update!.
path must be relative; PathInvalid is an absolute path, one holding a
NUL, or a lexical .. escape, and is returned before any file I/O.
NotFound is no such file under the store, ReadFailed is a file that
is there and could not be read, and TextureLoadFailed is bytes raylib
would not decode as an image, including any file whose extension is not
one of the ImageFormat ones.
A host-owned GPU texture: an opaque, reference-counted native handle plus
the pixel width and height, kept on the value so layout and
source-rectangle math stays pure.
This is the platform's Texture value. Draw.Texture names the same
type, so loading and drawing need no conversion.
An opened, explicitly located disk asset store. The host retains the
directory handle, not the process working directory; every relative asset
lookup is made through that handle.
stub is what every host resource in the platform calls its
resource-free test value, so a model full of assets can be written
down in an expect.
The handle never resolves to an open directory, so every load made
through it fails the way a load through a released store does. It
exists for the app that keeps a store in its model, to let a pure
expect build that model. Do not use it to test asset resolution or
resource lifetime.
Whether opening a store checks the asset-set manifest named
roc-assets.manifest beside it. IgnoreManifest does not look;
RequireManifest fails the open unless the manifest is there and matches
the expectation.
How closely a manifest's declared content has to match. AnyContent
deliberately leaves it unconstrained, which is what a directory of loose
files under development wants. Sha256 holds the 64-character
hexadecimal digest the manifest must declare.
What a RequireManifest open expects the manifest to say: which asset
set it describes, which schema version it was written to, which content
version it is, and which content it declares.
PathInvalid is a store directory whose path beneath the handle meets
a symbolic link. PermissionDenied is a stub handle, which gives no
access. The next three are about the
directory itself: RootNotFound is nothing there, RootNotDirectory is
something there that is not a directory, and RootUnreadable is a
directory the process may not open.
The rest are about the roc-assets.manifest a RequireManifest policy
asked for. ManifestMissing is no manifest beside the assets,
ManifestUnreadable is one that could not be read, and
ManifestMalformed is one that is not a manifest. Of the four
comparisons, AssetSetMismatch is a manifest describing a different
asset set than the one expected, SchemaMismatch a manifest written to a
different schema version, ContentVersionMismatch a different content
version, and ContentHashMismatch a declared content hash that is not
the expected one. InvalidExpectedContentHash is the expectation itself
being unusable -- a Sha256 string that is not 64 hexadecimal
characters. ResourceLimit is the host's store table being full.
Image formats the bundled raylib decodes: PNG, JPEG, BMP, GIF (its first
frame), and QOI. load_texture! reads the same five, by the file's
extension: .png, .jpg or .jpeg, .bmp, .gif, and .qoi.
An authored image embedded with a compile-time file import, tagged with
its format. The format is stated rather than sniffed, so a mislabelled
file fails to decode instead of decoding as something else.