Skip to content

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

text
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 thread

Responsibilities 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, and ShaderProgram are 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.

Go deeper

Rune Project brings Rune Story authoring together with the Rune Engine rendering foundation.