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 /// demoCapture.c
14 ///
15 /// This is capture code for the demo library.
16 ///
17 ////===========================================================================
18
19 #include <cafe/demo.h>
20 #include <cafe/gx2.h>
21
22 DEMOCaptureDataStore DEMOCaptureData; // global
23 static void* DEMOCaptureDLMem = NULL; //display list memory
24 #define DEMO_DL_SIZE_MAX (8*1024)
25
26 typedef struct _DEMOColor
27 {
28 u8 x, y, z, w;
29 } DEMOColor;
30
31 void _DEMOEncodeTGA(const u8* inData, u8* outData, u32* outByteSize,
32 u32 width, u32 height, u32 pitch, GX2SurfaceFormat format);
33 void _DEMOComputeRunlengths(u32 cols, DEMOColor *pixelrow, s32 *runlength);
34 void _DEMODumpSerial(const u8* data, const u32 byte);
35
36 // GX2_UUENC is a basic 1 character encoding function to make a char printing
37 #define _DEMO_UUENC(c) ((c) ? ((c) & 077) + ' ': '`')
38
_DEMOComputeRunlengths(u32 cols,DEMOColor * pixelrow,s32 * runlength)39 void _DEMOComputeRunlengths(u32 cols, DEMOColor *pixelrow, s32 *runlength)
40 {
41 s32 col, start;
42
43 // Initialize all run lengths to 0. (This is just an error check.)
44 for (col = 0; col < cols; ++col)
45 runlength[col] = 0;
46
47 // Find runs of identical pixels.
48 for ( col = 0; col < cols; ) {
49 start = col;
50 do {
51 ++col;
52 } while ( col < cols &&
53 col - start < 128 &&
54 (pixelrow[col].x == pixelrow[start].x) &&
55 (pixelrow[col].y == pixelrow[start].y) &&
56 (pixelrow[col].z == pixelrow[start].z) &&
57 (pixelrow[col].w == pixelrow[start].w) );
58 runlength[start] = col - start;
59 }
60
61 // Now look for runs of length-1 runs, and turn them into negative runs.
62 for (col = 0; col < cols; )
63 {
64 if (runlength[col] == 1)
65 {
66 start = col;
67 while (col < cols &&
68 col - start < 128 &&
69 (runlength[col] == 1) )
70 {
71 runlength[col] = 0;
72 ++col;
73 }
74 runlength[start] = - ( col - start );
75 } else
76 col += runlength[col];
77 }
78 }
79
_DEMODumpSerial(const u8 * data,const u32 byte)80 void _DEMODumpSerial(const u8* data, const u32 byte)
81 {
82 u32 i, j, idx, leftByte, numLoop;
83 s32 ch;
84 // uuencode allows 6 bits (=63)
85 const u32 maxOneLine = 63;
86 // 63/3*4 = 84 characters
87 char line[90];
88 char *pc;
89
90 idx = 0;
91 numLoop = (u32)(byte / maxOneLine);
92
93 DEMOPrintf("begin 644 GX2Capture.tga\n");
94
95 for(i=0; i<numLoop; i++)
96 {
97 pc = &line[0];
98 *pc++ = '_'; // '_' is 63+32 = 95 in ascii
99 for(j=0; j<maxOneLine/3; j++)
100 {
101 ch = data[3*idx] >> 2;
102 ch = _DEMO_UUENC(ch);
103 *pc++ = ch;
104 ch = ((data[3*idx] << 4) & 060) | ((data[3*idx+1] >> 4) & 017);
105 ch = _DEMO_UUENC(ch);
106 *pc++ = ch;
107 ch = ((data[3*idx+1] << 2) & 074) | ((data[3*idx+2] >> 6) & 03);
108 ch = _DEMO_UUENC(ch);
109 *pc++ = ch;
110 ch = data[3*idx+2] & 077;
111 ch = _DEMO_UUENC(ch);
112 *pc++ = ch;
113 idx++;
114 }
115 *pc++ = '\n';
116 *pc++ = 0;
117 DEMOPrintf("%s", line);
118 }
119
120 leftByte = byte % maxOneLine;
121 if (leftByte > 0) {
122 pc = &line[0];
123 *pc++ = leftByte+32;
124
125 if (leftByte >= 3) {
126 for(j=0; j<leftByte/3; j++)
127 {
128 ch = data[3*idx] >> 2;
129 ch = _DEMO_UUENC(ch);
130 *pc++ = ch;
131 ch = ((data[3*idx] << 4) & 060) | ((data[3*idx+1] >> 4) & 017);
132 ch = _DEMO_UUENC(ch);
133 *pc++ = ch;
134 ch = ((data[3*idx+1] << 2) & 074) | ((data[3*idx+2] >> 6) & 03);
135 ch = _DEMO_UUENC(ch);
136 *pc++ = ch;
137 ch = data[3*idx+2] & 077;
138 ch = _DEMO_UUENC(ch);
139 *pc++ = ch;
140 idx++;
141 }
142 }
143
144 if(leftByte % 3 != 0)
145 {
146 ch = data[3*idx] >> 2;
147 ch = _DEMO_UUENC(ch);
148 *pc++ = ch;
149 if(leftByte % 3 == 2){
150 ch = ((data[3*idx] << 4) & 060) | ((data[3*idx+1] >> 4) & 017);
151 ch = _DEMO_UUENC(ch);
152 *pc++ = ch;
153 ch = ((data[3*idx+1] << 2) & 074);
154 ch = _DEMO_UUENC(ch);
155 *pc++ = ch;
156 ch = 0;
157 ch = _DEMO_UUENC(ch);
158 *pc++ = ch;
159 }else{
160 ch = ((data[3*idx] << 4) & 060);
161 ch = _DEMO_UUENC(ch);
162 *pc++ = ch;
163 ch = 0;
164 ch = _DEMO_UUENC(ch);
165 *pc++ = ch;
166 ch = data[3*idx+2] & 077;
167 ch = _DEMO_UUENC(ch);
168 *pc++ = ch;
169 }
170 }
171 *pc++ = '\n';
172 *pc++ = 0;
173 DEMOPrintf("%s", line);
174 }
175 DEMOPrintf("`\nend\n");
176 }
177
_DEMOEncodeTGA(const u8 * inData,u8 * outData,u32 * outByteSize,u32 width,u32 height,u32 pitch,GX2SurfaceFormat format)178 void _DEMOEncodeTGA(const u8* inData, u8* outData, u32* outByteSize,
179 u32 width, u32 height, u32 pitch, GX2SurfaceFormat format)
180 {
181 u8 *pbuffer;
182 u32 rows = DEMOCaptureData.colorBuffer.surface.height;
183 u32 cols = DEMOCaptureData.colorBuffer.surface.width;
184 s32* runlength;
185 u32 row, col, i, realrow, byte = 0;
186 DEMOColor* buffer;
187
188 DEMOAssert( GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB == format ||
189 GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM == format );
190
191 pbuffer = outData;
192 DEMOAssert(pbuffer);
193
194 // 18-byte TGA header
195 // (16-bit values are written in little-endian order)
196 // TGA Image format codes:
197 // 1: uncompressed, color-mapped 9: RLE-compressed, color-mapped
198 // 2: uncompressed, true-color 10: RLE-compressed, true-color
199 // 3: uncompressed, BW or gray 11: RLE-compressed, BW or gray
200 //
201 *pbuffer++ = 0; // Image ID length (0: no ID)
202 *pbuffer++ = 0; // Color map type (0: no color map)
203 *pbuffer++ = 10; // Image type (10: RLE true-color image)
204 *pbuffer++ = 0; *pbuffer++ = 0; // Color map first index offset (16 bit)
205 *pbuffer++ = 0; *pbuffer++ = 0; // Color map length (16 bit)
206 *pbuffer++ = 0; // Color map bpp
207 *pbuffer++ = 0; *pbuffer++ = 0; // Image X origin (16 bit)
208 *pbuffer++ = 0; *pbuffer++ = 0; // Image Y origin (16 bit)
209 *pbuffer++ = (u8)(width % 256); // Image width lo
210 *pbuffer++ = (u8)(width / 256); // Image width hi
211 *pbuffer++ = (u8)(height % 256); // Image height lo
212 *pbuffer++ = (u8)(height / 256); // Image height hi
213 *pbuffer++ = (u8) 32; // Image pixel bpp (32 bits)
214 *pbuffer++ = (u8) 8; // Image alpha bpp (8 bits), direction code
215
216 buffer = (DEMOColor*)inData;
217
218 // do run length encoding here
219 runlength = DEMOAlloc(sizeof(s32)*cols);
220 DEMOAssert(runlength && "DEMOAlloc failed");
221
222 // write out pixels
223 for( row = 0; row < rows; ++row )
224 {
225 realrow = rows - row - 1;
226 _DEMOComputeRunlengths( cols, &buffer[realrow * pitch], runlength );
227 for( col = 0; col < cols; ){
228 if( runlength[col] > 0 ){
229 // set runlength
230 pbuffer[byte] = (u8)(0x80 + runlength[col] - 1); byte++;
231 // write pixel
232 // TGA expects BGRA order
233 pbuffer[byte] = buffer[realrow * pitch + col].z; byte++;
234 pbuffer[byte] = buffer[realrow * pitch + col].y; byte++;
235 pbuffer[byte] = buffer[realrow * pitch + col].x; byte++;
236 pbuffer[byte] = buffer[realrow * pitch + col].w; byte++;
237 col += runlength[col];
238 }else if( runlength[col] < 0 ){
239 // set runlength
240 pbuffer[byte] = (u8)(- runlength[col] - 1); byte++;
241 // write pixels
242 // TGA expects BGRA order
243 for ( i = 0; i < - runlength[col]; ++i )
244 {
245 pbuffer[byte] = buffer[realrow * pitch + col + i].z; byte++;
246 pbuffer[byte] = buffer[realrow * pitch + col + i].y; byte++;
247 pbuffer[byte] = buffer[realrow * pitch + col + i].x; byte++;
248 pbuffer[byte] = buffer[realrow * pitch + col + i].w; byte++;
249 }
250 col += -runlength[col];
251 }else
252 DEMOAssert(!"Internal error: zero run length");
253 }
254 }
255
256 DEMOFree(runlength);
257
258 *outByteSize = 18+byte;
259 }
260
DEMOCaptureInit(u32 width,u32 height,DEMOCaptureFormat format)261 void DEMOCaptureInit(u32 width, u32 height, DEMOCaptureFormat format)
262 {
263 DEMOCaptureData.captureFormat = format;
264 // Setup render buffer
265 DEMOCaptureData.colorBuffer.surface.dim = GX2_SURFACE_DIM_2D;
266 DEMOCaptureData.colorBuffer.surface.width = width;
267 DEMOCaptureData.colorBuffer.surface.height = height;
268 DEMOCaptureData.colorBuffer.surface.depth = 1;
269 DEMOCaptureData.colorBuffer.surface.numMips = 0;
270 if( SRGB8 == format ){
271 DEMOCaptureData.colorBuffer.surface.format =
272 GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB;
273 } else if( RGBA8 == format ){
274 DEMOCaptureData.colorBuffer.surface.format =
275 GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM;
276 } else {
277 DEMOAssert("Only RGBA8/SRGB8 formats supported at the moment.");
278 }
279
280 DEMOCaptureData.colorBuffer.surface.aa = GX2_AA_MODE_1X;
281 DEMOCaptureData.colorBuffer.surface.use =
282 GX2_SURFACE_USE_COLOR_BUFFER_TEXTURE;
283 DEMOCaptureData.colorBuffer.surface.tileMode = GX2_TILE_MODE_LINEAR_ALIGNED;
284 DEMOCaptureData.colorBuffer.surface.swizzle = 0;
285 DEMOCaptureData.colorBuffer.viewMip = 0;
286 DEMOCaptureData.colorBuffer.viewFirstSlice = 0;
287 DEMOCaptureData.colorBuffer.viewNumSlices = 1;
288 GX2CalcSurfaceSizeAndAlignment(&DEMOCaptureData.colorBuffer.surface);
289 GX2InitColorBufferRegs(&DEMOCaptureData.colorBuffer);
290
291 if (DEMOCaptureData.colorBuffer.surface.imagePtr) {
292 DEMOGfxFreeMEM2(DEMOCaptureData.colorBuffer.surface.imagePtr);
293 }
294 DEMOCaptureData.colorBuffer.surface.imagePtr
295 = DEMOGfxAllocMEM2(DEMOCaptureData.colorBuffer.surface.imageSize,
296 DEMOCaptureData.colorBuffer.surface.alignment);
297 DEMOAssert(DEMOCaptureData.colorBuffer.surface.imagePtr);
298 GX2Invalidate(GX2_INVALIDATE_CPU,
299 DEMOCaptureData.colorBuffer.surface.imagePtr,
300 DEMOCaptureData.colorBuffer.surface.imageSize);
301
302 if (DEMOCaptureData.TGAData) {
303 DEMOFree(DEMOCaptureData.TGAData);
304 }
305 // Worst case scenario is image 1/4 bigger than original
306 // because run length encoding adds up to 1 byte per 4 byte pixel
307 DEMOCaptureData.TGAData = DEMOAlloc((u32)(18+5*width*height));
308 DEMOAssert( DEMOCaptureData.TGAData );
309
310 //Setup Display list support
311 if(DEMOCaptureDLMem == NULL)
312 {
313 DEMOCaptureDLMem = DEMOGfxAllocMEM2(DEMO_DL_SIZE_MAX,
314 GX2_DISPLAY_LIST_ALIGNMENT);
315 DEMOAssert(DEMOCaptureDLMem);
316 }
317 }
318
DEMOCaptureShutdown(void)319 void DEMOCaptureShutdown(void)
320 {
321 if (DEMOCaptureData.colorBuffer.surface.imagePtr) {
322 DEMOGfxFreeMEM2(DEMOCaptureData.colorBuffer.surface.imagePtr);
323 DEMOCaptureData.colorBuffer.surface.imagePtr = NULL;
324 }
325
326 if(DEMOCaptureData.TGAData) {
327 DEMOGfxFreeMEM2(DEMOCaptureData.TGAData);
328 DEMOCaptureData.TGAData = NULL;
329 }
330
331 if(DEMOCaptureDLMem) {
332 DEMOGfxFreeMEM2(DEMOCaptureDLMem);
333 DEMOCaptureDLMem = NULL;
334 }
335 }
336
DEMOCaptureCopyHelper(const GX2Surface * srcSurface,u32 srcMip,u32 srcSlice,GX2Surface * dstSurface,u32 dstMip,u32 dstSlice)337 void DEMOCaptureCopyHelper(
338 const GX2Surface *srcSurface, u32 srcMip, u32 srcSlice,
339 GX2Surface *dstSurface, u32 dstMip, u32 dstSlice)
340 {
341 u32 dlSize;
342 DEMOAssert((DEMOCaptureDLMem != NULL) && "DEMOCaptureInit must be called first");
343
344 // Use Display lists and disable profiling modes - This prevents the profiling
345 // modes from impacting the capture copy.
346 // GX2DrawDone() is always called after using the display list below,
347 // so the memory should be free to reuse again.
348 GX2Invalidate(GX2_INVALIDATE_CPU, DEMOCaptureDLMem, DEMO_DL_SIZE_MAX);
349 GX2BeginDisplayListEx(DEMOCaptureDLMem, DEMO_DL_SIZE_MAX, GX2_FALSE);
350
351 GX2UTSetAndCopySurface(srcSurface, srcMip, srcSlice,
352 dstSurface, dstMip, dstSlice);
353
354 dlSize = GX2EndDisplayList(DEMOCaptureDLMem);
355 ASSERT(dlSize <= DEMO_DL_SIZE_MAX);
356 GX2CallDisplayList(DEMOCaptureDLMem, dlSize);
357 }
358
DEMOCaptureCopy(GX2ColorBuffer * srcBuffer,const char * dstFilePath)359 void DEMOCaptureCopy(GX2ColorBuffer *srcBuffer, const char *dstFilePath)
360 {
361 DEMOAssert(DEMOCaptureData.TGAData != NULL && "DEMOCaptureInit must be called first");
362
363 if (srcBuffer != NULL) {
364
365 GX2Boolean ok;
366
367 // Make sure dest buffer isn't in CPU cache
368 GX2Invalidate(GX2_INVALIDATE_CPU,
369 DEMOCaptureData.colorBuffer.surface.imagePtr,
370 DEMOCaptureData.colorBuffer.surface.imageSize);
371
372 // Grab a copy
373 if (srcBuffer->surface.aa == GX2_AA_MODE_1X) {
374 DEMOCaptureCopyHelper(&srcBuffer->surface,
375 srcBuffer->viewMip,
376 srcBuffer->viewFirstSlice,
377 &DEMOCaptureData.colorBuffer.surface, 0, 0);
378 } else {
379 // AA, so need to resolve into a temporary buffer first
380 GX2Surface tempSurf;
381 tempSurf = srcBuffer->surface;
382 tempSurf.aa = GX2_AA_MODE_1X;
383 GX2CalcSurfaceSizeAndAlignment(&tempSurf);
384 tempSurf.imagePtr = DEMOGfxAllocMEM2(tempSurf.imageSize,
385 tempSurf.alignment);
386 DEMOAssert(tempSurf.imagePtr);
387 GX2ResolveAAColorBuffer(srcBuffer,
388 &tempSurf, 0, 0);
389 DEMOGfxSetContextState();
390 DEMOCaptureCopyHelper(&tempSurf, 0, 0,
391 &DEMOCaptureData.colorBuffer.surface, 0, 0);
392 GX2DrawDone();
393 DEMOGfxFreeMEM2(tempSurf.imagePtr);
394 }
395
396 DEMOGfxSetContextState();
397
398 // Flush out dest caches
399 GX2Invalidate(GX2_INVALIDATE_COLOR_BUFFER,
400 DEMOCaptureData.colorBuffer.surface.imagePtr,
401 DEMOCaptureData.colorBuffer.surface.imageSize);
402
403 // Wait for GPU to finish
404 ok = GX2DrawDone();
405
406 // If DrawDone timed out, image is not valid, so set to 0xff
407 if (!ok) {
408 memset(DEMOCaptureData.colorBuffer.surface.imagePtr, 0xff,
409 DEMOCaptureData.colorBuffer.surface.imageSize);
410 }
411
412 // Convert to TGA format
413 _DEMOEncodeTGA(DEMOCaptureData.colorBuffer.surface.imagePtr,
414 DEMOCaptureData.TGAData,
415 &DEMOCaptureData.TGALength,
416 DEMOCaptureData.colorBuffer.surface.width,
417 DEMOCaptureData.colorBuffer.surface.height,
418 DEMOCaptureData.colorBuffer.surface.pitch,
419 DEMOCaptureData.colorBuffer.surface.format);
420 }
421
422 if(NULL != dstFilePath) {
423 if(*dstFilePath==0)
424 {
425 _DEMODumpSerial(DEMOCaptureData.TGAData, DEMOCaptureData.TGALength);
426 } else {
427 s32 res;
428 DEMOFSFileInfo finfo;
429 res = DEMOFSOpenFileMode(dstFilePath, &finfo, "w");
430 if (res != DEMO_FS_RESULT_OK) {
431 DEMOPrintf("Unable to open file %s for capture output\n", dstFilePath);
432 return;
433 }
434 res = DEMOFSWrite(&finfo, DEMOCaptureData.TGAData, DEMOCaptureData.TGALength);
435 if (res != DEMO_FS_RESULT_OK) {
436 DEMOPrintf("Unable to write to file %s for capture output\n", dstFilePath);
437 return;
438 }
439 res = DEMOFSCloseFile(&finfo);
440 if (res != DEMO_FS_RESULT_OK) {
441 DEMOPrintf("Error closing file %s for capture output\n", dstFilePath);
442 return;
443 }
444 }
445 }
446 }
447