Android 前后台切换与OpenGL(EGL)创建销毁的周期

来源:互联网 发布:软件培训班 编辑:程序博客网 时间:2024/06/05 05:41

EGL的创建是这样的。

/** * Creates an EGL rendering context and all associated elements */void CreateEGL(EGLNativeWindowType window, EGLDisplay* outDisplay, EGLContext* outContext, EGLSurface* outSurface, EGLConfig*  outConfig){*outDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);if (*outDisplay == EGL_NO_DISPLAY){ALog_A(0, "CreateEGL failed EGL unable to eglGetDisplay");}if (!eglInitialize(*outDisplay, NULL/*major*/, NULL/*minor*/)){ALog_A(0, "CreateEGL failed EGL unable to eglInitialize");}EGLint numConfigs;// Here specify the attributes of the desired configuration.// Below, we select an EGLConfig with at least 8 bits per color// component compatible with on-screen windowsconst EGLint configAttribs[] ={   EGL_DEPTH_SIZE,      16,   EGL_RED_SIZE,        8,   //   5,   EGL_GREEN_SIZE,      8,   //   6,   EGL_BLUE_SIZE,       8,   //   5,   EGL_ALPHA_SIZE,      8,   //   0,   EGL_STENCIL_SIZE,    8,   EGL_SURFACE_TYPE,    EGL_WINDOW_BIT,   EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,   EGL_NONE};/* Here, the application chooses the configuration it desires. In this * sample, we have a very simplified selection process, where we pick * the first EGLConfig that matches our criteria */eglChooseConfig(*outDisplay, configAttribs, outConfig, 1, &numConfigs);if (numConfigs < 1){ALog_A(0, "CreateEGL failed no config match eglChooseConfig");}const EGLint surfaceAttrs[] ={EGL_RENDER_BUFFER,EGL_BACK_BUFFER,EGL_NONE};*outSurface = eglCreateWindowSurface(*outDisplay, *outConfig, window, surfaceAttrs);if (*outSurface == EGL_NO_SURFACE){ALog_A(0, "CreateEGL failed EGL unable to eglCreateWindowSurface");}const EGLint contextAttribs[] ={EGL_CONTEXT_CLIENT_VERSION, 2,EGL_NONE};*outContext = eglCreateContext(*outDisplay, *outConfig, EGL_NO_CONTEXT, contextAttribs);if (*outContext == EGL_NO_CONTEXT){ALog_A(0, "CreateEGL failed EGL unable to eglCreateContext");}if (!eglMakeCurrent(*outDisplay, *outSurface, *outSurface, *outContext)){ALog_A(0, "CreateEGL failed EGL unable to eglMakeCurrent");}//eglSwapInterval(*display, 1);}

    我们可以看到,我们需要一个EGLNativeWindowType window,然后我们会创建display,context,config,surface。其中,display是和显示器相关的。context和config相关,surface是和window相关的。最为关键的是最后一句eglMakeCurrent就整合了这一切。以后的OpenGL指令才能正确生效。


   当android应用被切换到后台的时候,会执行一系列的回调函数,onWindowDestroyed,onPause,onStop,恢复的时候又会执行onResume,onWindowCreated,onResized等等。期间在后台的时候,EGLNativeWindowType window会被销毁掉,也就会导致eglMakeCurrent失效,OpenGL不可用状态。恢复的时候我们需要重建EGL这个初始化的过程。


    通过阅读源码,我们会看到NDK提供的native-activity中,会在onWindowDestroyed销毁整个EGL也就是包括了display,context,config,surface。然后在onWindowCreated时候再次创建EGL相关的所有元素。还可以阅读GLSurfaceView.java为我们封装EGL生命周期的view层。会发现同样的处理流程,就是系统的window发生变化的时候,选择了销毁EGL相关的一切。然后甚至在resized时候,对,window又发生变化了都会销毁EGL。然后在重建。


    从EGL相关的几个元素display,context,config,surface,我们会发现似乎之后surface是和系统的window密切相关的。也就是eglCreateWindowSurface和ANativeWindow_setBuffersGeometry函数使用了window变量。其它的display,context,config都是不相关的。尤其是context,存放了OpenGL当前的状态。如果销毁,那么我们就丢失了OpenGL所有的状态,在恢复的时候我们需要重新恢复这些状态。否则就会造成绘制错误各种问题。


    所以,我们确定去实现2点。第一,在resized之后也就是当window稳定之后再去初始化EGL,防止window的变化导致需要修正EGL。我们看到resized回调总是在windowCreated之后调用的。第二,就是当window变化的时候,我们选择只销毁surface,其他的都保持不变,在恢复的时候重建surface,然后EGL使用新的surface即可。经过测试,这个思路是可以的。并且程序切换前后台速度要快了很多。因为context没有被销毁,OpenGL的绘制状态全部都被保存了。我们需要增加一个ResetSurface函数,在恢复的时候调用。用来删除旧的surface并设置新的surface。如下:

static void ResetSurface(EGLNativeWindowType window, EGLDisplay display, EGLContext context, EGLConfig config, EGLSurface* surface){    if (*surface != EGL_NO_SURFACE)    {        eglDestroySurface(display, *surface);    }const EGLint surfaceAttrs[] ={EGL_RENDER_BUFFER,EGL_BACK_BUFFER,EGL_NONE};*surface = eglCreateWindowSurface(display, config, window, surfaceAttrs);if (!eglMakeCurrent(display, *surface, *surface, context)){ALog_A(0, "ResetSurface failed EGL unable to eglMakeCurrent");}}


    当然了,display,context,config,这些我们需要保存下来。eglCreateWindowSurface和eglMakeCurrent需要使用到。包括ANativeWindow_setBuffersGeometry使用到的format也是需要保存下来的。



0 0
原创粉丝点击