From e7f55985dbda3be0b4ac01578afb0446b2836617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Madar=C3=A1sz?= Date: Wed, 27 Oct 2021 10:55:07 +0200 Subject: [PATCH] Remove GC for OOB entities + perf improvements --- code/game/src/debug_draw.c | 2 +- code/game/src/debug_ui.c | 1 - code/game/src/game.c | 33 ------- code/game/src/game.h | 91 +++++++++-------- code/game/src/profiler.c | 123 ++++++++++++----------- code/game/src/profiler.h | 73 +++++++------- code/game/src/world_view.c | 196 ++++++++++++++++++------------------- 7 files changed, 238 insertions(+), 281 deletions(-) diff --git a/code/game/src/debug_draw.c b/code/game/src/debug_draw.c index 6ca1c57..dc14b23 100644 --- a/code/game/src/debug_draw.c +++ b/code/game/src/debug_draw.c @@ -3,7 +3,7 @@ static debug_draw_queue draw_queue = {0}; -#ifdef ECO2D_PROD +#ifndef _DEBUG static bool draw_is_enabled = false; #else static bool draw_is_enabled = true; diff --git a/code/game/src/debug_ui.c b/code/game/src/debug_ui.c index 868bd0c..85cce3b 100644 --- a/code/game/src/debug_ui.c +++ b/code/game/src/debug_ui.c @@ -193,7 +193,6 @@ static debug_item items[] = { { .kind = DITEM_RAW, .val = PROF_RENDER, .proc = DrawProfilerDelta }, { .kind = DITEM_RAW, .val = PROF_UPDATE_SYSTEMS, .proc = DrawProfilerDelta }, { .kind = DITEM_RAW, .val = PROF_ENTITY_LERP, .proc = DrawProfilerDelta }, - { .kind = DITEM_RAW, .val = PROF_ENTITY_REMOVAL, .proc = DrawProfilerDelta }, { .kind = DITEM_RAW, .val = PROF_INTEGRATE_POS, .proc = DrawProfilerDelta }, { .kind = DITEM_END }, }, diff --git a/code/game/src/game.c b/code/game/src/game.c index dc50cf6..f380a63 100644 --- a/code/game/src/game.c +++ b/code/game/src/game.c @@ -217,10 +217,6 @@ void game_update() { network_server_tick(); } } - - if (game_mode != GAMEKIND_HEADLESS) { - game_world_cleanup_entities(); - } } void game_render() { @@ -244,35 +240,6 @@ void game_action_send_keystate(float x, pkt_send_keystate_send(active_viewer->view_id, x, y, mx, my, use, sprint, ctrl, drop, selected_item, swap, swap_from, swap_to); } -#define GAME_ENT_REMOVAL_TIME 10000 -#define GAME_ENT_REMOVAL_TRESHOLD 500 - -void game_world_cleanup_entities(void) { - // TODO(zaklaus): not the best approach to do a cleanup, let memory stay for a while as it might be reused later on anyway. -#if 1 - profile(PROF_ENTITY_REMOVAL) { - static uint64_t last_removal_time = 0; - if (last_removal_time > zpl_time_rel_ms()) return; - last_removal_time = zpl_time_rel_ms() + GAME_ENT_REMOVAL_TIME; - - for (int i = 0; i < zpl_buffer_count(world_viewers); i += 1){ - entity_view_tbl *view = &world_viewers[i].entities; - uint32_t deletions = 0; - - for (int j = 0; j < zpl_array_count(view->entries); j += 1) { - if (deletions > GAME_ENT_REMOVAL_TRESHOLD) return; - - entity_view *e = &view->entries[j].value; - if (e->tran_effect == ETRAN_REMOVE) { - entity_view_tbl_remove(view, e->ent_id); - deletions++; - } - } - } - } -#endif -} - void game_request_close() { platform_request_close(); } \ No newline at end of file diff --git a/code/game/src/game.h b/code/game/src/game.h index 22e3939..9701f20 100644 --- a/code/game/src/game.h +++ b/code/game/src/game.h @@ -1,47 +1,46 @@ -#pragma once -#include "system.h" -#include "world_view.h" - -typedef enum { - GAMEKIND_SINGLE, - GAMEKIND_CLIENT, - GAMEKIND_HEADLESS, - FORCE_GAMEKIND_UINT8 = UINT8_MAX -} game_kind; - -void game_init(game_kind play_mode, uint32_t num_viewers, int32_t seed, uint16_t chunk_size, uint16_t chunk_amount, int8_t is_dash_enabled); -void game_shutdown(); -void game_request_close(); -uint8_t game_is_running(); -int8_t game_is_networked(); -float game_time(); -game_kind game_get_kind(void); - -//~ NOTE(zaklaus): game events -void game_input(); -void game_update(); -void game_render(); - -//~ NOTE(zaklaus): world view management -world_view *game_world_view_get_active(void); -world_view *game_world_view_get(uint16_t idx); -void game_world_view_set_active_by_idx(uint16_t idx); -void game_world_view_set_active(world_view *view); -void game_world_view_cycle_active(int8_t dir); -void game_world_view_active_entity_map(void (*map_proc)(uint64_t key, entity_view * value)); -entity_view *game_world_view_active_get_entity(uint64_t ent_id); -void game_world_cleanup_entities(void); - -//~ NOTE(zaklaus): viewer -> host actions -void game_action_send_keystate(float x, - float y, - float mx, - float my, - uint8_t use, - uint8_t sprint, - uint8_t ctrl, - uint8_t drop, - uint8_t selected_item, - uint8_t swap, - uint8_t swap_from, +#pragma once +#include "system.h" +#include "world_view.h" + +typedef enum { + GAMEKIND_SINGLE, + GAMEKIND_CLIENT, + GAMEKIND_HEADLESS, + FORCE_GAMEKIND_UINT8 = UINT8_MAX +} game_kind; + +void game_init(game_kind play_mode, uint32_t num_viewers, int32_t seed, uint16_t chunk_size, uint16_t chunk_amount, int8_t is_dash_enabled); +void game_shutdown(); +void game_request_close(); +uint8_t game_is_running(); +int8_t game_is_networked(); +float game_time(); +game_kind game_get_kind(void); + +//~ NOTE(zaklaus): game events +void game_input(); +void game_update(); +void game_render(); + +//~ NOTE(zaklaus): world view management +world_view *game_world_view_get_active(void); +world_view *game_world_view_get(uint16_t idx); +void game_world_view_set_active_by_idx(uint16_t idx); +void game_world_view_set_active(world_view *view); +void game_world_view_cycle_active(int8_t dir); +void game_world_view_active_entity_map(void (*map_proc)(uint64_t key, entity_view * value)); +entity_view *game_world_view_active_get_entity(uint64_t ent_id); + +//~ NOTE(zaklaus): viewer -> host actions +void game_action_send_keystate(float x, + float y, + float mx, + float my, + uint8_t use, + uint8_t sprint, + uint8_t ctrl, + uint8_t drop, + uint8_t selected_item, + uint8_t swap, + uint8_t swap_from, uint8_t swap_to); \ No newline at end of file diff --git a/code/game/src/profiler.c b/code/game/src/profiler.c index 3fd1b99..42da93b 100644 --- a/code/game/src/profiler.c +++ b/code/game/src/profiler.c @@ -1,62 +1,61 @@ -#include "profiler.h" -#include "raylib.h" -#include - -#define PROF_COLLATE_WINDOW 0.5 - -// NOTE(zaklaus): KEEP ORDER IN SYNC WITH profiler_kind ENUM !!! -static profiler profilers[] = { - { .id = PROF_TOTAL_TIME, .name = "measured time" }, - { .id = PROF_MAIN_LOOP, .name = "main loop" }, - { .id = PROF_WORLD_WRITE, .name = "world write" }, - { .id = PROF_RENDER, .name = "render" }, - { .id = PROF_UPDATE_SYSTEMS, .name = "update systems" }, - { .id = PROF_ENTITY_LERP, .name = "entity lerp" }, - { .id = PROF_ENTITY_REMOVAL, .name = "entity removal" }, - { .id = PROF_INTEGRATE_POS, .name = "entity movement" }, -}; - -static_assert((sizeof(profilers)/sizeof(profilers[0])) == MAX_PROF, "mismatched profilers"); - -void profiler_reset(profiler_kind id) { - profilers[id].num_invocations = 0; - profilers[id].total_time = 0.0; -} - -void profiler_start(profiler_kind id) { - profilers[id].start_time = GetTime(); -} - -void profiler_stop(profiler_kind id) { - profilers[id].num_invocations += 1; - profilers[id].total_time += GetTime() - profilers[id].start_time; - profilers[id].start_time = 0.0; -} - -void profiler_collate() { - static double frame_counter = 0.0; - static uint64_t frames = 0; - - frame_counter += GetFrameTime(); - frames++; - - if (frame_counter >= PROF_COLLATE_WINDOW) { - profilers[PROF_TOTAL_TIME].delta_time = frame_counter / (double)frames; - - for (uint32_t i = PROF_MAIN_LOOP; i < MAX_PROF; i += 1) { - profiler *p = &profilers[i]; - p->delta_time = p->num_invocations == 0 ? 0.0 : p->total_time / (double)p->num_invocations; - } - - frame_counter = 0.0; - frames = 0; - } -} - -double profiler_delta(profiler_kind id) { - return profilers[id].delta_time; -} - -char const *profiler_name(profiler_kind id) { - return profilers[id].name; -} +#include "profiler.h" +#include "raylib.h" +#include + +#define PROF_COLLATE_WINDOW 0.5 + +// NOTE(zaklaus): KEEP ORDER IN SYNC WITH profiler_kind ENUM !!! +static profiler profilers[] = { + { .id = PROF_TOTAL_TIME, .name = "measured time" }, + { .id = PROF_MAIN_LOOP, .name = "main loop" }, + { .id = PROF_WORLD_WRITE, .name = "world write" }, + { .id = PROF_RENDER, .name = "render" }, + { .id = PROF_UPDATE_SYSTEMS, .name = "update systems" }, + { .id = PROF_ENTITY_LERP, .name = "entity lerp" }, + { .id = PROF_INTEGRATE_POS, .name = "entity movement" }, +}; + +static_assert((sizeof(profilers)/sizeof(profilers[0])) == MAX_PROF, "mismatched profilers"); + +void profiler_reset(profiler_kind id) { + profilers[id].num_invocations = 0; + profilers[id].total_time = 0.0; +} + +void profiler_start(profiler_kind id) { + profilers[id].start_time = GetTime(); +} + +void profiler_stop(profiler_kind id) { + profilers[id].num_invocations += 1; + profilers[id].total_time += GetTime() - profilers[id].start_time; + profilers[id].start_time = 0.0; +} + +void profiler_collate() { + static double frame_counter = 0.0; + static uint64_t frames = 0; + + frame_counter += GetFrameTime(); + frames++; + + if (frame_counter >= PROF_COLLATE_WINDOW) { + profilers[PROF_TOTAL_TIME].delta_time = frame_counter / (double)frames; + + for (uint32_t i = PROF_MAIN_LOOP; i < MAX_PROF; i += 1) { + profiler *p = &profilers[i]; + p->delta_time = p->num_invocations == 0 ? 0.0 : p->total_time / (double)p->num_invocations; + } + + frame_counter = 0.0; + frames = 0; + } +} + +double profiler_delta(profiler_kind id) { + return profilers[id].delta_time; +} + +char const *profiler_name(profiler_kind id) { + return profilers[id].name; +} diff --git a/code/game/src/profiler.h b/code/game/src/profiler.h index 1636898..4c93d5f 100644 --- a/code/game/src/profiler.h +++ b/code/game/src/profiler.h @@ -1,37 +1,36 @@ -#pragma once -#include "system.h" - -typedef enum { - PROF_TOTAL_TIME, - PROF_MAIN_LOOP, - - PROF_WORLD_WRITE, - PROF_RENDER, - PROF_UPDATE_SYSTEMS, - PROF_ENTITY_LERP, - PROF_ENTITY_REMOVAL, - PROF_INTEGRATE_POS, - - MAX_PROF, - PROF_FORCE_UINT8 = UINT8_MAX -} profiler_kind; - -typedef struct { - profiler_kind id; - char const *name; - - uint32_t num_invocations; - double start_time; - double delta_time; - double total_time; -} profiler; - -void profiler_reset(profiler_kind id); -void profiler_start(profiler_kind id); -void profiler_stop(profiler_kind id); -void profiler_collate(void); - -double profiler_delta(profiler_kind id); -char const *profiler_name(profiler_kind id); - -#define profile(id) defer(profiler_start(id), profiler_stop(id)) +#pragma once +#include "system.h" + +typedef enum { + PROF_TOTAL_TIME, + PROF_MAIN_LOOP, + + PROF_WORLD_WRITE, + PROF_RENDER, + PROF_UPDATE_SYSTEMS, + PROF_ENTITY_LERP, + PROF_INTEGRATE_POS, + + MAX_PROF, + PROF_FORCE_UINT8 = UINT8_MAX +} profiler_kind; + +typedef struct { + profiler_kind id; + char const *name; + + uint32_t num_invocations; + double start_time; + double delta_time; + double total_time; +} profiler; + +void profiler_reset(profiler_kind id); +void profiler_start(profiler_kind id); +void profiler_stop(profiler_kind id); +void profiler_collate(void); + +double profiler_delta(profiler_kind id); +char const *profiler_name(profiler_kind id); + +#define profile(id) defer(profiler_start(id), profiler_stop(id)) diff --git a/code/game/src/world_view.c b/code/game/src/world_view.c index 2fc5c39..69c70fd 100644 --- a/code/game/src/world_view.c +++ b/code/game/src/world_view.c @@ -1,101 +1,95 @@ -#include "zpl.h" -#include "world_view.h" -#include "entity_view.h" -#include "prediction.h" -#include "librg.h" -#include "world/world.h" -#include "game.h" - -int32_t tracker_read_remove(librg_world *w, librg_event *e) { - int64_t entity_id = librg_event_entity_get(w, e); - world_view *view = (world_view*)librg_world_userdata_get(w); - - if (view != game_world_view_get_active()) { - entity_view_destroy(&view->entities, entity_id); - } else { - entity_view_mark_for_removal(&view->entities, entity_id); - } - - entity_view_remove_chunk_texture(&view->entities, entity_id); - return 0; -} - -int32_t tracker_read_update(librg_world *w, librg_event *e) { - int64_t entity_id = librg_event_entity_get(w, e); - size_t actual_length = librg_event_size_get(w, e); - char *buffer = librg_event_buffer_get(w, e); - world_view *view = (world_view*)librg_world_userdata_get(w); - - entity_view data = entity_view_unpack_struct(buffer, actual_length); - entity_view *d = entity_view_get(&view->entities, entity_id); -#if 1 - if (d && d->layer_id < view->active_layer_id) { - if (zpl_time_rel_ms() - d->last_update > WORLD_TRACKER_UPDATE_NORMAL_MS) { - d->layer_id = zpl_min(WORLD_TRACKER_LAYERS-1, d->layer_id+1); - } - // NOTE(zaklaus): reject updates from slower layers - else return 0; - } -#endif - - data.last_update = zpl_time_rel_ms(); - data.layer_id = view->active_layer_id; - predict_receive_update(d, &data); - entity_view_update_or_create(&view->entities, entity_id, data); - entity_view_remove_chunk_texture(&view->entities, entity_id); - entity_view_update_chunk_texture(&view->entities, entity_id, view); - return 0; -} - -int32_t tracker_read_create(librg_world *w, librg_event *e) { - int64_t entity_id = librg_event_entity_get(w, e); - size_t actual_length = librg_event_size_get(w, e); - char *buffer = librg_event_buffer_get(w, e); - world_view *view = (world_view*)librg_world_userdata_get(w); - - entity_view data = entity_view_unpack_struct(buffer, actual_length); - data.ent_id = entity_id; - data.layer_id = view->active_layer_id; - data.tran_time = 0.0f; - data.color = rand(); // TODO(zaklaus): feed from server - if (data.flag & EFLAG_INTERP) { - data.tx = data.x; - data.ty = data.y; - data.theading = data.heading; - } - entity_view_update_or_create(&view->entities, entity_id, data); - entity_view_mark_for_fadein(&view->entities, entity_id); - entity_view_update_chunk_texture(&view->entities, entity_id, view); - return 0; -} - -world_view world_view_create(uint16_t view_id) { - world_view view = {0}; - view.view_id = view_id; - view.tracker = librg_world_create(); - entity_view_init(&view.entities); - return view; -} - -void world_view_init(world_view *view, uint32_t seed, uint64_t ent_id, uint16_t chunk_size, uint16_t chunk_amount) { - view->seed = seed; - view->owner_id = ent_id; - view->chunk_size = chunk_size; - view->chunk_amount = chunk_amount; - view->dim = WORLD_BLOCK_SIZE * chunk_size * chunk_amount; - view->size = view->dim * view->dim; - - librg_config_chunksize_set(view->tracker, WORLD_BLOCK_SIZE * chunk_size, WORLD_BLOCK_SIZE * chunk_size, 1); - librg_config_chunkamount_set(view->tracker, chunk_amount, chunk_amount, 1); - librg_config_chunkoffset_set(view->tracker, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG, 0); - - librg_event_set(view->tracker, LIBRG_READ_CREATE, tracker_read_create); - librg_event_set(view->tracker, LIBRG_READ_REMOVE, tracker_read_remove); - librg_event_set(view->tracker, LIBRG_READ_UPDATE, tracker_read_update); - librg_world_userdata_set(view->tracker, view); -} - -void world_view_destroy(world_view *view) { - librg_world_destroy(view->tracker); - entity_view_free(&view->entities); -} +#include "zpl.h" +#include "world_view.h" +#include "entity_view.h" +#include "prediction.h" +#include "librg.h" +#include "world/world.h" +#include "game.h" + +int32_t tracker_read_remove(librg_world *w, librg_event *e) { + int64_t entity_id = librg_event_entity_get(w, e); + world_view *view = (world_view*)librg_world_userdata_get(w); + entity_view_remove_chunk_texture(&view->entities, entity_id); + entity_view_destroy(&view->entities, entity_id); + return 0; +} + +int32_t tracker_read_update(librg_world *w, librg_event *e) { + int64_t entity_id = librg_event_entity_get(w, e); + size_t actual_length = librg_event_size_get(w, e); + char *buffer = librg_event_buffer_get(w, e); + world_view *view = (world_view*)librg_world_userdata_get(w); + + entity_view data = entity_view_unpack_struct(buffer, actual_length); + entity_view *d = entity_view_get(&view->entities, entity_id); +#if 1 + if (d && d->layer_id < view->active_layer_id) { + if (zpl_time_rel_ms() - d->last_update > WORLD_TRACKER_UPDATE_NORMAL_MS) { + d->layer_id = zpl_min(WORLD_TRACKER_LAYERS-1, d->layer_id+1); + } + // NOTE(zaklaus): reject updates from slower layers + else return 0; + } +#endif + + data.last_update = zpl_time_rel_ms(); + data.layer_id = view->active_layer_id; + predict_receive_update(d, &data); + entity_view_update_or_create(&view->entities, entity_id, data); + entity_view_remove_chunk_texture(&view->entities, entity_id); + entity_view_update_chunk_texture(&view->entities, entity_id, view); + return 0; +} + +int32_t tracker_read_create(librg_world *w, librg_event *e) { + int64_t entity_id = librg_event_entity_get(w, e); + size_t actual_length = librg_event_size_get(w, e); + char *buffer = librg_event_buffer_get(w, e); + world_view *view = (world_view*)librg_world_userdata_get(w); + + entity_view data = entity_view_unpack_struct(buffer, actual_length); + data.ent_id = entity_id; + data.layer_id = view->active_layer_id; + data.tran_time = 0.0f; + data.color = rand(); // TODO(zaklaus): feed from server + if (data.flag & EFLAG_INTERP) { + data.tx = data.x; + data.ty = data.y; + data.theading = data.heading; + } + entity_view_update_or_create(&view->entities, entity_id, data); + entity_view_mark_for_fadein(&view->entities, entity_id); + entity_view_update_chunk_texture(&view->entities, entity_id, view); + return 0; +} + +world_view world_view_create(uint16_t view_id) { + world_view view = {0}; + view.view_id = view_id; + view.tracker = librg_world_create(); + entity_view_init(&view.entities); + return view; +} + +void world_view_init(world_view *view, uint32_t seed, uint64_t ent_id, uint16_t chunk_size, uint16_t chunk_amount) { + view->seed = seed; + view->owner_id = ent_id; + view->chunk_size = chunk_size; + view->chunk_amount = chunk_amount; + view->dim = WORLD_BLOCK_SIZE * chunk_size * chunk_amount; + view->size = view->dim * view->dim; + + librg_config_chunksize_set(view->tracker, WORLD_BLOCK_SIZE * chunk_size, WORLD_BLOCK_SIZE * chunk_size, 1); + librg_config_chunkamount_set(view->tracker, chunk_amount, chunk_amount, 1); + librg_config_chunkoffset_set(view->tracker, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG, 0); + + librg_event_set(view->tracker, LIBRG_READ_CREATE, tracker_read_create); + librg_event_set(view->tracker, LIBRG_READ_REMOVE, tracker_read_remove); + librg_event_set(view->tracker, LIBRG_READ_UPDATE, tracker_read_update); + librg_world_userdata_set(view->tracker, view); +} + +void world_view_destroy(world_view *view) { + librg_world_destroy(view->tracker); + entity_view_free(&view->entities); +}