summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJonathan Bradley <jcb@pikum.xyz>2025-08-19 13:51:40 -0400
committerJonathan Bradley <jcb@pikum.xyz>2025-08-19 14:33:08 -0400
commit154436ab88925540f86f43c0ac885c080949aa9b (patch)
tree43a22f26286428f0d165fc1ff801cd0cb87092c6 /src
parentebcae77b137a759c453b89a774ece5a755078a38 (diff)
pke: ui box type button image
Diffstat (limited to 'src')
-rw-r--r--src/asset-manager.cpp2
-rw-r--r--src/asset-manager.hpp2
-rw-r--r--src/ecs.cpp19
-rw-r--r--src/entities.cpp4
-rw-r--r--src/game.cpp1
-rw-r--r--src/project.cpp2
-rw-r--r--src/serialization-static-ui.cpp108
-rw-r--r--src/serialization.hpp3
-rw-r--r--src/static-ui.cpp372
-rw-r--r--src/static-ui.hpp31
-rw-r--r--src/window.cpp427
-rw-r--r--src/window.hpp5
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, &copy_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 */