objectpool: dynamic extents and other improvements

This commit is contained in:
Andrei Alexeyev 2017-12-23 23:56:14 +02:00
parent b2660103e3
commit de31a20497
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
18 changed files with 244 additions and 120 deletions

View file

@ -12,6 +12,7 @@
#include "list.h"
#include "global.h"
#include "stageobjects.h"
#include "objectpool_util.h"
void aniplayer_create(AniPlayer *plr, Animation *ani) {
memset(plr,0,sizeof(AniPlayer));
@ -22,10 +23,16 @@ AniPlayer* aniplayer_create_copy(AniPlayer *src) {
// XXX: maybe it needs another name since it allocates memory?
// or maybe aniplayer_create needs another name since it doesn't?
// AniPlayer *plr = malloc(sizeof(AniPlayer));
AniPlayer *plr = (AniPlayer*)objpool_acquire(stage_object_pools.aniplayers);
aniplayer_copy(plr, src);
size_t data_size;
void *src_data = objpool_object_contents(NULL, &src->object_interface, NULL);
void *dst_data = objpool_object_contents(stage_object_pools.aniplayers, &plr->object_interface, &data_size);
memcpy(dst_data, src_data, data_size);
plr->queue = NULL;
plr->queuesize = 0;
return plr;
}
@ -51,12 +58,6 @@ void aniplayer_reset(AniPlayer *plr) { // resets to a neutral state with empty q
}
}
void aniplayer_copy(AniPlayer *dst, AniPlayer *src) {
memcpy(dst, src, sizeof(AniPlayer));
dst->queue = NULL;
dst->queuesize = 0;
}
AniSequence *aniplayer_queue(AniPlayer *plr, int row, int loops, int delay) {
AniSequence *s = (AniSequence*)list_append((List**)&plr->queue, calloc(1, sizeof(AniSequence)));
plr->queuesize++;

View file

@ -32,8 +32,9 @@ struct AniSequence{
bool backwards;
};
typedef struct {
ObjectInterface object_interface; // hack for the boss glow effect
typedef struct AniPlayer AniPlayer;
struct AniPlayer{
OBJECT_INTERFACE(AniPlayer); // hack for the boss glow effect
Animation *ani;
int clock;
@ -43,12 +44,11 @@ typedef struct {
AniSequence *queue;
int queuesize;
} AniPlayer;
};
void aniplayer_create(AniPlayer *plr, Animation *ani);
void aniplayer_free(AniPlayer *plr);
void aniplayer_reset(AniPlayer *plr); // resets to a neutral state with empty queue.
void aniplayer_copy(AniPlayer *dst, AniPlayer *src);
AniPlayer* aniplayer_create_copy(AniPlayer *src);
void aniplayer_free_copy(AniPlayer *ani);

View file

@ -99,7 +99,7 @@ typedef struct Attack {
typedef struct Boss {
// XXX: temporary hack for binary compatibility of pos with Enemy and Projectile structs
ObjectInterface object_interface;
OBJECT_INTERFACE(struct Boss);
complex pos;
Attack *attacks;

View file

@ -15,6 +15,7 @@
#define PRAGMA(p)
#else
#define PRAGMA(p) _Pragma(#p)
#define USE_GNU_EXTENSIONS
#endif
#ifdef __MINGW_PRINTF_FORMAT

View file

@ -27,13 +27,7 @@ enum {
};
struct Enemy {
union {
ObjectInterface object_interface;
struct {
Enemy *next;
Enemy *prev;
};
};
OBJECT_INTERFACE(Enemy);
complex pos;
complex pos0;

View file

@ -11,6 +11,7 @@
#include "util.h"
#include "resource/texture.h"
#include "objectpool.h"
typedef struct Item Item;
@ -27,8 +28,7 @@ typedef enum {
} ItemType;
struct Item {
Item *next;
Item *prev;
OBJECT_INTERFACE(Item);
int birthtime;
complex pos;

View file

@ -19,8 +19,7 @@ typedef complex (*LaserPosRule)(Laser* l, float time);
typedef void (*LaserLogicRule)(Laser* l, int time);
struct Laser {
struct Laser *next;
struct Laser *prev;
OBJECT_INTERFACE(Laser);
complex pos;

View file

@ -9,14 +9,22 @@
#pragma once
#include "taisei.h"
#define LIST_INTERFACE_BASE(typename) struct { \
typename *next; \
typename *prev; \
}
#define LIST_INTERFACE(typename) union { \
List list_interface; \
LIST_INTERFACE_BASE(typename); \
}
typedef struct List {
struct List *next;
struct List *prev;
LIST_INTERFACE_BASE(struct List);
} List;
typedef struct ListContainer {
struct ListContainer *next;
struct ListContainer *prev;
LIST_INTERFACE(struct ListContainer);
void *data;
} ListContainer;

View file

@ -12,27 +12,26 @@
#include "util.h"
#include "list.h"
#ifdef OBJPOOL_DEBUG
#define OBJ_USED(pool,obj) ((pool)->usemap[((uintptr_t)(obj) - (uintptr_t)(pool)->objects) / (pool)->size_of_object])
#endif
struct ObjectPool {
char *tag;
size_t size_of_object;
size_t max_objects;
size_t usage;
size_t peak_usage;
size_t num_extents;
char **extents;
ObjectInterface *free_objects;
IF_OBJPOOL_DEBUG(
bool *usemap;
)
char objects[];
};
static inline ObjectInterface* obj_ptr(ObjectPool *pool, size_t idx) {
return (ObjectInterface*)(pool->objects + idx * pool->size_of_object);
static inline ObjectInterface* obj_ptr(ObjectPool *pool, char *objects, size_t idx) {
return (ObjectInterface*)(objects + idx * pool->size_of_object);
}
static void objpool_register_objects(ObjectPool *pool, char *objects) {
for(size_t i = 0; i < pool->max_objects; ++i) {
list_push((List**)&pool->free_objects, (List*)obj_ptr(pool, objects, i));
}
}
ObjectPool *objpool_alloc(size_t obj_size, size_t max_objects, const char *tag) {
@ -44,17 +43,12 @@ ObjectPool *objpool_alloc(size_t obj_size, size_t max_objects, const char *tag)
pool->free_objects = NULL;
pool->usage = 0;
pool->peak_usage = 0;
pool->num_extents = 0;
pool->extents = NULL;
pool->tag = strdup(tag);
IF_OBJPOOL_DEBUG({
pool->usemap = calloc(max_objects, sizeof(bool));
})
memset(pool->objects, 0, obj_size * max_objects);
for(size_t i = 0; i < max_objects; ++i) {
list_push((List**)&pool->free_objects, (List*)obj_ptr(pool, i));
}
objpool_register_objects(pool, pool->objects);
log_debug("[%s] Allocated pool for %zu objects, %zu bytes each",
pool->tag,
@ -65,14 +59,45 @@ ObjectPool *objpool_alloc(size_t obj_size, size_t max_objects, const char *tag)
return pool;
}
static char *objpool_add_extent(ObjectPool *pool) {
pool->extents = realloc(pool->extents, (++pool->num_extents) * sizeof(pool->extents));
char *extent = pool->extents[pool->num_extents - 1] = calloc(pool->max_objects, pool->size_of_object);
objpool_register_objects(pool, extent);
return extent;
}
static char* objpool_fmt_size(ObjectPool *pool) {
switch(pool->num_extents) {
case 0:
return strfmt("%zu objects, %zu bytes each",
pool->max_objects,
pool->size_of_object
);
case 1:
return strfmt("%zu objects, %zu bytes each, with 1 extent",
pool->max_objects * 2,
pool->size_of_object
);
default:
return strfmt("%zu objects, %zu bytes each, with %zu extents",
pool->max_objects * (1 + pool->num_extents),
pool->size_of_object,
pool->num_extents
);
}
}
ObjectInterface *objpool_acquire(ObjectPool *pool) {
ObjectInterface *obj = (ObjectInterface*)list_pop((List**)&pool->free_objects);
if(obj) {
acquired:
memset(obj, 0, pool->size_of_object);
IF_OBJPOOL_DEBUG({
OBJ_USED(pool, obj) = true;
obj->_object_private.used = true;
})
if(++pool->usage > pool->peak_usage) {
@ -83,49 +108,35 @@ ObjectInterface *objpool_acquire(ObjectPool *pool) {
return obj;
}
log_fatal("[%s] Object pool exhausted (%zu objects, %zu bytes each)",
char *tmp = objpool_fmt_size(pool);
log_warn("[%s] Object pool exhausted (%s), extending",
pool->tag,
pool->max_objects,
pool->size_of_object
tmp
);
free(tmp);
objpool_add_extent(pool);
obj = (ObjectInterface*)list_pop((List**)&pool->free_objects);
assert(obj != NULL);
goto acquired;
}
void objpool_release(ObjectPool *pool, ObjectInterface *object) {
objpool_memtest(pool, object);
IF_OBJPOOL_DEBUG({
char *objofs = (char*)object;
char *minofs = pool->objects;
char *maxofs = pool->objects + (pool->max_objects - 1) * pool->size_of_object;
if(objofs < minofs || objofs > maxofs) {
log_fatal("[%s] Object pointer %p is not within range %p - %p",
pool->tag,
(void*)objofs,
(void*)minofs,
(void*)maxofs
);
}
ptrdiff_t misalign = (ptrdiff_t)(objofs - pool->objects) % pool->size_of_object;
if(misalign) {
log_fatal("[%s] Object pointer %p is misaligned by %zi",
pool->tag,
(void*)objofs,
(ssize_t)misalign
);
}
if(!OBJ_USED(pool, object)) {
if(!object->_object_private.used) {
log_fatal("[%s] Attempted to release an unused object %p",
pool->tag,
(void*)objofs
(void*)object
);
}
OBJ_USED(pool, object) = false;
object->_object_private.used = false;
})
list_push((List**)&pool->free_objects, (List*)object);
pool->usage--;
// log_debug("[%s] Usage: %zu", pool->tag, pool->usage);
}
@ -139,17 +150,74 @@ void objpool_free(ObjectPool *pool) {
log_warn("[%s] %zu objects still in use", pool->tag, pool->usage);
}
IF_OBJPOOL_DEBUG({
free(pool->usemap);
})
for(size_t i = 0; i < pool->num_extents; ++i) {
free(pool->extents[i]);
}
free(pool->extents);
free(pool->tag);
free(pool);
}
size_t objpool_object_size(ObjectPool *pool) {
return pool->size_of_object;
}
void objpool_get_stats(ObjectPool *pool, ObjectPoolStats *stats) {
stats->tag = pool->tag;
stats->capacity = pool->max_objects;
stats->capacity = pool->max_objects * (1 + pool->num_extents);
stats->usage = pool->usage;
stats->peak_usage = pool->peak_usage;
}
__attribute__((unused))
static bool objpool_object_in_subpool(ObjectPool *pool, ObjectInterface *object, char *objects) {
char *objofs = (char*)object;
char *minofs = objects;
char *maxofs = objects + (pool->max_objects - 1) * pool->size_of_object;
if(objofs < minofs || objofs > maxofs) {
return false;
}
ptrdiff_t misalign = (ptrdiff_t)(objofs - objects) % pool->size_of_object;
if(misalign) {
log_fatal("[%s] Object pointer %p is misaligned by %zi",
pool->tag,
(void*)objofs,
(ssize_t)misalign
);
}
return true;
}
__attribute__((unused))
static bool objpool_object_in_pool(ObjectPool *pool, ObjectInterface *object) {
if(objpool_object_in_subpool(pool, object, pool->objects)) {
return true;
}
for(size_t i = 0; i < pool->num_extents; ++i) {
if(objpool_object_in_subpool(pool, object, pool->extents[i])) {
return true;
}
}
return false;
}
void objpool_memtest(ObjectPool *pool, ObjectInterface *object) {
assert(pool != NULL);
assert(object != NULL);
IF_OBJPOOL_DEBUG({
if(!objpool_object_in_pool(pool, object)) {
log_fatal("[%s] Object pointer %p does not belong to this pool",
pool->tag,
(void*)object
);
}
})
}

View file

@ -35,18 +35,42 @@ struct ObjectPoolStats {
size_t peak_usage;
};
#define OBJECT_INTERFACE_BASE(typename) struct { \
LIST_INTERFACE(typename); \
IF_OBJPOOL_DEBUG( \
struct { \
bool used; \
} _object_private; \
) \
}
#define OBJECT_INTERFACE(typename) union { \
ObjectInterface object_interface; \
OBJECT_INTERFACE_BASE(typename); \
}
struct ObjectInterface {
union {
List list_interface;
struct {
ObjectInterface *next;
ObjectInterface *prev;
};
};
OBJECT_INTERFACE_BASE(ObjectInterface);
};
#ifdef USE_GNU_EXTENSIONS
// Do some compile-time checks to make sure the type is compatible with object pools
#define OBJPOOL_ALLOC(typename,max_objects) (__extension__ ({ \
static_assert(__builtin_types_compatible_p(ObjectInterface, __typeof__(((typename*)(0))->object_interface)), \
#typename " must implement ObjectInterface (use the OBJECT_INTERFACE macro)"); \
static_assert(__builtin_offsetof(typename, object_interface) == 0, \
"object_interface must be the first member in " #typename); \
objpool_alloc(sizeof(typename), max_objects, #typename); \
}))
#else
#define OBJPOOL_ALLOC(typename,max_objects) objpool_alloc(sizeof(typename), max_objects, #typename)
#endif
ObjectPool *objpool_alloc(size_t obj_size, size_t max_objects, const char *tag);
void objpool_free(ObjectPool *pool);
ObjectInterface *objpool_acquire(ObjectPool *pool);
void objpool_release(ObjectPool *pool, ObjectInterface *object);
void objpool_get_stats(ObjectPool *pool, ObjectPoolStats *stats);
void objpool_memtest(ObjectPool *pool, ObjectInterface *object);
size_t objpool_object_size(ObjectPool *pool);

View file

@ -15,6 +15,8 @@ struct ObjectPool {
size_t size_of_object;
};
size_t sizeof_objpool_interface = sizeof(ObjectInterface);
ObjectPool *objpool_alloc(size_t obj_size, size_t max_objects, const char *tag) {
ObjectPool *pool = malloc(sizeof(ObjectPool));
pool->size_of_object = obj_size;
@ -37,3 +39,12 @@ void objpool_get_stats(ObjectPool *pool, ObjectPoolStats *stats) {
memset(&stats, 0, sizeof(ObjectPoolStats));
stats->tag = "<N/A>";
}
void objpool_memtest(ObjectPool *pool, ObjectInterface *object) {
assert(pool != NULL);
assert(object != NULL);
}
size_t objpool_object_size(ObjectPool *pool) {
return pool->size_of_object;
}

View file

@ -9,6 +9,7 @@
#include "taisei.h"
#include "objectpool_util.h"
#include "util.h"
static void* objpool_release_list_callback(List **dest, List *elem, void *vpool) {
list_unlink(dest, elem);
@ -25,3 +26,18 @@ bool objpool_is_full(ObjectPool *pool) {
objpool_get_stats(pool, &stats);
return stats.capacity == stats.usage;
}
size_t objpool_object_contents_size(ObjectPool *pool) {
return objpool_object_size(pool) - sizeof(ObjectInterface);
}
void *objpool_object_contents(ObjectPool *pool, ObjectInterface *obj, size_t *out_size) {
assert(obj != NULL);
if(out_size != NULL) {
objpool_memtest(pool, obj);
*out_size = objpool_object_contents_size(pool);
}
return (char*)obj + sizeof(ObjectInterface);
}

View file

@ -9,7 +9,11 @@
#pragma once
#include "taisei.h"
#include <stdint.h>
#include "objectpool.h"
void objpool_release_list(ObjectPool *pool, List **dest);
bool objpool_is_full(ObjectPool *pool);
size_t objpool_object_contents_size(ObjectPool *pool);
void *objpool_object_contents(ObjectPool *pool, ObjectInterface *obj, size_t *out_size);

View file

@ -54,13 +54,7 @@ typedef enum ProjFlags {
} ProjFlags;
struct Projectile {
union {
ObjectInterface object_interface;
struct {
Projectile *next;
Projectile *prev;
};
};
OBJECT_INTERFACE(Projectile);
complex pos;
complex pos0;

View file

@ -27,13 +27,7 @@
#endif
typedef struct CacheEntry {
union {
ObjectInterface object_interface;
struct {
struct CacheEntry *next;
struct CacheEntry *prev;
};
};
OBJECT_INTERFACE(struct CacheEntry);
SDL_Surface *surf;
int width;
@ -241,7 +235,7 @@ void fontrenderer_draw(FontRenderer *f, const char *text, Font *font) {
void init_fonts(void) {
TTF_Init();
memset(&resources.fontren, 0, sizeof(resources.fontren));
cache_pool = objpool_alloc(sizeof(CacheEntry), 512, "fontcache");
cache_pool = OBJPOOL_ALLOC(CacheEntry, 512);
}
void uninit_fonts(void) {

View file

@ -505,7 +505,7 @@ static void stage_draw_hud_objpool_stats(float x, float y, float width, Font *fo
char buf[32];
objpool_get_stats(*pool, &stats);
snprintf(buf, sizeof(buf), "%zu | %4zu", stats.usage, stats.peak_usage);
snprintf(buf, sizeof(buf), "%zu | %5zu", stats.usage, stats.peak_usage);
draw_text(AL_Left | AL_Flag_NoAdjust, (int)x, (int)y, stats.tag, font);
draw_text(AL_Right | AL_Flag_NoAdjust, (int)(x + width), (int)y, buf, font);

View file

@ -15,26 +15,35 @@
#include "laser.h"
#include "aniplayer.h"
#define MAX_PROJECTILES 8192
#define MAX_ITEMS MAX_PROJECTILES
#define MAX_ENEMIES 256
#define MAX_LASERS 256
#define MAX_ANIPLAYERS 32
#define MAX_projectiles 1024
#define MAX_items MAX_projectiles
#define MAX_enemies 64
#define MAX_lasers 64
#define MAX_aniplayers 32
#define OBJECT_POOLS \
OBJECT_POOL(Projectile, projectiles) \
OBJECT_POOL(Item, items) \
OBJECT_POOL(Enemy, enemies) \
OBJECT_POOL(Laser, lasers) \
OBJECT_POOL(AniPlayer, aniplayers) \
StageObjectPools stage_object_pools;
void stage_objpools_alloc(void) {
stage_object_pools.projectiles = objpool_alloc(sizeof(Projectile), MAX_PROJECTILES, "proj+part");
stage_object_pools.items = objpool_alloc(sizeof(Item), MAX_ITEMS, "item");
stage_object_pools.enemies = objpool_alloc(sizeof(Enemy), MAX_ENEMIES, "enemy");
stage_object_pools.lasers = objpool_alloc(sizeof(Laser), MAX_LASERS, "laser");
stage_object_pools.aniplayers = objpool_alloc(sizeof(AniPlayer), MAX_ANIPLAYERS, "aniplr");
stage_object_pools = (StageObjectPools){
#define OBJECT_POOL(type,field) \
.field = OBJPOOL_ALLOC(type, MAX_##field),
OBJECT_POOLS
#undef OBJECT_POOL
};
}
void stage_objpools_free(void) {
objpool_free(stage_object_pools.projectiles);
objpool_free(stage_object_pools.items);
objpool_free(stage_object_pools.enemies);
objpool_free(stage_object_pools.lasers);
objpool_free(stage_object_pools.aniplayers);
#define OBJECT_POOL(type,field) \
objpool_free(stage_object_pools.field);
OBJECT_POOLS
#undef OBJECT_POOL
}

View file

@ -10,6 +10,7 @@
#include "taisei.h"
#include "build_config.h"
#include "compat.h"
#ifdef TAISEI_BUILDCONF_DEBUG
#define DEBUG 1