diff --git a/src/base/base_math.c b/src/base/base_math.c index 7c1c52db..9a96044a 100644 --- a/src/base/base_math.c +++ b/src/base/base_math.c @@ -38,7 +38,7 @@ i64 SaturateI64(i64 v) { return v < 0 ? 0 : v > 1 ? 1 : v; } f64 SaturateF64(f64 v) { return v < 0 ? 0 : v > 1 ? 1 : v; } //////////////////////////////////////////////////////////// -//~ Float ops +//~ Numeric ops //- Sign diff --git a/src/base/base_math.h b/src/base/base_math.h index 5e910cb6..ff390cc8 100644 --- a/src/base/base_math.h +++ b/src/base/base_math.h @@ -234,7 +234,7 @@ i64 SaturateI64(i64 v); f64 SaturateF64(f64 v); //////////////////////////////////////////////////////////// -//~ Float ops +//~ Numeric ops //- Round #define RoundF32 roundf @@ -257,6 +257,8 @@ f64 SaturateF64(f64 v); #define ModF64 fmod //- Abs +#define AbsI32 abs +#define AbsI64 llabs #define AbsF32 fabsf #define AbsF64 fabs diff --git a/src/base/base_shader.gh b/src/base/base_shader.gh index fc615bf4..bd7ce248 100644 --- a/src/base/base_shader.gh +++ b/src/base/base_shader.gh @@ -89,39 +89,39 @@ u32 countof(T arr[N]) #define ClampF64 (f64)clamp //- Round -#define RoundF32 (f32)round -#define RoundF64 (f64)round +#define RoundF32(a) round((f32)(a)) +#define RoundF64(a) round((f64)(a)) //- Floor -#define FloorF32 (f32)floor -#define FloorF64 (f64)floor +#define FloorF32(a) floor((f32)(a)) +#define FloorF64(a) floor((f64)(a)) //- Ceil -#define CeilF32 (f32)ceil -#define CeilF64 (f64)ceil +#define CeilF32(a) ceil((f32)(a)) +#define CeilF64(a) ceil((f64)(a)) //- Trunc -#define TruncF32 (f32)trunc -#define TruncF64 (f64)trunc +#define TruncF32(a) trunc((f32)(a)) +#define TruncF64(a) trunc((f64)(a)) //- Mod -#define ModF32 (f32)fmod -#define ModF64 (f64)fmod +#define ModF32(a, b) fmod((f32)(a), (f32)(b)) +#define ModF64(a, b) fmod((f64)(a), (f64)(b)) //- Abs -#define AbsF32 (f32)abs -#define AbsF64 (f64)abs +#define AbsF32(a) abs((f32)(a)) +#define AbsF64(a) abs((f64)(a)) //- Sign -#define SignF32 (f32)sign -#define SignF64 (f64)sign +#define SignF32(a) sign((f32)(a)) +#define SignF64(a) sign((f64)(a)) //- Smoothstep -#define SmoothstepF32 (f32)smoothstep -#define SmoothstepF64 (f64)smoothstep +#define SmoothstepF32(a, b, t) smoothstep((f32)(a), (f32)(b), (f32)(t)) +#define SmoothstepF64(a, b, t) smoothstep((f64)(a), (f64)(b), (f64)(t)) //////////////////////////////////////////////////////////// -//~ Float ops +//~ Numeric ops //- Normalize diff --git a/src/config.h b/src/config.h index d802de64..70059efa 100644 --- a/src/config.h +++ b/src/config.h @@ -1,20 +1,14 @@ // Project-wide configurable constants -// How many ticks back in time should the user thread blend between? -// = * -// E.g: At 1.5, the user thread will render 75ms back in time if the sim runs at 50tps -#define USER_INTERP_RATIO 1.2 -#define USER_INTERP_ENABLED 1 - #define SIM_MAX_PING 5.0 #define SIM_PHYSICS_SUBSTEPS 4 -#define SIM_TICKS_PER_SECOND 64 +#define SIM_TICKS_PER_SECOND 32 #define SIM_TICK_INTERVAL_NS (NsFromSeconds(1) / SIM_TICKS_PER_SECOND) // Like USER_INTERP_RATIO, but applies to snapshots received by the local sim from the // master sim (how far back in time should the client render the server's state) -#define SIM_CLIENT_INTERP_RATIO 2.0 +// #define SIM_CLIENT_INTERP_RATIO 2.0 #define GPU_NAMES IsRtcEnabled diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index aa9d32eb..6e7c6453 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -363,7 +363,6 @@ void V_TickForever(WaveLaneCtx *lane) P_World *sim_world = P_AcquireWorld(); P_World *predict_world = P_AcquireWorld(); - P_World *local_world = P_AcquireWorld(); i64 local_controls_cap = NextPow2U64(SIM_MAX_PING * SIM_TICKS_PER_SECOND); P_Control *local_controls = PushStructs(perm, P_Control, local_controls_cap); @@ -524,6 +523,7 @@ void V_TickForever(WaveLaneCtx *lane) frame->quads_arena = AcquireArena(Gibi(64)); frame->dverts_arena = AcquireArena(Gibi(64)); frame->dvert_idxs_arena = AcquireArena(Gibi(64)); + frame->local_world = P_AcquireWorld(); } ////////////////////////////// @@ -577,11 +577,13 @@ void V_TickForever(WaveLaneCtx *lane) Arena *old_quads_arena = frame->quads_arena; Arena *old_dverts_arena = frame->dverts_arena; Arena *old_dvert_idxs_arena = frame->dvert_idxs_arena; + P_World *old_local_world = frame->local_world; ZeroStruct(frame); frame->arena = old_arena; frame->quads_arena = old_quads_arena; frame->dverts_arena = old_dverts_arena; frame->dvert_idxs_arena = old_dvert_idxs_arena; + frame->local_world = old_local_world; } frame->cl = G_PrepareCommandList(G_QueueKind_Direct); ResetArena(frame->arena); @@ -1006,8 +1008,8 @@ void V_TickForever(WaveLaneCtx *lane) Vec2 look_pos = frame->look; look_pos = RotateVec2(look_pos, InvertRot(rot)); - P_Ent *player = P_EntFromKey(local_world->last_frame, V.player_key); - P_Ent *guy = P_EntFromKey(local_world->last_frame, player->guy); + P_Ent *player = P_EntFromKey(frame->local_world->last_frame, V.player_key); + P_Ent *guy = P_EntFromKey(frame->local_world->last_frame, player->guy); Vec2 guy_center = P_WorldShapeFromEnt(guy).centroid; Vec2 screen_center = MulVec2(frame->screen_dims, 0.5); target_camera_pos = guy_center; @@ -1363,12 +1365,13 @@ void V_TickForever(WaveLaneCtx *lane) // If the server reports it has too many of our commands buffered up, we // slow the rate of prediction. + f64 dilation_factor = 0; { // 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; - f64 dilation_factor = SmoothstepF64( + 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 @@ -1630,7 +1633,7 @@ void V_TickForever(WaveLaneCtx *lane) } predict_world->seed = sim_world->seed; - P_ClearFrames(predict_world, I64Min, first_predict_tick - 1); + P_ClearFrames(predict_world, I64Min, MinI64(first_predict_tick - 1, prev_frame->blend_from_tick - 1)); predict_frame = P_PushFrame(predict_world, P_FrameFromTick(sim_world, first_predict_tick), first_predict_tick); for (i64 predict_tick = first_predict_tick + 1; predict_tick <= last_predict_tick; ++predict_tick) @@ -1833,21 +1836,92 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Update local world - // TODO: Remove this + P_Frame *prev_local_frame = prev_frame->local_world->last_frame; P_Frame *local_frame = &P_NilFrame; { - if (local_world->tiles_hash != predict_world->tiles_hash) + if (frame->local_world->tiles_hash != predict_world->tiles_hash) { - local_world->tiles_hash = predict_world->tiles_hash; - CopyStructs(local_world->tiles, predict_world->tiles, P_TilesCount); + frame->local_world->tiles_hash = predict_world->tiles_hash; + CopyStructs(frame->local_world->tiles, predict_world->tiles, P_TilesCount); frame->tiles_dirty = 1; } - local_world->seed = predict_world->seed; + frame->local_world->seed = predict_world->seed; + P_ClearFrames(frame->local_world, I64Min, I64Max); - P_ClearFrames(local_world, I64Min, local_world->last_frame->tick - 1); - local_frame = P_PushFrame(local_world, predict_world->last_frame, predict_world->last_frame->tick); + i64 target_blend_dt_ns = frame->dt_ns + frame->dt_ns * dilation_factor; + i64 blend_dt_ns = frame->dt_ns; - // LogDebugF("First frame: %F, Last frame: %F", FmtSint(local_world->first_frame->tick), FmtSint(local_world->last_frame->tick)); + 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, 5); + if (predict_to != prev_frame_predict_to) + { + i64 delay_ns = SIM_TICK_INTERVAL_NS * interp_ratio; + V.target_blend_time_ns = predict_world->last_frame->time_ns - delay_ns; + } + + f64 blend_to_target_lerp_rate = 0.05; + 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; + } + + if (TweakBool("Interpolation enabled", 1)) + { + P_Frame *left_frame = &P_NilFrame; + P_Frame *right_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_frame = tmp; + left_frame = tmp->prev; + break; + } + } + + if (P_IsFrameNil(left_frame) || P_IsFrameNil(right_frame)) + { + right_frame = predict_world->last_frame; + left_frame = right_frame->prev; + } + + frame->blend_from_tick = left_frame->tick; + frame->blend_to_tick = right_frame->tick; + + local_frame = P_PushFrame(frame->local_world, left_frame, left_frame->tick); + { + f64 blend_t = (f64)(V.blend_time_ns - left_frame->time_ns) / (f64)(right_frame->time_ns - left_frame->time_ns); + + for (P_Ent *ent = P_FirstEnt(local_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) + { + P_Ent *left = ent; + P_Ent *right = P_EntFromKey(right_frame, ent->key); + + ent->exists = LerpF32(left->exists, right->exists, blend_t); + if (!P_IsEntNil(right)) + { + 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); @@ -1858,6 +1932,34 @@ void V_TickForever(WaveLaneCtx *lane) + + + + // // TODO: Remove this + // 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, frame->local_world->last_frame->tick - 1); + // local_frame = P_PushFrame(frame->local_world, predict_world->last_frame, predict_world->last_frame->tick); + + // // LogDebugF("First frame: %F, Last frame: %F", FmtSint(frame->local_world->first_frame->tick), FmtSint(frame->local_world->last_frame->tick)); + + + // P_DebugDrawFrame(local_frame); + // } + + + + + + // { // i64 delay_ns = NsFromSeconds(100); @@ -1883,8 +1985,8 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Query local player - P_Ent *local_player = P_EntFromKey(local_world->last_frame, V.player_key); - P_Ent *local_guy = P_EntFromKey(local_world->last_frame, local_player->guy); + P_Ent *local_player = P_EntFromKey(frame->local_world->last_frame, V.player_key); + P_Ent *local_guy = P_EntFromKey(frame->local_world->last_frame, local_player->guy); ////////////////////////////// //- Compute crosshair position @@ -2082,7 +2184,7 @@ void V_TickForever(WaveLaneCtx *lane) { // TODO: Real world query P_Shape cursor_shape = P_ShapeFromDesc(.count = 1, .points = { frame->world_cursor }); - for (P_Ent *ent = P_FirstEnt(local_world->last_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) + for (P_Ent *ent = P_FirstEnt(frame->local_world->last_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) { P_Shape ent_shape = P_WorldShapeFromEnt(ent); b32 is_hovered = P_CollisionResultFromShapes(ent_shape, cursor_shape).collision_points_count > 0; @@ -2300,7 +2402,7 @@ void V_TickForever(WaveLaneCtx *lane) if (bullet->is_bullet) { // FIXME: Use 'last visible' pos - P_Ent *old_bullet = P_EntFromKey(local_world->last_frame->prev, bullet->key); + P_Ent *old_bullet = P_EntFromKey(prev_local_frame, bullet->key); Vec2 start = old_bullet->xf.t; Vec2 end = bullet->xf.t; if (P_IsEntNil(old_bullet)) @@ -2374,7 +2476,7 @@ void V_TickForever(WaveLaneCtx *lane) if (bullet->is_bullet && bullet->has_hit) { // FIXME: Use actual velocity - P_Ent *old_bullet = P_EntFromKey(local_frame->prev, bullet->key); + P_Ent *old_bullet = P_EntFromKey(prev_local_frame, bullet->key); Vec2 start = old_bullet->xf.t; Vec2 end = bullet->xf.t; if (P_IsEntNil(old_bullet)) @@ -4166,10 +4268,10 @@ void V_TickForever(WaveLaneCtx *lane) UI_BuildLabelF("Predicted world entities: %F", FmtSint(predict_world->last_frame->ents_count)); UI_BuildLabelF("Predicted world constraints: %F", FmtSint(predict_world->last_frame->constraints_count)); UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); - UI_BuildLabelF("Local world seed: 0x%F", FmtHex(local_world->seed)); - UI_BuildLabelF("Local world tick: %F", FmtSint(local_world->last_frame->tick)); - UI_BuildLabelF("Local world entities: %F", FmtSint(local_world->last_frame->ents_count)); - UI_BuildLabelF("Local world constraints: %F", FmtSint(local_world->last_frame->constraints_count)); + UI_BuildLabelF("Local world seed: 0x%F", FmtHex(frame->local_world->seed)); + UI_BuildLabelF("Local world tick: %F", FmtSint(frame->local_world->last_frame->tick)); + UI_BuildLabelF("Local world entities: %F", FmtSint(frame->local_world->last_frame->ents_count)); + UI_BuildLabelF("Local world constraints: %F", FmtSint(frame->local_world->last_frame->constraints_count)); } UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); { @@ -4795,7 +4897,7 @@ void V_TickForever(WaveLaneCtx *lane) G_CopyCpuToTexture( frame->cl, gpu_tiles_res, VEC3I32(0, 0, 0), - local_world->tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1), + frame->local_world->tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1), RNG3I32(VEC3I32(0, 0, 0), VEC3I32(tiles_dims.x, tiles_dims.y, 1)) ); } @@ -5175,6 +5277,7 @@ void V_TickForever(WaveLaneCtx *lane) G_CommitCommandList(frame->cl); i32 vsync = !!TweakBool("Vsync", 1); + vsync = 1; UI_EndFrame(ui_frame, vsync); } diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index b57e67cd..f91e87d3 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -228,12 +228,16 @@ Struct(V_Frame) Embed(V_SharedFrame, shared_frame); + P_World *local_world; RandState rand; NET_Key sim_key; NET_Key desired_sim_key; f64 predict_tick_accum; + i64 blend_from_tick; + i64 blend_to_tick; + Button held_buttons[Button_COUNT]; V_Palette palette; @@ -260,6 +264,9 @@ 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;