From 039e354a9d5921fa478c8369c22ec00817e12557 Mon Sep 17 00:00:00 2001 From: jacob Date: Thu, 19 Mar 2026 18:33:22 -0500 Subject: [PATCH] entity observation cache --- src/pp/pp.h | 6 + src/pp/pp_vis/pp_vis_core.c | 256 ++++++++++++++++++++++++++++-------- src/pp/pp_vis/pp_vis_core.h | 25 ++++ 3 files changed, 235 insertions(+), 52 deletions(-) diff --git a/src/pp/pp.h b/src/pp/pp.h index 265a5573..1e75c5fa 100644 --- a/src/pp/pp.h +++ b/src/pp/pp.h @@ -84,6 +84,7 @@ Struct(P_DebugDrawNode) #define P_RollTimeNs NsFromSeconds(0.5) #define P_RollTurnTimeNs (NsFromSeconds(0.1)) #define P_RollTimeoutNs NsFromSeconds(0.5) +#define P_ObservationLifetimeNs NsFromSeconds(2) Struct(P_Control) { @@ -118,6 +119,11 @@ Struct(P_Ent) i64 created_at_tick; f64 lifetime_seconds; + //- Client data + + i64 local_observation_ns; + b32 has_observed; + //- Build data u64 continuity_gen; diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index a33e9eb3..3a3f3b2e 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -1892,6 +1892,61 @@ void V_TickForever(WaveLaneCtx *lane) + ////////////////////////////// + //- Observe entities + + { + for (P_Ent *ent = P_FirstEnt(local_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) + { + P_EntKey key = ent->key; + i64 expiration_ns = P_ObservationLifetimeNs; + i64 observation_time_ns = 0; + { + V_ObservationBin *bin = &V.observation_bins[key.v % countof(V.observation_bins)]; + V_Observation *obs = bin->first; + for (; obs; obs = obs->next) + { + if (obs->key == key.v) + { + break; + } + } + if (obs) + { + if (obs->time_ns + expiration_ns > frame->time_ns) + { + // Re-observe expired observation + DllQueueRemove(V.first_observation, V.last_observation, obs); + DllQueuePush(V.first_observation, V.last_observation, obs); + obs->time_ns = frame->time_ns; + } + } + else + { + if (V.first_observation && V.first_observation->time_ns + expiration_ns <= frame->time_ns) + { + // Remove expired observation for reuse + obs = V.first_observation; + V_ObservationBin *old_bin = &V.observation_bins[obs->key % countof(V.observation_bins)]; + DllQueueRemove(V.first_observation, V.last_observation, obs); + DllQueueRemoveNP(old_bin->first, old_bin->last, obs, next_in_bin, prev_in_bin); + } + else + { + obs = PushStruct(perm, V_Observation); + } + obs->key = key.v; + obs->time_ns = frame->time_ns; + DllQueuePush(V.first_observation, V.last_observation, obs); + DllQueuePushNP(bin->first, bin->last, obs, next_in_bin, prev_in_bin); + } + observation_time_ns = obs->time_ns; + } + ent->local_observation_ns = observation_time_ns; + ent->has_observed = observation_time_ns == frame->time_ns; + } + } + @@ -2341,72 +2396,165 @@ void V_TickForever(WaveLaneCtx *lane) + + + + + + + + + + + + + ////////////////////////////// - //- Push test bullet particles + //- Push bullet trail particles // TODO: Not like this - for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) - { - if (bullet->is_bullet) - { - // FIXME: Use 'last visible' pos - 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)) - { - start = end; - } + // for (P_Ent *trail = P_FirstEnt(local_frame); !P_IsEntNil(trail); trail = P_NextEnt(trail)) + // { + // if (trail->is_trail) + // { + // // FIXME: Use 'last visible' pos + // 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)) + // { + // start = end; + // } - b32 skip = 0; - if (bullet->has_hit) - { - Vec2 hit_pos = bullet->hit_entry; - if (DotVec2(SubVec2(hit_pos, start), SubVec2(end, start)) < 0) - { - skip = 1; - } - // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, start), Color_Red); - // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, end), Color_Purple); - end = hit_pos; - } + // b32 skip = 0; + // if (bullet->has_hit) + // { + // Vec2 hit_pos = bullet->hit_entry; + // if (DotVec2(SubVec2(hit_pos, start), SubVec2(end, start)) < 0) + // { + // skip = 1; + // } + // // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, start), Color_Red); + // // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, end), Color_Purple); + // end = hit_pos; + // } - if (!skip) - { - { - f32 trail_len = Vec2Len(SubVec2(end, start)); - f32 particles_count = trail_len * frame->dt * Kibi(8); // Particles per meter per second - particles_count = MaxF32(particles_count, 1); + // if (!skip) + // { + // { + // f32 trail_len = Vec2Len(SubVec2(end, start)); + // f32 particles_count = trail_len * frame->dt * Kibi(8); // Particles per meter per second + // particles_count = MaxF32(particles_count, 1); - f32 angle = AngleFromVec2(PerpVec2(SubVec2(end, start))); - // f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start))); + // f32 angle = AngleFromVec2(PerpVec2(SubVec2(end, start))); + // // f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start))); - V_Emitter emitter = Zi; - { - emitter.kind = V_ParticleKind_BulletTrail; - emitter.count = particles_count; + // V_Emitter emitter = Zi; + // { + // emitter.kind = V_ParticleKind_BulletTrail; + // emitter.count = particles_count; - f32 angle_spread = Tau / 4.0; + // f32 angle_spread = Tau / 4.0; - emitter.angle.min = angle - angle_spread / 2; - emitter.angle.max = angle + angle_spread / 2; + // emitter.angle.min = angle - angle_spread / 2; + // emitter.angle.max = angle + angle_spread / 2; - emitter.pos.p0 = start; - emitter.pos.p1 = end; + // emitter.pos.p0 = start; + // emitter.pos.p1 = end; - // emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1)); + // // emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1)); - // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1)); + // // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1)); - emitter.speed.min = -1; - emitter.speed.max = 1; - } - V_PushParticles(emitter); - } - } - } - } + // emitter.speed.min = -1; + // emitter.speed.max = 1; + // } + // V_PushParticles(emitter); + // } + // } + // } + // } + + + + + + + + + + + + + + + // ////////////////////////////// + // //- Push test bullet particles + + // // TODO: Not like this + + // for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) + // { + // if (bullet->is_bullet) + // { + // // FIXME: Use 'last visible' pos + // 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)) + // { + // start = end; + // } + + // b32 skip = 0; + // if (bullet->has_hit) + // { + // Vec2 hit_pos = bullet->hit_entry; + // if (DotVec2(SubVec2(hit_pos, start), SubVec2(end, start)) < 0) + // { + // skip = 1; + // } + // // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, start), Color_Red); + // // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, end), Color_Purple); + // end = hit_pos; + // } + + // if (!skip) + // { + // { + // f32 trail_len = Vec2Len(SubVec2(end, start)); + // f32 particles_count = trail_len * frame->dt * Kibi(8); // Particles per meter per second + // particles_count = MaxF32(particles_count, 1); + + // f32 angle = AngleFromVec2(PerpVec2(SubVec2(end, start))); + // // f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start))); + + // V_Emitter emitter = Zi; + // { + // emitter.kind = V_ParticleKind_BulletTrail; + // emitter.count = particles_count; + + // f32 angle_spread = Tau / 4.0; + + // emitter.angle.min = angle - angle_spread / 2; + // emitter.angle.max = angle + angle_spread / 2; + + // emitter.pos.p0 = start; + // emitter.pos.p1 = end; + + // // emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1)); + + // // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1)); + + // emitter.speed.min = -1; + // emitter.speed.max = 1; + // } + // V_PushParticles(emitter); + // } + // } + // } + // } @@ -2615,6 +2763,10 @@ void V_TickForever(WaveLaneCtx *lane) // } } + + + + ////////////////////////////// //- Push test explosion diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index c220bf8d..5e27e14d 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -227,6 +227,27 @@ Struct(V_TimelineMarker) f64 tick; }; +//////////////////////////////////////////////////////////// +//~ Observation types + +#define V_ObservationBinsCount Kibi(32) + +Struct(V_Observation) +{ + V_Observation *next; + V_Observation *prev; + V_Observation *next_in_bin; + V_Observation *prev_in_bin; + u64 key; + i64 time_ns; +}; + +Struct(V_ObservationBin) +{ + V_Observation *first; + V_Observation *last; +}; + //////////////////////////////////////////////////////////// //~ State types @@ -283,6 +304,10 @@ Struct(V_Ctx) i64 connect_try_ns; + V_ObservationBin observation_bins[V_ObservationBinsCount]; + V_Observation *first_observation; + V_Observation *last_observation; + // Notifications V_Notif *first_notif; V_Notif *last_notif;