Graphics/Sprites/allocation_test/source/main.c

#include <nds.h>
#include <stdio.h>

//our very simple sprite engine...
#define  SPRITE_MAX 128

//these are placed in an array to allow for random size selection
//these are defined as part of the new sprite api
SpriteSize sizes[] = {
        SpriteSize_8x8, 
        SpriteSize_8x16,
        SpriteSize_16x8,
        SpriteSize_8x32,
        SpriteSize_16x16, 
        SpriteSize_32x8,
        SpriteSize_16x32,  
        SpriteSize_32x16, 
        SpriteSize_32x32, 
        SpriteSize_32x64, 
        SpriteSize_64x32, 
        SpriteSize_64x64,       
};

//this is our game entity. Notice it has a bit more info than
//would fit into OAM.  This method is a lot more flexible than trying
//to treat oam as a game object directly.
typedef struct {
        int x,y,z;
        int dx, dy;
        bool alive;
        u16* gfx;
        SpriteColorFormat format;
        SpriteSize size;
}mySprite;

//an array of sprites
mySprite sprites[SPRITE_MAX];

u32 spriteMemoryUsage = 0;

u32 oomCount = 0;
u32 allocationCount = 0;
u32 spriteMemSize = 128 * 1024;

bool oom = false;
OamState *oam = &oamMain;

//a sprite constructor
void createSprite(mySprite* s, int x, int y, int z, SpriteSize size, SpriteColorFormat format, int dx, int dy) {
        s->alive = true;
        s->x = x;
        s->y = y;
        s->z = z; 
        s->dx = dx;
        s->dy = dy;
        s->size = size;
        s->format = format;
    
        //api: allocate a chunk of sprite graphics memory
        s->gfx = oamAllocateGfx(oam, size, format);
        
        allocationCount++;
        if(s->gfx) {
                spriteMemoryUsage += (size & 0xFFF) << 5;
                oom = false;
        } else {
                oom = true;
                //only a failure of the allocator if there was enough room
                if(spriteMemoryUsage + ((size & 0xFFF) << 5) < spriteMemSize)
                        oomCount++;
        }
}

//sprite deconstructor
void killSprite(mySprite *s) {
        s->alive = false;  
 
        //api: free the graphics
        if(s->gfx) {    
                oamFreeGfx(oam, s->gfx);
                spriteMemoryUsage -= (s->size & 0xFFF) << 5;
        }

        s->gfx = 0;
}

//a qsort function which sorts on z order
int zsort(const void* a, const void* b) {
        mySprite *first = (mySprite*)a;
        mySprite *second = (mySprite*)b;

        //the trivial case 
        if(first == second) return 0;

        //handle nulls
        if(!first && second) return -1;
        if(first && !second) return 1;

        //a dead sprite is always after a living one in the sort
        if(!first->alive && second->alive) return -1;
        if(first->alive && !second->alive) return 1;
        if(!first->alive && !second->alive) return 0;

        //finally if both are alive and not null sort them by depth
        if(first->z == second->z) return 0;
        if(first->z < second->z ) return -1;
        if(first->z > second->z) return 1;

        return 0; 
}  
  
//map our sprite to oam entries
void updateSprites(void) {
        int i;

        //sort our sprites on z
        //a more efficient way would be to keep a sorted list of sprites
        qsort(sprites, SPRITE_MAX, sizeof(mySprite), zsort);

        //set oam to values required by my sprite
        for(i = 0; i < SPRITE_MAX; i++) {
                //an api function: void oamSet(OamState* oam, int id,  int x, int y, int priority, int palette_alpha, SpriteSize size, SpriteColorFormat format, const void* gfxOffset, int affineIndex, bool sizeDouble, bool hide);
                oamSet(oam, 
                        i, 
                        sprites[i].x, sprites[i].y, 
                        0, 
                        0,
                        sprites[i].size,
                        sprites[i].format, 
                        sprites[i].gfx, 
                        -1, 
                        false, 
                        !sprites[i].alive,
                        false,
                        false, 
                        false);
        }
}

//create a sprite with a random position, speed, and size 
void randomSprite(mySprite* s) {
        //pick a random color index 
        u8 c = rand() % 256;

        //two pixels at a time
        u16 color = c | (c << 8);

        //create a randomly oriented sprite going off in a random direction
        createSprite(s, rand() % 256, rand() % 192, 0, sizes[(rand() % 12)], SpriteColorFormat_256Color, rand() % 4 - 2, rand() % 4 - 2);

        //dont let sprites get stuck with 0 velocity
        if(s->dx == 0 && s->dy == 0) {   
                s->dx = rand() % 3 + 1;
                s->dy = rand() % 3 + 1;
        }

        //the size (in pixels) is encoded in the low 12 bits of the Size attribute (shifted left by 5)
        //we load new graphics each time as this is as much a test of my allocator as an example of api usage
        if(s->gfx) {
                swiCopy(&color, s->gfx, ((s->size & 0xFFF) << 4) | COPY_MODE_FILL);
        }       else {  
                s->alive = false;
        }
}

//move the sprite based on its velocity, kill it if it goes off screen and create a new random one
void moveSprites(void) {
        int i;

        for(i = 0; i < SPRITE_MAX; i++) {
                sprites[i].x += sprites[i].dx;
                sprites[i].y += sprites[i].dy;

                if(sprites[i].x >= 256 || sprites[i].x < 0 || sprites[i].y >= 192 || sprites[i].y < 0) {
                        killSprite(&sprites[i]);
                        randomSprite(&sprites[i]);
                }
        }
}

int main(void)  {
        int i;
        int memUsageTemp = 0xFFFFFFFF;

        videoSetMode(MODE_0_2D);
        videoSetModeSub(MODE_0_2D);
        vramSetBankA(VRAM_A_MAIN_SPRITE);
        vramSetBankB(VRAM_B_MAIN_SPRITE);
        vramSetBankD(VRAM_D_SUB_SPRITE);

        consoleDemoInit();
//      consoleDebugInit(DebugDevice_NOCASH); //send stderr to no$gba debug window

        //api: initialize OAM to 1D mapping with XX byte offsets and no external palette
        oamInit(oam, SpriteMapping_1D_128, false);

        //create some sprites
        for(i = 0; i < SPRITE_MAX; i++)
                randomSprite(&sprites[i]);

        //load a randomly colored palette
        for(i = 0; i < 256; i++) {
      SPRITE_PALETTE[i] = rand();
      SPRITE_PALETTE_SUB[i] = rand();
        }

        while(1) { 
                moveSprites();

                updateSprites();

                swiWaitForVBlank();
                
                //api: updates real oam memory 
                oamUpdate(oam);

                if(oom) {       
                        memUsageTemp = memUsageTemp > spriteMemoryUsage ? spriteMemoryUsage : memUsageTemp;
        }       

                consoleClear();
                
                printf("Memory usage: %i %i%% \n",  spriteMemoryUsage, 100 * spriteMemoryUsage / (spriteMemSize));
                printf("Percentage fail: %i%% \n", oomCount * 100 / allocationCount);
                printf("Lowest Usage at fail %i %i%% \n", memUsageTemp, 100 * memUsageTemp / (spriteMemSize));                          
        }

        return 0;
}
 全て クラス ファイル 関数 変数 型定義 列挙型 列挙型の値 マクロ定義
libndsに対してMon Apr 26 23:39:49 2010に生成されました。  doxygen 1.6.3