Android opengl着色器程序将图像从相机复制到SSBO,用于TF lite GPU推断

Android opengl着色器程序将图像从相机复制到SSBO,用于TF lite GPU推断,android,shader,tensorflow-lite,opengl-es-3.1,Android,Shader,Tensorflow Lite,Opengl Es 3.1,Tensorflow lite gpu委托文档提供了在Android中使用Opengl和SSBO运行tflite推理的更快方法[3]。该文档提供了创建SSBO并将其与 图像已在GPU中。我们如何从android live camera复制或转换图像,并使用OpenGL着色器代码将其复制到SSBO?当我们只是将CPU内存转储到SSBO时,性能会比 正常的gpu委托执行。那么,将相机图像传递给SSBO以使tflite推断更快的正确或最有效的方法是什么 在下面的代码中,我们尝试将相机帧转换为位图 然后

Tensorflow lite gpu委托文档提供了在Android中使用Opengl和SSBO运行tflite推理的更快方法[3]。该文档提供了创建SSBO并将其与 图像已在GPU中。我们如何从android live camera复制或转换图像,并使用OpenGL着色器代码将其复制到SSBO?当我们只是将CPU内存转储到SSBO时,性能会比 正常的gpu委托执行。那么,将相机图像传递给SSBO以使tflite推断更快的正确或最有效的方法是什么

在下面的代码中,我们尝试将相机帧转换为位图 然后将其转换为纹理,最后将其复制到SSBO。然而,这种方法比普通的GPU委托执行管道(数据从CPU复制到GPU的管道)慢得多,这是一种开销。其目的是降低成本 CPU到GPU复制图像数据,方法是使图像数据在GPU内存中可用,然后将其传递给模型。 我们能够使用标准GPU委托推理机制以40-50毫秒的速度运行模型[1];而这需要90-100毫秒 使用上述SSBO方法(未优化)。上述定时指的是 在tensorflow lite中运行解释器.run()方法的时间。 此外,这种SSBO机制似乎只适用于OpenGLES3.1或更高版本

理想的用例(如tensorflow所建议的)如下[2]:

  • 以表面纹理的形式获取摄影机输入
  • 创建OpenGL着色器存储缓冲区对象(SSBO)
  • 使用
    GPUDelegate.bindGLBufferToSensor()
    将该SSBO与输入张量关联

  • 编写一个小型着色器程序,将[1]的表面纹理有效地转储到[2]的SSBO中。

  • 运行推断

  • 我们可以获取原始字节的相机帧,或者将其转换为纹理,甚至将其渲染到曲面视图。 但我们无法实现tensorflow所建议的加速

  • Android代码:

    public int[] initializeShaderBuffer(){
            android.opengl.EGLContext eglContext = eglGetCurrentContext();
            int[] id = new int[1];
            GLES31.glGenBuffers(id.length, id, 0);
            GLES31.glBindBuffer(GL_SHADER_STORAGE_BUFFER, id[0]);
            GLES31.glBufferData(GL_SHADER_STORAGE_BUFFER, 257*257*3*4, null, GLES31.GL_STREAM_COPY);
    
            GLES31.glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);// unbind
            return id;
        }
    
    @Override
        public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
    .....
    .....
    
    mTextureDataHandle0 = TextureHelper.loadTexture(mActivityContext,
                    R.drawable.srcim);//No error
    
    }
    
    
    @Override
        public void onDrawFrame(GL10 glUnused) {
    
    
    
    
    
            int inputSsboId = initializeShaderBuffer()[0];
    
            interpreter = new Interpreter(GLActivity.tfliteModel);
    
            Tensor inputTensor = interpreter.getInputTensor(0);
            GpuDelegate gpuDelegate = new GpuDelegate();
            gpuDelegate.bindGlBufferToTensor(inputTensor, inputSsboId);
            interpreter.modifyGraphWithDelegate(gpuDelegate);
    
    
    
    final int computeShaderHandle = ShaderHelper.compileShader(
                    GLES31.GL_COMPUTE_SHADER, fragmentShader);//No error
                mProgramHandle = ShaderHelper.createAndLinkProgram(vertexShaderHandle,
                        computeShaderHandle);//No error 
    
    mTextureUniformHandle0 = GLES31.glGetUniformLocation(mProgramHandle,
                "u_Texture0");
    
    
    /**
             * First texture map
             */
            // Set the active texture0 unit to texture unit 0.
            GLES31.glActiveTexture(GLES31.GL_TEXTURE0 );
    
            // Bind the texture to this unit.
            GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, mTextureDataHandle0);
    
            // Tell the texture uniform sampler to use this texture in the shader by
            // binding to texture unit 0.
            GLES31.glUniform1i(mTextureUniformHandle0, 0);
    
    
            GLES31.glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 1, inputSsboId, 0, 257*257*3*4);
    
            GLES31.glUseProgram(mProgramHandle);
            if(compute==1)//Always set to 1
                GLES31.glDispatchCompute(16,16,1);
    
            GLES31.glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);  // unbind
            GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, 0);  // unbind
    
    
            //Tflite code ...
    
    
            byte [][] outputArray = new byte [1][66049];//size based on model output
            Log.d("GPU_CALL_RUN","DONE");
            long oms1=System.currentTimeMillis();
            interpreter.run(null,outputArray);
    
            long cms1=System.currentTimeMillis();
            Log.d("TIME_RUN_MODEL",""+(cms1-oms1));
    
            Log.d("OUTVAL", Arrays.deepToString(outputArray));
    
    }
    
    计算着色器:-

    #version 310 es
    layout(local_size_x = 16, local_size_y = 16) in;
    layout(binding = 0) uniform sampler2D u_Texture0;
    layout(std430) buffer;
    layout(binding = 1) buffer Output { float elements[]; } output_data;
    void main() {
        ivec2 gid = ivec2(gl_GlobalInvocationID.xy);
        //if (gid.x >= 257 || gid.y >= 257) return;
        vec3 pixel = texelFetch(u_Texture0, gid, 0).xyz;
        int linear_index = 3 * (gid.y * 257 + gid.x);
        output_data.elements[linear_index + 0] = pixel.x;
        output_data.elements[linear_index + 1] = pixel.y;
        output_data.elements[linear_index + 2] = pixel.z;
    }
    

    没有简单的方法可以直接将SurfaceTexture转储到SSBO。最简单的路径是SurfaceTexture->GlTexture->SSBO。TFLite GPU团队还试图引入另一个API(bindGlTextureToTensor),但在此之前,我使用了一个着色器程序进行GlTexutre->SSBO转换:

        #version 310 es
    
        layout(local_size_x = 16, local_size_y = 16) in;
        layout(binding = 0) uniform sampler2D input_texture;
        layout(std430) buffer;
        layout(binding = 1) buffer Output { float elements[]; } output_data;
    
        void main() {
          ivec2 gid = ivec2(gl_GlobalInvocationID.xy);
          if (gid.x >= 224 || gid.y >= 224) return;
          vec3 pixel = texelFetch(input_texture, gid, 0).xyz;
          int linear_index = 3 * (gid.y * 224 + gid.x);
          output_data.elements[linear_index + 0] = pixel.x;
          output_data.elements[linear_index + 1] = pixel.y;
          output_data.elements[linear_index + 2] = pixel.z;
        }
    

    请注意,这是用于输入张量大小为224x224x3的MobileNet v1。

    为什么您甚至需要一个着色器?难道你不能通过将SSBO绑定为PBO并执行
    glGetTexImage
    ,直接将纹理数据复制到SSBO中吗?除非……OpenGL ES没有PBOs,这将是非常不幸的。