sim snasphot store -> client refactor

This commit is contained in:
jacob 2025-02-20 17:16:36 -06:00
parent 044fc1db9d
commit e0dee3e9e8
19 changed files with 2672 additions and 3201 deletions

View File

@ -6,7 +6,7 @@
#include "work.h"
#include "user.h"
#include "sim.h"
#include "sim_snapshot.h"
#include "sim.h"
#include "playback.h"
#include "log.h"
#include "resource.h"
@ -25,6 +25,7 @@
#include "rng.h"
#include "sock.h"
#include "host.h"
#include "bitbuff.h"
struct exit_callback {
app_exit_callback_func *func;
@ -235,8 +236,8 @@ void app_entry_point(struct string args_str)
struct sound_startup_receipt sound_sr = sound_startup(&work_sr, &asset_cache_sr, &resource_sr);
struct draw_startup_receipt draw_sr = draw_startup(&renderer_sr, &font_sr);
struct phys_startup_receipt phys_sr = phys_startup();
struct sim_snapshot_startup_receipt sim_snapshot_sr = sim_snapshot_startup();
struct user_startup_receipt user_sr = user_startup(&work_sr, &renderer_sr, &font_sr, &sprite_sr, &draw_sr, &asset_cache_sr, &sound_sr, &mixer_sr, &phys_sr, &host_sr, &sim_snapshot_sr, args_str, &window);
struct sim_startup_receipt sim_sr = sim_startup();
struct user_startup_receipt user_sr = user_startup(&work_sr, &renderer_sr, &font_sr, &sprite_sr, &draw_sr, &asset_cache_sr, &sound_sr, &mixer_sr, &phys_sr, &host_sr, &sim_sr, args_str, &window);
struct playback_startup_receipt playback_sr = playback_startup(&mixer_sr);
(UNUSED)user_sr;

View File

@ -140,16 +140,19 @@ struct bitbuff_writer bw_from_bitbuff_no_debug(struct bitbuff *bb)
return res;
}
/* FIXME: Handle overflowed bw */
u64 bw_num_bits_written(struct bitbuff_writer *bw)
{
return bw->cur_bit;
}
/* FIXME: Handle overflowed bw */
u64 bw_num_bytes_written(struct bitbuff_writer *bw)
{
return (bw->cur_bit + 7) >> 3;
}
/* FIXME: Handle overflowed bw */
struct string bw_get_written(struct arena *arena, struct bitbuff_writer *bw)
{
struct string res = ZI;
@ -159,6 +162,12 @@ struct string bw_get_written(struct arena *arena, struct bitbuff_writer *bw)
return res;
}
/* FIXME: Handle overflowed bw */
u8 *bw_get_written_raw(struct bitbuff_writer *bw)
{
return bw->base;
}
/* Returns true if num_bits would cause the writer to overflow its fixed buffer size (if writer is not backed by a dynamic arena bitbuff) */
b32 bw_check_overflow_bits(struct bitbuff_writer *bw, u64 num_bits)
{
@ -374,24 +383,28 @@ struct bitbuff_reader br_from_bitbuff_no_debug(struct bitbuff *bb)
}
/* Returns the number of bits read from the bitbuff */
/* FIXME: Handle overflowed br */
u64 br_cur_bit(struct bitbuff_reader *br)
{
return br->cur_bit;
}
/* Returns the number of *full* bytes read from the bitbuff */
/* FIXME: Handle overflowed br */
u64 br_cur_byte(struct bitbuff_reader *br)
{
return br->cur_bit >> 3;
}
/* Returns the number of bits left until the bitbuff overflows */
/* FIXME: Handle overflowed br */
u64 br_num_bits_left(struct bitbuff_reader *br)
{
return (br->base_len << 3) - br->cur_bit;
}
/* Returns the number of *full* bytes left until the bitbuff overflows */
/* FIXME: Handle overflowed br */
u64 br_num_bytes_left(struct bitbuff_reader *br)
{
return br->base_len - (br->cur_bit >> 3);

View File

@ -42,6 +42,7 @@ u64 bw_num_bits_written(struct bitbuff_writer *bw);
u64 bw_num_bytes_written(struct bitbuff_writer *bw);
struct string bw_get_written(struct arena *arena, struct bitbuff_writer *bw);
u8 *bw_get_written_raw(struct bitbuff_writer *bw);
b32 bw_check_overflow_bits(struct bitbuff_writer *bw, u64 num_bits);

View File

@ -1,6 +1,6 @@
#include "phys.h"
#include "sim_snapshot.h"
#include "sim_ent.h"
#include "sim_step.h"
#include "math.h"
#include "scratch.h"
#include "space.h"
@ -39,16 +39,16 @@ struct phys_startup_receipt phys_startup(void)
* Contact
* ========================== */
struct phys_collision_data_array phys_create_and_update_contacts(struct arena *arena, struct phys_ctx *ctx, f32 elapsed_dt, u64 phys_iteration)
struct phys_collision_data_array phys_create_and_update_contacts(struct arena *arena, struct phys_step_ctx *ctx, f32 elapsed_dt, u64 phys_iteration)
{
__prof;
struct phys_collision_data_array res = ZI;
res.a = arena_dry_push(arena, struct phys_collision_data);
struct sim_snapshot *ss = ctx->ss;
struct space *space = ctx->space;
struct sim_ent_lookup *contact_lookup = ctx->contact_lookup;
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
struct space *space = ctx->sim_step_ctx->accel->space;
struct sim_lookup *contact_lookup = &ctx->sim_step_ctx->accel->contact_lookup;
#if COLLIDER_DEBUG
struct sim_ent_lookup *debug_lookup = ctx->collision_debug_lookup;
struct sim_lookup *debug_lookup = ctx->collision_debug_lookup;
#endif
struct sim_ent *root = sim_ent_from_handle(ss, SIM_ENT_ROOT_HANDLE);
@ -96,8 +96,8 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
e1_collider = check0_collider;
}
struct sim_ent_lookup_key key = sim_ent_lookup_key_from_two_handles(e0->handle, e1->handle);
struct sim_ent_lookup_entry *constraint_entry = sim_ent_lookup_get(contact_lookup, key);
struct sim_lookup_key key = sim_lookup_key_from_two_handles(e0->handle, e1->handle);
struct sim_lookup_entry *constraint_entry = sim_lookup_get(contact_lookup, key);
struct sim_ent *constraint_ent = sim_ent_nil();
if (constraint_entry) {
@ -111,7 +111,7 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
}
} else {
/* Constraint ent no longer valid, delete constraint_entry*/
sim_ent_lookup_remove(contact_lookup, constraint_entry);
sim_lookup_remove(contact_lookup, constraint_entry);
constraint_entry= NULL;
}
}
@ -139,7 +139,7 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
sim_ent_enable_prop(constraint_ent, SIM_ENT_PROP_CONTACT_CONSTRAINT);
sim_ent_activate(constraint_ent, tick);
ASSERT(!constraint_entry); /* Existing entry should never be present here */
sim_ent_lookup_set(contact_lookup, key, constraint_ent->handle);
sim_lookup_set(contact_lookup, key, constraint_ent->handle);
}
/* Push collision data */
@ -238,7 +238,7 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
#if COLLIDER_DEBUG && COLLIDER_DEBUG_DETAILED
{
struct sim_ent *dbg_ent = sim_ent_nil();
struct sim_ent_lookup_entry *dbg_entry = sim_ent_lookup_get(debug_lookup, key);
struct sim_lookup_entry *dbg_entry = sim_lookup_get(debug_lookup, key);
if (dbg_entry) {
dbg_ent = sim_ent_from_handle(ss, dbg_entry->entity);
}
@ -247,7 +247,7 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
/* FIXME: Entity never released */
dbg_ent = sim_ent_alloc(root);
sim_ent_enable_prop(dbg_ent, SIM_ENT_PROP_COLLISION_DEBUG);
sim_ent_lookup_set(debug_lookup, key, dbg_ent->handle);
sim_lookup_set(debug_lookup, key, dbg_ent->handle);
}
struct phys_collision_debug *dbg = &dbg_ent->collision_debug_data;
@ -280,11 +280,11 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
return res;
}
void phys_prepare_contacts(struct phys_ctx *ctx, u64 phys_iteration)
void phys_prepare_contacts(struct phys_step_ctx *ctx, u64 phys_iteration)
{
__prof;
struct sim_snapshot *ss = ctx->ss;
struct sim_ent_lookup *contact_lookup = ctx->contact_lookup;
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
struct sim_lookup *contact_lookup = &ctx->sim_step_ctx->accel->contact_lookup;
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
struct sim_ent *constraint_ent = &ss->ents[sim_ent_index];
@ -365,10 +365,10 @@ void phys_prepare_contacts(struct phys_ctx *ctx, u64 phys_iteration)
sim_ent_disable_prop(constraint_ent, SIM_ENT_PROP_ACTIVE);
sim_ent_enable_prop(constraint_ent, SIM_ENT_PROP_RELEASE_THIS_TICK);
/* Remove from lookup */
struct sim_ent_lookup_key key = sim_ent_lookup_key_from_two_handles(constraint->e0, constraint->e1);
struct sim_ent_lookup_entry *entry = sim_ent_lookup_get(contact_lookup, key);
struct sim_lookup_key key = sim_lookup_key_from_two_handles(constraint->e0, constraint->e1);
struct sim_lookup_entry *entry = sim_lookup_get(contact_lookup, key);
if (entry) {
sim_ent_lookup_remove(contact_lookup, entry);
sim_lookup_remove(contact_lookup, entry);
} else {
ASSERT(false); /* This should always exist */
}
@ -376,7 +376,7 @@ void phys_prepare_contacts(struct phys_ctx *ctx, u64 phys_iteration)
}
#if COLLIDER_DEBUG
struct sim_ent_lookup *debug_lookup = ctx->debug_lookup;
struct sim_lookup *debug_lookup = ctx->debug_lookup;
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
struct sim_ent *dbg_ent = &ss->ents[sim_ent_index];
if (!sim_ent_is_valid_and_active(dbg_ent)) continue;
@ -395,11 +395,11 @@ void phys_prepare_contacts(struct phys_ctx *ctx, u64 phys_iteration)
sim_ent_enable_prop(dbg_ent, SIM_ENT_PROP_RELEASE_THIS_TICK);
/* Remove from lookup */
struct sim_ent_lookup_key key = sim_ent_lookup_key_from_two_handles(dbg->e0, dbg->e1);
struct sim_ent_lookup_entry *entry = sim_ent_lookup_get(debug_lookup, key);
struct sim_lookup_key key = sim_lookup_key_from_two_handles(dbg->e0, dbg->e1);
struct sim_lookup_entry *entry = sim_lookup_get(debug_lookup, key);
if (entry) {
sim_ent_lookup_remove(debug_lookup, entry);
sim_lookup_remove(debug_lookup, entry);
} else {
ASSERT(false); /* This should always exist */
}
@ -408,10 +408,10 @@ void phys_prepare_contacts(struct phys_ctx *ctx, u64 phys_iteration)
#endif
}
void phys_warm_start_contacts(struct phys_ctx *ctx)
void phys_warm_start_contacts(struct phys_step_ctx *ctx)
{
__prof;
struct sim_snapshot *ss = ctx->ss;
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
struct sim_ent *constraint_ent = &ss->ents[sim_ent_index];
if (!sim_ent_is_valid_and_active(constraint_ent)) continue;
@ -463,10 +463,10 @@ void phys_warm_start_contacts(struct phys_ctx *ctx)
}
}
void phys_solve_contacts(struct phys_ctx *ctx, f32 dt, b32 apply_bias)
void phys_solve_contacts(struct phys_step_ctx *ctx, f32 dt, b32 apply_bias)
{
__prof;
struct sim_snapshot *ss = ctx->ss;
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
struct sim_ent *constraint_ent = &ss->ents[sim_ent_index];
if (!sim_ent_is_valid_and_active(constraint_ent)) continue;
@ -594,10 +594,10 @@ struct phys_motor_joint phys_motor_joint_from_def(struct phys_motor_joint_def de
return res;
}
void phys_prepare_motor_joints(struct phys_ctx *ctx)
void phys_prepare_motor_joints(struct phys_step_ctx *ctx)
{
__prof;
struct sim_snapshot *ss = ctx->ss;
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
@ -658,10 +658,10 @@ void phys_prepare_motor_joints(struct phys_ctx *ctx)
}
}
void phys_warm_start_motor_joints(struct phys_ctx *ctx)
void phys_warm_start_motor_joints(struct phys_step_ctx *ctx)
{
__prof;
struct sim_snapshot *ss = ctx->ss;
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
@ -690,10 +690,10 @@ void phys_warm_start_motor_joints(struct phys_ctx *ctx)
}
}
void phys_solve_motor_joints(struct phys_ctx *ctx, f32 dt)
void phys_solve_motor_joints(struct phys_step_ctx *ctx, f32 dt)
{
__prof;
struct sim_snapshot *ss = ctx->ss;
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
@ -783,10 +783,10 @@ struct phys_mouse_joint phys_mouse_joint_from_def(struct phys_mouse_joint_def de
return res;
}
void phys_prepare_mouse_joints(struct phys_ctx *ctx)
void phys_prepare_mouse_joints(struct phys_step_ctx *ctx)
{
__prof;
struct sim_snapshot *ss = ctx->ss;
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
@ -830,10 +830,10 @@ void phys_prepare_mouse_joints(struct phys_ctx *ctx)
}
}
void phys_warm_start_mouse_joints(struct phys_ctx *ctx)
void phys_warm_start_mouse_joints(struct phys_step_ctx *ctx)
{
__prof;
struct sim_snapshot *ss = ctx->ss;
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
@ -852,10 +852,10 @@ void phys_warm_start_mouse_joints(struct phys_ctx *ctx)
}
}
void phys_solve_mouse_joints(struct phys_ctx *ctx, f32 dt)
void phys_solve_mouse_joints(struct phys_step_ctx *ctx, f32 dt)
{
__prof;
struct sim_snapshot *ss = ctx->ss;
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
@ -939,10 +939,10 @@ INTERNAL struct xform get_derived_xform(struct sim_ent *ent, f32 dt)
return xf;
}
void phys_integrate_forces(struct phys_ctx *ctx, f32 dt)
void phys_integrate_forces(struct phys_step_ctx *ctx, f32 dt)
{
__prof;
struct sim_snapshot *ss = ctx->ss;
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
struct sim_ent *ent = &ss->ents[sim_ent_index];
if (!sim_ent_is_valid_and_active(ent)) continue;
@ -981,10 +981,10 @@ void phys_integrate_forces(struct phys_ctx *ctx, f32 dt)
}
}
void phys_integrate_velocities(struct phys_ctx *ctx, f32 dt)
void phys_integrate_velocities(struct phys_step_ctx *ctx, f32 dt)
{
__prof;
struct sim_snapshot *ss = ctx->ss;
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
struct sim_ent *ent = &ss->ents[sim_ent_index];
if (!sim_ent_is_valid_and_active(ent)) continue;
@ -999,11 +999,11 @@ void phys_integrate_velocities(struct phys_ctx *ctx, f32 dt)
* Earliest time of impact
* ========================== */
f32 phys_determine_earliest_toi_for_bullets(struct phys_ctx *ctx, f32 step_dt, f32 tolerance, u32 max_iterations)
f32 phys_determine_earliest_toi_for_bullets(struct phys_step_ctx *ctx, f32 step_dt, f32 tolerance, u32 max_iterations)
{
__prof;
struct sim_snapshot *ss = ctx->ss;
struct space *space = ctx->space;
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
struct space *space = ctx->sim_step_ctx->accel->space;
f32 smallest_t = 1;
for (u64 e0_index = 0; e0_index < ss->num_ents_reserved; ++e0_index) {
@ -1050,10 +1050,10 @@ f32 phys_determine_earliest_toi_for_bullets(struct phys_ctx *ctx, f32 step_dt, f
* Space
* ========================== */
void phys_update_aabbs(struct phys_ctx *ctx)
void phys_update_aabbs(struct phys_step_ctx *ctx)
{
struct sim_snapshot *ss = ctx->ss;
struct space *space = ctx->space;
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
struct space *space = ctx->sim_step_ctx->accel->space;
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
struct sim_ent *ent = &ss->ents[sim_ent_index];
if (!sim_ent_is_valid_and_active(ent)) continue;
@ -1075,11 +1075,12 @@ void phys_update_aabbs(struct phys_ctx *ctx)
* ========================== */
/* Returns phys iteration to be fed into next step. Supplied iteration must be > 0. */
void phys_step(struct phys_ctx *ctx, f32 timestep)
void phys_step(struct phys_step_ctx *ctx, f32 timestep)
{
__prof;
phys_integrate_forces(ctx, timestep);
u64 phys_iteration = ctx->ss->phys_iteration;
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
u64 phys_iteration = ss->phys_iteration;
phys_update_aabbs(ctx);
@ -1112,7 +1113,7 @@ void phys_step(struct phys_ctx *ctx, f32 timestep)
if (ctx->pre_solve_callback) {
__profscope(pre_solve_callback);
ctx->pre_solve_callback(collision_data, ctx->ss);
ctx->pre_solve_callback(collision_data, ctx->sim_step_ctx);
}
f32 substep_dt = step_dt / SIM_PHYSICS_SUBSTEPS;
@ -1146,11 +1147,11 @@ void phys_step(struct phys_ctx *ctx, f32 timestep)
if (ctx->post_solve_callback) {
__profscope(post_solve_callback);
ctx->post_solve_callback(collision_data, ctx->ss);
ctx->post_solve_callback(collision_data, ctx->sim_step_ctx);
}
scratch_end(scratch);
}
ctx->ss->phys_iteration = phys_iteration;
ss->phys_iteration = phys_iteration;
}

View File

@ -6,7 +6,7 @@
struct space;
struct sim_ent_lookup;
struct sim_snapshot;
struct sim_step_ctx;
struct phys_contact_constraint;
struct phys_collision_data {
@ -25,20 +25,14 @@ struct phys_collision_data_array {
};
struct phys_collision_data;
#define PHYS_COLLISION_CALLBACK_FUNC_DEF(name, arg_collision_data, arg_ss) void name(struct phys_collision_data_array arg_collision_data, struct sim_snapshot *arg_ss)
typedef PHYS_COLLISION_CALLBACK_FUNC_DEF(phys_collision_callback_func, collision_data, ss);
#define PHYS_COLLISION_CALLBACK_FUNC_DEF(name, arg_collision_data, arg_sim_step_ctx) void name(struct phys_collision_data_array arg_collision_data, struct sim_step_ctx *arg_sim_step_ctx)
typedef PHYS_COLLISION_CALLBACK_FUNC_DEF(phys_collision_callback_func, collision_data, ctx);
/* Structure containing data used for a single physics step */
struct phys_ctx {
struct sim_snapshot *ss;
struct phys_step_ctx {
struct sim_step_ctx *sim_step_ctx;
phys_collision_callback_func *pre_solve_callback;
phys_collision_callback_func *post_solve_callback;
struct space *space;
struct sim_ent_lookup *contact_lookup;
#if COLLIDER_DEBUG
struct sim_ent_lookup *collision_debug_lookup;
#endif
};
/* ========================== *
@ -107,10 +101,10 @@ struct phys_collision_debug {
struct xform xf1;
};
struct phys_collision_data_array phys_create_and_update_contacts(struct arena *arena, struct phys_ctx *ctx, f32 elapsed_dt, u64 phys_iteration);
void phys_prepare_contacts(struct phys_ctx *ctx, u64 phys_iteration);
void phys_warm_start_contacts(struct phys_ctx *ctx);
void phys_solve_contacts(struct phys_ctx *ctx, f32 dt, b32 apply_bias);
struct phys_collision_data_array phys_create_and_update_contacts(struct arena *arena, struct phys_step_ctx *ctx, f32 elapsed_dt, u64 phys_iteration);
void phys_prepare_contacts(struct phys_step_ctx *ctx, u64 phys_iteration);
void phys_warm_start_contacts(struct phys_step_ctx *ctx);
void phys_solve_contacts(struct phys_step_ctx *ctx, f32 dt, b32 apply_bias);
/* ========================== *
* Motor joint
@ -147,9 +141,9 @@ struct phys_motor_joint {
};
struct phys_motor_joint phys_motor_joint_from_def(struct phys_motor_joint_def def);
void phys_prepare_motor_joints(struct phys_ctx *ctx);
void phys_warm_start_motor_joints(struct phys_ctx *ctx);
void phys_solve_motor_joints(struct phys_ctx *ctx, f32 dt);
void phys_prepare_motor_joints(struct phys_step_ctx *ctx);
void phys_warm_start_motor_joints(struct phys_step_ctx *ctx);
void phys_solve_motor_joints(struct phys_step_ctx *ctx, f32 dt);
/* ========================== *
* Mouse joint
@ -180,33 +174,33 @@ struct phys_mouse_joint {
};
struct phys_mouse_joint phys_mouse_joint_from_def(struct phys_mouse_joint_def def);
void phys_prepare_mouse_joints(struct phys_ctx *ctx);
void phys_warm_start_mouse_joints(struct phys_ctx *ctx);
void phys_solve_mouse_joints(struct phys_ctx *ctx, f32 dt);
void phys_prepare_mouse_joints(struct phys_step_ctx *ctx);
void phys_warm_start_mouse_joints(struct phys_step_ctx *ctx);
void phys_solve_mouse_joints(struct phys_step_ctx *ctx, f32 dt);
/* ========================== *
* Integration
* ========================== */
void phys_integrate_forces(struct phys_ctx *ctx, f32 dt);
void phys_integrate_velocities(struct phys_ctx *ctx, f32 dt);
void phys_integrate_forces(struct phys_step_ctx *ctx, f32 dt);
void phys_integrate_velocities(struct phys_step_ctx *ctx, f32 dt);
/* ========================== *
* Earliest time of impact
* ========================== */
f32 phys_determine_earliest_toi_for_bullets(struct phys_ctx *ctx, f32 step_dt, f32 tolerance, u32 max_iterations);
f32 phys_determine_earliest_toi_for_bullets(struct phys_step_ctx *ctx, f32 step_dt, f32 tolerance, u32 max_iterations);
/* ========================== *
* Space
* ========================== */
void phys_update_aabbs(struct phys_ctx *ctx);
void phys_update_aabbs(struct phys_step_ctx *ctx);
/* ========================== *
* Step
* ========================== */
void phys_step(struct phys_ctx *ctx, f32 timestep);
void phys_step(struct phys_step_ctx *ctx, f32 timestep);
#endif

2001
src/sim.c

File diff suppressed because it is too large Load Diff

223
src/sim.h
View File

@ -1,15 +1,125 @@
#ifndef SIM_H
#define SIM_H
#include "bitbuff.h"
#define SIM_CLIENT_NIL_HANDLE ((struct sim_client_handle) { .gen = 0, .idx = 0 })
struct sprite_startup_receipt;
struct phys_startup_receipt;
struct host_startup_receipt;
struct sim_snapshot_startup_receipt;
/* Absolute layers */
#define SIM_LAYER_FLOOR_DECALS (-300)
#define SIM_LAYER_BULLETS (-200)
#define SIM_LAYER_TRACERS (-100)
#define SIM_LAYER_SHOULDERS (0)
/* Relative layers */
#define SIM_LAYER_RELATIVE_DEFAULT (0)
#define SIM_LAYER_RELATIVE_WEAPON (1)
/* ========================== *
* Control
* Startup
* ========================== */
struct sim_startup_receipt { i32 _; };
struct sim_startup_receipt sim_startup(void);
/* ========================== *
* Client store
* ========================== */
struct sim_client_lookup_bucket {
struct sim_client_handle first;
struct sim_client_handle last;
};
struct sim_client_store {
b32 valid;
struct arena arena;
/* Client lookup */
struct sim_client_lookup_bucket *client_lookup_buckets;
u64 num_client_lookup_buckets;
/* Clients */
struct arena clients_arena;
struct sim_client *clients;
struct sim_client_handle first_free_client;
u64 num_clients_allocated;
u64 num_clients_reserved;
};
INLINE struct sim_client_store *sim_client_store_nil(void)
{
extern READONLY struct sim_client_store **_g_sim_client_store_nil;
return *_g_sim_client_store_nil;
}
struct sim_client_store *sim_client_store_alloc(void);
void sim_client_store_release(struct sim_client_store *store);
/* ========================== *
* Client
* ========================== */
struct sim_snapshot;
enum sim_client_kind {
SIM_CLIENT_KIND_INVALID,
SIM_CLIENT_KIND_USER,
SIM_CLIENT_KIND_LOCAL_SIM,
SIM_CLIENT_KIND_SLAVE_SIM,
SIM_CLIENT_KIND_MASTER_SIM,
};
struct sim_snapshot_lookup_bucket {
struct sim_snapshot *first;
struct sim_snapshot *last;
};
struct sim_client {
b32 valid;
enum sim_client_kind kind;
struct sim_client_handle handle;
struct sim_client_store *store;
struct arena snapshots_arena;
struct host_channel_id channel_id;
u64 channel_hash;
struct sim_client_handle next_free;
struct sim_client_handle next_in_bucket;
struct sim_client_handle prev_in_bucket;
/* This is the last confirmed tick of ours that we know this client has received */
u64 ack;
/* This is the last confirmed client tick that the client knows we have received */
u64 reverse_ack;
/* Snapshots sorted by tick (low to high) */
u64 first_tick;
u64 last_tick;
u64 num_ticks;
struct sim_snapshot *first_free_snapshot;
/* Tick -> snapshot lookup */
u64 num_snapshot_lookup_buckets;
struct sim_snapshot_lookup_bucket *snapshot_lookup_buckets;
};
INLINE struct sim_client *sim_client_nil(void)
{
extern READONLY struct sim_client **_g_sim_client_nil;
return *_g_sim_client_nil;
}
struct sim_client *sim_client_alloc(struct sim_client_store *store, enum sim_client_kind kind);
void sim_client_release(struct sim_client *client);
struct sim_client *sim_client_from_channel_id(struct sim_client_store *store, struct host_channel_id channel_id);
void sim_client_set_channel_id(struct sim_client *client, struct host_channel_id channel_id);
struct sim_client *sim_client_from_handle(struct sim_client_store *store, struct sim_client_handle handle);
/* ========================== *
* Snapshot
* ========================== */
enum sim_control_flag {
@ -31,22 +141,95 @@ struct sim_control {
u32 flags;
};
/* ========================== *
* Layers
* ========================== */
struct sim_snapshot {
b32 valid;
u64 tick;
struct sim_client *client;
struct sim_snapshot *next_free;
struct sim_snapshot *next_in_bucket;
struct sim_snapshot *prev_in_bucket;
u64 prev_tick;
u64 next_tick;
/* Absolute layers */
#define SIM_LAYER_FLOOR_DECALS (-300)
#define SIM_LAYER_BULLETS (-200)
#define SIM_LAYER_TRACERS (-100)
#define SIM_LAYER_SHOULDERS (0)
struct arena arena;
/* Relative layers */
#define SIM_LAYER_RELATIVE_DEFAULT (0)
#define SIM_LAYER_RELATIVE_WEAPON (1)
struct sim_client_handle producer_client;
b32 producer_client_is_local;
struct sim_snapshot;
struct sim_snapshot_store;
struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct sim_snapshot *prev_snapshot, struct sim_cmd_frame_list input_frames, i64 real_dt_ns);
/* ====================================================================== */
/* SIM_SNAPSHOT_FLAG_CONTROL */
struct sim_control control;
/* ====================================================================== */
/* SIM_SNAPSHOT_FLAG_STATE */
/* Was this snapshot created by the master sim */
b32 is_master;
/* Time of local snapshot publish to user thread */
i64 publish_dt_ns;
i64 publish_time_ns;
/* Real time (guaranteed to increase by real_dt_ns each sim step) */
i64 real_dt_ns;
i64 real_time_ns;
/* World time (affected by timescale) */
f64 world_timescale;
i64 world_dt_ns;
i64 world_time_ns;
/* If != previous tick's continuity then don't lerp */
u64 continuity_gen;
/* The last physics iteration (used for tracking contact lifetime) */
u64 phys_iteration;
/* The receiver's client handle on the sender's machine (use this to find local client ent in snapshot) */
struct sim_client_handle local_client;
/* Entities */
struct arena ents_arena;
struct sim_ent *ents;
struct sim_ent_handle first_free_ent;
u64 num_ents_allocated;
u64 num_ents_reserved;
};
struct sim_snapshot_list_node {
struct sim_snapshot *ss;
struct sim_snapshot_list_node *next;
};
struct sim_snapshot_list {
struct sim_snapshot_list_node *first;
struct sim_snapshot_list_node *last;
};
INLINE struct sim_snapshot *sim_snapshot_nil(void)
{
extern READONLY struct sim_snapshot **_g_sim_snapshot_nil;
return *_g_sim_snapshot_nil;
}
struct bitbuff_writer;
struct bitbuff_reader;
/* Alloc */
struct sim_snapshot *sim_snapshot_alloc(struct sim_client *client, struct sim_snapshot *src, u64 tick);
void sim_snapshot_release(struct sim_snapshot *sim_snapshot);
void sim_snapshot_release_ticks_in_range(struct sim_client *client, u64 start, u64 end);
/* Lookup */
struct sim_snapshot *sim_snapshot_from_tick(struct sim_client *client, u64 tick);
struct sim_snapshot *sim_snapshot_from_closest_tick_lte(struct sim_client *client, u64 tick);
/* Lerp */
struct sim_snapshot *sim_snapshot_alloc_from_lerp(struct sim_client *client, struct sim_snapshot *ss0, struct sim_snapshot *ss1, f64 blend);
/* Encode / decode */
void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_client *receiver, struct sim_snapshot *ss0, struct sim_snapshot *ss1);
void sim_snapshot_decode(struct bitbuff_reader *br, struct sim_snapshot *ss);
#endif

View File

@ -1,189 +0,0 @@
#include "sim_client.h"
#include "sim.h"
#include "sim_snapshot.h"
#include "host.h"
#include "arena.h"
#include "util.h"
#include "arena.h"
/* ========================== *
* Client
* ========================== */
INTERNAL u64 hash_from_channel_id(struct host_channel_id channel_id)
{
return hash_fnv64(HASH_FNV64_BASIS, STRING_FROM_STRUCT(&channel_id));
}
struct sim_client *sim_client_from_handle(struct sim_snapshot *ss, struct sim_client_handle handle)
{
if (handle.gen != 0 && handle.idx < ss->num_clients_reserved) {
struct sim_client *client = &ss->clients[handle.idx];
if (client->handle.gen == handle.gen) {
return client;
}
}
return sim_client_nil();
}
struct sim_client *sim_client_from_channel_id(struct sim_snapshot *ss, struct host_channel_id channel_id)
{
struct sim_client *res = sim_client_nil();
u64 channel_hash = hash_from_channel_id(channel_id);
u64 bucket_index = channel_hash % ss->num_client_lookup_buckets;
struct sim_client_lookup_bucket *bucket = &ss->client_lookup_buckets[bucket_index];
for (struct sim_client *client = sim_client_from_handle(ss, bucket->first); client->valid; client = sim_client_from_handle(ss, client->next_in_bucket)) {
if (client->channel_hash == channel_hash) {
res = client;
break;
}
}
return res;
}
struct sim_client *sim_client_alloc(struct sim_snapshot *ss, struct host_channel_id channel_id, enum sim_client_kind kind)
{
struct sim_client_handle handle = ZI;
struct sim_client *client = sim_client_from_handle(ss, ss->first_free_client);
if (client->valid) {
ss->first_free_client = client->next_free;
handle = client->handle;
++handle.gen;
} else {
client = arena_push(&ss->clients_arena, struct sim_client);
handle.gen = 1;
handle.idx = ss->num_clients_reserved;
++ss->num_clients_reserved;
}
++ss->num_clients_allocated;
*client = *sim_client_nil();
client->ss = ss;
client->valid = true;
client->kind = kind;
client->handle = handle;
/* Insert into channel lookup */
sim_client_set_channel_id(client, channel_id);
return client;
}
void sim_client_release(struct sim_client *client)
{
struct sim_snapshot *ss = client->ss;
client->valid = false;
++client->handle.gen;
client->next_free = ss->first_free_client;
ss->first_free_client = client->handle;
--ss->num_clients_allocated;
/* Remove from channel lookup */
sim_client_set_channel_id(client, HOST_CHANNEL_ID_NIL);
}
void sim_client_set_channel_id(struct sim_client *client, struct host_channel_id channel_id)
{
struct sim_snapshot *ss = client->ss;
struct host_channel_id old_channel_id = client->channel_id;
/* Remove from channel lookup */
if (!host_channel_id_is_nil(old_channel_id)) {
u64 bucket_index = client->channel_hash % ss->num_client_lookup_buckets;
struct sim_client_lookup_bucket *bucket = &ss->client_lookup_buckets[bucket_index];
struct sim_client *prev = sim_client_from_handle(ss, client->prev_in_bucket);
struct sim_client *next = sim_client_from_handle(ss, client->next_in_bucket);
if (prev->valid) {
prev->next_in_bucket = next->handle;
} else {
bucket->first = next->handle;
}
if (next->valid) {
next->prev_in_bucket = prev->handle;
} else {
bucket->last = prev->handle;
}
}
/* Insert into channel lookup */
u64 channel_hash = hash_from_channel_id(channel_id);
client->channel_id = channel_id;
client->channel_hash = channel_hash;
if (!host_channel_id_is_nil(channel_id)) {
u64 bucket_index = channel_hash % ss->num_client_lookup_buckets;
struct sim_client_lookup_bucket *bucket = &ss->client_lookup_buckets[bucket_index];
{
struct sim_client *prev_in_bucket = sim_client_from_handle(ss, bucket->last);
if (prev_in_bucket->valid) {
prev_in_bucket->next_in_bucket = client->handle;
client->prev_in_bucket = prev_in_bucket->handle;
} else {
bucket->first = client->handle;
}
bucket->last = client->handle;
}
}
}
/* ========================== *
* Lerp
* ========================== */
void sim_client_lerp(struct sim_client *c, struct sim_client *c0, struct sim_client *c1, f64 blend)
{
if (c0->valid && c1->valid && c0->handle.gen == c1->handle.gen) {
c0->cursor_pos = v2_lerp(c0->cursor_pos, c1->cursor_pos, blend);
c->control.move = v2_lerp(c0->control.move, c1->control.move, blend);
c->control.focus = v2_lerp(c0->control.focus, c1->control.focus, blend);
}
}
/* ========================== *
* Encode
* ========================== */
void sim_client_encode(struct bitbuff_writer *bw, struct sim_client *c0, struct sim_client *c1)
{
struct sim_snapshot *ss = c1->ss;
/* TODO: Granular delta encoding */
u64 pos = 0;
c1->ss = c0->ss;
while (pos < sizeof(*c1)) {
u64 chunk_size = min_u64(pos + 8, sizeof(*c1)) - pos;
u8 *chunk0 = (u8 *)c0 + pos;
u8 *chunk1 = (u8 *)c1 + pos;
if (MEMEQ(chunk0, chunk1, chunk_size)) {
bw_write_bit(bw, 0);
} else {
bw_write_bit(bw, 1);
u64 bits = 0;
MEMCPY(&bits, chunk1, chunk_size);
bw_write_ubits(bw, bits, 64);
}
pos += 8;
}
c1->ss = ss;
}
/* ========================== *
* Decode
* ========================== */
void sim_client_decode(struct bitbuff_reader *br, struct sim_client *e)
{
struct sim_snapshot *ss = e->ss;
u64 pos = 0;
while (pos < sizeof(*e)) {
u8 *chunk = (u8 *)e + pos;
if (br_read_bit(br)) {
u64 chunk_size = min_u64(pos + 8, sizeof(*e)) - pos;
u64 bits = br_read_ubits(br, 64);
MEMCPY(chunk, &bits, chunk_size);
}
pos += 8;
}
e->ss = ss;
}

View File

@ -1,81 +0,0 @@
#ifndef SIM_CLIENT_H
#define SIM_CLIENT_H
#include "sim_ent.h"
#define SIM_CLIENT_NIL_HANDLE ((struct sim_client_handle) { .gen = 0, .idx = 0 })
struct sim_client_channel_lookup_bucket;
struct sim_snapshot;
enum sim_client_kind {
SIM_CLIENT_KIND_INVALID,
SIM_CLIENT_KIND_SIM_SLAVE,
SIM_CLIENT_KIND_SIM_MASTER
};
struct sim_client_lookup_bucket {
struct sim_client_handle first;
struct sim_client_handle last;
};
struct sim_client {
b32 valid;
enum sim_client_kind kind;
struct sim_client_handle handle;
struct sim_snapshot *ss;
struct host_channel_id channel_id;
u64 channel_hash;
struct sim_client_handle next_free;
struct sim_client_handle next_in_bucket;
struct sim_client_handle prev_in_bucket;
/* This is the last tick we know that this client has received */
u64 ack;
struct v2 cursor_pos;
struct sim_ent_handle camera_ent;
struct sim_ent_handle control_ent;
struct sim_control control;
b32 dbg_drag_start;
b32 dbg_drag_stop;
struct sim_ent_handle dbg_drag_joint_ent;
};
INLINE struct sim_client *sim_client_nil(void)
{
extern READONLY struct sim_client **_g_sim_client_nil;
return *_g_sim_client_nil;
}
/* ========================== *
* Client
* ========================== */
struct sim_client *sim_client_from_handle(struct sim_snapshot *ss, struct sim_client_handle handle);
struct sim_client *sim_client_from_channel_id(struct sim_snapshot *ss, struct host_channel_id channel_id);
struct sim_client *sim_client_alloc(struct sim_snapshot *ss, struct host_channel_id channel_id, enum sim_client_kind kind);
void sim_client_release(struct sim_client *client);
void sim_client_set_channel_id(struct sim_client *client, struct host_channel_id channel_id);
/* ========================== *
* Lerp
* ========================== */
void sim_client_lerp(struct sim_client *c, struct sim_client *c0, struct sim_client *c1, f64 blend);
/* ========================== *
* Encode
* ========================== */
struct bitbuff_writer;
struct bitbuff_reader;
void sim_client_encode(struct bitbuff_writer *bw, struct sim_client *c0, struct sim_client *c1);
void sim_client_decode(struct bitbuff_reader *br, struct sim_client *c);
#endif

View File

@ -1,10 +1,10 @@
#include "sim_ent.h"
#include "sim.h"
#include "sim_snapshot.h"
#include "math.h"
#include "bitbuff.h"
/* ========================== *
* Entity allocation
* Ent allocation
* ========================== */
INTERNAL struct sim_ent *sim_ent_alloc_internal(struct sim_snapshot *ss)
@ -68,7 +68,7 @@ void sim_ent_release(struct sim_ent *ent)
}
/* ========================== *
* Query
* Ent query
* ========================== */
/* Returns a valid ent or read-only nil ent. Always safe to read result, need to check `valid` to write. */
@ -119,7 +119,79 @@ struct sim_ent *sim_ent_find_first_match_all(struct sim_snapshot *ss, struct sim
}
/* ========================== *
* Xform
* Ent tree
* ========================== */
void sim_ent_link_parent(struct sim_ent *ent, struct sim_ent *parent)
{
struct sim_snapshot *ss = ent->ss;
if (ent->parent.gen) {
/* Unlink from current parent */
sim_ent_unlink_from_parent(ent);
}
struct sim_ent_handle handle = ent->handle;
struct sim_ent_handle parent_handle = parent->handle;
ent->parent = parent_handle;
struct sim_ent_handle last_child_handle = parent->last;
struct sim_ent *last_child = sim_ent_from_handle(ss, last_child_handle);
if (last_child->valid) {
ent->prev = last_child_handle;
last_child->next = handle;
} else {
parent->first = handle;
}
parent->last = handle;
if (parent->is_root) {
ent->is_top = true;
ent->top = handle;
} else {
ent->top = parent->top;
}
}
/* NOTE: Entity will be dangling after calling this, should re-link to root ent. */
void sim_ent_unlink_from_parent(struct sim_ent *ent)
{
struct sim_snapshot *ss = ent->ss;
struct sim_ent_handle parent_handle = ent->parent;
struct sim_ent *parent = sim_ent_from_handle(ss, parent_handle);
struct sim_ent *prev = sim_ent_from_handle(ss, ent->prev);
struct sim_ent *next = sim_ent_from_handle(ss, ent->next);
/* Unlink from parent & siblings */
if (prev->valid) {
prev->next = next->handle;
} else {
parent->first = next->handle;
}
if (next->valid) {
next->prev = prev->handle;
} else {
parent->last = prev->handle;
}
ent->prev = sim_ent_nil()->handle;
ent->next = sim_ent_nil()->handle;
}
/* ========================== *
* Activate
* ========================== */
void sim_ent_activate(struct sim_ent *ent, u64 current_tick)
{
sim_ent_enable_prop(ent, SIM_ENT_PROP_ACTIVE);
ent->activation_tick = current_tick;
++ent->continuity_gen;
}
/* ========================== *
* Ent xform
* ========================== */
INTERNAL void sim_ent_mark_child_xforms_dirty(struct sim_snapshot *ss, struct sim_ent *ent)
@ -210,7 +282,7 @@ void sim_ent_set_local_xform(struct sim_ent *ent, struct xform xf)
}
/* ========================== *
* Movement
* Ent movement
* ========================== */
void sim_ent_apply_linear_impulse(struct sim_ent *ent, struct v2 impulse, struct v2 point)
@ -254,190 +326,7 @@ void sim_ent_apply_torque(struct sim_ent *ent, f32 torque)
}
/* ========================== *
* Tree
* ========================== */
void sim_ent_link_parent(struct sim_ent *ent, struct sim_ent *parent)
{
struct sim_snapshot *ss = ent->ss;
if (ent->parent.gen) {
/* Unlink from current parent */
sim_ent_unlink_from_parent(ent);
}
struct sim_ent_handle handle = ent->handle;
struct sim_ent_handle parent_handle = parent->handle;
ent->parent = parent_handle;
struct sim_ent_handle last_child_handle = parent->last;
struct sim_ent *last_child = sim_ent_from_handle(ss, last_child_handle);
if (last_child->valid) {
ent->prev = last_child_handle;
last_child->next = handle;
} else {
parent->first = handle;
}
parent->last = handle;
if (parent->is_root) {
ent->is_top = true;
ent->top = handle;
} else {
ent->top = parent->top;
}
}
/* NOTE: Entity will be dangling after calling this, should re-link to root ent. */
void sim_ent_unlink_from_parent(struct sim_ent *ent)
{
struct sim_snapshot *ss = ent->ss;
struct sim_ent_handle parent_handle = ent->parent;
struct sim_ent *parent = sim_ent_from_handle(ss, parent_handle);
struct sim_ent *prev = sim_ent_from_handle(ss, ent->prev);
struct sim_ent *next = sim_ent_from_handle(ss, ent->next);
/* Unlink from parent & siblings */
if (prev->valid) {
prev->next = next->handle;
} else {
parent->first = next->handle;
}
if (next->valid) {
next->prev = prev->handle;
} else {
parent->last = prev->handle;
}
ent->prev = sim_ent_nil()->handle;
ent->next = sim_ent_nil()->handle;
}
/* ========================== *
* Entity lookup
* ========================== */
struct sim_ent_lookup sim_ent_lookup_alloc(u64 num_buckets)
{
ASSERT(num_buckets > 0);
struct sim_ent_lookup l = ZI;
l.arena = arena_alloc(GIGABYTE(64));
l.buckets = arena_push_array_zero(&l.arena, struct sim_ent_lookup_bucket, num_buckets);
l.num_buckets = num_buckets;
return l;
}
void sim_ent_lookup_release(struct sim_ent_lookup *l)
{
arena_release(&l->arena);
}
struct sim_ent_lookup_entry *sim_ent_lookup_get(struct sim_ent_lookup *l, struct sim_ent_lookup_key key)
{
u64 index = key.hash % l->num_buckets;
struct sim_ent_lookup_bucket *bucket = &l->buckets[index];
struct sim_ent_lookup_entry *res = NULL;
for (struct sim_ent_lookup_entry *e = bucket->first; e; e = e->next) {
if (e->key.hash == key.hash) {
res = e;
break;
}
}
return res;
}
void sim_ent_lookup_set(struct sim_ent_lookup *l, struct sim_ent_lookup_key key, struct sim_ent_handle handle)
{
u64 index = key.hash % l->num_buckets;
struct sim_ent_lookup_bucket *bucket = &l->buckets[index];
struct sim_ent_lookup_entry *prev = NULL;
struct sim_ent_lookup_entry **slot = &bucket->first;
while (*slot) {
if ((*slot)->key.hash == key.hash) {
break;
}
prev = *slot;
slot = &(*slot)->next;
}
struct sim_ent_lookup_entry *entry = *slot;
if (entry) {
/* Set existing entry */
entry->ent = handle;
} else {
/* Allocate entry */
if (l->first_free_entry) {
entry = l->first_free_entry;
l->first_free_entry->prev = NULL;
l->first_free_entry = entry->next;
} else {
entry = arena_push(&l->arena, struct sim_ent_lookup_entry);
}
MEMZERO_STRUCT(entry);
entry->key = key;
entry->ent = handle;
if (prev) {
entry->prev = prev;
prev->next = entry;
}
bucket->last = entry;
*slot = entry;
}
}
void sim_ent_lookup_remove(struct sim_ent_lookup *l, struct sim_ent_lookup_entry *entry)
{
struct sim_ent_lookup_bucket *bucket = &l->buckets[entry->key.hash % l->num_buckets];
struct sim_ent_lookup_entry *prev = entry->prev;
struct sim_ent_lookup_entry *next = entry->next;
if (prev) {
prev->next = next;
} else {
bucket->first = next;
}
if (next) {
next->prev = prev;
} else {
bucket->last = prev;
}
if (l->first_free_entry) {
l->first_free_entry->prev = entry;
}
entry->next = l->first_free_entry;
entry->prev = NULL;
l->first_free_entry = entry;
}
struct sim_ent_lookup_key sim_ent_lookup_key_from_two_handles(struct sim_ent_handle h0, struct sim_ent_handle h1)
{
struct sim_ent_lookup_key key = ZI;
struct string b0 = STRING_FROM_STRUCT(&h0);
struct string b1 = STRING_FROM_STRUCT(&h1);
key.hash = hash_fnv64(HASH_FNV64_BASIS, b0);
key.hash = hash_fnv64(key.hash, b1);
return key;
}
/* ========================== *
* Activate
* ========================== */
void sim_ent_activate(struct sim_ent *ent, u64 current_tick)
{
sim_ent_enable_prop(ent, SIM_ENT_PROP_ACTIVE);
ent->activation_tick = current_tick;
++ent->continuity_gen;
}
/* ========================== *
* Lerp
* Ent lerp
* ========================== */
void sim_ent_lerp(struct sim_ent *e, struct sim_ent *e0, struct sim_ent *e1, f64 blend)
@ -477,7 +366,7 @@ void sim_ent_lerp(struct sim_ent *e, struct sim_ent *e0, struct sim_ent *e1, f64
}
/* ========================== *
* Encode
* Ent encode
* ========================== */
void sim_ent_encode(struct bitbuff_writer *bw, struct sim_ent *e0, struct sim_ent *e1)
@ -506,7 +395,7 @@ void sim_ent_encode(struct bitbuff_writer *bw, struct sim_ent *e0, struct sim_en
}
/* ========================== *
* Decode
* Ent decode
* ========================== */
void sim_ent_decode(struct bitbuff_reader *br, struct sim_ent *e)

View File

@ -20,6 +20,9 @@ enum sim_ent_prop {
SIM_ENT_PROP_RELEASE_THIS_TICK,
SIM_ENT_PROP_RELEASE_NEXT_TICK,
SIM_ENT_PROP_CLIENT,
SIM_ENT_PROP_LOCAL_CLIENT,
SIM_ENT_PROP_PHYSICAL_DYNAMIC,
SIM_ENT_PROP_PHYSICAL_KINEMATIC,
@ -54,29 +57,6 @@ enum sim_ent_prop {
SIM_ENT_PROP_COUNT
};
struct sim_ent_lookup_key {
u64 hash;
};
struct sim_ent_lookup_entry {
struct sim_ent_lookup_key key;
struct sim_ent_handle ent;
struct sim_ent_lookup_entry *next;
struct sim_ent_lookup_entry *prev;
};
struct sim_ent_lookup_bucket {
struct sim_ent_lookup_entry *first;
struct sim_ent_lookup_entry *last;
};
struct sim_ent_lookup {
struct arena arena;
struct sim_ent_lookup_bucket *buckets;
u64 num_buckets;
struct sim_ent_lookup_entry *first_free_entry;
};
struct sim_ent {
/* ====================================================================== */
/* Metadata */
@ -154,12 +134,31 @@ struct sim_ent {
/* SIM_ENT_PROP_MOUSE_JOINT */
struct phys_mouse_joint mouse_joint_data;
/* ====================================================================== */
/* Client */
/* SIM_ENT_PROP_CLIENT */
/* FIXME: Lerp */
struct sim_client_handle client_handle;
struct sim_control client_control;
struct v2 client_cursor_pos;
struct sim_ent_handle client_control_ent;
struct sim_ent_handle client_camera_ent;
struct sim_ent_handle client_dbg_drag_joint_ent;
b32 client_dbg_drag_start;
b32 client_dbg_drag_stop;
/* ====================================================================== */
/* Control */
/* SIM_ENT_PROP_CONTROLLED */
struct sim_client_handle controlling_client;
struct sim_ent_handle controlling_client;
f32 control_force; /* How much force is applied to achieve desired control movement */
f32 control_force_max_speed; /* Maximum linear velocity achieved by force (m/s) */
@ -362,6 +361,18 @@ INLINE b32 sim_ent_is_valid_and_active(struct sim_ent *ent)
struct sim_ent *sim_ent_alloc(struct sim_ent *parent);
void sim_ent_release(struct sim_ent *ent);
/* Query */
struct sim_ent *sim_ent_from_handle(struct sim_snapshot *ss, struct sim_ent_handle handle);
struct sim_ent *sim_ent_find_first_match_one(struct sim_snapshot *ss, enum sim_ent_prop prop);
struct sim_ent *sim_ent_find_first_match_all(struct sim_snapshot *ss, struct sim_ent_prop_array props);
/* Tree */
void sim_ent_link_parent(struct sim_ent *parent, struct sim_ent *child);
void sim_ent_unlink_from_parent(struct sim_ent *ent);
/* Activate */
void sim_ent_activate(struct sim_ent *ent, u64 current_tick);
/* Xform */
struct xform sim_ent_get_xform(struct sim_ent *ent);
struct xform sim_ent_get_local_xform(struct sim_ent *ent);
@ -377,26 +388,6 @@ void sim_ent_apply_force_to_center(struct sim_ent *ent, struct v2 force);
void sim_ent_apply_angular_impulse(struct sim_ent *ent, f32 impulse);
void sim_ent_apply_torque(struct sim_ent *ent, f32 torque);
/* Query */
struct sim_ent *sim_ent_from_handle(struct sim_snapshot *ss, struct sim_ent_handle handle);
struct sim_ent *sim_ent_find_first_match_one(struct sim_snapshot *ss, enum sim_ent_prop prop);
struct sim_ent *sim_ent_find_first_match_all(struct sim_snapshot *ss, struct sim_ent_prop_array props);
/* Tree */
void sim_ent_link_parent(struct sim_ent *parent, struct sim_ent *child);
void sim_ent_unlink_from_parent(struct sim_ent *ent);
/* Lookup */
struct sim_ent_lookup sim_ent_lookup_alloc(u64 num_buckets);
void sim_ent_lookup_release(struct sim_ent_lookup *l);
struct sim_ent_lookup_entry *sim_ent_lookup_get(struct sim_ent_lookup *l, struct sim_ent_lookup_key key);
void sim_ent_lookup_set(struct sim_ent_lookup *l, struct sim_ent_lookup_key key, struct sim_ent_handle handle);
void sim_ent_lookup_remove(struct sim_ent_lookup *l, struct sim_ent_lookup_entry *entry);
struct sim_ent_lookup_key sim_ent_lookup_key_from_two_handles(struct sim_ent_handle h0, struct sim_ent_handle h1);
/* Activate */
void sim_ent_activate(struct sim_ent *ent, u64 current_tick);
/* Lerp */
void sim_ent_lerp(struct sim_ent *e, struct sim_ent *e0, struct sim_ent *e1, f64 blend);

View File

@ -1,575 +0,0 @@
#include "sim_snapshot.h"
#include "sim.h"
#include "sim_ent.h"
#include "sim_client.h"
#include "arena.h"
#include "scratch.h"
#define TICK_LOOKUP_BUCKETS 127
#define CLIENT_LOOKUP_BUCKETS 127
struct sim_snapshot_lookup_bucket {
struct sim_snapshot *first;
struct sim_snapshot *last;
};
/* ========================== *
* Startup
* ========================== */
GLOBAL struct {
struct arena nil_arena;
struct sim_snapshot_store *nil_snapshot_store;
struct sim_snapshot *nil_snapshot;
struct sim_ent *nil_ent;
struct sim_client *nil_client;
} G = ZI, DEBUG_ALIAS(G, G_sim_snapshot);
/* Accessed via `sim_snapshot_store_nil()` */
READONLY struct sim_snapshot_store **_g_sim_snapshot_store_nil = &G.nil_snapshot_store;
/* Accessed via `sim_snapshot_nil()` */
READONLY struct sim_snapshot **_g_sim_snapshot_nil = &G.nil_snapshot;
/* Accessed via `sim_ent_nil()` */
READONLY struct sim_ent **_g_sim_ent_nil = &G.nil_ent;
/* Accessed via `sim_client_nil()` */
READONLY struct sim_client **_g_sim_client_nil = &G.nil_client;
struct sim_snapshot_startup_receipt sim_snapshot_startup(void)
{
G.nil_arena = arena_alloc(GIGABYTE(1));
/* Nil snapshot store */
G.nil_snapshot_store = arena_push_zero(&G.nil_arena, struct sim_snapshot_store);
G.nil_snapshot_store->valid = false;
/* Nil snapshot */
G.nil_snapshot = arena_push_zero(&G.nil_arena, struct sim_snapshot);
G.nil_snapshot->valid = false;
G.nil_snapshot->store = sim_snapshot_store_nil();
/* Nil ent */
G.nil_ent = arena_push_zero(&G.nil_arena, struct sim_ent);
G.nil_ent->ss = sim_snapshot_nil();
G.nil_ent->valid = false;
G.nil_ent->handle = SIM_ENT_NIL_HANDLE;
G.nil_ent->local_xform = XFORM_IDENT;
G.nil_ent->cached_global_xform = XFORM_IDENT;
G.nil_ent->cached_global_xform_dirty = false;
G.nil_ent->friction = 0.5f;
G.nil_ent->mass_unscaled = 1;
G.nil_ent->inertia_unscaled = 1;
G.nil_ent->sprite_local_xform = XFORM_IDENT;
G.nil_ent->sprite_tint = COLOR_WHITE;
/* Nil client */
G.nil_client = arena_push_zero(&G.nil_arena, struct sim_client);
G.nil_client->ss = sim_snapshot_nil();
G.nil_client->valid = false;
/* Lock nil arena */
arena_set_readonly(&G.nil_arena);
return (struct sim_snapshot_startup_receipt ) { 0 };
}
/* ========================== *
* Store
* ========================== */
struct sim_snapshot_store *sim_snapshot_store_alloc(void)
{
__prof;
struct sim_snapshot_store *store;
{
struct arena arena = arena_alloc(GIGABYTE(64));
store = arena_push_zero(&arena, struct sim_snapshot_store);
store->arena = arena;
}
store->valid = true;
store->num_lookup_buckets = TICK_LOOKUP_BUCKETS;
store->lookup_buckets = arena_push_array_zero(&store->arena, struct sim_snapshot_lookup_bucket, store->num_lookup_buckets);
return store;
}
void sim_snapshot_store_release(struct sim_snapshot_store *store)
{
__prof;
/* Release snapshot internal memory */
for (u64 i = 0; i < store->num_lookup_buckets; ++i) {
struct sim_snapshot_lookup_bucket *bucket = &store->lookup_buckets[i];
struct sim_snapshot *ss = bucket->first;
while (ss) {
struct sim_snapshot *next = ss->next_in_bucket;
arena_release(&ss->clients_arena);
arena_release(&ss->ents_arena);
arena_release(&ss->arena);
ss = next;
}
}
{
struct sim_snapshot *ss = store->first_free_snapshot;
while (ss) {
struct sim_snapshot *next = ss->next_free;
arena_release(&ss->clients_arena);
arena_release(&ss->ents_arena);
arena_release(&ss->arena);
ss = next;
}
}
/* Release store */
arena_release(&store->arena);
}
/* Releases all snapshots with tick in range [start, end] */
void sim_snapshot_store_release_ticks_in_range(struct sim_snapshot_store *store, u64 start, u64 end)
{
if (start > end) {
u64 swp = start;
start = end;
end = swp;
}
struct sim_snapshot *ss = sim_snapshot_from_tick(store, store->first_tick);
while (ss->valid) {
u64 tick = ss->tick;
u64 next_tick = ss->next_tick;
if (tick >= start) {
if (tick <= end) {
sim_snapshot_release(ss);
} else {
break;
}
}
ss = sim_snapshot_from_tick(store, next_tick);
}
}
/* ========================== *
* Alloc
* ========================== */
/* Produces a new snapshot at `tick` with data copied from `src` snapshot. */
struct sim_snapshot *sim_snapshot_alloc(struct sim_snapshot_store *store, struct sim_snapshot *src, u64 tick)
{
if (tick == 0) {
return sim_snapshot_nil();
}
struct sim_snapshot *ss;
{
struct arena arena = ZI;
struct arena clients_arena = ZI;
struct arena ents_arena = ZI;
{
ss = store->first_free_snapshot;
if (ss) {
/* Re-use existing snasphot arenas */
store->first_free_snapshot = ss->next_free;
ents_arena = ss->ents_arena;
clients_arena = ss->clients_arena;
arena = ss->arena;
} else {
/* Arenas allocated here will be released along with the entire snasphot store */
arena = arena_alloc(GIGABYTE(8));
clients_arena = arena_alloc(GIGABYTE(8));
ents_arena = arena_alloc(GIGABYTE(8));
}
}
arena_reset(&arena);
ss = arena_push_zero(&arena, struct sim_snapshot);
ss->arena = arena;
ss->clients_arena = clients_arena;
arena_reset(&ss->clients_arena);
ss->ents_arena = ents_arena;
arena_reset(&ss->ents_arena);
}
ss->tick = tick;
ss->valid = true;
ss->store = store;
++store->num_ticks;
/* Copy src info */
ss->is_master = src->is_master;
ss->real_dt_ns = src->real_dt_ns;
ss->real_time_ns = src->real_time_ns;
ss->world_timescale = src->world_timescale;
ss->world_dt_ns = src->world_dt_ns;
ss->world_time_ns = src->world_time_ns;
ss->continuity_gen = src->continuity_gen;
ss->local_client = src->local_client;
ss->phys_iteration = src->phys_iteration;
/* Copy client lookup buckets */
if (src->num_client_lookup_buckets > 0) {
ss->num_client_lookup_buckets = src->num_client_lookup_buckets;
ss->client_lookup_buckets = arena_push_array(&ss->arena, struct sim_client_lookup_bucket, ss->num_client_lookup_buckets);
MEMCPY(ss->client_lookup_buckets, src->client_lookup_buckets, sizeof(*ss->client_lookup_buckets) * ss->num_client_lookup_buckets);
} else {
ss->num_client_lookup_buckets = CLIENT_LOOKUP_BUCKETS;
ss->client_lookup_buckets = arena_push_array_zero(&ss->arena, struct sim_client_lookup_bucket, ss->num_client_lookup_buckets);
}
/* Copy clients */
ss->first_free_client = src->first_free_client;
ss->num_clients_allocated = src->num_clients_allocated;
ss->num_clients_reserved = src->num_clients_reserved;
ss->clients = arena_push_array(&ss->clients_arena, struct sim_client, ss->num_clients_reserved);
for (u64 i = 0; i < ss->num_clients_reserved; ++i) {
struct sim_client *dst_client = &ss->clients[i];
struct sim_client *src_client = &src->clients[i];
*dst_client = *src_client;
dst_client->ss = ss;
}
/* Copy entities */
ss->first_free_ent = src->first_free_ent;
ss->num_ents_allocated = src->num_ents_allocated;
ss->num_ents_reserved = src->num_ents_reserved;
ss->ents = arena_push_array(&ss->ents_arena, struct sim_ent, ss->num_ents_reserved);
if (ss->num_ents_reserved == 0) {
/* Create root ent if copying from nil store */
struct sim_ent *root = arena_push(&ss->ents_arena, struct sim_ent);
*root = *sim_ent_nil();
root->ss = ss;
root->handle = SIM_ENT_ROOT_HANDLE;
root->valid = true;
root->is_root = true;
++ss->num_ents_allocated;
++ss->num_ents_reserved;
} else {
for (u64 i = 0; i < ss->num_ents_reserved; ++i) {
struct sim_ent *dst_ent = &ss->ents[i];
struct sim_ent *src_ent = &src->ents[i];
*dst_ent = *src_ent;
dst_ent->ss = ss;
}
}
/* Release duplicate tick if it exists */
{
struct sim_snapshot *existing = sim_snapshot_from_tick(store, tick);
if (existing->valid) {
sim_snapshot_release(existing);
}
}
/* Linear search to insert snapshot in tick order */
{
struct sim_snapshot *prev = sim_snapshot_from_tick(store, store->last_tick);
while (prev->valid) {
if (prev->tick < tick) {
break;
}
prev = sim_snapshot_from_tick(store, prev->prev_tick);
}
if (prev->valid) {
struct sim_snapshot *next = sim_snapshot_from_tick(store, prev->next_tick);
if (next->valid) {
next->prev_tick = tick;
} else {
store->last_tick = tick;
}
ss->next_tick = prev->next_tick;
ss->prev_tick = prev->tick;
prev->next_tick = ss->tick;
} else {
struct sim_snapshot *first = sim_snapshot_from_tick(store, store->first_tick);
if (first->valid) {
ss->next_tick = first->tick;
first->prev_tick = tick;
} else {
store->last_tick = tick;
}
ss->next_tick = store->first_tick;
store->first_tick = tick;
}
}
/* Insert into lookup */
{
u64 bucket_index = tick % store->num_lookup_buckets;
struct sim_snapshot_lookup_bucket *bucket = &store->lookup_buckets[bucket_index];
if (bucket->last) {
bucket->last->next_in_bucket = ss;
ss->prev_in_bucket = bucket->last;
} else {
bucket->first = ss;
}
bucket->last = ss;
}
return ss;
}
void sim_snapshot_release(struct sim_snapshot *ss)
{
struct sim_snapshot_store *store = ss->store;
/* Remove from lookup */
{
u64 bucket_index = ss->tick % store->num_lookup_buckets;
struct sim_snapshot_lookup_bucket *bucket = &store->lookup_buckets[bucket_index];
struct sim_snapshot *prev = ss->prev_in_bucket;
struct sim_snapshot *next = ss->next_in_bucket;
if (prev) {
prev->next_in_bucket = next;
} else {
bucket->first = next;
}
if (next) {
next->prev_in_bucket = prev;
} else {
bucket->last = prev;
}
}
/* Remove from snapshot list */
{
struct sim_snapshot *prev = sim_snapshot_from_tick(store, ss->prev_tick);
struct sim_snapshot *next = sim_snapshot_from_tick(store, ss->next_tick);
if (prev->valid) {
prev->next_tick = next->tick;
} else {
store->first_tick = next->tick;
}
if (next->valid) {
next->prev_tick = prev->tick;
} else {
store->last_tick = prev->tick;
}
}
ss->valid = false;
ss->next_free = store->first_free_snapshot;
store->first_free_snapshot = ss;
--store->num_ticks;
}
/* ========================== *
* Lookup
* ========================== */
struct sim_snapshot *sim_snapshot_from_tick(struct sim_snapshot_store *store, u64 tick)
{
struct sim_snapshot *ss = sim_snapshot_nil();
if (tick > 0) {
u64 bucket_index = tick % store->num_lookup_buckets;
struct sim_snapshot_lookup_bucket *bucket = &store->lookup_buckets[bucket_index];
for (struct sim_snapshot *search = bucket->first; search; search = search->next_in_bucket) {
if (search->tick == tick) {
ss = search;
break;
}
}
}
return ss;
}
/* ========================== *
* Lerp
* ========================== */
struct sim_snapshot *sim_snapshot_alloc_from_lerp(struct sim_snapshot_store *store, struct sim_snapshot *ss0, struct sim_snapshot *ss1, f64 blend)
{
__prof;
/* New snapshot will be allocated with same tick as ss0 or ss1, so the result should go into a different store */
ASSERT(ss0->store != store && ss1->store != store);
struct sim_snapshot *ss;
b32 should_blend = true;
if (ss0->continuity_gen == ss1->continuity_gen && 0 < blend && blend < 1) {
ss = sim_snapshot_alloc(store, ss0, ss0->tick);
} else if (math_round_to_int64(blend) <= 0) {
ss = sim_snapshot_alloc(store, ss0, ss0->tick);
should_blend = false;
} else {
ss = sim_snapshot_alloc(store, ss1, ss1->tick);
should_blend = false;
}
if (!ss0->valid || !ss1->valid) {
/* New snapshot allocation caused one of the src snapshots original to release.
* ss0 & ss1 should be from a separate store than the allocating one. */
ASSERT(false);
}
if (should_blend) {
/* Blend time */
ss->real_dt_ns = math_lerp_i64(ss0->real_dt_ns, ss1->real_dt_ns, blend);
ss->real_time_ns = math_lerp_i64(ss0->real_time_ns, ss1->real_time_ns, blend);
ss->world_timescale = math_lerp_f64(ss0->world_timescale, ss1->world_timescale, blend);
ss->world_dt_ns = math_lerp_i64(ss0->world_dt_ns, ss1->world_dt_ns, blend);
ss->world_time_ns = math_lerp_i64(ss0->world_time_ns, ss1->world_time_ns, blend);
/* Blend clients */
{
__profscope(snapshot_lerp_clients);
u64 num_clients = min_u64(ss0->num_clients_reserved, ss1->num_clients_reserved);
for (u64 i = 0; i < num_clients; ++i) {
struct sim_client *c = &ss->clients[i];
struct sim_client *c0 = &ss0->clients[i];
struct sim_client *c1 = &ss1->clients[i];
sim_client_lerp(c, c0, c1, blend);
}
}
/* Blend entities */
{
__profscope(snapshot_lerp_entities);
u64 num_entities = min_u64(ss0->num_ents_reserved, ss1->num_ents_reserved);
for (u64 i = 0; i < num_entities; ++i) {
struct sim_ent *e = &ss->ents[i];
struct sim_ent *e0 = &ss0->ents[i];
struct sim_ent *e1 = &ss1->ents[i];
sim_ent_lerp(e, e0, e1, blend);
}
}
}
return ss;
}
/* ========================== *
* Encode
* ========================== */
void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_client *receiver, struct sim_snapshot *ss0, struct sim_snapshot *ss1)
{
__prof;
/* TODO: Don't encode this */
bw_write_bit(bw, ss1->is_master);
bw_write_iv(bw, ss1->real_dt_ns);
bw_write_iv(bw, ss1->real_time_ns);
bw_write_f64(bw, ss1->world_timescale);
bw_write_iv(bw, ss1->world_dt_ns);
bw_write_iv(bw, ss1->world_time_ns);
bw_write_uv(bw, ss1->continuity_gen);
bw_write_uv(bw, ss1->phys_iteration);
bw_write_uv(bw, receiver->handle.gen);
bw_write_uv(bw, receiver->handle.idx);
/* Clients */
if (ss1->num_clients_allocated == ss0->num_clients_allocated) {
bw_write_bit(bw, 0);
} else {
bw_write_bit(bw, 1);
bw_write_uv(bw, ss1->num_clients_allocated);
}
if (ss1->num_clients_reserved == ss0->num_clients_reserved) {
bw_write_bit(bw, 0);
} else {
bw_write_bit(bw, 1);
bw_write_uv(bw, ss1->num_clients_reserved);
}
for (u64 i = 0; i < ss1->num_clients_reserved; ++i) {
struct sim_client *c0 = sim_client_nil();
if (i < ss0->num_clients_reserved) {
c0 = &ss0->clients[i];
}
struct sim_client *c1 = &ss1->clients[i];
sim_client_encode(bw, c0, c1);
}
/* Ents */
if (ss1->num_ents_allocated == ss0->num_ents_allocated) {
bw_write_bit(bw, 0);
} else {
bw_write_bit(bw, 1);
bw_write_uv(bw, ss1->num_ents_allocated);
}
if (ss1->num_ents_reserved == ss0->num_ents_reserved) {
bw_write_bit(bw, 0);
} else {
bw_write_bit(bw, 1);
bw_write_uv(bw, ss1->num_ents_reserved);
}
for (u64 i = 0; i < ss1->num_ents_reserved; ++i) {
struct sim_ent *e0 = sim_ent_nil();
if (i < ss0->num_ents_reserved) {
e0 = &ss0->ents[i];
}
struct sim_ent *e1 = &ss1->ents[i];
sim_ent_encode(bw, e0, e1);
}
}
/* ========================== *
* Decode
* ========================== */
void sim_snapshot_decode(struct bitbuff_reader *br, struct sim_snapshot *ss)
{
__prof;
/* TODO: Don't encode this */
ss->is_master = br_read_bit(br);
ss->real_dt_ns = br_read_iv(br);
ss->real_time_ns = br_read_iv(br);
ss->world_timescale = br_read_f64(br);
ss->world_dt_ns = br_read_iv(br);
ss->world_time_ns = br_read_iv(br);
ss->continuity_gen = br_read_uv(br);
ss->phys_iteration = br_read_uv(br);
ss->local_client.gen = br_read_uv(br);
ss->local_client.idx = br_read_uv(br);
/* Clients */
if (br_read_bit(br)) {
ss->num_clients_allocated = br_read_uv(br);
}
if (br_read_bit(br)) {
u64 old_num_clients_reserved = ss->num_clients_reserved;
ss->num_clients_reserved = br_read_uv(br);
i64 reserve_diff = (i64)ss->num_clients_reserved - (i64)old_num_clients_reserved;
if (reserve_diff > 0) {
arena_push_array(&ss->clients_arena, struct sim_client, reserve_diff);
for (u64 i = old_num_clients_reserved; i < ss->num_clients_reserved; ++i) {
struct sim_client *c = &ss->clients[i];
*c = *sim_client_nil();
c->ss = ss;
}
}
}
for (u64 i = 0; i < ss->num_clients_reserved; ++i) {
struct sim_client *c = &ss->clients[i];
sim_client_decode(br, c);
}
/* Ents */
if (br_read_bit(br)) {
ss->num_ents_allocated = br_read_uv(br);
}
if (br_read_bit(br)) {
u64 old_num_ents_reserved = ss->num_ents_reserved;
ss->num_ents_reserved = br_read_uv(br);
i64 reserve_diff = (i64)ss->num_ents_reserved - (i64)old_num_ents_reserved;
if (reserve_diff > 0) {
arena_push_array(&ss->ents_arena, struct sim_ent, reserve_diff);
for (u64 i = old_num_ents_reserved; i < ss->num_ents_reserved; ++i) {
struct sim_ent *e = &ss->ents[i];
*e = *sim_ent_nil();
e->ss = ss;
}
}
}
for (u64 i = 0; i < ss->num_ents_reserved; ++i) {
struct sim_ent *e = &ss->ents[i];
sim_ent_decode(br, e);
}
}

View File

@ -1,160 +0,0 @@
#ifndef SIM_SNAPSHOT_H
#define SIM_SNAPSHOT_H
#include "sim_ent.h"
#include "sim_client.h"
#if 0
enum sim_snapshot_flag {
SIM_SNAPSHOT_FLAG_NONE = 0,
/* The snapshot contains client control data */
SIM_SNAPSHOT_FLAG_CONTROL = (1 << 0),
/* The snapshot contains entity state data */
SIM_SNAPSHOT_FLAG_STATE = (1 << 1)
};
#endif
struct sim_snapshot_store {
b32 valid;
struct arena arena;
/* Snapshots sorted by tick (low to high) */
u64 first_tick;
u64 last_tick;
u64 num_ticks;
struct sim_snapshot *first_free_snapshot;
/* Tick -> snapshot lookup */
u64 num_lookup_buckets;
struct sim_snapshot_lookup_bucket *lookup_buckets;
};
struct sim_snapshot {
b32 valid;
u64 tick;
u64 ack;
struct sim_snapshot_store *store;
struct sim_snapshot *next_free;
struct sim_snapshot *next_in_bucket;
struct sim_snapshot *prev_in_bucket;
u64 prev_tick;
u64 next_tick;
struct arena arena;
/* ====================================================================== */
/* SIM_SNAPSHOT_FLAG_CONTROL */
struct sim_control control;
/* ====================================================================== */
/* SIM_SNAPSHOT_FLAG_STATE */
/* Was this snapshot created by the master sim */
b32 is_master;
/* Time of local snapshot publish to user thread */
i64 publish_dt_ns;
i64 publish_time_ns;
/* Real time (guaranteed to increase by real_dt_ns each sim step) */
i64 real_dt_ns;
i64 real_time_ns;
/* World time (affected by timescale) */
f64 world_timescale;
i64 world_dt_ns;
i64 world_time_ns;
/* If != previous tick's continuity then don't lerp */
u64 continuity_gen;
/* The last physics iteration (used for tracking contact lifetime) */
u64 phys_iteration;
/* Which client in the client store represents the local host in the original snapshot */
struct sim_client_handle local_client;
/* Client lookup */
struct sim_client_lookup_bucket *client_lookup_buckets;
u64 num_client_lookup_buckets;
/* Clients */
struct arena clients_arena;
struct sim_client *clients;
struct sim_client_handle first_free_client;
u64 num_clients_allocated;
u64 num_clients_reserved;
/* Entities */
struct arena ents_arena;
struct sim_ent *ents;
struct sim_ent_handle first_free_ent;
u64 num_ents_allocated;
u64 num_ents_reserved;
};
#if 0
struct sim_snapshot_encoded {
u64 base_tick;
u64 tick;
struct string data; /* Contains snapshot deltas for `tick` from `base_tick` */
};
#endif
/* ========================== *
* Startup
* ========================== */
struct sim_snapshot_startup_receipt { i32 _; };
struct sim_snapshot_startup_receipt sim_snapshot_startup(void);
/* ========================== *
* Store
* ========================== */
struct sim_snapshot_store *sim_snapshot_store_alloc(void);
void sim_snapshot_store_release(struct sim_snapshot_store *store);
void sim_snapshot_store_release_ticks_in_range(struct sim_snapshot_store *store, u64 start, u64 end);
/* ========================== *
* Nil
* ========================== */
INLINE struct sim_snapshot *sim_snapshot_nil(void)
{
extern READONLY struct sim_snapshot **_g_sim_snapshot_nil;
return *_g_sim_snapshot_nil;
}
INLINE struct sim_snapshot_store *sim_snapshot_store_nil(void)
{
extern READONLY struct sim_snapshot_store **_g_sim_snapshot_store_nil;
return *_g_sim_snapshot_store_nil;
}
/* ========================== *
* Snapshot
* ========================== */
struct bitbuff_writer;
struct bitbuff_reader;
/* Alloc */
struct sim_snapshot *sim_snapshot_alloc(struct sim_snapshot_store *store, struct sim_snapshot *src, u64 tick);
void sim_snapshot_release(struct sim_snapshot *sim_snapshot);
/* Lookup */
struct sim_snapshot *sim_snapshot_from_tick(struct sim_snapshot_store *store, u64 tick);
/* Lerp */
struct sim_snapshot *sim_snapshot_alloc_from_lerp(struct sim_snapshot_store *store, struct sim_snapshot *ss0, struct sim_snapshot *ss1, f64 blend);
/* Encode / decode */
void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_client *receiver, struct sim_snapshot *ss0, struct sim_snapshot *ss1);
void sim_snapshot_decode(struct bitbuff_reader *br, struct sim_snapshot *ss);
#endif

1459
src/sim_step.c Normal file

File diff suppressed because it is too large Load Diff

83
src/sim_step.h Normal file
View File

@ -0,0 +1,83 @@
#ifndef SIM_STEP_H
#define SIM_STEP_H
struct space;
struct sim_snapshot;
struct sim_snapshot_list;
struct sim_lookup;
/* ========================== *
* Sim lookup
* ========================== */
/* Structure used to accelerate up entity lookup (rebuilt every step) */
/* TODO: Remove this and do something better. Just a hack to de-couple old sim ctx from step. */
struct sim_lookup_key {
u64 hash;
};
struct sim_lookup_entry {
struct sim_lookup_key key;
struct sim_ent_handle ent;
struct sim_lookup_entry *next;
struct sim_lookup_entry *prev;
};
struct sim_lookup_bucket {
struct sim_lookup_entry *first;
struct sim_lookup_entry *last;
};
struct sim_lookup {
struct arena arena;
struct sim_lookup_bucket *buckets;
u64 num_buckets;
struct sim_lookup_entry *first_free_entry;
};
struct sim_lookup sim_lookup_alloc(u64 num_buckets);
void sim_lookup_release(struct sim_lookup *l);
void sim_lookup_reset(struct sim_lookup *l);
struct sim_lookup_entry *sim_lookup_get(struct sim_lookup *l, struct sim_lookup_key key);
void sim_lookup_set(struct sim_lookup *l, struct sim_lookup_key key, struct sim_ent_handle handle);
void sim_lookup_remove(struct sim_lookup *l, struct sim_lookup_entry *entry);
struct sim_lookup_key sim_lookup_key_from_two_handles(struct sim_ent_handle h0, struct sim_ent_handle h1);
struct sim_lookup_key sim_lookup_key_from_client_handle(struct sim_client_handle handle);
/* ========================== *
* Sim accel
* ========================== */
/* Structure used to accelerate up entity lookup (rebuilt every step) */
/* TODO: Remove this and do something better. Just a hack to de-couple old sim ctx from step. */
struct sim_accel {
struct space *space;
struct sim_lookup client_lookup;
struct sim_lookup contact_lookup;
#if COLLIDER_DEBUG
struct sim_lookup collision_debug_lookup;
#endif
};
struct sim_accel sim_accel_alloc(void);
void sim_accel_release(struct sim_accel *accel);
void sim_accel_rebuild(struct sim_snapshot *ss, struct sim_accel *accel);
/* ========================== *
* Sim step
* ========================== */
struct sim_step_ctx {
struct sim_accel *accel;
struct sim_snapshot *world;
struct sim_snapshot_list *cmd_snapshots;
i64 real_dt_ns;
};
void sim_step(struct sim_step_ctx *step_ctx);
#endif

View File

@ -22,11 +22,14 @@ READONLY struct space _g_space_nil = { .valid = false };
* For example, at `num_buckets_sqrt` = 256 (65536 buckets), tiles <1, 1>, <1, 257>, and <257, 257> will collide. */
struct space *space_alloc(f32 cell_size, u32 num_buckets_sqrt)
{
struct space *space;
{
struct arena arena = arena_alloc(GIGABYTE(64));
struct space *space = arena_push_zero(&arena, struct space);
space = arena_push_zero(&arena, struct space);
space->entry_arena = arena;
}
space->valid = true;
space->entry_arena = arena;
space->entries = arena_dry_push(&space->entry_arena, struct space_entry);
space->cell_arena = arena_alloc(GIGABYTE(64));
@ -44,6 +47,17 @@ void space_release(struct space *space)
arena_release(&space->entry_arena);
}
void space_reset(struct space *space)
{
arena_pop_to(&space->entry_arena, (u64)space->entries - (u64)space->entry_arena.base);
arena_reset(&space->cell_arena);
space->buckets = arena_push_array_zero(&space->cell_arena, struct space_cell_bucket, space->num_buckets);;
space->num_entries_reserved = 0;
space->first_free_cell = NULL;
space->first_free_cell_node = NULL;
space->first_free_entry = NULL;
}
struct space *space_from_entry(struct space_entry *entry)
{
if (entry->valid) {

View File

@ -106,6 +106,8 @@ INLINE struct space *space_nil(void)
struct space *space_alloc(f32 cell_size, u32 num_buckets_sqrt);
void space_release(struct space *space);
void space_reset(struct space *space);
struct space *space_from_entry(struct space_entry *entry);
/* ========================== *

View File

@ -2,7 +2,7 @@
#include "app.h"
#include "sim.h"
#include "sim_ent.h"
#include "sim_snapshot.h"
#include "sim_step.h"
#include "renderer.h"
#include "font.h"
#include "sprite.h"
@ -48,12 +48,10 @@ GLOBAL struct {
struct sys_window *window;
struct string connect_address_str;
struct sim_snapshot_store *unblended_snapshot_store; /* Contains buffered snapshots from sim */
struct sim_snapshot_store *blended_snapshot_store; /* Contains single world snapshot from result of blending sim snapshots */
struct sim_snapshot *ss_blended;
/* Dynamic bitbuff used by encoders */
struct bitbuff encoder_bitbuff;
struct sim_client_store *user_client_store;
struct sim_client *user_unblended_snapshots_client; /* Contains buffered snapshots received from sim */
struct sim_client *user_blended_snapshots_client; /* Contains single world snapshot from result of blending sim snapshots */
struct sim_snapshot *ss_blended; /* Points to blended snapshot in blended snapshots client */
/* Usage stats */
i64 last_second_reset_ns;
@ -93,8 +91,9 @@ GLOBAL struct {
u64 user_sim_cmd_ack;
/* Local sim -> user */
struct sys_mutex local_sim_ss_mutex;
struct sim_snapshot_store *local_sim_ss_store;
struct sys_mutex local_sim_to_user_mutex;
struct sim_client_store *local_sim_to_user_client_store;
struct sim_client *local_sim_to_user_client;
/* Rolling window of local sim publish times */
i64 last_snapshot_published_at_ns;
@ -180,7 +179,7 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
struct mixer_startup_receipt *mixer_sr,
struct phys_startup_receipt *phys_sr,
struct host_startup_receipt *host_sr,
struct sim_snapshot_startup_receipt *sim_snapshot_sr,
struct sim_startup_receipt *sim_sr,
struct string connect_address_str,
struct sys_window *window)
{
@ -194,30 +193,31 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
(UNUSED)mixer_sr;
(UNUSED)phys_sr;
(UNUSED)host_sr;
(UNUSED)sim_snapshot_sr;
(UNUSED)sim_sr;
G.arena = arena_alloc(GIGABYTE(64));
G.real_time_ns = sys_time_ns();
/* TODO: Remove this */
G.connect_address_str = string_copy(&G.arena, connect_address_str);
/* Snapshot store */
G.unblended_snapshot_store = sim_snapshot_store_alloc();
G.blended_snapshot_store = sim_snapshot_store_alloc();
G.ss_blended = sim_snapshot_nil();
/* Sys events */
G.sys_events_mutex = sys_mutex_alloc();
G.sys_events_arena = arena_alloc(GIGABYTE(64));
/* User sim control */
G.user_sim_cmd_mutex = sys_mutex_alloc();
/* Snapshot store */
G.user_client_store = sim_client_store_alloc();
G.user_unblended_snapshots_client = sim_client_alloc(G.user_client_store, SIM_CLIENT_KIND_USER);
G.user_blended_snapshots_client = sim_client_alloc(G.user_client_store, SIM_CLIENT_KIND_USER);
G.ss_blended = sim_snapshot_nil();
/* Local sim snapshot store */
G.local_sim_ss_mutex = sys_mutex_alloc();
G.local_sim_ss_store = sim_snapshot_store_alloc();
G.local_sim_to_user_mutex = sys_mutex_alloc();
G.local_sim_to_user_client_store = sim_client_store_alloc();
G.local_sim_to_user_client = sim_client_alloc(G.local_sim_to_user_client_store, SIM_CLIENT_KIND_LOCAL_SIM);
G.encoder_bitbuff = bitbuff_alloc(GIGABYTE(64));
/* User sim control */
G.user_sim_cmd_mutex = sys_mutex_alloc();
G.world_to_ui_xf = XFORM_IDENT;
G.world_cmd_buffer = renderer_cmd_buffer_alloc();
@ -225,8 +225,6 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
G.final_cmd_buffer = renderer_cmd_buffer_alloc();
G.backbuffer_cmd_buffer = renderer_cmd_buffer_alloc();
G.real_time_ns = sys_time_ns();
G.window = window;
sys_window_register_event_callback(G.window, &window_event_callback);
@ -393,22 +391,17 @@ INTERNAL void user_update(void)
* ========================== */
{
struct sys_lock lock = sys_mutex_lock_e(&G.local_sim_ss_mutex);
u64 old_last_tick = G.unblended_snapshot_store->last_tick;
u64 last_tick = G.local_sim_ss_store->last_tick;
struct sys_lock lock = sys_mutex_lock_e(&G.local_sim_to_user_mutex);
u64 old_last_tick = G.user_unblended_snapshots_client->last_tick;
u64 last_tick = G.local_sim_to_user_client->last_tick;
if (last_tick > old_last_tick) {
struct sim_snapshot *src = sim_snapshot_from_tick(G.local_sim_ss_store, last_tick);
sim_snapshot_alloc(G.unblended_snapshot_store, src, src->tick);
#if 0
G.last_snapshot_received_at_ns = G.real_time_ns;
#else
struct sim_snapshot *src = sim_snapshot_from_tick(G.local_sim_to_user_client, last_tick);
sim_snapshot_alloc(G.user_unblended_snapshots_client, src, src->tick);
G.last_snapshot_published_at_ns = src->publish_time_ns;
G.snapshot_publish_dts_ns[G.snapshot_publish_dts_index++] = src->publish_dt_ns;
if (G.snapshot_publish_dts_index >= (i64)ARRAY_COUNT(G.snapshot_publish_dts_ns)) {
G.snapshot_publish_dts_index = 0;
}
#endif
}
sys_mutex_unlock(&lock);
}
@ -450,7 +443,7 @@ INTERNAL void user_update(void)
}
/* Predict local sim time based on average snapshot publish dt. */
struct sim_snapshot *newest_snapshot = sim_snapshot_from_tick(G.unblended_snapshot_store, G.unblended_snapshot_store->last_tick);
struct sim_snapshot *newest_snapshot = sim_snapshot_from_tick(G.user_unblended_snapshots_client, G.user_unblended_snapshots_client->last_tick);
G.local_sim_last_known_time_ns = newest_snapshot->real_time_ns;
G.local_sim_last_known_tick = newest_snapshot->tick;
u64 keep_unblended_tick = newest_snapshot->tick;
@ -474,7 +467,7 @@ INTERNAL void user_update(void)
struct sim_snapshot *left_snapshot = sim_snapshot_nil();
struct sim_snapshot *right_snapshot = newest_snapshot;
{
struct sim_snapshot *ss = sim_snapshot_from_tick(G.unblended_snapshot_store, G.unblended_snapshot_store->first_tick);
struct sim_snapshot *ss = sim_snapshot_from_tick(G.user_unblended_snapshots_client, G.user_unblended_snapshots_client->first_tick);
while (ss->valid) {
u64 next_tick = ss->next_tick;
i64 ss_time_ns = ss->real_time_ns;
@ -484,7 +477,7 @@ INTERNAL void user_update(void)
if (ss_time_ns > G.render_time_ns && ss_time_ns < right_snapshot->real_time_ns) {
right_snapshot = ss;
}
ss = sim_snapshot_from_tick(G.unblended_snapshot_store, next_tick);
ss = sim_snapshot_from_tick(G.user_unblended_snapshots_client, next_tick);
}
}
@ -495,11 +488,11 @@ INTERNAL void user_update(void)
/* Create world from blended snapshots */
if (left_snapshot->valid && right_snapshot->valid) {
f64 blend = (f64)(G.render_time_ns - left_snapshot->real_time_ns) / (f64)(right_snapshot->real_time_ns - left_snapshot->real_time_ns);
G.ss_blended = sim_snapshot_alloc_from_lerp(G.blended_snapshot_store, left_snapshot, right_snapshot, blend);
G.ss_blended = sim_snapshot_alloc_from_lerp(G.user_blended_snapshots_client, left_snapshot, right_snapshot, blend);
} else if (left_snapshot->valid) {
G.ss_blended = sim_snapshot_alloc(G.blended_snapshot_store, left_snapshot, left_snapshot->tick);
G.ss_blended = sim_snapshot_alloc(G.user_blended_snapshots_client, left_snapshot, left_snapshot->tick);
} else if (right_snapshot->valid) {
G.ss_blended = sim_snapshot_alloc(G.blended_snapshot_store, right_snapshot, right_snapshot->tick);
G.ss_blended = sim_snapshot_alloc(G.user_blended_snapshots_client, right_snapshot, right_snapshot->tick);
}
#else
/* Interp disabled, just copy latest snapshot */
@ -509,24 +502,24 @@ INTERNAL void user_update(void)
if (G.ss_blended->valid) {
sim_snapshot_release(G.ss_blended);
}
G.ss_blended = sim_snapshot_alloc(G.blended_snapshot_store, newest_snapshot, newest_snapshot->tick);
G.ss_blended = sim_snapshot_alloc(G.user_blended_snapshots_client, newest_snapshot, newest_snapshot->tick);
}
#endif
/* Release unneeded unblended sim snapshots */
if (keep_unblended_tick > 0) {
sim_snapshot_store_release_ticks_in_range(G.unblended_snapshot_store, 0, keep_unblended_tick - 1);
sim_snapshot_release_ticks_in_range(G.user_unblended_snapshots_client, 0, keep_unblended_tick - 1);
}
/* Release unused blended snapshots */
{
struct sim_snapshot *ss = sim_snapshot_from_tick(G.blended_snapshot_store, G.blended_snapshot_store->first_tick);
struct sim_snapshot *ss = sim_snapshot_from_tick(G.user_blended_snapshots_client, G.user_blended_snapshots_client->first_tick);
while (ss->valid) {
u64 next_tick = ss->next_tick;
if (ss != G.ss_blended) {
sim_snapshot_release(ss);
}
ss = sim_snapshot_from_tick(G.blended_snapshot_store, next_tick);
ss = sim_snapshot_from_tick(G.user_blended_snapshots_client, next_tick);
}
}
}
@ -624,9 +617,23 @@ INTERNAL void user_update(void)
* Find local entities
* ========================== */
struct sim_client *local_client = sim_client_from_handle(G.ss_blended, G.ss_blended->local_client);
struct sim_ent *local_player = sim_ent_from_handle(G.ss_blended, local_client->control_ent);
struct sim_ent *local_camera = sim_ent_from_handle(G.ss_blended, local_client->camera_ent);
struct sim_ent *local_client_ent = sim_ent_nil();
{
struct sim_client_handle local_client_handle = G.ss_blended->local_client;
for (u64 ent_index = 0; ent_index < G.ss_blended->num_ents_reserved; ++ent_index) {
struct sim_ent *ent = &G.ss_blended->ents[ent_index];
if (!sim_ent_is_valid_and_active(ent)) continue;
if (sim_ent_has_prop(ent, SIM_ENT_PROP_CLIENT)) {
struct sim_client_handle h = ent->client_handle;
if (h.gen != 0 && h.gen == local_client_handle.gen && h.idx == local_client_handle.idx) {
local_client_ent = ent;
break;
}
}
}
}
struct sim_ent *local_player = sim_ent_from_handle(G.ss_blended, local_client_ent->client_control_ent);
struct sim_ent *local_camera = sim_ent_from_handle(G.ss_blended, local_client_ent->client_camera_ent);
/* ========================== *
* Apply shake
@ -1426,7 +1433,6 @@ INTERNAL void user_update(void)
u32 old_flags = G.user_sim_cmd_control.flags;
G.user_sim_cmd_control = control;
G.user_sim_cmd_control.flags |= old_flags;
G.user_sim_cmd_control_cursor_pos = G.world_cursor;
G.user_sim_cmd_ack = G.local_sim_last_known_tick;
sys_mutex_unlock(&lock);
}
@ -1561,19 +1567,6 @@ INTERNAL void user_update(void)
}
#if 0
/* Update network usage stats */
G.client_bytes_read.last_second_end = G.host->bytes_received;
G.client_bytes_sent.last_second_end = G.host->bytes_sent;
if (G.real_time_ns - G.last_second_reset_ns > NS_FROM_SECONDS(1)) {
G.last_second_reset_ns = G.real_time_ns;
G.client_bytes_read.last_second = G.client_bytes_read.last_second_end - G.client_bytes_read.last_second_start;
G.client_bytes_sent.last_second = G.client_bytes_sent.last_second_end - G.client_bytes_sent.last_second_start;
G.client_bytes_read.last_second_start = G.client_bytes_read.last_second_end;
G.client_bytes_sent.last_second_start = G.client_bytes_sent.last_second_end;
}
#endif
/* ========================== *
* Render
* ========================== */
@ -1703,9 +1696,6 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_thread_entry_point, arg)
* Local sim thread
* ========================== */
#if 1
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
{
#if 0
@ -1727,34 +1717,10 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
} else {
is_master = true;
}
struct bitbuff msg_writer_bb = bitbuff_alloc(GIGABYTE(64));
struct bitbuff snapshot_writer_bb = bitbuff_alloc(GIGABYTE(64));
struct sim_accel accel = sim_accel_alloc();
struct sim_client_store *store = sim_client_store_alloc();
struct sim_client *local_client = sim_client_alloc(store, SIM_CLIENT_KIND_LOCAL_SIM);
@ -1770,7 +1736,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
struct temp_arena scratch = scratch_begin_no_conflict();
{
__profscope(local_sim_sleep);
sleep_frame(last_tick_ns, target_dt_ns);
sleep_frame(last_tick_ns, compute_dt_ns);
last_tick_ns = sys_time_ns();
}
++step_tick;
@ -1780,8 +1746,11 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
struct sim_snapshot *prev_user_cmd_ss = sim_snapshot_from_tick(user_client, user_client->last_tick);
struct sim_snapshot *user_cmd_ss = sim_snapshot_alloc(user_client, prev_user_cmd_ss, step_tick);
struct sys_lock lock = sys_mutex_lock_e(&G.user_sim_cmd_mutex);
user_cmd_ss->ack = G.user_sim_cmd_ack;
user_cmd_ss->producer_client = user_client->handle;
user_cmd_ss->producer_client_is_local = true;
user_cmd_ss->control = G.user_sim_cmd_control;
user_client->ack = G.user_sim_cmd_ack;
user_client->reverse_ack = step_tick;
++G.user_sim_cmd_gen;
sys_mutex_unlock(&lock);
}
@ -1810,7 +1779,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
}
} break;
case HOST_EVENT_KIND_CHANNEL_MSG:
case HOST_EVENT_KIND_MSG:
{
if (client->valid) {
struct bitbuff msg_bb = bitbuff_from_string(event->msg);
@ -1869,6 +1838,8 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
}
}
} break;
default: break;
}
}
}
@ -1882,7 +1853,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
u64 ack = client->ack;
u64 reverse_ack = client->reverse_ack;
if (reverse_ack > 1) {
sim_client_release_snapshot_ticks_in_range(client, 0, reverse_ack - 1);
sim_snapshot_release_ticks_in_range(client, 0, reverse_ack - 1);
}
if (ack < oldest_client_ack || oldest_client_ack == 0) {
oldest_client_ack = ack;
@ -1890,7 +1861,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
}
}
if (oldest_client_ack > 1) {
sim_client_release_snapshot_ticks_in_range(local_client, 0, oldest_client_ack - 1);
sim_snapshot_release_ticks_in_range(local_client, 0, oldest_client_ack - 1);
}
/* Pick client snapshot cmds to feed to sim step */
@ -1916,13 +1887,22 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
if (is_master) {
struct sim_snapshot *prev_step_ss = sim_snapshot_from_tick(local_client, step_tick - 1);
step_ss = sim_snapshot_alloc(local_client, prev_step_ss, step_tick);
step_ss->is_master = true;
} else {
struct sim_snapshot *newest_master_ss = sim_snapshot_from_tick(master_client, master_client->last_tick);
step_ss = sim_snapshot_alloc(local_client, newest_master_ss, step_tick);
step_ss->is_master = false;
}
/* Step */
sim_step(step_ss, client_step_cmds, step_dt_ns);
{
struct sim_step_ctx step_ctx = ZI;
step_ctx.accel = &accel;
step_ctx.world = step_ss;
step_ctx.cmd_snapshots = &client_step_cmds;
step_ctx.real_dt_ns = step_dt_ns;
sim_step(&step_ctx);
}
/* Publish snapshot to clients */
for (u64 i = 0; i < store->num_clients_reserved; ++i) {
@ -1959,13 +1939,13 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
case SIM_CLIENT_KIND_MASTER_SIM:
{
u64 ack = client->last_tick;
u64 base_tick = client->last_tick;
struct sim_snapshot *base_ss = sim_snapshot_from_tick(client, base_tick);
if (base_ss->tick == base_tick) {
u64 ack = client->last_tick;
u64 reverse_ack = client->ack;
bw_write_uv(&bw, ack);
bw_write_uv(&bw, reverse_ack);
bw_write_uv(&msg_bw, ack);
bw_write_uv(&msg_bw, reverse_ack);
/* Write all user cmd snapshots since last client ack */
struct sim_snapshot *send_ss = sim_snapshot_from_tick(user_client, base_ss->tick + 1);
while (send_ss->valid) {
@ -1973,6 +1953,11 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
bw_write_uv(&snapshot_bw, base_ss->tick);
bw_write_uv(&snapshot_bw, step_ss->tick);
sim_snapshot_encode(&snapshot_bw, client, base_ss, step_ss);
struct string encoded_tmp = ZI;
encoded_tmp.len = bw_num_bytes_written(&snapshot_bw);
encoded_tmp.text = bw_get_written_raw(&snapshot_bw);
bw_write_uv(&msg_bw, encoded_tmp.len);
bw_write_bytes(&msg_bw, encoded_tmp);
}
@ -1986,12 +1971,14 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
case SIM_CLIENT_KIND_USER:
{
/* TODO: Double buffer */
struct sys_lock lock = sys_mutex_lock_e(&G.local_to_user_snapshot_mutex);
struct sim_snapshot *pub_ss = sim_snapshot_alloc(G.local_to_user_snapshot_client, step_ss, step_ss->tick);
struct sys_lock lock = sys_mutex_lock_e(&G.local_sim_to_user_mutex);
struct sim_snapshot *pub_ss = sim_snapshot_alloc(G.local_sim_to_user_client, step_ss, step_ss->tick);
pub_ss->local_client = client->handle;
i64 publish_ns = sys_time_ns();
pub_ss->publish_dt_ns = publish_ns - last_publish_ns;
pub_ss->publish_time_ns = publish_ns;
last_publish_ns = publish_ns;
sim_snapshot_release_ticks_in_range(G.local_sim_to_user_client, 0, step_ss->tick - 1);
sys_mutex_unlock(&lock);
} break;
@ -2005,316 +1992,9 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
scratch_end(scratch);
}
sim_snapshot_store_release(store);
sim_client_store_release(store);
sim_accel_release(&accel);
bitbuff_release(&snapshot_writer_bb);
bitbuff_release(&msg_writer_bb);
host_release(host);
}
#else
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
{
#if 0
struct host_listen_address local_listen_addr = host_listen_address_from_local_name(LIT("LOCAL_SIM"));
struct host_listen_address net_listen_addr = host_listen_address_from_net_port(12345);
//struct host *host = host_alloc();
/* TODO: Host system should allocate & copy string stored in local_listen_addr */
//host_listen(host, local_listen_addr);
//host_listen(host, net_listen_addr);
#else
struct host *host = host_alloc(12345);
#endif
(UNUSED)arg;
b32 is_master = false;
if (G.connect_address_str.len > 0) {
struct sock_address addr = sock_address_from_string(G.connect_address_str);
host_queue_connect_to_address(host, addr);
} else {
is_master = true;
}
struct bitbuff encoder_bitbuff = bitbuff_alloc(GIGABYTE(64));
struct sim_snapshot_store *local_snapshot_store = sim_snapshot_store_alloc();
struct sim_snapshot_store *remote_snapshot_store = sim_snapshot_store_alloc();
u64 oldest_needed_remote_tick = 0;
u64 remote_ack = 0;
(UNUSED)remote_ack;
struct sim_snapshot *local_ss_prev = sim_snapshot_nil();
i64 last_publish_ns = 0;
i64 last_tick_ns = 0;
i64 target_dt_ns = NS_FROM_SECONDS(1) / SIM_TICKS_PER_SECOND;
while (!atomic_i32_eval(&G.local_sim_thread_shutdown)) {
struct temp_arena scratch = scratch_begin_no_conflict();
{
__profscope(local_sim_sleep);
sleep_frame(last_tick_ns, target_dt_ns);
last_tick_ns = sys_time_ns();
}
/* Release old snapshots */
/* TODO: Remove this */
{
u64 keep_count = 100;
u64 keep_tick = local_ss_prev->tick > keep_count ? local_ss_prev->tick - keep_count : 0;
if (keep_tick > 0) {
sim_snapshot_store_release_ticks_in_range(local_snapshot_store, 0, keep_tick);
}
}
/* Retrieve cmds */
struct sim_cmd_frame_list input_cmds = ZI;
struct sim_cmd_frame *user_cmd_frame;
{
/* Grab snapshots from host */
{
host_update(host);
struct host_event_array host_events = host_pop_events(scratch.arena, host);
sim_cmd_frames_decode(scratch.arena, host_events, &input_cmds);
}
/* Generate user sim cmd from user thread */
{
struct sys_lock lock = sys_mutex_lock_e(&G.user_sim_cmd_mutex);
user_cmd_frame = arena_push_zero(scratch.arena, struct sim_cmd_frame);
user_cmd_frame->tick = local_ss_prev->tick + 1;
user_cmd_frame->ack = G.user_sim_cmd_ack;
user_cmd_frame->sender_is_local = true;
struct sim_cmd *user_cmd = arena_push_zero(scratch.arena, struct sim_cmd);
user_cmd->kind = SIM_CMD_KIND_CLIENT_CONTROL;
user_cmd->control = G.user_sim_cmd_control;
user_cmd_frame->first = user_cmd;
user_cmd_frame->last = user_cmd;
++G.user_sim_cmd_gen;
sys_mutex_unlock(&lock);
}
if (input_cmds.last) {
input_cmds.last->next = user_cmd_frame;
} else {
input_cmds.first = user_cmd_frame;
}
input_cmds.last = user_cmd_frame;
}
for (struct sim_cmd_frame *frame = input_cmds.first; frame; frame = frame->next) {
/* FIXME: Verify cmd is from master */
if (!is_master && !frame->sender_is_local) {
struct sim_cmd *snapshot_cmd = NULL;
/* Grab newest snapshot cmd */
for (struct sim_cmd *cmd = frame->first; cmd; cmd = cmd->next) {
if (cmd->kind == SIM_CMD_KIND_SNAPSHOT && (!snapshot_cmd || cmd->snapshot_tick_end > snapshot_cmd->snapshot_tick_end)) {
snapshot_cmd = cmd;
}
}
/* Decode newest snapshot cmd */
if (snapshot_cmd && snapshot_cmd->snapshot_tick_end > remote_snapshot_store->last_tick) {
remote_ack = frame->ack;
u64 ss0_tick = snapshot_cmd->snapshot_tick_start;
u64 ss1_tick = snapshot_cmd->snapshot_tick_end;
struct sim_snapshot *ss0 = sim_snapshot_from_tick(remote_snapshot_store, ss0_tick);
struct sim_snapshot *ss1 = sim_snapshot_from_tick(remote_snapshot_store, ss1_tick);
if (ss0->tick == ss0_tick) {
if (!ss1->valid) {
/* Decode remote snapshot */
ss1 = sim_snapshot_alloc(remote_snapshot_store, ss0, ss1_tick);
struct bitbuff bb = bitbuff_from_string(snapshot_cmd->snapshot_encoded);
struct bitbuff_reader br = br_from_bitbuff(&bb);
sim_snapshot_decode(&br, ss1);
if (ss0->tick > oldest_needed_remote_tick) {
oldest_needed_remote_tick = ss0->tick;
}
/* Set master client channel id */
for (u64 i = 0; i < ss1->num_clients_reserved; ++i) {
struct sim_client *client = &ss1->clients[i];
if (client->valid && client->kind == SIM_CLIENT_KIND_SIM_MASTER) {
sim_client_set_channel_id(client, frame->sender_channel);
}
}
local_ss_prev = sim_snapshot_alloc(local_snapshot_store, ss1, ss1_tick + 20);
local_ss_prev->is_master = is_master;
}
} else {
/* User should always have src tick present */
ASSERT(false);
}
}
}
}
/* Release old remote ticks */
if (oldest_needed_remote_tick > 0) {
sim_snapshot_store_release_ticks_in_range(remote_snapshot_store, 0, oldest_needed_remote_tick - 1);
}
/* Step */
struct sim_snapshot *local_ss = sim_step(local_snapshot_store, local_ss_prev, input_cmds, target_dt_ns);
local_ss->is_master = is_master;
/* Publish snapshot to user */
/* TODO: Double buffer */
{
struct sys_lock lock = sys_mutex_lock_e(&G.local_sim_ss_mutex);
struct sim_snapshot *pub_ss = sim_snapshot_alloc(G.local_sim_ss_store, local_ss, local_ss->tick);
sim_snapshot_store_release_ticks_in_range(G.local_sim_ss_store, 0, pub_ss->tick - 1);
i64 publish_ns = sys_time_ns();
pub_ss->publish_dt_ns = publish_ns - last_publish_ns;
pub_ss->publish_time_ns = publish_ns;
last_publish_ns = publish_ns;
sys_mutex_unlock(&lock);
}
if (local_ss->is_master) {
/* Publish snapshot cmds to slave clients */
u64 oldest_ack_tick = 0;
for (u64 i = 0; i < local_ss->num_clients_reserved; ++i) {
struct sim_client *client = &local_ss->clients[i];
if (client->valid && client->kind == SIM_CLIENT_KIND_SIM_SLAVE) {
struct temp_arena temp = arena_temp_begin(scratch.arena);
if (oldest_ack_tick == 0 || client->ack < oldest_ack_tick) {
oldest_ack_tick = client->ack;
}
struct sim_snapshot *ss0 = sim_snapshot_from_tick(local_snapshot_store, client->ack);
struct sim_snapshot *ss1 = local_ss;
/* Create & encode snapshot cmd */
struct sim_cmd snapshot_cmd = ZI;
{
snapshot_cmd.kind = SIM_CMD_KIND_SNAPSHOT;
snapshot_cmd.snapshot_tick_start = ss0->tick;
snapshot_cmd.snapshot_tick_end = ss1->tick;
{
struct bitbuff_writer bw = bw_from_bitbuff(&encoder_bitbuff);
sim_snapshot_encode(&bw, client, ss0, ss1);
snapshot_cmd.snapshot_encoded = bw_get_written(temp.arena, &bw);
}
}
struct sim_cmd_frame snapshot_cmd_frame = ZI;
snapshot_cmd_frame.tick = ss1->tick;
snapshot_cmd_frame.ack = client->ack;
snapshot_cmd_frame.first = &snapshot_cmd;
snapshot_cmd_frame.last = &snapshot_cmd;
struct sim_cmd_frame_list cmd_frames = ZI;
cmd_frames.first = &snapshot_cmd_frame;
cmd_frames.last = &snapshot_cmd_frame;
/* Encode cmds */
struct string cmds_msg = ZI;
{
struct bitbuff_writer bw = bw_from_bitbuff(&encoder_bitbuff);
sim_cmd_frames_encode(&bw, cmd_frames);
cmds_msg = bw_get_written(temp.arena, &bw);
}
host_queue_write(host, client->channel_id, cmds_msg, 0);
arena_temp_end(temp);
}
}
} else {
/* Publish user cmd to master client */
for (u64 i = 0; i < local_ss->num_clients_reserved; ++i) {
struct sim_client *client = &local_ss->clients[i];
if (client->valid && client->kind == SIM_CLIENT_KIND_SIM_MASTER) {
struct temp_arena temp = arena_temp_begin(scratch.arena);
user_cmd_frame->ack = remote_snapshot_store->last_tick;
user_cmd_frame->sender_is_local = false;
struct sim_cmd_frame_list l = ZI;
l.first = user_cmd_frame;
l.last = user_cmd_frame;
/* Encode cmds */
struct string cmds_msg = ZI;
{
struct bitbuff_writer bw = bw_from_bitbuff(&encoder_bitbuff);
/* FIXME: Ack tick */
sim_cmd_frames_encode(&bw, l);
cmds_msg = bw_get_written(temp.arena, &bw);
}
host_queue_write(host, client->channel_id, cmds_msg, 0);
arena_temp_end(temp);
}
}
}
/* Send host messages */
host_update(host);
__profframe("Local sim");
{
/* Update network usage stats */
i64 stat_now_ns = sys_time_ns();
G.client_bytes_read.last_second_end = host->bytes_received;
G.client_bytes_sent.last_second_end = host->bytes_sent;
if (stat_now_ns - G.last_second_reset_ns > NS_FROM_SECONDS(1)) {
G.last_second_reset_ns = stat_now_ns;
G.client_bytes_read.last_second = G.client_bytes_read.last_second_end - G.client_bytes_read.last_second_start;
G.client_bytes_sent.last_second = G.client_bytes_sent.last_second_end - G.client_bytes_sent.last_second_start;
G.client_bytes_read.last_second_start = G.client_bytes_read.last_second_end;
G.client_bytes_sent.last_second_start = G.client_bytes_sent.last_second_end;
}
}
scratch_end(scratch);
if (local_ss->is_master) {
local_ss_prev = local_ss;
}
}
sim_snapshot_store_release(local_snapshot_store);
bitbuff_release(&encoder_bitbuff);
host_release(host);
}
#endif

View File

@ -12,7 +12,7 @@ struct sound_startup_receipt;
struct mixer_startup_receipt;
struct phys_startup_receipt;
struct host_startup_receipt;
struct sim_snapshot_startup_receipt;
struct sim_startup_receipt;
enum user_bind_kind {
USER_BIND_KIND_NONE,
@ -61,7 +61,7 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
struct mixer_startup_receipt *mixer_sr,
struct phys_startup_receipt *phys_sr,
struct host_startup_receipt *host_sr,
struct sim_snapshot_startup_receipt *sim_snapshot_sr,
struct sim_startup_receipt *sim_sr,
struct string connect_address_str,
struct sys_window *window);