2016-04-25 10 views
1

私の目標は、デフォルトのOpenGLフレームバッファの内容を読み取り、ピクセルデータをcv::Matに保存することです。OpenGLのデフォルトのフレームバッファからピクセルデータを読み込み:FBOとPBOのパフォーマンス

1)同期:どうやらこれを達成する異なる方法がありますFBOとglRealPixels

cv::Mat a = cv::Mat::zeros(cv::Size(1920, 1080), CV_8UC3); 
glReadPixels(0, 0, 1920, 1080, GL_BGR, GL_UNSIGNED_BYTE, a.data); 

2を使用)非同期:すべての情報から、PBOとglReadPixels

cv::Mat b = cv::Mat::zeros(cv::Size(1920, 1080), CV_8UC3); 
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo_userImage); 
    glReadPixels(0, 0, 1920, 1080, GL_BGR, GL_UNSIGNED_BYTE, 0); 
    unsigned char* ptr = static_cast<unsigned char*>(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY)); 
    std::copy(ptr, ptr + 1920 * 1080 * 3 * sizeof(unsigned char), b.data); 
    glUnmapBuffer(GL_PIXEL_PACK_BUFFER); 
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 

を使用します私はこのトピックで収集した、非同期バージョン2)ははるかに速くなければなりません。しかし、両方のバージョンの経過時間を比較すると、その差はしばしば最小であり、時にはバージョン1のイベントがPBOバリアントよりも優れていることがわかります。パフォーマンスをチェックするために

、私は(this回答に基づいて)次のコードを挿入した:

std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); 
.... 
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); 
std::cout << "Time difference = " << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << std::endl; 

PBOを作成するときに、私はまたの利用ヒントで実験しました:私は見つけられませんでしたGL_DYNAMIC_COPYGL_STREAM_READの違いの多くはここにあります。

フレームバッファからのこのピクセル読み取り操作の速度をさらに向上させる方法を提案してうれしいです。

+1

'glReadPixels()'呼び出しの直後に結果が到着するのをブロックしているので、2番目のバージョンは実際には非同期ではありません。 –

+0

'std :: copy'の呼び出しを意味していますか?実際に私がこの行をコメントアウトすると、効果は最小限に抑えられますが、バージョン1は時には高速です。 – Schnigges

+0

GPUバッファをCPUメモリにマップしないと、予想通りですが、それ以降は 'cv :: Mat'をベクターに格納したいので、かなり大きな違いがあります – Schnigges

答えて

5

2番目のバージョンは、コピーをトリガーした直後にバッファーをマッピングするため、非同期ではありません。マップ呼び出しは、バッファの内容が有効になるまでブロックされ、効果的に同期します。

または、ドライバによっては実際に読むとブロックされます。換言すれば、ドライバは、ページフォールトおよびその後の同期化を引き起こすような方法でマッピングを実装することができる。あなたがまだstd::copyのためにすぐにそのデータにアクセスしているので、あなたの場合はそれほど重要ではありません。

これを行う正しい方法は、sync objects and fencesです。

PBOの設定を保存しますが、glReadPixelsをPBOに発行した後、glFenceSyncを介してストリームに同期オブジェクトを挿入します。その後、しばらくして、そのフェンスの同期オブジェクトをポーリングして完了させるか(または単にそれを完全に待つ)glClientWaitSync経由で行います。

glClientWaitSyncがフェンスの前のコマンドが完了した場合、CPU/GPUの高価なシンクなしでバッファから読み込むことができます。 (ドライバが特に愚かで、バッファの内容をマップ可能なアドレスに移動していない場合、PBOの使用方法のヒントにもかかわらず、別のスレッドを使用してマップを実行することができます。glGetBufferSubDataは、 「Tは、マップ可能な範囲にあることが必要です。)


を使用すると、フレームごとにこれを実行する必要がある場合、あなたはそれはあなたが複数のPBOをする必要があります可能性が非常に高いということに気づくでしょうつまり、それらの小さなプールを持っています。これは、次のフレームで、前のフレームのデータのリードバックがまだ完了せず、対応するフェンスが通知されないためです。 (GPLは最近大量にパイプライン化されており、送信キューの後ろにいくつかのフレームが存在します)。

+0

私は( 'glReadPixels'からPBOへ)とその結果(' glMapBuffer'、 'glGetBufferSubData'など)を読み出す間に何かをしていると、非同期で転送を行うだけで利益が得られるということを付け加えてください。同期オブジェクトを作成してすぐにそれを待つのは、単に 'glReadPixels'だけではありません。 –

関連する問題