remove embedded noise data, mix bits for determinism instead

This commit is contained in:
jacob 2025-02-27 08:18:14 -06:00
parent bd693ac1eb
commit 48999f5ab4
11 changed files with 52 additions and 168 deletions

BIN
res/noise.dat (Stored with Git LFS)

Binary file not shown.

View File

@ -322,7 +322,6 @@ void app_entry_point(struct string args_str)
struct sock_startup_receipt sock_sr = sock_startup(); struct sock_startup_receipt sock_sr = sock_startup();
struct host_startup_receipt host_sr = host_startup(&sock_sr); struct host_startup_receipt host_sr = host_startup(&sock_sr);
struct resource_startup_receipt resource_sr = resource_startup(); struct resource_startup_receipt resource_sr = resource_startup();
struct rng_startup_receipt rng_sr = rng_startup(&resource_sr);
struct renderer_startup_receipt renderer_sr = renderer_startup(&window); struct renderer_startup_receipt renderer_sr = renderer_startup(&window);
struct work_startup_receipt work_sr = work_startup(worker_count); struct work_startup_receipt work_sr = work_startup(worker_count);
struct asset_cache_startup_receipt asset_cache_sr = asset_cache_startup(&work_sr); struct asset_cache_startup_receipt asset_cache_sr = asset_cache_startup(&work_sr);
@ -339,7 +338,6 @@ void app_entry_point(struct string args_str)
(UNUSED)user_sr; (UNUSED)user_sr;
(UNUSED)playback_sr; (UNUSED)playback_sr;
(UNUSED)rng_sr;
/* Show window */ /* Show window */
sys_window_show(&window); sys_window_show(&window);

101
src/rng.c
View File

@ -1,111 +1,26 @@
#include "rng.h" #include "rng.h"
#include "sys.h" #include "sys.h"
#include "resource.h"
#include "arena.h"
GLOBAL struct { u32 rng_true_u32(void)
struct arena arena;
u64 *noise;
u64 noise_count;
} G = ZI, DEBUG_ALIAS(G, G_rng);
/* ========================== *
* Generate random number file
* (unused function)
* ========================== */
#if 0
INTERNAL void gen_random_file(struct string path, u32 count)
{
{
/* Clear file */
struct sys_file f = sys_file_open_write(path);
sys_file_write(f, STRING(0, 0));
sys_file_close(f);
}
struct sys_file f = sys_file_open_append(path);
for (u32 i = 0; i < count; ++i) {
u64 rand = rng_rand_u64();
sys_file_write(f, STRING_FROM_STRUCT(&rand));
}
sys_file_close(f);
}
#endif
/* ========================== *
* Startup
* ========================== */
struct rng_startup_receipt rng_startup(struct resource_startup_receipt *resource_sr)
{
(UNUSED)resource_sr;
struct string noise_path = LIT("res/noise.dat");
if (resource_exists(noise_path)) {
struct resource r = resource_open(noise_path);
G.noise_count = r.data.len / sizeof(*G.noise);
G.arena = arena_alloc(sizeof(u64) * G.noise_count);
G.noise = arena_push_array(&G.arena, u64, G.noise_count);
MEMCPY(G.noise, r.data.text, r.data.len);
resource_close(r);
} else {
sys_panic(LIT("Failed to locate pre-computed noise resource"));
}
return (struct rng_startup_receipt) { 0 };
}
/* ========================== *
* Rand
* ========================== */
u32 rng_rand_u32(void)
{ {
u32 v = 0; u32 v = 0;
sys_rand(STRING_FROM_STRUCT(&v)); sys_true_rand(STRING_FROM_STRUCT(&v));
return v; return v;
} }
u64 rng_rand_u64(void) u64 rng_true_u64(void)
{ {
u64 v = 0; u64 v = 0;
sys_rand(STRING_FROM_STRUCT(&v)); sys_true_rand(STRING_FROM_STRUCT(&v));
return v; return v;
} }
f32 rng_rand_f32(f32 range_start, f32 range_end) f32 rng_true_f32(f32 range_start, f32 range_end)
{ {
return ((f32)rng_rand_u32() / (f32)U32_MAX) * (range_end - range_start) + range_start; return ((f32)rng_true_u32() / (f32)U32_MAX) * (range_end - range_start) + range_start;
} }
f64 rng_rand_f64(f64 range_start, f64 range_end) f64 rng_true_f64(f64 range_start, f64 range_end)
{ {
return ((f64)rng_rand_u64() / (f64)U64_MAX) * (range_end - range_start) + range_start; return ((f64)rng_true_u64() / (f64)U64_MAX) * (range_end - range_start) + range_start;
}
/* ========================== *
* Noise
* Functions that return a deterministic pre-computed random number based on the provided seed
*
* NOTE: Noise pattern repeats after period depending on how much noise data exists in noise.dat
* ========================== */
/* TODO: Use deterministic prng rather than embedded data */
u32 rng_noise_u32(u64 seed)
{
return (u32)G.noise[seed % G.noise_count];
}
u64 rng_noise_u64(u64 seed)
{
return G.noise[seed % G.noise_count];
}
f32 rng_noise_f32(u64 seed, f32 range_start, f32 range_end)
{
return ((f32)rng_noise_u32(seed) / (f32)U32_MAX) * (range_end - range_start) + range_start;
}
f32 rng_noise_f64(u64 seed, f64 range_start, f64 range_end)
{
return ((f64)rng_noise_u64(seed) / (f64)U64_MAX) * (range_end - range_start) + range_start;
} }

View File

@ -1,22 +1,10 @@
#ifndef RNG_H #ifndef RNG_H
#define RNG_H #define RNG_H
struct resource_startup_receipt;
/* Startup */
struct rng_startup_receipt { i32 _; };
struct rng_startup_receipt rng_startup(struct resource_startup_receipt *resource_sr);
/* Rand */ /* Rand */
u32 rng_rand_u32(void); u32 rng_true_u32(void);
u64 rng_rand_u64(void); u64 rng_true_u64(void);
f32 rng_rand_f32(f32 range_start, f32 range_end); f32 rng_true_f32(f32 range_start, f32 range_end);
f64 rng_rand_f64(f64 range_start, f64 range_end); f64 rng_true_f64(f64 range_start, f64 range_end);
/* Noise */
u32 rng_noise_u32(u64 seed);
u64 rng_noise_u64(u64 seed);
f32 rng_noise_f32(u64 seed, f32 range_start, f32 range_end);
f32 rng_noise_f64(u64 seed, f64 range_start, f64 range_end);
#endif #endif

View File

@ -381,20 +381,6 @@ INLINE b32 sim_ent_is_valid_and_active(struct sim_ent *ent)
return ent->valid && sim_ent_has_prop(ent, SIM_ENT_PROP_ACTIVE); return ent->valid && sim_ent_has_prop(ent, SIM_ENT_PROP_ACTIVE);
} }
#if 0
INLINE b32 sim_ent_should_simulate(struct sim_ent *ent)
{
b32 res = false;
if (sim_ent_is_valid_and_active(ent)) {
res = true;
if (sim_ent_has_prop(ent, SIM_ENT_PROP_SYNC_DST) && !sim_ent_has_prop(ent, SIM_ENT_PROP_SYNC_PREDICT)) {
res = false;
}
}
return res;
}
#else
INLINE b32 sim_ent_should_predict(struct sim_ent *ent) INLINE b32 sim_ent_should_predict(struct sim_ent *ent)
{ {
return sim_ent_id_eq(ent->predictor, ent->ss->local_client_ent); return sim_ent_id_eq(ent->predictor, ent->ss->local_client_ent);
@ -415,7 +401,6 @@ INLINE b32 sim_ent_should_simulate(struct sim_ent *ent)
return res; return res;
} }
#endif
/* ========================== * /* ========================== *
* Ent functions * Ent functions
* ========================== */ * ========================== */

View File

@ -51,14 +51,15 @@ void sim_accel_reset(struct sim_snapshot *ss, struct sim_accel *accel)
/* TODO: Remove this */ /* TODO: Remove this */
INTERNAL void spawn_test_entities(struct sim_snapshot *world, struct v2 offset) INTERNAL void spawn_test_entities(struct sim_step_ctx *ctx, struct v2 offset)
{ {
struct sim_snapshot *world = ctx->world;
struct sim_ent *root = sim_ent_from_id(world, SIM_ENT_ROOT_ID); struct sim_ent *root = sim_ent_from_id(world, SIM_ENT_ROOT_ID);
root->mass_unscaled = F32_INFINITY; root->mass_unscaled = F32_INFINITY;
root->inertia_unscaled = F32_INFINITY; root->inertia_unscaled = F32_INFINITY;
/* Enemy */ /* Enemy */
{ if (ctx->is_master) {
struct sim_ent *e = sim_ent_alloc_sync_src(root); struct sim_ent *e = sim_ent_alloc_sync_src(root);
struct v2 pos = V2(1, -2); struct v2 pos = V2(1, -2);
@ -73,7 +74,7 @@ INTERNAL void spawn_test_entities(struct sim_snapshot *world, struct v2 offset)
e->layer = SIM_LAYER_SHOULDERS; e->layer = SIM_LAYER_SHOULDERS;
sim_ent_enable_prop(e, SIM_ENT_PROP_PHYSICAL_DYNAMIC); sim_ent_enable_prop(e, SIM_ENT_PROP_PHYSICAL_DYNAMIC);
e->mass_unscaled = 10; e->mass_unscaled = 1;
e->inertia_unscaled = 10; e->inertia_unscaled = 10;
e->linear_ground_friction = 250; e->linear_ground_friction = 250;
e->angular_ground_friction = 200; e->angular_ground_friction = 200;
@ -127,8 +128,9 @@ INTERNAL void spawn_test_entities(struct sim_snapshot *world, struct v2 offset)
#endif #endif
} }
INTERNAL struct sim_ent *spawn_test_player(struct sim_snapshot *world) INTERNAL struct sim_ent *spawn_test_player(struct sim_step_ctx *ctx)
{ {
struct sim_snapshot *world = ctx->world;
struct sim_ent *root = sim_ent_from_id(world, SIM_ENT_ROOT_ID); struct sim_ent *root = sim_ent_from_id(world, SIM_ENT_ROOT_ID);
/* Player */ /* Player */
@ -226,8 +228,9 @@ INTERNAL struct sim_ent *spawn_test_player_camera(struct sim_snapshot *world, st
return camera_ent; return camera_ent;
} }
INTERNAL void test_clear_level(struct sim_snapshot *world) INTERNAL void test_clear_level(struct sim_step_ctx *ctx)
{ {
struct sim_snapshot *world = ctx->world;
for (u64 j = 0; j < world->num_ents_reserved; ++j) { for (u64 j = 0; j < world->num_ents_reserved; ++j) {
struct sim_ent *ent = &world->ents[j]; struct sim_ent *ent = &world->ents[j];
if (ent->valid) { if (ent->valid) {
@ -296,7 +299,7 @@ INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, collision_data_array, st
/* Create test blood */ /* Create test blood */
/* TODO: Remove this */ /* TODO: Remove this */
{ {
struct xform xf = XFORM_TRS(.t = point, .r = rng_rand_f32(0, TAU)); struct xform xf = XFORM_TRS(.t = point, .r = rng_true_f32(0, TAU));
struct sim_ent *decal = sim_ent_alloc_sync_src(root); struct sim_ent *decal = sim_ent_alloc_sync_src(root);
decal->sprite = sprite_tag_from_path(LIT("res/graphics/blood.ase")); decal->sprite = sprite_tag_from_path(LIT("res/graphics/blood.ase"));
decal->sprite_tint = RGBA_32_F(1, 1, 1, 0.25f); decal->sprite_tint = RGBA_32_F(1, 1, 1, 0.25f);
@ -305,10 +308,10 @@ INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, collision_data_array, st
f32 perp_range = 0.5; f32 perp_range = 0.5;
struct v2 linear_velocity = v2_mul(normal, 0.5); struct v2 linear_velocity = v2_mul(normal, 0.5);
linear_velocity = v2_add(linear_velocity, v2_mul(v2_perp(normal), rng_rand_f32(-perp_range, perp_range))); linear_velocity = v2_add(linear_velocity, v2_mul(v2_perp(normal), rng_true_f32(-perp_range, perp_range)));
f32 angular_velocity_range = 5; f32 angular_velocity_range = 5;
f32 angular_velocity = rng_rand_f32(-angular_velocity_range, angular_velocity_range); f32 angular_velocity = rng_true_f32(-angular_velocity_range, angular_velocity_range);
sim_ent_enable_prop(decal, SIM_ENT_PROP_PHYSICAL_KINEMATIC); sim_ent_enable_prop(decal, SIM_ENT_PROP_PHYSICAL_KINEMATIC);
sim_ent_set_linear_velocity(decal, linear_velocity); sim_ent_set_linear_velocity(decal, linear_velocity);
@ -509,7 +512,6 @@ void sim_step(struct sim_step_ctx *ctx)
} }
} }
/* Dereference hovered ent */
client_ent->client_hovered_ent = cmd_ent->cmd_hovered_ent; client_ent->client_hovered_ent = cmd_ent->cmd_hovered_ent;
u32 flags = control->flags; u32 flags = control->flags;
@ -523,8 +525,8 @@ void sim_step(struct sim_step_ctx *ctx)
} }
} }
if (flags & SIM_CONTROL_FLAG_CLEAR_ALL) { if (flags & SIM_CONTROL_FLAG_CLEAR_ALL) {
if (!(old_control.flags & SIM_CONTROL_FLAG_CLEAR_ALL)) { if (is_master && !(old_control.flags & SIM_CONTROL_FLAG_CLEAR_ALL)) {
test_clear_level(world); test_clear_level(ctx);
} }
} }
if (flags & SIM_CONTROL_FLAG_SPAWN_TEST) { if (flags & SIM_CONTROL_FLAG_SPAWN_TEST) {
@ -533,7 +535,7 @@ void sim_step(struct sim_step_ctx *ctx)
u32 count = 1; u32 count = 1;
f32 spread = 1; f32 spread = 1;
for (u32 j = 0; j < count; ++j) { for (u32 j = 0; j < count; ++j) {
spawn_test_entities(world, V2(0, (((f32)j / (f32)count) - 0.5) * spread)); spawn_test_entities(ctx, V2(0, (((f32)j / (f32)count) - 0.5) * spread));
} }
} }
} }
@ -554,7 +556,7 @@ void sim_step(struct sim_step_ctx *ctx)
/* FIXME: Ents never released when client disconnects */ /* FIXME: Ents never released when client disconnects */
struct sim_ent *control_ent = sim_ent_from_id(world, ent->client_control_ent); struct sim_ent *control_ent = sim_ent_from_id(world, ent->client_control_ent);
if (!control_ent->valid) { if (!control_ent->valid) {
control_ent = spawn_test_player(world); control_ent = spawn_test_player(ctx);
control_ent->predictor = ent->id; control_ent->predictor = ent->id;
sim_ent_enable_prop(control_ent, SIM_ENT_PROP_CONTROLLED); sim_ent_enable_prop(control_ent, SIM_ENT_PROP_CONTROLLED);
ent->client_control_ent = control_ent->id; ent->client_control_ent = control_ent->id;

View File

@ -442,7 +442,7 @@ struct string sys_get_clipboard_text(struct arena *arena);
* Util * Util
* ========================== */ * ========================== */
void sys_rand(struct string b); void sys_true_rand(struct string b);
u32 sys_num_logical_processors(void); u32 sys_num_logical_processors(void);
void sys_exit(void); void sys_exit(void);
void sys_panic(struct string msg); void sys_panic(struct string msg);

View File

@ -1856,7 +1856,7 @@ struct string sys_get_clipboard_text(struct arena *arena)
* RNG * RNG
* ========================== */ * ========================== */
void sys_rand(struct string b) void sys_true_rand(struct string b)
{ {
BCryptGenRandom(BCRYPT_RNG_ALG_HANDLE, (PUCHAR)b.text, b.len, 0); BCryptGenRandom(BCRYPT_RNG_ALG_HANDLE, (PUCHAR)b.text, b.len, 0);
} }

View File

@ -6,7 +6,7 @@
struct uid uid_rand(void) struct uid uid_rand(void)
{ {
struct uid res = ZI; struct uid res = ZI;
sys_rand(STRING_FROM_STRUCT(&res)); sys_true_rand(STRING_FROM_STRUCT(&res));
return res; return res;
} }

View File

@ -750,11 +750,10 @@ INTERNAL void user_update(void)
i64 frequency_ns = NS_FROM_SECONDS(0.01f); i64 frequency_ns = NS_FROM_SECONDS(0.01f);
f32 shake = ent->shake; f32 shake = ent->shake;
if (shake > 0) { if (shake > 0) {
u64 basis = ent->id.uid.lo; u64 angle_seed0 = ent->id.uid.lo + (u64)(G.ss_blended->sim_time_ns / frequency_ns);
u64 angle_seed0 = basis + (u64)(G.ss_blended->sim_time_ns / frequency_ns);
u64 angle_seed1 = angle_seed0 + 1; u64 angle_seed1 = angle_seed0 + 1;
f32 angle0 = rng_noise_f32(angle_seed0, 0, TAU); f32 angle0 = ((f32)mix64(angle_seed0) / (f32)U64_MAX) * TAU;
f32 angle1 = rng_noise_f32(angle_seed1, 0, TAU); f32 angle1 = ((f32)mix64(angle_seed1) / (f32)U64_MAX) * TAU;
struct v2 vec0 = v2_with_len(v2_from_angle(angle0), shake); struct v2 vec0 = v2_with_len(v2_from_angle(angle0), shake);
/* NOTE: vec1 not completely accurate since shake can change between frames, just a prediction */ /* NOTE: vec1 not completely accurate since shake can change between frames, just a prediction */
@ -2303,11 +2302,11 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
i64 num_predict_ticks = math_round_to_int64(rtt_tick_ratio) + 2; i64 num_predict_ticks = math_round_to_int64(rtt_tick_ratio) + 2;
step_end_tick = step_base_tick + num_predict_ticks; step_end_tick = step_base_tick + num_predict_ticks;
compute_timescale = 1.1; compute_timescale = 1.1;
} else if (cmds_ahead_on_master > 4) { } else if (cmds_ahead_on_master > 3) {
/* Slow down time to bring sim time closer to master time */ /* Slow down simulation rate to bring sim time closer to master time */
compute_timescale = 0.9; compute_timescale = 0.9;
} else if (cmds_ahead_on_master < 2) { } else if (cmds_ahead_on_master < 1) {
/* Speed up time to give master more inputs to work with */ /* Speed up simulation rate to give master more inputs to work with */
compute_timescale = 1.1; compute_timescale = 1.1;
} else { } else {
compute_timescale = 1; compute_timescale = 1;

View File

@ -15,8 +15,6 @@
* Hash utils * Hash utils
* ========================== */ * ========================== */
/* TODO: Replace with better hash functions */
/* FNV-1a parameters for different hash sizes: /* FNV-1a parameters for different hash sizes:
* https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters * https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters
*/ */
@ -32,20 +30,22 @@ INLINE u64 hash_fnv64(u64 seed, struct string s)
return hash; return hash;
} }
#if 0 /* ========================== *
#define HASH_FNV128_BASIS U128(0x6C62272E07BB0142, 0x62B821756295C58D) * Bit mixing
INLINE u128 hash_fnv128(u128 seed, struct string s) * ========================== */
/* Based on Jon Maiga's "mx3"
* (https://jonkagstrom.com/mx3/mx3_rev2.html)
*/
INLINE u64 mix64(u64 x)
{ {
/* FIXME: Verify MSVC version of 128 is same */ x = (x ^ (x >> 32)) * 0xbea225f9eb34556d;
u128 hash = seed; x = (x ^ (x >> 29)) * 0xbea225f9eb34556d;
for (u64 i = 0; i < s.len; ++i) { x = (x ^ (x >> 32)) * 0xbea225f9eb34556d;
u8 c = (u8)s.text[i]; x = (x ^ (x >> 29));
hash = u128_xor_u8(hash, c); return x;
hash = u128_mul(hash, U128(0x1000000, 0x000000000000013B));
}
return hash;
} }
#endif
/* ========================== * /* ========================== *
* Merge sort * Merge sort
@ -119,7 +119,7 @@ INLINE void merge_sort(void *items, u64 item_count, u64 item_size, sort_compare_
/* ========================== * /* ========================== *
* Fixed Dict * Fixed Dict
* *
* Simple fixed bin-count string->value chaining dict for generic use * Simple fixed-bin-count string -> pointer chaining hash table for generic use
* ========================== */ * ========================== */
struct fixed_dict_entry { struct fixed_dict_entry {