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.
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.
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.
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.
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.
//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.
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.
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.
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.
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.
//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.
//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).