2

我在处理 openGL 项目时遇到了一个不寻常的问题。本质上,我需要灰度单通道格式的帧数据来处理一些简历。我正在使用自定义着色器、FBO 和 PBO 来完成任务。

程序流程如下。

  1. 绑定生成的FBO
  2. draw() 到 FBO
  3. 绑定 PBO 和 glReadPixels()
  4. 从前一帧和 glMapBufferRange() 绑定 PBO
  5. 处理从 glMapBufferRange() 提供的像素数据

我想确认该过程运行正常。我想知道的是是否可以做任何事情来提高性能。我将发布一些我正在使用的代码,以便我们都可以遵循。

PBO 生成器代码

    final int[] pbuffers = new int[2];

    GLES30.glGenBuffers(2, pbuffers, 0);

    for (int i = 0; i < pbuffers.length; i++) {
        GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, pbuffers[i]);
        GLES30.glBufferData(GLES30.GL_PIXEL_PACK_BUFFER, width * height, null, GLES30.GL_DYNAMIC_READ);
        GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, 0);
    }

    pbo_id[PBO_PRIMARY_ID] = pbuffers[0];
    pbo_id[PBO_SECONDARY_ID] = pbuffers[1];

列表中的第 3 步 -> 绑定 PBO 和 glReadPixels()

    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, pbo_id[currentBuffer]);
    GLES30.glReadBuffer(GLES30.GL_COLOR_ATTACHMENT0);

    JNI.glReadPixels(0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, 0);

    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, 0);

    final int prevBuffer = previousBuffer;

    previousBuffer = currentBuffer;
    currentBuffer = prevBuffer;

列表中的第 4 步 -> 从前一帧和 glMapBufferRange() 绑定 PBO。这是从上一帧执行 glReadPixels 的 PBO。

    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, pbo_id[currentBuffer]);

    JNI.glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, width * height, GL_MAP_READ_BIT);

    GLES30.glUnmapBuffer(GLES30.GL_PIXEL_PACK_BUFFER);
    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, 0);

这就是性能问题的来源。目前我正在读回 480 x 360 单通道灰度的像素(从着色器计算)。我已经运行了一些基准测试,结果如下。

40-50ms -> JNI.glReadPixels(0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, 0);
0-1ms -> JNI.glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, width * height, GL_MAP_READ_BIT);

据我了解,来自 PBO 的 glReadPixels 并不意味着是一个阻塞调用,但无论出于何种原因,它都会在这里阻塞它(并且性能比仅仅从 FBO 读取要差得多)。似乎 glMapBufferRange 的行为符合预期,并正确返回了所需的数据。

我唯一能想到的是我正在使用 GL_RED 并且只读回一个通道,但这仍然不能解释为什么 glReadPixels 会阻塞。

我用于基准测试的设备(一致的行为)。

  1. HTC One M8s (40-50ms)
  2. Nexus 5x (20-30ms)
  3. 谷歌像素(15-30 毫秒)

在这件事上的任何帮助将不胜感激!与此同时,我将尝试做更多的实验,看看是否有什么明显的我错过了。

编辑-> 2017 年 3 月 16 日(为清楚起见添加了更多代码)

FBO 设置代码

    final int[] values = new int[1];
    GLES30.glGenTextures(1, values, 0);
    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, values[0]);

    // we only want GRAYSCALE / Single channel texture
    GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_R8, texWidth, texHeight, 0, GLES30.GL_RED, GLES30.GL_UNSIGNED_BYTE, null);
    GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);
    GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE);
    GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
    GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_NEAREST);

    this.tex_id[0] = values[0];

    GLES30.glGenFramebuffers(1, values, 0);
    GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, values[0]);

    this.fbo_id[0] = values[0];
    GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, this.tex_id[0], 0);

    final int status = GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER);

    if (status != GLES30.GL_FRAMEBUFFER_COMPLETE) {
        Debug.LogError("Framebuffer incomplete. Status: " + status);
    }

    GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);

完整的渲染代码。为了清楚起见,我已经尽可能多地解构了逻辑和流程。

    // bind the offscreen FBO and render the current camera frame
    GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, dualFBO.getID());
    camera.draw(ShaderType.GRAYSCALE);

    // ping-pong the FBO ID's
    dualFBO.swap();

    // dualFBO will now return the ID for last frame
    GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, dualFBO.getID());

    // bind the current PB and submit (meant to be async) glReadPixels
    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, dualPBO.getID());
    GLES30.glReadBuffer(GLES30.GL_COLOR_ATTACHMENT0);

    // this call locks for 30-50ms... why? (meant to be async???)
    JNI.glReadPixels(0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, 0);

    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, 0);

    // ping-pong the PBO ID's.
    dualPBO.swap();

    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, dualPBO.getID());

    // this call is instant
    JNI.glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, width * height, GL_MAP_READ_BIT);

    GLES30.glUnmapBuffer(GLES30.GL_PIXEL_PACK_BUFFER);
    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, 0);
4

0 回答 0