summaryrefslogtreecommitdiff
path: root/src/font.cpp
diff options
context:
space:
mode:
authorJonathan Bradley <jcb@pikum.xyz>2025-01-23 21:57:31 -0500
committerJonathan Bradley <jcb@pikum.xyz>2025-01-23 21:57:31 -0500
commite93eb289ca44e98967482ab80fd5329f85ccd03e (patch)
tree4164b6d5b9ac2e40d18ec3eea52730c9f9606ccb /src/font.cpp
parent846a6e1185417ee3e187edc06ef327d180bf0d9b (diff)
pke: first-pass 2d overlay render pass scaffolding
Diffstat (limited to 'src/font.cpp')
-rw-r--r--src/font.cpp638
1 files changed, 638 insertions, 0 deletions
diff --git a/src/font.cpp b/src/font.cpp
new file mode 100644
index 0000000..d218430
--- /dev/null
+++ b/src/font.cpp
@@ -0,0 +1,638 @@
+
+#include "font.hpp"
+#include "asset-manager.hpp"
+#include "pk.h"
+#include "window.hpp"
+#include "static-plane.hpp"
+#include "vendor-stb-image-include.h"
+// #include "bucketed-array.hpp"
+
+// TODO threading
+
+TypeSafeInt_B(FontTypeIndex);
+TypeSafeInt_B(FontRenderIndex);
+
+struct FontTypeData {
+ FontType *arr_ft;
+ FontTypeIndex n_ft{0};
+ FontTypeIndex h_ft{0};
+} ftd;
+
+// BucketContainer<FontType, TextRenderHandle, 2> bktFont;
+
+void FontType_Init() {
+ ftd.h_ft = FontTypeIndex{5};
+ ftd.arr_ft = pk_new<FontType>(5);
+}
+
+void FontType_Teardown() {
+ FontTypeIndex i;
+ FontType *ft;
+ for (i = FontTypeIndex{0}; i < ftd.h_ft; ++i) {
+ FontType_Unload(i);
+ ft = &ftd.arr_ft[(FontTypeIndex_T)i];
+ pk_delete<FontRender>(ft->renders, (FontTypeIndex_T)ft->n_render);
+ }
+ pk_delete(ftd.arr_ft, (FontTypeIndex_T)ftd.n_ft);
+}
+
+FontType* FontType_GetFonts(FontTypeIndex &count)
+{
+ count = ftd.h_ft;
+ return ftd.arr_ft;
+}
+
+FontTypeIndex FontType_RegisterFont(pk_cstr title, AssetHandle fontTextureHandle, AssetHandle glyphsHandle) {
+ VkResult vkResult;
+ pk_arr arr_vert_mem_reqs;
+ memset(&arr_vert_mem_reqs, 0, sizeof(pk_arr));
+ arr_vert_mem_reqs.alignment = alignof(VkMemoryRequirements);
+ arr_vert_mem_reqs.stride = sizeof(VkMemoryRequirements);
+ pk_arr arr_inst_mem_reqs(arr_vert_mem_reqs);
+ pk_arr arr_texture_mem_reqs(arr_vert_mem_reqs);
+ VkDeviceSize index;
+ constexpr VkDeviceSize expectedBufferCount = 3;
+ constexpr VkDeviceSize startingGlyphCount = 4;
+
+ VkMemoryRequirements combined_vert_mem_reqs;
+ VkMemoryRequirements combined_inst_mem_reqs;
+ VkMemoryRequirements combined_texture_mem_reqs;
+
+ const Asset *fontTexture = AM_Get(fontTextureHandle);
+ const Asset *glyphs = AM_Get(glyphsHandle);
+ if (ftd.h_ft > ftd.n_ft) {
+ index = (VkDeviceSize)ftd.n_ft;
+ ftd.n_ft += FontTypeIndex{5};
+ FontType *arr = pk_new<FontType>((FontTypeIndex_T)ftd.n_ft);
+ memcpy(arr, ftd.arr_ft, sizeof(FontType) * index);
+ pk_delete<FontType>(ftd.arr_ft, index);
+ ftd.arr_ft = arr;
+ }
+
+ FontTypeIndex idx = ftd.h_ft++;
+ FontTypeIndex_T idx_t = (FontTypeIndex_T)idx;
+ FontType *ft = &ftd.arr_ft[idx_t];
+ ft->title = title;
+ ft->fontTextureAssetKey = fontTextureHandle;
+ ft->glyphDetailsAssetKey = glyphsHandle;
+ ft->renders = pk_new<FontRender>(startingGlyphCount);
+ ft->n_render = FontRenderIndex{startingGlyphCount};
+
+ int txtr_x, txtr_y, txtr_chan;
+ stbi_uc *txtr_bytes = stbi_load_from_memory((unsigned char*)fontTexture->ptr, fontTexture->size, &txtr_x, &txtr_y, &txtr_chan, 4);
+ assert(txtr_chan == 4);
+
+ /*
+ * vulkan setup
+ */
+ VkBuffer buffer;
+ VkBufferCreateInfo bufferCI;
+ bufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufferCI.pNext = nullptr;
+ bufferCI.flags = {};
+ bufferCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ bufferCI.queueFamilyIndexCount = 1;
+ bufferCI.pQueueFamilyIndices = &graphicsFamilyIndex;
+
+ /*
+ * Vulkan resource setup
+ */
+ // vertex
+ {
+ for (uint64_t index = 0; index < expectedBufferCount; ++index) {
+ if (index == 0) {
+ bufferCI.size = sizeof(glm::vec2) * 4;
+ } else if (index == 1) {
+ bufferCI.size = sizeof(glm::vec2) * 4;
+ } else if (index == 2) {
+ bufferCI.size = sizeof(uint16_t) * 6;
+ }
+ if (index == 2) {
+ bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
+ } else {
+ bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+ }
+ vkCreateBuffer(vkDevice, &bufferCI, vkAllocator, &buffer);
+ VkMemoryRequirements tmp;
+ vkGetBufferMemoryRequirements(vkDevice, buffer, &tmp);
+ pk_arr_append(&arr_vert_mem_reqs, &tmp);
+ vkDestroyBuffer(vkDevice, buffer, vkAllocator);
+ }
+ }
+ // instance
+ {
+ bufferCI.size = 0;
+ bufferCI.size += sizeof(glm::vec4); // fgColor
+ bufferCI.size += sizeof(glm::vec4); // bgColor
+ bufferCI.size += sizeof(glm::vec4); // sprite_region
+ bufferCI.size += sizeof(float); // width
+ bufferCI.size *= startingGlyphCount;
+ bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+ vkCreateBuffer(vkDevice, &bufferCI, vkAllocator, &buffer);
+ VkMemoryRequirements tmp;
+ vkGetBufferMemoryRequirements(vkDevice, buffer, &tmp);
+ pk_arr_append(&arr_inst_mem_reqs, &tmp);
+ vkDestroyBuffer(vkDevice, buffer, vkAllocator);
+ }
+ // texture
+ VkImageCreateInfo imageCreateInfo{};
+ VkImageSubresourceRange vkImageSubresourceRange;
+ {
+ imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ imageCreateInfo.pNext = nullptr;
+ imageCreateInfo.flags = 0;
+ imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
+ imageCreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
+ imageCreateInfo.extent.width = txtr_x;
+ imageCreateInfo.extent.height = txtr_y;
+ imageCreateInfo.extent.depth = 1;
+ imageCreateInfo.mipLevels = 1;
+ imageCreateInfo.arrayLayers = 1;
+ imageCreateInfo.samples = renderSampleCount;
+ 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 = nullptr;
+ imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+ vkImageSubresourceRange.aspectMask = VkImageAspectFlagBits::VK_IMAGE_ASPECT_COLOR_BIT;
+ vkImageSubresourceRange.baseMipLevel = 0u;
+ vkImageSubresourceRange.levelCount = 1u;
+ vkImageSubresourceRange.baseArrayLayer = 0u;
+ vkImageSubresourceRange.layerCount = 1u;
+
+ VkImageViewCreateInfo vkImageViewCreateInfo;
+ vkImageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ vkImageViewCreateInfo.pNext = nullptr;
+ vkImageViewCreateInfo.flags = 0;
+ vkImageViewCreateInfo.image = ft->textureImage;
+ vkImageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ vkImageViewCreateInfo.format = imageCreateInfo.format;
+ 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;
+
+ VkImage tmpImage;
+ vkResult = vkCreateImage(vkDevice, &imageCreateInfo, vkAllocator, &tmpImage);
+ assert(vkResult == VK_SUCCESS);
+
+ VkMemoryRequirements imageMemoryRequirements;
+ vkGetImageMemoryRequirements(vkDevice, tmpImage, &imageMemoryRequirements);
+
+ VkDeviceSize paddedImageSize = imageMemoryRequirements.size + (imageMemoryRequirements.alignment - (imageMemoryRequirements.size % imageMemoryRequirements.alignment));
+ assert(paddedImageSize % imageMemoryRequirements.alignment == 0);
+
+ vkDestroyImage(vkDevice, tmpImage, vkAllocator);
+
+ VkMemoryAllocateInfo vkMemoryAllocateInfo{};
+ vkMemoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ vkMemoryAllocateInfo.pNext = nullptr;
+ vkMemoryAllocateInfo.allocationSize = paddedImageSize * MAX_FRAMES_IN_FLIGHT;
+ vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(imageMemoryRequirements.memoryTypeBits,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+
+ vkResult = vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &ft->deviceMemoryTexture);
+ assert(vkResult == VK_SUCCESS);
+
+ vkResult = vkCreateImage(vkDevice, &imageCreateInfo, vkAllocator, &ft->textureImage);
+ assert(vkResult == VK_SUCCESS);
+ vkResult = vkBindImageMemory(vkDevice, ft->textureImage, ft->deviceMemoryTexture, 0);
+ assert(vkResult == VK_SUCCESS);
+
+ vkImageViewCreateInfo.image = ft->textureImage;
+ vkResult = vkCreateImageView(vkDevice, &vkImageViewCreateInfo, vkAllocator, &ft->textureImageView);
+ assert(vkResult == VK_SUCCESS);
+ }
+
+ /*
+ * Vulkan memory allocation
+ */
+
+ VkMemoryAllocateInfo vkMemoryAllocateInfo;
+ vkMemoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ vkMemoryAllocateInfo.pNext = nullptr;
+
+ // verts
+ CalculateCombinedMemReqs(arr_vert_mem_reqs.next, (VkMemoryRequirements*)arr_vert_mem_reqs.data, combined_vert_mem_reqs);
+ vkMemoryAllocateInfo.allocationSize = combined_vert_mem_reqs.size;
+ vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(combined_vert_mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &ft->deviceMemoryVert);
+
+ // inst
+ CalculateCombinedMemReqs(arr_inst_mem_reqs.next, (VkMemoryRequirements*)arr_inst_mem_reqs.data, combined_inst_mem_reqs);
+ vkMemoryAllocateInfo.allocationSize = combined_inst_mem_reqs.size;
+ vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(combined_inst_mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &ft->deviceMemoryInst);
+
+ // texture
+ CalculateCombinedMemReqs(arr_texture_mem_reqs.next, (VkMemoryRequirements*)arr_texture_mem_reqs.data, combined_texture_mem_reqs);
+ vkMemoryAllocateInfo.allocationSize = combined_texture_mem_reqs.size;
+ vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(combined_texture_mem_reqs.memoryTypeBits, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
+ if (vkMemoryAllocateInfo.memoryTypeIndex == 0) {
+ vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(combined_texture_mem_reqs.memoryTypeBits, 0);
+ }
+ vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &ft->deviceMemoryTexture);
+
+ /*
+ * Vulkan data transfer
+ */
+ VkBuffer transferBuffer;
+ VkDeviceMemory transferMemory;
+ void *deviceData;
+ VkDeviceSize runningOffset, alignmentPadding;
+ // vert
+ BeginTransferBuffer(combined_vert_mem_reqs.size, transferBuffer, transferMemory, deviceData);
+ {
+ index = 0;
+ runningOffset = 0;
+
+ uint32_t offsetVert = runningOffset;
+ uint32_t sizeVert = sizeof(pkeIntrinsicsPlane.vert[0]) * 4;
+ ft->bindings.vertexBD.firstBinding = index;
+ ft->bindings.vertexBD.bindingCount = 1;
+ alignmentPadding = sizeVert % combined_vert_mem_reqs.alignment;
+ alignmentPadding = alignmentPadding == 0 ? 0 : combined_vert_mem_reqs.alignment - alignmentPadding;
+ sizeVert += alignmentPadding;
+ bufferCI.size = sizeVert;
+ bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+ vkCreateBuffer(vkDevice, &bufferCI, vkAllocator, &ft->bindings.vertexBD.buffer);
+ vkBindBufferMemory(vkDevice, ft->bindings.vertexBD.buffer, ft->deviceMemoryVert, offsetVert);
+ runningOffset += sizeVert;
+ index += 1;
+
+ // uv
+ uint32_t offsetUV = runningOffset;
+ uint32_t sizeUV = sizeof(pkeIntrinsicsPlane.uv[0]) * 4;
+ ft->bindings.uvBD.firstBinding = index;
+ ft->bindings.uvBD.bindingCount = 1;
+ alignmentPadding = sizeUV % combined_vert_mem_reqs.alignment;
+ alignmentPadding = alignmentPadding == 0 ? 0 : combined_vert_mem_reqs.alignment - alignmentPadding;
+ sizeUV += alignmentPadding;
+ bufferCI.size = sizeUV;
+ vkCreateBuffer(vkDevice, &bufferCI, vkAllocator, &ft->bindings.uvBD.buffer);
+ vkBindBufferMemory(vkDevice, ft->bindings.uvBD.buffer, ft->deviceMemoryVert, offsetUV);
+ runningOffset += sizeUV;
+ index += 1;
+
+ // 2023-09-27 - JCB
+ // I don't know where else to put this
+ ft->bindings.instanceBD.firstBinding = index;
+ ft->bindings.instanceBD.bindingCount = 1;
+ // no index += 1 because index just happens to be the right value here for
+ // the binding index, whereas binding the IndexBuffer doesn't need a binding index.
+
+ // index
+ uint32_t offsetIndex = runningOffset;
+ uint32_t sizeIndex = sizeof(pkeIntrinsicsPlane.index[0]) * 6;
+ ft->bindings.indexBD.bindingCount = 1;
+ ft->bindings.indexCount = 6;
+ alignmentPadding = sizeIndex % combined_vert_mem_reqs.alignment;
+ alignmentPadding = alignmentPadding == 0 ? 0 : combined_vert_mem_reqs.alignment - alignmentPadding;
+ sizeIndex += alignmentPadding;
+ bufferCI.size = sizeIndex;
+ bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
+ vkCreateBuffer(vkDevice, &bufferCI, vkAllocator, &ft->bindings.indexBD.buffer);
+ vkBindBufferMemory(vkDevice, ft->bindings.indexBD.buffer, ft->deviceMemoryVert, offsetIndex);
+ runningOffset += sizeIndex;
+ // index += 1;
+
+ assert(runningOffset == combined_vert_mem_reqs.size);
+
+
+ runningOffset = 0;
+
+ char *dstPtr = nullptr;
+ char *srcPtr = nullptr;
+
+ dstPtr = static_cast<char *>(deviceData) + runningOffset;
+ srcPtr = reinterpret_cast<char *>(pkeIntrinsicsPlane.vert);
+ memcpy(dstPtr, srcPtr, sizeVert);
+ runningOffset += sizeVert;
+
+ dstPtr = static_cast<char *>(deviceData) + runningOffset;
+ srcPtr = reinterpret_cast<char *>(pkeIntrinsicsPlane.uv);
+ memcpy(dstPtr, srcPtr, sizeUV);
+ runningOffset += sizeUV;
+
+ dstPtr = static_cast<char *>(deviceData) + runningOffset;
+ srcPtr = reinterpret_cast<char *>(pkeIntrinsicsPlane.index);
+ memcpy(dstPtr, srcPtr, sizeIndex);
+ // runningOffset += sizeIndex;
+
+ VkCommandBufferBeginInfo vkCommandBufferBeginInfo;
+ vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ vkCommandBufferBeginInfo.pNext = nullptr;
+ // TODO consider single-use?
+ vkCommandBufferBeginInfo.flags = 0;
+ vkCommandBufferBeginInfo.pInheritanceInfo = nullptr;
+ vkBeginCommandBuffer(transferCommandBuffer, &vkCommandBufferBeginInfo);
+ VkBufferCopy bufferCopys[expectedBufferCount];
+ for (VkDeviceSize i = 0; i < expectedBufferCount; ++i) {
+ bufferCopys[i].dstOffset = 0;
+ }
+ index = 0;
+ bufferCopys[index].srcOffset = offsetVert;
+ bufferCopys[index].size = sizeVert;
+ vkCmdCopyBuffer(transferCommandBuffer, transferBuffer, ft->bindings.vertexBD.buffer, 1, &bufferCopys[index]);
+ index+=1;
+
+ bufferCopys[index].srcOffset = offsetUV;
+ bufferCopys[index].size = sizeUV;
+ vkCmdCopyBuffer(transferCommandBuffer, transferBuffer, ft->bindings.uvBD.buffer, 1, &bufferCopys[index]);
+ index+=1;
+
+ bufferCopys[index].srcOffset = offsetIndex;
+ bufferCopys[index].size = sizeIndex;
+ vkCmdCopyBuffer(transferCommandBuffer, transferBuffer, ft->bindings.indexBD.buffer, 1, &bufferCopys[index]);
+ // index+=1;
+
+ vkEndCommandBuffer(transferCommandBuffer);
+
+ 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 = &transferCommandBuffer;
+ submitInfo.signalSemaphoreCount = 0;
+ submitInfo.pSignalSemaphores = nullptr;
+ vkQueueSubmit(transferQueue, 1, &submitInfo, nullptr);
+ vkQueueWaitIdle(transferQueue);
+ }
+ EndTransferBuffer(transferBuffer, transferMemory);
+ // set up instance buffer
+ {
+ ft->bindings.instanceBufferMaxCount = startingGlyphCount;
+ bufferCI.size = combined_inst_mem_reqs.size;
+ bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+ vkCreateBuffer(vkDevice, &bufferCI, vkAllocator, &ft->bindings.instanceBD.buffer);
+ vkBindBufferMemory(vkDevice, ft->bindings.instanceBD.buffer, ft->deviceMemoryInst, 0);
+ }
+ // texture // transition image layout and copy to buffer
+ BeginTransferBuffer(combined_texture_mem_reqs.size, transferBuffer, transferMemory, deviceData);
+ memcpy(deviceData, txtr_bytes, combined_texture_mem_reqs.size);
+ {
+ VkImageMemoryBarrier vkImageMemoryBarrier;
+ vkImageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ vkImageMemoryBarrier.pNext = nullptr;
+ 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.image = ft->textureImage;
+ vkImageMemoryBarrier.subresourceRange = VkImageSubresourceRange {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ // TODO MipMap
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ };
+
+ 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(transferCommandBuffer, &vkCommandBufferBeginInfo);
+
+ vkCmdPipelineBarrier(transferCommandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &vkImageMemoryBarrier);
+
+ VkBufferImageCopy vkBufferImageCopy;
+ vkBufferImageCopy.bufferOffset = 0;
+ vkBufferImageCopy.bufferRowLength = txtr_x;
+ vkBufferImageCopy.bufferImageHeight = txtr_y;
+ vkBufferImageCopy.imageSubresource = VkImageSubresourceLayers {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .mipLevel = 0,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ };
+ vkBufferImageCopy.imageOffset = VkOffset3D {
+ .x = 0,
+ .y = 0,
+ .z = 0,
+ };
+ vkBufferImageCopy.imageExtent = VkExtent3D {
+ .width = static_cast<uint32_t>(txtr_x),
+ .height = static_cast<uint32_t>(txtr_y),
+ .depth = 1,
+ };
+ vkCmdCopyBufferToImage(transferCommandBuffer, transferBuffer, ft->textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &vkBufferImageCopy);
+
+ vkEndCommandBuffer(transferCommandBuffer);
+
+ 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 = &transferCommandBuffer;
+ submitInfo.signalSemaphoreCount = 0;
+ submitInfo.pSignalSemaphores = nullptr;
+ vkQueueSubmit(transferQueue, 1, &submitInfo, nullptr);
+ vkQueueWaitIdle(transferQueue);
+ vkResetCommandBuffer(transferCommandBuffer, 0);
+ }
+ EndTransferBuffer(transferBuffer, transferMemory);
+ {
+ VkImageMemoryBarrier vkImageMemoryBarrier;
+ vkImageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ vkImageMemoryBarrier.pNext = nullptr;
+ 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;
+ vkImageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ vkImageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ vkImageMemoryBarrier.image = ft->textureImage;
+ vkImageMemoryBarrier.subresourceRange = VkImageSubresourceRange {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ // TODO MipMap
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ };
+
+ 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(graphicsCommandBuffer, &vkCommandBufferBeginInfo);
+
+ vkCmdPipelineBarrier(graphicsCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &vkImageMemoryBarrier);
+
+ vkEndCommandBuffer(graphicsCommandBuffer);
+
+ 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 = &graphicsCommandBuffer;
+ submitInfo.signalSemaphoreCount = 0;
+ submitInfo.pSignalSemaphores = nullptr;
+ vkQueueSubmit(graphicsQueue, 1, &submitInfo, nullptr);
+ vkQueueWaitIdle(graphicsQueue);
+ }
+
+ /*
+ * Vulkan descriptor sets
+ */
+ VkDescriptorPoolSize descriptorPoolSizes[1];
+ descriptorPoolSizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ descriptorPoolSizes[0].descriptorCount = MAX_FRAMES_IN_FLIGHT;
+
+ VkDescriptorPoolCreateInfo vkDescriptorPoolCreateInfo;
+ vkDescriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+ vkDescriptorPoolCreateInfo.pNext = nullptr;
+ vkDescriptorPoolCreateInfo.flags = 0;
+ vkDescriptorPoolCreateInfo.maxSets = MAX_FRAMES_IN_FLIGHT;
+ vkDescriptorPoolCreateInfo.poolSizeCount = (uint32_t)1;
+ vkDescriptorPoolCreateInfo.pPoolSizes = descriptorPoolSizes;
+
+ // consider making me a global pool
+ vkResult = vkCreateDescriptorPool(vkDevice, &vkDescriptorPoolCreateInfo, vkAllocator, &ft->vkDescriptorPool);
+ assert(vkResult == VK_SUCCESS);
+
+ VkDescriptorSetLayout descriptorSets[MAX_FRAMES_IN_FLIGHT];
+ for (long i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
+ descriptorSets[i] = pkePipelines.descr_layouts.named.glyph;
+ }
+ VkDescriptorSetAllocateInfo vkDescriptorSetAllocateInfo;
+ vkDescriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ vkDescriptorSetAllocateInfo.pNext = nullptr;
+ vkDescriptorSetAllocateInfo.descriptorPool = ft->vkDescriptorPool;
+ vkDescriptorSetAllocateInfo.descriptorSetCount = MAX_FRAMES_IN_FLIGHT;
+ vkDescriptorSetAllocateInfo.pSetLayouts = descriptorSets;
+
+ ft->vkDescriptorSets = pk_new<VkDescriptorSet>(MAX_FRAMES_IN_FLIGHT);
+ for (long i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
+ ft->vkDescriptorSets[i] = VkDescriptorSet{};
+ }
+ vkResult = vkAllocateDescriptorSets(vkDevice, &vkDescriptorSetAllocateInfo, ft->vkDescriptorSets);
+ assert(vkResult == VK_SUCCESS);
+
+ VkWriteDescriptorSet writeDescriptorSets[1 * MAX_FRAMES_IN_FLIGHT];
+ for (long i = 0; i < 1 * MAX_FRAMES_IN_FLIGHT; ++i) {
+ writeDescriptorSets[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ writeDescriptorSets[i].pNext = nullptr;
+ writeDescriptorSets[i].dstSet = nullptr;
+ writeDescriptorSets[i].dstBinding = i;
+ writeDescriptorSets[i].dstArrayElement = 0;
+ writeDescriptorSets[i].descriptorCount = 1;
+ writeDescriptorSets[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ writeDescriptorSets[i].pImageInfo = nullptr;
+ writeDescriptorSets[i].pBufferInfo = nullptr;
+ writeDescriptorSets[i].pTexelBufferView = nullptr;
+ }
+
+ VkDescriptorImageInfo textureDescriptorInfo;
+ textureDescriptorInfo.sampler = pkePipelines.vkSampler_Texture;
+ textureDescriptorInfo.imageView = ft->textureImageView;
+ textureDescriptorInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+ VkDescriptorBufferInfo vkDescriptorBufferInfo[MAX_FRAMES_IN_FLIGHT];
+
+ for (long i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
+ vkDescriptorBufferInfo[i].buffer = UniformBuffers[i];
+ vkDescriptorBufferInfo[i].offset = 0;
+ vkDescriptorBufferInfo[i].range = sizeof(UniformBufferObject);
+
+ long uboIndex = i * 2;
+ long samplerIndex = uboIndex + 1;
+
+ writeDescriptorSets[uboIndex].pBufferInfo = &vkDescriptorBufferInfo[i];
+ writeDescriptorSets[uboIndex].dstSet = ft->vkDescriptorSets[i];
+
+ writeDescriptorSets[samplerIndex].pImageInfo = &textureDescriptorInfo;
+ writeDescriptorSets[samplerIndex].dstSet = ft->vkDescriptorSets[i];
+ }
+
+ vkUpdateDescriptorSets(vkDevice, 2 * MAX_FRAMES_IN_FLIGHT, writeDescriptorSets, 0, nullptr);
+
+ return idx;
+}
+
+void FontType_Unload(FontTypeIndex idx)
+{
+ FontType *ft = &ftd.arr_ft[(FontTypeIndex_T)idx];
+
+ if (ft->vkDescriptorSets != VK_NULL_HANDLE && ft->vkDescriptorPool != VK_NULL_HANDLE) {
+ // 2023-09-27 - JCB (copied from entities.cpp)
+ // We are not setting the pool flag for allowing freeing descriptor sets
+ // so all we need to do is destroy the pool
+ // If we switch to a global pool, we will need to free here, and
+ // destroy the pool outside of this loop
+ vkDestroyDescriptorPool(vkDevice, ft->vkDescriptorPool, vkAllocator);
+ ft->vkDescriptorSets = CAFE_BABE(VkDescriptorSet);
+ ft->vkDescriptorPool = VK_NULL_HANDLE;
+ }
+
+ if (ft->bindings.vertexBD.buffer != VK_NULL_HANDLE)
+ vkDestroyBuffer(vkDevice, ft->bindings.vertexBD.buffer, vkAllocator);
+ ft->bindings.vertexBD.buffer = VK_NULL_HANDLE;
+ ft->bindings.vertexBD.firstBinding = 0;
+ ft->bindings.vertexBD.bindingCount = 0;
+ ft->bindings.vertexBD.offsets[0] = 0;
+
+ if (ft->bindings.uvBD.buffer != VK_NULL_HANDLE)
+ vkDestroyBuffer(vkDevice, ft->bindings.uvBD.buffer, vkAllocator);
+ ft->bindings.uvBD.buffer = VK_NULL_HANDLE;
+ ft->bindings.uvBD.firstBinding = 0;
+ ft->bindings.uvBD.bindingCount = 0;
+ ft->bindings.uvBD.offsets[0] = 0;
+
+ if (ft->bindings.indexBD.buffer != VK_NULL_HANDLE)
+ vkDestroyBuffer(vkDevice, ft->bindings.indexBD.buffer, vkAllocator);
+ ft->bindings.indexBD.buffer = VK_NULL_HANDLE;
+ ft->bindings.indexBD.bindingCount = 0;
+ ft->bindings.indexBD.offsets[0] = 0;
+ ft->bindings.indexCount = 0;
+
+ if (ft->bindings.instanceBD.buffer != VK_NULL_HANDLE)
+ vkDestroyBuffer(vkDevice, ft->bindings.instanceBD.buffer, vkAllocator);
+ ft->bindings.instanceBD.buffer = VK_NULL_HANDLE;
+ ft->bindings.instanceBD.firstBinding = 0;
+ ft->bindings.instanceBD.bindingCount = 0;
+ ft->bindings.instanceCounter = 0;
+ ft->bindings.instanceBufferMaxCount = 0;
+ ft->bindings.instanceBD.offsets[0] = 0;
+
+ if (ft->textureImageView != VK_NULL_HANDLE)
+ vkDestroyImageView(vkDevice, ft->textureImageView, vkAllocator);
+ ft->textureImageView = VK_NULL_HANDLE;
+
+ if (ft->textureImage != VK_NULL_HANDLE)
+ vkDestroyImage(vkDevice, ft->textureImage, vkAllocator);
+ ft->textureImage = VK_NULL_HANDLE;
+
+ if (ft->deviceMemoryInst != VK_NULL_HANDLE)
+ vkFreeMemory(vkDevice, ft->deviceMemoryInst, vkAllocator);
+ ft->deviceMemoryInst = VK_NULL_HANDLE;
+
+ if (ft->deviceMemoryVert != VK_NULL_HANDLE)
+ vkFreeMemory(vkDevice, ft->deviceMemoryVert, vkAllocator);
+ ft->deviceMemoryVert = VK_NULL_HANDLE;
+
+ if (ft->deviceMemoryTexture != VK_NULL_HANDLE)
+ vkFreeMemory(vkDevice, ft->deviceMemoryTexture, vkAllocator);
+ ft->deviceMemoryTexture = VK_NULL_HANDLE;
+}