Syoyo Fujita's Blog

raytracing monte carlo

Month: December, 2006

グラフィックスの学会 2

http://lucille.atso-net.jp/blog/?p=164

の続きです。

きぬ先生からのリクエストを反映するのをすっかり忘れていましたので、
反映分を含め追記しておきます。

IEEE Visualization(アイトリプルイー•ビジュアリゼーション)
http://vis.computer.org/

可視化技術の学会。私は可視化のコミュニティはよくわかりませんが、
たぶん可視化では最高峰の学会になるのではないでしょうか。

可視化では、主に医療画像処理(ボリュームレンダリングなど)や、
エンジニアリング(流れの可視化)などが取り上げられます。
それらの GPU による処理の高速化なども見受けられます。

CGI(Computer Graphics International)
http://www.inf.ufrgs.br/cgi2007/

結構古くからある学会のようです。International とありますが、
どちらかというとエイジャが中心です。
とくに学会色に特徴はみられません(レンダリング系をみる限りは)。
参加するときは、帰りがけにアジア通貨を買うのを忘れずに?…

SCCG(Spring Conference on Computer Graphics)
http://www.sccg.sk/pages/welcome/welcome.php

WSCG が冬に開催されるのに対して、春に開催されているらしい。
大域照明の研究で有名なブタペスト工科大からの論文がいくつかみられます。
WSCG と同じくヨーロッパで開催されているようですが、
相変わらずヨーロッパのどこで開催されているのかよくわからない、
はぐれメタルな学会です(個人的感想)。
帰りがけにユーロ通貨を買うのを忘れずに?…

Graphite(グラファイト)
http://www.gmm.fsksm.utm.my/~graphite2006/index.html

オーストラリアなどの南半球で開催される学会です。
冬場に暖かいところに行きたいときにはいいかも。
帰りがけに鉱物資源を買うのを忘れずに?…

Afrigraph(アフリグラフ)
http://www.afrigraph.org/

アフリカで開催されるグラフィックスの学会。
これから大きく発展しそう?…
帰りがけにゴールドやダイヤモンドやランド通貨を買うのを忘れずに?…

こまごまとした学会はまだありますが、これで中堅どころまではだいたいカバーしたかな?
ほかにもこいつは取り上げるべきだろうという学会がありましたらお知らせください。

Monster House

今月の CG World [1] に、Monster House [2] のメイキングの取材記事がありました。

Monster House は、全編 Arnold レンダラでレンダリングされています。
Arnold レンダラといえば、グローバルイルミネーションレンダラの元祖と言えるレンダラです。
今回、SPI は Monster House で粘土(クレイ)っぽい質感を表現したかったらしく、
ちょうど Arnold がその要求にマッチしていたので、Arnold レンダラの作者 Marcoss を SPI
に呼び出して Arnold によるレンダリングパイプラインを構成したとのこと。

ところで、日本の雑誌なのに日本人で唯一(?)の arnold シェーダ書きである masuo さんが
今回の Monster House の記事に登場していないのはなんでなんだろう?…

さて、Arnold レンダラはパストレベースのレンダラです
(昔からずっとパストレベースのままなのかな? 双方向パストレとか導入していないのかな?)。
パストレは粘土調の質感を出すのは非常に得意です。
なので Arnold が粘土調を出すのが得意というよりは、パストレが粘土調を出すのが
得意といったほうが正確でしょう。
が、パストレは逆に言うと粘土調でなくフォトリアルなシャープな質感を出すのは苦手なんですけどね、、、

いずれにせよ、Monster House はちょうどそのようなパストレの性質と
アートディレクションの要求がマッチしたよい結果だったようです。

最近はプロダクションでも GI レンダリングを導入し始めてはいますが、
Monster House や Pixar の映画のように、GI つかってリアルなライティング計算を行うんだけど、
でもどちらかというと結局求められるのはノンフォト系、みたいな流れが多い気がします。

Maxwell render などにみられる、双方向パストレや MLT をベースとした
真のフォトリアルな GI レンダラというのは、まだまだ需要がないようです。

lucille はフォトリアルな真の GI レンダラを目指しています。
(まあ中身は BPT or MLT or ERPT ですが)
今はマーケットが無くても、197 年後くらいには需要がありそうですので、
精進して作り続けていきたいと思います。

[1] http://www.wgn.co.jp/cgw/
[2] http://www.sonypictures.com/homevideo/monsterhouse/index.html

radium renderer

http://www.radiumrenderer.com/

Java で MLT なレンダラ。ERPT にも挑戦中みたい。
狙うはクロスプラットフォームで Indigo render の置き換えらしい。

最近はまたレンダラが多く現れはじめましたね。
うーむ、新レンダラ界の神となるのは一体誰か… !?

Vector support of LLVM

LLVM は SSE と AltiVec によるベクトル処理をサポートしていることがわかりました。


/* vec.c */
#include 

void
mudah()
{
        __m128 a, b, c;

        a = _mm_add_ps(b, c);
}

int
main(int argc, char **argv)
{
        mudah();
}

こんなコードを、llvm-gccでコンパイルして(x86 darwin 上)まずはバイトコードを生成します。


$ llvm-gcc -c -emit-llvm -msse2 vec.c
$ llvm-dis vec.o
$ cat vec.o.ll

; ModuleID = 'vec.o'
target datalayout = "e-p:32:32"
target endian = little
target pointersize = 32
target triple = "i686-apple-darwin8"

implementation   ; Functions:

void %func() {
entry:
        %tmp = alloca , align 16
        %a = alloca , align 16
        %b = alloca , align 16
        %c = alloca , align 16
        %__B = alloca , align 16
        %__A = alloca , align 16
        %tmp2 = alloca , align 16
        "alloca point" = cast int 0 to int
        %tmp = load * %b
        store  %tmp, * %__A
        %tmp1 = load * %c
        store  %tmp1, * %__B
        %tmp3 = load * %__B
        %tmp4 = load * %__A
        %tmp5 = add  %tmp4, %tmp3
        store  %tmp5, * %tmp2
        %tmp6 = load * %tmp2
        store  %tmp6, * %tmp
        br label %bb

bb:             ; preds = %entry
        %tmp7 = load * %tmp
        store  %tmp7, * %a
        br label %return

return:         ; preds = %bb
        ret void
}

int %main(int %argc, sbyte** %argv) {
entry:
        %argc_addr = alloca int
        %argv_addr = alloca sbyte**
        %retval = alloca int, align 4
        "alloca point" = cast int 0 to int
        store int %argc, int* %argc_addr
        store sbyte** %argv, sbyte*** %argv_addr
        call void (...)* cast (void ()* %func to void (...)*)( )
        br label %return

return:         ; preds = %entry
        %retval = load int* %retval
        ret int %retval
}

add インストラクションが、float 4 個の型の変数に対して行われています。
バイトコードから、Core2 プロセッサをターゲットとしてアセンブラに変換してみます。


$ llc -mcpu=core2 vec.o
$ cat vec.o.s

.text
        .align  4
        .globl  _func
_func:
        subl $124, %esp
        movaps 64(%esp), %xmm0
        movaps %xmm0, 16(%esp)
        movaps 48(%esp), %xmm0
        movaps %xmm0, 32(%esp)
        addps 16(%esp), %xmm0
        movaps %xmm0, (%esp)
        movaps %xmm0, 96(%esp)
LBB1_1: #bb
        movaps 96(%esp), %xmm0
        movaps %xmm0, 80(%esp)
LBB1_2: #return
        addl $124, %esp
        ret


        .align  4
        .globl  _main
_main:
        subl $28, %esp
        fnstcw 14(%esp)
        movb $2, 15(%esp)
        fldcw 14(%esp)
        movl 32(%esp), %eax
        movl %eax, 24(%esp)
        movl 36(%esp), %eax
        movl %eax, 20(%esp)
        call _func
LBB2_1: #return
        movl 16(%esp), %eax
        addl $28, %esp
        ret

        .subsections_via_symbols

addps 命令など、SSE2 命令が使われています。期待どおりです。

では、SSE2 命令がない CPU では?


$ llc -f -mcpu=i386 vec.o
$ cat vec.o.s

.text
        .align  4
        .globl  _func
_func:
        subl $124, %esp
        flds 76(%esp)
        flds 68(%esp)
        flds 72(%esp)
        flds 64(%esp)
        fxch %st(3)
        fstps 28(%esp)
        fxch %st(1)
        fstps 20(%esp)
        fstps 24(%esp)
        fstps 16(%esp)
        flds 60(%esp)
        flds 52(%esp)
        flds 56(%esp)
        flds 48(%esp)
        fxch %st(3)
        fstps 44(%esp)
        fxch %st(1)
        fstps 36(%esp)
        fstps 40(%esp)
        fstps 32(%esp)
        flds 20(%esp)
        flds 28(%esp)
        flds 24(%esp)
        flds 16(%esp)
        fxch %st(3)
        fadds 36(%esp)
        fxch %st(2)
        fadds 44(%esp)
        fxch %st(1)
        fadds 40(%esp)
        fxch %st(3)
        fadds 32(%esp)
        fxch %st(1)
        fstps 12(%esp)
        fxch %st(1)
        fstps 4(%esp)
        fxch %st(1)
        fstps 8(%esp)
        fstps (%esp)
        flds 12(%esp)
        flds 4(%esp)
        flds 8(%esp)
        flds (%esp)
        fxch %st(3)
        fstps 108(%esp)
        fxch %st(1)
        fstps 100(%esp)
        fstps 104(%esp)
        fstps 96(%esp)
        #FP_REG_KILL
LBB1_1: #bb
        flds 108(%esp)
        flds 100(%esp)
        flds 104(%esp)
        flds 96(%esp)
        fxch %st(3)
        fstps 92(%esp)
        fxch %st(1)
        fstps 84(%esp)
        fstps 88(%esp)
        fstps 80(%esp)
        #FP_REG_KILL
LBB1_2: #return
        addl $124, %esp
        ret


        .align  4
        .globl  _main
_main:
        subl $28, %esp
        fnstcw 14(%esp)
        movb $2, 15(%esp)
        fldcw 14(%esp)
        movl 32(%esp), %eax
        movl %eax, 24(%esp)
        movl 36(%esp), %eax
        movl %eax, 20(%esp)
        call _func
LBB2_1: #return
        movl 16(%esp), %eax
        addl $28, %esp
        ret

        .subsections_via_symbols

めでたく? SSE2 命令を使わずに X87 命令を使うようなアセンブラが出力されました。

さらに、ターゲットを AltiVec 対応の PPC にすると、


$ llc -f -march=ppc32 -mcpu=g4 vec.o
$ cat vec.o.s

.text
        .globl  _mudah
        .align  4
_mudah:
        mfspr r2, 256
        oris r3, r2, 12288
        mtspr 256, r3
        addi r3, r1, -48
        lvx v2, 0, r3
        addi r3, r1, -96
        stvx v2, 0, r3
        addi r4, r1, -64
        lvx v2, 0, r4
        addi r4, r1, -80
        stvx v2, 0, r4
        lvx v3, 0, r3
        addi r3, r1, -112
        vaddfp v2, v3, v2
        addi r4, r1, -16
        stvx v2, 0, r3
        stvx v2, 0, r4
LBB1_1: ;bb
        addi r3, r1, -16
        lvx v2, 0, r3
        addi r3, r1, -32
        stvx v2, 0, r3
LBB1_2: ;return
        mtspr 256, r2
        blr


        .globl  _main
        .align  4
_main:
        stwu r1, -80(r1)
        stw r1, 76(r1)
        mflr r11
        stw r11, 88(r1)
        stw r3, 72(r1)
        stw r4, 68(r1)
        bl _mudah
LBB2_1: ;return
        lwz r3, 64(r1)
        lwz r11, 88(r1)
        mtlr r11
        lwz r1, 76(r1)
        addi r1, r1, 80
        blr

        .subsections_via_symbols

AltiVec 命令(vaddfp)が使われるようになっています。
(アセンブラ自体は効率悪そうだけど…)

これはつまり、、、

o SSE なプログラムを書いておけば、バイトコードから SSE コードとスカラコード、AltiVec コードを出力できる

ということになりますね。パフォーマンスはともかくとして、SSE と AltiVec、SIMD とスカラというふうに別々にプログラミングしなくてよいのはいいかも(もう最初っから SSE SIMD でプログラミング)。

The LLVM Compiler Infrastructure

nyaxt さんの日記で知りました。

The LLVM Compiler Infrastructure
http://llvm.org/

要は、VM の API + VM ランタイム + JIT コンパイラ + バイトコード後のプログラムの各種最適化処理を含んだもの、という感じでしょうか。

LLVM のバイトコードを出力するように変更を加えた gcc も提供されているため、

o C プログラム -> LLVM バイトコード -> インタプリタ実行
o C プログラム -> LLVM バイトコード -> JIT コンパイル

という一連の流れまでサポートしています。

Mac OS X の時期バージョン Leopard では、OpenGL(の実装)が LLVM 上で動くようになっています。
http://lists.cs.uiuc.edu/pipermail/llvmdev/2006-August/006492.html

OpenGL のプログラムでは、パラメータが多いため(フォグが有効か?など)、
あらかじめどの設定にも適した最適化バイナリを作っておくことが難しい。
ただ、一度設定されれば、あとはずっと不変の場合が多い。
そこで、一度 OpenGL プログラムを LLVM のバイトコードにしておき、
実行時に必要に応じて JIT コンパイルすることでパフォーマンスの改善を期待する、
とあります。

なるほど、OpenGL やシェーディング処理などはこのフレームワークに適していそうですね。

リンク先の一文にある specialization というのも、シェーダの世界では
MSR による先駆的な研究があります。

Brian Guenter, Todd B. Knoblock, Erik Ruf.
specializing shaders.
SIGGRAPH ’95. Pages: 343 – 350, 1995.
http://citeseer.ist.psu.edu/guenter95specializing.html

ここで言われている LLVM の specialization というのは、specializing shaders の
specialization とはもしかしたら違うのかもしれませんが、基本的には、
よく実行される部分をキャッシュしたりひとまとめにしておくことで処理の高速化をはかる、
というような意味合いだと思います。

シェーディングを LLVM 化するとどうなる?…

lucille のシェーダ言語方式は、

RenderMan SL -> C プログラム -> ネイティブコード

という形を取っていますが、
C コンパイラを LLVM バイトコードを出力するコンパイラに変更することで、
LLVM 上で動かすことができるでしょう。
もしくは、RenderMan SL から直接 LLVM バイトコード(LLVM アセンブラ)を
出力するパーサを書くのもよさそうです。

VM 化することの利点として、

o C シェーダにバグがあるとレンダラそのものが落ちる(mental ray のように)、ということはない
o バイトコード自体はクロスプラットフォームなので、プラットフォームごとにコンパイルする必要がなくなる

という点があげられると思います。

また、バイトコード後でも LLVM の各種最適化処理の恩恵を受けられる、というのもあります。
(自分で最適化処理のコードを書かなくて済むのは大きい。
LLVM はプラグインという形で自作の最適化処理を追加することもできるようです)

コンパイル前にコードを最適化するよりも、
実行時のシェーディング処理の動きをみて最適化処理をかけながら(Profile-Guided optimization)、
JIT コンパイルを行ったほうが効果が大きい場合は(OpenGL の場合と同じでシェーダもたぶんそうだと思う)、
VM 化によるパフォーマンス低下はネイティブコードよりもそれほど無いと考えています。

また、LLVM は VM 自体を拡張できるそうなので、
シェーダでよく使われる関数を VM のインストラクションに追加することで、
パフォーマンスの改善が期待できそうです。
(たとえば trace() インストラクションを追加するなど)

多くの RenderMan レンダラではシェーダ言語を中間言語方式で実装していますが、
このようにシェーダ言語を中間言語方式で動かすことの利点は、
このようにシェーディングに特化したインストラクションセット + クロスプラットフォーム
実行可能であると思うので、VM ランタイムを自分でわざわざ書かなくとも、
LLVM のインフラを使ってそれを実現することができるのは大きいと考えます(しかも JIT つき)。

問題は、LLVM 自体のライブラリがでかいことでしょうか。
気軽には自作のプログラムに LLVM をリンクさせたり、LLVM に拡張を追加するのは難しそうです。

API 利用のサンプルなどは多いので、理解はしやすそうではあります。