
Lately I’ve been digging into the details of the Mac OS X graphics subsystem, both because a lot of my (Java) code exercises it, and partly because I might be building something similar at some point.
OS X owes its renowned visual stability to the fact that every single window is buffered; to be exact, drawing commands from programs are executed into offscreen buffers, which are flushed to the screen (with an eye to where the CRT beam is!) when WaitNextEvent or QDFlushPortBuffer are called. Keeping a bitmap for each window is what allows OS X to sling windows around without visual glitching, even when applications are frozen up compeletely. It is, of course, a very expensive proposition in terms of the system RAM that it consumes. It hadn’t sunk in for me before that every single open window, visible or not, eats up significant RAM—maybe that’ll convince me to close old windows a little more often, especially on my poor 640MB iBook.
The other thing that I had heard about before but hadn’t tried before is the Quartz Debug utility, which you’ll find in /Developer/Applications/Performance Tools/Quartz Debug if you have the Developer Tools installed on your machine. If you run that and check off Flash screen updates (yellow) and No delay after flash, you’ll get a nice view into the workings of the graphics system, because any area of the screen buffer that changes will flash yellow. One interesting thing that this makes apparent is an advantage that Safari has over Firefox: when scrolling, Safari updates only the newly-exposed area of the window, making for smoother scrolling than Firefox, which always redraws the whole content pane. Another thing that it made obvious to me is how horribly inefficient some parts of the Geobrowser are in terms of redrawing things which haven’t changed visually—I need to be more conscientious about calling repaint() with an affected area and minding the clip rectangle in paintComponent(). (It actually makes me happy to discover clear-cut avenues for optimization like that.)
I’m still looking for real nuts-and-bolts details on what happens during screen updates. Here’s the mental model that I’ve pieced together so far:
- An application performs some drawing operations, which modify an offscreen window buffer #1
- The application calls
QDFlushPortBufferwith an affected region rectangle, which blocks while… - The affected region of the application window is blitted from buffer #1 to buffer #2 (the Window Server’s buffer for that window)
- The affected region of the screen is re-composited by Quartz Compositor (Extreme or otherwise) and blitted to the frame buffer when the CRT beam or LCD refresh point is elsewhere
If you know better and I’m getting some details wrong, don’t hesitate to comment.