1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     LightingToonApple.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 /*
17  *------------------------------------------------------------
18  * Copyright(c) 2009-2010 by Digital Media Professionals Inc.
19  * All rights reserved.
20  *------------------------------------------------------------
21  * This source code is the confidential and proprietary
22  * of Digital Media Professionals Inc.
23  *------------------------------------------------------------
24  */
25 
26 /*
27  * Comments
28  * --------
29  *
30  * VN masking means that light reflection is set to zero when VN is smaller
31  * than some threshold value 'outline.' This method works poorly when an
32  * object has concave places (such as a center part of the inside of a palm).
33  */
34 
35 #include <nn/gx.h>
36 #include <nn/fs.h>
37 #include <nn/math.h>
38 #include <nn/init.h>
39 #include <nn/os.h>
40 #include <nn/applet.h>
41 
42 #include "demo.h"
43 
44 #include "Util.h"
45 #include "Loader.h"
46 
47 #include <string.h>
48 
49 #include "Memory.h"
50 
51 #define APP_NAME "LightingToonApple"
52 
53 /* program id */
54 GLuint s_ProgramID;
55 
56 /* shader id */
57 GLuint s_ShaderID;
58 
59 /* env texture id */
60 GLuint s_EnvTexName;
61 /* obj file loader class object */
62 dat_t sphere;
63 
64 /* texture collection id */
65 GLuint s_TexColl;
66 
67 /* ExpHeap for app. */
68 nn::fnd::ExpHeap s_AppHeap;
69 uptr s_HeapForGx;
70 uptr s_HeapForMalloc;
71 const u32 s_GxHeapSize = 0x400000;
72 const u32 s_HeapSize = 0x200000;
73 
74 demo::RenderSystem s_RenderSystem;
75 
76 /* if USE_LN is not defined, half-vector is used as the vector controlling both diffuse illumination and highlight position */
77 #define USE_LN
78 /* if WINDOW is defined, a window highlight is used */
79 #define WINDOW
80 
SetLutTable(void)81 static void SetLutTable(void)
82 {
83     GLuint luts[4];
84     glGenTextures(4, luts);
85 
86     const float outline = 0.15f;
87     const float highlight_eps = 0.01f;
88     float delta[] = {1.f, 0.7f, 0.5f, -1.f};
89 
90     GLfloat lut[512];
91     int j;
92     memset(lut, 0, sizeof(lut));
93     float previous = 0, LN;
94     int i = 0;
95 
96     /* f(D) = D is used as the initial shading function - see "Illumination Models in Maestro" for details
97     first specify the steps in the [0, 1) interval */
98     for (j = 127; j >= 0; j--)
99     {
100         LN = (float)j/127.9375f;
101         if (LN > delta[i])
102             lut[j] = previous;
103         else
104         {
105             lut[j] = LN;
106             previous = lut[j];
107             i++;
108         }
109     }
110     for (j = 0; j < 127; j++)
111         lut[j + 256] = lut[j+1] - lut[j];
112     lut[127 + 256] = 0.f;
113 
114     /* now specify the steps in the [-1, 0) interval */
115     for (j = 255; j  >= 128; j--)
116     {
117         LN = (float)(j - 256) /128.f;
118         if (LN > delta[i])
119             lut[j] = previous;
120         else
121         {
122             lut[j] = LN;
123             previous = lut[j];
124             i++;
125         }
126     }
127 
128     /* now calculate differences (some are 0, some are not) */
129     for (j = 128; j < 255; j++)
130             lut[j + 256] = lut[j+1] - lut[j];
131     lut[255 + 256] = lut[0] - lut[255];
132 
133     glBindTexture(GL_LUT_TEXTURE0_DMP, luts[0]);
134     glTexImage1D(GL_LUT_TEXTURE0_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, lut);
135 
136     for (j = 0; j < 256; j++)
137         if ((float)j/255.9375f <= 1.f - highlight_eps)
138             lut[j] = 0.f;
139         else
140             lut[j] = 1.f;
141     for (j = 0; j < 255; j++)
142         lut[j + 256] = lut[j+1] - lut[j];
143     lut[255 + 256] = 0.f;
144 
145     glBindTexture(GL_LUT_TEXTURE1_DMP, luts[1]);
146     glTexImage1D(GL_LUT_TEXTURE1_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, lut);
147 
148     for (j = 0; j < 256; j++)
149         if ((float)j/255.9375f < outline)
150             lut[j] = 0.f;
151         else
152             lut[j] = 1.f;
153 
154     for (j = 0; j < 255; j++)
155         lut[j + 256] = lut[j+1] - lut[j];
156     lut[255 + 256] = 1.f - lut[255];
157 
158     glBindTexture(GL_LUT_TEXTURE2_DMP, luts[2]);
159     glTexImage1D(GL_LUT_TEXTURE2_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, lut);
160 
161     for (j = 0; j < 256; j++)
162         lut[j] = 1.f;
163 
164     for (j = 0; j < 256; j++)
165         lut[j + 256] = 0.f;
166 
167     glBindTexture(GL_LUT_TEXTURE3_DMP, luts[3]);
168     glTexImage1D(GL_LUT_TEXTURE3_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, lut);
169 }
170 
171 /* set lighting render state */
SetRenderState(void)172 static void SetRenderState(void)
173 {
174     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLighting.enabled"), GL_TRUE);
175     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].enabled"), GL_TRUE);
176 
177     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputD0"), GL_TRUE);
178     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputD1"), GL_TRUE);
179     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputFR"), GL_TRUE);
180     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputRR"), GL_FALSE);
181 
182 #ifdef USE_LN
183     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputRR"), GL_LIGHT_ENV_LN_DMP);
184     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputD0"), GL_LIGHT_ENV_LN_DMP);
185 #else
186     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputRR"), GL_LIGHT_ENV_NH_DMP);
187     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputD0"), GL_LIGHT_ENV_NH_DMP);
188 #endif
189     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputD1"), GL_LIGHT_ENV_NV_DMP);
190     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputFR"), GL_LIGHT_ENV_NV_DMP);
191 
192     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].shadowed"), GL_FALSE);
193     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].twoSideDiffuse"), GL_FALSE);
194     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].geomFactor0"), GL_FALSE);
195     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].geomFactor1"), GL_FALSE);
196 
197     glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleD0"), 1.f);
198     glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleD1"), 1.f);
199     glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleSP"), 1.f);
200     glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleFR"), 1.f);
201     glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleRB"), 1.f);
202     glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleRG"), 1.f);
203     glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleRR"), 1.f);
204 
205     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutEnabledRefl"), GL_TRUE);
206     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutEnabledSP"), GL_FALSE);
207     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.config"), GL_LIGHT_ENV_LAYER_CONFIG6_DMP);
208     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.bumpMode"), GL_LIGHT_ENV_BUMP_NOT_USED_DMP);
209 
210     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutEnabledD0"), GL_TRUE);
211     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutEnabledD1"), GL_TRUE);
212     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.clampHighlights"), GL_FALSE);
213     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.fresnelSelector"), GL_LIGHT_ENV_PRI_SEC_ALPHA_FRESNEL_DMP);
214 
215     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.samplerRR"), 0);
216     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.samplerD0"), 1);
217     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.samplerD1"), 2);
218     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.samplerFR"), 3);
219 
220     SetLutTable();
221 
222     GLfloat ma[] = {0.f, 0.f, 0.f, 1.f};    /* material ambient */
223     GLfloat md[] = {0.f, 0.f, 0.f, 1.f};    /* material diffuse */
224     GLfloat ms[] = {1.f, 1.f, 1.f, 1.f};    /* material specular */
225 #ifdef WINDOW
226     ms[0] = 0.f;
227     ms[1] = 0.f;
228     ms[2] = 0.f;
229 #endif
230     glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.diffuse"), 1, md);
231     glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.specular0"), 1, ms);
232     glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.ambient"), 1, ma);
233 
234     GLfloat ld[] = {1.f, 1.f, 1.f, 1.f};    /* light diffuse */
235     GLfloat ls[] = {1.f, 1.f, 1.f, 1.f};    /* light specular */
236     GLfloat ls2[] = {1.f, 0.2f, 0.f, 1.f};  /* light specular2 */
237     GLfloat la[] = {1.f, 1.f, 1.f, 1.f};    /* light ambient */
238 
239     glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].diffuse"), 1, ld);
240     glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].specular0"), 1, ls);
241     glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].specular1"), 1, ls2);
242     glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].ambient"), 1, la);
243 
244     nn::math::Matrix44 proj;
245     /* set projection matrix */
246     nn::math::MTX44Frustum(&proj, -0.015f, 0.015f, -0.015f * nn::gx::DISPLAY0_HEIGHT / nn::gx::DISPLAY0_WIDTH,
247                  0.015f * nn::gx::DISPLAY0_HEIGHT / nn::gx::DISPLAY0_WIDTH, 0.2f, 200.f);
248     glUniformMatrix4fv(glGetUniformLocation(s_ProgramID, "uProjection"), 1, GL_TRUE, static_cast<f32*>(proj));
249 
250     /* set texture setting */
251 #ifdef WINDOW
252     /* output color is EnvTexture_rgb * PriColor_alpha + SecColor_rgb
253     EnvTexture_rgb is environment highlight specular, PriColor_alpha is frensel reflection,
254     and SecColor_rgb is toon shading diffuse color. */
255     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_Texture[0].samplerType"), GL_TEXTURE_CUBE_MAP);
256     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].combineRgb"), GL_MULT_ADD_DMP);
257     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].combineAlpha"), GL_REPLACE);
258     glUniform3i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_SRC_ALPHA, GL_SRC_COLOR);
259     glUniform3i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].srcRgb"), GL_TEXTURE0, GL_FRAGMENT_PRIMARY_COLOR_DMP, GL_FRAGMENT_SECONDARY_COLOR_DMP);
260 #else
261     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_Texture[0].samplerType"), GL_FALSE);
262     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].combineRgb"), GL_REPLACE);
263     glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].combineAlpha"), GL_REPLACE);
264     glUniform3i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_COLOR);
265     glUniform3i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].srcRgb"), GL_FRAGMENT_SECONDARY_COLOR_DMP, GL_CONSTANT, GL_CONSTANT);
266 #endif
267 }
268 
269 /* load objects */
LoadObjects(void)270 static void LoadObjects(void)
271 {
272     /* load obj file geometry and diffuse texture */
273     loadDAT( "rom:/resources/sphere.dat", &sphere);
274 
275     /* if you environment highlight specular, load environment cube texture */
276 #ifdef WINDOW
277     char *env_tex[] =
278     {
279          "rom:/resources/wright.tga",
280          "rom:/resources/wleft.tga",
281          "rom:/resources/wtop.tga",
282          "rom:/resources/wbottom.tga",
283          "rom:/resources/wback.tga",
284          "rom:/resources/wfront.tga",
285     };
286     glGenTextures(1, &s_EnvTexName);
287     glBindTexture(GL_TEXTURE_CUBE_MAP, s_EnvTexName);
288     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_LOD, 0);
289 
290     for (int face = 0; face < 6; face++)
291     {
292         bool use_alpha;
293         loadTexture(env_tex[face], GL_TEXTURE_CUBE_MAP_POSITIVE_X+face, 0, use_alpha, true, 0, GL_RGBA);
294     }
295     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
296     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
297 #endif
298 }
299 
UnloadObjects(void)300 static void UnloadObjects(void)
301 {
302     unloadDAT(&sphere);
303 
304     return;
305 }
306 
DrawFrame(void)307 int DrawFrame(void)
308 {
309     static int f = 0;
310 
311     s_RenderSystem.SetRenderTarget(NN_GX_DISPLAY0);
312     s_RenderSystem.Clear();
313 
314     nn::math::Matrix44 id;
315 
316     nn::math::MTX44Identity(&id);
317     glUniformMatrix4fv(glGetUniformLocation(s_ProgramID, "uWorld"), 1, GL_FALSE, static_cast<f32*>(id));
318 
319     /* modelview setting */
320     nn::math::Matrix34 mv;
321     nn::math::Vector3 camPos(0.f, 5.75f, 22.5f);
322     nn::math::Vector3 camUp(0.f, 1.f, 0.f);
323     nn::math::Vector3 target(0.f, 0.f, 0.f);
324     nn::math::MTX34LookAt(&mv, &camPos, &camUp, &target);
325 
326     /* light direction setting */
327     nn::math::Vector4 lpos0(28.5f, 9.5f, 14.f, 1.f);
328     nn::math::Vector4 mv0(mv.m[0][0], mv.m[0][1], mv.m[0][2], mv.m[0][3]);
329     nn::math::Vector4 mv1(mv.m[1][0], mv.m[1][1], mv.m[1][2], mv.m[1][3]);
330     nn::math::Vector4 mv2(mv.m[2][0], mv.m[2][1], mv.m[2][2], mv.m[2][3]);
331     nn::math::Vector4 lpos(nn::math::VEC4Dot(&mv0, &lpos0), nn::math::VEC4Dot(&mv1, &lpos0), nn::math::VEC4Dot(&mv2, &lpos0), lpos0.w);
332     glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].position"), 1, static_cast<f32*>(lpos));
333 
334     nn::math::Matrix34 rot;
335     nn::math::MTX34RotXYZDeg(&rot, 0.f, static_cast<f32>(-f), 0.f);
336     nn::math::MTX34Mult(&mv, &mv, &rot);
337     nn::math::Matrix44 m(mv);
338     glUniformMatrix4fv(glGetUniformLocation(s_ProgramID, "uModelView"), 1, GL_TRUE, static_cast<f32*>(m));
339 
340     /* draw objects */
341     for (int i = 0; i < sphere.obj_num; i++)
342     {
343         glBindBuffer(GL_ARRAY_BUFFER, sphere.posVB);
344         glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)sphere.obj[i].vtx_offset);
345         glBindBuffer(GL_ARRAY_BUFFER, sphere.normVB);
346         glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)sphere.obj[i].nrm_offset);
347 
348         for (unsigned j = sphere.obj[i].patch_offset; j < sphere.obj[i].patch_size + sphere.obj[i].patch_offset; j++)
349         {
350             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sphere.idxVB);
351             glDrawElements(GL_TRIANGLES, sphere.patch[j].elm_size,
352                 GL_UNSIGNED_SHORT, (GLvoid*)(sphere.patch[j].elm_offset + sphere.obj[i].elm_offset));
353         }
354     }
355 
356     glFinish();
357 
358     s_RenderSystem.SwapBuffers();
359 
360     s_RenderSystem.SetRenderTarget(NN_GX_DISPLAY1);
361     s_RenderSystem.Clear();
362     s_RenderSystem.SwapBuffers();
363 
364     s_RenderSystem.WaitVsync(NN_GX_DISPLAY_BOTH);
365 
366     f++;
367 
368     return !glGetError();
369 }
370 
371 /* initialization */
Initialize(void)372 static int Initialize(void)
373 {
374     s_AppHeap.Initialize(nn::os::GetDeviceMemoryAddress(), nn::os::GetDeviceMemorySize() );
375     s_HeapForGx = reinterpret_cast<uptr>(s_AppHeap.Allocate(s_GxHeapSize));
376     /* Initialize display */
377     s_RenderSystem.Initialize(s_HeapForGx, s_GxHeapSize);
378 
379     /* Create heap for malloc*/
380     s_HeapForMalloc = reinterpret_cast<uptr>(s_AppHeap.Allocate(s_HeapSize));
381     setMemoryHeap(s_HeapForMalloc, s_HeapSize);
382 
383     glClearColor(0.36f, 0.42f, 0.5f, 1.0f);
384     glClearDepthf(1.f);
385 
386     glEnable(GL_DEPTH_TEST);
387     glDepthFunc(GL_LESS);
388 
389     glEnable(GL_CULL_FACE);
390     glFrontFace(GL_CCW);
391     glCullFace(GL_BACK);
392 
393     s_ProgramID = glCreateProgram();
394     s_ShaderID = glCreateShader(GL_VERTEX_SHADER);
395 
396     nn::fs::FileReader file(L"rom:/shader.shbin");
397     size_t fileSize = file.GetSize();
398     void* buf = s_AppHeap.Allocate(fileSize);
399 
400     s32 read = file.Read(buf, fileSize);
401     glShaderBinary(1, &s_ShaderID, GL_PLATFORM_BINARY_DMP, buf, read);
402     file.Finalize();
403     s_AppHeap.Free(buf);
404 
405     glAttachShader(s_ProgramID, s_ShaderID);
406 
407     /* attach fixed-function fragment shader */
408     glAttachShader(s_ProgramID, GL_DMP_FRAGMENT_SHADER_DMP);
409 
410     glBindAttribLocation(s_ProgramID, 0, "aPosition");
411     glBindAttribLocation(s_ProgramID, 1, "aNormal");
412 
413     glLinkProgram(s_ProgramID);
414     glValidateProgram(s_ProgramID);
415     /* set program as current one to enable setting its uniforms */
416     glUseProgram(s_ProgramID);
417 
418     /* enable arrays that will be mapped to position and normal */
419     glEnableVertexAttribArray(0);
420     glEnableVertexAttribArray(1);
421 
422     /* create and bind texture collection object to capture subsequent modifications in the texture state
423      both lookup table-related one and ordinary (2D, cube) textures */
424     glGenTextures(1, &s_TexColl) ;
425     glBindTexture(GL_TEXTURE_COLLECTION_DMP, s_TexColl);
426 
427     LoadObjects();
428 
429     return 0;
430 }
431 
nnMain(void)432 void nnMain(void)
433 {
434     // Call only nn::applet::Enable to also allow execution from the HOME Menu
435     // HOME Menu transitions, POWER Button presses, and sleep are all unsupported
436     nn::applet::Enable();
437 
438     // fs initialization
439     nn::fs::Initialize();
440 
441     const size_t ROMFS_BUFFER_SIZE = 1024 * 64;
442     static char buffer[ROMFS_BUFFER_SIZE];
443     NN_UTIL_PANIC_IF_FAILED(
444         nn::fs::MountRom(16, 16, buffer, ROMFS_BUFFER_SIZE));
445 
446     /* initialization */
447     if (Initialize() >= 0)
448     {
449         /* set another render state */
450         SetRenderState();
451 
452         /* Enter loop */
453         while (1)
454         {
455             (void)DrawFrame();
456         }
457     }
458 
459     UnloadObjects();
460     /* shutdown_display */
461     s_RenderSystem.Finalize();
462     s_AppHeap.Free(reinterpret_cast<void*>(s_HeapForMalloc));
463     s_AppHeap.Free(reinterpret_cast<void*>(s_HeapForGx));
464     s_AppHeap.Finalize();
465 }
466