From 02290601c371e0ec9c134d4c409b8bf1e065fc49 Mon Sep 17 00:00:00 2001 From: jacob Date: Mon, 6 Jan 2025 17:09:39 -0600 Subject: [PATCH] cleanup collision debug leak & merge structure with contact lookup into entity lookup --- src/config.h | 1 + src/game.c | 230 ++++++++++++++++++++++++++++----------------------- 2 files changed, 127 insertions(+), 104 deletions(-) diff --git a/src/config.h b/src/config.h index 7200689f..55e619fe 100644 --- a/src/config.h +++ b/src/config.h @@ -35,6 +35,7 @@ #define GAME_PHYSICS_SUBSTEPS 4 #define GAME_PHYSICS_ENABLE_WARM_STARTING 1 #define GAME_PHYSICS_ENABLE_RELAXATION 1 +#define GAME_PHYSICS_ENABLE_TOI 1 #define USER_DRAW_MENKOWSKI 0 #define GAME_PHYSICS_ENABLE_COLLISION 1 diff --git a/src/game.c b/src/game.c index 3432d3cb..774adb67 100644 --- a/src/game.c +++ b/src/game.c @@ -12,32 +12,29 @@ #include "log.h" #include "collider.h" -struct contact_lookup_entry { +struct entity_lookup_key { u64 hash; - struct entity_handle contact_ent_handle; - struct contact_lookup_entry *next; - struct contact_lookup_entry *prev; }; -struct contact_lookup_bucket { - struct contact_lookup_entry *first; - struct contact_lookup_entry *last; +struct entity_lookup_entry { + struct entity_lookup_key key; + struct entity_handle entity; + struct entity_lookup_entry *next; + struct entity_lookup_entry *prev; }; -struct contact_lookup { +struct entity_lookup_bucket { + struct entity_lookup_entry *first; + struct entity_lookup_entry *last; +}; + +struct entity_lookup { struct arena arena; - struct contact_lookup_bucket buckets[4096]; - struct contact_lookup_entry *first_free_entry; + struct entity_lookup_bucket *buckets; + u64 num_buckets; + struct entity_lookup_entry *first_free_entry; }; -#if COLLIDER_DEBUG -/* TODO: Remove this (debugging) */ -struct collision_debug_lookup { - struct arena arena; - struct fixed_dict dict; -}; -#endif - GLOBAL struct { struct atomic_i32 game_thread_shutdown; struct sys_thread game_thread; @@ -64,9 +61,9 @@ GLOBAL struct { f32 mouse_joint_max_force; /* Bookkeeping structures */ - struct contact_lookup contact_lookup; + struct entity_lookup contact_lookup; #if COLLIDER_DEBUG - struct collision_debug_lookup collision_debug_lookup; + struct entity_lookup collision_debug_lookup; #endif /* Ticks */ @@ -176,36 +173,31 @@ INTERNAL void activate_now(struct entity *ent) } /* ========================== * - * Contact lookup + * Entity lookup * ========================== */ -INTERNAL void contact_lookup_alloc(struct contact_lookup *l) +INTERNAL struct entity_lookup entity_lookup_alloc(u64 num_buckets) { - MEMZERO_STRUCT(l); - l->arena = arena_alloc(GIGABYTE(64)); + ASSERT(num_buckets > 0); + struct entity_lookup l = ZI; + l.arena = arena_alloc(GIGABYTE(64)); + l.buckets = arena_push_array_zero(&l.arena, struct entity_lookup_bucket, num_buckets); + l.num_buckets = num_buckets; + return l; } -INTERNAL void contact_lookup_release(struct contact_lookup *l) +INTERNAL void entity_lookup_release(struct entity_lookup *l) { arena_release(&l->arena); } -INTERNAL u64 contact_lookup_hash_from_entities(struct entity_handle h0, struct entity_handle h1) +INTERNAL struct entity_lookup_entry *entity_lookup_get(struct entity_lookup *l, struct entity_lookup_key key) { - struct buffer b0 = BUFFER_FROM_STRUCT(&h0); - struct buffer b1 = BUFFER_FROM_STRUCT(&h1); - u64 hash = hash_fnv64(HASH_FNV64_BASIS, b0); - hash = hash_fnv64(hash, b1); - return hash; -} - -INTERNAL struct contact_lookup_entry *contact_lookup_get(struct contact_lookup *l, u64 hash) -{ - u64 index = hash % ARRAY_COUNT(l->buckets); - struct contact_lookup_bucket *bucket = &l->buckets[index]; - struct contact_lookup_entry *res = NULL; - for (struct contact_lookup_entry *e = bucket->first; e; e = e->next) { - if (e->hash == hash) { + u64 index = key.hash % l->num_buckets; + struct entity_lookup_bucket *bucket = &l->buckets[index]; + struct entity_lookup_entry *res = NULL; + for (struct entity_lookup_entry *e = bucket->first; e; e = e->next) { + if (e->key.hash == key.hash) { res = e; break; } @@ -213,25 +205,25 @@ INTERNAL struct contact_lookup_entry *contact_lookup_get(struct contact_lookup * return res; } -INTERNAL void contact_lookup_set(struct contact_lookup *l, u64 hash, struct entity_handle handle) +INTERNAL void entity_lookup_set(struct entity_lookup *l, struct entity_lookup_key key, struct entity_handle handle) { - u64 index = hash % ARRAY_COUNT(l->buckets); - struct contact_lookup_bucket *bucket = &l->buckets[index]; + u64 index = key.hash % l->num_buckets; + struct entity_lookup_bucket *bucket = &l->buckets[index]; - struct contact_lookup_entry *prev = NULL; - struct contact_lookup_entry **slot = &bucket->first; + struct entity_lookup_entry *prev = NULL; + struct entity_lookup_entry **slot = &bucket->first; while (*slot) { - if ((*slot)->hash == hash) { + if ((*slot)->key.hash == key.hash) { break; } prev = *slot; slot = &(*slot)->next; } - struct contact_lookup_entry *entry = *slot; + struct entity_lookup_entry *entry = *slot; if (entry) { /* Set existing entry */ - entry->contact_ent_handle = handle; + entry->entity = handle; } else { /* Allocate entry */ if (l->first_free_entry) { @@ -239,12 +231,12 @@ INTERNAL void contact_lookup_set(struct contact_lookup *l, u64 hash, struct enti l->first_free_entry->prev = NULL; l->first_free_entry = entry->next; } else { - entry = arena_push(&l->arena, struct contact_lookup_entry); + entry = arena_push(&l->arena, struct entity_lookup_entry); } MEMZERO_STRUCT(entry); - entry->hash = hash; - entry->contact_ent_handle = handle; + entry->key = key; + entry->entity = handle; if (prev) { entry->prev = prev; prev->next = entry; @@ -255,11 +247,11 @@ INTERNAL void contact_lookup_set(struct contact_lookup *l, u64 hash, struct enti } } -INTERNAL void contact_lookup_remove(struct contact_lookup *l, struct contact_lookup_entry *entry) +INTERNAL void entity_lookup_remove(struct entity_lookup *l, struct entity_lookup_entry *entry) { - struct contact_lookup_bucket *bucket = &l->buckets[entry->hash % ARRAY_COUNT(l->buckets)]; - struct contact_lookup_entry *prev = entry->prev; - struct contact_lookup_entry *next = entry->next; + struct entity_lookup_bucket *bucket = &l->buckets[entry->key.hash % l->num_buckets]; + struct entity_lookup_entry *prev = entry->prev; + struct entity_lookup_entry *next = entry->next; if (prev) { prev->next = next; @@ -281,6 +273,16 @@ INTERNAL void contact_lookup_remove(struct contact_lookup *l, struct contact_loo l->first_free_entry = entry; } +INTERNAL struct entity_lookup_key entity_lookup_key_from_two_handles(struct entity_handle h0, struct entity_handle h1) +{ + struct entity_lookup_key key = ZI; + struct buffer b0 = BUFFER_FROM_STRUCT(&h0); + struct buffer b1 = BUFFER_FROM_STRUCT(&h1); + key.hash = hash_fnv64(HASH_FNV64_BASIS, b0); + key.hash = hash_fnv64(key.hash, b1); + return key; +} + /* ========================== * * Reset * ========================== */ @@ -291,17 +293,14 @@ INTERNAL void reset_world(void) /* Release world */ world_release(&G.tick); /* Release bookkeeping */ -#if COLLIDER_DEBUG - arena_release(&G.collision_debug_lookup.arena); -#endif - contact_lookup_release(&G.contact_lookup); + entity_lookup_release(&G.collision_debug_lookup); + entity_lookup_release(&G.contact_lookup); } /* Create bookkeeping */ - contact_lookup_alloc(&G.contact_lookup); + G.contact_lookup = entity_lookup_alloc(4096); #if COLLIDER_DEBUG - G.collision_debug_lookup.arena = arena_alloc(GIGABYTE(64)); - G.collision_debug_lookup.dict = fixed_dict_init(&G.collision_debug_lookup.arena, 4096); + G.collision_debug_lookup = entity_lookup_alloc(4096); #endif /* Re-create world */ @@ -507,12 +506,12 @@ INTERNAL void create_contacts(void) e1_collider = check0_collider; } - u64 lookup_hash = contact_lookup_hash_from_entities(e0->handle, e1->handle); - struct contact_lookup_entry *entry = contact_lookup_get(&G.contact_lookup, lookup_hash); + struct entity_lookup_key key = entity_lookup_key_from_two_handles(e0->handle, e1->handle); + struct entity_lookup_entry *entry = entity_lookup_get(&G.contact_lookup, key); struct entity *constraint_ent = entity_nil(); if (entry) { - constraint_ent = entity_from_handle(store, entry->contact_ent_handle); + constraint_ent = entity_from_handle(store, entry->entity); if (entity_is_valid_and_active(constraint_ent)) { if (constraint_ent->contact_constraint_data.last_iteration >= G.tick.tick_id) { /* Already processed constraint this iteration */ @@ -522,7 +521,7 @@ INTERNAL void create_contacts(void) } } else { /* Constraint ent no longer valid, delete entry */ - contact_lookup_remove(&G.contact_lookup, entry); + entity_lookup_remove(&G.contact_lookup, entry); entry = NULL; } } @@ -583,7 +582,7 @@ INTERNAL void create_contacts(void) entity_enable_prop(constraint_ent, ENTITY_PROP_CONTACT_CONSTRAINT); activate_now(constraint_ent); ASSERT(!entry); /* Existing entry should never be present here */ - contact_lookup_set(&G.contact_lookup, lookup_hash, constraint_ent->handle); + entity_lookup_set(&G.contact_lookup, key, constraint_ent->handle); } } constraint = &constraint_ent->contact_constraint_data; @@ -648,21 +647,17 @@ INTERNAL void create_contacts(void) /* TODO: Remove this (debugging) */ #if COLLIDER_DEBUG { - struct string fdkey = STRING_FROM_BUFFER(BUFFER_FROM_STRUCT(&lookup_hash)); - struct entity_handle *dbg_ent_handle = fixed_dict_get(&G.collision_debug_lookup.dict, fdkey); - if (!dbg_ent_handle) { - /* FIXME: Handle never released */ - dbg_ent_handle = arena_push_zero(&G.collision_debug_lookup.arena, struct entity_handle); + struct entity *dbg_ent = entity_nil(); + struct entity_lookup_entry *dbg_entry = entity_lookup_get(&G.collision_debug_lookup, key); + if (dbg_entry) { + dbg_ent = entity_from_handle(store, dbg_entry->entity); } - struct entity *dbg_ent = entity_from_handle(store, *dbg_ent_handle); - if (!dbg_ent->valid) { /* FIXME: Entity never released */ dbg_ent = entity_alloc(root); entity_enable_prop(dbg_ent, ENTITY_PROP_COLLISION_DEBUG); - *dbg_ent_handle = dbg_ent->handle; - fixed_dict_set(&G.collision_debug_lookup.arena, &G.collision_debug_lookup.dict, fdkey, dbg_ent_handle); + entity_lookup_set(&G.collision_debug_lookup, key, dbg_ent->handle); } struct collision_debug *dbg = &dbg_ent->collision_debug_data; @@ -785,15 +780,53 @@ INTERNAL void prepare_contacts(void) entity_disable_prop(constraint_ent, ENTITY_PROP_ACTIVE); entity_enable_prop(constraint_ent, ENTITY_PROP_RELEASE_AT_END_OF_FRAME); /* Remove from lookup */ - u64 hash = contact_lookup_hash_from_entities(constraint->e0, constraint->e1); - struct contact_lookup_entry *entry = contact_lookup_get(&G.contact_lookup, hash); + struct entity_lookup_key key = entity_lookup_key_from_two_handles(constraint->e0, constraint->e1); + struct entity_lookup_entry *entry = entity_lookup_get(&G.contact_lookup, key); if (entry) { - contact_lookup_remove(&G.contact_lookup, entry); + entity_lookup_remove(&G.contact_lookup, entry); } else { ASSERT(false); /* This should always exist */ } } } + +#if COLLIDER_DEBUG + for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { + struct entity *dbg_ent = &store->entities[entity_index]; + if (!entity_is_valid_and_active(dbg_ent)) continue; + if (!entity_has_prop(dbg_ent, ENTITY_PROP_COLLISION_DEBUG)) continue; + + struct collision_debug *dbg = &dbg_ent->collision_debug_data; + struct entity *e0 = entity_from_handle(store, dbg->e0); + struct entity *e1 = entity_from_handle(store, dbg->e1); + + + if (!entity_is_valid_and_active(e0) || !entity_is_valid_and_active(e1) + || !(entity_has_prop(e0, ENTITY_PROP_PHYSICAL_DYNAMIC) || entity_has_prop(e0, ENTITY_PROP_PHYSICAL_KINEMATIC)) + || !(entity_has_prop(e1, ENTITY_PROP_PHYSICAL_DYNAMIC) || entity_has_prop(e1, ENTITY_PROP_PHYSICAL_KINEMATIC))) { + /* Mark dbg ent for removal */ + entity_disable_prop(dbg_ent, ENTITY_PROP_ACTIVE); + entity_enable_prop(dbg_ent, ENTITY_PROP_RELEASE_AT_END_OF_FRAME); + + /* Remove from lookup */ + struct entity_lookup_key key = entity_lookup_key_from_two_handles(dbg->e0, dbg->e1); + struct entity_lookup_entry *entry = entity_lookup_get(&G.collision_debug_lookup, key); + + if (e0->valid) { + --e0->colliding; + } + if (e1->valid) { + --e0->colliding; + } + + if (entry) { + entity_lookup_remove(&G.collision_debug_lookup, entry); + } else { + ASSERT(false); /* This should always exist */ + } + } + } +#endif } INTERNAL void warm_start_contacts(void) @@ -1563,7 +1596,6 @@ INTERNAL f32 determine_earliest_toi(f32 dt, f32 tolerance, u32 max_iterations) struct entity_store *store = G.tick.entity_store; //struct entity *root = G.root; - for (u64 e0_index = 0; e0_index < store->reserved; ++e0_index) { struct entity *e0 = &store->entities[e0_index]; if (!entity_is_valid_and_active(e0)) continue; @@ -1583,7 +1615,6 @@ INTERNAL f32 determine_earliest_toi(f32 dt, f32 tolerance, u32 max_iterations) e0_xf_t1 = xform_basis_rotated_world(e0_xf_t1, tick_angular_velocity); } - /* Start e1 index at e0 index + 1 to prevent redundant checks */ for (u64 e1_index = e0_index + 1; e1_index < store->reserved; ++e1_index) { struct entity *e1 = &store->entities[e1_index]; @@ -1609,7 +1640,6 @@ INTERNAL f32 determine_earliest_toi(f32 dt, f32 tolerance, u32 max_iterations) if (t != 0 && t < smallest_t) { smallest_t = t; } - } } @@ -1617,24 +1647,6 @@ INTERNAL f32 determine_earliest_toi(f32 dt, f32 tolerance, u32 max_iterations) } - - - - - - - - - - - - - - - - - - /* ========================== * * Update @@ -2254,11 +2266,21 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) f32 remaining_dt = dt; integrate_velocities_from_forces(dt); while (remaining_dt > 0) { - const f32 min_toi = 0.000001f; - const f32 tolerance = 0.00001f; - const u32 max_iterations = 128; - f32 earliest_toi = max_f32(determine_earliest_toi(remaining_dt, tolerance, max_iterations), min_toi); + f32 earliest_toi = 1; + { +#if GAME_PHYSICS_ENABLE_TOI + const f32 min_toi = 0.000001f; + const f32 tolerance = 0.00001f; + const u32 max_iterations = 128; + earliest_toi = max_f32(determine_earliest_toi(remaining_dt, tolerance, max_iterations), min_toi); +#else + (UNUSED)toi; + (UNUSED)determine_earliest_toi; +#endif + } + f32 step_dt = remaining_dt * earliest_toi; + create_contacts(); create_mouse_joints(game_cmds);