From 153290d57e2026a1c631fd1e2e5733b06b15fa8b Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 26 Feb 2025 11:36:34 -0600 Subject: [PATCH] bitbuff dbg markers, rough arg parsing --- src/app.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++-- src/bitbuff.c | 30 ++++++++++++-- src/bitbuff.h | 14 ++++++- src/config.h | 2 +- src/sim.c | 56 +++++++++++++++----------- src/sim_ent.c | 8 +--- src/sim_step.c | 2 +- src/user.c | 72 ++++++++++++++++++++++++++++----- 8 files changed, 242 insertions(+), 47 deletions(-) diff --git a/src/app.c b/src/app.c index da4e173b..d3b11c49 100644 --- a/src/app.c +++ b/src/app.c @@ -122,6 +122,87 @@ void app_register_exit_callback(app_exit_callback_func *func) sys_mutex_unlock(&lock); } +/* ========================== * + * Args + * ========================== */ + +struct app_arg { + struct string key; + struct string value; + struct app_arg *next; +}; + +struct app_arg_list { + struct app_arg *first; + struct app_arg *last; + u64 count; +}; + +/* TODO: Remove this and do real argument parsing */ +INTERNAL struct app_arg_list parse_args(struct arena *arena, struct string args_str) +{ + struct app_arg_list res = ZI; + i64 mode = 0; + i64 i = 0; + i64 key_start = -1; + i64 key_end = -1; + i64 value_start = -1; + i64 value_end = -1; + while (i < (i64)args_str.len) { + u8 c = args_str.text[i]; + switch (mode) { + case 0: + { + if (c == '-') { + mode = 1; + key_start = i + 1; + } + ++i; + } break; + + case 1: + { + if (c == '=') { + key_end = i; + value_start = i + 1; + mode = 2; + } + ++i; + } break; + + case 2: + { + if (c == '-' || i == (i64)args_str.len - 1) { + if (c == '-') { + value_end = i; + } else { + value_end = i + 1; + } + if (key_start >= 0 && key_end > key_start && key_end <= (i64)args_str.len && value_start >= 0 && value_end > value_start && value_end <= (i64)args_str.len) { + struct string key = string_copy(arena, STRING(key_end - key_start, args_str.text + key_start)); + struct string value = string_copy(arena, STRING(value_end - value_start, args_str.text + value_start)); + struct app_arg *arg = arena_push_zero(arena, struct app_arg); + arg->key = key; + arg->value = value; + if (res.last) { + res.last->next = arg; + } else { + res.first = arg; + } + res.last = arg; + ++res.count; + } + key_start = i + 1; + mode = 1; + } + ++i; + } break; + default: break; + } + } + return res; +} + /* ========================== * * Entry point * ========================== */ @@ -130,6 +211,19 @@ void app_entry_point(struct string args_str) { struct temp_arena scratch = scratch_begin_no_conflict(); + struct app_arg_list args = parse_args(scratch.arena, args_str); + struct string logfile_name = LIT("log.txt"); + struct string connect_address = ZI; + for (struct app_arg *arg = args.first; arg; arg = arg->next) { + struct string key = arg->key; + struct string value = arg->value; + if (string_eq(key, LIT("log"))) { + logfile_name = value; + } else if (string_eq(key, LIT("connect"))) { + connect_address = value; + } + } + #if !RTC /* Verify test modes aren't left on by accident in release mode */ CT_ASSERT(BITBUFF_DEBUG == 0); @@ -167,13 +261,18 @@ void app_entry_point(struct string args_str) /* Startup logging */ { struct temp_arena temp = arena_temp_begin(scratch.arena); - struct string logfile_path = app_write_path_cat(temp.arena, LIT("log.txt")); + + struct string logfile_path = logfile_path = app_write_path_cat(temp.arena, logfile_name); + struct log_startup_receipt log_sr = log_startup(logfile_path); (UNUSED)log_sr; logf_info("Start of logs"); arena_temp_end(temp); } - logf_info("App started with args: \"%F\"", FMT_STR(args_str)); + logf_info("App started with args \"%F\" (%F parsed)", FMT_STR(args_str), FMT_UINT(args.count)); + for (struct app_arg *arg = args.first; arg; arg = arg->next) { + logf_info("Parsed arg: key = \"%F\", value = \"%F\"", FMT_STR(arg->key), FMT_STR(arg->value)); + } /* Create window */ struct sys_window window = sys_window_alloc(); @@ -235,7 +334,7 @@ void app_entry_point(struct string args_str) struct draw_startup_receipt draw_sr = draw_startup(&renderer_sr, &font_sr); struct phys_startup_receipt phys_sr = phys_startup(); struct sim_startup_receipt sim_sr = sim_startup(); - struct user_startup_receipt user_sr = user_startup(&work_sr, &renderer_sr, &font_sr, &sprite_sr, &draw_sr, &asset_cache_sr, &sound_sr, &mixer_sr, &phys_sr, &host_sr, &sim_sr, args_str, &window); + struct user_startup_receipt user_sr = user_startup(&work_sr, &renderer_sr, &font_sr, &sprite_sr, &draw_sr, &asset_cache_sr, &sound_sr, &mixer_sr, &phys_sr, &host_sr, &sim_sr, connect_address, &window); struct playback_startup_receipt playback_sr = playback_startup(&mixer_sr); (UNUSED)user_sr; diff --git a/src/bitbuff.c b/src/bitbuff.c index 97cd5399..e2e9c11d 100644 --- a/src/bitbuff.c +++ b/src/bitbuff.c @@ -48,10 +48,10 @@ INTERNAL void _dbg_read_magic(struct bitbuff_reader *br, enum dbg_magic expected u8 stored_num_bits = (stored >> 16) & 0xFF; /* Verify stored magic match */ - ASSERT(stored_magic == expected_magic); + ASSERT(expected_magic == stored_magic); /* Verify stored bit count match */ - ASSERT(stored_num_bits == expected_num_bits); + ASSERT(expected_num_bits == stored_num_bits); } } @@ -259,9 +259,11 @@ void bw_write_ibits(struct bitbuff_writer *bw, i64 value, u8 num_bits) bw_write_ubits(bw, ubits, num_bits); } -void bw_write_bit(struct bitbuff_writer *bw, u8 value) +/* Returns written bit to make writing delta encoding logic cleaner */ +b32 bw_write_bit(struct bitbuff_writer *bw, u8 value) { bw_write_ubits(bw, value, 1); + return value; } /* Writes a variable length unsigned integer. @@ -359,6 +361,16 @@ void bw_write_bytes(struct bitbuff_writer *bw, struct string bytes) bw->cur_bit += num_bits; } +#if BITBUFF_DEBUG +void bw_write_dbg_marker(struct bitbuff_writer *bw, struct string name) +{ + bw->cur_bit += (8 - (bw->cur_bit & 7)) & 7; + for (u64 i = 0; i < name.len; ++i) { + bw_write_ubits_nomagic(bw, name.text[i], 8); + } +} +#endif + /* ========================== * * Reader * ========================== */ @@ -652,6 +664,18 @@ void br_seek_to_byte(struct bitbuff_reader *br, u64 pos) } +#if BITBUFF_DEBUG +void br_read_dbg_marker(struct bitbuff_reader *br, struct string name) +{ + br->cur_bit += (8 - (br->cur_bit & 7)) & 7; + for (u64 i = 0; i < name.len; ++i) { + u8 c_stored = br_read_ubits_nomagic(br, 8); + u8 c_expected = name.text[i]; + ASSERT(c_expected == c_stored); + } +} +#endif + /* ========================== * * Test * ========================== */ diff --git a/src/bitbuff.h b/src/bitbuff.h index 85bff67a..b554d583 100644 --- a/src/bitbuff.h +++ b/src/bitbuff.h @@ -50,7 +50,7 @@ void bw_align(struct bitbuff_writer *bw); void bw_write_ubits(struct bitbuff_writer *bw, u64 value, u8 num_bits); void bw_write_ibits(struct bitbuff_writer *bw, i64 value, u8 num_bits); -void bw_write_bit(struct bitbuff_writer *bw, u8 value); +b32 bw_write_bit(struct bitbuff_writer *bw, u8 value); void bw_write_uv(struct bitbuff_writer *bw, u64 value); void bw_write_iv(struct bitbuff_writer *bw, i64 value); @@ -63,6 +63,12 @@ void bw_write_string(struct bitbuff_writer *bw, struct string s); void bw_write_bytes(struct bitbuff_writer *bw, struct string bytes); +#if BITBUFF_DEBUG +void bw_write_dbg_marker(struct bitbuff_writer *bw, struct string name); +#else +#define bw_write_dbg_marker(bw, name) +#endif + /* ========================== * * Reader * ========================== */ @@ -108,6 +114,12 @@ u8 *br_read_bytes_raw(struct bitbuff_reader *br, u64 num_bytes); void br_seek_bytes(struct bitbuff_reader *br, u64 num_bytes); void br_seek_to_byte(struct bitbuff_reader *br, u64 pos); +#if BITBUFF_DEBUG +void br_read_dbg_marker(struct bitbuff_reader *br, struct string name); +#else +#define br_read_dbg_marker(br, name) +#endif + #if BITBUFF_TEST void bitbuff_test(void); #endif diff --git a/src/config.h b/src/config.h index 49679df2..929d6213 100644 --- a/src/config.h +++ b/src/config.h @@ -60,7 +60,7 @@ #define COLLIDER_DEBUG_DETAILED_DRAW_MENKOWSKI 0 /* If enabled, bitbuffs will insert/verify magic numbers & length for each read & write */ -#define BITBUFF_DEBUG 0 +#define BITBUFF_DEBUG 1 #define BITBUFF_TEST RTC /* If enabled, things like network writes & memory allocations will be tracked in a global statistics struct */ diff --git a/src/sim.c b/src/sim.c index 40af86e3..53fb5c8a 100644 --- a/src/sim.c +++ b/src/sim.c @@ -657,6 +657,8 @@ void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_client *receiver, { __prof; + bw_write_dbg_marker(bw, LIT("SNAPSHOT START")); + bw_write_iv(bw, ss1->sim_dt_ns); bw_write_iv(bw, ss1->sim_time_ns); @@ -667,6 +669,7 @@ void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_client *receiver, /* Id bins */ /* TODO: Don't encode these */ + bw_write_dbg_marker(bw, LIT("SNAPSHOT BINS")); for (u64 i = 0; i < ss1->num_id_bins; ++i) { u32 old_first = 0; u32 old_last = 0; @@ -676,21 +679,15 @@ void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_client *receiver, old_last = old_bin->last; } struct sim_ent_bin *bin = &ss1->id_bins[i]; - u32 new_first = bin->first; - u32 new_last = bin->last; - if (new_first != old_first || new_last != old_last) { + b32 first_diff = bin->first != old_first; + b32 last_diff = bin->last != old_last; + if (first_diff || last_diff) { bw_write_bit(bw, 1); bw_write_uv(bw, i); - if (old_first == bin->first) { - bw_write_bit(bw, 0); - } else { - bw_write_bit(bw, 1); + if (bw_write_bit(bw, first_diff)) { bw_write_uv(bw, bin->first); } - if (old_last == bin->last) { - bw_write_bit(bw, 0); - } else { - bw_write_bit(bw, 1); + if (bw_write_bit(bw, last_diff)) { bw_write_uv(bw, bin->last); } } @@ -698,19 +695,18 @@ void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_client *receiver, bw_write_bit(bw, 0); /* Ents */ - if (ss1->num_ents_allocated == ss0->num_ents_allocated) { - bw_write_bit(bw, 0); - } else { - bw_write_bit(bw, 1); + bw_write_dbg_marker(bw, LIT("SNAPSHOT NUM ENTS")); + if (bw_write_bit(bw, ss1->num_ents_allocated != ss0->num_ents_allocated)) { bw_write_uv(bw, ss1->num_ents_allocated); } - if (ss1->num_ents_reserved == ss0->num_ents_reserved) { - bw_write_bit(bw, 0); - } else { - bw_write_bit(bw, 1); + if (bw_write_bit(bw, ss1->num_ents_reserved != ss0->num_ents_reserved)) { bw_write_uv(bw, ss1->num_ents_reserved); } - for (u64 i = 0; i < ss1->num_ents_reserved; ++i) { + + bw_write_dbg_marker(bw, LIT("SNAPSHOT ENTS")); + bw_write_dbg_marker(bw, STRING_FROM_STRUCT(&ss1->num_ents_reserved)); + + for (u64 i = 1; i < ss1->num_ents_reserved; ++i) { struct sim_ent *e0 = sim_ent_nil(); if (i < ss0->num_ents_reserved) { e0 = &ss0->ents[i]; @@ -718,6 +714,8 @@ void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_client *receiver, struct sim_ent *e1 = &ss1->ents[i]; sim_ent_encode(bw, e0, e1); } + + bw_write_dbg_marker(bw, LIT("SNAPSHOT END")); } /* ========================== * @@ -728,6 +726,8 @@ void sim_snapshot_decode(struct bitbuff_reader *br, struct sim_snapshot *ss) { __prof; + br_read_dbg_marker(br, LIT("SNAPSHOT START")); + ss->sim_dt_ns = br_read_iv(br); ss->sim_time_ns = br_read_iv(br); @@ -738,6 +738,7 @@ void sim_snapshot_decode(struct bitbuff_reader *br, struct sim_snapshot *ss) /* Id bins */ /* TODO: Don't decode these, determine them implicitly from decoded ents */ + br_read_dbg_marker(br, LIT("SNAPSHOT BINS")); { b32 bin_changed = br_read_bit(br); while (bin_changed) { @@ -760,10 +761,12 @@ void sim_snapshot_decode(struct bitbuff_reader *br, struct sim_snapshot *ss) } /* Ents */ + br_read_dbg_marker(br, LIT("SNAPSHOT NUM ENTS")); if (br_read_bit(br)) { ss->num_ents_allocated = br_read_uv(br); } - if (br_read_bit(br)) { + b32 num_ents_reserved_changed = br_read_bit(br); + if (num_ents_reserved_changed) { u64 old_num_ents_reserved = ss->num_ents_reserved; ss->num_ents_reserved = br_read_uv(br); i64 reserve_diff = (i64)ss->num_ents_reserved - (i64)old_num_ents_reserved; @@ -774,13 +777,22 @@ void sim_snapshot_decode(struct bitbuff_reader *br, struct sim_snapshot *ss) *e = *sim_ent_nil(); e->ss = ss; } + } else if (reserve_diff < 0) { + /* TODO: Handle this */ + /* NOTE: Should be impossible for snasphot reserve count to decrease at the moment */ + ASSERT(false); } } - for (u64 i = 0; i < ss->num_ents_reserved; ++i) { + + br_read_dbg_marker(br, LIT("SNAPSHOT ENTS")); + br_read_dbg_marker(br, STRING_FROM_STRUCT(&ss->num_ents_reserved)); + for (u64 i = 1; i < ss->num_ents_reserved; ++i) { struct sim_ent *e = &ss->ents[i]; e->ss = ss; sim_ent_decode(br, e); } + + br_read_dbg_marker(br, LIT("SNAPSHOT END")); } diff --git a/src/sim_ent.c b/src/sim_ent.c index aa1eaa99..ebd24da7 100644 --- a/src/sim_ent.c +++ b/src/sim_ent.c @@ -617,8 +617,6 @@ void sim_ent_sync(struct sim_ent *local, struct sim_ent *remote) void sim_ent_encode(struct bitbuff_writer *bw, struct sim_ent *e0, struct sim_ent *e1) { struct sim_snapshot *ss = e1->ss; - - /* FIXME: Things like xforms need to be retreived manually rather than memcopied. * This will also be true for things like ent handles once uids are implemented. */ @@ -630,10 +628,7 @@ void sim_ent_encode(struct bitbuff_writer *bw, struct sim_ent *e0, struct sim_en u64 chunk_size = min_u64(pos + 8, sizeof(*e1)) - pos; u8 *chunk0 = (u8 *)e0 + pos; u8 *chunk1 = (u8 *)e1 + pos; - if (MEMEQ(chunk0, chunk1, chunk_size)) { - bw_write_bit(bw, 0); - } else { - bw_write_bit(bw, 1); + if (bw_write_bit(bw, MEMEQ(chunk0, chunk1, chunk_size))) { u64 bits = 0; MEMCPY(&bits, chunk1, chunk_size); bw_write_ubits(bw, bits, 64); @@ -663,6 +658,7 @@ void sim_ent_decode(struct bitbuff_reader *br, struct sim_ent *e) } } e->ss = old_ss; + } #else diff --git a/src/sim_step.c b/src/sim_step.c index 78496167..9be58014 100644 --- a/src/sim_step.c +++ b/src/sim_step.c @@ -1262,7 +1262,7 @@ void sim_step(struct sim_step_ctx *ctx) if (publish_client->valid) { struct sim_snapshot *pub_world = sim_snapshot_from_tick(publish_client, world->tick); if (!pub_world->valid) { - struct sim_snapshot *prev_pub_world = sim_snapshot_from_tick(publish_client, publish_client->last_tick); + struct sim_snapshot *prev_pub_world = sim_snapshot_from_tick(publish_client, world->tick - 1); pub_world = sim_snapshot_alloc(publish_client, prev_pub_world, world->tick); /* Sync */ sim_snapshot_sync_ents(pub_world, world, world_client->ent_id); diff --git a/src/user.c b/src/user.c index 57d58f6e..b0d87abe 100644 --- a/src/user.c +++ b/src/user.c @@ -1880,16 +1880,17 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) /* TODO: Host system should allocate & copy string stored in local_listen_addr */ //host_listen(host, local_listen_addr); //host_listen(host, net_listen_addr); -#else - struct host *host = host_alloc(12345); #endif (UNUSED)arg; b32 is_master = false; + struct host *host; if (G.connect_address_str.len > 0) { + host = host_alloc(0); struct sock_address addr = sock_address_from_string(G.connect_address_str); host_queue_connect_to_address(host, addr); } else { + host = host_alloc(12345); is_master = true; } @@ -1909,16 +1910,40 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) + + + + + + + + + i64 last_tick_from_master = 0; + i64 last_ticks_ahead_from_master = 0; + + + + + + + + + + + + + + i64 last_publish_ns = 0; i64 last_tick_ns = 0; i64 step_dt_ns = NS_FROM_SECONDS(1) / SIM_TICKS_PER_SECOND; - i64 compute_dt_ns = step_dt_ns; + f64 compute_timescale = 1.0; while (!atomic_i32_eval(&G.local_sim_thread_shutdown)) { __profscope(local_sim_loop); struct temp_arena scratch = scratch_begin_no_conflict(); { __profscope(local_sim_sleep); - sleep_frame(last_tick_ns, compute_dt_ns); + sleep_frame(last_tick_ns, step_dt_ns * compute_timescale); last_tick_ns = sys_time_ns(); } @@ -2106,9 +2131,19 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) /* Release unneeded user input snapshots */ sim_snapshot_release_ticks_in_range(user_input_client, 0, local_client->first_tick - 1); + if (master_client->valid) { + last_tick_from_master = master_client->last_tick; + last_ticks_ahead_from_master = master_client->ack - last_tick_from_master; + if (last_ticks_ahead_from_master > 4) { + compute_timescale = 0.9; + } else if (last_ticks_ahead_from_master < 2) { + compute_timescale = 1.1; + } else { + compute_timescale = 1; + } + } - - +#if 0 /* Determine step tick */ u64 desired_step_tick = 0; if (is_master) { @@ -2120,12 +2155,29 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) num_predict_ticks += 2; /* Jitter buffer */ desired_step_tick = master_client->last_tick + num_predict_ticks; } +#else + /* Snap to master sim tick if too far */ + i64 num_sim_ticks = 1; + if (!is_master && math_abs_i64(last_ticks_ahead_from_master) > 50) { + i64 rtt_ns = master_client->rtt_ns; + f64 rtt_tick_ratio = (f64)(rtt_ns + (step_dt_ns - 1)) / (f64)step_dt_ns; + i64 num_predict_ticks = math_round_to_int64(rtt_tick_ratio) + 2; + i64 desired_step_tick = master_client->last_tick + num_predict_ticks; + sim_snapshot_release_ticks_in_range(local_client, desired_step_tick, U64_MAX); + sim_snapshot_release_ticks_in_range(user_input_client, desired_step_tick, U64_MAX); + num_sim_ticks = desired_step_tick - num_sim_ticks; + } +#endif + + + + /* Create user input */ { struct sim_snapshot *prev_user_input_ss = sim_snapshot_from_tick(user_input_client, user_input_client->last_tick); - struct sim_snapshot *user_input_ss = sim_snapshot_alloc(user_input_client, prev_user_input_ss, local_client->last_tick + 1); + struct sim_snapshot *user_input_ss = sim_snapshot_alloc(user_input_client, prev_user_input_ss, local_client->last_tick + num_sim_ticks); struct sim_ent *user_input_root = sim_ent_from_id(user_input_ss, SIM_ENT_ROOT_ID); /* Find / create local control cmd ent */ struct sim_ent *control_cmd_ent = sim_ent_find_first_match_one(user_input_ss, SIM_ENT_PROP_CMD_CONTROL); @@ -2184,9 +2236,9 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) user_input_client->ent_id = master_ss->local_client_ent; struct sim_snapshot *prev_world = sim_snapshot_alloc(local_client, master_ss, master_ss->tick); - /* FIXME: Don't take use desired_step_tick at face value. Dilate compute_dt to work towards it. */ - i64 num_predict_ticks = desired_step_tick - master_ss->tick; - for (i64 i = 0; i < num_predict_ticks; ++i) { + + i64 num_steps = (local_client->last_tick + num_sim_ticks) - master_ss->tick; + for (i64 i = 0; i < num_steps; ++i) { ctx.world = sim_snapshot_alloc(local_client, prev_world, prev_world->tick + 1); sim_step(&ctx); prev_world = ctx.world;