view presen/osc2010.ind @ 0:9887d6547572

presen not done.
author tkaito
date Wed, 29 Sep 2010 22:55:09 +0900
parents
children
line wrap: on
line source

-title: 2009-09-26 (土)   Cerium を用いた PS3 でのゲームの作り方

--Linux 上でのゲームフレームワーク

   Cerium Task Manager

   Blender / SceneGraph

--Cell ってどうなの?

   結局、Video Chip に触れない
   SPUでレンダリング 
     ソフトレンダリングにしては速いが...

   新型PS3では、Linux は使えない
   とかいろいろだめ

--Cell Architecture

<img src="photo/Cell-main2.png" alt="pipeline" height="200">

Linux 側から使える SPE は 6 個
SPE は 256KB の Local Store (LS) 
SPE からメインメモリへ直接アクセスできない (DMAを使う)
SPE は 128 ビットレジスタを 128 個持っている


--楽するための並列プログラム

トリビアルなプログラムでも並列実行する必要がある

Single Thread でもPPE がクソ遅いので、SPEで実行するべき

例えば、

    Word Count

--プログラムを Task に分割

Task には依存関係がある

Open/CL , Spurs Engine 

--並列性

	データ並列
	パイプライン

結局、これしかないらしい。

--階層的並列プログラミング

      Row Level での並列性 
        Vector/Streaming
        結局、データ並列とパイプライン

      SPE Level での
        データ読み込み、 実行、データ書込
        のパイプライン並列

      High Level での並列性
        Rendering
        Scene Graph

-- データ読み込み、 実行、データ書込 

<img src="photo/pipeline.jpg" alt="pipeline" height="300">

パイプラインバッファはいくつ?

--Task は良いけど、データはどうするの?

データを右から左に...
     処理中に必要なデータがメインメモリ上にある
     でも、SPE には、256k しかメモリがない

メインメモリ(256MB、広くはないが十分)から、256Kに必要なだけキャッシュする
     ハードウェアでやれよ!
     普通の並列ハードウェアはキャッシュになっている
     IBMはそう提案したらしいが、SCEの人が拒否したらしい

なので、自分でキャッシュする必要がある

--Cerium Task Manager

       Open/GL Mesa に Cell driver を書いたが、
       メインメモリに依存しすぎ
       Task base で書く必要がある
       Redering Engine も自前で一つ持っていて良い
       SPEは256Kなのでコード管理も必要
       SPURSは公開されないらしい

そんな経緯で作成することに...

--Crium Task Manager の特徴

       PPU/SPUで、Task の互換性がある
          SPU上の最適化は当面禁止
       OS X 上でも動く
          コードのデバッグはOS X 上でやる
          並列化とチューニングだけPS3上で行なう

       SPU上のメモリをCode と Data を Hash とメモリリストで管理する
       SPUに入り切らない巨大なTaskでも実行できる

--ゲームの作り方

       Blender で、3Dモデリング
            階層化、グルーピング

       これを、Blender の Python plugin で XMLに変換
            画像/Texture もXMLに埋め込まれる

       XMLを読み込み SceneGraph を作る

<img src="photo/cerium_sg_tree.jpg" alt="sg" >

--Blender から SceneGraph 用の xml 生成

PythonScript の導入

<ol>
<li> Blender をダウンロードしてインストール</li>
<li> export_xml.py を用意する</li>
<li>"/Applications/blender-version/blender.app/Contens/MacOS/.blender/scripts
"
           以下にexport_xml.py をコピー</li>
<li>Blender を起動すると File -> Export に Libps3 (.xml) が追加される</li>
</ol>

ゲームの初期化部分でcrateFromXMLfileを呼ぶ
<font size="4"><pre>
void
game_init(TaskManager *manager, int bg)
{
   sgroot->createFromXMLfile(manager, "xml_file/SG.xml");
...
</pre></font>

--SceneGraph

       階層化された3Dオブジェクト
       子供の向きを決定する変換行列
       Camera
       背景
       入力デバイス

SceneGraph のノードには、
       MoveTask
       Collision Task 

       がある

--ゲームとは、

       SceneGraph を
         MoveTask
         CollsionTask
      で書き換えていく
         MoveTask, CollsionTask は、ステートパターンで
         入れ換えられる

これで、すべて書ける。

--つまり、

    SceneGraph の構築
    MoveTask
    CollsionTask
だけを書けば、あとは、Cell が自動的に並列に実行してくれる

--Task の作り方

SchedTask を継承した class を作る
<pre>
class SpeTask : public SchedTask {
    SchedConstructor( SpeTask );
    int run(TaskManager *manager, void *rbuf, void *wbuf);
};
</pre>

run 関数は Task における main 関数のようなもの
SchedConstructor() で class 名を登録する
(C++のnew を使うとメモリを食われる...)

SchedRegisterTask(TASK_SPE, SpeTask) で SpeTask に TASK_SPE という ID をつけて
登録します


--Task の作り方(Con't)
<pre>
/* 先ほど登録した ID を指定して Task を生成 */
HTaskPtr task = manager->create_task(TASK_SPE);

/* 入出力先の指定 */
task->add_inData(rbuff, rbuff_SIZE);
task->add_outData(wbuff, wbuff_SIZE);

/* CPU の指定 */
task->set_cpu(SPE_ANY);

/* Task の投入 */
task->spawn();
</pre>

--Task の作り方(Con't)
Task は依存関係を記述する事ができる
<pre>
/* taskB は taskA が終わるまで待つ */
taskB->wait_for(taskA);

/* taskC は taskB が終わるまで待つ */
taskC->wait_for(taskB);
</pre>

--並列アーキテクチャは並列でないと...

特に、Cell/PS3 は、SPUで実行しないとだめ
     既存のプログラミングでは、まったく歯が立たない

トリビアルなプログラムでも、並列にする必要がある

--WordCount

      Task をグラフ構造的に構築する (SceneGraphのMoveTask)
      File を mmap する
      mmap した部分をTask に割り当て、word countする
      Task の構築
      Task の依存関係

    char *file_mmap = st_mmap.file_mmap;

--WordCount

<img src="photo/wc_graf1.jpg" alt="sg" height="300">

--WordCount 

    int word_flag = 0;
    int i;
    for (i = 0; i < task_num; i++) {
        t_exec = manager->create_task(TASK_EXEC);
        t_exec->add_inData(file_mmap + i*division_size, division_size);
        t_exec->add_outData(o_data + i*status_num, division_out_size);
        t_exec->add_param(division_size);
        t_exec->add_param(word_flag);
        t_exec->set_cpu(SPE_ANY);
        t_print->wait_for(t_exec);
        t_exec->spawn();
        word_flag = ((file_mmap[(i+1)*division_size-1] != 0x20) && (file_mmap[(i+
1)*division_size-1] != 0x0A)); 
        size -= division_size;
    }
</pre>

--WordCount 各SPEの結果を合計
<pre>
       ....
    /* taskの数 */
    int task_num = size / division_size;
    int out_task_num = task_num + (division_size*task_num < size);

    t_print = manager->create_task(TASK_PRINT);
    t_print->add_inData(o_data, out_size);
    t_print->add_param(out_task_num);
    t_print->add_param(status_num);
</pre>



--やってはいけないこと

      最初に大量のTaskをすべて作る
      Task が自分でデータを拾って来る
      Task 同士が、同期を行なう

--Cerium Engine での同期

      Task 内では同期はしない
      Local Storage/専有したメインメモリしか使わない

      Task が終了した時に、Single Thread で動いているPPE
      がデータの整理/同期を行なう

      Task 側では、Task の生成は行なわない
      Task の post_func (continuation) で、Taskを生成する

--SPE Task, PPE Task
      Task 内で生成しても即座には実行されない
      Task 終了時に、SPEに送る Task List が作られる
      Task List のアドレスがSPEにメールされる
      SPEがメールを見て、Task List を読み込み実行する
      Task List の読み込みとSPEのTaskの実行は並列
      Task List がなくなる(なくなりそうになると)と、
           PPEにメールで要求する

PPE Task
      SPE Task と互換。メインメモリを自由に参照できる

--Fifo TaskManager

全部、同じCPU上で実行する。

OS X 上で動作する (Linux でも)

デバッグ用

--MemList と Hash

      get_segment/put_segment/wait_segement
      明示的にキャッシュ制御する必要がある

特に、

Dynamic SPE Task
      SPE上に常駐しないTask
      MemList と Hash で管理されている

256K (GBAと同じ!?) しかメモリがないので重要

--SPU上でのコード管理

       GCCのOverlayを使う 
       Overlay では、異なる場所にコードを置けない
         部分的にPICではなく、絶対参照に変更する

       自分自身へは相対参照。ライブラリへは絶対参照
       Perl Script で書き換える

--Task list

       task_list にオブジェクト生成するコードを入れる
       オブジェクトが生成されてしまえば、普通に扱える

       task 実行中にコードが追い出されることはない
           現在実行中のコード
           次にロードするコード
       の二つは必ずメモリ上にある


--SceneGraph と Rendering Engine

      SceneGraph -> SceneGraph
      SceneGraph -> Polygon
      Polygon -> Span Pack
      Span Pack を Texture を使って Rendering

      これらを大きく並列に実行する
      Rendering は細かく並列に実行する

--SceneGraph と Rendering Engine

1 dot 1 dot SPUが書いていく

<td><img src="photo/rendering.png" alt="rendering" ></td>

--Rendering Task

SG2PP
	SceneGraph を操作後、ポリゴンに変換し PolygonPack (ポリゴンの集合)を生成する
PP2SP
	ポリゴンの中から、Span (ポリゴン内にあるx軸に水平な線分) を抽出し、 SpanPack (Span の集合)を生成する
DrawSpan
	Span を使って 1 ラインずつ FrameBuffer に描画していく



--Demo されている Chain

      相互制約が非常に大きい物理シミュレーションの例
      非ホロノミック系なので、単純な積分では力学を決定できない
      SPU上で、すべての要素を同時に計算する必要がある
          (あまり並列計算向きではない...ベクトル向き)

(地味です...)


--まとめ

Blender/Linux/Cerium を用いたオープンソースなゲームフレームワーク

SceneGraphを作れば、move/collision を記述するだけで並列に実行される

ソフトウェアレンダリングなんで、なんでも自分で書ける

--将来的には、

	C++ やめたい。悪いことばかり。メモリ食い。

	CbC (Continuatin based C)で書き直す予定。

	Cell は、やばそうなので、新しい並列アーキテクチャとか。

	Open Scene Graph, Open/CLとの関係とか。