fpc/packages/libndsfpc/examples/graphics/Sprites/allocation_test/AllocationTest.pp
Legolas 7c02f2b531 * Nintendo DS port updated to libnds ver.1.7.3
git-svn-id: trunk@42202 -
2019-06-10 06:39:46 +00:00

248 lines
6.1 KiB
ObjectPascal

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.