OS X 上全屏模式下的 Sprite Kit 严重 FPS 问题

Sprite Kit Serious FPS Issue In Full Screen Mode on OS X

我正在制作一个相当复杂的 sprite 套件游戏。我最近添加了对 OS X 的支持。无论在 window 调整大小时(即使调整到最大屏幕 space 时)我的游戏如何缩放,我总是得到 60 fps。但是,当我让我的应用程序输入 "Full Screen," 时,fps 会下降到 30-40 fps 并保持这种状态?但是,如果我在启用全屏时使用鼠标光标并显示菜单栏,则 fps 会恢复到 60 fps!

您甚至可以使用默认模板为 Xcode 中的 mac 制作精灵套件游戏来测试此错误。这是我为 mac.

拍摄的默认游戏模板的屏幕截图

我建议您自己尝试一下,如果您使用 Apple 的默认 sprite 工具包模板 OS X,您甚至不需要编写任何代码。

最大 Window(无 FPS 问题:59-60 FPS)

全屏模式(FPS 降至 30-40 FPS)

鼠标在顶部显示菜单栏的全屏模式(令人惊讶的是,没有 FPS 问题:59-60 FPS)

任何人都知道可能导致此问题的原因。如果这意味着用户将 失去 性能,我不想以全屏模式发布我的应用程序。你会认为全屏模式可以更好地优化绘图,但显然恰恰相反。我是 运行 这个 Yosemite。

好的,在调查这个问题几周后,我找到了解决这个问题的一些方法。在开始之前,让我先解释一下我的设置。我在包含 SKView 的故事板中使用 NSViewController。我已经在 MacBook Pro(Retina,15 英寸,2013 年初)上测试了变通方法,我不知道下面介绍的变通方法是否适用于其他 Mac。我相信它应该,当我有机会时,我会测试并看看下面的解决方法是否有效。

所以在我开始之前,让我们回顾一下问题是什么。问题是通过单击全屏按钮使您的应用程序进入全屏会导致 FPS 大幅下降。以下是启用全屏按钮的方法:

self.view.window!.collectionBehavior = .FullScreenPrimary

于是我四处搜索,找到了使用以下代码进入全屏的不同方式:

 self.view.enterFullScreenMode(NSScreen.mainScreen()!, withOptions: nil)

但我的 FPS 仍然大幅下降。请记住,在最大化 window 模式下,甚至在菜单栏可见的全屏模式下,我都没有遇到 fps 问题! (见有问题的图片)。

所以我尝试了一种不太高级的全屏方法。我找到了 Apple here

的指南

使用指南中的一些代码,我设法通过将 window 大小设置为显示大小并将 window 置于所有 OS 之上来进入全屏XUI。代码如下:

self.view.window!.styleMask = NSBorderlessWindowMask
self.view.window!.level = Int(CGWindowLevelForKey(Int32(kCGMainMenuWindowLevelKey))) + 1
self.view.window!.opaque = true
self.view.window!.hidesOnDeactivate = true
let size = NSScreen.mainScreen()!.frame.size
self.view.window!.setFrame(CGRect(x: 0, y: 0, width: size.width, height: size.height), display:true)

但是,遗憾的是,同样的问题... FPS 和以前一样下降。

然后我想如果我把 window 的 size/position 弄乱了怎么办?所以我尝试将 window 向下移动,以便只有菜单栏可见,如下所示。 这成功了。我的 fps 不再下降。但显然它不是真正的全屏,因为菜单栏是可见的

self.view.window!.setFrame(CGRect(x: 0, y: 0, width: size.width, height: size.height-NSApplication.sharedApplication().mainMenu!.menuBarHeight), display:true)

事实上,事实证明,只需将 window 大小调整 1 点即可修复 fps 的下降。 因此,当您的 window 大小与屏幕大小匹配时,该错误必须与苹果所做的优化(多么讽刺)有关。

不相信我?这是来自 link 的引述。

OS X v10.6 and later automatically optimize the performance of screen-sized windows

因此,要解决此问题,我们需要做的就是将我们的 window 尺寸高度增大 1 点,这将阻止 OS X 尝试优化我们的 window。这将导致您的应用程序在顶部略微被截断,但 1 个像素根本不会引人注意。在最坏的情况下,您可以将节点位置调整 1 点来解决这个问题。


为方便起见,下面列出了 2 种解决方法。这两种解决方法都不会导致 FPS 下降。您的应用程序应该像在最大化 window 模式下一样运行。第一个解决方法是让您的应用全屏显示并在顶部显示菜单栏。第二种解决方法是让您的应用程序完全全屏显示,没有菜单栏。

解决方法 1:带菜单栏的全屏

self.view.window!.styleMask = NSBorderlessWindowMask
self.view.window!.level = Int(CGWindowLevelForKey(Int32(kCGMainMenuWindowLevelKey))) + 1
self.view.window!.opaque = true
self.view.window!.hidesOnDeactivate = true

let size = NSScreen.mainScreen()!.frame.size
self.view.window!.setFrame(CGRect(x: 0, y: 0, width: size.width, height: size.height-NSApplication.sharedApplication().mainMenu!.menuBarHeight), display:true)

解决方法 2:没有菜单栏的全屏

self.view.window!.styleMask = NSBorderlessWindowMask
self.view.window!.level = Int(CGWindowLevelForKey(Int32(kCGMainMenuWindowLevelKey))) + 1
self.view.window!.opaque = true
self.view.window!.hidesOnDeactivate = true
let size = NSScreen.mainScreen()!.frame.size
NSMenu.setMenuBarVisible(false)
self.view.window!.setFrame(CGRect(x: 0, y: 0, width: size.width, height: size.height+1), display:true)

如果由于某种原因这些解决方法不起作用,请尝试使用 window 的 size/position 进行更多操作。此外,您可能需要更改 window 级别,具体取决于您是否有其他视图,例如您的应用不应重叠的对话。 另外请记得向 Apple 提交错误报告。


关于 NSBorderlessWindowMask 的附加信息

这些解决方法使用 NSBorderlessWindowMask。这些类型的 windows 在键 window 改变时不接受键盘输入。因此,如果您的游戏使用键盘输入,您应该覆盖以下内容。参见 here

 class CustomWindow: NSWindow {
    override var canBecomeKeyWindow: Bool {
        get {
            return true
        }
    }
    override var canBecomeMainWindow: Bool {
        get {
            return true
        }
    }
}

更新:一些坏消息

在 Mac Book Air 上测试了这个解决方法,除非减去大约 100 个点(这显然非常明显),否则它不起作用。我不知道为什么。 andyvn22 的解决方案也是如此。我还注意到很少,也许每 60 次启动一次,所提供的变通办法根本不适用于 Mac Book Air。唯一的修复方法是重新启动应用程序。也许 Max Book Air 是个特例。可能缺少显卡与此问题有关。希望苹果能解决这个问题。我现在在支持全屏和不支持全屏之间左右为难。我真的希望用户能够进入全屏模式,但同时我不想让用户冒失去一半 FPS 的风险。

基于 Epic Byte 的非常有用的工作,我找到了一种更简单的方法来禁用 Apple 的全屏 "optimization"。您仍然可以使用 OS X 的内置全屏功能;您所要做的就是在 window 的委托中实现以下方法:

func window(window: NSWindow, willUseFullScreenContentSize proposedSize: NSSize) -> NSSize {
    return NSSize(width: proposedSize.width, height: proposedSize.height - 1)
}

不幸的是,这样加一个像素似乎不起作用,只能减一个,所以你失去了一行屏幕 space。不过,继续使用内置的全屏功能对我来说完全值得,尤其是在等待 Apple 修复其优化错误的时候。

我相信这个问题出现在所有使用 OpenGL 渲染的应用程序上。具有以下视频配置的 MPV(视频播放器)具有相同的问题:vo=opengl hwdec=no

Cpu 使用率 - 窗口化:平均 42%

Cpu 使用率 - 全屏(原生):62%

Cpu 使用率 - 全屏(non-native/in 应用程序):60%

Cpu 使用率 - 全屏(带有菜单栏的本机):45%

Cpu 使用率 - 离屏(使用原生全屏):95%

这也发生在带有 OpenGL 后端的 PPSSPP 上,除了增加 GPU 而不是 cpu 使用:

Gpu 使用率 - 窗口化:平均 20%

Gpu 使用率 - 全屏(带菜单栏):20%

Gpu 使用率 - 全屏(原生):35%

Gpu 使用率 - 离屏(使用原生全屏):90%

然而,当开发人员实现他们自己的 "Special" 全屏时,似乎不会出现此问题。在 Enter the Gungeon 的情况下,cpu 使用情况和 gpu 使用情况显示窗口化和 FS 之间没有区别。虽然我还没有时间检查他们是如何实现全屏的。

在 OSX 10.11.6

的 MBP 2015 年末 13' 上测试

如您所说,在全屏期间略有增加的使用率有点烦人,并且可能导致帧丢失,但最让我担心的是 CPU 和 GPU 在 openGL 应用程序中的使用率接近 100%在后台。 (注意:无论它在做什么,即使暂停时,它在 ppsspp 上都是 90%)。