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