【问题标题】:Opaque NSWindow and WindowServer poor performance不透明的 NSWindow 和 WindowServer 性能不佳
【发布时间】:2020-04-06 03:16:10
【问题描述】:

我需要创建不透明的透明窗口,里面有不透明的子视图(称为表面视图)。每个子视图可以包含数千个子视图(称它们为 controlView)。所以结构一定是这样的:

NSWindow(非透明)
- NSView(非透明)(窗口内容视图)
-- NSView(不透明)(SurfaceView)
--- NSView (不透明) (ControlView)

Illustration

问题是,当内部有数千个 controlView 时,WindowServer 变得过载。看起来窗口中的任何 NSView 都变得不透明。我不明白我必须做什么。

如果 NSWindow 不是不透明的,则 WindowServer 不会过载。但我需要不透明的窗口。这个案例证明了不透明的 NSView 可以包含很多性能良好的子视图。

如果 NSWindow 有 stylemask [.titled, .resizable] 没有过载,窗口变成透明的(证明性能好的透明窗口是可能的),但是鼠标不能通过窗口的透明部分工作。 NSWindow 也有来自私有类 NSThemeframe 的圆角。这个解决方案很脏,因为它需要重新实现鼠标事件并替换私有类中的一些方法。

如果表面是 childWindows 而不是子视图,则不会过载。但是在这种情况下,主窗口不裁剪表面,小型化动画不起作用,实际上窗口不是实际窗口。

我尝试过与 CAlayer、opaque 等不同的组合和操作,但它看起来像一个错误。绝对有可能获得良好的性能,但我不明白如何。有什么想法吗?

【问题讨论】:

    标签: macos cocoa nsview nswindow appkit


    【解决方案1】:

    也许这会对某人有所帮助。问题在于两个意想不到的事实。

    1. NSWindow 属性ignoresMouseEvents 有非常奇怪的行为。如果此 ignoresMouseEvents 为真 - 窗口对鼠标完全透明,如果为假 - 窗口对鼠标完全不透明,即使窗口的某些部分是透明的。窗口 ignoresMouseEvents 的默认值为 false,但诀窍是,如果您不更改此属性,则窗口 ignoresMouseEvents 的实际行为将具有第三个选项 - 窗口在其透明时对鼠标变为透明部件,这是窗口的默认状态。

    所以 ignoresMouseEvents 是一个布尔值,实际上有三个选项:true、false 和 Schrödinger 的默认状态(如果您没有更改它)。

    1. ignoresMouseEvents 的这个不直观的第三个选项会导致 windowServer 的 CPU 意外过载,如果窗口具有透明背景并且窗口内有很多 CALayer。因此,windowServer 对鼠标的透明度检查在某些情况下速度非常慢

    因此,默认情况下,此代码将使您的 windowServer 在鼠标移动时哭泣:

        let window = NSWindow(contentRect: windowrect, styleMask: [.borderless], backing: .buffered, defer: false);
        window.setIsVisible(true);
    
        window.contentView?.wantsLayer = true;
        window.contentView?.layer?.borderWidth = 1;
        window.backgroundColor = NSColor.clear;
    
        for _ in 0...5000 {
            let view =  NSView(frame: viewrect);
            view.wantsLayer = true;
            view.layer?.backgroundColor = CGColor(gray: 1.0, alpha: 1.0);
            window.contentView?.addSubview(view);
        }
    

    您的视图或图层是否不透明并不重要,它们是否是其他视图的子视图。但是,如果您添加 window.ignoresMouseEvents = false 或 true,您的 windowServer 会平静下来,但窗口会变得完全不透明或对鼠标完全透明。

    解决方案是实现您自己的窗口透明度检查鼠标移动(使用 trackingArea 或 GlobalMonitor),如果鼠标在窗口的透明部分,则将 ignoresmouseevents 设置为 true,否则设置为 false。好在鼠标检查的实现很难写,会比默认的windowServer方式慢。

    附:抱歉语言不好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-08-17
      • 2011-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-25
      • 1970-01-01
      相关资源
      最近更新 更多