optimize and simplify object pools
This commit is contained in:
parent
43ed2dcde2
commit
36c5d6974b
17 changed files with 89 additions and 172 deletions
|
@ -12,7 +12,6 @@
|
|||
#include "list.h"
|
||||
#include "global.h"
|
||||
#include "stageobjects.h"
|
||||
#include "objectpool_util.h"
|
||||
|
||||
void aniplayer_create(AniPlayer *plr, Animation *ani, const char *startsequence) {
|
||||
memset(plr,0,sizeof(AniPlayer));
|
||||
|
|
|
@ -63,9 +63,9 @@ Enemy *create_enemy_p(EnemyList *enemies, complex pos, float hp, EnemyVisualRule
|
|||
log_fatal("Tried to spawn an enemy while in drawing code");
|
||||
}
|
||||
|
||||
// XXX: some code relies on the insertion logic
|
||||
Enemy *e = (Enemy*)alist_insert(enemies, enemies->first, objpool_acquire(stage_object_pools.enemies));
|
||||
// Enemy *e = (Enemy*)alist_append(enemies, objpool_acquire(stage_object_pools.enemies));
|
||||
// FIXME: some code relies on the insertion logic (which?)
|
||||
Enemy *e = alist_insert(enemies, enemies->first, (Enemy*)objpool_acquire(stage_object_pools.enemies));
|
||||
// Enemy *e = alist_append(enemies, (Enemy*)objpool_acquire(stage_object_pools.enemies));
|
||||
e->moving = false;
|
||||
e->dir = 0;
|
||||
|
||||
|
@ -129,7 +129,7 @@ static void* _delete_enemy(ListAnchor *enemies, List* enemy, void *arg) {
|
|||
|
||||
e->logic_rule(e, EVENT_DEATH);
|
||||
ent_unregister(&e->ent);
|
||||
objpool_release(stage_object_pools.enemies, (ObjectInterface*)alist_unlink(enemies, enemy));
|
||||
objpool_release(stage_object_pools.enemies, alist_unlink(enemies, enemy));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ typedef void (*EntityDrawHookCallback)(EntityInterface *ent, void *arg);
|
|||
typedef void (*EntityAreaDamageCallback)(EntityInterface *ent, complex ent_origin, void *arg);
|
||||
|
||||
#define ENTITY_INTERFACE_BASE(typename) struct { \
|
||||
OBJECT_INTERFACE(typename); \
|
||||
LIST_INTERFACE(typename); \
|
||||
EntityType type; \
|
||||
EntityDrawFunc draw_func; \
|
||||
EntityDamageFunc damage_func; \
|
||||
|
@ -100,7 +100,7 @@ typedef void (*EntityAreaDamageCallback)(EntityInterface *ent, complex ent_origi
|
|||
}
|
||||
|
||||
#define ENTITY_INTERFACE_NAMED(typename, name) union { \
|
||||
OBJECT_INTERFACE(typename); \
|
||||
LIST_INTERFACE(typename); \
|
||||
EntityInterface entity_interface; \
|
||||
EntityInterface name; \
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include "global.h"
|
||||
#include "list.h"
|
||||
#include "stageobjects.h"
|
||||
#include "objectpool_util.h"
|
||||
|
||||
static const char* item_sprite_name(ItemType type) {
|
||||
static const char *const map[] = {
|
||||
|
@ -85,7 +84,7 @@ Item* create_item(complex pos, complex v, ItemType type) {
|
|||
|
||||
void delete_item(Item *item) {
|
||||
ent_unregister(&item->ent);
|
||||
objpool_release(stage_object_pools.items, &alist_unlink(&global.items, item)->object_interface);
|
||||
objpool_release(stage_object_pools.items, alist_unlink(&global.items, item));
|
||||
}
|
||||
|
||||
Item *create_clear_item(complex pos, uint clear_flags) {
|
||||
|
|
|
@ -102,7 +102,7 @@ void lasers_free(void) {
|
|||
static void ent_draw_laser(EntityInterface *ent);
|
||||
|
||||
Laser *create_laser(complex pos, float time, float deathtime, const Color *color, LaserPosRule prule, LaserLogicRule lrule, complex a0, complex a1, complex a2, complex a3) {
|
||||
Laser *l = (Laser*)alist_push(&global.lasers, objpool_acquire(stage_object_pools.lasers));
|
||||
Laser *l = alist_push(&global.lasers, (Laser*)objpool_acquire(stage_object_pools.lasers));
|
||||
|
||||
l->birthtime = global.frames;
|
||||
l->timespan = time;
|
||||
|
@ -345,7 +345,7 @@ static void* _delete_laser(ListAnchor *lasers, List *laser, void *arg) {
|
|||
l->lrule(l, EVENT_DEATH);
|
||||
|
||||
ent_unregister(&l->ent);
|
||||
objpool_release(stage_object_pools.lasers, (ObjectInterface*)alist_unlink(lasers, laser));
|
||||
objpool_release(stage_object_pools.lasers, alist_unlink(lasers, laser));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,6 @@ taisei_src = files(
|
|||
'list.c',
|
||||
'log.c',
|
||||
'main.c',
|
||||
'objectpool_util.c',
|
||||
'player.c',
|
||||
'plrmodes.c',
|
||||
'progress.c',
|
||||
|
|
|
@ -12,42 +12,45 @@
|
|||
#include "util.h"
|
||||
#include "list.h"
|
||||
|
||||
typedef struct ObjHeader {
|
||||
alignas(alignof(max_align_t)) struct ObjHeader *next;
|
||||
} ObjHeader;
|
||||
|
||||
struct ObjectPool {
|
||||
char *tag;
|
||||
size_t size_of_object;
|
||||
size_t max_objects;
|
||||
#ifdef OBJPOOL_TRACK_STATS
|
||||
size_t usage;
|
||||
size_t peak_usage;
|
||||
#endif
|
||||
size_t num_extents;
|
||||
char **extents;
|
||||
ObjectInterface *free_objects;
|
||||
ObjHeader *free_objects;
|
||||
char objects[];
|
||||
};
|
||||
|
||||
static inline ObjectInterface* obj_ptr(ObjectPool *pool, char *objects, size_t idx) {
|
||||
return (ObjectInterface*)(void*)(objects + idx * pool->size_of_object);
|
||||
inline attr_must_inline attr_returns_max_aligned
|
||||
static ObjHeader *obj_ptr(ObjectPool *pool, char *objects, size_t idx) {
|
||||
return CASTPTR_ASSUME_ALIGNED(objects + idx * pool->size_of_object, ObjHeader);
|
||||
}
|
||||
|
||||
static void objpool_register_objects(ObjectPool *pool, char *objects) {
|
||||
for(size_t i = 0; i < pool->max_objects; ++i) {
|
||||
list_push(&pool->free_objects, obj_ptr(pool, objects, i));
|
||||
ObjHeader *o = obj_ptr(pool, objects, i);
|
||||
o->next = pool->free_objects;
|
||||
pool->free_objects = o;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
ObjectPool *pool = calloc(1, 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->num_extents = 0;
|
||||
pool->extents = NULL;
|
||||
pool->tag = strdup(tag);
|
||||
|
||||
memset(pool->objects, 0, obj_size * max_objects);
|
||||
objpool_register_objects(pool, pool->objects);
|
||||
|
||||
log_debug("[%s] Allocated pool for %zu objects, %zu bytes each",
|
||||
|
@ -89,22 +92,20 @@ static char* objpool_fmt_size(ObjectPool *pool) {
|
|||
}
|
||||
}
|
||||
|
||||
ObjectInterface *objpool_acquire(ObjectPool *pool) {
|
||||
ObjectInterface *obj = list_pop(&pool->free_objects);
|
||||
void *objpool_acquire(ObjectPool *pool) {
|
||||
ObjHeader *obj = pool->free_objects;
|
||||
|
||||
if(obj) {
|
||||
acquired:
|
||||
pool->free_objects = obj->next;
|
||||
memset(obj, 0, pool->size_of_object);
|
||||
|
||||
IF_OBJPOOL_DEBUG({
|
||||
obj->_object_private.used = true;
|
||||
})
|
||||
|
||||
#ifdef OBJPOOL_TRACK_STATS
|
||||
if(++pool->usage > pool->peak_usage) {
|
||||
pool->peak_usage = pool->usage;
|
||||
}
|
||||
#endif
|
||||
|
||||
// log_debug("[%s] Usage: %zu", pool->tag, pool->usage);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -116,39 +117,27 @@ acquired:
|
|||
free(tmp);
|
||||
|
||||
objpool_add_extent(pool);
|
||||
obj = list_pop(&pool->free_objects);
|
||||
obj = pool->free_objects;
|
||||
assert(obj != NULL);
|
||||
goto acquired;
|
||||
}
|
||||
|
||||
void objpool_release(ObjectPool *pool, ObjectInterface *object) {
|
||||
void objpool_release(ObjectPool *pool, void *object) {
|
||||
objpool_memtest(pool, object);
|
||||
|
||||
IF_OBJPOOL_DEBUG({
|
||||
if(!object->_object_private.used) {
|
||||
log_fatal("[%s] Attempted to release an unused object %p",
|
||||
pool->tag,
|
||||
(void*)object
|
||||
);
|
||||
}
|
||||
|
||||
object->_object_private.used = false;
|
||||
})
|
||||
|
||||
list_push(&pool->free_objects, object);
|
||||
|
||||
ObjHeader *obj = object;
|
||||
obj->next = pool->free_objects;
|
||||
pool->free_objects = obj;
|
||||
#ifdef OBJPOOL_TRACK_STATS
|
||||
pool->usage--;
|
||||
// log_debug("[%s] Usage: %zu", pool->tag, pool->usage);
|
||||
#endif
|
||||
}
|
||||
|
||||
void objpool_free(ObjectPool *pool) {
|
||||
if(!pool) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef OBJPOOL_TRACK_STATS
|
||||
if(pool->usage != 0) {
|
||||
log_warn("[%s] %zu objects still in use", pool->tag, pool->usage);
|
||||
}
|
||||
#endif
|
||||
|
||||
for(size_t i = 0; i < pool->num_extents; ++i) {
|
||||
free(pool->extents[i]);
|
||||
|
@ -166,12 +155,17 @@ size_t objpool_object_size(ObjectPool *pool) {
|
|||
void objpool_get_stats(ObjectPool *pool, ObjectPoolStats *stats) {
|
||||
stats->tag = pool->tag;
|
||||
stats->capacity = pool->max_objects * (1 + pool->num_extents);
|
||||
#ifdef OBJPOOL_TRACK_STATS
|
||||
stats->usage = pool->usage;
|
||||
stats->peak_usage = pool->peak_usage;
|
||||
#else
|
||||
stats->usage = 0;
|
||||
stats->peak_usage = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
attr_unused
|
||||
static bool objpool_object_in_subpool(ObjectPool *pool, ObjectInterface *object, char *objects) {
|
||||
static bool objpool_object_in_subpool(ObjectPool *pool, ObjHeader *object, char *objects) {
|
||||
char *objofs = (char*)object;
|
||||
char *minofs = objects;
|
||||
char *maxofs = objects + (pool->max_objects - 1) * pool->size_of_object;
|
||||
|
@ -194,7 +188,7 @@ static bool objpool_object_in_subpool(ObjectPool *pool, ObjectInterface *object,
|
|||
}
|
||||
|
||||
attr_unused
|
||||
static bool objpool_object_in_pool(ObjectPool *pool, ObjectInterface *object) {
|
||||
static bool objpool_object_in_pool(ObjectPool *pool, ObjHeader *object) {
|
||||
if(objpool_object_in_subpool(pool, object, pool->objects)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -208,16 +202,13 @@ static bool objpool_object_in_pool(ObjectPool *pool, ObjectInterface *object) {
|
|||
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
|
||||
);
|
||||
}
|
||||
})
|
||||
#ifdef OBJPOOL_DEBUG
|
||||
void objpool_memtest(ObjectPool *pool, void *object) {
|
||||
if(!objpool_object_in_pool(pool, object)) {
|
||||
log_fatal("[%s] Object pointer %p does not belong to this pool",
|
||||
pool->tag,
|
||||
object
|
||||
);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -18,13 +18,13 @@
|
|||
#endif
|
||||
|
||||
#ifdef OBJPOOL_DEBUG
|
||||
#define OBJPOOL_TRACK_STATS
|
||||
#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 {
|
||||
|
@ -34,46 +34,20 @@ 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 {
|
||||
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);
|
||||
|
||||
#define OBJPOOL_ALLOC(typename,max_objects) objpool_alloc(sizeof(typename), max_objects, #typename)
|
||||
#define OBJPOOL_ACQUIRE(pool, type) CASTPTR_ASSUME_ALIGNED(objpool_acquire(pool), type)
|
||||
|
||||
ObjectPool *objpool_alloc(size_t obj_size, size_t max_objects, const char *tag) attr_returns_nonnull attr_nodiscard attr_nonnull(3);
|
||||
void objpool_free(ObjectPool *pool) attr_nonnull(1);
|
||||
void *objpool_acquire(ObjectPool *pool) attr_returns_max_aligned attr_returns_nonnull attr_nodiscard attr_hot attr_nonnull(1);
|
||||
void objpool_release(ObjectPool *pool, void *object) attr_hot attr_nonnull(1, 2);
|
||||
void objpool_get_stats(ObjectPool *pool, ObjectPoolStats *stats) attr_nonnull(1, 2);
|
||||
size_t objpool_object_size(ObjectPool *pool) attr_nonnull(1);
|
||||
|
||||
#ifdef OBJPOOL_DEBUG
|
||||
void objpool_memtest(ObjectPool *pool, void *object) attr_nonnull(1, 2);
|
||||
#else
|
||||
#define objpool_memtest(pool, object) ((void)(pool), (void)(object))
|
||||
#endif
|
||||
|
||||
#endif // IGUARD_objectpool_h
|
||||
|
|
|
@ -21,11 +21,11 @@ ObjectPool *objpool_alloc(size_t obj_size, size_t max_objects, const char *tag)
|
|||
return pool;
|
||||
}
|
||||
|
||||
ObjectInterface *objpool_acquire(ObjectPool *pool) {
|
||||
void *objpool_acquire(ObjectPool *pool) {
|
||||
return calloc(1, pool->size_of_object);
|
||||
}
|
||||
|
||||
void objpool_release(ObjectPool *pool, ObjectInterface *object) {
|
||||
void objpool_release(ObjectPool *pool, void *object) {
|
||||
free(object);
|
||||
}
|
||||
|
||||
|
@ -38,11 +38,11 @@ void objpool_get_stats(ObjectPool *pool, ObjectPoolStats *stats) {
|
|||
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;
|
||||
}
|
||||
|
||||
#ifdef OBJPOOL_DEBUG
|
||||
void objpool_memtest(ObjectPool *pool, void *object) {
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* This software is licensed under the terms of the MIT-License
|
||||
* See COPYING for further information.
|
||||
* ---
|
||||
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
||||
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@alienslab.net>.
|
||||
*/
|
||||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "objectpool_util.h"
|
||||
#include "util.h"
|
||||
|
||||
bool objpool_is_full(ObjectPool *pool) {
|
||||
ObjectPoolStats stats;
|
||||
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);
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* This software is licensed under the terms of the MIT-License
|
||||
* See COPYING for further information.
|
||||
* ---
|
||||
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
||||
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@alienslab.net>.
|
||||
*/
|
||||
|
||||
#ifndef IGUARD_objectpool_util_h
|
||||
#define IGUARD_objectpool_util_h
|
||||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "objectpool.h"
|
||||
|
||||
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);
|
||||
|
||||
#endif // IGUARD_objectpool_util_h
|
|
@ -295,7 +295,7 @@ static void* _delete_projectile(ListAnchor *projlist, List *proj, void *arg) {
|
|||
Projectile *p = (Projectile*)proj;
|
||||
proj_call_rule(p, EVENT_DEATH);
|
||||
ent_unregister(&p->ent);
|
||||
objpool_release(stage_object_pools.projectiles, (ObjectInterface*)alist_unlink(projlist, proj));
|
||||
objpool_release(stage_object_pools.projectiles, alist_unlink(projlist, proj));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
#include "stagetext.h"
|
||||
#include "aniplayer.h"
|
||||
|
||||
#define MAX_projectiles 1024
|
||||
#define MAX_projectiles 2048
|
||||
#define MAX_items MAX_projectiles
|
||||
#define MAX_enemies 64
|
||||
#define MAX_lasers 64
|
||||
#define MAX_stagetext 128
|
||||
#define MAX_stagetext 1024
|
||||
|
||||
#define OBJECT_POOLS \
|
||||
OBJECT_POOL(Projectile, projectiles) \
|
||||
|
|
|
@ -51,7 +51,7 @@ StageText* stagetext_add_numeric(int n, complex pos, Alignment align, Font *font
|
|||
}
|
||||
|
||||
static void* stagetext_delete(List **dest, List *txt, void *arg) {
|
||||
objpool_release(stage_object_pools.stagetext, (ObjectInterface*)list_unlink(dest, txt));
|
||||
objpool_release(stage_object_pools.stagetext, list_unlink(dest, txt));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ typedef struct StageTextTable StageTextTable;
|
|||
#define STAGETEXT_BUF_SIZE 76
|
||||
|
||||
struct StageText {
|
||||
OBJECT_INTERFACE(StageText);
|
||||
LIST_INTERFACE(StageText);
|
||||
|
||||
Font *font;
|
||||
complex pos;
|
||||
|
|
|
@ -233,6 +233,14 @@ typedef complex max_align_t;
|
|||
#define attr_must_inline \
|
||||
__attribute__ ((always_inline))
|
||||
|
||||
// Function returns a pointer aligned to x bytes
|
||||
#define attr_returns_aligned(x) \
|
||||
__attribute__ ((assume_aligned(x)))
|
||||
|
||||
// Function returns a pointer aligned the same as max_align_t
|
||||
#define attr_returns_max_aligned \
|
||||
attr_returns_aligned(alignof(max_align_t))
|
||||
|
||||
// Structure must not be initialized with an implicit (non-designated) initializer.
|
||||
#if __has_attribute(designated_init) && defined(TAISEI_BUILDCONF_USE_DESIGNATED_INIT)
|
||||
#define attr_designated_init \
|
||||
|
|
|
@ -40,7 +40,7 @@ void capproach_asymptotic_p(complex *val, complex target, double rate, double ep
|
|||
double psin(double) attr_const;
|
||||
int sign(double) attr_const;
|
||||
double swing(double x, double s) attr_const;
|
||||
uint32_t topow2_u32(uint32_t x) attr_const;
|
||||
uint32_t topow2_u32(uint32_t x) attr_const;
|
||||
uint64_t topow2_u64(uint64_t x) attr_const;
|
||||
float ftopow2(float x) attr_const;
|
||||
float smooth(float x) attr_const;
|
||||
|
|
Loading…
Reference in a new issue