# HG changeset patch # User koba # Date 1296815915 -32400 # Node ID 398e732edfb62ea164a503368f6bb78fab4c6856 # Parent bd91d56ca33ac68ce5cd8c18246a7cce2c5fe7f1 finish chapter 4. diff -r bd91d56ca33a -r 398e732edfb6 paper/abstract.tex --- a/paper/abstract.tex Tue Feb 01 18:33:29 2011 +0900 +++ b/paper/abstract.tex Fri Feb 04 19:38:35 2011 +0900 @@ -1,34 +1,5 @@ \begin{abstract} -我々は、これまで家庭用ゲーム機上におけるゲームプログラミングをサポートする -オープンな開発フレームワークの研究を行なってきた。 -家庭用ゲーム機の多くは特殊なアーキテクチャであり、その性能を活かすためには -アーキテクチャに直結したプログラミングが求められる。そのようなプログラミングには -アーキテクチャの理解が求められるが、フレームワークとして抽象化することによって -アーキテクチャの理解に費やす時間を開発時間にあてることができる。 -現在は2006年に発売された家庭用ゲーム機 PlayStation3 を用いた開発、研究を行なっている。 - -PlayStation3 のアーキテクチャは Cell Broadband Engine と呼ばれ、 -1つの制御系プロセッサ Power Processor Element (PPE) と、 -8つのデータ処理演算プロセッサ Synergistic Processor Element (SPE) -から構成される。我々は、このような Many Core Architecture を用いた -並列プログラムの開発フレームワークとして Cerium Game Engine を開発した。 -Cerium では、 プログラムを Task という単位で管理しており、 -この Task と計算に必要なパラメータを PPE や各 SPE に並列に処理させる事により、 -プログラムの動作を実現している。 - -ゲームプログラムの特徴として、衝突判定などのオブジェクト間で相互に干渉する -パラメータの存在や、プレイヤーのゲームパッドからの入力やコード内に -埋め込まれた乱数などの非決定的な要素が多いことが挙げられる。 -Cerium においてゲーム開発を行う場合もプログラムを Task 単位に分けて -処理させるが、Task 間のパラメータの同期や Task 処理のタイミングによって -生成される乱数が異なるなど、バグの状態を再現することが難しく、 -シーケンシャルなプログラムに比べて、デバッグに費やす時間が大きくなってしまう。 - -そこで本研究では Cerium におけるゲームプログラムのテスト環境の構築とテスト手法の -提案を行う。個々のオブジェクトの持つパラメータの収集やプレイヤー入力などの -固定化ができる環境を構築し、そこで得られたテストデータを元に - \end{abstract} diff -r bd91d56ca33a -r 398e732edfb6 paper/early.tex --- a/paper/early.tex Tue Feb 01 18:33:29 2011 +0900 +++ b/paper/early.tex Fri Feb 04 19:38:35 2011 +0900 @@ -1,37 +1,51 @@ -\chapter{先行研究} \label{chapter:early} -ここでは先行研究である Game Framework Cerium、および同じ並列プログラミングの -フレームワークである OpenCL について説明する。 +\chapter{Cell BE と Cerium} \label{chapter:early} +ここでは Cerium がターゲットとしている Cell Broadband Engine の説明と +Game Framework Cerium の実装、および同じ並列プログラミングのフレームワーク +である OpenCL について説明する。 + +\section{Cell Broadband Engine}\label{sec:cell} +Cell Broadband Engine は SCEI と IBM によって開発された CPU である。 +2 thread の PPE(PowerPC Processor Element)と、8個の SPE +(Synergistic Processor Element)からなる非対称なマルチコアプロセッサであり、 +高速リングパスであるEIB(Element Interface Bus)で構成されている。 +Cerium の動作環境である PS3 Linux では PPE と 6個の SPE が使用できる。 +(図\ref{fig:cell}) + +\subsection{PPE (PowerPC Processor Element)}\label{sec:ppe} +PPE は Cell Broadband Engine のメインプロセッサで、複数の SPE を +コアプロセッサとして使用することができる汎用プロセッサである。 +メインメモリや外部デバイスへの入出力、SPE を制御する役割を担っている。 + +\subsection{SPE (Synergistic Processor Element)}\label{sec:spe} +SPE には 256KB の Local Store(LS) と呼ばれる、SPE から唯一、直接参照できる +メモリ領域があり、バスに負担をかける事なく並列に計算を進めることができる。 +SPE からメインメモリへは、直接アクセスすることはできず、SPE を構成する一つ +である MFC(Memory Flow Controller)へ、チャネルを介して DMA(Direct Memory +Access) 命令を送ることで行われる。 + +\newpage + +\begin{figure}[htbp] +\begin{center} +\includegraphics[scale=0.8]{images/cell.eps} +\end{center} +\caption{Cell Broadband Engine} +\label{fig:cell} +\end{figure} \section{Game Framework Cerium}\label{sec:cerium} -Game Framework Cerium は我々が提案したゲーム開発フレームワークで、独自に -Rendering Engine を持つ。ゲーム中のオブジェクトの振る舞いやルールは -SceneGraph で管理し、それらの動きやレンダリングの処理を動的に SPE に割り振る -カーネルとして Task Manager が用いられる。Cerium は C++ で実装されており、 -画像の読み込みや入力デバイスは SDL を用いて行っている。 - -\section{Task Manager}\label{sec:taskmanager} -Task Manager は、Task と呼ばれる分割された各プログラムを管理する。Task の -単位はサブルーチンまたは関数とし、Task 同士の依存関係を考慮しながら実行していく。 - -\section{Task Manager の API} +Cerium は我々が提案したゲーム開発フレームワークで、独自の Rendering Engine +を持つ。ゲーム中のオブジェクトの振る舞いやルールは SceneGraph で管理し、 +それらの動きやレンダリングの処理を動的に SPE に割り振るカーネルとして +Task Manager が用いられる。Cerium は C++ で実装されており、画像の読み込みや +入力デバイスは SDL を用いて行っている。 -\section{SceneGraph}\label{sec:scenegraph} -ゲーム中の一つの場面(Scene)を構成するオブジェクトやその振る舞いを格納したノードの -集合を SceneGraph とする。SceneGraph のノードは親子関係を持つ tree で -構成される。(図\ref{fig:sgtree})親子関係とは、親オブジェクトの回転や平行移動などの -行列計算による頂点座標の変更が、子オブジェクトにも反映する関係のことである。 -これは子に対してスタックに積まれた親の変換行列を掛けることで実現できる。 - -SceneGraph のノードは以下のようなデータを持つ。 - -\begin{enumerate} -\item Vertex: ポリゴンオブジェクトの頂点座標 -\item Texture: ポリゴンオブジェクトのテクスチャ座標 -\item TextureImage: テクスチャイメージ -\item TransMatrix: ポリゴンオブジェクトの変換行列 -\item Coordinates: オブジェクトの座標 -\item Angle: オブジェクトの角度 -\end{enumerate} +\subsection{SceneGraph}\label{sec:scenegraph} +ゲームを構成するオブジェクトやその振る舞いを格納したノードの集合を +SceneGraph とする。SceneGraph のノードは親子関係を持つ tree で構成される。 +親子関係とは、親オブジェクトの回転や平行移動などの行列計算による +頂点座標の変更が、子オブジェクトにも反映する関係のことである。これは子に +対してスタックに積まれた親の変換行列を掛けることで実現できる。 \begin{figure}[h] \begin{center} @@ -41,58 +55,346 @@ \label{fig:sgtree} \end{figure} -\section{Rendering Engine}\label{sec:rendering} +\newpage + +\subsection{Rendering Engine}\label{sec:rendering} Cerium の Rendering Engine は主に以下の 3 つの Task を持つ。 \begin{itemize} -\item CreatePolygonFromSceneGraph: SceneGraph が持つ Polygon の座標から、実際に画面に表示する座標の計算を行い、 - PolygonPack(\ref{sec:renddata}節) を生成する Task -\item CreateSpan: PolygonPack から同じ Y 座標を持つ線分の集合である SpanPack(\ref{sec:renddata}節) を生成する Task -\item DrawSpan: SpanPack を Texture を読み込みながら Z Buffer を用いて描画する Task +\item CreatePolygonFromSceneGraph: SceneGraph が持つ Polygon の座標から、 + 実際に画面に表示する座標の計算を行い、PolygonPackを生成する +\item CreateSpan: PolygonPack から同じ Y 座標を持つ線分の集合である + SpanPack を生成する +\item DrawSpan: SpanPack を Texture を読み込みながら Z Buffer を用いて + 描画する \end{itemize} -Rendering Engine では、SceneGraph から、実際に表示するポリゴンを抽出、 -ポリゴンから Span の生成、Span に RGB をマッピングし描画する部分と -3 つに分けることができる。 -ここでいう Span とは、ポリゴンに対するある特定の Y 座標に関するデータを -抜き出したものである。(図\ref{fig:span}) +Rendering Engine における描画処理は、SceneGraph から、実際に表示する +ポリゴンを抽出、ポリゴンから Span の生成、Span に RGB をマッピングし +描画する、といった 3 つの行程に分けることができる。そして各行程が Task として +定義され、Cerium に組み込まれている。 +ここでいう Span とは、ポリゴンに対するある特定の Y 座標に関する +データを抜き出したものである。 + +\section{Task Manager}\label{sec:taskmanager} +Task Manager は、Cerium における並列処理を可能とするフレームワークであり、 +Task と呼ばれる分割された各プログラムを管理する。Task の単位はサブルーチン +または関数とし、Task 同士の依存関係を考慮しながら実行していく。 +現在実装されている基本的な TaskManager の API の実装を表 \ref{tb:tm_api} に +示す。また、本研究で使用した API の詳細については \ref{sec:tm_api} 節にて +述べる。 + +\begin{table} +\caption{Task Manager の API} +\begin{tabular}{c|l} +\hline +\hline +create\_task & Task を生成する \\ \hline +run & 実行 Task Queue の実行\\ \hline +allocate & 環境のアライメントを考慮したメモリアロケータ\\ \hline \hline +set\_inData & Task への入力データのアドレスを追加 \\ \hline +set\_outData & Task からのデータ出力先アドレスを追加 \\ \hline +add\_param & Task に 32 bit の情報を追加 \\ \hline +wait\_for & Task 同士の依存関係をセット \\ \hline +set\_cpu & Task を実行する CPU(PPE,SPE0〜5) の設定 \\ \hline +set\_post & Task が終了した後 PPE 側で実行される関数の登録 \\ \hline +spawn & Task を実行 Task Queue に登録する \\ \hline +\end{tabular} +\label{tb:tm_api} +\end{table} + +\section{メインスレッドの実装}\label{sec:main_thread} +メインスレッドでは、主に Task の起動と、Task に渡すオプションの設定を行う。 +ここでは、Task の定義と、Task のオプション設定について説明する。 + +\subsection{Task の定義}\ref{task_struct} +実行される Task のデータ構造は以下のようになる。このデータは PPE、SPE の +各スレッドで使用される + +\begin{verbatim} +class Task { +public: // variables + int task_size; + int command; + int param_count; + int inData_count; + int outData_count; + int inData_offset; + int outData_offset; + void *data[] __attribute__ ((aligned (DEFAULT_ALIGNMENT))); +... +} + +class ListElement { + +int size; +memaddr addr; +\end{verbatim} + +Task クラスは、各 CPU が実行する Task の単位オブジェクトである。 +Task のサイズを示す task\_size や Task ID を格納する command といった +パラメータの他、後述する set\_param や set\_inData、set\_outData でセットした +値を格納する data というバッファを持つ。 +以下は SPE が Task に格納された各種パラメータを用いて 処理を実行する +ステップである。(図\ref{fig:task_struct}) + +\begin{enumerate} +\item inDataElement にあるアドレス(メインメモリ空間)を参照し、DMA で + メインメモリからデータを取得する +\item command を見て、どの Task を実行するか決定する +\item DMA read した値を Task に渡し、実行する +\item 演算結果の DMA 転送先を、outDataElement を参照して決定する +\item DMA write する +\end{enumerate} \begin{figure}[h] \begin{center} -\includegraphics[scale=0.8]{images/span.pdf} +\includegraphics[scale=0.7]{images/task_struct.pdf} \end{center} -\caption{Span} -\label{fig:span} +\caption{Task の構造と SPE からのデータの参照} +\label{fig:task_struct} \end{figure} -\subsection{Rendering で使うデータ構造}\label{sec:renddata} -レンダリング処理は SPE で行う事を前提とし、それに合わせたデータ構造を -採用している。 +\newpage + +また、PPE では Task クラスの他に HTask クラスが存在する。 + +\begin{verbatim} +class HTask : public SimpleTask { + + QueueInfo *wait_me; // List of task waiting for me + QueueInfo *wait_i; // List of task for which I am waiting + PostFunction post_func; + void *post_arg1; + void *post_arg2; + CPU_TYPE cpu_type; +... +\end{verbatim} + +HTask は wait\_me、wait\_i というキューを持ち、Task の依存関係を記録する。 +また、cpu\_type は Task が実行される CPU の切り替えに、post\_func と +post\_arg は、Task 終了時に PPE で実行される関数と引数になる。 +以上のことからわかるように、Task そのものにはコードの記述はなく、SPE にロード +してある Task の配列から、command に格納してある Task ID に従ってコードを +取得し、実行するだけである。よって、予めコード部分を SPE にロードしておく +必要がある。 + +\subsection{Task Manager の API}\label{sec:tm_api} +Task で並列処理をさせる時、ユーザはまず PPE 側で Task の生成、初期化を行う +必要がある。ここではその際に使用する Task Manager の API を紹介する。 + +\subsubsection*{create\_task}\label{sec:task_make} +Task を生成するには、TaskManager の API である {\bf create\_task } +を実行する。 + +\begin{verbatim} +HTaskPtr task = manager->create_task(ID); +\end{verbatim} + +ID とは、各 Task に割り振られたグローバル ID である。通常の逐次型プログラム +では、Task を選択して実行する場合、その関数ポインタを指定して実行すれば良い。 +しかし Cell の場合、PPE と SPE 上ではアドレス空間が異なるため、SPE 上で +実行される Task をメインスレッドから直接アドレス指定することはできない。 +そこで、Task 毎に ID を割り振り、その ID から、SPE 上にある Task が +格納された配列を参照して実行する。(図\ref{fig:task_struct} の (2) を参照) + +\subsubsection*{set\_cpu}\label{sec:set_cpu} +TaskManager の{\bf set\_cpu }により、Task をどの CPU で実行させるかを +選択する事ができる。 -\subsubsection*{PolygonPack} -PolygonPack は SceneGraph から抽出されたポリゴンの集合である。 -光源やテクスチャ、頂点の座標から構成される。 +\begin{verbatim} +//SPE 1 で実行 + task1->set_cpu(SPE_1); +//SPE のどれかで実行 + task1->set_cpu(SPE_ANY); +//PPE で実行 + task1->set_cpu(PPE); +\end{verbatim} + + +\subsubsection*{set\_inData set\_outData set\_param}\label{sec:task_io} +Task に入力と出力先を渡す API として、{\bf set\_inData }と +{\bf set\_outData }、そして{\bf set\_param }がある。 + +\begin{verbatim} + int a; + Data *addr; + + task->set_param(0, (memaddr)a); + task->set_inData(0, addr, sizeof(Data)); + task->set_outData(0, addr, sizeof(Data)); +\end{verbatim} -\subsubsection*{SpanPack} -SpanPack はポリゴンから抽出された Span の集合である。 -Span はその座標と、対応するテクスチャの座標を持つ。(図\ref{fig:spack}) +set\_inData は格納する場所の番号と Task に渡すデータのアドレス、 +そのデータのサイズを引数として入力する。このアドレスに格納されているデータは +DMA 転送によって SPE に送られる為、16 バイトアライメントが取れており、 +データサイズは 16 バイト倍数である必要がある。 +set\_param は、Task に 32 bit のデータを DMA 転送ではなく、直接渡す。 +このため set\_inData で渡すには小さいデータを送るのに適している。 +Task の出力先は set\_outData で指定する。使用方法は set\_inData と +同じである。 + +\subsubsection*{wait\_for}\label{sec:dependency} +TaskManager は Task 依存を解決する機能を持っている。 + +\begin{verbatim} + task1->wait_for(task2); + task1->wait_for(task3); +\end{verbatim} + +この例では task1 が task2、task3 の終了を待つ。task2、task3 が終了すると +task1 が SPE に割り振られる。 + +\if0 +この時、task1 は TaskManager の持つ waitTaskQueue へ、task2、3 は +activeTaskQueue へ格納される。activeTaskQueue から各 CPU へ Task が +割り振られ、Task が終了したらメインスレッドへ Task 終了のメールを発行する。 +メインスレッドはそれを受け取り、waitTaskQueue の Task を調べ、Task 依存を +満たした Task を activeTaskQueue に移し替える。 +(図\ref{fig:dependency}) \begin{figure}[h] \begin{center} -\includegraphics[scale=0.8]{images/spack.pdf} +\includegraphics[scale=0.8]{images/dependency.pdf} \end{center} -\caption{Span 構造} -\label{fig:spack} +\caption{Task の依存関係の解決} +\label{fig:dependency} +\end{figure} +\fi + +\subsubsection*{set\_post}\label{sec:set_post} +Task が終了した時、{\bf set\_post }を使うことでメインスレッドで実行される +関数と、その引数を指定できる。また、set\_post が実行されるタイミングで +ユーザが Task が終了したことを検知することができる。 + +\section{CPU スレッドの実装}\label{sec:cpu_thread} +各 CPU(PPE、SPE) では、メインスレッドで生成された Task を受け取り、その情報を +元に、Task を実行する。ここでは Task 処理の詳細と Task の本体部分の記述、 +そして 本研究で使用した API について説明する。 + +\subsection{Task の処理}\label{sec:task_exec} +Task 本体の記述例は以下のようになっている。 + +\begin{verbatim} +static int +state(SchedTask *smanager, void *rbuf, void *wbuf) +{ + CHARACTER *p = (CHARACTER*)smanager->get_input(rbuf, 0); + CHARACTER *q = (CHARACTER*)smanager->get_output(wbuf, 0); + player *jiki = (player*)smanager->get_input(rbuf, 1); + + p->y += p->vy; + p->x += p->vx; + if ((p->y < jiki->y) && (p->y + 16 > jiki->y)) { + p->vy = -2; + p->vx = ((jiki->x > p->x) ? 4 : -4); + } + *q = *p; + return 0; +} +\end{verbatim} + +これは図\ref{fig:task_struct} の (3) にあたる処理である。 +\ref{sec:task_io} 節でセットしたデータを get\_input で受け取り、 +get\_output で書き出し用のバッファのアドレスを受け取っている。 +上記の例では、セットしたデータを用いて処理した後で書き出し用のバッファに +書き込んでいる。 + +\newpage + +\subsection{SchedTask}\label{sec:schedtask} +SchedTask とは、Task の処理中に Task に関する処理を行うことができる +オブジェクトで、Task の引数として与えられている。SchedTask の API を +表 \ref{tb:st_api} に示す。 + +\begin{table}[h] +\caption{SchedTask の API} +\begin{tabular}{c|l} +\hline +\hline +create\_task & Task を生成する \\ \hline +allocate & 環境のアライメントを考慮したメモリアロケータ\\ \hline \hline +get\_input & set\_inData で指定したデータを取得する \\ \hline +get\_output & set\_outData で指定した領域に書きこむバッファを取得する \\ \hline +get\_param & set\_param で指定した 32 ビットデータを取得する \\ \hline \hline +global\_alloc & Task 間で共用するデータの allocate \\ \hline +global\_get & global\_alloc した領域のアドレスを取得 \\ \hline +global\_free & global\_alloc した領域の free \\ \hline \hline +mainMem\_alloc & メインメモリ上の allocate \\ \hline +mainMem\_get & mainMem\_alloc した領域のアドレス(メインメモリ空間)を取得 \\ \hline +mainMem\_wait & mainMem\_alloc が完了するまで待つ \\ \hline +\end{tabular} +\label{tb:st_api} +\end{table} + +\subsubsection*{Input Data の取得}\label{sec:indata} +\ref{sec:task_exec} 節のコードの rbuf にはメインスレッドで set\_inData により +指定したデータの実体が入っている。(図\ref{fig:rbuf}) + +\begin{figure}[h] +\begin{center} +\includegraphics[scale=0.8]{images/rbuf.pdf} +\end{center} +\caption{set\_inData による rbuf の構造} +\label{fig:rbuf} \end{figure} -\section{OpenCL} +\newpage + +このデータを扱う場合、直接 rbuf を見るのではなく + +\begin{verbatim} +int *data = (int*)smanager->get_input(rbuf, index); +\end{verbatim} + +というようにして取得する。index は set\_inData で指定した番号になる。 +同様に set\_param で設定した値は、get\_param(index) で得られる。ここの index +も get\_input と同じく、指定した番号である。 + +\subsubsection*{Output Data の取得}\label{sec:outdata} +Task が出力を行う場合、Task 生成時に set\_outData を行い、出力先のアドレスと +データサイズが指定されている。wbuf は rbuf 同様、指定したサイズ分の +バッファで、Task の処理を行う前に生成時に設定したデータサイズを元にバッファを + allocate する。wbuf はこの時点ではただのバッファで、中には何も入っていない +(不定)。Task 処理の終了時、wbuf の値を set\_outData で指定したアドレスに DMA +転送する。(図\ref{fig:wbuf}) + +\begin{figure}[h] +\begin{center} +\includegraphics[scale=0.6]{images/wbuf.pdf} +\end{center} +\caption{set\_outData による wbuf の構造} +\label{fig:wbuf} +\end{figure} + +\subsubsection*{Task 間の共用空間}\label{sec:global} +各 Task は独立して動作するため、使用するメモリ領域も他の Task と干渉すること +はない。しかし、処理によっては Task 間で同じデータを使用する場合がある。 +共用領域を allocate するには global\_alloc を使う。 +allocate した領域のアドレスは global\_get で取得できる。 +global\_alloc した領域はユーザが global\_free で解放する必要がある。 + +共用領域は各 SPE に置かれるので、違う SPE の領域を参照する事はできない。 +従って、同じデータを使う可能性の Task を同じ SPE 上で実行させることにより、 +メモリ領域も軽減できる。(図\ref{fig:global}) + +\begin{figure}[h] +\begin{center} +\includegraphics[scale=0.6]{images/global.pdf} +\end{center} +\caption{Task 間の共用領域} +\label{fig:global} +\end{figure} + +\section{OpenCL}\label{sec:opencl} OpenCL(Open Computing Language) とは、マルチコア CPU や GPU、その他の プロセッサによる、ヘテロジニアスコンピューティングのフレームワークである。 -OpenCL C プログラミング言語は ISO/IEC 9899:1999(C99) 規格をベースとしている。 - OpenCL のプラットフォームモデルは図 \ref{fig:opencl} のようになり、一つの -Host と複数の OpenCL Device で構成されている。OpenCL devices の中では Compute -Units(CUs) として分割され、その中でさらに Processing Elements(PEs) として -分割される。 +OpenCL のプラットフォームモデルは Host と複数の OpenCL Device で +構成されている。OpenCL devices の中では Compute Units(CUs) として分割され、 +その中でさらに Processing Elements(PEs) として分割される。 + +\if0 +(図\ref{fig:opencl}) \begin{figure}[h] \begin{center} @@ -101,44 +403,11 @@ \caption{OpenCL Platform} \label{fig:opencl} \end{figure} - -OpenCL アプリケーションでは、実行する処理コマンドが Host から PEs へ送られる。 -PEs では SIMD(Single Instruction Multiple Data)もしくは SPMD(Single Program -Multiple Data) で実行される。 - -OpenCL devices や Host には kernel があり、Host kernel では OpenCL devices -context や実行コマンドの処理を行う。Host は OpenCL devices での kernel の -実行を調整するために、 command-queue を生成し、context へ渡す。 -command-queue には 以下のコマンドがある。 - -\begin{itemize} -\item Kernel execution commands\\ - 実行コマンド queue -\item Memory commands\\ - データの送受信に関する情報(Host アドレス空間) -\item Synchronization commands\\ - commannd の依存や同期等に関する情報 -\end{itemize} +\fi また、OpenCL Device は 4 つの違うメモリ領域 (Global Memory, Constant Memory, Local Memory, Private Memory) を持ち、それぞれ Host や CUs、PEs からの -アクセス権限が異なる(アドレス空間が異なる)。 - -プログラミングモデルとしては、データ並列、タスク並列をサポートしている。 -OpenCL は Host や各 device に kernel があることや、device 毎にメモリアドレス -空間が違う、プログラミングモデル等、TaskManager とよく似ている。 - -OpenCL は汎用的な実装となっているため、例えば Task のメモリアクセス API も -複雑な記述となっている。対して Task Manager は、現在 Cell 環境に重きを -置いているため、メモリアクセスは DMA に対応した記述が行われる。DMA では -明示的にアドレスを指定することになるので、OpenCL よりも簡潔な記述が可能と -なっている。このことから、Task Manager は OpenCL の軽めな実装の一つという -捉え方もできる。 - -OpenCL に対して Task Manager が優れている点は、Mac OSX や Linux、Cell といった -複数の環境で動作する事による信頼性の確保のしやすさにある。Task Manager では、 -Task 毎に実行する CPU を選択できるため、動作環境が変わっても大幅なコードの -変更が必要なく、Single Core プログラムとして動作させることができる。 -これにより、 Task 単体やそれらを繋げたプログラム全体を逐次型プログラムとみなし、 -二分法などのデバッグ手法を用いる事ができる。このことは図\ref{fig:devstep} -のような段階に沿って開発が可能になることに繋がる。 +アクセス権限が異なる(アドレス空間が異なる)。プログラミングモデルとしては、 +データ並列、タスク並列をサポートしている。OpenCL は Host や各 device に +kernel があることや、device 毎にメモリアドレス空間が違う、 +プログラミングモデル等、TaskManager とよく似ている。 diff -r bd91d56ca33a -r 398e732edfb6 paper/game.tex --- a/paper/game.tex Tue Feb 01 18:33:29 2011 +0900 +++ b/paper/game.tex Fri Feb 04 19:38:35 2011 +0900 @@ -1,28 +1,25 @@ -\chapter{ゲームプログラミングにおけるテストと Super Dandy} \label{chapter:game} -ここではゲームプログラムの特徴とテストの関連性、そして実験に使用した -ゲーム Super Dandy の実装について述べる。 - -\section{ゲームプログラムのテスト}\label{sec:feature} +\chapter{ゲームプログラミングにおけるテスト} \label{chapter:game} 多くのゲームでは多数のオブジェクトが存在し、プレイヤーのコントローラー入力や -ゲームの進行状況によって生成される。生成されたオブジェクトは他のオブジェクトの -座標などのパラメータに影響され、衝突判定を行ったり、その動きを変える。 -(図\ref{fig:feature}) +ゲームの進行状況によって新たなオブジェクトが生成される。 +生成されたオブジェクトは他のオブジェクトの座標などのパラメータに影響され、 +衝突判定を行ったり、挙動が変化する。 +(図\ref{fig:game}) \begin{figure}[h] \begin{center} -\includegraphics[scale=0.6]{images/feature.pdf} +\includegraphics[scale=0.6]{images/game.pdf} \end{center} \caption{ゲームオブジェクトの相互作用} -\label{fig:feature} +\label{fig:game} \end{figure} このようにゲームプログラムでは常にプレイヤーの入力がゲームに影響され、 オブジェクト同士のパラメーターが相互に干渉し合うため、遷移する状態が -膨大であり、状態遷移が仕様の範囲に収まるかをテストする一般的な -テスト駆動のようなテストにはならない。 -よってゲームプログラムは実際にプレイすることが重要なテストである。 +膨大であり、一般的なテスト駆動のように遷移する状態が仕様の範囲内に収まる +のかチェックするようなテストは向かない。ゲームプログラムは実際にプレイヤーが +ゲームをプレイすることが重要なテストとなる。 -\subsection{プレイヤーの入力}\label{sec:player} +\section{プレイヤーの入力}\label{sec:player} ゲームプログラムではコントローラーなどのインターフェースにより、 プレイヤーから入力が与えられ、それによってゲーム内のパラメータが変化する。 例えばプレイヤーの操作によりキャラクターが移動する、攻撃するといった事が @@ -31,9 +28,9 @@ プレイヤーの入力は常に非決定的であり、例え同じ人間が同じゲームの同じ場面を プレイしたとしても毎回全く同じ入力をする可能性は極めて低い。 こうした事からプレイヤーは制御不能なランダム要素であると考えられ、 -ゲームプログラムテストにおけるバグの再現性を低下させる要因となっている。 +ゲームプログラムテストにおけるバグの再現性を低下させている。 -\subsection{乱数}\label{sec:random} +\section{乱数}\label{sec:random} ゲームにおける乱数は、オブジェクトの振る舞いに多様性を持たせたり、ランダムな 配置を実現する為に使われ、ゲームのボリュームや面白さを広げる役割を担ってきた。 これは例えば以下のようなコードにより実現される。