You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

809 lines
16 KiB
C

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "linalg.h"
#define MINCAP 128
#define VECTOR4 4
#define MATRIX 16
// Constant: version should be 0, id is the constant id, persistent should be 1
static float c_ident_vec[4] = { 0,0,0,1 };
static float c_ident_mat[16] = {
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1,
};
static float c_ident_quat[4] = {
0, 0, 0, 1,
};
struct constant {
float * ptr;
int size;
};
static struct constant c_constant_table[LINEAR_TYPE_COUNT] = {
{ c_ident_mat, MATRIX },
{ c_ident_vec, VECTOR4 },
{ c_ident_quat, VECTOR4 },
};
struct stackid_ {
uint32_t version:24;
uint32_t id:24;
uint32_t type : LINEAR_TYPE_BITS_NUM; // 0:matrix 1:vector4 2:float 3:quaternion 4:euler
uint32_t persistent:1; // 0: persisent 1: temp
};
union stackid {
struct stackid_ s;
int64_t i;
};
int64_t
lastack_constant(int cons) {
if (cons < 0 || cons >= LINEAR_TYPE_COUNT)
return 0;
union stackid sid;
sid.s.version = 0;
sid.s.id = cons;
sid.s.persistent = 1;
sid.s.type = cons;
return sid.i;
}
struct lastack {
int temp_vector_cap;
int temp_vector_top;
int temp_matrix_cap;
int temp_matrix_top;
int version;
int stack_cap;
int stack_top;
float * temp_vec;
float * temp_mat;
struct blob * per_vec;
struct blob * per_mat;
struct oldpage *old;
union stackid *stack;
size_t oldpage_size;
};
#define TAG_FREE 0
#define TAG_USED 1
#define TAG_WILLFREE 2
struct slot {
uint32_t id : 30;
uint32_t tag : 2;
};
struct oldpage {
struct oldpage *next;
void *page;
};
struct blob {
int size;
int cap;
int freeslot; // free slot list
int freelist; // will free
char * buffer;
struct slot *s;
struct oldpage *old;
size_t oldpage_size;
};
static inline void
init_blob_slots(struct blob * B, int slot_beg, int slot_end) {
int i;
for (i = slot_beg; i < slot_end; ++i) {
B->s[i].tag = TAG_FREE;
B->s[i].id = i + 2;
}
B->s[slot_end - 1].id = 0;
B->freeslot = slot_beg + 1;
}
static struct blob *
blob_new(int size, int cap) {
struct blob * B = malloc(sizeof(*B));
B->size = size;
B->cap = cap;
B->freelist = 0; // empty list
B->buffer = malloc(size * cap);
B->s = malloc(cap * sizeof(*B->s));
init_blob_slots(B, 0, cap);
B->old = NULL;
B->oldpage_size = 0;
return B;
}
static size_t
blob_size(struct blob *B) {
return sizeof(*B) + (B->size + sizeof(*B->s)) * B->cap + B->oldpage_size;
}
static void
free_oldpage(struct oldpage *p) {
while (p) {
struct oldpage *next = p->next;
free(p->page);
free(p);
p = next;
}
}
#define SLOT_INDEX(idx) ((idx)-1)
#define SLOT_EMPTY(idx) ((idx)==0)
static int
blob_alloc(struct blob *B, int version) {
if (SLOT_EMPTY(B->freeslot)) {
struct oldpage * p = malloc(sizeof(*p));
p->next = B->old;
p->page = B->buffer;
B->old = p;
int cap = B->cap;
B->oldpage_size += sizeof(*p) + B->size * cap;
B->cap *= 2;
B->buffer = malloc(B->size * B->cap);
memcpy(B->buffer, p->page, B->size * cap);
B->s = realloc(B->s, B->cap * sizeof(*B->s));
static int alloc_count = 0;
alloc_count ++;
init_blob_slots(B, cap, B->cap);
}
int ret = SLOT_INDEX(B->freeslot);
struct slot *s = &B->s[ret];
B->freeslot = s->id; // next free slot
s->tag = TAG_USED;
s->id = version;
return ret;
}
static void *
blob_address(struct blob *B, int index, int version) {
struct slot *s = &B->s[index];
if (s->tag != TAG_USED || s->id != version)
return NULL;
return B->buffer + index * B->size;
}
static void
blob_dealloc(struct blob *B, int index, int version) {
struct slot *s = &B->s[index];
if (s->tag != TAG_USED || s->id != version)
return;
s->id = B->freelist;
s->tag = TAG_WILLFREE;
B->freelist = index + 1;
}
static void
blob_flush(struct blob *B) {
int slot = B->freelist;
while (slot) {
struct slot *s = &B->s[SLOT_INDEX(slot)];
s->tag = TAG_FREE;
if (SLOT_EMPTY(s->id)) {
s->id = B->freeslot;
B->freeslot = B->freelist;
B->freelist = 0;
break;
}
slot = s->id;
}
free_oldpage(B->old);
B->old = NULL;
B->oldpage_size = 0;
}
static void
blob_delete(struct blob *B) {
if (B) {
free(B->buffer);
free(B->s);
free_oldpage(B->old);
free(B);
}
}
static void
print_list(struct blob *B, const char * h, int list, int tag) {
printf("%s ", h);
while (!SLOT_EMPTY(list)) {
int index = SLOT_INDEX(list);
struct slot *s = &B->s[index];
if (s->tag == tag) {
printf("%d,", SLOT_INDEX(list));
} else {
printf("%d [ERROR]", SLOT_INDEX(list));
break;
}
list = s->id;
}
printf("\n");
}
static void
blob_print(struct blob *B) {
int i;
printf("USED: ");
for (i=0;i<B->cap;i++) {
if (B->s[i].tag == TAG_USED) {
printf("%d,", i);
}
}
printf("\n");
print_list(B, "FREE :", B->freeslot, TAG_FREE);
print_list(B, "WILLFREE :", B->freelist, TAG_WILLFREE);
}
#if 0
int
blob_test_main() {
struct blob *B = blob_new(4, 10);
int a = blob_alloc(B,1);
int b = blob_alloc(B,1);
int c = blob_alloc(B,1);
blob_print(B);
blob_dealloc(B, a, 1);
blob_print(B);
blob_flush(B);
blob_print(B);
return 0;
}
#endif
struct lastack *
lastack_new() {
struct lastack * LS = malloc(sizeof(*LS));
LS->temp_vector_cap = MINCAP;
LS->temp_vector_top = 0;
LS->temp_matrix_cap = MINCAP;
LS->temp_matrix_top = 0;
LS->version = 1; // base 1
LS->stack_cap = MINCAP;
LS->stack_top = 0;
LS->temp_vec = malloc(LS->temp_vector_cap * VECTOR4 * sizeof(float));
LS->temp_mat = malloc(LS->temp_matrix_cap * MATRIX * sizeof(float));
LS->per_vec = blob_new(VECTOR4 * sizeof(float), MINCAP);
LS->per_mat = blob_new(MATRIX * sizeof(float), MINCAP);
LS->old = NULL;
LS->stack = malloc(LS->stack_cap * sizeof(*LS->stack));
LS->oldpage_size = 0;
return LS;
}
size_t
lastack_size(struct lastack *LS) {
return sizeof(*LS)
+ LS->temp_vector_cap * VECTOR4 * sizeof(float)
+ LS->temp_matrix_cap * MATRIX * sizeof(float)
+ LS->stack_cap * sizeof(*LS->stack)
+ blob_size(LS->per_vec)
+ blob_size(LS->per_mat);
}
void
lastack_delete(struct lastack *LS) {
if (LS == NULL)
return;
free(LS->temp_vec);
free(LS->temp_mat);
blob_delete(LS->per_vec);
blob_delete(LS->per_mat);
free(LS->stack);
free_oldpage(LS->old);
free(LS);
}
static inline void
push_id(struct lastack *LS, union stackid id) {
if (LS->stack_top >= LS->stack_cap) {
LS->stack = realloc(LS->stack, (LS->stack_cap *= 2) * sizeof(*LS->stack));
}
LS->stack[LS->stack_top++] = id;
}
static void *
new_page(struct lastack *LS, void *page, size_t page_size) {
struct oldpage * p = malloc(sizeof(*p));
p->next = LS->old;
p->page = page;
LS->old = p;
LS->oldpage_size += page_size + sizeof(*p);
return page;
}
int
lastack_typesize(int type) {
const int sizes[LINEAR_TYPE_COUNT] = { 16, 4, 4 };
// assert(LINEAR_TYPE_MAT <= type && type < LINEAR_TYPE_COUNT);
return sizes[type];
}
const char *
lastack_typename(int t) {
static const char * type_names[] = {
"mat",
"v4",
"quat",
};
if (t < 0 || t >= sizeof(type_names)/sizeof(type_names[0]))
return "unknown";
return type_names[t];
}
static float *
check_matrix_pool(struct lastack *LS) {
if (LS->temp_matrix_top >= LS->temp_matrix_cap) {
size_t sz = LS->temp_matrix_cap * sizeof(float) * MATRIX;
void * p = new_page(LS, LS->temp_mat, sz);
LS->temp_mat = malloc(sz * 2);
memcpy(LS->temp_mat, p, sz);
LS->temp_matrix_cap *= 2;
}
return LS->temp_mat + LS->temp_matrix_top * MATRIX;
}
void
lastack_pushmatrix(struct lastack *LS, const float *mat) {
float * pmat = check_matrix_pool(LS);
memcpy(pmat, mat, sizeof(float) * MATRIX);
union stackid sid;
sid.s.type = LINEAR_TYPE_MAT;
sid.s.persistent = 0;
sid.s.version = LS->version;
sid.s.id = LS->temp_matrix_top;
push_id(LS, sid);
++ LS->temp_matrix_top;
}
void
lastack_pushsrt(struct lastack *LS, const float *s, const float *r, const float *t) {
#define NOTIDENTITY (~0)
float * mat = check_matrix_pool(LS);
uint32_t * mark = (uint32_t *)&mat[3*4];
// scale
float *scale = &mat[0];
if (s == NULL) {
scale[0] = 1;
scale[1] = 1;
scale[2] = 1;
scale[3] = 0;
mark[0] = 0;
} else {
float sx = s[0];
float sy = s[1];
float sz = s[2];
scale[0] = sx;
scale[1] = sy;
scale[2] = sz;
scale[3] = 0;
if (sx == 1 && sy == 1 && sz == 1) {
mark[0] = NOTIDENTITY;
}
}
// rotation
float *rotation = &mat[4*1];
if (r == NULL || (r[0] == 0 && r[1] == 0 && r[2] == 0 && r[3] == 1)) {
rotation[0] = 0;
rotation[1] = 0;
rotation[2] = 0;
rotation[3] = 1;
mark[1] = 0;
} else {
rotation[0] = r[0];
rotation[1] = r[1];
rotation[2] = r[2];
rotation[3] = r[3];
mark[1] = NOTIDENTITY;
}
// translate
float *translate = &mat[4*2];
if (t == NULL || (t[0] == 0 && t[1] == 0 && t[2] == 0)) {
translate[0] = 0;
translate[1] = 0;
translate[2] = 0;
translate[3] = 1;
mark[2] = 0;
} else {
translate[0] = t[0];
translate[1] = t[1];
translate[2] = t[2];
translate[3] = 1;
mark[2] = NOTIDENTITY;
}
// mark identity
if (mark[0] == 0 && mark[1] == 0 && mark[2] == 0) {
mark[3] = 0;
} else {
mark[3] = NOTIDENTITY;
}
union stackid sid;
sid.s.type = LINEAR_TYPE_MAT;
sid.s.persistent = 0;
sid.s.version = LS->version;
sid.s.id = LS->temp_matrix_top;
push_id(LS, sid);
++ LS->temp_matrix_top;
}
void
lastack_pushobject(struct lastack *LS, const float *v, int type) {
if (type == LINEAR_TYPE_MAT) {
lastack_pushmatrix(LS, v);
return;
}
assert(type >= LINEAR_TYPE_VEC4 && type <= LINEAR_TYPE_QUAT);
const int size = lastack_typesize(type);
if (LS->temp_vector_top >= LS->temp_vector_cap) {
size_t sz = LS->temp_vector_cap * sizeof(float) * VECTOR4;
void * p = new_page(LS, LS->temp_vec, sz);
LS->temp_vec = malloc(sz * 2);
memcpy(LS->temp_vec, p, sz);
LS->temp_vector_cap *= 2;
}
memcpy(LS->temp_vec + LS->temp_vector_top * VECTOR4, v, sizeof(float) * size);
union stackid sid;
sid.s.type = type;
sid.s.persistent = 0;
sid.s.version = LS->version;
sid.s.id = LS->temp_vector_top;
push_id(LS, sid);
++ LS->temp_vector_top;
}
void
lastack_pushvec4(struct lastack *LS, const float *vec4) {
lastack_pushobject(LS, vec4, LINEAR_TYPE_VEC4);
}
void
lastack_pushquat(struct lastack *LS, const float *v) {
lastack_pushobject(LS, v, LINEAR_TYPE_QUAT);
}
const float *
lastack_value(struct lastack *LS, int64_t ref, int *type) {
union stackid sid;
sid.i = ref;
int id = sid.s.id;
int ver = sid.s.version;
void * address = NULL;
if (type)
*type = sid.s.type;
if (sid.s.persistent) {
if (sid.s.version == 0) {
// constant
int id = sid.s.id;
if (id < 0 || id >= LINEAR_TYPE_COUNT)
return NULL;
struct constant * c = &c_constant_table[id];
return c->ptr;
}
if (lastack_typesize(sid.s.type) == MATRIX) {
address = blob_address( LS->per_mat , id, ver);
} else {
address = blob_address( LS->per_vec , id, ver);
}
return address;
} else {
if (ver != LS->version) {
// version expired
return NULL;
}
if (lastack_typesize(sid.s.type) == MATRIX) {
if (id >= LS->temp_matrix_top) {
return NULL;
}
return LS->temp_mat + id * MATRIX;
} else {
if (id >= LS->temp_vector_top) {
return NULL;
}
return LS->temp_vec + id * VECTOR4;
}
}
}
int
lastack_pushref(struct lastack *LS, int64_t ref) {
union stackid id;
id.i = ref;
// check alive
const void *address = lastack_value(LS, id.i, NULL);
if (address == NULL)
return 1;
push_id(LS, id);
return 0;
}
void
lastack_unmark(struct lastack *LS, int64_t markid) {
union stackid id;
id.i = markid;
if (id.s.persistent && id.s.version != 0) {
if (lastack_typesize(id.s.type) != MATRIX) {
blob_dealloc(LS->per_vec, id.s.id, id.s.version);
} else {
blob_dealloc(LS->per_mat, id.s.id, id.s.version);
}
}
}
int
lastack_isconstant(int64_t markid) {
union stackid id;
id.i = markid;
return (id.s.persistent && id.s.version == 0);
}
int64_t
lastack_mark(struct lastack *LS, int64_t tempid) {
if (lastack_isconstant(tempid))
return tempid;
int t;
const float *address = lastack_value(LS, tempid, &t);
if (address == NULL) {
//printf("--- mark address = null ---");
return 0;
}
int id;
union stackid sid;
sid.s.version = LS->version;
sid.s.type = t;
if (lastack_typesize(t) != MATRIX) {
id = blob_alloc(LS->per_vec, LS->version);
void * dest = blob_address(LS->per_vec, id, LS->version);
memcpy(dest, address, sizeof(float) * VECTOR4);
} else {
id = blob_alloc(LS->per_mat, LS->version);
void * dest = blob_address(LS->per_mat, id, LS->version);
memcpy(dest, address, sizeof(float) * MATRIX);
}
sid.s.id = id;
if (sid.s.id != id) {
//printf(" --- s.id(%d) != id(%d) --- \n ",sid.s.id,id);
return 0;
}
sid.s.persistent = 1;
return sid.i;
}
int
lastack_marked(int64_t id, int *type) {
union stackid sid;
sid.i = id;
if (type) {
*type = sid.s.type;
}
return sid.s.persistent;
}
int
lastack_sametype(int64_t id1, int64_t id2) {
union stackid sid1,sid2;
sid1.i = id1;
sid2.i = id2;
return sid1.s.type == sid2.s.type;
}
int64_t
lastack_pop(struct lastack *LS) {
if (LS->stack_top <= 0)
return 0;
union stackid sid = LS->stack[--LS->stack_top];
return sid.i;
}
int64_t
lastack_top(struct lastack *LS) {
if (LS->stack_top <= 0)
return 0;
union stackid sid = LS->stack[LS->stack_top-1];
return sid.i;
}
int64_t
lastack_dup(struct lastack *LS, int index) {
if (LS->stack_top < index || index <= 0)
return 0;
union stackid sid = LS->stack[LS->stack_top-index];
push_id(LS, sid);
return sid.i;
}
int64_t
lastack_swap(struct lastack *LS) {
if (LS->stack_top <= 1)
return 0;
union stackid top = LS->stack[LS->stack_top-1];
union stackid newtop = LS->stack[LS->stack_top-2];
LS->stack[LS->stack_top-2] = top;
LS->stack[LS->stack_top-1] = newtop;
return newtop.i;
}
void
lastack_reset(struct lastack *LS) {
union stackid v;
v.s.version = LS->version + 1;
if (v.s.version == 0)
++ v.s.version;
LS->version = v.s.version;
LS->stack_top = 0;
free_oldpage(LS->old);
LS->old = NULL;
LS->oldpage_size = 0;
blob_flush(LS->per_vec);
blob_flush(LS->per_mat);
LS->temp_vector_top = 0;
LS->temp_matrix_top = 0;
}
static void
print_float(const float *address, int n) {
int i;
for (i=0;i<n-1;i++) {
printf("%.3g ", address[i]);
}
printf("%.3g",address[i]);
}
static void
print_object(const float *address, int id, int type) {
switch(type) {
case LINEAR_TYPE_NONE:
printf("(None");
break;
case LINEAR_TYPE_MAT:
printf("(M%d: ",id);
print_float(address, 16);
break;
case LINEAR_TYPE_VEC4:
printf("(V%d: ",id);
print_float(address, 4);
break;
case LINEAR_TYPE_QUAT:
printf("(Q%d: ",id);
print_float(address, 4);
break;
default:
printf("(Invalid");
break;
}
printf(")");
}
void
lastack_print(struct lastack *LS) {
printf("version = %d\n", LS->version);
printf("stack %d/%d:\n", LS->stack_top, LS->stack_cap);
int i;
for (i=0;i<LS->stack_top;i++) {
union stackid id = LS->stack[i];
int type;
const float *address = lastack_value(LS, id.i, &type);
printf("\t[%d]", i);
if (id.s.persistent) {
printf("version = %d ", id.s.version);
}
print_object(address, id.s.id, type);
printf("\n");
}
printf("Persistent Vector ");
blob_print(LS->per_vec);
printf("Persistent Matrix ");
blob_print(LS->per_mat);
}
int
lastack_gettop(struct lastack *LS) {
return LS->stack_top;
}
void
lastack_dump(struct lastack *LS, int from) {
if (from < 0) {
from = LS->stack_top + from;
if (from < 0)
from = 0;
}
int i;
for (i=LS->stack_top-1;i>=from;i--) {
union stackid id = LS->stack[i];
int type;
const float *address = lastack_value(LS, id.i, &type);
print_object(address, id.s.id, type);
}
if (from > 0) {
for (i=0;i<from;i++) {
printf(".");
}
}
}
int
lastack_type(struct lastack *LS, int64_t id) {
union stackid sid;
sid.i = id;
return sid.s.type;
}
char *
lastack_idstring(int64_t id, char tmp[64]) {
union stackid sid;
sid.i = id;
char flags[3] = { 0,0,0 };
switch(sid.s.type) {
case LINEAR_TYPE_MAT:
flags[0] = 'M';
break;
case LINEAR_TYPE_VEC4:
flags[0] = 'V';
break;
case LINEAR_TYPE_QUAT:
flags[0] = 'Q';
break;
default:
flags[0] = '?';
break;
}
if (sid.s.persistent) {
flags[1] = 'P';
}
snprintf(tmp, 64, "id=%d version=%d %s",sid.s.id, sid.s.version, flags);
return tmp;
}
#if 0
int
test_main() {
struct lastack *LS = lastack_new();
float v[4] = { 1,2,3,4 };
float m[16] = {
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1
};
lastack_pushvector(LS, v, LINEAR_TYPE_VEC4);
int64_t c = lastack_mark(LS);
lastack_pushmatrix(LS, m);
lastack_pushref(LS, -c);
lastack_dup(LS);
lastack_print(LS);
lastack_reset(LS);
lastack_pushref(LS, c);
lastack_dup(LS);
lastack_dup(LS);
lastack_dup(LS);
lastack_dup(LS);
lastack_dup(LS);
lastack_dup(LS);
lastack_print(LS);
lastack_reset(LS);
lastack_print(LS);
lastack_delete(LS);
return 0;
}
#endif