2. 基本的な処理の流れ

GD ライブラリを使用してグラフィックスを表示するために最低限必要となる処理を、行うべき順番(多少前後することがあります)で示すと以下のようになります。

  • GD ライブラリの初期化
  • フレームバッファの確保
  • シェーダプログラムのロード
  • 頂点データの準備
  • ビューポートの設定
  • 描画の実行
  • 描画結果の表示
  • 終了処理

上記以外の処理については「4. 各モジュールの説明」などを参照してください。

補足:

この章では関数の詳細については説明しません。関数の詳細な情報については、以降の章や関数リファレンスを参照してください。

2.1. GD ライブラリの初期化

GD ライブラリの初期化は nn::gd::System::Initialize() を呼び出すことで行われます。初期化は GD ライブラリのほかの関数を呼び出す前に行われている必要があります。

また、nn::gd::System::Initialize() は内部での作業用メモリ確保のために nngxInitialize() 経由で与えられているアロケータを使用します。このため、GD ライブラリの初期化に先立って nngxInitialize() による GX ライブラリの初期化と、関数内で実行される 3D コマンドのためにコマンドリストの確保を行う必要があります。

以下は GD ライブラリの初期化に必要な流れの一例です。

コード 2-1. 初期化の例
nngxInitialize(...);

nngxGenCmdlists(1, &cmdListId);
nngxBindCmdlist(cmdListId);
nngxCmdlistStorage(0x80000, 128);
nnResult result = nn::gd::System::Initialize();

GD ライブラリでは、同じコマンドリストを使用した状態でも、ほかのフレームワーク(NintendoWare など)と平行して利用することができます。ただし、すでに GD ライブラリ以外でコマンドリストを確保していた場合は、GD ライブラリの初期化前にコマンドリストの確保を行わないように注意してください。

ディスプレイバッファを確保するための関数は GD ライブラリにはありません。ディスプレイバッファの確保は nngx 関数で行ってください。

2.2. フレームバッファの確保

カラーバッファとデプス(ステンシル)バッファの確保は GD ライブラリで行います。

デスクリプタクラスの設定に違いがあるものの、どちらのバッファも Texture2DResource オブジェクトを作成しなければならないのは同じです。バッファのクリアや描画の効率から、以下のコード例のようにカラーバッファとデプス(ステンシル)バッファは VRAM-A と VRAM-B に分散して確保することを推奨します。

コード 2-2. フレームバッファで使用する Texture2DResource オブジェクトの作成
static nn::gd::Texture2DResource* s_texture2DResource_ColorBuffer = 0;
static nn::gd::Texture2DResource* s_texture2DResource_DepthStencilBuffer = 0;

//Color buffer
nn::gd::Texture2DResourceDescription Text2DResDesc_ColorBuffer =
       {nn::gx::DISPLAY0_WIDTH, nn::gx::DISPLAY0_HEIGHT, 1,
        nn::gd::Resource::NATIVE_FORMAT_RGBA_8888,
        nn::gd::Memory::LAYOUT_BLOCK_8, nn::gd::Memory::VRAMA};
nnResult res = nn::gd::Resource::CreateTexture2DResource(
        &Text2DResDesc_ColorBuffer, 0, GD_FALSE,
        &s_texture2DResource_ColorBuffer);
if ( GD_NNRESULT_IS_FAILURE(res) ) {NN_PANIC("\n");}

//Depth Stencil buffer
nn::gd::Texture2DResourceDescription Text2DResDesc_DepthStencilBuffer =
       {nn::gx::DISPLAY0_WIDTH, nn::gx::DISPLAY0_HEIGHT, 1,
        nn::gd::Resource::NATIVE_FORMAT_DEPTH_24_STENCIL_8,
        nn::gd::Memory::LAYOUT_BLOCK_8, nn::gd::Memory::VRAMB};
res = nn::gd::Resource::CreateTexture2DResource(
        &Text2DResDesc_DepthStencilBuffer, 0, GD_FALSE,
        &s_texture2DResource_DepthStencilBuffer);
if ( GD_NNRESULT_IS_FAILURE(res) ) {NN_PANIC("\n");}

作成した Texture2DResource オブジェクトを使って、カラーバッファならば RenderTarget オブジェクトを、デプス(ステンシル)バッファならば DepthStencilTarget オブジェクトをそれぞれ作成します。

コード 2-3. RenderTarget オブジェクトと DepthStencilTarget オブジェクトの作成
static nn::gd::RenderTarget* s_RenderTarget = 0;
static nn::gd::DepthStencilTarget* s_DepthStencilTarget = 0;

//Color buffer
nn::gd::RenderTargetDescription descRenderTarget = {0};
res = nn::gd::OutputStage::CreateRenderTarget(
        s_texture2DResource_ColorBuffer, &descRenderTarget,
        &s_RenderTarget);
if ( GD_NNRESULT_IS_FAILURE(res) ) {NN_PANIC("\n");}

//Depth Stencil buffer
nn::gd::DepthStencilTargetDescription descDepthStencilTarget = {0};
res = nn::gd::OutputStage::CreateDepthStencilTarget(
        s_texture2DResource_DepthStencilBuffer, &descDepthStencilTarget,
        &s_DepthStencilTarget);
if ( GD_NNRESULT_IS_FAILURE(res) ) {NN_PANIC("\n");}

パイプラインへの設定はアウトプットステージで行い、RenderTarget オブジェクトならば SetRenderTarget() を、DepthStencilTarget オブジェクトならば SetDepthStencilTarget() をそれぞれ呼び出します。

コード 2-4. フレームバッファのパイプラインへの設定
//Set the color/depthstencil targets
nn::gd::OutputStage::SetRenderTarget(s_RenderTarget);
nn::gd::OutputStage::SetDepthStencilTarget(s_DepthStencilTarget);

2.3. シェーダプログラムのロード

描画に使用するシェーダプログラムをパイプラインに反映させるまでには、以下の手順が必要です。

  • シェーダバイナリの読み込み
  • ShaderBinary オブジェクトの作成
  • Shader オブジェクトの作成
  • ShaderPipeline オブジェクトの作成
  • パイプラインへの反映

以下のコード例では、シェーダバイナリ(rBufrSize バイトのファイルを読み込み済み)に含まれている頂点シェーダをパイプラインに反映させています。

コード 2-5. シェーダプログラムのロード
static nn::gd::ShaderBinary* shaderBinary = 0;
static nn::gd::ShaderPipeline* shaderPipeline = 0;
static nn::gd::Shader* vertexShader = 0;

nnResult res;
res = nn::gd::ShaderStage::CreateShaderBinary(rBuf, rSize, &shaderBinary);
if ( GD_NNRESULT_IS_FAILURE(res) ) {NN_PANIC("\n");}
res = nn::gd::ShaderStage::CreateShader(shaderBinary, 0, &vertexShader);
if ( GD_NNRESULT_IS_FAILURE(res) ) {NN_PANIC("\n");}
res = nn::gd::ShaderStage::CreateShaderPipeline(
                            vertexShader, NULL, &shaderPipeline);
if ( GD_NNRESULT_IS_FAILURE(res) ) {NN_PANIC("\n");}

nn::gd::ShaderStage::SetShaderPipeline(shaderPipeline);

シェーダプログラムで使用する各レジスタへの値の設定は、シェーダステージの GetShaderUniformLocation() の引数に ShaderPipeline オブジェクトとデータ名を指定して取得した、UniformLocation オブジェクトを介して行います。

コード 2-6. ユニフォーム設定を介したレジスタへの値の設定
static nn::gd::UniformLocation s_shaderVariable_proj;
static nn::gd::UniformLocation s_shaderVariable_view;

s_shaderVariable_proj = nn::gd::ShaderStage::GetShaderUniformLocation(
                            shaderPipeline, "uProjection");
s_shaderVariable_view = nn::gd::ShaderStage::GetShaderUniformLocation(
                            shaderPipeline, "uModelView");
NN_ASSERT(s_shaderVariable_proj.IsValid());
NN_ASSERT(s_shaderVariable_view.IsValid());

nn::math::Matrix44 proj, mv;
nn::gd::ShaderStage::SetShaderPipelineConstantFloat(
        shaderPipeline, s_shaderVariable_proj, static_cast<f32*>(proj));
nn::gd::ShaderStage::SetShaderPipelineConstantFloat(
        shaderPipeline, s_shaderVariable_view, static_cast<f32*>(mv));

2.4. 頂点データの準備

GD ライブラリでは、プリミティブの描画には必ず頂点バッファを使用しなければなりません。

頂点バッファを使用するには、以下の手順が必要となります。

  • InputLayout オブジェクトの作成
  • VertexBufferResource オブジェクトの作成
  • パイプラインへの反映(入力レイアウトと頂点バッファ)

以下のコード例では、頂点座標と頂点カラーがインターリーブされた頂点データを使用しています。

コード 2-7. 頂点データの準備
u16 idxs[] = {0, 1, 2};
nnResult res;

float coords_color[] = {
     0.5f, 0.0f, 0.f, 1.f, 1.f, 0.0f, 0.0f,
    -0.5f, 0.5f, 0.f, 1.f, 0.f, 1.0f, 0.0f,
    -0.5f,-0.5f, 0.f, 1.f, 0.f, 0.0f, 1.0f,
};

nn::gd::InputElementDescription descs[] =
{
    { 0, "aPosition",
         nn::gd::VertexInputStage::STREAM_TYPE_FLOAT, 4, 0},
    { 0, "aColor",
         nn::gd::VertexInputStage::STREAM_TYPE_FLOAT, 3, sizeof(float) * 4},
};
u32 strides[] = { sizeof(float) * 7 };
res = nn::gd::VertexInputStage::CreateInputLayout(
        descs, 2, strides, vertexShader, &inputLayout);
if ( GD_NNRESULT_IS_FAILURE(res) ) {NN_PANIC("\n");}

nn::gd::VertexBufferResourceDescription desc;
desc.m_ByteSize = sizeof(coords_color);
desc.m_MemLocation = nn::gd::Memory::FCRAM;
res = nn::gd::Resource::CreateVertexBufferResource(
        &desc, coords_color, &bufferCoord);
if ( GD_NNRESULT_IS_FAILURE(res) ) {NN_PANIC("\n");}

desc.m_ByteSize = sizeof(idxs);
res = nn::gd::Resource::CreateVertexBufferResource(&desc, idxs, &bufferIndex);
if ( GD_NNRESULT_IS_FAILURE(res) ) {NN_PANIC("\n");}

nn::gd::VertexBufferResource* buffers[] = { bufferCoord };
u32 offsets[] = { 0 };
nn::gd::VertexInputStage::SetVertexBuffers(0, 1, buffers, offsets);
nn::gd::VertexInputStage::SetInputLayout(inputLayout);
nn::gd::VertexInputStage::SetIndexBuffer(
        bufferIndex, nn::gd::VertexInputStage::INDEX_FORMAT_USHORT, 0);

2.5. ビューポートの設定

ビューポートの設定はラスタライザステージの SetViewport() で行います。

以下のコード例では、アウトプットステージの GetRenderTargetProperties() で取得したレンダーターゲットの情報をビューポートの設定に利用しています。

コード 2-8. ビューポートの設定
nn::gd::TargetProperties renderTargetProperty;
nn::gd::OutputStage::GetRenderTargetProperties(
        s_RenderTarget, &renderTargetProperty);
nn::gd::Viewport viewPort(0, 0,
        renderTargetProperty.m_Width, renderTargetProperty.m_Height);
nn::gd::RasterizerStage::SetViewport(viewPort);

2.6. 描画の実行

描画のために呼び出す関数は頂点インデックスを使用するかどうかで異なります。頂点インデックスを使用する場合は nn::gd::System::DrawIndexed() を、使用しない場合 nn::gd::System::Draw() を呼び出します。なお、頂点バッファリソースを使用せずに描画を行う nn::gd::System::DrawImmediate()nn::gd::System::DrawImmediateIndexed() も用意されています。ただし、頂点バッファリソースを使用しない描画は頂点データの取り扱いが柔軟になりますが、描画の実行が遅くなります。

フレームバッファのクリアには nn::gd::Memory::ClearTargets() を使用してください。

以下のコード例では、フレームバッファのクリアと頂点インデックスを使用した描画を行っています。

コード 2-9. 描画の実行
//Clear the render buffers
u8 clearColor[] = {25, 25, 122, 255};
nn::gd::Memory::ClearTargets(s_RenderTarget, s_DepthStencilTarget,
                             clearColor, 1.f, 0);
//Draw
nn::gd::System::DrawIndexed(3, 0);

2.7. 描画結果の表示

カラーバッファの内容を nn::gd::Memory::CopyTexture2DResourceBlockToLinear() でディスプレイバッファにコピーし、描画結果を LCD に表示させます。この関数は nngxTransferRenderImage() とほぼ同じ機能を有していますが、コピー先のアドレスは nngxGetDisplaybufferParameteri() などを利用してアプリケーションで取得しなければなりません。

コード 2-10. 描画結果の表示
//Transfer framebuffer to display buffer
int dstAddr;
nngxActiveDisplay(NN_GX_DISPLAY0);
nngxGetDisplaybufferParameteri(NN_GX_DISPLAYBUFFER_ADDRESS, &dstAddr);
nn::gd::Memory::CopyTexture2DResourceBlockToLinear(
    s_texture2DResource_ColorBuffer,            //source
    0,                                          //sourceMipLevelIndex
    0,                                          //srcOffsetY
    (u8*)dstAddr,                               //dstAddr
    nn::gx::DISPLAY0_WIDTH,                     //dstWidth
    nn::gx::DISPLAY0_HEIGHT,                    //dstHeight
    nn::gd::Resource::NATIVE_FORMAT_RGB_888,    //dstFormat
    nn::gd::Memory::DOWNSCALING_NONE,           //DownScalingMode
    GD_FALSE                                    //yFlip
);
nngxWaitCmdlistDone();
nngxSwapBuffers(NN_GX_DISPLAY_BOTH);

2.8. 終了処理

GD ライブラリの終了処理は nn::gd::System::Finalize() を呼び出すことで行われます。この関数の実行が完了したあとは GD ライブラリの関数を呼び出すことができなくなります。

終了処理では、アプリケーションで確保したものを含めて、ライブラリが管理しているリソースなどが自動的に解放されます。しかし、アプリケーションで作成したステートやリソースなどのオブジェクトは、なるべくアプリケーションで明示的に解放することを推奨します。