Replace Malloc With Pools
This commit introduces an object pool and replaces instances of malloc with pools where sensible.
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
#ifndef POOL_H
|
||||
#define POOL_H
|
||||
|
||||
#include <tonc.h>
|
||||
|
||||
#define POOL_BITS_PER_WORD 32
|
||||
#define POOL_WORD_T u32
|
||||
#define POOL_MAX_CAPACITY 128
|
||||
|
||||
typedef struct PoolBitmap {
|
||||
u32 *w;
|
||||
u32 nbits;
|
||||
u32 nwords;
|
||||
} PoolBitmap;
|
||||
|
||||
void pool_clear_idx(PoolBitmap *bm, int idx);
|
||||
int pool_get_free_idx(PoolBitmap *bm);
|
||||
|
||||
#define DECLARE_POOL_TYPE(type) \
|
||||
typedef struct type##Pool { \
|
||||
PoolBitmap bm; \
|
||||
type * objects; \
|
||||
u32 max_entries; \
|
||||
} type##Pool; \
|
||||
type *pool_get_##type(type##Pool* pool); \
|
||||
void pool_free_##type(type##Pool *pool, type *obj); \
|
||||
void pool_init_##type(type##Pool *pool);
|
||||
|
||||
#define DEFINE_POOL_TYPE(type, capacity) \
|
||||
static type type##_storage[capacity]; \
|
||||
static u32 type##_bitmap_w[sizeof(POOL_WORD_T)] = {0}; \
|
||||
static type##Pool type##_pool = { \
|
||||
.bm = { \
|
||||
.w = type##_bitmap_w, \
|
||||
.nbits = POOL_BITS_PER_WORD, \
|
||||
.nwords = sizeof(POOL_WORD_T)}, \
|
||||
.objects = type##_storage, \
|
||||
.max_entries = capacity, \
|
||||
}; \
|
||||
type * pool_get_##type(type##Pool *pool) { \
|
||||
int free_offset = pool_get_free_idx(&pool->bm); \
|
||||
if(free_offset == -1) return NULL; \
|
||||
return &pool->objects[free_offset]; \
|
||||
} \
|
||||
void pool_free_##type(type##Pool *pool, type *entry) { \
|
||||
if(entry == NULL) return; \
|
||||
int offset = entry - &pool->objects[0]; \
|
||||
pool_clear_idx(&pool->bm, offset); \
|
||||
}
|
||||
|
||||
#define POOL_GET(type) pool_get_##type(&type##_pool)
|
||||
#define POOL_FREE(type, obj) pool_free_##type(&type##_pool, obj)
|
||||
|
||||
#endif
|
||||
+1
-1
@@ -63,4 +63,4 @@ Sprite* sprite_object_get_sprite(SpriteObject* sprite_object);
|
||||
void sprite_object_set_focus(SpriteObject* sprite_object, bool focus);
|
||||
bool sprite_object_is_focused(SpriteObject* sprite_object);
|
||||
|
||||
#endif // SPRITE_H
|
||||
#endif // SPRITE_H
|
||||
|
||||
+14
-6
@@ -9,6 +9,16 @@
|
||||
// Audio
|
||||
#include "soundbank.h"
|
||||
|
||||
#include "pool.h"
|
||||
|
||||
#define MAX_CARDS_PER_HAND (NUM_SUITS * NUM_RANKS)
|
||||
#define MAX_CARDS_ON_SCREEN 16
|
||||
|
||||
DECLARE_POOL_TYPE(Card);
|
||||
DEFINE_POOL_TYPE(Card, MAX_CARDS_PER_HAND);
|
||||
DECLARE_POOL_TYPE(CardObject);
|
||||
DEFINE_POOL_TYPE(CardObject, MAX_CARDS_ON_SCREEN);
|
||||
|
||||
// Card sprites lookup table. First index is the suit, second index is the rank. The value is the tile index.
|
||||
const static u16 card_sprite_lut[NUM_SUITS][NUM_RANKS] = {
|
||||
{0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192},
|
||||
@@ -25,7 +35,7 @@ void card_init()
|
||||
// Card methods
|
||||
Card *card_new(u8 suit, u8 rank)
|
||||
{
|
||||
Card *card = malloc(sizeof(Card));
|
||||
Card *card = POOL_GET(Card);
|
||||
|
||||
card->suit = suit;
|
||||
card->rank = rank;
|
||||
@@ -35,8 +45,7 @@ Card *card_new(u8 suit, u8 rank)
|
||||
|
||||
void card_destroy(Card **card)
|
||||
{
|
||||
if (*card == NULL) return;
|
||||
free(*card);
|
||||
POOL_FREE(Card, *card);
|
||||
*card = NULL;
|
||||
}
|
||||
|
||||
@@ -61,7 +70,7 @@ u8 card_get_value(Card *card)
|
||||
// CardObject methods
|
||||
CardObject *card_object_new(Card *card)
|
||||
{
|
||||
CardObject *card_object = malloc(sizeof(CardObject));
|
||||
CardObject *card_object = POOL_GET(CardObject);
|
||||
|
||||
card_object->card = card;
|
||||
card_object->sprite_object = sprite_object_new();
|
||||
@@ -73,8 +82,7 @@ void card_object_destroy(CardObject **card_object)
|
||||
{
|
||||
if (*card_object == NULL) return;
|
||||
sprite_object_destroy(&((*card_object)->sprite_object));
|
||||
//card_destroy(&(*card_object)->card); // In practice, this is unnecessary because the card will be inserted into the discard pile and then back into the deck. If you need to destroy the card, you can do it manually before calling this function.
|
||||
free(*card_object);
|
||||
POOL_FREE(CardObject, *card_object);
|
||||
*card_object = NULL;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -586,7 +586,7 @@ void sort_cards()
|
||||
if (hand[i] != NULL)
|
||||
{
|
||||
// card_object_get_sprite() will not work here since we need the address
|
||||
sprite_destroy(&(hand[i]->sprite_object->sprite));
|
||||
sprite_destroy(&hand[i]->sprite_object->sprite);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-8
@@ -11,10 +11,12 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pool.h"
|
||||
|
||||
#define JOKER_SCORE_TEXT_Y 48
|
||||
#define NUM_JOKERS_PER_SPRITESHEET 2
|
||||
|
||||
static const unsigned int *joker_gfxTiles[] =
|
||||
static const unsigned int *joker_gfxTiles[] =
|
||||
{
|
||||
#define DEF_JOKER_GFX(idx) joker_gfx##idx##Tiles,
|
||||
#include "../include/def_joker_gfx_table.h"
|
||||
@@ -27,6 +29,14 @@ static const unsigned short *joker_gfxPal[] =
|
||||
#undef DEF_JOKER_GFX
|
||||
};
|
||||
|
||||
// Jokers in deck
|
||||
DECLARE_POOL_TYPE(Joker)
|
||||
DEFINE_POOL_TYPE(Joker, 8); // 8 should be fine, we only have a max of 2
|
||||
|
||||
// Jokers on screen
|
||||
DECLARE_POOL_TYPE(JokerObject)
|
||||
DEFINE_POOL_TYPE(JokerObject, 8) // 8 should be fine here as well, max of 2 from the shop in game.c
|
||||
|
||||
const static u8 edition_price_lut[MAX_EDITIONS] =
|
||||
{
|
||||
0, // BASE_EDITION
|
||||
@@ -48,7 +58,7 @@ static bool used_layers[MAX_JOKER_OBJECTS] = {false}; // Track used layers for j
|
||||
|
||||
// Maps the spritesheet index to the palette bank index allocated to it.
|
||||
// Spritesheets that were not allocated are
|
||||
static int* joker_spritesheet_pb_map;
|
||||
static int joker_spritesheet_pb_map[128];
|
||||
static int joker_pb_num_sprite_users[JOKER_LAST_PB - JOKER_BASE_PB + 1] = { 0 };
|
||||
|
||||
static int get_num_spritesheets()
|
||||
@@ -123,7 +133,6 @@ void joker_init()
|
||||
{
|
||||
// This should init once only so no need to free
|
||||
int num_spritesheets = get_num_spritesheets();
|
||||
joker_spritesheet_pb_map = (int*)malloc(sizeof(int) * num_spritesheets);
|
||||
|
||||
for (int i = 0; i < num_spritesheets; i++)
|
||||
{
|
||||
@@ -135,7 +144,7 @@ Joker *joker_new(u8 id)
|
||||
{
|
||||
if (id >= get_joker_registry_size()) return NULL;
|
||||
|
||||
Joker *joker = (Joker*)malloc(sizeof(Joker));
|
||||
Joker *joker = POOL_GET(Joker);
|
||||
const JokerInfo *jinfo = get_joker_registry_entry(id);
|
||||
|
||||
joker->id = id;
|
||||
@@ -149,8 +158,7 @@ Joker *joker_new(u8 id)
|
||||
|
||||
void joker_destroy(Joker **joker)
|
||||
{
|
||||
if (*joker == NULL) return;
|
||||
free(*joker);
|
||||
POOL_FREE(Joker, *joker);
|
||||
*joker = NULL;
|
||||
}
|
||||
|
||||
@@ -175,7 +183,7 @@ int joker_get_sell_value(const Joker* joker)
|
||||
// JokerObject methods
|
||||
JokerObject *joker_object_new(Joker *joker)
|
||||
{
|
||||
JokerObject *joker_object = malloc(sizeof(JokerObject));
|
||||
JokerObject *joker_object = POOL_GET(JokerObject);
|
||||
|
||||
int layer = 0;
|
||||
for (int i = 0; i < MAX_JOKER_OBJECTS; i++)
|
||||
@@ -232,7 +240,7 @@ void joker_object_destroy(JokerObject **joker_object)
|
||||
|
||||
sprite_object_destroy(&(*joker_object)->sprite_object); // Destroy the sprite
|
||||
joker_destroy(&(*joker_object)->joker); // Destroy the joker
|
||||
free(*joker_object);
|
||||
POOL_FREE(JokerObject, *joker_object);
|
||||
*joker_object = NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "pool.h"
|
||||
|
||||
void pool_clear_idx(PoolBitmap *bm, int idx) {
|
||||
u32 i = idx / POOL_BITS_PER_WORD; // word offset
|
||||
u32 b = idx % POOL_BITS_PER_WORD; // bit offset
|
||||
bm->w[i] &= ~((POOL_WORD_T)1 << b);
|
||||
}
|
||||
|
||||
int pool_get_free_idx(PoolBitmap *bm) {
|
||||
for (u32 i = 0; i < bm->nwords; i++) {
|
||||
POOL_WORD_T inv = ~bm->w[i];
|
||||
|
||||
// guard so we don't cal `ctz` with 0, since __builtin_ctz(0) is undefined
|
||||
// https://gcc.gnu.org/onlinedocs/gcc/Bit-Operation-Builtins.html#index-_005f_005fbuiltin_005fctz
|
||||
//
|
||||
// By using the bitwise inverse of the word, you can skip words that are full
|
||||
// quickly (where the value is 0 or 'false' since all bits are '1', or 'in use'). Any value greater
|
||||
// than 0 indicates there is a free slot. Then, when counting the trailing 0's, you can test very quickly
|
||||
// where the first free slot is. This operation prevents looping through every bit of filled flags, and
|
||||
// will instead operate only on the first word with free slots.
|
||||
if (inv) {
|
||||
int bit = __builtin_ctz(inv);
|
||||
bm->w[i] |= ((POOL_WORD_T)1 << bit);
|
||||
int idx = i * POOL_BITS_PER_WORD + bit;
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
+19
-20
@@ -3,6 +3,7 @@
|
||||
#include "util.h"
|
||||
#include "audio_utils.h"
|
||||
#include "soundbank.h"
|
||||
#include "pool.h"
|
||||
|
||||
#include <tonc.h>
|
||||
#include <stdlib.h>
|
||||
@@ -15,27 +16,22 @@
|
||||
OBJ_ATTR obj_buffer[MAX_SPRITES];
|
||||
OBJ_AFFINE *obj_aff_buffer = (OBJ_AFFINE*)obj_buffer;
|
||||
|
||||
static Sprite *free_sprites[MAX_SPRITES] = {NULL};
|
||||
DECLARE_POOL_TYPE(SpriteObject)
|
||||
DEFINE_POOL_TYPE(SpriteObject, MAX_SPRITES)
|
||||
|
||||
DECLARE_POOL_TYPE(Sprite)
|
||||
DEFINE_POOL_TYPE(Sprite, MAX_SPRITES)
|
||||
|
||||
static bool free_affines[MAX_AFFINES] = {false};
|
||||
|
||||
// Sprite methods
|
||||
Sprite *sprite_new(u16 a0, u16 a1, u32 tid, u32 pb, int sprite_index)
|
||||
{
|
||||
Sprite *sprite = malloc(sizeof(Sprite));
|
||||
Sprite* sprite = POOL_GET(Sprite);
|
||||
|
||||
sprite->obj = NULL;
|
||||
sprite->aff = NULL;
|
||||
|
||||
if(!free_sprites[sprite_index])
|
||||
{
|
||||
free_sprites[sprite_index] = sprite;
|
||||
}
|
||||
else
|
||||
{
|
||||
free(sprite);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (a0 & ATTR0_AFF)
|
||||
{
|
||||
int aff_index = MAX_AFFINES;
|
||||
@@ -52,7 +48,6 @@ Sprite *sprite_new(u16 a0, u16 a1, u32 tid, u32 pb, int sprite_index)
|
||||
|
||||
if (aff_index == MAX_AFFINES)
|
||||
{
|
||||
free(sprite);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -62,26 +57,30 @@ Sprite *sprite_new(u16 a0, u16 a1, u32 tid, u32 pb, int sprite_index)
|
||||
sprite->aff = &obj_aff_buffer[aff_index];
|
||||
obj_set_attr(sprite->obj, a0, a1, ATTR2_PALBANK(pb) | tid);
|
||||
obj_aff_identity(&obj_aff_buffer[aff_index]);
|
||||
return sprite;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite->obj = &obj_buffer[sprite_index];
|
||||
obj_set_attr(sprite->obj, a0, a1, ATTR2_PALBANK(pb) | tid);
|
||||
return sprite;
|
||||
}
|
||||
|
||||
return sprite;
|
||||
}
|
||||
|
||||
|
||||
void sprite_destroy(Sprite **sprite)
|
||||
{
|
||||
if (*sprite == NULL) return;
|
||||
|
||||
obj_hide((*sprite)->obj);
|
||||
free_sprites[(*sprite)->obj - obj_buffer] = NULL;
|
||||
|
||||
if ((*sprite)->aff != NULL)
|
||||
{
|
||||
free_affines[(*sprite)->aff - obj_aff_buffer] = false;
|
||||
}
|
||||
free(*sprite);
|
||||
|
||||
POOL_FREE(Sprite, *sprite);
|
||||
|
||||
*sprite = NULL;
|
||||
}
|
||||
|
||||
@@ -115,7 +114,7 @@ int sprite_get_pb(const Sprite *sprite)
|
||||
// SpriteObject methods
|
||||
SpriteObject* sprite_object_new()
|
||||
{
|
||||
SpriteObject* sprite_object = (SpriteObject*)malloc(sizeof(SpriteObject));
|
||||
SpriteObject* sprite_object = POOL_GET(SpriteObject);
|
||||
sprite_object->sprite = NULL;
|
||||
sprite_object_reset_transform(sprite_object);
|
||||
sprite_object->selected = false;
|
||||
@@ -127,8 +126,8 @@ SpriteObject* sprite_object_new()
|
||||
void sprite_object_destroy(SpriteObject** sprite_object)
|
||||
{
|
||||
if (*sprite_object == NULL) return;
|
||||
sprite_destroy(&((*sprite_object)->sprite));
|
||||
free(*sprite_object);
|
||||
sprite_destroy(&(*sprite_object)->sprite);
|
||||
POOL_FREE(SpriteObject, *sprite_object);
|
||||
*sprite_object = NULL;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user