#include #include #include #include #include #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;icap;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;iversion); printf("stack %d/%d:\n", LS->stack_top, LS->stack_cap); int i; for (i=0;istack_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