本ドキュメントは、CTR-SDK の GD ライブラリを用いたプログラミングについて説明したものです。グラフィックスの基本的な概念や一部ライブラリ(GX ライブラリなど)の使用方法については既に習得していることを前提としており、純粋に GD ライブラリに焦点を当てた内容となっています。
本書は「3DS プログラミングマニュアル」の「グラフィックス基本編」および「グラフィックス応用編」に記載されている内容を理解していることを前提に書かれています。本書を読む前に、これらのドキュメントをご一読ください。
1.1. GD ライブラリとは
GD ライブラリは CTR-SDK のグラフィックスライブラリの 1 つです。主に 3D コマンドの生成を行う目的で使用します。
GPU の状態と同期する内部状態をもとにライブラリが必要な 3D コマンドのみを生成しますので、GPU の状態を意識せずにグラフィックス処理を実装することができます。また、API の構成は 3DS のグラフィックスハードウェア構成と密接に結びついており、アプリケーションで簡単に用いることが可能でありながらも、高いパフォーマンスを引き出せるように設計されています。
GD ライブラリは DMPGL(gl 関数)との互換性はありません。コマンドリストの管理など、一部のシステム系機能については GX ライブラリ(nngx 関数)を用いる必要があります。また、C++ のインターフェースのみが提供されています。
1.1.1. コマンドリスト
先に述べたように、GD ライブラリではコマンドリストの操作を管理しておらず、GX ライブラリで提供されている機能をそのまま使用することになります。つまり、コマンドリストをどのように準備したり、使用したり、同期を取ったりするかについては、すべてアプリケーションで実装しなければなりません。
コマンドリクエストを発行する関数はアプリコア(コア 0)でのみ呼び出しが可能です。
1.1.2. イミディエート関数と非イミディエート関数
GD ライブラリでは、現在のコマンドリストオブジェクトで指定されている 3D コマンドバッファに 3D コマンドを書き込むタイミングの違いによって、関数をイミディエート関数と非イミディエート関数の 2 つに分類しています。
イミディエート関数は、呼ばれたタイミングでデータを 3D コマンドバッファに直接書き込む関数のことを指します。パケットレコーディング機能(「6. パケットレコーディング」参照)を使用する場合に、記録されているパケットを直接編集することができるという利点があります。
非イミディエート関数は 3D コマンドバッファへの書き込みを行わずに GD ライブラリの内部状態を更新します。これらの内部状態は描画関数(nn::gd::System::Draw()
または nn::gd::System::DrawIndexed()
)が呼び出された際に読み込まれ、必要に応じて 3D コマンドバッファにデータを書き出します。
一部の関数はイミディエート関数と非イミディエート関数の両方の性質を持っており、3D コマンドバッファに書き込みを行いつつ GD ライブラリの内部状態を更新することもあります。
1.1.3. エラーハンドリング
内部で処理に失敗する可能性のある関数は nnResult
型のオブジェクトを返り値として返すように実装されています。つまり、エラーが発生した場合には内容に応じたエラー値が返されます。
GD ライブラリでエラーをハンドリングする方法は 2 つ存在します。1 つは呼び出した GD 関数の返り値を常にチェックする方法です。もう 1 つは nn::gd::System::SetCallbackFunctionError()
を呼び出してエラーハンドリング用のコールバック関数を登録する方法です。これらの方法は同時に使用することができ、コールバックを呼び出したあとで GD 関数そのものの返り値でエラーを受け取ることができます。
typedef void (*nn::gd::System::gdCallbackfunctionErrorPtr)( nnResult result, const char* functionName); static void nn::gd::System::SetCallbackFunctionError( nn::gd::System::gdCallbackfunctionErrorPtr callbackFunctionError);
GD 関数内部でのエラーチェックの詳細度は、ビルドの種類(Debug/Development/Release)によって異なります。
また、デバッグ用途向けに、与えられた nnResult
型のオブジェクトのエラーに対応したメッセージを nn::gd::System::GetErrorStringFromResult()
で取得することができます。
static char* nn::gd::System::GetErrorStringFromResult(nnResult result);
1.2. モジュール
モジュールは 3DS のレンダリングパイプライン内で関連する処理をひとまとめにしたものです。
GD ライブラリには、以下のモジュールが定義されています。
モジュール | 定義名 |
---|---|
頂点入力ステージ |
nn::gd::System::MODULE_VERTEX_INPUT
|
シェーダステージ |
nn::gd::System::MODULE_SHADER
|
ラスタライザステージ |
nn::gd::System::MODULE_RASTERIZER
|
テクスチャステージ |
nn::gd::System::MODULE_TEXTURE
|
プロシージャルテクスチャステージ |
nn::gd::System::MODULE_TEXTURE_PROCEDURAL
|
ライティングステージ |
nn::gd::System::MODULE_LIGHTING
|
コンバイナステージ |
nn::gd::System::MODULE_TEXTURE_COMBINER
|
フォグステージ |
nn::gd::System::MODULE_GAS_FOG
|
アウトプットステージ |
nn::gd::System::MODULE_OUTPUT
|
パイプラインでの処理が段階的に分けられていることもあり、モジュールのことをステージと記述することがあります。
1.2.1. Static クラスによる定義
GD ライブラリのほとんどの関数は各モジュールで定義された static
なクラスを介してアクセスします。これらのクラスは static
な関数のみを持ち、内部状態を持たず、インスタンス化もされていません。つまり、クラス分けは単純に機能ごとに関数定義をグルーピングするために用いられています。
なお、各モジュールに対して 1 つ、または複数個のクラスが以下のように用意されています。
モジュール | 対応するクラス | 行われる処理 |
---|---|---|
頂点入力ステージ |
VertexInputStage
|
頂点バッファの入力 |
シェーダステージ |
ShaderStage
|
シェーダプログラムのアタッチ |
ラスタライザステージ |
RasterizerStage
|
カリング、クリッピング、ビューポート設定、シザリング、アーリーデプステスト |
テクスチャステージ |
TextureStage
|
テクスチャユニットの設定 |
プロシージャルテクスチャステージ |
ProceduralTextureStage
|
プロシージャルテクスチャの設定 |
ライティングステージ |
LightingStage
|
フラグメントライティングの設定 |
コンバイナステージ |
CombinerStage
|
テクスチャコンバイナの設定 |
フォグステージ |
FogStage
|
フォグの設定、ガスの設定 |
アウトプットステージ |
OutputStage
|
デプステスト、ステンシルテスト、アルファテスト、論理演算、ブレンディング、書き込みマスク、レンダーターゲットの設定、フラグメントオペレーションの設定 |
1.2.2. モジュールの状態管理
いくつかのモジュールではライブラリで内部状態が管理されており、2 つのレンダリングパスで内部状態に変更がなかった場合には、そのモジュールに関するコマンドを再送しません。これにより不要なコマンドパケットが生成されず、パフォーマンスを向上させることができます。ただし、何らかの GD 関数の呼び出しによって内部状態が変更された場合、そのモジュールは「dirty」(変更あり)と見なされます。「dirty」と見なされたモジュールはすべて、次回の描画時にその内部状態を参照したコマンド送信が行われます。
モジュールが「dirty」であるかどうかはライブラリで管理され、自動的に処理されます。そのため GD ライブラリを利用するプログラマは通常、どのモジュールが「dirty」であるかについて気にする必要はありません。しかしながら、モジュールに関連したコマンドすべてを確実に再送したいようなケースもあります。そのような場合のために、GD ライブラリでは明示的にモジュールを「dirty」にする手段を以下の関数で提供しています。
void nn::gd::System::ForceDirty(u32 moduleFlag);
moduleFlag
には、「dirty」にするモジュールを表 1-1 の定義名の論理和で指定します。
例えば、事前に作成しておいたコマンドパケットを実行したあとや、ほかのグラフィックスライブラリ(NintendoWare や DMPGL、GR ライブラリなど)の使用によって、ハードウェア状態が更新されてしまっている状況で GD ライブラリのすべての状態を確実に反映させたい場合に有効です。
1.3. ステートオブジェクトとリソースオブジェクト
ステートオブジェクトは、作成後に変更されることのない不変のオブジェクトです。初期化時に作成され、実行時の必要なタイミングでそのステートをパイプラインにセットするような使われ方を想定しています。オブジェクトの作成にはメモリ確保やデータ処理を伴うため時間がかかりますが、そこで作成されたステートをあとで 3D コマンドバッファに出力する際の処理は必要最小限の単純なものだけで済みます。これらステートオブジェクトの中身に直接アクセスする方法は提供されていません。
ステートオブジェクトの作成が要求された場合、GD ライブラリはまず以前に全く同じ設定を持ったオブジェクトが作成されているかどうかを確認します。そこで同じ設定のものが見つかった場合は新たなオブジェクトを作成せず、単にそのオブジェクトへのポインタを返します。
ステートオブジェクトとして以下のものが定義されています。
ステートオブジェクト | 使用するステージ | 内包する設定 |
---|---|---|
CombinerState
|
コンバイナステージ | コンバイナ、コンバイナバッファ |
BlendState
|
アウトプットステージ | 論理演算、ブレンディング |
DepthStencilState
|
アウトプットステージ | デプステスト、ステンシルテスト |
InputLayout
|
頂点入力ステージ | 頂点バッファ |
SamplerState
|
テクスチャステージ | テクスチャパラメータ |
また、ステートオブジェクトとは別に以下のようなリソースオブジェクトがあります。作成時に必要な処理がほぼ完了しているという意味では同様の使われ方をします。
リソースオブジェクト | 説明 |
---|---|
ShaderBinary
|
シェーダバイナリの保持やロードを行います。 |
Shader
|
浮動小数点定数の GPU へのロードや入出力レジスタの設定を行います。 |
ShaderPipeline
|
シェーダパイプラインへのシェーダプログラムのアタッチを行います。 |
VertexBufferResource
|
頂点バッファのデータを保持します。 |
Texture2DResource
|
テクスチャやカラーバッファなど、幅と高さを持つリソースを保持します。 |
Texture2D
|
Texture2DResource を 2 次元テクスチャとして扱います。 |
TextureCube
|
6 つの Texture2DResource をキューブマップテクスチャとして扱います。 |
RenderTarget
|
Texture2DResource をカラーバッファとして扱います。 |
DepthStencilTarget
|
Texture2DResource をデプス(ステンシル)バッファとして扱います。 |
ステートオブジェクトやリソースオブジェクトを作成するために必要なメモリは、GX ライブラリの初期化で指定したアロケータを介して、デバイスメモリ(NN_GX_MEM_FCRAM
)からシステム用バッファ(NN_GX_MEM_SYSTEM
)として確保されます。そのため、ライブラリによるメモリ使用を考慮して、デバイスメモリのサイズに余裕を持たせることを推奨します。
確保されるメモリのサイズについては、「9.2. 関数内で確保されるメモリのサイズ」を参照してください。
1.3.1. デスクリプタクラス
ステートオブジェクトやリソースオブジェクトのようなパイプライン設定用のオブジェクトを作成する際には、必要な設定を行ったデスクリプタクラスをオブジェクト作成関数に与えることになります。
デスクリプタクラスのすべてのメンバ変数は public
で定義されています。また、デスクリプタクラスには複数のメンバ変数を簡単に設定するためのメンバ関数が定義されている場合もあります。