2009年03月25日

プラグインリポジトリのテクスチャプラグインをマルチスレッド対応に

前回でテクスチャプラグインをマルチスレッドレンダリングに対応させるために必要なことが大体分かりましたので、プラグインリポジトリにあるテクスチャプラグインをマルチスレッドに対応できるかを試してみました。結果を先に言うと、ほとんどのものが前回の記事で書いた修正内容だけでマルチスレッドレンダリングできるようになりました。

プラグインリポジトリにあるプラグインの多くは GPL、Public Domainなどの再配布、改変を許可するライセンスで公開されていますが、一部そうなっていないものもあります。ライセンス上問題ないものについて、修正済みのソースコード、コンパイル済みのバイナリファイル(Windows 32bit、Linux 32/64bit)をまとめてみました。

texture_plugins_modified.zip

この中に含まれるのは、brick、brick1、circdots_rgb、dots2、led、pattern、pie、r_weave、refract、rings2、rtilings、sarah0、scales、sinus、spirals、t_bricks、t_clouds、t_marble、t_marble_terrain、t_terrain、t_wood、trellis、water、wbricks の24のテクスチャプラグインになります。
各プラグインのソースファイルが入っているフォルダに、「changes.txt」という名前のファイルを入れてあります。これは、オリジナルのソースコードと今回修正したソースコードの内容の違いを記述した差分ファイルで、前回使ったMeldというツールで作成しています。このファイルを見ていただくと、どこをどう修正したのかが一目でわかると思います。
pic090324_01.jpg pic090324_02.jpg

ほとんどのものは、
  1. plugin_tex_doit()関数の引数にresult[]配列へのポインタを追加し、プロトタイプ宣言もそれに合わせて変更
  2. plugin_instance_init()関数とそのプロトタイプ宣言を追加し、plugin_getinfo()関数の中に関連するコードを追加
という修正内容だけで済んでいますが、一部にはそれだけではうまくいかないものがありました。ここから先は、それら問題の起こったプラグインにどのような修正をしているかを見ていきたいと思います。

実際に問題が起こったプラグインは、dots2、led、trellis、ceramictilesの4つですが、このうちceramictilesはライセンスの関係で今回は外してあります。ちなみに修正後に起こった問題の具体的な内容ですが、dots2、ledの2つのプラグインでは修正後にプレビュー画面が真っ黒になり、プラグインが正常に動作しなくなりました。trellis、 ceramictilesの2つのプラグインのでは、上記の修正を行った後もノイズがなくならないという感じになります。もしかすると、私が気づいていないだけで、これ以外にもまだノイズが発生するものがあるかもしれません。

まず、問題が起こったプラグインのうちのdots2、ledについてです。
このdots2のソースファイル「dots2.c」を見ると、plugin_tex_doit()関数の中から別の関数calc_dot_value()を呼び出して、計算処理をそちらで行っています。
void calc_dot_value(Cast *cast,float x,float y)
{
  ....
}

int plugin_tex_doit(int stype, Cast *cast, float *texvec, float *dxt, float *dyt)
{
  ...
  calc_dot_value(cast,x,y);
  ...
}

プラグインリポジトリにあるプラグインでは、plugin_tex_doit()関数から別の関数を呼び出すということはごく普通に行われています。問題が起こったプラグインではすべてこのような構造になっていて、さらにdots2、ledの2つのプラグインでは、この呼ばれている関数の中でresult[]配列が使われているという点が共通しています。ただし、ledの方では関数内に直接result[]配列の記述があるのではなく、呼ばれている関数内で使われているマクロの中にresult[]配列の記述があります。
pic090324_03.jpg pic090324_04.jpg

plugin_tex_doit()関数の引数にresult[]配列へのポインタを追加する修正をした場合、そのままだとplugin_tex_doit()関数内のresult[]配列と、呼び出されている関数内のresult[]配列は名前は同じでもまったく別のものとなっています。
このため、本来の計算処理が行われずにプレビュー画面が真っ黒になってしまいます。

void calc_dot_value(Cast *cast,float x,float y)
{
  ....
 result[0]=0.;
  ....
}

int plugin_tex_doit(int stype, Cast *cast, float *texvec, float *dxt, float *dyt, float *result)
{
  ...
  calc_dot_value(cast,x,y);
  ...
}
plugin_tex_doit()関数以外の関数についても、result[]配列を使用している関数すべてにresult[]配列へのポインタを引数で渡すようにすればこの問題は解決します。
void calc_dot_value(Cast *cast,float x,float y, float *result)
{
  ....
 result[0]=0.;
  ....
}

int plugin_tex_doit(int stype, Cast *cast, float *texvec, float *dxt, float *dyt, float *result)
{
  ...
  calc_dot_value(cast,x,y, result);
  ...
}

次にtrellis、ceramictilesについてです。
この2つのプラグインで問題になるのはグローバル変数を使っていることです。 trellisのソースコード「trellis.c」を見てみると、126行目あたりに次のような記述があります。
/* We use globals to avoid passing lots of float args around every call */
float x, y, z;            /* Normalised coordinates */
float t;            /* thickness */
float t2;            /* 1/2 thickness */
int multi;            /* Use multi-colours? */
これらのグローバル変数は、plugin_tex_doit()関数から呼ばれるcalc_trellis()とcalc_cube()という2つの関数の中で何度も使われています。これらの変数は、result[]配列のようにBlenderに渡される強度、色などのデータそのものを保持するわけではありませんが、複数のスレッドがこれらのデータを上書きし合う状況では計算結果が本来のものと全く違ったものになってしまい、結果としてノイズのようなものが起こることになります。
int plugin_tex_doit(int stype, Cast * cast, float *texvec, float *dxt, float *dyt) {
    ...
        result[0] = calc_trellis(cast->pattern);
    ...
        result[0] = calc_cube((float) rand() / (float) RAND_MAX);
    ...
}
今回は、これらのグローバル変数をplugin_tex_doit()関数内のローカル変数に修正し、calc_trellis()とcalc_cube()の両関数の引数としてこれらの全ての値を渡すようにしてみました。
int plugin_tex_doit(int stype, Cast *cast, float *texvec, float *dxt, float *dyt, float *result)
 {
    float x, y, z;            /* Normalised coordinates */
    float t;            /* thickness */
    float t2;            /* 1/2 thickness */
    int multi;            /* Use multi-colours? */


    ...
        result[0] = calc_trellis(cast->pattern, x, y, z, t, t2, multi);
    ...
       
        result[0] = calc_cube((float) rand() / (float) RAND_MAX, x, y, z, t, t2, multi);
    ...
}
私がソースコードに対して行った修正は、以上のような感じです。
ところが、実はソースコード自体に問題がなくても、コンパイル時に問題が起こったものがいくつかあります。そのため、今回修正したソースファイルを使ってコンパイルを行う場合、いくつかのプラグインでコンパイルに失敗する可能性があります。
次回は、それらのコンパイル時の問題への対応方法について書くつもりです。
posted by mato at 02:09| Comment(0) | Blender | このブログの読者になる | 更新情報をチェックする
×

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