1 /*---------------------------------------------------------------------------*
2 
3   Copyright (C) Nintendo.  All rights reserved.
4 
5   These coded instructions, statements, and computer programs contain
6   proprietary information of Nintendo of America Inc. and/or Nintendo
7   Company Ltd., and are protected by Federal copyright law.  They may
8   not be disclosed to third parties or copied or duplicated in any form,
9   in whole or in part, without the prior written consent of Nintendo.
10 
11  *---------------------------------------------------------------------------*/
12 ////===========================================================================
13 ///  DEMOGfx.c
14 ///
15 ///     This is graphics system code for the DEMO library.
16 ///
17 ////===========================================================================
18 
19 #include <stdio.h>
20 #include <cafe/demo.h>
21 #include <cafe/gx2.h>
22 #include <cafe/procui.h>
23 
24 // A bit like asserts, but even for NDEBUG=TRUE:
25 #define DEMOCheck(x,y) if (!(x)) { OSReport("%s\n", y); while(1) OSSleepSeconds(1); }
26 
27 GX2ColorBuffer   DEMOColorBuffer;
28 GX2DepthBuffer   DEMODepthBuffer;
29 void            *DEMOScanBufferPtr;
30 
31 // DEMO Gfx Context State
32 GX2ContextState *DEMOContextState;
33 
34 u32             *DEMOCommandBufferBasePtr;
35 #define DEMO_GFX_COMMAND_BUFFER_POOL_SIZE 1024*1024*sizeof(u32)
36 
37 static BOOL gDemoGfxRunningFlag=FALSE;
38 static BOOL gDemoGfxTVEnabled=FALSE;
39 static BOOL gDemoGfxForceMEM2=FALSE;
40 
41 BOOL gDemoGfxInForeground=TRUE;
42 
43 static MEMHeapHandle  gDEMOMem1Heap = MEM_HEAP_INVALID_HANDLE;
44 static MEMHeapHandle  gDEMOBucketHeap = MEM_HEAP_INVALID_HANDLE;
45 
46 static char gDemoAssetsDir[MAX_ASSET_DIR_LEN] = "assets/";
47 
48 // State from init that needs to be preserved when reacquiring foreground.
49 static GX2TVRenderMode gDemoGfxRenderMode;
50 static GX2SurfaceFormat gDemoGfxScanOutCBFormat;
51 static u32 gDemoGfxScanSize;
52 
53 // Other preserved items
54 static u32 gDemoGfxRenderWidth;
55 static u32 gDemoGfxRenderHeight;
56 static u32 gDemoGfxSwapInterval;
57 
58 // Instance
59 static DEMOGfxInstance* gDemoGfxCurInstance = NULL;
60 
61 //GPU Job Control
62 DEMOGfxGPUTask *pgDemoGfxGPUTaskList = NULL;
63 u32 gDemoGfxGPUFence = 1; // Start with fence unlocked
64 u64 *gpDemoGfxGPUTs = NULL;
65 u64 gDemoGfxTimeVal = 0;
66 
67 
DEMOGX2RAlloc(GX2RResourceFlags resourceFlags,u32 byteCount,u32 alignment)68 static void* DEMOGX2RAlloc(GX2RResourceFlags resourceFlags, u32 byteCount, u32 alignment)
69 {
70     if (GX2TestBitFlagsAny(resourceFlags, GX2R_BIND_COLOR_BUFFER | GX2R_BIND_DEPTH_BUFFER | GX2R_BIND_SCAN_BUFFER | GX2R_BIND_GS_RING | GX2R_USAGE_FORCE_MEM1) &&
71         !GX2TestBitFlagsAny(resourceFlags, GX2R_USAGE_FORCE_MEM2))
72     {
73         return DEMOGfxAllocMEM1(byteCount, alignment);
74     }
75     else
76     {
77         return DEMOGfxAllocMEM2(byteCount, alignment);
78     }
79 }
80 
81 
DEMOGX2RFree(GX2RResourceFlags resourceFlags,void * pMem)82 static void DEMOGX2RFree(GX2RResourceFlags resourceFlags, void* pMem)
83 {
84     if (GX2TestBitFlagsAny(resourceFlags, GX2R_BIND_COLOR_BUFFER | GX2R_BIND_DEPTH_BUFFER | GX2R_BIND_SCAN_BUFFER | GX2R_BIND_GS_RING | GX2R_USAGE_FORCE_MEM1) &&
85         !GX2TestBitFlagsAny(resourceFlags, GX2R_USAGE_FORCE_MEM2))
86     {
87         DEMOGfxFreeMEM1(pMem);
88     }
89     else
90     {
91         DEMOGfxFreeMEM2(pMem);
92     }
93 }
94 
95 
96 
DEMOGfxAllocMEM1(u32 size,u32 align)97 void *DEMOGfxAllocMEM1(u32 size, u32 align)
98 {
99     void *ptr;
100 
101     if (align < 4)
102     {
103         align = 4;
104     }
105 
106     // DEMOGfxMem1HeapInit() must be called first to init the heap.
107     DEMOAssert(gDEMOMem1Heap != MEM_HEAP_INVALID_HANDLE);
108 
109     // Use the expanded heap for demos.
110     ptr = MEMAllocFromExpHeapEx(gDEMOMem1Heap, size, align);
111     GX2NotifyMemAlloc(ptr, size, align);
112 
113     DEMOAssert(ptr&&"Failed MEM1 alloc");
114 
115     return ptr;
116 }
117 
DEMOGfxFreeMEM1(void * ptr)118 void DEMOGfxFreeMEM1(void * ptr)
119 {
120     if (gDemoGfxInForeground) MEMFreeToExpHeap(gDEMOMem1Heap, ptr);
121     GX2NotifyMemFree(ptr);
122 }
123 
DEMOGfxAllocBucket(u32 size,u32 align)124 void *DEMOGfxAllocBucket(u32 size, u32 align)
125 {
126     DEMOAssert(gDEMOBucketHeap != MEM_HEAP_INVALID_HANDLE);
127 
128     void * ptr = MEMAllocFromExpHeapEx(gDEMOBucketHeap, size, align);
129     GX2NotifyMemAlloc(ptr, size, align);
130 
131     DEMOAssert(ptr);
132 
133     return ptr;
134 }
135 
DEMOGfxFreeBucket(void * ptr)136 void DEMOGfxFreeBucket(void * ptr)
137 {
138     if (gDemoGfxInForeground) MEMFreeToExpHeap(gDEMOBucketHeap, ptr);
139     GX2NotifyMemFree(ptr);
140 }
141 
DEMOGfxAllocMEM2(u32 size,u32 align)142 void *DEMOGfxAllocMEM2(u32 size, u32 align)
143 {
144     void *ptr;
145 
146     if (align < 4)
147     {
148         align = 4;
149     }
150         // FINAL MEMORY MANAGEMENT MODEL
151         ptr = DEMOAllocEx(size, align);
152         GX2NotifyMemAlloc(ptr, size, align);
153 
154     DEMOAssert(ptr&&"Failed MEM2 alloc");
155 
156     return ptr;
157 }
158 
159 
DEMOGfxFreeMEM2(void * ptr)160 void DEMOGfxFreeMEM2(void * ptr)
161 {
162         // FINAL MEMORY MANAGEMENT MODEL
163     DEMOFree(ptr);
164     GX2NotifyMemFree(ptr);
165 }
166 
DEMOGfxCallbackAcquiredForeground(void * unused)167 static u32 DEMOGfxCallbackAcquiredForeground(void* unused)
168 {
169     DEMOGfxAcquiredForeground();
170 
171     // No issues
172     return 0;
173 }
174 
DEMOGfxCallbackReleaseForeground(void * unused)175 static u32 DEMOGfxCallbackReleaseForeground(void* unused)
176 {
177     DEMOGfxReleaseForeground();
178 
179     // No issues
180     return 0;
181 }
182 
DEMOGfxInit(int argc,char * argv[])183 DEMOGfxInstance* DEMOGfxInit(int argc, char *argv[])
184 {
185     void *ptr;
186 
187     u32 scanSize;
188     GX2Boolean scaleNeeded;
189 
190     u32 i;
191     char *p;
192     u32 renderWidth = 1280;
193     u32 renderHeight = 720;
194     // Default to SRGB for gamma
195     GX2SurfaceFormat renderCBFormat  = GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB;
196     GX2SurfaceFormat scanOutCBFormat = GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB;
197     GX2SurfaceFormat renderDBFormat  = GX2_SURFACE_FORMAT_TCD_R32_FLOAT;
198     GX2AAMode renderAAMode = GX2_AA_MODE_1X;
199 	GX2Boolean renderHiZ = GX2_TRUE;
200 
201     static const u32 tvRenderModeTable[] = {
202          640,  480, GX2_TV_RENDER_480_NARROW,   // 4:3 ratio (640x480)
203          854,  480, GX2_TV_RENDER_480_WIDE,     // 16:9 ratio (854x480)
204         1280,  720, GX2_TV_RENDER_720,          // 16:9 for all the rest...
205         1920, 1080, GX2_TV_RENDER_1080,
206     };
207 #define RM_TABLE_SZ 4
208 
209     GX2TVRenderMode renderMode;
210 
211     // Init flags
212     gDemoGfxRunningFlag=FALSE;
213     gDemoGfxTVEnabled=FALSE;
214     gDemoGfxForceMEM2=FALSE;
215 
216     // Asset Directory
217     strcpy(gDemoAssetsDir, "assets/");
218 
219     // Alloc Gfx command buffer pool
220     DEMOCommandBufferBasePtr = (u32 *) DEMOAllocEx(
221                                        DEMO_GFX_COMMAND_BUFFER_POOL_SIZE,
222                                        GX2_DEFAULT_BUFFER_ALIGNMENT);
223     DEMOCheck(DEMOCommandBufferBasePtr, "Failed to allocate command buffer pool");
224 
225     // Passing user allocated cb buffer and command-line arguments to gx2Init
226     u32 gx2InitAttribs[] =
227     {
228         GX2_INIT_ATTRIB_CB_BASE, (u32)DEMOCommandBufferBasePtr,          // cb buffer
229         GX2_INIT_ATTRIB_CB_SIZE, (u32)DEMO_GFX_COMMAND_BUFFER_POOL_SIZE, // cb size
230         GX2_INIT_ATTRIB_ARGC,    (u32)argc,  // number of args
231         GX2_INIT_ATTRIB_ARGV,    (u32)argv,  // command-line args
232         GX2_INIT_ATTRIB_NULL                 // terminates the list
233     };
234 
235     // Initialize GX2 library
236     GX2Init(gx2InitAttribs);
237 
238     s32 gx2DebugMode=-1;
239     s32 gx2rDebugOptions=-1;
240     u32 swapInterval = 1;
241 
242 #define SKIP_NON_DIGIT(c) ((c)!=0&&((c)<'0'||(c)>'9'))
243 
244     // Analyze arguments
245     // Note that all arguments might be in a single string!
246     for (i = 0; i < argc; ++i)
247     {
248         p = strstr(argv[i], "DEMO_WIDTH");
249         if (p != 0){
250             renderWidth = (u32)atoi(p+10+SKIP_NON_DIGIT(p[10]));
251         }
252         p = strstr(argv[i], "DEMO_HEIGHT");
253         if (p != 0){
254             renderHeight = (u32)atoi(p+11+SKIP_NON_DIGIT(p[11]));
255         }
256         p = strstr(argv[i], "DEMO_CB_FORMAT");
257         if (p != 0){
258             p = p+14+SKIP_NON_DIGIT(p[14]);
259             if(strncmp(p, "10_10_10_2", 10) == 0){
260                 renderCBFormat = GX2_SURFACE_FORMAT_TCS_R10_G10_B10_A2_UNORM;
261             } else if(strncmp(p, "2_10_10_10", 10) == 0){
262                 renderCBFormat = GX2_SURFACE_FORMAT_TCS_A2_B10_G10_R10_UNORM;
263             } else if(strncmp(p, "8_8_8_8_SRGB", 12) == 0){
264                 renderCBFormat = GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB;
265             } else if(strncmp(p, "8_8_8_8", 7) == 0){
266                 renderCBFormat = GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM;
267             } else if(strncmp(p, "16_16_16_16F", 12) == 0){
268                 renderCBFormat = GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_FLOAT;
269             } else if(strncmp(p, "32_32_32_32F", 12) == 0){
270                 renderCBFormat = GX2_SURFACE_FORMAT_TC_R32_G32_B32_A32_FLOAT;
271             } else if(strncmp(p, "16", 2) == 0){
272                 renderCBFormat = GX2_SURFACE_FORMAT_TCD_R16_UNORM;
273             } else if(strncmp(p, "32F", 3) == 0){
274                 renderCBFormat = GX2_SURFACE_FORMAT_TCD_R32_FLOAT;
275             } else {
276                 DEMOPrintf("Unrecognized CB format: %s\n",p);
277             }
278         }
279         p = strstr(argv[i], "DEMO_SCAN_FORMAT");
280         if (p != 0){
281             p = p+16+SKIP_NON_DIGIT(p[16]);
282             if(strncmp(p, "10_10_10_2", 10) == 0){
283                 scanOutCBFormat = GX2_SURFACE_FORMAT_TCS_R10_G10_B10_A2_UNORM;
284             }else if(strncmp(p, "2_10_10_10", 10) == 0){
285                 scanOutCBFormat = GX2_SURFACE_FORMAT_TCS_A2_B10_G10_R10_UNORM;
286             }else if(strncmp(p, "8_8_8_8_SRGB", 12) == 0){
287                 scanOutCBFormat = GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB;
288             }else if(strncmp(p, "8_8_8_8", 7) == 0){
289                 scanOutCBFormat = GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM;
290             } else {
291                 DEMOPrintf("Unrecognized SCAN format: %s\n",p);
292             }
293         }
294         p = strstr(argv[i], "DEMO_DB_FORMAT");
295         if (p != 0){
296             p = p+14+SKIP_NON_DIGIT(p[14]);
297             if(strncmp(p, "16", 2) == 0){
298                 renderDBFormat = GX2_SURFACE_FORMAT_TCD_R16_UNORM;
299             }else if(strncmp(p, "8_24F", 5) == 0){
300                 renderDBFormat = GX2_SURFACE_FORMAT_D_D24_S8_FLOAT;
301             }else if(strncmp(p, "8_24", 4) == 0){
302                 renderDBFormat = GX2_SURFACE_FORMAT_D_D24_S8_UNORM;
303             }else if(strncmp(p, "X24_8_32F", 9) == 0){
304                 renderDBFormat = GX2_SURFACE_FORMAT_D_D32_FLOAT_S8_UINT_X24;
305             }else if(strncmp(p, "32F", 3) == 0){
306                 renderDBFormat = GX2_SURFACE_FORMAT_TCD_R32_FLOAT;
307             } else  {
308                 DEMOPrintf("Unrecognized DB format: %s\n",p);
309             }
310         }
311         p = strstr(argv[i], "DEMO_AA_MODE");
312         if (p != 0){
313             renderAAMode = (GX2AAMode)atoi(p+12+SKIP_NON_DIGIT(p[12]));
314             GX2_CHECK_ENUM_RANGE(renderAAMode, GX2_AA_MODE)
315         }
316         p = strstr(argv[i], "DEMO_FONT_DISABLE");
317         if (p != 0){
318             DEMOFontDrawEnable(FALSE);
319             DEMOPrintf("DEMO: DISABLE_FONT\n");
320         }
321         p = strstr(argv[i], "DEMO_HIZ_DISABLE");
322         if (p != 0){
323             DEMOPrintf("DEMO: DISABLE_HIZ\n");
324 			renderHiZ = GX2_FALSE;
325         }
326 
327         p = strstr(argv[i], "DEMO_GX2_DEBUG_MODE");
328         if (p != 0)
329         {
330             sscanf(p+strlen("DEMO_GX2_DEBUG_MODE="), "%i", &gx2DebugMode);       // allows hex notation
331         }
332 
333         p = strstr(argv[i], "DEMO_GX2R_DEBUG_OPTIONS");
334         if (p != 0)
335         {
336             sscanf(p+strlen("DEMO_GX2R_DEBUG_OPTIONS="), "%i", &gx2rDebugOptions); // allows hex notation
337         }
338 
339         p = strstr(argv[i], "DEMO_SWAP_INTERVAL");
340         if (p != 0)
341         {
342             u32 offset=strlen("DEMO_SWAP_INTERVAL");
343             swapInterval = atoi(p+offset+SKIP_NON_DIGIT(p[offset]));
344             OSReport("DEMO: swap interval=%d\n", swapInterval);
345         }
346 
347         p = strstr(argv[i], "DEMO_FORCE_MEM2");
348         if (p != 0){
349             gDemoGfxForceMEM2 = TRUE;
350             DEMOPrintf("DEMO: FORCE_MEM2\n");
351             DEMOPrintf("DEMO: Performance will be signifcantly worse!\n");
352         }
353 
354         // Modify the assets directory if DEMO_ASSETS_DIR is passed in
355         p = strstr(argv[i], "DEMO_ASSETS_DIR=");
356         if (p != 0){
357             char tempBuffer[MAX_ASSET_DIR_LEN];
358             tempBuffer[MAX_ASSET_DIR_LEN - 2] = 0;
359 
360             strncpy(tempBuffer, p+strlen("DEMO_ASSETS_DIR="), MAX_ASSET_DIR_LEN - 2);
361             // replace EOL or ',' with terminating 0
362             char* p2 = tempBuffer;
363             for( ; *p2 != '\n' && *p2 != '\r' && *p2 != ',' && *p2 != 0; p2++ ) {}
364             *p2 = 0;
365 
366             // Make sure the asset directory ends with a forward slash
367             if (*(p2 - 1) != '/')
368             {
369                 *p2 = '/';
370                 *(p2 + 1) = 0;
371             }
372 
373             strcpy(gDemoAssetsDir, tempBuffer);
374         }
375     }
376 
377     //Use a different MEM1 allocator for DEMO lib.
378     DEMOGfxMem1HeapInit();
379     DEMOGfxBucketHeapInit();
380 
381     if (gx2DebugMode!=-1)
382     {
383         DEMOPrintf("DEMO: GX2 debug mode=%#04x\n", gx2DebugMode);
384         GX2SetDebugMode((GX2DebugMode)gx2DebugMode);
385     }
386 
387 #ifdef _DEBUG
388     // If no GX2R debug options specified, default some of them in debug builds
389     if (gx2rDebugOptions==-1)
390     {
391         gx2rDebugOptions = GX2R_DEBUG_GUARD_BANDS_ENABLED | GX2R_DEBUG_LEAK_CHECK | GX2R_DEBUG_CHECK_GPU_CONTENTION;
392     }
393 #endif
394 
395     if (gx2rDebugOptions!=-1)
396     {
397         DEMOPrintf("DEMO: GX2R debug options=%#04x\n", gx2rDebugOptions);
398         GX2RSetDebugOptions((GX2RDebugOptions)gx2rDebugOptions);
399     }
400 
401     // Hook in the demo allocators for GX2R
402     GX2RSetAllocator(DEMOGX2RAlloc, DEMOGX2RFree);
403 
404     char *aaStr[4]={ "1X", "2X", "4X", "8X" };
405 
406     DEMOPrintf("DEMO: Rendering TV:%dx%d CB:%s DB:%s ScanCB:%s AA:%s\n",
407                renderWidth, renderHeight,
408                &DEMOGfxGetSurfaceFormatName(renderCBFormat)[19],
409                &DEMOGfxGetSurfaceFormatName(renderDBFormat)[19],
410                &DEMOGfxGetSurfaceFormatName(scanOutCBFormat)[19],
411                aaStr[renderAAMode]);
412 
413     // Choose an appropriate TV render mode based on desired dimensions
414     for (i = 0; i < RM_TABLE_SZ; i++) {
415         if (((tvRenderModeTable[i*3] >= renderWidth) &&
416              (tvRenderModeTable[i*3+1] >= renderHeight)) || i==(RM_TABLE_SZ-1)) {
417             renderMode = (GX2TVRenderMode) tvRenderModeTable[i*3+2];
418             break;
419         }
420     }
421 
422     // Setup Scan Buffers; this must be done before setting up TV render targets!
423     GX2CalcTVSize(renderMode, scanOutCBFormat,
424                   GX2_BUFFERING_DOUBLE, &scanSize, &scaleNeeded);
425 
426     // For Cafe, should put scan buffers in the foreground bucket
427     DEMOScanBufferPtr = DEMOGfxAllocBucket(scanSize, GX2_SCAN_BUFFER_ALIGNMENT);
428     DEMOCheck(DEMOScanBufferPtr!=NULL, "Scan buffer alloc failed");
429     GX2Invalidate(GX2_INVALIDATE_CPU, DEMOScanBufferPtr, scanSize);
430 
431     GX2SetTVBuffer(DEMOScanBufferPtr, scanSize, renderMode,
432                    scanOutCBFormat, GX2_BUFFERING_DOUBLE);
433 
434     // First, we do all the memory allocations to see what fits into MEM1.
435 
436     // Setup render buffer; designate it as "final" TV render target.
437     GX2InitColorBufferFTV(&DEMOColorBuffer,
438                           renderWidth, renderHeight,
439                           renderCBFormat,
440                           renderAAMode);
441 
442     // AA mode may have changed due to display-related issues.
443     // Please refer to the GX2 display documentation.
444     renderAAMode = DEMOColorBuffer.surface.aa;
445 
446     ptr = DEMOGfxAllocMEM1(DEMOColorBuffer.surface.imageSize,
447                            DEMOColorBuffer.surface.alignment);
448     DEMOCheck(ptr!=NULL, "Color buffer alloc failed");
449     GX2Invalidate(GX2_INVALIDATE_CPU, ptr, DEMOColorBuffer.surface.imageSize);
450 
451     GX2InitColorBufferPtr(&DEMOColorBuffer, ptr);
452 
453     // Setup aux buffer
454     if (DEMOColorBuffer.surface.aa != GX2_AA_MODE_1X) {
455         u32 size, align;
456         GX2CalcColorBufferAuxInfo(&DEMOColorBuffer, &size, &align);
457         ptr = DEMOGfxAllocMEM1(size, align);
458         DEMOCheck(ptr!=NULL, "Color aux buffer alloc failed");
459         GX2InitColorBufferAuxPtr(&DEMOColorBuffer, ptr);
460         GX2Invalidate(GX2_INVALIDATE_CPU, ptr, size);
461         // Aux buffer must be cleared manually to this value once:
462         GX2UTSetupColorAuxBuffer(&DEMOColorBuffer);
463     }
464 
465     // Setup Depth Buffer
466     GX2InitDepthBuffer(&DEMODepthBuffer,
467                        renderWidth, renderHeight,
468                        renderDBFormat,
469                        renderAAMode);
470     ptr = DEMOGfxAllocMEM1(DEMODepthBuffer.surface.imageSize,
471                            DEMODepthBuffer.surface.alignment);
472     DEMOCheck(ptr!=NULL, "Depth buffer alloc failed");
473     GX2Invalidate(GX2_INVALIDATE_CPU, ptr, DEMODepthBuffer.surface.imageSize);
474     GX2InitDepthBufferPtr(&DEMODepthBuffer, ptr);
475 
476     // Setup Hi-Z buffer
477     if (renderHiZ) {
478         u32 size, align;
479         GX2CalcDepthBufferHiZInfo(&DEMODepthBuffer, &size, &align);
480         ptr = DEMOGfxAllocMEM1(size, align);
481         DEMOCheck(ptr!=NULL, "Depth Hi-Z buffer alloc failed");
482         GX2Invalidate(GX2_INVALIDATE_CPU, ptr, size);
483         GX2InitDepthBufferHiZPtr(&DEMODepthBuffer, ptr);
484     }
485 
486     // Register a callback upon foreground acquire and release
487     ProcUIRegisterCallback(PROCUI_MESSAGE_ACQUIRE, DEMOGfxCallbackAcquiredForeground, NULL, 100);
488     ProcUIRegisterCallback(PROCUI_MESSAGE_RELEASE, DEMOGfxCallbackReleaseForeground, NULL, 100);
489 
490     // Save state for DEMOGfxAcquiredForeground
491     gDemoGfxRenderMode = renderMode;
492     gDemoGfxScanOutCBFormat = scanOutCBFormat;
493     gDemoGfxScanSize = scanSize;
494 
495     // Save state for instancing
496     gDemoGfxRenderWidth = renderWidth;
497     gDemoGfxRenderHeight = renderHeight;
498     gDemoGfxSwapInterval = swapInterval;
499 
500     // Create a new instance (including the context state)
501     gDemoGfxCurInstance = NULL;
502     DEMOGfxAddInstance();
503 
504     // The main core is the one with GFX
505     DEMOSetMainCore(OSGetCoreId());
506 
507     // Indicate that graphics has been started
508     gDemoGfxRunningFlag = TRUE;
509 
510     // Align on a cache-line boundary
511     gpDemoGfxGPUTs = (u64*)DEMOGfxAllocMEM2(sizeof(u64), 32);
512     *gpDemoGfxGPUTs = GX2_INVALID_COUNTER_VALUE_U64;
513     GX2Invalidate(GX2_INVALIDATE_CPU, gpDemoGfxGPUTs, 32);
514 
515     return gDemoGfxCurInstance;
516 }
517 
DEMOGfxShutdown()518 void DEMOGfxShutdown()
519 {
520     // Delete the instances
521     DEMOGfxDeleteInstance(gDemoGfxCurInstance);
522 
523     // It is possible to exit from the background, but some API calls
524     // can only happen from the foreground. Check a flag to see if this
525     // processes is exiting from the foreground before calling APIs
526     // that deal with foreground-only resources.
527 
528     // Disable video outputs
529     if (gDemoGfxInForeground && OS_SHUTDOWN_RESTART != OSGetShutdownReason())
530     {
531         GX2SetTVEnable(GX2_FALSE);
532     }
533     gDemoGfxTVEnabled = FALSE;
534 
535     // Free allocated buffers
536     DEMOGfxFreeBucket(DEMOScanBufferPtr);
537     if (DEMODepthBuffer.hiZPtr) {
538         DEMOGfxFreeMEM1(DEMODepthBuffer.hiZPtr);
539         DEMODepthBuffer.hiZPtr = NULL;
540     }
541     DEMOGfxFreeMEM1(DEMODepthBuffer.surface.imagePtr);
542     if (DEMOColorBuffer.auxPtr) {
543         DEMOGfxFreeMEM1(DEMOColorBuffer.auxPtr);
544         DEMOColorBuffer.auxPtr = NULL;
545     }
546     DEMOGfxFreeMEM1(DEMOColorBuffer.surface.imagePtr);
547     if (gDemoGfxInForeground) DEMOFree(DEMOCommandBufferBasePtr);
548 
549     // Free GPU Task Control System
550     DEMOGfxFreeMEM2(gpDemoGfxGPUTs);
551     if (pgDemoGfxGPUTaskList)
552     {
553         DEMOGfxFreeGPUTasks();
554     }
555 
556     // Shutdown GX2 (which is safe to call from the background);
557     GX2Shutdown();
558 
559     // Dump any leaked resources if enabled
560     if (GX2RGetDebugOptions() & GX2R_DEBUG_LEAK_CHECK)
561     {
562         if (GX2TempGetResourceCount()>0)
563         {
564             OSReport("***DEMO: resources were not destroyed on shutdown\n");
565             GX2TempDumpResources();
566             ASSERT(FALSE && "Resources were not destroyed on shutdown");
567         }
568     }
569     // Unhook the GX2R allocator functions.
570     GX2RSetAllocator(NULL, NULL);
571 
572     // Only destroy these heaps if this process is in the foreground.
573     if (gDemoGfxInForeground)
574     {
575         DEMOGfxMem1HeapDestroy();
576         DEMOGfxBucketHeapDestroy();
577     }
578 
579     gDemoGfxRunningFlag = FALSE;
580 }
581 
DEMOGfxReleaseForeground()582 void DEMOGfxReleaseForeground()
583 {
584     // First wait for all pending draws to complete.
585     GX2DrawDone();
586 
587     // Release resources in MEM1
588     if (DEMODepthBuffer.hiZPtr) {
589         DEMOGfxFreeMEM1(DEMODepthBuffer.hiZPtr);
590         DEMODepthBuffer.hiZPtr = NULL;
591     }
592     DEMOGfxFreeMEM1(DEMODepthBuffer.surface.imagePtr);
593     if (DEMOColorBuffer.auxPtr) {
594         DEMOGfxFreeMEM1(DEMOColorBuffer.auxPtr);
595         DEMOColorBuffer.auxPtr = NULL;
596     }
597     DEMOGfxFreeMEM1(DEMOColorBuffer.surface.imagePtr);
598 
599     // Free resources from the foreground bucket
600     DEMOGfxFreeBucket(DEMOScanBufferPtr);
601 
602     // Destroy heaps that will not be available.
603     DEMOGfxMem1HeapDestroy();
604     DEMOGfxBucketHeapDestroy();
605 
606     // Mark the TV as disabled.
607     gDemoGfxTVEnabled=FALSE;
608 
609     // Set a flag to indicate that this process is in the background.
610     gDemoGfxInForeground=FALSE;
611     OSMemoryBarrier();
612 }
613 
DEMOGfxAcquiredForeground()614 void DEMOGfxAcquiredForeground()
615 {
616     void *ptr;
617 
618     // Set a flag to indicate that this process is in the foreground.
619     gDemoGfxInForeground=TRUE;
620     OSMemoryBarrier();
621 
622     // Recreate heaps that were lost.
623     DEMOGfxMem1HeapInit();
624     DEMOGfxBucketHeapInit();
625 
626     // For Cafe, should put scan buffers in the foreground bucket
627     DEMOScanBufferPtr = DEMOGfxAllocBucket(gDemoGfxScanSize, GX2_SCAN_BUFFER_ALIGNMENT);
628     GX2Invalidate(GX2_INVALIDATE_CPU, DEMOScanBufferPtr, gDemoGfxScanSize);
629 
630     GX2SetTVBuffer(DEMOScanBufferPtr, gDemoGfxScanSize, gDemoGfxRenderMode,
631                    gDemoGfxScanOutCBFormat, GX2_BUFFERING_DOUBLE);
632 
633     // Re-allocate color buffer
634     ptr = DEMOGfxAllocMEM1(DEMOColorBuffer.surface.imageSize,
635                            DEMOColorBuffer.surface.alignment);
636     GX2Invalidate(GX2_INVALIDATE_CPU, ptr, DEMOColorBuffer.surface.imageSize);
637 
638     GX2InitColorBufferPtr(&DEMOColorBuffer, ptr);
639 
640     // Setup aux buffer
641     if (DEMOColorBuffer.surface.aa != GX2_AA_MODE_1X) {
642         u32 size, align;
643         GX2CalcColorBufferAuxInfo(&DEMOColorBuffer, &size, &align);
644         ptr = DEMOGfxAllocMEM1(size, align);
645         GX2InitColorBufferAuxPtr(&DEMOColorBuffer, ptr);
646         GX2Invalidate(GX2_INVALIDATE_CPU, ptr, size);
647         // Aux buffer must be cleared manually to this value once:
648         GX2UTSetupColorAuxBuffer(&DEMOColorBuffer);
649     }
650 
651     // Re-allocate depth buffer
652     ptr = DEMOGfxAllocMEM1(DEMODepthBuffer.surface.imageSize,
653                            DEMODepthBuffer.surface.alignment);
654     GX2Invalidate(GX2_INVALIDATE_CPU, ptr, DEMODepthBuffer.surface.imageSize);
655     GX2InitDepthBufferPtr(&DEMODepthBuffer, ptr);
656 
657     // Setup Hi-Z buffer
658     if (1) { // Allow testing with & without Hi-Z
659         u32 size, align;
660         GX2CalcDepthBufferHiZInfo(&DEMODepthBuffer, &size, &align);
661         ptr = DEMOGfxAllocMEM1(size, align);
662         GX2Invalidate(GX2_INVALIDATE_CPU, ptr, size);
663         GX2InitDepthBufferHiZPtr(&DEMODepthBuffer, ptr);
664     }
665 
666     GX2SetContextState(DEMOContextState);
667 
668     // Indicate that graphics has been started
669     gDemoGfxRunningFlag = TRUE;
670 }
671 
DEMOGfxIsRunning()672 BOOL DEMOGfxIsRunning()
673 {
674     return gDemoGfxRunningFlag;
675 }
676 
DEMOGfxSetContextState(void)677 void DEMOGfxSetContextState(void)
678 {
679     GX2UTDebugTagIndent("DEMOGfxSetContextState()");
680     GX2SetContextState(DEMOContextState);
681     GX2UTDebugTagUndent();
682 }
683 
DEMOGfxBeforeRender(void)684 void DEMOGfxBeforeRender(void)
685 {
686     GX2UTDebugTagIndent("DEMOGfxBeforeRender()");
687     // Allow CPU to run 1 frame ahead of GPU and display
688     //DEMOGfxWaitForSwap(1, 0);
689     //TEMP TODO: most of the demos rely on the CPU not being ahead of the GPU, until that's (maybe) going to get fixed
690     // we need to leave the swap queue depth at 0.
691     DEMOGfxWaitForSwap(0, 0);
692 
693     // Enabled when Demo Perf condition is met
694     DEMOTestCheckPerfBegin();
695     GX2UTDebugTagUndent();
696 }
697 
DEMOGfxDoneRender(void)698 void DEMOGfxDoneRender(void)
699 {
700     GX2UTDebugTagIndent("DEMOGfxDoneRender()");
701     // Enabled when Demo Perf condition is met
702     DEMOTestCheckPerfEnd();
703 
704     // This extra flush avoids a potential race condition that can occur with
705     // the GX2R Resource Tracker: the swap buffer command might finish before
706     // the time stamp of its CB is updated.  The race condition would make the
707     // resource appear still in use at the start of the next frame.  This extra
708     // flush makes sure that no GX2R resources share the same time stamp as
709     // the swap buffer CB, thus avoiding the problem.
710     GX2Flush();
711 
712     GX2SwapBuffers(&DEMOColorBuffer);
713 
714     // Restore the context state after copy
715     GX2SetContextState(DEMOContextState);
716 
717     // Do this before the flush so it's in the same CB as everything else
718     GX2UTDebugTagUndent();
719 
720     // Flush after swap, since swap goes into the FIFO
721     GX2Flush();
722 
723     // Enable video output after first frame rendered
724     if (gDemoGfxTVEnabled == FALSE) {
725         GX2SetTVEnable(GX2_TRUE);
726         gDemoGfxTVEnabled = TRUE;
727     }
728 }
729 
DEMOGfxDrawDone(void)730 void DEMOGfxDrawDone(void)
731 {
732     // Only if running and in the foreground
733     if (APP_IN_FOREGROUND && DEMOGfxIsRunning())
734         GX2DrawDone();
735 }
736 
DEMOGfxWaitForSwap(u32 depth,u32 percent)737 void DEMOGfxWaitForSwap(u32 depth, u32 percent)
738 {
739     GX2UTDebugTagIndent("DEMOGfxWaitForSwap()");
740     u32 swapCount, flipCount, waitCount=0;
741     OSTime tLastFlip, tLastVsync;
742     OSTime tNow;
743     const OSTime t60  = (OSTime)OSSecondsToTicks(1.0f/59.94f);
744     OSTime period;
745     u32    swapInt = GX2GetSwapInterval();
746 
747     if (swapInt != 0)
748     {
749         // Note: must be careful about unsigned wrap-around!
750 
751         // Wait for "depth" frames ago to post
752         while (1)
753         {
754             GX2GetSwapStatus(&swapCount, &flipCount, &tLastFlip, &tLastVsync);
755             if (flipCount+depth >= swapCount) break;
756 
757             // If we've waited over 10 seconds for a flip, consider the GPU hung
758             // and stop running.
759             if (waitCount++ > 60*GX2GetGPUTimeout()/1000) {
760                 OSReport("DEMOGfxWaitForSwap timed out. Potential GPU hang detected?\n");
761                 GX2SetMiscParam(GX2_MISC_HANG_STATE, GX2_HANG_STATE_ETC);
762                 if (GX2GetMiscParam(GX2_MISC_HANG_RESPONSE) == GX2_HANG_RESPONSE_DEBUG) {
763                     GX2PrintGPUStatus();
764                     OSDebugStrIfConnected(0);
765                     DEMOStopRunning();
766                 }
767                 break;
768             }
769 
770             // Call WaitForVsync instead of WaitForFlip due to possible
771             // race condition of flip happening right after above test.
772             // (There will always be more vsyncs, but not always more flips.)
773             GX2WaitForVsync();
774         }
775 
776         // Wait for (percent * Swap Period) milliseconds since last flip
777         period = (percent * swapInt * t60 / 100);
778         tNow = OSGetSystemTime();
779         if (period > (tNow - tLastFlip))
780         {
781             OSSleepTicks(period - (tNow - tLastFlip));
782         }
783     }
784 
785     GX2UTDebugTagUndent();
786 }
787 
DEMOGfxMem1HeapInit(void)788 void DEMOGfxMem1HeapInit(void)
789 {
790     //A real game should use the frame heap directly to save memory,
791     //but using an expanded heap makes the demos simpler.
792 
793     //For more information on MEM1 usage, go to
794     // Operating System->Cafe Core OS (COS) Overview->Basic Memory Allocation->MEM1
795     //in the MAN pages.
796     MEMHeapHandle hMEM1 = MEMGetBaseHeapHandle(MEM_ARENA_1);
797     u32 uMEM1Size = MEMGetAllocatableSizeForFrmHeapEx(hMEM1,4);
798     if (!gDemoGfxForceMEM2)
799     {
800         void* pStartOfMem1 = MEMAllocFromFrmHeapEx(hMEM1, uMEM1Size, 4);
801         gDEMOMem1Heap = MEMCreateExpHeap(pStartOfMem1, uMEM1Size);
802     }
803     else
804     {
805         void* pStartOfMem1 = MEMAllocFromDefaultHeapEx(uMEM1Size, 4);
806         gDEMOMem1Heap = MEMCreateExpHeap(pStartOfMem1, uMEM1Size);
807     }
808     DEMOAssert(gDEMOMem1Heap != MEM_HEAP_INVALID_HANDLE);
809 }
810 
DEMOGfxMem1HeapDestroy(void)811 void DEMOGfxMem1HeapDestroy(void)
812 {
813     DEMOAssert(gDEMOMem1Heap != MEM_HEAP_INVALID_HANDLE);
814 
815     //Destroy the expanded heap and reset the frame heap to restore the allocation.
816     MEMDestroyExpHeap(gDEMOMem1Heap);
817     if (!gDemoGfxForceMEM2)
818     {
819         MEMHeapHandle hMEM1 = MEMGetBaseHeapHandle(MEM_ARENA_1);
820         MEMFreeToFrmHeap(hMEM1, MEM_FRMHEAP_FREE_ALL);
821     }
822     else
823     {
824         MEMFreeToDefaultHeap(gDEMOMem1Heap);
825     }
826     gDEMOMem1Heap = MEM_HEAP_INVALID_HANDLE;
827 }
828 
DEMOGfxBucketHeapInit(void)829 void DEMOGfxBucketHeapInit(void)
830 {
831     //A real game should use the frame heap directly to save memory,
832     //but using an expanded heap makes the demos simpler.
833 
834     //For more information on Foreground Bucket usage, go to
835     // Operating System->Cafe Core OS (COS) Overview->Basic Memory Allocation->Foreground Bucket
836     //in the MAN pages.
837     MEMHeapHandle hMEMFg = MEMGetBaseHeapHandle(MEM_ARENA_FG);
838     u32 uMEMFgSize = MEMGetAllocatableSizeForFrmHeapEx(hMEMFg,4);
839     void* pStartOfMemFg = MEMAllocFromFrmHeapEx(hMEMFg, uMEMFgSize, 4);
840     gDEMOBucketHeap = MEMCreateExpHeap(pStartOfMemFg, uMEMFgSize);
841     DEMOAssert(gDEMOBucketHeap != MEM_HEAP_INVALID_HANDLE);
842 }
843 
DEMOGfxBucketHeapDestroy(void)844 void DEMOGfxBucketHeapDestroy(void)
845 {
846     DEMOAssert(gDEMOBucketHeap != MEM_HEAP_INVALID_HANDLE);
847 
848     //Destroy the expanded heap and reset the frame heap to restore the allocation.
849     MEMDestroyExpHeap(gDEMOBucketHeap);
850     gDEMOBucketHeap = MEM_HEAP_INVALID_HANDLE;
851     MEMHeapHandle hMEMFg = MEMGetBaseHeapHandle(MEM_ARENA_FG);
852     MEMFreeToFrmHeap(hMEMFg, MEM_FRMHEAP_FREE_ALL);
853 }
854 
855 
DEMOGfxAddInstance(void)856 DEMOGfxInstance* DEMOGfxAddInstance(void)
857 {
858     // Setup Context State buffer
859     DEMOContextState = (GX2ContextState *)DEMOGfxAllocMEM2(sizeof(GX2ContextState), GX2_CONTEXT_STATE_ALIGNMENT);
860     DEMOCheck(DEMOContextState != NULL, "Context state buffer alloc failed");
861     // GX2SetupContextState will invalidate CPU cache for us
862     GX2SetupContextState(DEMOContextState);
863 
864     // Setup render/depth buffers to be used now
865     GX2SetColorBuffer(&DEMOColorBuffer, GX2_RENDER_TARGET_0);
866     GX2SetDepthBuffer(&DEMODepthBuffer);
867 
868     // Misc graphics setup
869     GX2SetViewport(0, 0, (f32)gDemoGfxRenderWidth, (f32)gDemoGfxRenderHeight, 0.0f, 1.0f);
870     GX2SetScissor(0, 0, gDemoGfxRenderWidth, gDemoGfxRenderHeight);
871 
872     // Indicate that swaps can happen as fast as every vertical interval (1/60 sec)
873     GX2SetSwapInterval(gDemoGfxSwapInterval);
874 
875     /////////////////////////////////////////////////////////////////////////////
876     // Create the new instance
877     gDemoGfxCurInstance = (DEMOGfxInstance *)DEMOAlloc(sizeof(DEMOGfxInstance));
878     gDemoGfxCurInstance->contextState = DEMOContextState;
879 
880     return gDemoGfxCurInstance;
881 }
882 
DEMOGfxDeleteInstance(DEMOGfxInstance * instance)883 void DEMOGfxDeleteInstance(DEMOGfxInstance *instance)
884 {
885     DEMOGfxFreeMEM2(instance->contextState);
886     DEMOFree(instance);
887 
888     // Edge case:  delete current
889     if (gDemoGfxCurInstance == instance) {
890         gDemoGfxCurInstance = NULL;
891         DEMOContextState = NULL;
892     }
893 }
894 
895 
DEMOGfxSetInstance(DEMOGfxInstance * instance)896 void DEMOGfxSetInstance(DEMOGfxInstance *instance)
897 {
898     gDemoGfxCurInstance = instance;
899 
900     DEMOContextState = instance->contextState;
901     GX2SetContextState(DEMOContextState);
902 }
903 
DEMOGfxGetInstance(void)904 DEMOGfxInstance* DEMOGfxGetInstance(void)
905 {
906     return gDemoGfxCurInstance;
907 }
908 
DEMOGfxOpenAssetFile(const char * path,DEMOFSFileInfo * info)909 s32 DEMOGfxOpenAssetFile(const char* path, DEMOFSFileInfo* info)
910 {
911     char filename[MAX_ASSET_DIR_FULL_LEN];
912 
913     strcpy(filename, gDemoAssetsDir);
914     strncat(filename, path, MAX_ASSET_DIR_FULL_LEN - MAX_ASSET_DIR_LEN);
915 
916     return DEMOFSOpenFile(filename, info);
917 }
918 
DEMOGfxGetAssetFileLength(const DEMOFSFileInfo * fileInfo,u32 * length)919 s32 DEMOGfxGetAssetFileLength(const DEMOFSFileInfo* fileInfo, u32* length)
920 {
921     return DEMOFSGetLength(fileInfo, length);
922 }
923 
DEMOGfxReadAssetFile(DEMOFSFileInfo * fileInfo,void * addr,s32 length,s32 offset)924 s32 DEMOGfxReadAssetFile(DEMOFSFileInfo* fileInfo, void* addr, s32 length, s32 offset)
925 {
926     return DEMOFSRead(fileInfo, addr, length, offset);
927 }
928 
DEMOGfxCloseAssetFile(DEMOFSFileInfo * fileInfo)929 s32 DEMOGfxCloseAssetFile(DEMOFSFileInfo* fileInfo)
930 {
931     return DEMOFSCloseFile(fileInfo);
932 }
933 
DEMOGfxLoadAssetFile(const char * path,u32 * len)934 void* DEMOGfxLoadAssetFile(const char* path, u32* len)
935 {
936     char filename[MAX_ASSET_DIR_FULL_LEN];
937 
938     strcpy(filename, gDemoAssetsDir);
939     strncat(filename, path, MAX_ASSET_DIR_FULL_LEN - MAX_ASSET_DIR_LEN);
940 
941     return DEMOFSSimpleRead(filename, len);
942 }
943 
DEMOGfxLoadAssetFileAlign(const char * path,u32 * len,u32 alignSize)944 void* DEMOGfxLoadAssetFileAlign(const char* path, u32* len, u32 alignSize)
945 {
946     char filename[MAX_ASSET_DIR_FULL_LEN];
947 
948     strcpy(filename, gDemoAssetsDir);
949     strncat(filename, path, MAX_ASSET_DIR_FULL_LEN - MAX_ASSET_DIR_LEN);
950 
951     return DEMOFSSimpleReadAlign(filename, len, alignSize);
952 }
953 
DEMOGfxScanAssetDir(const char * path,u32 * pFileCount,u32 maxFiles,char ** ppFileNames)954 s32 DEMOGfxScanAssetDir(const char *path, u32 *pFileCount, u32 maxFiles, char** ppFileNames)
955 {
956     char filename[MAX_ASSET_DIR_FULL_LEN];
957 
958     strcpy(filename, gDemoAssetsDir);
959     strncat(filename, path, MAX_ASSET_DIR_FULL_LEN - MAX_ASSET_DIR_LEN);
960 
961     return DEMOFSScanDir(filename, path, pFileCount, maxFiles, ppFileNames);
962 }
963 
DEMOGfxFileExists(const char * path)964 BOOL DEMOGfxFileExists(const char* path)
965 {
966     char filename[MAX_ASSET_DIR_FULL_LEN];
967 
968     strcpy(filename, gDemoAssetsDir);
969     strncat(filename, path, MAX_ASSET_DIR_FULL_LEN - MAX_ASSET_DIR_LEN);
970 
971     return DEMOFSFileExists(path);
972 }
973 
DEMOGfxGetAttribFormatName(GX2AttribFormat format)974 const char *DEMOGfxGetAttribFormatName(GX2AttribFormat format)
975 {
976     switch(format) {
977     case GX2_ATTRIB_FORMAT_8_UNORM:                   return "GX2_ATTRIB_FORMAT_8_UNORM";                   // 0x0000
978     case GX2_ATTRIB_FORMAT_8_UINT:                    return "GX2_ATTRIB_FORMAT_8_UINT";                    // 0x0100
979     case GX2_ATTRIB_FORMAT_8_SNORM:                   return "GX2_ATTRIB_FORMAT_8_SNORM";                   // 0x0200
980     case GX2_ATTRIB_FORMAT_8_SINT:                    return "GX2_ATTRIB_FORMAT_8_SINT";                    // 0x0300
981     case GX2_ATTRIB_FORMAT_8_UINT_TO_FLOAT:           return "GX2_ATTRIB_FORMAT_8_UINT_TO_FLOAT";           // 0x0800
982     case GX2_ATTRIB_FORMAT_8_SINT_TO_FLOAT:           return "GX2_ATTRIB_FORMAT_8_SINT_TO_FLOAT";           // 0x0a00
983     case GX2_ATTRIB_FORMAT_4_4_UNORM:                 return "GX2_ATTRIB_FORMAT_4_4_UNORM";                 // 0x0001
984     case GX2_ATTRIB_FORMAT_16_UNORM:                  return "GX2_ATTRIB_FORMAT_16_UNORM";                  // 0x0002
985     case GX2_ATTRIB_FORMAT_16_UINT:                   return "GX2_ATTRIB_FORMAT_16_UINT";                   // 0x0102
986     case GX2_ATTRIB_FORMAT_16_SNORM:                  return "GX2_ATTRIB_FORMAT_16_SNORM";                  // 0x0202
987     case GX2_ATTRIB_FORMAT_16_SINT:                   return "GX2_ATTRIB_FORMAT_16_SINT";                   // 0x0302
988     case GX2_ATTRIB_FORMAT_16_FLOAT:                  return "GX2_ATTRIB_FORMAT_16_FLOAT";                  // 0x0803
989     case GX2_ATTRIB_FORMAT_16_UINT_TO_FLOAT:          return "GX2_ATTRIB_FORMAT_16_UINT_TO_FLOAT";          // 0x0802
990     case GX2_ATTRIB_FORMAT_16_SINT_TO_FLOAT:          return "GX2_ATTRIB_FORMAT_16_SINT_TO_FLOAT";          // 0x0a02
991     case GX2_ATTRIB_FORMAT_8_8_UNORM:                 return "GX2_ATTRIB_FORMAT_8_8_UNORM";                 // 0x0004
992     case GX2_ATTRIB_FORMAT_8_8_UINT:                  return "GX2_ATTRIB_FORMAT_8_8_UINT";                  // 0x0104
993     case GX2_ATTRIB_FORMAT_8_8_SNORM:                 return "GX2_ATTRIB_FORMAT_8_8_SNORM";                 // 0x0204
994     case GX2_ATTRIB_FORMAT_8_8_SINT:                  return "GX2_ATTRIB_FORMAT_8_8_SINT";                  // 0x0304
995     case GX2_ATTRIB_FORMAT_8_8_UINT_TO_FLOAT:         return "GX2_ATTRIB_FORMAT_8_8_UINT_TO_FLOAT";         // 0x0804
996     case GX2_ATTRIB_FORMAT_8_8_SINT_TO_FLOAT:         return "GX2_ATTRIB_FORMAT_8_8_SINT_TO_FLOAT";         // 0x0a04
997     case GX2_ATTRIB_FORMAT_32_UINT:                   return "GX2_ATTRIB_FORMAT_32_UINT";                   // 0x0105
998     case GX2_ATTRIB_FORMAT_32_SINT:                   return "GX2_ATTRIB_FORMAT_32_SINT";                   // 0x0305
999     case GX2_ATTRIB_FORMAT_32_FLOAT:                  return "GX2_ATTRIB_FORMAT_32_FLOAT";                  // 0x0806
1000     case GX2_ATTRIB_FORMAT_16_16_UNORM:               return "GX2_ATTRIB_FORMAT_16_16_UNORM";               // 0x0007
1001     case GX2_ATTRIB_FORMAT_16_16_UINT:                return "GX2_ATTRIB_FORMAT_16_16_UINT";                // 0x0107
1002     case GX2_ATTRIB_FORMAT_16_16_SNORM:               return "GX2_ATTRIB_FORMAT_16_16_SNORM";               // 0x0207
1003     case GX2_ATTRIB_FORMAT_16_16_SINT:                return "GX2_ATTRIB_FORMAT_16_16_SINT";                // 0x0307
1004     case GX2_ATTRIB_FORMAT_16_16_FLOAT:               return "GX2_ATTRIB_FORMAT_16_16_FLOAT";               // 0x0808
1005     case GX2_ATTRIB_FORMAT_16_16_UINT_TO_FLOAT:       return "GX2_ATTRIB_FORMAT_16_16_UINT_TO_FLOAT";       // 0x0807
1006     case GX2_ATTRIB_FORMAT_16_16_SINT_TO_FLOAT:       return "GX2_ATTRIB_FORMAT_16_16_SINT_TO_FLOAT";       // 0x0a07
1007     case GX2_ATTRIB_FORMAT_10_11_11_FLOAT:            return "GX2_ATTRIB_FORMAT_10_11_11_FLOAT";            // 0x0809
1008     case GX2_ATTRIB_FORMAT_8_8_8_8_UNORM:             return "GX2_ATTRIB_FORMAT_8_8_8_8_UNORM";             // 0x000a
1009     case GX2_ATTRIB_FORMAT_8_8_8_8_UINT:              return "GX2_ATTRIB_FORMAT_8_8_8_8_UINT";              // 0x010a
1010     case GX2_ATTRIB_FORMAT_8_8_8_8_SNORM:             return "GX2_ATTRIB_FORMAT_8_8_8_8_SNORM";             // 0x020a
1011     case GX2_ATTRIB_FORMAT_8_8_8_8_SINT:              return "GX2_ATTRIB_FORMAT_8_8_8_8_SINT";              // 0x030a
1012     case GX2_ATTRIB_FORMAT_8_8_8_8_UINT_TO_FLOAT:     return "GX2_ATTRIB_FORMAT_8_8_8_8_UINT_TO_FLOAT";     // 0x080a
1013     case GX2_ATTRIB_FORMAT_8_8_8_8_SINT_TO_FLOAT:     return "GX2_ATTRIB_FORMAT_8_8_8_8_SINT_TO_FLOAT";     // 0x0a0a
1014     case GX2_ATTRIB_FORMAT_10_10_10_2_UNORM:          return "GX2_ATTRIB_FORMAT_10_10_10_2_UNORM";          // 0x000b
1015     case GX2_ATTRIB_FORMAT_10_10_10_2_UINT:           return "GX2_ATTRIB_FORMAT_10_10_10_2_UINT";           // 0x010b
1016     case GX2_ATTRIB_FORMAT_10_10_10_2_SNORM:          return "GX2_ATTRIB_FORMAT_10_10_10_2_SNORM";          // 0x020b
1017     case GX2_ATTRIB_FORMAT_10_10_10_2_SINT:           return "GX2_ATTRIB_FORMAT_10_10_10_2_SINT";           // 0x030b
1018     case GX2_ATTRIB_FORMAT_32_32_UINT:                return "GX2_ATTRIB_FORMAT_32_32_UINT";                // 0x010c
1019     case GX2_ATTRIB_FORMAT_32_32_SINT:                return "GX2_ATTRIB_FORMAT_32_32_SINT";                // 0x030c
1020     case GX2_ATTRIB_FORMAT_32_32_FLOAT:               return "GX2_ATTRIB_FORMAT_32_32_FLOAT";               // 0x080d
1021     case GX2_ATTRIB_FORMAT_16_16_16_16_UNORM:         return "GX2_ATTRIB_FORMAT_16_16_16_16_UNORM";         // 0x000e
1022     case GX2_ATTRIB_FORMAT_16_16_16_16_UINT:          return "GX2_ATTRIB_FORMAT_16_16_16_16_UINT";          // 0x010e
1023     case GX2_ATTRIB_FORMAT_16_16_16_16_SNORM:         return "GX2_ATTRIB_FORMAT_16_16_16_16_SNORM";         // 0x020e
1024     case GX2_ATTRIB_FORMAT_16_16_16_16_SINT:          return "GX2_ATTRIB_FORMAT_16_16_16_16_SINT";          // 0x030e
1025     case GX2_ATTRIB_FORMAT_16_16_16_16_FLOAT:         return "GX2_ATTRIB_FORMAT_16_16_16_16_FLOAT";         // 0x080f
1026     case GX2_ATTRIB_FORMAT_16_16_16_16_UINT_TO_FLOAT: return "GX2_ATTRIB_FORMAT_16_16_16_16_UINT_TO_FLOAT"; // 0x080e
1027     case GX2_ATTRIB_FORMAT_16_16_16_16_SINT_TO_FLOAT: return "GX2_ATTRIB_FORMAT_16_16_16_16_SINT_TO_FLOAT"; // 0x0a0e
1028     case GX2_ATTRIB_FORMAT_32_32_32_UINT:             return "GX2_ATTRIB_FORMAT_32_32_32_UINT";             // 0x0110
1029     case GX2_ATTRIB_FORMAT_32_32_32_SINT:             return "GX2_ATTRIB_FORMAT_32_32_32_SINT";             // 0x0310
1030     case GX2_ATTRIB_FORMAT_32_32_32_FLOAT:            return "GX2_ATTRIB_FORMAT_32_32_32_FLOAT";            // 0x0811
1031     case GX2_ATTRIB_FORMAT_32_32_32_32_UINT:          return "GX2_ATTRIB_FORMAT_32_32_32_32_UINT";          // 0x0112
1032     case GX2_ATTRIB_FORMAT_32_32_32_32_SINT:          return "GX2_ATTRIB_FORMAT_32_32_32_32_SINT";          // 0x0312
1033     case GX2_ATTRIB_FORMAT_32_32_32_32_FLOAT:         return "GX2_ATTRIB_FORMAT_32_32_32_32_FLOAT";         // 0x0813
1034     default:                                          return "invalid format";
1035     }
1036 }
1037 
DEMOGfxGetSurfaceFormatName(GX2SurfaceFormat format)1038 const char *DEMOGfxGetSurfaceFormatName(GX2SurfaceFormat format)
1039 {
1040     switch(format) {
1041     case GX2_SURFACE_FORMAT_INVALID:                  return "GX2_SURFACE_FORMAT_INVALID";                  // 0x0000
1042     case GX2_SURFACE_FORMAT_TC_R8_UNORM:              return "GX2_SURFACE_FORMAT_TC_R8_UNORM";              // 0x0001
1043     case GX2_SURFACE_FORMAT_TC_R8_UINT:               return "GX2_SURFACE_FORMAT_TC_R8_UINT";               // 0x0101
1044     case GX2_SURFACE_FORMAT_TC_R8_SNORM:              return "GX2_SURFACE_FORMAT_TC_R8_SNORM";              // 0x0201
1045     case GX2_SURFACE_FORMAT_TC_R8_SINT:               return "GX2_SURFACE_FORMAT_TC_R8_SINT";               // 0x0301
1046     case GX2_SURFACE_FORMAT_T_R4_G4_UNORM:            return "GX2_SURFACE_FORMAT_T_R4_G4_UNORM";            // 0x0002
1047     case GX2_SURFACE_FORMAT_TCD_R16_UNORM:            return "GX2_SURFACE_FORMAT_TCD_R16_UNORM";            // 0x0005
1048     case GX2_SURFACE_FORMAT_TC_R16_UINT:              return "GX2_SURFACE_FORMAT_TC_R16_UINT";              // 0x0105
1049     case GX2_SURFACE_FORMAT_TC_R16_SNORM:             return "GX2_SURFACE_FORMAT_TC_R16_SNORM";             // 0x0205
1050     case GX2_SURFACE_FORMAT_TC_R16_SINT:              return "GX2_SURFACE_FORMAT_TC_R16_SINT";              // 0x0305
1051     case GX2_SURFACE_FORMAT_TC_R16_FLOAT:             return "GX2_SURFACE_FORMAT_TC_R16_FLOAT";             // 0x0806
1052     case GX2_SURFACE_FORMAT_TC_R8_G8_UNORM:           return "GX2_SURFACE_FORMAT_TC_R8_G8_UNORM";           // 0x0007
1053     case GX2_SURFACE_FORMAT_TC_R8_G8_UINT:            return "GX2_SURFACE_FORMAT_TC_R8_G8_UINT";            // 0x0107
1054     case GX2_SURFACE_FORMAT_TC_R8_G8_SNORM:           return "GX2_SURFACE_FORMAT_TC_R8_G8_SNORM";           // 0x0207
1055     case GX2_SURFACE_FORMAT_TC_R8_G8_SINT:            return "GX2_SURFACE_FORMAT_TC_R8_G8_SINT";            // 0x0307
1056     case GX2_SURFACE_FORMAT_TCS_R5_G6_B5_UNORM:       return "GX2_SURFACE_FORMAT_TCS_R5_G6_B5_UNORM";       // 0x0008
1057     case GX2_SURFACE_FORMAT_TC_R5_G5_B5_A1_UNORM:     return "GX2_SURFACE_FORMAT_TC_R5_G5_B5_A1_UNORM";     // 0x000a
1058     case GX2_SURFACE_FORMAT_TC_R4_G4_B4_A4_UNORM:     return "GX2_SURFACE_FORMAT_TC_R4_G4_B4_A4_UNORM";     // 0x000b
1059     case GX2_SURFACE_FORMAT_TC_A1_B5_G5_R5_UNORM:     return "GX2_SURFACE_FORMAT_TC_A1_B5_G5_R5_UNORM";     // 0x000c
1060     case GX2_SURFACE_FORMAT_TC_R32_UINT:              return "GX2_SURFACE_FORMAT_TC_R32_UINT";              // 0x010d
1061     case GX2_SURFACE_FORMAT_TC_R32_SINT:              return "GX2_SURFACE_FORMAT_TC_R32_SINT";              // 0x030d
1062     case GX2_SURFACE_FORMAT_TCD_R32_FLOAT:            return "GX2_SURFACE_FORMAT_TCD_R32_FLOAT";            // 0x080e
1063     case GX2_SURFACE_FORMAT_TC_R16_G16_UNORM:         return "GX2_SURFACE_FORMAT_TC_R16_G16_UNORM";         // 0x000f
1064     case GX2_SURFACE_FORMAT_TC_R16_G16_UINT:          return "GX2_SURFACE_FORMAT_TC_R16_G16_UINT";          // 0x010f
1065     case GX2_SURFACE_FORMAT_TC_R16_G16_SNORM:         return "GX2_SURFACE_FORMAT_TC_R16_G16_SNORM";         // 0x020f
1066     case GX2_SURFACE_FORMAT_TC_R16_G16_SINT:          return "GX2_SURFACE_FORMAT_TC_R16_G16_SINT";          // 0x030f
1067     case GX2_SURFACE_FORMAT_TC_R16_G16_FLOAT:         return "GX2_SURFACE_FORMAT_TC_R16_G16_FLOAT";         // 0x0810
1068     case GX2_SURFACE_FORMAT_D_D24_S8_UNORM:           return "GX2_SURFACE_FORMAT_D_D24_S8_UNORM";           // 0x0011
1069 //    case GX2_SURFACE_FORMAT_T_R24_UNORM_X8:           return "GX2_SURFACE_FORMAT_T_R24_UNORM_X8";           // 0x0011
1070     case GX2_SURFACE_FORMAT_T_X24_G8_UINT:            return "GX2_SURFACE_FORMAT_T_X24_G8_UINT";            // 0x0111
1071     case GX2_SURFACE_FORMAT_D_D24_S8_FLOAT:           return "GX2_SURFACE_FORMAT_D_D24_S8_FLOAT";           // 0x0811
1072     case GX2_SURFACE_FORMAT_TC_R11_G11_B10_FLOAT:     return "GX2_SURFACE_FORMAT_TC_R11_G11_B10_FLOAT";     // 0x0816
1073     case GX2_SURFACE_FORMAT_TCS_R10_G10_B10_A2_UNORM: return "GX2_SURFACE_FORMAT_TCS_R10_G10_B10_A2_UNORM"; // 0x0019
1074     case GX2_SURFACE_FORMAT_TC_R10_G10_B10_A2_UINT:   return "GX2_SURFACE_FORMAT_TC_R10_G10_B10_A2_UINT";   // 0x0119
1075     case GX2_SURFACE_FORMAT_TC_R10_G10_B10_A2_SNORM:  return "GX2_SURFACE_FORMAT_TC_R10_G10_B10_A2_SNORM";  // 0x0219
1076     case GX2_SURFACE_FORMAT_TC_R10_G10_B10_A2_SINT:   return "GX2_SURFACE_FORMAT_TC_R10_G10_B10_A2_SINT";   // 0x0319
1077     case GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM:    return "GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM";    // 0x001a
1078     case GX2_SURFACE_FORMAT_TC_R8_G8_B8_A8_UINT:      return "GX2_SURFACE_FORMAT_TC_R8_G8_B8_A8_UINT";      // 0x011a
1079     case GX2_SURFACE_FORMAT_TC_R8_G8_B8_A8_SNORM:     return "GX2_SURFACE_FORMAT_TC_R8_G8_B8_A8_SNORM";     // 0x021a
1080     case GX2_SURFACE_FORMAT_TC_R8_G8_B8_A8_SINT:      return "GX2_SURFACE_FORMAT_TC_R8_G8_B8_A8_SINT";      // 0x031a
1081     case GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB:     return "GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB";     // 0x041a
1082     case GX2_SURFACE_FORMAT_TCS_A2_B10_G10_R10_UNORM: return "GX2_SURFACE_FORMAT_TCS_A2_B10_G10_R10_UNORM"; // 0x001b
1083     case GX2_SURFACE_FORMAT_TC_A2_B10_G10_R10_UINT:   return "GX2_SURFACE_FORMAT_TC_A2_B10_G10_R10_UINT";   // 0x011b
1084     case GX2_SURFACE_FORMAT_D_D32_FLOAT_S8_UINT_X24:  return "GX2_SURFACE_FORMAT_D_D32_FLOAT_S8_UINT_X24";  // 0x081c
1085 //    case GX2_SURFACE_FORMAT_T_R32_FLOAT_X8_X24:       return "GX2_SURFACE_FORMAT_T_R32_FLOAT_X8_X24";       // 0x081c
1086     case GX2_SURFACE_FORMAT_T_X32_G8_UINT_X24:        return "GX2_SURFACE_FORMAT_T_X32_G8_UINT_X24";        // 0x011c
1087     case GX2_SURFACE_FORMAT_TC_R32_G32_UINT:          return "GX2_SURFACE_FORMAT_TC_R32_G32_UINT";          // 0x011d
1088     case GX2_SURFACE_FORMAT_TC_R32_G32_SINT:          return "GX2_SURFACE_FORMAT_TC_R32_G32_SINT";          // 0x031d
1089     case GX2_SURFACE_FORMAT_TC_R32_G32_FLOAT:         return "GX2_SURFACE_FORMAT_TC_R32_G32_FLOAT";         // 0x081e
1090     case GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_UNORM: return "GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_UNORM"; // 0x001f
1091     case GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_UINT:  return "GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_UINT";  // 0x011f
1092     case GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_SNORM: return "GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_SNORM"; // 0x021f
1093     case GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_SINT:  return "GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_SINT";  // 0x031f
1094     case GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_FLOAT: return "GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_FLOAT"; // 0x0820
1095     case GX2_SURFACE_FORMAT_TC_R32_G32_B32_A32_UINT:  return "GX2_SURFACE_FORMAT_TC_R32_G32_B32_A32_UINT";  // 0x0122
1096     case GX2_SURFACE_FORMAT_TC_R32_G32_B32_A32_SINT:  return "GX2_SURFACE_FORMAT_TC_R32_G32_B32_A32_SINT";  // 0x0322
1097     case GX2_SURFACE_FORMAT_TC_R32_G32_B32_A32_FLOAT: return "GX2_SURFACE_FORMAT_TC_R32_G32_B32_A32_FLOAT"; // 0x0823
1098     case GX2_SURFACE_FORMAT_T_BC1_UNORM:              return "GX2_SURFACE_FORMAT_T_BC1_UNORM";              // 0x0031
1099     case GX2_SURFACE_FORMAT_T_BC1_SRGB:               return "GX2_SURFACE_FORMAT_T_BC1_SRGB";               // 0x0431
1100     case GX2_SURFACE_FORMAT_T_BC2_UNORM:              return "GX2_SURFACE_FORMAT_T_BC2_UNORM";              // 0x0032
1101     case GX2_SURFACE_FORMAT_T_BC2_SRGB:               return "GX2_SURFACE_FORMAT_T_BC2_SRGB";               // 0x0432
1102     case GX2_SURFACE_FORMAT_T_BC3_UNORM:              return "GX2_SURFACE_FORMAT_T_BC3_UNORM";              // 0x0033
1103     case GX2_SURFACE_FORMAT_T_BC3_SRGB:               return "GX2_SURFACE_FORMAT_T_BC3_SRGB";               // 0x0433
1104     case GX2_SURFACE_FORMAT_T_BC4_UNORM:              return "GX2_SURFACE_FORMAT_T_BC4_UNORM";              // 0x0034
1105     case GX2_SURFACE_FORMAT_T_BC4_SNORM:              return "GX2_SURFACE_FORMAT_T_BC4_SNORM";              // 0x0234
1106     case GX2_SURFACE_FORMAT_T_BC5_UNORM:              return "GX2_SURFACE_FORMAT_T_BC5_UNORM";              // 0x0035
1107     case GX2_SURFACE_FORMAT_T_BC5_SNORM:              return "GX2_SURFACE_FORMAT_T_BC5_SNORM";              // 0x0235
1108     case GX2_SURFACE_FORMAT_T_NV12_UNORM:             return "GX2_SURFACE_FORMAT_T_NV12_UNORM";             // 0x0081
1109     default:                                          return "invalid format";
1110     }
1111 }
1112 
DEMOGfxBeginGPUTask()1113 DEMOGfxGPUTask* DEMOGfxBeginGPUTask()
1114 {
1115     // Create a new time value, meaning a new key in the list
1116     u64 timeVal = gDemoGfxTimeVal;
1117 
1118     DEMOGfxGPUTask *pTask = (DEMOGfxGPUTask *)DEMOAlloc(sizeof(DEMOGfxGPUTask));
1119     ASSERT(pTask && "Failed to allocate pTask!");
1120 
1121     pTask->pDLPatchLoc = GX2BeginGPUTask();
1122     pTask->timeVal = timeVal;
1123     pTask->next = pgDemoGfxGPUTaskList;
1124     pgDemoGfxGPUTaskList = pTask;
1125 
1126     GX2SubmitUserTimeStamp(gpDemoGfxGPUTs, timeVal, GX2_PIPE_EVENT_TOP, GX2_FALSE);
1127 
1128     return pTask;
1129 }
1130 
DEMOGfxEndGPUTask()1131 void DEMOGfxEndGPUTask()
1132 {
1133     u64 timeVal = gDemoGfxTimeVal | DEMO_GPU_TASK_END_TS;
1134     GX2SubmitUserTimeStamp(gpDemoGfxGPUTs, timeVal, GX2_PIPE_EVENT_TOP, GX2_FALSE);
1135     GX2EndGPUTask(&gDemoGfxGPUFence);
1136 
1137     // Move to next timeval
1138     gDemoGfxTimeVal++;
1139 }
1140 
DEMOGfxInsertGPUTask(void * pDisplayList,u32 byteSize)1141 void DEMOGfxInsertGPUTask(void *pDisplayList, u32 byteSize)
1142 {
1143     DEMOGfxGPUTask *pTask;
1144     u64 timeVal;
1145 
1146     // Set the fence
1147     gDemoGfxGPUFence = 0;
1148     GX2Invalidate(GX2_INVALIDATE_CPU, &gDemoGfxGPUFence, sizeof(u32));
1149 
1150     timeVal = GX2ReadUserTimeStamp(gpDemoGfxGPUTs);
1151 
1152     // If it is a begin Ts
1153     if ((timeVal & DEMO_GPU_TASK_END_TS) == 0)
1154     {
1155         // Look for the patch entry for the next job
1156         timeVal++;
1157     }
1158     else
1159     {
1160         // Try looking up the patch entry after the next one
1161         timeVal = (timeVal & (~DEMO_GPU_TASK_END_TS)) + 2ull;
1162     }
1163 
1164     for (pTask = pgDemoGfxGPUTaskList; pTask != NULL; pTask = pTask->next)
1165     {
1166         if (pTask->timeVal == timeVal)
1167             break;
1168     }
1169     if (pTask)
1170     {
1171         // Read pTask->pDLPatchLoc, pass it to GX2SubmitUserGpuTask
1172         GX2InsertGPUTask((u32*)pTask->pDLPatchLoc, (u32*)pDisplayList, byteSize);
1173     }
1174     else
1175     {
1176         // the gpu is already idle, just call the new task directly
1177         GX2DirectCallDisplayList(pDisplayList, byteSize);
1178     }
1179 
1180     // Release the fence
1181     gDemoGfxGPUFence = 1;
1182     GX2Invalidate(GX2_INVALIDATE_CPU, &gDemoGfxGPUFence, sizeof(u32));
1183 }
1184 
DEMOGfxFreeGPUTask(DEMOGfxGPUTask * pTask)1185 void DEMOGfxFreeGPUTask(DEMOGfxGPUTask *pTask)
1186 {
1187     DEMOGfxGPUTask *pList;
1188 
1189     ASSERT(pgDemoGfxGPUTaskList && "Can't free a GPU Task from an empty list!");
1190     for (pList = pgDemoGfxGPUTaskList; pList != NULL; pList = pList->next)
1191     {
1192         if (pList == pTask)
1193         {
1194             pList = pTask->next;
1195             break;
1196         }
1197     }
1198     DEMOFree(pTask);
1199 }
1200 
DEMOGfxFreeGPUTasks(void)1201 void DEMOGfxFreeGPUTasks(void)
1202 {
1203     DEMOGfxGPUTask *pList, *pNext;
1204 
1205     ASSERT(pgDemoGfxGPUTaskList && "Can't free GPU Tasks from an empty list!");
1206     for (pList = pgDemoGfxGPUTaskList; pList != NULL; pList = pNext)
1207     {
1208         pNext = pList->next;
1209         DEMOFree(pList);
1210     }
1211 }
1212