glMapBufferRange не работает с GL_OUT_OF_MEMORY на Samsung Galaxy S8 / S10 / Note 10 Lite - PullRequest
0 голосов
/ 05 мая 2020

Я использую библиотеку CameraView для Android, чтобы захватывать кадры с камеры и обрабатывать их с помощью шейдеров OpenGL. Я столкнулся со странным поведением при попытке использовать вычислительные шейдеры. Я хочу получить кадр камеры в виде RGB-буфера с плавающей запятой, обработать его на ЦП, а затем отобразить с помощью OpenGL. Я наследую BaseFilter CameraView, настраиваю свою вычислительную программу и буфер SSBO, отправляю вычисления в onPreDraw () и, наконец, визуализирую кадр камеры на поверхности, как и все другие фильтры.

Все работает нормально, пока я либо начать запись видео ИЛИ переключить камеру с передней на заднюю. Независимо от того, как долго я запускаю фильтр, как только я переключаюсь лицом, я больше не могу получить содержимое моего SSBO с помощью glMapBufferRange. Вызов OpenGL завершается с ошибкой с кодом GL_OUT_OF_MEMORY, который, как я полагаю, означает случайную ошибку «не удалось получить буфер». текущая облицовка, но сбой сразу после следующего переключения. Удивительно, но установка фильтра на NONE перед переключением не помогает: я могу запустить свой фильтр с лицевой стороны, затем установить фильтр на NONE, затем переключить ориентацию, затем снова установить мой фильтр, и он не удастся. Не имеет значения, повторно использую ли я экземпляр своего фильтра ИЛИ создаю новый каждый раз, когда устанавливаю его на CameraView.

Некоторые случайные наблюдения:

  1. Эта проблема возникает только с Устройства Samsung, подтвержденные на Galaxy S8, Galaxy S10 и Galaxy Note 10 Lite. Эта проблема не может быть воспроизведена в Google Pixel 3
  2. Эта проблема не может быть воспроизведена, когда я изменяю свой вычислительный шейдер, чтобы не выполнять выборку кадра камеры через samplerExternalOES. Все работает нормально.
  3. Эта проблема не может быть воспроизведена, когда я изменяю свой вычислительный шейдер, чтобы не выполнять никакого вывода в 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?

1 Ответ

2 голосов
/ 05 мая 2020

На данный момент лучший обходной путь, предложенный владельцем CameraView, - это добавить проход рендеринга, выгружать текстуру OES в буфер кадра, а затем обращаться к буферу кадра через sampler2D uniform из вычислительного шейдера. Он работает нормально, хотя и съедает некоторый FPS из-за дополнительного шага.

Итак, проблема в странном взаимодействии между вычислительными шейдерами, SSBO и samplerExternalOES. Я надеюсь, что этот обходной путь поможет кому-то сэкономить время, решив ту же проблему

...