You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GLSurfaceView共享EGL上下文给Native库后出现GL_FRAMEBUFFER_UNDEFINED异常

Android OpenGL共享上下文导致帧缓冲异常问题

原本正常运行的Android应用,通过GLSurfaceView将外部OES纹理渲染到帧缓冲,转换为普通OpenGL纹理后再输出到屏幕。为了将普通OpenGL纹理传入Native库做TFLite GPU delegate后处理,在onSurfaceCreated中把当前EGL上下文作为共享上下文初始化Native库(Native库运行在GLSurfaceView线程外)。但添加初始化代码后,渲染画面全黑,解绑帧缓冲时调用GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER)返回GL_FRAMEBUFFER_UNDEFINED,尽管eglGetCurrentContext显示上下文状态正常。


相关代码

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    supportActionBar?.hide()

    surfaceView = GLSurfaceView(this)
    surfaceView.setPreserveEGLContextOnPause(true);
    surfaceView.setEGLContextClientVersion(3);
    surfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
    surfaceView.setRenderer(this);
    surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);

    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
    setContentView(surfaceView);
}

override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {
    val glContext: Long = eglGetCurrentContext().nativeHandle

    // Initialize the library and share the context, before generating any buffers
    // If I comment this out, everything works well
    library = NativeLib.initialize(glContext)

    val textures = IntArray(1)
    GLES20.glGenTextures(1, textures, 0)
    cameraTextureId = textures[0]

    val textureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES
    GLES20.glBindTexture(textureTarget, cameraTextureId)
    GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE)
    GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE)
    GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR)
    GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR)
    surfaceTexture = SurfaceTexture(cameraTextureId)
    ...
    // Initialize shaders and attributes
    // create glTexture
    ...
   
    GLES20.glDisable(GLES20.GL_DEPTH_TEST)
    GLES20.glDisable(GLES20.GL_CULL_FACE)
    val values = IntArray(1)
    GLES20.glGenFramebuffers(1, values, 0)
    frameBuffer = values[0]
}

override fun onDrawFrame(p0: GL10?) {
    bindFramebuffer(glTexture)

    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    surfaceTexture?.updateTexImage()

    GLES20.glTexParameteri(
        GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri(
        GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri(
        GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
    GLES20.glTexParameteri(
        GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);

   ....
   // setup coordinates and attribute pointers
   ...

    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

    unbindFrameBuffer()
    // Now draw to screen

    GLES20.glDisable(GLES20.GL_DEPTH_TEST);
    GLES20.glDepthMask(false);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
    ShaderUtil.checkGLError(TAG, "glDrawArrays");
    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
}

private fun _bindFrameBuffer(frameBuffer_: Int, texture: Int?, width: Int, height: Int)
{
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer_)
    texture?.apply {
        GLES20.glFramebufferTexture2D(
            GLES20.GL_FRAMEBUFFER,
            GLES20.GL_COLOR_ATTACHMENT0,
            GLES20.GL_TEXTURE_2D,
            this,
            0
        )
    }
    val status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER)
    if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
        throw java.lang.RuntimeException("Framebuffer not complete, status=$status")
    }
    GLES20.glViewport(0, 0, width, height)
}

fun bindFramebuffer(texture: Int) {
    _bindFrameBuffer(frameBuffer, texture, targetSurfaceWidth, targetSurfaceHeight)
}

fun unbindFrameBuffer() {
    _bindFrameBuffer(0, null, screenSurfaceWidth, screenSurfaceHeight)
}

Native库代码

create_egl_context(EGLContext share_context)
{
    m_egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    eglInitialize(m_egl_display, nullptr, nullptr);

    const EGLint config_attr[] = {
            // clang-format off
            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
            // Allow rendering to pixel buffers or directly to windows.
            EGL_SURFACE_TYPE,
            EGL_PBUFFER_BIT | EGL_WINDOW_BIT,
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_ALPHA_SIZE, 8,  // if you need the alpha channel
            EGL_DEPTH_SIZE, 16,  // if you need the depth buffer
            EGL_NONE
            // clang-format on
    };

    EGLint w, h, format;
    EGLint numConfigs;
    EGLConfig config = nullptr;
    /* Here, the application chooses the configuration it desires.
    * find the best match if possible, otherwise use the very first one
    */
    eglChooseConfig(m_egl_display, config_attr, &config,1, &numConfigs);
    assert(numConfigs);

    if (config == nullptr) {
        std::cerr << "Failed getting config" << std::endl;
        return EGL_NO_CONTEXT;
    }


    const EGLint context_attr[] = {
            // clang-format off
            EGL_CONTEXT_CLIENT_VERSION, 3,
            EGL_NONE
            // clang-format on
    };

    auto egl_context = eglCreateContext(m_egl_display, config, share_context, context_attr);
    eglMakeCurrent(m_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_context);
    return egl_context;
}

问题根源

问题出在Native库初始化时的eglMakeCurrent调用:

  • GLSurfaceView的渲染线程原本绑定了对应屏幕surface的EGL上下文,Native库中调用eglMakeCurrent(m_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_context)会抢占当前线程的上下文绑定,导致后续GLSurfaceView线程中创建纹理、帧缓冲等OpenGL操作,实际是在Native的无surface上下文中执行的,而非原来绑定到屏幕的上下文。
  • 后续创建的帧缓冲、纹理属于Native上下文,GLSurfaceView的渲染上下文无法正确访问,最终出现帧缓冲状态异常、渲染全黑的问题。

解决方案

1. 不在GLSurfaceView线程中激活Native上下文

修改Native库初始化逻辑,只创建共享上下文,不在GLSurfaceView线程中调用eglMakeCurrent,而是在Native库自己的工作线程中激活上下文:

// 修改create_egl_context,只创建上下文不激活
create_egl_context(EGLContext share_context)
{
    m_egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    eglInitialize(m_egl_display, nullptr, nullptr);

    const EGLint config_attr[] = {
            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
            EGL_SURFACE_TYPE, EGL_PBUFFER_BIT | EGL_WINDOW_BIT,
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_ALPHA_SIZE, 8,
            EGL_DEPTH_SIZE, 16,
            EGL_NONE
    };

    EGLint numConfigs;
    EGLConfig config = nullptr;
    eglChooseConfig(m_egl_display, config_attr, &config, 1, &numConfigs);
    assert(numConfigs);

    if (config == nullptr) {
        std::cerr << "Failed getting config" << std::endl;
        return EGL_NO_CONTEXT;
    }

    const EGLint context_attr[] = {
            EGL_CONTEXT_CLIENT_VERSION, 3,
            EGL_NONE
    };

    return eglCreateContext(m_egl_display, config, share_context, context_attr);
}

// 在Native库的独立工作线程中执行上下文激活和后续操作
void init_native_worker(EGLContext native_context) {
    // 确保此函数在Native库的专属线程中调用
    eglMakeCurrent(m_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, native_context);
    // 初始化TFLite GPU delegate等操作放在此处
}

2. 恢复GLSurfaceView线程的上下文绑定(可选)

如果无法避免在GLSurfaceView线程中初始化Native库,初始化后重新绑定原上下文:

override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {
    val glContext = eglGetCurrentContext()
    val display = eglGetCurrentDisplay()
    val surface = surfaceView.holder.surface

    val nativeGlContext = glContext.nativeHandle
    library = NativeLib.initialize(nativeGlContext)

    // 重新绑定GLSurfaceView的原上下文
    eglMakeCurrent(display, surface, surface, glContext)

    // 后续创建纹理、帧缓冲的逻辑不变
    val textures = IntArray(1)
    GLES20.glGenTextures(1, textures, 0)
    cameraTextureId = textures[0]
    // ... 其他初始化代码
}

3. 确保EGL配置一致性

确认Native库使用的EGL配置与GLSurfaceView完全匹配:GLSurfaceView的配置是EGL_RED_SIZE=8, EGL_GREEN_SIZE=8, EGL_BLUE_SIZE=8, EGL_ALPHA_SIZE=8, EGL_DEPTH_SIZE=16,Native库的config_attr已经匹配,但可以通过打印配置参数验证实际选中的配置是否一致。


额外注意事项

  • 共享上下文仅共享纹理、缓冲区等对象,每个线程必须绑定独立的上下文,不能在同一线程中随意切换上下文。
  • TFLite GPU delegate的操作必须在Native库的专属线程中执行,避免干扰GLSurfaceView的渲染流程。

内容的提问来源于stack exchange,提问作者La bla bla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 08:55:19