123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- program AllocationTest;
- {$mode objfpc}
- uses
- ctypes, nds9;
- const
- SPRITE_MAX = 128;
- var
- sizes: array [0..11] of SpriteSize;
- //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.
- type
- TMySprite = record
- x, y, z: integer;
- dx, dy: integer;
- alive: boolean;
- gfx: pcuint16;
- format: SpriteColorFormat;
- size: SpriteSize;
- end;
- PMySprite = ^TMySprite;
- var
- sprites: array [0..SPRITE_MAX - 1] of TMySprite;
- spriteMemoryUsage: cuint32 = 0;
- oomCount: cuint32 = 0;
- allocationCount: cuint32 = 0;
- spriteMemSize: cuint32 = 128 * 1024;
- oom: boolean = false;
- oam: POamState = @oamMain;
- i: integer;
- procedure createSprite(var s: TmySprite; x, y, z: integer; size: SpriteSize; format: SpriteColorFormat; dx, dy: integer);
- begin
- 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 := allocationCount + 1;
- if (s.gfx <> nil) then
- begin
- spriteMemoryUsage := spriteMemoryUsage + ((size and $FFF) shl 5);
- oom := false;
- end else
- begin
- oom := true;
- //only a failure of the allocator if there was enough room
- if (spriteMemoryUsage + ((size and $FFF) shl 5) < spriteMemSize) then
- oomCount := oomCount + 1;
- end;
- end;
- procedure killSprite(var s: TMySprite);
- begin
- s.alive := false;
- //api: free the graphics
- if (s.gfx <> nil) then
- begin
- oamFreeGfx(oam^, s.gfx);
- spriteMemoryUsage := spriteMemoryUsage - ((s.size and $FFF) shl 5);
- end;
- s.gfx := nil;
- end;
- function zsort(const a, b: pointer): integer;
- var
- first, second: PMySprite;
- begin
- first := PMySprite(a);
- second := PMySprite(b);
- //the trivial case
- if (first = second) then
- result := 0;
- //handle nulls
- if (first = nil) and (second <> nil) then
- result := -1;
- if (first <> nil) and (second = nil) then
- result := 1;
- //a dead sprite is always after a living one in the sort
- if (not first^.alive) and (second^.alive) then
- result := -1;
- if (first^.alive) and (not second^.alive) then
- result := 1;
- if (not first^.alive) and (not second^.alive) then
- result := 0;
- //finally if both are alive and not null sort them by depth
- if (first^.z = second^.z) then
- result := 0;
- if(first^.z < second^.z ) then
- result := -1;
- if(first^.z > second^.z) then
- result := 1;
- result := 0;
- end;
- procedure updateSprites();
- var
- i: integer;
- begin
- //sort our sprites on z
- //a more efficient way would be to keep a sorted list of sprites
- qsort(@sprites, SPRITE_MAX, sizeof(TMySprite), TSort(@zsort));
- //set oam to values required by my sprite
- for i := 0 to SPRITE_MAX - 1 do
- begin
- //an api function: void oamSet(int id, SpriteSize size, int x, int y, SpriteColorFormat format, const void* gfxOffset, bool hide);
- oamSet(oam^, i, sprites[i].x, sprites[i].y, 0, 0, sprites[i].size, sprites[i].format, sprites[i].gfx, -1, false, not sprites[i].alive, false, false, false);
- end;
- end;
- procedure randomSprite(var s: TMySprite);
- var
- c: cuint8;
- color: cuint16;
- begin
- //pick a random color index
- c := random(256);
- //two pixels at a time
- color := c or (c shl 8);
- //create a randomly oriented sprite going off in a random direction
- createSprite(s, random(256), random(192), 0, integer(sizes[random(12)]), SpriteColorFormat_256Color, random(4) - 2, random(4) - 2);
- //dont let sprites get stuck with 0 velocity
- if (s.dx = 0) and (s.dy = 0) then
- begin
- s.dx := random(3) + 1;
- s.dy := random(3) + 1;
- end;
- //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 <> nil) then
- swiCopy(@color, s.gfx, ((s.size and $FFF) shl 4) or COPY_MODE_FILL)
- else
- s.alive := false;
- end;
- procedure moveSprites();
- var
- i: integer;
- begin
- for i := 0 to SPRITE_MAX - 1 do
- begin
- sprites[i].x := sprites[i].x + sprites[i].dx;
- sprites[i].y := sprites[i].y + sprites[i].dy;
- if (sprites[i].x >= 256) or (sprites[i].x < 0) or (sprites[i].y >= 192) or (sprites[i].y < 0) then
- begin
- killSprite(sprites[i]);
- randomSprite(sprites[i]);
- end;
- end;
- end;
- var
- memUsageTemp: longint;// = $FFFFFFFF;
- keys: integer;
- begin
- randomize;
- sizes[0] := SpriteSize_8x8;
- sizes[1] := SpriteSize_8x16;
- sizes[2] := SpriteSize_16x8;
- sizes[3] := SpriteSize_8x32;
- sizes[4] := SpriteSize_16x16;
- sizes[5] := SpriteSize_32x8;
- sizes[6] := SpriteSize_16x32;
- sizes[7] := SpriteSize_32x16;
- sizes[8] := SpriteSize_32x32;
- sizes[9] := SpriteSize_32x64;
- sizes[10] := SpriteSize_64x32;
- sizes[11] := SpriteSize_64x64;
- 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 to SPRITE_MAX - 1 do
- randomSprite(sprites[i]);
- //load a randomly colored palette
- for i := 0 to 255 do
- begin
- SPRITE_PALETTE[i] := random((1 shl 15) - 1);
- SPRITE_PALETTE_SUB[i] := random((1 shl 15) - 1);
- end;
- while true do
- begin
- moveSprites();
- updateSprites();
- swiWaitForVBlank();
- scanKeys();
- keys := keysDown();
- if (keys and KEY_START) <> 0 then break;
- //api: updates real oam memory
- oamUpdate(oam^);
- if oom then
- if memUsageTemp > spriteMemoryUsage then
- memUsageTemp := spriteMemoryUsage;
- consoleClear();
- printf('Memory usage: %li %li%% '#10, spriteMemoryUsage, 100 * spriteMemoryUsage div (spriteMemSize));
- printf('Percentage fail: %li%% '#10, oomCount * 100 div allocationCount);
- printf('Lowest Usage at fail %li %li%% '#10, memUsageTemp, 100 * memUsageTemp div (spriteMemSize));
- end;
- end.
-
|