简体   繁体   English

glMapBufferRange 在三星 Galaxy S8 / S10 / Note 10 Lite 上出现 GL_OUT_OF_MEMORY 失败

[英]glMapBufferRange fails with GL_OUT_OF_MEMORY on Samsung Galaxy S8 / S10 / Note 10 Lite

I am using CameraView library for Android to capture frames from the camera and process them with OpenGL shaders.我正在使用 Android 的 CameraView 库从相机捕获帧并使用 OpenGL 着色器处理它们。 I have encountered some strange behavior while trying to use compute shaders.我在尝试使用计算着色器时遇到了一些奇怪的行为。 I want to grab a camera frame as an RGB buffer of floating points, process it on CPU and then render it with OpenGL.我想抓取一个相机帧作为浮点的 RGB 缓冲区,在 CPU 上处理它,然后用 OpenGL 渲染它。 I inherit CameraView's BaseFilter, setup my compute program and SSBO buffer, dispatch computation in onPreDraw(), and finally render camera frame on the surface, just like all the other filters do.我继承了 CameraView 的 BaseFilter,设置了我的计算程序和 SSBO 缓冲区,在 onPreDraw() 中调度计算,最后在表面上渲染相机帧,就像所有其他过滤器一样。

Everything works fine, until I either start recording video OR toggle camera facing between front and back.一切正常,直到我开始录制视频或在正面和背面之间切换相机。 No matter how long I run the filter, as soon as I toggle facing, I am no longer able to get the contents of my SSBO with glMapBufferRange.无论我运行过滤器多长时间,一旦我切换面对,我就不再能够使用 glMapBufferRange 获取我的 SSBO 的内容。 OpenGL call fails with error code GL_OUT_OF_MEMORY, which I believe stands for a random "failed to obtain buffer" error. OpenGL 调用失败,错误代码为 GL_OUT_OF_MEMORY,我认为它代表随机的“无法获取缓冲区”错误。

Toggling between front and back BEFORE setting the filter has no effect - the filter will start running normally with the current facing, but fail immediately after the next toggle.在设置过滤器之前在正面和背面之间切换无效 - 过滤器将开始以当前面正常运行,但在下一次切换后立即失败。 Surprisingly, setting filter to NONE before toggling does not help: I can start my filter with front facing, then set filter to NONE, then toggle facing, then set my filter again, and it will fail.令人惊讶的是,在切换之前将过滤器设置为 NONE 并没有帮助:我可以从正面开始过滤器,然后将过滤器设置为 NONE,然后切换面向,然后再次设置过滤器,它会失败。 It does not matter if I re-use the instance of my filter OR create new one every time I set it to CameraView.如果我重新使用我的过滤器实例或每次将它设置为 CameraView 时创建一个新实例,这并不重要。

Some random observations:一些随机观察:

  1. This issue happens only with Samsung devices, confirmed on Galaxy S8, Galaxy S10 and Galaxy Note 10 Lite.此问题仅发生在三星设备上,已在 Galaxy S8、Galaxy S10 和 Galaxy Note 10 Lite 上确认。 This issue cannot be reproduced on Google Pixel 3此问题无法在 Google Pixel 3 上重现
  2. This issue cannot be reproduced when I modify my compute shader to not sample camera frame via samplerExternalOES.当我将计算着色器修改为不通过 samplerExternalOES 对相机帧进行采样时,无法重现此问题。 Everything works fine.一切正常。
  3. This issue cannot be reproduced when I modify my compute shader to not perform any output to SSBO.当我修改我的计算着色器以不执行任何 output 到 SSBO 时,无法重现此问题。 Everything works fine.一切正常。

My compute shader looks like this (sample code to reproduce the issue):我的计算着色器看起来像这样(重现问题的示例代码):

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

And here is the entire filter class, which can reproduce the issue (all error checks are omitted for clarity, except for the relevant one):这是整个过滤器 class,它可以重现问题(为清楚起见,所有错误检查都被省略了,相关的除外):

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()
}

Please help.请帮忙。 Am I doing something wrong with samplerExternalOES or SSBO or both?我对 samplerExternalOES 或 SSBO 或两者都做错了吗? Or is it a CameraView library issue?还是 CameraView 库问题?

The best workaround so far, suggested by the CameraView owner, is to add a render pass, dumping OES texture to frame buffer, then access the frame buffer via sampler2D uniform from the compute shader.到目前为止,CameraView 所有者建议的最佳解决方法是添加渲染通道,将 OES 纹理转储到帧缓冲区,然后通过计算着色器中的 sampler2D 统一访问帧缓冲区。 It works fine, although eating some FPS due to the extra step.它工作正常,尽管由于额外的步骤而吃掉了一些 FPS。

So, the problem is weird interaction between compute shaders, SSBO and samplerExternalOES.因此,问题在于计算着色器、SSBO 和 samplerExternalOES 之间的奇怪交互。 I hope this workaround can save some time for someone, messing with the same problem我希望这种解决方法可以为解决同样问题的人节省一些时间

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM