prediction progress

This commit is contained in:
jacob 2025-02-25 11:55:07 -06:00
parent 7ab7af81a3
commit 50da5f1bc6
3 changed files with 188 additions and 108 deletions

View File

@ -334,16 +334,21 @@ void sim_step(struct sim_step_ctx *ctx)
__prof;
struct temp_arena scratch = scratch_begin_no_conflict();
b32 is_master = ctx->is_master;
struct sim_snapshot *world = ctx->world;
struct sim_client_store *client_store = world->client->store;
struct sim_client *world_client = world->client;
struct sim_client *user_input_client = ctx->user_input_client;
struct sim_client *publish_client = ctx->publish_client;
struct sim_client *master_client = ctx->master_client;
i64 sim_dt_ns = ctx->sim_dt_ns;
/* ========================== *
* Begin frame
* ========================== */
//sys_sleep_precise(rng_rand_f32(0, 0.050));
//sys_sleep_precise(0.050);
world->sim_dt_ns = max_i64(0, sim_dt_ns);
world->sim_time_ns += world->sim_dt_ns;
f32 sim_dt = SECONDS_FROM_NS(world->sim_dt_ns);
@ -352,6 +357,69 @@ void sim_step(struct sim_step_ctx *ctx)
struct sim_ent *root = sim_ent_from_id(world, SIM_ENT_ROOT_ID);
/* ========================== *
* Sync remote ents
* ========================== */
{
for (u64 client_index = 0; client_index < client_store->num_clients_reserved; ++client_index) {
struct sim_client *client = &client_store->clients[client_index];
if (client->valid && client != world_client && client != publish_client) {
/* Create client ent if necessary */
if (is_master) {
struct sim_ent *client_ent = sim_ent_from_id(world, client->ent_id);
if (!client_ent->valid) {
/* FIXME: Client ent never released upon disconnect */
client_ent = sim_ent_alloc_sync_src(root);
client_ent->client_handle = client->handle;
sim_ent_enable_prop(client_ent, SIM_ENT_PROP_CLIENT);
sim_ent_enable_prop(client_ent, SIM_ENT_PROP_SYNC_PREDICT);
sim_ent_enable_prop(client_ent, SIM_ENT_PROP_ACTIVE);
client->ent_id = client_ent->id;
if (client == user_input_client) {
user_input_client->ent_id = client_ent->id;
world_client->ent_id = client_ent->id;
world->local_client_ent = client_ent->id;
}
}
}
/* Sync ents from client */
{
struct sim_snapshot *src_ss = sim_snapshot_from_tick(client, world->tick);
if (src_ss->valid) {
sim_snapshot_sync(world, src_ss);
if (client == master_client) {
world_client->ent_id = src_ss->local_client_ent;
world->local_client_ent = src_ss->local_client_ent;
}
}
}
}
}
/* Mark incoming cmds with correct client */
for (u64 i = 0; i < world->num_ents_reserved; ++i) {
struct sim_ent *ent = &world->ents[i];
if (ent->valid && sim_ent_has_prop(ent, SIM_ENT_PROP_CMD_CONTROL) && sim_ent_has_prop(ent, SIM_ENT_PROP_SYNC_DST)) {
struct sim_client *src_client = sim_client_from_handle(client_store, ent->sync_src_client);
ent->cmd_client = src_client->ent_id;
}
}
/* Mark any locally created CMDs as sync sources */
if (!is_master) {
for (u64 i = 0; i < world->num_ents_reserved; ++i) {
struct sim_ent *ent = &world->ents[i];
if (sim_ent_is_valid_and_active(ent) && sim_ent_has_prop(ent, SIM_ENT_PROP_CMD_CONTROL)) {
if (!sim_ent_id_eq(ent->cmd_client, SIM_ENT_NIL_ID) && sim_ent_id_eq(ent->cmd_client, world->local_client_ent)) {
sim_ent_enable_prop(ent, SIM_ENT_PROP_SYNC_SRC);
}
}
}
}
}
/* ========================== *
* Release entities at beginning of frame
* ========================== */
@ -403,7 +471,7 @@ void sim_step(struct sim_step_ctx *ctx)
if (sim_ent_has_prop(cmd_ent, SIM_ENT_PROP_CMD_CONTROL)) {
struct sim_ent *client_ent = sim_ent_from_id(world, cmd_ent->cmd_client);
if (sim_ent_should_simulate(client_ent)) {
if (!ctx->is_master && !sim_ent_id_eq(client_ent->id, world->local_client_ent)) {
if (!is_master && !sim_ent_id_eq(client_ent->id, world->local_client_ent)) {
/* We are not the master and the command is not our own, skip processing */
continue;
}
@ -468,7 +536,7 @@ void sim_step(struct sim_step_ctx *ctx)
* Create client player ents
* ========================== */
if (ctx->is_master) {
if (is_master) {
for (u64 i = 0; i < world->num_ents_reserved; ++i) {
struct sim_ent *ent = &world->ents[i];
if (!sim_ent_should_simulate(ent)) continue;
@ -778,7 +846,7 @@ void sim_step(struct sim_step_ctx *ctx)
if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTROLLED)) {
struct sim_ent *joint_ent = sim_ent_from_id(world, ent->move_joint);
if (ctx->is_master && !sim_ent_is_valid_and_active(joint_ent)) {
if (is_master && !sim_ent_is_valid_and_active(joint_ent)) {
joint_ent = sim_ent_alloc_sync_src(root);
joint_ent->mass_unscaled = F32_INFINITY;
joint_ent->inertia_unscaled = F32_INFINITY;
@ -821,7 +889,7 @@ void sim_step(struct sim_step_ctx *ctx)
/* Retrieve / create aim joint */
struct sim_ent *joint_ent = sim_ent_from_id(world, ent->aim_joint);
if (ctx->is_master && !sim_ent_is_valid_and_active(joint_ent)) {
if (is_master && !sim_ent_is_valid_and_active(joint_ent)) {
joint_ent = sim_ent_alloc_sync_src(root);
joint_ent->mass_unscaled = F32_INFINITY;
joint_ent->inertia_unscaled = F32_INFINITY;
@ -925,7 +993,7 @@ void sim_step(struct sim_step_ctx *ctx)
def.max_force = ent->linear_ground_friction;
def.max_torque = ent->angular_ground_friction;
if (joint_ent->motor_joint_data.max_force != def.max_force || joint_ent->motor_joint_data.max_torque != def.max_torque) {
if (ctx->is_master && !sim_ent_is_valid_and_active(joint_ent)) {
if (is_master && !sim_ent_is_valid_and_active(joint_ent)) {
joint_ent = sim_ent_alloc_sync_src(root);
sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_MOTOR_JOINT);
sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_ACTIVE);
@ -943,7 +1011,7 @@ void sim_step(struct sim_step_ctx *ctx)
* Create mouse joints from client debug drag
* ========================== */
if (ctx->is_master) {
if (is_master) {
for (u64 i = 0; i < world->num_ents_reserved; ++i) {
struct sim_ent *client_ent = &world->ents[i];
if (!sim_ent_should_simulate(client_ent)) continue;
@ -1187,6 +1255,20 @@ void sim_step(struct sim_step_ctx *ctx)
sim_ent_release_all_with_prop(world, SIM_ENT_PROP_RELEASE);
/* ========================== *
* Sync to publish client
* ========================== */
if (publish_client->valid) {
struct sim_snapshot *pub_world = sim_snapshot_from_tick(publish_client, world->tick);
if (!pub_world->valid) {
struct sim_snapshot *prev_pub_world = sim_snapshot_from_tick(publish_client, publish_client->last_tick);
pub_world = sim_snapshot_alloc(publish_client, prev_pub_world, world->tick);
pub_world->local_client_ent = world->local_client_ent;
sim_snapshot_sync(pub_world, world);
}
}
/* ========================== *
* End frame
* ========================== */

View File

@ -27,8 +27,13 @@ void sim_accel_reset(struct sim_snapshot *ss, struct sim_accel *accel);
struct sim_step_ctx {
b32 is_master;
struct sim_accel *accel;
struct sim_snapshot *world;
i64 sim_dt_ns;
struct sim_snapshot *world; /* The world to simulate */
i64 sim_dt_ns; /* How much sim time should progress */
struct sim_client *user_input_client; /* The client that contains input from the user thread */
struct sim_client *master_client; /* The master client to read snapshots from (nil if world is master) */
struct sim_client *publish_client; /* The publish client to write syncable state to (nil if skipping publish) */
};
void sim_step(struct sim_step_ctx *ctx);

View File

@ -1896,7 +1896,22 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
struct sim_client *master_client = sim_client_nil(); /* Stores snapshots received from master (if relevant) */
b32 initialized_from_master = false;
u64 step_tick = 0;
u64 ahead = 0;
if (!is_master) {
ahead = 50;
}
i64 last_publish_ns = 0;
i64 last_tick_ns = 0;
i64 step_dt_ns = NS_FROM_SECONDS(1) / SIM_TICKS_PER_SECOND;
@ -1909,7 +1924,6 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
sleep_frame(last_tick_ns, compute_dt_ns);
last_tick_ns = sys_time_ns();
}
++step_tick;
/* Read net messages */
struct sim_decode_queue queue = ZI;
@ -2046,10 +2060,9 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
if (!is_master && !initialized_from_master) {
if (master_client->valid && master_client->last_tick > 0) {
step_tick = master_client->last_tick;
//step_tick = master_client->last_tick;
initialized_from_master = true;
} else {
step_tick = 0;
goto skip_step;
}
}
@ -2057,7 +2070,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
/* Create user input */
{
struct sim_snapshot *prev_user_input_ss = sim_snapshot_from_tick(user_input_client, user_input_client->last_tick);
struct sim_snapshot *user_input_ss = sim_snapshot_alloc(user_input_client, prev_user_input_ss, step_tick);
struct sim_snapshot *user_input_ss = sim_snapshot_alloc(user_input_client, prev_user_input_ss, local_client->last_tick + ahead + 1);
struct sim_ent *user_input_root = sim_ent_from_id(user_input_ss, SIM_ENT_ROOT_ID);
/* Find / create local control cmd ent */
struct sim_ent *control_cmd_ent = sim_ent_find_first_match_one(user_input_ss, SIM_ENT_PROP_CMD_CONTROL);
@ -2077,61 +2090,15 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
}
}
#if 0
/* Allocate local snapshot */
struct sim_snapshot *prev_local_ss = sim_snapshot_from_tick(local_client, local_client->last_tick);
struct sim_snapshot *local_ss = sim_snapshot_alloc(local_client, prev_local_ss, step_tick);
/* Sync remote ents to local */
{
struct sim_ent *local_root = sim_ent_from_id(local_ss, SIM_ENT_ROOT_ID);
for (u64 client_index = 0; client_index < store->num_clients_reserved; ++client_index) {
struct sim_client *client = &store->clients[client_index];
if (client->valid) {
/* Create client ent if necessary */
if (is_master && client != publish_client && client != local_client && client != master_client) {
struct sim_ent *client_ent = sim_ent_from_id(local_ss, client->ent_id);
if (!client_ent->valid) {
/* FIXME: Client ent never released upon disconnect */
client_ent = sim_ent_alloc_sync_src(local_root);
client_ent->client_handle = client->handle;
sim_ent_enable_prop(client_ent, SIM_ENT_PROP_CLIENT);
sim_ent_enable_prop(client_ent, SIM_ENT_PROP_SYNC_PREDICT);
sim_ent_enable_prop(client_ent, SIM_ENT_PROP_ACTIVE);
client->ent_id = client_ent->id;
if (client == user_input_client) {
user_input_client->ent_id = client_ent->id;
local_client->ent_id = client_ent->id;
local_ss->local_client_ent = client_ent->id;
}
}
}
/* Sync w/ client */
if (client != publish_client && client != local_client) {
struct sim_snapshot *client_ss = sim_snapshot_from_tick(client, step_tick);
if (client_ss->valid) {
sim_snapshot_sync(local_ss, client_ss);
if (!is_master && client == master_client) {
user_input_client->ent_id = client_ss->local_client_ent;
local_client->ent_id = client_ss->local_client_ent;
local_ss->local_client_ent = client_ss->local_client_ent;
}
}
}
}
}
}
/* Point cmds to correct client ents */
for (u64 i = 0; i < local_ss->num_ents_reserved; ++i) {
struct sim_ent *ent = &local_ss->ents[i];
if (ent->valid && sim_ent_has_prop(ent, SIM_ENT_PROP_CMD_CONTROL) && sim_ent_has_prop(ent, SIM_ENT_PROP_SYNC_DST)) {
struct sim_client *src_client = sim_client_from_handle(store, ent->sync_src_client);
struct sim_ent *client_ent = sim_ent_from_id(local_ss, src_client->ent_id);
ent->cmd_client = client_ent->id;
}
for (u64 i = 0; i < ahead; ++i) {
struct sim_snapshot *prev_predict_ss = sim_snapshot_from_tick(local_client, local_ss->tick + i);
sim_snapshot_alloc(local_client, prev_predict_ss, prev_predict_ss->tick + 1);
}
#endif
/* Release unneeded received snapshots */
u64 oldest_client_ack = 0;
@ -2139,7 +2106,8 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
struct sim_client *client = &store->clients[i];
if (client->valid && client != local_client && client != publish_client && client != user_input_client) {
if (client->double_ack > 0) {
u64 keep_tick = max_u64(min_u64(client->double_ack, step_tick), 1);
//u64 keep_tick = max_u64(min_u64(client->double_ack, step_tick), 1);
u64 keep_tick = 1;
sim_snapshot_release_ticks_in_range(client, 0, keep_tick - 1);
}
if (client->ack < oldest_client_ack || oldest_client_ack == 0) {
@ -2160,18 +2128,25 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
sim_snapshot_release_ticks_in_range(publish_client, 0, keep_tick);
}
/* Release unneeded user input snapshots */
sim_snapshot_release_ticks_in_range(user_input_client, 0, step_tick - 1);
/* Release old local snapshots */
{
u64 keep_range = 100;
u64 keep_range = 500;
if (local_client->last_tick > keep_range) {
u64 keep_tick = local_client->last_tick - keep_range;
sim_snapshot_release_ticks_in_range(local_client, 0, keep_tick);
}
}
/* Release unneeded user input snapshots */
sim_snapshot_release_ticks_in_range(user_input_client, 0, local_client->last_tick - 1);
@ -2181,53 +2156,70 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
/* Step */
#if 0
{
struct sim_step_ctx step_ctx = ZI;
step_ctx.is_master = is_master;
step_ctx.accel = &accel;
step_ctx.world = local_ss;
step_ctx.sim_dt_ns = step_dt_ns;
step_ctx.accel = &accel;
step_ctx.user_input_client = user_input_client;
step_ctx.master_client = master_client;
step_ctx.publish_client = publish_client;
if (is_master) {
ctx.prev_world = sim_snapshot_from_tick(local_client, local_client->last_tick);
sim_step(&step_ctx);
} else {
for (u64 i = 0; i < ahead; ++i) {
ctx.prev_world = sim_snapshot_from_tick(local_client, master_client->last_tick + i);
sim_step(&step_ctx);
}
/* Signal any locally created CMDs as sync sources */
for (u64 i = 0; i < local_ss->num_ents_reserved; ++i) {
struct sim_ent *ent = &local_ss->ents[i];
if (sim_ent_is_valid_and_active(ent) && sim_ent_has_prop(ent, SIM_ENT_PROP_CMD_CONTROL)) {
if (!sim_ent_id_eq(ent->cmd_client, SIM_ENT_NIL_ID) && sim_ent_id_eq(ent->cmd_client, local_ss->local_client_ent)) {
sim_ent_enable_prop(ent, SIM_ENT_PROP_SYNC_SRC);
}
}
}
/* Construct publishable snapshot */
#else
{
struct sim_snapshot *pub_ss;
struct sim_step_ctx ctx = ZI;
ctx.is_master = is_master;
ctx.sim_dt_ns = step_dt_ns;
ctx.accel = &accel;
ctx.user_input_client = user_input_client;
ctx.master_client = master_client;
ctx.publish_client = publish_client;
if (is_master) {
pub_ss = sim_snapshot_alloc(publish_client, sim_snapshot_from_tick(publish_client, publish_client->last_tick), local_ss->tick);
u64 prev_tick = local_client->last_tick;
struct sim_snapshot *prev_world = sim_snapshot_from_tick(local_client, prev_tick);
ctx.world = sim_snapshot_alloc(local_client, prev_world, prev_tick + 1);
sim_step(&ctx);
} else {
pub_ss = sim_snapshot_alloc(publish_client, sim_snapshot_from_tick(publish_client, publish_client->last_tick), local_ss->tick + 5);
struct sim_snapshot *master_ss = sim_snapshot_from_tick(master_client, master_client->last_tick);
if (master_ss->valid) {
struct sim_snapshot *prev_world = sim_snapshot_alloc(local_client, master_ss, master_ss->tick);
for (u64 i = 0; i < ahead; ++i) {
ctx.world = sim_snapshot_alloc(local_client, prev_world, prev_world->tick + 1);
sim_step(&ctx);
prev_world = ctx.world;
}
sim_snapshot_sync(pub_ss, local_ss);
}
}
}
#endif
@ -2282,15 +2274,16 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
/* Copy local snapshot to user client */
{
struct sim_snapshot *local_world = sim_snapshot_from_tick(local_client, local_client->last_tick);
/* TODO: Double buffer */
struct sys_lock lock = sys_mutex_lock_e(&G.local_to_user_client_mutex);
struct sim_snapshot *copy_ss = sim_snapshot_alloc(G.local_to_user_client, local_ss, local_ss->tick);
copy_ss->local_client_ent = local_ss->local_client_ent;
struct sim_snapshot *copy_ss = sim_snapshot_alloc(G.local_to_user_client, local_world, local_world->tick);
i64 publish_ns = sys_time_ns();
copy_ss->publish_dt_ns = publish_ns - last_publish_ns;
copy_ss->publish_time_ns = publish_ns;
last_publish_ns = publish_ns;
sim_snapshot_release_ticks_in_range(G.local_to_user_client, 0, local_ss->tick - 1);
sim_snapshot_release_ticks_in_range(G.local_to_user_client, 0, local_world->tick - 1);
sys_mutex_unlock(&lock);
}