Texture Memoryとテンプレート

あけましておめでとうございます。

またまたCUDAネタです。

Texture Memoryは型変換や補間などけっこう便利なのですが、同一ファイル内でグローバル変数として宣言しなければならないなど、使用が非常に面倒です。
そして、どうにかして楽に使う方法はないかと探しているときに遭遇した不思議な現象がこのお話です。

初めはテクスチャの宣言を固定長配列としておこない、後から参照する方法を考えていましたが早々に断念。テクスチャを使用した際のptxコードを見ればわかるとおり、どのテクスチャを使用するかはコンパイル段階で決定されていないといけないようです。

次に考えたのは、型テンプレートを使用する方法。texture宣言をstaticメンバで含む型をテンプレート化するもので、

test.cu

...
template<int n>
struct Tex{
 static texture<float4, 2, cudaReadModeElementType> tex;
};
texture<float4, 2, cudaReadModeElementType> Tex<0>::tex;
...
texture<float4, 2, cudaReadModeElementType> Tex<10>::tex;
...

のような.cuコードを使い、使用する場所でTex<0-10>::texと指定します。

ところがこれをnvccにかけると、次のようなエラーが。

error C2720: 'Tex<n>::tex' : 'static ' ストレージ クラスの指定子が識別子に対して誤って指定されています。

このエラー、発生場所をみるとptxの作成やGPU側オブジェクトコードの作成は問題なく済んだあと、HOST側コードのコンパイル段階で発生している。
MSDNでみると、Compiler Error C2720 | Microsoft Docsとなっている。元の.cuではstaticはつけていないのに、はて?と思ってnvccによって生成された.cu.cppをみてびっくり。

test.cu.cpp

...
template<int n> 
struct Tex { 
static   texture< float4, 2, cudaReadModeElementType>  tex; 
}; 
static   texture< float4, 2, cudaReadModeElementType>  Tex< 0> ::tex; 
...

なんかついてる・・・!

ついでに微妙に変なスペースもはいってる。

ここから先は想像ですが、nvccが.cu.cppを生成する際にtexture宣言された変数に手当たり次第static修飾子をつけているのではないだろうか?
通常のグローバル変数の場合はそれで問題ないのだが、staticメンバとして宣言された場合は、static修飾子は型のほうにすでにあるため文法エラーになる。しかしnvccがそれを認識せずにstatic修飾子をつけるため、エラーになっている。
そしてこの.cu.cppを手動で編集しstatic修飾子を取り除くと、問題なくコンパイルされました。ようはnvccの手抜き実装が原因?

ちなみにテクスチャにアクセスする時に使うtex1Dなどの関数はtexture_fetch_functions.hで定義されているので、Texture Memoryを使う場合は確認しておいたほうがいいです。

2010-01-04 追記
staticメンバのテンプレート特殊化の宣言を.cuではなく別の.cppファイルに移すことで、特殊化の部分のみnvccを迂回してコンパイルすればとりあえず回避できるようです。コンパイラはCUDA 2.3 win32 xp, VC++ 2008 Expressです。

test.cu

...
template<int n>
struct Tex{
 static texture<float4, 2, cudaReadModeElementType> tex;
};
...

test.cpp

texture<float4, 2, cudaReadModeElementType> Tex<0>::tex;
...
texture<float4, 2, cudaReadModeElementType> Tex<10>::tex;

2010-01-04 追記 その2
Texクラスのテンプレート引数を増やすと、nvcc内の__text_varマクロの展開で落ちる模様。がんばれば処理できそうだけど、大人しくテンプレートを使わず普通にテクスチャ宣言したほうがよさそう。