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