Window updates are broken on Sonoma

Our MacOS application has a single window which is occupied by an NSView-derived view. It's been working for the last ten years or so, but when using the Sonoma beta, window updates are badly broken.

We rely on using setNeedsDisplayInRect to redisplay any portions of the view that need to be redisplayed, but no matter how small a rectangle we specify, the entire window is repainted with the background colour before our drawRect implementation is called. We already provide an overload of isOpaque in our view which returns true, and in the past this was effective for suppressing the background fill, but it no longer seems to work (although I can confirm that it is still called along the way).

I've attached an image that shows an example of how a sample window looks after resizing (which is correct) and then what it looks like after using setNeedsDisplayInRect to invalidate the region occupied by the button in the centre. I've explicitly set the NSWindow background colour to blue to make it more obvious :

Is it still possible to inhibit the background fill? Repainting the entire view for every update is not really an option for us, for performance reasons

Answered by agx in 766535022

Setting NSView.clipsToBounds = YES does NOT solve the problem. Indeed, apps linked against older SDKs, have the same problem.

regardless of the value of NSView.clipsToBounds, I confirm that by invalidating even just a single pt, with -[NSView setNeedsDisplayInRect:], at runloop time, dirtyRect of -[NSView drawRect: dirtyRect] is as large as the bounds of the view. In other words a catastrophe of biblical proportions, in terms of performance. These problems are easily visible with Quartz Debug.app

The layer-backed NSView-derived main view (with layer.drawsAsynchronously = YES and layer.opaque = YES) of our application, for example, can easily contain thousands of vector shapes to be drawn. And with Sonoma, all drawing tools are painfully slow (compromised) because the area to be updated during drawing does not match the invalidated area during tracking. In other words, the substantial change to allow drawing outside the bounds of the View has introduced significant side effects.

Even NSScrollView appears to have been completely revised, and everything that was covered in WWDC 2013 Session 215, "Optimizing Drawing and Scrolling," doesn't seem to hold true anymore. Particularly but not exclusively regarding responsive scrolling, overdraw region, etc, ...

In summary, the problems are very similar to those encountered with first release of Big Sur. But those were bugs. Here, however, I fear that things are much more serious.

https://developer.apple.com/documentation/macos-release-notes/appkit-release-notes-for-macos-14 https://developer.apple.com/videos/play/wwdc2023/10054/?time=676

NSView clipToBounds is set to NO by default now. See the others similar thread in the [AppKit] category on this forum, and the AppKit release notes of macOS 14.

Accepted Answer

Setting NSView.clipsToBounds = YES does NOT solve the problem. Indeed, apps linked against older SDKs, have the same problem.

regardless of the value of NSView.clipsToBounds, I confirm that by invalidating even just a single pt, with -[NSView setNeedsDisplayInRect:], at runloop time, dirtyRect of -[NSView drawRect: dirtyRect] is as large as the bounds of the view. In other words a catastrophe of biblical proportions, in terms of performance. These problems are easily visible with Quartz Debug.app

The layer-backed NSView-derived main view (with layer.drawsAsynchronously = YES and layer.opaque = YES) of our application, for example, can easily contain thousands of vector shapes to be drawn. And with Sonoma, all drawing tools are painfully slow (compromised) because the area to be updated during drawing does not match the invalidated area during tracking. In other words, the substantial change to allow drawing outside the bounds of the View has introduced significant side effects.

Even NSScrollView appears to have been completely revised, and everything that was covered in WWDC 2013 Session 215, "Optimizing Drawing and Scrolling," doesn't seem to hold true anymore. Particularly but not exclusively regarding responsive scrolling, overdraw region, etc, ...

In summary, the problems are very similar to those encountered with first release of Big Sur. But those were bugs. Here, however, I fear that things are much more serious.

https://developer.apple.com/documentation/macos-release-notes/appkit-release-notes-for-macos-14 https://developer.apple.com/videos/play/wwdc2023/10054/?time=676

There was a thread on the "Appkit Abusers" slack about a similar issue. Official word is that it's a known issue.

After investigation, the -[NSView makeBackingLayer] method (of a layer host view) on macOS Sonoma, creates a layer of type NSViewBackingLayer, compared to the layer of type _ NSViewBackingLayer, created on macOS Ventura and earlier. Similarly, the -[NSClipView makeBackingLayer] method on macOS Sonoma creates a layer of type NSViewBackingLayer (like NSView), compared to the layer of type _NSClipViewBackingLayer, created on macOS Ventura and earlier. I think all the problems with invalidating and updating a view's rectangle arise from these changes. Last but not least, these changes interfere, inevitably and very negatively, with the responsive scrolling of the view itself.

Apparently NSViewBackingLayer seems to work in a much less sophisticated way than _ NSViewBackingLayer. With macOS Ventura and earlier, tiling, clipping, responsive scrolling even with very large views at high magnification and prefetching of view content outside of its immediate visibleRect, were perfect. With macOS Sonoma, everything has gotten significantly worse.

This is a serious bug. I just hope Apple fixes this immediately.

ref. https://devstreaming-cdn.apple.com/videos/wwdc/2013/215xax3xz5pbbxeaxxe7z1mk3q/215/215-HD.mov

Behavior has been changed by several macOS updates. Share the details in the following. All issues have not been clear yet on macOS 15.0 beta 6(24A5320a) though.

macOS 14.0 - macOS 14.3 : The drawing mechanism of NSView has changed significantly, and there is a problem that even if a partial area is specified with setNeedsDisplayInRect in an app program, the full area is always passed in drawRect.

macOS 14.3 - macOS 14.4: The issue with setNeedsDisplayInRect has been corrected, and the partial area is now passed in drawRect. However, another issue occurred that the number of NSView drawRect and mouseDragged events gradually decreased when continuously performing setNeedsDisplayInRect on a partial area.

macOS 14.5: Even if a partial area is specified in setNeedsDisplayInRect in an app program and drawn continuously, the system will now periodically draw the entire area. This change has eased the problem of events-decreasing which are described above.

macOS 15.0 beta (24A5289h) : The number of events such as drawRect and mouseDragged in NSView has been dramatically reduced, so the regular full-area drawing by the system described above is no longer useful and is worse than in macOS 14.4.

macOS 15.0 beta 4 (24A5298h) : It is back to macOS 14.5 equivalent behavior.

macOS 15.0 beta 6 (24A5320a) : No change. Even on the latest macOS 15.0 beta 6 (24A5320a), there is an issue that the system periodically draws the whole area even if a partial area is specified in setNeedsDisplayInRect and drawn continuously. macOS 13 did not have such an issue.

Our engineering teams need to investigate this issue, as resolution may involve changes to Apple's software. I'd greatly appreciate it if you could open a bug report, include a sample we can use for reproducing your results (please include some directions for using the sample) and post the FB number here once you do.

Bug Reporting: How and Why? has tips on creating your bug report.

Window updates are broken on Sonoma
 
 
Q