object pools
This commit is contained in:
parent
ca98b39216
commit
abbc62b49c
23 changed files with 429 additions and 35 deletions
|
@ -52,6 +52,7 @@ set(SRCs
|
|||
stage.c
|
||||
stagedraw.c
|
||||
stagetext.c
|
||||
stageobjects.c
|
||||
replay.c
|
||||
global.c
|
||||
events.c
|
||||
|
@ -64,6 +65,9 @@ set(SRCs
|
|||
list.c
|
||||
refs.c
|
||||
hashtable.c
|
||||
objectpool.c
|
||||
# objectpool_fake.c
|
||||
objectpool_util.c
|
||||
boss.c
|
||||
plrmodes.c
|
||||
plrmodes/marisa.c
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "aniplayer.h"
|
||||
#include "list.h"
|
||||
#include "global.h"
|
||||
#include "stageobjects.h"
|
||||
|
||||
void aniplayer_create(AniPlayer *plr, Animation *ani) {
|
||||
memset(plr,0,sizeof(AniPlayer));
|
||||
|
@ -19,11 +20,20 @@ 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 = malloc(sizeof(AniPlayer));
|
||||
|
||||
AniPlayer *plr = (AniPlayer*)objpool_acquire(stage_object_pools.aniplayers);
|
||||
aniplayer_copy(plr, src);
|
||||
return plr;
|
||||
}
|
||||
|
||||
void aniplayer_free_copy(AniPlayer *ani) {
|
||||
if(ani) {
|
||||
aniplayer_free(ani);
|
||||
objpool_release(stage_object_pools.aniplayers, (ObjectInterface*)ani);
|
||||
}
|
||||
}
|
||||
|
||||
void aniplayer_free(AniPlayer *plr) {
|
||||
plr->queuesize = 0; // prevent aniplayer_reset from messing with the queue, since we're going to wipe all of it anyway
|
||||
list_free_all((List**)&plr->queue);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
*/
|
||||
|
||||
#include "resource/animation.h"
|
||||
#include "stageobjects.h"
|
||||
|
||||
typedef struct AniSequence AniSequence;
|
||||
struct AniSequence{
|
||||
|
@ -31,6 +32,8 @@ struct AniSequence{
|
|||
};
|
||||
|
||||
typedef struct {
|
||||
ObjectInterface object_interface; // hack for the boss glow effect
|
||||
|
||||
Animation *ani;
|
||||
int clock;
|
||||
|
||||
|
@ -42,11 +45,13 @@ typedef struct {
|
|||
} AniPlayer;
|
||||
|
||||
void aniplayer_create(AniPlayer *plr, Animation *ani);
|
||||
AniPlayer* aniplayer_create_copy(AniPlayer *src);
|
||||
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);
|
||||
|
||||
AniSequence *aniplayer_queue(AniPlayer *plr, int row, int loops, int delay); // 0 loops: played one time
|
||||
AniSequence *aniplayer_queue_pro(AniPlayer *plr, int row, int start, int duration, int delay, int speed); // self-documenting pro version
|
||||
void aniplayer_update(AniPlayer *plr); // makes the inner clocks tick
|
||||
|
|
|
@ -171,7 +171,7 @@ static void BossGlow(Projectile *p, int t) {
|
|||
static int boss_glow_rule(Projectile *p, int t) {
|
||||
if(t == EVENT_DEATH) {
|
||||
AniPlayer *aplr = REF(p->args[2]);
|
||||
free(aplr);
|
||||
aniplayer_free_copy(aplr);
|
||||
free_ref(p->args[2]);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -97,11 +97,13 @@ typedef struct Attack {
|
|||
} Attack;
|
||||
|
||||
typedef struct Boss {
|
||||
// XXX: temporary hack for binary compatibility of pos with Enemy and Projectile structs
|
||||
ObjectInterface object_interface;
|
||||
complex pos;
|
||||
|
||||
Attack *attacks;
|
||||
Attack *current;
|
||||
|
||||
complex pos;
|
||||
|
||||
char *name;
|
||||
|
||||
int acount;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "projectile.h"
|
||||
#include "list.h"
|
||||
#include "aniplayer.h"
|
||||
#include "stageobjects.h"
|
||||
|
||||
#ifdef create_enemy_p
|
||||
#undef create_enemy_p
|
||||
|
@ -33,7 +34,7 @@ Enemy *create_enemy_p(Enemy **enemies, complex pos, int hp, EnemyVisualRule visu
|
|||
}
|
||||
|
||||
// XXX: some code relies on the insertion logic
|
||||
Enemy *e = (Enemy*)list_insert((List**)enemies, malloc(sizeof(Enemy)));
|
||||
Enemy *e = (Enemy*)list_insert((List**)enemies, (List*)objpool_acquire(stage_object_pools.enemies));
|
||||
e->moving = false;
|
||||
e->dir = 0;
|
||||
|
||||
|
@ -77,8 +78,8 @@ void* _delete_enemy(List **enemies, List* enemy, void *arg) {
|
|||
|
||||
e->logic_rule(e, EVENT_DEATH);
|
||||
del_ref(enemy);
|
||||
objpool_release(stage_object_pools.enemies, (ObjectInterface*)list_unlink(enemies, enemy));
|
||||
|
||||
free(list_unlink(enemies, enemy));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
10
src/enemy.h
10
src/enemy.h
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "util.h"
|
||||
#include "projectile.h"
|
||||
#include "objectpool.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define ENEMY_DEBUG
|
||||
|
@ -25,8 +26,13 @@ enum {
|
|||
};
|
||||
|
||||
struct Enemy {
|
||||
Enemy *next;
|
||||
Enemy *prev;
|
||||
union {
|
||||
ObjectInterface object_interface;
|
||||
struct {
|
||||
Enemy *next;
|
||||
Enemy *prev;
|
||||
};
|
||||
};
|
||||
|
||||
complex pos;
|
||||
complex pos0;
|
||||
|
|
12
src/item.c
12
src/item.c
|
@ -9,6 +9,8 @@
|
|||
#include "item.h"
|
||||
#include "global.h"
|
||||
#include "list.h"
|
||||
#include "stageobjects.h"
|
||||
#include "objectpool_util.h"
|
||||
|
||||
static Texture* item_tex(ItemType type) {
|
||||
static const char *const map[] = {
|
||||
|
@ -31,6 +33,10 @@ static int item_prio(List *litem) {
|
|||
return item->type;
|
||||
}
|
||||
|
||||
static void* alloc_item(void) {
|
||||
return objpool_acquire(stage_object_pools.items);
|
||||
}
|
||||
|
||||
Item* create_item(complex pos, complex v, ItemType type) {
|
||||
if((creal(pos) < 0 || creal(pos) > VIEWPORT_W)) {
|
||||
// we need this because we clamp the item position to the viewport boundary during motion
|
||||
|
@ -38,7 +44,7 @@ Item* create_item(complex pos, complex v, ItemType type) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
Item *i = (Item*)list_insert_at_priority((List**)&global.items, malloc(sizeof(Item)), type, item_prio);
|
||||
Item *i = (Item*)list_insert_at_priority((List**)&global.items, alloc_item(), type, item_prio);
|
||||
i->pos = pos;
|
||||
i->pos0 = pos;
|
||||
i->v = v;
|
||||
|
@ -50,7 +56,7 @@ Item* create_item(complex pos, complex v, ItemType type) {
|
|||
}
|
||||
|
||||
void delete_item(Item *item) {
|
||||
free(list_unlink((List**)&global.items, (List*)item));
|
||||
objpool_release(stage_object_pools.items, (ObjectInterface*)list_unlink((List**)&global.items, (List*)item));
|
||||
}
|
||||
|
||||
Item* create_bpoint(complex pos) {
|
||||
|
@ -89,7 +95,7 @@ void draw_items(void) {
|
|||
}
|
||||
|
||||
void delete_items(void) {
|
||||
list_free_all((List**)&global.items);
|
||||
objpool_release_list(stage_object_pools.items, (List**)&global.items);
|
||||
}
|
||||
|
||||
void move_item(Item *i) {
|
||||
|
|
|
@ -9,9 +9,10 @@
|
|||
#include "laser.h"
|
||||
#include "global.h"
|
||||
#include "list.h"
|
||||
#include "stageobjects.h"
|
||||
|
||||
Laser *create_laser(complex pos, float time, float deathtime, Color color, LaserPosRule prule, LaserLogicRule lrule, complex a0, complex a1, complex a2, complex a3) {
|
||||
Laser *l = (Laser*)list_push((List**)&global.lasers, malloc(sizeof(Laser)));
|
||||
Laser *l = (Laser*)list_push((List**)&global.lasers, (List*)objpool_acquire(stage_object_pools.lasers));
|
||||
|
||||
l->birthtime = global.frames;
|
||||
l->timespan = time;
|
||||
|
@ -173,7 +174,7 @@ void* _delete_laser(List **lasers, List *laser, void *arg) {
|
|||
l->lrule(l, EVENT_DEATH);
|
||||
|
||||
del_ref(laser);
|
||||
free(list_unlink(lasers, laser));
|
||||
objpool_release(stage_object_pools.lasers, (ObjectInterface*)list_unlink(lasers, laser));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -128,6 +128,10 @@ List* list_unlink(List **dest, List *elem) {
|
|||
}
|
||||
|
||||
List* list_pop(List **dest) {
|
||||
if(*dest == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return list_unlink(dest, *dest);
|
||||
}
|
||||
|
||||
|
|
146
src/objectpool.c
Normal file
146
src/objectpool.c
Normal file
|
@ -0,0 +1,146 @@
|
|||
|
||||
#include "objectpool.h"
|
||||
#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;
|
||||
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);
|
||||
}
|
||||
|
||||
ObjectPool *objpool_alloc(size_t obj_size, size_t max_objects, const char *tag) {
|
||||
// TODO: overflow handling
|
||||
|
||||
ObjectPool *pool = malloc(sizeof(ObjectPool) + (obj_size * max_objects));
|
||||
pool->size_of_object = obj_size;
|
||||
pool->max_objects = max_objects;
|
||||
pool->free_objects = NULL;
|
||||
pool->usage = 0;
|
||||
pool->peak_usage = 0;
|
||||
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));
|
||||
}
|
||||
|
||||
log_debug("[%s] Allocated pool for %zu objects, %zu bytes each",
|
||||
pool->tag,
|
||||
pool->max_objects,
|
||||
pool->size_of_object
|
||||
);
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
ObjectInterface *objpool_acquire(ObjectPool *pool) {
|
||||
ObjectInterface *obj = (ObjectInterface*)list_pop((List**)&pool->free_objects);
|
||||
|
||||
if(obj) {
|
||||
memset(obj, 0, pool->size_of_object);
|
||||
|
||||
IF_OBJPOOL_DEBUG({
|
||||
OBJ_USED(pool, obj) = true;
|
||||
})
|
||||
|
||||
if(++pool->usage > pool->peak_usage) {
|
||||
pool->peak_usage = pool->usage;
|
||||
}
|
||||
|
||||
// log_debug("[%s] Usage: %zu", pool->tag, pool->usage);
|
||||
return obj;
|
||||
}
|
||||
|
||||
log_fatal("[%s] Object pool exhausted (%zu objects, %zu bytes each)",
|
||||
pool->tag,
|
||||
pool->max_objects,
|
||||
pool->size_of_object
|
||||
);
|
||||
}
|
||||
|
||||
void objpool_release(ObjectPool *pool, ObjectInterface *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)) {
|
||||
log_fatal("[%s] Attempted to release an unused object %p",
|
||||
pool->tag,
|
||||
(void*)objofs
|
||||
);
|
||||
}
|
||||
|
||||
OBJ_USED(pool, object) = false;
|
||||
})
|
||||
|
||||
list_push((List**)pool->free_objects, (List*)object);
|
||||
pool->usage--;
|
||||
// log_debug("[%s] Usage: %zu", pool->tag, pool->usage);
|
||||
}
|
||||
|
||||
void objpool_free(ObjectPool *pool) {
|
||||
if(!pool) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(pool->usage != 0) {
|
||||
log_warn("[%s] %zu objects still in use", pool->tag, pool->usage);
|
||||
}
|
||||
|
||||
IF_OBJPOOL_DEBUG({
|
||||
free(pool->usemap);
|
||||
})
|
||||
|
||||
free(pool->tag);
|
||||
free(pool);
|
||||
}
|
||||
|
||||
void objpool_get_stats(ObjectPool *pool, ObjectPoolStats *stats) {
|
||||
stats->tag = pool->tag;
|
||||
stats->capacity = pool->max_objects;
|
||||
stats->usage = pool->usage;
|
||||
stats->peak_usage = pool->peak_usage;
|
||||
}
|
44
src/objectpool.h
Normal file
44
src/objectpool.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "list.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define OBJPOOL_DEBUG
|
||||
#endif
|
||||
|
||||
#ifdef OBJPOOL_DEBUG
|
||||
#define IF_OBJPOOL_DEBUG(code) code
|
||||
#else
|
||||
#define IF_OBJPOOL_DEBUG(code)
|
||||
#endif
|
||||
|
||||
typedef struct ObjectPool ObjectPool;
|
||||
typedef struct ObjectInterface ObjectInterface;
|
||||
typedef struct ObjectPoolStats ObjectPoolStats;
|
||||
|
||||
struct ObjectPoolStats {
|
||||
const char *tag;
|
||||
size_t capacity;
|
||||
size_t usage;
|
||||
size_t peak_usage;
|
||||
};
|
||||
|
||||
struct ObjectInterface {
|
||||
union {
|
||||
List list_interface;
|
||||
struct {
|
||||
ObjectInterface *next;
|
||||
ObjectInterface *prev;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
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);
|
30
src/objectpool_fake.c
Normal file
30
src/objectpool_fake.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
|
||||
#include "objectpool.h"
|
||||
#include "util.h"
|
||||
|
||||
struct ObjectPool {
|
||||
size_t size_of_object;
|
||||
};
|
||||
|
||||
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;
|
||||
return pool;
|
||||
}
|
||||
|
||||
ObjectInterface *objpool_acquire(ObjectPool *pool) {
|
||||
return calloc(1, pool->size_of_object);
|
||||
}
|
||||
|
||||
void objpool_release(ObjectPool *pool, ObjectInterface *object) {
|
||||
free(object);
|
||||
}
|
||||
|
||||
void objpool_free(ObjectPool *pool) {
|
||||
free(pool);
|
||||
}
|
||||
|
||||
void objpool_get_stats(ObjectPool *pool, ObjectPoolStats *stats) {
|
||||
memset(&stats, 0, sizeof(ObjectPoolStats));
|
||||
stats->tag = "<N/A>";
|
||||
}
|
12
src/objectpool_util.c
Normal file
12
src/objectpool_util.c
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
#include "objectpool_util.h"
|
||||
|
||||
static void* objpool_release_list_callback(List **dest, List *elem, void *vpool) {
|
||||
list_unlink(dest, elem);
|
||||
objpool_release((ObjectPool*)vpool, (ObjectInterface*)elem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void objpool_release_list(ObjectPool *pool, List **dest) {
|
||||
list_foreach(dest, objpool_release_list_callback, pool);
|
||||
}
|
6
src/objectpool_util.h
Normal file
6
src/objectpool_util.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "objectpool.h"
|
||||
|
||||
void objpool_release_list(ObjectPool *pool, List **dest);
|
|
@ -12,6 +12,7 @@
|
|||
#include "global.h"
|
||||
#include "list.h"
|
||||
#include "vbo.h"
|
||||
#include "stageobjects.h"
|
||||
|
||||
static ProjArgs defaults_proj = {
|
||||
.texture = "proj/",
|
||||
|
@ -110,7 +111,7 @@ static Projectile* _create_projectile(ProjArgs *args) {
|
|||
log_fatal("Tried to spawn a projectile while in drawing code");
|
||||
}
|
||||
|
||||
Projectile *p = calloc(1, sizeof(Projectile));
|
||||
Projectile *p = (Projectile*)objpool_acquire(stage_object_pools.projectiles);
|
||||
|
||||
p->birthtime = global.frames;
|
||||
p->pos = p->pos0 = args->pos;
|
||||
|
@ -160,7 +161,8 @@ static void* _delete_projectile(List **projs, List *proj, void *arg) {
|
|||
p->rule(p, EVENT_DEATH);
|
||||
|
||||
del_ref(proj);
|
||||
free(list_unlink(projs, proj));
|
||||
objpool_release(stage_object_pools.projectiles, (ObjectInterface*)list_unlink(projs, proj));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "resource/texture.h"
|
||||
#include "color.h"
|
||||
#include "recolor.h"
|
||||
#include "objectpool.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define PROJ_DEBUG
|
||||
|
@ -52,8 +53,14 @@ typedef enum ProjFlags {
|
|||
} ProjFlags;
|
||||
|
||||
struct Projectile {
|
||||
struct Projectile *next;
|
||||
struct Projectile *prev;
|
||||
union {
|
||||
ObjectInterface object_interface;
|
||||
struct {
|
||||
Projectile *next;
|
||||
Projectile *prev;
|
||||
};
|
||||
};
|
||||
|
||||
complex pos;
|
||||
complex pos0;
|
||||
complex size; // this is currently ignored if tex is not NULL.
|
||||
|
|
|
@ -134,6 +134,7 @@ void load_fonts(float quality) {
|
|||
_fonts.hud = load_font("res/fonts/Laconic_Regular.otf", 19);
|
||||
_fonts.mono = load_font("res/fonts/ShareTechMono-Regular.ttf", 19);
|
||||
_fonts.monosmall = load_font("res/fonts/ShareTechMono-Regular.ttf", 14);
|
||||
_fonts.monotiny = load_font("res/fonts/ShareTechMono-Regular.ttf", 10);
|
||||
}
|
||||
|
||||
void reload_fonts(float quality) {
|
||||
|
@ -158,26 +159,48 @@ void free_fonts(void) {
|
|||
|
||||
static void draw_text_texture(Alignment align, float x, float y, Texture *tex) {
|
||||
float m = 1.0 / resources.fontren.quality;
|
||||
bool adjust = !(align & AL_Flag_NoAdjust);
|
||||
align &= 0xf;
|
||||
|
||||
switch(align) {
|
||||
case AL_Center:
|
||||
break;
|
||||
if(adjust) {
|
||||
switch(align) {
|
||||
case AL_Center:
|
||||
break;
|
||||
// tex->w/2 is integer division and must be done first
|
||||
case AL_Left:
|
||||
x += m*(tex->w/2);
|
||||
break;
|
||||
case AL_Right:
|
||||
x -= m*(tex->w/2);
|
||||
break;
|
||||
default:
|
||||
log_fatal("Invalid alignment %x", align);
|
||||
}
|
||||
|
||||
// tex->w/2 is integer division and must be done first
|
||||
case AL_Left:
|
||||
x += m*(tex->w/2);
|
||||
break;
|
||||
case AL_Right:
|
||||
x -= m*(tex->w/2);
|
||||
break;
|
||||
// if textures are odd pixeled, align them for ideal sharpness.
|
||||
|
||||
if(tex->w&1) {
|
||||
x += 0.5;
|
||||
}
|
||||
|
||||
if(tex->h&1) {
|
||||
y += 0.5;
|
||||
}
|
||||
} else {
|
||||
switch(align) {
|
||||
case AL_Center:
|
||||
break;
|
||||
case AL_Left:
|
||||
x += m*(tex->w/2.0);
|
||||
break;
|
||||
case AL_Right:
|
||||
x -= m*(tex->w/2.0);
|
||||
break;
|
||||
default:
|
||||
log_fatal("Invalid alignment %x", align);
|
||||
}
|
||||
}
|
||||
|
||||
// if textures are odd pixeled, align them for ideal sharpness.
|
||||
if(tex->w&1)
|
||||
x += 0.5;
|
||||
if(tex->h&1)
|
||||
y += 0.5;
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(x, y, 0);
|
||||
glScalef(m, m, 1);
|
||||
|
|
|
@ -17,6 +17,9 @@ typedef enum {
|
|||
AL_Right
|
||||
} Alignment;
|
||||
|
||||
enum {
|
||||
AL_Flag_NoAdjust = 0x10,
|
||||
};
|
||||
|
||||
// Size of the buffer used by the font renderer at quality == 1.0.
|
||||
// No text larger than this can be drawn.
|
||||
|
@ -63,6 +66,7 @@ struct Fonts {
|
|||
TTF_Font *hud;
|
||||
TTF_Font *mono;
|
||||
TTF_Font *monosmall;
|
||||
TTF_Font *monotiny;
|
||||
};
|
||||
|
||||
extern struct Fonts _fonts;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "log.h"
|
||||
#include "stagetext.h"
|
||||
#include "stagedraw.h"
|
||||
#include "stageobjects.h"
|
||||
|
||||
static size_t numstages = 0;
|
||||
StageInfo *stages = NULL;
|
||||
|
@ -624,6 +625,7 @@ void stage_loop(StageInfo *stage) {
|
|||
// I really want to separate all of the game state from the global struct sometime
|
||||
global.stage = stage;
|
||||
|
||||
stage_objpools_alloc();
|
||||
stage_preload();
|
||||
stage_draw_preload();
|
||||
|
||||
|
@ -695,5 +697,6 @@ void stage_loop(StageInfo *stage) {
|
|||
player_free(&global.plr);
|
||||
tsrand_switch(&global.rand_visual);
|
||||
free_all_refs();
|
||||
stage_objpools_free();
|
||||
stop_sounds();
|
||||
}
|
||||
|
|
|
@ -13,8 +13,10 @@
|
|||
|
||||
#ifdef DEBUG
|
||||
#define GRAPHS_DEFAULT 1
|
||||
#define OBJPOOLSTATS_DEFAULT 1
|
||||
#else
|
||||
#define GRAPHS_DEFAULT 0
|
||||
#define OBJPOOLSTATS_DEFAULT 0
|
||||
#endif
|
||||
|
||||
static struct {
|
||||
|
@ -28,6 +30,7 @@ static struct {
|
|||
uint32_t u_split;
|
||||
} hud_text;
|
||||
bool framerate_graphs;
|
||||
bool objpool_stats;
|
||||
} stagedraw;
|
||||
|
||||
void stage_draw_preload(void) {
|
||||
|
@ -61,6 +64,7 @@ void stage_draw_preload(void) {
|
|||
glUseProgram(0);
|
||||
|
||||
stagedraw.framerate_graphs = getenvint("TAISEI_FRAMERATE_GRAPHS", GRAPHS_DEFAULT);
|
||||
stagedraw.objpool_stats = getenvint("TAISEI_OBJPOOL_STATS", OBJPOOLSTATS_DEFAULT);
|
||||
// As an optimization, static HUD text could be pre-rendered here.
|
||||
// However, it must be re-rendered on a TE_VIDEO_MODE_CHANGED event in that case.
|
||||
}
|
||||
|
@ -487,6 +491,22 @@ static void stage_draw_hud_scores(float ypos_hiscore, float ypos_score, char *bu
|
|||
glUniform1f(stagedraw.hud_text.u_split, 0.0);
|
||||
}
|
||||
|
||||
static void stage_draw_hud_objpool_stats(float x, float y, float width, TTF_Font *font) {
|
||||
ObjectPool **last = &stage_object_pools.first + (sizeof(StageObjectPools)/sizeof(ObjectPool*) - 1);
|
||||
|
||||
for(ObjectPool **pool = &stage_object_pools.first; pool <= last; ++pool) {
|
||||
ObjectPoolStats stats;
|
||||
char buf[32];
|
||||
objpool_get_stats(*pool, &stats);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%zu | %4zu", 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);
|
||||
|
||||
y += stringheight(buf, font) * 1.1;
|
||||
}
|
||||
}
|
||||
|
||||
struct labels_s {
|
||||
struct {
|
||||
float ofs;
|
||||
|
@ -520,6 +540,10 @@ void stage_draw_hud_text(struct labels_s* labels) {
|
|||
draw_text(AL_Left, labels->x.ofs, labels->y.graze, "Graze:", _fonts.hud);
|
||||
glUniform4f(stagedraw.hud_text.u_colortint, 1.00, 1.00, 1.00, 1.00);
|
||||
|
||||
if(stagedraw.objpool_stats) {
|
||||
stage_draw_hud_objpool_stats(labels->x.ofs, labels->y.graze + 32, 250, _fonts.monotiny);
|
||||
}
|
||||
|
||||
// Score/Hi-Score values
|
||||
stage_draw_hud_scores(labels->y.hiscore + labels->y.mono_ofs, labels->y.score + labels->y.mono_ofs, buf, sizeof(buf));
|
||||
|
||||
|
@ -549,7 +573,7 @@ void stage_draw_hud_text(struct labels_s* labels) {
|
|||
snprintf(buf, sizeof(buf), "%.2f fps", global.fps.fps);
|
||||
#endif
|
||||
|
||||
draw_text(AL_Right, SCREEN_W, rint(SCREEN_H - 0.5 * stringheight(buf, _fonts.monosmall)), buf, _fonts.monosmall);
|
||||
draw_text(AL_Right | AL_Flag_NoAdjust, SCREEN_W, rint(SCREEN_H - 0.5 * stringheight(buf, _fonts.monosmall)), buf, _fonts.monosmall);
|
||||
|
||||
if(global.replaymode == REPLAY_PLAY) {
|
||||
// XXX: does it make sense to use the monospace font here?
|
||||
|
|
31
src/stageobjects.c
Normal file
31
src/stageobjects.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
|
||||
#include "stageobjects.h"
|
||||
#include "projectile.h"
|
||||
#include "item.h"
|
||||
#include "enemy.h"
|
||||
#include "laser.h"
|
||||
#include "aniplayer.h"
|
||||
|
||||
#define MAX_PROJECTILES 8192
|
||||
#define MAX_ITEMS MAX_PROJECTILES
|
||||
#define MAX_ENEMIES 64
|
||||
#define MAX_LASERS 256
|
||||
#define MAX_ANIPLAYERS 32
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
23
src/stageobjects.h
Normal file
23
src/stageobjects.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "objectpool.h"
|
||||
|
||||
typedef struct StageObjectPools {
|
||||
union {
|
||||
struct {
|
||||
ObjectPool *projectiles; // includes particles as well
|
||||
ObjectPool *items;
|
||||
ObjectPool *enemies;
|
||||
ObjectPool *lasers;
|
||||
ObjectPool *aniplayers; // hack for the boss glow effect
|
||||
};
|
||||
|
||||
ObjectPool *first;
|
||||
};
|
||||
} StageObjectPools;
|
||||
|
||||
extern StageObjectPools stage_object_pools;
|
||||
|
||||
void stage_objpools_alloc(void);
|
||||
void stage_objpools_free(void);
|
Loading…
Reference in a new issue