FPGAでラスタライザ再考察
仕事が忙しく、ずいぶん間が空いてしまいましたが、再びGPU考察です。
ポリゴンのお絵かきにラスタライザは必須です。
以前、ソフトやシミュレーションレベルでは軽く実験しましたが、その際にお世話になったのが下記のページ。
http://ushiroad.com/rasterizer-refs/
http://memo.render.jp/tekitou-hon-yaku-memo/re
で、FPGA化踏まえて再考察。
Zynq UltraScale+ には Mali-400 というARM社のGPUがつくらしいですし、同じようなもの作るメリットが無いので、ちょっと変わったものを作ろうと考えている。
手元に「デジタル数値演算回路の実用設計」という本があり、パースペクティブ補正用のの逆数計算で非常にお世話になった本である。
この本にビデオ映像のページめくりのような面白いリアルタイム画像変形が載っており、以前作ったテクスチャキャッシュと組み合わせれば効率よくいろんな変形ができそうだなと考えている。ここにGPU色を出した自由度の高い変換を考え中だ。
当方μITRONのRTOS書いて遊んでいたころから、リアルタイムシステムには馴染んでおり、GPUのハードリアルタイム処理に使いにくい事には前々から思うところがあったので、今回は、ハードリアルタイムに耐えるGPU。すなわちビデオ映像の加工に使えてインタラクション性の高いGPUを目指してみたいと思う。
具体的には、「デジタル数値演算回路の実用設計」同様に、出力の走査順に絵を作っていく事を考えている。これはレイトレーシングの分野とかでは普通にある演算順序ではあるが、リアルタイム保証のやりやすい考え方に思える。
ただ、その場合の課題は、同時に描画可能なポリゴンの数だけラスタライザが必要!
ってことである。
で、今日はラスタライザの考察です。
ラスタライザのお仕事は、とかく物理的なピクセルと対応づくなX-Y座標に対して
・そのピクセル座標はポリゴンの範囲か?
・そのピクセル座標の色は? 法線は?
・そのピクセル座標に対応するテクスチャのUVアドレスは?
といった、実際にピクセルシェーディングするX-Yピクセル座標(物理的なモニタのピクセルに対応)から、CGの論理座標への変換が多数発生します。
で、上記は全部
a*x + b*y + c
を計算することに他ならないわけで、同じ演算器ずらっと並ぶことになります(多分)。
加えて、先のURLにあるように、境界判定は
> 大部分の人々がそのような状況ですることは、倍精度浮動小数点数を使うことです。
と、固定小数点が基本で、FPGA向けです。
これは、浮動小数点でやっちゃうと、隣接するポリゴン間で、隙間ができたり、重なりができたりするからです(隙間はもちろん、重なりもαブレンドするときは問題)。
そして、
と、ある。記事はソフトの計算なので、16bit*16bitして32bitの結果から16bit引っ張り出しているが、FPGAならいるところしか使わないので、つまるところ16bit精度でよいとなる。
(2018/01/14追記、すみませんやっぱり32bit必要でした。ピクセル12bit サブピクセル4bitの場合、傾き計算で32bit精度が要りますね)。
色情報はもともと8bit精度しかないし、UV座標も最後は画面同様の精度であるので、FPGAならそれぞれの項目で必要な最終結果にあう精度を用意すればよい(ただ、uv計算とかパースペクティブ補正で逆数を扱い場合の固定小数点の範囲は注意が必要そう)。
加えて
> 三角形の辺Eが(X, Y)から(X+dx, Y+dy)を結ぶとき > E(x, y) = (x - X) * dy - (y - Y) * dx > として、 > 全ての辺について E(x, y) <= 0 なら(x, y)は三角形の内部にある(y軸が下向き、辺が時計回りの場合)
というわかりやすい関係から、隣接するポリゴンの境界チェックの演算器は共用可能です(符号ひっくり返すだけ)。
で、a*x + b*y + c をどこまでコンパクトに計算できるか書いてみました。
> ですが、一度どこかの点についてE(x, y)を計算してしまえば > E(x+1, y) = E(x, y) + dy > E(x, y+1) = E(x, y) - dx > という計算量の少ない式で、
はい、まったくそのとおりですね(以前の実験でもやりました)。
つまりRTLにすると
reg signed [WIDTH-1:0] reg_value;
always @(posedge clk) begin
if ( reset ) begin
reg_value <= {WIDTH{1'b0}};
end
else if ( cke ) begin
reg_value <= reg_value + dx;
if ( x_first ) begin
reg_value <= reg_value + dy_stride;
if ( y_first ) begin
reg_value <= reg_value + offset;
end
end
end
end
で、すんじゃうわけです。
合成結果は LUT16個のみで、Maximum Frequency: 630.517MHz だそうです。
小型化するにはコツがあって、XILINXのCLBはLUTの後に加算のためのキャリーチェーンがあるので、リセット以外の論理が加算の手前に来るように論理を考える必要があります。
なので、
・ 一旦リセットしてから初期値を足す(1フレームに1サイクルだけ余分が必要)
・ 右端から次のラインに走査する時のストライドを別途計算しておく
・ハンドシェークはCEで止めることで行う
などの工夫が必要ですが、特に困る制約ではないわけです。
なんかいろいろ期待が持てそうですね。
ただ問題は、走査に先立ってこれらのパラメータの計算をやる部分が必要でして、ZynqのARMにやらせてもいいのですが、ここは高速なシェーダーが書きたいなと考える次第です。
[追記]
上記の演算器を3つ並べてVeritakで三角形を描いてみました。
とりあえず目論見どおりには動きました。
ただ、精度に関しては安直に書くには画素サイズの2倍精度が要りそうですね。上記はVGAサイズですが、20bit幅で計算しています。
同じロジックでグーローシェーディングしてみました。
うーん、何も考えずに外積で平面求めたら精度に苦労してしまった。
領域内での精度が8bitあればよいだけなので、うまいこと調整が必要そうだ。
結論から言うと、
・傾きMAXは1pixelで 0→255 or 255->0
・傾きの最小精度は、画面の端から端の距離で最後のpixelで1増える
・ポリゴン範囲外でのオーバーフロー/アンダーフローは無視してよい
なので、必然的に精度が決まりそうだ。
テクスチャ座標の場合はバイリニア補完があるので小数点以下の精度の話も出てくるが同様に上下限を決めることはできそうだ。
ただ、テクスチャも画面サイズ程度の以上を用意しても意味が無いので、結果的に画面サイズ表すのに必要なbit数の2倍程度あれば一通りやりたいことには足りる気がする。
« FPGAでの倍速ALUの考察 | トップページ | ポリゴン描画実験 »
「FPGA」カテゴリの記事
- LUT-Networkの蒸留とMobileNet風構成とセマンティックセグメンテーション(2020.03.09)
- LUT-Networkの蒸留(Distillation)について(2019.12.29)
- FPGAでのDNN(Deep Neural Network)の整理(LUT-Netまとめ)(2019.12.15)
- LUT-NetのFPGAリソースについて(2019.12.08)
- MNIST認識のリアルタイム動作環境更新(2019.09.02)
この記事へのコメントは終了しました。
コメント