diff --git a/src/app.c b/src/app.c index d6615dd0..19df6846 100644 --- a/src/app.c +++ b/src/app.c @@ -31,6 +31,8 @@ struct exit_callback { struct sys_thread thread; }; +struct app_statistics _g_app_statistics = ZI; + GLOBAL struct { struct arena arena; struct string write_path; diff --git a/src/app.h b/src/app.h index 2ea537f0..8a74aae6 100644 --- a/src/app.h +++ b/src/app.h @@ -4,6 +4,19 @@ #define APP_EXIT_CALLBACK_FUNC_DEF(name) void name(void) typedef APP_EXIT_CALLBACK_FUNC_DEF(app_exit_callback_func); +struct app_statistics { + struct atomic_u64 host_bytes_sent; + struct atomic_u64 host_bytes_received; + struct atomic_u64 memory_committed; + struct atomic_u64 memory_reserved; +}; + +INLINE struct app_statistics *app_statistics(void) +{ + extern struct app_statistics _g_app_statistics; + return &_g_app_statistics; +} + struct string app_write_path_cat(struct arena *arena, struct string filename); /* Register a function that will be called when the application exits */ diff --git a/src/arena.c b/src/arena.c index e9732122..d0bc2e9c 100644 --- a/src/arena.c +++ b/src/arena.c @@ -2,6 +2,8 @@ #include "sys.h" #include "memory.h" #include "string.h" +#include "atomic.h" +#include "app.h" /* Arbitrary block size */ #define ARENA_BLOCK_SIZE 4096 @@ -25,16 +27,18 @@ struct arena arena_alloc(u64 reserve) sys_panic(LIT("Failed to reserve memory")); } arena.reserved = reserve; + atomic_u64_eval_add_u64(&app_statistics()->memory_reserved, arena.reserved); /* Commit one block to start with */ arena.base = sys_memory_commit(arena.base, ARENA_BLOCK_SIZE); - __profalloc(arena.base, ARENA_BLOCK_SIZE); - ASAN_POISON(arena.base, ARENA_BLOCK_SIZE); if (!arena.base) { /* Hard fail on commit failure */ sys_panic(LIT("Failed to commit initial memory block: System may be out of memory")); } arena.committed = ARENA_BLOCK_SIZE; + atomic_u64_eval_add_u64(&app_statistics()->memory_committed, arena.committed); + __profalloc(arena.base, ARENA_BLOCK_SIZE); + ASAN_POISON(arena.base, ARENA_BLOCK_SIZE); /* Arena should be 64k aligned */ ASSERT(((u64)arena.base & 0xFFFF) == 0); @@ -44,9 +48,11 @@ struct arena arena_alloc(u64 reserve) void arena_release(struct arena *arena) { + ASAN_UNPOISON(arena->base, arena->committed); __prof; __proffree(arena->base); - ASAN_UNPOISON(arena->base, arena->committed); + atomic_u64_eval_add_i64(&app_statistics()->memory_committed, -((i64)arena->committed)); + atomic_u64_eval_add_i64(&app_statistics()->memory_reserved, -((i64)arena->reserved)); sys_memory_release(arena->base); } @@ -79,10 +85,11 @@ void *arena_push_bytes(struct arena *arena, u64 size, u64 align) /* Hard fail on memory allocation failure for now */ sys_panic(LIT("Failed to commit new memory block: System may be out of memory")); } + arena->committed += commit_bytes; + atomic_u64_eval_add_u64(&app_statistics()->memory_committed, commit_bytes); __proffree(arena->base); __profalloc(arena->base, arena->committed + commit_bytes); ASAN_POISON(commit_address, commit_bytes); - arena->committed += commit_bytes; } start = arena->base + aligned_start_pos; arena->pos = new_pos; @@ -114,6 +121,7 @@ void arena_decommit_unused_blocks(struct arena *arena) u64 decommit_size = (arena->base + arena->committed) - decommit_start; sys_memory_decommit(decommit_start, decommit_size); arena->committed = next_block_pos; + atomic_u64_eval_add_i64(&app_statistics()->memory_committed, -((i64)decommit_size)); } } diff --git a/src/atomic.h b/src/atomic.h index e8cb47e3..50417561 100644 --- a/src/atomic.h +++ b/src/atomic.h @@ -22,7 +22,8 @@ FORCE_INLINE volatile i64 *atomic_i64_raw(struct atomic_i64 *x) { return &x->_v; FORCE_INLINE u32 atomic_u32_eval(struct atomic_u32 *x) { return (u32)_InterlockedExchangeAdd((volatile long *)&x->_v, 0); } FORCE_INLINE u32 atomic_u32_inc_eval(struct atomic_u32 *x) { return (u32)_InterlockedIncrement((volatile long *)&x->_v); } FORCE_INLINE u32 atomic_u32_dec_eval(struct atomic_u32 *x) { return (u32)_InterlockedDecrement((volatile long *)&x->_v); } -FORCE_INLINE u32 atomic_u32_eval_add(struct atomic_u32 *x, u32 a) { return (u32)_InterlockedExchangeAdd((volatile long *)&x->_v, (long)a); } +FORCE_INLINE u32 atomic_u32_eval_add_u32(struct atomic_u32 *x, u32 a) { return (u32)_InterlockedExchangeAdd((volatile long *)&x->_v, (long)a); } +FORCE_INLINE u32 atomic_u32_eval_add_i32(struct atomic_u32 *x, i32 a) { return (u32)_InterlockedExchangeAdd((volatile long *)&x->_v, (long)a); } FORCE_INLINE u32 atomic_u32_eval_exchange(struct atomic_u32 *x, u32 e) { return (u32)_InterlockedExchange((volatile long *)&x->_v, (long)e); } FORCE_INLINE u32 atomic_u32_eval_compare_exchange(struct atomic_u32 *x, u32 c, u32 e) { return (u32)_InterlockedCompareExchange((volatile long *)&x->_v, (long)e, (long)c); } FORCE_INLINE volatile u32 *atomic_u32_raw(struct atomic_u32 *x) { return &x->_v; } @@ -30,7 +31,8 @@ FORCE_INLINE volatile u32 *atomic_u32_raw(struct atomic_u32 *x) { return &x->_v; FORCE_INLINE u64 atomic_u64_eval(struct atomic_u64 *x) { return (u64)_InterlockedOr64((volatile i64 *)&x->_v, 0); } FORCE_INLINE u64 atomic_u64_inc_eval(struct atomic_u64 *x) { return (u64)_InterlockedIncrement64((volatile i64 *)&x->_v); } FORCE_INLINE u64 atomic_u64_dec_eval(struct atomic_u64 *x) { return (u64)_InterlockedDecrement64((volatile i64 *)&x->_v); } -FORCE_INLINE u64 atomic_u64_eval_add(struct atomic_u64 *x, u64 a) { return (u64)_InterlockedExchangeAdd64((volatile i64 *)&x->_v, (i64)a); } +FORCE_INLINE u64 atomic_u64_eval_add_u64(struct atomic_u64 *x, u64 a) { return (u64)_InterlockedExchangeAdd64((volatile i64 *)&x->_v, (i64)a); } +FORCE_INLINE u64 atomic_u64_eval_add_i64(struct atomic_u64 *x, i64 a) { return (u64)_InterlockedExchangeAdd64((volatile i64 *)&x->_v, (i64)a); } FORCE_INLINE u64 atomic_u64_eval_exchange(struct atomic_u64 *x, u64 e) { return (u64)_InterlockedExchange64((volatile i64 *)&x->_v, (i64)e); } FORCE_INLINE u64 atomic_u64_eval_compare_exchange(struct atomic_u64 *x, u64 c, u64 e) { return (u64)_InterlockedCompareExchange64((volatile i64 *)&x->_v, (i64)e, (i64)c); } FORCE_INLINE volatile u64 *atomic_u64_raw(struct atomic_u64 *x) { return &x->_v; } diff --git a/src/game.c b/src/game.c index 73791784..86facce8 100644 --- a/src/game.c +++ b/src/game.c @@ -100,6 +100,7 @@ INTERNAL void reset_world(void) /* Release world */ world_release(&G.tick); /* Release bookkeeping */ + space_release(G.space); #if COLLIDER_DEBUG entity_lookup_release(&G.collision_debug_lookup); #endif diff --git a/src/host.c b/src/host.c index 6fc9e820..2ae02a10 100644 --- a/src/host.c +++ b/src/host.c @@ -6,6 +6,8 @@ #include "util.h" #include "log.h" #include "buddy.h" +#include "app.h" +#include "atomic.h" //#define HOST_NETWORK_ADDRESS_STRING(str) //#define HOST_NETWORK_ADDRESS_ALL_LOCAL_INTERFACES(port) @@ -768,6 +770,8 @@ void host_update(struct host *host) default: break; } } + host->bytes_received += packet->data.len; + atomic_u64_eval_add_u64(&app_statistics()->host_bytes_received, packet->data.len); } } /* Reset read buffer */ @@ -931,15 +935,18 @@ void host_update(struct host *host) for (u64 i = 0; i < host->num_channels_reserved; ++i) { struct sock *sock = host->sock; struct host_channel *channel = &host->channels[i]; + u64 total_sent = 0; if (channel->valid) { struct sock_address address = channel->address; /* Send reliable packets to channel */ - for (struct host_snd_packet *host_packet = channel->first_reliable_packet; host_packet; host_packet = host_packet->next) { - sock_write(sock, address, STRING(host_packet->data_len, host_packet->data)); + for (struct host_snd_packet *packet = channel->first_reliable_packet; packet; packet = packet->next) { + sock_write(sock, address, STRING(packet->data_len, packet->data)); + total_sent += packet->data_len; } /* Send unreliable packets to channel */ - for (struct host_snd_packet *host_packet = channel->first_unreliable_packet; host_packet; host_packet = host_packet->next) { - sock_write(sock, address, STRING(host_packet->data_len, host_packet->data)); + for (struct host_snd_packet *packet = channel->first_unreliable_packet; packet; packet = packet->next) { + sock_write(sock, address, STRING(packet->data_len, packet->data)); + total_sent += packet->data_len; } /* Release unreliable packets */ if (channel->first_unreliable_packet) { @@ -949,6 +956,8 @@ void host_update(struct host *host) channel->last_unreliable_packet = NULL; channel->num_unreliable_packets = 0; } + host->bytes_sent += total_sent; + atomic_u64_eval_add_u64(&app_statistics()->host_bytes_sent, total_sent); } } } diff --git a/src/host.h b/src/host.h index e0248ccd..68daf7a2 100644 --- a/src/host.h +++ b/src/host.h @@ -77,7 +77,7 @@ struct host { struct host_channel *first_free_channel; u64 num_channels_reserved; - struct host_snd_packet *first_free_packet; /* Allocated in `arena` */ + struct host_snd_packet *first_free_packet; /* Allocated in `arena` */ struct host_msg_assembler *first_free_msg_assembler; /* Allocated in `arena` */ struct host_channel_lookup_bucket *channel_lookup_buckets; /* Allocated in `arena` */ @@ -91,6 +91,9 @@ struct host { struct host_rcv_buffer *rcv_buffer_read; struct host_rcv_buffer *rcv_buffer_write; + u64 bytes_received; + u64 bytes_sent; + struct sys_thread receiver_thread; }; diff --git a/src/sprite.c b/src/sprite.c index dca932aa..2fa5e8cb 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -381,7 +381,7 @@ INTERNAL void cache_node_load_texture(struct cache_node *n, struct sprite_tag ta } arena_set_readonly(&n->arena); n->memory_usage = n->arena.committed + memory_size; - atomic_u64_eval_add(&G.cache.memory_usage, n->memory_usage); + atomic_u64_eval_add_u64(&G.cache.memory_usage, n->memory_usage); f64 elapsed = SECONDS_FROM_NS(sys_time_ns() - start_ns); logf_info("Finished loading sprite texture \"%F\" in %F seconds (cache size: %F bytes).", @@ -681,7 +681,7 @@ INTERNAL void cache_node_load_sheet(struct cache_node *n, struct sprite_tag tag) #endif arena_set_readonly(&n->arena); n->memory_usage = n->arena.committed; - atomic_u64_eval_add(&G.cache.memory_usage, n->memory_usage); + atomic_u64_eval_add_u64(&G.cache.memory_usage, n->memory_usage); f64 elapsed = SECONDS_FROM_NS(sys_time_ns() - start_ns); logf_info("Finished loading sprite sheet \"%F\" in %F seconds (cache size: %F bytes).", @@ -1178,7 +1178,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg) if (n->next_hash) { n->next_hash->prev_hash = n->prev_hash; } - atomic_u64_eval_add(&G.cache.memory_usage, -((i64)n->memory_usage)); + atomic_u64_eval_add_i64(&G.cache.memory_usage, -((i64)n->memory_usage)); /* Add to evicted list */ en->next_evicted = head_evicted; head_evicted = en; diff --git a/src/user.c b/src/user.c index 53d6ebca..7504f9da 100644 --- a/src/user.c +++ b/src/user.c @@ -35,6 +35,12 @@ struct blend_tick { struct world world; }; +struct second_stat { + u64 last_second_start; + u64 last_second_end; + u64 last_second; +}; + GLOBAL struct { struct atomic_i32 user_thread_shutdown; struct sys_thread user_thread; @@ -45,6 +51,11 @@ GLOBAL struct { struct host *host; + /* Usage stats */ + i64 last_second_reset_ns; + struct second_stat client_bytes_read; + struct second_stat client_bytes_sent; + /* Render targets */ struct renderer_texture final_texture; struct renderer_texture world_texture; @@ -1608,6 +1619,20 @@ INTERNAL void user_update(void) draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("entities: %F/%F"), FMT_UINT(G.world.entity_store->num_allocated), FMT_UINT(G.world.entity_store->num_reserved))); pos.y += spacing; + pos.y += spacing; + draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Client data read: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_read.last_second * 8 / 1024 / 1024, 2))); + pos.y += spacing; + + draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Client data sent: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_sent.last_second * 8 / 1024 / 1024, 2))); + pos.y += spacing; + + draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Memory committed: %F MiB"), FMT_FLOAT_P((f64)atomic_u64_eval(&app_statistics()->memory_committed) / 1024 / 1024, 2))); + pos.y += spacing; + + draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Memory reserved: %F TiB"), FMT_FLOAT_P((f64)atomic_u64_eval(&app_statistics()->memory_reserved) / 1024 / 1024 / 1024 / 1024, 2))); + pos.y += spacing; + +#if 0 draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("screen_size: (%F, %F)"), FMT_FLOAT((f64)G.screen_size.x), FMT_FLOAT((f64)G.screen_size.y))); pos.y += spacing; @@ -1652,6 +1677,7 @@ INTERNAL void user_update(void) struct v2 player_pos = entity_get_xform(entity_find_first_match_one(store, ENTITY_PROP_PLAYER_CONTROLLED)).og; draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("player pos: (%F, %F)"), FMT_FLOAT_P((f64)player_pos.x, 12), FMT_FLOAT_P((f64)player_pos.y, 12))); pos.y += spacing; +#endif #if COLLIDER_DEBUG draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("collider gjk steps: %F"), FMT_UINT(collider_debug_steps))); @@ -1675,6 +1701,17 @@ INTERNAL void user_update(void) host_update(G.host); + /* 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 (now_ns - G.last_second_reset_ns > NS_FROM_SECONDS(1)) { + G.last_second_reset_ns = 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; + } + /* ========================== * * Render * ========================== */ diff --git a/src/util.h b/src/util.h index dd90a65f..217f0068 100644 --- a/src/util.h +++ b/src/util.h @@ -15,6 +15,8 @@ * Hash utils * ========================== */ +/* TODO: Replace with better hash functions */ + /* FNV-1a parameters for different hash sizes: * https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters */