From a19c2395ae6790462a0b6b4c040c6f70c0768f9c Mon Sep 17 00:00:00 2001 From: jacob Date: Fri, 13 Mar 2026 10:03:45 -0500 Subject: [PATCH] timeline wip --- src/pp/pp.c | 26 ++--- src/pp/pp.h | 2 + src/pp/pp_sim/pp_sim_core.c | 4 + src/pp/pp_vis/pp_vis_core.c | 166 +++++++++++++++++++++++++++++--- src/pp/pp_vis/pp_vis_core.h | 1 + src/pp/pp_vis/pp_vis_shared.cgh | 1 + 6 files changed, 172 insertions(+), 28 deletions(-) diff --git a/src/pp/pp.c b/src/pp/pp.c index 731f047d..98a601dd 100644 --- a/src/pp/pp.c +++ b/src/pp/pp.c @@ -267,7 +267,8 @@ P_Anim P_AnimFromEnt(P_Frame *frame, P_Ent *ent) result.span = SPR_SpanKeyFromName(Lit("roll")); result.weapon_over = 1; } - else if (Vec2LenSq(ent->control.move) > (0.01 * 0.01)) + // else if (Vec2LenSq(ent->control.move) > (0.01 * 0.01)) + else if (0) { result.span = SPR_SpanKeyFromName(Lit("walk")); if (ent->is_guy) @@ -1935,22 +1936,23 @@ void P_SpawnEntsFromList(P_Frame *frame, P_EntList ents) DllQueuePushNPZ(&P_NilEnt, frame->first_ent, frame->last_ent, dst, next, prev); DllQueuePushNP(bin->first, bin->last, dst, next_in_bin, prev_in_bin); } - P_Ent *old_next = dst->next; - P_Ent *old_prev = dst->prev; - P_Ent *old_next_in_bin = dst->next_in_bin; - P_Ent *old_prev_in_bin = dst->prev_in_bin; { - *dst = *src; - dst->xf = NormXform(dst->xf); + P_Ent *old_next = dst->next; + P_Ent *old_prev = dst->prev; + P_Ent *old_next_in_bin = dst->next_in_bin; + P_Ent *old_prev_in_bin = dst->prev_in_bin; + { + *dst = *src; + dst->xf = NormXform(dst->xf); + } + dst->next = old_next; + dst->prev = old_prev; + dst->next_in_bin = old_next_in_bin; + dst->prev_in_bin = old_prev_in_bin; } - dst->next = old_next; - dst->prev = old_prev; - dst->next_in_bin = old_next_in_bin; - dst->prev_in_bin = old_prev_in_bin; dst->created_at_ns = frame->time_ns; dst->created_at_tick = frame->tick; ++frame->ents_count; - if (!P_tl.is_predicting) { dst->sim = 1; diff --git a/src/pp/pp.h b/src/pp/pp.h index a2306e0e..89f946ea 100644 --- a/src/pp/pp.h +++ b/src/pp/pp.h @@ -118,6 +118,8 @@ Struct(P_Ent) //- Build data + u64 continuity_gen; + f32 exists; b32 sim; diff --git a/src/pp/pp_sim/pp_sim_core.c b/src/pp/pp_sim/pp_sim_core.c index fbb11522..eff9947c 100644 --- a/src/pp/pp_sim/pp_sim_core.c +++ b/src/pp/pp_sim/pp_sim_core.c @@ -580,6 +580,7 @@ void S_TickForever(WaveLaneCtx *lane) if (!P_IsEntNil(ent)) { ent->xf = msg->xf; + ent->continuity_gen += 1; } } break; //- Prefab @@ -601,6 +602,7 @@ void S_TickForever(WaveLaneCtx *lane) bot->is_bot = 1; P_SetEntString(bot, Lit("Bot")); } + bot->continuity_gen += 1; P_Ent *guy = P_EntFromKey(world_frame, bot->guy); if (!guy->is_guy) { @@ -611,6 +613,7 @@ void S_TickForever(WaveLaneCtx *lane) bot->guy = guy->key; } guy->xf = msg->xf; + guy->continuity_gen += 1; } break; case P_PrefabKind_GuySpawn: @@ -623,6 +626,7 @@ void S_TickForever(WaveLaneCtx *lane) spawn->is_guy_spawn = 1; } spawn->xf = msg->xf; + spawn->continuity_gen += 1; } break; } P_SpawnEntsFromList(world_frame, ents); diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 5fe0d69d..8386c581 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -571,6 +571,7 @@ void V_TickForever(WaveLaneCtx *lane) frame->is_editing = prev_frame->is_editing; frame->ui_debug = prev_frame->ui_debug; frame->show_console = prev_frame->show_console; + frame->show_timeline = prev_frame->show_timeline; frame->look = prev_frame->look; frame->fire_presses = prev_frame->fire_presses; frame->roll_presses = prev_frame->roll_presses; @@ -1619,6 +1620,7 @@ void V_TickForever(WaveLaneCtx *lane) P_World *predict_world = predict_worlds[cur_predict_world_seq % countof(predict_worlds)]; P_World *prev_predict_world = predict_worlds[MaxI64(cur_predict_world_seq - 1, 0) % countof(predict_worlds)]; + P_Frame *predict_frame = predict_world->last_frame; { if (predict_world->tiles_hash != sim_world->tiles_hash) @@ -1854,22 +1856,35 @@ void V_TickForever(WaveLaneCtx *lane) // i64 target_blend_dt_ns = frame->dt_ns + frame->dt_ns * dilation_factor; i64 target_blend_dt_ns = frame->dt_ns; i64 blend_dt_ns = frame->dt_ns; + // i64 blend_dt_ns = target_blend_dt_ns; - V.target_blend_time_ns += target_blend_dt_ns; + // V.target_blend_time_ns += target_blend_dt_ns; V.blend_time_ns += blend_dt_ns; // How many ticks back in time should the user thread blend between? // = * // E.g: At 1.5, the world will render 75ms back in time if the sim runs at 50tps f32 interp_ratio = TweakFloat("Interpolation ratio", 1.2, 0, V_MaxInterpRatio); + // if (ack != prev_frame_ack) + // { + // i64 delay_ns = SIM_TICK_INTERVAL_NS * interp_ratio; + // V.target_blend_time_ns = ack_frame->time_ns - delay_ns; + // } + + if (ack != prev_frame_ack) { i64 delay_ns = SIM_TICK_INTERVAL_NS * interp_ratio; - V.target_blend_time_ns = ack_frame->time_ns - delay_ns; + V.target_blend_time_ns = predict_frame->time_ns - delay_ns; } + + // f64 blend_to_target_lerp_rate = 0.01 * frame->dt; + + f64 blend_to_target_lerp_rate = 1 * frame->dt; + + // f64 blend_to_target_lerp_rate = 0.0; // f64 blend_to_target_lerp_rate = 0.05; - f64 blend_to_target_lerp_rate = 1; // f64 blend_to_target_lerp_rate = 1; V.blend_time_ns = LerpI64(V.blend_time_ns, V.target_blend_time_ns, blend_to_target_lerp_rate); if (AbsI64(V.blend_time_ns - V.target_blend_time_ns) > (SIM_TICK_INTERVAL_NS * interp_ratio * 2)) @@ -1878,7 +1893,7 @@ void V_TickForever(WaveLaneCtx *lane) V.blend_time_ns = V.target_blend_time_ns; } - if (TweakBool("Interpolation enabled", 0)) + if (TweakBool("Interpolation enabled", 1)) { P_Frame *left_predict_frame = &P_NilFrame; P_Frame *right_predict_frame = &P_NilFrame; @@ -1911,19 +1926,28 @@ void V_TickForever(WaveLaneCtx *lane) P_World *tmp_right_world = predict_worlds[tmp_right_predict_world_idx]; P_World *tmp_left_world = predict_worlds[tmp_left_predict_world_idx]; - // P_Frame *tmp_right = P_FrameFromTick(tmp_right_world, tmp_right_world->last_frame->base_predict_to); - // P_Frame *tmp_left = P_FrameFromTick(tmp_left_world, tmp_left_world->last_frame->base_predict_to); - // P_Frame *tmp_right = tmp_right_world->last_frame; // P_Frame *tmp_left = tmp_left_world->last_frame; + // P_Frame *tmp_right = P_FrameFromTick(tmp_right_world, tmp_right_world->last_frame->base_predict_to); + // P_Frame *tmp_left = P_FrameFromTick(tmp_left_world, tmp_left_world->last_frame->base_predict_to); + P_Frame *tmp_right = P_FrameFromTick(tmp_right_world, tmp_right_world->last_frame->base_predict_from); P_Frame *tmp_left = P_FrameFromTick(tmp_left_world, tmp_left_world->last_frame->base_predict_from); - // P_Frame *tmp_right = tmp_right_world->last_frame; - // P_Frame *tmp_left = tmp_left_world->last_frame; - if (tmp_right->base_time_ns >= V.blend_time_ns && tmp_left->base_time_ns <= V.blend_time_ns) + + // i64 right_time_ns = tmp_right->base_time_ns; + // i64 left_time_ns = tmp_left->base_time_ns; + + + + i64 right_time_ns = tmp_right->time_ns; + i64 left_time_ns = tmp_left->time_ns; + + + + if (right_time_ns >= V.blend_time_ns && left_time_ns <= V.blend_time_ns) { right_predict_frame = tmp_right; left_predict_frame = tmp_left; @@ -2004,6 +2028,9 @@ void V_TickForever(WaveLaneCtx *lane) f64 blend_t = (f64)(V.blend_time_ns - left_predict_frame->base_time_ns) / (f64)(right_predict_frame->base_time_ns - left_predict_frame->base_time_ns); + + + if (IsInf(blend_t)) { blend_t = 1; @@ -2028,7 +2055,7 @@ void V_TickForever(WaveLaneCtx *lane) } ent->exists = LerpF32(left->exists, right->exists, blend_t); - if (!P_IsEntNil(right)) + if (!P_IsEntNil(right) && right->continuity_gen == left->continuity_gen) { ent->health = LerpF32(left->health, right->health, blend_t); ent->xf.t = LerpVec2(left->xf.t, right->xf.t, blend_t); @@ -2037,7 +2064,6 @@ void V_TickForever(WaveLaneCtx *lane) ent->v = LerpVec2(left->v, right->v, blend_t); ent->w = LerpF32(left->w, right->w, blend_t); } - } } } @@ -2814,9 +2840,9 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Push test emitter - if (frame->held_buttons[Button_F]) + // if (frame->held_buttons[Button_F]) // if (frame->held_buttons[Button_F] && !prev_frame->held_buttons[Button_F]) - // if (0) + if (0) { { V_Emitter emitter = Zi; @@ -2876,9 +2902,9 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Push test explosion - if (frame->held_buttons[Button_G]) + // if (frame->held_buttons[Button_G]) // if (frame->held_buttons[Button_G] && !prev_frame->held_buttons[Button_G]) - // if (0) + if (0) { // Fire { @@ -4542,6 +4568,108 @@ void V_TickForever(WaveLaneCtx *lane) UI_PopCP(UI_TopCP()); } + + ////////////////////////////// + //- Build timeline debug UI + + if (frame->show_timeline) + { + UI_Key timeline_key = UI_KeyF("Timeline box"); + + + + + // FIXME: Remove this + i64 timeline_span_ns = NsFromSeconds(10); + PERSIST i64 last_timeline_switch_ns = 0; + PERSIST i64 timeline_start_ns = 0; + + if (frame->time_ns - last_timeline_switch_ns > timeline_span_ns) + { + last_timeline_switch_ns = frame->time_ns; + timeline_start_ns = ack_frame->time_ns; + } + + UI_Size timeline_width = UI_FNT(80, 1); + UI_Size timeline_height = UI_FNT(2.5, 1); + Vec4 tmld_bg = Color_Black; + f32 timeline_opacity = 0.75; + Vec2 timeline_pos = VEC2(frame->screen_dims.x * 0.5, frame->screen_dims.y * 0.25); + + //- Push markers + Struct(TimelineMarker) + { + TimelineMarker *next; + i64 time_ns; + Vec4 color; + }; + TimelineMarker *first_marker = 0; + TimelineMarker *last_marker = 0; + { + // Now frame time + // { + // TimelineMarker *marker = PushStruct(frame->arena, TimelineMarker); + // SllQueuePush(first_marker, last_marker, marker); + // marker->color = Color_White; + // marker->time_ns = frame->time_ns; + // } + // Ack frame time + { + TimelineMarker *marker = PushStruct(frame->arena, TimelineMarker); + SllQueuePush(first_marker, last_marker, marker); + marker->color = Color_Yellow; + marker->time_ns = ack_frame->time_ns; + } + } + + // i64 timeline_span_ns = NsFromSeconds(10); + i64 timeline_span_ns = NsFromSeconds(1); + i64 timeline_start_ns = (frame->time_ns / timeline_span_ns) * timeline_span_ns; + + i64 timeline_end_ns = timeline_start_ns + timeline_span_ns; + + UI_Size marker_width = UI_FNT(0.5, 1); + UI_Size marker_height = timeline_height; + + UI_PushCP(UI_NilKey); + { + UI_Push(Tint, VEC4(1, 1, 1, timeline_opacity)); + + //- Build timeline + { + UI_SetNext(Anchor, UI_Region_Top); + UI_SetNext(Width, timeline_width); + UI_SetNext(Height, timeline_height); + UI_SetNext(Anchor, UI_Region_Center); + UI_SetNext(FloatingPos, timeline_pos); + UI_SetNext(BackgroundColor, tmld_bg); + UI_SetNext(Flags, UI_BoxFlag_Floating); + UI_PushCP(UI_BuildRowEx(timeline_key)); + { + } + UI_PopCP(UI_TopCP()); + } + + //- Build markers + for (TimelineMarker *marker = first_marker; marker; marker = marker->next) + { + f64 marker_ratio = (f64)(marker->time_ns - timeline_start_ns) / (f64)(timeline_end_ns - timeline_start_ns); + Vec2 marker_pos = timeline_pos; + marker_pos.x += marker_ratio * timeline_width.v; + + UI_SetNext(Flags, UI_BoxFlag_Floating); + UI_SetNext(BackgroundColor, marker->color); + UI_SetNext(Anchor, UI_Region_Center); + UI_SetNext(Width, marker_width); + UI_SetNext(Height, marker_height); + UI_SetNext(FloatingPos, marker_pos); + UI_BuildBox(); + } + } + UI_PopCP(UI_TopCP()); + } + + ////////////////////////////// //- Build scoreboard UI @@ -4948,6 +5076,12 @@ void V_TickForever(WaveLaneCtx *lane) frame->show_console = new; } break; + case V_CmdKind_toggle_timeline: + { + b32 new = !frame->show_timeline; + frame->show_timeline = new; + } break; + case V_CmdKind_toggle_fullscreen: { b32 new = !window_frame.fullscreen; diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index f382ad05..edac1141 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -11,6 +11,7 @@ X(save_level, Save level, V_CmdDescFlag_None, V_HOTKEY( Button_S, .ctrl = 1 ), ) \ X(toggle_ui_debug, Toggle UI Debug, V_CmdDescFlag_None, V_HOTKEY( Button_F5 ), ) \ X(toggle_console, Toggle Developer Console, V_CmdDescFlag_None, V_HOTKEY( Button_GraveAccent ), ) \ + X(toggle_timeline, Toggle Debug Timeline, V_CmdDescFlag_None, V_HOTKEY( Button_GraveAccent, .alt = 1 ), ) \ X(toggle_fullscreen, Toggle Fullscreen Mode, V_CmdDescFlag_None, V_HOTKEY( Button_Enter, .alt = 1 ) ) \ X(toggle_window_topmost, Toggle Window Topmost, V_CmdDescFlag_None, V_HOTKEY( Button_F4 ), ) \ X(tp_player, Teleport Player, V_CmdDescFlag_None, V_HOTKEY( Button_Q ), ) \ diff --git a/src/pp/pp_vis/pp_vis_shared.cgh b/src/pp/pp_vis/pp_vis_shared.cgh index c9a8e9d4..1a70106b 100644 --- a/src/pp/pp_vis/pp_vis_shared.cgh +++ b/src/pp/pp_vis/pp_vis_shared.cgh @@ -283,6 +283,7 @@ Struct(V_SharedFrame) b32 is_editing; b32 ui_debug; b32 show_console; + b32 show_timeline; b32 is_selecting; b32 is_panning; b32 has_mouse_focus;