解决Android暂停时丢失OpenGL上下文的方法?


40

Android文档说:

在某些情况下,EGL渲染上下文将丢失。通常在设备进入睡眠状态后唤醒时会发生这种情况。当EGL上下文丢失时,与该上下文关联的所有OpenGL资源(例如纹理)将被自动删除。为了保持正确的渲染,渲染器必须重新创建仍然需要的任何丢失的资源。onSurfaceCreated(GL10,EGLConfig)方法是执行此操作的便捷位置。

但是在暂停后重新进入应用程序时,必须在OpenGL上下文中重新加载所有纹理既痛苦又伤及用户的游戏体验。我知道“愤怒的小鸟”会以某种方式避免这种情况,我正在寻找有关如何实现相同目标的建议?

我正在使用Android NDK r5(CrystaX版本。)确实找到了解决问题的方法,但是我试图避免构建整个自定义SDK版本。


睡眠和唤醒是指设备,因此当用户只是暂停您的游戏或切换进程时,它应该会释放任何EGL上下文。
Ali1S232

Answers:


22

副本岛具有GLSurfaceView 的修改版本,可以解决此问题(并与早期的Android版本一起使用)。根据克里斯·普鲁特Chris Pruett)的说法:

基本上,我修改了原始的GLSurfaceView来解决一个非常特定的问题:我想在应用程序中进行不同的活动,而又不放弃所有OpenGL状态。主要更改是将EGLSurface与EGLContext分开,并将前者丢弃在onPause()上,但保留后者直到上下文被明确丢失为止。GLSurfaceView的默认实现(顺便说一下,我没有写过),在活动暂停时将丢弃所有GL状态,并在恢复活动时调用onSurfaceCreated()。这意味着,当我的游戏中弹出一个对话框时,由于必须重新加载所有纹理,关闭该对话框会导致延迟。

您应该使用默认的GLSurfaceView。如果您必须具有与我的功能相同的功能,则可以查看我的功能。但是,我所做的事情暴露了某些手机中的各种可怕的驱动程序错误(请参阅该文件末尾的很长的注释),并且您可以通过仅使用默认值来避免所有麻烦。

编辑:我刚刚意识到您已经将链接发布到了类似的hack。我认为在蜂窝之前没有任何内置的解决方案。复制岛是一款在许多设备上均可运行的流行游戏,您可能会发现Chris的实现和注释很有用。


1
在尝试了各种方法来避免这种情况后,我决定最好的解决方案是重新编码应用程序以重新创建上下文。这是一种浪费的方法,但看起来似乎没有什么好的解决方案。code.google.com
p/

9

我想为此再加上一个答案,这个答案是Chris Pruett(Replica Island,Wind-Up Knight等)传递给我一两年的。这在2013年特别有用,因为setPreserveEglContextOnPause(true)在4.3上似乎不起作用。(对此我可能是错的,但是当我更新2011年最后触及的游戏代码时,这就是现在的样子)。

基本上,诀窍是将您的GLSurfaceView从Activity的onPause()的视图层次结构中分离出来。由于onPause()运行时它不在视图层次结构中,因此上下文永远不会被破坏。

因此,您的Activity的onPause()应该如下所示:

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

然后,您不是从onResume()而是从onWindowFocusChanged()将GLSurfaceView还原到层次结构中:

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

请注意,您永远不会调用GLSurfaceView的onPause()和onResume(),并且这是官方的SDK GLSurfaceView,不需要经过修改的替代版本。


实际上,这种方法似乎行不通。克里斯·普鲁特(Chris Pruett)将它与Unity结合使用,这种解决方法是否可能不适用于本机项目?但是,只有不调用GLView.onPause()才能起作用!还有其他人可以确认这一点吗?
sjkm

它对我适用于Android 2.2 OpenGl ES2。不知道它在其他设备上的性能如何。我有LG G2 D802手机。有谁知道这是否是一般解决方案?
Pixel 2016年

7

<rant> 我在这个问题上花了很多时间,我尝试了许多不同的解决方案,直到今天都没有奏效,我认为这是我什至见过的最糟糕的设计决定之一,但来自Android团队,我不是真的很惊讶 </ rant>

因此,解决方案是向上移动eglContext成员并将其设为静态(全局),这样它就不会被破坏,那么您只需在再次创建它之前检查它是否为null。

到目前为止,该解决方案似乎对我们有效,并且我们不在乎它是否在2005年的设备上崩溃。


4

使用蜂窝API。有一个选项可以保留您的OGL上下文。否则,您需要重新加载上下文。这既不困难也不痛苦。

您需要了解两种情况(Android 2.1):

  • 屏幕暂停:您的应用程序始终是最主要的应用程序=>可用的hack
  • 应用程序暂停:还有另一个前端应用程序=>没有解决方案

注意:旧的Android GPU不支持多上下文。因此,当您切换到另一个应用程序时,opengl上下文会丢失=>没有可用的解决方案(您可以破解以在屏幕暂停时保留上下文)。

注意2:HoneyComb函数被设置为PreserveEGLContextOnPause


1
我正在将C ++游戏移植到Android NDK上,该游戏并非旨在轻松地重新加载上下文,因此至少对我而言有点困难。除了困难之外,重新加载纹理会带来其他设备(iPhone,PSP,DS等)不存在的延迟。很高兴听到蜂窝修复此问题,但不幸的是,我们需要支持2.1+
Nick Gotch

1
这根本没有回答他的问题。
notlesh 2011年

1
如果您不了解,您将无法做到。
埃利斯,

2

对于在Android上早期使用OpenGL ES而言,这里的一些答案是合理的。最初的GLES设备仅支持单个上下文,因此GLSurfaceView旨在主动丢弃状态。令人信服的GLSurfaceView做到这一点并不容易。

对于较新的Android版本(可能使用GLES 2.x的任何版本),最好的答案是使用普通的SurfaceView并进行自己的EGL和线程管理。你可以找到GLES的多个例子使用在一个普通的SurfaceView Grafika,包括简单的类来创建和销毁EGL上下文库。

在应用程序进入后台时卸载状态仍然是一种好习惯,但是以Chris Pruett为例(该应用程序仍在前台,但是托管GLSurfaceView的Activity已被关闭),撕裂没有任何价值在上下文中。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.