OpenGL de プログラミング - PixelBufferObject
現在地 >> メニュー >> 基本編10 >> PixelBufferObject
関連VertexBufferObjectFrameBufferObject

はじめに

PBO


「ピクセルデータ」を貯めておく「バッファオブジェクト」が「PixelBufferObject(PBO)」を呼ばれる。

「PBO」は「VertexBufferObject」のAPIを使っているが、そこに「PBO」用の「トークン」を指定する。
これによって、「メモリマネージャ(OpenGLのDriver)」が「shared memory」か「ビデオメモリ」がよいか判断する。

また、「PBO」がどのように使われるかも指定する。
  • GL_PIXEL_PACK_BUFFER_ARB → 「ピクセルデータ」を「PBO」に転送する時に使う
  • GL_PIXEL_UNPACK_BUFFER_ARB → 「PBO」から「ピクセルデータ」に転送する時に使う。




▲PBOにおける「PACK」「UNPACK」の概念図

これは、OpenGLの関数でいうと
  • 「glReadPixels()」や「glGetTexImage()」 → 「PACK」の処理
  • 「glDrawPixels()」や「glTexImage2D()」、「glTexSubImage2D()」 → 「UNPACK」の処理
となる。


以下はglReadPixelsを用いた時の動作例。

[例1]

  1. 「GL_PIXEL_PACK_BUFFER_ARB」 を指定
  2. glReadPixels()を実行する
⇒OpenGLのフレームバッファから読み込んで、PBOにデータを格納する




[例2]

  1. 「GL_PIXEL_UNPACK_BUFFER_ARB」を指定
  2. glReadPixels()を実行する
⇒PBOからデータを読み込んで、OpenGLのフレームバッファにコピーする



手順

  1. PBOの作成
  2. マッピング

[1].PBOの作成


PBOはVBOのAPIを使っている。
違いは「GL_PIXEL_PACK_BUFFER_ARB」と「GL_PIXEL_UNPACK_BUFFER_ARB」を使う点である。

これは、
  • GL_PIXEL_PACK_BUFFER_ARB:ピクセルデータをOpenGLからアプリケーションに転送する時
  • GL_PIXEL_UNPACK_BUFFER_ARB:ピクセルデータをアプリケーションからOpenGLに転送する時
に指定する。

※このトークンを基に動作しようとするが、最終的にはOpenGLのドライバが判断する。

作成手順

  1. 作成
  2. バインド
  3. データのコピー

これはVBOと同じ。

[例]

GLuint PboId[1];
const int DataSize = ImageWidth * ImageHeight  * nChannel;  //データサイズ
... ...
glGenBuffersARB(1, PboId);  //PBOを1つ作成
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, PboId[0]); //バインド
glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, DataSize, 0, GL_STREAM_DRAW_ARB); //コピー


GL_STREAM_DRAW_ARB:動的なテクスチャのアップロードなどに使う。
GL_STREAM_READ_ARB:非同期のフレームバッファなどで使う。


[2].マッピング


データの更新には、「glMapBufferARB()関数」、「glUnmapBufferARB()関数」を使用する

glMapBufferARB()関数


この関数は成功するとバッファオブジェクトへのポインタをわたす。失敗するとNULL。
void* glMapBufferARB(GLenum target, GLenum access)

第1引数
  • GL_PIXEL_PACK_BUFFER_ARB
  • GL_PIXEL_UNPACK_BUFFER_ARB


第2引数

ここで、マッピングされたバッファをどうするか決める。
  • GL_READ_ONLY_ARB:読み込むだけ
  • GL_WRITE_ONLY_ARB:書き込むだけ
  • GL_READ_WRITE_ARB:読み書きの両方



※メモ

もしGPUがバッファオブジェクトに関して動作していると、GPUが処理を終えるまで、この関数はreturnをしない。

この待機状態を避けるには、まず、glBufferDataARB()をNULLポインタでよぶ。それからglMapBufferARB()関数をよぶ。
すると、OpenGLは古いバッファをすてて、新しくメモリを確保する。

glUnmapBufferARB()関数


この関数を使って、マッピングされたバッファを解放する。

[例]

glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB,DataSize, 0, GL_STREAM_DRAW_ARB);
unsigned char *color = (unsigned char*)glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY_ARB);
if(color)
{
        //--- colorに対し何らかの書き込み ----//
        glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB);  //解放
}


メモ


実際には「PBO」を2つ用意して、「ダブルバッファ」のように入れ替えて使うと効率が良い。


サンプルコード >> PBO::まとめコード1(UNPACKの例)、PBO::まとめコード2(PACKの例)