Skip to content

Zooming out with a Magic Wand selection freezes the app (marching ants redraw scales with outline size) #112

Description

@MyronKoch

What happens

After a Magic Wand selection on detailed artwork, zooming out freezes the app. The main thread stays pegged at 100% and never recovers. I hit it on a 27,000 x 5,400 banner at 0.6% zoom, and it also reproduces on unmodified main with a 10,800 x 5,400 PSD.

Steps

  1. Open a document with detailed artwork (ornate lettering, logos).
  2. Click the Magic Wand on the artwork with default settings (tolerance 32, contiguous).
  3. Zoom out. At 10% it's already sluggish; at around 1% it stops responding.

Measurements

These were taken on main at 01e8e52 from a local test. It ran MagicWand.select on a real 10,800 x 5,400 PSD, then timed exactly what TransformOverlay.drawSelection() does: a solid 1 pt stroke, then a [4, 4] dashed stroke, into a view-sized context.

The wand's outline size depends on what's clicked:

Clicked on Outline edges
Flat background 4
Schedule text layer 26 to 52,006
Poster artwork layers 194,548 to 271,662

Drawing the largest outline (271,662 edges) once:

Zoom Solid Solid + dashed
100% 0.027 s 0.055 s
10% 2.68 s 3.27 s
0.6% 63.9 s 94.9 s

The ants timer (EditorCanvas.swift:2008) marks the overlay dirty every 0.12 s. From about 10% zoom down, a single redraw takes longer than that interval, so the next one is already queued and the app never catches up. The cost rises as you zoom out because every edge is still processed while hundreds of thousands of them collapse into a few screen pixels. The time is spent inside Core Graphics, so a Release build doesn't change it.

A sample of the frozen app shows the main thread in a single CA::Layer::display → CGDisplayListDrawInContextDelegate → ripc_DrawPath → aa_render → aa_distribute_edges call, identical across every sample for minutes.

The wand allows outlines of up to 8,000,000 edges (WandPixels.c:7, wand_edge_limit), so an outline far larger than the one measured here can reach the overlay.

Where

  • Compositor/Rendering/TransformOverlay.swift:197, drawSelection(): strokes the full selection.path twice per redraw, at full detail, at any zoom.
  • Compositor/Rendering/EditorCanvas.swift:2008: the 0.12 s marching-ants timer.

Possible fixes

  1. Level of detail for the outline. When a document pixel is smaller than a screen point, draw the ants from an outline traced at screen resolution instead of per document pixel. Tracing the selection mask downsampled to the current zoom, or caching one outline per zoom step, keeps the edge count bounded by what's on screen. This is the real fix.
  2. Backpressure on the ants timer. Skip a tick while the previous redraw is still pending, so a slow outline stutters instead of freezing the app. It's small and independent of fix 1, and worth having in any case.

I can send a PR for fix 2 if you'd like it, or try fix 1 if you have a preferred approach.

Environment: macOS 27.0 (26A428), Apple M3 Max, Compositor main at 01e8e52.

🤖 Generated with Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions