我正在使用Android的CameraView库来捕获相机中的帧,并使用OpenGL着色器对其进行处理。尝试使用计算着色器时遇到一些奇怪的行为。我想抓取一个相机帧作为RGB浮点缓冲区,在CPU上对其进行处理,然后使用OpenGL进行渲染。我继承了CameraView的BaseFilter,设置了我的计算程序和SSBO缓冲区,在onPreDraw()中调度了计算,最后像其他所有滤镜一样在表面上渲染了摄像头框架。
一切正常,直到我开始录制视频或在正面和反面之间切换相机。不管我运行过滤器多长时间,只要切换面孔,我就无法再使用glMapBufferRange获取SSBO的内容。 OpenGL调用失败,错误代码为GL_OUT_OF_MEMORY,我相信它代表随机的“无法获取缓冲区”错误。
在设置滤镜之前在前后之间切换无效-滤镜将以当前朝向开始正常运行,但在下一次切换后立即失效。令人惊讶的是,在切换前将滤镜设置为NONE无济于事:我可以从正面开始启动滤镜,然后将滤镜设置为NONE,然后切换为正面,然后再次设置滤镜,否则它将失败。重复使用滤镜实例还是每次将其设置为CameraView时都创建一个实例都没关系。
一些随机观察结果:
我的计算着色器看起来像这样(重现该问题的示例代码:]
private fun getComputeShaderText(cx: Int, cy: Int, bind: Int) = """#version 310 es #extension GL_OES_EGL_image_external_essl3: enable precision mediump float; layout(local_size_x = 8, local_size_y = 8) in; layout(std430) buffer; layout(binding = 0) uniform samplerExternalOES in_data; layout(binding = ${bind}) buffer Input { float elements[]; } out_data; void main() { if (gl_GlobalInvocationID.x >= ${cx}u || gl_GlobalInvocationID.y >= ${cy}u) return; float u = float(gl_GlobalInvocationID.x) / $cx.0; float v = float(gl_GlobalInvocationID.y) / $cy.0; vec3 texColor = texture(in_data, vec2(u,v)).rgb; uint index = gl_GlobalInvocationID.x + ${cx}u * gl_GlobalInvocationID.y; out_data.elements[index] = texColor.r; } """
这里是整个过滤器类,可以重现此问题(为清晰起见,所有错误检查均被省略,但相关的检查除外):
import android.opengl.GLES31 import android.util.Log import com.otaliastudios.cameraview.filter.BaseFilter class DummyCameraFilter : BaseFilter() { private val ssboDimX = 64 private val ssboDimY = 64 private val ssboSize = ssboDimX * ssboDimY * 4 /* size of float */ private val ssboBind = 1 /* binding point */ private var renderProgram: Int = -1 private var computeShader: Int = -1 private var computeProgram: Int = -1 private val ssbo = IntArray(1) override fun onCreate(programHandle: Int) { super.onCreate(programHandle) // keep program handle: this.renderProgram = programHandle // create shader: computeShader = GLES31.glCreateShader(GLES31.GL_COMPUTE_SHADER) GLES31.glShaderSource(computeShader, getComputeShaderText(ssboDimX, ssboDimY, ssboBind)) GLES31.glCompileShader(computeShader) computeProgram = GLES31.glCreateProgram() GLES31.glAttachShader(computeProgram, computeShader) GLES31.glLinkProgram(computeProgram) // create ssbo: GLES31.glGenBuffers(1, ssbo, 0) GLES31.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, ssbo[0]) GLES31.glBufferData(GLES31.GL_SHADER_STORAGE_BUFFER, ssboSize, null, GLES31.GL_STREAM_COPY) } override fun onDestroy() { GLES31.glDeleteBuffers(1, ssbo, 0) GLES31.glDeleteShader(computeShader) GLES31.glDeleteProgram(computeProgram) super.onDestroy() } override fun onPreDraw(timestampUs: Long, transformMatrix: FloatArray) { super.onPreDraw(timestampUs, transformMatrix) // compute: GLES31.glUseProgram(computeProgram) GLES31.glBindBufferRange(GLES31.GL_SHADER_STORAGE_BUFFER, ssboBind, ssbo[0], 0, ssboSize) GLES31.glDispatchCompute(64 / 8, 64 / 8, 1) GLES31.glMemoryBarrier(GLES31.GL_SHADER_STORAGE_BARRIER_BIT) // fetch data: GLES31.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, ssbo[0]) GLES31.glMapBufferRange(GLES31.GL_SHADER_STORAGE_BUFFER, 0, ssboSize, GLES31.GL_MAP_READ_BIT) GLES31.glUnmapBuffer(GLES31.GL_SHADER_STORAGE_BUFFER) if (GLES31.glGetError() != GLES31.GL_NO_ERROR) { Log.d("CameraView", "This starts failing after toggle facing!") } } override fun onDraw(timestampUs: Long) { GLES31.glUseProgram(renderProgram) super.onDraw(timestampUs) } override fun getFragmentShader() = createDefaultFragmentShader() }
请帮助。我对samplerExternalOES或SSBO或两者做错了吗?还是是CameraView库问题?
我正在使用Android的CameraView库来捕获相机中的帧,并使用OpenGL着色器对其进行处理。尝试使用计算着色器时遇到一些奇怪的行为。我想要...
迄今为止,CameraView所有者建议的最佳解决方法是添加渲染通道,将OES纹理转储到帧缓冲区,然后通过计算着色器中的sampler2D统一访问帧缓冲区。它工作正常,尽管由于额外的步骤而吃了一些FPS。