client side windows borkage on Gdk/Quartz

I finally switched our app (ardour) over to a more-or-less current version of GTK (git head from a few weeks ago). we immediately noticed a major slowdown in drawing performance. After quite a few hours of adding gettimeofday() calls all over the place, I've finally got a handle on what is causing this. It appears (I stress *appears*) to be that the implementation of client side windows was done with a complete misunderstanding of how the quartz backend works.

The situation is pretty simple: when we get into process_all_updates() during idle redraw, we run process_updates_internal() for each window that appears to need an update,

During process_updates_internal(), the Quartz backend notes the update area for a given GdkWindow, finds the NSView corresponding to this, and puts the NSWindow containing the NSView into an update list. It also disables any window "flush" on the window, meaning that drawing won't actually happen until we reenable flushes.

Once each window has had process_updates_internal() called, we end up calling _gdk_windowing_after_process_all_updates().

In the quartz backend, this looks at a list of NSWindows that have been marked as needing a redraw. It makes appropriate Cocoa calls to do this, and then reenables NSWindow flushing, flushes the NSWindow to the screen. And we're done. I know this code fairly well because I wrote most of it with help and ideas from Richard Hult - it resulted in a dramatic drawing speedup for the Quartz backend about 18 months ago (or more).

So far, so good. What's the problem? Well, the Cocoa code that we use is a call to "displayIfNeeded" on the top level NSView in the NSWindow. This does a recursive descent over all the NSViews it contains, checking whether or not they need to be updated, and if so, calling their "drawRect" methods.

What does "drawRect" do? Well, a bit of housekeeping, and then it calls _gdk_window_process_updates_recurse(). Still no obvious problem. Except wait a second ... this takes the GdkWindow associated with the NSView, and does ... you guessed it ... recursive descent over all GdkWindows it has a children, generating exposes for each one. So what? well, the "GdkWindow" associated with the NSView is the GdkWindow corresponding to the top level window! Every single "drawRect" of an NSView causes a recursive descent across the entire GdkWindow heirarchy of the top level window!! Arrgh! Yikes!

In Ardour, this means that we spend up to 0.5 seconds in a the idle "redraw" callback every time we need to redraw an NSView...

In older GDK/GTK code, the Quartz backend generated an expose for the GdkWindow corresponding precisely to the NSView, but apparently after the introduction.of client side windows, somebody decided that the relationship between GdkWindows and NSViews as represented by the existing Quartz backend was wrong, and that when drawRect was called for *this* NSView, we still had to do a recursive descent through all child GdkWindows of the affected GdkWindow. This ignores the fact that Quartz is already doing this for us.

I have to do bit more investigation (e.g. ripping out code and adding the old approach back), but I'd appreciate it if either Richard or whoever may have worked with him on this issue could comment. Does my description make sense, and is it sensible that forgetting that Quartz was already recursing over children could have led to this?


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]