GL_FRAMEBUFFER_UNDEFINED与本机库共享上下文后在GLSurfaceView中显示



我有一个Android应用程序,它可以将图像读取到外部OES纹理,用纹理附件绑定帧缓冲区,并对其进行渲染,将其转换为正常的OpenGL纹理。

缓冲区随后被解除绑定并再次渲染以渲染到屏幕。该过程使用GLSurfaceView,运行良好。

然后,我想将常规OpenGL纹理传递到本地库进行后期处理(特别是它将传递给支持在OpenGL纹理上运行的TFLite GPU代理(

为了在本机库中访问纹理,当我创建库时,我将当前OpenGL上下文作为共享上下文传递给在本机库创建上下文的函数(它是一个独立的库,可以在许多输入上运行,它运行自己的线程机制,因此它将在不同于GLSurfaceView线程的线程上运行(

如果我添加创建初始化本地库的代码,那么渲染的纹理都是黑色的,并且当试图解除绑定以在屏幕上绘制时,对GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER)的调用返回GL_FRAMEBUFFER_UNDEFINED

上下文似乎仍然可以(调用eglGetCurrentContext()返回与本机库初始化前相同的值(

我的想法是,在共享上下文时,它会以某种方式扰乱原始上下文的缓冲区,即使它们是在创建本地库之后创建的

这是的相关代码

主活动.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)
}

本机库

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;
}

每个部分独立运行良好(我对库进行了单元测试,该库使用OpenCV将图像加载到OpenGL纹理中,然后运行管道(,但当将它们组合在一起时,失败

问题是在同一个GLThread上创建NativeLib导致当前线程的上下文更改为新的共享上下文。

我可以看到的2种可能的解决方案

在单独的线程中运行本机lib创建

...
thread(start = true) {
library = NativeLib.initialize(glContext)
}.join()
...

或者在创建库之前保存当前线程的显示和读取/绘制表面,然后在之后将其设置回

val context = EGL14.eglGetCurrentContext()
val display = EGL14.eglGetCurrentDisplay()
val readSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_READ)
val drawSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW)
library = NativeLib.initialize(glContext)
// Restore current
EGL14.eglMakeCurrent(display, drawSurface, readSurface, context)

这两种方法都经过了测试并发挥了作用。我选择了第二个1,在我看来似乎更干净

最新更新