I have an interesting case for you. And need help.
My Mac app has a layer-hosted NSView with lots of layers and sublayers and sub-sub layers inside. Imagine a Finder window with items (icons, text labels)... something like that. Each item has a button, selection background layer.
And everything is fine until you have 1000 of those items.
Now with lots of those items, my app becomes unresponsive when I try to interact with this view. And the tricky part is that it is not my app that consumes the CPU, but WindowServer. It gets to 100% and the system freezes for some time.
Important Note:
My view is a part of the app interface. There is a main window with other views. They have their own structure as well.
And I've noticed that if I put this layer-hosted view into a separate window, it works just fine with no freezes having the same 1000 items. But starts to halt the system if I put it back to the main window.
The Question
I've run some tests and removed all the sublayers for every item, leaving just one-two for each. It has reduced the load on the system, but still bad. I've disabled all the drawings - just small images for the icons. Didn't help as well.
WindowServer
's main job is drawing graphics related things for macOS which means everything you can see from the entire screen, you must go through WindowServer
and let it draw for you.
There are many reasons why WindowServer is using high CPU.
Until seeing your exact code, I cannot tell you why placing elements into separate window can help. Perhaps the window itself hide something to make it easier to draw?
One of my software has a lot of graphic elements and with animations. But I never have encountered your issue. How about creating a testing project and try the similar things? Sometimes, it could help to see the problem on the clearer project structure.
I created a simple demo which has 100*100 sublayers in the window. It seems no problems at all.
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet weak var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
self.window.contentView?.wantsLayer = true
let size = 10
for i in 0..<10000 {
let x = i%100
let y = i/100
let layer = CALayer()
layer.frame = NSRect(x: x*size, y: y*size, width: size, height: size)
layer.backgroundColor = .random()
self.window.contentView?.layer?.addSublayer(layer)
}
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
extension CGFloat {
static func random() -> CGFloat {
return CGFloat(arc4random()) / CGFloat(UInt32.max)
}
}
extension CGColor {
static func random() -> CGColor {
return CGColor(red: .random(),
green: .random(),
blue: .random(),
alpha: 1.0)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With