-
-
Notifications
You must be signed in to change notification settings - Fork 205
Options for handling EGL Context loss on Android
Under certain conditions, Android may free the current EGL context, meaning all graphical data is no longer valid. This can occur due to certain events, for example:
- Application is in the background, and OS needs to free up resources
- Android OS setting changes that affect current EGL context, and require them to be recreated (such as changing system font in Android settings)
Recovery from an EGL context loss means reloading all image data from disk and recreating all textures. This part is fine, but where we run into problems is with textures created at runtime, such as via RenderTexture
, which cannot simply be reloaded from any media, since they only exist in memory.
In Axmol, an event is dispatched, named EVENT_RENDERER_RECREATED
, which triggers the recovery process, to reload all textures from disk. Unfortunately, there is no simple way to recover RenderTexture
texture data, since it does not exist on disk. If your application makes use of RenderTexture
, then you have a number of options available to you on how to handle this:
- Add an event listener for the
EVENT_RENDERER_RECREATED
, and on that event, re-create allRenderTexture
textures based on the data that you would have kept track of in your app. - Force your entire app to restart via the
AX_ENABLE_RESTART_APPLICATION_ON_CONTEXT_LOST
config option inPlatformMacros.h
. You can set this in yourCMakeLists.txt
via:
if(ANDROID)
add_definitions(-DAX_ENABLE_CACHE_TEXTURE_DATA=0 -DAX_ENABLE_RESTART_APPLICATION_ON_CONTEXT_LOST=1)
endif()
- To avoid a full app restart, add code to restart from the first scene. This is a little bit more involved, since your application would need to have dummy scene in the scene stack in order to use
Director::getInstance()->popToRootScene()
and then clear all references to texture data, since those references point to invalid data due to the EGL context loss. An example of this is below:
void AppDelegate::applicationDidFinishLoading()
{
...
...
auto* director = Director::getInstance();
// Add a dummy root scene. This should be your own scene inheriting from ax::Scene, but for
// the purposes of this example, we will just use "ax::Scene"
auto* rootScene = utils::createInstance<Scene>();
director->runWithScene(rootScene); // Start up with this scene
// Now, push your real initial scene on top of the root scene that is already in the stack
// which causes this new scene to be the current running scene
auto* initialScene = utils::createInstance<YourInitialScene>();
director->pushScene(initialScene);
#if AX_ENABLE_RESTART_APPLICATION_ON_CONTEXT_LOST == 0
// add a listener for EVENT_RENDERER_RECREATED
director->getEventDispatcher()->addCustomEventListener(EVENT_RENDERER_RECREATED, [director](EventCustom*) {
AXLOGI("GL Context Lost.");
// Pop all scenes back to the root scene
director->popToRootScene();
// Create a dummy scene that will be overriden after the first update of that scene
// and the sole purpose of this is to ensure all references to textures are released
// before we attempt to clear the texture cache
auto dummyScene = utils::createInstance<Scene>();
director->pushScene(dummyScene);
// Schedule a single callback on the first update, which is used
// to clear all references to the invalid texture data, and
// also to load our initial scene
dummyScene->scheduleOnce([director](float){
FontAtlasCache::purgeCachedData();
FontFNT::purgeCachedData();
director->getTextureCache()->removeAllTextures();
auto* scene = utils::createInstance<YourInitialScene>();
director->replaceScene(scene); // replace the current dummy scene with the actual scene we want to start with
}, 0.0f, "RESET_VIEW");
});
#endif
}