Path: chuka.playstation.co.uk!news From: Andrew Partington Newsgroups: scee.yaroze.freetalk.english Subject: Performance Date: Wed, 18 Jun 2003 18:52:35 +0000 Organization: PlayStation Net Yaroze (SCEE) Lines: 463 Message-ID: NNTP-Posting-Host: public2-ward1-6-cust140.oldh.broadband.ntl.com Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------000509070104050202070206" User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.3) Gecko/20030313 X-Accept-Language: en-us, en This is a multi-part message in MIME format. --------------000509070104050202070206 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Hi all, Can anyone tell me just what kind of performance is theoretically possible from the Yaroze libs with regard to 2D sprites? Surely 440+ scaled/rotated sprites, plus 768 transparent GsBOXF's per frame (most of which there is a ScaleMatrix() operation for) isn't enough to choke the GPU? Yet that is what seems to be happening for me. Those figures are probably the maximum for any given scene, in practice I see pretty bad performance hits at around half to three-quarters of that - not exactly demo reel material! All this seems due to the amount of scaled sprites making up the background, when you zoom in to their natural size (64*64) performance is acceptable - and when you commment out the call to GsSortSprite inside the inner loop of the map routine, performance is ace (well, duh! ;) ) So, am I running up against the texture cache problem again? If so, what is the best way to organise textures in VRAM to avoid this problem for a given texture depth? (Latest BG textures are 4-bit versions). Or do I need a DrawSync() somewhere along the line to let the GPU sort itself out, or something similar? Somebody else here said they had a problem when they were sorting >5 of the same type of sprite in a row (when not using the fixed version of the sprite routine). Attached is my BG code, feel free to use/abuse it, but if anyone can tell me any way at all of optimising it, (or even if you find it useful (ha!!)) please let me know! Or is a port to PS2/Linux on the cards? :) At least most of it is nicely modular now so I can use it as a framework for any other 2D game I attempt... Latest build in my ftp area: http://www.netyaroze-europe.com/~partinga/ftp Cheers, AndyP --------------000509070104050202070206 Content-Type: text/plain; name="map.c" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="map.c" /** * * ZoomRotating map, and GsMAP/GsBG initialisation stuff * (c) 2000 Andrew Partington. All rights reserved. * */ #include #include #include #include "map.h" #include "globals.h" #include "GsTools.h" #include "collision.h" /*=====================================================================* * Initialise GsCELL background tile descriptors *--------------------------------------------------- * Args: GsIMAGE* img - pointer to image data containing background gfx * GsCELL* cells - pointer to the array of GsCELL structs (of the right size) * int sizex - number of blocks going across * int sizey - number of blocks going down * int offsetX - number of pixels to offset in texture page (x) * int offsetY *--------------------------------------------------- * Info: The GsCELL struct contains an x and y position into a texture, * and the pointers in the GsIMAGE struct of the gfx stuff. This info * must be set up for EACH TILE. * 3/9/2000: Added support for multiple graphics within a single * texture page. *======================================================================*/ void initCells(GsCELL* cells,GsIMAGE* img,int sizex,int sizey, int offsetX, int offsetY) { int x,y,ptr; GsCELL wcell; int xOffset = offsetX / 16; //GsBG stuff is fixed size on Yaroze :-( int yOffset = offsetY / 16; ptr=0; for(y=yOffset;ycx,img->cy); wcell.flag=0; wcell.tpage=GetTPage(img->pmode,0,img->px,img->py); cells[ptr]=wcell; ptr++; } } } /* * Initialise GsMAP struct *-------------------------- * Args: GsMAP* mapInfo - ptr to GsMAP struct * GsCELL* cells - ptr to bg tile info * u_short* mapdata - ptr to map data * sizex - no. of blocks in map (horizontally) * sizey - no, of blocks in map (vertically) *---------------------------- */ void initMap(GsMAP* mapinfo,GsCELL* cells,u_short* mapdata,int sizex,int sizey) { mapinfo->cellw=16; //Doesn't matter, they're gonna be mapinfo->cellh=16; // 16 anyway... :( mapinfo->ncellw=sizex; //width of map (cells) mapinfo->ncellh=sizey; //height of map (cells) mapinfo->base=cells; //pointer to cell array mapinfo->index=mapdata; //pointer to map data } /** * * Initialise GsBG background - link the image and map data in with the * background data * * * in: GsBG* bg: Main background structure - holds image and map data pointers etc * GsMAP* map: Pointer to the map data * GsIMAGE* img: Pointer to the tile image data * int sizex: Number of tiles to display horizontally * int sizey: Number of tiles to display vertically */ void initBgrnd(GsBG* bg,GsMAP* mapinfo,GsIMAGE* img,int sizex,int sizey) { bg->map=mapinfo; bg->attribute=(img->pmode<<24); //shift pmode left 24 bits to set attribute bg->x=0; bg->y=0; bg->w = sizex << 4; //display size is in pixels... bg->h = sizey << 4; bg->scrollx=0; bg->scrolly=0; bg->r=128; bg->g=128; bg->b=128; bg->mx=0; bg->my=0; bg->scalex=ONE; bg->scaley=ONE; bg->rotate=0; } /** * Allocate space in memory for an array of sprites, * then populate the array. * * For each piece of map data, create a sprite for it, * then set the appropriate texture page coordinates * for the sprite. * * Offset X and Y are used to grab graphics data from * parts of the texture page if they are used for other * stuff too. */ void allocateBackgroundObject(MapGraphicsData* background, GsIMAGE* imageData, int blocksX, int blocksY, int blockSizeX, int blockSizeY, int offsetX, int offsetY) { int i=0; int x=0; int y=0; int xOffset = offsetX / blockSizeX; int yOffset = offsetY / blockSizeY; GsSPRITE sprite; background->numberOfBlocksX = blocksX; background->numberOfBlocksY = blocksY; background->blockSizeX = blockSizeX; background->blockSizeY = blockSizeY; background->spriteData = (GsSPRITE*) malloc((blocksX * blocksY) * sizeof(GsSPRITE)); for(y = yOffset; y < blocksY + yOffset; y++) { for(x = xOffset; x < blocksX + xOffset; x++) { sprite = InitSprite(imageData, x * blockSizeX, y * blockSizeY, blockSizeX, blockSizeY); sprite.u = x * blockSizeX; //Adjust texturemap coordinates to point sprite.v = y * blockSizeY; // to the correct area for the block //recalculate texture page if out of range of // the u/v texture page coordinates // so we can support sprite banks that go // outside a single texture page if(x * blockSizeX >= 255) //can increment more than one texture page, { // and there are 4 sprites/tpage... sprite.tpage++; //Just increment texture page by 1 sprite.u &= 255; // if the next page is to the right } if(y * blockSizeY >= 255) { sprite.tpage += 16; // effectively, y * xSize + x, but can just sprite.v &= 255; // increment by 16 to get next Y texture page } // if(imageData->pmode & 3 == 0) // { sprite.attribute = 0x00000020; // } // else // { // sprite.attribute += (SPRITE_ON+TRANS_OFF+TRANS_NORMAL+MODE_7_ON); // } background->spriteData[i++] = sprite; } } } /** * * Free resources dynamically allocated to this background * (i.e. the array of sprites which make up the map) */ void freeMapGraphicsData(MapGraphicsData* mapGraphicsData) { free(mapGraphicsData->spriteData); } /** * * Draw sprite-based backgound object. * * Info: To set up zoomrotation around the centre * of the background, each sprite that makes up the * background must have its mx and my values set relative * to the origin of the map. * * +mx | -mx mx and my get progressively closer to, * +my | +my and further away from the origin with each * | sprite drawn, depending on the sign of the mx and my value. * -----O----- The coordinates of O can be set by setting the * +mx | -mx usual X and Y coordinates of each sprite to * -my | -my the desired position. Fortunately, mx and my * | values can be used for X/Y positioning as well! * * * *** OPTIMISATION MEASURES *** * * - Most values have been changed to long, in order to fit the register size * - Got rid of array indexing in favour of pointer arithmetic (reputed to be slightly faster) * - Doing majority of dereferencing before the inner/outer loops (again, reputed to result in a speed increase) * */ void drawBackgroundObjectUsingCollisionArray(BackgroundObject* background, CollisionObject* collisionArray, int* cameraStatus, GsOT* WorldOT, int priority) { //If the size of the map is greater than or equal to the screen, truncate to size of screen. //If the size of the map is greater than or equal to the display size, truncate to display size //If the display size is greater than the size of the map, repeat the data register long x = 0; register long y = 0; register long index; register long indextemp; long originXCopy; long originYCopy; register long offsetX = 0; register long offsetY = 0; long offsetXCopy; //Copy of the above calcs to avoid doing any long offsetYCopy; // divides later on... long pixelXScroll; //How many pixels to scroll each block in the map before long pixelYScroll; // looking up a new one on the X or Y axis register GsSPRITE* spriteRef = (GsSPRITE*) background->mapGraphics->spriteData; u_short* mapDataRef = (u_short*) background->mapData; long BGScaleX = background->scaleX; long BGScaleY = background->scaleY; long BGRotate = background->rotate; long BGPosX = background->xPos; long BGPosY = background->yPos; long BGOriginX = background->originX; long BGOriginY = background->originY; long BGSizeX = background->backgroundSizeX; long BGSizeY = background->backgroundSizeY; long BGBlockSizeX = background->mapGraphics->blockSizeX; long BGBlockSizeY = background->mapGraphics->blockSizeY; long BGDisplaySizeX = background->displaySizeX; long BGDisplaySizeY = background->displaySizeY; long oldIndex = NULL_BLOCK; int BGCollisionArrayIndex = 0; register GsSPRITE* workSpriteRef; //offsetX and offsetY are indices to a piece of map data, created by taking the // X and Y coordinates and dividing them by the blocksize, to get the nearest block. offsetX = background->scrollX >> 6; // / BGBlockSizeX; offsetY = background->scrollY >> 6; // / BGBlockSizeY; if(offsetX < 0) { offsetX = BGSizeX + offsetX; } if(offsetY < 0) { offsetY = BGSizeY + offsetY; } offsetX = offsetX & BGSizeX - 1; // offsetX %= BGSizeX; offsetY = offsetY & BGSizeY - 1; // offsetY %= BGSizeY; offsetXCopy = offsetX; //Copy of the above calcs to avoid doing any offsetYCopy = offsetY; // divides later on... //By doing a MOD (%) with the scroll value and the block size, we get the // remainder part of the above calculation, i.e. how many pixels to scroll // the block. pixelXScroll = background->scrollX & BGBlockSizeX - 1; pixelYScroll = background->scrollY & BGBlockSizeY - 1; originXCopy = BGOriginX; //Take a copy of the origin coords so we originYCopy = BGOriginY; // can just replace the copies without recalculating them for(y=0;ymapGraphics->spriteData[index]; workSpriteRef->x = BGOriginX + BGPosX; //put all sprites at the same coordinates, workSpriteRef->y = BGOriginY + BGPosY; // but use mx and my to offset them workSpriteRef->scalex = BGScaleX; // from this origin. workSpriteRef->scaley = BGScaleY; workSpriteRef->rotate = BGRotate; } workSpriteRef->mx = originXCopy + pixelXScroll; //mx and my are positions relative to the workSpriteRef->my = originYCopy + pixelYScroll; // origin specified at xPos and yPos workSpriteRef->attribute = 0x00000020; if(collisionArray != NULL && workSpriteRef->mx >= COLLISION_MIN_BOUNDS_X && workSpriteRef->mx <= COLLISION_MAX_BOUNDS_X && workSpriteRef->my >= COLLISION_MIN_BOUNDS_Y && workSpriteRef->my <= COLLISION_MAX_BOUNDS_Y) { // workSpriteRef->attribute = 0x00000000; //Store x,y and index values in the next free element in each array // mx and my are coords relative to centre of sprite if(BGCollisionArrayIndex < MAX_COLLISION_ARRAY_ELEMENTS) { (collisionArray + BGCollisionArrayIndex)->x = workSpriteRef->x; (collisionArray + BGCollisionArrayIndex)->y = workSpriteRef->y; (collisionArray + BGCollisionArrayIndex)->xPos = workSpriteRef->mx; (collisionArray + BGCollisionArrayIndex)->yPos = workSpriteRef->my; (collisionArray + BGCollisionArrayIndex)->type = index; BGCollisionArrayIndex++; } // scannerState = SCANNER_DANGER; } GsSortSprite(workSpriteRef, WorldOT, background->priority); } originXCopy -= BGBlockSizeX; if(offsetX++ < 0) { offsetX += BGSizeX; } offsetX = offsetX & (BGSizeX - 1); // offsetX %= BGSizeX; } if(offsetY++ < 0) { offsetY += BGSizeY; } offsetY = offsetY & (BGSizeY -1); //offsetY %= BGSizeY; offsetX = offsetXCopy; originXCopy = BGOriginX; originYCopy -= BGBlockSizeY; } } --------------000509070104050202070206--