Start here / one real picture
Follow one tile from layer to screen.
Forget the cache names for a moment. Begin with three transparent sheets: a sun above mountains above a sky. We will cut every sheet into tiles and follow the same tile coordinate through the renderer.
The document model
Layers are separate sheets, not one image.
The final picture is produced by looking upward from the bottom sheet and combining each visible sheet in order.
- 1Sky Content supplies the bottom pixels.
- 2Mountain Content is blended over the sky.
- 3Sun Content is first shaped by its Mask, then blended over the accumulated lower picture.
Visibility does not erase the Sun Content or Mask. It only decides whether the already-prepared Sun Projection participates in the parent stack.
The bigger system / architecture playground
Layers × planes × LODs × tiles.
A PSD does not become one giant flattened bitmap inside the editor. Each layer keeps authoritative source information. Derived planes are built at the LOD and tile coordinates demanded by the viewport, then retained only while useful.
Read the title as four questions
One square,
four coordinates.
Layers say whose pixels they are. Planes say what processing stage they represent. LOD says the working resolution. Tile says which square of that resolution. Together, those four answers identify one reusable piece of image data.
Document → Sun → Content → LOD 2 → (column 2, row 0)
Begin with ordinary PSD layers.
VISIBILITY / PARENT PROPERTY
Hide the upper Sun layer
Cold versus warm
Warm visibility toggle
Memory pressure
Retained does not mean permanent.
Raster edits commit to Content at LOD 0. Text, vector, fill, adjustment, and filter nodes keep authoritative recipes. Higher Content mips plus every Projection, Backdrop, and Composite tile are derived: they may be invalidated, evicted, and reconstructed.
A real document / measured, not guessed
What does Luffy cost?
Luffy-Colour-Updated-Effect.psd demonstrates why “one PSD” is not one bitmap in memory. The numbers below come from LayerDesk’s current inspector and cache constants. Its 21 PSD layer records include structural divider records; the parsed tree contains 17 pixel nodes and 2 groups. Tile totals describe a full-canvas plane; real layers can have smaller bounds and only demanded tiles become resident.
Current selection
LOD 2Hypothetical resident bytes
38.30 MiB| LOD | Output image | 64px store grid | Store tiles | RGBA plane | 256px transfer grid | Transfer tiles | Full payload |
|---|
64×64 store tiles are the engine’s cache and composition units: 16 KiB for RGBA8 or 4 KiB for an 8-bit mask. 256×256 transfer tiles are viewport transport units: 256 KiB RGBA8. One transfer tile covers 4×4 store tiles at the same LOD. At LOD 2 the whole Luffy canvas is 165 transfer tiles, but the historical visibility acceptance needed only 48 intersecting viewport tiles—not the whole document.
Current implementation / RAM ceilings
Hot pixels live in several independent budgets.
Disk caches and the two meanings of swap
LayerDesk swap is not macOS swap.
authoritative or reconstructible→hot RAM + GPU
focused viewport→LayerDesk scratch
evicted source tiles
Do not design around macOS swap. It is an emergency consequence of whole-system pressure. LayerDesk’s own tile swap is explicit application scratch, currently created in the system temporary directory. Current source gives its file allocator no explicit maximum; it grows to a high-water mark and reuses freed ranges.
The naive answer
“Keep every tile” does not fit.
That 13.6 GiB is an intentionally conservative upper bound, not measured Luffy residency. Real layer bounds can be smaller. It also excludes Projection, Backdrop, Composite, renderer bytes, and GPU textures—so eagerly retaining every derived plane would be substantially worse.
The practical answer
Retain the working set, not the document.
At 25% zoom LayerDesk selects LOD 2. A full Luffy RGBA plane at that LOD is only 38.30 MiB in store tiles, and a viewport usually touches less than the full 165-transfer-tile canvas.
NOT FINAL POLICY
Is this the best possible memory design?
No—not yet.
The architectural direction is right: sparse tiles, demand-driven LOD, reconstructible derived planes, dependency-directed invalidation, pinning, and atomic publication. The current budget implementation is still fragmented. Independent fixed limits do not form one enforceable application memory budget.
What should remain
What should change
Recommended long-term memory ownership
00 / The problem
We kept the result, not the work.
A root Composite cache is excellent until an edit invalidates it. On a damaged cache miss, the previous renderer still had to walk and blend much of the stack from transparent—even when every source tile was already warm.
“I know the final pixel—or I start over.”
Content and masks survive. Composite tiles outside damage survive. But the renderer retains no operational per-node output and no reusable stack checkpoint inside the damaged region.
“Reuse the nearest valid answer.”
Each cache level answers a different question: where are the source pixels, what does this node look like, what was below it, what is the final image, and when may the user see it?
01 / Five responsibilities
Click through the memory hierarchy.
These are not five copies of the same image. Each level owns a narrower promise. Select a level to see what it stores, what it solves, and what it deliberately does not solve.
Content / Mask
02 / Toggle simulator
Move the edit through the stack.
Choose a layer, then run the conceptual simulation. The counters are work units—not benchmark milliseconds. They show why Backdrop helps most near the top of a deep stack and least near the bottom.
03 / Validity
Do not throw away what did not change.
Revision orders work; damage determines stale pixels. The mutation type tells us which dependency edges changed. Explore how the preservation boundary moves.
A derived tile is valid while present because invalidation already removed every affected entry. No visibility-state combination is part of the key.
04 / “Seamless” decoded
Backdrop fixes waiting, not tearing.
Two problems can feel like one. The cache hierarchy controls how quickly the candidate frame is calculated. Atomic publication controls whether an incomplete candidate is ever visible.
Computation problem
The new frame takes too long.
Projection avoids repeating intrinsic node work. Backdrop avoids re-blending the unaffected lower prefix. Together they shorten the period for which the old frame must remain visible.
- Owned by the Rust render engine.
- Measured as operations visited, cache hits, and compose wall time.
- Backdrop is the decisive optimization for deep flat stacks.
Presentation problem
The user sees a mixed or broken frame.
Keep the last acknowledged sharp frame. Build target tiles offscreen. Validate revision, graph sequence, operation ownership, cancellation, complete coverage, and texture readiness. Then swap once.
- Backdrop cannot guarantee this.
- Tile gutters/access rects solve pixel-edge artifacts.
- Transactional texture residency prevents checkerboard holes.
05 / Bounded memory
Retained does not mean retained forever.
Caching every intermediate after every layer would trade latency for an unbounded memory problem. Checkpoints are opportunistic and derived outputs are disposable.
- The currently acknowledged sharp viewport frame.
- The complete candidate target set being published.
- Content, Projection, and Backdrop leases held by active compose jobs.
- Offscreen Composite tiles.
- Unused Backdrop checkpoints.
- Unused node Projections.
- Unpinned source tiles when package/source backing exists.
Previous: cache the answer. New: cache the reusable reasoning.
Content prevents repeated source decoding. Projection prevents repeated node preparation. Backdrop prevents repeated lower-stack blending. Composite prevents repeated final rendering. Atomic frame prevents partial presentation. SeamlessUpdate is the transaction that validates and connects all five.