vai_q_tensorflowをソースからビルドできない問題
vai_q_tensorflow
はGithubにリポジトリが公開されており,ビルド方法もREADME.md
に記されているが,実行したところBUILD
ファイルが見つからないと怒られる.vai_q_tensorflow
はTensorflow r1.15
からのフォークであり,これと同様にbazel
を用いてビルドするがレシピであるBUILD
ファイルがなぜか見当たらない..gitignore
を参照したところ,どうやら間違って一部のBUILD
ファイルを含めないよう設定されているようだ(適当すぎる...).そこでフォーク元のtensorflow
からBUILD
ファイルを取得し配置する必要があった.その他にも問題が複数発生したので対処法を記す.
↓修正済みのリポジトリ
github.com
手順1
tensorflow
をgit pull
.vai_q_tensorflow
と同階層のディレクトリに配置する.その後tensorflow
のブランチをr1.15
にcheckout
しておく.
手順2
次のシェルを実行する
手順3
私の環境では次のエラーも発生した.これはフォーク元のtensorflow側の問題の様だ.それぞれTensorflow
のGithubに同様のissueが存在した.
#34197
github.com 3つのソースを書き替える必要がある.
#41061
github.com
上記のissue
で述べられているが,次の変更を加えることでコンパイルが成功する.
github.com
RT(TTU)コアの処理内容・内部構造の調査
図などは上記の明細書を参考のこと
TTU - Tree Traversal Unit (nVidiaのTuringコアアーキテクチャから採用された"RTコア"の開発名)
レイトレーシング(Ray Tracing)において、1つの光線毎に、のプリミティブ(オブジェクトの最小単位,たいてい三角形)への衝突判定を行う必要があるが、愚直に計算するのは非効率.
処理数の削減を行うためにBVH(Bounding Volume Hierarchy)と呼ばれる木構造が導入される.下記のサイトを参照.
shinjiogaki.github.io
TTUは,BVHを辿る処理(AABB (Axis Aligned Bounding Box) への衝突判定を含む,数千命令に相当)をハードウェアで実現する.
TTUの具体的処理
用語
SM - Streaming Multi-processor
Compression Block - BVHの部分木に対応するデータブロック
処理
SMから,対象のRay,Compression Blockへのポインタを含んだ命令を受け取る.
Schudulerに命令本体が転送される.Ray,Compression Blockへのポインタはそれぞれ別のLocal Storageに保管される.
Setup Unitに命令本体とCompression Blockへのポインタが転送される.
Setup Unitは、受け取った命令を基にLocal Storageに保存されたRayの情報と,受け取ったCompression Blockへのポインタを基にメモリから取得(キャッシュがヒットすれば即座に取得できる)したCompression Blockのデータを取得する.
Setup Unitは、各Traversal UnitにRayとCompression Blockを送信しCompression Block毎に並列に処理を実行する.疑似コードのOuter Loopに相当するLoopを回す.
Traversal Unitは、処理が完了するとStack Management Unit内部のResultキューにRayが衝突する可能性のあるプリミティブへのポインタを入れる.疑似コードのInner Loopに相当するLoopを回す.後述するがTraversal UnitはAABBへの衝突判定は行うがプリミティブへの衝突判定は行わない.プリミティブへの衝突判定は別のハードウェアで実現する.Resultキューに入ったプリミティブ(へのポインタ)は,プリミティブ専用の衝突判定処理を待機する.
Stack Management Unitは、処理が完了したTraversal Unitが有り,Local StorageにCompression Blockが存在するならばSchedulerにTraversal Unitが再度処理可能であることを通知する.
疑似コード
procedure outerTraversal(Ray, *bvhRoot) traversalStack traversalStack.push(bvhRoot) while traversalStack is not empty do *node = traversalStack.pop intersectedExternalNodes = innerTraversal(ray,node) traversalStack.push(intersectedExternalNodes) end do end
procedure innerTraversal(Ray, *blockRoot) intersectedExternalNodes localStack.push(blockRoot) while localStack is not empty do *node = localStack.pop() if ray intersects node then addToResultQueue(node) end else if node is TransitionNode then intersectedExternalNodes.add(node.externalNodePointer) end else then childNodes = node.childNodes sort childNodes localStack.push(childNodes) end return intersectedExternalNodes end do end
内部データ構造
やや具体的な話、実装によって多少変わってくると思う.
Compression Blockのアラインメントの例
struct Compression_Block{ // is restricted to Cache Size struct Node nodes[N]; };
Nodeのアラインメントの例
// double fixedは2ワード長の固定小数点型を想定 struct Node{ // Root Node char type_id : 2; char AABB_Bitmask : 6; double x_1; // 2word floating point data double x_2; double y_1; double y_2; double z_1; double z_2; struct Compression_Block *child_ptr; }; struct Node{ // Internal/Leaf Node char type_id : 2; char AABB_Bitmask : 6; char x_1; // 1Byte=256 step data char y_1; char y_2; struct Compression_Block *child_ptr; };
nodes 各ノードのデータ,連続配置される.順番は深さ優先,幅優先どちらでもよい.
type_id ノードの種別(root,leaf,transition)を表す
AABB_Bitmask 後述
x_1,x_2,y_1...等 座標の数値
child_ptr 葉ノードでなければ子ノードへのポインタ,葉ノードであればプリミティブへのポインタ(メモリ上を指す)
各ノードはAABBの情報(3次元直交座標系でおいては2つのベクトル,つまり6つの数値)を持つ.このうち,BVH全体のrootノードは全体座標系(Global Coordinate)における6つの数値を必ず持つ.しかしその子孫であれば,親ノードからの相対座標(Local Coordinate)で指定できる.さらに親ノードの数値を継承する場合、省略することが出来る.これらの工夫により数値の表現に必要なビット数が節約できる.上の構造体を模した疑似コードにおいて,AABB_Bitmaskは親ノードの数値を引き継ぐので省略する座標をビットマスクで表現している.
疲れたのでここまで