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