/*---------------------------------------------------------------------------* Project: Dolphin/Revolution gx demo File: fill-perf.c Copyright 1998-2006 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. Fill-rate performance test *---------------------------------------------------------------------------*/ #include #include #include #include /*---------------------------------------------------------------------------* Type definitions *---------------------------------------------------------------------------*/ // statistic data display style typedef enum { STAT_TL, // top left STAT_BL, // bottom left STAT_TLD, // double-size font, top-left STAT_BLD, // double-size font, bottom-left STAT_IO // dump using OSReport } StatDispMode; // statistic types typedef enum { STAT_GP0, // GXReadGP0Metric STAT_GP1, // GXReadGP1Metric STAT_MEM, // GXReadMemMetric STAT_PIX, // GXReadPixMetric STAT_VC, // GXReadVCacheMetric STAT_FR, // Fill rate calc STAT_TBW, // Texture bandwidth calc STAT_TBP, // Texture B/pixel STAT_MYC, // print out user-computed count STAT_MYR // print out user-computed rate (stat/count) } StatType; // used as "stat" argument when stat_type == STAT_MEM typedef enum { STAT_MEM_CP, // GXReadMemMetric(CP Req.) STAT_MEM_TC, // GXReadMemMetric(TC Req.) STAT_MEM_CPUR, // GXReadMemMetric(CPU Rd Req.) STAT_MEM_CPUW, // GXReadMemMetric(CPU Wr Req.) STAT_MEM_DSP, // GXReadMemMetric(DSP Req.) STAT_MEM_IO, // GXReadMemMetric(IO Req.) STAT_MEM_VI, // GXReadMemMetric(VI Req.) STAT_MEM_PE, // GXReadMemMetric(PE Req.) STAT_MEM_RF, // GXReadMemMetric(RF Req.) STAT_MEM_FI // GXReadMemMetric(FI Req.) } MemStatArg; // used as "stat" argument when stat_type == STAT_PIX typedef enum { STAT_PIX_TI, // GXReadPixMetric(Top Pixel In) STAT_PIX_TO, // GXReadPixMetric(Top Pixel Out) STAT_PIX_BI, // GXReadPixMetric(Bottom Pixel In) STAT_PIX_BO, // GXReadPixMetric(Bottom Pixel Out) STAT_PIX_CI, // GXReadPixMetric(Color Pixel In) STAT_PIX_CC // GXReadPixMetric(Copy Clocks) } PixStatArg; // used as "stat" argument when stat_type == STAT_VC typedef enum { STAT_VC_CHK, // GXReadVCacheMetric(Check) STAT_VC_MISS, // GXReadVCacheMetric(Miss) STAT_VC_STALL // GXReadVCacheMetric(Stall) } VcStatArg; typedef struct { char text[48]; // 8 x 48 = 386 pixels StatType stat_type; // statics type u32 stat; // metric to measure u32 count; // count returned from metric function } StatObj; /*---------------------------------------------------------------------------* Forward references *---------------------------------------------------------------------------*/ void main ( void ); static void CameraInit ( void ); static void ViewInit ( void ); static void DrawInit ( void ); static void InitDrawState ( void ); static void DrawTick ( void ); static void AnimTick ( void ); static void MakeModelMtx ( Vec xAxis, Vec yAxis, Vec zAxis, Mtx m ); static void PrintIntro ( void ); static void DrawCubeIndx ( void ); static void DrawCubeDirect ( void ); static void DrawArrow ( void ); static void init_rand ( void ); static u32 lrand ( void ); static f32 frand ( void ); static GXTexRegion* MyTexRegionCallback( const GXTexObj* texObj, GXTexMapID mapID ); static void PrintStats ( void ); static void UpdateStats ( GXBool inc ); static void WriteStats ( GXBool update ); static void SetStats ( StatObj* stat, u32 nstats, StatDispMode disp ); /*---------------------------------------------------------------------------* Defines *---------------------------------------------------------------------------*/ #define INDEXED_DATA 1 // comment out for direct data #define TCACHE_SZ GX_TEXCACHE_32K #define NUM_REGIONS 8 // number of caches #define NUM_TEXOBJS 9 // for 8 tev stages, plus 1 to swap between #define MAX_TEV 8 // for this test #define NUM_ROTMATS 10 // just because #define STAT_SIZE (sizeof(MyStats) / sizeof(StatObj)) #define SIDE 50.0F /*---------------------------------------------------------------------------* Global variables *---------------------------------------------------------------------------*/ // // Cube data // f32 Vert[] ATTRIBUTE_ALIGN(32) = { -SIDE, SIDE, -SIDE, //0 -SIDE, SIDE, SIDE, //1 -SIDE, -SIDE, SIDE, //2 -SIDE, -SIDE, -SIDE, //3 SIDE, SIDE, -SIDE, //4 SIDE, -SIDE, -SIDE, //5 SIDE, -SIDE, SIDE, //6 SIDE, SIDE, SIDE, //7 0, 0, -SIDE, //8 SIDE/4, 0, 0, //9 -SIDE/4, 0, 0, //10 0, SIDE/4, 0 //11 }; f32 Norm[] ATTRIBUTE_ALIGN(32) = { -1.0F, 0.0F, 0.0F, //0 0.0F, -1.0F, 0.0F, //1 0.0F, 0.0F, -1.0F, //2 1.0F, 0.0F, 0.0F, //3 0.0F, 1.0F, 0.0F, //4 0.0F, 0.0F, 1.0F //5 }; f32 TexCoord[] ATTRIBUTE_ALIGN(32) = { 0.0F, 0.0F, //0 1.0F, 0.0F, //1 1.0F, 1.0F, //2 0.0F, 1.0F, //3 }; u32 MyColors[] ATTRIBUTE_ALIGN(32) = { 0xff0000ff, // red 0x00ff00ff, // green 0x0000ffff, // blue 0xffff00ff, // yellow 0xff00ffff, // magenta 0x00ffffff // cyan }; // // Test structure // typedef struct { u32 numx; // number of cubes in x direction u32 numy; // number of cubes in y direction u32 numz; // number of cubes in z direction f32 dx; // dist between cubes in x direction f32 dy; // dist between cubes in y direction f32 dz; // dist between cubes in z direction u8 ntev; // number of textures per cube (num tev stages) u32 nc_tx; // number of cubes per texture change u32 tcb; // texture region allocator callback u32 tci; // invalidate tex region on each GXLoadTexObj when tcb=1 f32 mods; // model scale f32 rot; // model rotation f32 dr; // model rotation speed Vec axis[NUM_ROTMATS]; // random rotation axis, initialized by DrawInit } TestData; static TestData testData; // // Statistics // // StatObj MyStats1[] = { // General Performance { "GP Clocks [1/60]..", STAT_GP0, GX_PERF0_CLOCKS, 0 }, { "Vertices..........", STAT_GP1, GX_PERF1_VERTICES, 0 }, { "Fill Rate [MP/Sec]", STAT_FR, GX_PERF0_NONE, 0 }, { "TextureBW [MB/Sec]", STAT_TBW, GX_PERF0_NONE, 0 }, { "TexB/pix..........", STAT_TBP, GX_PERF0_NONE, 0 }, }; StatObj MyStats2[] = { // Texture Performance Detail { "Texels.........", STAT_GP1, GX_PERF1_TEXELS, 0 }, { "Tex 1-2 Lines..", STAT_GP1, GX_PERF1_TC_CHECK1_2, 0 }, { "Tex 3-4 Lines..", STAT_GP1, GX_PERF1_TC_CHECK3_4, 0 }, { "Tex 5-6 Lines..", STAT_GP1, GX_PERF1_TC_CHECK5_6, 0 }, { "Tex 7-8 Lines..", STAT_GP1, GX_PERF1_TC_CHECK7_8, 0 }, { "Tex Cache Miss.", STAT_GP1, GX_PERF1_TC_MISS, 0 }, { "Total Checks...", STAT_MYC, GX_PERF1_NONE, 0 }, { "Tex$ Miss Rate.", STAT_MYR, GX_PERF1_NONE, 0 }, }; StatObj MyStats3[] = { // Pixel Performance Detail { "Pixels Entered.", STAT_PIX, STAT_PIX_TI, 0 }, { "Pixels Z Passed", STAT_PIX, STAT_PIX_TO, 0 }, { "Pixels Blended.", STAT_PIX, STAT_PIX_CI, 0 }, { "Quads w>0 Cvg..", STAT_GP0, GX_PERF0_QUAD_NON0CVG, 0 }, { "Quads w/1 Cvg..", STAT_GP0, GX_PERF0_QUAD_1CVG, 0 }, { "Quads w/2 Cvg..", STAT_GP0, GX_PERF0_QUAD_2CVG, 0 }, { "Quads w/3 Cvg..", STAT_GP0, GX_PERF0_QUAD_3CVG, 0 }, { "Quads w/4 Cvg..", STAT_GP0, GX_PERF0_QUAD_4CVG, 0 }, }; StatObj MyStats4[] = { // Memory Access Detail { "CP Reqs........", STAT_MEM, STAT_MEM_CP, 0 }, { "TX Cache Reqs..", STAT_MEM, STAT_MEM_TC, 0 }, { "CPU Rd Reqs....", STAT_MEM, STAT_MEM_CPUR, 0 }, { "CPU Wr Reqs....", STAT_MEM, STAT_MEM_CPUW, 0 }, { "DSP Reqs.......", STAT_MEM, STAT_MEM_DSP, 0 }, { "IO Reqs........", STAT_MEM, STAT_MEM_IO, 0 }, { "VI Reqs........", STAT_MEM, STAT_MEM_VI, 0 }, { "PE Reqs........", STAT_MEM, STAT_MEM_PE, 0 }, { "Refresh Reqs...", STAT_MEM, STAT_MEM_RF, 0 }, { "Forced Idle....", STAT_MEM, STAT_MEM_FI, 0 }, }; StatObj MyStats5[] = { // Current Setting { "Num Of Cubes...", STAT_MYC, 0, 0 }, { "Cubes/Texture..", STAT_MYC, 0, 0 }, { "Num TevStages..", STAT_MYC, 0, 0 }, }; #define STAT_MENU0 0 #define STAT_MENU1 (sizeof(MyStats1)/sizeof(StatObj)) #define STAT_MENU2 (sizeof(MyStats2)/sizeof(StatObj)) #define STAT_MENU3 (sizeof(MyStats3)/sizeof(StatObj)) #define STAT_MENU4 (sizeof(MyStats4)/sizeof(StatObj)) #define STAT_MENU5 (sizeof(MyStats5)/sizeof(StatObj)) static Mtx v, mv; static u32 animMode = 0; static u32 showStats = 1; // show fill rate by default static u32 stopAnim = 0; static void (*testDraw)() = NULL; // function pointer for draw routine static GXTexObj texObj[NUM_TEXOBJS]; // texture object static GXTexRegion MyTexRegions[NUM_REGIONS]; // cache regions static GXTexRegionCallback oldCallback; static GXLightObj MyLight; // light object static Vec CamX = {1.0F, 0.0F, 0.0F}; static Vec CamY = {0.0F, 1.0F, 0.0F}; static Vec CamZ = {0.0F, 0.0F, 1.0F}; static f32 CamS = 100.0F; // camera zoom factor static OSStopwatch myTotTime; // For stats static GXBool StatEnable = GX_FALSE; static StatObj* Stat = NULL; static u32 StatIndx = 0; static u32 StatMaxIndx = 0; static u32 StatClocks = 0; static u32 StatDisp = 0; static u32 StatStrLen = 0; // other statistics counts that can be counted in parallel static u32 topPixIn, topPixOut; static u32 botPixIn, botPixOut; static u32 clrPixIn, copyClks; static u32 vcCheck, vcMiss, vcStall; static u32 cpReq, tcReq, cpuRdReq, cpuWrReq, dspReq, ioReq, viReq, peReq, rfReq, fiReq; /*---------------------------------------------------------------------------* Application main loop *---------------------------------------------------------------------------*/ void main ( void ) { DEMOInit(NULL); DrawInit(); PrintIntro(); OSInitStopwatch(&myTotTime, "myTotTime"); OSResetStopwatch(&myTotTime); testDraw = DrawCubeIndx; while (!(DEMOPadGetButton(0) & PAD_BUTTON_MENU)) { DEMOPadRead(); AnimTick(); InitDrawState(); CameraInit(); OSStartStopwatch(&myTotTime); DEMOBeforeRender(); DrawTick(); if ( showStats == 2 ) { // total check count MyStats2[6].count = MyStats2[1].count + MyStats2[2].count + MyStats2[3].count + MyStats2[4].count; // compute miss rate MyStats2[7].stat = MyStats2[5].count; // misses MyStats2[7].count = MyStats2[6].count; // total checks } else if ( showStats == 5 ) { MyStats5[0].count = testData.numx * testData.numy * testData.numz; MyStats5[1].count = testData.nc_tx; MyStats5[2].count = testData.ntev; } GXDrawDone(); OSStopStopwatch(&myTotTime); if ( StatEnable ) { UpdateStats(GX_TRUE); PrintStats(); GXDrawDone(); UpdateStats(GX_FALSE); } DEMODoneRender(); } OSHalt("End of test"); } /*---------------------------------------------------------------------------* Functions *---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------* Name: CameraInit Description: Initialize the projection matrix and load into hardware. Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void CameraInit ( void ) { Mtx44 p; MTXFrustum(p, .24F * CamS,-.24F * CamS, -.32F * CamS, .32F * CamS, .5F * CamS, 40.0F * CamS); GXSetProjection(p, GX_PERSPECTIVE); ViewInit(); } /*---------------------------------------------------------------------------* Name: ViewInit Description: Initialize the view matrix. Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void ViewInit ( void ) { Mtx trans; MakeModelMtx(CamX, CamY, CamZ, v); // Make a new view matrix MTXTranspose(v, v); MTXTrans(trans, 0.0F, 0.0F, -8.0F * CamS); MTXConcat(trans, v, v); } /*---------------------------------------------------------------------------* Name: DrawInit Description: Initialize one-time state settings Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void DrawInit( void ) { u32 i; GXColor color = {0, 255, 255, 255}; TPLPalettePtr tpl = 0; // texture palette // // Initialize test structure // testData.numx = 1; testData.numy = 1; testData.numz = 1; testData.nc_tx = testData.numx * testData.numz; testData.tcb = 0; // default texregion callback testData.tci = 0; // don't invalidate tex cache regions testData.dx = 2*SIDE + 10.0F; testData.dy = 2*SIDE + 10.0F; testData.dz = -(2*SIDE + 10.0F); testData.ntev = 1; // num textures testData.mods = 1.0F; // model scale testData.rot = 0.0F; // model rotation testData.dr = 1.0F; // rotation speed SetStats(MyStats1, STAT_MENU1, STAT_TLD); // init texture regions for ( i = 0 ; i < NUM_REGIONS ; i++ ) { // The region is used as a 32K cache. GXInitTexCacheRegion( &MyTexRegions[i], GX_FALSE, // 32b mipmap 0x00000 + i * 0x08000, // tmem_even TCACHE_SZ, // size_even 0x80000 + i * 0x08000, // tmem_odd TCACHE_SZ ); // size_odd } // initialize oldCallback to default callback oldCallback = GXSetTexRegionCallback(MyTexRegionCallback); GXSetTexRegionCallback(oldCallback); // model axis init_rand(); for (i = 0; i < NUM_ROTMATS; i++) { // make random axis testData.axis[i].x = frand(); testData.axis[i].y = frand(); testData.axis[i].z = frand(); VECNormalize(&testData.axis[i], &testData.axis[i]); } GXInitLightPos(&MyLight, 0.0F, 0.0F, 0.0F); GXInitLightColor(&MyLight, color); // Load the texture palette TPLGetPalette(&tpl, "gxTests/tex-07.tpl"); // Initialize a texture object to contain the correct texture for (i = 0; i < NUM_TEXOBJS; i++) { OSReport("Reading tex obj %d\n", i); TPLGetGXTexObjFromPalette(tpl, &texObj[i], i); GXInitTexObjUserData(&texObj[i], (void*)i); } InitDrawState(); GXDrawDone(); // make sure state is in GP } /*---------------------------------------------------------------------------* Name: InitDrawState Description: Sets drawing state that gets destroyed by DEMOPrintf when the statistics are printed. Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void InitDrawState( void ) { GXColor color = {0, 255, 0, 255}; GXSetCurrentMtx(0); GXSetCullMode(GX_CULL_BACK); GXLoadLightObjImm(&MyLight, GX_LIGHT0); GXSetChanMatColor(GX_COLOR0A0, color); GXSetChanCtrl( GX_COLOR0A0, GX_FALSE, // enable channel GX_SRC_REG, // amb source GX_SRC_VTX, // mat source GX_LIGHT_NULL, // light mask GX_DF_NONE, // diffuse function GX_AF_NONE); // Load the texture object; tex0 is used in stage 0 GXSetNumChans(1); GXSetNumTexGens(1); GXSetNumTevStages(testData.ntev); GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL); GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP1, GX_COLOR_NULL); GXSetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD0, GX_TEXMAP2, GX_COLOR_NULL); GXSetTevOrder(GX_TEVSTAGE3, GX_TEXCOORD0, GX_TEXMAP3, GX_COLOR_NULL); GXSetTevOrder(GX_TEVSTAGE4, GX_TEXCOORD0, GX_TEXMAP4, GX_COLOR_NULL); GXSetTevOrder(GX_TEVSTAGE5, GX_TEXCOORD0, GX_TEXMAP5, GX_COLOR_NULL); GXSetTevOrder(GX_TEVSTAGE6, GX_TEXCOORD0, GX_TEXMAP6, GX_COLOR_NULL); GXSetTevOrder(GX_TEVSTAGE7, GX_TEXCOORD0, GX_TEXMAP7, GX_COLOR_NULL); GXSetTevOp(GX_TEVSTAGE0, GX_REPLACE); GXSetTevOp(GX_TEVSTAGE1, GX_REPLACE); GXSetTevOp(GX_TEVSTAGE2, GX_REPLACE); GXSetTevOp(GX_TEVSTAGE3, GX_REPLACE); GXSetTevOp(GX_TEVSTAGE4, GX_REPLACE); GXSetTevOp(GX_TEVSTAGE5, GX_REPLACE); GXSetTevOp(GX_TEVSTAGE6, GX_REPLACE); GXSetTevOp(GX_TEVSTAGE7, GX_REPLACE); GXSetBlendMode(GX_BM_BLEND, GX_BL_ONE, GX_BL_ZERO, GX_LO_CLEAR); GXSetZMode(GX_ENABLE, GX_LEQUAL, GX_ENABLE); GXClearVtxDesc(); #ifdef INDEXED_DATA //OSReport("Using indexed data\n"); GXSetVtxDesc(GX_VA_POS, GX_INDEX16); GXSetVtxDesc(GX_VA_CLR0, GX_INDEX8); GXSetVtxDesc(GX_VA_TEX0, GX_INDEX8); #else //OSReport("Using direct data\n"); GXSetVtxDesc(GX_VA_POS, GX_DIRECT); GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT); GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT); #endif // INDEXED_DATA GXSetArray(GX_VA_POS, Vert, sizeof(f32)*3); GXSetArray(GX_VA_CLR0, MyColors, sizeof(u32)); GXSetArray(GX_VA_TEX0, TexCoord, sizeof(f32)*2); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); } /*---------------------------------------------------------------------------* Name: DrawTick Description: Draws spheres Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void DrawTick( void ) { u32 i, j, k, cnt; // indices u32 tx; // offset of texture Mtx m, t; f32 x, z; // for now do only 2-D grid // center grid on 0, 0, 0 z = 0.0F - ((testData.numz / 2) * testData.dz); for (cnt = i = 0; i < testData.numz; i++) { x = 0.0F - ((testData.numx / 2) * testData.dx); for (j = 0; j < testData.numx; j++, cnt++) { // Set texture state every n cubes if ((cnt % testData.nc_tx) == 0) { tx = ((cnt / testData.nc_tx) & 1) * (NUM_TEXOBJS - 1); for (k = 0; k < testData.ntev; k++) { if (tx) GXLoadTexObj(&texObj[tx], (GXTexMapID)k); else GXLoadTexObj(&texObj[k], (GXTexMapID)k); } } // build matrix MTXScale(m, testData.mods, testData.mods, testData.mods); MTXRotAxisDeg(t, &testData.axis[cnt%NUM_ROTMATS], testData.rot); MTXConcat(t, m, m); // scale, rotate MTXTrans(t, x, 0.0F, z); MTXConcat(t, m, m); // translate MTXConcat(v, m, mv); // concat view matrix GXLoadPosMtxImm(mv, 0); GXLoadNrmMtxImm(mv, 0); testDraw(); x += testData.dx; } z += testData.dz; } // draw arrow on top-center MTXScale(m, testData.mods, testData.mods, testData.mods); MTXTrans(t, 0.0F, -100.0F*testData.mods, 0.0F); MTXConcat(t, m, m); // scale, translate MTXConcat(v, m, mv); // concat view matrix GXLoadPosMtxImm(mv, 0); DrawArrow(); } /*---------------------------------------------------------------------------* Name: AnimTick Description: Animates the camera and object based on the joystick's state. Arguments: v view matrix m model matrix Returns: none *---------------------------------------------------------------------------*/ static void AnimTick ( void ) { Mtx rot; Vec temp; Vec yAxis = {0.0F, 1.0F, 0.0F}; f32 dot; // Get controller status through DEMOPad library u16 buttons = DEMOPadGetButton(0); u16 downs = DEMOPadGetButtonDown(0); u16 dirs = DEMOPadGetDirsNew(0); s8 stickX = DEMOPadGetStickX(0); s8 stickY = DEMOPadGetStickY(0); // Scale model up/down if (buttons & PAD_TRIGGER_L) { testData.mods *= 0.95F; if (testData.mods < 0.001F) testData.mods = 0.001F; } if (buttons & PAD_TRIGGER_R) { testData.mods *= 1.05F; } // Model rotation testData.rot += testData.dr; if (testData.rot >= 360.0F) testData.rot -= 360.0F; // change number of cubes if (dirs & DEMO_SUBSTICK_UP) { testData.numz++; } if (dirs & DEMO_SUBSTICK_DOWN) { testData.numz--; if (testData.numz <= 1) testData.numz = 1; } if (dirs & DEMO_SUBSTICK_RIGHT) { testData.numx++; } if (dirs & DEMO_SUBSTICK_LEFT) { testData.numx--; if (testData.numx <= 1) testData.numx = 1; } // Zoom camera in/out if(buttons & PAD_BUTTON_Y) { CamS *= .95F; if(CamS < 0.001F) { CamS = 0.001F; } } if(buttons & PAD_BUTTON_A) { CamS *= 1.05F; } // Rotate viewpoint if(stickX || stickY) { if(stickX > 30 || stickX < -30) { if(stickX > 30) MTXRotDeg(rot, 'y', -1.0F); else if(stickX < -30) MTXRotDeg(rot, 'y', 1.0F); MTXMultVec(rot, &CamX, &CamX); MTXMultVec(rot, &CamY, &CamY); MTXMultVec(rot, &CamZ, &CamZ); } if(stickY > 30 || stickY < -30) { if(stickY > 30) MTXRotAxisDeg(rot, &CamX, -1.0F); else if(stickY < -30) MTXRotAxisDeg(rot, &CamX, 1.0F); MTXMultVec(rot, &CamY, &temp); dot = VECDotProduct(&temp, &yAxis); if(dot > 0.05F && dot <= 1.0F) { CamY = temp; MTXMultVec(rot, &CamZ, &CamZ); } } } // change mode if(downs & PAD_BUTTON_X) { animMode++; animMode %= 6; switch (animMode) { case 0: OSReport("Button B increments the number of tev stages\n"); break; case 1: OSReport("Button B toggles the statistics display\n"); break; case 2: OSReport("Button B toggles the cube animation\n"); break; case 3: OSReport("Button B decrements the number of cubes per texture\n"); break; case 4: OSReport("Button B toggles the texture cache algorithm\n"); break; case 5: OSReport("Button B toggles texture invalidation mode\n"); break; default: break; } } // change param depending on mode if (downs & PAD_BUTTON_B) { if (animMode == 0) { testData.ntev++; if (testData.ntev > MAX_TEV) testData.ntev = 1; } else if (animMode == 1) { showStats++; switch(showStats) { case 1 : SetStats(MyStats1, STAT_MENU1, STAT_TLD); break; case 2 : SetStats(MyStats2, STAT_MENU2, STAT_TLD); break; case 3 : SetStats(MyStats3, STAT_MENU3, STAT_TLD); break; case 4 : SetStats(MyStats4, STAT_MENU4, STAT_TLD); break; case 5 : SetStats(MyStats5, STAT_MENU5, STAT_TLD); break; default: SetStats(NULL, STAT_MENU0, STAT_TLD); showStats = 0; break; } } else if (animMode == 2) { stopAnim ^= 1; if (stopAnim) testData.dr = 0.0F; // rotation speed else testData.dr = 1.0F; // rotation speed } else if (animMode == 3) { testData.nc_tx /= 2; if (testData.nc_tx == 0) testData.nc_tx = testData.numx * testData.numz; } else if (animMode == 4) { testData.tcb ^= 1; if (testData.tcb == 0) { OSReport("Texture cache = round robin\n"); GXSetTexRegionCallback(oldCallback); } else { OSReport("Texture cache = mapID\n"); oldCallback = GXSetTexRegionCallback(MyTexRegionCallback); } GXInvalidateTexAll(); } else if (animMode == 5) { testData.tci ^= 1; if (testData.tci && testData.tcb) OSReport("Invalidate tex region on each GXLoadTexObj\n"); else OSReport("Do not invalidate tex region on each GXLoadTexObj\n"); } } } /*---------------------------------------------------------------------------* Name: MakeModelMtx Description: computes a model matrix from 3 vectors representing an object's coordinate system. Arguments: xAxis vector for the object's X axis yAxis vector for the object's Y axis zAxis vector for the object's Z axis Returns: none *---------------------------------------------------------------------------*/ static void MakeModelMtx ( Vec xAxis, Vec yAxis, Vec zAxis, Mtx m ) { VECNormalize(&xAxis,&xAxis); VECNormalize(&yAxis,&yAxis); VECNormalize(&zAxis,&zAxis); m[0][0] = xAxis.x; m[0][1] = xAxis.y; m[0][2] = xAxis.z; m[0][3] = 0.0F; m[1][0] = yAxis.x; m[1][1] = yAxis.y; m[1][2] = yAxis.z; m[1][3] = 0.0F; m[2][0] = zAxis.x; m[2][1] = zAxis.y; m[2][2] = zAxis.z; m[2][3] = 0.0F; MTXInverse(m, m); } /*---------------------------------------------------------------------------* Name: MyTexRegionCallback Description: Tex cache allocator using simple round algorithm Arguments: texObj : a pointer to texture object to be loaded mapID : destination texmap ID (just same as GXLoadTexObj) Returns: appropriate tex cache region for loading texture. *---------------------------------------------------------------------------*/ static GXTexRegion* MyTexRegionCallback(const GXTexObj* texObj, GXTexMapID mapID) { #pragma unused(mapID) u32 regionNum, texID; texID = (u32)GXGetTexObjUserData(texObj); regionNum = texID % NUM_REGIONS; // see effect of cold cache load if (testData.tci) GXInvalidateTexRegion(&MyTexRegions[regionNum]); return &MyTexRegions[regionNum]; } /*---------------------------------------------------------------------------* Name: PrintIntro Description: Print usage for test Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void PrintIntro( void ) { OSReport("\n"); OSReport("************************************\n"); OSReport(" Check fill-rate performance\n"); OSReport("************************************\n"); OSReport("\n"); OSReport("SUBSTICK: Change the number of cubes\n"); OSReport("TRIGGER R: Increase the size of the cubes\n"); OSReport("TRIGGER L: Decrease the size of the cubes\n"); OSReport("\n"); OSReport("STICK: Rotate camera\n"); OSReport("BUTTON A/Y: Zoom camera in/out\n"); OSReport("BUTTON X: Change Button B mode\n"); OSReport("BUTTON B: Controls various parameters depending on mode\n"); OSReport("\n"); OSReport("to quit hit the menu button\n"); OSReport("\n"); } /*---------------------------------------------------------------------------* Name: DrawCubeIndx Description: Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void DrawCubeIndx( void ) { GXBegin(GX_QUADS, GX_VTXFMT0, 4*6); GXPosition1x16(4); GXColor1x8(0); GXTexCoord1x8(0); // GXPosition1x16(5); GXColor1x8(0); GXTexCoord1x8(1); // GXPosition1x16(6); GXColor1x8(0); GXTexCoord1x8(2); // GXPosition1x16(7); GXColor1x8(0); GXTexCoord1x8(3); // GXPosition1x16(2); GXColor1x8(1); GXTexCoord1x8(0); // GXPosition1x16(6); GXColor1x8(1); GXTexCoord1x8(1); // GXPosition1x16(5); GXColor1x8(1); GXTexCoord1x8(2); // GXPosition1x16(3); GXColor1x8(1); GXTexCoord1x8(3); // GXPosition1x16(1); GXColor1x8(2); GXTexCoord1x8(0); // GXPosition1x16(0); GXColor1x8(2); GXTexCoord1x8(1); // GXPosition1x16(4); GXColor1x8(2); GXTexCoord1x8(2); // GXPosition1x16(7); GXColor1x8(2); GXTexCoord1x8(3); // GXPosition1x16(0); GXColor1x8(3); GXTexCoord1x8(0); // GXPosition1x16(1); GXColor1x8(3); GXTexCoord1x8(1); // GXPosition1x16(2); GXColor1x8(3); GXTexCoord1x8(2); // GXPosition1x16(3); GXColor1x8(3); GXTexCoord1x8(3); // GXPosition1x16(5); GXColor1x8(4); GXTexCoord1x8(0); // GXPosition1x16(4); GXColor1x8(4); GXTexCoord1x8(1); // GXPosition1x16(0); GXColor1x8(4); GXTexCoord1x8(2); // GXPosition1x16(3); GXColor1x8(4); GXTexCoord1x8(3); // GXPosition1x16(6); GXColor1x8(5); GXTexCoord1x8(0); // GXPosition1x16(2); GXColor1x8(5); GXTexCoord1x8(1); // GXPosition1x16(1); GXColor1x8(5); GXTexCoord1x8(2); // GXPosition1x16(7); GXColor1x8(5); GXTexCoord1x8(3); // GXEnd(); } /*---------------------------------------------------------------------------* Name: DrawCubeDirect Description: Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void DrawCubeDirect( void ) { GXBegin(GX_QUADS, GX_VTXFMT0, 4*6); GXPosition3f32( SIDE, SIDE, -SIDE); //4 GXColor1u32(0xff0000ff);// red GXTexCoord2f32(0.0F, 0.0F);// GXPosition3f32(SIDE, -SIDE, -SIDE);//5 GXColor1u32(0xff0000ff);// red GXTexCoord2f32(1.0F, 0.0F);// GXPosition3f32(SIDE, -SIDE, SIDE); //6 GXColor1u32(0xff0000ff);// red GXTexCoord2f32(1.0F, 1.0F);// GXPosition3f32(SIDE, SIDE, SIDE); //7 GXColor1u32(0xff0000ff);// red GXTexCoord2f32(0.0F, 1.0F);// GXPosition3f32(-SIDE, -SIDE, SIDE);//2 GXColor1u32(0x00ff00ff);// green GXTexCoord2f32(0.0F, 0.0F);// GXPosition3f32(SIDE, -SIDE, SIDE); //6 GXColor1u32(0x00ff00ff);// green GXTexCoord2f32(1.0F, 0.0F);// GXPosition3f32(SIDE, -SIDE, -SIDE);//5 GXColor1u32(0x00ff00ff);// green GXTexCoord2f32(1.0F, 1.0F);// GXPosition3f32(-SIDE, -SIDE, -SIDE);//3 GXColor1u32(0x00ff00ff);// green GXTexCoord2f32(0.0F, 1.0F);// GXPosition3f32(-SIDE, SIDE, SIDE); //1 GXColor1u32(0x0000ffff);// blue GXTexCoord2f32(0.0F, 0.0F);// GXPosition3f32(-SIDE, SIDE, -SIDE);//0 GXColor1u32(0x0000ffff);// blue GXTexCoord2f32(1.0F, 0.0F);// GXPosition3f32( SIDE, SIDE, -SIDE); //4 GXColor1u32(0x0000ffff);// blue GXTexCoord2f32(1.0F, 1.0F);// GXPosition3f32(SIDE, SIDE, SIDE); //7 GXColor1u32(0x0000ffff);// blue GXTexCoord2f32(0.0F, 1.0F);// GXPosition3f32(-SIDE, SIDE, -SIDE);//0 GXColor1u32(0xffff00ff);// yellow GXTexCoord2f32(0.0F, 0.0F);// GXPosition3f32(-SIDE, SIDE, SIDE); //1 GXColor1u32(0xffff00ff);// yellow GXTexCoord2f32(1.0F, 0.0F);// GXPosition3f32(-SIDE, -SIDE, SIDE);//2 GXColor1u32(0xffff00ff);// yellow GXTexCoord2f32(1.0F, 1.0F);// GXPosition3f32(-SIDE, -SIDE, -SIDE);//3 GXColor1u32(0xffff00ff);// yellow GXTexCoord2f32(0.0F, 1.0F);// GXPosition3f32(SIDE, -SIDE, -SIDE);//5 GXColor1u32(0xff00ffff);// magenta GXTexCoord2f32(0.0F, 0.0F);// GXPosition3f32( SIDE, SIDE, -SIDE); //4 GXColor1u32(0xff00ffff);// magenta GXTexCoord2f32(1.0F, 0.0F);// GXPosition3f32(-SIDE, SIDE, -SIDE);//0 GXColor1u32(0xff00ffff);// magenta GXTexCoord2f32(1.0F, 1.0F);// GXPosition3f32(-SIDE, -SIDE, -SIDE);//3 GXColor1u32(0xff00ffff);// magenta GXTexCoord2f32(0.0F, 1.0F);// GXPosition3f32(SIDE, -SIDE, SIDE); //6 GXColor1u32(0x00ffffff);// cyan GXTexCoord2f32(0.0F, 0.0F);// GXPosition3f32(-SIDE, -SIDE, SIDE);//2 GXColor1u32(0x00ffffff);// cyan GXTexCoord2f32(1.0F, 0.0F);// GXPosition3f32(-SIDE, SIDE, SIDE); //1 GXColor1u32(0x00ffffff);// cyan GXTexCoord2f32(1.0F, 1.0F);// GXPosition3f32(SIDE, SIDE, SIDE); //7 GXColor1u32(0x00ffffff);// cyan GXTexCoord2f32(0.0F, 1.0F);// GXEnd(); } /*---------------------------------------------------------------------------* Name: DrawArrow Description: Draw arrow pointing in direction of Z sort (-Z axis) Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void DrawArrow( void ) { GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_INDEX16); GXSetVtxDesc(GX_VA_CLR0, GX_INDEX8); GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0); GXSetTevOp(GX_TEVSTAGE0, GX_PASSCLR); GXSetNumChans(1); GXSetNumTexGens(0); GXSetNumTevStages(1); GXBegin(GX_TRIANGLES, GX_VTXFMT0, 3*4); // tri 0 GXPosition1x16(10); GXColor1x8(1);// green GXPosition1x16(8); GXColor1x8(1);// green GXPosition1x16(11); GXColor1x8(1);// green // tri 1 GXPosition1x16(10); GXColor1x8(0);// red GXPosition1x16(11); GXColor1x8(0);// red GXPosition1x16(9); GXColor1x8(0);// red // tri 2 GXPosition1x16(9); GXColor1x8(1);// green GXPosition1x16(11); GXColor1x8(1);// green GXPosition1x16(8); GXColor1x8(1);// green // tri 3 GXPosition1x16(10); GXColor1x8(1);// green GXPosition1x16(9); GXColor1x8(1);// green GXPosition1x16(8); GXColor1x8(1);// green GXEnd(); } // // My random functions // #define MAX_RAND (0xffffffff) #define SEED (0x38ba990e) static void init_rand( void ) { srand(SEED); } static u32 lrand( void ) { u32 r; r = (u32) ((rand() & 0xffff) << 16) + (rand() & 0xffff); return(r); } static f32 frand( void ) { f32 f; f = (f32)lrand()/(f32)MAX_RAND; return (f); } /*---------------------------------------------------------------------------* Stats engine *---------------------------------------------------------------------------*/ #define FLIPPER_CLOCK 162.0F // MHz #define STAT_TEXT_TOP 16 #define STAT_TEXT_BOT 16 #define STAT_TEXT_LFT 16 #define STAT_TEXT_RHT 16 #define STAT_CHAR_WD 8 #define STAT_CHAR_HT 8 #define STAT_CHAR_YSP 2 // vertical space /*---------------------------------------------------------------------------* Functions *---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------* Name: SetStats Description: This function sets an array of GX performance metrics to count. The name of each metric is specified in the text array. The current stat count is kept in the count array. At the end of each loop, the metric will be printed at the corner of the screen specified, either top-left, top- right, bottom-left, or bottom-right. If stat is NULL or nstats is zero, the performance monitoring is disabled. Arguments: stat : An array of GXPerf metrics to count. nstats: number of stats in the array or number you wish to display. disp : display style of the stats: 0 = top-left 2 = bot-left 8 = serial output (OSReport) Returns: None *---------------------------------------------------------------------------*/ static void SetStats( StatObj *stat, u32 nstats, StatDispMode disp ) { if (stat == NULL || nstats == 0) { StatEnable = GX_DISABLE; } else { StatEnable = GX_TRUE; Stat = stat; StatIndx = 0; StatMaxIndx = nstats; StatDisp = disp; StatStrLen = strlen(Stat[0].text); } } /*---------------------------------------------------------------------------* Name: WriteStats Description: This function calls a metric function for the next stat to be counted. Arguments: none Returns: None *---------------------------------------------------------------------------*/ static void WriteStats( GXBool update ) { u32 cnt0, cnt1, cnt2, cnt3, cnt4; u32 cnt5, cnt6, cnt7, cnt8, cnt9; switch (Stat[StatIndx].stat_type) { case STAT_GP0: if (update) { cnt0 = GXReadGP0Metric(); Stat[StatIndx].count = cnt0; GXSetGP0Metric(GX_PERF0_NONE); } else { GXSetGP0Metric((GXPerf0)Stat[StatIndx].stat); GXClearGP0Metric(); } break; case STAT_GP1: if (update) { cnt0 = GXReadGP1Metric(); Stat[StatIndx].count = cnt0; GXSetGP1Metric(GX_PERF1_NONE); } else { GXSetGP1Metric((GXPerf1)Stat[StatIndx].stat); GXClearGP1Metric(); } break; case STAT_MEM: if (update) { GXReadMemMetric( &cnt0, &cnt1, &cnt2, &cnt3, &cnt4, &cnt5, &cnt6, &cnt7, &cnt8, &cnt9 ); cpReq = cnt0; tcReq = cnt1; cpuRdReq = cnt2; cpuWrReq = cnt3; dspReq = cnt4; ioReq = cnt5; viReq = cnt6; peReq = cnt7; rfReq = cnt8; fiReq = cnt9; } else { GXClearMemMetric(); } break; case STAT_PIX: if (update) { GXReadPixMetric(&cnt0, &cnt1, &cnt2, &cnt3, &cnt4, &cnt5); topPixIn = cnt0; topPixOut = cnt1; botPixIn = cnt2; botPixOut = cnt3; clrPixIn = cnt4; copyClks = cnt5; } else { GXClearPixMetric(); } break; case STAT_VC: if (update) { GXReadVCacheMetric(&cnt0, &cnt1, &cnt2); vcCheck = cnt0; vcMiss = cnt1; vcStall = cnt2; } else { GXSetVCacheMetric(GX_VC_POS); GXClearVCacheMetric(); } break; case STAT_FR: // fill rate info if (update) { GXReadPixMetric(&cnt0, &cnt1, &cnt2, &cnt3, &cnt4, &cnt5); topPixIn = cnt0; topPixOut = cnt1; botPixIn = cnt2; botPixOut = cnt3; clrPixIn = cnt4; copyClks = cnt5; StatClocks = GXReadGP0Metric(); GXSetGP0Metric(GX_PERF0_NONE); } else { GXClearPixMetric(); GXSetGP0Metric(GX_PERF0_CLOCKS); GXClearGP0Metric(); } break; case STAT_TBP: // texture B/pixel case STAT_TBW: // texture bandwidth/pixel GXClearPixMetric(); if (update) { GXReadPixMetric(&cnt0, &cnt1, &cnt2, &cnt3, &cnt4, &cnt5); topPixIn = cnt0; topPixOut = cnt1; botPixIn = cnt2; botPixOut = cnt3; clrPixIn = cnt4; copyClks = cnt5; StatClocks = GXReadGP0Metric(); GXReadMemMetric( &cnt0, &cnt1, &cnt2, &cnt3, &cnt4, &cnt5, &cnt6, &cnt7, &cnt8, &cnt9 ); tcReq = cnt1; GXSetGP0Metric(GX_PERF0_NONE); } else { GXClearMemMetric(); GXSetGP0Metric(GX_PERF0_CLOCKS); GXClearGP0Metric(); } break; case STAT_MYC: case STAT_MYR: // do nothing, user will compute break; default: OSHalt("SetStats: Unknown demo stat type\n"); break; } } /*---------------------------------------------------------------------------* Name: UpdateStats Description: This function calls GXPerfMetric for the next stat to be counted. Arguments: none Returns: None *---------------------------------------------------------------------------*/ static void UpdateStats( GXBool inc ) { WriteStats(inc); if (inc) { StatIndx++; if (StatIndx == StatMaxIndx) { StatIndx = 0; } } } /*---------------------------------------------------------------------------* Name: PrintStats Description: This function prints the statistics currently being counted. Only one statistic gets updated each frame, but they are printed every frame. Arguments: none Returns: None *---------------------------------------------------------------------------*/ #define StatPrintf(str, val) \ DEMOPrintf(text_x, text_y, 0, str, Stat[i].text, val) #define StatOSReport(str, val) \ OSReport(str, Stat[i].text, val) static void PrintStats( void ) { GXRenderModeObj* rmode; u32 i; s16 text_x, text_y, text_yinc; u16 wd, ht; f32 rate; if (StatDisp == STAT_IO) // dump to serial output { for (i = 0; i < StatMaxIndx; i++) { switch (Stat[i].stat_type) { case STAT_PIX: switch(Stat[i].stat) { case STAT_PIX_TI: StatOSReport("%s: %8d\n", topPixIn); break; case STAT_PIX_TO: StatOSReport("%s: %8d\n", topPixOut); break; case STAT_PIX_BI: StatOSReport("%s: %8d\n", botPixIn); break; case STAT_PIX_BO: StatOSReport("%s: %8d\n", botPixOut); break; case STAT_PIX_CI: StatOSReport("%s: %8d\n", clrPixIn); break; case STAT_PIX_CC: StatOSReport("%s: %8d\n", copyClks); break; } break; case STAT_FR: rate = FLIPPER_CLOCK * (f32)(topPixIn + botPixIn) / (f32)(StatClocks - copyClks); StatOSReport("%s: %8.2f\n", rate); break; case STAT_TBW: rate = FLIPPER_CLOCK * (f32)(tcReq*32) / (f32)(StatClocks - copyClks); StatOSReport("%s: %8.2f\n", rate); break; case STAT_TBP: rate = (f32)(tcReq*32) / (topPixIn + botPixIn); StatOSReport("%s: %8.2f\n", rate); break; case STAT_VC: switch(Stat[i].stat) { case STAT_VC_CHK: StatOSReport("%s: %8d\n", vcCheck); break; case STAT_VC_MISS: StatOSReport("%s: %8d\n", vcMiss); break; case STAT_VC_STALL: StatOSReport("%s: %8d\n", vcStall); break; } break; case STAT_MYR: // use stat as a second input rate = (f32) Stat[i].stat / (f32) Stat[i].count; StatOSReport("%s: %8.2f\n", rate); break; case STAT_MEM: switch(Stat[i].stat) { case STAT_MEM_CP: StatOSReport("%s: %8d\n", cpReq); break; case STAT_MEM_TC: StatOSReport("%s: %8d\n", tcReq); break; case STAT_MEM_CPUR: StatOSReport("%s: %8d\n", cpuRdReq); break; case STAT_MEM_CPUW: StatOSReport("%s: %8d\n", cpuWrReq); break; case STAT_MEM_DSP: StatOSReport("%s: %8d\n", dspReq); break; case STAT_MEM_IO: StatOSReport("%s: %8d\n", ioReq); break; case STAT_MEM_VI: StatOSReport("%s: %8d\n", viReq); break; case STAT_MEM_PE: StatOSReport("%s: %8d\n", peReq); break; case STAT_MEM_RF: StatOSReport("%s: %8d\n", rfReq); break; case STAT_MEM_FI: StatOSReport("%s: %8d\n", fiReq); break; } break; default: StatOSReport("%s: %8d\n", Stat[i].count); break; } } } else // dump to screen { rmode = DEMOGetRenderModeObj(); switch (StatDisp) { case STAT_TL: // text origin is top-left text_x = STAT_TEXT_LFT; text_y = STAT_TEXT_TOP; text_yinc = STAT_CHAR_HT + STAT_CHAR_YSP; wd = rmode->fbWidth; ht = rmode->xfbHeight; break; case STAT_BL: // text origin is bottom-left text_x = STAT_TEXT_LFT; text_y = (s16)(rmode->xfbHeight - STAT_TEXT_BOT - STAT_CHAR_HT); text_yinc = -(STAT_CHAR_HT + STAT_CHAR_YSP); wd = rmode->fbWidth; ht = rmode->xfbHeight; break; case STAT_TLD: // double-sized fonts, text origin is top-left text_x = (s16)(STAT_TEXT_LFT / 2); text_y = (s16)(STAT_TEXT_TOP / 2); text_yinc = STAT_CHAR_HT + STAT_CHAR_YSP / 2; wd = (u16)(rmode->fbWidth / 2); ht = (u16)(rmode->xfbHeight / 2); break; case STAT_BLD: // double-sized fonts, text origin is bottom-left text_x = (s16)(STAT_TEXT_LFT / 2); text_y = (s16)((rmode->xfbHeight - STAT_TEXT_BOT - STAT_CHAR_HT) / 2); text_yinc = -(STAT_CHAR_HT + STAT_CHAR_YSP / 2); wd = (u16)(rmode->fbWidth / 2); ht = (u16)(rmode->xfbHeight / 2); break; } // Init DEMOPuts library fonts DEMOInitCaption(DM_FT_OPQ, wd, ht); for ( i = 0; i < StatMaxIndx; i++ ) { switch (Stat[i].stat_type) { case STAT_PIX: // Pix Metric switch(Stat[i].stat) { case STAT_PIX_TI: StatPrintf("%s: %8d\n", topPixIn); break; case STAT_PIX_TO: StatPrintf("%s: %8d\n", topPixOut); break; case STAT_PIX_BI: StatPrintf("%s: %8d\n", botPixIn); break; case STAT_PIX_BO: StatPrintf("%s: %8d\n", botPixOut); break; case STAT_PIX_CI: StatPrintf("%s: %8d\n", clrPixIn); break; case STAT_PIX_CC: StatPrintf("%s: %8d\n", copyClks); } break; case STAT_FR: // Fill rate (MPixels/Sec) rate = FLIPPER_CLOCK * (f32)(topPixIn + botPixIn) / (f32)(StatClocks - copyClks); StatPrintf("%s: %8.2f\n", rate); break; case STAT_TBW: // Texture bandwidth (MB/sec) rate = FLIPPER_CLOCK * (f32)(tcReq*32) / (f32)(StatClocks - copyClks); StatPrintf("%s: %8.2f\n", rate); break; case STAT_TBP: // Texture bandwidth per pixel rate = (f32)(tcReq*32) / (f32)(topPixIn - botPixIn); StatPrintf("%s: %8.3f\n", rate); break; case STAT_VC: // VertexCache Metric switch(Stat[i].stat) { case STAT_VC_CHK: StatPrintf("%s: %8d\n", vcCheck); break; case STAT_VC_MISS: StatPrintf("%s: %8d\n", vcMiss); break; case STAT_VC_STALL: StatPrintf("%s: %8d\n", vcStall); } break; case STAT_MEM: // Mem Access Metric switch(Stat[i].stat) { case STAT_MEM_CP: StatPrintf("%s: %8d\n", cpReq); break; case STAT_MEM_TC: StatPrintf("%s: %8d\n", tcReq); break; case STAT_MEM_CPUR: StatPrintf("%s: %8d\n", cpuRdReq); break; case STAT_MEM_CPUW: StatPrintf("%s: %8d\n", cpuWrReq); break; case STAT_MEM_DSP: StatPrintf("%s: %8d\n", dspReq); break; case STAT_MEM_IO: StatPrintf("%s: %8d\n", ioReq); break; case STAT_MEM_VI: StatPrintf("%s: %8d\n", viReq); break; case STAT_MEM_PE: StatPrintf("%s: %8d\n", peReq); break; case STAT_MEM_RF: StatPrintf("%s: %8d\n", rfReq); break; case STAT_MEM_FI: StatPrintf("%s: %8d\n", fiReq); break; } break; case STAT_GP0: case STAT_GP1: case STAT_MYC: StatPrintf("%s: %8d", Stat[i].count); break; case STAT_MYR: // use stat as a second input rate = (f32) Stat[i].stat / (f32) Stat[i].count; StatPrintf("%s: %8.3f", rate); break; default: OSReport("Undefined stat type %d in PrintStats()\n", Stat[i].stat_type); break; } // update current line text_y += text_yinc; } } // screen dump } /*===========================================================================*/