frameStream
Carries an image the GPU drew out to another program — the view a camera renders, read back off its render target and written into a byte stream frame after frame, so pixels that sit in GPU memory reach a process outside the engine while the world runs.
require("@builtin::modules.api.engine.frameStream") -- frameStream (also available as global 'frameStream')
The frameStream namespace: 12 functions.
globals/frameStream/attach
frameStream.attach(texture: string, stream: string, opts: AttachOpts?) -> (string?, string?)
Carry an image the GPU drew out to an open byte stream, frame
after frame. texture is the guid of the render target it was
drawn into — renderer.texture.create({ width = W, height = H })
makes one, and a Camera component draws into it as its
textureHandle; the session reads that target back when a frame
comes due, so what the camera drew last reaches the far end.
stream is a handle from stream.open. What reaches the stream
is one frame's pixels then the next frame's, with nothing between
them: a frame is width * height * bytesPerPixel bytes of tight
rows, written in a single call so a consumer reads a whole frame
or none of it. Each frame is read back off the render thread, so
the stream never holds the renderer up. fps caps how often a
frame is taken and defaults to one per rendered frame; format
accepts "rgb24" (3 bytes per pixel, the default) or "rgba8"
(4) — a call with a format outside those two raises, naming both;
flipY writes the last texture row first. Returns the session
handle, or nil and the reason an empty texture, a handle naming no
open stream, a stream another session already carries, or a
non-positive fps was refused with.
Parameters
texturestring— Guid of the render target the image was drawn into (a Camera's textureHandle).streamstring— Stream handle from stream.open.optsAttachOpts(optional) — Rate, pixel layout and row order (optional).
Returns (string?, string?) — Session handle, or nil and the refusal reason.
local session = frameStream.attach(rt.guid, handle, { fps = 30 })
globals/frameStream/detach
frameStream.detach(handle: string) -> boolean
End the session and free the staging buffers it read frames back through. The stream stays open — whoever opened it closes it.
Parameters
handlestring— Session handle from frameStream.attach.
Returns boolean — True if a session was ended, false if handle already named none.
frameStream.detach(session)
globals/frameStream/list
frameStream.list() -> { string }
Every live session handle, in a stable order.
Returns { string } — Array of session handles.
for _, h in frameStream.list() do frameStream.detach(h) end
globals/frameStream/status
frameStream.status(handle: string) -> FrameStreamStatus?
Report what the session has carried and lost. frames counts
the frames the stream accepted and bytes the bytes they
carried. dropped counts the frames it refused, of which
droppedBackpressure is the part refused because the consumer was
behind; stalledReadbacks counts the frames that came due while
every staging buffer still held a copy on its way from the GPU.
achievedFps is the rate the accepted frames arrived at, across
the span from the first to the most recent, and reads 0 until two
have been accepted — compare it against requestedFps to see a
display running slower than it was asked to. lastCopyFrame is
the engine frame, in time.frameCount() numbering, the copy of the
most recent accepted frame was taken on, and is nil until one is
accepted: every copy one engine frame takes of a texture holds the
same image, so two sessions on one texture whose lastCopyFrame
agree hold the same image as the newest frame of their streams.
lastOutcome names what became of the most recent frame offered.
nil when handle names no live session.
Parameters
handlestring— Session handle from frameStream.attach.
Returns FrameStreamStatus? — Session status, or nil when handle names no live session.
local s = frameStream.status(session); print(s.frames, s.dropped, s.achievedFps)
modules/frameStream/attach
attach(texture: string, stream: string, opts: AttachOpts?): (string?, string?)
Carry an image the GPU drew out to an open byte stream, frame
after frame. texture is the guid of the render target it was
drawn into — renderer.texture.create({ width = W, height = H })
makes one, and a Camera component draws into it as its
textureHandle; the session reads that target back when a frame
comes due, so what the camera drew last reaches the far end.
stream is a handle from stream.open. What reaches the stream
is one frame's pixels then the next frame's, with nothing between
them: a frame is width * height * bytesPerPixel bytes of tight
rows, written in a single call so a consumer reads a whole frame
or none of it. Each frame is read back off the render thread, so
the stream never holds the renderer up. fps caps how often a
frame is taken and defaults to one per rendered frame; format
accepts "rgb24" (3 bytes per pixel, the default) or "rgba8"
(4) — a call with a format outside those two raises, naming both;
flipY writes the last texture row first. Returns the session
handle, or nil and the reason an empty texture, a handle naming no
open stream, a stream another session already carries, or a
non-positive fps was refused with.
Parameters
texturestring— Guid of the render target the image was drawn into (a Camera's textureHandle).streamstring— Stream handle from stream.open.optsAttachOpts?(optional) — Rate, pixel layout and row order (optional).
local session = frameStream.attach(rt.guid, handle, { fps = 30 })
modules/frameStream/detach
detach(handle: string): boolean
End the session and free the staging buffers it read frames back through. The stream stays open — whoever opened it closes it.
Parameters
handlestring— Session handle from frameStream.attach.
frameStream.detach(session)
modules/frameStream/list
list(): { string }
Every live session handle, in a stable order.
for _, h in frameStream.list() do frameStream.detach(h) end
modules/frameStream/status
status(handle: string): FrameStreamStatus?
Report what the session has carried and lost. frames counts
the frames the stream accepted and bytes the bytes they
carried. dropped counts the frames it refused, of which
droppedBackpressure is the part refused because the consumer was
behind; stalledReadbacks counts the frames that came due while
every staging buffer still held a copy on its way from the GPU.
achievedFps is the rate the accepted frames arrived at, across
the span from the first to the most recent, and reads 0 until two
have been accepted — compare it against requestedFps to see a
display running slower than it was asked to. lastCopyFrame is
the engine frame, in time.frameCount() numbering, the copy of the
most recent accepted frame was taken on, and is nil until one is
accepted: every copy one engine frame takes of a texture holds the
same image, so two sessions on one texture whose lastCopyFrame
agree hold the same image as the newest frame of their streams.
lastOutcome names what became of the most recent frame offered.
nil when handle names no live session.
Parameters
handlestring— Session handle from frameStream.attach.
local s = frameStream.status(session); print(s.frames, s.dropped, s.achievedFps)
typed/builtin//modules/api/engine/frameStream/frameStream/attach
frameStream.attach(texture: string, stream: string, opts: AttachOpts?) -> (string?, string?)
Carry an image the GPU drew out to an open byte stream, frame
after frame. texture is the guid of the render target it was
drawn into — renderer.texture.create({ width = W, height = H })
makes one, and a Camera component draws into it as its
textureHandle; the session reads that target back when a frame
comes due, so what the camera drew last reaches the far end.
stream is a handle from stream.open. What reaches the stream
is one frame's pixels then the next frame's, with nothing between
them: a frame is width * height * bytesPerPixel bytes of tight
rows, written in a single call so a consumer reads a whole frame
or none of it. Each frame is read back off the render thread, so
the stream never holds the renderer up. fps caps how often a
frame is taken and defaults to one per rendered frame; format
accepts "rgb24" (3 bytes per pixel, the default) or "rgba8"
(4) — a call with a format outside those two raises, naming both;
flipY writes the last texture row first. Returns the session
handle, or nil and the reason an empty texture, a handle naming no
open stream, a stream another session already carries, or a
non-positive fps was refused with.
Parameters
texturestring— Guid of the render target the image was drawn into (a Camera's textureHandle).streamstring— Stream handle from stream.open.optsAttachOpts(optional) — Rate, pixel layout and row order (optional).
Returns (string?, string?) — Session handle, or nil and the refusal reason.
local session = frameStream.attach(rt.guid, handle, { fps = 30 })
typed/builtin//modules/api/engine/frameStream/frameStream/detach
frameStream.detach(handle: string) -> boolean
End the session and free the staging buffers it read frames back through. The stream stays open — whoever opened it closes it.
Parameters
handlestring— Session handle from frameStream.attach.
Returns boolean — True if a session was ended, false if handle already named none.
frameStream.detach(session)
typed/builtin//modules/api/engine/frameStream/frameStream/list
frameStream.list() -> { string }
Every live session handle, in a stable order.
Returns { string } — Array of session handles.
for _, h in frameStream.list() do frameStream.detach(h) end
typed/builtin//modules/api/engine/frameStream/frameStream/status
frameStream.status(handle: string) -> FrameStreamStatus?
Report what the session has carried and lost. frames counts
the frames the stream accepted and bytes the bytes they
carried. dropped counts the frames it refused, of which
droppedBackpressure is the part refused because the consumer was
behind; stalledReadbacks counts the frames that came due while
every staging buffer still held a copy on its way from the GPU.
achievedFps is the rate the accepted frames arrived at, across
the span from the first to the most recent, and reads 0 until two
have been accepted — compare it against requestedFps to see a
display running slower than it was asked to. lastCopyFrame is
the engine frame, in time.frameCount() numbering, the copy of the
most recent accepted frame was taken on, and is nil until one is
accepted: every copy one engine frame takes of a texture holds the
same image, so two sessions on one texture whose lastCopyFrame
agree hold the same image as the newest frame of their streams.
lastOutcome names what became of the most recent frame offered.
nil when handle names no live session.
Parameters
handlestring— Session handle from frameStream.attach.
Returns FrameStreamStatus? — Session status, or nil when handle names no live session.
local s = frameStream.status(session); print(s.frames, s.dropped, s.achievedFps)