Skip to content

Drawing commands and paths

Rune Engine records drawing as two immutable buffers: a linear command stream for replay order and a typed data buffer for path payloads and other large immutable records.

Mental model

text
record on RuneNode
  -> cmd_buffer: properties, transforms, draw ops, clips, brush records
  -> data_buffer: typed DATA_PATH payloads
  -> replay walks commands linearly and resolves typed offsets as needed

Responsibilities and boundaries

  • Commands describe painter-order state changes and draw operations.
  • Brush records are CMD_DATA entries that configure retained slot-backed realizations.
  • Paths are typed offsets into DATA_PATH records, not raw geometry pointers.
  • ResourceSlot owns backend realizations; commands and path handles only point at immutable snapshot records plus those stable slots.

Stable current behavior

Command replay is explicit state

The active command set covers:

  • property changes such as offset and local opacity
  • transform save/restore and matrix concatenation
  • filled primitives, stroked primitives, shaped text, nine-slice, child replay, SVG assets, and box shadows
  • rectangular, rounded-rectangle, and path clips

Replay stays linear until it hits a child boundary, where compositing decisions become a snapshot concern rather than a draw-command concern.

Paths are recorded once, reused by handle

CreatePath() writes one immutable DATA_PATH record into the snapshot data buffer and returns a typed handle. Fill, stroke, and path-clip commands store that handle. Replay resolves it back to bytecode, realizes backend geometry, and reuses the slot's cached path resource when the resolved configuration hash matches.

Brushes are data records, not immediate draw objects

Solid, gradient, image, transformed, blur, acrylic, cached, and shader brushes all record descriptor data into CMD_DATA entries. Shape commands then carry only a Brush handle plus geometry values.

That keeps command structs small and lets one slot-backed brush realization be reused across multiple draws and snapshots.

Stroke trim is a first-class command

CmdStrokeTrimmedPath() is not emulated by changing dash arrays. It transactionally records:

  1. one measured anonymous DATA_PATH, and
  2. one fixed trimmed-stroke command with live width/start/end/offset values.

Trim uses normalized arc-length intervals, supports forward seam wrapping when end < start, and reuses the full path geometry cache when the trim range is effectively complete.

Child replay is still "just a draw"

CmdDrawNode() and CmdDrawSnapshot() both publish CMD_DRAW_CHILD boundaries. That means clipping, transform state, bounds calculation, and draw ordering stay in one uniform command model even when the content on the other side is an entire snapshot subtree.

SVG is a retained Engine resource

RuneSvg wraps a finished immutable SVG document snapshot plus its viewBox and intrinsic size. CmdDrawSvg() records a CMD_DRAW_SVG command with live destination RuneValues and preserve-aspect behavior. Replay draws the retained SVG document into that destination rectangle; bounds/value analysis recognizes the same command, so SVG participates in ordinary snapshot replay rather than a Story-only raster fallback.

Current limitations

  • Each node snapshot has separate 1 MiB command and data arena limits.
  • Individual commands still use a 16-bit size field, so one command cannot exceed 0xFFFF bytes even though DATA_PATH records can be larger.
  • SamplePath() and trimmed stroke both require static single-figure geometry in V1.
  • Trim is stroke-only in V1 and does not expose a reusable shared measured-path handle.

How the pieces connect

Managed code records RuneValue-backed commands and typed path payloads on the UI thread. Commit freezes them beside child slots and resource references. Replay then walks the command stream once, resolves values for the current frame, hydrates slot-backed resources, and draws in painter order.

Because paths, brushes, clips, and child boundaries all use the same immutable recording model, higher-level Story features such as gradient fills, path followers, path trim, and layered composition all land on one consistent render pipeline.

Go deeper

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