しばらく前に、CUDAを使ってGPU上に作ったデータをそのままGPU上でpixelの配列にしてそのまま画面に描画しようとしていた。
cuda_practice/ising-show at master · ToruNiina/cuda_practice · GitHub
例えばこれなのだが。これは、isingモデルをシミュレーションしつつ描画するコードだ。シミュレーションしているのでスピンの状態がGPUに乗っている。OpenGLのバッファもGPUの上にあるのなら、CPUに戻してから再度GPUにコピーするのは無駄以外の何物でもない。GPU上で直接RGBの配列を作って、それを描画すれば良い。
というわけでここではcudaGraphicsGLRegisterImage
を使ってOpenGLのrender bufferをcudaのgraphics resourceにバインドしている。そこからcudaArray_tを取り出して、 cudaBindSurfaceToArray
を使ってsurface<void, cudaSurfaceType2D>
に変換し、そこにsurf2Dwrite
で書き込んでいる。具体的なコードは長くなるので上のレポジトリを見てもらうのが早い。モデルが単純なので「画像をGPUで作ってそれを直接ウィンドウに描画する」と言う目的のノイズになる部分はほとんどない。
で、上のコードには問題がある。cudaBindSurfaceToArray
はだいぶ前から非推奨なのだ。「動くから良し!」で諦めたい気持ちはあるが、非推奨にするからにはより良い方法が提供されているはずだろう。そっちを使った方がいいに決まってる。特に自分の趣味プロジェクトなら、締め切りも要件もないのだし。
で、さまよっていたらこんなレポジトリを見つけた。
これを読めばdeprecatedでないやり方がわかるのではないかと思って読んでみた。ちなみにこのレポジトリではdriver APIが叩かれているので、上のレポジトリで私が叩いているruntime APIと名前が違う。だがだいたい対応するものがあり、概ねdriver APIのCUxxx
はruntime APIのcudaXxx
に対応している。全てがこの規則通りと言うわけでもないが、まあ目安として。
まず、ここでOpenGLのバッファたちをCUgraphicsResource
にbindし、CUarray
を取り出している。ここまでは同じだ。
実際、非推奨なのはcudaBindSurfaceToArray
なので、問題なのはCUarray
に書き込む方法だけなのだ。このあとCUarray
をどうするのか追いかけていこう。
このCUarray
はここでCUsurfref
型のm_surfWriteRef
という変数に関連づけられている。
さらにm_surfWriteRef
はcuModuleGetSurfRef
によってmodule
に"volumeTexOut"
という名前で関連づけられている。
moduleって何だ? そしてこれ以降m_surfWriteRef
は一切使われておらず、糸が途切れてしまった。
どうなってるんだ? と思って書き込んでいそうな方を見てみると、volumeTexOut
というグローバル変数が突如現れている。
レポジトリ内を検索しても、ここと、これをコンパイルしたであろうptx
、そして
cuModuleGetSurfRef( &m_surfWriteRef, m_module, "volumeTexOut" )
の呼び出ししかない。
このあたりで、m_module
の"volumeTexOut"
というのがこのグローバル変数(ファイルを跨いでいるので通常アクセスできない)を指しているとしか考えられなくなった。module
はおそらくGPUで走るプログラム全体か、翻訳単位あたりを指しているのか、と思ってドキュメントを検索してみた。
The driver API provides an additional level of control by exposing lower-level concepts such as CUDA contexts - the analogue of host processes for the device - and CUDA modules - the analogue of dynamically loaded libraries for the device.
Programming Guide :: CUDA Toolkit Documentation
どうやらそういうことらしい。そしてmodule経由でsurface
を取り出すためにdriver APIの方を叩いているのだということもわかった。
どうなっているのかわかったし、多分これで真似してみる準備は概ね整ったと思うが、力技すぎないか!? driver APIを使って別のファイルにあってスコープも分かれているグローバル変数を文字列で名前指定して取得する? 本当にそれでいいのか、nvidiaよ……。