protocol GPUResourceHost
Swift
protocol GPUResourceHost : AnyObject, Sendable
Who names the things that live on a GPU.
A GlyphInstance carries a font id and an image command carries a texture
id, and both are only meaningful to the process that owns the atlas they
index. In a normal app that process is this one, so the ids come from the
Editor and nobody has to think about it. Under a shared renderer they
come from the compositor, and a locally-invented number would draw the
wrong face or the wrong picture — silently, because a stale id is still a
valid index.
So this is the seam between the two modes, and it is deliberately the whole seam: a client differs from a windowed app in who answers these five questions and in nothing else. Shaping, measurement, layout and emit are unchanged, because none of them ever needed a device — only naming does.
Note what is not here: pixels. A remote host is asked to register a file it can open itself, never handed a decoded bitmap, which is the same rule the control plane follows for draw lists. Whoever owns the GPU does the decode, because they are about to own the result.
Sources/LavaUI/ResourceHost.swift:24
Methods
func registerFont(path: String, pixelSize26_6: UInt32, faceIndex: UInt32, rasterFlags: UInt32) -> UInt32?
Registers a face and returns the id to stamp into GlyphInstance.
Idempotent per face, which is a stronger claim than per path: the host opens the file and keys on what it contained, so two names for one file are one id, and a file whose bytes changed is correctly a new one. Nil if the file will not load.
The size is 26.6 fixed point — pixels times 64 — because a Float is
not something two processes can compare for equality and agree on, and
FreeType wants it quantised anyway. rasterFlags is a
FontRasterFlags.raw; unknown bits are refused, not ignored.
Sources/LavaUI/ResourceHost.swift:36
func registerFont(path: String, pixelSize: Float) -> UInt32?
Face 0 of path at pixelSize, hinted the renderer's default way —
what a caller that just wants a font file at a size should use.
Rounds the size to 26.6 here rather than making every caller do it, which is the point: there is exactly one conversion from "pixels a human typed" to "the number the renderer keys on", and it lives here.
Sources/LavaUI/ResourceHost.swift:135
func registerImage(path: String, maxPixelSize: UInt32, turn: ImageTurn) -> UIImage?
Registers an image, decoded from path, capped to maxPixelSize
(0 = native) and quarter-turned by turn, returning a handle with the
id and the decoded size.
Blocks. For anything an app needs before its first frame — an icon, a brand mark — where a placeholder would be worse than a stall.
The turn is asked of the host rather than done by the caller because
one of the two hosts is a compositor the caller cannot decode for — see
ImageTurn. Most callers want none and should use the two-argument
convenience below.
Sources/LavaUI/ResourceHost.swift:52
func registerImage(data: [UInt8], maxPixelSize: UInt32) -> UIImage?
Registers an image from encoded bytes (PNG, JPEG, …) rather than a path, for one that was downloaded, generated, or unpacked and never touched the filesystem.
Identity is the content, so registering the same bytes twice is one
texture without the caller keeping a key. Prefer registerImage(path:)
whenever there is a path: under a compositor this one sends the file
through shared memory, where a path sends a path.
Blocks, like its sibling.
Sources/LavaUI/ResourceHost.swift:77
func registerImage(path: String, maxPixelSize: UInt32) -> UIImage?
The overwhelmingly common case: no turn.
A convenience rather than a defaulted argument because a protocol requirement cannot carry one, and every caller but a viewer's rotate button wants a picture the way it is stored.
Sources/LavaUI/ResourceHost.swift:113
func registerImageAsync(path: String, maxPixelSize: UInt32, turn: ImageTurn, completion: @escaping @Sendable (UIImage?) -> Void)
The same registration off the calling thread, with completion run on
the main queue.
A protocol requirement rather than a helper because the two hosts split the work differently and only they know where. Locally, the decode belongs on a worker and the upload must be back on the main thread — it touches the device. Remotely the whole thing is one call, and there is no main-thread half at all.
Sources/LavaUI/ResourceHost.swift:62
func registerImageAsync(path: String, maxPixelSize: UInt32, completion: @escaping @Sendable (UIImage?) -> Void)
Registers an image off the calling thread with no turn; completion runs
on the main queue. See registerImageAsync(path:maxPixelSize:turn:completion:).
Sources/LavaUI/ResourceHost.swift:119
func registerImageAsync(path: String, maxPixelSize: UInt32, turn: ImageTurn, completion: @escaping @Sendable (UIImage?) -> Void)
Off-thread registration for a host that has no main-thread half — a
remote one, where the call is already a round trip and touches nothing
local. Editor overrides this, because it does.
Sources/LavaUI/ResourceHost.swift:147
func releaseImage(key: String)
Drops one reference to a registered image. key is the cacheKey
from the handle, not the bare path — the same file at two decode sizes
is two textures.
Sources/LavaUI/ResourceHost.swift:104
func uploadPixels(_ pixels: UnsafeRawBufferPointer, width: UInt32, height: UInt32, stride: UInt32, format: PixelFormat) -> UIImage?
Uploads pixels the caller rasterised itself — a PDF page, a chart — as a new texture.
pixels holds height rows stride bytes apart, the last of which
needs only width * 4. The bytes are read before this returns, so the
caller may reuse its buffer at once. Nil if the buffer is shorter than
that, or the host could not take it.
Every call is a new texture, under a key from
ImageStore.pixelKey(), and releaseImage(key:) frees it rather than
keeping it dormant: nothing could ever ask for the same pixels again.
Not cached by ImageStore; the caller owns the handle and releases it.
Blocks. Under a compositor this is a copy into shared memory and one
round trip, from any thread. The built-in renderer uploads to its own
device, which is the main thread's alone, so on Editor call it from
there.
Sources/LavaUI/ResourceHost.swift:96