2. Basic Flow of Operations

The following list shows the minimum number and (with slight variations) the order of operations necessary to display graphics using the GD library.

  • Initializing the GD Library
  • Allocating the Framebuffer
  • Loading a Shader Program
  • Preparing Vertex Data
  • Viewport Settings
  • Rendering
  • Displaying the Rendered Results
  • Finalization

For all other operations, see other chapters, such as 4. Module Descriptions.

Note:

This chapter does not provide detailed information on each function. For more information, see later chapters in this document or the CTR-SDK API Reference.

2.1. Initializing the GD Library

The nn::gd::System::Initialize() function initializes the GD library. The GD library must be initialized before any other GD functions are called.

The nn::gd::System::Initialize() function uses the allocator passed to the nngxInitialize() function to allocate internal working memory. You must initialize the GX library using nngxInitialize() and allocate a command list for the 3D commands executed within functions before you initialize the GD library.

The following example shows the steps needed to initialize the GD library.

Code 2-1. Initialization Sample
nngxInitialize(...);

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

The GD library can be used in parallel with other frameworks (such as NintendoWare) even when they use the same command lists. However, if command lists are already allocated outside the GD library, do not allocate other command lists until you have initialized the GD library.

The GD library does not have any functions for allocating display buffers. Use nngx() functions to allocate display buffers.

2.2. Allocating the Framebuffer

Allocate a color buffer and depth (stencil) buffer with the GD library.

Although they have different descriptor class settings, both buffers require the creation of a Texture2DResource object. To make it more efficient to clear and render the buffers, we recommend that you allocate the color and depth (stencil) buffers separately in VRAM-A and VRAM-B, as shown in the following sample code.

Code 2-2. Creating the Texture2DResource Objects Used by the Framebuffer
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");} 

Using the Texture2DResource objects that were created, create a RenderTarget object for the color buffer and a DepthStencilTarget object for the depth (stencil) buffer.

Code 2-3. Creating the RenderTarget and DepthStencilTarget Objects
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");} 

Configure the pipeline in the output stage, calling the SetRenderTarget() function for the RenderTarget object and the SetDepthStencilTarget() function for the DepthStencilTarget object.

Code 2-4. Configuring the Pipeline's Framebuffer
//Set the color/depthstencil targets.
nn::gd::OutputStage::SetRenderTarget(s_RenderTarget);
nn::gd::OutputStage::SetDepthStencilTarget(s_DepthStencilTarget); 

2.3. Loading a Shader Program

You must perform the following procedures before applying a shader program to the pipeline so that it can be used for rendering.

  • Load a shader binary.
  • Create a ShaderBinary object.
  • Create a Shader object.
  • Create a ShaderPipeline object.
  • Apply settings to the pipeline.

The following sample code applies the vertex shader included in a shader binary (with an rSize-byte file already loaded in rBuf) to the pipeline.

Code 2-5. Loading a Shader Program
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); 

The register values used by the shader program are set through UniformLocation objects that are obtained by specifying a ShaderPipeline object and data name as arguments to the GetShaderUniformLocation() function in the shader stage.

Code 2-6. Setting Register Values Using Uniform Settings
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. Preparing Vertex Data

The GD library requires the use of vertex buffers to render primitives.

To use a vertex buffer, you must do the following.

  • Create an InputLayout object.
  • Create a VertexBufferResource object.
  • Apply the settings (the input layout and vertex buffer) to the pipeline.

The following sample code uses vertex coordinates and vertex color as interleaved vertex data.

Code 2-7. Preparing Vertex Data
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. Configuring the Viewport

Use the SetViewport() function in the rasterizer stage to configure the viewport.

The following sample code configures the viewport using information about the render target obtained by calling the GetRenderTargetProperties() function in the output stage.

Code 2-8. Configuring the Viewport
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. Rendering

You call different rendering functions depending on whether you use vertex indices. Call the nn::gd::System::DrawIndexed() function when you use vertex indices and the nn::gd::System::Draw() function when you do not. The library also provides nn::gd::System::DrawImmediate() and nn::gd::System::DrawImmediateIndexed() for rendering without using a vertex buffer resource. Although it is more flexible to handle vertex data if you render without using a vertex buffer resource, rendering is performed more slowly.

Use the nn::gd::Memory::ClearTargets() function to clear the framebuffer.

The following sample code clears the framebuffer and then uses vertex indices for rendering.

Code 2-9. Rendering
//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. Displaying the Rendered Results

Use the nn::gd::Memory::CopyTexture2DResourceBlockToLinear() function to copy the content of the color buffer to the display buffer and then display the rendered results on the LCDs. This function has nearly the same functionality as the nngxTransferRenderImage() function, but the application must use a function such as nngxGetDisplaybufferParameteri to get the copy destination address.

Code 2-10. Displaying the Rendered Results
//Transfer the framebuffer to the 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. Finalizing

The nn::gd::System::Finalize() function shuts down the GD library. You can no longer call GD functions after this function has completed.

Finalization automatically deallocates memory, including resources managed by the library and those allocated by the application. However, when possible, have applications explicitly deallocate objects that they create (such as state and resource objects).


CONFIDENTIAL