2009年04月13日

vectexをマルチスレッドレンダリング対応にする(2)

前回、Blenderのマルチスレッドレンダリングで排他制御を行う関数をテクスチャプラグインから使用できるようにしました。Blenderの内部ではBLI_lock_thread()、BLI_unlock_thread()という関数名になっていましたが、実際にエクスポートしている関数はlock_thread()、unlock_thread()という名前にしてあります。あとは、vectexのソースコードの中から、マルチスレッドレンダリング時に問題が起こっている部分を探し出して、その部分をエクスポートした関数で挟み込むだけです。

と、言葉で書くと簡単そうなのですが、このプログラムがどんな風に動作しているのか全然わからない状態では問題のある場所を探すのは難しそうです。それほど行数が多いわけではないのですが、vectexの内部で重要な役割を果たしているミップマップの処理は、レンダリング時のカメラの設定次第で(描画されるポリゴンの面積がどのような状態になるかによって)挙動が変化するので、ソースコードを眺めるだけで処理の流れを追うのは難しそうです。

ありがたいことに、vectexのソースコードには開発時に使われたデバッグ用のコードがそのまま残っています。コンパイル時にMakefileの一部を修正すると、ソースコードに埋め込まれたマクロが有効になって、Blenderを起動させたコンソールにデバッグ用の文字列を出力させることができます。

INC=-fPIC -shared -O -ansi -Iblender -Iagg-2.5/include -Iexpat-1.95.8/lib
# -DDEBUG

この部分で「#」を削除して、「-DDEBUG」をINCの定義のオプションに追加します。

INC=-fPIC -shared -O -ansi -Iblender -Iagg-2.5/include -Iexpat-1.95.8/lib -DDEBUG

pic090413_01.jpg pic090413_02.jpg

「DEBUG」定数が設定されるとvectex.hに書かれたマクロが有効になり、vectex.c、vectex_agg.cppの各関数内に埋め込まれたマクロがprintf()関数に展開されます。
pic090413_03.jpg
#ifdef DEBUG
#define MSG0(F)         printf(F);
#define MSG1(F,A)       printf(F, (A));
#define MSG2(F,A,B)     printf(F, (A), (B));
#define MSG3(F,A,B,C)   printf(F, (A), (B), (C));
#define MSG4(F,A,B,C,D) printf(F, (A), (B), (C), (D));
#else
#define MSG0(F)
#define MSG1(F,A)
#define MSG2(F,A,B)
#define MSG3(F,A,B,C)
#define MSG4(F,A,B,C,D)

#endif
例えば、vectex.cの中のcreate_tile_set()という関数の中には、2つのMSGマクロが埋め込まれています。
pic090413_13.jpg
...
MSG0("[create_tile_set]\n");
...   
MSG2("- created tile set max_pow=%d size=%d\n", instance->tile_set_max_power, size);
...
この関数create_tile_set()が実行されると、コンソール画面に「create_tile_set」「-created tile set max_pow= xxx size= xxx」というように表示されることになります。逆に言えば、コンソール画面に表示される内容から、ソースコードのどの部分が実行されたのかがわかるということになります。

ということで、早速、DEBUGモードでコンパイルされたvectexを使用して、vectexに付属のサンプルtest-static.blendでのレンダリングの過程を調べてみたいと思います。
Blenderを起動し、test-static.blendを開いた直後のコンソール画面です。
pic090413_04.jpg
[plugin_tex_getversion]
[plugin_getinfo]
[plugin_init]
[plugin_instance_init]
- plugin started enabled
[create_instance]
[create_backend]
- open ok
[create_tile_set]
- created tile set max_pow=8 size=9
> creating tile #8 res=256 [0,0]


スレッド数を1にしてレンダリングしてみます。
pic090413_05.jpg
- growing tile set max_pow=10 size=29 old_size=9
> creating tile #26 res=1024 [3,1]
> creating tile #11 res=512 [1,0]
> creating tile #27 res=1024 [3,2]
> creating tile #12 res=512 [1,1]
> creating tile #9 res=512 [0,0]
> creating tile #7 res=128 [0,0]
> creating tile #28 res=1024 [3,3]
> creating tile #13 res=1024 [0,0]
> creating tile #6 res=64 [0,0]
> creating tile #5 res=32 [0,0]
> creating tile #4 res=16 [0,0]
> creating tile #3 res=8 [0,0]
> creating tile #22 res=1024 [2,1]
> creating tile #23 res=1024 [2,2]
> creating tile #18 res=1024 [1,1]
> creating tile #17 res=1024 [1,0]
> creating tile #25 res=1024 [3,0]
> creating tile #21 res=1024 [2,0]
> creating tile #1 res=2 [0,0]
> creating tile #0 res=1 [0,0]
> creating tile #2 res=4 [0,0]
- growing tile set max_pow=11 size=93 old_size=29
> creating tile #70 res=2048 [5,1]
> creating tile #78 res=2048 [6,1]

1枚の画像をレンダリングするだけで、かなりたくさんのテクスチャが生成されているようです。

今度は、test-static.blendを読み込んでスレッド数4でレンダリングします。レンダリングが途中でフリーズした状態のコンソール画面へのデバッグ出力です。
pic090413_06.jpg
[plugin_tex_getversion]
[plugin_getinfo]
[plugin_init]
[plugin_instance_init]
- plugin started enabled
[create_instance]
[create_backend]
- open ok
[create_tile_set]
- created tile set max_pow=8 size=9
> creating tile #8 res=256 [0,0]
- growing tile set max_pow=10 size=29 old_size=9
> creating tile #27 res=1024 [3,2]
> creating tile #26 res=1024 [3,1]
> creating tile #11 res=512 [1,0]
> creating tile #7 res=128 [0,0]
何回かレンダリングすると、その時々で出力される内容は変わりますが、「creating tile ...」というようにデバッグ出力している関数、あるいはそこから呼ばれている関数を実行している途中でレンダリングがフリーズしているようです。

「creating tile」という文字列が出力されるMSGマクロは一つだけで、449行目のget_tile()という関数の中です。get_tile()関数の中を見てみると、memory_alloc()、memcpy()、memory_free()などのメモリ処理に関わる関数がたくさん使われています。
pic090413_07.jpg pic090413_08.jpg

この関数の中のどこかで問題が起こっていそうなのでそれを調べてみてもいいのですが、とりあえずはこの関数を呼び出している部分を探して、get_tile()関数の呼び出し部分をlock_thread()、unlock_thread()で囲んでみたいと思います。検索してみると、get_tile()関数を呼び出しているのは507行目と547行目の2ヶ所だけで、sample()という関数の中です。
pic090413_09.jpg pic090413_11.jpg
...
lock_thread(LOCK_IMAGE);
int index = get_tile(instance, x, y, power);
unlock_thread(LOCK_IMAGE);
...
このようにlock_thread()、unlock_thread()関数、LOCK_IMAGE定数を使うには、これらのプロトタイプ宣言が書かれているutil.hをインクルードする必要があります。
...
#include "plugin.h"
#include "util.h"

#include "vectex.h"
...
vectex.cを以上のように修正し、vectexのソースコードのblenderフォルダ内にあるutil.hを前回作成したBlender修正版のものに置き換えれば、コンパイルの準備が完了です。コンパイルしてできたvectex.soファイルをBlender修正版で読み込んでtest-static.blendをレンダリングしてみると、スレッド数4でも問題なくレンダリングが完了しました。
pic090413_12.jpg

lock_thread()、unlock_thread()関数を使用する場所については、get_tile()関数の中をもっと調べて本当に問題の起こっている部分だけを囲むようにした方がいいのかもしれませんが、動作に問題がなければこのままでもよさそうです。

こちらに今回の修正版vectexを置いておきます。

vectex_modified2.zip

このvectexは前回作成した修正版Blender専用となります。通常のBlenderからは使用できませんのでご注意ください。
posted by mato at 22:03| Comment(0) | Blender | このブログの読者になる | 更新情報をチェックする
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。