summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJonathan Bradley <jcb@pikum.xyz>2025-09-16 16:58:02 -0400
committerJonathan Bradley <jcb@pikum.xyz>2025-09-16 17:00:08 -0400
commit73c5e170260638cc566cba2689ea570caee39251 (patch)
tree256b36398d33650054a885e572437909f88485cb /src
parente52ef2e49ae660833370befc34ba79d412cb4604 (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.cpp32
-rw-r--r--src/camera.hpp1
-rw-r--r--src/ecs.cpp23
-rw-r--r--src/font.cpp159
-rw-r--r--src/font.hpp20
-rw-r--r--src/game.cpp15
-rw-r--r--src/level.cpp29
-rw-r--r--src/level.hpp1
-rw-r--r--src/physics.cpp24
-rw-r--r--src/project.cpp9
-rw-r--r--src/scene.cpp24
-rw-r--r--src/scene.hpp2
-rw-r--r--src/serialization.cpp10
-rw-r--r--src/static-ui.cpp106
-rw-r--r--src/thread-pool.cpp10
-rw-r--r--src/window.cpp10
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);