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