Resolution & Bit Depth
Resolution and bit depth are set once for the project and can be overridden per node. Together with the dirty-driven evaluation pass, they decide how much work a change costs and how much memory a graph holds.
Project resolution
Section titled “Project resolution”Set in the Properties panel with no node selected. The choices are square: 256 × 256, 512 × 512, 1024 × 1024, 2048 × 2048, 4096 × 4096, 8192 × 8192. 8192 is the top of what every graphics adapter the desktop app runs on guarantees for a single texture, which is why the ladder stops there.
Per-node resolution
Section titled “Per-node resolution”Select a node and use its Resolution dropdown:
- Inherit (N × N): follows the project resolution. This is the default, and the label shows the current project value.
- Match input (W × H): takes the largest upstream resolution, measured by pixel count, and falls back to the project default for a node with no inputs. The label shows what the node resolves to right now.
- A fixed square size from the same ladder the project dropdown offers, capped the same way per device.
Bit depth
Section titled “Bit depth”Depth choices are 8-bit, 16-bit and 32-bit float, both project-wide and per node. The per-node dropdown offers:
- Absorb inputs (…): the default. Takes the deepest depth found upstream, so raising depth anywhere travels downstream automatically. A node with no inputs falls back to the project default, which makes this behave identically to “inherit project” until an override appears somewhere upstream.
- Inherit project (…): always the project default.
- 8-bit / 16-bit / 32-bit float: pinned.
Both dropdown labels show what the node currently resolves to, so you can see the effect without doing the arithmetic. Export validates against the authored depth and warns you when the chosen file container would write shallower than what you authored. See Bit Depth & EXR for the export side.
When the graph re-evaluates
Section titled “When the graph re-evaluates”Evaluation is dirty-driven and debounced. Changing a parameter, rewiring, adding or deleting a node marks the affected node and everything downstream of it dirty; a pass runs shortly afterwards.
- Normal debounce: about 150 ms after the last change.
- While you are actively dragging a control, the engine switches into an interactive mode: the debounce drops to about 24 ms and the expensive nodes render at a draft cap of 256 pixels on their longest axis. Cheap nodes are not drafted at all: they render at full resolution for every frame of the drag, which is what lets the 2D preview show real pixels while you move a slider. Expensive here means the marches and gathers (Detail Blur, Slope Blur, Bevel, Curvature / Cavity, Tile Sampler, Surface Detail, Directional Occlusion, Trim Sheet, Image and the Flood Fill family), the compute nodes (Distance Field, Erosion), and subgraphs and Custom Shader nodes, which are assumed expensive because their cost cannot be read off a type id.
- About 220 ms after your last edit, interactive mode ends and everything still showing a draft is re-rendered at full resolution.
Nodes are cached by content signature: a node’s type, target resolution, parameter values and the signatures of everything feeding it. A node marked dirty whose signature has not actually changed is served straight from cache instead of re-rendering, so an edit on an unrelated branch costs nothing.
Automatic texture readbacks are capped at 256² previews; full-resolution data is materialized only when something actually needs it (the 2D preview, an export, a 3D upload, a fallback render, or a memory eviction rescue).
The memory governor
Section titled “The memory governor”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 after each evaluation pass the app trims:
- Drop stale previous-frame copies, least-recently-rendered first.
- 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 (up to 256²) thumbnail regardless, so node bodies stay visible on the canvas even after their full texture has been evicted.
The budget starts at a conservative floor and is raised at startup to a figure derived from the device: a share of the memory headroom the process actually has on iPad, a share of physical RAM on desktop. A device reading can only raise the budget, never lower it.
A memory guard watches the same numbers and says something before you run out. At 80% of the budget it toasts “High memory use — previews may reduce quality”; if the governor has actually evicted something, or the OS asks for memory back, it toasts “Reduced preview quality to stay within memory”. Each fires once per episode and stays quiet until usage falls back well under the line, so a graph parked near the limit does not toast on every render.
The budgets are deliberately conservative and may change; the current numbers are listed in Performance & Memory.