コマンドリストは 3DS が独自に導入したもので、3D グラフィックス処理で呼び出される gl
、nngx
関数をコマンドとして記録し、まとめて実行させることができます。コマンドリストへの処理は、コマンドリストオブジェクトを用いて行われます。3DS では、コマンドリストを 3D グラフィックスの描画の実行単位として扱います。
コマンドリストは GPU が直接実行するレジスタ書き込みコマンド(3D コマンド)と、CPU から GPU に命令を伝えるためのコマンドリクエストで構成されます。3D コマンドは、gl
、nngx
関数で描画などが行われると、3D コマンドバッファに蓄積されます。コマンドリクエストは、要因となる特定の gl
、nngx
関数の呼び出しによりキューイングされます。コマンドリクエストの種類と詳細については「4.2. コマンドリクエストの種類」で説明します。
コマンドリクエストにキューイングされた 3D 実行コマンドが処理されると、GPU は 3D コマンドバッファから 3D コマンドを読み込んで実行します。3D コマンドは複数で 1 つのコマンドセットとして扱われ、コマンドセット単位で実行されます。各コマンドセットの最後のコマンドは区切りとなるもので、GPU が 3D バッファの読み込みを終了するコマンドとなります。
コマンドリクエストを発行する関数はアプリコア(コア 0)でのみ呼び出しが可能です。
4.1. 使用方法
3DS では、GPU による 3D 描画はコマンドリスト単位で実行されます。そのため、アプリケーションはコマンドリストオブジェクトを生成し、gl
関数などで蓄積された 3D コマンドをまとめて実行することになります。
4.1.1. オブジェクトの生成
最初に、nngxGenCmdlists()
でコマンドリストオブジェクトを生成します。
void nngxGenCmdlists(GLsizei n, GLuint* cmdlists);
n
個のコマンドリストオブジェクトを生成して、cmdlists
にその名前(オブジェクト名)を格納します。
コマンドリストは固有の名前空間を持っています。オブジェクト名が 0 のコマンドリストはシステムで予約されています。
エラー |
原因 |
---|---|
|
|
|
管理領域の確保に失敗した |
4.1.2. バインド
次に、生成したコマンドリストオブジェクトを nngxBindCmdlist()
で GPU に関連付け(バインド)ます。バインドしたコマンドリストの 3D コマンドバッファに 3D コマンドが蓄積されていきます。
void nngxBindCmdlist(GLuint cmdlist);
cmdlist
に未使用のオブジェクト名が指定された場合はオブジェクトの生成が行われます。
エラー |
原因 |
---|---|
|
管理領域の確保に失敗した |
|
コマンドキャッシュ(詳しくは「3DS プログラミングマニュアル - グラフィックス応用編」を参照してください)を利用して、コマンドリストの保存を行っている状態で呼び出した |
4.1.3. メモリ領域の確保
バインドしたコマンドリストに対して、nngxCmdlistStorage()
でメモリ領域の確保を行います。
void nngxCmdlistStorage(GLsizei bufsize, GLsizei requestcount);
bufsize
に 3D コマンドバッファのサイズを、requestcount
にコマンドリクエストをキューイング可能な個数を指定します。
コマンドリストオブジェクトを複数生成している場合は、それぞれに対して nngxBindCmdlist()
と nngxCmdlistStorage()
を呼び出してください。オブジェクト名が 0 のコマンドリストをバインドしている場合、この関数の呼び出しは無視されます。また、すでに領域を確保しているオブジェクトに対して、この関数を再度呼び出した場合は確保していた領域を解放し、再度領域の確保が行われます。
確保した 3D コマンドバッファのサイズを超えて 3D コマンドが蓄積された場合や 3D コマンドバッファが未設定の場合は、該当の関数呼び出しで GL_ERROR_COMMANDBUFFER_FULL_DMP
のエラーが生成されます。コマンドリクエストにキューイング可能な個数を超えてキューイングされた場合やバッファが未設定の場合は、該当の関数呼び出しで GL_ERROR_COMMANDREQUEST_FULL_DMP
のエラーが生成されます。
エラー |
原因 |
---|---|
|
メモリ領域の確保に失敗した |
|
実行中のコマンドリストに対して呼び出した |
|
引数に負の値を指定した |
4.1.4. 実行
バインドされているコマンドリストにキューイングされたコマンドリクエストの実行を開始させるには、nngxRunCmdlist()
を呼び出します。
void nngxRunCmdlist(void); void nngxRunCmdlistByID(GLuint cmdlist);
オブジェクト名が 0 のコマンドリストをバインドしている場合、実行は無視されます。また、コマンドリクエストの実行中にほかのコマンドリストをバインドし、この関数を実行しても無視されます。
コマンドリクエストの実行開始後は、続けて同じコマンドリストにコマンドを蓄積することもできますが、別のコマンドリストをバインドしてコマンドを蓄積させることもできます。ただし、コマンドの蓄積順と実行順は同じでなければなりません。
実行中のコマンドリストにコマンドを蓄積する場合はアプリコアで行う必要があります。アプリコア以外で行った場合は不定な動作をすることがあります。
nngxRunCmdlistByID()
は、現在バインドされているコマンドリストではなく、cmdlist
で指定されたコマンドリストを実行します。指定されたコマンドリストを実行する以外には、nngxRunCmdlist()
の動作との違いはありません。
エラー |
原因 |
---|---|
|
メモリ領域が確保されていないコマンドリストに対して呼び出した( |
|
メモリ領域が確保されていないコマンドリストに対して呼び出した( |
4.1.4.1. 実行状態の取得
コマンドリストが実行中かどうかは nngxGetIsRunning()
で取得することができます。
GLboolean nngxGetIsRunning(void);
この関数は、コマンドリストが現在バインドされているものかどうかに関係なく、実行中のコマンドリストがあれば GL_TRUE
を返します。
同様に、コマンドリストが実行中かどうかを nngxGetCmdlistParameteri()
の pname
に NN_GX_CMDLIST_IS_RUNNING
を渡して取得できますが、この方法で取得する実行状態は、現在バインドされているコマンドリストが実行中かどうかだけです。nngxGetCmdlistParameteri()
については、「4.1.10. パラメータ取得」を参照してください。
4.1.5. 破棄
不要になったコマンドリストオブジェクトは nngxDeleteCmdlists()
で破棄することができます。
void nngxDeleteCmdlists(GLsizei n, const GLuint* cmdlists);
cmdlists
に格納されている、n 個のオブジェクト名で指定されたコマンドリストオブジェクトを破棄します。実行中のコマンドリストが含まれていた場合、GL_ERROR_8003_DMP
のエラーが生成されますがコマンドリストの実行に影響はなく、ほかに指定されているコマンドリストオブジェクトが破棄されます。
エラー |
原因 |
---|---|
|
|
|
|
4.1.6. 停止
実行中のコマンドリストを停止するには、以下の関数を呼び出します。
void nngxStopCmdlist(void); void nngxReserveStopCmdlist(GLint id);
nngxStopCmdlist()
は、呼び出し時に実行中だったコマンドリクエストが完了した時点で停止します。すでに実行が開始されているコマンドリクエスト(実行開始待ちを含む)を中止することはできません。
nngxReserveStopCmdlist()
は id
番目に蓄積したコマンドリクエストの実行完了直後に停止します。
停止したコマンドリストを再開する場合は、nngxRunCmdlist()
を呼び出します。ただし、停止を指示してからコマンドの実行が完了するまでに呼び出した場合は無視されてしまうことに注意してください。
エラー |
原因 |
---|---|
|
実行中のコマンドリストに対して呼び出した |
|
|
4.1.7. 3D コマンドバッファの区切り
nngxSplitDrawCmdlist()
で 3D コマンドバッファにバッファ読み込み終了コマンドを追加し、3D 実行コマンドをキューイングすることができます。コマンドリストが 3D コマンドを蓄積しながら実行している場合は、この関数で区切られた部分までの 3D コマンドが実行されます。
void nngxSplitDrawCmdlist(void);
3D 実行コマンドはバッファ読み込み終了コマンドが追加されるまでキューイングされません。この関数以外にも 3D 実行コマンドをキューイングする関数があります。glClear()
や glTexImage2D()
などは 3D コマンドの実行を停止させなければならないため、バッファ読み込み終了コマンドの追加と 3D 実行コマンドのキューイングを行います。
エラー |
原因 |
---|---|
|
オブジェクト名が 0 のコマンドリストをバインドしているときに呼び出した |
|
すでにコマンドリクエストの蓄積数が最大数に達していた |
|
この関数が追加するコマンドにより 3D コマンドバッファが一杯になる |
この関数を内部で呼び出している関数が、これらのエラーを生成することがあります。
4.1.7.1. 蓄積済み 3D コマンドバッファのフラッシュ
nngxSplitDrawCmdlist()
を呼び出すと、3D コマンドバッファに 3D コマンドが蓄積されていない場合でも、バッファ読み込み終了コマンドの追加と 3D 実行コマンドのキューイングが行われます。つまり、意図せず無駄なコマンドが追加されることになりますので、3D コマンドが蓄積されている場合にのみ 3D コマンドバッファを区切るためのコマンドを追加する nngxFlush3DCommand()
または nngxFlush3DCommandNoCacheFlush()
を呼び出すことを推奨します。キャッシュのフラッシュが複数回発生するような場合はキャッシュをフラッシュしない後者の関数を呼び出し、nngxUpdateBufferLight()
でまとめてキャッシュを反映することで CPU コストを抑えられる可能性があります。
void nngxFlush3DCommand(void); void nngxFlush3DCommandNoCacheFlush(void);
これらの関数は、バインドされているコマンドリストの 3D コマンドバッファが最後に区切られたあと、3D コマンドが蓄積されていなければバッファ読み込み終了コマンドと 3D 実行コマンドの追加を行わず、3D コマンドが蓄積されていればバッファ読み込み終了コマンドと 3D 実行コマンドの追加を行います。3D コマンドを蓄積しながら実行している場合は、この関数で区切られた部分までの 3D コマンドが実行されます。
エラー |
原因 |
---|---|
|
オブジェクト名が 0 のコマンドリストをバインドしているときに呼び出した |
|
すでにコマンドリクエストの蓄積数が最大数に達していた |
|
この関数が追加するコマンドにより 3D コマンドバッファが一杯になる |
4.1.7.2. 蓄積済み 3D コマンドバッファの部分フラッシュ
指定したサイズ分の 3D コマンドを実行する nngxFlush3DCommandPartially()
が用意されています。この関数は nngxFlush3DCommand()
の機能を拡張したもので、nngxAdd3DCommand()
などで追加した、コマンドバッファ実行レジスタのキックコマンドを含む 3D コマンドを正しく実行させるために呼び出します。詳細は「3DS プログラミングマニュアル - グラフィックス応用編」の「コマンドバッファ実行レジスタ(0x0238 ~ 0x023D)」を参照してください。
void nngxFlush3DCommandPartially(GLsizei buffersize);
buffersize
に、実行するコマンドバッファのサイズをバイト数で指定します。16 の倍数でなければなりません。
buffersize
には、前回のコマンドフラッシュ後のアドレスから、最初のキックコマンドまで(キックコマンドを含む)のサイズを正しく指定する必要があります。誤った値を指定した場合、意図しない順序でコマンドが実行される、正しく実行を終了できないなどの動作を起こす可能性があります。
前回のコマンドフラッシュ後から、この関数を呼び出すまでに蓄積された 3D コマンドバッファのキャッシュフラッシュはアプリケーションで確実に行ってください。この関数内でも割り込み発生コマンドなどが生成されるため、キャッシュ全体のフラッシュは関数を呼び出したあとでなければなりません。また、キャッシュがフラッシュされる前に実行されないようにするため、この関数は実行中のコマンドリストに対して呼び出すことができません。なお、glClear()
や nngxTransferRenderImage()
、glCopyTexImage2D()
などの関数は、関数内で nngxFlush3DCommand()
と同じ手法のフラッシュを実行します。これらの関数を呼び出す前に、必ず本関数でフラッシュしてください。
nngxAddJumpCommand()
や nngxAddSubroutineCommand()
を使用してキックコマンドを追加した場合、最初のキックコマンドまでを実行サイズとしてキックするようにドライバ側がサイズを調整します。そのため、これらの関数を使用している場合は、nngxFlush3DCommandPartially()
を呼び出す必要はありません。
nngxAddSubroutineCommand()
でキックコマンドを追加したコマンドバッファに対して部分フラッシュを行うと、ドライバ側で計算された実行サイズではなく、buffersize
で指定されたサイズが実行サイズとして使用されることに注意してください。
エラー |
原因 |
---|---|
|
オブジェクト名が 0 のコマンドリストをバインドしているときに呼び出した |
|
すでにコマンドリクエストの蓄積数が最大数に達していた |
|
この関数が追加するコマンドにより 3D コマンドバッファが一杯になる |
|
|
|
実行中のコマンドリストに対して呼び出した |
4.1.8. クリア
コマンドリストをクリアして、3D コマンドバッファとコマンドリクエストのキューを未使用状態(メモリ領域確保直後の状態)にします。
void nngxClearCmdlist(void);
エラー |
原因 |
---|---|
|
実行中のコマンドリストに対して呼び出した |
4.1.8.1. クリアと 3D コマンドバッファのフィル
4.1.9. パラメータ設定
nngxSetCmdlistParameteri()
を呼び出すことで、コマンドリストの設定パラメータを指定することができます。
void nngxSetCmdlistParameteri(GLenum pname, GLint param);
pname の値 |
設定の内容 |
---|---|
|
この設定はコマンドリストオブジェクトごとに設定され、以下の値から設定を選択します。
この設定が
この設定は、 ガス密度情報描画の加算ブレンド結果の更新については、「3DS プログラミングマニュアル - グラフィックス応用編」の「ガス制御設定レジスタ」も併せて参照してください。 |
エラー |
原因 |
---|---|
|
実行中のコマンドリストに対して呼び出した |
|
|
4.1.10. パラメータ取得
nngxGetCmdlistParameteri()
を呼び出すことで、コマンドリストの設定パラメータを取得することができます。
void nngxGetCmdlistParameteri(GLenum pname, GLint* param);
pname の値 |
取得可能な設定の内容 |
---|---|
|
コマンドリストの実行状態。
|
|
蓄積された 3D コマンドバッファのバイトサイズ。 |
|
蓄積されたコマンドリクエストの個数。 |
|
3D コマンドバッファの最大サイズ。
|
|
コマンドリクエストの最大個数。
|
|
3D コマンドバッファの先頭アドレス。 |
|
現在バインドされているコマンドリストのオブジェクト名。 |
|
実行済みの 3D コマンドバッファのバイトサイズ。 |
|
実行済みのコマンドリクエストの個数。 |
|
コマンドリクエストのリクエストキュー用データ領域の先頭アドレス。 |
|
次に実行されるコマンドリクエストまたは実行中のコマンドリクエストのコマンドの種類。
コマンドの種類は以下のマクロで定義されています。
|
|
コマンドバッファのアドレスとバイトサイズ。
現在バインドされているコマンドリストが実行停止中の場合は、次に実行されるコマンドリクエストのパラメータ情報が返されます。実行中の場合は、現在実行中のコマンドリクエストのパラメータ情報が返されます。すべてのコマンドリクエストの実行が完了している場合は何も返しません。 次に実行される、または実行中のコマンドリクエストが 3D 実行コマンドである場合にのみ対応しています。それ以外のコマンドの場合は何も返しません。 |
|
ハードウェアの状態を示す 32 ビットのデータ。 以下の状態である場合に対応するビットに 1 がセットされます。 ビット 20 : ポスト転送が実行中 |
|
現在バインドされているコマンドリストで、次にコマンドが蓄積される 3D コマンドバッファのアドレス。 |
エラー |
原因 |
---|---|
|
|
|
オブジェクト名が 0 のコマンドリストがバインドされているときに、 |
4.1.11. コマンド終了割り込み
コマンドリストのコマンドリクエストが終了したタイミングで割り込みを発生させ、割り込みハンドラを呼び出すことができます。割り込みハンドラは nngxSetCmdlistCallback()
で登録することができます。
void nngxSetCmdlistCallback(void (*func)(GLint));
割り込みハンドラはバインドされているコマンドリストに対してのみ有効です。func
に 0(NULL
)を渡して呼び出した場合はハンドラの登録を解除します。
割り込みハンドラはメインスレッドとは異なるスレッドから呼び出されますので、メインスレッドと共有するデータを参照する場合は排他処理が必要です。ただし、同じグラフィックス関連の nngxSetVSyncCallback()
によって登録されたコールバック関数との間では排他処理が不要となっています。
エラー |
原因 |
---|---|
|
実行中のコマンドリストに対して呼び出した |
割り込みを発生させるには、nngxEnableCmdlistCallback()
で終了時に割り込みを発生させるコマンドリクエストを指定します。nngxDisableCmdlistCallback()
は割り込みの発生を無効化することができます。
void nngxEnableCmdlistCallback(GLint id); void nngxDisableCmdlistCallback(GLint id);
id
には何番目に蓄積されたコマンドリクエストの終了時に割り込みを発生させるかを指定します。1 つのコマンドリストに対して別々の id
で複数回呼び出し、割り込みを複数回発生させることもできます。id
番目に実行されるコマンドリクエストではなく、id
番目に蓄積されたコマンドリクエストであることに注意してください。指定する id
の値には、nngxGetCmdlistParameteri()
の pname
を NN_GX_CMDLIST_USED_REQCOUNT
で呼び出して取得した結果を利用することができます。id
に -1 を指定した場合は、コマンドリストに蓄積されたコマンドリクエストすべてが終了したときに割り込みが発生します。
コマンドリストに蓄積されたコマンドリクエストの最後尾以外への割り込みは、割り込みハンドラが呼び出されたときにまだコマンドリストは実行中です。そのため、コマンドリストの実行中に呼び出すことができない関数は割り込みハンドラ内で呼び出すことはできません。
割り込みハンドラを登録しなくても、nngxGetCmdlistParameteri()
の pname
を NN_GX_CMDLIST_IS_RUNNING
で呼び出して取得した結果が GL_FALSE
になるまで待つことで、コマンドリクエストの実行終了を判断することができます。
エラー |
原因 |
---|---|
|
|
4.1.12. コマンド実行の完了待ち
nngxWaitCmdlistDone()
の呼び出しで、コマンドリストに蓄積されたコマンドリクエストの実行がすべて完了するまで待つことができます。
void nngxWaitCmdlistDone(void);
3D 実行コマンドは区切られた部分まで実行されます。蓄積されている 3D 実行コマンドをすべて実行させるには、この関数を呼び出す前に nngxSplitDrawCmdlist()
を呼び出してください。
この関数はコマンドの実行が完了するまで処理を返しませんが、nngxSetTimeout()
でタイムアウト時間を設定することができます。
void nngxSetTimeout(GLint64EXT time, void (*callback)(void));
time
には、nngxWaitCmdlistDone()
の処理がタイムアウトするまでの時間をチック値で指定します。0 を指定した場合はタイムアウトが発生しません。
callback
には、タイムアウト時に呼び出されるコールバック関数を指定します。NULL
を指定した場合はタイムアウト発生時にコールバック関数を呼び出しません。
デフォルトは time
に 0 を、callback
に NULL
を指定した状態ですので、タイムアウトは発生しません。
4.1.13. DMA 転送コマンドの追加
nngxAddVramDmaCommand()
または nngxAddVramDmaCommandNoCacheFlush()
の呼び出しで、VRAM への DMA 転送を行うコマンドがコマンドリストに蓄積されます。前者は転送元のキャッシュフラッシュを行いますが、後者はキャッシュフラッシュを行いません。これらの関数では、メインメモリから VRAM への DMA 転送のみを行うことができます。
void nngxAddVramDmaCommand( const GLvoid* srcaddr, GLvoid* dstaddr, GLsizei size); void nngxAddVramDmaCommandNoCacheFlush( const GLvoid* srcaddr, GLvoid* dstaddr, GLsizei size);
srcaddr
には転送元のアドレス、dstaddr
には転送先のアドレスをそれぞれ指定し、size
には転送するデータのサイズを指定します。
nngxAddVramDmaCommand()
では、有効なコマンドリストがバインドされていない状態で呼び出されたときは GL_ERROR_8062_DMP
のエラーを、size
に負の値を指定したときは GL_ERROR_8064_DMP
のエラーを生成します。
nngxAddVramDmaCommandNoCacheFlush()
では、有効なコマンドリストがバインドされていない状態で呼び出されたときは GL_ERROR_8090_DMP
のエラーを、size
に負の値を指定したときは GL_ERROR_8091_DMP
のエラーを生成します。
4.1.14. アンチエイリアスフィルタ転送コマンドの追加
nngxFilterBlockImage()
の呼び出しで、アンチエイリアスフィルタを適用したイメージ転送を行うコマンドがコマンドリストに蓄積されます。イメージ転送はブロックフォーマットのまま行われ、フォーマットの変換は行われません。アンチエイリアスの指定は 2x2 にのみ対応しています。
void nngxFilterBlockImage(const GLvoid* srcaddr, GLvoid* dstaddr, GLsizei width, GLsizei height, GLenum format);
srcaddr
には転送元のアドレス、dstaddr
には転送先のアドレスをそれぞれ指定し、width
、height
、format
には転送元のイメージの幅、高さ、フォーマットをそれぞれ指定します。
width
と height
は format
の指定によって以下のように制限されています。
format |
width |
height |
---|---|---|
|
64 以上かつ、64 の倍数 |
64 以上かつ、16 の倍数 |
|
128 以上かつ、128 の倍数 |
128 以上かつ、16 の倍数 |
転送元の領域と転送先の領域が重なる場合、srcaddr
と dstaddr
が等しい、または srcaddr
が dstaddr
より大きいならば正常に動作します。なお、srcaddr
が dstaddr
より小さい場合は転送結果が壊れる可能性があります。
srcaddr
にデバイスメモリ上のアドレスを指定した場合、転送元の領域のキャッシュがフラッシュされていなければ、正しい結果にならない可能性があります。
エラー |
原因 |
---|---|
|
オブジェクト名が 0 のコマンドリストをバインドしているとき、コマンドリクエストのキューに空きがないときに呼び出した |
|
|
|
制限に抵触する |
|
制限に表記されている以外のフォーマットを |
4.1.15. 画像イメージ転送コマンドの追加
nngxTransferLinearImage()
の呼び出しで、レンダーバッファまたはテクスチャへの画像イメージ転送を行うコマンドがコマンドリストに蓄積されます。カレントの 3D コマンドバッファに区切られていないコマンドが蓄積されている場合は、区切りのコマンドを追加してから転送コマンドが追加されます。
イメージの転送中にリニアフォーマットからブロックフォーマットへの変換が行われますが、行われるのはアドレッシングの変換のみです。レンダーバッファに対して呼び出した場合、転送時のブロックアドレッシングへの変換はブロックモードの設定によって、自動的に 8 ブロック用と 32 ブロック用のアドレッシング変換が行われます。テクスチャに対して呼び出した場合は 8 ブロック用のアドレッシング変換が行われます。どちらの場合でも、転送元画像に対して V 方向のフリップとバイトオーダーの変換を事前に行う必要があります。
ブロックモードについては、「3DS プログラミングマニュアル - グラフィックス応用編」の「ブロックモードの設定」を参照してください。
void nngxTransferLinearImage(const GLvoid* srcaddr, GLuint dstid, GLenum target);
srcaddr
には転送元画像の先頭アドレスを指定します。画像は、転送先のレンダーバッファまたはテクスチャと同じフォーマット、同じ幅、高さでなければなりません。ただし、ピクセルフォーマットが 24 ビットフォーマット同士の転送はハードウェアでサポートされていませんので、転送先のピクセルフォーマットが 24 ビットフォーマットの場合、転送元のピクセルデータは 32 ビットフォーマットでなければなりません。このとき、転送元データは 4 バイトごとに最初の 1 バイト(内部フォーマットのアルファ成分)が切り捨てられて転送されます。
dstid
には転送先のレンダーバッファまたはテクスチャのオブジェクト ID を、target
には転送先オブジェクトの種類を指定します。
target の値 |
dstid に指定する値 |
---|---|
|
レンダーバッファのオブジェクト ID。 0 を指定した場合は、カレントのフレームバッファにアタッチされているカラーバッファに転送されます。 |
|
2D テクスチャのオブジェクト ID。 |
|
キューブマップテクスチャのオブジェクト ID。 |
転送先のレンダーバッファの幅および高さは、ブロックモードがブロック 8 モードの場合は 8 の倍数、ブロック 32 モードの場合は 32 の倍数でなければなりません。また、幅および高さは 128 以上でなければなりません。
エラー |
原因 |
---|---|
|
オブジェクト名が 0 のコマンドリストをバインドしているときに呼び出した |
|
すでにコマンドリクエストの蓄積数が最大値に達していた |
|
この関数が追加するコマンドにより 3D コマンドバッファが一杯になる |
|
|
|
転送先のレンダーバッファの幅および高さの制限に抵触する |
|
|
|
転送先のレンダーバッファまたはテクスチャのピクセルサイズが 32 ビット、24 ビット、16 ビット以外 |
4.1.16. ブロックイメージからリニアイメージへの変換転送コマンドの追加
nngxAddB2LTransferCommand()
の呼び出しで、ブロックイメージをリニアイメージに変換して転送を行うコマンドがコマンドリストに追加されます。nngxTransferRenderImage()
でも同様の機能が提供されていますが、この関数は、より汎用的な機能を提供します。また、3D コマンドの区切りコマンドを追加せず、転送リクエストコマンドのみを追加する点が異なります。
void nngxAddB2LTransferCommand( const GLvoid* srcaddr, GLsizei srcwidth, GLsizei srcheight, GLenum srcformat, GLvoid* dstaddr, GLsizei dstwidth, GLsizei dstheight, GLenum dstformat, GLenum aamode, GLboolean yflip, GLsizei blocksize);
srcaddr
には転送元(ブロックイメージ)のアドレスを指定します。dstaddr
には転送先(リニアイメージ)のアドレスを指定します。srcaddr
と dstaddr
は、ともに 16 バイトアライメントでなければなりません。
srcwidth
と srcheight
、dstwidth
と dstheight
には、それぞれ転送元イメージの幅と高さ、転送先イメージの幅と高さをピクセル数で指定します。転送元イメージおよび転送先イメージの幅と高さは、ブロックサイズ(8 または 32)の倍数でなければなりません。さらに、転送先イメージのピクセルサイズが 24 bit、かつブロックサイズが 8 の場合、転送元イメージの幅と転送先イメージの幅は 16 の倍数でなければなりません。srcwidth
、srcheight
、dstwidth
、dstheight
のいずれかに 0 が指定されていると、コマンドは追加されません。転送先イメージの幅と高さのピクセル数は、転送元イメージと同じか小さくなければなりません。
転送元イメージおよび転送先イメージの幅と高さのピクセル数は最小サイズの制限があります。転送元イメージの幅と高さの最小値は128です。転送先イメージの幅と高さの最小値は、アンチエイリアスの設定に依存します。アンチエイリアスが無効の場合は幅と高さともに128、2x1アンチエイリアスが有効な場合は幅が64、高さが128、2x2アンチエイリアスが有効な場合は幅と高さともに64です。
srcformat
と dstformat
には、それぞれ転送元イメージおよび転送先イメージのピクセルフォーマットを指定します。指定可能なピクセルフォーマットは以下の 5 種類です。
定義 |
ビット数 |
フォーマットの詳細 |
---|---|---|
|
16 |
RGBA 各成分とも 4 bit |
|
16 |
RGB 各成分が 5 bit、アルファ成分が 1 bit |
|
16 |
RB 成分が各 5 bit、G 成分が 6 bit。アルファ成分なし |
|
24 |
RGB 各成分とも 8 bit。アルファ成分なし |
|
32 |
RGBA 各成分とも 8 bit |
ピクセルフォーマットのビット数が大きくなる変換はできません。つまり、24 bit のフォーマットから 32 bit のフォーマットへの変換、16 bit のフォーマットから 24 bit または 32 bit のフォーマットへの変換はできません。
aamode
にはアンチエイリアスフィルタのモードを指定します。指定可能なモードは以下の 3 種類です。表中の幅と高さは、転送元に必要なサイズが転送先の何倍以上であるかを示したものです。
定義 |
アンチエイリアス |
幅 |
高さ |
---|---|---|---|
|
アンチエイリアスなし |
等倍 |
等倍 |
|
2x1 アンチエイリアスで転送 |
2 倍 |
等倍 |
|
2x2 アンチエイリアスで転送 |
2 倍 |
2 倍 |
yflip
にはイメージを転送するときに縦方向のフリップを有効にするかどうかを指定します。GL_TRUE
(または 0 以外の値)を指定した場合はフリップが行われ、GL_FALSE
(または 0 )を指定した場合はフリップが行われません。
blocksize
には、転送元イメージのブロックサイズを 8 または 32 で指定します。
エラー |
原因 |
---|---|
|
オブジェクト名が 0 のコマンドリストをバインドしている、またはコマンドリクエストのキューに空きがない |
|
|
|
|
|
|
|
|
|
|
|
|
GL_ERROR_8083_DMP
|
転送先イメージが転送元イメージより幅または高さのピクセル数が大きくなるような指定をした |
|
転送元イメージの幅または高さのピクセル数に最小値未満の値を指定した |
|
転送先イメージの幅または高さのピクセル数に最小値未満の値を指定した |
4.1.17. リニアイメージからブロックイメージへの変換転送コマンドの追加
nngxAddL2BTransferCommand()
の呼び出しで、リニアイメージをブロックイメージに変換して転送を行うコマンドがコマンドリストに追加されます。nngxTransferLinearImage()
でも同様の機能が提供されていますが、この関数は、より汎用的な機能を提供します。また、3D コマンドの区切りコマンドを追加せず、転送リクエストコマンドのみを追加する点が異なります。
void nngxAddL2BTransferCommand( const GLvoid* srcaddr, GLvoid* dstaddr, GLsizei width, GLsizei height, GLenum format, GLsizei blocksize);
srcaddr
には転送元(リニアイメージ)のアドレスを指定します。dstaddr
には転送先(ブロックイメージ)のアドレスを指定します。srcaddr
と dstaddr
は、ともに 16 バイトアライメントでなければなりません。
width
と height
には、それぞれ転送元および転送先のイメージの幅と高さをピクセル数で指定します。イメージの幅と高さは、転送元と転送先で同じでなければならず、128 以上かつブロックサイズ(8 または 32)の倍数でなければなりません。さらに、転送先イメージのピクセルサイズが 24 bit の場合は、ブロックサイズが 8 であっても、イメージの幅は 32 の倍数でなければなりません。width
または height
に 0 が指定されていると、コマンドは追加されません。
format
には転送されるイメージのピクセルフォーマットを指定します。指定可能なピクセルフォーマットは nngxAddB2LTransferCommand()
と同じです(表 4-22)。転送元と転送先のイメージは同じピクセルフォーマットでなければなりません。ただし、24 bit のフォーマットの場合、ハードウェアによる 24 bit から 24 bit への転送がサポートされていないため、転送元イメージが 32 bit のフォーマットでなければなりません。その場合、転送元データの 4 バイトごとに最初の 1 バイトが切り捨てられて転送されます。
blocksize
には変換先イメージのブロックサイズを 8 または 32 から指定します。
エラー |
原因 |
---|---|
|
オブジェクト名が 0 のコマンドリストをバインドしている、またはコマンドリクエストのキューに空きがない |
|
|
|
|
|
|
|
|
4.1.18. ブロックイメージ転送コマンドの追加
nngxAddBlockImageCopyCommand()
の呼び出しで、ブロックイメージの転送を行うコマンドがコマンドリストに追加されます。追加されるコマンドにより、描画されたレンダーバッファやテクスチャ間での画像のコピーを行うことができます。転送サイズとスキップサイズの組を指定した転送を行うため、転送元イメージの部分領域を切り出したり、転送先イメージの部分領域にはめ込んだりすることができます。この関数はブロックフォーマットのイメージを転送することを主な目的としていますが、フォーマットの変換を行わないため、各種データの転送に利用することができます。
void nngxAddBlockImageCopyCommand( const GLvoid* srcaddr, GLsizei srcunit, GLsizei srcinterval, GLvoid* dstaddr, GLsizei dstunit, GLsizei dstinterval, GLsizei totalsize);
srcaddr
には転送元の開始アドレスを指定します。dstaddr
には転送先の開始アドレスを指定します。srcaddr
と dstaddr
は、ともに 16 バイトアライメントでなければなりません。
totalsize
には転送を行うサイズの合計をバイト単位で指定します。totalsize
は 16 の倍数でなければなりません。
srcunit
と srcinterval
には、それぞれ転送元の読み込み単位サイズとスキップサイズをバイト単位で指定します。srcunit
バイトのデータ転送と srcinterval
バイトの読み込みアドレスのスキップが交互に繰り返し行われ、転送されたサイズが totalsize
に達したときに転送が終了します。srcinterval
に 0 を指定した場合スキップは行われず、totalsize
バイトの連続領域の読み込みが行われることになります。srcinterval
に 0 以外を指定した場合は読み込みとスキップを繰り返すため、転送元イメージの部分領域を切り出して転送することができます。
dstunit
と dstinterval
には、それぞれ転送先の書き込み単位サイズとスキップサイズをバイト単位で指定します。dstunit
バイトのデータ書き込みと、dstinterval
バイトの書き込みアドレスのスキップが交互に繰り返し行われ、転送されたサイズが totalsize
に達したときに転送が終了します。dstinterval
に 0 を指定した場合スキップは行われず、totalsize
バイトの連続領域の書き込みが行われることになります。dstinterval
に 0 以外を指定した場合は書き込みとスキップを繰り返すため、転送先イメージの部分領域にイメージをはめ込むような転送を行うことができます。
srcunit
、srcinterval
、dstunit
、dstinterval
は 16 の倍数でなければなりません。また、0x100000 以上の値や負の値は指定することができません。
描画結果などのブロックイメージを転送する際は、転送イメージの先頭アドレス(転送元と転送先)が画像の左上(OpenGL ES では左下)である点や、ブロックサイズ 8 のフォーマットであれば 8×8 ピクセルのブロック単位でデータが配置されている点などに注意して、引数を設定してください。ブロックフォーマットの詳細については、「7.10. PICA ネイティブフォーマット」を参照してください。
エラー |
原因 |
---|---|
|
オブジェクト名が 0 のコマンドリストをバインドしている、またはコマンドリクエストのキューに空きがない |
|
|
|
|
|
|
4.1.19. メモリフィルコマンドの追加
nngxAddMemoryFillCommand()
の呼び出しで、指定した領域を指定したデータで埋める(フィルする)コマンドがコマンドリストに追加されます。この関数で追加されるコマンドは、カラーバッファとデプスバッファ(ステンシルバッファ)をクリアする場合などに使用します。glClear()
でも同様の機能を提供していますが、この関数は、より汎用的な機能を提供します。独立したパラメータの指定が可能な 2 つのチャンネル設定により、サイズの異なる 2 つの領域を同時にクリアすることができます。
void nngxAddMemoryFillCommand( GLvoid* startaddr0, GLsizei size0, GLuint data0, GLsizei width0, GLvoid* startaddr1, GLsizei size1, GLuint data1, GLsizei width1);
startaddr0
、size0
、data0
、width0
がチャンネル 0 の設定、startaddr1
、size1
、data1
、width1
がチャンネル 1 の設定です。チャンネル 0 とチャンネル 1 によるメモリフィルは同時に実行されます。そのため、チャンネル 0 とチャンネル 1 で指定された領域が重なる場合、どちらの結果が最終的に反映されるのかは不定です。
startaddr0
と startaddr1
には領域の先頭アドレスを指定します。アドレスは 16 バイトアライメントでなければなりません。アドレスに 0 を指定した場合は、そのチャンネルを使用しません。startaddr0
に 0 を指定した場合、size0
、data0
、width0
の指定に関するエラーはチェックされません。startaddr1
に 0 を指定した場合、size1
、data1
、width1
の指定に関するエラーはチェックはされません。
size0
と size1
には領域のサイズをバイト単位で指定します。サイズは 16 の倍数でなければなりません。
data0
と data1
にはフィルパターンのデータを指定します。領域には指定されたデータが繰り返し格納されます。
width0
と width1
にはフィルパターンのビット幅を指定します。指定可能なビット幅は 16、24、32 のいずれかです。16 を指定した場合は、データのビット [ 15 : 0 ] を使って 16 bit 単位で埋められます。24 を指定した場合は、データのビット [ 23 : 0 ] を使って 24 bit 単位で埋められます。32 を指定した場合は、データのビット [ 31 : 0 ] を使って 32 bit 単位で埋められます。
下表は、レンダーバッファのフォーマットによるフィルパターンの指定(ビット幅、各成分値)をまとめたものです。
レンダーバッファのフォーマット |
ビット幅 |
R / D |
G / S |
B |
A |
---|---|---|---|---|---|
|
32 |
[ 31 : 24 ] |
[ 23 : 16 ] |
[ 15 : 8 ] |
[ 7 : 0 ] |
|
24 |
[ 23 : 16 ] |
[ 15 : 8 ] |
[ 7 : 0 ] |
- |
|
16 |
[ 15 : 12 ] |
[ 11 : 8 ] |
[ 7 : 4 ] |
[ 3 : 0 ] |
|
16 |
[ 15 : 11 ] |
[ 10 : 6 ] |
[ 5 : 1 ] |
[ 0 : 0 ] |
|
16 |
[15 : 11 ] |
[ 10 : 5 ] |
[ 4 : 0 ] |
- |
|
32 |
[ 23 : 0 ] |
[ 31 : 24 ] |
- |
- |
|
24 |
[ 23 : 0 ] |
- |
- |
- |
|
16 |
[ 15 : 0 ] |
- |
- |
- |
エラー |
原因 |
---|---|
|
オブジェクト名が 0 のコマンドリストをバインドしている、またはコマンドリクエストのキューに空きがない |
|
|
|
|
|
|
4.1.20. 3D コマンドバッファのポインタ移動
nngxMoveCommandbufferPointer()
の呼び出しで、現在バインドされているコマンドリストの 3D コマンドバッファのポインタ(3D コマンドの実行位置)を移動させることができます。
void nngxMoveCommandbufferPointer(GLint offset);
offset
にはポインタの移動量をバイト単位で指定します。
コマンドリストがバインドされていない場合や、ポインタが 3D コマンドバッファの領域外に移動してしまう場合は GL_ERROR_8061_DMP
のエラーを生成します。
4.1.21. ジャンプコマンドの追加
nngxAddJumpCommand()
の呼び出しで、指定された領域にある 3D コマンドを実行するジャンプコマンドが、現在バインドされているコマンドリストに追加されます。ジャンプコマンドを利用すると、割り込みを発生させることなく、別のコマンドリストを続けて実行することができます。
この関数は PICA レジスタのコマンドバッファ実行レジスタを利用しています。チャンネル 0 のみを使用していますので、2 つのレジスタ(0x0238 と 0x023A)の内容が関数の実行時に書き換えられます。動作の詳細については、「3DS プログラミングマニュアル - グラフィックス応用編」の「コマンドバッファ実行レジスタ(0x0238 ~ 0x023D)」と「コマンドバッファの連続実行」を参照してください。
void nngxAddJumpCommand(const GLvoid* bufferaddr, GLsizei buffersize);
bufferaddr
と buffersize
には、続けて実行されるコマンドバッファのアドレスとバッファのサイズを指定します。bufferaddr
と buffersize
は、ともに 16 の倍数である必要があります。
ジャンプ先(bufferaddr
と buffersize
で指定されたコマンドリスト)のコマンドバッファの内容は、現在バインドされているコマンドリストのコマンドバッファにコピーされません。ジャンプコマンドはコマンドバッファの実行アドレスを変更し、ジャンプ先のコマンドバッファを直接実行します。そのため、ジャンプ先の領域のキャッシュがフラッシュされていることをアプリケーションで保証しなければなりません。
ジャンプ先で最後に実行されるコマンドは、区切りコマンド(nngxSplitDrawCmdlist()
で追加される区切りコマンド設定レジスタへの書き込みコマンド)でなければなりません。区切りコマンドの代わりにジャンプコマンドでジャンプを繰り返すことができます。ただし、ジャンプを繰り返した場合は、最終的に実行されるコマンドバッファの最後に区切りコマンドが必要です。
この関数は、3D 実行コマンドのコマンドリクエストを追加します。nngxFlush3DCommand()
などでコマンドバッファがフラッシュされた直後に呼び出しても意味がないため、GL_ERROR_809A_DMP
のエラーが生成されます。フラッシュされた直後のコマンドバッファに 3D コマンドを追加する場合は nngxAdd3DCommand()
を呼び出してください。
エラー |
原因 |
---|---|
|
オブジェクト名が 0 のコマンドリストをバインドしている |
|
|
|
|
|
|
|
現在バインドされているコマンドリストのコマンドバッファがフラッシュされた直後 |
|
この関数で追加されるコマンドリクエストによってキューがあふれる |
|
この関数で追加されるコマンドによってコマンドバッファがあふれる |
4.1.22. サブルーチンコマンドの追加
nngxAddSubroutineCommand()
の呼び出しで、指定された領域にある 3D コマンドを実行するジャンプコマンドとジャンプ元のコマンドバッファに戻るためのアドレス情報設定用のコマンドが、現在バインドされているコマンドリストに追加されます。サブルーチンコマンドを利用すると、割り込みを発生させることなく、別のコマンドリストをサブルーチンのように実行することができます。
この関数は PICA レジスタのコマンドバッファ実行レジスタを利用しています。すべてのチャンネルを使用していますので、4 つのレジスタ(0x0238 ~ 0x023B)の内容が関数の実行時に書き換えられます。動作の詳細については、「3DS プログラミングマニュアル - グラフィックス応用編」の「コマンドバッファ実行レジスタ(0x0238 ~ 0x023D)」と「同じコマンドバッファの繰り返し実行」 を参照してください。
void nngxAddSubroutineCommand(const GLvoid* bufferaddr, GLsizei buffersize);
bufferaddr
と buffersize
には、続けて実行されるコマンドバッファのアドレスとバッファのサイズを指定します。bufferaddr
と buffersize
は、ともに 16 の倍数である必要があります。
ジャンプ先(bufferaddr
と buffersize
で指定されたコマンドリスト)のコマンドバッファの内容は、現在バインドされているコマンドリストのコマンドバッファにコピーされません。ジャンプコマンドはコマンドバッファの実行アドレスを変更し、ジャンプ先のコマンドバッファを直接実行します。そのため、ジャンプ先の領域のキャッシュがフラッシュされていることをアプリケーションで保証しなければなりません。
ジャンプコマンドはチャンネル 0 で実行され、元のコマンドバッファに戻るコマンドはチャンネル 1 で実行されます。そのため、ジャンプ先で最後に実行されるコマンドはチャンネル 1 のキックコマンド(コマンドバッファ実行レジスタ 0x023D への書き込みコマンド)でなければなりません。代わりにジャンプコマンドで別のコマンドバッファへジャンプすることもできますが、ジャンプに使用するチャンネルは 0 でなければならず、最終的に実行されるコマンドバッファの最後にチャンネル 1 のキックコマンドが必要です。また、その途中でチャンネル 1 のアドレス設定(0x0239、0x023B)を書き換えないでください。この関数が追加するのはジャンプコマンド(チャンネル 0)とアドレス設定(チャンネル 1)です。チャンネル 1 のキックコマンドやサブルーチン内でのジャンプコマンドはアプリケーションが配置しなければなりません。
この関数は、3D 実行コマンドのコマンドリクエストを追加しません。この関数を呼び出したあとも続けてコマンドを蓄積し、nngxFlush3DCommand()
などでコマンドバッファをフラッシュしてから実行してください。コマンドバッファがフラッシュされるまで、この関数が追加するチャンネル 1 のサイズ設定レジスタ(0x023B)への書き込まれる値は未確定の状態ですので、コマンドバッファをフラッシュする前にコピーした内容を再利用したときの動作は不定です。
エラー |
原因 |
---|---|
|
オブジェクト名が 0 のコマンドリストをバインドしている |
|
|
|
|
|
|
|
この関数で追加されるコマンドによってコマンドバッファがあふれる |
4.2. コマンドリクエストの種類
コマンドリストにキューイングされるコマンドリクエストには、以下のコマンドがあります。
DMA 転送コマンド
メインメモリから VRAM への、テクスチャイメージや頂点バッファの DMA 転送を行うコマンドです。
glTexImage2D()
などのテクスチャ領域確保や glBufferData()
などの頂点バッファ領域確保でキューイングされます。
3D 実行コマンド
3D コマンドバッファに蓄積されている 3D コマンドを、コマンドセット 1 つ分実行するコマンドです。
glClear()
や glTexImage2D()
などの呼び出しでバッファ読み込み終了の 3D コマンドが書き込まれ、蓄積していた 3D コマンドバッファを 1 つの 3D 実行コマンドとしてキューイングされます。
nngxSplitDrawCmdlist()
により、任意のタイミングで 3D 実行コマンドをキューイングさせることもできます。
メモリフィルコマンド
GPU のメモリフィル機能によって、VRAM 上に確保されている領域を指定したデータパターンでクリアするコマンドです。
レンダーバッファを指定し、glClear()
を呼び出したときにキューイングされます。glClear()
の実行には、メモリフィル以外にも 3D コマンドの実行が必要となります。つまり glClear()
を呼び出すと、3D コマンドバッファに glClear()
用とバッファ読み込み終了の 3D コマンドが書き込まれ、3D 実行コマンドとメモリフィルコマンドが続けてキューイングされます。
ポスト転送コマンド
GPU のポストフィルタ機能によって、PICA ブロックフォーマットで描画されたイメージを LCD が読み込めるリニアフォーマットに変換するコマンドです。
nngxTransferRenderImage()
を呼び出したときにキューイングされます。nngxSplitDrawCmdlist()
を事前に呼び出して 3D コマンドバッファの読み込みを終了させていなければ、バッファ読み込み終了コマンドの書き込みと 3D 実行コマンドのキューイングのあとに、このコマンドがキューイングされることになります。
レンダーテクスチャ転送コマンド
GPU の描画結果をテクスチャイメージとしてメモリにコピーするコマンドです。
glCopyTexImage2D()
または glCopyTexSubImage2D()
を呼び出したときにキューイングされます。
nngxSplitDrawCmdlist()
を事前に呼び出して 3D コマンドバッファの読み込みを終了させていなければ、バッファ読み込み終了コマンドの書き込みと 3D 実行コマンドのキューイングのあとに、このコマンドがキューイングされることになります。
4.3. 3D コマンドバッファのパフォーマンスを最適化する手法
3D コマンドバッファ実行時のパフォーマンスを最適化する手法を説明します。
4.3.1. アドレスとサイズによるロード速度の変化
3D コマンドバッファのアドレスとサイズが、実行時のロード速度に影響する場合があります。
3D コマンドバッファの実行方法には、コマンドリクエストにキューイングされた 3D 実行コマンドでの実行と、コマンドバッファ実行レジスタでの実行の 2 種類があります。
3D 実行コマンドにより実行される場合は、nngxFlush3DCommand()
や nngxSplitDrawCmdlist()
で区切りコマンドが追加された直後のアドレスから、次に区切りコマンドが追加されるまでのサイズにより影響を受けます。3D コマンドバッファに蓄積中の 3D コマンドのアドレスは、nngxGetCmdlistParameteri()
の pname
に NN_GX_CMDLIST_CURRENT_BUFADDR
を渡して取得できます。
コマンドバッファ実行レジスタにより実行される場合は、nngxAddJumpCommand()
で追加されるコマンドバッファのアドレスとサイズ、nngxAddSubroutineCommand()
で追加されるサブルーチンとしてのコマンドバッファおよびサブルーチンから呼び出し元に戻るために実行されるコマンドバッファのアドレスとサイズにより影響を受けます。
3D コマンドバッファのアドレスが 128 バイトアライメントの場合、サイズが 256 バイト単位(256 バイト、512 バイト、768 バイトなど)ならば転送速度が向上する可能性があります。
3D コマンドバッファのアドレスが 128 バイトアライメントではない場合、直前の 128 バイトアライメントのアドレスから 3D コマンドバッファの終端までのサイズが 256 バイト単位である場合に速度が向上する可能性があります。例えば 3D コマンドバッファのアドレスとサイズがそれぞれ 0x20000010 と 0x1F0 だった場合、直前の 128 バイトアライメントのアドレスは 0x10 だけ手前の 0x20000000 です。そこから終端までは 0x1F0 + 0x10 で 0x200 となり、256 バイト単位であると言えます。
GPU の実装上の理由により、3D コマンドバッファのアドレスとサイズには上記のような特性がありますが、格納場所や 3D コマンドの内容、ほかのモジュールとのメモリアクセスの競合などにより、大きな効果が得られない場合があります。
4.3.2. サブルーチン実行の利用
3D コマンドバッファのサブルーチン実行を利用することで、パフォーマンスが向上する可能性があります。
4.3.2.1. 概要
3D コマンドバッファのサブルーチン実行とは、コマンドバッファ実行レジスタを使用した実行方法です。3D コマンドを一続きの 3D コマンドバッファに格納して実行する通常の手法とは異なり、別の場所に格納してあるコマンドバッファをコマンドバッファのアドレスジャンプ機能を使って連続で実行させる手法です。3D コマンドバッファのアドレスを特定のアドレスへジャンプさせ、ジャンプ先の 3D コマンドバッファを実行したあと、ジャンプ元へ戻るという制御を行うことから、この手法をコマンドバッファのサブルーチン実行と呼びます。
コマンドバッファのサブルーチン実行を行うための具体的な方法については、「4.1.22. サブルーチンコマンドの追加」および「3DS プログラミングマニュアル - グラフィックス応用編」の「コマンドバッファ実行レジスタ(0x0238 ~ 0x023D)」を参照してください。
4.3.2.2. 動作への影響
コマンドバッファをサブルーチン化して実行することには、以下のメリットがあります。
- サブルーチン化されたコマンドバッファへのジャンプコマンドなどを格納するだけでよいため、3D コマンドのコピーに必要な CPU 処理を削減できます。つまり、参照テーブルのデータやシェーダプログラムのロードなど、ある程度サイズが大きく、頻繁に設定が行われる処理で効果を発揮します。
- サブルーチン化されたコマンドバッファはカレントの 3D コマンドバッファへコピーされずに GPU から直接参照されるため、コマンドバッファの総サイズを縮小することができます。
- サブルーチン化されたコマンドバッファを VRAM に格納すれば、GPU からコマンドバッファへのアクセスがメインメモリ(デバイスメモリ)に比べて高速になります。そのため、コマンドバッファへのメモリアクセスがボトルネックとなっている場合に、システム全体の処理速度の向上が期待できます。
一方、以下のデメリットがあります。
- ジャンプコマンドによるアドレスの切り替えで、メモリアクセスのオーバーヘッドが発生します。そのため、サブルーチン化の粒度を小さくして頻繁に呼び出すような実装では、GPU の処理速度が低下する可能性があります。
サブルーチン化による処理速度への影響は、メモリアクセスの競合などに左右されるため、実際のアプリケーションの実装に強く依存します。
4.3.2.3. 格納場所
コマンドバッファのアクセス速度はメインメモリ(デバイスメモリ)より VRAM の方が高速ですので、サブルーチン化されたコマンドバッファは VRAM に格納することを推奨します。
ジャンプコマンドによるサブルーチン化されたコマンドバッファの実行ではメモリアクセスのオーバーヘッドが発生しますが、実行するコマンドバッファが VRAM に格納されている場合はオーバーヘッドが軽減されます。
コマンドバッファを VRAM に格納するには、デバイスメモリ上に生成したあとに、nngxAddVramDmaCommand()
を利用して VRAM に DMA 転送する必要があります。VRAM への DMA 転送については「4.1.13. DMA 転送コマンドの追加」を参照してください。
4.3.2.4. 実行処理とアクセス処理のバランス
サブルーチン化されたコマンドバッファの内容によっては、ボトルネックとなる処理が 3D コマンドのアクセス処理と実行処理の間で移動する場合があります。
3D コマンドがラスタライゼーションモジュール以降のモジュール(ラスタライゼーションモジュールを含む)のレジスタライトコマンドである場合、1 個の 3D コマンドを処理するのに 2 サイクルかかるため、実行処理の比重が大きくなります。3D コマンドがバーストコマンドで構成されている場合は、アクセス処理に対する実行処理の比重はさらに大きくなります。このような場合、実行処理がボトルネックとなり、サブルーチン化で発生するメモリアクセスの処理コストが隠蔽されることになります。
3D コマンドが、ラスタライゼーションモジュールより前のモジュール(ラスタライゼーションモジュールを含まない)のレジスタライトコマンドである場合、1 個の 3D コマンドを処理するのに 1 サイクルかかるため、前者よりも実行処理の比重が小さくなります。この場合、アクセス処理がボトルネックになりやすく、サブルーチン化で発生するメモリアクセスの処理コストが全体のパフォーマンスに影響しやすくなります。
各モジュールの位置関係については「2.2. レンダリングパイプライン」を参照してください。