android - Android (ARCore) 阻塞上的 PixelBuffer 对象和 glReadPixel

标签 android opengl-es arcore

我知道默认的 glReadPixels() 会等到所有绘图命令都在 GL 线程上执行完。但是当你绑定(bind)一个 PixelBuffer 对象然后调用 glReadPixels() 它应该是异步的并且不会等待任何事情。 但是当我绑定(bind) PBO 并执行 glReadPixels() 时,它会阻塞一段时间。

下面是我如何初始化 PBO:

mPboIds = IntBuffer.allocate(2); 

GLES30.glGenBuffers(2, mPboIds);

GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, mPboIds.get(0));
GLES30.glBufferData(GLES30.GL_PIXEL_PACK_BUFFER, mPboSize, null, GLES30.GL_STATIC_READ); //allocates only memory space given data size

GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, mPboIds.get(1));
GLES30.glBufferData(GLES30.GL_PIXEL_PACK_BUFFER, mPboSize, null, GLES30.GL_STATIC_READ);

GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, 0);

然后我使用这两个缓冲区进行乒乓球:

    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, mPboIds.get(mPboIndex)); //1st PBO
    JNIWrapper.glReadPixels(0, 0, mRowStride / mPixelStride, (int)height, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE); //read pixel from the screen and write to 1st buffer(native C++ code)

    //don't load anything in the first frame
    if (mInitRecord) {
        GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, 0);

        //reverse the index
        mPboIndex = (mPboIndex + 1) % 2;
        mPboNewIndex = (mPboNewIndex + 1) % 2;
        mInitRecord = false;
        return;
    }

    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, mPboIds.get(mPboNewIndex)); //2nd PBO
    //glMapBufferRange returns pointer to the buffer object
    //this is the same thing as calling glReadPixel() without a bound PBO
    //The key point is that we can pipeline this call

    ByteBuffer byteBuffer = (ByteBuffer) GLES30.glMapBufferRange(GLES30.GL_PIXEL_PACK_BUFFER, 0, mPboSize, GLES30.GL_MAP_READ_BIT); //downdload from the GPU to CPU

    Bitmap bitmap = Bitmap.createBitmap((int)mScreenWidth,(int)mScreenHeight, Bitmap.Config.ARGB_8888);
    bitmap.copyPixelsFromBuffer(byteBuffer);

    GLES30.glUnmapBuffer(GLES30.GL_PIXEL_PACK_BUFFER);

    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, 0);

    //reverse the index
    mPboIndex = (mPboIndex + 1) % 2;
    mPboNewIndex = (mPboNewIndex + 1) % 2;

这在我的绘制方法中每一帧都会被调用。 根据我的理解,glReadPixels 根本不应该花费任何时间,但它需要大约 25 毫秒(在 Google Pixel 2 上)并且创建位图需要另外 40 毫秒。这只能达到 13 FPS,这比没有 PBO 的 glReadPixels 差。

我的代码中是否有任何遗漏或错误的地方?

最佳答案

已编辑,因为您指出我最初的假设是不正确的(初始 PboIndex == PboNextIndex)。希望对您有所帮助,这是我刚刚在 native 端编写的 C++ 代码,使用 GLES 3 通过 JNI 从 Android 调用。它似乎可以工作并且不会阻塞 glReadPixels(...)。注意只有一个 glPboIndex 变量:

  glBindBuffer(GL_PIXEL_PACK_BUFFER, glPboIds[glPboIndex]);
  glReadPixels(0, 0, frameWidth_, frameHeight_, GL_RGBA, GL_UNSIGNED_BYTE, 0);
  glPboReady[glPboIndex] = true;
  glPboIndex = (glPboIndex + 1) % 2;
  if (glPboReady[glPboIndex]) {
    glBindBuffer(GL_PIXEL_PACK_BUFFER, glPboIds[glPboIndex]);
    GLubyte* rgbaBytes = (GLubyte*)glMapBufferRange(
        GL_PIXEL_PACK_BUFFER, 0, frameByteCount_, GL_MAP_READ_BIT);
    if (rgbaBytes) {
      size_t minYuvByteCount = frameWidth_ * frameHeight_ * 3 / 2; // 12 bits/pixel
      if (videoFrameBufferSize_ < minYuvByteCount) {
        return; // !!! not logging error inside render loop
      }
      convertToVideoYuv420NV21FromRgbaInverted(
          videoFrameBufferAddress_, rgbaBytes,
          frameWidth_, frameHeight_);
    }
    glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
    glPboReady[glPboIndex] = false;
  }
  glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);

...

先前没有根据的假设:

您的问题没有显示设置 mPboIndex 和 mPboNewIndex 初始值的代码,但如果将它们设置为相同的初始值,例如 0,那么它们将在每个循环中具有匹配值,这将导致映射刚读过的同一个 PBO。在那个假设/真实的场景中,即使使用了 2 个 PBO,它们也不会在 glReadPixels 和 glMapBufferRange 之间交替,这将阻塞直到 GPU 完成数据传输。我建议进行此更改以确保 PBO 交替:

mPboNewIndex = mPboIndex;
mPboIndex = (mPboNewIndex + 1) % 2;

关于android - Android (ARCore) 阻塞上的 PixelBuffer 对象和 glReadPixel,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47443422/

相关文章:

android - android 监视器使用情况应用程序如何工作

java opengl : glDrawElements() with >32767 vertices

ios - 从 JPEG-2000 创建 openGL 纹理的快速方法?

android - 如何获得彩色的 ARCore AcquireCameraImageBytes()?

arcore - 如何将物体置于空中?

java - ARCore – arFragment 中的图像与片段的显示不同

android - 使用 RelativeLayout 在 ListView 中重复 RatingBar wonkiness

android - android中的缓存 Activity

java - 在新的月份更新变量不会失败

java - 在同一轴上平移和旋转时出现奇怪的行为