New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/mobile: preserve eglContext when activity paused on Android #26962

Open
ntop001 opened this Issue Aug 13, 2018 · 1 comment

Comments

Projects
None yet
2 participants
@ntop001

ntop001 commented Aug 13, 2018

I have read some code in x/mobile/app, specially, android.c and android.go. Gomobile will create Surface and eglContext very time when application resumed:

// android.go
case w := <-windowRedrawNeeded:
    if C.surface == nil {
		if errStr := C.createEGLSurface(w); errStr != nil {
			return fmt.Errorf("%s (%s)", C.GoString(errStr), eglGetError())
		}
	}
	theApp.sendLifecycle(lifecycle.StageFocused)
	widthPx := int(C.ANativeWindow_getWidth(w))
	heightPx := int(C.ANativeWindow_getHeight(w))
	theApp.eventsIn <- size.Event{
		WidthPx:     widthPx,
		HeightPx:    heightPx,
		WidthPt:     geom.Pt(float32(widthPx) / pixelsPerPt),
		HeightPt:    geom.Pt(float32(heightPx) / pixelsPerPt),
		PixelsPerPt: pixelsPerPt,
		Orientation: orientation,
	}
	theApp.eventsIn <- paint.Event{External: true}
case <-windowDestroyed:
	if C.surface != nil {
		if errStr := C.destroyEGLSurface(); errStr != nil {
			return fmt.Errorf("%s (%s)", C.GoString(errStr), eglGetError())
		}
	}
	C.surface = nil
	theApp.sendLifecycle(lifecycle.StageAlive)	
// android.c
char* createEGLSurface(ANativeWindow* window) {
	char* err;
	EGLint numConfigs, format;
	EGLConfig config;
	EGLContext context;

	if (display == 0) {
		if ((err = initEGLDisplay()) != NULL) {
			return err;
		}
	}

	if (!eglChooseConfig(display, RGB_888, &config, 1, &numConfigs)) {
		return "EGL choose RGB_888 config failed";
	}
	if (numConfigs <= 0) {
		return "EGL no config found";
	}

	eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
	if (ANativeWindow_setBuffersGeometry(window, 0, 0, format) != 0) {
		return "EGL set buffers geometry failed";
	}

	surface = eglCreateWindowSurface(display, config, window, NULL);
	if (surface == EGL_NO_SURFACE) {
		return "EGL create surface failed";
	}

	const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
	context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);

	if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
		return "eglMakeCurrent failed";
	}
	return NULL;
}

char* destroyEGLSurface() {
	if (!eglDestroySurface(display, surface)) {
		return "EGL destroy surface failed";
	}
	return NULL;
}

This will cause the application lost eglContext when activity is paused(eg: press Home). A simple way to avoid this is to preserve the eglContext(only destroy Surface) and reuse it when needed. I have also make some research:

  1. Rethink how JNI and Map interact in lifecycle
  2. 100% NDK version of setPreserveEGLContextOnPause?
  3. ndk_helper/GLContext.cpp

PS: The sample app basic don't have the problem, cause it recreate the shader/index-buffer/vertex-buffer each time the window recreated, but it's impossible in a game with large amount of textures and buffers .

@gopherbot gopherbot added this to the Unreleased milestone Aug 13, 2018

@gopherbot gopherbot added the mobile label Aug 13, 2018

@ntop001

This comment has been minimized.

ntop001 commented Aug 14, 2018

I have write some code to test the idea, very simple, just declare the eglContext as a global variable.


EGLDisplay display = NULL;
EGLSurface surface = NULL;
EGLContext context = NULL;

char* createEGLSurface(ANativeWindow* window) {
    ...
    if (context == NULL) {
        const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
        context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
    }

	if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
		return "eglMakeCurrent failed";
	}
	return NULL;
}
// called on destroy, maybe need not to do this.
void terminate() {
    if(display != NULL){
        eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
        if (context != NULL) {
            eglDestroyContext(display, context );
        }
        if (surface != NULL) {
            eglDestroySurface(display, surface );
        }
        eglTerminate( display );
    }
    display = NULL;
    context = NULL;
    surface = NULL;
}

It works. But my code is not well tested (only on my own android device).
CC @eliasnaur

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment