ICS 上的eglCreateWindowSurface,以及从 2D 切换到 3D
我正在尝试让基于 NDK 的游戏在 Android ICS 上运行。它在蜂窝和姜饼上效果很好。
游戏使用一些 2D 渲染,一些 3D 渲染,在执行的不同阶段在两者之间切换。 (由于第三方代码,这是不可协商的。)我们使用 ANativeWindow_lock()
/ANativeWindow_unlockAndPost()
进行 2D 渲染,并使用 eglCreateWindowSurface()
/gl*()
/eglSwapBuffers()
用于 3D 渲染。
在 Honeycomb 和 Gingerbread 上这一切都运行良好。在 ICS 上,eglCreateWindowSurface()
失败,并在日志中显示以下消息:
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)
查看源代码,很明显 eglCreateWindowSurface()
失败,因为某些内容获取了本机窗口打开 2D 渲染,如果不先释放表面,它不会让我将其更改为 3D。然而,ANativeWindow API 似乎没有任何明显的方法来做到这一点。
有其他人遇到过这个问题吗?解决方案是什么?
已更新
因此,我重写了 2D 渲染代码以改用 OpenGL 原语(将后台缓冲区上传到纹理,通过一对三角形、交换缓冲区渲染纹理)。这在一定程度上是有效的。 现在发生的情况是 2D 渲染工作正常;然后我销毁该表面,创建一个新表面以准备 3D 渲染,但第二次调用 eglCreateWindowSurface()
失败。这次,使用:
E/SurfaceTexture( 1869): [com.fnord/com.fnord.MyActivity] connect: already connected (cur=1, req=1)
...它会抛出 AEGL_BAD_NATIVE_WINDOW
的 EGL 错误。
请注意,新表面的创建属性与旧表面完全相同。我什至尝试确保在两次调用 eglCreateWindowSurface()
之间调用 eglTerminate()
/ eglInitialize()
。
我可以只使用每个 ANativeWindow 一次吗?如果是这样,那是否未通过 Khronos 的 EGL 一致性测试?那么有没有办法让 NativeActivity 重新创建窗口呢?
更新更新
结果最后一个问题是由于我在销毁上下文和表面之前未能调用 eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)
引起的。这导致销毁被延迟,直到线程解除上下文和表面的绑定,当然事实并非如此,因此导致对 eglCreateWindowSurface()
的调用合法失败(你不能有两个同一本机窗口上的窗口表面)。
这有点违反直觉,因为人们会认为调用 eglTerminate()
会破坏所有 EGL 资源,但规范允许这样做。请注意!
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!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这不是受支持的用例。事实上,它之前的工作原理只是运气好,它可能会在某些设备上失败。您可以通过在不同的窗口中渲染 2D 或 3D 内容,或者在位图中渲染 2D 内容,然后将其用作 GL 纹理等来解决此问题。
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.
您可以从 OpenGL 切换到 CPU 渲染 (
ANativeWindow_lock
),反之亦然,但您需要使用Surface.release()
释放使用的Surface
并从您的SurfaceTexture
为其他 API 创建一个新的。示例应用
You can switch from OpenGL to CPU rendering (
ANativeWindow_lock
) and vice versa but you need to release the usedSurface
withSurface.release()
and create a new one from yourSurfaceTexture
for the other API.Example app