2017-11-22 17 views
10

デフォルトのglReadPixels()はすべての描画コマンドがGLスレッドで実行されるまで待機することがわかりました。しかし、PixelBufferオブジェクトをバインドしてからglReadPixels()を呼び出すと、非同期である必要があり、何も待たないでしょう。 しかし、PBOをバインドしてglReadPixels()を実行すると、しばらくブロックされています。PixelBufferオブジェクトとAndroid(ARCore)ブロッキングのglReadPixel

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); 

し、私はピンポンの周りに二つのバッファを使用します:

は、ここで私はPBOを初期化する方法を説明します

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; 

これは、フレームごとに私のdrawメソッドで呼び出されます。 私の理解では、glReadPixelsはまったく時間を取るべきではありませんが、Google Pixel 2では約25msかかっており、ビットマップの作成にはさらに40msかかります。これは、PBOを使用しないglReadPixelsよりも悪い13 FPSのようにしか達成できません。

コードに紛失や間違いがありますか?

+0

私のケースでは、コールは小さいバッファ(360x640ピクセルなど)に対してのみブロックしていました。より大きなバッファ(例えば、720x1280)の場合、呼び出しは非同期になりました。 GPUによる「最適化」のようなものかもしれません。この動作を無効にする方法が見つかりませんでした。 – coeing

答えて

3

私の元の仮説が間違っていたことを指摘して以来、EDITED(最初のPboIndex == PboNextIndex)です。参考にしたいと思っていますが、ここではC++コードをAndroidからJNI経由でGLES 3を使用して呼び出すネイティブ側で書いています。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; 
+0

私の最初のインデックス値は以下のように設定されていました: 'mPboIndex = 0;' 'mPboNewIndex = 1;' – snowrain

+0

私の元の仮説は間違っていて、役に立たなかった –

関連する問題