diff options
| author | Jonathan Bradley <jcb@pikum.xyz> | 2025-09-16 16:58:02 -0400 |
|---|---|---|
| committer | Jonathan Bradley <jcb@pikum.xyz> | 2025-09-16 17:00:08 -0400 |
| commit | 73c5e170260638cc566cba2689ea570caee39251 (patch) | |
| tree | 256b36398d33650054a885e572437909f88485cb /src | |
| parent | e52ef2e49ae660833370befc34ba79d412cb4604 (diff) | |
pke: major object lifetime overhaul.
Added pke-test-load-unload to ensure objects are
managed as expected.
Diffstat (limited to 'src')
| -rw-r--r-- | src/camera.cpp | 32 | ||||
| -rw-r--r-- | src/camera.hpp | 1 | ||||
| -rw-r--r-- | src/ecs.cpp | 23 | ||||
| -rw-r--r-- | src/font.cpp | 159 | ||||
| -rw-r--r-- | src/font.hpp | 20 | ||||
| -rw-r--r-- | src/game.cpp | 15 | ||||
| -rw-r--r-- | src/level.cpp | 29 | ||||
| -rw-r--r-- | src/level.hpp | 1 | ||||
| -rw-r--r-- | src/physics.cpp | 24 | ||||
| -rw-r--r-- | src/project.cpp | 9 | ||||
| -rw-r--r-- | src/scene.cpp | 24 | ||||
| -rw-r--r-- | src/scene.hpp | 2 | ||||
| -rw-r--r-- | src/serialization.cpp | 10 | ||||
| -rw-r--r-- | src/static-ui.cpp | 106 | ||||
| -rw-r--r-- | src/thread-pool.cpp | 10 | ||||
| -rw-r--r-- | src/window.cpp | 10 |
16 files changed, 307 insertions, 168 deletions
diff --git a/src/camera.cpp b/src/camera.cpp index ca2c11f..07e4253 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -158,20 +158,6 @@ void PkeCamera_SetPrimary(CameraHandle cameraHandle) { } } -void PkeCamera_Destroy(CameraHandle cameraHandle) { - assert(cameraHandle != CameraHandle_MAX); - auto *camPtr = &cam_mstr.bktc_cameras[cameraHandle]; - auto &cam = *camPtr; - - if (cam.phys.constraint != nullptr && cam.phys.constraint != CAFE_BABE(btTypedConstraint)) { - // reminder: this is not currently handled by ECS - BtDynamicsWorld->removeConstraint(cam.phys.constraint); - pk_delete<btTypedConstraint>(cam.phys.constraint, MemBkt_Bullet); - } - - ECS_MarkForRemoval(camPtr); -} - pk_bkt_arr &PkeCamera_GetPkBktArr() { return cam_mstr.bktc_cameras; } @@ -217,16 +203,30 @@ void PkeCamera_Tick(double delta) { bool b; b = pk_bkt_arr_iter_begin(&cam_mstr.bktc_cameras, &iter_cam); while (b == true) { + inst = ECS_GetInstance(iter_cam->phys.instHandle); if (iter_cam->isMarkedForRemoval == true) { - pk_bkt_arr_free_handle(&cam_mstr.bktc_cameras, iter_cam->camHandle); if (ActiveCamera == iter_cam.data) { ActiveCamera = &NullCamera; } + if (iter_cam->phys.constraint != nullptr && iter_cam->phys.constraint != CAFE_BABE(btTypedConstraint)) { + // reminder: this is not currently handled by ECS + BtDynamicsWorld->removeConstraint(iter_cam->phys.constraint); + pk_delete<btTypedConstraint>(iter_cam->phys.constraint, MemBkt_Bullet); + } + if (inst != nullptr) { + if (inst->bt.rigidBody != nullptr) { + BtDynamicsWorld->removeRigidBody(inst->bt.rigidBody); + pk_delete<btRigidBody>(inst->bt.rigidBody, MemBkt_Bullet); + } + if (inst->bt.motionState != nullptr) { + pk_delete<btDefaultMotionState>(inst->bt.motionState, MemBkt_Bullet); + } + } + pk_bkt_arr_free_handle(&cam_mstr.bktc_cameras, iter_cam->camHandle); new (iter_cam.data) PkeCamera{}; b = pk_bkt_arr_iter_increment(&cam_mstr.bktc_cameras, &iter_cam); continue; } - inst = ECS_GetInstance(iter_cam->phys.instHandle); assert(inst != nullptr); if (inst->isNeedingUpdated == true) { iter_cam->stale = iter_cam->stale | PKE_CAMERA_STALE_POSROT; diff --git a/src/camera.hpp b/src/camera.hpp index 9486c84..1226c11 100644 --- a/src/camera.hpp +++ b/src/camera.hpp @@ -51,7 +51,6 @@ void PkeCamera_TargetInstance(CameraHandle cameraHandle, CompInstance *inst); void PkeCamera_UntargetInstance(CameraHandle cameraHandle); pk_bkt_arr &PkeCamera_GetPkBktArr(); void PkeCamera_SetPrimary(CameraHandle handle); -void PkeCamera_Destroy(CameraHandle handle); void PkeCamera_Teardown(); void PkeCamera_Tick(double delta); diff --git a/src/ecs.cpp b/src/ecs.cpp index dd521ce..b4391dc 100644 --- a/src/ecs.cpp +++ b/src/ecs.cpp @@ -15,6 +15,7 @@ typedef pk_bkt_arr_t<Entity_Base *> type_bkt_arr_entities ; struct ECS { struct pk_membucket *bkt = nullptr; + pk_ev_mgr_id_T ev_mgr_id; struct ECSBucketContainers { pk_bkt_arr_t<Entity_Base> generics{}; type_bkt_arr_entities entityPtrs{}; @@ -28,6 +29,15 @@ struct ECS { * Entities that have been marked for removal by calling ECS_MarkForRemoval * * Used to build the other "removal" lists. + * + * Each ECS module is REQUIRED to manage the lifetimes of its objects. + * Each module MUST wait for an entity to be flagged "IsMarkedForRemoval". + * Even though the module itself is sometimes the source of reporting the + * entity to be cleaned up, it must wait to free resources until marked. + * Do note that this means that it can take several ticks for all resources + * of a level to get freed. + * Ex: box with text; text isn't marked for removal until after box. + * (This happens when deserializing, the FontRender doesn't know about its parent") */ pk_arr_t<Entity_Base *> entitiesMarkedForRemoval{}; /* @@ -68,7 +78,7 @@ void ECS_Init() { pk_arr_reserve(&entitiesMarkedForRemoval, 16); pk_arr_reserve(&entitiesYetToBeRemoved, 16); pk_arr_reserve(&EntitiesWithExcessInstances, 16); - pk_ev_create_mgr(); + ecs.ev_mgr_id = pk_ev_create_mgr(); } Entity_Base *ECS_CreateGenericEntity() { @@ -105,8 +115,8 @@ Entity_Base *ECS_GetEntityByUUID(pk_uuid uuid) { auto entity_ptr_find_cb = [](void *user_data, const void *arr_user_data, const void *arr_obj_data) { (void)user_data; const pk_uuid &uuid = *reinterpret_cast<const pk_uuid*>(arr_user_data);; - const Entity_Base &ent = **reinterpret_cast<const Entity_Base*const*>(arr_obj_data); - return (ent.uuid == uuid); + const Entity_Base *ent = *reinterpret_cast<const Entity_Base*const*>(arr_obj_data); + return (ent->uuid == uuid); }; EntityHandle handle { pk_bkt_arr_find_first_handle(&ecs.bc.entityPtrs, entity_ptr_find_cb, NULL, &uuid) }; if (handle == EntityHandle_MAX) return nullptr; @@ -203,8 +213,7 @@ void ECS_Tick_Early(double delta) { while (b == true) { Entity_Base *ent = ecs.bc.entityPtrs[iter_comp_ev->entity_handle]; if (ent->isMarkedForRemoval) { - pk_ev_destroy_mgr(iter_comp_ev->ev_mgr_id); - pk_bkt_arr_free_handle(&ecs.bc.ev_mgrs, iter_comp_ev->entity_handle); + pk_bkt_arr_free_handle(&ecs.bc.ev_mgrs, iter_comp_ev->pke_event_handle); new (&(*iter_comp_ev)) pke_component_event{}; } b = pk_bkt_arr_iter_increment(&ecs.bc.ev_mgrs, &iter_comp_ev); @@ -565,9 +574,8 @@ CompInstance *ECS_GetInstance(InstanceHandle instanceHandle ) { if (instanceHandle == InstanceHandle_MAX) return nullptr; assert(pk_bkt_arr_handle_validate(&ecs.bc.instances, instanceHandle) == PK_BKT_ARR_HANDLE_VALIDATION_VALID); - auto *inst = &ecs.bc.instances[instanceHandle]; - return inst; + return &ecs.bc.instances[instanceHandle]; } void ECS_GetInstances(Entity_Base *entity, pk_arr_t<CompInstance *> &arr) { @@ -657,6 +665,7 @@ pk_bkt_arr *ECS_GetEvs() { } void ECS_Teardown() { + pk_ev_destroy_mgr(ecs.ev_mgr_id); pk_arr_reset(&EntitiesWithExcessInstances); pk_arr_reset(&entitiesYetToBeRemoved); pk_arr_reset(&entitiesMarkedForRemoval); diff --git a/src/font.cpp b/src/font.cpp index b24ea8f..58d5f50 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -34,9 +34,9 @@ TypeSafeInt_B(FONT_GLYPH_CHAR_FLAG); TypeSafeInt_B(FONT_RENDER_SURFACE_AREA_TYPE_FLAG); struct FontTypeData { - FontType *arr_ft; + FontType *arr_ft = nullptr; + uint64_t unused_fts = 0xFFFFFFFFFFFFFFFF; FontTypeIndex n_ft{0}; - FontTypeIndex h_ft{0}; } ftd; struct FontInstanceBufferItem { @@ -51,6 +51,8 @@ struct FontInstanceBufferItem { float padding[3]; }; +void FontType_Unload(FontTypeIndex idx); + uint32_t utf8_to_unicode(const char* str, uint32_t &out) { uint32_t i = 0; out = 0; @@ -243,10 +245,9 @@ void FontType_Inner_CalcTransforms(const FontType *ft, FontRender *fr, FontInsta void FontType_Init() { FontTypeIndex fti; - ftd.h_ft = FontTypeIndex{0}; - ftd.n_ft = FontTypeIndex{5}; - ftd.arr_ft = pk_new_arr<FontType>(5); - for (FontTypeIndex_T i = 0; i < 5; ++i) { + ftd.n_ft = FontTypeIndex{8}; + ftd.arr_ft = pk_new_arr<FontType>(8); + for (FontTypeIndex_T i = 0; i < 8; ++i) { FontType *ft = &ftd.arr_ft[i]; ft->glyphs = nullptr; ft->renders = nullptr; @@ -276,9 +277,10 @@ void FontType_Init() { } void FontType_Teardown() { - FontTypeIndex i; - for (i = FontTypeIndex{0}; i < ftd.h_ft; ++i) { - FontType_Unload(i); + int i; + for (i = (int)(FontTypeIndex_T)ftd.n_ft-1; i >= 0; --i) { + if ((ftd.unused_fts & (1llu << i)) != 0) continue; + FontType_Unload(FontTypeIndex{(FontTypeIndex_T)i}); } if (ftd.arr_ft != nullptr) pk_delete_arr<FontType>(ftd.arr_ft, (FontTypeIndex_T)ftd.n_ft); } @@ -294,19 +296,43 @@ void FontType_Tick(double delta) { size_t index; FontType *ft; FontRender *fr; - for (FontTypeIndex_T i = 0; i < (FontTypeIndex_T)ftd.h_ft; ++i) { + for (FontTypeIndex_T i = 0; i < (FontTypeIndex_T)ftd.n_ft; ++i) { + + if ((ftd.unused_fts & (1llu << i)) != 0) continue; + index = 0; ft = &ftd.arr_ft[i]; - if (pkeSettings.rt.was_framebuffer_resized == false && ft->gr.should_update_instance_buffer == false) { + + for (FontRenderIndex_T k = 0; k < (FontRenderIndex_T)ft->n_render; ++k) { + if ((ft->unused_frs & (1llu << k)) != 0) continue; + fr = &ft->renders[k]; + if (fr->isMarkedForRemoval == true) { + if (fr->text.reserved > 0) { + // 2025-04-16 - JCB + // not passing a specific bucket because pk_cstr doesn't store it. + // FontType_AddStringRender requires a `pk_cstr &&`, so we are the proper owner. + // It's up to us to free it. + pk_delete_arr<char>(fr->text.val, fr->text.reserved); + } + ft->unused_frs |= (1llu << k); + new (fr) FontRender{}; + ft->gr.should_update_instance_buffer = true; + continue; + } + index += fr->n_glyphs; + } + + if (ft->isMarkedForRemoval == true) { + FontType_Unload(FontTypeIndex{i}); continue; } - // TODO specific bucket - for (FontRenderIndex_T k = 0; k < (FontRenderIndex_T)ft->h_render; ++k) { - index += ft->renders[k].n_glyphs; + if (pkeSettings.rt.was_framebuffer_resized == false && ft->gr.should_update_instance_buffer == false) { + continue; } if (index == 0) { + ft->bindings.instance_counter = index; continue; } ft->gr.should_update_instance_buffer = false; @@ -314,7 +340,8 @@ void FontType_Tick(double delta) { fibis = pk_new_arr<FontInstanceBufferItem>(index); ft->bindings.instance_counter = index; index = 0; - for (FontRenderIndex_T k = 0; k < (FontRenderIndex_T)ft->h_render; ++k) { + for (FontRenderIndex_T k = 0; k < (FontRenderIndex_T)ft->n_render; ++k) { + if ((ft->unused_frs & (1llu << k)) != 0) continue; fr = &ft->renders[k]; FontType_Inner_CalcTransforms(ft, fr, &fibis[index]); index += fr->n_glyphs; @@ -525,13 +552,14 @@ void FontType_Deserialize(std::istream &stream) { } FontType* FontType_Get(FontTypeIndex idx) { - assert(idx < ftd.h_ft); + assert((ftd.unused_fts & (1llu << FontTypeIndex_T(idx))) == 0); return &ftd.arr_ft[static_cast<FontTypeIndex_T>(idx)]; } FontType* FontType_GetByTitle(const pk_cstr title) { assert(title.val != nullptr); - for (FontTypeIndex_T i = 0; i < static_cast<FontTypeIndex_T>(ftd.h_ft); ++i) { + for (FontTypeIndex_T i = 0; i < (FontTypeIndex_T)ftd.n_ft; ++i) { + if ((ftd.unused_fts & (1llu << i)) != 0) continue; if (ftd.arr_ft[i].title.val == title.val) return &ftd.arr_ft[i]; if (strcmp(ftd.arr_ft[i].title.val, title.val) == 0) @@ -540,28 +568,40 @@ FontType* FontType_GetByTitle(const pk_cstr title) { return nullptr; } -FontType* FontType_GetFonts(FontTypeIndex &count) { - count = ftd.h_ft; +FontType* FontType_GetFonts(uint64_t &idx_unused) { + idx_unused = ftd.unused_fts; return ftd.arr_ft; } FontTypeIndex FontType_RegisterFont(pk_cstr title, AssetHandle fontTextureHandle, AssetHandle glyphsHandle, FontTypeMSDFSettings *msdf_settings, FontTypeSpacing *spacing) { VkResult vkResult; - pk_arr arr_vert_mem_reqs; - memset(&arr_vert_mem_reqs, 0, sizeof(pk_arr)); - arr_vert_mem_reqs.alignment = alignof(VkMemoryRequirements); - arr_vert_mem_reqs.stride = sizeof(VkMemoryRequirements); constexpr VkDeviceSize startingGlyphCount = 4; + const Asset *fontTexture = nullptr; + const Asset *glyphs = nullptr; + FontTypeIndex idx{0}; + FontTypeIndex_T idx_t; + FontType *ft; + uint16_t u; + assert(fontTextureHandle != AssetHandle_MAX); assert(glyphsHandle != AssetHandle_MAX); assert(msdf_settings != nullptr); assert(spacing != nullptr); - const Asset *fontTexture = AM_Get(fontTextureHandle); - FontTypeIndex idx = ftd.h_ft; - ftd.h_ft += FontTypeIndex{1}; - if (ftd.h_ft >= ftd.n_ft) { - ftd.n_ft += FontTypeIndex{5}; + for (u = 0; u < (FontTypeIndex_T)ftd.n_ft; ++u) { + if ((ftd.unused_fts & (1llu << u)) != 0) { + idx_t = u; + idx = FontTypeIndex{idx_t}; + ftd.unused_fts &= ~(1llu << idx_t); + break; + } + } + + if (idx >= ftd.n_ft) { + if (idx_t >= PKE_FONT_MAX_FONT_TYPES) { + throw "[font.cpp] out of font type slots"; + } + ftd.n_ft = FontTypeIndex{8}; FontType *arr = pk_new_arr<FontType>((FontTypeIndex_T)ftd.n_ft); if (idx > FontTypeIndex{0}) { memcpy(arr, ftd.arr_ft, sizeof(FontType) * (FontTypeIndex_T)idx); @@ -570,18 +610,20 @@ FontTypeIndex FontType_RegisterFont(pk_cstr title, AssetHandle fontTextureHandle ftd.arr_ft = arr; } - FontTypeIndex_T idx_t = (FontTypeIndex_T)idx; - FontType *ft = &ftd.arr_ft[idx_t]; + fontTexture = AM_Get(fontTextureHandle); + + idx_t = (FontTypeIndex_T)idx; + ft = &ftd.arr_ft[idx_t]; ft->title = title; ft->fontTextureAssetHandle = fontTextureHandle; ft->glyphDetailsAssetHandle = glyphsHandle; ft->renders = pk_new_arr<FontRender>(startingGlyphCount); - ft->h_render = FontRenderIndex{0}; ft->n_render = FontRenderIndex{startingGlyphCount}; + ft->unused_frs = 0xFFFFFFFFFFFFFFFF; ft->msdf_settings = *msdf_settings; ft->spacing = *spacing; - const Asset *glyphs = AM_Get(glyphsHandle); + glyphs = AM_Get(glyphsHandle); ft->n_glyphs = glyphs->size / sizeof(FontGlyphChar); ft->glyphs = (FontGlyphChar*)glyphs->ptr; @@ -708,11 +750,14 @@ FontTypeIndex FontType_RegisterFont(pk_cstr title, AssetHandle fontTextureHandle } void FontType_Unload(FontTypeIndex idx) { + assert((ftd.unused_fts & (1llu << (FontTypeIndex_T)idx)) == 0); + FontType *ft = &ftd.arr_ft[(FontTypeIndex_T)idx]; // TODO specific bucket if (ft->renders != nullptr) { - for (FontRenderIndex_T i = 0; i < (FontRenderIndex_T)ft->h_render; ++i) { + for (FontRenderIndex_T i = 0; i < (FontRenderIndex_T)ft->n_render; ++i) { + if ((ft->unused_frs & (1llu << i)) != 0) continue; if (ft->renders[i].glyph_indices != nullptr) { pk_delete_arr<uint32_t>(ft->renders[i].glyph_indices, ft->renders[i].n_glyphs); } @@ -793,7 +838,8 @@ void FontType_Unload(FontTypeIndex idx) { ft->title.val = nullptr; ft->title.length = 0; } - ECS_MarkForRemoval(ft); + ftd.unused_fts |= (1llu << (FontTypeIndex_T)idx); + new (ft) FontType{}; } void FontType_cstr_to_unicode(const FontType &ft, pk_arr_t<uint32_t> &arr, const pk_cstr &str) { @@ -843,12 +889,22 @@ FontRenderHandle FontType_AddStringRender(FontTypeIndex idx_ft, const pk_cstr && FontType *ft = &ftd.arr_ft[(FontTypeIndex_T)idx_ft]; FontRender *fr; uint32_t i, count; - FontRenderIndex idx_fr = ft->h_render; - ft->h_render += FontRenderIndex{1}; + FontRenderIndex idx_fr = FontRenderIndex{0}; - if (ft->h_render >= ft->n_render) { + for (i = 0; i < (FontRenderIndex_T)ft->n_render; ++i) { + if ((ft->unused_frs & (1llu << i)) != 0) { + ft->unused_frs &= ~(1llu << i); + idx_fr = FontRenderIndex{0}; + break; + } + } + + if (idx_fr >= ft->n_render) { + if (idx_fr >= FontRenderIndex{PKE_FONT_MAX_FONT_RENDERS}) { + throw "[font.cpp] out of FontRender slots"; + } FontRenderIndex_T old_count{static_cast<FontRenderIndex_T>(ft->n_render)}; - ft->n_render += FontRenderIndex{5}; + ft->n_render += FontRenderIndex{8}; FontRender *arr = pk_new_arr<FontRender>((FontRenderIndex_T)ft->n_render); memcpy(arr, ft->renders, sizeof(FontRender) * old_count); pk_delete_arr<FontRender>(ft->renders, old_count); @@ -890,11 +946,11 @@ FontRenderHandle FontType_AddStringRender(FontTypeIndex idx_ft, const pk_cstr && } FontRender *FontType_GetFontRender(FontRenderHandle frh) { - if (frh.index_ft >= ftd.h_ft) { + if ((ftd.unused_fts & (1llu << (FontTypeIndex_T)frh.index_ft)) != 0) { return nullptr; } FontType *ft = &ftd.arr_ft[static_cast<FontTypeIndex_T>(frh.index_ft)]; - if (frh.index_fr >= ft->h_render) { + if ((ft->unused_frs & (1llu << (FontRenderIndex_T)frh.index_fr)) != 0) { return nullptr; } return &ft->renders[static_cast<FontRenderIndex_T>(frh.index_fr)]; @@ -902,18 +958,18 @@ FontRender *FontType_GetFontRender(FontRenderHandle frh) { void FontType_UpdateStringRender(FontRenderHandle frh, FontRenderSettings *settings) { assert(settings != nullptr); - assert((FontTypeIndex_T)ftd.h_ft > (FontTypeIndex_T)frh.index_ft); + assert((ftd.unused_fts & (1llu << (FontTypeIndex_T)frh.index_ft)) == 0); FontType *ft = &ftd.arr_ft[(FontTypeIndex_T)frh.index_ft]; - assert((FontRenderIndex_T)ft->h_render > (FontRenderIndex_T)frh.index_fr); + assert((ft->unused_frs & (1llu << (FontTypeIndex_T)frh.index_fr)) == 0); ft->gr.should_update_instance_buffer = true; ft->renders[(FontRenderIndex_T)frh.index_fr].settings = *settings; } void FontType_UpdateStringRenderText(FontRenderHandle frh, pk_cstr &&cstr) { uint32_t i, count; - assert(frh.index_ft < ftd.h_ft); + assert((ftd.unused_fts & (1llu << (FontTypeIndex_T)frh.index_ft)) == 0); FontType &ft = ftd.arr_ft[static_cast<FontTypeIndex_T>(frh.index_ft)]; - assert(frh.index_fr < ft.h_render); + assert((ft.unused_frs & (1llu << (FontTypeIndex_T)frh.index_fr)) == 0); FontRender &fr = ft.renders[static_cast<FontRenderIndex_T>(frh.index_fr)]; pk_cstr old_str = fr.text; if (window == NULL) { @@ -941,25 +997,10 @@ void FontType_UpdateStringRenderText(FontRenderHandle frh, pk_cstr &&cstr) { void FontType_RemoveStringRender(FontRenderHandle handle) { FontRender *fr; - uint32_t buffer_start_index; FontType *ft = &ftd.arr_ft[(FontTypeIndex_T)handle.index_ft]; // hack, but works ft->gr.should_update_instance_buffer = true; fr = &ft->renders[(FontRenderIndex_T)handle.index_fr]; - ft->bindings.instance_counter -= fr->n_glyphs; - buffer_start_index = fr->buffer_start_index; - fr->n_glyphs = 0; - for (FontRenderIndex_T i = (FontRenderIndex_T)handle.index_fr + 1; FontRenderIndex{i} < ft->h_render; ++i) { - fr = &ft->renders[i]; - fr->buffer_start_index = buffer_start_index; - buffer_start_index += fr->n_glyphs; - } - if (fr->text.reserved > 0) { - // 2025-04-16 - JCB - // not passing a specific bucket because pk_cstr doesn't store it. - // font.cpp assumes the user has already cloned the string if necessary - only assigns the value. - pk_delete_arr<char>(fr->text.val, fr->text.reserved); - } ECS_MarkForRemoval(fr); } diff --git a/src/font.hpp b/src/font.hpp index 912327e..75035b8 100644 --- a/src/font.hpp +++ b/src/font.hpp @@ -7,6 +7,9 @@ #include "vendor-glm-include.hpp" +#define PKE_FONT_MAX_FONT_TYPES 64 +#define PKE_FONT_MAX_FONT_RENDERS 64 + TypeSafeInt_H(FontTypeIndex, uint16_t, 0xFFFF); TypeSafeInt_H(FontRenderIndex, uint16_t, 0xFFFF); @@ -65,12 +68,12 @@ struct FontRenderSettings { FONT_RENDER_SURFACE_AREA_TYPE_FLAG surface_area_type_flags; // TODO }; struct FontRender : public Entity_Base { - uint32_t *glyph_indices; - FontRenderHandle fr_handle; - uint32_t n_glyphs; - uint32_t buffer_start_index; - FontRenderSettings settings; - pk_cstr text; + uint32_t *glyph_indices = nullptr; + FontRenderHandle fr_handle = FontRenderHandle_MAX; + uint32_t n_glyphs = 0; + uint32_t buffer_start_index = 0; + FontRenderSettings settings{}; + pk_cstr text{}; }; struct FontTypeMSDFSettings { float minimum_scale; @@ -94,7 +97,7 @@ struct FontType : public Entity_Base { glm::vec2 atlas_size; uint32_t n_glyphs; FontRenderIndex n_render = FontRenderIndex{0}; - FontRenderIndex h_render = FontRenderIndex{0}; + uint64_t unused_frs; FontTypeIndex index_ft = FontTypeIndex{0}; struct FontTypeMSDFSettings msdf_settings; struct FontTypeSpacing spacing; @@ -127,9 +130,8 @@ void FontType_Serialize(std::ostream &stream, FontType *ft); void FontType_Deserialize(std::istream &stream); FontType* FontType_Get(FontTypeIndex idx); FontType* FontType_GetByTitle(const pk_cstr title); -FontType* FontType_GetFonts(FontTypeIndex &count); +FontType* FontType_GetFonts(uint64_t &idx_unused); FontTypeIndex FontType_RegisterFont(pk_cstr title, AssetHandle fontTextureHandle, AssetHandle glyphsHandle, FontTypeMSDFSettings *msdf_settings, FontTypeSpacing *spacing); -void FontType_Unload(FontTypeIndex idx); FontRenderHandle FontType_AddStringRender(FontTypeIndex idx_ft, const pk_cstr &&str, FontRenderSettings *settings, Entity_Base *parent = nullptr, pk_uuid uuid = pk_uuid_zed); FontRender *FontType_GetFontRender(FontRenderHandle frh); void FontType_UpdateStringRender(FontRenderHandle frh, FontRenderSettings *settings); diff --git a/src/game.cpp b/src/game.cpp index 448bf3d..ef3f2fb 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -104,11 +104,13 @@ void Game_Tick(double delta) { */ ECS_Tick_Early(delta); - EntityType_Tick(delta); - ECS_Tick(delta); pke_input_tick(delta); + + EntityType_Tick(delta); + pke_level_tick(delta); pke_ui_tick(delta); - FontType_Tick(delta); + pke_scene_tick(delta); + PkeCamera_Tick(delta); for (long i = 0; i < LoadedPkePlugins.next; ++i) { if (LoadedPkePlugins[i].OnTick != nullptr) { @@ -120,7 +122,8 @@ void Game_Tick(double delta) { reinterpret_cast<void(*)(double delta)>(pkeSettings.rt.activeLevel->pke_cb_tick.func)(delta); } - PkeCamera_Tick(delta); + FontType_Tick(delta); + ECS_Tick(delta); pke_audio_tick(delta); @@ -146,9 +149,9 @@ void Game_Main(PKEWindowProperties windowProps, const char *executablePath) { CreateWindow(windowProps); EntityType_Init(); pke_input_init(); + FontType_Init(); pke_ui_init(); pke_ui_init_bindings(); - FontType_Init(); PkeProject_Load(pkeSettings.args.projectPath); if (pkeSettings.args.pluginPath != nullptr) { PkePlugin_Load(pkeSettings.args.pluginPath); @@ -288,8 +291,8 @@ GAME_SHUTDOWN: } PkePlugin_Teardown(); EntityType_Teardown(); - FontType_Teardown(); pke_ui_teardown(); + FontType_Teardown(); pke_input_teardown(); pke_scene_master_teardown(); pke_level_teardown(); diff --git a/src/level.cpp b/src/level.cpp index 120ef57..27e7722 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -4,6 +4,7 @@ #include "ecs.hpp" #include "pk.h" #include "scene.hpp" +#include "static-ui.hpp" struct level_mstr { pk_membucket *bkt; @@ -98,6 +99,25 @@ struct pk_bkt_arr *pke_level_get_levels() { return &level_mstr.bc; } +void pke_level_tick(double delta) { + (void)delta; + pk_iter_t<pke_level> iter; + bool b; + + b = pk_bkt_arr_iter_begin(&level_mstr.bc, &iter); + while (b == true) { + if (iter->isMarkedForRemoval) { + pk_arr_reset(&iter->scene_instances); + pk_arr_reset(&iter->cameras); + pk_arr_reset(&iter->root_ui_boxes); + pk_arr_reset(&iter->input_handles); + pk_bkt_arr_free_handle(&level_mstr.bc, iter->levelHandle); + new (&*iter) pke_level{}; + } + b = pk_bkt_arr_iter_increment(&level_mstr.bc, &iter); + } +} + void pke_level_teardown(pke_level *level) { uint32_t u; assert(level != nullptr); @@ -106,10 +126,10 @@ void pke_level_teardown(pke_level *level) { } ECS_MarkForRemoval(level); for (u = 0; u < level->scene_instances.next; ++u) { - pke_scene_remove(level->scene_instances[u].scene_handle); + ECS_MarkForRemoval(pke_scene_get_by_handle(level->scene_instances[u].scene_handle)); } for (u = 0; u < level->cameras.next; ++u) { - PkeCamera_Destroy(level->cameras[u]->camHandle); + ECS_MarkForRemoval(level->cameras[u]); } for (u = 0; u < level->input_handles.next; ++u) { pke_input_deactivate_set(level->input_handles[u]); @@ -124,11 +144,6 @@ void pke_level_teardown(pke_level *level) { if (level->bkt != nullptr) { pk_mem_bucket_destroy(level->bkt); } - pk_arr_reset(&level->scene_instances); - pk_arr_reset(&level->cameras); - pk_arr_reset(&level->root_ui_boxes); - pk_arr_reset(&level->input_handles); - pk_bkt_arr_free_handle(&level_mstr.bc, level->levelHandle); } void pke_level_register_camera(pke_level *level, PkeCamera *camera) { diff --git a/src/level.hpp b/src/level.hpp index e9cb0c5..b85a608 100644 --- a/src/level.hpp +++ b/src/level.hpp @@ -10,6 +10,7 @@ pke_level *pke_level_create(const char *levelName, pk_uuid level_uuid, pk_uuid e pke_level *pke_level_get(LevelHandle handle); pke_level *pke_level_get_by_name(const char *levelName); struct pk_bkt_arr *pke_level_get_levels(); +void pke_level_tick(double delta); void pke_level_teardown(pke_level *level); diff --git a/src/physics.cpp b/src/physics.cpp index 34cfbed..fe0385c 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -22,10 +22,11 @@ struct AllocedData { }; pk_arr_t<AllocedData> bulletAllocs{}; +btHashedOverlappingPairCache *overlappingPairCache = nullptr; btDefaultCollisionConfiguration *btConfiguration = nullptr; btCollisionDispatcher *btDispatcher = nullptr; -btBroadphaseInterface *btBroadphase = nullptr; -btConstraintSolver *btSolver = nullptr; +btDbvtBroadphase *btBroadphase = nullptr; +btSequentialImpulseConstraintSolver *btSolver = nullptr; struct EntityCollision { CompInstance *a, *b; @@ -111,7 +112,7 @@ void Physics_Init() { btBroadphase = pk_new<btDbvtBroadphase>(MemBkt_Bullet); #if 1 - btHashedOverlappingPairCache *overlappingPairCache = pk_new<btHashedOverlappingPairCache>(MemBkt_Bullet); + overlappingPairCache = pk_new<btHashedOverlappingPairCache>(MemBkt_Bullet); overlappingPairCache->setOverlapFilterCallback(&collisionHandlerStruct); new (btBroadphase) btDbvtBroadphase(overlappingPairCache); #else @@ -136,7 +137,22 @@ int32_t Physics_Tick(double delta) { void Physics_Teardown() { pk_arr_reset(&collisionsThisTick); - pk_delete<btDiscreteDynamicsWorld>(BtDynamicsWorld, MemBkt_Bullet); + if (BtDynamicsWorld != nullptr) + pk_delete<btDiscreteDynamicsWorld>(BtDynamicsWorld, MemBkt_Bullet); + if (overlappingPairCache != nullptr) + pk_delete<btHashedOverlappingPairCache>(overlappingPairCache, MemBkt_Bullet); + if (btConfiguration != nullptr) + pk_delete<btDefaultCollisionConfiguration>(btConfiguration, MemBkt_Bullet); + if (btSolver != nullptr) + pk_delete<btSequentialImpulseConstraintSolver>(btSolver, MemBkt_Bullet); + if (btDispatcher != nullptr) + pk_delete<btCollisionDispatcher>(btDispatcher, MemBkt_Bullet); + if (btBroadphase != nullptr) + pk_delete<btDbvtBroadphase>(btBroadphase, MemBkt_Bullet); + btConfiguration = nullptr; + btSolver = nullptr; + btBroadphase = nullptr; + btDispatcher = nullptr; BtDynamicsWorld = nullptr; // TODO should we manually delete each bullet alloc? // or just drop? diff --git a/src/project.cpp b/src/project.cpp index 62525ef..e1c3333 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -452,10 +452,11 @@ void PkeProject_Save(const char *filePath) { b = pk_bkt_arr_iter_increment(bkt_arr_ent_types, &iter_ent_type); } - FontTypeIndex font_count; - FontType *fonts = FontType_GetFonts(font_count); - for (FontTypeIndex b = FontTypeIndex{0}; b < font_count; ++b) { - FontType *ft = &fonts[(FontTypeIndex_T)b]; + uint64_t idx_unused; + FontType *fonts = FontType_GetFonts(idx_unused); + for (uint64_t u = 0; u < PKE_FONT_MAX_FONT_TYPES; ++u) { + if ((idx_unused & (1llu << u)) != 0) continue; + FontType *ft = &fonts[(FontTypeIndex_T)u]; if (ft->title.val == nullptr) continue; if (PK_HAS_FLAG(ft->entity_flags, ENTITY_FLAG_DO_NOT_SERIALIZE)) continue; stream << PKE_PROJ_FILE_OBJ_FONT << std::endl; diff --git a/src/scene.cpp b/src/scene.cpp index ecc0fc8..4d5cc76 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -86,15 +86,19 @@ pk_bkt_arr *pke_scene_get_scenes() { return &scene_mstr.bc; } -void pke_scene_remove(SceneHandle handle) { - pke_scene *scn = &scene_mstr.bc[handle]; - assert(scn != nullptr && "[pke_scene_remove] Failed to find scene by requested SceneHandle"); - ECS_MarkForRemoval(scn); - if (scn->file_path.reserved > 0) { - pk_delete_arr<char>(scn->file_path.val, scn->file_path.reserved); +void pke_scene_tick(double delta) { + (void)delta; + pk_iter_t<pke_scene> iter; + bool b; + b = pk_bkt_arr_iter_begin(&scene_mstr.bc, &iter); + while (b == true) { + if (iter->isMarkedForRemoval == true) { + if (iter->file_path.reserved > 0) { + pk_delete_arr<char>(iter->file_path.val, iter->file_path.reserved); + } + pk_bkt_arr_free_handle(&scene_mstr.bc, iter->scene_handle); + new (&*iter) pke_scene{}; + } + b = pk_bkt_arr_iter_increment(&scene_mstr.bc, &iter); } - scn->file_path = {}; - scn->name[0] = '\0'; - scn->scene_handle = SceneHandle_MAX; - pk_bkt_arr_free_handle(&scene_mstr.bc, handle); } diff --git a/src/scene.hpp b/src/scene.hpp index 70b5f06..e61b8ba 100644 --- a/src/scene.hpp +++ b/src/scene.hpp @@ -11,6 +11,6 @@ struct pke_scene *pke_scene_create(const char *scene_name); struct pke_scene *pke_scene_get_by_handle(SceneHandle scene_handle); struct pke_scene *pke_scene_get_by_name(const char *scene_name); pk_bkt_arr *pke_scene_get_scenes(); -void pke_scene_remove(SceneHandle handle); +void pke_scene_tick(double delta); #endif /* PKE_SCENE_HPP */ diff --git a/src/serialization.cpp b/src/serialization.cpp index 63123fe..dbf9646 100644 --- a/src/serialization.cpp +++ b/src/serialization.cpp @@ -80,11 +80,13 @@ void pke_serialize_scene(srlztn_serialize_helper *h) { pke_serialize_input_set(h, &sets[i]); } - FontTypeIndex font_type_count; - FontType *fonts = FontType_GetFonts(font_type_count); - for (FontTypeIndex_T b = 0; b < (FontTypeIndex_T)font_type_count; ++b) { + uint64_t idx_unused; + FontType *fonts = FontType_GetFonts(idx_unused); + for (FontTypeIndex_T b = 0; b < PKE_FONT_MAX_FONT_TYPES; ++b) { + if ((idx_unused & (1llu << b)) != 0) continue; FontType *ft = &fonts[b]; - for (FontRenderIndex_T i = 0; i < (FontRenderIndex_T)ft->h_render; ++i) { + for (FontRenderIndex_T i = 0; i < (FontRenderIndex_T)ft->n_render; ++i) { + if ((ft->unused_frs & (1llu << i)) != 0) continue; pke_serialize_font_render(h, &ft->renders[i]); } } diff --git a/src/static-ui.cpp b/src/static-ui.cpp index b4bdc5c..c8200b0 100644 --- a/src/static-ui.cpp +++ b/src/static-ui.cpp @@ -52,9 +52,10 @@ struct pke_ui_master { const pke_input_event *mouse_middle = nullptr; pke_ui_box *deepest_pressed = nullptr; } state; + pk_arr_t<pke_ui_box*> boxes_to_delete{}; } pke_ui_master; -void pke_ui_teardown_box_recursive(pke_ui_box *box); +void pke_ui_teardown_box_recursive(pke_ui_box *box, uint8_t depth); // typedef void(*pke_box_search)(pke_ui_box &box); typedef std::function<void(pke_ui_box &box)> pke_box_iterate ; @@ -554,6 +555,12 @@ void pke_ui_tick(double delta) { (void)delta; pke_ui_box_count_T i, ii; uint32_t u; + + for (u = 0; u < pke_ui_master.boxes_to_delete.next; ++u) { + pk_delete<pke_ui_box>(pke_ui_master.boxes_to_delete[u], pke_ui_master.bkt); + } + pk_arr_clear(&pke_ui_master.boxes_to_delete); + if (pke_ui_master.h_root_boxes == 0) return; pke_ui_master.state.mouse_left = pke_input_query_by_action_name(ui_ux_mouse_left); @@ -576,19 +583,17 @@ void pke_ui_tick(double delta) { // Leaving this as-is for the time being because you can control the // visibility of child boxes with flags and don't need to completely remove them. if (box->isMarkedForRemoval == true) { - pke_ui_teardown_box_recursive(box); + pke_ui_teardown_box_recursive(box, 0); for (ii = 0; i + ii + 1 < pke_ui_master.r_root_boxes; ++ii) { pke_ui_master.root_boxes[i + ii] = pke_ui_master.root_boxes[i + ii + 1]; } pke_ui_master.h_root_boxes -= 1; i -= 1; pke_ui_master.should_update_buffer = true; - goto skip_calc; + continue; } pke_ui_calc_px(arr, tmp_txtr_arr, nullptr, box); pke_ui_recalc_sizes_recursive(arr, tmp_txtr_arr, box); -skip_calc: - continue; } for (i = 0; i < tmp_txtr_arr.next; ++i) { @@ -629,50 +634,80 @@ skip_calc: } } -void pke_ui_teardown_box_recursive(pke_ui_box *box) { +void pke_ui_teardown_box_recursive(pke_ui_box *box, uint8_t depth) { + FontRender *fr; + pke_ui_graphics_bindings_texture *bindings = nullptr; for (pke_ui_box_count_T i = 0; i < box->internal.h_children; ++i) { - pke_ui_teardown_box_recursive(box->internal.children[i]); + pke_ui_teardown_box_recursive(box->internal.children[i], depth + 1); } if (box->internal.children != nullptr) { pk_delete_arr<pke_ui_box *>(box->internal.children, box->internal.r_children, pke_ui_master.bkt); } if (box->type_data != nullptr) { - if (box->type == PKE_UI_BOX_TYPE_BUTTON_IMAGE && box->type_data->button_image.gr_binds_bkt_arr_handle != pk_bkt_arr_handle_MAX) { - - pke_ui_graphics_bindings_texture *bindings = &pke_ui_master.bindings_texture[box->type_data->button_image.gr_binds_bkt_arr_handle]; - - // reminder that we are not passing the flag on the pool to manually manage descriptor sets, so all we need to do is destroy the pool - if (bindings->descriptor_pool != VK_NULL_HANDLE) { - pkvk_queue_vk_descriptor_pool_destroy(bindings->descriptor_pool); - } - if (bindings->descriptor_sets != nullptr) { - pk_delete_arr(bindings->descriptor_sets, prevSwapchainLength, MemBkt_Vulkan); - } - - if (bindings->image_view!= VK_NULL_HANDLE) { - pkvk_queue_vk_image_view_destroy(bindings->image_view); - } - if (bindings->image!= VK_NULL_HANDLE) { - pkvk_queue_vk_image_destroy(bindings->image); - } - if (bindings->image_memory != VK_NULL_HANDLE) { - pkvk_queue_vk_memory_free(bindings->image_memory); - } - - bindings->descriptor_sets = nullptr; - bindings->descriptor_pool = VK_NULL_HANDLE; - pk_bkt_arr_free_handle(&pke_ui_master.bindings_texture, box->type_data->button_image.gr_binds_bkt_arr_handle); - bindings = nullptr; + switch (box->type) { + case PKE_UI_BOX_TYPE_TEXT: + // 2025-09-16 JCB + // If correctly parented, this is unnecessary, but serialization does + // not construct text boxes in this way. + // Handle to avoid memory leaks. + fr = FontType_GetFontRender(box->type_data->text.font_render_handle); + if (fr != nullptr && fr->parentHandle != box->handle) { + FontType_RemoveStringRender(box->type_data->text.font_render_handle); + } + break; + case PKE_UI_BOX_TYPE_BUTTON_TEXT: + fr = FontType_GetFontRender(box->type_data->button_text.font_render_handle); + if (fr != nullptr && fr->parentHandle != box->handle) { + FontType_RemoveStringRender(box->type_data->button_text.font_render_handle); + } + break; + case PKE_UI_BOX_TYPE_BUTTON_IMAGE: + if (box->type_data->button_image.gr_binds_bkt_arr_handle != pk_bkt_arr_handle_MAX) { + bindings = &pke_ui_master.bindings_texture[box->type_data->button_image.gr_binds_bkt_arr_handle]; + // reminder that we are not passing the flag on the pool to manually manage descriptor sets, so all we need to do is destroy the pool + if (bindings->descriptor_pool != VK_NULL_HANDLE) { + pkvk_queue_vk_descriptor_pool_destroy(bindings->descriptor_pool); + } + if (bindings->descriptor_sets != nullptr) { + pk_delete_arr(bindings->descriptor_sets, prevSwapchainLength, MemBkt_Vulkan); + } + if (bindings->image_view!= VK_NULL_HANDLE) { + pkvk_queue_vk_image_view_destroy(bindings->image_view); + } + if (bindings->image!= VK_NULL_HANDLE) { + pkvk_queue_vk_image_destroy(bindings->image); + } + if (bindings->image_memory != VK_NULL_HANDLE) { + pkvk_queue_vk_memory_free(bindings->image_memory); + } + bindings->descriptor_sets = nullptr; + bindings->descriptor_pool = VK_NULL_HANDLE; + pk_bkt_arr_free_handle(&pke_ui_master.bindings_texture, box->type_data->button_image.gr_binds_bkt_arr_handle); + bindings = nullptr; + } + break; + case PKE_UI_BOX_TYPE_INPUT_TEXT: + // TODO + break; + case PKE_UI_BOX_TYPE_INPUT_MULTILINE_TEXT: + // TODO + break; + default: + fprintf(stderr, "[%s][pke_ui_teardown_box_recursive] unknown box type: %hhu", __FILE__, static_cast<PKE_UI_BOX_TYPE_T>(box->type)); } pk_delete<pke_ui_box_type_data>(box->type_data, pke_ui_master.bkt); + box->type_data = nullptr; + } + pk_arr_append_t<pke_ui_box*>(&pke_ui_master.boxes_to_delete, box); + if (depth != 0) { + ECS_MarkForRemoval(box); } - pk_delete<pke_ui_box>(box, pke_ui_master.bkt); pke_ui_master.should_update_buffer = true; } void pke_ui_teardown() { for (pke_ui_box_count_T i = 0; i < pke_ui_master.h_root_boxes; ++i) { - pke_ui_teardown_box_recursive(pke_ui_master.root_boxes[i]); + pke_ui_teardown_box_recursive(pke_ui_master.root_boxes[i], 0); } pk_delete_arr<pke_ui_box *>(pke_ui_master.root_boxes, pke_ui_master.r_root_boxes, pke_ui_master.bkt); pke_ui_master.bindings_texture.~texture_binding_bkt_arr(); @@ -720,6 +755,7 @@ void pke_ui_teardown() { vkFreeMemory(vkDevice, pke_ui_master.bindings.deviceMemoryVert, vkAllocator); pke_ui_master.bindings.deviceMemoryVert = VK_NULL_HANDLE; + pk_arr_reset(&pke_ui_master.boxes_to_delete); pke_input_unregister_set(pke_ui_master.input_action_set_handle); } diff --git a/src/thread-pool.cpp b/src/thread-pool.cpp index f9ff155..d040fa4 100644 --- a/src/thread-pool.cpp +++ b/src/thread-pool.cpp @@ -74,6 +74,9 @@ void inline PkeThreads_Reset_Inner(ThreadPool &tp) { tp.mutex.lock(); tp.maxJobQueueCount = 0; tp.completedCount = 0; + for (uint32_t i = 0; i < tp.threads.next; ++i) { + pk_delete<std::thread>(tp.threads[i]); + } pk_arr_clear(&tp.jobQueue); pk_arr_clear(&tp.threads); tp.mutex.unlock(); @@ -208,6 +211,13 @@ void PkeThreads_Teardown(ThreadPoolHandle handle) { PkeThreads_Shutdown_Inner(*tp); PkeThreads_JoinAll_Inner(*tp); PkeThreads_Reset_Inner(*tp); + + for (uint32_t i = 0; i < tp->threads.next; ++i) { + if (tp->threads[i] != nullptr) { + pk_delete<std::thread>(tp->threads[i]); + } + } + pk_arr_reset(&tp->jobQueue); pk_arr_reset(&tp->threads); tp->jobQueue.bkt = CAFE_BABE(struct pk_membucket); diff --git a/src/window.cpp b/src/window.cpp index 6655488..d3a7234 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -3728,11 +3728,11 @@ void RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { vkCmdSetScissor(commandBuffer, 0, 1, &scissor); // 2d - font glyphs - FontTypeIndex count; - FontType *fts = FontType_GetFonts(count); - for (FontTypeIndex i = FontTypeIndex{0}; i < count; ++i) - { - FontType *ft = &fts[(FontTypeIndex_T)i]; + uint64_t idx_unused; + FontType *fts = FontType_GetFonts(idx_unused); + for (uint32_t u = 0; u < 64; ++u) { + if ((idx_unused & (1llu << u)) != 0) continue; + FontType *ft = &fts[(FontTypeIndex_T)u]; if (ft->bindings.instance_counter == 0) continue; vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pkePipelines.pipelines.named.font_glyph); |
