11

I'm making a fairly complex sprite kit game. I recently added support for OS X. I get 60 fps always, regardless of how my game is scaled when the window is resized (even when resized to max screen space). However, the moment I make my App enter "Full Screen," the fps drops to 30-40 fps and stays that way? But if I take my mouse cursor and reveal the menu bar while full screen is enabled, the fps goes back up to 60 fps!

You can even test this bug by making a sprite kit game for mac in Xcode using the default template. Here are the screen shots I took of the default game template for mac.

I suggest trying it out for yourself, you don't even have to write any code if you use Apple's default sprite kit template for OS X.

Max Window (No FPS Problems: 59-60 FPS) enter image description here

Full Screen Mode (FPS Drops to 30-40 FPS) enter image description here

Full Screen Mode With Mouse At Top Revealing Menu Bar (Surprisingly, NO FPS Issues: 59-60 FPS) enter image description here

Anyone have any idea what might be causing this problem. I don't want to release my App with full screen mode if it means users will lose performance. You would think full screen mode could better optimize drawing but apparently it's quite the opposite. I'm running this on Yosemite.

Epic Byte
  • 33,840
  • 12
  • 45
  • 93
  • can you reproduce this behavior on other macs (or a fresh os install on your mac, ie boot from ext. drive) and with other sprite kit (demo/sample/open source) apps? If not I'd hesitate to call this a Sprite Kit *bug*. – CodeSmile Jan 04 '15 at 10:28
  • @LearnCocos2D Just tested it on iMac also running Yosemite. Same problem. And the computer I originally tested this on was a retina mac book pro. It only takes a couple minutes to test, I would be curious to see if other people are experiencing the same bug. But regardless, do you have any idea what I could try to fix this issue? Maybe full-screen mode is enabling some window or view property that doesn't play well with sprite kit. – Epic Byte Jan 04 '15 at 16:51
  • well, if you can bring up the menu by moving the mouse to the top, you are using the regular app fullscreen mode. What the game ought to do/use is the exclusive fullscreen mode where only the game renders stuff (ie like most games implement fullscreen). no idea how to do so with sk. – CodeSmile Jan 04 '15 at 17:11
  • @LearnCocos2D I know I'm using the standard app full screen mode. That's the bug I am facing, sprite kit does not work well in full-screen mode on Yosemite (when it should and obviously can). Sprite kit itself doesn't support full screen, it just allows you to specify the size of the skview/scene. And the skview adjusts to the size of the window. The issue is that when the app is in full screen mode, for some reason the fps drops (yet not when the menu bar is revealed). I'm still looking for a solution perhaps related to the rendering mode of the window. – Epic Byte Jan 04 '15 at 19:48
  • 3
    Same problem here, i'll open a bug report. – pascalbros Feb 03 '15 at 10:42
  • @pakizip Thanks, it's good to see someone actually reproduce this issue rather than rushing to close the question. I have additional information that I will edit into the question later about possible workarounds and other full-screen problems. – Epic Byte Feb 03 '15 at 14:10
  • I've got the exact same issue (except I noticed the increase in FPS when my app displayed a contextual menu, rather than the menu bar). Bizarre. Please do let me know if you have a workaround! – andyvn22 Mar 31 '15 at 17:19
  • @pakizip I posted some workarounds. – Epic Byte Apr 02 '15 at 17:59
  • @andyvn22 I posted some workarounds. – Epic Byte Apr 02 '15 at 18:00

3 Answers3

10

Ok, after weeks looking into this issue I have found some workarounds to this issue. Before I begin, let me start by explaining my setup. I'm using an NSViewController in a storyboard which holds an SKView. I've tested the workaround on MacBook Pro (Retina, 15-inch, Early 2013), I have no idea if the workarounds I present below will work on other Macs. I believe it should, when I get the chance I will test and see if the workarounds below work.

So before I begin, lets recap what the issue is. The issue is that making your App enter fullscreen by clicking the fullscreen button causes a massive drop in FPS. Below is how you enable the fullscreen button:

self.view.window!.collectionBehavior = .FullScreenPrimary

So then I searched around and found a different way of entering fullscreen using this code:

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

But I still had a massive drop in FPS. Keep in mind, I had no fps issues when in maximized window mode or even full screen with the menu bar visible! (see pictures in question).

So then I tried a less high-level approach to going full screen. I found a guide by Apple here

Using some of the code from the guide, I managed to enter fullscreen by setting the window size to the size of the display, and positioning the window above all OS X UI. The code for this is as follows:

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)

But, sadly, same problem... The FPS just dropped just like before.

So then I thought what if I mess with the size/position of the window. So I tried moving the window down so that just the menu bar was visible as shown below. AND THIS WORKED. I no longer had a drop in fps. But obviously it's not truly fullscreen because the menu bar is visible

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

In fact, as it turns out, just be adjusting the window size by 1 point fixes the drop in fps. Thus the bug must be related to an optimization (how ironic) apple does when your window size matches the screen size.

Don't believe me? Here is a quote from the link.

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

So to fix the issue all we need to do is make our window size height 1 point larger which will prevent OS X from trying to optimize our window. This will cause your App to get slightly cut off on the top but 1 pixel shouldn't be noticeable at all. And in the worst case you could adjust your nodes position by 1 point to account for this.


For your convenience, listed below are the 2 workarounds. Both of these workarounds do not cause any drop in FPS. Your App should function just like it did in maximized window mode. The first workaround puts your App in fullscreen and displays the menu bar at the top. The second workaround puts your App in complete full screen with no menu bar.

Workaround 1: Fullscreen with Menu Bar

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)

Workaround 2: Fullscreen with no Menu Bar

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)

If for some reason these workarounds don't work, try messing some more with the size/position of the window. Also you may need to change the window level depending on if you have other views such as dialogues that your App should not overlap. Also please remember to file bug reports with Apple.


Additional Info About NSBorderlessWindowMask

These workarounds use an NSBorderlessWindowMask. These type of windows do not accept keyboard input when the key window changes. So if your game uses keyboard input, you should override the following. See here

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

Update: Some bad news

Tested this workaround on Mac Book Air, and it did not work unless about 100 points were subtracted (which obviously is extremely noticeable). I have no idea why. Same goes for andyvn22's solution. I also have noticed that very rarely, perhaps once every 60 launches the workarounds provided simply don't work on the Mac Book Air at all. And the only way to fix is to relaunch the App. Maybe the Max Book Air is a special case. Maybe lack of a graphics card has to do with the issue. Hopefully Apple gets the issue sorted out. I'm now torn between supporting fullscreen and not supporting fullscreen. I really want users to be able to enter fullscreen mode, but at the same time I don't want to risk users loosing half their FPS.

Community
  • 1
  • 1
Epic Byte
  • 33,840
  • 12
  • 45
  • 93
  • 1
    Thanks so much for this post! I've been going crazy over this. I'm using the C++ sdk (with the cocos2d-x game engine) and adding 1 to the height fixed the problem for me. I tested on an iMac (21.5-inch, Late 2012) running OSX 10.11.1 – Sheado Nov 24 '15 at 03:40
  • 1
    @Sheado Trust me I know how you feel. This issue may have been fixed in the latest release of Xcode/OS X. When I get a chance to check I will update the answer to include my findings. – Epic Byte Nov 24 '15 at 04:48
  • Hi, don't suppose you came across a more permanent solution, did you? I'm having the same problems... – Todd Apr 05 '16 at 18:27
  • @Todd Apple has informed me that they fixed this issue in the latest release. I was planning on updating this post revealing this but never got the chance. Did you try running on the latest Xcode, latest OS X etc.? – Epic Byte Apr 05 '16 at 18:32
  • @Todd I personally haven't got a chance to work on my game in a long time so I have no idea if they really fixed this. – Epic Byte Apr 05 '16 at 18:33
  • @EpicByte - yup, I'm running El Capitan. I'm beginning to wonder if my MacBook Pro 17" is too old for this, but I really don't have any problems running graphics heavy software... – Todd Apr 05 '16 at 18:50
  • 1
    @Todd did you remember to set your OS X Deployment Target to the latest version. It's easy to forget and Sprite kit changes internally depending on this value. – Epic Byte Apr 05 '16 at 19:34
  • @EpicByte a vote up for effort! Unfortunately, it doesn't appear to make a bit of difference. – Todd Apr 05 '16 at 20:05
4

Based on Epic Byte's very helpful work, I found an even easier way to disable Apple's full screen "optimization". You can still use OS X's built in full screen capability; all you have to do is implement the following method in your window's delegate:

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

Unfortunately adding one pixel doesn't seem to work this way, only subtracting one, so you lose a row of screen space. Totally worth it to me, though, to continue using the built-in full screen function, especially while just waiting for Apple to fix their optimization bug.

andyvn22
  • 14,696
  • 1
  • 52
  • 74
  • Awesome answer. Thanks for this, I might actually end up going with this solution. You are right the + 1 doesn't work but the -1 does (Apple must cap the height). I'm a little worried if this solution is safe though. Adjusting the window size while in full screen mode seems a little hacky. But then again why else would this method exist. – Epic Byte Apr 11 '15 at 17:01
  • 1
    Exactly—that's the whole purpose of this method, so I'm sure it's "safe" in that sense. No guarantees it'll continue to bypass the "optimization", but hopefully they'll fix that soon enough anyway! – andyvn22 Apr 11 '15 at 20:01
  • 1
    Tested on Mac Book Air, and both of our workarounds did not work unless roughly 100 points were subtracted. bizarre... – Epic Byte Apr 12 '15 at 20:44
0

I believe this problem occurs on all apps using OpenGL to render. MPV (video player) with the following video config has the same issues: vo=opengl hwdec=no

Cpu usage - windowed: average 42%

Cpu usage - fullscreen (native): 62%

Cpu usage - fullscreen (non-native/in app): 60%

Cpu usage - fullscreen (native with menu bar): 45%

Cpu usage - offscreen (using native full screen): 95%

This also occurs on PPSSPP with OpenGL backend except with increased GPU instead of cpu usage:

Gpu usage - windowed: average 20%

Gpu usage - fullscreen (with menu bar): 20%

Gpu usage - fullscreen (native): 35%

Gpu usage - offscreen (using native full screen): 90%

This problem however does not seem to occur when developers implement their own "Special" fullscreen. In the case of Enter the Gungeon, where cpu usage and gpu usage shows no difference between windowed and FS. Although I haven't had time to check how they've implemented fullscreen yet.

Tested on MBP Late 2015 13' on OSX 10.11.6

The slightly increased usage during fullscreen is a bit annoying as you've said and can cause framedrops, but what's worrying me the most is the near 100% usage of both CPU and GPU in openGL applications when in background. (Note: it's 90% on ppsspp no matter what it's doing, even when paused).

kokowang
  • 1
  • 1