Sprite Kit Серьезная проблема FPS в полноэкранном режиме на OS X
Я делаю довольно сложную игру для спрайтов. Недавно я добавил поддержку OS X. Я получаю 60 кадров в секунду всегда, независимо от того, как масштабируется моя игра при изменении размера окна (даже при изменении размера до максимального размера экрана). Тем не менее, в тот момент, когда мое приложение переходит в полноэкранный режим, частота кадров падает до 30-40 кадров в секунду и остается такой же? Но если я возьму курсор мыши и покажу строку меню, пока полноэкранный режим включен, частота кадров снова возрастет до 60 кадров в секунду!
Вы даже можете проверить эту ошибку, создав игру для спрайтов для Mac в XCode, используя шаблон по умолчанию. Вот снимки экрана, которые я сделал из шаблона игры по умолчанию для Mac.
Я предлагаю попробовать это самостоятельно, вам даже не нужно писать код, если вы используете стандартный шаблон спрайта Apple для OS X.
Макс. Окно (без проблем FPS: 59-60 кадров в секунду)
Полноэкранный режим (FPS падает до 30-40 FPS)
Полноэкранный режим с мышью вверху показывает строку меню (на удивление, без проблем FPS: 59-60 кадров в секунду)
У любого есть идея, что может быть причиной этой проблемы. Я не хочу выпускать свое приложение в полноэкранном режиме, если это означает, что пользователи потеряют производительность. Вы могли бы подумать, что полноэкранный режим может лучше оптимизировать рисование, но, видимо, все наоборот. Я управляю этим на Йосемити.
3 ответа
Хорошо, после нескольких недель изучения этой проблемы я нашел несколько обходных путей к этой проблеме. Прежде чем я начну, позвольте мне начать с объяснения моей настройки. Я использую NSViewController в раскадровке, которая содержит SKView. Я тестировал обходной путь на MacBook Pro (Retina, 15-дюймовый, начало 2013 г.), не знаю, подойдут ли обходные пути, представленные ниже, на других Mac. Я полагаю, что это должно произойти, когда я получу шанс, я протестирую и посмотрю, работают ли обходные пути ниже.
Итак, прежде чем я начну, давайте вспомним, в чем проблема. Проблема заключается в том, что при переходе вашего приложения в полноэкранный режим путем нажатия кнопки полноэкранного режима происходит значительное снижение FPS. Ниже описано, как включить полноэкранную кнопку:
self.view.window!.collectionBehavior = .FullScreenPrimary
Итак, я искал и нашел другой способ ввода полноэкранного режима с помощью этого кода:
self.view.enterFullScreenMode(NSScreen.mainScreen()!, withOptions: nil)
Но у меня все еще было значительное падение FPS. Имейте в виду, у меня не было проблем с fps в режиме развернутого окна или даже в полноэкранном режиме с видимой строкой меню! (см. фотографии в вопросе).
Тогда я попробовал менее высокоуровневый подход к переходу на весь экран. Я нашел руководство от Apple здесь
Используя часть кода из руководства, мне удалось войти в полноэкранный режим, установив размер окна в соответствии с размером дисплея и расположив окно над всем интерфейсом OS X. Код для этого выглядит следующим образом:
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. Таким образом, ошибка должна быть связана с оптимизацией (как ни парадоксально) яблоко, когда размер вашего окна соответствует размеру экрана.
Не веришь мне? Вот цитата из ссылки.
OS X v10.6 и выше автоматически оптимизирует производительность окон размером с экран
Таким образом, чтобы решить проблему, все, что нам нужно сделать, это увеличить высоту размера окна на 1 пункт, что не позволит OS X попытаться оптимизировать наше окно. Это приведет к тому, что ваше приложение будет слегка обрезано сверху, но 1 пиксель вообще не должен быть заметен. И в худшем случае вы можете отрегулировать положение своих узлов на 1 балл, чтобы учесть это.
Для вашего удобства ниже перечислены 2 обходных пути. Оба эти обходных пути не вызывают падения FPS. Ваше приложение должно работать так же, как и в режиме развернутого окна. Первый обходной путь переводит ваше приложение в полноэкранный режим и отображает строку меню вверху. Второй обходной путь переводит ваше приложение в полноэкранный режим без строки меню.
Обходной путь 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)
Если по какой-то причине эти обходные пути не работают, попробуйте поэкспериментировать с размером / положением окна. Также вам может потребоваться изменить уровень окна в зависимости от того, есть ли у вас другие представления, например диалоги, которые не должны перекрывать ваше приложение. Также, пожалуйста, не забывайте подавать отчеты об ошибках в Apple.
Дополнительная информация о NSBorderlessWindowMask
Эти обходные пути используют NSBorderlessWindowMask. Окна такого типа не принимают ввод с клавиатуры при изменении окна клавиш. Так что, если ваша игра использует ввод с клавиатуры, вы должны переопределить следующее. Смотрите здесь
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 - особый случай. Возможно, отсутствие видеокарты связано с проблемой. Надеюсь, Apple решит проблему. Я сейчас разрываюсь между поддержкой полноэкранного режима и не поддержкой полноэкранного режима. Я действительно хочу, чтобы пользователи могли входить в полноэкранный режим, но в то же время я не хочу рисковать тем, что пользователи теряют половину своего FPS.
Основываясь на очень полезной работе Epic Byte, я нашел еще более простой способ отключить полноэкранную "оптимизацию" Apple. Вы все еще можете использовать встроенную полноэкранную возможность OS X; все, что вам нужно сделать, это реализовать следующий метод в делегате вашего окна:
func window(window: NSWindow, willUseFullScreenContentSize proposedSize: NSSize) -> NSSize {
return NSSize(width: proposedSize.width, height: proposedSize.height - 1)
}
К сожалению, добавление одного пикселя, похоже, не работает таким образом, только вычитая один, так что вы теряете ряд экранного пространства. Тем не менее, мне стоит продолжать использовать встроенную полноэкранную функцию, особенно в ожидании того, как Apple исправит ошибку в оптимизации.
Я считаю, что эта проблема возникает во всех приложениях, использующих OpenGL для рендеринга. MPV (видеоплеер) со следующей конфигурацией видео имеет те же проблемы: vo=opengl hwdec=no
Использование процессора - оконное: в среднем 42%
Использование процессора - полноэкранный (родной): 62%
Использование ЦП - полноэкранный режим (не нативный / в приложении): 60%
Использование ЦП - полноэкранный (родной с строкой меню): 45%
Использование процессора - вне экрана (с использованием собственного полноэкранного режима): 95%
Это также происходит на PPSSPP с бэкэндом OpenGL, за исключением увеличения графического процессора вместо использования процессора:
Использование графического процессора - оконное: в среднем 20%
Использование графического процессора - полноэкранный режим (с панелью меню): 20%
Использование графического процессора - полноэкранный (родной): 35%
Использование Gpu - вне экрана (с использованием собственного полноэкранного режима): 90%
Эта проблема, однако, не возникает, когда разработчики реализуют свой собственный "Специальный" полноэкранный режим. В случае Enter the Gungeon, где использование процессора и использование процессора не показывает никакой разницы между оконным и FS. Хотя у меня еще не было времени проверить, как они реализовали полноэкранный режим.
Протестировано на MBP в конце 2015 года 13'на OSX 10.11.6
Немного увеличенное использование в полноэкранном режиме, как вы уже сказали, немного раздражает и может привести к кадровым сбоям, но меня больше всего беспокоит почти 100% использование как CPU, так и GPU в приложениях openGL в фоновом режиме. (Примечание: на ppsspp это 90% независимо от того, что он делает, даже если он приостановлен).