3. LCD

On the 3DS system, the GX (graphics) library is closely involved with the images displayed on the LCD screens. Figure 3-1 shows the workflow starting from rendering and continuing until display on the LCD.

Figure 3-1. Workflow From Rendering to Display on the LCD.

Renderbuffer(Block format) Displaybuffer(Linear format) LCD Rendering Copy and Format conversion GPU Antialiasing Y-Flip Buffer swap

The process is roughly split into the following steps.

  1. Render.
  2. Copy data from the render buffer to the display buffer and convert its format.
  3. Swap buffers to update the regions displayed on the LCDs.

The rendering in step 1 is described in the documentation, starting from 4. Command Lists. This chapter describes the specifications for the output destination, the LCD, and the initialization needed when using the GX library. It also describes the allocation and transfer of buffers, synchronizing screen updates, and finalization, in that order.

Note:

For more information about stereoscopic display, see the 3DS Programming Manual: Advanced Graphics.

3.1. LCD Resolution, Placement, and Orientation

The LCDs on the 3DS system differ from the LCDs on the Nintendo DS (NITRO) and Nintendo DSi (TWL) systems, both in terms of resolution and arrangement. Table 3-1 shows the differences in resolution and Figure 3-2 shows the differences in placement and orientation.

Table 3-1. LCD Resolution

LCD

NITRO/TWL

3DS

Upper screen (width × height)

256 × 192

240 × 400

Lower screen (width × height)

256 × 192

240 × 320

Figure 3-2. LCD Placement and Orientation

Upper LCD Lower LCD 256 192 NITRO/TWL 320 400 3DS

The standard LCD arrangement on the NITRO and TWL systems places the long edges of the LCDs (the screen width) horizontally, with the lower screen in front. The standard LCD arrangement on the 3DS system places the short edges of the LCDs horizontally, with the lower screen to the left of the upper screen (as when the front of the controller is rotated 90 degrees to the right).

3.2. Required Initialization Process

A framebuffer architecture has been adopted for 3DS. Because the images displayed on the LCDs are based on the framebuffer content, the GX library must be initialized first.

3.2.1. Initializing the GX Library

Call the nngxInitialize() function to initialize the GX library.

Code 3-1. Initialization Function for the GX Library
GLboolean nngxInitialize(
        GLvoid* (*allocator)(GLenum, GLenum, GLuint, GLsizei),
        void (deallocator)(GLenum, GLenum, GLuint, GLvoid));
GLboolean nngxGetIsInitialized(); 

An allocator and a deallocator are specified to the nngxInitialize() function, using the allocator and deallocator parameters, respectively. The allocator is called when a display buffer or other memory region is allocated, and the deallocator is called when the same memory region is freed. For more information about the allocator and deallocator, see 3.2.1.1. Allocator and 3.2.1.2. Deallocator.

A value of GL_TRUE is returned when initialization succeeds, and GL_FALSE is returned when initialization fails. A value of GL_FALSE is also returned if this function is called after the library has already been initialized but before the nngxFinalize() function has been called to shut down the library. Behavior is not guaranteed if any other gl or nngx() functions are called before using this function to initialize the library. The default render buffer, among others, is not allocated during initialization. These buffers must be allocated by the application after initialization.

The nngxGetIsInitialized() function returns GL_TRUE if the nngxInitialize() function has initialized the GX library.

3.2.1.1. Allocator

The first allocator argument indicates the memory from which to allocate a region. The following values can be passed.

  • NN_GX_MEM_FCRAM
    Allocate from main (device) memory.
  • NN_GX_MEM_VRAMA
    Allocate from VRAM-A.
  • NN_GX_MEM_VRAMB
    Allocate from VRAM-B.

Given a value of NN_GX_MEM_FCRAM, the allocator allocates from main memory, but the region to allocate must also be located in device memory. Device memory is a memory region that guarantees address consistency when it is accessed by peripheral devices. Applications are responsible for memory management. You can use the nn::os::GetDeviceMemoryAddress and nn::os::GetDeviceMemorySize() functions to get the starting address and size of the memory region, allocated as device memory.

Note:

For more information about device memory, see the 3DS Programming Manual: System.

If a value of NN_GX_MEM_VRAMA or NN_GX_MEM_VRAMB is specified for the allocator, use the nn::gx::GetVramStartAddr, nn::gx::GetVramEndAddr, and nn::gx::GetVramSize() functions to get the starting address, ending address, and size of VRAM, respectively. As with main memory, the application is responsible for memory management.

The second allocator argument is passed a value indicating the usage (buffer type) of the memory to allocate. Because addresses are aligned differently for each type, implement your application's allocator to comply with the following rules.

Table 3-2. Differences in Alignment Between Buffer Types

Argument Value

Buffer Type

Alignment

NN_GX_MEM_TEXTURE

Texture
(2D, environment map)

128 bytes for all formats.

NN_GX_MEM_VERTEXBUFFER

Vertex buffers.

Depends on the vertex attribute.

4 bytes (GLfloat type)
2 bytes (GLshort type, GLushort type)
1 byte (GLbyte type, GLubyte type)

NN_GX_MEM_RENDERBUFFER

Color buffer.

64 bytes

Depth buffer
(stencil buffer)

32 bytes (16-bit depth)
96 bytes (24-bit depth)
64 bytes (24-bit depth + 8-bit stencil)

NN_GX_MEM_DISPLAYBUFFER

Display buffer

16 bytes

NN_GX_MEM_COMMANDBUFFER

3D command buffer

16 bytes

NN_GX_MEM_SYSTEM

System buffer

4 bytes (when the memory size to allocate is a multiple of 4).
2 bytes (when the memory size to allocate is a multiple of 2, but not a multiple of 4).
1 byte (when none of the above).

In addition to these rules, your implementation must comply with the following hardware specifications.

  • All six faces of a cube map texture must fit within the same 32 MB boundaries.
  • All six faces of a cube map texture must have addresses that share the same value for the most-significant 7 bits.
  • The GL_TEXTURE_CUBE_MAP_POSITIVE_X face of a cube map texture must have a smaller address, or the same address, as every other face.
  • Data must not be placed so that it spans VRAM-A and VRAM-B.

 

Warning:

Do not allocate a display buffer in the last 1.5 MB of VRAM-A or VRAM-B.

If texture addresses are not correctly aligned, the GPU may hang, rendered results may be corrupted, or other problems may arise.

If NN_GX_MEM_VERTEXBUFFER, NN_GX_MEM_RENDERBUFFER, NN_GX_MEM_DISPLAYBUFFER, or NN_GX_MEM_COMMANDBUFFER was specified for the second argument, the third allocator argument is passed the name (ID) of that object.

The fourth allocator argument is passed the size of the region to allocate.

The application must account for address alignment and allocate the specified memory region based on these arguments, and then return the region's starting address. The application must return a value of 0 if it fails to allocate the region.

3.2.1.2. Deallocator

The first, second, and third deallocator arguments are passed the same values that were used when the memory region was allocated. The fourth argument is passed the starting address of the memory region. The application must use these arguments to release the memory region allocated by the allocator.

3.2.1.3. Getting an Allocator

You can get the allocator that was configured when the GX library was initialized by calling the nngxGetAllocator() function.

Code 3-2. Getting an Allocator
void nngxGetAllocator (
                GLvoid* (**allocator)(GLenum, GLenum, GLuint, GLsizei),
                void (*deallocator)(GLenum, GLenum, GLuint, GLvoid));

The allocator and deallocator parameters specify pointers that will be assigned pointers to the allocator and deallocator. No allocator or deallocator is obtained when specifying NULL for each of the arguments.

3.2.1.4. Getting an Initialization Register Setting Command

You can get an executable Initialization Register Setting Command when calling the nngxGetInitializationCommand and nngxInitialize() functions.

Note:

This function was added as a measure related to rendering that takes place when returning to an application from the HOME Menu when generating a setting command directly in the register without using the graphics library supported by SDK. Consequently, it is not normally necessary to use it.

Code 3-3. Getting an Initialization Register Setting Command
GLsizei nngxGetInitializationCommand(GLsizei datasize, GLvoid* data); 

For the data parameter, you can specify a pointer to the command that receives the register setting command. For the datasize parameter, you can specify the buffer byte size indicated by the data parameter.

This function returns the size in bytes of the Initialization Register Setting Command. Specifying 0 (NULL) for data gets the register setting command, and returns the buffer size to allocate. After you specify 0 for data, prepare that size of the buffer and get the command by making a call-out again.

If you are calling the nngxInitialize() function before initialization, 0 is returned without getting the command.

Table 3-3. Error Generated by the nngxGetInitializationCommand Function

Error Code

Cause

GL_ERROR_80B4_DMP

The value specified for datasize is smaller than the obtained register command.

3.2.2. Command-List Object Creation

You must create command list objects after initializing the GX library. Command list objects are introduced independently by the Nintendo 3DS, and are handled as execution units for 3D graphics processing. This section only explains how to create them. For more information about command lists and command list objects, see 4. Command Lists.

First use the nngxGenCmdlists() function to create one or more command list objects, individually bind them to the GPU with the nngxBindCmdlist() function, and then allocate individual memory regions with the nngxCmdlistStorage() function.

Command list objects comprise a 3D command buffer and command requests. Set bufsize and requestcount for the nngxCmdlistStorage() function to the size of the 3D command buffer and the number of command requests that can be queued. When you create multiple command list objects, you must call the nngxBindCmdlist() and nngxCmdlistStorage() function on each of them.

The following code sample creates a single command list object that has a 3D command buffer size of 256 KB and can queue 128 command requests.

Code 3-4. Creating a Command List Object
GLuint commandList;

nngxGenCmdlists(1, &commandList);
nngxBindCmdlist(commandList);
nngxCmdlistStorage(256 * 1024, 128); 

3.3. Allocating Buffers

Two of the buffers used graphics processing are involved in LCD output: the framebuffer, which is the GPU render target; and the display buffer, which is used to copy rendering results and display them on the LCDs. Processing to the framebuffer and the display buffer occur using the framebuffer object and the display buffer object, respectively. For the configuration, see Figure 3-3.

Figure 3-3. Configuration of the Framebuffer and the Display Buffer

Framebuffer, Render Buffer, Display Buffer

3.3.1. Allocating Render Buffers

First, you must use the glGenFramebuffers() function to create a framebuffer object to specify as the render target. You can then bind the various render buffers (color, depth, or stencil) to the framebuffer object to specify those render buffers as the render target. The 3DS system has two screens (an upper and a lower screen), but if they have the same format and do not need to be rendered in parallel, we recommend that they share an object to conserve memory resources. In this case, specify the dimensions of the upper screen as the width and height of the buffer.

Code 3-5. Creating a Framebuffer Object
GLuint frameBufferObject;
glGenFramebuffers(1, &frameBufferObject); 

Next, use the glGenRenderbuffers() function to create the render buffer objects. If the render target includes a depth buffer, stencil buffer, or both, in addition to a color buffer, you need two render buffer objects for the framebuffer object.

Code 3-6. Creating Render Buffer Objects
GLuint renderBuffer[2];
glGenRenderbuffers(2, renderBuffer); 

Use the glBindRenderbuffer() function to specify the render buffer object to bind to the framebuffer object, and then use the glRenderbufferStorage() function to allocate a render buffer.

Code 3-7. Definition of the glRenderbufferStorage Function
void glRenderbufferStorage(GLenum target, GLenum internalformat, 
                           GLsizei width, GLsizei height); 

Set width and height to the width and height of the render buffer. Neither the width nor the height can be greater than 1024 pixels.

Warning:

Block-shaped noise may be rendered on some pixels if the render buffer has 262,128 or more pixels (the product of its width and height). For more information, see 15.8. Block-Shaped Noise Is Rendered on Certain Pixels.

By setting target to the bitwise OR of GL_RENDERBUFFER and the following bitmasks, you can specify the memory from which to allocate the buffer. If no bitmask is specified, the buffer is allocated as if NN_GX_MEM_VRAMA was specified.

Table 3-4. Specifiable Bitmasks for the target Parameter of glRenderbufferStorage

Bitmask

Buffer Is Allocated In

NN_GX_MEM_VRAMA

VRAM-A

NN_GX_MEM_VRAMB

VRAM-B

Specify the buffer type (format) in the internalformat parameter. You can choose from the following formats on the 3DS system.

Table 3-5. Specifiable Formats for the internalformat Parameter of glRenderbufferStorage

Format

Bits

Description of Format

GL_DEPTH_COMPONENT16

16

16-bit depth.

GL_DEPTH_COMPONENT24_OES

24

24-bit depth.

GL_RGBA4

16

The R, G, B, and alpha components are 4 bits each.

GL_RGB5_A1

16

The R, G, and B components are 5 bits each, and the alpha component is 1 bit.

GL_RGB565

16

5-bit RB components and 6-bit G component. No alpha component.

GL_RGBA8_OES

32

The R, G, B, and alpha components are 8 bits each.

GL_DEPTH24_STENCIL8_EXT

32

24-bit depth and 8-bit stencil value.

GL_GAS_DMP

32

Density values used during gas rendering (the rendered results cannot be copied to the display buffer).

To specify an allocated render buffer as the render target, use the glFramebufferRenderbuffer() function to bind it to the framebuffer object that was specified by the glBindFramebuffer() function.

Code 3-8. Definitions of glBindFramebuffer and glFramebufferRenderbuffer Functions
void glBindFramebuffer(GLenum target, GLuint framebuffer);
void glFramebufferRenderbuffer(GLenum target, GLenum attachment, 
        GLenum renderbuffertarget, GLuint renderbuffer); 

Set target to GL_FRAMEBUFFER for both functions.

Set framebuffer to the framebuffer object and renderbuffer to the render buffer object. Set renderbuffertarget to GL_RENDERBUFFER.

The value to specify for attachment depends on the render buffer format.

Table 3-6. Render Buffer Formats and the attachment Values to Use With Them

Format

Value of attachment

GL_DEPTH_COMPONENT16

GL_DEPTH_ATTACHMENT

GL_DEPTH_COMPONENT24_OES

GL_RGBA4

GL_COLOR_ATTACHMENT0

GL_RGB5_A1

GL_RGB565

GL_RGBA8_OES

GL_GAS_DMP

GL_DEPTH24_STENCIL8_EXT

GL_DEPTH_STENCIL_ATTACHMENT

You can call the glCheckFramebufferStatus() function to get the state of the render buffer object that is bound to the framebuffer object. Set the function’s target parameter to GL_FRAMEBUFFER. A GL_INVALID_ENUM error is generated if you specify any other value.

If GL_FRAMEBUFFER_COMPLETE is returned, a proper render buffer object has been bound.

If GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT is returned, neither the color buffer nor the depth (stencil) buffer has been attached.

If GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT is returned, either memory has not been allocated for the buffer that is bound, or the same render buffer object has been bound to both the color buffer and the depth buffer.

If GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS is returned, buffers with different sizes have been bound.

The following sample code allocates render buffers. Note that settings differ between the color and depth (stencil) buffers. The render buffer is shared by the upper and lower screens, so its width and height are specified using the dimensions of the upper screen.

Code 3-9. Allocating Render Buffers (Color, Depth, Stencil)
// FrameBuffer
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject);
// Color
glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer[0]);
glRenderbufferStorage(GL_RENDERBUFFER | NN_GX_MEM_VRAMA, GL_RGBA8_OES, 
    nn::gx::DISPLAY0_WIDTH, nn::gx::DISPLAY0_HEIGHT);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 
    GL_RENDERBUFFER, renderBuffer[0]);
// Depth / Stencil
glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer[1]);
glRenderbufferStorage(GL_RENDERBUFFER | NN_GX_MEM_VRAMB, 
    GL_DEPTH24_STENCIL8_EXT, nn::gx::DISPLAY0_WIDTH, nn::gx::DISPLAY0_HEIGHT);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 
    GL_RENDERBUFFER, renderBuffer[1]); 

3.3.2. Allocating Display Buffers

Before you allocate a display buffer, you must call the nngxActiveDisplay() function to specify whether to use it for the upper or lower screen.

Code 3-10. Definition of the nngxActiveDisplay Function
void nngxActiveDisplay(GLenum display); 

Specify either the upper or lower screen with the value you pass to display. A GL_ERROR_801F_DMP error occurs when display is set to a value other than the ones listed in Table 3-7.

Table 3-7. Values to Set for display

Value of display

Specified Screen

NN_GX_DISPLAY0

Upper screen (during stereoscopic display, this is the image for the left eye).

NN_GX_DISPLAY0_EXT

Upper screen (specifiable only during stereoscopic display, when this is the image for the right eye).

NN_GX_DISPLAY1

Lower screen.

Note:

For more information about stereoscopic display, see the 3DS Programming Manual: Advanced Graphics.

Next, use the nngxGenDisplaybuffers() function to create the display buffer objects.

Code 3-11. Definition of the nngxGenDisplaybuffers Function
void nngxGenDisplaybuffers(GLsizei n, GLuint* buffers); 

Set n to the number of display buffer objects to be created. Set buffers to point to an array in which to store the display buffer objects. If you are using more than one display buffer for a single screen through multi-buffering, create only as many objects as you need.

Table 3-8. Errors Generated by the nngxGetInitializationCommand Function

Error Code

Cause

GL_ERROR_801C_DMP

A negative value was specified for n.

GL_ERROR_801D_DMP

The internal buffer failed to be allocated.

Call the nngxBindDisplaybuffer() function on the generated display buffer object to set it as the target display buffer.

Code 3-12. Definition of the nngxGenDisplaybuffers Function
void nngxBindDisplaybuffer(GLuint buffer); 

An object is created as long as an unused object name is specified for buffer. A GL_ERROR_8020_DMP error occurs when the internal buffer failed to be allocated.

Use the nngxDisplaybufferStorage() function to allocate a display buffer.

Code 3-13. Definition of the nngxDisplaybufferStorage Function
void nngxDisplaybufferStorage(GLenum format, GLsizei width, GLsizei height, 
                              GLenum area); 

For format specify one of the following buffer formats. You cannot specify a format that uses more bits per pixel than the format set when the color buffer was allocated.

Table 3-9. Specifiable Formats for format of nngxDisplaybufferStorage

Format

Bits

Description of Format

GL_RGBA4

16

The R, G, B, and alpha components are 4 bits each.

GL_RGB5_A1

16

The R, G, and B components are 5 bits each, and the alpha component is 1 bit.

GL_RGB565

16

5-bit RB components and 6-bit G component. No alpha component.

GL_RGB8_OES

24

8-bit RGB components. No alpha component.

For width and height, specify the display buffer size. Both dimensions must be positive numbers that are multiples of the block size. The block size is either 8 or 32, depending on the render buffer's current block mode. For information about block mode settings, see the 3DS Programming Manual: Advanced Graphics.

For area, specify the memory in which to allocate the buffer.

Table 3-10. Specifiable Flags for area of nngxDisplaybufferStorage Function

Flag

Buffer Is Allocated In

NN_GX_MEM_FCRAM

Main (device) memory.

NN_GX_MEM_VRAMA

VRAM-A

NN_GX_MEM_VRAMB

VRAM-B

To capture and save a screen image, the display buffer must be allocated in main memory where it can be accessed by the CPU.

If a display buffer object already has a display buffer allocated and you allocate a new display buffer for it, the old memory region is freed and a new memory region is allocated.

Table 3-11. Errors Generated by the nngxGetInitializationCommand Function

Error Code

Cause

GL_ERROR_8021_DMP

The target is a display buffer whose object name is 0.

GL_ERROR_8022_DMP

An invalid value is specified for width or height.

GL_ERROR_8023_DMP

An invalid value is specified for format.

GL_ERROR_8024_DMP

An invalid value is specified for area.

GL_ERROR_8025_DMP

The memory region failed to be allocated.

You can allocate multiple display buffers. The following sample code uses double-buffering.

Code 3-14. Allocating Display Buffers
GLuint display0Buffers[2];
GLuint display1Buffers[2];
// Displaybuffer for UpperLCD
nngxActiveDisplay(NN_GX_DISPLAY0);
nngxGenDisplaybuffers(2, display0Buffers);
nngxBindDisplaybuffer(display0Buffers[0]);
nngxDisplaybufferStorage(GL_RGB8_OES, nn::gx::DISPLAY0_WIDTH, 
        nn::gx::DISPLAY0_HEIGHT, NN_GX_MEM_FCRAM);
nngxBindDisplaybuffer(display0Buffers[1]);
nngxDisplaybufferStorage(GL_RGB8_OES, nn::gx::DISPLAY0_WIDTH, 
        nn::gx::DISPLAY0_HEIGHT, NN_GX_MEM_FCRAM);
nngxDisplayEnv(0, 0);
// Displaybuffer for LowerLCD
nngxActiveDisplay(NN_GX_DISPLAY1);
nngxGenDisplaybuffers(2, display1Buffers);
nngxBindDisplaybuffer(display1Buffers[0]);
nngxDisplaybufferStorage(GL_RGB8_OES, nn::gx::DISPLAY1_WIDTH, 
        nn::gx::DISPLAY1_HEIGHT, NN_GX_MEM_FCRAM);
nngxBindDisplaybuffer(display1Buffers[1]);
nngxDisplaybufferStorage(GL_RGB8_OES, nn::gx::DISPLAY1_WIDTH, 
        nn::gx::DISPLAY1_HEIGHT, NN_GX_MEM_FCRAM);
nngxDisplayEnv(0, 0); 

3.3.2.1. Getting Parameters

You can use the nngxGetDisplaybufferParameteri() function to get information about the target display buffer.

Code 3-15. Function for Obtaining Display Buffer Parameters
void nngxGetDisplaybufferParameteri(GLenum pname, GLint* param); 

You can set pname to one of the following values. Specifying any other value results in a GL_ERROR_8033_DMP error.

Table 3-12. Display Buffer Parameters

pname

Parameter Information to Get

NN_GX_DISPLAYBUFFER_ADDRESS

Starting address of the display buffer.

NN_GX_DISPLAYBUFFER_FORMAT

The display buffer format.

NN_GX_DISPLAYBUFFER_WIDTH

The display buffer width.

NN_GX_DISPLAYBUFFER_HEIGHT

The display buffer height.

3.4. Copying From the Color Buffer to the Display Buffer

When rendering is done, the content of the color buffer is in block format. This cannot be output to the LCDs, which can only display data in a linear format. When rendering has finished, the content of the color buffer is in block format. This data cannot be output to the LCDs, which can only display data in a linear format. You must call the nngxTransferRenderImage() function to convert the data into a format that can be output to the LCDs. This function also copies the data to the display buffer from the color buffer that is bound to the framebuffer object specified by glBindFramebuffer.

Code 3-16. Function for Copying Data From the Color Buffer to the Display Buffer
void nngxTransferRenderImage(GLuint buffer, GLenum mode, GLboolean yflip, 
                             GLint colorx, GLint colory); 

The buffer parameter specifies the display buffer object into which to copy the data. If the 3D command buffer has accumulated unsplit commands, the function adds a split command and a transfer command request to the command requests. There is no guarantee that any added transfer commands have completed running after this function has executed. Wait until the command list has finished executing before performing other tasks such as deleting color buffers or changing buffer contents.

The mode parameter specifies the degree of anti-aliasing to apply when the rendered results are copied to the display buffer.

Table 3-13. Anti-Aliasing Specifications

mode

Anti-Aliasing Specification

Width

Height

NN_GX_ANTIALIASE_NOT_USED

Off (none).

Equal

Equal

NN_GX_ANTIALIASE_2x1

2×1 anti-aliasing.

2 times

Equal

NN_GX_ANTIALIASE_2x2

2×2 anti-aliasing.

2 times

2 times

The value of yflip specifies whether to apply a y-flip (a vertical flip) when the rendered results are copied to the display buffer. It is applied if the argument is set to GL_TRUE. Any value other than 0 is treated as GL_TRUE.

The values of colorx and colory specify the offsets to use when copying the data from the color buffer. (The function assumes that the origin is at the lower-left corner and that the positive axes are pointing up and right.) You must specify offsets that are positive multiples of the block size, which is 8 in block-8 mode and 32 in block-32 mode. (For more information about block mode settings, see the 3DS Programming Manual: Advanced Graphics.)

Starting from the offset position in the color buffer, this function copies a region with the same width and height as the display buffer to the display buffer. Subtract the offset values from the color buffer's width and height to find the dimensions of the region that can be copied from the color buffer.

The height and width of the region to copy, as measured in pixels, must be at least as big as the minimum allowed. The minimum height and width when copying from a color buffer is 128. The minimum height and width when copying to a display buffer depends on the anti-alias setting. If anti-aliasing is disabled, the minimum for both height and width is 128. If 2x1 anti-aliasing is enabled, the height minimum is 128 and the width minimum is 64. If 2x2 anti-aliasing is enabled, the minimum for both height and width is 64.

Table 3-14. Errors Generated by the nngxTransferRenderImage Function

Error Code

Cause

GL_ERROR_8027_DMP

Called when the bound command list’s object name is 0.

GL_ERROR_8028_DMP

The command request has already reached the maximum number of accumulated command requests allowable.

GL_ERROR_8029_DMP

A valid display buffer has not been bound.

GL_ERROR_802A_DMP

A valid color buffer has not been bound.

GL_ERROR_802B_DMP

An invalid value was specified for mode.

GL_ERROR_802C_DMP

The display buffer is larger than the region that can be copied. (When anti-aliasing, apply the width and height ratios from Table 3-13.)

GL_ERROR_802D_DMP

Invalid values were specified for colorx and colory.

GL_ERROR_802E_DMP

The display buffer uses more bits per pixel than the color buffer.

GL_ERROR_802F_DMP

The 3D command buffer is full because of commands added by this function.

GL_ERROR_8059_DMP

The width and height of the color buffer and display buffer are not multiples of 32 in block-32 mode.

GL_ERROR_805A_DMP

The width of the color buffer and display buffer are not multiples of 16 when the display buffer’s pixel size is 24 bits and the block mode is block 8.

GL_ERROR_80B5_DMP The specified height or width for copying from a color buffer was smaller than the minimum.

GL_ERROR_80B6_DMP 

The specified height or width for copying to a display buffer was smaller than the minimum.

 

3.5. Updating the LCD Render Regions by Swapping Buffers

After data has finished being copied to the display buffer, the buffer-swap function displays the rendered results to one or both LCDs.

Use the nngxActiveDisplay() function to specify a display and the nngxBindDisplaybuffer() function to bind the display buffer from which to display. You can then call the nngxDisplayEnv() function to specify the offsets to use when data is output from the display buffer to the LCDs. The function assumes that the origin is at the lower-left corner and the positive axes are pointing up and right, and that they are always positive values. Set (0, 0) for the offsets, if the display buffer is the same size as the LCD. Specifying a negative value for the offset results in a GL_ERROR_8026_DMP error.

Code 3-17. Functions for Specifying the Display and the Display Buffer
void nngxActiveDisplay(GLenum display);
void nngxBindDisplaybuffer(GLuint buffer);
void nngxDisplayEnv(GLint displayx, GLint displayy); 

Next, use the buffer-swap function to switch the buffer that is output to the LCD.

Code 3-18. Buffer-Swap Function
void nngxSwapBuffers(GLenum display); 

The nngxSwapBuffers() function swaps the buffers during the next VSync. This function can be called at any time, but if it is called more than once before a VSync, only the last call is valid.

Use display to specify which display is affected when the buffers are swapped. Specify NN_GX_DISPLAY0 to target only the upper screen, NN_GX_DISPLAY1 to target only the lower screen, orNN_GX_DISPLAY_BOTH to target both screens.

This function configures the GPU with the address of the display buffer to display, switching the image that is displayed on the LCDs. The display buffer address that is ultimately set in the GPU is calculated from the starting address of the buffer allocated by the nngxDisplaybufferStorage() function. The calculation takes into account several variables, including the display buffer resolution, the pixel size or number of bits per pixel (bpp), the LCD resolution, and the offsets configured by the nngxDisplayEnv() function.

The following equation calculates the address to configure.

BufferAddress + PixelSize × (DisplayBufferWidth × (DisplayBufferHeight - LcdHeight - DisplayY) + DisplayX)

Table 3-15. Errors Generated by the nngxTransferRenderImage Function

Error Code

Cause

GL_ERROR_8030_DMP

An invalid value was specified for display.

GL_ERROR_8031_DMP

A valid display buffer has not been bound.

GL_ERROR_8032_DMP

The display region that considered the offset will fall outside of the display buffer.

GL_ERROR_8053_DMP

The display buffer address set in the GPU is not 16-byte aligned.

GL_ERROR_9000_DMP

The display buffer bound to the upper screen for the right eye during stereoscopic display (NN_GX_DISPLAY0_EXT) is 0, or that region has not been allocated.

GL_ERROR_9001_DMP

The nngxDisplayEnv() function specifies a display region that is outside the display buffer.

GL_ERROR_9002_DMP

The display buffers bound to the two upper screens (NN_GX_DISPLAY0 and NN_GX_DISPLAY0_EXT) have different resolutions, formats, or memory regions.

In the system’s initial state, a black screen is forced to be displayed on the LCDs. If a valid display buffer for LCD output has been prepared in the buffer-swap function, call the nngxStartLcdDisplay() function at the same time as a VSync to start LCD output. You only need to call this function the first time.

Code 3-19. Function for Starting LCD Output
void nngxStartLcdDisplay( void ); 

3.5.1. Swapping Buffers for a Specified Address

By calling the nngxSwapBuffersByAddress() function, you can specify the address of a buffer to display (on the LCDs) without using a display buffer object.

Code 3-20. Swapping Buffers for a Specified Address
void nngxSwapBuffersByAddress(GLenum display,
                              const GLvoid* addr, const GLvoid* addrB,
                              GLsizei width, GLenum format); 

Buffers are swapped during the first VSync after this function (like the nngxSwapBuffers() function) is called. If this function is called more than once on the same display before a VSync occurs, only the last function call is valid.

Use display to specify which display is affected when the buffers are swapped. Specify NN_GX_DISPLAY0 for the upper screen or NN_GX_DISPLAY1 for the lower screen.

Use addr to specify the address of the buffer to display. If stereoscopic display is enabled and you have specified the upper screen with display, this argument provides the address of images to display for the left eye. You must specify an address that is 16-byte aligned.

Use addrB to specify the address of images to display for the right eye when stereoscopic display is enabled. This argument is only valid when you have specified the upper screen with display. It is ignored if stereoscopic display is disabled or if you have specified NN_GX_DISPLAY1 in display. You must specify an address that is 16-byte aligned.

The display position specified by the nngxDisplayEnv() function is ignored when the buffer specified by this function is displayed. Consequently, consider offsets and other factors in the addresses that you specify in addr and addrB. For more information about how addresses are calculated, see 3.5. Updating the LCD Render Regions by Swapping Buffers.

Use width to specify the width (in pixels) of the buffer to display. Note that width specifies the width of the buffer rather than the width of the LCD. Both the upper and lower screens have a width of 240 pixels, but if you want to partially display a buffer that is wider than that, specify the width of the entire buffer (in pixels), including the parts that will not be displayed. width must be a multiple of 8 and cannot be less than 240.

Table 3-16. Errors Generated by the nngxSwapBuffersbyAddress Function

Error Code

Cause

GL_ERROR_8087_DMP

An invalid value was specified for display.

GL_ERROR_8088_DMP

The address specified in addr is not 16-byte aligned.

GL_ERROR_8089_DMP

The address specified in addrB is not 16-byte aligned.

GL_ERROR_808A_DMP

An invalid value was specified for width.

GL_ERROR_808B_DMP

A value other than a specifiable format was specified for format.

3.5.2. Details of Swapping Buffers

The nngxSwapBuffers and nngxSwapBuffersByAddress() functions do not show the content of the display buffer after they swap buffers. These functions simply schedule the display buffer address (used when the screen is rendered after a VSync) to be changed during a VBlank.

The GPU displays the content of the display buffer to the LCD. Because the GPU reads a single line of data from the display buffer for each scan line, memory is accessed frequently outside of a VBlank. This causes tearing to occur if the content of the display buffer is overwritten outside of a VBlank.

The GPU accesses memory at the highest priority when it transfers (displays) the content of the display buffer to the LCD. By placing the display buffer in VRAM that can only be accessed by the GPU, you can avoid problems caused by memory access conflicts. If, however, you place the display buffer in main memory (device memory), there may be memory access conflicts with the CPU and other devices.

You can use the nngxSetMemAccessPrioMode() function to adjust the priority at which the GPU, CPU, and other devices access main memory.

Code 3-21. Function for Adjusting the Priority of Main Memory Access
void nngxSetMemAccessPrioMode(nngxMemAccessPrioMode mode); 
Table 3-17. Differences in Main Memory Access Priorities

mode

GPU

CPU

Other Devices

NN_GX_MEM_ACCESS_PRIO_MODE_0

Uniform priority.

NN_GX_MEM_ACCESS_PRIO_MODE_1

 

High priority.

 

NN_GX_MEM_ACCESS_PRIO_MODE_2

 

Extremely high priority.

 

NN_GX_MEM_ACCESS_PRIO_MODE_3

High priority.

High priority.

 

NN_GX_MEM_ACCESS_PRIO_MODE_4

High priority.

 

 

NN_GX_MEM_ACCESS_PRIO_MODE_1 is the default setting.

By raising the priority of memory accesses from the CPU, you can reduce the effect of the GPU and other devices on the time taken by processes that involve accessing main memory from the CPU.

Warning:

If you place the display buffer in main memory and specify NN_GX_MEM_ACCESS_PRIO_MODE_2, a large number of memory accesses from the CPU may cause noise resembling vertical lines to appear on the screen because of insufficient bandwidth for transferring images to be displayed on the LCD. To avoid this, either place the display buffer in VRAM or specify another mode.

Note:

If there is insufficient bandwidth for displaying images on the LCD, the nngxGetCmdlistParameteri() function returns a bit array with a value of 1 stored in both bit 17 and bit 18 when NN_GX_CMDLIST_HW_STATE is specified for param. For information about the nngxGetCmdlistParameteri() function, see 4.1.10. Getting Command List Parameters.

3.6. Screen Update Synchronization

You can use the following functions for processing that needs to line up with vertical LCD synchronization (VSync).

Code 3-22. VSync Functions
GLint nngxCheckVSync(GLenum display);
void nngxWaitVSync(GLenum display);
void nngxSetVSyncCallback(GLenum display, void (*func)(GLenum)); 

The nngxCheckVSync() function returns the value of the VSync counter updated within the library. By checking for changes in this value you can asynchronously check for VSync updates. Because the return value is an update counter used internally by the library, it wraps around to 0 after it exceeds the implementation's maximum value. (This may change in future implementations.)

The nngxWaitVSync() function waits for a VSync update on the specified LCD. Control does not return until the VSync update.

The nngxSetVSyncCallback() function registers the callback function that is invoked when VSync is updated. If this function is called with func set to 0 (NULL), it unregisters the callback function. The registered callback function is called from a different thread than the main thread, so mutual exclusion is required when referencing any data shared with the main thread. However, mutual exclusion is not required for data shared with any interrupt handlers registered using the nngxSetCmdlistCallback() function even if they are for the same graphics processing.

Warning:

You can call the nngx() functions from within a VSync callback, but note that command request completion interrupts are forced to wait until the callback function completes. Consequently, minimize any calls within callback functions to functions that issue command requests.

Setting display to NN_GX_DISPLAY0, NN_GX_DISPLAY1, or NN_GX_DISPLAY_BOTH in any of these functions causes it to operate on the upper screen, lower screen, or both, respectively. Passing any other value causes a GL_ERROR_8019_DMP error in calls to nngxCheckVSync, a GL_ERROR_801A_DMP error in calls to nngxWaitVSync, and a GL_ERROR_801B_DMP error in calls to nngxSetVSyncCallback.

The system tries to prevent extreme time delay between screens, but there are approximately 100 microseconds between VSync updates for the upper and lower screens. Avoid creating code that strongly depends on this delay because heavy processing in the VSync callback for the upper screen or the start of an extremely high-priority thread may force the VSync callback for the lower screen to wait.

The LCD screen VSync interval is 59.831 Hz for both the upper and lower screens. This does not change when stereoscopic display (the parallax barrier) is enabled or disabled.

3.7. Finalizing

Call the nngxFinalize() function when you stop using the GX library (such as, when the application shuts down). This releases all remaining unreleased objects.

Code 3-23. Definition of the nngxFinalize Function
void nngxFinalize(void); 

To destroy the framebuffer objects, render buffers, and display buffers allocated for displaying graphics on the LCDs, call glDeleteFramebuffers, glDeleteRenderbuffers, and nngxDeleteDisplaybuffers, respectively.

Code 3-24. Functions for Destroying Framebuffer Objects, Render Buffers, and Display Buffers
void glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers);
void glDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers);
void nngxDeleteDisplaybuffers(GLsizei n, GLuint* buffers); 

In each function, n specifies the number of object arrays passed to the second parameter. A GL_ERROR_801E_DMP error occurs when n is set to a negative value in the nngxDeleteDisplaybuffers()function.

If any of the specified framebuffer objects or render buffers is in use, it is unaffected. All other objects are deleted. Display buffers are swapped for display buffers with an object name of 0 and the objects in use are destroyed.

3.8. Specifying Display Portions

Figure 3-4 indicates which display portions are set to be specified for the transfer from the color buffer to the display buffer, and then to display on the LCD from the display buffer. The transfer from the color buffer to the display buffer assumes that anti-aliasing is disabled.

Figure 3-4. Specifying Display Portions

Table 3-18. Specifying Display Portions
Variable Name Description
cw, ch Width and height of the color buffer
Values specified by width and height in the glRenderbufferStorage() function. (See Code 3-7.)
cx, cy

Offset for when copying from the color buffer to the display buffer.
Values specified by colorx and colory in the nngxTransferRenderImage() function. (See Code 3-16.)

dw, dh Width and height of the display buffer
Values specified by width and height in the nngxDisplaybufferStorage() function. (See Code 3-13.)
dx, dy

Offset for when producing output from the display buffer to the LCD.
Values specified by displayx and sy in the nngxDisplayEnv() function. (See Code 3-17.)

lw, lh

The height and width of the output destination LCD. The height and width differ for the upper screen and lower screen.
See 3.1. LCD Resolution, Placement, and Orientation.


CONFIDENTIAL