base blend time off of accumulated prediction tick

This commit is contained in:
jacob 2026-03-13 17:13:12 -05:00
parent 1caf006e33
commit 63c3b4d427
3 changed files with 560 additions and 212 deletions

View File

@ -5,9 +5,8 @@ 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;
V.transient_markers_arena = AcquireArena(Gibi(64));
V.persistent_markers_arena = AcquireArena(Gibi(64));
DispatchWave(Lit("Vis"), 1, V_TickForever, 0);
OnExit(V_Shutdown);
}
@ -260,16 +259,17 @@ void V_DrawPoint(Vec2 p, Vec4 srgb)
void V_PushTimelineMarker(i64 time_ns, Vec4 color, b32 transient)
{
if (!V.timeline.paused)
V_Frame *prev_frame = V_PrevFrame();
if (!prev_frame->prev_timeline.paused)
{
V_TimelineMarker *marker = 0;
if (transient)
{
marker = PushStruct(V.timeline.transient_markers_arena, V_TimelineMarker);
marker = PushStruct(V.transient_markers_arena, V_TimelineMarker);
}
else
{
marker = PushStruct(V.timeline.persistent_markers_arena, V_TimelineMarker);
marker = PushStruct(V.persistent_markers_arena, V_TimelineMarker);
}
marker->time_ns = time_ns;
marker->color = color;
@ -603,9 +603,13 @@ void V_TickForever(WaveLaneCtx *lane)
frame->equipped_prefab = prev_frame->equipped_prefab ;
frame->edit_camera_pos = prev_frame->edit_camera_pos;
frame->edit_camera_zoom = prev_frame->edit_camera_zoom;
frame->prev_timeline = prev_frame->timeline;
frame->timeline = prev_frame->timeline;
frame->sim_key = prev_frame->sim_key;
frame->desired_sim_key = prev_frame->desired_sim_key;
frame->target_predict_tick_accum = prev_frame->target_predict_tick_accum;
frame->predict_tick_accum = prev_frame->predict_tick_accum;
frame->blend_tick = prev_frame->blend_tick;
frame->af = prev_frame->af;
frame->tick = V.cur_frame_tick;
@ -621,6 +625,12 @@ void V_TickForever(WaveLaneCtx *lane)
TrueRand(StringFromStruct(&V.player_key));
}
if (frame->tick == 1)
{
frame->timeline.show = 1;
frame->timeline.locked = 1;
}
//////////////////////////////
//- Initialize persistent gpu resources
@ -1359,6 +1369,13 @@ void V_TickForever(WaveLaneCtx *lane)
}
}
//////////////////////////////
//- Determine local simulation bounds
//
@ -1373,6 +1390,8 @@ void V_TickForever(WaveLaneCtx *lane)
f64 dilation_factor = 0;
{
// TODO: Warm start
// How many buffered commands of ours we'd like the server to have
i64 target_buffered_controls_count = 1;
f64 rtt_bias_factor = 10.0;
@ -1383,12 +1402,21 @@ void V_TickForever(WaveLaneCtx *lane)
target_buffered_controls_count - remote_buffered_controls_count
) * 2 - 1;
f64 predict_dt = frame->dt + frame->dt * dilation_factor;
frame->predict_tick_accum += predict_dt * SIM_TICKS_PER_SECOND;
f64 target_predict_dt = frame->dt + frame->dt * dilation_factor;
frame->target_predict_tick_accum += target_predict_dt * SIM_TICKS_PER_SECOND;
// Warm start
frame->predict_tick_accum += frame->dt * SIM_TICKS_PER_SECOND;
f64 lerp_rate = 5.0 * frame->dt;
// f64 lerp_rate = 1.0 * frame->dt;
// f64 lerp_rate = 1;
frame->predict_tick_accum = LerpF64(frame->predict_tick_accum, frame->target_predict_tick_accum, lerp_rate);
if (ack == 0 || AbsF64(frame->predict_tick_accum - ack) > SIM_TICKS_PER_SECOND)
{
frame->predict_tick_accum = ack + (SIM_TICKS_PER_SECOND * smoothed_rtt * 0.5);
frame->target_predict_tick_accum = ack + (SIM_TICKS_PER_SECOND * smoothed_rtt * 0.5);
frame->predict_tick_accum = frame->target_predict_tick_accum;
LogDebugF("RESET");
}
@ -1402,10 +1430,98 @@ void V_TickForever(WaveLaneCtx *lane)
// );
frame->predict_tick_accum = MaxF64(prev_frame->predict_tick_accum, frame->predict_tick_accum);
V_PushTimelineMarker(frame->predict_tick_accum * SIM_TICK_INTERVAL_NS, Color_Red, 1);
V_PushTimelineMarker(frame->target_predict_tick_accum * SIM_TICK_INTERVAL_NS, Color_Yellow, 1);
}
i64 prev_frame_predict_to = FloorF64(prev_frame->predict_tick_accum);
i64 predict_to = FloorF64(frame->predict_tick_accum);
// //////////////////////////////
// //- Determine local simulation bounds
// //
// // Prediction rate will increase based on how many commands of ours we know
// // that the server has buffered up or is missing.
// //
// // If the server is missing commands, we increase the rate of prediction
// // until the server reports it has enough of our commands buffered up.
// //
// // If the server reports it has too many of our commands buffered up, we
// // slow the rate of prediction.
// f64 dilation_factor = 0;
// {
// // TODO: Warm start
// // How many buffered commands of ours we'd like the server to have
// i64 target_buffered_controls_count = 1;
// f64 rtt_bias_factor = 10.0;
// dilation_factor = SmoothstepF64(
// -(SIM_TICKS_PER_SECOND * smoothed_rtt * rtt_bias_factor),
// (SIM_TICKS_PER_SECOND * smoothed_rtt * rtt_bias_factor),
// target_buffered_controls_count - remote_buffered_controls_count
// ) * 2 - 1;
// f64 target_predict_dt = frame->dt + frame->dt * dilation_factor;
// frame->target_predict_tick_accum += target_predict_dt * SIM_TICKS_PER_SECOND;
// frame->predict_tick_accum += frame->dt * SIM_TICKS_PER_SECOND;
// // f64 lerp_rate = 5.0 * frame->dt;
// f64 lerp_rate = 1.0 * frame->dt;
// // f64 lerp_rate = 1;
// frame->predict_tick_accum = LerpF64(frame->predict_tick_accum, frame->target_predict_tick_accum, lerp_rate);
// if (ack == 0 || AbsF64(frame->predict_tick_accum - ack) > SIM_TICKS_PER_SECOND)
// {
// frame->target_predict_tick_accum = ack + (SIM_TICKS_PER_SECOND * smoothed_rtt * 0.5);
// frame->predict_tick_accum = frame->target_predict_tick_accum;
// LogDebugF("RESET");
// }
// // LogDebugF(
// // "Buffered count: %F. Ack: %F, Predict tick accum: %F, Predict dt: %F, Dilation factor: %F",
// // FmtSint(remote_buffered_controls_count),
// // FmtFloat(ack),
// // FmtFloat(frame->predict_tick_accum),
// // FmtFloat(predict_dt),
// // FmtFloat(dilation_factor)
// // );
// frame->predict_tick_accum = MaxF64(prev_frame->predict_tick_accum, frame->predict_tick_accum);
// V_PushTimelineMarker(frame->predict_tick_accum * SIM_TICK_INTERVAL_NS, Color_Red, 1);
// V_PushTimelineMarker(frame->target_predict_tick_accum * SIM_TICK_INTERVAL_NS, Color_Yellow, 1);
// }
// i64 prev_frame_predict_to = FloorF64(prev_frame->predict_tick_accum);
// i64 predict_to = FloorF64(frame->predict_tick_accum);
//////////////////////////////
//- Create player control
@ -1636,6 +1752,10 @@ void V_TickForever(WaveLaneCtx *lane)
ack_frame->base_predict_from = ack_frame->tick;
ack_frame->base_predict_to = predict_to;
cur_predict_world_seq += 1;
if (ack != prev_frame_ack)
{
V_PushTimelineMarker(ack_frame->time_ns, Color_Green, 0);
}
}
@ -1875,6 +1995,15 @@ void V_TickForever(WaveLaneCtx *lane)
P_Frame *prev_local_frame = prev_frame->local_world->last_frame;
P_Frame *local_frame = &P_NilFrame;
{
@ -1887,89 +2016,21 @@ void V_TickForever(WaveLaneCtx *lane)
frame->local_world->seed = predict_world->seed;
P_ClearFrames(frame->local_world, I64Min, I64Max);
// 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 = frame->dt_ns + frame->dt_ns * dilation_factor;
// V.target_blend_time_ns += target_blend_dt_ns;
V.blend_time_ns += blend_dt_ns;
frame->blend_tick += frame->dt * SIM_TICKS_PER_SECOND;
// How many ticks back in time should the user thread blend between?
// <Delay> = <USER_INTERP_RATIO> * <Tick interval>
// 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 (predict_to != prev_frame_predict_to)
// {
// i64 delay_ns = SIM_TICK_INTERVAL_NS * interp_ratio;
// V.target_blend_time_ns = predict_frame->time_ns - delay_ns;
// }
{
P_Frame *target_blend_frame = P_FrameFromTick(predict_world, predict_world->last_frame->base_predict_to);
i64 delay_ns = SIM_TICK_INTERVAL_NS * interp_ratio;
V.target_blend_time_ns = target_blend_frame->time_ns - delay_ns;
}
if (ack != prev_frame_ack)
{
V_PushTimelineMarker(ack_frame->time_ns, Color_Green, 0);
}
// 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 = 1 * frame->dt;
// f64 blend_to_target_lerp_rate = 0.0 * 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;
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))
{
LogDebugF("Blend reset");
V.blend_time_ns = V.target_blend_time_ns;
V_PushTimelineMarker(V.blend_time_ns, Color_Red, 0);
}
frame->blend_tick = frame->predict_tick_accum - interp_ratio;
if (TweakBool("Interpolation enabled", 1))
{
P_Frame *left_predict_frame = &P_NilFrame;
P_Frame *right_predict_frame = &P_NilFrame;
// for (P_Frame *tmp = predict_world->last_frame; !P_IsFrameNil(tmp); tmp = tmp->prev)
// {
// if (tmp->time_ns >= V.blend_time_ns && tmp->prev->time_ns <= V.blend_time_ns)
// {
// right_predict_frame = tmp;
// left_predict_frame = tmp->prev;
// break;
// }
// }
V_PushTimelineMarker(V.blend_time_ns, Color_Red, 1);
V_PushTimelineMarker(V.target_blend_time_ns, Color_Yellow, 1);
// Locate right & left frames
{
for (
i64 predict_world_seq = cur_predict_world_seq;
@ -1977,97 +2038,65 @@ void V_TickForever(WaveLaneCtx *lane)
--predict_world_seq
)
{
i64 tmp_right_predict_world_idx = predict_world_seq % countof(predict_worlds);
i64 tmp_left_predict_world_idx = (predict_world_seq - 1) % countof(predict_worlds);
P_World *tmp_right_world = predict_worlds[tmp_right_predict_world_idx];
P_World *tmp_left_world = predict_worlds[tmp_left_predict_world_idx];
b32 search_right = P_IsFrameNil(right_predict_frame);
b32 search_left = P_IsFrameNil(left_predict_frame);
// 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);
// i64 right_time_ns = tmp_right->base_time_ns;
// i64 left_time_ns = tmp_left->base_time_ns;
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;
i64 right_time_ns = tmp_right->time_ns;
i64 left_time_ns = tmp_left->time_ns;
V_PushTimelineMarker(left_time_ns, Color_Cyan, 1);
V_PushTimelineMarker(right_time_ns, Color_Orange, 1);
if (right_time_ns >= V.blend_time_ns && left_time_ns <= V.blend_time_ns)
// Locate right frame
if (search_right)
{
i64 tmp_world_idx = predict_world_seq % countof(predict_worlds);
P_World *tmp_world = predict_worlds[tmp_world_idx];
for (P_Frame *tmp_right_frame = tmp_world->last_frame; !P_IsFrameNil(tmp_right_frame); tmp_right_frame = tmp_right_frame->prev)
{
P_Frame *tmp_left_frame = tmp_right_frame->prev;
if (tmp_right_frame->tick >= frame->blend_tick && tmp_left_frame->tick <= frame->blend_tick)
{
right_predict_frame = tmp_right_frame;
break;
}
}
}
// Locate left frame
if (search_left)
{
i64 tmp_world_idx = (predict_world_seq - 1) % countof(predict_worlds);
P_World *tmp_world = predict_worlds[tmp_world_idx];
for (P_Frame *tmp_frame = tmp_world->last_frame; !P_IsFrameNil(tmp_frame); tmp_frame = tmp_frame->prev)
{
if (tmp_frame->tick <= frame->blend_tick)
{
left_predict_frame = tmp_frame;
break;
}
}
}
// V_PushTimelineMarker(left_time_ns, Color_Cyan, 1);
// V_PushTimelineMarker(right_time_ns, Color_Orange, 1);
if (!search_right && !search_left)
{
right_predict_frame = tmp_right;
left_predict_frame = tmp_left;
break;
}
}
// for (P_Frame *tmp = predict_world->last_frame; !P_IsFrameNil(tmp); tmp = tmp->prev)
// {
// if (tmp->time_ns >= V.blend_time_ns && tmp->prev->time_ns <= V.blend_time_ns)
// {
// right_predict_frame = tmp;
// break;
// }
// }
// for (P_Frame *tmp = prev_predict_world->last_frame; !P_IsFrameNil(tmp); tmp = tmp->prev)
// {
// if (tmp->time_ns >= V.blend_time_ns && tmp->prev->time_ns <= V.blend_time_ns)
// {
// left_predict_frame = tmp->prev;
// break;
// }
// }
if (P_IsFrameNil(left_predict_frame) || P_IsFrameNil(right_predict_frame))
{
LogDebugF("Missing blend");
right_predict_frame = predict_world->last_frame;
left_predict_frame = right_predict_frame;
DEBUGBREAKABLE;
V_PushTimelineMarker(frame->blend_tick * SIM_TICK_INTERVAL_NS, Color_Red, 0);
}
}
// for (P_Frame *tmp = predict_world->last_frame; !P_IsFrameNil(tmp); tmp = tmp->prev)
// {
// if (tmp->time_ns >= V.blend_time_ns && tmp->prev->time_ns <= V.blend_time_ns)
// {
// right_predict_frame = tmp;
// break;
// }
// }
// for (P_Frame *tmp = prev_predict_world->last_frame; !P_IsFrameNil(tmp); tmp = tmp->prev)
// {
// if (tmp->time_ns >= V.blend_time_ns && tmp->prev->time_ns <= V.blend_time_ns)
// {
// left_predict_frame = tmp->prev;
// break;
// }
// }
if (P_IsFrameNil(left_predict_frame) || P_IsFrameNil(right_predict_frame))
{
LogDebugF("Missing blend");
right_predict_frame = predict_world->last_frame;
// left_predict_frame = right_predict_frame->prev;
left_predict_frame = right_predict_frame;
DEBUGBREAKABLE;
}
frame->blend_from_tick = left_predict_frame->tick;
frame->blend_to_tick = right_predict_frame->tick;
@ -2090,7 +2119,8 @@ void V_TickForever(WaveLaneCtx *lane)
// P_Frame *left_sim_frame = right_sim_frame->prev;
// 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);
f64 blend_t = (f64)(V.blend_time_ns - left_predict_frame->time_ns) / (f64)(right_predict_frame->time_ns - left_predict_frame->time_ns);
// f64 blend_t = (f64)(V.blend_time_ns - left_predict_frame->time_ns) / (f64)(right_predict_frame->time_ns - left_predict_frame->time_ns);
f64 blend_t = (f64)(frame->blend_tick - left_predict_frame->tick) / (f64)(right_predict_frame->tick - left_predict_frame->tick);
@ -2143,6 +2173,312 @@ void V_TickForever(WaveLaneCtx *lane)
// P_Frame *prev_local_frame = prev_frame->local_world->last_frame;
// P_Frame *local_frame = &P_NilFrame;
// {
// if (frame->local_world->tiles_hash != predict_world->tiles_hash)
// {
// frame->local_world->tiles_hash = predict_world->tiles_hash;
// CopyStructs(frame->local_world->tiles, predict_world->tiles, P_TilesCount);
// frame->tiles_dirty = 1;
// }
// frame->local_world->seed = predict_world->seed;
// P_ClearFrames(frame->local_world, I64Min, I64Max);
// // 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 = frame->dt_ns + frame->dt_ns * dilation_factor;
// // 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?
// // <Delay> = <USER_INTERP_RATIO> * <Tick interval>
// // 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 (predict_to != prev_frame_predict_to)
// // {
// // i64 delay_ns = SIM_TICK_INTERVAL_NS * interp_ratio;
// // V.target_blend_time_ns = predict_frame->time_ns - delay_ns;
// // }
// {
// P_Frame *target_blend_frame = P_FrameFromTick(predict_world, predict_world->last_frame->base_predict_to);
// i64 delay_ns = SIM_TICK_INTERVAL_NS * interp_ratio;
// V.target_blend_time_ns = target_blend_frame->time_ns - delay_ns;
// }
// if (ack != prev_frame_ack)
// {
// V_PushTimelineMarker(ack_frame->time_ns, Color_Green, 0);
// }
// // 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 = 1 * frame->dt;
// // f64 blend_to_target_lerp_rate = 0.0 * 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;
// 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))
// {
// LogDebugF("Blend reset");
// V.blend_time_ns = V.target_blend_time_ns;
// V_PushTimelineMarker(V.blend_time_ns, Color_Red, 0);
// }
// if (TweakBool("Interpolation enabled", 1))
// {
// P_Frame *left_predict_frame = &P_NilFrame;
// P_Frame *right_predict_frame = &P_NilFrame;
// // for (P_Frame *tmp = predict_world->last_frame; !P_IsFrameNil(tmp); tmp = tmp->prev)
// // {
// // if (tmp->time_ns >= V.blend_time_ns && tmp->prev->time_ns <= V.blend_time_ns)
// // {
// // right_predict_frame = tmp;
// // left_predict_frame = tmp->prev;
// // break;
// // }
// // }
// V_PushTimelineMarker(V.blend_time_ns, Color_Red, 1);
// V_PushTimelineMarker(V.target_blend_time_ns, Color_Yellow, 1);
// {
// for (
// i64 predict_world_seq = cur_predict_world_seq;
// predict_world_seq > MaxI64(0, cur_predict_world_seq - countof(predict_worlds));
// --predict_world_seq
// )
// {
// i64 tmp_right_predict_world_idx = predict_world_seq % countof(predict_worlds);
// i64 tmp_left_predict_world_idx = (predict_world_seq - 1) % countof(predict_worlds);
// 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_from);
// // P_Frame *tmp_left = P_FrameFromTick(tmp_left_world, tmp_left_world->last_frame->base_predict_from);
// // i64 right_time_ns = tmp_right->base_time_ns;
// // i64 left_time_ns = tmp_left->base_time_ns;
// 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;
// i64 right_time_ns = tmp_right->time_ns;
// i64 left_time_ns = tmp_left->time_ns;
// V_PushTimelineMarker(left_time_ns, Color_Cyan, 1);
// V_PushTimelineMarker(right_time_ns, Color_Orange, 1);
// 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;
// break;
// }
// }
// // for (P_Frame *tmp = predict_world->last_frame; !P_IsFrameNil(tmp); tmp = tmp->prev)
// // {
// // if (tmp->time_ns >= V.blend_time_ns && tmp->prev->time_ns <= V.blend_time_ns)
// // {
// // right_predict_frame = tmp;
// // break;
// // }
// // }
// // for (P_Frame *tmp = prev_predict_world->last_frame; !P_IsFrameNil(tmp); tmp = tmp->prev)
// // {
// // if (tmp->time_ns >= V.blend_time_ns && tmp->prev->time_ns <= V.blend_time_ns)
// // {
// // left_predict_frame = tmp->prev;
// // break;
// // }
// // }
// }
// // for (P_Frame *tmp = predict_world->last_frame; !P_IsFrameNil(tmp); tmp = tmp->prev)
// // {
// // if (tmp->time_ns >= V.blend_time_ns && tmp->prev->time_ns <= V.blend_time_ns)
// // {
// // right_predict_frame = tmp;
// // break;
// // }
// // }
// // for (P_Frame *tmp = prev_predict_world->last_frame; !P_IsFrameNil(tmp); tmp = tmp->prev)
// // {
// // if (tmp->time_ns >= V.blend_time_ns && tmp->prev->time_ns <= V.blend_time_ns)
// // {
// // left_predict_frame = tmp->prev;
// // break;
// // }
// // }
// if (P_IsFrameNil(left_predict_frame) || P_IsFrameNil(right_predict_frame))
// {
// LogDebugF("Missing blend");
// right_predict_frame = predict_world->last_frame;
// // left_predict_frame = right_predict_frame->prev;
// left_predict_frame = right_predict_frame;
// DEBUGBREAKABLE;
// }
// frame->blend_from_tick = left_predict_frame->tick;
// frame->blend_to_tick = right_predict_frame->tick;
// local_frame = P_PushFrame(frame->local_world, left_predict_frame, left_predict_frame->tick);
// {
// P_Frame *left_sim_frame = left_predict_frame;
// P_Frame *right_sim_frame = right_predict_frame;
// // P_Frame *left_sim_frame = predict_world->first_frame;
// // P_Frame *right_sim_frame = left_sim_frame->next;
// // P_Frame *right_sim_frame = P_FrameFromTick(predict_world, first_predict_tick);
// // P_Frame *left_sim_frame = right_sim_frame->prev;
// // P_Frame *right_sim_frame = P_FrameFromTick(sim_world, ack - 1);
// // P_Frame *left_sim_frame = right_sim_frame->prev;
// // P_Frame *right_sim_frame = P_FrameFromTick(sim_world, right_predict_frame->src_tick);
// // P_Frame *left_sim_frame = right_sim_frame->prev;
// // 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);
// f64 blend_t = (f64)(V.blend_time_ns - left_predict_frame->time_ns) / (f64)(right_predict_frame->time_ns - left_predict_frame->time_ns);
// if (IsInf(blend_t))
// {
// blend_t = 1;
// }
// for (P_Ent *ent = P_FirstEnt(local_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
// {
// P_Ent *left = ent;
// P_Ent *right = ent;
// // if (ent->sim)
// if (0)
// {
// left = ent;
// right = P_EntFromKey(right_predict_frame, ent->key);
// }
// else
// {
// left = P_EntFromKey(left_sim_frame, ent->key);
// right = P_EntFromKey(right_sim_frame, ent->key);
// }
// ent->exists = LerpF32(left->exists, right->exists, blend_t);
// 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);
// ent->xf.r = SlerpVec2(left->xf.r, right->xf.r, blend_t);
// ent->walk_time_accum_ns = LerpI64(left->walk_time_accum_ns, right->walk_time_accum_ns, blend_t);
// ent->v = LerpVec2(left->v, right->v, blend_t);
// ent->w = LerpF32(left->w, right->w, blend_t);
// }
// }
// }
// }
// else
// {
// local_frame = P_PushFrame(frame->local_world, predict_world->last_frame, predict_world->last_frame->tick);
// }
// P_DebugDrawFrame(local_frame);
// }
@ -4645,30 +4981,31 @@ void V_TickForever(WaveLaneCtx *lane)
f64 timeline_span_seconds = TweakFloat("Timeline span", 1, 0.01, 5);
i64 timeline_span_ns = NsFromSeconds(timeline_span_seconds);
b32 was_locked = prev_frame->prev_timeline.locked;
b32 was_paused = prev_frame->prev_timeline.paused;
b32 was_showing = prev_frame->prev_timeline.show;
b32 locked = prev_frame->timeline.locked;
b32 paused = prev_frame->timeline.paused;
b32 showing = prev_frame->timeline.show;
// Current frame time marker
if (!V.timeline.paused)
if (!was_paused)
{
current_marker_time_ns = frame->time_ns - last_timeline_matchup_ns + timeline_start_ns;
}
if (V.timeline.locked)
if (locked)
{
V_PushTimelineMarker(current_marker_time_ns, Color_White, 1);
}
b32 was_locked = V.timeline.locked;
b32 locked = frame->held_buttons[Button_R];
b32 paused = frame->held_buttons[Button_E];
V.timeline.locked = locked;
V.timeline.paused = paused;
if (V.timeline.show)
if (showing)
{
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);
Vec2 timeline_pos = VEC2(frame->screen_dims.x * 0.5, frame->screen_dims.y * 0.75);
f32 timeline_opacity = 0.75;
f32 marker_opacity = 0.75;
@ -4703,22 +5040,23 @@ void V_TickForever(WaveLaneCtx *lane)
}
Vec2 marker_offset = Zi;
marker_offset.x = timeline_width.v * 0.5;
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;
marker_offset.x += -((f64)(offset_ns - timeline_start_ns) / (f64)timeline_span_ns) * timeline_width.v;
}
//- Build markers
Arena *marker_arenas[] = {
V.timeline.transient_markers_arena,
V.timeline.persistent_markers_arena,
V.transient_markers_arena,
V.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;
b32 is_transient = marker_arena == V.transient_markers_arena;
V_TimelineMarker *markers = ArenaFirst(marker_arena, V_TimelineMarker);
for (u64 marker_idx = 0; marker_idx < markers_count; ++marker_idx)
{
@ -4768,35 +5106,30 @@ void V_TickForever(WaveLaneCtx *lane)
UI_PopCP(UI_TopCP());
}
i64 reset_span_ns = timeline_span_ns;
if (locked)
{
reset_span_ns = timeline_span_ns * 0.5;
}
// reset_span_ns = NsFromSeconds(10);
b32 should_reset = 0;
if (!paused)
{
b32 is_first_ack = prev_frame_ack == 0 && ack != 0;
should_reset = (frame->time_ns - last_timeline_reset_ns > reset_span_ns) || is_first_ack || (locked != was_locked);
}
if (!locked && should_reset)
i64 reset_span_ns = timeline_span_ns * 0.5;
b32 is_first_ack = prev_frame_ack == 0 && ack != 0;
b32 should_reset = (
!paused && (
(frame->time_ns - last_timeline_reset_ns > reset_span_ns) ||
locked != was_locked ||
paused != was_paused ||
showing != was_showing ||
is_first_ack
)
);
if (!locked && should_reset || is_first_ack || showing != was_showing)
{
last_timeline_matchup_ns = frame->time_ns;
timeline_start_ns = ack_frame->time_ns;
timeline_start_ns = frame->predict_tick_accum * SIM_TICK_INTERVAL_NS;
}
if (should_reset)
{
last_timeline_reset_ns = frame->time_ns;
ResetArena(V.timeline.persistent_markers_arena);
ResetArena(V.persistent_markers_arena);
}
if (!paused)
{
ResetArena(V.timeline.transient_markers_arena);
ResetArena(V.transient_markers_arena);
}
}
@ -5203,14 +5536,22 @@ void V_TickForever(WaveLaneCtx *lane)
case V_CmdKind_toggle_console:
{
b32 new = !frame->show_console;
frame->show_console = new;
frame->show_console = !frame->show_console;
} break;
case V_CmdKind_toggle_timeline:
{
b32 new = !V.timeline.show;
V.timeline.show = new;
frame->timeline.show = !frame->timeline.show;
} break;
case V_CmdKind_pause_timeline:
{
frame->timeline.paused = !frame->timeline.paused;
} break;
case V_CmdKind_lock_timeline:
{
frame->timeline.locked = !frame->timeline.locked;
} break;
case V_CmdKind_toggle_fullscreen:

View File

@ -12,6 +12,8 @@
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(pause_timeline, Pause Debug Timeline, V_CmdDescFlag_HideFromPalette, V_HOTKEY( Button_E ), ) \
X(lock_timeline, Lock Debug Timeline, V_CmdDescFlag_HideFromPalette, V_HOTKEY( Button_R ), ) \
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 ), ) \
@ -245,7 +247,9 @@ Struct(V_Frame)
NET_Key sim_key;
NET_Key desired_sim_key;
f64 target_predict_tick_accum;
f64 predict_tick_accum;
f64 blend_tick;
i64 blend_from_tick;
i64 blend_to_tick;
@ -270,14 +274,8 @@ 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;
Arena *transient_markers_arena;
Arena *persistent_markers_arena;
i64 panels_count;
i64 windows_count;
@ -286,9 +284,6 @@ Struct(V_Ctx)
i64 connect_try_ns;
i64 target_blend_time_ns;
i64 blend_time_ns;
// Notifications
V_Notif *first_notif;
V_Notif *last_notif;

View File

@ -233,6 +233,13 @@ Enum(V_EditMode)
V_EditMode_Tile,
};
Struct(V_Timeline)
{
b32 show;
b32 paused;
b32 locked;
};
Struct(V_Affines)
{
// World <-> screen (raw)
@ -288,6 +295,11 @@ Struct(V_SharedFrame)
b32 has_mouse_focus;
b32 has_keyboard_focus;
//- Timeline
V_Timeline prev_timeline;
V_Timeline timeline;
//- Editor state
V_EditMode edit_mode;