Skip to content

Performance & Memory

Every rendered node can hold up to two full RGBA textures: its current output and the previous clean frame. On a large graph at high resolution the total can exceed what the device allows, so a memory governor trims the caches after each evaluation pass.

After each evaluation pass the app releases memory in this order:

  1. Drop stale previous-frame copies, least-recently-rendered first.
  2. If still over budget, drop cached outputs on sink nodes only (nodes nothing downstream reads), oldest first.

A cached output that feeds another node is never evicted, because that would break downstream re-renders. Every node keeps its small thumbnail (256² or smaller) regardless, so node bodies stay visible on the canvas even after their full texture has been evicted.

The budget is not a fixed number. It starts at a conservative floor and is raised at startup to a figure derived from the device:

ScopeValue
Floor, iPadOS512 MB
Floor, desktop1.5 GB
iPadOS, calibratedHalf of the memory allowance the process has at launch - what it already holds plus the headroom left before iOS kills it
Desktop, calibratedA quarter of physical RAM

A device reading can only raise the budget, never lower it: whatever the calibration produces is clamped up to the floor. The half of the iPad allowance and the three quarters of a desktop’s RAM that are left over go to everything that is not a node texture - the Flutter engine, the GPU driver’s own allocations, decoded images, and the export buffers, which at 8192 run to a gigabyte on their own.

Textures the native render core allocates are tracked separately and reported alongside in diagnostics, but are not counted against this budget: the two accountings overlap in ways neither side can resolve, and the budget was calibrated against the governor’s own numbers.

When the governor evicts a texture, the node’s full-resolution preview is gone until the node is next evaluated, at which point it re-renders. Thumbnails clearing and then re-rendering on a large project is expected behavior, not a sign of a broken graph. The small canvas thumbnails themselves are always retained.

Separately from the governor, a guard watches the totals and says something before you run out. It is not an 8K feature: a 2048 graph with a hundred nodes exhausts an iPad exactly as thoroughly as a small graph at 8192.

MessageWhen
High memory use - previews may reduce qualityTexture use reached 80% of the budget. Nothing has been dropped yet
Reduced preview quality to stay within memoryThe governor actually evicted something, the OS asked for memory back, or the app has used 90% of its iOS memory allowance

Each level fires once per episode, not once per interval, and re-arms only after use falls back under 65% of budget and nothing is being dropped - a graph parked at 80% would otherwise warn on every render settle. A critical message subsumes the warning, so one episode never produces both. Sampling piggybacks on the settle the canvas already runs after a render burst; nothing polls on a timer and nothing blocks a frame.

The preview redraws only when something changes. With nothing dirty and nothing in flight, the render loop blocks and the GPU goes fully idle, which is what keeps a parked preview off the battery and the fans quiet. While a viewport is dirty it redraws on a roughly 30 fps budget.

Texture exports encode on a worker thread, so a 4K or 8K export does not freeze the live preview while it writes.

On weak graphics hardware the core asks for extra headroom above the guaranteed-safe limits but never for more than the adapter reports, so a limited device gets a reduced preview rather than a failed one. See Settings for the 3D Preview GPU and, on Windows, the Graphics Backend controls.

There is no formal minimum-spec sheet. The figures the project has actually measured are:

  • 8192 × 8192 textures on an M3 iPad Air. That figure predates the handheld cap: iPadOS and Android now stop at 4096, because evaluating an 8K graph runs tablets and phones out of memory even where a single texture that size allocates.
  • 512 × 512 and larger on Windows with an RTX 3070; the Erosion node runs roughly 230 ms for 80 steps at 2048² on that card.

See Resolution & Bit Depth for how resolution and bit depth drive memory use.

For a Custom Shader node, cost is pixels times work per pixel (an 8-tap loop at 2048 × 2048 is 33 million texture reads), so prefer loops with small constant bounds.