spbench.c 9.43 KB
/**************************************************************************
 *                                                                        *
 *               Copyright (C) 1995, Silicon Graphics, Inc.               *
 *                                                                        *
 *  These coded instructions, statements, and computer programs  contain  *
 *  unpublished  proprietary  information of Silicon Graphics, Inc., 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 Silicon Graphics, Inc.  *
 *                                                                        *
 *************************************************************************/

/*
 * spbench.c
 *
 * Simple sprite benchmarking application.
 *
 * Written by Kato San, Nintendo Corporation.
 */
#include "spbench.h"

#define	ROOT_DL_LEN	32
#define	SPRITE_DL_LEN	20000

typedef struct{s16 x,y,dx,dy,n;} SpriteData;

u64 bootStack[BOOT_STACKSIZE/sizeof(u64)];

static u64		idleStack[IDLE_STACKSIZE/sizeof(u64)];
static u64		mainStack[MAIN_STACKSIZE/sizeof(u64)];
static u64		rmonStack[RMON_STACKSIZE/sizeof(u64)];
static OSThread		idleThread, mainThread, rmonThread;
static OSMesgQueue	fieldMesgQueue,rspMesgQueue,rdpMesgQueue;
static OSMesg		fieldMesg[1],rspMesg[1],rdpMesg[1];
static u64		dram_stack[SP_DRAM_STACK_SIZE64];
static Gfx		root_dl[2][ROOT_DL_LEN];
static Gfx		sprite_dl[2][SPRITE_DL_LEN];
static SpriteData	spriteData[MAX_SPRITES];

/*
 * Static display list to clear the screen and initialize various modes.
 */

static Gfx setup_dl[]={
    gsDPSetCycleType(G_CYC_FILL),
    gsDPPipelineMode(G_PM_NPRIMITIVE),
    gsDPSetFillColor(0x10001*(u32)GPACK_RGBA5551(0x3f,0x7f,0x7f,1)),
    gsDPFillRectangle(0,0,SCREEN_WD-1, SCREEN_HT-1),
    gsDPPipeSync(),
    gsDPSetTexturePersp(G_TP_NONE),
    gsDPSetTextureLOD(G_TL_TILE),
    gsDPSetTextureLUT(G_TT_NONE),
    gsDPSetTextureFilter(G_TF_POINT),
    gsDPSetTextureConvert(G_TC_FILT),
    gsDPSetScissor(G_SC_NON_INTERLACE,0,0,SCREEN_WD,SCREEN_HT),
    gsDPSetCycleType(G_CYC_1CYCLE),
    gsDPSetAlphaCompare(G_AC_THRESHOLD),
    gsDPSetBlendColor(0, 0, 0, 0x01),
    gsDPSetCombineMode(G_CC_DECALRGBA,G_CC_DECALRGBA),
    gsDPSetRenderMode(G_RM_OPA_SURF,G_RM_OPA_SURF2),
    gsSPEndDisplayList()
};

/*
 * RSP task structure.
 */
static OSTask tl[]={
{{
    M_GFXTASK,OS_TASK_DP_WAIT,
    (u64*)rspbootTextStart,
    0/* size_of_rspboot */,
    (u64*)gspFast3DTextStart,SP_UCODE_SIZE,
    (u64*)gspFast3DDataStart,SP_UCODE_DATA_SIZE,
    (u64*)&(dram_stack[0]),SP_DRAM_STACK_SIZE8,
    (u64*)0x0,(unsigned long long *)0x0,
    0/* begin_of_dl */,
    0/* size_of_dl */,
    (u64*)NULL,0xDA0
},},
{{
    M_GFXTASK,OS_TASK_DP_WAIT,
    (u64*)rspbootTextStart,
    0/* size_of_rspboot */,
    (u64*)gspFast3DTextStart,SP_UCODE_SIZE,
    (u64*)gspFast3DDataStart,SP_UCODE_DATA_SIZE,
    (u64*)&(dram_stack[0]),SP_DRAM_STACK_SIZE8,
    (u64*)0x0,(unsigned long long *)0x0,
    0/* begin_of_dl */,
    0/* size_of_dl */,
    (u64*)NULL,0xDA0
},}};

/*
 * random
 *
 * Random number generator
 */
static u16 r;

static int
random(void) {
    int i;

    for(i=0; i != 7; ++i){
	if (r&1)
	    r = r>>1^0x8812;
	else
	    r >>= 1;
    }
    return(r);
}

/*
 * initSpriteData
 *
 * Generate initial sprite locations and directions based on sprite size
 * parameters.  A note about the alignment parameter here:
 *
 * If a span does not cross a 64 byte boundary (in copy mode),
 * the access can be done in a single 64 byte rdram request.
 * For this app, 64 bytes = 32 pixels = 128 x coordintaes.
 * But since we are only applying this alignment to 16 pixel sprites,
 * we can avoid crossing a 64 byte grid boundary as long as the starting
 * x position and the delta x is a multiple of 64 x coordinates.
 */

static void
initSpriteData(SpriteData *ps)
{
    int i;

    /* re-initialize seed */

    r = 0x1234;

    for(i=0; i != MAX_SPRITES; ++i) {
	do {
	    ps->x=random() & 0x7ff & ~(currentParam->alignment-1);
	} while (ps->x>= (SCREEN_WD<<2));
	do {
	    ps->y=random()&0x7ff;
	} while (ps->y>= (SCREEN_HT<<2));
	do {
	    if (currentParam->alignment == 1)
		ps->dx=random() & 0xf;
	    else
		ps->dx=64;
	} while(ps->dx==0);
	do {
	    ps->dy=random() & 0xf;
	} while(ps->dy==0);

	 ps->n = (random() & (currentParam->texturesInTmem-1)) *
		 (currentParam->textureSizeY << 5);

	++ps;
    }
}

/*
 * prepare_dl
 *
 * Prepare the display list for the given frame.  Simply generate textured
 * rectangles based on the sprite size and location; then update the sprite
 * location for next time.
 */
static void
prepare_dl(int frame, SpriteData *ps)
{
    int i;
    Gfx *dlp;
    dlp=sprite_dl[frame&1];


    gDPSetCycleType(dlp++, currentParam->cycleType);
    for(i=0; i < currentParam->numSprites; i++) {
	if ((i % currentParam->loadFactor) == 0) {
	    gDPLoadTextureBlock(dlp++, currentParam->textureData,
		G_IM_FMT_RGBA, G_IM_SIZ_16b,
		currentParam->textureSizeX,
		currentParam->textureSizeY*currentParam->texturesInTmem,
		0,
		G_TX_WRAP|G_TX_NOMIRROR,G_TX_WRAP|G_TX_NOMIRROR,
		G_TX_NOMASK,G_TX_NOMASK,G_TX_NOLOD,G_TX_NOLOD);
	    }
	gSPTextureRectangle(dlp++,
			    ps->x, ps->y,
			    (ps->x-4)+currentParam->spriteSizeX*4,
			    (ps->y-4)+currentParam->spriteSizeY*4,
			    0,
			    0, ps->n,
			    currentParam->dsdx, currentParam->dtdy);
	ps->x += ps->dx;
	if (ps->x<0) {
	    ps->x = -ps->x;
	    ps->dx=-ps->dx;
	}
	if (ps->x >= (SCREEN_WD - currentParam->spriteSizeX)*4) {
	    ps->x = 2*(SCREEN_WD - currentParam->spriteSizeX)*4 - ps->x;
	    ps->dx =- ps->dx;
	}

	ps->y += ps->dy;
	if (ps->y < 0) {
	    ps->y =- ps->y;
	    ps->dy=-ps->dy;
	}
	if (ps->y >= (SCREEN_HT - currentParam->spriteSizeY) * 4) {
	    ps->y = 2*(SCREEN_HT-currentParam->spriteSizeY)*4 - ps->y;
	    ps->dy =- ps->dy;
	}

	++ps;
    }
    gSPEndDisplayList(dlp++);

    if ((dlp-sprite_dl[frame&1]) > SPRITE_DL_LEN) {
	osSyncPrintf("spbench: sprite display list too long (%d vs %s)\n",
		dlp-sprite_dl[frame&1], SPRITE_DL_LEN);
	osExit();
	for (;;);
    }
    osWritebackDCache(sprite_dl[frame&1],
		(dlp-sprite_dl[frame&1]) * sizeof(Gfx));

    dlp=root_dl[frame&1];
    gDPSetColorImage(dlp++,G_IM_FMT_RGBA,G_IM_SIZ_16b,SCREEN_WD,
	framebuffer[frame&1]);
    gSPDisplayList(dlp++,setup_dl);
    gSPDisplayList(dlp++,sprite_dl[frame&1]);
    graphMeter(dlp++);
    gDPFullSync(dlp++);
    gSPEndDisplayList(dlp++);

    tl[frame&1].t.data_ptr=(u64*)root_dl[frame&1];
    tl[frame&1].t.data_size=((int)(dlp-root_dl[frame&1])*sizeof(Gfx));
    if ((dlp-root_dl[frame&1]) > ROOT_DL_LEN) {
	osSyncPrintf("spbench: root display list too long (%d vs %s)\n",
		dlp-root_dl[frame&1], ROOT_DL_LEN);
	osExit();
	for (;;);
    }
    osWritebackDCache(root_dl[frame&1],
		(dlp-root_dl[frame&1]) * sizeof(Gfx));
}

/*
 * idle
 *
 * This thread fires up the application threads and then lowers its priority
 * to become the idle thread.
 */
static void
idle(void *arg)
{
    osCreateThread(&rmonThread, (OSId)0, rmonMain, (void*)0,
		   rmonStack+RMON_STACKSIZE/sizeof(u64), OS_PRIORITY_RMON);
    osStartThread(&rmonThread);

    osCreateThread(&mainThread,(OSId)3,main,(void*)0,
		   mainStack+MAIN_STACKSIZE/sizeof(u64),(OSPri)10);
    osStartThread(&mainThread);

    osSetThreadPri((OSThread*)0,(OSPri)0);

    for (;;);
}

/*
 * boot
 *
 * Initial boot code; initialize the OS and fire up the idle thread.
 */
void
boot(void)
{
    osInitialize();

    osCreateThread(&idleThread,1,idle,(void*)0,
		  idleStack+IDLE_STACKSIZE/sizeof(u64),(OSPri)10);
    osStartThread(&idleThread);
}

/*
 * main
 *
 * Loop, rendering the previous frame's display list while generating the
 * current one.  Check the controller at every frame to adjust sprite
 * parameters.
 */
void
main(void *arg)
{
    u32 frame;
    int controllerNum;

    osCreateViManager(OS_PRIORITY_VIMGR);
    osViSetMode(&osViModeTable[OS_VI_NTSC_LAN1]);
    osViSetSpecialFeatures(OS_VI_GAMMA_OFF);
    osCreateMesgQueue(&fieldMesgQueue,fieldMesg,1);
    osViSetEvent(&fieldMesgQueue,(OSMesg)0,1);
    osCreateMesgQueue(&rspMesgQueue,rspMesg,1);
    osSetEventMesg(OS_EVENT_SP,&rspMesgQueue,(OSMesg)0);
    osCreateMesgQueue(&rdpMesgQueue,rdpMesg,1);
    osSetEventMesg(OS_EVENT_DP,&rdpMesgQueue,(OSMesg)0);

    tl[0].t.ucode_boot_size=((int)rspbootTextEnd-(int)rspbootTextStart);
    tl[1].t.ucode_boot_size=((int)rspbootTextEnd-(int)rspbootTextStart);

    if ((controllerNum = initController()) == -1) {
	osSyncPrintf("spbench: no controller connected\n");
	osExit();
	for (;;);
    }
    osSyncPrintf("spbench: using controller %d\n\n", controllerNum);
    printCurrentParam();

    initialMeter();

    initSpriteData(spriteData);

    frame=0;
    prepare_dl(frame, spriteData);

    for (;;) {
	if (MQ_IS_FULL(&fieldMesgQueue))
	    osRecvMesg(&fieldMesgQueue,(OSMesg*)0,OS_MESG_BLOCK);
	osRecvMesg(&fieldMesgQueue,(OSMesg*)0,OS_MESG_BLOCK);

	timeMeter(0);				/* reset meter */

	osSpTaskLoad(&tl[frame&1]);
	osSpTaskStartGo(&tl[frame&1]);
	frame+=1;
	if (readController(controllerNum)) {
	    timeMeter(1);			/* mark after controller read */
	    initSpriteData(spriteData);
	} else {
	    timeMeter(1);			/* mark after controller read */
	}
	prepare_dl(frame, spriteData);
	timeMeter(1);				/* mark after dl preparation */

	osRecvMesg(&rspMesgQueue,(OSMesg*)0,OS_MESG_BLOCK);
	osRecvMesg(&rdpMesgQueue,(OSMesg*)0,OS_MESG_BLOCK);
	timeMeter(1);				/* mark after dl render */

	osViSwapBuffer(framebuffer[frame&1^1]);
	timeMeter(1);				/* final mark at retrace */
    }
}