あかすくぱるふぇ

同人サークル「あかすくぱるふぇ」のブログです。

工学社の"はじめてのCUDAプログラミング"のまとめ。
内容が古かったり、分からなかったりしたところを補完します。

・GPUのアーキテクチャ(p.26~28)
演算性能[Flops] = SP数 * SPクロック * 2(積和演算)
メモリバンド幅[bit/sec] = メモリインターフェース[bit] * メモリクロック

・スレッドの階層構造(p.44~46)
1つのGPUで1グリッド。
グリッドは複数のブロックで構成される。1SMで1ブロック。ブロック数はGPUごとに異なる。
ブロックは複数のスレッドで構成される。1SP(CUDAコア)で1スレッド。スレッド数は世代ごとに異なる。

・高速アクセスのコツ
ウォープ:連続するスレッドでは同じ分岐を通るようにする。
コアレッシング:アライメントずれはSharedMemoryを使って解決する。
バンクコンフリクト:SharedMemoryの同じバンクに同時アクセスしない。

バッファオブジェクトやシェーダなどOpenGL拡張機能を使う場合、GLEWを利用するのが一般的かと思います。
ただ、GLEWなしでOpenGL拡張機能を使えるようにすることは可能ですし、それほど手間もかかりません。
余計なライブラリを使いたくない場合などは試してみるとよいかと思います。

まず、使い方の例から。
無題
1. glext.hをインクルード
2. 使いたい拡張関数に対して、PFN”関数名"PROC型の変数を宣言
3. wglGetProcAddress()
4. 拡張関数を使う

・glext.h
OpenGLの拡張機能が宣言されているヘッダファイルです。
OpenGLの公式ページからダウンロードできる安心安全な代物です♪
GLEWなんていらなかったんや!
https://www.opengl.org/registry/

・PFN"関数名"PROCの意味
定義へ移動してみると分かりますが、これは関数ポインタの型です。
関数ポインタの説明は以下のページが分かりやすいと思います。
http://www7b.biglobe.ne.jp/~robe/cpphtml/html03/cpp03009.html
例えば、PFNGLGENFRAMEBUFFERSPROCは、引数が(GLsizei, GLuint), 返り値がvoidの関数ポインタの型ということです。
なお、APIENTRYPはエントリポイント(関数のアドレス)という意味なので、単に"*"と読み替えればよいです。

・wglGetProcAddress()
デバイスドライバから関数のアドレスを取得し、glGenFrameBuffers(という関数ポインタ型の変数)に代入します。
上記リンクにあるように、関数の呼び出しは"関数のアドレス()"で行うので、glGenFrameBuffers()と書くことで、拡張機能を呼び出すことができます。

「バッファをバインドするんだな」と、納得したフリをしていたけれど……。
具体的に何が起こっているのかはよく分からない。

glBindBuffer関数について深く考えるきっかけになったのは、VBOやPBOについて勉強してる時でした。
glBindBuffer関数の有無によって、glVertexPointer関数やglReadPixels関数の引数の意味が変わっている。
例えば、VBOなしのVertexArray(glBindBuffer関数なし)ではglVertexPointer関数の第四引数が配列の先頭ポインタなのに対して、VBOにおけるVertexArray(glBindBuffer関数あり)ではVBO内のオフセットに変わっている。
http://www.slis.tsukuba.ac.jp/~fujisawa.makoto.fu/cgi-bin/wiki/index.php?OpenGL%20-%20VBO

これはいったいどういうことなんだろう。
そんな疑問を解決したいと思って色々考えてみました。

結論から言うと、glVertexPointer関数などの引数として与えるポインタは、「バインドされているバッファの先頭アドレスからのオフセット」であり、バッファがバインドされていない場合に特例として、アドレス0からのオフセットと解釈されるようです。
以下、解説していきます。

まず、glBindBuffer(target, bufferId)の引数から解説します。
targetはバッファの種類を表しています。
そして、glBindBuffer関数では、targetで指定したバッファ種類について「バッファとしてbufferIdのバッファを使ってね」と設定(バインド)するわけです。
以下では、具体例として、targetにGL_ARRAY_BUFFERとGL_PIXEL_PACK_BUFFERが指定された場合について説明します。

・GL_ARRAY_BUFFER(glVertexPointer, glDrawBuffer)
glVertexPointer関数で頂点配列の「バインドされているバッファの先頭からのオフセット」を指定し、glDrawBuffer関数で指定された頂点配列を描画します。
この時、バインドされているバッファがない--つまり、glBindBuffer(GL_ARRAY_BUFFER, 0)となっている場合は、アドレス0からのオフセットと解釈されます。
アドレス0からのオフセットってことはつまり、頂点配列の先頭アドレスを与えることになるわけです。

・GL_PIXEL_PACK_BUFFER(glReadPixels)
glReadPixels関数で格納先の「バインドされているバッファの先頭からのオフセット」を指定し、ピクセル情報を読み込みます。
この時、バッファがバインドされていれば、そのバッファに読み込みます。
一方、glBindBuffer(GL_PIXEL_PACK_BUFFER, 0)となっている場合は、アドレス0からのオフセットを指定する必要があるので、格納先の先頭アドレスを指定することになるわけです。

↑このページのトップヘ