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