androidandroid-4.0-ice-cream-sandwichnativewindowegl

eglCreateWindowSurface on ICS, and switching from 2D to 3D


I'm trying to make an NDK-based game work on Android ICS. It worked fine on Honeycomb and Gingerbread.

The game uses some 2D rendering, some 3D rendering, switching between the two at various stages of execution. (This is not negotiable due to third-party code.) We're using ANativeWindow_lock()/ANativeWindow_unlockAndPost() for the 2D rendering, and eglCreateWindowSurface()/gl*()/eglSwapBuffers() for the 3D rendering.

On Honeycomb and Gingerbread this all worked fine. On ICS, eglCreateWindowSurface() fails with the following messages in the log:

E/SurfaceTexture( 1765): [com.fnord/com.fnord.MyActivity] connect: already connected (cur=2, req=1)
E/libEGL  ( 5466): EGLNativeWindowType 0x29e9b8 already connected to another API
E/libEGL  ( 5466): eglCreateWindowSurface:374 error 300b (EGL_BAD_NATIVE_WINDOW)
E/libEGL  ( 5466): call to OpenGL ES API with no current context (logged once per thread)

Looking at the source code it seems pretty obvious that eglCreateWindowSurface() is failing because something's got the native window open for 2D rendering and it won't let me change it to 3D without somehow releasing the surface first. However, the ANativeWindow API doesn't seem to have any obvious way to do this.

Has anyone else run into this, and what's the solution?

Updated

So I've rewritten my 2D rendering code to use OpenGL primitives instead (upload backbuffer to a texture, render texture via a pair of triangles, swapbuffers). This works up to a point. Now what's happening is that the 2D rendering works fine; then I destroy the surface, create a new one in preparation for 3D rendering, and the second call to eglCreateWindowSurface() fails. This time, with:

E/SurfaceTexture( 1869): [com.fnord/com.fnord.MyActivity] connect: already connected (cur=1, req=1)

...and it throws an EGL error of AEGL_BAD_NATIVE_WINDOW.

Note that the new surface is created with precisely the same attributes as the old one. I've even tried making sure I call eglTerminate() / eglInitialize() between the two calls to eglCreateWindowSurface().

Is it the case that I can use each ANativeWindow precisely once? If so, doesn't that fail Khronos' EGL conformance tests? Is there a way to get NativeActivity to recreate the window, then?

Updated updated

Turns out that the last problem was caused by me failing to call eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) before destroying the context and surface. This was causing the destruction to be delayed until the thread unbound the context and surface, which of course it wasn't, so causing the call to eglCreateWindowSurface() to legitimately fail (you can't have two window surfaces on the same native window).

This is a bit counterintuitive, because one would assume that calling eglTerminate() would destroy all EGL resources, but allowed for by the spec. Be warned!


Solution

  • This is not a supported use case. The fact that it worked before was just luck for you and it could fail on some devices. You can work around this by rendering either the 2D or 3D content in a different window, or render the 2D content in a Bitmap that you then use as a GL texture, etc.