【发布时间】:2020-05-05 13:51:14
【问题描述】:
我正在使用适用于 Android 的 CameraView 库从摄像头捕获帧并使用 OpenGL 着色器对其进行处理。我在尝试使用计算着色器时遇到了一些奇怪的行为。我想抓取一个相机帧作为浮点的 RGB 缓冲区,在 CPU 上处理它,然后用 OpenGL 渲染它。我继承了 CameraView 的 BaseFilter,设置了我的计算程序和 SSBO 缓冲区,在 onPreDraw() 中调度计算,最后在表面上渲染相机帧,就像所有其他过滤器一样。
一切正常,直到我开始录制视频或在前后切换摄像头。无论我运行过滤器多长时间,一旦我切换面对,我就不再能够使用 glMapBufferRange 获取我的 SSBO 的内容。 OpenGL 调用失败,错误代码为 GL_OUT_OF_MEMORY,我认为它代表随机的“无法获取缓冲区”错误。
在设置过滤器之前在正面和背面之间切换无效 - 过滤器将以当前面开始正常运行,但在下一次切换后立即失败。令人惊讶的是,在切换之前将过滤器设置为 NONE 并没有帮助:我可以从正面开始过滤器,然后将过滤器设置为 NONE,然后切换面向,然后再次设置过滤器,它会失败。如果我重新使用我的过滤器实例或每次将它设置为 CameraView 时创建一个新实例,这并不重要。
一些随机观察:
- 此问题仅发生在三星设备上,已在 Galaxy S8、Galaxy S10 和 Galaxy Note 10 Lite 上确认。此问题无法在 Google Pixel 3 上重现
- 当我将计算着色器修改为不通过 samplerExternalOES 对相机帧进行采样时,无法重现此问题。一切正常。
- 当我将计算着色器修改为不对 SSBO 执行任何输出时,无法重现此问题。一切正常。
我的计算着色器看起来像这样(重现问题的示例代码):
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 库的问题?
【问题讨论】:
-
三星 Galaxy 的哪个版本 - Snapdragon 或 Exynos?
-
Exynos。图形是 Mali-G72(适用于我的 Galaxy Note 10 Lite)
-
谢谢,将向 Mali 驱动程序团队提出错误。
-
如果你能分享一个复制器,你可以给开发者
arm com 发邮件。谢谢!
标签: android opengl-es samsung-galaxy camera-view ssbo