summaryrefslogtreecommitdiff
path: root/src/static-ui.cpp
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/static-ui.cpp
parentebcae77b137a759c453b89a774ece5a755078a38 (diff)
pke: ui box type button image
Diffstat (limited to 'src/static-ui.cpp')
-rw-r--r--src/static-ui.cpp372
1 files changed, 308 insertions, 64 deletions
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);
}