diff options
| author | Jonathan Bradley <jcb@pikum.xyz> | 2025-08-19 13:51:40 -0400 |
|---|---|---|
| committer | Jonathan Bradley <jcb@pikum.xyz> | 2025-08-19 14:33:08 -0400 |
| commit | 154436ab88925540f86f43c0ac885c080949aa9b (patch) | |
| tree | 43a22f26286428f0d165fc1ff801cd0cb87092c6 /src | |
| parent | ebcae77b137a759c453b89a774ece5a755078a38 (diff) | |
pke: ui box type button image
Diffstat (limited to 'src')
| -rw-r--r-- | src/asset-manager.cpp | 2 | ||||
| -rw-r--r-- | src/asset-manager.hpp | 2 | ||||
| -rw-r--r-- | src/ecs.cpp | 19 | ||||
| -rw-r--r-- | src/entities.cpp | 4 | ||||
| -rw-r--r-- | src/game.cpp | 1 | ||||
| -rw-r--r-- | src/project.cpp | 2 | ||||
| -rw-r--r-- | src/serialization-static-ui.cpp | 108 | ||||
| -rw-r--r-- | src/serialization.hpp | 3 | ||||
| -rw-r--r-- | src/static-ui.cpp | 372 | ||||
| -rw-r--r-- | src/static-ui.hpp | 31 | ||||
| -rw-r--r-- | src/window.cpp | 427 | ||||
| -rw-r--r-- | src/window.hpp | 5 |
12 files changed, 852 insertions, 124 deletions
diff --git a/src/asset-manager.cpp b/src/asset-manager.cpp index 27fbf67..7d595ce 100644 --- a/src/asset-manager.cpp +++ b/src/asset-manager.cpp @@ -23,6 +23,7 @@ AssetKey EngineDefinedAssets[EngineDefinedAssetCount] = { "pke_glyph_frg\0\0", "pke_ui_bs_vrt\0\0", "pke_ui_bs_frg\0\0", + "pke_ui_txt_frg\0", }; void AM_Init() { @@ -36,6 +37,7 @@ void AM_Init() { AM_Register(EngineDefinedAssets[5], PKE_ASSET_TYPE_SHADER, "assets/shaders/glyph.frag.spv", nullptr); AM_Register(EngineDefinedAssets[6], PKE_ASSET_TYPE_SHADER, "assets/shaders/ui-base.vert.spv", nullptr); AM_Register(EngineDefinedAssets[7], PKE_ASSET_TYPE_SHADER, "assets/shaders/ui-base.frag.spv", nullptr); + AM_Register(EngineDefinedAssets[8], PKE_ASSET_TYPE_SHADER, "assets/shaders/ui-txtr.frag.spv", nullptr); } void AM_Load_Task(Asset &asset) { diff --git a/src/asset-manager.hpp b/src/asset-manager.hpp index a50aeac..f8ff6a8 100644 --- a/src/asset-manager.hpp +++ b/src/asset-manager.hpp @@ -33,7 +33,7 @@ const AssetType PKE_ASSET_TYPE_ALL = AssetType {0xFF}; const AssetFlags PKE_ASSET_FLAGS_NONE = AssetFlags {0x00}; const AssetFlags PKE_ASSET_FLAGS_MEM_STATIC = AssetFlags {0x01}; -constexpr int64_t EngineDefinedAssetCount = 8; +constexpr int64_t EngineDefinedAssetCount = 9; extern AssetKey EngineDefinedAssets[EngineDefinedAssetCount]; union pke_asset_details { diff --git a/src/ecs.cpp b/src/ecs.cpp index de8b0cd..0294c44 100644 --- a/src/ecs.cpp +++ b/src/ecs.cpp @@ -11,11 +11,13 @@ #include <BulletCollision/CollisionShapes/btConvexHullShape.h> #include <glm/gtc/type_ptr.hpp> +typedef pk_bkt_arr_t<Entity_Base *> type_bkt_arr_entities ; + struct ECS { struct pk_membucket *bkt = nullptr; struct ECSBucketContainers { pk_bkt_arr_t<Entity_Base> generics{}; - pk_bkt_arr_t<Entity_Base *> entityPtrs{}; + type_bkt_arr_entities entityPtrs{}; pk_bkt_arr_t<CompGrBinds> grBinds{}; pk_bkt_arr_t<CompInstance> instances{}; pk_bkt_arr_t<pke_component_event> ev_mgrs{}; @@ -87,7 +89,10 @@ EntityHandle ECS_CreateEntity(Entity_Base *entity, Entity_Base *parentEntity) { assert(entity->handle == EntityHandle_MAX && "Entity already created!"); EntityHandle entityHandle{pk_bkt_arr_new_handle(&ecs.bc.entityPtrs)}; entity->handle = entityHandle; - if (parentEntity) entity->parentHandle = parentEntity->handle; + entity->parentHandle = EntityHandle_MAX; + if (parentEntity != nullptr) { + entity->parentHandle = parentEntity->handle; + } if (entity->uuid == pk_uuid_max || entity->uuid == pk_uuid_zed) entity->uuid = pk_uuid_new_v7(); ecs.bc.entityPtrs[entityHandle] = entity; return entityHandle; @@ -123,6 +128,7 @@ pk_bkt_arr *ECS_GetEntities() { void ECS_Tick_Early(double delta) { // these reserves might happen 1 tick early, but that's fine (void)delta; + pk_arr_clear(&EntitiesToBeRemoved); bool shouldRun = entitiesMarkedForRemoval.next > 0 || EntitiesToBeRemoved.next > 0 || entitiesYetToBeRemoved.next > 0; pk_arr_reserve(&entitiesYetToBeRemoved, entitiesMarkedForRemoval.reserved); pk_arr_reserve(&EntitiesToBeRemoved, entitiesMarkedForRemoval.reserved); @@ -130,6 +136,7 @@ void ECS_Tick_Early(double delta) { pk_arr_clear(&entitiesYetToBeRemoved); if (!shouldRun) return; + EntitiesToBeRemoved.next = entitiesMarkedForRemoval.next; pk_arr_clear(&entitiesMarkedForRemoval); // this has the potential to be slow as balls @@ -143,9 +150,9 @@ void ECS_Tick_Early(double delta) { // That may or may not have implications about this logic. // Might need to do several passes? - auto ent_remove_cb = [](void *user_data, void *arr_obj_data) { - (void)user_data; - Entity_Base *ent = *reinterpret_cast<Entity_Base*const*>(arr_obj_data); + type_bkt_arr_entities::FN_Iter iter_tmpln; + iter_tmpln.func = [](Entity_Base **ent_ptr) { + Entity_Base *ent = *ent_ptr; Entity_Base *parentEnt = nullptr; if (ent->parentHandle != EntityHandle_MAX) { parentEnt = ecs.bc.entityPtrs[ent->parentHandle]; @@ -162,7 +169,7 @@ void ECS_Tick_Early(double delta) { pk_arr_append_t(&EntitiesToBeRemoved, ent); } }; - pk_bkt_arr_iterate(&ecs.bc.entityPtrs, ent_remove_cb, NULL); + pk_bkt_arr_iterate(&ecs.bc.entityPtrs, type_bkt_arr_entities::FN_Iter::invoke, &iter_tmpln); } struct updateGrBindsAfter { diff --git a/src/entities.cpp b/src/entities.cpp index 33d9bb7..82b10a8 100644 --- a/src/entities.cpp +++ b/src/entities.cpp @@ -966,7 +966,7 @@ void EntityType_LoadMesh(EntityType_LoadHelperStruct &helper, const int64_t mesh // index index = 3; offsetIndex = runningOffset; - sizeIndex = sizeof(uint16_t) * etdHelper.physDbg.indexes.next; + sizeIndex = sizeof(uint16_t) * etdHelper.physDbg.indexes.next; etdHelper.etd->grBinds->physIndxBD.firstBinding = index; etdHelper.etd->grBinds->physIndxBD.bindingCount = 1; alignmentPadding = sizeIndex % helper.physVertMemoryRequirementsCombined.alignment; @@ -1006,7 +1006,7 @@ void EntityType_LoadMesh(EntityType_LoadHelperStruct &helper, const int64_t mesh dstPtr = static_cast<char *>(tmpBufferDetails.deviceData) + runningOffset; srcPtr = reinterpret_cast<char *>(etdHelper.physDbg.indexes.data); - memcpy(dstPtr, srcPtr, sizeIndex); + memcpy(dstPtr, srcPtr, sizeof(uint16_t) * etdHelper.physDbg.indexes.next); VkCommandBufferBeginInfo vkCommandBufferBeginInfo; vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; diff --git a/src/game.cpp b/src/game.cpp index 27c42ac..5dc149f 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1,6 +1,7 @@ #include "game.hpp" +#include "asset-manager.hpp" #include "audio.hpp" #include "camera.hpp" #include "components.hpp" diff --git a/src/project.cpp b/src/project.cpp index cf774a1..c981f03 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -361,7 +361,7 @@ void PkeProject_Load(const char *filePath) { const char *safeFilePath = filePath == nullptr ? PKE_PROJ_DEFAULT_FILENAME : filePath; std::ifstream f(safeFilePath); if (!f.is_open()) { - fprintf(stderr, "While attempting to load project file, failed to open requested file for reading: %s", safeFilePath); + fprintf(stderr, "While attempting to load project file, failed to open requested file for reading: %s\n", safeFilePath); return; } memset(projReadLine, '\0', projReadLineLength); diff --git a/src/serialization-static-ui.cpp b/src/serialization-static-ui.cpp index 9ed86a0..1fa7cfd 100644 --- a/src/serialization-static-ui.cpp +++ b/src/serialization-static-ui.cpp @@ -1,11 +1,61 @@ #include "serialization-static-ui.hpp" +#include "asset-manager.hpp" #include "compile-time-assert.hpp" #include "ecs.hpp" #include "font.hpp" #include "pk.h" +#include "serialization.hpp" #include "static-ui.hpp" +pk_handle pke_serialize_ui_box_internal(srlztn_serialize_helper *h, pke_ui_box_type_data::pke_ui_box_type_data_button_image *data) { + char *s; + pke_kve kve{}; + pke_kve_container kvec{}; + + kvec.srlztn_handle = h->handle_head; + kvec.type_code = cstring_to_pk_cstr(SRLZTN_OBJ_UI_BOX_TYPE_DATA); + kvec.bkt = h->bkt; + kvec.arr.bkt = h->bkt; + kvec.children.bkt = h->bkt; + kvec.child_handles.bkt = h->bkt; + h->handle_head.itemIndex++; + + compt_a<64==sizeof(pke_ui_box_type_data::pke_ui_box_type_data_button_image)>(); + { + kve.key = SRLZTN_UI_BOX_DATA_BUTTON_IMAGE_ASSET_KEY_DEFAULT; + s = pk_new_arr<char>(AssetKeyLength, h->bkt); + memcpy(s, data->img_key_default, AssetKeyLength); + kve.val = s; + kve.end = SRLZTN_KVE_END; + pk_arr_append_t(&kvec.arr, kve); + } + { + kve.key = SRLZTN_UI_BOX_DATA_BUTTON_IMAGE_ASSET_KEY_HOVERED; + s = pk_new_arr<char>(AssetKeyLength, h->bkt); + memcpy(s, data->img_key_hovered, AssetKeyLength); + kve.val = s; + kve.end = SRLZTN_KVE_END; + pk_arr_append_t(&kvec.arr, kve); + } + { + kve.key = SRLZTN_UI_BOX_DATA_BUTTON_IMAGE_ASSET_KEY_PRESSED; + s = pk_new_arr<char>(AssetKeyLength, h->bkt); + memcpy(s, data->img_key_pressed, AssetKeyLength); + kve.val = s; + kve.end = SRLZTN_KVE_END; + pk_arr_append_t(&kvec.arr, kve); + } + // 2025-08-19 TODO JCB + // pressed callback + + pk_arr_append_t(&h->kvp_containers, kvec); + kvec.arr.data = nullptr; + kvec.children.data = nullptr; + kvec.child_handles.data = nullptr; + return kvec.srlztn_handle; +} + pk_handle pke_serialize_ui_box_internal(srlztn_serialize_helper *h, pke_ui_box_type_data::pke_ui_box_type_data_text *data) { char *s; pke_kve kve{}; @@ -29,6 +79,8 @@ pk_handle pke_serialize_ui_box_internal(srlztn_serialize_helper *h, pke_ui_box_t kve.end = SRLZTN_KVE_END; pk_arr_append_t(&kvec.arr, kve); } + // 2025-08-19 TODO JCB + // pressed callback pk_arr_append_t(&h->kvp_containers, kvec); kvec.arr.data = nullptr; @@ -56,6 +108,49 @@ void pke_deserialize_ui_box_internal(srlztn_deserialize_helper *h, pke_kve_conta data->font_render_handle = fr->fr_handle; } +void pke_deserialize_ui_box_internal(srlztn_deserialize_helper *h, pke_kve_container *kvec, pke_ui_box_type_data::pke_ui_box_type_data_button_image *data) { + (void)h; + uint32_t u; + pke_kve *kve; + assert(kvec != nullptr); + assert(data != nullptr); + assert(kvec->arr.next == 3); + + data->gr_binds_bkt_arr_handle = pk_bkt_arr_handle_MAX; + data->pke_event_handle = PkeEventMgrHandle_MAX; + data->ev_id = {}; + + compt_a<64==sizeof(pke_ui_box_type_data::pke_ui_box_type_data_button_image)>(); + for (u = 0; u < kvec->arr.next; ++u) { + kve = &kvec->arr[u]; + if (strstr(SRLZTN_UI_BOX_DATA_BUTTON_IMAGE_ASSET_KEY_DEFAULT, kve->key)) { + // TODO there's gotta be a more elegant solution to this + if (strlen(kve->val) < AssetKeyLength) { + sprintf(data->img_key_default, "%s", kve->val); + } else { + memcpy(data->img_key_default, kve->val, AssetKeyLength); + } + continue; + } + if (strstr(SRLZTN_UI_BOX_DATA_BUTTON_IMAGE_ASSET_KEY_HOVERED, kve->key)) { + if (strlen(kve->val) < AssetKeyLength) { + sprintf(data->img_key_hovered, "%s", kve->val); + } else { + memcpy(data->img_key_hovered, kve->val, AssetKeyLength); + } + continue; + } + if (strstr(SRLZTN_UI_BOX_DATA_BUTTON_IMAGE_ASSET_KEY_PRESSED, kve->key)) { + if (strlen(kve->val) < AssetKeyLength) { + sprintf(data->img_key_pressed, "%s", kve->val); + } else { + memcpy(data->img_key_pressed, kve->val, AssetKeyLength); + } + continue; + } + } +} + pk_handle pke_serialize_ui_box(srlztn_serialize_helper *h, pke_ui_box *box) { char *s; int len; @@ -67,6 +162,9 @@ pk_handle pke_serialize_ui_box(srlztn_serialize_helper *h, pke_ui_box *box) { if (box->type == PKE_UI_BOX_TYPE_TEXT) { pk_arr_append_t(&kvec.child_handles, pke_serialize_ui_box_internal(h, &box->type_data->text)); } + if (box->type == PKE_UI_BOX_TYPE_BUTTON_IMAGE) { + pk_arr_append_t(&kvec.child_handles, pke_serialize_ui_box_internal(h, &box->type_data->button_image)); + } kvec.srlztn_handle = h->handle_head; kvec.type_code = cstring_to_pk_cstr(SRLZTN_OBJ_UI_BOX); @@ -325,6 +423,7 @@ void pke_deserialize_ui_box(srlztn_deserialize_helper *h, pke_kve_container *kve case PKE_UI_BOX_TYPE_STANDARD: case PKE_UI_BOX_TYPE_TEXT: case PKE_UI_BOX_TYPE_INPUT_TEXT: + case PKE_UI_BOX_TYPE_BUTTON_IMAGE: break; default: fprintf(stderr, "[pke_deserialize_ui_box] Parsed unknown ui box data type from: '%s'\n", kve->val); @@ -372,6 +471,9 @@ void pke_deserialize_ui_box(srlztn_deserialize_helper *h, pke_kve_container *kve case PKE_UI_BOX_TYPE_TEXT: pke_deserialize_ui_box_internal(h, kvec->children[0], &bx.type_data->text); break; + case PKE_UI_BOX_TYPE_BUTTON_IMAGE: + pke_deserialize_ui_box_internal(h, kvec->children[0], &bx.type_data->button_image); + break; default: break; } @@ -394,11 +496,15 @@ void pke_deserialize_ui_box(srlztn_deserialize_helper *h, pke_kve_container *kve // TODO consider type-specific clone functions if (box->type_data != nullptr && bx.type_data != nullptr) { - *box->type_data = *box->type_data; + *box->type_data = *bx.type_data; pk_delete<pke_ui_box_type_data>(bx.type_data); bx.type_data = nullptr; } + if (box->type == PKE_UI_BOX_TYPE_BUTTON_IMAGE) { + pke_ui_box_update_textures(box); + } + srlztn_ecs_mapping map{}; map.serialized_uuid = bx.uuid; map.created_entity = box; diff --git a/src/serialization.hpp b/src/serialization.hpp index 9e592e0..4d95d61 100644 --- a/src/serialization.hpp +++ b/src/serialization.hpp @@ -61,6 +61,9 @@ iccsc SRLZTN_UI_BOX_TYPE = "Type:"; iccsc SRLZTN_UI_BOX_COLOR_BORDER = "ColorBorder:"; iccsc SRLZTN_UI_BOX_COLOR_BACKGROUND = "ColorBackground:"; iccsc SRLZTN_UI_BOX_DATA_TEXT_FONT_RENDER_UUID = "FontRenderUUID:"; +iccsc SRLZTN_UI_BOX_DATA_BUTTON_IMAGE_ASSET_KEY_DEFAULT = "AssetKeyDefault:"; +iccsc SRLZTN_UI_BOX_DATA_BUTTON_IMAGE_ASSET_KEY_HOVERED = "AssetKeyHovered:"; +iccsc SRLZTN_UI_BOX_DATA_BUTTON_IMAGE_ASSET_KEY_PRESSED = "AssetKeyPressed:"; iccsc SRLZTN_UI_FONT_RENDER_UUID = "UUID:"; iccsc SRLZTN_UI_FONT_RENDER_FONT_TYPE_TITLE = "FontTypeTitle:"; diff --git a/src/static-ui.cpp b/src/static-ui.cpp index 7e500d9..d8990a3 100644 --- a/src/static-ui.cpp +++ b/src/static-ui.cpp @@ -30,15 +30,18 @@ struct pke_ui_box_instance_buffer_item { glm::vec4 color_background; glm::vec2 px_scale; float depth; - float padding[1]; + float texture_layer; }; +typedef pk_bkt_arr_t<pke_ui_graphics_bindings_texture> texture_binding_bkt_arr; + struct pke_ui_master { pk_membucket *bkt; pke_ui_box **root_boxes; pke_ui_box_count_T h_root_boxes; pke_ui_box_count_T r_root_boxes; pke_ui_graphics_bindings bindings; + texture_binding_bkt_arr bindings_texture{}; bool should_update_buffer = false; glm::vec2 px_scale; pk_arr_t<pke_input_event> events_this_tick; @@ -51,6 +54,8 @@ struct pke_ui_master { } state; } pke_ui_master; +void pke_ui_teardown_box_recursive(pke_ui_box *box); + // typedef void(*pke_box_search)(pke_ui_box &box); typedef std::function<void(pke_ui_box &box)> pke_box_iterate ; @@ -75,10 +80,12 @@ void pke_ui_internal_box_iterate_top_down_recursive(pke_box_iterate *iters, int void pke_ui_init() { pke_ui_master.bkt = pk_mem_bucket_create("pke ui", PK_MEM_DEFAULT_BUCKET_SIZE, PK_MEMBUCKET_FLAG_NONE); - pke_ui_master.root_boxes = pk_new_arr<pke_ui_box*>(1, pke_ui_master.bkt); + pke_ui_master.r_root_boxes = 8; + pke_ui_master.root_boxes = pk_new_arr<pke_ui_box*>(pke_ui_master.r_root_boxes, pke_ui_master.bkt); pke_ui_master.h_root_boxes = 0; - pke_ui_master.r_root_boxes = 1; pke_ui_master.bindings = {}; + pke_ui_master.events_this_tick.bkt = pke_ui_master.bkt; + new (&pke_ui_master.bindings_texture) texture_binding_bkt_arr{ pk_bkt_arr_handle_MAX_constexpr, pke_ui_master.bkt, MemBkt_Vulkan }; pke_input_set ui_controls_set{}; ui_controls_set.title = "debug-editor-controls"; @@ -230,7 +237,7 @@ struct pke_ui_flex_params { float unit_total; }; -void pke_ui_calc_px(pk_arr_t<pke_ui_box_instance_buffer_item> &buffer, pke_ui_flex_params *flex_params, pke_ui_box *box) { +void pke_ui_calc_px(pk_arr_t<pke_ui_box_instance_buffer_item> &buffer, pk_arr_t<std::pair<pke_ui_box*,pke_ui_box_instance_buffer_item>> &tmp_txtr_buffer, pke_ui_flex_params *flex_params, pke_ui_box *box) { assert(box != nullptr); glm::vec2 px_size; glm::vec2 px_min_size; @@ -238,6 +245,8 @@ void pke_ui_calc_px(pk_arr_t<pke_ui_box_instance_buffer_item> &buffer, pke_ui_fl glm::vec2 parent_size_padded; glm::vec2 parent_pos_and_offset; + if (box->isMarkedForRemoval == true) return; + assert(box->pos_top_left.x >= 0.0); assert(box->pos_top_left.y >= 0.0); assert(box->max_size.x >= 0.0); @@ -387,17 +396,31 @@ void pke_ui_calc_px(pk_arr_t<pke_ui_box_instance_buffer_item> &buffer, pke_ui_fl tmp.px_scale.x = (2.0 / (float)Extent.width); tmp.px_scale.y = (2.0 / (float)Extent.height); tmp.depth = (float)box->layer; - pk_arr_append_t(&buffer, tmp); + tmp.texture_layer = -1; + if (PK_HAS_FLAG(box->type, PKE_UI_BOX_TYPE_BUTTON_IMAGE)) { + if (PK_HAS_FLAG(box->state_flags_mem, PKE_UI_BOX_STATE_FLAG_ON_BTN_PRIMARY_PRESS) && PK_HAS_FLAG(box->state_flags, PKE_UI_BOX_STATE_FLAG_MOUSE_HOVER)) { + tmp.texture_layer = 2; + } else if (PK_HAS_FLAG(box->state_flags, PKE_UI_BOX_STATE_FLAG_MOUSE_HOVER)) { + tmp.texture_layer = 1; + } else { + tmp.texture_layer = 0; + } + pk_arr_append_t(&tmp_txtr_buffer, {box, tmp}); + } else { + pk_arr_append_t(&buffer, tmp); + } } -void pke_ui_recalc_sizes_recursive(pk_arr_t<pke_ui_box_instance_buffer_item> &arr, pke_ui_box *box) { +void pke_ui_recalc_sizes_recursive(pk_arr_t<pke_ui_box_instance_buffer_item> &arr, pk_arr_t<std::pair<pke_ui_box*,pke_ui_box_instance_buffer_item>> &tmp_txtr_buffer, pke_ui_box *box) { PKE_UI_BOX_FLAG_T flags_masked; uint8_t flex_count = 0; pke_ui_flex_params flex_params{}; + if (box->isMarkedForRemoval == true) return; + flags_masked = PKE_UI_BOX_FLAG_T(box->flags & PKE_UI_BOX_FLAG_POSITION_TYPE_ALL); if (flags_masked == 0b000 || flags_masked == 0b011 || flags_masked == 0b101 || flags_masked == 0b110 || flags_masked == 0b111) { - fprintf(stderr, "[%s] ui box invalid flags: position type", __FILE__); + fprintf(stderr, "[%s] ui box invalid flags: position type\n", __FILE__); return; } @@ -409,7 +432,7 @@ void pke_ui_recalc_sizes_recursive(pk_arr_t<pke_ui_box_instance_buffer_item> &ar } if (flex_count != 0 && flex_count != box->internal.h_children) { - fprintf(stderr, "[%s] ui box invalid flags: if one child is type FLEX, then all children must be type FLEX", __FILE__); + fprintf(stderr, "[%s] ui box invalid flags: if one child is type FLEX, then all children must be type FLEX\n", __FILE__); return; } if (flex_count != 0) { @@ -420,13 +443,23 @@ void pke_ui_recalc_sizes_recursive(pk_arr_t<pke_ui_box_instance_buffer_item> &ar } for (pke_ui_box_count_T i = 0; i < box->internal.h_children; ++i) { - pke_ui_calc_px(arr, &flex_params, box->internal.children[i]); - pke_ui_recalc_sizes_recursive(arr, box->internal.children[i]); + pke_ui_calc_px(arr, tmp_txtr_buffer, &flex_params, box->internal.children[i]); + pke_ui_recalc_sizes_recursive(arr, tmp_txtr_buffer, box->internal.children[i]); } } +int pke_ui_box_ibi_sort_for_buffer(const void *lhs, const void *rhs) { + const pke_ui_box_instance_buffer_item *l = reinterpret_cast<const pke_ui_box_instance_buffer_item*>(lhs); + const pke_ui_box_instance_buffer_item *r = reinterpret_cast<const pke_ui_box_instance_buffer_item*>(rhs); + return l->texture_layer > r->texture_layer; +} + void pke_ui_update_instance_buffer(pk_arr_t<pke_ui_box_instance_buffer_item> &arr) { VkResult vkResult; + if (arr.next == 0) { + pke_ui_master.bindings.instance_counter = 0; + return; + } if (arr.next > pke_ui_master.bindings.instance_buffer_max_count) { VkBuffer newBuffer; VkBufferCreateInfo bufferCI; @@ -473,51 +506,54 @@ void pke_ui_update_instance_buffer(pk_arr_t<pke_ui_box_instance_buffer_item> &ar pke_ui_master.bindings.deviceMemoryInst = new_memory; pke_ui_master.bindings.instance_buffer_max_count = arr.next; } - PKVK_TmpBufferDetails tmpBufferDetails{}; - PKVK_BeginBuffer(graphicsFamilyIndex, sizeof(pke_ui_box_instance_buffer_item) * arr.next, tmpBufferDetails); - { - VkCommandBufferBeginInfo vkCommandBufferBeginInfo; - vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - vkCommandBufferBeginInfo.pNext = nullptr; - // TODO consider single-use? - vkCommandBufferBeginInfo.flags = 0; - vkCommandBufferBeginInfo.pInheritanceInfo = nullptr; - vkResult = vkBeginCommandBuffer(tmpBufferDetails.cmdBuffer, &vkCommandBufferBeginInfo); - assert(vkResult == VK_SUCCESS); - - memcpy(tmpBufferDetails.deviceData, arr.data, sizeof(pke_ui_box_instance_buffer_item) * arr.next); - - VkBufferCopy vk_buffer_copy{}; - vk_buffer_copy.srcOffset = 0; - vk_buffer_copy.dstOffset = 0; - vk_buffer_copy.size = sizeof(pke_ui_box_instance_buffer_item) * arr.next; - vkCmdCopyBuffer(tmpBufferDetails.cmdBuffer, tmpBufferDetails.buffer, pke_ui_master.bindings.bd_instance.buffer, 1, &vk_buffer_copy); - - vkResult = vkEndCommandBuffer(tmpBufferDetails.cmdBuffer); - assert(vkResult == VK_SUCCESS); - - VkSubmitInfo submitInfo; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.pNext = nullptr; - submitInfo.waitSemaphoreCount = 0; - submitInfo.pWaitSemaphores = nullptr; - submitInfo.pWaitDstStageMask = nullptr; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &tmpBufferDetails.cmdBuffer; - submitInfo.signalSemaphoreCount = 0; - submitInfo.pSignalSemaphores = nullptr; - vkResult = vkQueueSubmit(tmpBufferDetails.queue, 1, &submitInfo, nullptr); - assert(vkResult == VK_SUCCESS); - vkResult = vkQueueWaitIdle(tmpBufferDetails.queue); - assert(vkResult == VK_SUCCESS); + if (arr.next > 0 && arr.data != nullptr ) { + PKVK_TmpBufferDetails tmpBufferDetails{}; + PKVK_BeginBuffer(graphicsFamilyIndex, sizeof(pke_ui_box_instance_buffer_item) * arr.next, tmpBufferDetails); + { + VkCommandBufferBeginInfo vkCommandBufferBeginInfo; + vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + vkCommandBufferBeginInfo.pNext = nullptr; + // TODO consider single-use? + vkCommandBufferBeginInfo.flags = 0; + vkCommandBufferBeginInfo.pInheritanceInfo = nullptr; + vkResult = vkBeginCommandBuffer(tmpBufferDetails.cmdBuffer, &vkCommandBufferBeginInfo); + assert(vkResult == VK_SUCCESS); + + memcpy(tmpBufferDetails.deviceData, arr.data, sizeof(pke_ui_box_instance_buffer_item) * arr.next); + + VkBufferCopy vk_buffer_copy{}; + vk_buffer_copy.srcOffset = 0; + vk_buffer_copy.dstOffset = 0; + vk_buffer_copy.size = sizeof(pke_ui_box_instance_buffer_item) * arr.next; + vkCmdCopyBuffer(tmpBufferDetails.cmdBuffer, tmpBufferDetails.buffer, pke_ui_master.bindings.bd_instance.buffer, 1, &vk_buffer_copy); + + vkResult = vkEndCommandBuffer(tmpBufferDetails.cmdBuffer); + assert(vkResult == VK_SUCCESS); + + VkSubmitInfo submitInfo; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.pNext = nullptr; + submitInfo.waitSemaphoreCount = 0; + submitInfo.pWaitSemaphores = nullptr; + submitInfo.pWaitDstStageMask = nullptr; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &tmpBufferDetails.cmdBuffer; + submitInfo.signalSemaphoreCount = 0; + submitInfo.pSignalSemaphores = nullptr; + vkResult = vkQueueSubmit(tmpBufferDetails.queue, 1, &submitInfo, nullptr); + assert(vkResult == VK_SUCCESS); + vkResult = vkQueueWaitIdle(tmpBufferDetails.queue); + assert(vkResult == VK_SUCCESS); + } + PKVK_EndBuffer(tmpBufferDetails); } - PKVK_EndBuffer(tmpBufferDetails); pke_ui_master.bindings.instance_counter = arr.next; } void pke_ui_tick(double delta) { (void)delta; - pke_ui_box_count_T i; + pke_ui_box_count_T i, ii; + uint32_t u; 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); @@ -525,15 +561,50 @@ void pke_ui_tick(double delta) { pke_ui_master.state.mouse_middle = pke_input_query_by_action_name(ui_ux_mouse_middle); pke_ui_master.state.deepest_pressed = nullptr; - pk_arr_t<pke_ui_box_instance_buffer_item> arr; + pk_arr_t<pke_ui_box_instance_buffer_item> arr{}; + arr.bkt = pkeSettings.mem_bkt.game_transient; + pk_arr_t<std::pair<pke_ui_box*,pke_ui_box_instance_buffer_item>> tmp_txtr_arr; + tmp_txtr_arr.bkt = pkeSettings.mem_bkt.game_transient; pke_ui_master.px_scale = glm::vec2( 2.0 / (float)Extent.width, 2.0 / (float)Extent.height ); for (i = 0; i < pke_ui_master.h_root_boxes; ++i) { pke_ui_box *box = pke_ui_master.root_boxes[i]; - pke_ui_calc_px(arr, nullptr, box); - pke_ui_recalc_sizes_recursive(arr, box); + // 2025-08-18 JCB + // This currently only calls tear-down for boxes on the root list. + // 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. + for (u = 0; u < EntitiesToBeRemoved.next; ++u) { + if (EntitiesToBeRemoved[u] == box) { + pke_ui_teardown_box_recursive(box); + 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; + } + } + 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) { + const std::pair<pke_ui_box*,pke_ui_box_instance_buffer_item> &pair = tmp_txtr_arr[i]; + if (pair.first->type_data->button_image.gr_binds_bkt_arr_handle == pk_bkt_arr_handle_MAX) { + continue; + } + pke_ui_graphics_bindings_texture *bindings = &pke_ui_master.bindings_texture[pair.first->type_data->button_image.gr_binds_bkt_arr_handle]; + if (bindings == nullptr || bindings->descriptor_sets == nullptr) { + continue; + } + bindings->instance_count = 1; + bindings->instance_offset = arr.next; + pk_arr_append_t(&arr, pair.second); } if (pke_ui_master.should_update_buffer || pkeSettings.rt.was_framebuffer_resized) { @@ -552,7 +623,7 @@ void pke_ui_tick(double delta) { component_event = ECS_GetEv(pke_ui_master.state.deepest_pressed->type_data->button_image.pke_event_handle); break; default: - fprintf(stderr, "[static-ui] Something was pressed but we didn't know the type and couldn't emit."); + fprintf(stderr, "[static-ui] Something was pressed but we didn't know the type and couldn't emit.\n"); } if (component_event != nullptr) { pk_ev_emit(component_event->ev_mgr_id, component_event->ev_id, nullptr); @@ -567,6 +638,37 @@ void pke_ui_teardown_box_recursive(pke_ui_box *box) { 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; + } + pk_delete<pke_ui_box_type_data>(box->type_data, pke_ui_master.bkt); + } + pk_delete<pke_ui_box>(box, pke_ui_master.bkt); pke_ui_master.should_update_buffer = true; } @@ -575,6 +677,7 @@ void pke_ui_teardown() { pke_ui_teardown_box_recursive(pke_ui_master.root_boxes[i]); } 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(); pk_mem_bucket_destroy(pke_ui_master.bkt); pke_ui_master.bkt = nullptr; pke_ui_master.root_boxes = nullptr; @@ -650,13 +753,7 @@ void pke_ui_internal_new_typed_box(pke_ui_box *box, const PKE_UI_BOX_TYPE type) break; case PKE_UI_BOX_TYPE_BUTTON_IMAGE: tp_data = pk_new<pke_ui_box_type_data>(pke_ui_master.bkt); - tp_data->button_image.image_memory = VK_NULL_HANDLE; - tp_data->button_image.image_default = VK_NULL_HANDLE; - tp_data->button_image.image_view_default = VK_NULL_HANDLE; - tp_data->button_image.image_hovered = VK_NULL_HANDLE; - tp_data->button_image.image_view_hovered = VK_NULL_HANDLE; - tp_data->button_image.image_pressed = VK_NULL_HANDLE; - tp_data->button_image.image_view_pressed = VK_NULL_HANDLE; + tp_data->button_image.gr_binds_bkt_arr_handle = pk_bkt_arr_handle_MAX; pressed_ev = ECS_CreateEv(box, pk_uuid_max); tp_data->button_image.pke_event_handle = pressed_ev->pke_event_handle; tp_data->button_image.ev_id = pressed_ev->ev_id; @@ -682,8 +779,9 @@ pke_ui_box *pke_ui_box_new_root(const PKE_UI_BOX_TYPE type, pk_uuid uuid) { if (pke_ui_master.root_boxes != nullptr) pk_delete_arr<pke_ui_box*>(pke_ui_master.root_boxes, prev_r_root_boxes, pke_ui_master.bkt); pke_ui_master.root_boxes = boxes; } + assert(pke_ui_master.h_root_boxes < pke_ui_master.r_root_boxes); pke_ui_box *box = pk_new<pke_ui_box>(pke_ui_master.bkt); - *box = {}; + new (box) pke_ui_box{}; pke_ui_master.root_boxes[pke_ui_master.h_root_boxes] = box; pke_ui_master.h_root_boxes += 1; pke_ui_master.should_update_buffer = true; @@ -701,7 +799,7 @@ pke_ui_box *pke_ui_box_new_child(pke_ui_box *parent, const PKE_UI_BOX_TYPE type, // validation if (parent->type >= PKE_UI_BOX_TYPE_TEXT) { // this might be too broad, alter if needed - fprintf(stdout, "[pke_ui_box_new_child] Validation error: PKE_UI_BOX_TYPE_TEXT cannot have children."); + fprintf(stdout, "[pke_ui_box_new_child] Validation error: PKE_UI_BOX_TYPE_TEXT cannot have children.\n"); return nullptr; } @@ -731,6 +829,152 @@ pke_ui_box *pke_ui_box_new_child(pke_ui_box *parent, const PKE_UI_BOX_TYPE type, return box; } -pke_ui_graphics_bindings *pke_ui_get_graphics_bindings() { - return &pke_ui_master.bindings; +void pke_ui_box_update_textures(pke_ui_box *box) { + uint32_t i; + pke_ui_graphics_bindings_texture *bindings = nullptr; + pkvk_texture_upload_data data{}; + pkvk_texture_upload_data_out out{}; + AssetHandle handle_default; + AssetHandle handle_hovered; + AssetHandle handle_pressed; + const Asset *asset_default = nullptr; + const Asset *asset_hovered = nullptr; + const Asset *asset_pressed = nullptr; + + if (box->type != PKE_UI_BOX_TYPE_BUTTON_IMAGE) { + fprintf(stderr, "[pke_ui_box_handle_textures] attempted to process textures on non-texture typed ui box. Got type: %hhu\n", static_cast<PKE_UI_BOX_TYPE_T>(box->type)); + return; + } + + if (box->type_data->button_image.gr_binds_bkt_arr_handle == pk_bkt_arr_handle_MAX) { + box->type_data->button_image.gr_binds_bkt_arr_handle = pk_bkt_arr_new_handle(&pke_ui_master.bindings_texture); + bindings = &pke_ui_master.bindings_texture[box->type_data->button_image.gr_binds_bkt_arr_handle]; + bindings->descriptor_sets = nullptr; + bindings->descriptor_pool = VK_NULL_HANDLE; + bindings->instance_count = 0; + bindings->instance_offset = 0; + } else { + bindings = &pke_ui_master.bindings_texture[box->type_data->button_image.gr_binds_bkt_arr_handle]; + } + assert(bindings != nullptr); + + handle_default = AM_GetHandle(box->type_data->button_image.img_key_default); + handle_hovered = AM_GetHandle(box->type_data->button_image.img_key_hovered); + handle_pressed = AM_GetHandle(box->type_data->button_image.img_key_pressed); + + if (handle_default == AssetHandle_MAX) return; + if (handle_hovered == AssetHandle_MAX) return; + if (handle_pressed == AssetHandle_MAX) return; + + asset_default = AM_Get(handle_default); + asset_hovered = AM_Get(handle_hovered); + asset_pressed = AM_Get(handle_pressed); + + if (asset_default == nullptr || asset_default->type != PKE_ASSET_TYPE_TEXTURE) { + fprintf(stderr, "[pke_ui_box_update_textures] `img_key_default` was not found or was not a texture.\n"); + return; + } + if (asset_hovered == nullptr || asset_hovered->type != PKE_ASSET_TYPE_TEXTURE) { + fprintf(stderr, "[pke_ui_box_update_textures] `img_key_hovered` was not found or was not a texture.\n"); + } + if (asset_pressed == nullptr || asset_pressed->type != PKE_ASSET_TYPE_TEXTURE) { + fprintf(stderr, "[pke_ui_box_update_textures] `img_key_pressed` was not found or was not a texture.\n"); + } + + data.n_textures = 3; + data.texture_assets[0] = asset_default; + data.texture_assets[1] = asset_hovered; + data.texture_assets[2] = asset_pressed; + + pkvk_texture_upload_array(&data, &out); + + bindings->image = out.images[0]; + bindings->image_view = out.image_views[0]; + bindings->image_memory = out.device_memory; + + if (bindings->descriptor_sets != nullptr) { + vkDestroyDescriptorPool(vkDevice, bindings->descriptor_pool, vkAllocator); + pk_delete_arr<VkDescriptorSet>(bindings->descriptor_sets, prevSwapchainLength, MemBkt_Vulkan); + } + + VkDescriptorPoolSize pool_size{}; + pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + pool_size.descriptorCount = (uint32_t)swapchainLength; + + VkDescriptorPoolCreateInfo dpci{}; + dpci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + dpci.pNext = VK_NULL_HANDLE; + dpci.flags = 0; + dpci.maxSets = swapchainLength; + dpci.poolSizeCount = (uint32_t)1; + dpci.pPoolSizes = &pool_size; + + VkDescriptorSetLayout *descriptorSets = pk_new_arr<VkDescriptorSetLayout>(swapchainLength, pkeSettings.mem_bkt.game_transient); + for (i = 0; i < swapchainLength; ++i) { + descriptorSets[i] = pkePipelines.descr_layouts.named.txtr; + } + + VkDescriptorSetAllocateInfo vkDescriptorSetAllocateInfo; + vkDescriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + vkDescriptorSetAllocateInfo.pNext = nullptr; + vkDescriptorSetAllocateInfo.descriptorSetCount = swapchainLength; + vkDescriptorSetAllocateInfo.pSetLayouts = descriptorSets; + + VkWriteDescriptorSet *writeDescriptorSets = pk_new_arr<VkWriteDescriptorSet>(swapchainLength, pkeSettings.mem_bkt.game_transient); + for (i = 0; i < swapchainLength; ++i) { + writeDescriptorSets[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeDescriptorSets[i].pNext = VK_NULL_HANDLE; + writeDescriptorSets[i].dstSet = VK_NULL_HANDLE; + writeDescriptorSets[i].dstBinding = 0; + writeDescriptorSets[i].dstArrayElement = 0; + writeDescriptorSets[i].descriptorCount = 1; + writeDescriptorSets[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writeDescriptorSets[i].pImageInfo = VK_NULL_HANDLE; + writeDescriptorSets[i].pBufferInfo = VK_NULL_HANDLE; + writeDescriptorSets[i].pTexelBufferView = nullptr; + } + + VkDescriptorImageInfo textureDescriptorInfo; + textureDescriptorInfo.sampler = global_sampler; + textureDescriptorInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + textureDescriptorInfo.imageView = bindings->image_view; + + // consider making me a global pool + auto vkResult = vkCreateDescriptorPool(vkDevice, &dpci, vkAllocator, &bindings->descriptor_pool); + assert(vkResult == VK_SUCCESS); + + vkDescriptorSetAllocateInfo.descriptorPool = bindings->descriptor_pool; + + bindings->descriptor_sets = pk_new_arr<VkDescriptorSet>(swapchainLength, MemBkt_Vulkan); + + vkResult = vkAllocateDescriptorSets(vkDevice, &vkDescriptorSetAllocateInfo, bindings->descriptor_sets); + assert(vkResult == VK_SUCCESS); + + for (i = 0; i < swapchainLength; ++i) { + writeDescriptorSets[i].pImageInfo = &textureDescriptorInfo; + writeDescriptorSets[i].dstSet = bindings->descriptor_sets[i]; + } + + vkUpdateDescriptorSets(vkDevice, swapchainLength, writeDescriptorSets, 0, nullptr); + + AM_Release(handle_pressed); + AM_Release(handle_hovered); + AM_Release(handle_default); +} + +const pke_ui_graphics_bindings &pke_ui_get_graphics_bindings() { + return pke_ui_master.bindings; +} + +void pke_ui_get_graphics_bindings_texture(pk_arr *arr) { + pk_arr_t<pke_ui_graphics_bindings_texture> &arr_t = *static_cast<pk_arr_t<pke_ui_graphics_bindings_texture>*>(arr); + assert(arr_t.stride == sizeof(pke_ui_graphics_bindings_texture)); + assert(arr_t.alignment == alignof(pke_ui_graphics_bindings_texture)); + + texture_binding_bkt_arr::FN_Iter iter_fn_tmpln; + iter_fn_tmpln.func = [&arr_t](pke_ui_graphics_bindings_texture *arr_item) { + pk_arr_append_t(&arr_t, *arr_item); + }; + + pk_bkt_arr_iterate(&pke_ui_master.bindings_texture, texture_binding_bkt_arr::FN_Iter::invoke, &iter_fn_tmpln); } diff --git a/src/static-ui.hpp b/src/static-ui.hpp index 3abfd26..eb26a45 100644 --- a/src/static-ui.hpp +++ b/src/static-ui.hpp @@ -146,13 +146,10 @@ union pke_ui_box_type_data { pk_ev_id_T ev_id; } button_text; struct pke_ui_box_type_data_button_image { - VkDeviceMemory image_memory; - VkImage image_default; - VkImageView image_view_default; - VkImage image_hovered; - VkImageView image_view_hovered; - VkImage image_pressed; - VkImageView image_view_pressed; + AssetKey img_key_default; + AssetKey img_key_hovered; + AssetKey img_key_pressed; + pk_bkt_arr_handle gr_binds_bkt_arr_handle; PkeEventHandle pke_event_handle; pk_ev_id_T ev_id; } button_image; @@ -170,6 +167,18 @@ struct pke_ui_graphics_bindings { uint32_t instance_buffer_max_count; }; +/* minimum amount of data needed to reuse the normal bindings but also bind textures + */ +struct pke_ui_graphics_bindings_texture { + uint32_t instance_offset; + uint32_t instance_count; + VkDescriptorSet *descriptor_sets; + VkDescriptorPool descriptor_pool; + VkDeviceMemory image_memory; + VkImage image; + VkImageView image_view; +}; + void pke_ui_init(); void pke_ui_init_bindings(); void pke_ui_tick(double delta); @@ -180,12 +189,14 @@ pke_ui_box **pke_ui_get_root_boxes(pke_ui_box_count_T *count); pke_ui_box *pke_ui_box_new_root(const PKE_UI_BOX_TYPE type = PKE_UI_BOX_TYPE_STANDARD, pk_uuid uuid = pk_uuid_zed); pke_ui_box *pke_ui_box_new_child(pke_ui_box *parent, const PKE_UI_BOX_TYPE type = PKE_UI_BOX_TYPE_STANDARD, pk_uuid uuid = pk_uuid_zed); +void pke_ui_box_update_textures(pke_ui_box *box); #ifdef PKE_TEST_EXPOSE -void pke_ui_calc_px(pk_arr_t<pke_ui_box_instance_buffer_item> &buffer, pke_ui_flex_params *flex_params, pke_ui_box *box); -void pke_ui_recalc_sizes_recursive(pk_arr_t<pke_ui_box_instance_buffer_item> &arr, pke_ui_box *box); +void pke_ui_calc_px(pk_arr_t<pke_ui_box_instance_buffer_item> &buffer, pk_arr_t<std::pair<pke_ui_box*,pke_ui_box_instance_buffer_item>> &tmp_txtr_buffer, pke_ui_flex_params *flex_params, pke_ui_box *box); +void pke_ui_recalc_sizes_recursive(pk_arr_t<pke_ui_box_instance_buffer_item> &arr, pk_arr_t<std::pair<pke_ui_box*,pke_ui_box_instance_buffer_item>> &tmp_txtr_buffer, pke_ui_box *box); #endif -pke_ui_graphics_bindings *pke_ui_get_graphics_bindings(); +const pke_ui_graphics_bindings &pke_ui_get_graphics_bindings(); +void pke_ui_get_graphics_bindings_texture(pk_arr *arr); #endif /* PKE_STATIC_UI_HPP */ diff --git a/src/window.cpp b/src/window.cpp index 0c7f281..773c767 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -76,8 +76,11 @@ VkSampleCountFlagBits global_sample_count; struct pkvk_queued_actions { struct pkvk_queued_actions_delete { + pk_arr_t<VkImageView> image_view{}; + pk_arr_t<VkImage> image{}; pk_arr_t<VkBuffer> buffers{}; pk_arr_t<VkDeviceMemory> memory{}; + pk_arr_t<VkDescriptorPool> descriptor_pool{}; } destroy; } pkvk_actn_queue; @@ -465,6 +468,7 @@ void pkvk_texture_upload(pkvk_texture_upload_data *data, pkvk_texture_upload_dat VkDeviceSize offsets[PKVK_TEXTURE_UPLOAD_ARR_MAX_LENGTH]; VkDeviceSize size = 0; VkDeviceSize offset = 0; + VkDeviceSize padding = 0; memset(&offsets, 0, sizeof(offsets)); @@ -551,7 +555,10 @@ void pkvk_texture_upload(pkvk_texture_upload_data *data, pkvk_texture_upload_dat */ for (i = 0; i < data->n_textures; ++i) { - assert(data->texture_assets[i]->type == PKE_ASSET_TYPE_TEXTURE); + if(data->texture_assets[i]->type != PKE_ASSET_TYPE_TEXTURE) { + fprintf(stderr, "[pkvk_texture_upload] Expected all passed assets to be textures, got: %hhu for idx#%i.", static_cast<AssetType_T>(data->texture_assets[i]->type), i); + return; + } imageCreateInfo.extent.width = data->texture_assets[i]->details.texture.width; imageCreateInfo.extent.height = data->texture_assets[i]->details.texture.height; @@ -626,7 +633,8 @@ void pkvk_texture_upload(pkvk_texture_upload_data *data, pkvk_texture_upload_dat vkImageMemoryBarriers[i].image = out->images[i]; offset += size; - offset += (combined_mem_reqs.alignment - (size % combined_mem_reqs.alignment)) % combined_mem_reqs.alignment; + padding = (combined_mem_reqs.alignment - (size % combined_mem_reqs.alignment)) % combined_mem_reqs.alignment; + offset += padding; } /* @@ -712,6 +720,276 @@ void pkvk_texture_upload(pkvk_texture_upload_data *data, pkvk_texture_upload_dat PKVK_EndBuffer(tmpBufferDetails); } +void pkvk_texture_upload_array(pkvk_texture_upload_data *data, pkvk_texture_upload_data_out *out) { + uint8_t i; + int txtr_x, txtr_y, txtr_chan; + VkResult vkResult{}; + VkImage tmpImage{}; + PKVK_TmpBufferDetails tmpBufferDetails{}; + VkImageViewCreateInfo vkImageViewCreateInfo{}; + VkImageCreateInfo imageCreateInfo{}; + VkMemoryRequirements combined_mem_reqs{}; + + VkBufferImageCopy2 vkBufferImageCopies[PKVK_TEXTURE_UPLOAD_ARR_MAX_LENGTH]; + VkCopyBufferToImageInfo2 copy_buffer_to_image_2{}; + VkImageMemoryBarrier vkImageMemoryBarrier{}; + VkDeviceSize size = 0; + VkDeviceSize offset = 0; + + vkImageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + vkImageViewCreateInfo.pNext = VK_NULL_HANDLE; + vkImageViewCreateInfo.flags = 0; + vkImageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + vkImageViewCreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB; + vkImageViewCreateInfo.components = VkComponentMapping { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }; + vkImageViewCreateInfo.subresourceRange = VkImageSubresourceRange { + .aspectMask = VkImageAspectFlagBits::VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0u, + // TODO MipMap + .levelCount = 1u, + .baseArrayLayer = 0u, + .layerCount = (uint32_t)data->n_textures, + }; + + imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageCreateInfo.pNext = VK_NULL_HANDLE; + imageCreateInfo.flags = 0; + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB; + imageCreateInfo.extent.width = data->texture_assets[0]->details.texture.width; + imageCreateInfo.extent.height = data->texture_assets[0]->details.texture.height; + imageCreateInfo.extent.depth = 1; + imageCreateInfo.mipLevels = 1; + imageCreateInfo.arrayLayers = data->n_textures; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.queueFamilyIndexCount = 0; + imageCreateInfo.pQueueFamilyIndices = VK_NULL_HANDLE; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateInfo.extent = { + .width = data->texture_assets[0]->details.texture.width, + .height = data->texture_assets[0]->details.texture.height, + .depth = 1, + }; + + vkImageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + vkImageMemoryBarrier.pNext = VK_NULL_HANDLE; + vkImageMemoryBarrier.srcAccessMask = {}; + vkImageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + vkImageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + vkImageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + vkImageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vkImageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vkImageMemoryBarrier.subresourceRange = vkImageViewCreateInfo.subresourceRange; + + for (i = 0; i < data->n_textures; ++i) { + vkBufferImageCopies[i].sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2; + vkBufferImageCopies[i].pNext = VK_NULL_HANDLE; + vkBufferImageCopies[i].bufferOffset = 0; + vkBufferImageCopies[i].bufferRowLength = data->texture_assets[0]->details.texture.width; + vkBufferImageCopies[i].bufferImageHeight = data->texture_assets[0]->details.texture.width; + vkBufferImageCopies[i].bufferRowLength = 0; + vkBufferImageCopies[i].bufferImageHeight = 0; + vkBufferImageCopies[i].imageSubresource = VkImageSubresourceLayers { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = i, + .layerCount = 1, + }; + vkBufferImageCopies[i].imageOffset = VkOffset3D { + .x = 0, + .y = 0, + .z = 0, + }; + vkBufferImageCopies[i].imageExtent = VkExtent3D { + .width = data->texture_assets[0]->details.texture.width, + .height = data->texture_assets[0]->details.texture.height, + .depth = 1, + }; + } + + copy_buffer_to_image_2.sType = VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2; + copy_buffer_to_image_2.pNext = VK_NULL_HANDLE; + copy_buffer_to_image_2.srcBuffer = VK_NULL_HANDLE; + copy_buffer_to_image_2.dstImage = VK_NULL_HANDLE; + copy_buffer_to_image_2.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + copy_buffer_to_image_2.regionCount = (uint32_t)data->n_textures; + copy_buffer_to_image_2.pRegions = vkBufferImageCopies; + + /* + * get memory requirements + */ + for (i = 0; i < data->n_textures; ++i) { + if(data->texture_assets[i]->type != PKE_ASSET_TYPE_TEXTURE) { + fprintf(stderr, "[pkvk_texture_upload] Expected all passed assets to be textures, got: %hhu for idx#%i.", static_cast<AssetType_T>(data->texture_assets[i]->type), i); + return; + } + if (data->texture_assets[i]->details.texture.width != data->texture_assets[0]->details.texture.width + || data->texture_assets[i]->details.texture.height != data->texture_assets[0]->details.texture.height) { + fprintf(stderr, "[pkvk_texture_upload] Requested array but the dimensions of texture (idx %i) did not match.", i); + return; + } + } + + vkResult = vkCreateImage(vkDevice, &imageCreateInfo, vkAllocator, &tmpImage); + assert(vkResult == VK_SUCCESS); + vkGetImageMemoryRequirements(vkDevice, tmpImage, &combined_mem_reqs); + vkDestroyImage(vkDevice, tmpImage, vkAllocator); + + /* + * memory allocation + */ + + VkMemoryAllocateInfo vkMemoryAllocateInfo; + vkMemoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + vkMemoryAllocateInfo.pNext = nullptr; + vkMemoryAllocateInfo.allocationSize = combined_mem_reqs.size; + vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(combined_mem_reqs.memoryTypeBits, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); + if (vkMemoryAllocateInfo.memoryTypeIndex == 0) { + vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(combined_mem_reqs.memoryTypeBits, 0); + } + vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &out->device_memory); + + /* + * buffer setup + */ + + char *temp_buffer = (char *)malloc(combined_mem_reqs.size); + + // copy data to temp buffer + // 2025-08-06 - JCB - PERF TODO actually test performance + // It's possible that copying these to a temporary buffer is a performance loss. + // The idea here is to separate this number-crunching from the buffer recording. + // If this texture upload function ends up being a bottleneck here's a few suggestions: + // - split stbi work to separate threads (likely biggest impact) + // - reduce # of data-copies (copy straight to the vulkan buffer instead of intermediary) + for (i = 0; i < data->n_textures; ++i) { + + vkBufferImageCopies[i].bufferOffset = offset; + + /* run texture data through stbi, putting it in the VkFormat we desire, & copy */ + stbi_uc *txtr_bytes = stbi_load_from_memory((unsigned char*)data->texture_assets[i]->ptr, data->texture_assets[i]->size, &txtr_x, &txtr_y, &txtr_chan, 4); + assert(txtr_bytes != nullptr); + assert(txtr_chan == 4); + + size = sizeof(uint8_t) * txtr_x * txtr_y * txtr_chan; + memcpy(temp_buffer + offset, txtr_bytes, size); + + free(txtr_bytes); + + offset += size; + } + + // create image + + vkResult = vkCreateImage(vkDevice, &imageCreateInfo, vkAllocator, &out->images[0]); + assert(vkResult == VK_SUCCESS); + vkResult = vkBindImageMemory(vkDevice, out->images[0], out->device_memory, 0); + assert(vkResult == VK_SUCCESS); + + // create image view + + vkImageViewCreateInfo.image = out->images[0]; + vkResult = vkCreateImageView(vkDevice, &vkImageViewCreateInfo, vkAllocator, &out->image_views[0]); + assert(vkResult == VK_SUCCESS); + + // set up image copy parameters + vkImageMemoryBarrier.image = out->images[0]; + + /* + * transition layout & copy to buffer + */ + + PKVK_BeginBuffer(transferFamilyIndex, combined_mem_reqs.size, tmpBufferDetails); + + // copy data to vulkan buffer + memcpy(tmpBufferDetails.deviceData, temp_buffer, combined_mem_reqs.size); + free(temp_buffer); + + VkCommandBufferBeginInfo vkCommandBufferBeginInfo; + vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + vkCommandBufferBeginInfo.pNext = nullptr; + vkCommandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + vkCommandBufferBeginInfo.pInheritanceInfo = nullptr; + vkBeginCommandBuffer(tmpBufferDetails.cmdBuffer, &vkCommandBufferBeginInfo); + + vkCmdPipelineBarrier(tmpBufferDetails.cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &vkImageMemoryBarrier); + + copy_buffer_to_image_2.dstImage = out->images[0]; + copy_buffer_to_image_2.srcBuffer = tmpBufferDetails.buffer; + vkCmdCopyBufferToImage2(tmpBufferDetails.cmdBuffer, ©_buffer_to_image_2); + + vkEndCommandBuffer(tmpBufferDetails.cmdBuffer); + + VkSubmitInfo submitInfo; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.pNext = nullptr; + submitInfo.waitSemaphoreCount = 0; + submitInfo.pWaitSemaphores = nullptr; + submitInfo.pWaitDstStageMask = nullptr; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &tmpBufferDetails.cmdBuffer; + submitInfo.signalSemaphoreCount = 0; + submitInfo.pSignalSemaphores = nullptr; + vkQueueSubmit(tmpBufferDetails.queue, 1, &submitInfo, nullptr); + vkQueueWaitIdle(tmpBufferDetails.queue); + vkResetCommandBuffer(tmpBufferDetails.cmdBuffer, 0); + + PKVK_EndBuffer(tmpBufferDetails); + + // update command buffer data + vkImageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + vkImageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + vkImageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + vkImageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + tmpBufferDetails = {}; + PKVK_BeginBuffer(graphicsFamilyIndex, 0, tmpBufferDetails); + { + + VkCommandBufferBeginInfo vkCommandBufferBeginInfo; + vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + vkCommandBufferBeginInfo.pNext = nullptr; + vkCommandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + vkCommandBufferBeginInfo.pInheritanceInfo = nullptr; + + vkBeginCommandBuffer(tmpBufferDetails.cmdBuffer, &vkCommandBufferBeginInfo); + + vkCmdPipelineBarrier(tmpBufferDetails.cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &vkImageMemoryBarrier); + + vkEndCommandBuffer(tmpBufferDetails.cmdBuffer); + + VkSubmitInfo submitInfo; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.pNext = nullptr; + submitInfo.waitSemaphoreCount = 0; + submitInfo.pWaitSemaphores = nullptr; + submitInfo.pWaitDstStageMask = nullptr; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &tmpBufferDetails.cmdBuffer; + submitInfo.signalSemaphoreCount = 0; + submitInfo.pSignalSemaphores = nullptr; + vkQueueSubmit(tmpBufferDetails.queue, 1, &submitInfo, nullptr); + vkQueueWaitIdle(tmpBufferDetails.queue); + } + PKVK_EndBuffer(tmpBufferDetails); +} + +void pkvk_queue_vk_image_view_destroy(VkImageView image_view) { + pk_arr_append_t(&pkvk_actn_queue.destroy.image_view, image_view); +} + +void pkvk_queue_vk_image_destroy(VkImage image) { + pk_arr_append_t(&pkvk_actn_queue.destroy.image, image); +} + void pkvk_queue_vk_buffer_destroy(VkBuffer buffer) { pk_arr_append_t(&pkvk_actn_queue.destroy.buffers, buffer); } @@ -720,6 +998,10 @@ void pkvk_queue_vk_memory_free(VkDeviceMemory memory) { pk_arr_append_t(&pkvk_actn_queue.destroy.memory, memory); } +void pkvk_queue_vk_descriptor_pool_destroy(VkDescriptorPool descriptor_pool) { + pk_arr_append_t(&pkvk_actn_queue.destroy.descriptor_pool, descriptor_pool); +} + unsigned int FindQueueFamilyIndex(VkPhysicalDevice device, char hasPresentSupport = -1, VkQueueFlagBits includeBits = (VkQueueFlagBits)0U, VkQueueFlagBits excludeBits = (VkQueueFlagBits)0U) { if (hasPresentSupport == -1 && includeBits == 0 && excludeBits == 0) { @@ -1954,8 +2236,7 @@ void CreateGraphicsPipelines() { vkDescriptorSetLayoutCreateInfo.pBindings = VK_NULL_HANDLE; vkResult = vkCreateDescriptorSetLayout(vkDevice, &vkDescriptorSetLayoutCreateInfo, vkAllocator, &pkePipelines.descr_layouts.named.base); assert(vkResult == VK_SUCCESS); - assert(pkePipelines.descr_layouts.named.txtr != VK_NULL_HANDLE); - + assert(pkePipelines.descr_layouts.named.base != VK_NULL_HANDLE); VkPipelineLayoutCreateInfo vkPipelineLayoutCreateInfo { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, @@ -1987,6 +2268,7 @@ void CreateGraphicsPipelines() { AssetHandle fragGlyphAssetHandle { AM_GetHandle(AssetKey{"pke_glyph_frg\0\0"})}; AssetHandle asset_handle_vert_ui_base { AM_GetHandle(AssetKey{"pke_ui_bs_vrt\0\0"})}; AssetHandle asset_handle_frag_ui_base { AM_GetHandle(AssetKey{"pke_ui_bs_frg\0\0"})}; + AssetHandle asset_handle_frag_ui_txtr { AM_GetHandle(AssetKey{"pke_ui_txt_frg\0"})}; const long vertexBindingCount = 4; long index = 0; @@ -2189,7 +2471,7 @@ void CreateGraphicsPipelines() { offset = 0; const long vertexBindingCount_ui_base = 3; VkVertexInputBindingDescription vertInputBD_ui_base[vertexBindingCount_ui_base]; - const long vertexAttrDescCount_ui_base = 10; + const long vertexAttrDescCount_ui_base = 11; VkVertexInputAttributeDescription vertAttrDesc_ui_base[vertexAttrDescCount_ui_base]; VkPipelineVertexInputStateCreateInfo vkPipelineVertexInputStateCreateInfo_ui_base{vkPipelineVertexInputStateCreateInfo_txtr}; { @@ -2213,7 +2495,7 @@ void CreateGraphicsPipelines() { + sizeof(glm::vec4) // in_background_color + sizeof(glm::vec2) // px_scale + sizeof(float) // depth - + (sizeof(float) * 1) // padding + + sizeof(float) // texture_layer + 0; vertInputBD_ui_base[index].inputRate = VK_VERTEX_INPUT_RATE_INSTANCE; // index += 1; @@ -2258,18 +2540,25 @@ void CreateGraphicsPipelines() { offset += sizeof(glm::vec4); index += 1; - // instance - in_sprite_region_min + // instance - px_scale vertAttrDesc_ui_base[index].binding = 2; vertAttrDesc_ui_base[index].format = VK_FORMAT_R32G32_SFLOAT; vertAttrDesc_ui_base[index].offset = offset; offset += sizeof(glm::vec2); index += 1; - // instance - in_sprite_region_min + // instance - depth vertAttrDesc_ui_base[index].binding = 2; vertAttrDesc_ui_base[index].format = VK_FORMAT_R32_SFLOAT; vertAttrDesc_ui_base[index].offset = offset; - // offset += sizeof(glm::vec2); + offset += sizeof(float); + index += 1; + + // instance - texture_layer + vertAttrDesc_ui_base[index].binding = 2; + vertAttrDesc_ui_base[index].format = VK_FORMAT_R32_SFLOAT; + vertAttrDesc_ui_base[index].offset = offset; + // offset += sizeof(float); // index += 1; vkPipelineVertexInputStateCreateInfo_ui_base.vertexBindingDescriptionCount = vertexBindingCount_ui_base; @@ -2408,8 +2697,15 @@ void CreateGraphicsPipelines() { vkPipelineShaderStageCreateInfo_ui_base[0].stage = VK_SHADER_STAGE_VERTEX_BIT; vkPipelineShaderStageCreateInfo_ui_base[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; - VkGraphicsPipelineCreateInfo vkGraphicsPipelineCreateInfo[4]; - for (long i = 0; i < 4; ++i) { + /* pipelines + * 0: entity_standard + * 1: entity_wireframe + * 2: glyph + * 3: ui_base + * 4: ui_txtr + */ + std::array<VkGraphicsPipelineCreateInfo, 5> vkGraphicsPipelineCreateInfo; + for (size_t i = 0; i < vkGraphicsPipelineCreateInfo.size(); ++i) { vkGraphicsPipelineCreateInfo[i].sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; vkGraphicsPipelineCreateInfo[i].pNext = nullptr; vkGraphicsPipelineCreateInfo[i].flags = {}; @@ -2417,6 +2713,7 @@ void CreateGraphicsPipelines() { vkGraphicsPipelineCreateInfo[i].pInputAssemblyState = &vkPipelineInputAssemblyStateCreateInfo; vkGraphicsPipelineCreateInfo[i].pTessellationState = nullptr; vkGraphicsPipelineCreateInfo[i].pViewportState = &vkPipelineViewportStateCreateInfo; + vkGraphicsPipelineCreateInfo[i].pRasterizationState = &vkPipelineRasterizationStateCreateInfoFill; vkGraphicsPipelineCreateInfo[i].pMultisampleState = &vkPipelineMultisampleStateCreateInfo; vkGraphicsPipelineCreateInfo[i].pDepthStencilState = &vkPipelineDepthStencilStateCreateInfo; vkGraphicsPipelineCreateInfo[i].pColorBlendState = &vkPipelineColorBlendStateCreateInfo; @@ -2424,46 +2721,50 @@ void CreateGraphicsPipelines() { vkGraphicsPipelineCreateInfo[i].subpass = 0; vkGraphicsPipelineCreateInfo[i].basePipelineHandle = VK_NULL_HANDLE; vkGraphicsPipelineCreateInfo[i].basePipelineIndex = {}; + vkGraphicsPipelineCreateInfo[i].renderPass = VK_NULL_HANDLE; // dynamic rendering } vkGraphicsPipelineCreateInfo[0].layout = pkePipelines.pipe_layouts.named.ubo_txtr; vkGraphicsPipelineCreateInfo[1].layout = pkePipelines.pipe_layouts.named.ubo_txtr; vkGraphicsPipelineCreateInfo[2].layout = pkePipelines.pipe_layouts.named.txtr; vkGraphicsPipelineCreateInfo[3].layout = pkePipelines.pipe_layouts.named.base; - - vkGraphicsPipelineCreateInfo[0].renderPass = VK_NULL_HANDLE; // dynamic rendering - vkGraphicsPipelineCreateInfo[1].renderPass = VK_NULL_HANDLE; // dynamic rendering - vkGraphicsPipelineCreateInfo[2].renderPass = VK_NULL_HANDLE; // dynamic rendering - vkGraphicsPipelineCreateInfo[3].renderPass = VK_NULL_HANDLE; // dynamic rendering + vkGraphicsPipelineCreateInfo[4].layout = pkePipelines.pipe_layouts.named.txtr; vkGraphicsPipelineCreateInfo[0].pVertexInputState = &vkPipelineVertexInputStateCreateInfo_txtr; vkGraphicsPipelineCreateInfo[1].pVertexInputState = &vkPipelineVertexInputStateCreateInfo_txtr; vkGraphicsPipelineCreateInfo[2].pVertexInputState = &vkPipelineVertexInputStateCreateInfo_glyph; vkGraphicsPipelineCreateInfo[3].pVertexInputState = &vkPipelineVertexInputStateCreateInfo_ui_base; + vkGraphicsPipelineCreateInfo[4].pVertexInputState = &vkPipelineVertexInputStateCreateInfo_ui_base; - vkGraphicsPipelineCreateInfo[0].pRasterizationState = &vkPipelineRasterizationStateCreateInfoFill; vkGraphicsPipelineCreateInfo[1].pRasterizationState = &vkPipelineRasterizationStateCreateInfoLine; - vkGraphicsPipelineCreateInfo[2].pRasterizationState = &vkPipelineRasterizationStateCreateInfoFill; - vkGraphicsPipelineCreateInfo[3].pRasterizationState = &vkPipelineRasterizationStateCreateInfoFill; vkGraphicsPipelineCreateInfo[0].pStages = vkPipelineShaderStageCreateInfo_txtr; vkGraphicsPipelineCreateInfo[1].pStages = vkPipelineShaderStageCreateInfo_txtr; vkGraphicsPipelineCreateInfo[2].pStages = vkPipelineShaderStageCreateInfo_glyph; vkGraphicsPipelineCreateInfo[3].pStages = vkPipelineShaderStageCreateInfo_ui_base; + vkGraphicsPipelineCreateInfo[4].pStages = vkPipelineShaderStageCreateInfo_ui_base; // deffered shader creation - vkPipelineShaderStageCreateInfo_txtr[0].module = UploadShader(vertShaderAssetHandle); - vkPipelineShaderStageCreateInfo_txtr[1].module = UploadShader(textureFragShaderAssetHandle); - vkPipelineShaderStageCreateInfo_glyph[0].module = UploadShader(vertGlyphAssetHandle); - vkPipelineShaderStageCreateInfo_glyph[1].module = UploadShader(fragGlyphAssetHandle); - vkPipelineShaderStageCreateInfo_ui_base[0].module = UploadShader(asset_handle_vert_ui_base); - vkPipelineShaderStageCreateInfo_ui_base[1].module = UploadShader(asset_handle_frag_ui_base); + auto shader_module_3d_vert = UploadShader(vertShaderAssetHandle); + auto shader_module_3d_frag = UploadShader(textureFragShaderAssetHandle); + auto shader_module_glyph_vert = UploadShader(vertGlyphAssetHandle); + auto shader_module_glyph_frag = UploadShader(fragGlyphAssetHandle); + auto shader_module_ui_base_vert = UploadShader(asset_handle_vert_ui_base); + auto shader_module_ui_clr_frag = UploadShader(asset_handle_frag_ui_base); + auto shader_module_ui_txtr_frag = UploadShader(asset_handle_frag_ui_txtr); + vkPipelineShaderStageCreateInfo_txtr[0].module = shader_module_3d_vert; + vkPipelineShaderStageCreateInfo_txtr[1].module = shader_module_3d_frag; + vkPipelineShaderStageCreateInfo_glyph[0].module = shader_module_glyph_vert; + vkPipelineShaderStageCreateInfo_glyph[1].module = shader_module_glyph_frag; + vkPipelineShaderStageCreateInfo_ui_base[0].module = shader_module_ui_base_vert; + vkPipelineShaderStageCreateInfo_ui_base[1].module = shader_module_ui_clr_frag; VkPipelineRenderingCreateInfo renderingInfo{}; vkGraphicsPipelineCreateInfo[0].pNext = &renderingInfo; vkGraphicsPipelineCreateInfo[1].pNext = &renderingInfo; vkGraphicsPipelineCreateInfo[2].pNext = &renderingInfo; vkGraphicsPipelineCreateInfo[3].pNext = &renderingInfo; + vkGraphicsPipelineCreateInfo[4].pNext = &renderingInfo; renderingInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO; std::array<VkFormat, 3> color_formats; @@ -2489,16 +2790,25 @@ void CreateGraphicsPipelines() { vkResult = vkCreateGraphicsPipelines(vkDevice, VK_NULL_HANDLE, 1, &vkGraphicsPipelineCreateInfo[3], vkAllocator, &pkePipelines.pipelines.named.ui_base); assert(vkResult == VK_SUCCESS); + + vkPipelineShaderStageCreateInfo_ui_base[1].module = shader_module_ui_txtr_frag; + vkResult = vkCreateGraphicsPipelines(vkDevice, VK_NULL_HANDLE, 1, &vkGraphicsPipelineCreateInfo[4], vkAllocator, &pkePipelines.pipelines.named.ui_txtr); + assert(vkResult == VK_SUCCESS); + assert(pkePipelines.pipelines.named.entity_standard != VK_NULL_HANDLE); assert(pkePipelines.pipelines.named.entity_wireframe != VK_NULL_HANDLE); assert(pkePipelines.pipelines.named.font_glyph != VK_NULL_HANDLE); assert(pkePipelines.pipelines.named.ui_base != VK_NULL_HANDLE); + assert(pkePipelines.pipelines.named.ui_txtr != VK_NULL_HANDLE); + + vkDestroyShaderModule(vkDevice, shader_module_ui_txtr_frag, vkAllocator); + vkDestroyShaderModule(vkDevice, shader_module_ui_clr_frag, vkAllocator); + vkDestroyShaderModule(vkDevice, shader_module_ui_base_vert, vkAllocator); + vkDestroyShaderModule(vkDevice, shader_module_glyph_frag, vkAllocator); + vkDestroyShaderModule(vkDevice, shader_module_glyph_vert, vkAllocator); + vkDestroyShaderModule(vkDevice, shader_module_3d_frag, vkAllocator); + vkDestroyShaderModule(vkDevice, shader_module_3d_vert, vkAllocator); - for (long i = 0; i < 2; ++i) { - vkDestroyShaderModule(vkDevice, vkPipelineShaderStageCreateInfo_txtr[i].module, vkAllocator); - vkDestroyShaderModule(vkDevice, vkPipelineShaderStageCreateInfo_glyph[i].module, vkAllocator); - vkDestroyShaderModule(vkDevice, vkPipelineShaderStageCreateInfo_ui_base[i].module, vkAllocator); - } } // debug texture @@ -3107,6 +3417,7 @@ void pkvk_transition_image_layout(VkCommandBuffer command_buffer, } void RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { + uint32_t i, counter; vkResetCommandBuffer(commandBuffer, 0); VkCommandBufferBeginInfo beginInfo; @@ -3336,15 +3647,30 @@ void RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { vkCmdSetScissor(commandBuffer, 0, 1, &scissor); // 2d overlay grbinds - pke_ui_graphics_bindings *ui_gr = pke_ui_get_graphics_bindings(); - if (ui_gr != nullptr && ui_gr->instance_counter > 0) + const pke_ui_graphics_bindings &ui_gr = pke_ui_get_graphics_bindings(); + pk_arr_t<pke_ui_graphics_bindings_texture> ui_gr_texture; + pke_ui_get_graphics_bindings_texture(&ui_gr_texture); + if (ui_gr.instance_counter > 0) { + counter = 0; + for (i = 0; i < ui_gr_texture.next; ++i) { + counter += ui_gr_texture[i].instance_count; + } + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pkePipelines.pipelines.named.ui_base); - vkCmdBindIndexBuffer(commandBuffer, ui_gr->bd_index.buffer, ui_gr->bd_index.offsets[0], VK_INDEX_TYPE_UINT16); - vkCmdBindVertexBuffers(commandBuffer, ui_gr->bd_vertex.firstBinding, ui_gr->bd_vertex.bindingCount, &ui_gr->bd_vertex.buffer, ui_gr->bd_vertex.offsets); - vkCmdBindVertexBuffers(commandBuffer, ui_gr->bd_uv.firstBinding, ui_gr->bd_uv.bindingCount, &ui_gr->bd_uv.buffer, ui_gr->bd_uv.offsets); - vkCmdBindVertexBuffers(commandBuffer, ui_gr->bd_instance.firstBinding, ui_gr->bd_instance.bindingCount, &ui_gr->bd_instance.buffer, ui_gr->bd_instance.offsets); - vkCmdDrawIndexed(commandBuffer, ui_gr->index_count, ui_gr->instance_counter, 0, 0, 0); + vkCmdBindIndexBuffer(commandBuffer, ui_gr.bd_index.buffer, ui_gr.bd_index.offsets[0], VK_INDEX_TYPE_UINT16); + vkCmdBindVertexBuffers(commandBuffer, ui_gr.bd_vertex.firstBinding, ui_gr.bd_vertex.bindingCount, &ui_gr.bd_vertex.buffer, ui_gr.bd_vertex.offsets); + vkCmdBindVertexBuffers(commandBuffer, ui_gr.bd_uv.firstBinding, ui_gr.bd_uv.bindingCount, &ui_gr.bd_uv.buffer, ui_gr.bd_uv.offsets); + vkCmdBindVertexBuffers(commandBuffer, ui_gr.bd_instance.firstBinding, ui_gr.bd_instance.bindingCount, &ui_gr.bd_instance.buffer, ui_gr.bd_instance.offsets); + vkCmdDrawIndexed(commandBuffer, ui_gr.index_count, ui_gr.instance_counter - counter, 0, 0, 0); + + if (counter > 0) { + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pkePipelines.pipelines.named.ui_txtr); + for (i = 0; i < ui_gr_texture.next; ++i) { + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pkePipelines.pipe_layouts.named.txtr, 0, 1, &ui_gr_texture[i].descriptor_sets[imageIndex], 0, {}); + vkCmdDrawIndexed(commandBuffer, ui_gr.index_count, ui_gr_texture[i].instance_count, 0, 0, ui_gr_texture[i].instance_offset); + } + } } vkCmdEndRendering(commandBuffer); @@ -3790,6 +4116,7 @@ void CreateWindow(PKEWindowProperties wp) { void DestroyWindow() { if (vkInstance == nullptr) return; + window_tick_late(0.0); if (pke_window_did_init_imgui == true) { ImGui_ImplVulkan_Shutdown(); ImGui_ImplGlfw_Shutdown(); @@ -3817,6 +4144,8 @@ void DestroyWindow() { vkDestroyPipeline(vkDevice, pkePipelines.pipelines.named.font_glyph, vkAllocator); if (pkePipelines.pipelines.named.ui_base != VK_NULL_HANDLE) vkDestroyPipeline(vkDevice, pkePipelines.pipelines.named.ui_base, vkAllocator); + if (pkePipelines.pipelines.named.ui_txtr != VK_NULL_HANDLE) + vkDestroyPipeline(vkDevice, pkePipelines.pipelines.named.ui_txtr, vkAllocator); if (pkePipelines.pipe_layouts.named.ubo_txtr != VK_NULL_HANDLE) vkDestroyPipelineLayout(vkDevice, pkePipelines.pipe_layouts.named.ubo_txtr, vkAllocator); @@ -3860,8 +4189,11 @@ void DestroyWindow() { fprintf(stderr, "VkAllocator has '%u' outstanding allocations!", vulkanAllocs.next); } pk_arr_reset(&vulkanAllocs); + pk_arr_reset(&pkvk_actn_queue.destroy.image_view); + pk_arr_reset(&pkvk_actn_queue.destroy.image); pk_arr_reset(&pkvk_actn_queue.destroy.buffers); pk_arr_reset(&pkvk_actn_queue.destroy.memory); + pk_arr_reset(&pkvk_actn_queue.destroy.descriptor_pool); // TODO there's un-freed vulkan stuff in the bucket // comment this out to see it in the debug printout pk_mem_bucket_destroy(MemBkt_Vulkan); @@ -3932,6 +4264,7 @@ void CalculateCombinedMemReqs(uint64_t memReqsCount, VkMemoryRequirements *memRe * Vulkan was emitting an error that the buffer was in use by a command buffer. * Processing these actions after the graphics queue is idle solved that specific issue. * This is a stop-gap measure so I can keep working and is not intended to be permanent. + * This might be okay for switching levels (maybe?) * PERF * This will block the main thread until all in-flight renders are no longer using any of the resources needed to render a scene. * Move this to a fire-and-forget background thread that uses a memory barrier and a semaphore specifically for the target resource. @@ -3939,18 +4272,34 @@ void CalculateCombinedMemReqs(uint64_t memReqsCount, VkMemoryRequirements *memRe void window_tick_late(double delta) { (void)delta; uint32_t i; - if (pkvk_actn_queue.destroy.buffers.next > 0 - || pkvk_actn_queue.destroy.memory.next > 0) { + if (pkvk_actn_queue.destroy.image_view.next > 0 + || pkvk_actn_queue.destroy.image.next > 0 + || pkvk_actn_queue.destroy.buffers.next > 0 + || pkvk_actn_queue.destroy.memory.next > 0 + || pkvk_actn_queue.destroy.descriptor_pool.next > 0 + ) { vkQueueWaitIdle(pkvk_shared.queue.graphics); } + for (i = 0; i < pkvk_actn_queue.destroy.image_view.next; ++i) { + vkDestroyImageView(vkDevice, pkvk_actn_queue.destroy.image_view[i], vkAllocator); + } + for (i = 0; i < pkvk_actn_queue.destroy.image.next; ++i) { + vkDestroyImage(vkDevice, pkvk_actn_queue.destroy.image[i], vkAllocator); + } for (i = 0; i < pkvk_actn_queue.destroy.buffers.next; ++i) { vkDestroyBuffer(vkDevice, pkvk_actn_queue.destroy.buffers[i], vkAllocator); } for (i = 0; i < pkvk_actn_queue.destroy.memory.next; ++i) { vkFreeMemory(vkDevice, pkvk_actn_queue.destroy.memory[i], vkAllocator); } + for (i = 0; i < pkvk_actn_queue.destroy.descriptor_pool.next; ++i) { + vkDestroyDescriptorPool(vkDevice, pkvk_actn_queue.destroy.descriptor_pool[i], vkAllocator); + } + pk_arr_clear(&pkvk_actn_queue.destroy.image_view); + pk_arr_clear(&pkvk_actn_queue.destroy.image); pk_arr_clear(&pkvk_actn_queue.destroy.buffers); pk_arr_clear(&pkvk_actn_queue.destroy.memory); + pk_arr_clear(&pkvk_actn_queue.destroy.descriptor_pool); } void Render() { diff --git a/src/window.hpp b/src/window.hpp index 90793ed..3844998 100644 --- a/src/window.hpp +++ b/src/window.hpp @@ -88,6 +88,7 @@ struct ImplementedPKVK { VkPipeline entity_wireframe; VkPipeline font_glyph; VkPipeline ui_base; + VkPipeline ui_txtr; } named; } pipelines; }; @@ -150,8 +151,12 @@ struct pkvk_texture_upload_data_out { VkDeviceMemory device_memory; }; void pkvk_texture_upload(pkvk_texture_upload_data *data, pkvk_texture_upload_data_out *out); +void pkvk_texture_upload_array(pkvk_texture_upload_data *data, pkvk_texture_upload_data_out *out); +void pkvk_queue_vk_image_view_destroy(VkImageView image_view); +void pkvk_queue_vk_image_destroy(VkImage image); void pkvk_queue_vk_buffer_destroy(VkBuffer buffer); void pkvk_queue_vk_memory_free(VkDeviceMemory memory); +void pkvk_queue_vk_descriptor_pool_destroy(VkDescriptorPool descriptor_pool); #endif /* PKE_WINDOW_HPP */ |
