From df5346f514ff2fb5e267410ef015b72ab49e12cb Mon Sep 17 00:00:00 2001 From: jacob Date: Fri, 13 Mar 2026 13:10:09 -0500 Subject: [PATCH] timeline visualization --- src/pp/pp_vis/pp_vis_core.c | 249 +++++++++++++++++++++----------- src/pp/pp_vis/pp_vis_core.h | 23 +++ src/pp/pp_vis/pp_vis_shared.cgh | 3 +- 3 files changed, 190 insertions(+), 85 deletions(-) diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 8386c581..90489bf5 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -5,6 +5,9 @@ V_Ctx V = Zi; void V_Bootstrap(void) { + V.timeline.transient_markers_arena = AcquireArena(Gibi(64)); + V.timeline.persistent_markers_arena = AcquireArena(Gibi(64)); + V.timeline.show = 1; DispatchWave(Lit("Vis"), 1, V_TickForever, 0); OnExit(V_Shutdown); } @@ -252,6 +255,27 @@ void V_DrawPoint(Vec2 p, Vec4 srgb) V_DrawShape(ui_shape, AffineIdentity, srgb, 24, V_DrawFlag_None); } +//////////////////////////////////////////////////////////// +//~ Timeline helpers + +void V_PushTimelineMarker(i64 time_ns, Vec4 color, b32 transient) +{ + if (!V.timeline.paused) + { + V_TimelineMarker *marker = 0; + if (transient) + { + marker = PushStruct(V.timeline.transient_markers_arena, V_TimelineMarker); + } + else + { + marker = PushStruct(V.timeline.persistent_markers_arena, V_TimelineMarker); + } + marker->time_ns = time_ns; + marker->color = color; + } +} + //////////////////////////////////////////////////////////// //~ Theme @@ -571,7 +595,6 @@ 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; @@ -1671,6 +1694,12 @@ void V_TickForever(WaveLaneCtx *lane) // P_DebugDrawFrame(predict_frame); // TODO: Extract information that occurred between first & last prediction, like bullet hits etc? + + V_PushTimelineMarker(predict_frame->time_ns, Color_Purple, 1); + if (predict_to != prev_frame_predict_to) + { + V_PushTimelineMarker(predict_frame->time_ns, Color_Purple, 0); + } } @@ -1876,6 +1905,8 @@ void V_TickForever(WaveLaneCtx *lane) { i64 delay_ns = SIM_TICK_INTERVAL_NS * interp_ratio; V.target_blend_time_ns = predict_frame->time_ns - delay_ns; + + V_PushTimelineMarker(ack_frame->time_ns, Color_Cyan, 0); } @@ -2029,6 +2060,10 @@ 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); + V_PushTimelineMarker(V.blend_time_ns, Color_Yellow, 1); + V_PushTimelineMarker(V.target_blend_time_ns, Color_Red, 1); + + if (IsInf(blend_t)) @@ -2334,7 +2369,7 @@ void V_TickForever(WaveLaneCtx *lane) frame->shade_cursor = MulAffineVec2(frame->af.screen_to_shade, frame->screen_cursor); frame->world_cursor = MulAffineVec2(frame->af.screen_to_world, frame->screen_cursor); - b32 show_editor_ui = TweakBool("Show editor UI", 1); + b32 show_editor_ui = TweakBool("Show editor UI", 0); frame->world_selection_start = frame->world_cursor; if (frame->is_editing) @@ -4572,101 +4607,149 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- 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; + f64 timeline_span_seconds = TweakFloat("Timeline span", 1, 0.01, 5); + i64 timeline_span_ns = NsFromSeconds(timeline_span_seconds); + PERSIST i64 last_timeline_reset_ns = 0; PERSIST i64 timeline_start_ns = 0; + PERSIST i64 current_marker_time_ns = 0; - if (frame->time_ns - last_timeline_switch_ns > timeline_span_ns) + // Current frame time marker + if (!V.timeline.paused) { - last_timeline_switch_ns = frame->time_ns; + current_marker_time_ns = frame->time_ns - last_timeline_reset_ns + timeline_start_ns; + } + V_PushTimelineMarker(current_marker_time_ns, Color_White, 1); + + b32 locked = frame->held_buttons[Button_R]; + b32 paused = frame->held_buttons[Button_E]; + V.timeline.locked = locked; + V.timeline.paused = paused; + + i64 reset_span_ns = timeline_span_ns; + if (locked) + { + reset_span_ns *= 0.5; + } + + if (!paused && frame->time_ns - last_timeline_reset_ns > reset_span_ns) + { + last_timeline_reset_ns = frame->time_ns; timeline_start_ns = ack_frame->time_ns; + ResetArena(V.timeline.persistent_markers_arena); } - - 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) + if (V.timeline.show) { - 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 + UI_Key timeline_key = UI_KeyF("Timeline box"); + + UI_Size timeline_width = UI_FNT(120, 1); + UI_Size timeline_height = UI_FNT(2, 1); + Vec4 tmld_bg = Color_Black; + Vec2 timeline_pos = VEC2(frame->screen_dims.x * 0.5, frame->screen_dims.y * 0.25); + + f32 timeline_opacity = 0.75; + f32 marker_opacity = 0.75; + + // i64 timeline_span_ns = NsFromSeconds(10); + + i64 timeline_end_ns = timeline_start_ns + timeline_span_ns; + + UI_Size transient_marker_width = UI_FNT(0.1, 1); + UI_Size transient_marker_height = UI_FNT(2, 1); + + UI_Size persistent_marker_width = UI_FNT(0.1, 1); + UI_Size persistent_marker_height = timeline_height; + + UI_PushCP(UI_NilKey); { - TimelineMarker *marker = PushStruct(frame->arena, TimelineMarker); - SllQueuePush(first_marker, last_marker, marker); - marker->color = Color_Yellow; - marker->time_ns = ack_frame->time_ns; - } - } + UI_Push(Tint, VEC4(1, 1, 1, timeline_opacity)); - // 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)); + //- 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_BoxFlag_NoFloatingClamp); + UI_PushCP(UI_BuildRowEx(timeline_key)); + { + } + UI_PopCP(UI_TopCP()); } - 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; + Vec2 marker_offset = Zi; + if (locked) + { + i64 offset_ns = current_marker_time_ns; + marker_offset.x = -((f64)(offset_ns - timeline_start_ns) / (f64)timeline_span_ns) * timeline_width.v + timeline_width.v * 0.5; + } + + //- Build markers + Arena *marker_arenas[] = { + V.timeline.transient_markers_arena, + V.timeline.persistent_markers_arena, + }; + for (u64 marker_arena_idx = 0; marker_arena_idx < countof(marker_arenas); ++marker_arena_idx) + { + Arena *marker_arena = marker_arenas[marker_arena_idx]; + u64 markers_count = ArenaCount(marker_arena, V_TimelineMarker); + b32 is_transient = marker_arena == V.timeline.transient_markers_arena; + V_TimelineMarker *markers = ArenaFirst(marker_arena, V_TimelineMarker); + for (u64 marker_idx = 0; marker_idx < markers_count; ++marker_idx) + { + V_TimelineMarker *marker = &markers[marker_idx]; + + UI_Size width = persistent_marker_width; + UI_Size height = persistent_marker_height; + if (is_transient) + { + width = transient_marker_width; + height = transient_marker_height; + } + + Vec4 marker_color = marker->color; + + f64 marker_ratio = (f64)(marker->time_ns - timeline_start_ns) / (f64)timeline_span_ns; + Vec2 marker_pos = timeline_pos; + if (is_transient) + { + marker_pos.x += -timeline_width.v * 0.5 - width.v * 0.5; + marker_pos.y -= timeline_height.v * 0.5; + } + else + { + marker_pos.x += -timeline_width.v * 0.5 - width.v * 0.5; + marker_pos.y += timeline_height.v * 0.5; + } + marker_pos.x += marker_ratio * timeline_width.v; + + marker_pos = AddVec2(marker_pos, marker_offset); + + Vec4 tint = UI_Top(Tint); + tint.a *= marker_opacity; + + UI_SetNext(Tint, tint); + UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClamp); + UI_SetNext(BackgroundColor, marker_color); + UI_SetNext(Anchor, UI_Region_Center); + UI_SetNext(Width, width); + UI_SetNext(Height, height); + UI_SetNext(FloatingPos, marker_pos); + UI_BuildBox(); + } + } - 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()); + } + + if (!paused) + { + ResetArena(V.timeline.transient_markers_arena); } - UI_PopCP(UI_TopCP()); } @@ -5078,8 +5161,8 @@ void V_TickForever(WaveLaneCtx *lane) case V_CmdKind_toggle_timeline: { - b32 new = !frame->show_timeline; - frame->show_timeline = new; + b32 new = !V.timeline.show; + V.timeline.show = new; } break; case V_CmdKind_toggle_fullscreen: diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index edac1141..9a5e5430 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -216,6 +216,15 @@ Enum(V_DrawFlag) V_DrawFlag_Line = (1 << 0), }; +//////////////////////////////////////////////////////////// +//~ Timeline types + +Struct(V_TimelineMarker) +{ + Vec4 color; + i64 time_ns; +}; + //////////////////////////////////////////////////////////// //~ State types @@ -261,6 +270,15 @@ Struct(V_Ctx) P_EntKey player_key; P_EntKey follow_key; + struct + { + b32 show; + b32 paused; + b32 locked; + Arena *transient_markers_arena; + Arena *persistent_markers_arena; + } timeline; + i64 panels_count; i64 windows_count; V_Panel *root_panel; @@ -311,6 +329,11 @@ void V_DrawLine(Vec2 p0, Vec2 p1, Vec4 srgb); void V_DrawRect(Rng2 rect, Vec4 srgb, V_DrawFlag flags); void V_DrawPoint(Vec2 p, Vec4 srgb); +//////////////////////////////////////////////////////////// +//~ Timeline helpers + +void V_PushTimelineMarker(i64 time_ns, Vec4 color, b32 transient); + //////////////////////////////////////////////////////////// //~ Theme diff --git a/src/pp/pp_vis/pp_vis_shared.cgh b/src/pp/pp_vis/pp_vis_shared.cgh index 1a70106b..1ad7e583 100644 --- a/src/pp/pp_vis/pp_vis_shared.cgh +++ b/src/pp/pp_vis/pp_vis_shared.cgh @@ -229,8 +229,8 @@ Enum(V_SelectionMode) Enum(V_EditMode) { - V_EditMode_Tile, V_EditMode_Prefab, + V_EditMode_Tile, }; Struct(V_Affines) @@ -283,7 +283,6 @@ 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;