在OS X全屏模式下,Sprite Kit严重的帧率问题

11

我正在制作一个相当复杂的Sprite Kit游戏。最近我添加了对OS X的支持。无论窗口大小如何调整(即使调整到最大屏幕空间),我始终能获得60fps,但是一旦我让我的App进入“全屏”模式,帧率就会降至30-40fps并保持在那里。但是,如果我将鼠标光标移动到菜单栏上以启用全屏模式,则帧率会恢复到60fps!

您甚至可以通过使用Xcode为Mac创建Sprite Kit游戏来测试此错误。以下是我针对Mac默认游戏模板拍摄的屏幕截图。

我建议您自行尝试,如果您使用苹果的默认Sprite Kit模板,甚至不需要编写任何代码。

最大化窗口(没有帧速率问题:59-60 FPS) enter image description here

全屏模式(帧速率下降至30-40 FPS) enter image description here

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

有什么想法可能会导致这个问题吗?如果意味着用户会失去性能,我不想发布我的App的全屏模式。您会认为全屏模式可以更好地优化绘图,但显然相反。我在运行此操作时使用的是Yosemite。


你能在其他的 Mac 上重现这个问题吗?或者在你的 Mac 上进行一次干净的操作系统安装,例如从外部驱动器启动。同时,你可以试着在其他 Sprite Kit (演示/示例/开源) 应用程序中检测这个问题吗?如果不能,请勿轻易将其称为 Sprite Kit 中的 bug - CodeSmile
3
我也遇到了同样的问题,我会提交一个错误报告。 - pascalbros
感谢分享 ;) - pascalbros
我遇到了完全相同的问题(除了我注意到当我的应用程序显示上下文菜单时,而不是菜单栏时FPS会增加)。很奇怪,请告诉我是否有解决方法! - andyvn22
@andyvn22 我发布了一些解决方法。 - Epic Byte
显示剩余5条评论
3个回答

10

好的,经过数周的研究,我已经找到了一些解决这个问题的方法。在开始之前,让我先解释一下我的设置。我正在使用一个Storyboard中的NSViewController来容纳SKView。我已经在MacBook Pro (Retina, 15-inch, Early 2013)上测试了这个解决方法,但我不知道我以下呈现的解决方法是否适用于其他Mac。我相信应该是可以的,等有机会时我会测试并查看以下解决方法是否可行。

因此,在开始之前,让我们回顾一下问题所在。问题在于通过点击全屏按钮使您的应用程序进入全屏模式会导致FPS大幅下降。以下是如何启用全屏按钮:

self.view.window!.collectionBehavior = .FullScreenPrimary

然后我搜索了一下,并找到了另一种使用以下代码进入全屏的方法:

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

但是我的FPS仍然大幅下降。请记住,在最大化窗口模式或即使菜单栏可见的全屏模式下,我没有FPS问题!(请参见问题中的图片)。

于是我尝试了一种不太高级的全屏模式方法。我在苹果公司的指南中找到了一份指南here

使用指南中的一些代码,我将窗口大小设置为显示器的大小,并将窗口定位在所有OS X UI上方,以进入全屏模式。其代码如下:

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 就像之前一样下降了。

所以我想,如果我改变窗口的大小/位置会怎么样。于是我试着将窗口向下移动,只留下菜单栏如下所示。这个方法奏效了。FPS 不再下降了。但显然它并不是真正的全屏,因为菜单栏仍然可见。

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

事实上,只需将窗口大小调整1个点,即可解决fps下降的问题。因此,这个bug必须与苹果在窗口大小与屏幕大小匹配时进行的优化(多么讽刺)有关。
不相信吗?以下是链接中的一句话:
“OS X v10.6及更高版本会自动优化屏幕大小的窗口性能。”
因此,要解决这个问题,我们只需要将窗口的高度增加1个点,这样就可以防止OS X尝试优化我们的窗口。这会导致您的应用程序在顶部略微被剪裁,但1个像素根本不会引起注意。在最坏的情况下,您可以将您的节点位置调整1个点来解决这个问题。
为了方便起见,以下列出了两种解决方法。这两种解决方法都不会导致FPS下降。您的应用程序应该与在最大化窗口模式下的运行方式相同。第一种解决方法将您的应用程序置于全屏状态,并在顶部显示菜单栏。第二种解决方法将您的应用程序置于完全全屏模式,没有菜单栏。
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)

如果由于某种原因这些解决方法不起作用,尝试调整窗口的大小/位置。此外,根据您是否有其他视图(例如对话框)不应重叠到应用程序上,请更改窗口级别。还请记得向苹果提交错误报告。


关于NSBorderlessWindowMask的附加信息

这些解决方法使用了一个NSBorderlessWindowMask。这种类型的窗口在键盘窗口发生变化时不接受键盘输入。因此,如果您的游戏使用键盘输入,您需要覆盖以下内容。请参见这里
 class CustomWindow: NSWindow {
    override var canBecomeKeyWindow: Bool {
        get {
            return true
        }
    }
    override var canBecomeMainWindow: Bool {
        get {
            return true
        }
    }
}

更新:一些不好的消息

我在 MacBook Air 上尝试了这个解决方法,但除非减少约100个点(这显然非常明显),否则它将无法正常工作。我不知道为什么。andyvn22的解决方案也是一样。我还注意到,很少情况下,可能是每60次启动中的一次,提供的解决方案根本无法在 MacBook Air 上正常工作。唯一的修复方式是重新启动应用程序。也许 Max Book Air 是一个特例。也许缺乏图形卡与这个问题有关。希望苹果能解决这个问题。现在,我在支持全屏和不支持全屏之间犹豫不决。我真的希望用户能够进入全屏模式,但同时我又不想冒着让用户失去一半FPS的风险。


1
非常感谢您的这篇文章!我为此疯狂了。 我正在使用C++ SDK(与cocos2d-x游戏引擎一起),将高度增加1对我解决了问题。我在运行OSX 10.11.1的iMac(21.5英寸,2012年末)上进行了测试。 - Sheado
1
@Sheado 相信我,我知道你的感受。这个问题可能已经在最新版本的Xcode / OS X中得到解决。等我有时间检查后,我会更新答案并包含我的发现。 - Epic Byte
嗨,你不会偶然遇到更永久的解决方案了吧?我也遇到了同样的问题... - Todd
1
@Todd,你记得将你的OS X部署目标设置为最新版本吗?很容易忘记,而Sprite Kit会根据这个值在内部进行更改。 - Epic Byte
@EpicByte 为你的努力点赞!不幸的是,这似乎没有丝毫影响。 - Todd
显示剩余3条评论

4

根据Epic Byte的非常有用的工作,我发现了一种更简单的方法来禁用苹果的全屏“优化”。您仍然可以使用OS X内置的全屏功能;您只需要在窗口的代理中实现以下方法:

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

很不幸,增加一个像素似乎不起作用,只能减少一个像素,因此您会失去一行屏幕空间。对我来说完全值得,因为我可以继续使用内置的全屏功能,特别是在等待苹果修复他们的优化错误时。


非常棒的答案。感谢您,我可能会选择这个解决方案。您是对的,+1不起作用,但-1可以(苹果必须限制高度)。不过,我有点担心这个解决方案是否安全。在全屏模式下调整窗口大小似乎有点hacky。但另一方面,为什么还会存在这种方法呢。 - Epic Byte
1
没错,这就是这个方法的全部目的,所以我相信在这个意义上它是“安全”的。不能保证它会继续绕过“优化”,但希望他们很快就会修复这个问题! - andyvn22
1
在 MacBook Air 上进行了测试,我们的两个解决方案都无法正常工作,除非大约减去 100 点。真是奇怪... - Epic Byte

0

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

CPU使用率 - 窗口模式:平均42%

CPU使用率 - 全屏模式(本机):62%

CPU使用率 - 全屏模式(非本机/应用内):60%

CPU使用率 - 全屏模式(带菜单栏的本机):45%

CPU使用率 - 离屏模式(使用本机全屏):95%

除了增加GPU使用率而不是CPU使用率外,PPSSPP与OpenGL后端也存在此问题:

GPU使用率 - 窗口模式:平均20%

GPU使用率 - 全屏模式(带菜单栏):20%

GPU使用率 - 全屏模式(本机):35%

GPU使用率 - 离屏模式(使用本机全屏):90%

然而,在开发人员实现自己的“特殊”全屏时,似乎不会出现此问题。在Enter the Gungeon的情况下,窗口模式和FS之间的CPU使用率和GPU使用率没有区别。尽管我还没有时间检查他们如何实现全屏。

在OSX 10.11.6上测试了MBP Late 2015 13英寸

全屏模式下略微增加的使用率确实有点烦人,正如你所说,可能会导致帧数下降,但最让我担心的是在后台运行时OpenGL应用程序几乎占用了CPU和GPU的100%。(注意:在ppsspp上无论它在做什么,甚至在暂停时也是90%)。


在2023年的macOS 13.4 Ventura上仍然看到这个确切的问题。 - Jean-Michaël Celerier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接