1 /*---------------------------------------------------------------------------*
2 
3   Copyright 2010-2012 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 ///  demoTest.c
14 ///
15 ///     This contains test-related functions for the demos
16 ///
17 ////===========================================================================
18 
19 #include <cafe/demo.h>
20 #include <nn/save.h>
21 #include <nn/act.h>
22 #include <stdio.h>
23 
24 //----------------------------------------------------------------------
25 
26 // Global Data
27 
28 typedef struct _DEMOTestDataStore {
29     u32 count;
30     u32 stopSelect;
31     u32 testSelect;
32     u32 captureSelect;
33     u32 dumpSelect;
34     u32 pm4CaptureSelect;
35     u32 pm4CaptureNumFrames;
36     u32 maxCPUTime;
37     u32 maxGPUTime;
38     char pm4CaptureFilename[1024];
39     int result;
40     char compareFileName[1024];
41     char argv0[1024];
42 } DEMOTestDataStore;
43 
44 DEMOTestDataStore DEMOTestData = { 0, 0, (u32)-1, (u32)-1, };
45 
46 //----------------------------------------------------------------------
47 
48 // Local Data
49 
50 static u64 startCpuTime;
51 static u64 endCpuTime;
52 static u64 startGpuTime;
53 static u64 endGpuTime;
54 static u64* pTimestamp = NULL;
55 
56 //----------------------------------------------------------------------
57 
58 // Utility hash functions
59 
DEMOTestHashCRC16(const void * datap,u32 size)60 u16 DEMOTestHashCRC16(const void *datap, u32 size)
61 {
62     static const u16 crc16_table[16] = {
63         0x0000, 0xCC01, 0xD801, 0x1400,
64         0xF001, 0x3C00, 0x2800, 0xE401,
65         0xA001, 0x6C00, 0x7800, 0xB401,
66         0x5000, 0x9C01, 0x8801, 0x4400,
67     };
68 
69     u32     r = 0;
70     const u8 *p = (const u8 *)datap;
71 
72     while (size--)
73     {
74         u32     data = *p;
75         p++;
76 
77         r = (r >> 4) ^ crc16_table[(r ^ data) & 0xf];
78         data >>= 4;
79         r = (r >> 4) ^ crc16_table[(r ^ data) & 0xf];
80     }
81 
82     return (u16)r;
83 }
84 
DEMOTestHashCRC32(const void * datap,u32 size)85 u32 DEMOTestHashCRC32(const void *datap, u32 size)
86 {
87     static const u32 crc32_table[16] = {
88         0x00000000U, 0x1DB71064U, 0x3B6E20C8U, 0x26D930ACU,
89         0x76DC4190U, 0x6B6B51F4U, 0x4DB26158U, 0x5005713CU,
90         0xEDB88320U, 0xF00F9344U, 0xD6D6A3E8U, 0xCB61B38CU,
91         0x9B64C2B0U, 0x86D3D2D4U, 0xA00AE278U, 0xBDBDF21CU,
92     };
93 
94     u32     r = 0xffffffffU;
95     const u8 *p = (const u8 *)datap;
96 
97     while (size--)
98     {
99         u32     data = *p;
100         p++;
101 
102         r = (r >> 4) ^ crc32_table[(r ^ data) & 0xf];
103         data >>= 4;
104         r = (r >> 4) ^ crc32_table[(r ^ data) & 0xf];
105     }
106 
107     return ~r;
108 }
109 
110 //----------------------------------------------------------------------
111 
112 
DEMOTestInit(int argc,char * argv[])113 void DEMOTestInit(int argc, char *argv[])
114 {
115     int i;
116     char *p;
117 
118     DEMOTestData.count = 0;
119     DEMOTestData.stopSelect = 0xFFFFFFFF;
120     DEMOTestData.testSelect = 0xFFFFFFFF;
121     DEMOTestData.captureSelect = 0xFFFFFFFF;
122     DEMOTestData.dumpSelect = 0xFFFFFFFF;
123     DEMOTestData.pm4CaptureSelect = 0xFFFFFFFF;
124     DEMOTestData.pm4CaptureNumFrames = 1;
125     DEMOTestData.pm4CaptureFilename[0] = '\0';
126     DEMOTestData.maxCPUTime = 0;
127     DEMOTestData.maxGPUTime = 0;
128     DEMOTestData.result = 0;
129     DEMOTestData.compareFileName[0] = '\0';
130     if (argv != NULL) {
131         strcpy(DEMOTestData.argv0, argv[0]); //note this might be different to OSGetArgcArgv(), e.g. under Meta.
132     } else {
133         strcpy(DEMOTestData.argv0, "unknown");
134     }
135 
136 #define SKIP_NON_DIGIT(c) ((c)!=0&&((c)<'0'||(c)>'9'))
137     for (i = 0; i < argc; ++i)
138     {
139         // Simple parameter reading for now
140         p = strstr(argv[i], "STOP_SELECT=");
141         if (p) {
142             DEMOTestData.stopSelect = (u32) atoi(p+strlen("STOP_SELECT="));
143         }
144 
145         p = strstr(argv[i], "TEST_SELECT=");
146         if (p) {
147             DEMOTestData.testSelect = (u32) atoi(p+strlen("TEST_SELECT="));
148             FSInit(); // Need to initialize FS before SAVEInit().
149             SAVEInit();
150             SAVEStatus status = SAVEInitSaveDir(ACT_SLOT_NO_COMMON);
151             if (status != SAVE_STATUS_OK)
152             {
153                 OSHalt("Failed to make common save directory for tests.\n");
154             }
155         }
156 
157         p = strstr(argv[i], "CAPTURE_SELECT=");
158         if (p) {
159             DEMOTestData.captureSelect = (u32) atoi(p+strlen("CAPTURE_SELECT="));
160         }
161 
162         p = strstr(argv[i], "GX2R_DUMP_SELECT=");
163         if (p) {
164             DEMOTestData.dumpSelect = (u32) atoi(p+strlen("GX2R_DUMP_SELECT="));
165         }
166 
167         p = strstr(argv[i], "PM4_SELECT=");
168         if (p) {
169             u32 endFrame=0;
170             u32 numParams = sscanf(p+strlen("PM4_SELECT="), "%d-%d", &DEMOTestData.pm4CaptureSelect, &endFrame);
171             DEMOTestData.pm4CaptureNumFrames = (numParams==2) ? endFrame-DEMOTestData.pm4CaptureSelect+1 : 1;
172         }
173 
174         p = strstr(argv[i], "PM4_FILE=");
175         if (p) {
176             strcpy(DEMOTestData.pm4CaptureFilename, p+strlen("PM4_FILE="));
177             // replace EOL or ',' with terminating 0
178             p = DEMOTestData.pm4CaptureFilename;
179             for( ; *p != '\n' && *p != '\r' && *p != ',' && *p != 0; p++ ) {}
180             *p = 0;
181         }
182 
183         p = strstr(argv[i], "MAX_CPU_TIME=");
184         if (p) {
185             DEMOTestData.maxCPUTime = (u32) atoi(p+strlen("MAX_CPU_TIME="));
186         }
187 
188         p = strstr(argv[i], "MAX_GPU_TIME=");
189         if (p) {
190             DEMOTestData.maxGPUTime = (u32) atoi(p+strlen("MAX_GPU_TIME="));
191         }
192 
193         p = strstr(argv[i], "COMPARE_FILE=");
194         if (p) {
195             strcpy(DEMOTestData.compareFileName, p+strlen("COMPARE_FILE="));
196             // replace EOL or ',' with terminating 0
197             p = DEMOTestData.compareFileName;
198             for( ; *p != '\n' && *p != '\r' && *p != ',' && *p != 0; p++ ) {}
199             *p = 0;
200         }
201     }
202 
203     if (DEMOTestData.pm4CaptureSelect != 0xFFFFFFFF &&
204         DEMOTestData.pm4CaptureFilename[0]=='\0')
205     {
206         // if we asked for a PM4 capture but didn't specify a filename, default to argv[0].4mp
207         char baseName[256]="";
208         strcpy(baseName, argv[0]);
209         char* dot=NULL;
210         if (dot = strrchr(baseName, '.')) *dot = 0;
211         strcat(baseName, ".4mp");
212         strcpy(DEMOTestData.pm4CaptureFilename, baseName);
213     }
214 }
215 
DEMOTestShutdown(void)216 void DEMOTestShutdown(void)
217 {
218     DEMOCaptureShutdown();
219 }
220 
DEMOTestCheck(void)221 void DEMOTestCheck(void)
222 {
223     // Do this even on first frame. Treat 1 as the first frame number to appear consistent with other checks,
224     // which are done at the top of the following frame
225     if (DEMOTestData.count+1 == DEMOTestData.pm4CaptureSelect)
226     {
227         char str[1024]="";
228 
229         OSCalendarTime td;
230         OSTicksToCalendarTime(OSGetTime(), &td);
231 
232         sprintf(str, "DEMO: start PM4 capture of %s frame %d-%d to file \"%s\" at %4d-%02d-%02d %02d:%02d:%02d",
233                     DEMOTestData.argv0, DEMOTestData.pm4CaptureSelect, DEMOTestData.pm4CaptureSelect+DEMOTestData.pm4CaptureNumFrames-1,
234                     DEMOTestData.pm4CaptureFilename,
235                     td.year, td.mon+1, td.mday, td.hour, td.min, td.sec);
236         // Print it to the console
237         OSReport(str); OSReport("\n");
238 
239         // Start the capture
240         GX2DebugCaptureStart(DEMOTestData.pm4CaptureFilename, GX2_DEBUG_CAPTURE_DEFAULT);
241 
242         // Add the info string to the capture itself as a nop tag
243         GX2UTDebugTagComment(str);
244 
245         //GPUDB
246         // We need to start capture with a context state, to bring in any initial state settings.
247         // This could potentially be omitted if all relevant state is explicitly set up within the capture bracket.
248         DEMOGfxSetContextState();
249     }
250 
251     if (DEMOTestData.count > 0) {
252         // Do this first so we don't get the screen capture code in the PM4 capture
253         if (DEMOTestData.count == DEMOTestData.pm4CaptureSelect+DEMOTestData.pm4CaptureNumFrames-1)
254         {
255             char str[1024]="";
256             sprintf(str, "DEMO: end PM4 capture at frame %d to file \"%s\"", DEMOTestData.count, DEMOTestData.pm4CaptureFilename);
257 
258             GX2UTDebugTagComment(str);
259             OSReport(str); OSReport("\n");
260 
261             GX2DebugCaptureEnd(GX2_DEBUG_CAPTURE_DEFAULT);
262         }
263         if (DEMOTestData.count == DEMOTestData.testSelect) {
264             DEMOTestCompare();
265             DEMOStopRunning();
266         }
267         if (DEMOTestData.count == DEMOTestData.captureSelect) {
268             DEMOTestCapture();
269             DEMOStopRunning();
270         }
271         if (DEMOTestData.count == DEMOTestData.stopSelect) {
272             DEMOStopRunning();
273         }
274         if (DEMOTestData.count == DEMOTestData.dumpSelect) {
275             GX2TempDumpResources();
276         }
277     }
278     DEMOTestData.count++;
279 }
280 
281 void _DEMODumpSerial(const u8 *, const u32);
282 
DEMOTestCompare(void)283 void DEMOTestCompare(void)
284 {
285     u32 cmpLen;
286     u8 *cmpBuf;
287     u32 i;
288     GX2Boolean testPassed;
289 
290     DEMOCaptureInit(DEMOColorBuffer.surface.width, DEMOColorBuffer.surface.height,
291                     (DEMOColorBuffer.surface.format == GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM) ? RGBA8 : SRGB8);
292     DEMOCaptureCopy(&DEMOColorBuffer, NULL);
293     DEMOPrintf("Capture CRC=%08x\n", DEMOTestHashCRC32(DEMOCaptureData.TGAData, DEMOCaptureData.TGALength));
294 
295     cmpBuf = (u8 *) DEMOGfxLoadAssetFile(DEMOTestData.compareFileName, &cmpLen);
296     DEMOPrintf("Compare CRC=%08x\n", DEMOTestHashCRC32(cmpBuf, cmpLen));
297 
298     // A test is considered to have passed if the image captured from the
299     // color buffer matches the "golden" image. In the event that the TGA
300     // compressed image lengths differ, or bytes within the image differ,
301     // consider this test to have failed.
302     testPassed = (cmpLen == DEMOCaptureData.TGALength) ? GX2_TRUE : GX2_FALSE;
303 
304     if (GX2_FALSE != testPassed) {
305 
306         for(i=0; i<cmpLen; i++) {
307             if (cmpBuf[i] != DEMOCaptureData.TGAData[i]) {
308 
309                 testPassed = GX2_FALSE;
310                 break;
311             }
312         }
313     }
314 
315     DEMOTestData.result |= (int) !testPassed;
316 
317     if (GX2_FALSE == testPassed) {
318 
319         // Before printing a failure message, output the TGA file.
320         DEMOCaptureCopy(NULL, "/vol/save/common/GX2Capture.tga");
321 
322         DEMOPrintf("Test result: FAIL\n");
323 
324     } else {
325 
326         DEMOPrintf("Test result: PASS\n");
327     }
328 
329     DEMOFree(cmpBuf);
330 }
331 
DEMOTestCapture(void)332 void DEMOTestCapture(void)
333 {
334     DEMOCaptureInit(DEMOColorBuffer.surface.width, DEMOColorBuffer.surface.height,
335                     (DEMOColorBuffer.surface.format == GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM) ? RGBA8 : SRGB8);
336     DEMOCaptureCopy(&DEMOColorBuffer, "/vol/save/common/GX2Capture.tga");
337     DEMOPrintf("DEMO_CAPTURE_FINISH\n");
338     DEMOPrintf("Capture CRC=%08x\n", DEMOTestHashCRC32(DEMOCaptureData.TGAData, DEMOCaptureData.TGALength));
339 }
340 
DEMOTestResult()341 int DEMOTestResult()
342 {
343     return DEMOTestData.result;
344 }
345 
DEMOTestFrame()346 u32 DEMOTestFrame()
347 {
348     return DEMOTestData.count;
349 }
350 
351 //----------------------------------------------------------------------
352 // Perf Test util
353 
_DEMOTestGpuTicksToMicroseconds(u64 tick)354 inline s64 _DEMOTestGpuTicksToMicroseconds(u64 tick){ return (s64)(tick / (f64)(GX2_TOP_BOTTOM_CLOCK_CYCLES / 1000000)); }
355 
DEMOTestCheckPerfBegin(void)356 void DEMOTestCheckPerfBegin(void)
357 {
358     if (DEMOTestData.testSelect == 0xFFFFFFFF)
359         return;
360 
361     if (DEMOTestData.count == DEMOTestData.testSelect) {
362         if(DEMOGfxIsRunning()){
363             // Get Gpu Time
364             pTimestamp = (u64*)DEMOGfxAllocMEM2(sizeof(u64) * 2, 8);
365             DEMOAssert(pTimestamp);
366             GX2Invalidate(GX2_INVALIDATE_CPU, pTimestamp, 8);
367             GX2Flush();
368 
369             // Start timer when first command buffer is flushed
370             GX2SampleTopGPUCycle(&pTimestamp[0]);
371         }
372         // Get Cpu Time
373         startCpuTime = OSGetTime();
374     }
375 }
376 
DEMOTestCheckPerfEnd(void)377 void DEMOTestCheckPerfEnd(void)
378 {
379     if (DEMOTestData.testSelect == 0xFFFFFFFF)
380         return;
381 
382     if (DEMOTestData.count == DEMOTestData.testSelect) {
383         // Get Cpu Time
384         endCpuTime = OSGetTime();
385         DEMOPrintf("Demo CPU Time: %0.f usec\n", (f32)OSTicksToMicroseconds(endCpuTime - startCpuTime));
386 
387         if(DEMOGfxIsRunning()){
388             // Get Gpu time
389             GX2SampleBottomGPUCycle(&pTimestamp[1]);
390             GX2DrawDone();
391             endGpuTime = pTimestamp[1];
392             startGpuTime = pTimestamp[0];
393             DEMOPrintf("Demo GPU Time: %0.f usec\n", (f32)_DEMOTestGpuTicksToMicroseconds(endGpuTime - startGpuTime));
394             DEMOGfxFreeMEM2(pTimestamp);
395         }
396 
397         if (DEMOTestData.maxCPUTime > 0) {
398             // Verify that the CPU time was no more than the max that was set
399             DEMOPrintf("Maximum CPU Time: %d usec\n", DEMOTestData.maxCPUTime);
400             if ((f32)DEMOTestData.maxCPUTime < (f32)OSTicksToMicroseconds(endCpuTime - startCpuTime)) {
401                 DEMOTestData.result = 1;
402                 DEMOPrintf("Test result: FAIL\n");
403                 return;
404             }
405         }
406 
407         if (DEMOTestData.maxGPUTime > 0) {
408             // Verify that the GPU time was no more than the max that was set
409             DEMOPrintf("Maximum GPU Time: %d usec\n", DEMOTestData.maxGPUTime);
410             if ((f32)DEMOTestData.maxGPUTime < (f32)_DEMOTestGpuTicksToMicroseconds(endGpuTime - startGpuTime)) {
411                 DEMOTestData.result = 1;
412                 DEMOPrintf("Test result: FAIL\n");
413                 return;
414             }
415         }
416     }
417 }
418 
419