Resources and lifetime
Rune Engine keeps lifetime explicit because immutable snapshots, retained backend resources, and capture/export workflows all depend on old data staying valid after newer frames are already being authored.
Mental model
mutable node state
-> commit copies snapshot-owned data
-> snapshot addrefs shared resources
-> replay realizes backend objects lazily in slots
-> release queues cleanup on the render threadResponsibilities and boundaries
- Node state owns mutable authoring data such as the live animation working set and recorder scratch space.
- Snapshot state owns immutable command/data buffers, copied animations, child slots, and
resource_refs[]. - Global resources such as
Image,Font,ShapedText,NineSlice, andShaderProgramare independent refcounted handles. - ResourceSlot is the retained cache boundary for backend-specific brush and path realizations.
Stable current behavior
Snapshots pin what replay needs
When a snapshot is published, it addrefs every child snapshot and every shared resource written into command or data records. That lets replay keep using the published frame even after:
- the UI thread records new content,
- a context submits a new root, or
- the caller releases its original managed handle.
ResourceSlot is the long-lived realization anchor
Slots are stable handles rooted in an engine. Replay decides what each slot currently realizes:
- a brush resource such as solid, gradient, image, shader, blur, acrylic, or cached-brush state
- a path resource such as realized
ID2D1PathGeometry
If the requested type or configuration hash changes, replay replaces the old backend realization while the slot handle itself stays stable.
Immutable resources stay outside snapshots
Large or reusable assets are shared handles instead of copied payloads:
- bitmaps copy caller pixels at creation time and can be released by managed code immediately afterward
- fonts are family/style selections; size is chosen at shape time
- shaped text is immutable and can be recorded by many line draws
- shader programs are device-independent compiled resources with reflection metadata and diagnostics
Cleanup happens on the right thread
Most native handles use deferred destruction. When the last reference drops, the object is queued to the runtime and destroyed from the render thread, which is where Direct2D and COM-backed realization state can be released safely.
Current limitations
- Resources do not have an independent dirty channel. If a stable snapshot keeps the same root pointer but some underlying dynamic content changes, callers must request a render explicitly.
- The current published model is device-generation aware but still tied to the audited Direct2D resource types and cleanup rules.
- Exported frame snapshots use fresh image allocations rather than pooled textures, so GPU consumers can retain them safely without aliasing later exports.
How the pieces connect
Recording decides which stable handles a snapshot depends on. Commit freezes that dependency list. Replay lazily realizes device objects into slots only when the snapshot is actually rendered. Refcounted snapshots, shared handles, and deferred cleanup then make old frames safe for capture, reuse, and export.
This is why Story can keep reusing fonts, images, and shaped text while still capturing exact historical frames for preview or media export.