3

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

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

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

  1. 您以表面纹理的形式获得相机输入。
  2. 创建一个 OpenGL 着色器存储缓冲区对象 (SSBO)。
  3. 用于GPUDelegate.bindGlBufferToTensor()将该 SSBO 与输入张量相关联。

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

  5. 运行推理。

我们能够将相机帧作为原始字节获取或将其转换为纹理,甚至将其渲染到 GLSurface View。但是我们无法实现 tensorflow 所建议的加速。

  1. https://github.com/tensorflow/tensorflow/issues/26297
  2. https://github.com/tensorflow/tensorflow/issues/25657#issuecomment-466489248
  3. https://www.tensorflow.org/lite/performance/gpu_advanced#android_2

安卓代码:

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

1 回答 1

0

没有简单的方法可以直接将 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。

于 2019-04-09T18:48:23.480 回答