AllocationTest.pp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. program AllocationTest;
  2. {$mode objfpc}
  3. uses
  4. ctypes, nds9;
  5. const
  6. SPRITE_MAX = 128;
  7. var
  8. sizes: array [0..11] of SpriteSize;
  9. //this is our game entity. Notice it has a bit more info than
  10. //would fit into OAM. This method is a lot more flexible than trying
  11. //to treat oam as a game object directly.
  12. type
  13. TMySprite = record
  14. x, y, z: integer;
  15. dx, dy: integer;
  16. alive: boolean;
  17. gfx: pcuint16;
  18. format: SpriteColorFormat;
  19. size: SpriteSize;
  20. end;
  21. PMySprite = ^TMySprite;
  22. var
  23. sprites: array [0..SPRITE_MAX - 1] of TMySprite;
  24. spriteMemoryUsage: cuint32 = 0;
  25. oomCount: cuint32 = 0;
  26. allocationCount: cuint32 = 0;
  27. spriteMemSize: cuint32 = 128 * 1024;
  28. oom: boolean = false;
  29. oam: POamState = @oamMain;
  30. i: integer;
  31. procedure createSprite(var s: TmySprite; x, y, z: integer; size: SpriteSize; format: SpriteColorFormat; dx, dy: integer);
  32. begin
  33. s.alive := true;
  34. s.x := x;
  35. s.y := y;
  36. s.z := z;
  37. s.dx := dx;
  38. s.dy := dy;
  39. s.size := size;
  40. s.format := format;
  41. //api: allocate a chunk of sprite graphics memory
  42. s.gfx := oamAllocateGfx(oam^, size, format);
  43. allocationCount := allocationCount + 1;
  44. if (s.gfx <> nil) then
  45. begin
  46. spriteMemoryUsage := spriteMemoryUsage + ((size and $FFF) shl 5);
  47. oom := false;
  48. end else
  49. begin
  50. oom := true;
  51. //only a failure of the allocator if there was enough room
  52. if (spriteMemoryUsage + ((size and $FFF) shl 5) < spriteMemSize) then
  53. oomCount := oomCount + 1;
  54. end;
  55. end;
  56. procedure killSprite(var s: TMySprite);
  57. begin
  58. s.alive := false;
  59. //api: free the graphics
  60. if (s.gfx <> nil) then
  61. begin
  62. oamFreeGfx(oam^, s.gfx);
  63. spriteMemoryUsage := spriteMemoryUsage - ((s.size and $FFF) shl 5);
  64. end;
  65. s.gfx := nil;
  66. end;
  67. function zsort(const a, b: pointer): integer;
  68. var
  69. first, second: PMySprite;
  70. begin
  71. first := PMySprite(a);
  72. second := PMySprite(b);
  73. //the trivial case
  74. if (first = second) then
  75. result := 0;
  76. //handle nulls
  77. if (first = nil) and (second <> nil) then
  78. result := -1;
  79. if (first <> nil) and (second = nil) then
  80. result := 1;
  81. //a dead sprite is always after a living one in the sort
  82. if (not first^.alive) and (second^.alive) then
  83. result := -1;
  84. if (first^.alive) and (not second^.alive) then
  85. result := 1;
  86. if (not first^.alive) and (not second^.alive) then
  87. result := 0;
  88. //finally if both are alive and not null sort them by depth
  89. if (first^.z = second^.z) then
  90. result := 0;
  91. if(first^.z < second^.z ) then
  92. result := -1;
  93. if(first^.z > second^.z) then
  94. result := 1;
  95. result := 0;
  96. end;
  97. procedure updateSprites();
  98. var
  99. i: integer;
  100. begin
  101. //sort our sprites on z
  102. //a more efficient way would be to keep a sorted list of sprites
  103. qsort(@sprites, SPRITE_MAX, sizeof(TMySprite), TSort(@zsort));
  104. //set oam to values required by my sprite
  105. for i := 0 to SPRITE_MAX - 1 do
  106. begin
  107. //an api function: void oamSet(int id, SpriteSize size, int x, int y, SpriteColorFormat format, const void* gfxOffset, bool hide);
  108. 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);
  109. end;
  110. end;
  111. procedure randomSprite(var s: TMySprite);
  112. var
  113. c: cuint8;
  114. color: cuint16;
  115. begin
  116. //pick a random color index
  117. c := random(256);
  118. //two pixels at a time
  119. color := c or (c shl 8);
  120. //create a randomly oriented sprite going off in a random direction
  121. createSprite(s, random(256), random(192), 0, integer(sizes[random(12)]), SpriteColorFormat_256Color, random(4) - 2, random(4) - 2);
  122. //dont let sprites get stuck with 0 velocity
  123. if (s.dx = 0) and (s.dy = 0) then
  124. begin
  125. s.dx := random(3) + 1;
  126. s.dy := random(3) + 1;
  127. end;
  128. //the size (in pixels) is encoded in the low 12 bits of the Size attribute (shifted left by 5)
  129. //we load new graphics each time as this is as much a test of my allocator as an example of api usage
  130. if (s.gfx <> nil) then
  131. swiCopy(@color, s.gfx, ((s.size and $FFF) shl 4) or COPY_MODE_FILL)
  132. else
  133. s.alive := false;
  134. end;
  135. procedure moveSprites();
  136. var
  137. i: integer;
  138. begin
  139. for i := 0 to SPRITE_MAX - 1 do
  140. begin
  141. sprites[i].x := sprites[i].x + sprites[i].dx;
  142. sprites[i].y := sprites[i].y + sprites[i].dy;
  143. if (sprites[i].x >= 256) or (sprites[i].x < 0) or (sprites[i].y >= 192) or (sprites[i].y < 0) then
  144. begin
  145. killSprite(sprites[i]);
  146. randomSprite(sprites[i]);
  147. end;
  148. end;
  149. end;
  150. var
  151. memUsageTemp: longint;// = $FFFFFFFF;
  152. begin
  153. randomize;
  154. sizes[0] := SpriteSize_8x8;
  155. sizes[1] := SpriteSize_8x16;
  156. sizes[2] := SpriteSize_16x8;
  157. sizes[3] := SpriteSize_8x32;
  158. sizes[4] := SpriteSize_16x16;
  159. sizes[5] := SpriteSize_32x8;
  160. sizes[6] := SpriteSize_16x32;
  161. sizes[7] := SpriteSize_32x16;
  162. sizes[8] := SpriteSize_32x32;
  163. sizes[9] := SpriteSize_32x64;
  164. sizes[10] := SpriteSize_64x32;
  165. sizes[11] := SpriteSize_64x64;
  166. videoSetMode(MODE_0_2D);
  167. videoSetModeSub(MODE_0_2D);
  168. vramSetBankA(VRAM_A_MAIN_SPRITE);
  169. vramSetBankB(VRAM_B_MAIN_SPRITE);
  170. vramSetBankD(VRAM_D_SUB_SPRITE);
  171. consoleDemoInit();
  172. // consoleDebugInit(DebugDevice_NOCASH); //send stderr to no$gba debug window
  173. //api: initialize OAM to 1D mapping with XX byte offsets and no external palette
  174. oamInit(oam^, SpriteMapping_1D_128, false);
  175. //create some sprites
  176. for i := 0 to SPRITE_MAX - 1 do
  177. randomSprite(sprites[i]);
  178. //load a randomly colored palette
  179. for i := 0 to 255 do
  180. begin
  181. SPRITE_PALETTE[i] := random((1 shl 15) - 1);
  182. SPRITE_PALETTE_SUB[i] := random((1 shl 15) - 1);
  183. end;
  184. while true do
  185. begin
  186. moveSprites();
  187. updateSprites();
  188. swiWaitForVBlank();
  189. //api: updates real oam memory
  190. oamUpdate(oam^);
  191. if oom then
  192. if memUsageTemp > spriteMemoryUsage then
  193. memUsageTemp := spriteMemoryUsage;
  194. consoleClear();
  195. printf('Memory usage: %i %i%% '#10, spriteMemoryUsage, 100 * spriteMemoryUsage div (spriteMemSize));
  196. printf('Percentage fail: %i%% '#10, oomCount * 100 div allocationCount);
  197. printf('Lowest Usage at fail %i %i%% '#10, memUsageTemp, 100 * memUsageTemp div (spriteMemSize));
  198. end;
  199. end.