Обходной путь к потере контекста OpenGL, когда Android останавливается?

40

Документация Android гласит:

Есть ситуации, когда контекст рендеринга EGL будет потерян. Обычно это происходит, когда устройство просыпается после сна. Когда контекст EGL потерян, все ресурсы OpenGL (например, текстуры), связанные с этим контекстом, будут автоматически удалены. Для правильного рендеринга рендерер должен воссоздать все потерянные ресурсы, которые ему все еще нужны. Метод onSurfaceCreated (GL10, EGLConfig) является удобным местом для этого.

Но необходимость перезагрузить все текстуры в контексте OpenGL - это и боль, и ухудшает игровой процесс для пользователя при повторном входе в приложение после паузы. Я знаю, что «Angry Birds» как-то избегает этого, я ищу предложения о том, как сделать то же самое?

Я работаю с Android NDK r5 (версия CrystaX.) Я нашел это возможное решение проблемы, но я стараюсь избегать создания полной версии SDK.

Ник Готч
источник
режим сна и пробуждения относится к устройству, поэтому, пока пользователь просто приостанавливает вашу игру или переключает процессы, он может потерять любой контекст EGL.
Ali1S232

Ответы:

22

Остров Реплики имеет модифицированную версию GLSurfaceView, которая решает эту проблему (и работает с более ранними версиями Android). По словам Криса Прюетта :

По сути, я взломал оригинальный GLSurfaceView, чтобы решить очень специфическую проблему: я хотел перейти к различным действиям в своем приложении, не выбрасывая все свое состояние OpenGL. Основное изменение состояло в том, чтобы отделить EGLSurface от EGLContext и отбросить первое с помощью onPause (), но сохранить второе до тех пор, пока контекст не будет явно потерян. Реализация по умолчанию GLSurfaceView (которую я, кстати, не писал), выбрасывает все состояния GL, когда действие приостанавливается, и вызывает onSurfaceCreated (), когда оно возобновляется. Это означало, что когда в моей игре появлялось диалоговое окно, закрытие его вызывало задержку, потому что все текстуры должны были быть перезагружены.

Вы должны использовать GLSurfaceView по умолчанию. Если вы должны иметь ту же функциональность, что и моя, вы можете посмотреть на мою. Но то, что я делал, выявляло всевозможные ужасные ошибки драйверов в некоторых телефонах (см. Очень длинные комментарии в конце этого файла), и вы можете избежать всего этого, просто используя один из них по умолчанию.

Редактировать: я только что понял, что вы уже опубликовали ссылку на аналогичный взломать. Я не думаю, что есть какое-либо встроенное решение до соты. Replica Island - популярная игра, работающая на многих устройствах, и вам может помочь реализация Криса и комментарии.

Firas Assaad
источник
1
Попробовав различные подходы, чтобы обойти это, я решил, что лучшее решение - просто перекодировать приложение, чтобы воссоздать контекст. Это такой расточительный подход, но не похоже, что есть какое-то хорошее решение code.google.com/p/android/issues/detail?id=12774
Ник Готч
9

Я хотел бы добавить еще один ответ на этот вопрос, который был передан мне год или два назад Крисом Пруиттом (Остров Реплик, Рыцарь Ветра и т. Д.). Это особенно полезно здесь в 2013 году, поскольку setPreserveEglContextOnPause (true), похоже, не работает на 4.3. (Я могу ошибаться в этом, но именно так это выглядит сейчас, когда я обновляю код игры, последний раз затронутый в 2011 году).

По сути, дело в том, чтобы отсоединить ваш GLSurfaceView от иерархии представления от onPause () вашей активности. Так как он не находится в иерархии представления в момент запуска onPause (), контекст никогда не разрушается.

Таким образом, onPause () вашей Активности должен выглядеть так:

@Override
public void onPause() {
    view.setVisibility(View.GONE);
    super.onPause();
    ...
}

И вы восстанавливаете свой GLSurfaceView в иерархию не из onResume (), а из onWindowFocusChanged ():

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (hasFocus && view.getVisibility() == View.GONE) {
         view.setVisibility(View.VISIBLE);
    }
    ...
}

Обратите внимание, что вы никогда не вызываете функции GLSurfaceView onPause () и onResume () и что это официальный SDK GLSurfaceView, взломанная альтернативная версия не требуется.

Рувим Скрэттон
источник
На самом деле этот подход не работает. Крис Прюетт использовал его в сочетании с Unity. Возможно ли, что этот обходной путь не работает в нативных проектах? Но только не вызывает GLView.onPause (), кажется, работает !? Есть ли другие, которые могут это подтвердить?
sjkm
Он работал для меня, нацеленного на Android 2.2 OpenGl ES 2. Не уверен, как он работает на других устройствах. У меня телефон LG G2 D802. Кто-нибудь знает, если это общее решение?
Пиксель
7

<rant> Я потратил огромное количество времени на эту проблему, я пробовал много разных решений, и ни одно не работало до сегодняшнего дня, я думаю, что это одно из самых ужасных дизайнерских решений, которое я когда-либо видел, но из команды Android я не действительно удивлен </ rant>

Таким образом, решение состоит в том, чтобы переместить элемент eglContext вверх и сделать его статическим (глобальным), чтобы он не был уничтожен, тогда вам просто нужно проверить, является ли он нулевым или нет, прежде чем создавать его снова.

Пока что это решение, похоже, работает для нас, и нам все равно, будет ли оно работать на устройствах 2005 года.

Стефан Кокреомонт
источник
4

Используйте сотовый API. Есть опция сохранить ваш OGL-контекст. В противном случае вам необходимо перезагрузить контекст. Это не сложно и не больно.

Нужно понимать, что есть два случая (Android 2.1):

  • Пауза на экране: ваше приложение всегда переднее => доступно взломать
  • Приостановка приложения: есть другое переднее приложение => Нет решения

Примечание: старые андроид gpus не поддерживают мультиконтекст. Таким образом, контекст opengl теряется, когда вы переключаетесь на другое приложение => нет доступного решения (вы можете взломать, чтобы сохранить ваш контекст при паузе на экране).

Примечание 2: функция HoneyComb установлена ​​в PreREGEGLContextOnPause

Эллис
источник
1
Я портирую игру C ++ на Android NDK, которая не предназначена для простой перезагрузки контекста, так что, по крайней мере, для меня это несколько сложно. Помимо трудностей, перезагрузка текстур приведет к задержке, которой нет на других наших устройствах (iPhone, PSP, DS и т. Д.). Рад слышать, что сотовые исправляет это, но, к сожалению, нам нужна поддержка 2.1+
Ник Готч,
1
Это не отвечает на его вопрос вообще.
Нотлеш
1
Если вы не понимаете, вы не можете это сделать.
Эллис
2

Некоторые из ответов здесь разумны для раннего использования OpenGL ES на Android. Первые устройства GLES поддерживали только один контекст, поэтому GLSurfaceView был разработан для агрессивного сброса состояния. Убедить GLSurfaceView сделать иначе непросто.

Для более поздних версий Android (возможно, использующих GLES 2.x) лучшим решением будет использование простого SurfaceView и собственное управление EGL и потоками. Вы можете найти множество примеров GLES используются с простым SurfaceView в GRAFIKA , включая библиотеку простых классов для создания и уничтожения EGL контексты.

Хорошей практикой по-прежнему является выгрузка состояния, когда приложение переходит в фоновый режим, но для примера от Криса Пруетта - где приложение все еще находилось на переднем плане, но действие, в котором размещен GLSurfaceView, было отключено - разрывать смысла нет вниз по контексту.

Фадден
источник