You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: macOS backend flickers/flashes due to use of timers #31991
Right now, the macOS backend has a lot of "misfires" where it will momentary render a blank figure. This typically occurs when the figure is first displayed as well as during a window resize.
Here's a screen recording of me resizing "galleries/examples/pie_and_polar_charts/polar_demo.py" on an old MacBook Air:
Video of problem
⚠️WARNING: This video shows a flashing/flickering white box on top of a dark background.
movie.mp4
This also occurs on modern/faster hardware although not as pronounced.
The root cause is the use of timers to call setNeedsDisplay:YES. This was implemented in #21790 in order to fix #10980 and #17445. However, I believe that pull request was done without realizing it's possible to send the Agg buffer directly to an NSView (via layer-backing or layer-hosting) without the need of a -drawRect: callback.
At a very general level, the macOS run loop has different phases of execution:
Event-processing / Timers
Layout
Drawing
Right now, in response to a draw_idle() call, the macosx backend creates a single-shot NSTimer and adds it to the run loop. At best, the timer will not fire until the next run loop cycle. If we are in "Phase 1" above, the earliest that _draw_idle() will be called is the next "Phase 1". We will skip over the actual draw phase ("Phase 3") of the current run loop cycle.
"The system reserves the right to apply a small amount of tolerance to certain timers regardless of the value of the tolerance property.
Basically, using an NSTimer is the equivalent of telling the system: "perform this invocation no earlier than X, but you can delay a bit after X to save power."
draw_idle() should instead do the following:
If running in the main thread, immediately call setNeedsDisplay.
If running on a background thread, dispatch_async() to the main thread and then call setNeedsDisplay.
Caution
This assumes a layer-backed/layer-hosted approach. It won't work with the current -drawRect: approach without breaking blitting.
After a lot of issues with blitting in "pong_sgskip.py", I got it working on the MacBook Air. Now there's no flicker, but the redraw speed is still slow. There's not much that I can do about that - 99%+ of the CPU is now spent in RendererAgg doing the actual rendering.
Activity
iccir commentedon Jul 6, 2026
After a lot of issues with blitting in "pong_sgskip.py", I got it working on the MacBook Air. Now there's no flicker, but the redraw speed is still slow. There's not much that I can do about that - 99%+ of the CPU is now spent in RendererAgg doing the actual rendering.
movie2.mp4