dynamic time dilation for server->client prediction rate
This commit is contained in:
parent
4d7e06541e
commit
0c9dd684af
@ -162,7 +162,7 @@ u64 AlignU64(u64 x, u64 align)
|
||||
return result;
|
||||
}
|
||||
|
||||
u64 AlignU64ToNextPow2(u64 x)
|
||||
u64 NextPow2U64(u64 x)
|
||||
{
|
||||
u64 result = 0;
|
||||
if (x > 0)
|
||||
@ -213,22 +213,55 @@ f32 LerpAngleF32(f32 a, f32 b, f32 t)
|
||||
|
||||
i32 LerpI32(i32 val0, i32 val1, f32 t)
|
||||
{
|
||||
return val0 + RoundF32(((f32)val1 - (f32)val0) * t);
|
||||
return val0 + RoundF32((f32)(val1 - val0) * t);
|
||||
}
|
||||
|
||||
i64 LerpI64(i64 val0, i64 val1, f64 t)
|
||||
{
|
||||
return val0 + RoundF64(((f64)val1 - (f64)val0) * t);
|
||||
return val0 + RoundF64((f64)(val1 - val0) * t);
|
||||
}
|
||||
|
||||
i32 LerpU32(u32 val0, u32 val1, f32 t)
|
||||
{
|
||||
return val0 + RoundF32(((f32)val1 - (f32)val0) * t);
|
||||
return val0 + RoundF32((f32)(val1 - val0) * t);
|
||||
}
|
||||
|
||||
i64 LerpU64(u64 val0, u64 val1, f64 t)
|
||||
{
|
||||
return val0 + RoundF64(((f64)val1 - (f64)val0) * t);
|
||||
return val0 + RoundF64((f64)(val1 - val0) * t);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Smoothstep
|
||||
|
||||
f32 SmoothstepF32(f32 a, f32 b, f32 t)
|
||||
{
|
||||
f32 result = 0;
|
||||
if (a == b)
|
||||
{
|
||||
result = (t < a) ? 0 : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
t = ClampF32((t - a) / (b - a), 0, 1);
|
||||
result = t * t * (3.0f - 2.0f * t);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
f64 SmoothstepF64(f64 a, f64 b, f64 t)
|
||||
{
|
||||
f64 result = 0;
|
||||
if (a == b)
|
||||
{
|
||||
result = (t < a) ? 0 : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
t = ClampF64((t - a) / (b - a), 0, 1);
|
||||
result = t * t * (3.0f - 2.0f * t);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
@ -275,7 +275,7 @@ u64 PowU64(u64 v, u8 exp);
|
||||
//~ Align
|
||||
|
||||
u64 AlignU64(u64 x, u64 align);
|
||||
u64 AlignU64ToNextPow2(u64 x);
|
||||
u64 NextPow2U64(u64 x);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Trig
|
||||
@ -307,6 +307,12 @@ i64 LerpI64(i64 val0, i64 val1, f64 t);
|
||||
i32 LerpU32(u32 val0, u32 val1, f32 t);
|
||||
i64 LerpU64(u64 val0, u64 val1, f64 t);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Smoothstep
|
||||
|
||||
f32 SmoothstepF32(f32 a, f32 b, f32 t);
|
||||
f64 SmoothstepF64(f64 a, f64 b, f64 t);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Color
|
||||
|
||||
|
||||
@ -46,68 +46,70 @@ u32 countof(T arr[N])
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Min / max
|
||||
//~ C -> HLSL interoperability stubs
|
||||
|
||||
//- Min
|
||||
#define MinU8(...) min(__VA_ARGS__)
|
||||
#define MinI8(...) min(__VA_ARGS__)
|
||||
#define MinU32(...) min(__VA_ARGS__)
|
||||
#define MinI32(...) min(__VA_ARGS__)
|
||||
#define MinF32(...) min(__VA_ARGS__)
|
||||
#define MinU64(...) min(__VA_ARGS__)
|
||||
#define MinI64(...) min(__VA_ARGS__)
|
||||
#define MinF64(...) min(__VA_ARGS__)
|
||||
#define MinU8 min
|
||||
#define MinI8 min
|
||||
#define MinU32 min
|
||||
#define MinI32 min
|
||||
#define MinF32 min
|
||||
#define MinU64 min
|
||||
#define MinI64 min
|
||||
#define MinF64 min
|
||||
|
||||
//- Max
|
||||
#define MaxU8(...) max(__VA_ARGS__)
|
||||
#define MaxI8(...) max(__VA_ARGS__)
|
||||
#define MaxU32(...) max(__VA_ARGS__)
|
||||
#define MaxI32(...) max(__VA_ARGS__)
|
||||
#define MaxF32(...) max(__VA_ARGS__)
|
||||
#define MaxU64(...) max(__VA_ARGS__)
|
||||
#define MaxI64(...) max(__VA_ARGS__)
|
||||
#define MaxF64(...) max(__VA_ARGS__)
|
||||
#define MaxU8 max
|
||||
#define MaxI8 max
|
||||
#define MaxU32 max
|
||||
#define MaxI32 max
|
||||
#define MaxF32 max
|
||||
#define MaxU64 max
|
||||
#define MaxI64 max
|
||||
#define MaxF64 max
|
||||
|
||||
//- Clamp
|
||||
#define ClampU32(...) clamp(__VA_ARGS__)
|
||||
#define ClampI32(...) clamp(__VA_ARGS__)
|
||||
#define ClampF32(...) clamp(__VA_ARGS__)
|
||||
#define ClampU64(...) clamp(__VA_ARGS__)
|
||||
#define ClampI64(...) clamp(__VA_ARGS__)
|
||||
#define ClampF64(...) clamp(__VA_ARGS__)
|
||||
|
||||
#define MatchFloor(a, b) all(floor(a) == floor(b))
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Float ops
|
||||
#define ClampU32 clamp
|
||||
#define ClampI32 clamp
|
||||
#define ClampF32 clamp
|
||||
#define ClampU64 clamp
|
||||
#define ClampI64 clamp
|
||||
#define ClampF64 clamp
|
||||
|
||||
//- Round
|
||||
#define RoundF32(v) round(v)
|
||||
#define RoundF64(v) round(v)
|
||||
#define RoundF32 round
|
||||
#define RoundF64 round
|
||||
|
||||
//- Floor
|
||||
#define FloorF32(v) floor(v)
|
||||
#define FloorF64(v) floor(v)
|
||||
#define FloorF32 floor
|
||||
#define FloorF64 floor
|
||||
|
||||
//- Ceil
|
||||
#define CeilF32(v) ceil(v)
|
||||
#define CeilF64(v) ceil(v)
|
||||
#define CeilF32 ceil
|
||||
#define CeilF64 ceil
|
||||
|
||||
//- Trunc
|
||||
#define TruncF32(v) trunc(v)
|
||||
#define TruncF64(v) trunc(v)
|
||||
#define TruncF32 trunc
|
||||
#define TruncF64 trunc
|
||||
|
||||
//- Mod
|
||||
#define ModF32(v, m) fmod((v), (m))
|
||||
#define ModF64(v, m) fmod((v), (m))
|
||||
#define ModF32 fmod
|
||||
#define ModF64 fmod
|
||||
|
||||
//- Abs
|
||||
#define AbsF32(v) abs(v)
|
||||
#define AbsF64(v) abs(v)
|
||||
#define AbsF32 abs
|
||||
#define AbsF64 abs
|
||||
|
||||
//- Sign
|
||||
#define SignF32(v) sign(v)
|
||||
#define SignF64(v) sign(v)
|
||||
#define SignF32 sign
|
||||
#define SignF64 sign
|
||||
|
||||
//- Smoothstep
|
||||
#define SmoothstepF32 smoothstep
|
||||
#define SmoothstepF64 smoothstep
|
||||
|
||||
//- Matchfloor
|
||||
#define MatchFloor(a, b) all(floor(a) == floor(b))
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Derivative helpers
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
|
||||
#define SIM_MAX_PING 5.0
|
||||
#define SIM_TICKS_PER_SECOND 64
|
||||
#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
|
||||
|
||||
@ -967,7 +967,7 @@ G_ResourceHandle G_PushResource(G_ArenaHandle arena_handle, G_CommandListHandle
|
||||
d3d_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
d3d_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
d3d_desc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
d3d_desc.Width = AlignU64ToNextPow2(MaxU64(desc.buffer.size, min_buffer_size));
|
||||
d3d_desc.Width = NextPow2U64(MaxU64(desc.buffer.size, min_buffer_size));
|
||||
d3d_desc.Height = 1;
|
||||
d3d_desc.DepthOrArraySize = 1;
|
||||
d3d_desc.MipLevels = 1;
|
||||
@ -1593,7 +1593,7 @@ G_D12_StagingRegionNode *G_D12_PushStagingRegion(G_D12_CmdList *cl, u64 size)
|
||||
// Queue old ring for deletion
|
||||
old_ring = ring;
|
||||
ring = 0;
|
||||
u64 new_ring_size = MaxU64(AlignU64ToNextPow2(size), Mebi(8));
|
||||
u64 new_ring_size = MaxU64(NextPow2U64(size), Mebi(8));
|
||||
if (old_ring)
|
||||
{
|
||||
new_ring_size = MaxU64(new_ring_size, old_ring->size * 2);
|
||||
|
||||
16
src/pp/pp.c
16
src/pp/pp.c
@ -1357,14 +1357,14 @@ P_Frame *P_PushFrame(P_World *world, P_Frame *src_frame, i64 tick)
|
||||
SllStackPop(world->first_free_frame);
|
||||
i64 old_ent_bins_count = frame->ent_bins_count;
|
||||
P_EntBin *old_ent_bins = frame->ent_bins;
|
||||
i64 old_max_constraints = frame->max_constraints;
|
||||
i64 old_constraints_cap = frame->constraints_cap;
|
||||
P_Constraint *old_constraints = frame->constraints;
|
||||
{
|
||||
ZeroStruct(frame);
|
||||
}
|
||||
frame->ent_bins_count = old_ent_bins_count;
|
||||
frame->ent_bins = old_ent_bins;
|
||||
frame->max_constraints = old_max_constraints;
|
||||
frame->constraints_cap = old_constraints_cap;
|
||||
frame->constraints = old_constraints;
|
||||
ZeroStructs(frame->ent_bins, frame->ent_bins_count);
|
||||
}
|
||||
@ -1388,9 +1388,9 @@ P_Frame *P_PushFrame(P_World *world, P_Frame *src_frame, i64 tick)
|
||||
|
||||
if (!frame->constraints)
|
||||
{
|
||||
frame->max_constraints = Kibi(4);
|
||||
frame->constraints_cap = Kibi(4);
|
||||
frame->constraints_count = 0;
|
||||
frame->constraints = PushStructsNoZero(world->frames_arena, P_Constraint, frame->max_constraints);
|
||||
frame->constraints = PushStructsNoZero(world->frames_arena, P_Constraint, frame->constraints_cap);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1436,7 +1436,7 @@ void P_StepFrame(P_Frame *frame)
|
||||
P_World *world = frame->world;
|
||||
P_Frame *prev_frame = frame->prev;
|
||||
|
||||
i64 sim_dt_ns = NsFromSeconds(1) / SIM_TICKS_PER_SECOND;
|
||||
i64 sim_dt_ns = SIM_TICK_INTERVAL_NS;
|
||||
f64 sim_dt = SecondsFromNs(sim_dt_ns);
|
||||
|
||||
//////////////////////////////
|
||||
@ -1582,7 +1582,7 @@ void P_StepFrame(P_Frame *frame)
|
||||
//////////////////////////////
|
||||
//- Copy previous frame's constraints
|
||||
|
||||
frame->constraints_count = MinI64(prev_frame->constraints_count, frame->max_constraints);
|
||||
frame->constraints_count = MinI64(prev_frame->constraints_count, frame->constraints_cap);
|
||||
CopyStructs(frame->constraints, prev_frame->constraints, frame->constraints_count);
|
||||
|
||||
i32 solver_steps_count = 4;
|
||||
@ -1641,7 +1641,7 @@ void P_StepFrame(P_Frame *frame)
|
||||
}
|
||||
if (!match)
|
||||
{
|
||||
if (frame->constraints_count < frame->max_constraints)
|
||||
if (frame->constraints_count < frame->constraints_cap)
|
||||
{
|
||||
constraint = &frame->constraints[frame->constraints_count];
|
||||
frame->constraints_count += 1;
|
||||
@ -1809,7 +1809,7 @@ void P_StepFrame(P_Frame *frame)
|
||||
// }
|
||||
// if (!match)
|
||||
// {
|
||||
// if (frame->constraints_count < frame->max_constraints)
|
||||
// if (frame->constraints_count < frame->constraints_cap)
|
||||
// {
|
||||
// constraint = &frame->constraints[frame->constraints_count];
|
||||
// frame->constraints_count += 1;
|
||||
|
||||
@ -79,6 +79,9 @@ Struct(P_Control)
|
||||
i64 tick;
|
||||
i64 orig_tick; // Will differ from tick if this control was propagated from the control of another tick
|
||||
|
||||
// TODO: Move this to client-only code
|
||||
i64 produced_at_ns;
|
||||
|
||||
Vec2 move;
|
||||
Vec2 look;
|
||||
f32 fire_held;
|
||||
@ -132,9 +135,9 @@ Struct(P_Ent)
|
||||
//- Player
|
||||
|
||||
b32 is_player;
|
||||
|
||||
P_Key guy;
|
||||
|
||||
f32 ping;
|
||||
u8 string_len;
|
||||
u8 string_text[P_MaxPlayerNameLen + 8];
|
||||
|
||||
@ -239,7 +242,7 @@ Struct(P_Frame)
|
||||
i64 ent_bins_count;
|
||||
P_EntBin *ent_bins;
|
||||
|
||||
i64 max_constraints;
|
||||
i64 constraints_cap;
|
||||
i64 constraints_count;
|
||||
P_Constraint *constraints;
|
||||
|
||||
|
||||
@ -225,8 +225,8 @@ void S_TickForever(WaveLaneCtx *lane)
|
||||
client->name_len = MinI64(countof(client->name_text), src_name.len);
|
||||
CopyBytes(client->name_text, src_name.text, client->name_len);
|
||||
|
||||
client->max_controls = SIM_MAX_PING * SIM_TICKS_PER_SECOND;
|
||||
client->controls = PushStructs(perm, P_Control, client->max_controls);
|
||||
client->controls_cap = NextPow2U64(SIM_MAX_PING * SIM_TICKS_PER_SECOND);
|
||||
client->controls = PushStructs(perm, P_Control, client->controls_cap);
|
||||
|
||||
DllQueuePushNPZ(&S_NilClient, S.first_client, S.last_client, client, next, prev);
|
||||
DllQueuePushNP(bin->first, bin->last, client, next_in_bin, prev_in_bin);
|
||||
@ -344,7 +344,7 @@ void S_TickForever(WaveLaneCtx *lane)
|
||||
if (raw_control)
|
||||
{
|
||||
i64 control_tick = raw_control->tick;
|
||||
P_Control *control = &client->controls[control_tick % client->max_controls];
|
||||
P_Control *control = &client->controls[control_tick % client->controls_cap];
|
||||
{
|
||||
*control = *raw_control;
|
||||
control->move = ClampVec2Len(control->move, 1);
|
||||
@ -355,12 +355,12 @@ void S_TickForever(WaveLaneCtx *lane)
|
||||
// In case previous snapshots weren't received, backfill with newest snapshot
|
||||
//
|
||||
// TODO: Not like this
|
||||
i64 max_propagate_count = SIM_TICKS_PER_SECOND / 4;
|
||||
i64 max_propagate_count = SIM_TICKS_PER_SECOND;
|
||||
{
|
||||
// Propagate backwards
|
||||
for (i64 prop_tick = control_tick - 1; prop_tick >= MaxI64(prop_tick - max_propagate_count, world_frame->tick); --prop_tick)
|
||||
{
|
||||
P_Control *prop_control = &client->controls[prop_tick % client->max_controls];
|
||||
P_Control *prop_control = &client->controls[prop_tick % client->controls_cap];
|
||||
if (prop_control->orig_tick != prop_control->tick && control_tick < prop_control->orig_tick)
|
||||
{
|
||||
*prop_control = *control;
|
||||
@ -396,7 +396,7 @@ void S_TickForever(WaveLaneCtx *lane)
|
||||
i64 ack_tick = client->ack;
|
||||
for (i64 check_ack_tick = world_frame->tick; check_ack_tick < world_frame->tick + max_ack_count; ++check_ack_tick)
|
||||
{
|
||||
P_Control *ack_control = &client->controls[check_ack_tick % client->max_controls];
|
||||
P_Control *ack_control = &client->controls[check_ack_tick % client->controls_cap];
|
||||
if (ack_control->orig_tick == check_ack_tick)
|
||||
{
|
||||
ack_tick = check_ack_tick;
|
||||
@ -423,7 +423,7 @@ void S_TickForever(WaveLaneCtx *lane)
|
||||
|
||||
for (S_Client *client = S.first_client; !S_IsClientNil(client); client = client->next)
|
||||
{
|
||||
P_Control *control = &client->controls[world_frame->tick % client->max_controls];
|
||||
P_Control *control = &client->controls[world_frame->tick % client->controls_cap];
|
||||
if (control->tick == world_frame->tick)
|
||||
{
|
||||
P_Ent *player = P_EntFromKey(world_frame, client->player);
|
||||
@ -989,7 +989,7 @@ void S_TickForever(WaveLaneCtx *lane)
|
||||
|
||||
if (!shutdown)
|
||||
{
|
||||
i64 step_dt_ns = NsFromSeconds(1) / SIM_TICKS_PER_SECOND;
|
||||
i64 step_dt_ns = SIM_TICK_INTERVAL_NS;
|
||||
PLT_SleepFrame(time_ns, step_dt_ns);
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ Struct(S_Client)
|
||||
i32 name_len;
|
||||
u8 name_text[P_MaxPlayerNameLen];
|
||||
|
||||
i64 max_controls;
|
||||
i64 controls_cap;
|
||||
P_Control *controls;
|
||||
|
||||
i64 remote_ack;
|
||||
|
||||
@ -66,7 +66,7 @@ void V_PushParticles(V_Emitter src)
|
||||
{
|
||||
V_Frame *frame = V_CurrentFrame();
|
||||
i64 split_threshold = 256; // Arbitrary threshold. Lower counts cause less shader work but more cpu -> gpu upload bandwidth.
|
||||
src.count = MinU64(src.count, V_MaxParticles);
|
||||
src.count = MinU64(src.count, V_ParticlesCap);
|
||||
if (src.seed == 0)
|
||||
{
|
||||
src.seed = RandU64FromState(&frame->rand);
|
||||
@ -364,20 +364,25 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
P_World *predict_world = P_AcquireWorld();
|
||||
P_World *local_world = P_AcquireWorld();
|
||||
|
||||
i64 max_local_controls = SIM_MAX_PING * SIM_TICKS_PER_SECOND;
|
||||
P_Control *local_controls = PushStructs(perm, P_Control, max_local_controls);
|
||||
i64 local_controls_cap = NextPow2U64(SIM_MAX_PING * SIM_TICKS_PER_SECOND);
|
||||
P_Control *local_controls = PushStructs(perm, P_Control, local_controls_cap);
|
||||
|
||||
// Remote ack represents player control snapshots that the sim has received
|
||||
i64 remote_ack_received_at_ns = 0;
|
||||
i64 remote_ack = 0;
|
||||
i64 remote_ack_received_at_ns = 0;
|
||||
i64 prev_snapshot_sent_at_ns = 0;
|
||||
i64 known_sim_tick = 0;
|
||||
i64 remote_buffered_controls_count = 0;
|
||||
|
||||
// Which tick snapshots we have received and finished assembling from the server
|
||||
i64 ack = 0;
|
||||
i64 ack_mirror = 0;
|
||||
|
||||
// Basically same as ack, except it includes unassembled snapshots.
|
||||
i64 known_sim_tick = 0;
|
||||
f64 most_recent_rtt = 0;
|
||||
// i64 smoothed_rtt_ns = 0;
|
||||
// f64 smoothed_rtt = 0.300;
|
||||
f64 smoothed_rtt = 1.00;
|
||||
// f64 smoothed_rtt = 2;
|
||||
|
||||
f32 smooth_remote_buffered_ticks = 0;
|
||||
f32 smooth_remote_buffered_ticks_target = 2;
|
||||
@ -427,7 +432,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
gpu_particles = G_PushBuffer(
|
||||
gpu_perm, cl,
|
||||
V_Particle,
|
||||
V_MaxParticles,
|
||||
V_ParticlesCap,
|
||||
.flags = G_ResourceFlag_ZeroMemory | G_ResourceFlag_AllowShaderReadWrite
|
||||
);
|
||||
gpu_particles_ref = G_PushRWStructuredBufferRef(gpu_perm, gpu_particles, V_Particle);
|
||||
@ -568,6 +573,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
frame->edit_camera_zoom = prev_frame->edit_camera_zoom;
|
||||
frame->sim_key = prev_frame->sim_key;
|
||||
frame->desired_sim_key = prev_frame->desired_sim_key;
|
||||
frame->predict_tick_accum = prev_frame->predict_tick_accum;
|
||||
|
||||
frame->tick = V.current_frame_tick;
|
||||
frame->time_ns = TimeNs();
|
||||
@ -1048,13 +1054,6 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
//////////////////////////////
|
||||
//- Pop messages from sim
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// FIXME: Reject messages if not from currently connected sim
|
||||
|
||||
|
||||
P_MsgList in_msgs = Zi;
|
||||
{
|
||||
NET_MsgList net_msgs = NET_Swap(frame->arena, net_pipe);
|
||||
@ -1124,11 +1123,13 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
remote_ack_received_at_ns = frame->time_ns;
|
||||
remote_ack = tmp_remote_ack;
|
||||
ack_mirror = tmp_ack_mirror;
|
||||
}
|
||||
remote_buffered_controls_count = tmp_remote_ack - dst_tick;
|
||||
|
||||
if (dst_tick >= known_sim_tick)
|
||||
P_Control *acked_control = &local_controls[remote_ack % local_controls_cap];
|
||||
if (acked_control->tick == remote_ack)
|
||||
{
|
||||
i64 remote_buffered_cmds = dst_tick - tmp_remote_ack;
|
||||
most_recent_rtt = SecondsFromNs(frame->time_ns - acked_control->produced_at_ns);
|
||||
}
|
||||
}
|
||||
|
||||
if (dst_tick > ack && fragments_count < P_MaxFrameSnapshotFragments)
|
||||
@ -1196,6 +1197,14 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Compute smoothed rtt
|
||||
|
||||
{
|
||||
f64 change_rate = 1.0 * frame->dt;
|
||||
smoothed_rtt = LerpF64(smoothed_rtt, most_recent_rtt, change_rate);
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Apply sim msgs
|
||||
|
||||
@ -1242,7 +1251,6 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
//////////////////////////////
|
||||
//- Compute movement & look
|
||||
|
||||
if (frame->predict_to != prev_frame->predict_to)
|
||||
{
|
||||
Vec2 move = Zi;
|
||||
{
|
||||
@ -1253,8 +1261,8 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
}
|
||||
move = ClampVec2Len(move, 1);
|
||||
f32 fire_held = frame->held_buttons[Button_M1];
|
||||
f32 fire_presses = fire_held && !prev_frame->held_buttons[Button_M1];
|
||||
Vec2 look = Zi;
|
||||
f32 fire_presses = fire_held && !prev_frame->held_buttons[Button_M1];
|
||||
{
|
||||
Vec2 center = P_WorldShapeFromEnt(local_guy).centroid;
|
||||
look = SubVec2(frame->world_cursor, center);
|
||||
@ -1285,23 +1293,56 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
|
||||
//////////////////////////////
|
||||
//- 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.
|
||||
|
||||
// FIXME: Real ping
|
||||
// f64 ping = 0.250;
|
||||
f64 ping = 0;
|
||||
i64 ping_ns = NsFromSeconds(ping);
|
||||
{
|
||||
// How many buffered commands of ours we'd like the server to have
|
||||
i64 target_buffered_controls_count = 1;
|
||||
|
||||
// frame->predict_to = sim_world->last_frame->tick + MaxF64(CeilF64(ping * SIM_TICKS_PER_SECOND), 1.0);
|
||||
f64 rtt_bias_factor = 5.0;
|
||||
f64 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;
|
||||
|
||||
// frame->predict_to = ack + 10;
|
||||
frame->predict_to = MaxI64(prev_frame->predict_to, ack + 10);
|
||||
f64 predict_dt = frame->dt + frame->dt * dilation_factor;
|
||||
frame->predict_tick_accum += predict_dt * SIM_TICKS_PER_SECOND;
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
i64 prev_frame_predict_to = FloorF64(prev_frame->predict_tick_accum);
|
||||
i64 predict_to = FloorF64(frame->predict_tick_accum);
|
||||
|
||||
//////////////////////////////
|
||||
//- Create player control
|
||||
|
||||
if (frame->predict_to > prev_frame->predict_to)
|
||||
if (predict_to > prev_frame_predict_to)
|
||||
{
|
||||
i64 control_tick = frame->predict_to;
|
||||
i64 control_tick = predict_to;
|
||||
|
||||
P_Control control = Zi;
|
||||
control.tick = control_tick;
|
||||
@ -1309,18 +1350,20 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
control.move = frame->move;
|
||||
control.look = frame->look;
|
||||
control.fire_held = frame->fire_held;
|
||||
// FIXME: Don't propagate fire presses over multiple sim frames
|
||||
control.fire_presses = frame->fire_presses;
|
||||
|
||||
//- Fill controls buffer backwards
|
||||
i64 max_fill_count = SIM_TICKS_PER_SECOND / 4;
|
||||
for (i64 dst_tick = control_tick; dst_tick > MaxI64(prev_frame->predict_to, (dst_tick - max_local_controls)); --dst_tick)
|
||||
for (i64 dst_tick = control_tick; dst_tick > MaxI64(prev_frame_predict_to, (dst_tick - local_controls_cap)); --dst_tick)
|
||||
{
|
||||
P_Control *dst_control = &local_controls[dst_tick % max_local_controls];
|
||||
P_Control *dst_control = &local_controls[dst_tick % local_controls_cap];
|
||||
if (dst_tick > dst_control->orig_tick)
|
||||
{
|
||||
*dst_control = control;
|
||||
dst_control->tick = dst_tick;
|
||||
dst_control->orig_tick = control_tick;
|
||||
dst_control->produced_at_ns = frame->time_ns;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1348,9 +1391,9 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
i64 total_delta_bytes = 0;
|
||||
PackedDeltaNode *first_delta_node = 0;
|
||||
PackedDeltaNode *last_delta_node = 0;
|
||||
if (frame->predict_to >= ack)
|
||||
if (predict_to >= ack)
|
||||
{
|
||||
i64 last_send_tick = frame->predict_to;
|
||||
i64 last_send_tick = predict_to;
|
||||
i64 first_send_tick = MaxI64(ack, remote_ack + 1);
|
||||
first_send_tick = MaxI64(first_send_tick, last_send_tick - max_control_sends);
|
||||
for (i64 send_tick = first_send_tick; send_tick <= last_send_tick; ++send_tick)
|
||||
@ -1361,7 +1404,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
{
|
||||
// TODO: Delta compress
|
||||
should_transmit = 1;
|
||||
P_Control *control = &local_controls[send_tick % max_local_controls];
|
||||
P_Control *control = &local_controls[send_tick % local_controls_cap];
|
||||
BB_WriteBytes(&packer_bbw, StringFromStruct(control));
|
||||
}
|
||||
u64 delta_end = BB_GetNumBytesWritten(&packer_bbw);
|
||||
@ -1487,8 +1530,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
{
|
||||
// FIXME: Not like this
|
||||
i64 max_predict_ticks = SIM_TICKS_PER_SECOND;
|
||||
last_predict_tick = frame->predict_to;
|
||||
// first_predict_tick = ack - 2;
|
||||
last_predict_tick = predict_to;
|
||||
first_predict_tick = ack;
|
||||
first_predict_tick = MaxI64(first_predict_tick, last_predict_tick - max_predict_ticks);
|
||||
}
|
||||
@ -1517,7 +1559,6 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
|
||||
// Predict
|
||||
P_Frame *predict_frame = predict_world->last_frame;
|
||||
if (frame->predict_to != prev_frame->predict_to)
|
||||
{
|
||||
if (predict_world->tiles_hash != sim_world->tiles_hash)
|
||||
{
|
||||
@ -1535,7 +1576,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
P_Ent *predict_player = P_EntFromKey(predict_frame, V.player_key);
|
||||
if (!P_IsEntNil(predict_player))
|
||||
{
|
||||
P_Control *predict_control = &local_controls[predict_tick % max_local_controls];
|
||||
P_Control *predict_control = &local_controls[predict_tick % local_controls_cap];
|
||||
if (predict_control->tick == predict_tick)
|
||||
{
|
||||
predict_player->control = *predict_control;
|
||||
@ -1608,7 +1649,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
// P_Ent *predict_player = P_EntFromKey(predict_frame, local_player->key);
|
||||
// if (!P_IsEntNil(predict_player))
|
||||
// {
|
||||
// P_Control *predict_control = &local_controls[predict_tick % max_local_controls];
|
||||
// P_Control *predict_control = &local_controls[predict_tick % local_controls_cap];
|
||||
// if (predict_control->tick == predict_tick)
|
||||
// {
|
||||
// predict_player->control = *predict_control;
|
||||
@ -3574,6 +3615,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
}
|
||||
UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y);
|
||||
{
|
||||
UI_BuildLabelF("Ping: %F", FmtFloat(smoothed_rtt * 1000, .p = 3));
|
||||
UI_BuildLabelF("Client send: %F MiB", FmtFloat(CeilF64((f64)vis_pipe_stats.total_bytes_sent / 1024) / 1024, .p = 3));
|
||||
UI_BuildLabelF("Client recv: %F MiB", FmtFloat(CeilF64((f64)vis_pipe_stats.total_bytes_received / 1024) / 1024, .p = 3));
|
||||
UI_BuildLabelF("Server send: %F MiB", FmtFloat(CeilF64((f64)sim_pipe_stats.total_bytes_sent / 1024) / 1024, .p = 3));
|
||||
@ -3617,10 +3659,15 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
|
||||
if (frame->held_buttons[Button_Tab])
|
||||
{
|
||||
UI_Size name_sz = UI_GROW(0.75, 0);
|
||||
UI_Size ping_sz = UI_GROW(0.25, 0);
|
||||
UI_Size spacing_sz = UI_FNT(1, 0);
|
||||
|
||||
Struct(BoardRow)
|
||||
{
|
||||
BoardRow *next;
|
||||
String name;
|
||||
f32 ping;
|
||||
};
|
||||
i64 players_count = 0;
|
||||
i64 board_rows_count = 0;
|
||||
@ -3639,6 +3686,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
}
|
||||
String name = P_StringFromEnt(player);
|
||||
row->name = name;
|
||||
row->ping = player->ping;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3658,18 +3706,32 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
UI_SetNext(Flags, UI_BoxFlag_Floating);
|
||||
UI_PushCP(UI_BuildColumnEx(board_key));
|
||||
{
|
||||
UI_BuildSpacer(spacing_sz, Axis_Y);
|
||||
for (BoardRow *board_row = first_board_row; board_row; board_row = board_row->next)
|
||||
{
|
||||
String name = board_row->name;
|
||||
|
||||
UI_SetNext(Width, UI_GROW(1, 0));
|
||||
UI_SetNext(Width, UI_GROW(1, 1));
|
||||
UI_SetNext(Height, UI_FNT(2.5, 0));
|
||||
UI_SetNext(Tint, 0);
|
||||
UI_SetNext(ChildAlignment, UI_Region_Left);
|
||||
UI_PushCP(UI_BuildRow());
|
||||
UI_PushCP(UI_BuildRow()); // Scoreboard row
|
||||
{
|
||||
UI_BuildSpacer(spacing_sz, Axis_X);
|
||||
UI_SetNext(Width, name_sz);
|
||||
UI_PushCP(UI_BuildColumn()); // Player name column
|
||||
{
|
||||
UI_SetNext(FontSize, UI_Top(FontSize) * theme.h2);
|
||||
UI_BuildLabelF("Player: \"%F\"", FmtString(name));
|
||||
UI_BuildLabelF("Player: \"%F\"", FmtString(board_row->name));
|
||||
}
|
||||
UI_PopCP(UI_TopCP());
|
||||
|
||||
UI_BuildSpacer(spacing_sz, Axis_X);
|
||||
UI_SetNext(Width, ping_sz);
|
||||
UI_PushCP(UI_BuildColumn()); // Ping column
|
||||
{
|
||||
UI_SetNext(FontSize, UI_Top(FontSize) * theme.h2);
|
||||
UI_BuildLabelF("Ping: %F ", FmtFloat(board_row->ping, .p = 2));
|
||||
}
|
||||
UI_PopCP(UI_TopCP());
|
||||
}
|
||||
UI_PopCP(UI_TopCP());
|
||||
|
||||
@ -3678,6 +3740,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
UI_BuildDivider(UI_PIX(1, 1), theme.col.divider, Axis_Y);
|
||||
}
|
||||
}
|
||||
UI_BuildSpacer(spacing_sz, Axis_Y);
|
||||
}
|
||||
UI_PopCP(UI_TopCP());
|
||||
}
|
||||
@ -4140,7 +4203,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
// Clear particles
|
||||
if (should_clear_particles)
|
||||
{
|
||||
G_Compute(frame->cl, V_ClearParticlesCS, V_ThreadGroupSizeFromBufferSize(V_MaxParticles));
|
||||
G_Compute(frame->cl, V_ClearParticlesCS, V_ThreadGroupSizeFromBufferSize(V_ParticlesCap));
|
||||
}
|
||||
|
||||
// Discard render target
|
||||
@ -4160,7 +4223,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
G_DumbMemorySync(frame->cl, gpu_particles);
|
||||
|
||||
// Simulate particles
|
||||
G_Compute(frame->cl, V_SimParticlesCS, V_ThreadGroupSizeFromBufferSize(V_MaxParticles));
|
||||
G_Compute(frame->cl, V_SimParticlesCS, V_ThreadGroupSizeFromBufferSize(V_ParticlesCap));
|
||||
|
||||
// Barrier since stains were written
|
||||
G_DumbGlobalMemorySync(frame->cl);
|
||||
|
||||
@ -285,7 +285,7 @@ Struct(V_Frame)
|
||||
V_CmdNode *first_cmd_node;
|
||||
V_CmdNode *last_cmd_node;
|
||||
|
||||
i64 predict_to;
|
||||
f64 predict_tick_accum;
|
||||
|
||||
// Control
|
||||
Vec2 move;
|
||||
|
||||
@ -56,7 +56,7 @@ ComputeShader(V_ClearParticlesCS, 64)
|
||||
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
||||
RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(params.particles);
|
||||
u32 particle_idx = SV_DispatchThreadID;
|
||||
if (particle_idx < V_MaxParticles)
|
||||
if (particle_idx < V_ParticlesCap)
|
||||
{
|
||||
particles[particle_idx].exists = 0;
|
||||
}
|
||||
@ -332,7 +332,7 @@ ComputeShader(V_EmitParticlesCS, 64)
|
||||
for (u32 i = 0; i < emitter.count; ++i)
|
||||
{
|
||||
u32 particle_seq = emitter.first_particle_seq + i;
|
||||
u32 particle_idx = particle_seq % (u32)V_MaxParticles;
|
||||
u32 particle_idx = particle_seq % (u32)V_ParticlesCap;
|
||||
particles[particle_idx].exists = 1;
|
||||
particles[particle_idx].emitter_init_num = emitter_idx + 1;
|
||||
particles[particle_idx].seq = particle_seq;
|
||||
@ -352,7 +352,7 @@ ComputeShader(V_SimParticlesCS, 64)
|
||||
RWTexture2D<f32> drynesses = G_Dereference<f32>(params.drynesses);
|
||||
|
||||
u32 particle_idx = SV_DispatchThreadID;
|
||||
if (particle_idx < V_MaxParticles)
|
||||
if (particle_idx < V_ParticlesCap)
|
||||
{
|
||||
V_Particle particle = particles[particle_idx];
|
||||
if (particle.exists > 0)
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
#define V_CellsPerMeter 32.0
|
||||
#define V_CellsPerSqMeter (V_CellsPerMeter * V_CellsPerMeter)
|
||||
|
||||
// #define V_MaxParticles Kibi(128)
|
||||
// #define V_MaxParticles Mebi(1)
|
||||
#define V_MaxParticles Mebi(2)
|
||||
// #define V_MaxParticles Mebi(16)
|
||||
// #define V_ParticlesCap Kibi(128)
|
||||
// #define V_ParticlesCap Mebi(1)
|
||||
#define V_ParticlesCap Mebi(2)
|
||||
// #define V_ParticlesCap Mebi(16)
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Constant types
|
||||
|
||||
Loading…
Reference in New Issue
Block a user