1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     EarlyDepthTestSample.cpp
4 
5   Copyright (C)2009-2012 Nintendo Co., Ltd.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Rev: 47228 $
14  *---------------------------------------------------------------------------*/
15 
16 #include <nn/gx.h>
17 #include <nn/math.h>
18 #include <nn/fs.h>
19 #include <nn/os.h>
20 #include <nn/init.h>
21 #include <nn/applet.h>
22 #include <nn/fnd/fnd_ExpHeap.h>
23 
24 #include "Loader.h"
25 #include "Util.h"
26 
27 #include <string.h>
28 #include "Memory.h"
29 
30 #include "object.h"
31 
32 #include "MemoryManager.h"
33 
34 // Does not wait for VSync in order to make early depth test effects more easily perceptible.
35 //#define WAIT_VSYNC
36 #define USE_EARLY_DEPTH_TEST
37 // Displays the number of fragments input into the per-fragment operation module if enabled.
38 //#define GET_PROFILING_RESULT
39 
40 // Display alpha
41 static u32 s_Displaybuffer0[2];
42 static u32 s_Displaybuffer1[2];
43 static s32 s_CurrentDisplaybuffer0 = 0;
44 static s32 s_CurrentDisplaybuffer1 = 0;
45 
46 // Framebuffer and render buffer
47 static u32 s_Framebuffer = 0;
48 static u32 s_Renderbuffer[2];
49 
50 // Block 32 mode must be set when using an early depth test.
51 // A buffer having the same size as the screen cannot be used for this reason.
52 #ifdef USE_EARLY_DEPTH_TEST
53 static u32 s_Disp0Width  = 256;
54 static u32 s_Disp0Height = 416;
55 static u32 s_Disp1Width  = 256;
56 static u32 s_Disp1Height = 320;
57 #else
58 static u32 s_Disp0Width  = 240;
59 static u32 s_Disp0Height = 400;
60 static u32 s_Disp1Width  = 240;
61 static u32 s_Disp1Height = 320;
62 #endif // USE_EARLY_DEPTH_TEST
63 
64 // Command list
65 static u32 s_CommandList = 0;
66 
67 // Vertex buffer
68 static u32 s_CubeArrayBuffer;
69 static u32 s_CubeElementArrayBuffer;
70 
71 // Texture
72 static u32 s_Texture;
73 
74 // Shader
75 static u32 s_Program;
76 static u32 s_Shader;
77 
78 // Uniform location
79 static s32 s_UniformLocationProj;
80 static s32 s_UniformLocationView;
81 
82 // Matrix
83 static nn::math::MTX44  s_ProjMtx;
84 static nn::math::MTX34  s_ViewMtx;
85 
86 // FPS display
87 #define FPS_AVG_FRAME_COUNT 100
88 static u32 s_CurrentFpsCount = 0;
89 static f64 s_TmpFpsSum = 0.0;
90 
91 // ExpHeap for app.
92 nn::fnd::ExpHeap s_AppHeap;
93 uptr s_HeapForGx;
94 uptr s_HeapForMalloc;
95 const u32 s_GxHeapSize = 0x800000;
96 const u32 s_HeapSize   = 0x200000;
97 
98 #define PRINT(__msg) NN_LOG __msg
99 
DrawCube(void)100 static void DrawCube(void)
101 {
102     glBindBuffer(GL_ARRAY_BUFFER, s_CubeArrayBuffer);
103     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0) ;
104     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)sizeof(s_CubePos));
105     glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(s_CubePos) + sizeof(s_CubeNormal))) ;
106     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s_CubeElementArrayBuffer);
107     glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0);
108 }
109 
LoadObjects(void)110 static void LoadObjects(void)
111 {
112     // Cube
113     glGenBuffers(1, &s_CubeArrayBuffer);
114     glBindBuffer(GL_ARRAY_BUFFER, s_CubeArrayBuffer);
115     glBufferData(GL_ARRAY_BUFFER, sizeof(s_CubePos) + sizeof(s_CubeNormal) + sizeof(s_CubeTexCoord), 0, GL_STATIC_DRAW);
116     glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(s_CubePos), s_CubePos);
117     glBufferSubData(GL_ARRAY_BUFFER, sizeof(s_CubePos), sizeof(s_CubeNormal), s_CubeNormal);
118     glBufferSubData(GL_ARRAY_BUFFER, sizeof(s_CubePos) + sizeof(s_CubeNormal), sizeof(s_CubeTexCoord), s_CubeTexCoord);
119 
120     glGenBuffers(1, &s_CubeElementArrayBuffer);
121     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s_CubeElementArrayBuffer);
122     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(s_CubeIdx), s_CubeIdx, GL_STATIC_DRAW);
123 
124     glEnableVertexAttribArray(0);
125     glEnableVertexAttribArray(1);
126 
127     // Load textures
128     bool IsAlpha;
129     glGenTextures(1, &s_Texture);
130     glActiveTexture(GL_TEXTURE0);
131     glBindTexture(GL_TEXTURE_2D, s_Texture);
132     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0);
133     loadTexture( "rom:/resources/knight.tga", GL_TEXTURE_2D, 0, IsAlpha, true);
134 
135     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
136     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
137 }
138 
GetTimeMicroSeconds(void)139 s64 GetTimeMicroSeconds(void)
140 {
141     nn::os::Tick ticks = nn::os::Tick::GetSystemCurrent();
142     nn::fnd::TimeSpan TimeSpan = ticks;
143     return TimeSpan.GetMicroSeconds();
144 }
145 
DrawFrame(void)146 s32 DrawFrame(void)
147 {
148     static s32 f = 0;
149     s64 startTime = GetTimeMicroSeconds();
150 
151     // Bind the framebuffer
152     glBindFramebuffer(GL_FRAMEBUFFER, s_Framebuffer);
153 
154     // Viewport settings
155     glViewport(0, 0, nn::gx::DISPLAY0_WIDTH * 2, nn::gx::DISPLAY0_HEIGHT * 2);
156 
157     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
158 #ifdef USE_EARLY_DEPTH_TEST
159      glClear( GL_EARLY_DEPTH_BUFFER_BIT_DMP );
160 #endif // USE_EARLY_DEPTH_TEST
161 
162    // Model view matrix
163     nn::math::Vector3 camPos( 0.f, 0.f, 14.f );
164     nn::math::Vector3 camUp( 0.f, 1.f, 0.f );
165     nn::math::Vector3 target( 0.f, 0.f, 0.f );
166     nn::math::MTX34LookAt( &s_ViewMtx, &camPos, &camUp, &target );
167 
168     static f32 frontPos = 12.0f;
169     if( frontPos <= -60.0f )frontPos = 12.0f;
170 
171     // Object in screen foreground.
172     for(int i = 0; i < 16; i++)
173     {
174         // Model view matrix
175         nn::math::Matrix34 trans, vTmp;
176         nn::math::Vector3 transVec( 0.f, frontPos + ( i * 3.0f ), 7.2f );
177         nn::math::MTX34Translate( &trans, &transVec );
178         nn::math::MTX34Mult(&vTmp, &s_ViewMtx, &trans);
179 
180         nn::math::Matrix44 tmp(vTmp);
181         glUniformMatrix4fv(s_UniformLocationView, 1, GL_TRUE, static_cast<f32*>(tmp));
182         // Rendering a cube
183         DrawCube();
184     }
185 
186     frontPos -= 0.05f;
187 
188     // The fragment load has been increased to the extreme in order to clearly show the effect of the early depth test.
189     glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[1].enabled"), GL_TRUE);
190     glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[2].enabled"), GL_TRUE);
191     glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[3].enabled"), GL_TRUE);
192     glUniform1i(glGetUniformLocation(s_Program, "dmp_LightEnv.config"), GL_LIGHT_ENV_LAYER_CONFIG7_DMP);
193     glDisable(GL_CULL_FACE);
194 
195     static f32 backPos = -12.0f;
196     if( backPos >= -3.0f )backPos = -12.0f;
197 
198     // Object in screen background.
199     for(int i = 0; i < 8; i++)
200     {
201         // Model view matrix
202         nn::math::Matrix34 trans, vTmp;
203         nn::math::Vector3 transVec( 0.f, backPos + ( i * 3.0f ), 5.2f );
204         nn::math::MTX34Translate( &trans, &transVec );
205         nn::math::MTX34Mult(&vTmp, &s_ViewMtx, &trans);
206 
207         nn::math::Matrix44 tmp(vTmp);
208         glUniformMatrix4fv(s_UniformLocationView, 1, GL_TRUE, static_cast<f32*>(tmp));
209         // Rendering a cube
210         DrawCube();
211     }
212 
213     backPos += 0.05f;
214 
215     glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[1].enabled"), GL_FALSE);
216     glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[2].enabled"), GL_FALSE);
217     glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[3].enabled"), GL_FALSE);
218     glUniform1i(glGetUniformLocation(s_Program, "dmp_LightEnv.config"), GL_LIGHT_ENV_LAYER_CONFIG0_DMP);
219     glEnable(GL_CULL_FACE);
220 
221 
222     glFinish();
223 
224     nngxActiveDisplay(NN_GX_DISPLAY0);
225     nngxBindDisplaybuffer(s_Displaybuffer0[s_CurrentDisplaybuffer0]);
226     nngxTransferRenderImage(s_Displaybuffer0[s_CurrentDisplaybuffer0], NN_GX_ANTIALIASE_2x2, GL_FALSE, 0, 0);
227 
228     nngxActiveDisplay(NN_GX_DISPLAY1);
229     nngxBindDisplaybuffer(s_Displaybuffer1[s_CurrentDisplaybuffer1]);
230 
231     // Swap buffers
232     nngxSwapBuffers(NN_GX_DISPLAY_BOTH);
233 
234     s_CurrentDisplaybuffer0 = (s_CurrentDisplaybuffer0 == 0 ? 1 : 0);
235     s_CurrentDisplaybuffer1 = (s_CurrentDisplaybuffer1 == 0 ? 1 : 0);
236 
237     nngxWaitCmdlistDone();
238     nngxClearCmdlist();
239 
240 #ifdef WAIT_VSYNC
241     nngxWaitVSync(NN_GX_DISPLAY_BOTH);
242 #endif
243 
244 #ifdef GET_PROFILING_RESULT
245     static u32 resFragment = 0, oldResFragment = 0;
246     nngxGetProfilingResult( NN_GX_PROFILING_FRAGMENT, &resFragment );
247     NN_LOG("Fragment Count: %d pixels\n", resFragment - oldResFragment);
248     oldResFragment = resFragment;
249 #endif
250 
251     nngxRunCmdlist();
252 
253     if ( f == 0 )
254     {
255         nngxStartLcdDisplay();
256     }
257 
258     // FPS display
259     s_TmpFpsSum += GetTimeMicroSeconds() - startTime;
260     if (++s_CurrentFpsCount == FPS_AVG_FRAME_COUNT)
261     {
262         f64 avgtime = s_TmpFpsSum / FPS_AVG_FRAME_COUNT;
263         PRINT(("AVG Time: %0.5f    (fps: %0.3f)\n", avgtime, (1. / (avgtime/1000000.0))));
264         NN_UNUSED_VAR(avgtime);
265         s_CurrentFpsCount = 0;
266         s_TmpFpsSum = 0.0;
267     }
268 
269     f++;
270 
271     return !glGetError();
272 }
273 
274 
SetRenderState(void)275 static void SetRenderState(void)
276 {
277     f32 diffuse0[] = {0.5f,0.5f,0.5f,1.f} ;
278     f32 diffuse1[] = {0.15f,0.85f,0.15f,1.f} ;
279     f32 diffuse2[] = {0.15f,0.15f,0.85f,1.f} ;
280     f32 diffuse3[] = {0.85f,0.15f,0.15f,1.f} ;
281 
282     // Lighting
283     glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLighting.enabled"), GL_TRUE);
284     glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[0].enabled"), GL_TRUE);
285     glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[1].enabled"), GL_TRUE);
286     glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[2].enabled"), GL_TRUE);
287     glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[3].enabled"), GL_TRUE);
288 
289     glUniform1i(glGetUniformLocation(s_Program, "dmp_LightEnv.config"), GL_LIGHT_ENV_LAYER_CONFIG7_DMP);
290 
291     // Color
292     glUniform4fv(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[0].diffuse"), 1, diffuse0);
293     glUniform4fv(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[1].diffuse"), 1, diffuse1);
294     glUniform4fv(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[2].diffuse"), 1, diffuse2);
295     glUniform4fv(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[3].diffuse"), 1, diffuse3);
296 
297     // Light position
298     nn::math::Vector4 lightPos0(130.f, 10.f, 100.f, 1.f);
299     nn::math::Vector4 lightPos1(50.f, 90.f, 60.f,1.f);
300     nn::math::Vector4 lightPos2(-60.f, -120.f, 20.f,1.f);
301     nn::math::Vector4 lightPos3(0.f, 0.f, -80.f,1.f);
302 
303     glUniform4fv(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[0].position"), 1, static_cast<f32*>(lightPos0));
304     glUniform4fv(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[1].position"), 1, static_cast<f32*>(lightPos1));
305     glUniform4fv(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[2].position"), 1, static_cast<f32*>(lightPos2));
306     glUniform4fv(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[3].position"), 1, static_cast<f32*>(lightPos3));
307 
308     // Texture settings
309     glUniform1i(glGetUniformLocation(s_Program, "dmp_Texture[0].samplerType"), GL_TEXTURE_2D);
310 
311     glUniform1i(glGetUniformLocation(s_Program, "dmp_TexEnv[0].combineRgb"), GL_MODULATE);
312     glUniform1i(glGetUniformLocation(s_Program, "dmp_TexEnv[0].combineAlpha"), GL_REPLACE);
313     glUniform3i(glGetUniformLocation(s_Program, "dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_COLOR);
314     glUniform3i(glGetUniformLocation(s_Program, "dmp_TexEnv[0].operandAlpha"), GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA);
315     glUniform3i(glGetUniformLocation(s_Program, "dmp_TexEnv[0].srcRgb"), GL_TEXTURE0, GL_FRAGMENT_PRIMARY_COLOR_DMP, GL_CONSTANT);
316     glUniform3i(glGetUniformLocation(s_Program, "dmp_TexEnv[0].srcAlpha"), GL_CONSTANT, GL_CONSTANT, GL_CONSTANT);
317 
318     // The projection matrix
319     f32 aspect = static_cast<f32>(nn::gx::DISPLAY0_HEIGHT) / static_cast<f32>(nn::gx::DISPLAY0_WIDTH);
320     nn::math::MTX44Frustum(&s_ProjMtx, -0.04f, 0.04f, -0.04f * aspect, 0.04f * aspect, 0.22, 100.0f);
321     glUniformMatrix4fv(s_UniformLocationProj, 1, GL_TRUE, static_cast<f32*>(s_ProjMtx));
322 
323 }
InitShader(void)324 static void InitShader(void)
325 {
326     // Create a shader program
327     s_Program = glCreateProgram();
328     s_Shader = glCreateShader(GL_VERTEX_SHADER);
329 
330     // Load the shader.
331     nn::fs::FileReader file(L"rom:/shader.shbin");
332     size_t fileSize = file.GetSize();
333     void* buf = s_AppHeap.Allocate(fileSize);
334 
335     s32 read = file.Read(buf, fileSize);
336     glShaderBinary(1, &s_Shader, GL_PLATFORM_BINARY_DMP, buf, read);
337     file.Finalize();
338     s_AppHeap.Free(buf);
339 
340     // Attach a shader
341     glAttachShader(s_Program, s_Shader);
342     glAttachShader(s_Program, GL_DMP_FRAGMENT_SHADER_DMP);
343 
344     glBindAttribLocation(s_Program, 0, "aPosition");
345     glBindAttribLocation(s_Program, 1, "aNormal");
346     glBindAttribLocation(s_Program, 2, "aTexCoord");
347 
348     // Link the shader
349     glLinkProgram(s_Program);
350     glValidateProgram(s_Program);
351     glUseProgram(s_Program);
352 
353     // Get the uniform location
354     s_UniformLocationView = glGetUniformLocation(s_Program, "uModelView");
355     s_UniformLocationProj = glGetUniformLocation(s_Program, "uProjection");
356 }
357 
Initialize(void)358 static s32 Initialize(void)
359 {
360     // fs initialization
361     nn::fs::Initialize();
362 
363     const size_t ROMFS_BUFFER_SIZE = 1024 * 64;
364     static char buffer[ROMFS_BUFFER_SIZE];
365     NN_UTIL_PANIC_IF_FAILED(
366         nn::fs::MountRom(16, 16, buffer, ROMFS_BUFFER_SIZE));
367 
368     s_AppHeap.Initialize(nn::os::GetDeviceMemoryAddress(), nn::os::GetDeviceMemorySize() );
369     s_HeapForGx = reinterpret_cast<uptr>(s_AppHeap.Allocate(s_GxHeapSize));
370 
371     demo::memory_manager::InitializeMemoryManager(s_HeapForGx, s_GxHeapSize);
372     nngxInitialize(demo::memory_manager::GetAllocator, demo::memory_manager::GetDeallocator);
373 
374     s_HeapForMalloc = reinterpret_cast<uptr>(s_AppHeap.Allocate(s_HeapSize));
375     setMemoryHeap(s_HeapForMalloc, s_HeapSize);
376 
377     // Initialize the framebuffer object and render buffer
378     glGenFramebuffers(1, &s_Framebuffer);
379     glBindFramebuffer(GL_FRAMEBUFFER, s_Framebuffer);
380 
381     glGenRenderbuffers(2, s_Renderbuffer);
382     glBindRenderbuffer(GL_RENDERBUFFER, s_Renderbuffer[0]);
383     glRenderbufferStorage(GL_RENDERBUFFER | NN_GX_MEM_VRAMA, GL_RGBA8_OES, s_Disp0Width * 2, s_Disp0Height * 2);
384     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, s_Renderbuffer[0]);
385 
386     glBindRenderbuffer(GL_RENDERBUFFER, s_Renderbuffer[1]);
387     glRenderbufferStorage(GL_RENDERBUFFER | NN_GX_MEM_VRAMB, GL_DEPTH24_STENCIL8_EXT, s_Disp0Width * 2, s_Disp0Height * 2);
388     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, s_Renderbuffer[1]);
389 
390     // Initialize display buffer
391     nngxGenDisplaybuffers(2, s_Displaybuffer0);
392     nngxActiveDisplay(NN_GX_DISPLAY0);
393     nngxDisplayEnv(0, 0);
394     nngxBindDisplaybuffer(s_Displaybuffer0[0]);
395     nngxDisplaybufferStorage(GL_RGB8_OES, s_Disp0Width, s_Disp0Height, NN_GX_MEM_FCRAM);
396 
397     nngxBindDisplaybuffer(s_Displaybuffer0[1]);
398     nngxDisplaybufferStorage(GL_RGB8_OES, s_Disp0Width, s_Disp0Height, NN_GX_MEM_FCRAM);
399 
400     nngxGenDisplaybuffers(2, s_Displaybuffer1);
401     nngxActiveDisplay(NN_GX_DISPLAY1);
402     nngxDisplayEnv(0, 0);
403     nngxBindDisplaybuffer(s_Displaybuffer1[0]);
404     nngxDisplaybufferStorage(GL_RGB8_OES, s_Disp1Width, s_Disp1Height, NN_GX_MEM_FCRAM);
405 
406     nngxBindDisplaybuffer(s_Displaybuffer1[1]);
407     nngxDisplaybufferStorage(GL_RGB8_OES, s_Disp1Width, s_Disp1Height, NN_GX_MEM_FCRAM);
408 
409     // Initialize the command list object
410     nngxGenCmdlists(1, &s_CommandList);
411     nngxBindCmdlist(s_CommandList);
412     nngxCmdlistStorage(0x40000, 128);
413 
414     nngxRunCmdlist();
415 
416     glClearColor(0.31f, 0.40f, 0.5f, 1.0f);
417     glClearDepthf(1.f);
418 
419     // DepthTest
420     glEnable(GL_DEPTH_TEST);
421     glDepthFunc(GL_LESS);
422 
423     // Culling
424     glEnable(GL_CULL_FACE);
425     glFrontFace(GL_CCW);
426     glCullFace(GL_BACK);
427 
428 #ifdef USE_EARLY_DEPTH_TEST
429 
430     // Set an early depth test
431     glEnable(GL_EARLY_DEPTH_TEST_DMP);
432     glEarlyDepthFuncDMP(GL_LESS);
433     glClearEarlyDepthDMP(0xFFFFFF);
434 
435     // Change the block mode
436     glRenderBlockModeDMP(GL_RENDER_BLOCK32_MODE_DMP);
437 #endif // USE_EARLY_DEPTH_TEST
438 
439     // Initialize the shader
440     InitShader();
441 
442     // Set objects
443     LoadObjects();
444 
445     // Enable the vertex attribute array
446     glEnableVertexAttribArray(0);
447     glEnableVertexAttribArray(1);
448     glEnableVertexAttribArray(2);
449 
450     // Set render parameters
451     SetRenderState();
452 
453     return 0;
454 }
455 
Finalize()456 static void Finalize()
457 {
458     // Release the display buffer
459     nngxBindDisplaybuffer(0);
460     nngxDeleteDisplaybuffers(2, s_Displaybuffer0);
461     nngxDeleteDisplaybuffers(2, s_Displaybuffer1);
462     s_Displaybuffer0[0] = s_Displaybuffer0[1] = 0;
463     s_Displaybuffer1[0] = s_Displaybuffer1[1] = 0;
464 
465     // Delete the command list
466     nngxDeleteCmdlists(1, &s_CommandList);
467 
468     nngxFinalize();
469 }
470 
nnMain(void)471 void nnMain(void)
472 {
473     // Call only the nn::applet::Enable function to also allow execution from the HOME Menu
474     // HOME Menu transitions, POWER Button presses, and sleep are all unsupported
475     nn::applet::Enable();
476 
477     if (Initialize() >= 0)
478     {
479         while (1)
480         {
481             (void)DrawFrame();
482         }
483     }
484 
485     Finalize();
486 
487     /* shutdown_display */
488     s_AppHeap.Free(reinterpret_cast<void*>(s_HeapForGx));
489     s_AppHeap.Finalize();
490 }
491